big ol patch from Dave peticolas

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@1993 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Linas Vepstas 1999-12-31 00:05:41 +00:00
parent 49d51b4432
commit 77b52f628f
129 changed files with 14182 additions and 3502 deletions

1104
ChangeLog

File diff suppressed because it is too large Load Diff

7
Docs/En/tidy-up Executable file
View File

@ -0,0 +1,7 @@
#!/bin/ksh
# $ID$
# If you have Dave Raggett's "tidy" utility, this will tidy up
# the HTML files here.
for i in *.html ; do
tidy -m -i $i
donediff -u 'pristine/gnucash/Docs/En/xacc-about.html' 'working/gnucash/Docs/En/xacc-about.html'

View File

@ -1,286 +1,472 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>About GnuCash</title>
</head>
<body bgcolor="#eeeeee">
<body>
<h1>About GnuCash</h1>
<hr>
GnuCash is a program to keep track of your finances. Its
features include:
<ul>
<li>Multiple accounts, which can be open at
the same time. Create one xacc account
for each of your bank accounts.
<li>Each account keeps a running balance and
a reconciled balance, so you can keep track
of the checks that have cleared your account.
<li>A simple interface. If you can use the
register in the back of your checkbook,
you can use xacc.
<li>Automatic account reconciling. At the end
of the month, open up the reconcile window,
enter your bank statement's ending balance,
and check off the transactions that appear
in the bank statement. This makes it easy
to track down any discrepancies.
<li>QuickFill... if you begin typing
in the description field, and the text matches a
previous transaction, hitting TAB will copy
that previous transaction. Handy if you have
similar transactions on a regular basis.
<li>Stock/Mutual Fund Portfolios. Track stocks
individually (one per account) or in portfolio
of accounts (a group of accounts that can be
displayed together).
<li>Support for multiple currencies and currency
trading accounts. (partial, still broken).
Bank accounts may be established in different
currencies, and trades with indicated prices
may be made, much as stocks would be traded.
<li><a href="xacc-quicken.html">Quicken File Import</a>.
Import Quicken Version 3.0 QIF files.
<li><a href="xacc-reports.html">Reports</a>. Display
or output as HTML Balance or Profit&amp;Loss reports.
<li><b> Multiple accounts,</b> which can be open at the same
time. Create an xacc account for each of your bank
accounts.</li>
<li>Each account maintains both a running balance and a
reconciled balance, so you can keep track of the checks that
have cleared your account.</li>
<li>
<b>A simple interface.</b>
<p> If you can use the register in the back of your
checkbook, you can use <tt>xacc.</tt></p>
</li>
<li>
<b> <a href="xacc-recnwin.html"> Automated Tools for
Reconciling Accounts.</a></b>
<p>At the end of the month, open up the <b> reconcile</b>
window, enter the ending balance from your bank statement,
and check off the transactions that appear in the bank
statement. This agrees what you have recorded in GnuCash
with what your bank has reported, and makes it easier to
track down any discrepancies.</p>
</li>
<li>
<b> QuickFill.</b>
<p> If you begin typing in the description field, and the
text matches a previous transaction, hitting <tt> TAB</tt>
will copy in that previous transaction. This is a handy
time saver if you regularly create similar
transactions.</p>
</li>
<li>
<b>Stock/Mutual Fund Portfolios.</b>
<p> Track stocks individually (one per account) or in
portfolio of accounts (a group of accounts that can be
displayed together).</p>
<p> There are tools to <a href="xacc-ticker.html">
automatically collect stock quotes.</a></p>
</li>
<li>
Support for <b> <a href="xacc-currency.html"> multiple
currencies</a></b> and <b> currency trading accounts.</b>
(partial, still broken).
<p> Bank accounts may be established in different
currencies, and trades at varying exchange rates may be
made, in much the same way stocks trade at varying
prices.</p>
</li>
<li>
<b><a href="xacc-quicken.html#QIF">Quicken File
Import</a>.</b>
<p> Imports Quicken-style QIF files.</p>
</li>
<li><b> <a href="xacc-reports.html">Reports</a>.</b> Display
or output as HTML Balance or Profit&amp;Loss reports.</li>
</ul>
<h2>Advanced Features</h2>
GnuCash offers some features not usually found
in simpler accounting programs.
<p> GnuCash offers some features not found in simpler
accounting programs.</p>
<ul>
<li>Sub-accounts: A master account can have a hierarchy
of detail accounts underneath it. This allows similar
account types (e.g. Cash, Bank, Stock) to be grouped
into one master account (e.g. Assets).
<li><a href="xacc-double.html">Double Entry</a>:
Every transaction can appear in two
accounts; one account is debited and the other is
credited with exactly the same amount. With
double-entry, a transaction edited in one window
will be automatically updated in all other windows
showing that transaction, and in both of the
accounts.
<li><a href="xacc-expense.html">Income/Expense Account Types
(Categories)</a>. When used properly
with the double-entry feature, these can be used
to create both Balance Sheet and Profits &amp; Losses
reports. For example, savings account interest,
stock dividends, or paychecks can be marked as
both a deposit in a bank account, and as income in
an Income account type, using the double-entry
(transfer) feature. Similarly, credit card charges
can be noted in the credit card account, as well
as in a corresponding expense account.
<li>General Ledger: Multiple accounts can be displayed
in one register window at the same time. This can
ease the trouble of tracking down typing/entry errors.
It also provides a convenient way of viewing a
portfolio of many stocks, by showing all transactions
in that portfolio.
<li>
<b> <a href="xacc-groups.html"> Account Hierarchy</a></b>
<p>A master account can have a hierarchy of more detailed
accounts arranged underneath it. This allows related
account types (<em>e.g.</em> - Cash, Bank, Stock) to be
grouped under one master account (<em> e.g.</em> -
Assets).</p>
</li>
<li>
<b> <a href="xacc-double.html">Double Entry</a></b>
<p>Every transaction involves two accounts, and <em> each
transaction</em> is required to balance. This provides
assurance that the overall set of books will add up
correctly, and prevents out-of-balance errors
altogether.</p>
</li>
<li>
<b> <a href="xacc-expense.html"> Income/Expense Account
Types</a></b>
<p> Intuit's <a href="xacc-quicken.html"> Quicken</a>
product has what they call "categories" that are used to
track incomes and expenses. These may be used to create
Profits &amp; Losses reports.</p>
</li>
<li>
<b> General Ledger</b>
<p> Multiple accounts may be displayed in one register
window at the same time. This can make it easier to track
down data errors. It also provides a convenient way of
viewing a portfolio of many stocks, by showing all
transactions in that portfolio.</p>
</li>
<li><b> Handling of <a href="xacc-currency.html"> multiple
currencies</a></b></li>
</ul>
<h2>Version</h2>
This version is gnucash-1.3.x, a somewhat unstable, buggy version.
The latest stable release is 1.2.x, you should be using that.
<p>
Versions 1.0.x and 1.2.x are considered to be stable revisions with all
currently known bugs fixed. Versions 1.3.x are considered to be
development versions, which may have many bugs. The next stable
series will be 1.4.x
<p>
<p> The versioning scheme for X-Accountant parallels that of
the Linux kernel, where "even" sub-versions indicate versions
that are intended to be stable, only seeing maintenance to fix
bugs, and "odd" sub-versions indicate an "experimental" stream
that seeks to add enhancement.</p>
<p> The present "experimental" stream is gnucash-1.3.x, which
is somewhat unstable.</p>
<p> The latest stable release is 1.2.x; if you don't intend to
do development work, you should be using either this version,
or an older 1.0.x version. These versions are fairly stable,
with all currently known bugs fixed.</p>
<p> Once the 1.3.x series stabilizes, the next stable series
will be 1.4.x, and experimentation will likely continue on
1.5.x.</p>
<h2>Lead Developers</h2>
<dl>
<dt>Robin Clark &lt;rclark@hmc.edu&gt;
<dd>wrote the original X-Accountant in Motif
as a school project, taking it to version 0.9 by October 1997.
<dt>Robin Clark &lt;rclark@hmc.edu&gt;</dt>
<dt>Linas Vepstas &lt;linas@linas.org&gt;
<dd>liked what he saw: the GUI was slick,
the code was documented and well structured, and it was all GPL'ed.
And so he re-wrote it: adding cell-widgets to XbaeMatrix, so that
the combobox and arrows would make an even slicker GUI, rewrote the
X-Accountant internals to add double-entry, an account heirarchy,
split out a transaction mini-engine, add support for stocks, and spiff
up the help menus. This was version 1.0 as of January 1998. Since
then, for version 1.1, the engine was expanded &amp; refined, and the
register window code completely redesigned and made mostly
Motif-(and GUI-)independent. Did some prototype OFX work.
<dd>wrote the original X-Accountant in Motif as a school
project, taking it to version 0.9 by October 1997.</dd>
<dt>Jeremy Collins &lt;jcollins@gnucash.org&gt;
<dd>publicized the GnoMoney project
widely and broadly, and then changed its name to GnuCash. Jeremy
created the gnucash.org web site, registered the domain, got the
initial GTK/gnome code working.
<dt>Linas Vepstas &lt;linas@linas.org&gt;</dt>
<dt>Rob Browning &lt;rlb@cs.utexas.edu&gt;
<dd>abused everyone for not using perl,
and then added guile/scheme support. Rob maintains the build
infrastructure, is handling the whole guile/perl extension language
thing, and is dealing with configuration &amp; configurability.
<dd>liked what he saw: the GUI was slick, the code was
documented and well structured, and it was all GPL'ed. And so
he re-wrote it: adding cell-widgets to XbaeMatrix, so that
the combobox and arrows would make an even slicker GUI,
rewrote the X-Accountant internals to add double-entry, an
account heirarchy, split out a transaction mini-engine, add
support for stocks, and spiff up the help menus. This was
version 1.0 as of January 1998. Since then, for version 1.1,
the engine was expanded &amp; refined, and the register
window code completely redesigned and made mostly Motif-(and
GUI-)independent. Did some prototype OFX work.</dd>
<dt>Jeremy Collins &lt;jcollins@gnucash.org&gt;</dt>
<dd>publicized the GnoMoney project widely and broadly, and
then changed its name to GnuCash. Jeremy created the
gnucash.org web site, registered the domain, got the initial
GTK/gnome code working.</dd>
<dt>Rob Browning &lt;rlb@cs.utexas.edu&gt;</dt>
<dd>abused everyone for not using perl, and then added
guile/scheme support. Rob maintains the build infrastructure,
is handling the whole guile/perl extension language thing,
and is dealing with configuration &amp; configurability.</dd>
<dt>Dirk Schoenberger &lt;schoenberger@signsoft.com&gt;</dt>
<dd>is working on the Qt/KDE port</dd>
<dt>Dave Peticolas &lt;peticola@cs.ucdavis.edu&gt;</dt>
<dd>hacks obsessively on GnuCash. But he can stop anytime
he wants to. Really.</dd>
<dt>Dave Peticolas
<dd>did a whole lot and should write a bio here.
</dl>
<p>
<h2>Fixes &amp; Patches</h2>
<br>Andrew Arensburger &lt;arensb@cfar.umd.edu&gt; for FreeBSD &amp; other patches
<br>Matt Armstrong &lt;matt_armstrong@bigfoot.com&gt; for misc fixes
<br>Fred Baube &lt;fred@moremagic.com&gt; for attempted Java port/MoneyDance
<br>Christopher B. Browne &lt;cbbrowne@hex.net&gt; for perl stock scripts
<br>Graham Chapman &lt;grahamc@zeta.org.au&gt; for the xacc-rpts addon package
<br>George Chen &lt;georgec@sco.com&gt; for MS-Money QIF's &amp; fixes
<br>Jeremey Collins &lt;jcollins@gnucash.org&gt; for GnoMoney &amp; GTK port
<br>Patrick Condron &lt;pcondon@rackspace.com&gt; for webserver and T1 connection.
<br>Ciaran Deignan &lt;Ciaran.Deignan@bull.net&gt; for AIX binary version
<br>Tyson Dowd &lt;tyson@tyse.net&gt; for config/make patches &amp; debian maint.
<br>Koen D'Hondt &lt;ripley@xs4all.nl&gt; for Solaris patches to XmHTML
<br>Bob Drzyzgula &lt;bob@mostly.com&gt; for budgeting design notes
<br>Jan-Uwe Finck &lt;ju_finck@mail.netwave.de&gt; for German message translation
<br>Ron Forrester &lt;rjf@aracnet.com&gt; for gnome patches
<br>Dave Freese &lt;DFreese@osc.uscg.mil&gt; for leap-year fix
<br>Otto Hammersmith &lt;otto@bug.redhat.com&gt; for RedHat RPM version
<br>Alexandru Harsanyi &lt;haral@codec.ro&gt; for misc core dumps &amp; lockups.
<br>Jon K}re Hellan &lt;jk@isdn-a33.itea.ntnu.no&gt; misc core dump fixes
<br>Prakash Kailasa &lt;PrakashK@bigfoot.com&gt; for gnome build fixes
<br>Tom Kludy &lt;tkludy@csd.sgi.com&gt; for SGI Irix port
<br>Sven Kuenzler &lt;sk@xgm.de&gt; for SuSE README file
<br>Ted Lemon &lt;mellon@andare.fugue.com&gt; for NetBSD port
<br>Yannick Le Ny &lt;y-le-ny@ifrance.com&gt; pour la traduction en francais
<br>G. Allen Morris III &lt;gam3@ann.softgams.com&gt; for QIF core dump
<br>Peter Norton &lt;spacey@inch.com&gt; for a valiant attempt at a GTK port
<br>OmNiBuS &lt;webmaster@obsidian.uia.net&gt; web site graphics &amp; content
<br>Myroslav Opyr &lt;mopyr@IPM.Lviv.UA&gt; for misc patches
<br>Alain Peyrat &lt;Alain.Peyrat@nmu.alcatel.fr&gt; for configure.in patches
<br>Gavin Porter &lt;maufk@csv.warwick.ac.uk&gt; for euro style dates
<br>Ron Record &lt;rr@sco.com&gt; for SCO Unixware &amp; OpenServer binaries
<br>Christopher Seawood &lt;cls@seawood.org&gt; for XbaeMatrix core dump
<br>Mike Simons &lt;msimons@fsimons01.erols.com&gt; misc configure.in patches
<br>Richard Skelton &lt;rich@brake.demon.co.uk&gt; for Solaris cleanup
<br>Henning Spruth &lt;spruth@bigfoot.com&gt; for German text &amp; euro date rework
<br>Ken Yamaguchi &lt;gooch@ic.EECS.Berkeley.EDU&gt; QIF import fixes; MYM import
<br>
Andrew Arensburger &lt;arensb@cfar.umd.edu&gt; for FreeBSD
&amp; other patches <br>
Matt Armstrong &lt;matt_armstrong@bigfoot.com&gt; for misc
fixes <br>
Fred Baube &lt;fred@moremagic.com&gt; for attempted Java
port/MoneyDance <br>
<a href="http://www.hex.net/~cbbrowne"> Christopher B.
Browne</a> &lt;cbbrowne@hex.net&gt; for perl stock scripts,
Guile-based QIF import code <br>
Graham Chapman &lt;grahamc@zeta.org.au&gt; for the xacc-rpts
addon package <br>
George Chen &lt;georgec@sco.com&gt; for MS-Money QIF's &amp;
fixes <br>
Jeremey Collins &lt;jcollins@gnucash.org&gt; for GnoMoney
&amp; GTK port <br>
Patrick Condron &lt;pcondon@rackspace.com&gt; for webserver
and T1 connection. <br>
Ciaran Deignan &lt;Ciaran.Deignan@bull.net&gt; for AIX binary
version <br>
Tyson Dowd &lt;tyson@tyse.net&gt; for config/make patches
&amp; debian maint. <br>
Koen D'Hondt &lt;ripley@xs4all.nl&gt; for Solaris patches to
XmHTML <br>
Bob Drzyzgula &lt;bob@mostly.com&gt; for budgeting design
notes <br>
Jan-Uwe Finck &lt;ju_finck@mail.netwave.de&gt; for German
message translation <br>
Ron Forrester &lt;rjf@aracnet.com&gt; for gnome patches <br>
Dave Freese &lt;DFreese@osc.uscg.mil&gt; for leap-year fix
<br>
Otto Hammersmith &lt;otto@bug.redhat.com&gt; for RedHat RPM
version <br>
Alexandru Harsanyi &lt;haral@codec.ro&gt; for misc core dumps
&amp; lockups. <br>
Jon K}re Hellan &lt;jk@isdn-a33.itea.ntnu.no&gt; misc core
dump fixes <br>
Prakash Kailasa &lt;PrakashK@bigfoot.com&gt; for gnome build
fixes <br>
Tom Kludy &lt;tkludy@csd.sgi.com&gt; for SGI Irix port <br>
Sven Kuenzler &lt;sk@xgm.de&gt; for SuSE README file <br>
Ted Lemon &lt;mellon@andare.fugue.com&gt; for NetBSD port <br>
Yannick Le Ny &lt;y-le-ny@ifrance.com&gt; pour la traduction
en francais <br>
G. Allen Morris III &lt;gam3@ann.softgams.com&gt; for QIF core
dump <br>
Peter Norton &lt;spacey@inch.com&gt; for a valiant attempt at
a GTK port <br>
OmNiBuS &lt;webmaster@obsidian.uia.net&gt; web site graphics
&amp; content <br>
Myroslav Opyr &lt;mopyr@IPM.Lviv.UA&gt; for misc patches <br>
Alain Peyrat &lt;Alain.Peyrat@nmu.alcatel.fr&gt; for
configure.in patches <br>
Gavin Porter &lt;maufk@csv.warwick.ac.uk&gt; for euro style
dates <br>
Ron Record &lt;rr@sco.com&gt; for SCO Unixware &amp;
OpenServer binaries <br>
Christopher Seawood &lt;cls@seawood.org&gt; for XbaeMatrix
core dump <br>
Mike Simons &lt;msimons@fsimons01.erols.com&gt; misc
configure.in patches <br>
Richard Skelton &lt;rich@brake.demon.co.uk&gt; for Solaris
cleanup <br>
Henning Spruth &lt;spruth@bigfoot.com&gt; for German text
&amp; euro date rework <br>
Ken Yamaguchi &lt;gooch@ic.EECS.Berkeley.EDU&gt; QIF import
fixes; MYM import <br>
<h2>Supported Operating Systems</h2>
gnucash-1.0.18 (xacc-1.0.18) is known to work in the following configs:
<br>Linux 2.0.x -- Intel w/ RedHat Motif
<br>Linux 2.0.x -- Intel w/ Lesstif v0.81
<br>Linux Debian -- Intel w/ Lesstif v0.81
<br>SGI IRIX -- MIPS
<br>IBM AIX 4.1.5 -- RS/6000
<br>SCO Unixware 7 -- Intel
<br>SCO OpenServer 5.0.4 -- Intel
<br>NetBSD -- Intel
<p>
gnucash-1.0.18 (xacc-1.0.18) is known to work on the following
systems:
<a href="http://www.gnucash.org/">
<img src="logos/linux.gif"></a>
<ul>
<li>Linux 2.0.x -- Intel w/ RedHat Motif</li>
<a href="http://www.sco.com/skunkware">
<img src="logos/skunkware.gif"></a>
<li>Linux 2.0.x -- Intel w/ Lesstif v0.81</li>
<a href="http://www.bull.de/pub/">
<img src="logos/bullogogross.gif">
<img src="logos/ibm.gif">
</a>
<a href="http://www.sgi.com/">
<img src="logos/sgi.gif">
</a>
<li>Linux Debian -- Intel w/ Lesstif v0.81</li>
<a href="http://www.debian.org/">
<img src="logos/debian.jpg">
</a>
<li>SGI IRIX -- MIPS</li>
<img src = "logos/NetBSD-banner.gif">
<li>IBM AIX 4.1.5 -- RS/6000</li>
<li>SCO Unixware 7 -- Intel</li>
<li>SCO OpenServer 5.0.4 -- Intel</li>
<li>NetBSD -- Intel</li>
</ul>
<p> <a href="http://www.gnucash.org/"> <img src=
"logos/linux.gif"></a> <a href="http://www.sco.com/skunkware">
<img src="logos/skunkware.gif"></a> <a href=
"http://www.bull.de/pub/"><img src="logos/bullogogross.gif">
<img src="logos/ibm.gif"></a> <a href="http://www.sgi.com/">
<img src="logos/sgi.gif"></a> <a href="http://www.debian.org/">
<img src="logos/debian.jpg"></a> <img src=
"logos/NetBSD-banner.gif"></p>
<h2>History</h2>
The table below shows some historical lines-of-code and number-of-files
counts for the X-Accountant/GnuCash development project
<br>
<table border=1>
<CAPTION>Historical Development Stats</CAPTION>
<tr align=center>
<th>Version
<th>engine
<th>register
<th>ledger
<th>motif
<th>gnome
<th>qt
<th>prefs (scm)
<th>docs (html)
<th>misc
<th>Total
The table below shows some historical lines-of-code and
number-of-files counts for the X-Accountant/GnuCash development
project <br>
<tr align=center>
<td>xacc-0.9<br>Sept 97
<td> -
<td> -
<td> -
<td>34 files<br>(7.5+0.9)
<td> -
<td> -
<td> -
<td>5 files<br>(0.4)
<td> -
<td>39 files<br>(8.8)
<tr align=center>
<td>xacc-0.9w<br>Dec 97
<td> -
<td> -
<td> -
<td>51 files<br>(13.8+1.5)
<td> -
<td> -
<td> -
<td>9 files<br>(0.8)
<td> -
<td>60 files<br>(16.1)
<table border="1">
<caption>
Historical Development Stats
</caption>
<tr align=center>
<td>xacc-1.0.17<br>Feb 98
<td> -
<td> -
<td> -
<td>52 files<br>(14.8+1.8)
<td> -
<td> -
<td> -
<td>12 files<br>(1.4)
<td> -
<td>64 files<br>(18.0)
<tr align="center">
<th>Version</th>
<tr align=center>
<td>gnucash-1.1.15<br>Aug 98
<td>24 files<br>(6.2+1.5)
<td>31 files<br>(6.1+1.7)
<td>5 files<br>(1.4+0.4)
<td>30 files<br>(7.4+0.7)
<td>17 files<br>(3.4+0.5)
<td>16 files<br>(1.2+0.2)
<td>3 files<br>(0.3)
<td>16 files<br>(1.9)
<td>not counted<br>(>1.0)
<td>142 files<br>(32.9)
<th>engine</th>
<th>register</th>
<th>ledger</th>
<th>motif</th>
<th>gnome</th>
<th>qt</th>
<th>prefs (scm)</th>
<th>docs (html)</th>
<th>misc</th>
<th>Total</th>
</tr>
<tr align="center">
<td>xacc-0.9<br>
Sept 97</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>34 files<br>
(7.5+0.9)</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>5 files<br>
(0.4)</td>
<td>-</td>
<td>39 files<br>
(8.8)</td>
</tr>
<tr align="center">
<td>xacc-0.9w<br>
Dec 97</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>51 files<br>
(13.8+1.5)</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>9 files<br>
(0.8)</td>
<td>-</td>
<td>60 files<br>
(16.1)</td>
</tr>
<tr align="center">
<td>xacc-1.0.17<br>
Feb 98</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>52 files<br>
(14.8+1.8)</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>12 files<br>
(1.4)</td>
<td>-</td>
<td>64 files<br>
(18.0)</td>
</tr>
<tr align="center">
<td>gnucash-1.1.15<br>
Aug 98</td>
<td>24 files<br>
(6.2+1.5)</td>
<td>31 files<br>
(6.1+1.7)</td>
<td>5 files<br>
(1.4+0.4)</td>
<td>30 files<br>
(7.4+0.7)</td>
<td>17 files<br>
(3.4+0.5)</td>
<td>16 files<br>
(1.2+0.2)</td>
<td>3 files<br>
(0.3)</td>
<td>16 files<br>
(1.9)</td>
<td>not counted<br>
(&gt;1.0)</td>
<td>142 files<br>
(32.9)</td>
</tr>
</table>
<p>
Each cell contains:
<p> Each cell contains:<br>
<br>
<br>number of *c and *.h files
<br>(KLOCS in *.c + KLOCS in *.h),
<p>
where KLOC == kilo-lines-of-code, as reported by <tt>wc</tt>.
number of *c and *.h files<br>
(KLOCS in *.c + KLOCS in *.h),</p>
<p> where KLOC == kilo-lines-of-code, as reported by <tt>
wc</tt>.</p>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,41 +1,99 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Account Types</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h1>Account Types</h1>
<dl>
<dt><b>Bank</b>
<dd>The bank account type denotes a savings or checking account
held at a bank. Often interest bearing.
<dt><b>Cash</b>
<dd>The cash account type is used to denote a shoe-box or pillowcase
stuffed with cash.
<dt><b>Credit</b>
<dd>The Credit card account is used to denote credit (e.g. amex) and
debit (e.g. visa, mastercard) card accounts.
<dt><b>Asset, Liability</b>
<dd>Asset and liability accounts indicate generic, generalized accounts
that are none of the above.
<dt><b>Stock, Mutual Fund</b>
<dd>Stock and Mutual Fund accounts will typically be shown in registers
which show three columns: price, number of shares, and value.
<dt><b>Income, Expense</b>
<dd>Income and expense accounts are used to denote income and expenses.
Thus, when data in these accounts are displayed, the sign of the
entries is reversed from its usual meaning.
<dt><b>Equity</b>
<dd>Equity account is used to balance the balance sheet.
<dt><b>Currency</b>
<dd>Currency Accounts are used for trading currencies.
In many ways, they behave like stocks, except that the computation
of the value is different. Note that transfers cannot be made
directly between two accounts denominated in different currencies.
Such transfers may only be made into currency trading accounts.
(Safety checks may be a bit broken still ... )
<dl>
<dt><b>Cash</b></dt>
<dd>The <b>cash</b> account type is used to denote the cash
that you store in your wallet, shoebox, piggybank, or
mattress.</dd>
<dt><b>Bank</b></dt>
<dd>The <b>Bank</b> account type denotes a savings or
checking account held at a bank or other financial
institution. Such accounts often bear interest.</dd>
<dt><b>Credit</b></dt>
<dd>The <b> Credit</b> card account is used to denote credit
card accounts, whether involving floating lines of credit as
with VISA, MasterCard, or Discover, or others like American
Express, that do not permit you to maintain continuing
balances.</dd>
<dd>The introduction of <a href=
"http://www.visa.com/pd/debit/checkcard.html"> Check
Cards</a> where payments are withdrawn directly from a
checking account makes the selection less clear; it is
probably more appropriate to treat a "Check Card" as a <b>
Bank</b> account, as it does withdraw amounts directly from
such an account, not really involving any granting of
credit.</dd>
<dt><b>Asset, Liability</b></dt>
<dd><b>Asset</b> and <b>Liability</b> accounts are used for
tracking things that are of value, but that are not so
directly like cash.</dd>
<dd>For instance, you might collect the costs of purchasing a
house into an asset account entitled <b> My House,</b> or the
cost of a car into <b> My Car,</b> or collect together the
value of your <b> Computer Equipment.</b></dd>
<dd>And the home mortgage or car loan would be represented by
liability accounts, <b> Home Mortgage</b> and <b> Car
Loan</b>, to be drawn down as payments are made on these
loans.</dd>
<dt><b>Stock, Mutual Fund</b></dt>
<dd>
Stock and Mutual Fund accounts will typically be shown in
registers which show three columns:
<ul>
<li>Price</li>
<li>Number of shares</li>
<li>Value</li>
</ul>
</dd>
<dt><b>Income, Expense</b></dt>
<dd><b> Income</b> and <b>Expense</b> accounts are used to
collect incomes and expenses.</dd>
<dt><b>Equity</b></dt>
<dd><b>Equity</b> accounts are used to balance the balance
sheet.</dd>
<dt><a href="xacc-currency.html"> <b>Currency</b></a></dt>
<dd><b> Currency Accounts</b> are used for trading
currencies. In many ways, they behave like stocks, except
that the computation of the value is different. Note that
transfers cannot be made directly between two accounts
denominated in different currencies. Such transfers may only
be made into currency trading accounts. (Safety checks may be
a bit broken still ... )</dd>
</dl>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,36 +1,42 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>New Account Window</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h1>New Account Window</h1>
This is what a new account window looks like:
<p>
<br>
<img src="newaccwin-code.gif">
<br>
<p>
Pick an <a href="xacc-acctypes.html><b>Account Type</b></a>.
Fill in the blanks ... blah blah blah.
The <b>Currency</b> field should typically be a three-letter ISO curency
code (e.g. USD for U.S. Dollars, etc.)
The <b>Account Code</b> is a number that determines the
<a href="xacc-groups.html">sort order</a>
of the account when it appears in a report or in the
<a href="xacc-groups.html"><b>Chart of Accounts</b></a>.
<p>
The picure below shows an example for a stock or currency trading account.
Note that the Security field is not greyed out, and that you can
enter a value. That value is typically a stock-ticker symbol,
or a three-letter ISO currency code.
<p>
<br>
<img src="newaccwin-trade.gif">
<br>
<p> This is what a new account window looks like:</p>
<p> <img src="newaccwin-code.gif"></p>
<p> Pick an <a href="xacc-acctypes.html"><b>Account
Type</b></a>.</p>
<p> The <b>Currency</b> field should typically be a <a href=
"xacc-currency.html#ISOCURR"> three-letter ISO curency code
(<em>e.g.</em> - <tt> USD</tt> for U.S. Dollars).</a> The <b>
Account Code</b> is a number that determines the <a href=
"xacc-groups.html#SORTORDER"> sort order</a> of the account
when it appears in a report or in the <a href=
"xacc-groups.html"><b>Chart of Accounts</b></a>.</p>
<p> The picure below shows an example for a stock or currency
trading account. Note that the Security field is not greyed
out, and that you can enter a value. That value is typically a
stock-ticker symbol, or a <a href="xacc-currency.html#ISOCURR">
three-letter ISO currency code.</a></p>
<p><br>
<img src="newaccwin-trade.gif"><br>
</p>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,17 +1,21 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Adjust Balance Window</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h1>THIS FILE IS EMPTY</h1>
Adjust Balance window. Use this to adjust the balance.
Enter a dollar amount, and a register entry will be created
that sets the balance to the new balance.
Add more documentation here.
Adjust Balance window. Use this to adjust the balance. Enter a
dollar amount, and a register entry will be created that sets
the balance to the new balance. Add more documentation here.
<hr>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,54 +1,103 @@
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<title></title>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Accounts Payable/Accounts Receivable</title>
</head>
<body>
<h1>Accounts Payable &amp; Accounts Receivable</h1>
A/R and A/P are kind of deep,
<p> Anyways, let's consider A/R. We can't really relate to A/P
because we always pay <em>our</em> bills on time, don't we ?
:-)</p>
<p> A/R (Accounts Receivable) and A/P (Accounts Payable) are
somewhat deep to understand.</p>
<p> So anyways, let's say we give our customers 30 days to
pay.</p>
<p> Let us first examine A/R. After all, we really shouldn't
really <em>need</em> to relate to A/P because we always pay
<em>our</em> bills on time, don't we ? :-)</p>
<p> When we make a sale, the two accounts affected are Sales
(an income account) and A/R. A/R is an asset, but it's not
liquid, and it's not quite cash.</p>
<p> So, as a first cut, let us assume we don't require
customers to pay <em> instantly,</em> in cash, but rather issue
them an invoice, and give them 30 days to pay the bills. (Then
we can start charging interest and sending out harassing
letters :-)).</p>
<p> When we make a sale, the two accounts affected are <b>
Sales</b> (an income account) and <b>Accounts Receivable.</b>
Accounts Receivable is an asset, but it's not liquid, and it's
not <em> quite</em> cash.</p>
<p> Then when they come by to pay their bill, dropping off a
big bag of twenty-dollar bills, we transfer the amount from A/R
to Cash.</p>
large sack of twenty-dollar bills (or, more likely, a
check/cheque), we transfer the amount from A/R to Cash.</p>
<p> The reason we do it in two steps is that we've decided to
<p> The reason we do this in two steps is that we've decided to
do our accounting on an accrual basis and not on a cash basis,
bcos, well, most of our transactions are not cash, they're
obligations.</p>
because most of our transactions are not solely based on cash
changing hands, but rather based on <em> establishing
obligations.</em></p>
<p> In more sophisticated systems, there may be a whole
sequence of documents generated and tracked:</p>
<ul>
<li>A customer sends in a <b>Purchase Order</b>, thus
authorizing a purchase.</li>
<li>We set up a <b>Work Order</b> to schedule production of
whatever the customer is buying</li>
<li>We issue a <b>Shipping Notice</b>, to ship to goods to
the customer</li>
<li>Once shipped, we issue an <b>Invoice</b>, representing
the <em> request to pay</em></li>
</ul>
<p> We report sales in our sales figures as soon as we make
them, but if the auditor wants to know about whether we're
gonna get stuck with bad debts, we break down those A/R's by
how old they are: 0-30 says, 31-60 says, etc. At some point
when a particular debt is "written off", like when the cheesing
bastards go bankrupt, we dock both A/R and Sales, so we're
going back and patching up (or rather, "patching down") the
Sales account to show that the Sale was never made good.</p>
them, but if the auditor wants to know about whether we'll get
stuck with bad debts, we break down those A/R's based on the
"ages" of the debts, commonly segmented into three or four
periods, of 0-30 days, 31-60 days, etc.</p>
<p> We can use the same technique for things that we prepay. If
we have to plunk down six months' rent in advance, that is an
"accrued asset", and while it put a healthy dent in the Cash
account, it does show on the books as an asset. And if we've
been collecting payroll taxes from our employees and keeping
them in a special bank account, the money's not really ours, so
we have a growth in the Cash account on one side, and a growth
in an Accrued Liability, namely, Payroll Taxes Payable, on the
toher side. When we send the quarterly check to the Feds so
that they can make payroll too, our liability drops and so does
our Cash account.</p>
<p> At some point when a particular debt is "written off,"
perhaps when the cheesing bastards go bankrupt, we dock both
A/R and Sales, so we're going back and patching up (or rather,
less happily, "patching down") the Sales account to show that
the Sale was never made good. It is common to set up a <b> Bad
Debt Expense</b> account so that such incidences are recorded
in a way that makes it clearer that <em> sales went bad</em>
.</p>
<p> The above things, reversed, reflect how Accounts Payables
work; just switch customer with supplier, and see how the roles
reverse. If we buy materials "on account," accrual accounting
requires that we record that we incur the expense immediately,
and rather than reducing cash, we put the "credit" into the <b>
Accounts Payable</b> account. Three weeks later, the invoice
comes in, and we issue a payment, and so <em> Debit AP, Credit
Cash.</em></p>
<p> We can also use analagous techniques for expenses that we
prepay. If we have to pay out down six months of rent in
advance, that is treated as an "accrued asset," <b> Prepaid
Rent,</b> and while it puts an unfortunate dent in the Cash
account, it <em> does</em> show on the books as an asset that
will decline over time to zero. Each month, we draw the account
down via <em> Debit <b> Rent Expense</b>, Credit <b> Prepaid
Rent</b>.</em> Similarly, we've been collecting payroll taxes
from our employees and keeping them in a special bank account,
<em> that</em> money is not really ours, so we have a growth in
the Cash account on one side, and a growth in an Accrued
Liability, namely, <b>Payroll Taxes Payable</b>, on the other
side. When we send the quarterly check to the Government so
that they can make <em> their</em> payroll, our <b> Payroll
Taxes Payable</b> drops as does the balance in the <b>Checking
Account</b>.</p>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +1,44 @@
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<title></title>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Date Data Input</title>
</head>
<body>
<h1>Date Input</h1>
The date cell handles the following accelerator keys:
<pre>
'+':
'=': increment day
'_':
'-': decrement day
<ul>
<li><tt> +, =</tt> increment day</li>
'}':
']': increment month
<li><tt> _ , -</tt> decrement day</li>
'{':
'[': decrment month
<li><tt> } , ]</tt> increment month</li>
'M':
'm': begining of month
<li><tt> { , [</tt> decrment month</li>
'H':
'h': end of month
<li><tt> M , m</tt> begining of month</li>
'Y':
'y': begining of year
<li><tt> H , h</tt> end of month</li>
'R':
'r': end of year
<li><tt> Y , y</tt> begining of year</li>
'T':
't': today
<li><tt> R , r</tt> end of year</li>
</pre>
GnuCash can be compiled to use either European style dates or
US Style dates. <tt>Grep</tt> for <var> UK_DATES</var> in <tt>
dates.h</tt>
<li><tt> T , t</tt> today GnuCash can be compiled to use
either European style dates or US Style dates.</li>
<li>
<tt>Grep</tt> for <var> UK_DATES</var> in <tt> dates.h</tt>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</li>
</ul>
</body>
</html>

View File

@ -1,65 +1,120 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<title>Using the Double Entry Feature</title>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Understanding Double Entry Accounting</title>
</head>
<body bgcolor=#eeeeee>
<h2>What is Double Entry?</h2>
Double entry is an accounting methodology used by professionals
to make sure that all accounts are properly balanced. When accounts
balance, the likelihood that a data-entry error has been made is much
less. For large, complex accounts with many transactions, it is easy
to make errors that might go undetected for a long time. Double-entry
is a crucial technology for catching those errors.
<p>
A double-entry transaction is a transaction that appears in two
accounts. One account is debited by an amount exactly equal to
what the other is credited. Thus, all transactions are always
transfers between two accounts. Since they always appear with
a plus sign in one account, and a minus sign in the other, the
total over all accounts will always be zero, and thus, balanced
accounts are guaranteed.
<p>
Double-entry is already familiar to most people as a transfer
from one bank account to another, where money is withdrawn from
one and deposited in another. Far less familiar is the idea that
double entry can be used to track income and expenses as well as
bank transfers. See the
"<a href="xacc-expense.html">Income/Expense</a>" help window
for more information.
<body>
<h2>What is Double Entry Accounting?</h2>
<p> Double entry is an accounting methodology used by
professional accountants to make sure that all accounts and
indeed <em> each transaction</em> is properly balanced. When
these are all required to balance, the likelihood of data-entry
errors is greatly reduced. For large, complex sets of accounts
with many transactions, it is distressingly easy to make errors
that may go undetected for a long time. Double-entry is a
crucial technology that has been used since the 13th century to
avoid such errors.</p>
<p> A double-entry transaction is a transaction that contains
entries for two (or more) accounts that balance against one
another. One account is <em>debited</em> by an amount exactly
equal to what the other is <em>credited.</em> By ensuring that
each transaction balances, a balanced set of accounts is
guaranteed. This doesn't prevent you from having errors, but
certainly eliminates the large class of <b> I forgot to enter
that part of the transaction</b> errors.</p>
<p> Double-entry may be introduced in a more intuitive way via
the notion of a transfer from one bank account to another,
where an amount is taken out of one bank account and deposited
in the other. This is effectively the "rule" of double entry
accounting; if you add something in to one account, you <em>
have</em> to have another component to that transaction to
balance this.</p>
<p> <b> Not-quite-an-aside:</b> If you look at your bank
statements, they are typically written up from the <b>
bank's</b> perspective, which is exactly <em> opposite</em> to
yours. For instance, when you put money in, establishing a
deposit, this establishes a <b> DEBT</b> on their part.</p>
<p> The perhaps less obvious extension is the notion that
double entry can be used to represent income and expenses as
well as bank transfers. See the <a href="xacc-expense.html">
Income/Expense</a> page for more information.</p>
<p> <a name="IDENTITY"> In a traditional system that records
debits and credits separately, the identity that all
transactions are required to satisfy is that <tt> Total of
Debits = Total of Credits.</tt></a></p>
<p> X-Accountant treats "Debits" as positive values, and
"Credits" as negative values, so that this identity simplifies
to <tt> value<sub>1</sub> + value<sub>2</sub> +
value<sub>3</sub> + ... = 0</tt></p>
<h2>Using Double Entry</h2>
To use the double entry, click on a box in the column marked
"Transfer From" on the left-hand side of the register. A menu
will drop down, listing all of the accounts from which a transfer
may be made. Select one. When you record the transaction,
the double-entry will automatically be made, and the transaction
automatically appear in all windows showing the transfered-from
and the transferred-to accounts.
<p>
To change a double-entry transaction, edit it in any window in
which it appears. Any changes made will be automatically
reflected in both accounts and all windows displaying the
transaction. Similarly, when a double-entry transaction is
deleted, it is deleted from both accounts; balances are
automatically recalculated for both accounts.
<p>
To change the transfer account, simply select a new account
from the pull-down menu. When you record the transaction,
it will automatically be selected from the old account, and
inserted into the new account.
<p> To use the double entry system, click on a box in the
column marked "Transfer From" on the left-hand side of the
register. A menu will drop down, listing all of the accounts
from which a transfer may be made. Select one. When you record
the transaction, the double-entry will automatically be made,
and the transaction automatically appear in all windows showing
the transfered-from and the transferred-to accounts.</p>
<p> To <em> change</em> a double-entry transaction, edit the
transaction in any window in which it appears. Any changes made
will be automatically reflected in both accounts and all
windows displaying the transaction. Similarly, when a
double-entry transaction is deleted, it will be deleted from
both accounts; balances are automatically recalculated for <em>
both</em> accounts.</p>
<p> To change the transfer account, simply select a new account
from the pull-down menu. When you record the transaction, it
will automatically be selected from the old account, and
inserted into the new account.</p>
<h2>Scrubbing Clean</h2>
GnuCash can be configured to be strict about double entry,
or to be loose. In loose mode, you can create unbalanced transactions,
that is, transactions that don't pair up with a matching entry,
and thus don't balance to zero. To clean up these unbalanced transactions,
you can <b>scrub</b> the account clean by choosing "Scrub" from the
window menu. This will examine each transaction; if the transaction doesn't
balance, a split entry will be created and placed into an account
named "Unbalanced". You can then review these splits and move them
to thier proper accounts.
<p> GnuCash can be configured to be strict about double entry,
or you may configure it to be "loose."</p>
<p> In "loose" mode, you can create <em>unbalanced
transactions,</em> that is, transactions where the "splits"
don't balance to zero. That discards the validation that comes
from using the more strict double entry scheme, which is
probably not a really wise move. In effect:</p>
<ul>
<li>If you aren't sure of what you're doing, you likely do
not want to discard the validation of <b> double entry,</b>
as this helps you keep your accounts balanced even when
you're not perfectly clear on this.</li>
<li>If you are an <em> accounting whiz,</em> you'll know that
it's <em> really important</em> to keep things in balance,
and again will prefer <b> double entry.</b></li>
</ul>
<p> But if you decide to "outsmart the system," and have a
number of unbalanced transactions, you'll probably want to
clean this up at some point. To clean up these unbalanced
transactions, you <b> Scrub</b> the account clean by choosing
<tt>Scrub</tt> from the window menu. This will examine each
transaction; if the transaction doesn't balance, a split entry
will be created and placed into an account named <b>
Unbalanced.</b> You may then review these splits and move them
to their proper accounts.</p>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,68 +1,182 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Using Expense/Income Accounts</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h2>What is an Income/Expense Account?</h2>
The words "Income" and "Expense" are beguilingly simple; everyone
knows what they mean. Don't let this fool you: they have a
special meaning when applied to double-entry accounting.
<p>
To properly record income and expenses in a double entry system,
two special accounts must be created: one of type "Income" and one
of type "Expense". Income such salary, wages, bank interest and
stock dividends are then recorded as transfers from an income
account to a bank (or generally, asset) account. Similarly,
expenses are recorded as transfers from a credit card account
(or generally, a liability account).
<p>
Why are accounts of type "Income" and "Expense" considered
special? The answer lies in the nature of double-entry.
With a double-entry transaction, one account is always credited,
and another account is always debited. When salary is deposited
in a bank account, the bank account is credited, and the
income account is debited. In order to make income appear
positive (as it is), and expenses look negative, the meaning
of "debit" and "credit" is reversed for income and expense
accounts. This makes them special.
<p>
Another way in which income and expense accounts are special
is that their account totals do not appear on a balance
sheet. A balance sheet shows "Net Worth": the sum of all
assets minus all liabilities. Since income and expenses are
neither assets nor liabilities, they do not appear on the
balance sheet. There is a different kind of report, a
"Profit and Loss" (P&amp;L) report, that shows income and
expenses. The total profit (or loss) is the total income
minus total expenses. Since assets and liabilities are neither
income or expenses, they do not appear on a P&amp;L statement.
<p>
Even though the accounts may be "special", you do not need
to do anything "special"
to use income and expense accounts. Everything is handled
automatically, as long as you remember to transfer income
and expenses between income/expense accounts and ordinary
bank/asset/credit-card/liability accounts.
The words "Income" and "Expense" are beguilingly simple;
everyone thinks they know what they mean. Don't let this fool
you: they have a somewhat special meaning when applied to
double-entry accounting.
<p> If you have used other personal finance software, be aware
that <a href="xacc-quicken.html#QUICKENCATS"> Quicken calls
them "Categories."</a></p>
<p> In a double entry system, two kinds of accounts must be
created: some of type "Income" and some of type "Expense".
Income such as salary, wages, bank interest and stock dividends
are then recorded as transfers from an income account to a bank
(or generally, asset) account. Similarly, expenses are recorded
as transfers from a credit card account (or generally, a
liability account).</p>
<p> Another way of describing the requirement for "double
entry" is that when you receive an income, two things
happen:</p>
<ul>
<li>You receive a sum of <em> money,</em> and must record
that effect on your bank account.</li>
<li>You have received an income, and must record that effect
on an income account.</li>
</ul>
<p> Why are accounts of type "Income" and "Expense" considered
special? The answer lies in the nature of double-entry. With a
simple double-entry transaction, one account is always
credited, and another account is always debited. When salary is
deposited in a bank account, the bank account is credited, and
the income account is debited, thus:</p>
<table>
<tr>
<th>Account</th>
<th>Debit</th>
<th>Credit</th>
</tr>
<tr>
<td>Chequing Account</td>
<td>1,600.00</td>
<td>
</td>
</tr>
<tr>
<td>Salary</td>
<td>
</td>
<td>1,600.00</td>
</tr>
</table>
<p>This can be readily extended to a greater number of "split"
items thus:</p>
<table>
<tr>
<th>Account</th>
<th>Debit</th>
<th>Credit</th>
</tr>
<tr>
<td>Chequing Account</td>
<td>1,300.00</td>
<td>
</td>
</tr>
<tr>
<td>Income Taxes</td>
<td>200.00</td>
<td>
</td>
</tr>
<tr>
<td>Health Plan</td>
<td>100.00</td>
<td>
</td>
</tr>
<tr>
<td>Salary</td>
<td>
</td>
<td>1,600.00</td>
</tr>
</table>
<p> There may be a whole lot more than two entries in the
transaction, but the total sum of the Debits, $1,600.00, still
equals the total sum of the credits, $1,600.00.</p>
<p> If, as with <b>X-Accountant,</b> everything is forced onto
one column, so that <b> debits</b> are represented by positive
values, and <b> credits</b> are represented by negative values,
the income/expense accounts do a slightly non-intuitive thing
and you see incomes as <em> negative</em> values. That <em>
appear</em> contrary to intuition, but is necessary in order
for balances to balance.</p>
<p> Another way in which income and expense accounts are
special is that their account totals do not appear on a balance
sheet. A balance sheet shows "Net Worth": the sum of all assets
minus all liabilities. Since income and expenses are neither
assets nor liabilities, they do not appear on the balance
sheet; only their effects on <b> equity.</b> There is a
different kind of report, a "Profit and Loss" (P&amp;L) report,
that shows income and expenses. The total profit (or loss) is
the total income minus total expenses. Since assets and
liabilities are neither income or expenses, they do not appear
on a P&amp;L statement.</p>
<p> Even though the accounts may be somewhat "special", you do
not need to do anything particularly special to use income and
expense accounts. X-Accountant handles the values
automatically, so that if you record properly the effects of
the transactions on your bank account or credit card, the
income/expense side of the transaction should also be handled
correctly. The time when things get "peculiar," and when you
need to more deeply understand this, is when amounts are
transferred between income/expense accounts. (The <i>
causes</i> for such transfers tend to be somewhat peculiar, so
it's pretty fair for this to be a pretty odd situation.)</p>
<h2>Using Income/Expense Accounts</h2>
To use an income/expense account, simply create one from the
"New Account" dialogue window, and then be sure to transfer
income/expenses to it as you record paychecks, interest, etc.
<p>
If you have a complex account arrangement, you may want to
create multiple income/expense accounts. One can be used to
record salary, and only salary, another to record only bank
interest, and a third only to record stock dividends. This
partitioning is particularly useful when tax-time rolls around.
<h2>Notes</h2>
Users of Quicken (TM) products should realize that what Quicken
calls "Categories" are really just Income/Expense accounts.
Thus, if you are used to specifying a category in Quicken,
just create an income/expense account of the same name in
X-Accountant, and use that.
<p> To use an income/expense account, simply create one from
the "New Account" dialogue window, and then be sure to transfer
income/expenses to it as you record paychecks, interest,
etc.</p>
<p> You will doubtless wish to create quite a number of income
and expense accounts; it may be worth looking at the <a href=
"xacc-groups.html#SAMPLECHART"> Sample Chart of Accounts</a>
for ideas.</p>
<p> This partitioning of incomes and expenses proves
particularly useful for North Americans when <i>income tax
time</i> rolls around.</p>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,334 +1,341 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>GNU General Public License</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h1>GNU General Public License</h1>
<h2>Version 2, June 1991</h2>
<CENTER>
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA<P>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.<P>
</CENTER>
<P>
<center>
Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675
Mass Ave, Cambridge, MA 02139, USA
<p> Everyone is permitted to copy and distribute verbatim
copies of this license document, but changing it is not
allowed.</p>
</center>
<h3><a name="SEC001">Preamble</a></h3>
<P>
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
<p> The licenses for most software are designed to take away
your freedom to share and change it. By contrast, the GNU
General Public License is intended to guarantee your freedom to
share and change free software--to make sure the software is
free for all its users. This General Public License applies to
most of the Free Software Foundation's software and to any
other program whose authors commit to using it. (Some other
Free Software Foundation software is covered by the GNU Library
General Public License instead.) You can apply it to your
programs, too.</p>
<P>
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
<p> When we speak of free software, we are referring to
freedom, not price. Our General Public Licenses are designed to
make sure that you have the freedom to distribute copies of
free software (and charge for this service if you wish), that
you receive source code or can get it if you want it, that you
can change the software or use pieces of it in new free
programs; and that you know you can do these things.</p>
<P>
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
<p> To protect your rights, we need to make restrictions that
forbid anyone to deny you these rights or to ask you to
surrender the rights. These restrictions translate to certain
responsibilities for you if you distribute copies of the
software, or if you modify it.</p>
<P>
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
<p> For example, if you distribute copies of such a program,
whether gratis or for a fee, you must give the recipients all
the rights that you have. You must make sure that they, too,
receive or can get the source code. And you must show them
these terms so they know their rights.</p>
<P>
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
<p> We protect your rights with two steps: (1) copyright the
software, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the software.</p>
<P>
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
<p> Also, for each author's protection and ours, we want to
make certain that everyone understands that there is no
warranty for this free software. If the software is modified by
someone else and passed on, we want its recipients to know that
what they have is not the original, so that any problems
introduced by others will not reflect on the original authors'
reputations.</p>
<P>
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
<p> Finally, any free program is threatened constantly by
software patents. We wish to avoid the danger that
redistributors of a free program will individually obtain
patent licenses, in effect making the program proprietary. To
prevent this, we have made it clear that any patent must be
licensed for everyone's free use or not licensed at all.</p>
<P>
The precise terms and conditions for copying, distribution and
modification follow.
</P>
<HR>
<h3><a name="SEC002">GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</a></h3>
<p> The precise terms and conditions for copying, distribution
and modification follow.</p>
<hr>
<P>
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
<h3><a name="SEC002">GNU GENERAL PUBLIC LICENSE TERMS AND
CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</a></h3>
<P>
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
<p> 0. This License applies to any program or other work which
contains a notice placed by the copyright holder saying it may
be distributed under the terms of this General Public License.
The "Program", below, refers to any such program or work, and a
"work based on the Program" means either the Program or any
derivative work under copyright law: that is to say, a work
containing the Program or a portion of it, either verbatim or
with modifications and/or translated into another language.
(Hereinafter, translation is included without limitation in the
term "modification".) Each licensee is addressed as "you".</p>
<P>
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
<p> Activities other than copying, distribution and
modification are not covered by this License; they are outside
its scope. The act of running the Program is not restricted,
and the output from the Program is covered only if its contents
constitute a work based on the Program (independent of having
been made by running the Program). Whether that is true depends
on what the Program does.</p>
<P>
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
<p> 1. You may copy and distribute verbatim copies of the
Program's source code as you receive it, in any medium,
provided that you conspicuously and appropriately publish on
each copy an appropriate copyright notice and disclaimer of
warranty; keep intact all the notices that refer to this
License and to the absence of any warranty; and give any other
recipients of the Program a copy of this License along with the
Program.</p>
<P>
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
<p> You may charge a fee for the physical act of transferring a
copy, and you may at your option offer warranty protection in
exchange for a fee.</p>
<p> 2. You may modify your copy or copies of the Program or any
portion of it, thus forming a work based on the Program, and
copy and distribute such modifications or work under the terms
of Section 1 above, provided that you also meet all of these
conditions:</p>
<blockquote>
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
<P>
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
<P>
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
</P>
</blockquote>
a) You must cause the modified files to carry prominent
notices stating that you changed the files and the date of
any change.
<HR>
<p> b) You must cause any work that you distribute or
publish, that in whole or in part contains or is derived from
the Program or any part thereof, to be licensed as a whole at
no charge to all third parties under the terms of this
License.</p>
<p> c) If the modified program normally reads commands
interactively when run, you must cause it, when started
running for such interactive use in the most ordinary way, to
print or display an announcement including an appropriate
copyright notice and a notice that there is no warranty (or
else, saying that you provide a warranty) and that users may
redistribute the program under these conditions, and telling
the user how to view a copy of this License. (Exception: if
the Program itself is interactive but does not normally print
such an announcement, your work based on the Program is not
required to print an announcement.)</p>
</blockquote>
<hr>
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
identifiable sections of that work are not derived from the
Program, and can be reasonably considered independent and
separate works in themselves, then this License, and its terms,
do not apply to those sections when you distribute them as
separate works. But when you distribute the same sections as
part of a whole which is a work based on the Program, the
distribution of the whole must be on the terms of this License,
whose permissions for other licensees extend to the entire
whole, and thus to each and every part regardless of who wrote
it.
<P>
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
<p> Thus, it is not the intent of this section to claim rights
or contest your rights to work written entirely by you; rather,
the intent is to exercise the right to control the distribution
of derivative or collective works based on the Program.</p>
<P>
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
<p> In addition, mere aggregation of another work not based on
the Program with the Program (or with a work based on the
Program) on a volume of a storage or distribution medium does
not bring the other work under the scope of this License.</p>
<P>
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
<p> 3. You may copy and distribute the Program (or a work based
on it, under Section 2) in object code or executable form under
the terms of Sections 1 and 2 above provided that you also do
one of the following:</p>
<blockquote>
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
<P>
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
<P>
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
a) Accompany it with the complete corresponding
machine-readable source code, which must be distributed under
the terms of Sections 1 and 2 above on a medium customarily
used for software interchange; or,
<p> b) Accompany it with a written offer, valid for at least
three years, to give any third party, for a charge no more
than your cost of physically performing source distribution,
a complete machine-readable copy of the corresponding source
code, to be distributed under the terms of Sections 1 and 2
above on a medium customarily used for software interchange;
or,</p>
<p> c) Accompany it with the information you received as to
the offer to distribute corresponding source code. (This
alternative is allowed only for noncommercial distribution
and only if you received the program in object code or
executable form with such an offer, in accord with Subsection
b above.)</p>
</blockquote>
<P>
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
<p> The source code for a work means the preferred form of the
work for making modifications to it. For an executable work,
complete source code means all the source code for all modules
it contains, plus any associated interface definition files,
plus the scripts used to control compilation and installation
of the executable. However, as a special exception, the source
code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating
system on which the executable runs, unless that component
itself accompanies the executable.</p>
<P>
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
</P>
<p> If distribution of executable or object code is made by
offering access to copy from a designated place, then offering
equivalent access to copy the source code from the same place
counts as distribution of the source code, even though third
parties are not compelled to copy the source along with the
object code.</p>
<hr>
4. You may not copy, modify, sublicense, or distribute the
Program except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense or distribute the
Program is void, and will automatically terminate your rights
under this License. However, parties who have received copies,
or rights, from you under this License will not have their
licenses terminated so long as such parties remain in full
compliance.
<HR>
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
<p> 5. You are not required to accept this License, since you
have not signed it. However, nothing else grants you permission
to modify or distribute the Program or its derivative works.
These actions are prohibited by law if you do not accept this
License. Therefore, by modifying or distributing the Program
(or any work based on the Program), you indicate your
acceptance of this License to do so, and all its terms and
conditions for copying, distributing or modifying the Program
or works based on it.</p>
<P>
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
<p> 6. Each time you redistribute the Program (or any work
based on the Program), the recipient automatically receives a
license from the original licensor to copy, distribute or
modify the Program subject to these terms and conditions. You
may not impose any further restrictions on the recipients'
exercise of the rights granted herein. You are not responsible
for enforcing compliance by third parties to this License.</p>
<P>
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
<p> 7. If, as a consequence of a court judgment or allegation
of patent infringement or for any other reason (not limited to
patent issues), conditions are imposed on you (whether by court
order, agreement or otherwise) that contradict the conditions
of this License, they do not excuse you from the conditions of
this License. If you cannot distribute so as to satisfy
simultaneously your obligations under this License and any
other pertinent obligations, then as a consequence you may not
distribute the Program at all. For example, if a patent license
would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through
you, then the only way you could satisfy both it and this
License would be to refrain entirely from distribution of the
Program.</p>
<P>
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
<p> If any portion of this section is held invalid or
unenforceable under any particular circumstance, the balance of
the section is intended to apply and the section as a whole is
intended to apply in other circumstances.</p>
<P>
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
<p> It is not the purpose of this section to induce you to
infringe any patents or other property right claims or to
contest validity of any such claims; this section has the sole
purpose of protecting the integrity of the free software
distribution system, which is implemented by public license
practices. Many people have made generous contributions to the
wide range of software distributed through that system in
reliance on consistent application of that system; it is up to
the author/donor to decide if he or she is willing to
distribute software through any other system and a licensee
cannot impose that choice.</p>
<hr>
This section is intended to make thoroughly clear what is
believed to be a consequence of the rest of this License.
<P>
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
</P>
<p> 8. If the distribution and/or use of the Program is
restricted in certain countries either by patents or by
copyrighted interfaces, the original copyright holder who
places the Program under this License may add an explicit
geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries
not thus excluded. In such case, this License incorporates the
limitation as if written in the body of this License.</p>
<HR>
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
<p> 9. The Free Software Foundation may publish revised and/or
new versions of the General Public License from time to time.
Such new versions will be similar in spirit to the present
version, but may differ in detail to address new problems or
concerns.</p>
<P>
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
<p> Each version is given a distinguishing version number. If
the Program specifies a version number of this License which
applies to it and "any later version", you have the option of
following the terms and conditions either of that version or of
any later version published by the Free Software Foundation. If
the Program does not specify a version number of this License,
you may choose any version ever published by the Free Software
Foundation.</p>
<P>
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
<P>
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
<P>
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
<P>
<p> 10. If you wish to incorporate parts of the Program into
other free programs whose distribution conditions are
different, write to the author to ask for permission. For
software which is copyrighted by the Free Software Foundation,
write to the Free Software Foundation; we sometimes make
exceptions for this. Our decision will be guided by the two
goals of preserving the free status of all derivatives of our
free software and of promoting the sharing and reuse of
software generally.</p>
<h3><a name="SEC003">NO WARRANTY</a></h3>
<P>
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
<p> 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE
IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS
IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS
WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE
COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.</p>
<P>
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
<p> 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO
MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE,
BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS
OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED
BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY
HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p>
<h3>END OF TERMS AND CONDITIONS</h3>
<hr>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,19 +1,24 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Chart of Accounts and Account Numbering</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h1>Chart of Accounts</h1>
Typically accounts are arranged as a tree, in heirarchical form.
The main trunks represent entire categories or groups, while the
leaves of the tree denote individual bank accounts or expense
categories. When a summary report is requested, typically only
the main branches are shown in the report, not each of the
individual accounts. For example, a chart of accounts might look
like the following:
Typically accounts are arranged as a tree, in hierarchical
form. The main trunks represent entire categories or groups,
while the leaves of the tree denote individual bank accounts or
expense categories.
<p> When a summary report is requested, typically only the main
branches are shown in the report, not each of the individual
accounts. For example, a chart of accounts might look like the
following:</p>
<pre>
300 Expenses
|
@ -30,168 +35,336 @@
: :
</pre>
Note that accounts are coded: when a report is generated,
the sort order is determined entirely by the numbering.
By tradition and common practice, accounts that are not
leaf accounts have a number that ends in zero; each higher
level has one more zero in the account code.
<p> Note that accounts not only have names; they have <b>
codes;</b> when a report is generated, the <a name="SORTORDER">
sort order is determined entirely by the numbering. A sensible
hierarchy generally will have the "leaf" accounts end in
non-zero digits, whilst parent nodes have increasing numbers of
zeros. For instance, "cash" accounts might logically be
arranged thus:</a></p>
<p>
When you <a href="xacc-accwin.html">create a new account</a>,
GnuCash offers a guess
at what it thinks the account code might be; you are free
to change this. GnuCash does not prevent duplicate numbering,
although you are encouraged to avoid this. Account codes
are treated as numbers, base-36: thus, if you run out of numbers,
you can use the letters, a through z.
<ul>
<li>
Overall Assets: <tt> 100</tt>
<ul>
<li>
Overall Cash: <tt> 110</tt>
<h2>A Bigger Example</h2>
A "typical" chart of accounts is shown below. Each account
is of a given <a href="xacc-acctypes.html">account type</a>.
This example is a combination of some typical business
and personal accounts.
<p>
<ul>
<li>Assets
<ul>
<li>Cash On Hand
<ul>
<li>Checking account
<li>Money Market Account
<li>Certificate of Deposit
<li>Cash in Wallet: <tt> 111</tt></li>
<li>Cash in Sock in Closet: <tt> 112</tt></li>
<li>Cash in Mattress: <tt> 113</tt></li>
<li>Petty Cash Box: <tt> 114</tt></li>
</ul>
<li>Fixed Assets
</li>
<li>
Overall Banking Assets <tt> 120</tt>
<ul>
<li>Furniture
<li>Computers
<li>Jewelry, Collectibles
<li>Tools, Machinery
<li>Checking Account <tt> 121</tt></li>
<li>Savings Account <tt> 122</tt></li>
</ul>
<li>Investments
<ul>
<li>Stocks
<li>Bonds
<li>Mutual Funds
<li>Real Estate
</ul>
</ul>
<li>Liabilities
<ul>
<li>Taxes
<ul>
<li>Federal Income Tax
<li>Social Security
<li>Medicare
<li>FUTA
<li>State Income Tax
</ul>
<li>Accounts Payable
<ul>
<li>MasterCard
<li>Visa
<li>American Express
<li>Diner's Club
</ul>
<li>Loans
<ul>
<li>Debentures
<li>School Loan
<li>Uncle Harry's Tide-me-over
</ul>
</ul>
<li>Equity
<ul>
<li>Retained Earnings
<li>Current Year Earnings
<li>Historical Adjustments
</li>
</ul>
<li>Income
<p> When you <a href="xacc-accwin.html">create a new
account</a>, GnuCash offers a guess at what it thinks the
account code might be; you are free to change this. GnuCash
does not prevent duplicate numbering, although we would
encourage you to avoid this. Account codes are treated as
numbers in base-36, thus, if you run out of numbers, you
can use the letters, <tt>a</tt> through <tt>z</tt>.</p>
<h2>A Sample Chart of Accounts</h2>
<a name="SAMPLECHART"></a>
<p> A "typical" chart of accounts is shown below. Each
account is of a given <a href="xacc-acctypes.html">account
type</a>. This example is a combination of some typical
business and personal accounts.</p>
<ul>
<li>Interest Income
<li>
Assets
<ul>
<li>Bank Account Interest
<li>Certificate of Deposit
<li>Bond Interest
<li>
Cash On Hand
<ul>
<li>Checking account</li>
<li>Money Market Account</li>
<li>Certificate of Deposit</li>
</ul>
<li>Dividends
</li>
<li>
Fixed Assets
<ul>
<li>Stock
<li>Mutual Fund
<li>Furniture</li>
<li>Computers</li>
<li>Jewelry, Collectibles</li>
<li>Tools, Machinery</li>
</ul>
<li>Consulting
</li>
<li>
Investments
<ul>
<li>ABC Design
<li>PQR Infomatics
<li>Stocks</li>
<li>Bonds</li>
<li>Mutual Funds</li>
<li>Real Estate</li>
</ul>
<li>Salary
</li>
</ul>
</li>
<li>
Liabilities
<ul>
<li>My Day Job
</ul>
<li>Commissions
<li>
Taxes
<ul>
<li>Royalties
<li>Federal Income Tax</li>
<li>Social Security</li>
<li>Medicare</li>
<li>FUTA</li>
<li>State Income Tax</li>
</ul>
</li>
<li>
Accounts Payable
<ul>
<li>MasterCard</li>
<li>Visa</li>
<li>American Express</li>
<li>Diner's Club</li>
</ul>
</li>
<li>
Loans
<ul>
<li>Debentures</li>
<li>School Loan</li>
<li>Uncle Harry's Tide-me-over</li>
</ul>
</li>
</ul>
</li>
<li>
Equity
<ul>
<li>Retained Earnings</li>
<li>Current Year Earnings</li>
<li>Historical Adjustments</li>
</ul>
</li>
<li>
Income
<ul>
<li>
Interest Income
<ul>
<li>Bank Account Interest</li>
<li>Certificate of Deposit</li>
<li>Bond Interest</li>
</ul>
</li>
<li>
Dividends
<ul>
<li>Stock</li>
<li>Mutual Fund</li>
</ul>
</li>
<li>
Consulting
<ul>
<li>ABC Design</li>
<li>PQR Infomatics</li>
</ul>
</li>
<li>
Salary
<ul>
<li>My Day Job</li>
</ul>
</li>
<li>
Commissions
<ul>
<li>Royalties</li>
</ul>
</li>
</ul>
</li>
<li>
Expenses
<ul>
<li>
Rent and Utilities
<ul>
<li>Rent</li>
<li>Rent Late Fees</li>
<li>Electricity</li>
<li>Gas</li>
<li>Phone</li>
<li>Internet</li>
<li>Cable TV</li>
</ul>
</li>
<li>
Office Expenses
<ul>
<li>Accounting</li>
<li>Legal</li>
<li>Software</li>
<li>Postage</li>
<li>Bank Charges</li>
<li>Credit Card Charges</li>
<li>Toner, Paper, Paper Clips</li>
</ul>
</li>
<li>
Auto Expenses
<ul>
<li>Gas</li>
<li>Insurance</li>
<li>Repair</li>
<li>Rental</li>
</ul>
</li>
<li>
Taxes
<ul>
<li>Social Security</li>
<li>Unemployment</li>
<li>IRS penalties</li>
</ul>
</li>
<li>
Wages &amp; Salaries
<ul>
<li>Consulting</li>
<li>Wages</li>
<li>Health Insurance</li>
</ul>
</li>
<li>
Travel
<ul>
<li>Air</li>
<li>Hotel</li>
<li>Meals</li>
<li>Auto</li>
</ul>
</li>
<li>
Marketing
<ul>
<li>Advertising</li>
<li>Trade Shows</li>
<li>Give Aways</li>
</ul>
</li>
</ul>
</li>
</ul>
<li>Expenses
<ul>
<li>Rent and Utilities
<ul>
<li>Rent
<li>Rent Late Fees
<li>Electricity
<li>Gas
<li>Phone
<li>Internet
<li>Cable TV
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</li>
</ul>
<li>Office Expenses
<ul>
<li>Accounting
<li>Legal
<li>Software
<li>Postage
<li>Bank Charges
<li>Credit Card Charges
<li>Toner, Paper, Paper Clips
</ul>
<li>Auto Expenses
<ul>
<li>Gas
<li>Insurance
<li>Repair
<li>Rental
</ul>
<li>Taxes
<ul>
<li>Social Security
<li>Unemployment
<li>IRS penalties
</ul>
<li>Wages &amp; Salaries
<ul>
<li>Consulting
<li>Wages
<li>Health Insurance
</ul>
<li>Travel
<ul>
<li>Air
<li>Hotel
<li>Meals
<li>Auto
</ul>
<li>Marketing
<ul>
<li>Advertising
<li>Trade Shows
<li>Give Aways
</ul>
</ul>
</ul>
</body>
</html>

View File

@ -1,37 +1,70 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>GnuCash Help Main Window</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h1> Main Help Overview</h1>
For help on a specific topic:
<ul>
<li><a href="xacc-about.html">About GnuCash</a>
<li><a href="xacc-acctypes.html">Account Types</a>
<li><a href="xacc-apar.html">Accounts Payable &amp; Accounts Receivable</a>
<li><a href="xacc-quicken.html">Categories</a>
<li><a href="xacc-groups.html">Chart of Accounts</a>
<li><a href="xacc-accwin.html">Creating a new account</a>
<li><a href="xacc-date.html">Date Input</a>
<li><a href="xacc-currency.html">Foreign Currencies</a>
<li><a href="bofa-mym.html">Importing MYM (Managing Your Money) Files</a>
<li><a href="xacc-quicken.html">Quicken(TM) User's Guide</a>
<li><a href="xacc-gpl.html">License</a>
<li><a href="xacc-about.html">About GnuCash</a></li>
<li><a href="xacc-acctypes.html">Account Types</a></li>
<li><a href="xacc-apar.html">Accounts Payable &amp; Accounts
Receivable</a></li>
<li><a href="xacc-groups.html">Chart of Accounts</a></li>
<li><a href="xacc-accwin.html">Creating a new
account</a></li>
<li><a href="xacc-date.html">Date Input</a></li>
<li><a href="xacc-currency.html">Foreign Currency
Accounts</a></li>
<li><a href="bofa-mym.html">Importing MYM (Managing Your
Money) Files</a></li>
<li><a href="xacc-quicken.html">A Guide for Former Users of
Quicken(TM)</a></li>
<li>
<a href="xacc-gpl.html">License - GPL</a>
<ul>
<li><a href="xacc-gpl.html#SEC003">Warranty</a>
<li><a href="xacc-gpl.html#SEC003">Warranty</a></li>
</ul>
<li><a href="xacc-mainwin.html">The Main Window</a>
<li><a href="xacc-print.html">Printing and Web Serving</a>
<li><a href="xacc-recnwin.html">The Reconcile Window</a>
<li><a href="xacc-regwin.html">The Register Window</a>
<li><a href="xacc-reports.html">Reports</a>
<li><a href="xacc-ticker.html">Stock Ticker</a>
<li><a href="xacc-double.html">Using Double Entry</a>
<li><a href="xacc-expense.html">Using Income/Expense Accounts</a>
<li><a href="xacc-y2k.html">Y2K Considerations</a>
</li>
<li><a href="xacc-mainwin.html">The Main Window</a></li>
<li><a href="xacc-print.html">Printing and Web
Serving</a></li>
<li><a href="xacc-recnwin.html">The Reconcile Window</a></li>
<li><a href="xacc-regwin.html">The Register Window</a></li>
<li><a href="xacc-reports.html">Reports</a></li>
<li><a href="xacc-ticker.html">Stock Ticker</a></li>
<li><a href="xacc-double.html">Using Double Entry</a></li>
<li><a href="xacc-expense.html">Using Income/Expense
Accounts</a></li>
<li><a href="xacc-y2k.html">Y2K Considerations</a></li>
</ul>
<hr>
</body>
</html>

View File

@ -1,41 +1,49 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Main Window</title>
</head>
<body bgcolor=#eeeeee>
<h1>THIS FILE IS ALMOST EMPTY</h1>
This is the main account window. Control accounts
from here. Add more documentation.
<p>
Below is a picture of the main window, with only the
main accounts shown. Note how "ABC Bank" has been selected
by highlighting. To show the detail accounts,
click on the arrows on the left.
<p>
<img src="contract.gif">
<br>
<p>
Here is the main window, with the detail accounts showing.
<p>
<br>
<img src="expand.gif">
<br>
<body>
<h1>Main GnuCash Window</h1>
<p> This is the main account window. You control your set of
accounts from here.</p>
<p> Below is a picture of the main window, with only the main
accounts shown. Note how <b>ABC Bank</b> has been selected by
highlighting. To show the detail accounts, click on the arrows
on the left.</p>
<p> <img src="contract.gif"><br>
</p>
<hr>
The "Open Subaccounts" menu item is interesting only if
you choose an account with sub-accounts (detail accounts).
Accounts with sub-accounts will always have arrows on thier
left. By choosing the "Open Subaccounts" menu item, a
general ledger window is opened, which displays all transactions
for the lead and the detail accounts. Note that the
general ledger window is more complicated and harder to use than
the individual account registers. The general ledger window
allows a more comprehensive overview of accounts in a smaller
space. Because of its increased complixity, it use is recommended
only for accounting experts.
<p> Here is the main window, with the detail accounts
showing.</p>
<p><br>
<img src="expand.gif"><br>
</p>
<hr>
<p> The <tt>Open Subaccounts</tt> menu item is interesting only
if you choose an account with sub-accounts (detail accounts).
Accounts with sub-accounts will always have arrows on thier
left. By choosing the <tt>Open Subaccounts</tt> menu item, a
general ledger window is opened, which displays all
transactions for the lead and the detail accounts. Note that
the general ledger window is more complicated and harder to use
than the individual account registers. The general ledger
window allows a more comprehensive overview of accounts in a
smaller space. Because of its increased complexity, it use is
recommended only for accounting experts.</p>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,25 +1,39 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Printing and Web Serving</title>
</head>
<body bgcolor=#eeeeee>
<h1>Printing and Web Serving</h1>
Currently, GnuCash does not support any sort of printer output.
However, you can create a printout of the register window contents
by opening a register window and selecting "Print... To File" from the
"File" menu.
<p>
Following the latest fashionable trends, GnuCash can also act as a
cheesy web server! From an open register window, select
"Print ... To WWW" from the "File" menu. Then aim your browser at
<tt>http://localhost:1080/</tt> or
<tt>http://yourmachinename.com:1080/</tt>and you will see your
register window. But, remember, we said "cheesy": to view the
page again, you have to pick the menu item again.
</p>
<body>
<h1>Printing</h1>
<p> At present, GnuCash does not support any sort of printer
output. However, you may create a printout of the register
window contents by opening a register window and selecting <tt>
Print... To File</tt> from the "File" menu.</p>
<h1> GnuCash as Web Server</h1>
<p> Following the latest fashionable trends of <b> Internet
Hype,</b> GnuCash can also act as a cheesy web server! From an
open register window, select <tt>Print ... To WWW</tt> from the
<tt> File</tt> menu. Then aim your browser at <tt>
http://localhost:1080/</tt> or <tt>
http://yourmachinename.com:1080/</tt>and you will see your
register window.</p>
<p>But, remember, we said <em> cheesy;</em> in order to view
the page a second time, you have to <em> pick the menu item
again</em> to "reload" things and allow prepare it for another
data dump.</p>
<hr>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,64 +1,99 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Quicken User's Guide</title>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>A Guide for Former Users of Quicken</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h1>Quicken (TM) User's Guide</h1>
Not all accounting systems use the same words for the same
concepts. Below follows some notes for
users accustomed to Quicken products.
<p> Not all accounting systems use the same words for the same
concepts. The following are some notes that may be helpful to
users accustomed to Intuit's products.</p>
<h2>Categories</h2>
What Quicken calls "Categories" are really just
<a href="xacc-expense.html">Income/Expense accounts</a>.
Thus, if you are used to specifying a category in Quicken,
just create an income/expense account of the same name in
X-Accountant, and use that as a category.
<a name="QUICKENCATS"></a>
<p> What Quicken calls <tt>Categories</tt> are really just <a
href="xacc-expense.html">Income/Expense accounts</a>. Thus, if
you are used to specifying a category in Quicken, just create
an income/expense account of the same name in X-Accountant, and
use that as the name of the account.</p>
<h2>Quicken QIF File Import</h2>
X-Accountant supports the import of Quicken Import Files
(QIF). (Note: Only Quicken Version 3.0 QIF has been tested).
Due to the curious nature of QIF, please read this section
carefully, or you may be disappointed with the results.
<p>
To create a Quicken QIF file, start Quicken (yes, it works
Wine, and probably Wabi too), choose the menu "File"
and select the "Export..." menu entry. Under Quicken
Version 3.0, you can only export one account at a time,
and thus, exporting will prove to be a tedious process.
When exporting, note that there are a series of check
boxes marked "Transactions", "Accounts List", "Category
List", etc. For best results, you will want to make
sure that these three are checked. If you do *not*
check the "Accounts List" box, then the name of your
account will be lost.
<p>
To import a Quicken QIF file, choose the menu "File"
and select the entry "Import QIF". The imported wile
will be merged with whatever other data you currently
have in X-Accountant. This merge allows multiple
Quicken accounts to be imported and merged into
one account group. Note that during merge, a scan
is made for duplicate transactions, and duplicates
are removed. A duplicate transaction is one
where the date, description (payee), memo, quantity, share price,
and debited/credited accounts or categories match exactly.
Thus, the merge should be safe unless you have multiple
transactions on the same date, to the same account,
for the same amount, with the same description and memo.
<p>
Note that when the "Accounts List" and "Category List"
is exported from Quicken, <b>all</b> accounts and
categories will be exported, even if they are empty,
and contain no transactions. When these are imported,
they will appear as accounts with a balance of zero.
If you do not need or use these accounts, delete them.
A future X-Accountant enhancement will allow you to
delete them en-masse, or to make them invisible without
deleting them.
<a name="QIF"> X-Accountant supports the import of Quicken
Import Files (QIF). (Note: Only Quicken Version 3.0 QIF has
been tested).</a>
<p> Note that the QIF format is representive of a somewhat
peculiar data model that is not as expressive as one might
wish; please read this section carefully, or you may be
disappointed with the results.</p>
<p> To create a Quicken QIF file, start Quicken (yes, it works
on <a href="http://www.winehq.com/"> Wine,</a> and probably
Wabi too), choose the menu "File" and select the <tt>
Export...</tt> menu entry. Quicken can only export one account
to each data file, which means that if you have many accounts,
this may prove to be a somewhat tedious process. (On the other
hand, it should be a whole lot <em> less</em> tedious than
entering the data from scratch.)</p>
<p>When exporting, note that there are a series of check boxes
marked <tt>Transactions</tt>, <tt>Accounts List</tt>, <tt>
Category List</tt>, and so forth. For best results, you will
want to make sure that these three are checked. If you do <em>
not</em> check the <tt>Accounts List</tt> box, then the name of
your account will be lost.</p>
<p> To import a Quicken QIF file, choose the menu <tt>File</tt>
and select the entry <tt>Import QIF</tt>. The imported file
will be merged with whatever other data you currently have in
X-Accountant. This merge allows multiple Quicken accounts to be
imported and merged into one account group.</p>
<p> Note that during merge, a scan is made for duplicate
transactions, and duplicates are removed. A duplicate
transaction is one where the date, description (payee), memo,
quantity, share price, and debited/credited accounts or
categories match <em> exactly.</em> Thus, the merge should be
safe unless you have multiple transactions on the same date, to
the same account, for the same amount, with the same
description and memo.</p>
<p> This unfortunately <em> can</em> occur, the typical
scenario involving where you make multiple withdrawals of
identical amounts from ATM machines on the same day.</p>
<p> An ongoing project is to build an alternative import
utility in Guile that will be more flexible, and which, by
virtue of being stored as a set of Scheme scripts, may be
modified without needing to recompile the whole application. At
this point, it successfully parses QIF files; what remains is
the (rather complex task) of determine appropriate
correspondences between the Quicken <b> Categories</b> and the
GnuCash equivalents.</p>
<p> Further details about the QIF Interchange Format may be
found <a href=
"http://www.ntlug.org/~cbbrowne/financeformats.html#QIF">
here.</a></p>
<p> Note that when the <tt>Accounts List</tt> and <tt>Category
List</tt> is exported from Quicken, <b>all</b> accounts and
categories will be exported, even if they are empty and contain
no transactions. When these are imported, they will appear as
accounts with a balance of zero. If you do not plan to use such
accounts, feel free to delete them. Future enhancements may
allow you to delete them "en masse," or to make them invisible
without deleting them.</p>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,46 +1,93 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Reconcile Window</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h1>Reconcile Window</h1>
The Reconcile window is used to reconcile cleared transactins from
a bank statement. Enter a dollar amount from your last bank
statement in the box, and then click OK. The window will then show
all unreconciled transactions since your last bank statment.
The reconcile window looks like this:
<p>
<br>
<img src="recnwin.gif">
<br>
<p>
The Reconcile window is for reconciling the user's records at the
end of the month when the bank statement comes. For example, if
you write a check for something, enter the transaction. When you
know that the check has cleared, you can click the field between
the description and payment fields, and it changes from a 'n' to
a 'c', indicating the transaction has cleared. At the end of the
month, open the reconcile window, and xacc will prompt you to enter
the ending balance from the bank statement. Then the reconcile
window will pop-up, and you will see a credit and a debit column
that lists all the non-reconciled transactions. You can then
check off the transactions that appear in the bank statement, and
verify the amount fields are correct. At the bottom of the window
is a difference field, which should be $0.00 when you are done
reconciling. If it isn't then you missed a transaction, or one
of the amounts is wrong. When you press "Ok", then the 'n' or
'c' in the transactions that were checked off will change to a
'y'. When you change anything in a reconciled transaction, a
verify dialog box should pop-up, but this doesn't seem to be
happening anymore. Also, the "cleared" total at the bottom seems
to display $0.00 regardless of what transactions are cleared or
reconciled. The "cleared" total should display the total of only
the transactions that have been cleared ('c') or reconciled ('y').
The Reconcile window is used to reconcile cleared transactins
from a bank statement. Enter a dollar amount from your last
bank statement in the box, and then click <tt> OK</tt> . The
window will then show all unreconciled transactions since your
last bank statment. The reconcile window looks like this:
<p><br>
<img src="recnwin.gif"><br>
</p>
<p> The Reconcile window is used to reconcile the user's
records at the end of the month. This involves validating the
transactions in GnuCash against the transactions indicated on
your bank statement.</p>
<p> For example, when you write a check for something, you
enter the transaction into GnuCash.</p>
<p> At the end of the month, you receive your bank statement,
perhaps including cancelled checks.</p>
<ul>
<li>Open the reconcile window, and GnuCash will prompt you to
enter the ending balance from the bank statement.</li>
<li>Then the reconcile window will pop-up, and you will see a
credit and a debit column that lists all the non-reconciled
transactions.<br>
<br>
</li>
<li>
Now, examine each item on the bank statement.
<p> If a check has cleared, as indicated by your bank
statement, you should click the field between the
description and payment fields, and it will then change
from <tt>n</tt> to <tt>c</tt>, indicating the transaction
has <b>c</b>leared.</p>
<p> You then repeat this for each item that appears on the
bank statement, verifying that the amounts match with the
amounts in GnuCash, and marking off transactions in GnuCash
as they are reconciled.</p>
</li>
<li>At the bottom of the window is a difference field, which
should be $0.00 when you are done reconciling. If it isn't,
then you have either missed transactions, or some amounts may
be incorrect in GnuCash. (Or, probably rather less likely,
the bank may have made an error.)<br>
<br>
</li>
<li>When you have completed marking off all the items on the
bank statement, and when the difference heads to $0.00, you
should press <tt>Ok</tt>, then the <tt>n</tt> or <tt>c</tt>
in the transactions that were checked off will change to a
<tt>y</tt>, indicating that they have been validated to agree
between the bank statement and your own records.<br>
<br>
</li>
</ul>
<p>When you change anything in a transaction that has been
reconciled, a dialog box warning you that such changes are
unwise should pop up, but this doesn't seem to be happening
anymore. Also, the "cleared" total at the bottom seems to
display <tt> $0.00</tt> regardless of what transactions are
cleared or reconciled.</p>
<p> The "cleared" total should display the total of only the
transactions that have been cleared (<tt>c</tt>) or reconciled
(<tt>y</tt>).</p>
<hr>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,83 +1,104 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Register Window</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h1>Register Window</h1>
This is the "Register" or the "Ledger" window. Enter
transactions here. Add more documentation to this page.
<p>
This is what the register window should look like:</p>
<br>
<img src="regwin-single.gif">
<br>
<img src="regwin-double.gif">
<p> This is what the register window should look like:</p>
<br>
<img src="regwin-single.gif"> <br>
<img src="regwin-double.gif"> <br>
<img src="regwin-multi.gif">
<hr>
<h2>Reconciliation</h2>
At the bottom of the account window, there are two running balances,
the "cleared &amp; reconciled" balance, and the "total" balance... the
"cleared &amp; reconciled" balance should correspond to how much money
the bank thinks you have in your account, and the "total" balance
includes outstanding transactions.
<p>
Reconciliation can be done in the <a href="xacc-recnwin.html">
Reconcile Window</a>
<p>
(Not yet implemented) Transactions marked with 'y' can not be edited
without first changing this flag. This is because a reconciled
transaction should be thought of as something that has been checked
over, and therefore correct. Changing such a transaction should
be difficult: we want to avoid accidental errors.
<p>
(Not yet implemented) Transactions marked with 'f' are frozen,
can cannot be edited under any circumstances. These have been
frozen into an accounting period. That is, the books have been closed
for this period; they can no longer be reopened for editing.
<p>
<h2>Reconciliation</h2>
At the bottom of the account window, there are two running
balances, the "cleared &amp; reconciled" balance, and the
"total" balance... the "cleared &amp; reconciled" balance
should correspond to how much money the bank thinks you have in
your account, and the "total" balance includes outstanding
transactions.
<p> Reconciliation can be done in the <a href=
"xacc-recnwin.html">Reconcile Window</a></p>
<p>(Not yet implemented) Transactions marked with <tt> y</tt>
can not be edited without first changing this flag. This is
because a reconciled transaction should be thought of as
something that has been checked over, and therefore correct.
Changing such a transaction should be difficult: we want to
avoid accidental errors.</p>
<p>(Not yet implemented) Transactions marked with <tt>f</tt>
are frozen, and cannot be edited under any circumstances. These
have been "frozen" into an accounting period. That is, the
books have been closed for this period; they may no longer be
modified.</p>
<h2>Stock Portfolios</h2>
You can do stock transaction either from a single-stock window,
or from a portfolio-view window, shown below.
<p>
<img src="foliowin.gif">
<br>
<img src="foliowin-single.gif">
<br>
The portfolio ledger can be a bit daunting at first sight.
If you have trouble understanding it, then stick to creating
accounts which have a single stock in them.
Some important points to remember about the portfolio window:
<ul>
<li>Its shows all stocks for your portfolio, not just some.
The share amounts shown are for each particular stock.
<li>Notice that it uses a two line display. Debited
accounts and debited amounts are on the upper line, and
credited amounts and accounts are on the lower line.
<li>If you buy or sell a stock with money from the brokerage
account, the total balance will not change, since the
value of the stock equals the amount of money exchanged.
<li>If you buy or sell a stock with money from the brokerage
account, the value of the transaction will appear twice,
once in red, and once in black. If shares are purchased,
the amount of money debited from the brokerage account will be
in red, and the value of the shares in black. If shares are
sold, then value of the shares is in red, and the money
credited to the brokerage account in black.
<li>If you are having trouble indicating a share purchase/sale
in the portfolio ledger, then make sure that "Transfer From"
and "Transfer To" accounts are in the right order. A
transfer from a stock account is always interpreted
as a sale, even if you entered the data as a purchase.
The vice-versa is also true.
<li>Someday, X-Accountant will be enhanced with a simplified
portfolio ledger window.
<p> You can do stock transaction either from a single-stock
window, or from a portfolio-view window, shown below.</p>
<p> <img src="foliowin.gif"><br>
<img src="foliowin-single.gif"></p>
<p> The portfolio ledger can be a bit daunting at first sight.
If you have trouble understanding it, then stick to creating
accounts which track a single stock. Some important points to
remember about the portfolio window:</p>
<ul>
<li>
It shows <em>all</em> stocks in your portfolio, not just
one.
<p> The share amounts shown are for each particular
stock.</p>
</li>
<li>
Note that it uses a two line display.
<p> Debited accounts and debited amounts are on the upper
line, and credited amounts and accounts are on the lower
line.</p>
</li>
<li>If you buy or sell a stock with money from the brokerage
account, the total balance will not change, since the value
of the stock equals the amount of money exchanged.</li>
<li>If you buy or sell a stock with money from the brokerage
account, the value of the transaction will appear twice, once
in red, and once in black. If shares are purchased, the
amount of money debited from the brokerage account will be in
red, and the value of the shares in black. If shares are
sold, then value of the shares is in red, and the money
credited to the brokerage account in black.</li>
<li>If you are having trouble indicating a share
purchase/sale in the portfolio ledger, then make sure that
"Transfer From" and "Transfer To" accounts are in the right
order. A transfer from a stock account is always interpreted
as a sale, even if you entered the data as a purchase. The
vice-versa is also true.</li>
<li>Eventually, X-Accountant may be enhanced to provide a
simplified portfolio ledger window.</li>
</ul>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,28 +1,42 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Reports</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h1>Reports</h1>
There are two types of reports: a <b>Balance Sheet</b> and
a <b>Profit and Loss Statement</b>.
<p>
A Balance Sheet shows Assets, Liabilities and Equity. The sum of
all assets should equal the sum of Liabilities and Equity.
<p>
A Profit and Loss Statement shows Income and Expenses. The
sum of all Income minus all expenses is the Profit or Loss.
<p>
The change in Equity from day to day (year to year) should
equal that day's (year's) profit or loss.
<p>
There are two types of reports: a <b>Balance Sheet</b> and a
<b>Profit and Loss Statement</b>.
<p> A Balance Sheet shows Assets, Liabilities and Equity. The
sum of all Assets equals the sum of Liabilities and Equity.</p>
<p> A Profit and Loss Statement shows <a href=
"xacc-expense.html"> Income and Expenses.</a> The sum of all
Income minus all expenses is the Profit or Loss, which is the
change in equity for the period in question.</p>
<p> The change in Equity from day to day (year to year)
represents that day's (year's) profit or loss.</p>
<ul>
<li><a href="report-baln.phtml">Click Here to Show Balance Sheet</a>
<li><a href="report-pnl.phtml">Click Here to Show Profit &amp; Loss</a>
<li><a href="report-folio.phtml">Show Stock Portfolio Valuation</a>
<li><a href="report-baln.phtml">Click Here to Show Balance
Sheet</a></li>
<li><a href="report-pnl.phtml">Click Here to Show Profit
&amp; Loss</a></li>
<li><a href="report-folio.phtml">Show Stock Portfolio
Valuation</a></li>
</ul>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,68 +1,83 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>Stock Ticker</title>
</head>
<body bgcolor=#eeeeee>
<body>
<h1>Stock Pricing and Ticker Symbols</h1>
<p>
GnuCash currently provides rudimentary automated stcok quote
abilities. If an account is properly configured, and the
host computer is connected to the internet, the binary
<tt>gnc-prices</tt> can be used to load stock or mutual fund
price quotes from various web sites directly into GnuCash.
<p>
To make use of this feature, the following must be done:
<p> GnuCash currently provides a somewhat rudimentary automated
stock quote gathering abilities. If an account is properly
configured, and the host computer is connected to the internet,
the binary <tt> gnc-prices</tt> may be used to load stock and
mutual fund price quotes from various web sites directly into
GnuCash.</p>
<p> To make use of this facility, the following must be
done:</p>
<ul>
<li>Create an account and mark it as being of type "Mutual Fund"
or "Stock".
<li>Enter a valid ticker symbol in the box marked "Security:"
<li>Create an account and mark it as being of type "Mutual
Fund" or "Stock".</li>
<li>Enter a valid ticker symbol in the box marked
"Security:"</li>
<li>Select a quote source from the pull-down menu. Currently
supported quote sources include <b>Yahoo</b>, <b>Fidelity
Investments</b>, <b>T. Rowe Price</b> and the <b>Vanguard
Group</b>. Note that Yahoo will provide price quotes for
most mutual funds, including Fidelity, T.Rowe Price and
Vanguard, and that the quoted prices/NAV should be
identical to those on the source sites.
Group</b>. Note that Yahoo will provide price quotes for many
mutual funds <em>including</em> Fidelity, T.Rowe Price and
Vanguard, and that the quoted prices/NAV should be identical
to those on the source sites.</li>
</ul>
<p>
A sample image is shown below:
</p>
<p> A sample image is shown below:</p>
<br>
<img src="ticker-a.gif">
<p>
To update the prices stored in a gnucash account file, run the
command line command <tt>gnc-price</tt>, specifying the filename;
for example:
<p> To update the prices stored in a gnucash account file, run
the command line command <tt>gnc-price</tt>, specifying the
filename; for example:</p>
<pre>
gnc-prices myaccts.xac
</pre>
Running this command will print various diagnostic messages to
the screen while it loads data. It will work only when the host
computer is attached to the internet. It will work if the host
is behind a masq-style firewall. It does not currently work from
behind proxy or socks-style firewalls. The command can be run
many times in one day; however, it will update the accounts
at most once with the most recent trading days price data. Thus,
if gnc-prices is run on Friday, Saturday, Sunday and Monday,
only two price entires will be made: one containing Friday's
data, and one containing Monday's data, since the Saturday and
Sunday runs will only retreive the Friday closing price.
To keep gnc-prices from updating one account, while allowing it
to update another account, merely mark the data source for that
account as "(none)". You can do this from the "Edit Account"
window.
<p>
After running gnc-prices for a few days, your accounts will
begin to resemble the following:
</p>
<p> Running this command will print various diagnostic messages
to the screen while it loads data. It will work only when the
host computer is attached to the internet. It <em> can</em>
function in conjunction with masquerading-style firewalls, but
is not currently able to use proxy servers to get through proxy
or socks-style firewalls.</p>
<p> The command can be run many times in one day; however, it
will update the accounts at most once with the most recent
trading days price data.</p>
<p> Thus, if <tt>gnc-prices</tt> is run on Friday, Saturday,
Sunday and Monday, only two price entires will be made: one
containing Friday's data, and one containing Monday's data,
since the Saturday and Sunday runs will only retrieve the
Friday closing price.</p>
<p>To keep <tt>gnc-prices</tt> from updating one account, while
allowing it to update another account, merely mark the data
source for that account as <tt>(none)</tt>. You can do this
from the <tt>Edit Account</tt> window.</p>
<p> After running <tt>gnc-prices</tt> for a few days, your
accounts will begin to resemble the following:</p>
<br>
<img src="ticker-b.gif">
<hr>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

View File

@ -1,28 +1,47 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" title="normal" type="text/css" href=
"gnucash.css">
<title>GnuCash Y2K Readiness</title>
</head>
<body bgcolor="#eeeeee">
<body>
<h1>GnuCash Y2K Readiness</h1>
Gnucash version 1.1.25 and later store all dates as seconds and
nanoseconds, where the seconds are stored in a 64-bit signed integer.
This should suffice to store dates in the distant past as well as the
distant future, as long as they are less than several dozen times the
age of the universe.
<p>
The file format for version 1.1.25 and later stores dates in the
above-described fashion.
<p>
Some internal routines use the <tt>time_t</tt> type to express
seconds. Note that on most OS'es, this is a 32-bit quantity, and
is limited to the Unix era (Dec 1901 to Jan 2038).
<p>
Backup and log files are time-stamped using the standard Unix
<tt>ctime()</tt> routine, which takes a <tt>time_t</tt> argument.
Thus, the backup and log mechanism may experience trouble in 2038.
<p> Gnucash version 1.1.25 and later store all dates as seconds
and nanoseconds, where the seconds are stored in a 64-bit
signed integer. This should suffice to store dates in the
distant past as well as the distant future, as long as they are
less than several dozen times the present estimates of the age
of the universe.</p>
<p> The file format for version 1.1.25 and later stores dates
in the above-described fashion.</p>
<p> Some internal routines use the <tt>time_t</tt> type to
express seconds. Note that on most OSes, this is a 32-bit
quantity, and is limited to the Unix epoch, roughly December
1901 thru Jan 2038. It is reasonable to expect that this will
migrate to 64 bit values by that time.</p>
<p> Backup and log files are time-stamped using the standard
Unix <tt>ctime()</tt> routine, which takes a <tt>time_t</tt>
argument. Thus, the backup and log mechanism may experience
trouble in 2038, assuming your Unix continues to be in service
at that time without remediation.</p>
<p> Note that GnuCash also correctly recognizes February 29th,
2000 as a "leap day," another of the "critical Y2K dates."</p>
<p>Y2K issues are described in more detail at <a href=
"http://www.ntlug.org/~cbbrowne/linuxy2k.html"> Linux and Year
2000.</a></p>
<p> Return to <a href="xacc-main.html"> Main Documentation
Page.</a></p>
</body>
</html>

7
Docs/Fr/tidy-up Executable file
View File

@ -0,0 +1,7 @@
#!/bin/ksh
# $ID$
# If you have Dave Raggett's "tidy" utility, this will tidy up
# the HTML files here.
for i in *.html ; do
tidy -m -i $i
donediff -u 'pristine/gnucash/Docs/Fr/xacc-about-fr.html' 'working/gnucash/Docs/Fr/xacc-about-fr.html'

View File

@ -1,36 +1,41 @@
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
<title>Importing MYM Files</title>
</head>
<body>
<h1>Managing Your Money --> GNUCash</h1>
I have finally put the Perl script that allowed me to use GNUCash up on a web
site. I had a couple years of data in Managing Your Money 2.x that I didn't
want to reenter. The script will output a single QIF file with all
transactions, accounts, and categories. (Currently only non-investment
transactions are handled.) The QIF file can be imported to xacc-1.0.18 if a
small patch is applied to QIFIO.c. The site is
<h1>Managing Your Money --&gt; GNUCash</h1>
<p>
http://www-cad.eecs.berkeley.edu/~gooch/mymdump.html
I have finally put the Perl script that allowed me to use GNUCash
up on a web site. I had a couple years of data in Managing Your
Money 2.x that I didn't want to reenter. The script will output a
single QIF file with all transactions, accounts, and categories.
(Currently only non-investment transactions are handled.) The QIF
file can be imported to xacc-1.0.18 if a small patch is applied to
QIFIO.c. The site is
<p> <a href="http://www-cad.eecs.berkeley.edu/~gooch/mymdump.html">
MyMdump</a></p>
<h1>Duplicate Transactions</h1>
I also have a script that I use to remove duplicate transactions at the QIF
level. I use this script because Xacc is very strict about duplicates (which
is good) and because editing imported transactions will cause a re-import of
the same transactions to produce duplicates. (I download the same month's
transactions from my bank several times each month, so each downloaded QIF
file--after the first--contains transactions I have already imported. I
don't want to rely on the bank sending me the transactions in the same order or
with the same formatting.) The site is
<p>
http://www-cad.eecs.berkeley.edu/~gooch/qifuniq.html
<p>
I also have a script that I use to remove duplicate transactions at
the QIF level. I use this script because Xacc is very strict about
duplicates (which is good) and because editing imported
transactions will cause a re-import of the same transactions to
produce duplicates. (I download the same month's transactions from
my bank several times each month, so each downloaded QIF
file--after the first--contains transactions I have already
imported. I don't want to rely on the bank sending me the
transactions in the same order or with the same formatting.) The
site is
I hope others find these scripts useful.
<p>
Ken Yamaguchi October 1998
<p> <a href="http://www-cad.eecs.berkeley.edu/~gooch/qifuniq.html">
qifuniq</a></p>
<p> I hope others find these scripts useful.</p>
<p>Ken Yamaguchi October 1998</p>
</body>
</html>

35
Docs/gnucash.css Normal file
View File

@ -0,0 +1,35 @@
/* Style Sheet */
/* $Id$ */
BODY{ background-color:#EEFFEE;
/* background-image: url(http://www.isomedia.com/homes/tboyle/greenpaper.gif); */
background-repeat: repeat-y;}
SPAN.indent{margin-left:0.4in;}
/* This may be a bit excessively loud... */
H1, H2, H3, H4 {font-family: Optima, Lucida, Helvetica, sans-serif;
color: green; background-color: transparent;
font-weight: bolder; }
/* Utopia, Helvetica, Optima, Lucida Typewriter */
H1{ font-size: 18pt;}
H2{ font-size: 16pt;}
H3{ font-size: 14pt;}
H4{ font-size: 12pt;}
/* And make the main title big, centre it */
H1.title {font-size: 24pt; text-align: center; color: maroon;
background-color: transparent; font-family: Optima, Lucida, Helvetica,
sans-serif;}
DEL {font-weight: bold}
INS {background-color: white}
/* Zowie way of presenting PRE stuff */
PRE,TT.LITERAL,P.LITERALLAYOUT{ font-family:Lucida Typewriter, Courier,
monospace; font-weight: normal; background-color: gray; color: white;
border-width: thin; white-space:pre; }

View File

@ -51,7 +51,7 @@ include @top_srcdir@/Makefile.common
motif-static: motif.static
gnome-static: gnome.static
build-flavor:
build-flavor: config.status
@cd lib; $(MAKE) ${FLAVOR}
@cd src; $(MAKE) ${FLAVOR}
ln -sf gnucash.${FLAVOR} gnucash.bin
@ -64,17 +64,40 @@ motif.static:
${MAKE} FLAVOR=motif.static build-flavor
gnome:
${MAKE} FLAVOR=gnome build-flavor
${MAKE} @GNOME_TARGET@
gnome.static:
${MAKE} @GNOME_STATIC_TARGET@
gnome.real:
${MAKE} FLAVOR=gnome build-flavor
gnome.static.real:
${MAKE} FLAVOR=gnome.static build-flavor
#GNOME_TARGET if gnome build disabled due to missing libraries
gnome.disable:
echo "Gnome build disabled - see configure.log for details"
false
qt:
${MAKE} FLAVOR=qt build-flavor
depend:
@echo make depend is now superfluous.
configure: configure.in
autoconf
config.status: configure
if [ -f ./config.status ]; then \
./config.status --recheck; \
else \
echo "======> You need to run configure!"; \
exit 1; \
fi
CLEAN_SUBDIRS += lib src
TRASH += TAGS *~ *.o *.bak
@ -155,6 +178,7 @@ install:
$(INSTALL_DATA) Docs/*.gif ${GNC_SHAREDIR}/Docs
# $(INSTALL_DATA) Docs/*.jpg ${GNC_SHAREDIR}/Docs
$(INSTALL_DATA) Docs/*.xpm ${GNC_SHAREDIR}/Docs
$(INSTALL_DATA) Docs/*.css ${GNC_SHAREDIR}/Docs
@mkdir -p ${GNC_SHAREDIR}/Docs/images
# $(INSTALL_DATA) Docs/logos/*.* ${GNC_SHAREDIR}/Docs/images
@mkdir -p ${GNC_SHAREDIR}/Docs/logos
@ -193,9 +217,6 @@ install:
# ${INSTALL_DATA} $$file ${GNC_CONFIGDIR}/$$dest; \
# done
.PHONY: default install-private install motif motif-static gnome gnome-static qt
.PHONY: default install-private install
.PHONY: motif motif-static motif.static gnome gnome-static gnome.static qt
.PHONY: depend dist clean distclean
# Local Variables:
# tab-width: 2
# End:

View File

@ -15,6 +15,53 @@ from the guile side.
Given that and a reasonable understanding of GTK/GNOME, you should be
able to follow what I've done.
Introduction To Scheme and guile(rgmerk)
--------------------------------
Please skip this if you already know what Scheme is and why it's
so cool . . .
Scheme is a dialect of LISP (List Programming), one of the earliest
programming languages. It makes so many things easy it's just not
funny. It can be a little confusing for people raised on C and Java,
but any time taken to learn it is made up for with easier-to-write,
easier-to-debug, more reusable, and more robust code.
Guile is an implementation of standard Scheme which is easily
embeddable in C, making multi-language development relatively
straightforward. You can easily access data and procedures from
either end. Guile supports a superset of R4RS (the Scheme standard).
For initial experimentation, you can use Guile as an interactive Scheme
shell to play around with the system.
SLIB is a a library for Scheme implementations (including guile)
that implements a large collection of useful data structures
and algorithms.
FIXME: Starting gnucash as a guile shell. ..
While the Guile documentation (in info format) explains
Guile specifics, it doesn't have much information about Scheme,
the language. The Internet Scheme Repository:
http://www.cs.indiana.edu/scheme-repository/home.html
has quite a useful collection of information, including
FAQs, online copies of the Scheme standard (which is actually
quite readable and useful), and pointers to web tutorials
and other resources.
g-wrap
------
g-wrap is the tool used to automate the wrapping of C functions
to make them callable from the guile code. Gnucash installs
its own local copy of g-wrap. Documentation in info format
is available in gnucash/lib/g-wrap/doc/g-wrap.info. Available
C functions are wrapped in gnucash/src/g-wrap/gnc.gwp. Pointers
are wrapped using some stuff in gnucash/lib/g-wrap/guile/pointer.scm
which is not documented but looks reasonably straightforward.
Garbage collection:
-------------------

17
TODO
View File

@ -82,6 +82,22 @@ Done, except for the percentage bit.
56) mnemonics don't work in the menus
57) fix auto single/double modes
58) transactions cut/copy/paste
59) stock split functionality
60) account combobox needs to reflect account structure
61) allocate columns widths better in gnome
63) ability to reparent accounts
64) ability to merge accounts
65) better mousing on the gnome register
Fixed bugs:
-----------
@ -200,3 +216,4 @@ Non-memory-leak requires major redesign, and is in version 1.1
pull-down.
Feb 98 fixed
62) refresh accounts in register combobox when accounts change

406
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -108,6 +108,31 @@ if test ! -d ${PERLINCL}/CORE; then
fi
AC_SUBST(PERLINCL)
# Check for eperl (rgmerk)
# Permits compilation without eperl, but if it can't find eperl you
# must either use the --with-eperl option or, at a last resort,
# it'll use /usr/bin/eperl.
AC_PATH_PROG(EPERL,eperl,no)
AC_ARG_WITH(eperl,
[ --with-eperl=FILE which eperl executable to use ],
EPERL="${with_eperl}")
if test x"$EPERL" = xno; then
AC_MSG_WARN([Can't find eperl. Try using the --with-eperl flag.])
EPERL="/usr/bin/eperl"
fi
# for some reason the code needs both the full path and the basename.
# these get dumped into src/guile/pathconfig.h
EPERL_PATH="$EPERL"
EPERL_NAME=`basename $EPERL`
AC_SUBST(EPERL_PATH)
AC_SUBST(EPERL_NAME)
### -------------------------------------------------------------------
# Check for 'swig'
AC_PATH_PROG(SWIG,swig,no) # Sets @SWIG@
AC_ARG_WITH(swig,
@ -237,6 +262,54 @@ AC_CHECK_LIB(XmHTML, XmHTMLTextScrollToLine,
AC_SUBST(XMHTML_TARGET)
AC_SUBST(XMHTML_INC)
### -------------------------------------------------------------------------
## gtk-xmhtml
# if gtk-xmhtml header or library not found, gnome builds are disabled
# make gnome will run make gnome.disabled which echoes a warning message
## XXX - need to do this because gtk-xmhtml requires glibconfig.h
## and I don't want CPPFLAGS permanently altered.
## If there's a better way to tackle this please let me know!
OLDCPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS `$GNOME_CONFIG_BIN --cflags glib`"
# gnome targets if all goes well
# XXX - should we shift these to the rest of the gnome configuration
# section?
GNOME_TARGET="gnome.real"
GNOME_STATIC_TARGET="gnome.static.real"
#AC_CHECK_HEADER might work, but I'm not sure it uses CPPFLAGS
# this guarantees it - it works. Promise!!
AC_MSG_CHECKING([gtk-xmhtml/gtk-xmhtml.h])
AC_TRY_CPP([#include <gtk-xmhtml/gtk-xmhtml.h>], AC_MSG_RESULT(yes) ,
AC_MSG_WARN([Cannot find gtk-xmhtml.h -- gnome build disabled (not required for motif)])
GNOME_TARGET="gnome.disabled"
GNOME_STATIC_TARGET="gnome.disabled")
#undo damage to CPPFLAGS
CPPFLAGS="$OLDCPPFLAGS"
OLDLDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS `$GNOME_CONFIG_BIN --libs gtkxmhtml`"
#check for gtkxmhtml, export library link to variable GTK_XMHTML
AC_CHECK_LIB(gtkxmhtml, gtk_xmhtml_new,
GTK_XMHTML="gtkxmhtml",
AC_MSG_WARN([Cannotfind libgtkxmhtml -- gnome build disabled (not required for motif)])
GNOME_TARGET="gnome.disabled"
GNOME_STATIC_TARGET="gnome.disabled")
AC_SUBST(GTK_XMHTML)
LDFLAGS="$OLDLDFLAGS"
# XXX - should we export these here or later in the configure script?
AC_SUBST(GNOME_TARGET)
AC_SUBST(GNOME_STATIC_TARGET)
### --------------------------------------------------------------------------
## Nana
# XXX - There should probably be a --without-nana option (e.g., for
@ -394,6 +467,14 @@ AC_SUBST(GNC_LIBDIR)
AC_SUBST(GNC_CONFIGDIR)
AC_SUBST(GNC_SHAREDIR)
# HACK : inserts the path to gnucash.pm, which is used in the
# reporting code and is defined in gnucash.h
GNC_RUNTIME_PERLLIBPATH=`eval echo ${GNC_LIBDIR}`
AC_SUBST(GNC_RUNTIME_PERLLIBPATH)
# We have to handle these here because they are the *runtime* (not install
# time) defaults, and they're substituted into bootstrap.scm and gnucash.h
# directly, so:
@ -429,6 +510,7 @@ AC_OUTPUT(Makefile
src/register/Makefile
src/register/gnome/Makefile
src/reports/Makefile
src/reports/pathconfig.h
src/swig/Makefile
src/swig/perl5/Makefile
lib/Makefile
@ -438,6 +520,6 @@ AC_OUTPUT(Makefile
chmod +x gnucash
make -f Makefile.config.finish prefix=${prefix} \
${MAKE-make} -f Makefile.config.finish prefix=${prefix} \
GNC_SHAREDIR=${GNC_SHAREDIR} \
GNC_CONFIGDIR=${GNC_SHAREDIR}

View File

@ -69,13 +69,13 @@ dist:
chmod +x g-wrap/missing
motif: g-wrap-install
motif motif.static: g-wrap-install
@cd ComboBox-1.33; $(MAKE) default
@cd Xbae-4.6.2-linas; $(MAKE) default
gnome: g-wrap-install
gnome gnome.static: g-wrap-install
qt: g-wrap-install
.PHONY: all gnome motif qt dist
.PHONY: all gnome gnome.static motif motif.static qt dist

View File

@ -49,4 +49,5 @@ EditNotesWindow * editNotesWindow (Account *acc);
void xaccDestroyEditAccWindow (Account *);
void xaccDestroyEditNotesWindow (Account *);
void xaccSetDefaultNewaccountCurrency(char *new_default_currency);
#endif /* __XACC_NEWACCWINDOW_H__ */

View File

@ -52,23 +52,23 @@ include @top_srcdir@/Makefile.common
default: $(OBJS)
motif: ${MOTIF_OBJS}
motif motif.static: ${MOTIF_OBJS}
@cd engine; $(MAKE) default
@cd register; $(MAKE) motif
@cd reports; $(MAKE) default
@cd g-wrap; $(MAKE) motif
@cd swig; $(MAKE) motif
@cd guile; $(MAKE) default
@cd motif; $(MAKE) motif
@cd motif; $(MAKE) $@
gnome: ${GNOME_OBJS}
gnome gnome.static: ${GNOME_OBJS}
@cd engine; $(MAKE) default
@cd register; $(MAKE) gnome
@cd reports; $(MAKE) default
@cd g-wrap; $(MAKE) gnome
@cd swig; $(MAKE) gnome
@cd guile; $(MAKE) default
@cd gnome; $(MAKE) gnome
@cd gnome; $(MAKE) $@
qt: $(QT_OBJS)
@cd engine; $(MAKE) default
@ -79,8 +79,4 @@ qt: $(QT_OBJS)
@cd guile; $(MAKE) default
@cd qt; $(MAKE) qt
.PHONY: default qt gnome motif all
# Local Variables:
# tab-width: 2
# End:
.PHONY: default qt gnome gnome.static motif motif.static all

View File

@ -340,6 +340,8 @@ xaccLedgerDisplayGeneral (Account *lead_acc, Account **acclist, int ledger_type)
/* set up the query filter */
regData->query = xaccMallocQuery();
xaccQuerySetAccounts (regData->query, regData->displayed_accounts);
if ((regData->leader != NULL) &&
!accListHasAccount(regData->displayed_accounts, regData->leader))
xaccQueryAddAccount (regData->query, regData->leader);
/* by default, display only thirty transactions */
@ -352,7 +354,7 @@ xaccLedgerDisplayGeneral (Account *lead_acc, Account **acclist, int ledger_type)
* The main register window itself *
\******************************************************************/
/* MallocBasicRegister will malloc & initialize the register,
/* xaccMallocSplitRegister will malloc & initialize the register,
* but will not do the gui init */
regData->ledger = xaccMallocSplitRegister (ledger_type);
@ -414,7 +416,26 @@ xaccLedgerDisplayRefresh (xaccLedgerDisplay *regData)
if (regData->redraw) {
(regData->redraw) (regData);
}
}
/********************************************************************\
* refresh all the register windows, but only with the gui callback *
\********************************************************************/
void
xaccRegisterRefreshAllGUI (void)
{
xaccLedgerDisplay *regData;
int n;
if (!fullList) return;
n = 0; regData = fullList[n];
while (regData) {
if (regData->redraw)
(regData->redraw) (regData);
n++; regData = fullList[n];
}
}
/********************************************************************\
@ -614,9 +635,7 @@ xaccDestroyLedgerDisplay (Account *acc)
* frees memory allocated for an regWindow, and other cleanup *
* stuff *
* *
* Args: mw - the widget that called us *
* cd - regData - the data struct for this register *
* cb - *
* Args: regData - ledger display structure *
* Return: none *
\********************************************************************/
void
@ -628,9 +647,7 @@ xaccLedgerDisplayClose (xaccLedgerDisplay *regData)
acc = regData->leader;
/* Save any unsaved changes */
xaccSRSaveRegEntry (regData->ledger);
/* refresh the register windows if there were changes */
if (xaccSRSaveRegEntry (regData->ledger, NULL))
xaccSRRedrawRegEntry (regData->ledger);
xaccDestroySplitRegister (regData->ledger);

View File

@ -79,53 +79,66 @@ struct _xaccLedgerDisplay {
/*
* opens up a register window to display a single account
*/
extern xaccLedgerDisplay * xaccLedgerDisplaySimple (Account *acc);
xaccLedgerDisplay * xaccLedgerDisplaySimple (Account *acc);
/*
* opens up a register window to display the parent account
* and all of its children.
*/
extern xaccLedgerDisplay * xaccLedgerDisplayAccGroup (Account *acc);
xaccLedgerDisplay * xaccLedgerDisplayAccGroup (Account *acc);
/*
* display list of accounts in a general ledger.
*/
extern xaccLedgerDisplay * xaccLedgerDisplayGeneral
(Account *lead_acc, Account **acclist, int ledger_type);
xaccLedgerDisplay * xaccLedgerDisplayGeneral (Account *lead_acc,
Account **acclist,
int ledger_type);
/*
* redisplay/redraw all windows that contain any transactions
* that are associated with the indicated account.
*/
extern void xaccAccountDisplayRefresh (Account *acc);
extern void xaccAccListDisplayRefresh (Account **acc);
void xaccAccountDisplayRefresh (Account *acc);
void xaccAccListDisplayRefresh (Account **acc);
/*
* redisplay/redraw all windows that contain this transaction
* (or any of its member splits).
*/
extern void xaccTransDisplayRefresh (Transaction *trans);
void xaccTransDisplayRefresh (Transaction *trans);
/*
* redisplay/redraw only the indicated window.
* both routines do same thing, they differ only by the argument they
* take.
*/
extern void xaccLedgerDisplayRefresh (xaccLedgerDisplay *);
extern void xaccRegisterRefresh (SplitRegister *);
void xaccLedgerDisplayRefresh (xaccLedgerDisplay *);
void xaccRegisterRefresh (SplitRegister *);
/*
* Call the user refresh callback for all registers. This does not
* perform a full refresh, i.e., it does not reload transactions.
* This is just for updating gui controls.
*/
void xaccRegisterRefreshAllGUI (void);
/*
* return true if acc is a member of the ledger.
*/
int ledgerIsMember (xaccLedgerDisplay *reg, Account * acc);
/*
* close the window
*/
extern void xaccLedgerDisplayClose (xaccLedgerDisplay *);
void xaccLedgerDisplayClose (xaccLedgerDisplay *);
/********************************************************************\
* sort of a quick hack involving the layout of the register.
\********************************************************************/
extern void xaccRegisterCountHack (SplitRegister *splitreg);
void xaccRegisterCountHack (SplitRegister *splitreg);
extern void xaccDestroyLedgerDisplay (Account *acc);
void xaccDestroyLedgerDisplay (Account *acc);
#endif /* __MULTI_LEDGER_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -1066,6 +1066,13 @@ xaccAccountGetParent (Account *acc)
return (acc->parent);
}
Account *
xaccAccountGetParentAccount (Account * acc)
{
if (!acc) return NULL;
return xaccGroupGetParentAccount(acc->parent);
}
int
xaccAccountGetType (Account *acc)
{

View File

@ -140,6 +140,7 @@ char * xaccAccountGetCurrency (Account *);
char * xaccAccountGetSecurity (Account *);
AccountGroup * xaccAccountGetChildren (Account *);
AccountGroup * xaccAccountGetParent (Account *);
Account * xaccAccountGetParentAccount (Account *);
AccInfo * xaccAccountGetAccInfo (Account *);
double xaccAccountGetBalance (Account *);

View File

@ -108,6 +108,15 @@ xaccAccountGroupMarkSaved (AccountGroup *grp)
}
}
/********************************************************************\
\********************************************************************/
void
xaccAccountGroupMarkNotSaved (AccountGroup *grp)
{
if (!grp) return;
grp->saved = GNC_F;
}
/********************************************************************\
\********************************************************************/
int
@ -764,6 +773,13 @@ xaccGroupGetAccount (AccountGroup *grp, int i)
return (grp->account[i]);
}
Account *
xaccGroupGetParentAccount (AccountGroup * grp)
{
if (!grp) return NULL;
return grp->parent;
}
double
xaccGroupGetBalance (AccountGroup * grp)
{

View File

@ -54,9 +54,13 @@ void xaccMergeAccounts (AccountGroup *grp);
* The xaccAccountGroupMarkSaved() subroutine will mark
* the entire group as having been saved, including
* all of the child accounts.
*
* The xaccAccountGroupMarkNotSaved() subroutine will mark
* the given group as not having been saved.
*/
int xaccAccountGroupNotSaved (AccountGroup *grp);
void xaccAccountGroupMarkSaved (AccountGroup *grp);
void xaccAccountGroupMarkNotSaved (AccountGroup *grp);
/*
* The xaccRemoveAccount() subroutine will remove the indicated
@ -168,6 +172,11 @@ void xaccConsolidateGrpTransactions (AccountGroup *);
Account * xaccGroupGetAccount (AccountGroup *, int);
/* The xaccGroupGetParentAccount() subroutine returns the parent
* account of the group, or NULL.
*/
Account * xaccGroupGetParentAccount (AccountGroup *);
/*
* The xaccGroupGetNextFreeCode() method will try to guess a reasonable
* candidate for the next unused account code in this group.

View File

@ -32,6 +32,24 @@ static short module = MOD_ENGINE;
/* ------------------------------------------------------ */
gncBoolean accListHasAccount (Account **list, Account *findme)
{
Account *acc;
int nacc = 0;
if (!list || !findme) return GNC_F;
acc = list[0];
while (acc) {
if (acc == findme)
return GNC_T;
nacc++;
acc = list[nacc];
}
return GNC_F;
}
/* ------------------------------------------------------ */
int accListCount (Account **list)
{
Account *acc;

View File

@ -21,12 +21,14 @@
#ifndef __XACC_LEDGER_UTILS_H__
#define __XACC_LEDGER_UTILS_H__
#include "gnc-common.h"
#include "config.h"
#include "Account.h"
/** PROTOTYPES ******************************************************/
gncBoolean accListHasAccount (Account **list, Account *findme);
int accListCount (Account **list);
Account ** accListCopy (Account **list);
Account ** xaccGroupToList (Account *);

View File

@ -49,6 +49,13 @@
#define WFLAGS (O_WRONLY | O_CREAT | O_TRUNC)
#define RFLAGS O_RDONLY
/* Cleared values found in QIF files */
#define QRECCLEAR '*' /* Cleared, per Quicken */
#define QRECREC 'x' /* Reconciled, per Quicken */
#define QRECRECM 'X' /* Reconciled, per MS Money */
#define QRECBUDG '?' /* Budgeted amount, per CBB */
#define QRECBUDP '!' /* OLD Budgeted amount per CBB */
/** GLOBALS *********************************************************/
/* XXX hack alert -- this default currency should be made configurable
@ -60,6 +67,9 @@ static int error_code=0; /* error code, if error occurred */
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_IO;
static int FindDateDelimiter (char *str);
static void TryToFixDate (struct tm *date);
static int FavorDateType (int value);
/*******************************************************/
@ -397,7 +407,7 @@ char * xaccReadQIFAccList (int fd, AccountGroup *grp, int cat)
* parses date of the form MM/DD/YY *
* *
* Args: str -- pointer to string rep of date *
* Return: void *
* Return: time_t -- date in format used by UNIX time_t *
\********************************************************************/
time_t
@ -406,15 +416,22 @@ xaccParseQIFDate (char * str)
char * tok;
time_t secs;
struct tm dat;
int favechar = 0; /* Which delimiter do we have? */
if (!str) return 0;
tok = strchr (str, '/');
if (!str) return 0; /* If the string is null, we're done. */
/* First, figure out the delimiter. */
/* Choices: "." or "-" or "/" */
favechar = FindDateDelimiter(str);
if (!favechar) return 0;
tok = strchr (str, favechar);
if (!tok) return 0;
*tok = 0x0;
dat.tm_mon = atoi (str) - 1;
str = tok+sizeof(char);
tok = strchr (str, '/');
tok = strchr (str, favechar);
if (!tok) return 0;
*tok = 0x0;
dat.tm_mday = atoi (str);
@ -428,6 +445,8 @@ xaccParseQIFDate (char * str)
*tok = 0x0;
dat.tm_year = atoi (str);
TryToFixDate(&dat);
/* a quickie Y2K fix: assume two digit dates with
* a value less than 50 are in the 21st century. */
if (50 > dat.tm_year) dat.tm_year += 100;
@ -441,6 +460,120 @@ xaccParseQIFDate (char * str)
return secs;
}
/********************************************************************\
* FindDateDelimiter *
* determines which character is the delimiter for a date *
* typical would be "MM/DD/YY", "MM-DD-YY", "MM.DD.YY" *
* *
* Args: str -- pointer to string rep of date *
* Return: int containing '/', '.', '-', or NULL indicating failure *
\********************************************************************/
static int FindDateDelimiter (char *str) {
char *tok;
if (!str) return 0;
tok = strchr (str, '/');
if (tok)
return '/';
tok = strchr (str, '.');
if (tok)
return '.';
tok = strchr (str, '-');
if (tok)
return '-';
return 0;
}
/********************************************************************\
* TryToFixDate *
* Swaps around date components based on some heuristics *
* *
* Args: date -- pointer to time_t structure *
* Return: void *
\********************************************************************/
static void TryToFixDate (struct tm *date)
{
int first, second, third;
int st_first, st_second, st_third;
int mon, mday, year;
int results;
int which[5] = {0,0,0,0,0};
first = date->tm_mon;
second = date->tm_mday;
third = date->tm_year;
/* See what sort of date is favored by each component */
st_first = FavorDateType(first);
st_second = FavorDateType(second);
st_third = FavorDateType(third);
/* Plunk the values down into which[] */
which[st_first] = 1;
which[st_second] = 2;
which[st_third] = 3;
switch (which[4]) { /* Year */
case 1:
year = first;
break;
case 2:
year = second;
break;
case 3:
year = third;
break;
default:
return; /* No date component looks like a year --> ABORT */
}
switch (which[2]) { /* month */
case 1:
mon = first;
break;
case 2:
mon = second;
break;
case 3:
mon = third;
break;
default:
return; /* No date component looks like a month --> ABORT */
}
switch (which[1]) { /* mday */
case 1:
mday = first;
break;
case 2:
mday = second;
break;
case 3:
mday = third;
break;
default:
return; /* No date component looks like a day of the month - ABORT */
}
date->tm_mon = mon;
date->tm_mday = mday;
date->tm_year = year;
return;
}
static int FavorDateType (int value)
{
int favoring;
favoring = 2; /* Month */
if (value > 30)
favoring = 4; /* Year */
if (value < 0)
favoring = 4; /* Year */
if (value < 31)
if (value > 11)
favoring = 1; /* Day of month */
}
/********************************************************************\
\********************************************************************/
@ -643,6 +776,7 @@ xaccReadQIFTransaction (int fd, Account *acc, int guess_name,
Account *sub_acc = 0x0;
Account *xfer_acc = 0x0;
double adjust = 0.0;
char secondchar;
if (!acc) return NULL;
@ -659,16 +793,23 @@ xaccReadQIFTransaction (int fd, Account *acc, int guess_name,
while (qifline) {
switch (qifline[0])
{
case 'C':
/* C == Cleared / Reconciled */
/* Quicken uses C* and Cx, while MS Money uses CX.
* C* means cleared (but not yet reconciled)
* Cx or CX means reconciled
*/
if (('x' == qifline[1]) || ('X' == qifline[1])) {
xaccSplitSetReconcile (source_split, YREC);
} else {
case 'C': /* Cleared flag */
secondchar = qifline[1];
switch(secondchar)
{
case QRECCLEAR:
xaccSplitSetReconcile(source_split, CREC);
break;
case QRECREC:
case QRECRECM:
xaccSplitSetReconcile(source_split, YREC);
break;
case QRECBUDP:
case QRECBUDG:
xaccSplitSetReconcile(source_split, NREC);
break;
default:
xaccSplitSetReconcile(source_split, NREC);
}
break;
case 'D': /* D == date */
@ -749,7 +890,7 @@ xaccReadQIFTransaction (int fd, Account *acc, int guess_name,
/* O == adjustments */
/* hack alert -- sometimes adjustments are quite large.
* I have no clue why, and what to do about it. For what
* its worth, I can prove that Quicken version 3.0 makes
* it's worth, I can prove that Quicken version 3.0 makes
* math errors ... */
{
double pute;
@ -870,9 +1011,8 @@ xaccReadQIFTransaction (int fd, Account *acc, int guess_name,
return qifline;
}
/* fundamentally differnt handling for securities and non-securities */
/* fundamentally different handling for securities and non-securities */
if (is_security) {
/* if the transaction is a sale/purchase of a security,
* then it is a defacto transfer between the brokerage account
* and the stock account. */

View File

@ -225,6 +225,8 @@ xaccQuerySetDateRangeL (Query *q, long long early, long long late)
q->latest.tv_sec = late;
}
/* ================================================== */
/* ================================================== */
/* Note that the sort order for a transaction that is
* currently being edited is based on its old values,
@ -254,6 +256,10 @@ xaccQuerySetDateRangeL (Query *q, long long early, long long late)
if ( !(ta) && !(tb) ) return 0; \
#define CSTANDARD { \
retval = xaccSplitDateOrder(sa, sb); \
if (retval) return retval; \
}
#define CDATE { \
/* if dates differ, return */ \
@ -340,6 +346,8 @@ xaccQuerySetDateRangeL (Query *q, long long early, long long late)
} \
}
#define CNONE
#define DECLARE(ONE,TWO,THREE) \
static int Sort_##ONE##_##TWO##_##THREE \
(Split **sa, Split **sb) \
@ -392,6 +400,7 @@ sub recur {
/* ================================================== */
/* Define the sorting comparison functions */
DECLARE (STANDARD, NONE, NONE)
DECLARE (DESC, MEMO, AMOUNT)
DECLARE (DESC, MEMO, NUM)
DECLARE (DESC, MEMO, DATE)
@ -468,6 +477,7 @@ xaccQuerySetSortOrder (Query *q, int arga, int argb, int argc)
if (!q) return;
q->changed = 1;
DECIDE (STANDARD, NONE, NONE)
DECIDE (DESC, MEMO, AMOUNT)
DECIDE (DESC, MEMO, NUM)
DECIDE (DESC, MEMO, DATE)
@ -587,12 +597,12 @@ xaccQueryGetSplits (Query *q)
/* now go through the splits */
j=0; s = acc->splits[0];
while (s) {
if (s->parent->date_posted.tv_sec >= q->earliest.tv_sec) {
nsplits ++;
}
if (s->parent->date_posted.tv_sec > q->latest.tv_sec) {
break;
}
if (s->parent->date_posted.tv_sec >= q->earliest.tv_sec) {
nsplits ++;
}
j++; s = acc->splits[j];
}
i++; acc = q->acc_list[i];
@ -610,12 +620,12 @@ xaccQueryGetSplits (Query *q)
/* now go through the splits */
j=0; s = acc->splits[0];
while (s) {
if (s->parent->date_posted.tv_sec >= q->earliest.tv_sec) {
slist[k] = s; k++;
}
if (s->parent->date_posted.tv_sec > q->latest.tv_sec) {
break;
}
if (s->parent->date_posted.tv_sec >= q->earliest.tv_sec) {
slist[k] = s; k++;
}
j++; s = acc->splits[j];
}
i++; acc = q->acc_list[i];

View File

@ -38,11 +38,13 @@ typedef struct _Query Query;
/* sorting orders */
enum {
BY_STANDARD,
BY_DATE,
BY_NUM,
BY_AMOUNT,
BY_MEMO,
BY_DESC
BY_DESC,
BY_NONE
};
Query * xaccMallocQuery (void);

View File

@ -204,6 +204,7 @@ xaccConfigGetForceDoubleEntry (void)
#define MARK_SPLIT(split) { \
Account *acc = (Account *) ((split)->acc); \
if (acc) acc->changed |= ACC_INVALIDATE_ALL; \
if (acc) xaccAccountGroupMarkNotSaved(acc->parent); \
}
static void
@ -869,9 +870,14 @@ xaccSplitRebalance (Split *split)
void
xaccTransBeginEdit (Transaction *trans, int defer)
{
char open;
assert (trans);
open = trans->open;
trans->open = BEGIN_EDIT;
if (defer) trans->open |= DEFER_REBALANCE;
if (open & BEGIN_EDIT) return;
xaccOpenLog ();
xaccTransWriteLog (trans, 'B');
@ -1097,6 +1103,13 @@ xaccTransRollbackEdit (Transaction *trans)
LEAVE ("xaccTransRollbackEdit(): trans addr=%p\n", trans);
}
gncBoolean
xaccTransIsOpen (Transaction *trans)
{
if (trans == NULL) return GNC_F;
return ((trans->open & BEGIN_EDIT) != 0);
}
/********************************************************************\
\********************************************************************/

View File

@ -136,11 +136,16 @@ void xaccTransDestroy (Transaction *);
* started. This includes restoring any deleted splits, removing
* any added splits, and undoing the effects of xaccTransDestroy,
* as well as restoring prices, memo's descriptions, etc.
*
* The xaccTransIsOpen() method returns GNC_T if the transaction
* is open for editing. Otherwise, it returns false.
*/
void xaccTransBeginEdit (Transaction *, int defer);
void xaccTransCommitEdit (Transaction *);
void xaccTransRollbackEdit (Transaction *);
gncBoolean xaccTransIsOpen (Transaction *trans);
/* Convert a day, month, and year to a Timespec */
Timespec gnc_dmy2timespec(int day, int month, int year);

View File

@ -35,13 +35,43 @@
#include "config.h"
#include "date.h"
#include "util.h"
/* This is now user configured through the gnome options system() */
static DateFormat dateFormat = DATE_FORMAT_US;
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_ENGINE;
/* hack alert -- this should be turned into user-configurable parameter .. */
DateFormat dateFormat = DATE_FORMAT_US;
/********************************************************************\
\********************************************************************/
/**
* setDateFormat
* set date format to one of US, UK, CE, OR ISO
* checks to make sure it's a legal value
* Args: DateFormat: enumeration indicating preferred format
* returns: nothing
*
* Globals: dateFormat
**/
void setDateFormat(DateFormat df)
{
if(df >= DATE_FORMAT_FIRST && df <= DATE_FORMAT_LAST)
{
dateFormat = df;
}
else
{ /* hack alert - is this what we should be doing here? */
PERR("non-existent date format set");
}
return;
}
/**
* printDate
* Convert a date as day / month / year integers into a localized string
@ -72,6 +102,7 @@ printDate (char * buff, int day, int month, int year)
*/
switch(dateFormat)
{
case DATE_FORMAT_UK:
sprintf (buff, "%2d/%2d/%-4d", day, month, year);
break;
@ -81,11 +112,24 @@ printDate (char * buff, int day, int month, int year)
case DATE_FORMAT_ISO:
sprintf (buff, "%04d-%02d-%02d", year, month, day);
break;
case DATE_FORMAT_LOCALE:
{
struct tm tm_str;
tm_str.tm_mday = day;
tm_str.tm_mon = month - 1; /*tm_mon = 0 through 11 */
tm_str.tm_year = year - 1900; /* this is what the standard
* says, it's not a Y2K thing
*/
strftime(buff, MAX_DATE_LENGTH, "%x", &tm_str);
}
break;
case DATE_FORMAT_US:
default:
sprintf (buff, "%2d/%2d/%-4d", month, day, year);
break;
}
return;
}
void

View File

@ -81,13 +81,17 @@ typedef enum
DATE_FORMAT_US, /* United states: mm/dd/yyyy */
DATE_FORMAT_UK, /* Britain: dd/mm/yyyy */
DATE_FORMAT_CE, /* Continental Europe: dd.mm.yyyy */
DATE_FORMAT_ISO /* ISO: yyyy-mm-dd */
DATE_FORMAT_ISO, /* ISO: yyyy-mm-dd */
DATE_FORMAT_LOCALE /* Take from locale information */
} DateFormat;
#define DATE_FORMAT_FIRST DATE_FORMAT_US
#define DATE_FORMAT_LAST DATE_FORMAT_LOCALE
/* the maximum length of a string created by sprtDate() */
#define MAX_DATE_LENGTH 11
/** PROTOTYPES ******************************************************/
void setDateFormat(DateFormat df);
void printDate (char * buff, int day, int month, int year);
void printDateSecs (char * buff, time_t secs);
@ -102,6 +106,7 @@ void xaccTransSetDateStr (Transaction *trans, char *str);
time_t xaccDMYToSec (int day, int month, int year);
time_t xaccScanDateS (const char *buff);
/** GLOBALS *********************************************************/
extern DateFormat dateFormat;

View File

@ -49,7 +49,7 @@ int loglevel[MODULE_MAX] =
2, /* IO */
4, /* REGISTER */
2, /* LEDGER */
4, /* HTML */
2, /* HTML */
2, /* GUI */
4, /* SCRUB */
4, /* GTK_REG */
@ -150,6 +150,44 @@ ultostr (unsigned long val, int base)
return strdup (buf);
}
/********************************************************************\
* utility function to convert floating point value to a string
\********************************************************************/
static int
util_fptostr(char *buf, double val, int prec)
{
int i;
char formatString[10];
char prefix[] = "%0.";
char postfix[] = "f";
/* This routine can only handle precision between 0 and 9, so
* clamp precision to that range */
if (prec > 9) prec = 9;
if (prec < 0) prec = 0;
/* Make sure that the output does not resemble "-0.00" by forcing
* val to 0.0 when we have a very small negative number */
if ((val <= 0.0) && (val > -pow(0.1, prec+1) * 5.0))
val = 0.0;
/* Create a format string to pass into sprintf. By doing this,
* we can get sprintf to convert the number to a string, rather
* than maintaining conversion code ourselves. */
i = 0;
strcpy(&formatString[i], prefix);
i += strlen(prefix);
formatString[i] = '0' + prec; /* add prec to ASCII code for '0' */
i += 1;
strcpy(&formatString[i], postfix);
i += strlen(postfix);
sprintf(buf, formatString, val);
return strlen(buf);
}
/********************************************************************\
* stpcpy for those platforms that don't have it.
\********************************************************************/
@ -170,7 +208,6 @@ stpcpy (char *dest, const char *src)
* returned from the localconv() subroutine
\********************************************************************/
/* The PrtAmtComma() routine prints a comma-separated currency value */
/* THOU_SEP is a comma in U.S. but a period in some parts of Europe */
/* CENT_SEP is a period in U.S. but a comma in some parts of Europe */
@ -178,11 +215,11 @@ stpcpy (char *dest, const char *src)
#define CENT_SEP '.'
static int
PrtAmtComma (char * buf, double val, int prec)
PrintAmt(char *buf, double val, int prec, int use_commas)
{
int i, ival, ncommas = 0;
double tmp, amt=0.0;
char *start = buf;
int i, stringLength, numWholeDigits, commaCount;
char tempBuf[50];
char *bufPtr = buf;
/* check if we're printing infinity */
if (!finite(val)) {
@ -190,48 +227,48 @@ PrtAmtComma (char * buf, double val, int prec)
return 3;
}
/* Round to 100'ths or 1000'nths now. Must do this before we start printing. */
if (2 == prec) val += 0.005;
if (3 == prec) val += 0.0005;
util_fptostr(tempBuf, val, prec);
/* count number of commas */
tmp = val;
while (tmp > 1000.0) {
tmp *= 0.001;
ncommas ++;
if (!use_commas)
{
/* If we're not using commas, then the whole string is copied */
strcpy(buf, tempBuf);
}
else
{
/* Determine where the decimal place is, if there is one */
stringLength = strlen(tempBuf);
numWholeDigits = -1;
for (i = 0; i < stringLength; i++) {
if (tempBuf[i] == '.') {
numWholeDigits = i;
break;
}
}
/* print digits in groups of three, separated by commas */
for (i=ncommas; i>=0; i--) {
int j;
if (numWholeDigits < 0)
numWholeDigits = stringLength; /* Can't find decimal place, it's
* a whole number */
amt *= 1000.0;
tmp = val;
for (j=i; j>0; j--) tmp *= 0.001;
tmp -= amt;
ival = tmp;
if (i !=ncommas) {
buf += sprintf (buf, "%03d", ival);
} else {
buf += sprintf (buf, "%d", ival);
/* We now know the number of whole digits, now insert commas while
* copying them from the temp buffer to the destination */
bufPtr = buf;
for (i = 0; i < numWholeDigits; i++, bufPtr++) {
*bufPtr = tempBuf[i];
commaCount = (numWholeDigits - i) - 1;
if ( (commaCount % 3 == 0) &&
(commaCount != 0) &&
(tempBuf[i] != '-'))
{
bufPtr++;
*bufPtr = ',';
}
*buf = THOU_SEP; buf++;
amt += ival;
}
/* place decimal point */
buf --; *buf = CENT_SEP; buf++;
strcpy(bufPtr, &tempBuf[numWholeDigits]);
} /* endif */
/* print two or three decimal places */
if (3 == prec) {
ival = 1000.0 * (val-amt);
buf += sprintf (buf, "%03d", ival);
} else {
ival = 100.0 * (val-amt);
buf += sprintf (buf, "%02d", ival);
}
return (buf-start);
return strlen(buf);
}
int
@ -244,32 +281,18 @@ xaccSPrintAmount (char * bufp, double val, short shrs)
if (DEQ(val, 0.0))
val = 0.0;
if (0.0 > val) {
bufp[0] = '-';
bufp ++;
val = -val;
}
if (shrs & PRTSHR) {
if (shrs & PRTSEP) {
bufp += PrtAmtComma (bufp, val, 3);
} else {
bufp += sprintf( bufp, "%.3f", val );
}
bufp += PrintAmt(orig_bufp, val, 4, shrs & PRTSEP);
if (shrs & PRTSYM) {
/* stpcpy returns pointer to end of string, not like strcpy */
bufp = stpcpy (bufp, " shrs");
}
} else {
if (shrs & PRTSYM) {
bufp += sprintf( bufp, "%s ", CURRENCY_SYMBOL);
}
if (shrs & PRTSEP) {
bufp += PrtAmtComma (bufp, val, 2);
} else {
bufp += sprintf( bufp, "%.2f", val );
}
bufp += PrintAmt(orig_bufp, val, 2, shrs & PRTSEP);
}
/* return length of printed string */

View File

@ -41,8 +41,8 @@ CFLAGS = @CFLAGS@ ${INCLPATH}
LDFLAGS = @LDFLAGS@
GUILELIBS = @GUILELIBS@
LIBS = -L$(prefix)/lib @LIBS@ -lgtkxmhtml \
$(shell ${GNOME_CONFIG_BIN} --libs gnomeui) $(GUILELIBS) \
LIBS = -L$(prefix)/lib @LIBS@ \
$(shell ${GNOME_CONFIG_BIN} --libs gnomeui @GTK_XMHTML@) $(GUILELIBS) \
@top_srcdir@/lib/g-wrap-install/lib/libgwrapguile.a
ifeq (${HAVE_PLOTUTILS},1)
@ -65,10 +65,11 @@ OTHER_OBJS += $(wildcard @top_srcdir@/src/register/gnome/obj/gnome/*.o)
# See Makefile.common for information about these variables.
GNOME_SRCS := top-level.c window-main.c window-register.c window-adjust.c \
window-help.c cursors.c account-tree.c \
window-reconcile.c option-util.c \
window-reconcile.c option-util.c window-html.c \
dialog-options.c dialog-filebox.c dialog-transfer.c \
dialog-add.c dialog-edit.c dialog-utils.c \
scripts_menu.c query-user.c reconcile-list.c
scripts_menu.c query-user.c reconcile-list.c \
window-report.c global-options.c
######################################################################
all: gnome
@ -82,5 +83,5 @@ gnome: @top_srcdir@/gnucash.gnome
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
gnome.static: @top_srcdir@/gnucash.gnome.static
@top_srcdir@/gnucash.motif.static: ${GNOME_OBJS} ${OTHER_OBJS}
@top_srcdir@/gnucash.gnome.static: ${GNOME_OBJS} ${OTHER_OBJS}
$(CC) -static $(LDFLAGS) -o $@ $^ $(LIBS)

View File

@ -117,6 +117,7 @@ gnc_account_tree_init(GNCAccountTree *tree)
gtk_clist_set_shadow_type(GTK_CLIST(tree), GTK_SHADOW_IN);
gtk_clist_column_titles_passive(GTK_CLIST(tree));
gtk_clist_set_column_auto_resize(GTK_CLIST(tree), 0, TRUE);
gtk_clist_set_column_justification(GTK_CLIST(tree),
tree->balance_column,
GTK_JUSTIFY_RIGHT);
@ -286,8 +287,6 @@ gnc_account_tree_refresh(GNCAccountTree * tree)
tree->root_account),
gncGetCurrentGroup());
gtk_clist_thaw(clist);
gtk_clist_columns_autosize(clist);
gnc_account_tree_update_column_visibility(tree);
@ -297,10 +296,13 @@ gnc_account_tree_refresh(GNCAccountTree * tree)
if (adjustment != NULL)
{
save_value = CLAMP(save_value, adjustment->lower, adjustment->upper);
save_value = CLAMP(save_value, adjustment->lower,
adjustment->upper - adjustment->page_size);
gtk_adjustment_set_value(adjustment, save_value);
}
gtk_clist_thaw(clist);
g_hash_table_destroy(expanded_accounts);
}

View File

@ -35,6 +35,7 @@
#include "AccWindow.h"
#include "MainWindow.h"
#include "FileDialog.h"
#include "Refresh.h"
#include "window-main.h"
#include "dialog-utils.h"
#include "account-tree.h"
@ -47,7 +48,9 @@
static short module = MOD_GUI;
static int _accWindow_last_used_account_type = BANK;
static gchar * default_currency = "USD";
static gboolean default_currency_dynamically_allocated = FALSE;
struct _accwindow
@ -360,8 +363,10 @@ gnc_ui_accWindow_create_account(Account * account, Account * parent,
xaccAccountCommitEdit (account);
gnc_account_tree_insert_account(gnc_get_current_account_tree(),
account);
gnc_account_tree_insert_account(gnc_get_current_account_tree(), account);
/* Refresh register so they have this account in their lists */
gnc_group_ui_refresh(gncGetCurrentGroup());
}
@ -527,5 +532,24 @@ xaccDestroyEditNotesWindow (Account *acc)
}
/*********************************************************************\
* xaccSetDefaultNewaccountCurrency *
* Set the default currency for new accounts *
* intended to be called by option handling code *
* *
* Args: new default_currency *
* Globals: default_currency, default_currency_dynamically_allocated *
* Return value: none *
\*********************************************************************/
void
xaccSetDefaultNewaccountCurrency(char *new_default_currency)
{
if (default_currency_dynamically_allocated)
g_free(default_currency);
default_currency = g_strdup(new_default_currency);
default_currency_dynamically_allocated = TRUE;
}
/********************** END OF FILE *********************************\
\********************************************************************/

View File

@ -32,6 +32,8 @@
#include "AccWindow.h"
#include "MainWindow.h"
#include "Refresh.h"
#include "FileDialog.h"
#include "dialog-utils.h"
#include "messages.h"
#include "util.h"
@ -149,6 +151,7 @@ gnc_ui_EditAccWindow_ok_cb(GtkWidget * widget,
gnc_ui_free_field_strings(&strings);
gnc_refresh_main_window();
gnc_group_ui_refresh(gncGetCurrentGroup());
gnome_dialog_close(GNOME_DIALOG(editAccData->dialog));
}
@ -166,14 +169,21 @@ editAccWindow(Account *acc)
{
EditAccWindow * editAccData;
GtkWidget *vbox, *widget, *dialog;
char *name, *title;
FETCH_FROM_LIST (EditAccWindow, editAccList, acc, account, editAccData);
dialog = gnome_dialog_new(EDIT_ACCT_STR,
name = gnc_ui_get_account_full_name(acc, ":");
title = g_strconcat(name, " - ", EDIT_ACCT_STR, NULL);
dialog = gnome_dialog_new(title,
GNOME_STOCK_BUTTON_OK,
GNOME_STOCK_BUTTON_CANCEL,
NULL);
g_free(name);
g_free(title);
editAccData->dialog = dialog;
editAccData->account = acc;

View File

@ -25,8 +25,9 @@
#include <gnome.h>
#include "config.h"
#include "top-level.h"
#include "config.h"
#include "FileBox.h"
#include "messages.h"
#include "util.h"
@ -78,10 +79,13 @@ fileBox(const char * title, const char * filter)
fb_info.file_box = GTK_FILE_SELECTION(gtk_file_selection_new(title));
fb_info.file_name = NULL;
/* hack alert - this was filtering directory names as well as file
* names, so I think we should not do this by default (rgmerk)
*/
#if 0
if (filter != NULL)
gtk_file_selection_complete(fb_info.file_box, filter);
#endif
gtk_window_set_modal(GTK_WINDOW(fb_info.file_box), TRUE);
gtk_window_set_transient_for(GTK_WINDOW(fb_info.file_box),
GTK_WINDOW(gnc_get_ui_data()));

View File

@ -1,5 +1,5 @@
/********************************************************************\
* dialog-options.h -- GNOME option handling *
* dialog-options.c -- GNOME option handling *
* Copyright (C) 1998,1999 Linas Vepstas *
* *
* This program is free software; you can redistribute it and/or *
@ -25,14 +25,21 @@
#include "query-user.h"
#include "util.h"
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_GUI;
static GnomePropertyBox *options_dialog = NULL;
static void
/********************************************************************\
* gnc_option_set_ui_value *
* sets the GUI representation of an option with either its *
* current guile value, or its default value *
* *
* Args: option - option structure containing option *
* use_default - if true, use the default value, otherwise *
* use the current value *
* Return: nothing *
\********************************************************************/
void
gnc_option_set_ui_value(GNCOption *option, gboolean use_default)
{
gboolean bad_value = FALSE;
@ -64,13 +71,27 @@ gnc_option_set_ui_value(GNCOption *option, gboolean use_default)
{
if (gh_string_p(value))
{
char *string = gh_scm2newstr(gh_call0(getter), NULL);
char *string = gh_scm2newstr(value, NULL);
gtk_entry_set_text(GTK_ENTRY(option->widget), string);
free(string);
}
else
bad_value = TRUE;
}
else if (safe_strcmp(type, "multichoice") == 0)
{
int index;
index = gnc_option_value_permissible_value_index(option, value);
if (index < 0)
bad_value = TRUE;
else
{
gtk_option_menu_set_history(GTK_OPTION_MENU(option->widget), index);
gtk_object_set_data(GTK_OBJECT(option->widget), "gnc_multichoice_index",
GINT_TO_POINTER(index));
}
}
else
{
PERR("gnc_option_set_ui_value: Unknown type. Ignoring.\n");
@ -84,12 +105,14 @@ gnc_option_set_ui_value(GNCOption *option, gboolean use_default)
free(type);
}
void
_gnc_option_refresh_ui(SCM guile_option)
{
gnc_option_set_ui_value(gnc_get_option_by_SCM(guile_option), FALSE);
}
/********************************************************************\
* gnc_option_get_ui_value *
* returns the SCM representation of the GUI option value *
* *
* Args: option - option structure containing option *
* Return: SCM handle to GUI option value *
\********************************************************************/
SCM
gnc_option_get_ui_value(GNCOption *option)
{
@ -116,9 +139,20 @@ gnc_option_get_ui_value(GNCOption *option)
result = gh_str02scm(string);
g_free(string);
}
else if (safe_strcmp(type, "multichoice") == 0)
{
gpointer _index;
int index;
_index = gtk_object_get_data(GTK_OBJECT(option->widget),
"gnc_multichoice_index");
index = GPOINTER_TO_INT(_index);
result = gnc_option_value_permissible_value(option, index);
}
else
{
PERR("_gnc_option_get_guile_value: "
PERR("gnc_option_get_ui_value: "
"Unknown type for refresh. Ignoring.\n");
}
@ -130,12 +164,15 @@ gnc_option_get_ui_value(GNCOption *option)
static void
default_button_cb(GtkButton *button, gpointer data)
{
GtkWidget *pbox;
GNCOption *option = data;
gnc_option_set_ui_value(option, TRUE);
option->changed = TRUE;
gnome_property_box_changed(options_dialog);
pbox = gtk_widget_get_toplevel(GTK_WIDGET(button));
gnome_property_box_changed(GNOME_PROPERTY_BOX(pbox));
}
static GtkWidget *
@ -155,19 +192,85 @@ gnc_option_create_default_button(GNCOption *option)
static void
gnc_option_toggled_cb(GtkToggleButton *button, gpointer data)
{
GtkWidget *pbox;
GNCOption *option = data;
option->changed = TRUE;
gnome_property_box_changed(options_dialog);
pbox = gtk_widget_get_toplevel(GTK_WIDGET(button));
gnome_property_box_changed(GNOME_PROPERTY_BOX(pbox));
}
static void
gnc_option_changed_cb(GtkEditable *editable, gpointer data)
{
GtkWidget *pbox;
GNCOption *option = data;
option->changed = TRUE;
gnome_property_box_changed(options_dialog);
pbox = gtk_widget_get_toplevel(GTK_WIDGET(editable));
gnome_property_box_changed(GNOME_PROPERTY_BOX(pbox));
}
static void
gnc_option_multichoice_cb(GtkWidget *w, gint index, gpointer data)
{
GtkWidget *pbox, *omenu;
GNCOption *option = data;
gpointer _current;
gint current;
_current = gtk_object_get_data(GTK_OBJECT(option->widget),
"gnc_multichoice_index");
current = GPOINTER_TO_INT(_current);
if (current == index)
return;
gtk_option_menu_set_history(GTK_OPTION_MENU(option->widget), index);
gtk_object_set_data(GTK_OBJECT(option->widget), "gnc_multichoice_index",
GINT_TO_POINTER(index));
option->changed = TRUE;
omenu = gtk_object_get_data(GTK_OBJECT(w), "gnc_option_menu");
pbox = gtk_widget_get_toplevel(omenu);
gnome_property_box_changed(GNOME_PROPERTY_BOX(pbox));
}
static GtkWidget *
gnc_option_create_multichoice_widget(GNCOption *option)
{
GtkWidget *widget;
GNCOptionInfo *info;
int num_values;
int i;
num_values = gnc_option_value_num_permissible_values(option);
g_return_val_if_fail(num_values >= 0, NULL);
info = g_new0(GNCOptionInfo, num_values);
for (i = 0; i < num_values; i++)
{
info[i].name = gnc_option_value_permissible_value_name(option, i);
info[i].tip = gnc_option_value_permissible_value_description(option, i);
info[i].callback = gnc_option_multichoice_cb;
info[i].user_data = option;
}
widget = gnc_build_option_menu(info, num_values);
for (i = 0; i < num_values; i++)
{
free(info[i].name);
free(info[i].tip);
}
g_free(info);
return widget;
}
static GtkWidget *
@ -223,6 +326,27 @@ gnc_option_set_ui_widget(GNCOption *option)
gnc_option_create_default_button(option),
FALSE, FALSE, 0);
}
else if (safe_strcmp(type, "multichoice") == 0)
{
GtkWidget *label;
gchar *colon_name;
colon_name = g_strconcat(name, ":", NULL);
label= gtk_label_new(colon_name);
gtk_misc_set_alignment(GTK_MISC(label), 0.95, 0.5);
g_free(colon_name);
enclosing = gtk_hbox_new(FALSE, 5);
value = gnc_option_create_multichoice_widget(option);
option->widget = value;
gnc_option_set_ui_value(option, FALSE);
gtk_box_pack_start(GTK_BOX(enclosing), label, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(enclosing), value, FALSE, FALSE, 0);
gtk_box_pack_end(GTK_BOX(enclosing),
gnc_option_create_default_button(option),
FALSE, FALSE, 0);
}
else
{
PERR("gnc_option_set_ui_widget: Unknown type. Ignoring.\n");
@ -249,7 +373,8 @@ gnc_options_dialog_add_option(GtkWidget *page, GNCOption *option)
}
static void
gnc_options_dialog_append_page(GNCOptionSection *section)
gnc_options_dialog_append_page(GnomePropertyBox *propertybox,
GNCOptionSection *section)
{
GNCOption *option;
GtkWidget *page_label;
@ -257,14 +382,14 @@ gnc_options_dialog_append_page(GNCOptionSection *section)
gint num_options;
gint i;
page_label = gtk_label_new(section->section_name);
page_label = gtk_label_new(gnc_option_section_name(section));
gtk_widget_show(page_label);
page_content_box = gtk_vbox_new(FALSE, 5);
gtk_container_set_border_width(GTK_CONTAINER(page_content_box), 5);
gtk_widget_show(page_content_box);
gnome_property_box_append_page(options_dialog, page_content_box, page_label);
gnome_property_box_append_page(propertybox, page_content_box, page_label);
num_options = gnc_option_section_num_options(section);
for (i = 0; i < num_options; i++)
@ -274,71 +399,27 @@ gnc_options_dialog_append_page(GNCOptionSection *section)
}
}
static void
build_options_dialog_contents()
/********************************************************************\
* gnc_build_options_dialog_contents *
* builds an options dialog given a property box and an options *
* database *
* *
* Args: propertybox - gnome property box to use *
* odb - option database to use *
* Return: nothing *
\********************************************************************/
void
gnc_build_options_dialog_contents(GnomePropertyBox *propertybox,
GNCOptionDB *odb)
{
GNCOptionSection *section;
gint num_sections = gnc_num_option_sections();
gint num_sections = gnc_option_db_num_sections(odb);
gint i;
for (i = 0; i < num_sections; i++)
{
section = gnc_get_option_section(i);
gnc_options_dialog_append_page(section);
section = gnc_option_db_get_section(odb, i);
gnc_options_dialog_append_page(propertybox, section);
}
}
static void
gnc_options_dialog_apply_cb(GnomePropertyBox *propertybox,
gint arg1, gpointer user_data)
{
if (arg1 == -1)
gnc_options_commit();
}
static void
gnc_options_dialog_help_cb(GnomePropertyBox *propertybox,
gint arg1, gpointer user_data)
{
gnome_ok_dialog("Help on properties");
}
/* Options dialog... this should house all of the config options */
/* like where the docs reside, and whatever else is deemed necessary */
void
gnc_show_options_dialog()
{
if (gnc_num_option_sections() == 0)
{
gnc_warning_dialog("No options!");
return;
}
if (gnc_options_dirty())
{
if (options_dialog != NULL)
gtk_widget_destroy(GTK_WIDGET(options_dialog));
options_dialog = NULL;
}
if (options_dialog == NULL)
{
options_dialog = GNOME_PROPERTY_BOX(gnome_property_box_new());
gnome_dialog_close_hides(GNOME_DIALOG(options_dialog), TRUE);
build_options_dialog_contents();
gnc_options_clean();
gtk_signal_connect(GTK_OBJECT(options_dialog), "apply",
GTK_SIGNAL_FUNC(gnc_options_dialog_apply_cb),
NULL);
gtk_signal_connect(GTK_OBJECT(options_dialog), "help",
GTK_SIGNAL_FUNC(gnc_options_dialog_help_cb),
NULL);
}
gtk_widget_show(GTK_WIDGET(options_dialog));
gdk_window_raise(GTK_WIDGET(options_dialog)->window);
}

View File

@ -26,13 +26,11 @@
#include "option-util.h"
void gnc_show_options_dialog();
void gnc_build_options_dialog_contents(GnomePropertyBox *propertybox,
GNCOptionDB *odb);
SCM gnc_option_get_ui_value(GNCOption *option);
/* private */
void _gnc_option_refresh_ui(SCM option);
void gnc_option_set_ui_value(GNCOption *option, gboolean use_default);
#endif /* __OPTIONS_DIALOG_H__ */

View File

@ -617,6 +617,34 @@ gnc_ui_get_account_full_balance(Account *account)
}
char * gnc_ui_get_account_full_name(Account *account, const char *separator)
{
Account *a;
gchar **names, *name;
gint num = 0;
assert(account != NULL);
/* Figure out how many names */
for (a = account; a != NULL; a = xaccAccountGetParentAccount(a))
num++;
/* allocate the memory, plus one for null termination */
names = g_new0(gchar *, num + 1);
/* Make the names array */
for (a = account, num--; a != NULL;
a = xaccAccountGetParentAccount(a), num--)
names[num] = xaccAccountGetName(a);
name = g_strjoinv(separator, names);
g_free(names);
return name;
}
char * gnc_ui_get_account_field_value_string(Account *account, int field)
{
assert(account != NULL);
@ -667,6 +695,22 @@ void gnc_set_tooltip(GtkWidget *w, const gchar *tip)
}
static void
gnc_option_menu_cb(GtkWidget *w, gpointer data)
{
GNCOptionCallback cb;
gpointer _index;
gint index;
cb = gtk_object_get_data(GTK_OBJECT(w), "gnc_option_cb");
_index = gtk_object_get_data(GTK_OBJECT(w), "gnc_option_index");
index = GPOINTER_TO_INT(_index);
cb(w, index, data);
}
/********************************************************************\
* gnc_ui_create_option_button *
* create an option button given the option structure *
@ -692,10 +736,24 @@ gnc_build_option_menu(GNCOptionInfo *option_info, gint num_options)
for (i = 0; i < num_options; i++)
{
menu_item = gtk_menu_item_new_with_label(option_info[i].name);
gnc_set_tooltip(menu_item, option_info[i].tip);
gtk_widget_show(menu_item);
gtk_object_set_data(GTK_OBJECT(menu_item),
"gnc_option_cb",
option_info[i].callback);
gtk_object_set_data(GTK_OBJECT(menu_item),
"gnc_option_index",
GINT_TO_POINTER(i));
gtk_object_set_data(GTK_OBJECT(menu_item),
"gnc_option_menu",
omenu);
if (option_info[i].callback != NULL)
gtk_signal_connect(GTK_OBJECT(menu_item), "activate",
GTK_SIGNAL_FUNC(option_info[i].callback),
GTK_SIGNAL_FUNC(gnc_option_menu_cb),
option_info[i].user_data);
gtk_menu_append(GTK_MENU(menu), menu_item);

View File

@ -82,12 +82,17 @@ enum
};
/* option button callback function */
typedef void (*GNCOptionCallback) (GtkWidget *, gint index,
gpointer user_data);
/* Structure for building option buttons */
typedef struct _GNCOptionInfo GNCOptionInfo;
struct _GNCOptionInfo
{
char *name;
gpointer callback;
char *tip;
GNCOptionCallback callback;
gpointer user_data;
};
@ -95,6 +100,8 @@ struct _GNCOptionInfo
/**** PROTOTYPES *************************************************/
char * gnc_ui_get_account_field_name(int field);
char * gnc_ui_get_account_full_name(Account *account, const char *separator);
char * gnc_ui_get_account_field_value_string(Account *account, int field);
double gnc_ui_get_account_full_balance(Account *account);

273
src/gnome/global-options.c Normal file
View File

@ -0,0 +1,273 @@
/********************************************************************\
* global-options.c -- GNOME global option handling *
* Copyright (C) 1998,1999 Linas Vepstas *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
\********************************************************************/
#include <gnome.h>
#include "global-options.h"
#include "dialog-options.h"
#include "dialog-utils.h"
#include "option-util.h"
#include "query-user.h"
#include "guile-util.h"
#include "util.h"
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_GUI;
static GNCOptionDB *global_options = NULL;
static SCM guile_global_options = SCM_UNDEFINED;
/********************************************************************\
* gnc_options_init *
* initialize the options structures from the guile side *
* *
* Args: none *
* Returns: nothing *
\********************************************************************/
void
gnc_options_init()
{
SCM func = gh_eval_str("gnc:send-global-options");
if (gh_procedure_p(func))
gh_call0(func);
else
{
PERR("gnc_options_init: no guile options!");
}
global_options = gnc_option_db_new();
gnc_option_db_init(global_options, guile_global_options);
}
/********************************************************************\
* gnc_options_shutdown *
* unregister the scheme options and free the structure memory *
* *
* Args: none *
* Returns: nothing *
\********************************************************************/
void
gnc_options_shutdown()
{
gnc_option_db_destroy(global_options);
global_options = NULL;
gnc_unregister_c_side_scheme_ptr(guile_global_options);
guile_global_options = SCM_UNDEFINED;
}
/********************************************************************\
* gnc_register_option_change_callback *
* register a callback to be called whenever an option changes *
* this is rather heavy-weight, since all handlers will be called *
* whenever any option changes. We may need to refine it later. *
* *
* Args: callback - the callback function *
* Returns: nothing *
\********************************************************************/
void
gnc_register_option_change_callback(OptionChangeCallback callback,
gpointer user_data)
{
gnc_option_db_register_change_callback(global_options, callback, user_data);
}
/********************************************************************\
* gnc_get_option_by_name *
* returns an option given section name and name *
* *
* Args: section_name - name of section to search for *
* name - name to search for *
* Returns: given option, or NULL if none *
\********************************************************************/
GNCOption *
gnc_get_option_by_name(char *section_name, char *name)
{
return gnc_option_db_get_option_by_name(global_options,
section_name, name);
}
/********************************************************************\
* gnc_get_option_by_SCM *
* returns an option given SCM handle. Uses section and name. *
* *
* Args: guile_option - SCM handle of option *
* Returns: given option, or NULL if none *
\********************************************************************/
GNCOption *
gnc_get_option_by_SCM(SCM guile_option)
{
return gnc_option_db_get_option_by_SCM(global_options, guile_option);
}
/********************************************************************\
* gnc_lookup_boolean_option *
* looks up a boolean option. If present, returns its value, *
* otherwise returns the default. *
* *
* Args: section - section name of option *
* name - name of option *
* default - default value if not found *
* Return: gboolean option value *
\********************************************************************/
gboolean
gnc_lookup_boolean_option(char *section, char *name, gboolean default_value)
{
return gnc_option_db_lookup_boolean_option(global_options, section,
name, default_value);
}
/********************************************************************\
* gnc_lookup_string_option *
* looks up a string option. If present, returns its malloc'ed *
* value, otherwise returns the strdup'ed default, or NULL if *
* default was NULL. *
* *
* Args: section - section name of option *
* name - name of option *
* default - default value if not found *
* Return: char * option value *
\********************************************************************/
char *
gnc_lookup_string_option(char *section, char *name, char *default_value)
{
return gnc_option_db_lookup_string_option(global_options, section,
name, default_value);
}
/********************************************************************\
* gnc_lookup_multichoice_option *
* looks up a multichoice option. If present, returns its *
* name as a malloc'ed string *
* value, otherwise returns the strdup'ed default, or NULL if *
* default was NULL. *
* *
* Args: section - section name of option *
* name - name of option *
* default - default value if not found *
* Return: char * option value *
\********************************************************************/
char *
gnc_lookup_multichoice_option(char *section, char *name, char *default_value)
{
return gnc_option_db_lookup_multichoice_option(global_options, section,
name, default_value);
}
/********************************************************************\
* _gnc_option_refresh_ui *
* sets the GUI representation of an option with its current *
* current guile value. this is intended for use by guile only *
* *
* Args: option - SCM handle to option *
* Return: nothing *
\********************************************************************/
void
_gnc_option_refresh_ui(SCM guile_option)
{
GNCOption *option;
option = gnc_option_db_get_option_by_SCM(global_options, guile_option);
gnc_option_set_ui_value(option, FALSE);
}
/********************************************************************\
* _gnc_register_global_options *
* registers the global options. Intended to be called from guile.*
* *
* Args: options - the guile global options *
* Returns: nothing *
\********************************************************************/
void
_gnc_register_global_options(SCM options)
{
guile_global_options = options;
gnc_register_c_side_scheme_ptr(options);
}
static void
gnc_options_dialog_apply_cb(GnomePropertyBox *propertybox,
gint arg1, gpointer user_data)
{
if (arg1 == -1)
gnc_option_db_commit(global_options);
}
static void
gnc_options_dialog_help_cb(GnomePropertyBox *propertybox,
gint arg1, gpointer user_data)
{
gnome_ok_dialog("Help on properties");
}
/* Options dialog... this should house all of the config options */
/* like where the docs reside, and whatever else is deemed necessary */
void
gnc_show_options_dialog()
{
static GnomePropertyBox *options_dialog = NULL;
if (gnc_option_db_num_sections(global_options) == 0)
{
gnc_warning_dialog("No options!");
return;
}
if (gnc_option_db_dirty(global_options))
{
if (options_dialog != NULL)
gtk_widget_destroy(GTK_WIDGET(options_dialog));
options_dialog = NULL;
}
if (options_dialog == NULL)
{
options_dialog = GNOME_PROPERTY_BOX(gnome_property_box_new());
gnome_dialog_close_hides(GNOME_DIALOG(options_dialog), TRUE);
gnc_build_options_dialog_contents(options_dialog, global_options);
gnc_option_db_clean(global_options);
gtk_window_set_title(GTK_WINDOW(options_dialog), "GnuCash Preferences");
gtk_signal_connect(GTK_OBJECT(options_dialog), "apply",
GTK_SIGNAL_FUNC(gnc_options_dialog_apply_cb),
NULL);
gtk_signal_connect(GTK_OBJECT(options_dialog), "help",
GTK_SIGNAL_FUNC(gnc_options_dialog_help_cb),
NULL);
}
gtk_widget_show(GTK_WIDGET(options_dialog));
gdk_window_raise(GTK_WIDGET(options_dialog)->window);
}

View File

@ -0,0 +1,53 @@
/********************************************************************\
* global-options.h -- GNOME global option handling *
* Copyright (C) 1998,1999 Linas Vepstas *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
\********************************************************************/
#ifndef __GLOBAL_OPTIONS_H__
#define __GLOBAL_OPTIONS_H__
#include <gnome.h>
#include "option-util.h"
void gnc_options_init();
void gnc_options_shutdown();
void gnc_show_options_dialog();
void gnc_register_option_change_callback(OptionChangeCallback callback,
gpointer user_data);
GNCOption * gnc_get_option_by_name(char *section_name, char *name);
GNCOption * gnc_get_option_by_SCM(SCM guile_option);
gboolean gnc_lookup_boolean_option(char *section, char *name,
gboolean default_value);
char * gnc_lookup_string_option(char *section, char *name,
char *default_value);
char * gnc_lookup_multichoice_option(char *section, char *name,
char *default_value);
/* private */
void _gnc_option_refresh_ui(SCM option);
void _gnc_register_global_options(SCM options);
#endif /* __GLOBAL_OPTIONS_H__ */

View File

@ -28,6 +28,29 @@
/****** Structures *************************************************/
struct _GNCOptionSection
{
char * section_name;
GSList * options;
};
struct _GNCOptionDB
{
GSList *option_sections;
gboolean options_dirty;
GSList *change_callbacks;
};
typedef struct _ChangeCBInfo ChangeCBInfo;
struct _ChangeCBInfo
{
OptionChangeCallback callback;
gpointer user_data;
};
typedef struct _Getters Getters;
struct _Getters
{
@ -40,65 +63,114 @@ struct _Getters
SCM setter;
SCM default_getter;
SCM value_validator;
SCM permissible_values;
};
/****** Globals ****************************************************/
static Getters getters = {0, 0, 0, 0, 0, 0, 0, 0, 0};
static GSList *option_sections = NULL;
static gboolean options_dirty = FALSE;
static GSList *change_callbacks = NULL;
static Getters getters = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/* This static indicates the debugging module this .o belongs to. */
static short module = MOD_GUI;
static GPtrArray *option_dbs = NULL;
/*******************************************************************/
static GNCOptionDBHandle
gnc_option_db_get_handle(GNCOptionDB *odb)
{
int i;
for (i = 0; i < option_dbs->len; i++)
if (odb == g_ptr_array_index(option_dbs, i))
return i;
return -1;
}
/********************************************************************\
* gnc_options_init *
* gnc_option_db_new *
* allocate a new option database and initialize its values *
* *
* Args: none *
* Returns: a new option database *
\********************************************************************/
GNCOptionDB *
gnc_option_db_new()
{
GNCOptionDB *odb;
odb = g_new0(GNCOptionDB, 1);
odb->option_sections = NULL;
odb->options_dirty = FALSE;
odb->change_callbacks = NULL;
if (option_dbs == NULL)
option_dbs = g_ptr_array_new();
g_ptr_array_add(option_dbs, odb);
return odb;
}
/********************************************************************\
* gnc_option_db_init *
* initialize the options structures from the guile side *
* *
* Args: none *
* Args: odb - the option database to initialize *
* options - the guile options to initialize with *
* Returns: nothing *
\********************************************************************/
void
gnc_options_init()
gnc_option_db_init(GNCOptionDB *odb, SCM options)
{
SCM func = gh_eval_str("gnc:send-ui-options");
SCM func = gh_eval_str("gnc:send-options");
GNCOptionDBHandle handle;
option_sections = NULL;
options_dirty = FALSE;
change_callbacks = NULL;
if (gh_procedure_p(func))
gh_call0(func);
else
handle = gnc_option_db_get_handle(odb);
if (handle < 0)
{
PERR("gnc_options_init: no guile options!");
}
PERR("Can't find option database in list.\n");
return;
}
gh_call2(func, gh_int2scm(handle), options);
}
static void
gnc_option_free_cbinfo(gpointer data, gpointer not_used)
{
g_free(data);
}
/********************************************************************\
* gnc_options_shutdown *
* unregister the scheme options and free the structure memory *
* gnc_option_db_destroy *
* unregister the scheme options and free all the memory *
* associated with an option database, including the database *
* itself *
* *
* Args: none *
* Args: options database to destroy *
* Returns: nothing *
\********************************************************************/
void
gnc_options_shutdown()
gnc_option_db_destroy(GNCOptionDB *odb)
{
GSList *section_node;
GSList *option_node;
GNCOptionSection *section;
GNCOption *option;
section_node = option_sections;
if (odb == NULL)
return;
section_node = odb->option_sections;
while (section_node != NULL)
{
section = section_node->data;
@ -121,44 +193,73 @@ gnc_options_shutdown()
section_node = section_node->next;
}
if (option_sections != NULL)
g_slist_free(section->options);
g_slist_foreach(odb->change_callbacks, gnc_option_free_cbinfo, NULL);
option_sections = NULL;
options_dirty = FALSE;
g_slist_free(odb->option_sections);
g_slist_free(odb->change_callbacks);
odb->option_sections = NULL;
odb->change_callbacks = NULL;
odb->options_dirty = FALSE;
if (!g_ptr_array_remove(option_dbs, odb))
{
PERR("Option database not present in list.\n");
}
if (option_dbs->len == 0)
{
g_ptr_array_free(option_dbs, FALSE);
option_dbs = NULL;
}
g_free(odb);
}
/********************************************************************\
* gnc_register_option_change_callback *
* gnc_option_db_register_change_callback *
* register a callback to be called whenever an option changes *
* this is rather heavy-weight, since all handlers will be called *
* whenever any option changes. We may need to refine it later. *
* *
* Args: callback - the callback function *
* Args: odb - the option database to register with *
* callback - the callback function to register *
* Returns: nothing *
\********************************************************************/
void
gnc_register_option_change_callback(void (*callback) (void))
gnc_option_db_register_change_callback(GNCOptionDB *odb,
OptionChangeCallback callback,
gpointer data)
{
change_callbacks = g_slist_prepend(change_callbacks, callback);
ChangeCBInfo *cbinfo;
assert(odb != NULL);
cbinfo = g_new0(ChangeCBInfo, 1);
cbinfo->callback = callback;
cbinfo->user_data = data;
odb->change_callbacks = g_slist_prepend(odb->change_callbacks, cbinfo);
}
static void
gnc_call_option_change_callbacks()
gnc_call_option_change_callbacks(GNCOptionDB *odb)
{
GSList *node = change_callbacks;
void (*callback) (void);
GSList *node = odb->change_callbacks;
ChangeCBInfo *cbinfo;
while (node != NULL)
{
callback = node->data;
(*callback)();
cbinfo = node->data;
(cbinfo->callback)(cbinfo->user_data);
node = node->next;
}
}
static void
initialize_getters()
{
@ -167,18 +268,20 @@ initialize_getters()
if (getters_initialized)
return;
getters.section = gh_eval_str("gnc:configuration-option-section");
getters.name = gh_eval_str("gnc:configuration-option-name");
getters.type = gh_eval_str("gnc:configuration-option-type");
getters.sort_tag = gh_eval_str("gnc:configuration-option-sort-tag");
getters.section = gh_eval_str("gnc:option-section");
getters.name = gh_eval_str("gnc:option-name");
getters.type = gh_eval_str("gnc:option-type");
getters.sort_tag = gh_eval_str("gnc:option-sort-tag");
getters.documentation =
gh_eval_str("gnc:configuration-option-documentation");
getters.getter = gh_eval_str("gnc:configuration-option-getter");
getters.setter = gh_eval_str("gnc:configuration-option-setter");
gh_eval_str("gnc:option-documentation");
getters.getter = gh_eval_str("gnc:option-getter");
getters.setter = gh_eval_str("gnc:option-setter");
getters.default_getter =
gh_eval_str("gnc:configuration-option-default-getter");
gh_eval_str("gnc:option-default-getter");
getters.value_validator =
gh_eval_str("gnc:configuration-option-value-validator");
gh_eval_str("gnc:option-value-validator");
getters.permissible_values =
gh_eval_str("gnc:option-permissible-values");
getters_initialized = TRUE;
}
@ -343,6 +446,198 @@ gnc_option_value_validator(GNCOption *option)
}
/********************************************************************\
* gnc_option_value_num_permissible_values *
* returns the number of permissible values in the option, or *
* -1 if there are no values available. *
* *
* Args: option - the GNCOption *
* Returns: number of permissible options or -1 *
\********************************************************************/
int
gnc_option_value_num_permissible_values(GNCOption *option)
{
SCM values;
initialize_getters();
values = gnc_guile_call1_to_list(getters.permissible_values,
option->guile_option);
if (values == SCM_UNDEFINED)
return -1;
return gh_length(values);
}
/********************************************************************\
* gnc_option_value_permissible_value_index *
* returns the index of the permissible value matching the *
* provided value, or -1 if it couldn't be found *
* *
* Args: option - the GNCOption *
* value - the SCM handle of the value *
* Returns: index of permissible value, or -1 *
\********************************************************************/
int
gnc_option_value_permissible_value_index(GNCOption *option, SCM search_value)
{
SCM values, vector, value;
int num_values, i;
if (!gh_symbol_p(search_value))
return -1;
initialize_getters();
values = gnc_guile_call1_to_list(getters.permissible_values,
option->guile_option);
if (values == SCM_UNDEFINED)
return -1;
num_values = gh_length(values);
for (i = 0; i < num_values; i++)
{
vector = gh_list_ref(values, gh_int2scm(i));
if (!gh_vector_p(vector))
continue;
value = gh_vector_ref(vector, gh_int2scm(0));
if (gh_eq_p(value, search_value))
return i;
}
return -1;
}
/********************************************************************\
* gnc_option_value_permissible_value *
* returns the SCM handle to the indexth permissible value in the *
* option, or SCM_UNDEFINED if the index was out of range or *
* there was some other problem. *
* *
* Args: option - the GNCOption *
* index - the index of the permissible value *
* Returns: SCM handle to option value or SCM_UNDEFINED *
\********************************************************************/
SCM
gnc_option_value_permissible_value(GNCOption *option, int index)
{
SCM values, vector, value;
if (index < 0)
return SCM_UNDEFINED;
initialize_getters();
values = gnc_guile_call1_to_list(getters.permissible_values,
option->guile_option);
if (values == SCM_UNDEFINED)
return SCM_UNDEFINED;
if (index >= gh_length(values))
return SCM_UNDEFINED;
vector = gh_list_ref(values, gh_int2scm(index));
if (!gh_vector_p(vector))
return SCM_UNDEFINED;
value = gh_vector_ref(vector, gh_int2scm(0));
if (!gh_symbol_p(value))
return SCM_UNDEFINED;
return value;
}
/********************************************************************\
* gnc_option_value_permissible_value_name *
* returns the malloc'd name of the indexth permissible value in *
* the option, or NULL if the index was out of range or there are *
* no values available. *
* *
* Args: option - the GNCOption *
* index - the index of the permissible value *
* Returns: malloc'd name of permissible value or NULL *
\********************************************************************/
char *
gnc_option_value_permissible_value_name(GNCOption *option, int index)
{
SCM values, vector, name;
if (index < 0)
return NULL;
initialize_getters();
values = gnc_guile_call1_to_list(getters.permissible_values,
option->guile_option);
if (values == SCM_UNDEFINED)
return NULL;
if (index >= gh_length(values))
return NULL;
vector = gh_list_ref(values, gh_int2scm(index));
if (!gh_vector_p(vector))
return NULL;
name = gh_vector_ref(vector, gh_int2scm(1));
if (!gh_string_p(name))
return NULL;
return gh_scm2newstr(name, NULL);
}
/********************************************************************\
* gnc_option_value_permissible_value_description *
* returns the malloc'd description of the indexth permissible *
* value in the option, or NULL if the index was out of range or *
* there are no values available. *
* *
* Args: option - the GNCOption *
* index - the index of the permissible value *
* Returns: malloc'd description of permissible value or NULL *
\********************************************************************/
char *
gnc_option_value_permissible_value_description(GNCOption *option, int index)
{
SCM values, vector, help;
if (index < 0)
return NULL;
initialize_getters();
values = gnc_guile_call1_to_list(getters.permissible_values,
option->guile_option);
if (values == SCM_UNDEFINED)
return NULL;
if (index >= gh_length(values))
return NULL;
vector = gh_list_ref(values, gh_int2scm(index));
if (!gh_vector_p(vector))
return NULL;
help = gh_vector_ref(vector, gh_int2scm(2));
if (!gh_string_p(help))
return NULL;
return gh_scm2newstr(help, NULL);
}
static gint
compare_sections(gconstpointer a, gconstpointer b)
{
@ -394,47 +689,58 @@ compare_option_names(gconstpointer a, gconstpointer b)
}
#endif
/********************************************************************\
* gnc_options_dirty *
* returns true if guile has registered more options since the *
* options dialog was created. *
* gnc_option_db_dirty *
* returns true if guile has registered more options into the *
* database since the last time the database was cleaned. *
* *
* Returns: dirty flag *
\********************************************************************/
gboolean
gnc_options_dirty()
gnc_option_db_dirty(GNCOptionDB *odb)
{
return options_dirty;
assert(odb != NULL);
return odb->options_dirty;
}
/********************************************************************\
* gnc_options_clean *
* resets the dirty flag *
* gnc_option_db_clean *
* resets the dirty flag of the option database *
* *
\********************************************************************/
void
gnc_options_clean()
gnc_option_db_clean(GNCOptionDB *odb)
{
options_dirty = FALSE;
assert(odb != NULL);
odb->options_dirty = FALSE;
}
/********************************************************************\
* gnc_register_option_ui *
* registers an option with the GUI. Intended to be called from *
* guile. *
* _gnc_option_db_register_option *
* registers an option with an option database. Intended to be *
* called from guile. *
* *
* Args: option - the guile option *
* Args: odb - the option database *
* option - the guile option *
* Returns: nothing *
\********************************************************************/
void
_gnc_register_option_ui(SCM guile_option)
_gnc_option_db_register_option(GNCOptionDBHandle handle, SCM guile_option)
{
GNCOptionDB *odb;
GNCOption *option;
GNCOptionSection *section;
options_dirty = TRUE;
odb = g_ptr_array_index(option_dbs, handle);
assert(odb != NULL);
odb->options_dirty = TRUE;
/* Make the option structure */
option = g_new0(GNCOption, 1);
@ -447,7 +753,7 @@ _gnc_register_option_ui(SCM guile_option)
if (option->guile_option_id == SCM_UNDEFINED)
{
g_free(option);
PERR("_gnc_register_option_ui: couldn't register\n");
PERR("_gnc_option_db_register_option: couldn't register\n");
return;
}
@ -460,7 +766,7 @@ _gnc_register_option_ui(SCM guile_option)
{
GSList *old;
old = g_slist_find_custom(option_sections, section, compare_sections);
old = g_slist_find_custom(odb->option_sections, section, compare_sections);
if (old != NULL)
{
@ -470,8 +776,8 @@ _gnc_register_option_ui(SCM guile_option)
section = old->data;
}
else
option_sections = g_slist_insert_sorted(option_sections, section,
compare_sections);
odb->option_sections = g_slist_insert_sorted(odb->option_sections,
section, compare_sections);
}
section->options = g_slist_insert_sorted(section->options, option,
@ -480,30 +786,46 @@ _gnc_register_option_ui(SCM guile_option)
/********************************************************************\
* gnc_num_options_sections *
* returns the number of option sections registered so far *
* gnc_option_db_num_sections *
* returns the number of option sections registered so far in the *
* database *
* *
* Args: none *
* Args: odb - the database to count sections for *
* Returns: number of option sections *
\********************************************************************/
guint
gnc_num_option_sections()
gnc_option_db_num_sections(GNCOptionDB *odb)
{
return g_slist_length(option_sections);
return g_slist_length(odb->option_sections);
}
/********************************************************************\
* gnc_get_option_section *
* returns the ith option section, or NULL *
* gnc_option_db_get_section *
* returns the ith option section in the database, or NULL *
* *
* Args: i - index of section *
* Args: odb - the option database *
* i - index of section *
* Returns: ith option sectioin *
\********************************************************************/
GNCOptionSection *
gnc_get_option_section(gint i)
gnc_option_db_get_section(GNCOptionDB *odb, gint i)
{
return g_slist_nth_data(option_sections, i);
return g_slist_nth_data(odb->option_sections, i);
}
/********************************************************************\
* gnc_option_section_name *
* returns the name of the options section *
* *
* Args: section - section to get name of *
* Returns: name of option section *
\********************************************************************/
char *
gnc_option_section_name(GNCOptionSection *section)
{
return section->section_name;
}
@ -530,6 +852,7 @@ gnc_option_section_num_options(GNCOptionSection *section)
* Returns: ith option in section *
\********************************************************************/
GNCOption *
gnc_get_option_section_option(GNCOptionSection *section, int i)
{
return g_slist_nth_data(section->options, i);
@ -537,15 +860,17 @@ gnc_get_option_section_option(GNCOptionSection *section, int i)
/********************************************************************\
* gnc_get_option_by_name *
* gnc_option_db_get_option_by_name *
* returns an option given section name and name *
* *
* Args: section_name - name of section to search for *
* Args: odb - option database to search in *
* section_name - name of section to search for *
* name - name to search for *
* Returns: given option, or NULL if none *
\********************************************************************/
GNCOption *
gnc_get_option_by_name(char *section_name, char *name)
gnc_option_db_get_option_by_name(GNCOptionDB *odb, char *section_name,
char *name)
{
GSList *section_node;
GSList *option_node;
@ -557,7 +882,7 @@ gnc_get_option_by_name(char *section_name, char *name)
section_key.section_name = section_name;
section_node = g_slist_find_custom(option_sections, &section_key,
section_node = g_slist_find_custom(odb->option_sections, &section_key,
compare_sections);
if (section_node == NULL)
@ -585,14 +910,15 @@ gnc_get_option_by_name(char *section_name, char *name)
/********************************************************************\
* gnc_get_option_by_SCM *
* gnc_option_db_get_option_by_SCM *
* returns an option given SCM handle. Uses section and name. *
* *
* Args: guile_option - SCM handle of option *
* Args: odb - option database to search in *
* guile_option - SCM handle of option *
* Returns: given option, or NULL if none *
\********************************************************************/
GNCOption *
gnc_get_option_by_SCM(SCM guile_option)
gnc_option_db_get_option_by_SCM(GNCOptionDB *odb, SCM guile_option)
{
GNCOption option_key;
GNCOption *option;
@ -604,7 +930,7 @@ gnc_get_option_by_SCM(SCM guile_option)
section_name = gnc_option_section(&option_key);
name = gnc_option_name(&option_key);
option = gnc_get_option_by_name(section_name, name);
option = gnc_option_db_get_option_by_name(odb, section_name, name);
if (section_name != NULL)
free(section_name);
@ -684,15 +1010,15 @@ gnc_commit_option(GNCOption *option)
/********************************************************************\
* gnc_options_commit *
* gnc_option_db_commit *
* commits the options which have changed, and which are valid *
* for those which are not valid, error dialogs are shown. *
* *
* Args: none *
* Args: odb - option database to commit *
* Return: nothing *
\********************************************************************/
void
gnc_options_commit()
gnc_option_db_commit(GNCOptionDB *odb)
{
GSList *section_node;
GSList *option_node;
@ -700,7 +1026,9 @@ gnc_options_commit()
GNCOption *option;
gboolean changed_something = FALSE;
section_node = option_sections;
assert(odb != NULL);
section_node = odb->option_sections;
while (section_node != NULL)
{
section = section_node->data;
@ -724,28 +1052,30 @@ gnc_options_commit()
}
if (changed_something)
gnc_call_option_change_callbacks();
gnc_call_option_change_callbacks(odb);
}
/********************************************************************\
* gnc_lookup_boolean_option *
* gnc_option_db_lookup_boolean_option *
* looks up a boolean option. If present, returns its value, *
* otherwise returns the default. *
* *
* Args: section - section name of option *
* Args: odb - option database to search in *
* section - section name of option *
* name - name of option *
* default - default value if not found *
* Return: gboolean option value *
\********************************************************************/
gboolean
gnc_lookup_boolean_option(char *section, char *name, gboolean default_value)
gnc_option_db_lookup_boolean_option(GNCOptionDB *odb, char *section,
char *name, gboolean default_value)
{
GNCOption *option;
SCM getter;
SCM value;
option = gnc_get_option_by_name(section, name);
option = gnc_option_db_get_option_by_name(odb, section, name);
if (option == NULL)
return default_value;
@ -764,24 +1094,26 @@ gnc_lookup_boolean_option(char *section, char *name, gboolean default_value)
/********************************************************************\
* gnc_lookup_string_option *
* gnc_option_db_lookup_string_option *
* looks up a string option. If present, returns its malloc'ed *
* value, otherwise returns the strdup'ed default, or NULL if *
* default was NULL. *
* *
* Args: section - section name of option *
* Args: odb - option database to search in *
* section - section name of option *
* name - name of option *
* default - default value if not found *
* Return: char * option value *
\********************************************************************/
char *
gnc_lookup_string_option(char *section, char *name, char *default_value)
gnc_option_db_lookup_string_option(GNCOptionDB *odb, char *section,
char *name, char *default_value)
{
GNCOption *option;
SCM getter;
SCM value;
option = gnc_get_option_by_name(section, name);
option = gnc_option_db_get_option_by_name(odb, section, name);
if (option != NULL)
{
@ -799,3 +1131,44 @@ gnc_lookup_string_option(char *section, char *name, char *default_value)
return strdup(default_value);
}
/********************************************************************\
* gnc_option_db_lookup_multichoice_option *
* looks up a multichoice option. If present, returns its *
* name as a malloc'ed string *
* value, otherwise returns the strdup'ed default, or NULL if *
* default was NULL. *
* *
* Args: odb - option database to search in *
* section - section name of option *
* name - name of option *
* default - default value if not found *
* Return: char * option value *
\********************************************************************/
char *
gnc_option_db_lookup_multichoice_option(GNCOptionDB *odb, char *section,
char *name, char *default_value)
{
GNCOption *option;
SCM getter;
SCM value;
option = gnc_option_db_get_option_by_name(odb, section, name);
if (option != NULL)
{
getter = gnc_option_getter(option);
if (getter != SCM_UNDEFINED)
{
value = gh_call0(getter);
if (gh_symbol_p(value))
return gh_symbol2newstr(value, NULL);
}
}
if (default_value == NULL)
return NULL;
return strdup(default_value);
}

View File

@ -23,7 +23,6 @@
#include <gnome.h>
#include <guile/gh.h>
typedef struct _GNCOption GNCOption;
struct _GNCOption
{
@ -41,20 +40,21 @@ struct _GNCOption
};
typedef struct _GNCOptionSection GNCOptionSection;
struct _GNCOptionSection
{
char * section_name;
typedef struct _GNCOptionDB GNCOptionDB;
GSList * options;
};
typedef int GNCOptionDBHandle;
typedef void (*OptionChangeCallback)(gpointer user_data);
/***** Prototypes ********************************************************/
void gnc_options_init();
void gnc_options_shutdown();
GNCOptionDB * gnc_option_db_new();
void gnc_option_db_init(GNCOptionDB *odb, SCM options);
void gnc_option_db_destroy(GNCOptionDB *odb);
void gnc_register_option_change_callback(void (*callback) (void));
void gnc_option_db_register_change_callback(GNCOptionDB *odb,
OptionChangeCallback callback,
gpointer data);
char * gnc_option_section(GNCOption *option);
char * gnc_option_name(GNCOption *option);
@ -65,30 +65,48 @@ SCM gnc_option_getter(GNCOption *option);
SCM gnc_option_setter(GNCOption *option);
SCM gnc_option_default_getter(GNCOption *option);
SCM gnc_option_value_validator(GNCOption *option);
int gnc_option_value_num_permissible_values(GNCOption *option);
int gnc_option_value_permissible_value_index(GNCOption *option, SCM value);
SCM gnc_option_value_permissible_value(GNCOption *option, int index);
char * gnc_option_value_permissible_value_name(GNCOption *option, int index);
char * gnc_option_value_permissible_value_description(GNCOption *option,
int index);
guint gnc_num_option_sections();
guint gnc_option_db_num_sections(GNCOptionDB *odb);
char * gnc_option_section_name(GNCOptionSection *section);
guint gnc_option_section_num_options(GNCOptionSection *section);
GNCOptionSection * gnc_get_option_section(gint i);
GNCOptionSection * gnc_option_db_get_section(GNCOptionDB *odb, gint i);
GNCOption * gnc_get_option_section_option(GNCOptionSection *section, int i);
GNCOption * gnc_get_option_by_name(char *section_name, char *name);
GNCOption * gnc_get_option_by_SCM(SCM guile_option);
gboolean gnc_options_dirty();
void gnc_options_clean();
GNCOption * gnc_option_db_get_option_by_name(GNCOptionDB *odb,
char *section_name, char *name);
void gnc_options_commit();
GNCOption * gnc_option_db_get_option_by_SCM(GNCOptionDB *odb,
SCM guile_option);
gboolean gnc_lookup_boolean_option(char *section, char *name,
gboolean gnc_option_db_dirty(GNCOptionDB *odb);
void gnc_option_db_clean(GNCOptionDB *odb);
void gnc_option_db_commit(GNCOptionDB *odb);
gboolean gnc_option_db_lookup_boolean_option(GNCOptionDB *odb,
char *section, char *name,
gboolean default_value);
char * gnc_lookup_string_option(char *section, char *name,
char *default_value);
char * gnc_option_db_lookup_string_option(GNCOptionDB *odb, char *section,
char *name, char *default_value);
char * gnc_option_db_lookup_multichoice_option(GNCOptionDB *odb,
char *section, char *name,
char *default_value);
/* private */
void _gnc_register_option_ui(SCM guile_option);
void _gnc_option_db_register_option(GNCOptionDBHandle handle,
SCM guile_option);
#endif /* __OPTION_UTIL_H__ */

View File

@ -104,6 +104,8 @@ gnc_reconcile_list_init(GNCReconcileList *list)
list->num_columns = 0;
list->reconciled = g_hash_table_new(NULL, NULL);
list->current_row = -1;
list->current_split = NULL;
list->no_toggle = FALSE;
while (titles[list->num_columns] != NULL)
list->num_columns++;
@ -138,7 +140,6 @@ gnc_reconcile_list_init(GNCReconcileList *list)
GtkStyle *style = gtk_widget_get_style(GTK_WIDGET(list));
list->reconciled_style = gtk_style_copy(style);
list->normal_style = gtk_style_copy(style);
#if !USE_NO_COLOR
style = list->reconciled_style;
@ -151,12 +152,6 @@ gnc_reconcile_list_init(GNCReconcileList *list)
gdk_colormap_alloc_color(cm, &style->fg[GTK_STATE_NORMAL], FALSE, TRUE);
style->fg[GTK_STATE_SELECTED] = style->fg[GTK_STATE_NORMAL];
style->bg[GTK_STATE_SELECTED] = style->white;
list->normal_style->fg[GTK_STATE_SELECTED] = style->black;
list->normal_style->bg[GTK_STATE_SELECTED] = style->white;
gtk_widget_set_style(GTK_WIDGET(list), list->normal_style);
#endif
}
}
@ -221,10 +216,18 @@ gnc_reconcile_list_toggle(GNCReconcileList *list)
assert(GTK_IS_GNC_RECONCILE_LIST(list));
assert(list->reconciled != NULL);
if (list->no_toggle)
return;
row = list->current_row;
split = gtk_clist_get_row_data(GTK_CLIST(list), row);
current = g_hash_table_lookup(list->reconciled, split);
if (list->current_split != split)
list->current_split = split;
else
list->current_split = NULL;
if (current == NULL)
{
reconciled = TRUE;
@ -282,12 +285,6 @@ gnc_reconcile_list_destroy(GtkObject *object)
list->reconciled_style = NULL;
}
if (list->normal_style != NULL)
{
gtk_style_unref(list->normal_style);
list->normal_style = NULL;
}
if (list->reconciled != NULL)
{
g_hash_table_destroy(list->reconciled);
@ -319,6 +316,13 @@ gnc_reconcile_list_get_num_splits(GNCReconcileList *list)
return list->num_splits;
}
Split *
gnc_reconcile_list_get_current_split(GNCReconcileList *list)
{
assert(GTK_IS_GNC_RECONCILE_LIST(list));
return list->current_split;
}
/********************************************************************\
* gnc_reconcile_list_refresh *
@ -333,6 +337,8 @@ gnc_reconcile_list_refresh(GNCReconcileList *list)
GtkCList *clist = GTK_CLIST(list);
GtkAdjustment *adjustment;
gfloat save_value = 0.0;
Split *old_split;
gint new_row;
assert(GTK_IS_GNC_RECONCILE_LIST(list));
@ -344,12 +350,13 @@ gnc_reconcile_list_refresh(GNCReconcileList *list)
gtk_clist_clear(clist);
old_split = list->current_split;
list->num_splits = 0;
list->current_row = -1;
list->current_split = NULL;
gnc_reconcile_list_fill(list);
gtk_clist_thaw(clist);
gtk_clist_columns_autosize(clist);
if (adjustment != NULL)
@ -357,6 +364,20 @@ gnc_reconcile_list_refresh(GNCReconcileList *list)
save_value = CLAMP(save_value, adjustment->lower, adjustment->upper);
gtk_adjustment_set_value(adjustment, save_value);
}
if (old_split != NULL)
{
new_row = gtk_clist_find_row_from_data(clist, old_split);
if (new_row >= 0)
{
list->no_toggle = TRUE;
gtk_clist_select_row(clist, new_row, 0);
list->no_toggle = FALSE;
list->current_split = old_split;
}
}
gtk_clist_thaw(clist);
}
@ -429,6 +450,26 @@ gnc_reconcile_list_commit(GNCReconcileList *list)
}
/********************************************************************\
* gnc_reconcile_list_unselect_all *
* unselect all splits in the list *
* *
* Args: list - list to unselect all *
* Returns: nothing *
\********************************************************************/
void
gnc_reconcile_list_unselect_all(GNCReconcileList *list)
{
GtkCList *clist = GTK_CLIST(list);
list->no_toggle = TRUE;
gtk_clist_unselect_all(clist);
list->no_toggle = FALSE;
list->current_split = NULL;
}
static void
gnc_reconcile_list_fill(GNCReconcileList *list)
{

View File

@ -51,11 +51,13 @@ struct _GNCReconcileList
gint num_columns;
gint current_row;
Split *current_split;
gboolean no_toggle;
GHashTable *reconciled;
GtkStyle *reconciled_style;
GtkStyle *normal_style;
Account *account;
};
@ -81,12 +83,16 @@ gint gnc_reconcile_list_get_row_height(GNCReconcileList *list);
gint gnc_reconcile_list_get_num_splits(GNCReconcileList *list);
Split * gnc_reconcile_list_get_current_split(GNCReconcileList *list);
void gnc_reconcile_list_refresh (GNCReconcileList *list);
double gnc_reconcile_list_reconciled_balance(GNCReconcileList *list);
void gnc_reconcile_list_commit(GNCReconcileList *list);
void gnc_reconcile_list_unselect_all(GNCReconcileList *list);
#ifdef __cplusplus
}

View File

@ -19,55 +19,105 @@
#include "scripts_menu.h"
#include <guile/gh.h>
#include "top-level.h"
#include "guile-util.h"
#include "util.h"
typedef struct _ScriptInfo ScriptInfo;
struct _ScriptInfo
{
SCM script;
GnomeUIInfo info[2];
};
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_GUI;
/* FIXME: is this always kosher? Will SCM's always fit in a gpointer? */
static GSList *script_list = NULL;
static void
gnc_extensions_menu_cb( GtkWidget *w, gpointer p)
{
SCM closure = (SCM) p;
ScriptInfo *si = (ScriptInfo *) p;
if (!p)
if (si == NULL)
return;
gh_call0(closure);
gh_call0(si->script);
}
static ScriptInfo *
gnc_extensions_create_script_info(char *name, char *hint, SCM script)
{
ScriptInfo *si;
si = g_new0(ScriptInfo, 1);
si->script = script;
gnc_register_c_side_scheme_ptr(script);
script_list = g_slist_prepend(script_list, si);
si->info[0].type = GNOME_APP_UI_ITEM;
si->info[0].label = g_strdup(name);
si->info[0].hint = g_strdup(hint);
si->info[0].moreinfo = gnc_extensions_menu_cb;
si->info[0].user_data = si;
si->info[0].unused_data = NULL;
si->info[0].pixmap_type = GNOME_APP_PIXMAP_NONE;
si->info[0].pixmap_info = NULL;
si->info[0].accelerator_key = 0;
si->info[0].ac_mods = (GdkModifierType) 0;
si->info[0].widget = NULL;
si->info[1].type = GNOME_APP_UI_ENDOFINFO;
si->info[1].label = NULL;
si->info[1].moreinfo = NULL;
return si;
}
void
gnc_extensions_menu_add_item(char name[],
char hint[],
gpointer data)
gnc_extensions_menu_add_item(char *name, char *hint, SCM script)
{
GnomeUIInfo item_info[2];
GnomeUIInfo tmpi;
ScriptInfo *si;
tmpi.type = GNOME_APP_UI_ITEM;
tmpi.label = N_(name);
tmpi.hint = N_(hint);
tmpi.moreinfo = gnc_extensions_menu_cb;
tmpi.user_data = data;
tmpi.unused_data = NULL;
tmpi.pixmap_type = GNOME_APP_PIXMAP_NONE;
tmpi.pixmap_info = NULL;
tmpi.accelerator_key = 0;
tmpi.ac_mods = (GdkModifierType) 0;
tmpi.widget = NULL;
item_info[0] = tmpi;
g_return_if_fail(gh_procedure_p(script));
tmpi.type = GNOME_APP_UI_ENDOFINFO;
tmpi.label = NULL;
tmpi.moreinfo = NULL;
item_info[1] = tmpi;
si = gnc_extensions_create_script_info(name, hint, script);
PINFO ("gnc_extensions_menu_add_item(): %s %s %p\n", name, hint, data);
gnome_app_insert_menus(GNOME_APP(gnc_get_ui_data()), "Extensions/",
item_info);
gnome_app_install_menu_hints(GNOME_APP(gnc_get_ui_data()), item_info);
PINFO ("gnc_extensions_menu_add_item(): %s %s %p\n", name, hint, si);
gnome_app_insert_menus(GNOME_APP(gnc_get_ui_data()),
"Extensions/", si->info);
gnome_app_install_menu_hints(GNOME_APP(gnc_get_ui_data()), si->info);
}
static void
cleanup_script_info(gpointer script_info, gpointer not_used)
{
ScriptInfo *si = (ScriptInfo *) script_info;
gnc_register_c_side_scheme_ptr(si->script);
g_free(si->info[0].label);
g_free(si->info[0].hint);
g_free(si);
}
void
gnc_extensions_shutdown()
{
g_slist_foreach(script_list, cleanup_script_info, NULL);
g_slist_free(script_list);
script_list = NULL;
}

View File

@ -21,10 +21,9 @@
#define __SCRIPTS_MENU_H__
#include <gnome.h>
#include <guile/gh.h>
void gnc_extensions_menu_add_item(char name[], char hint[], gpointer data);
GnomeUIInfo *create_scripts_menu_data();
void destroy_scripts_menu_data();
void gnc_extensions_menu_add_item(char *name, char *hint, SCM script);
void gnc_extensions_shutdown();
#endif

View File

@ -26,21 +26,31 @@
#include "top-level.h"
#include "window-main.h"
#include "option-util.h"
#include "global-options.h"
#include "gnucash-sheet.h"
#include "gnucash-color.h"
#include "gnucash-style.h"
#include "scripts_menu.h"
#include "window-help.h"
#include "window-report.h"
#include "FileIO.h"
#include "FileBox.h"
#include "FileDialog.h"
#include "MainWindow.h"
#include "Destroy.h"
#include "Refresh.h"
#include "messages.h"
#include "TransLog.h"
#include "util.h"
#include "date.h"
#include "AccWindow.h"
/** PROTOTYPES ******************************************************/
static void gnc_configure_date_format_cb(gpointer);
static void gnc_configure_date_format(void);
static void gnc_configure_newacc_currency_cb(gpointer);
static void gnc_configure_newacc_currency(void);
/** GLOBALS *********************************************************/
/* This static indicates the debugging module that this .o belongs to. */
@ -99,6 +109,13 @@ gnucash_ui_init()
gnc_options_init();
gnc_configure_date_format();
gnc_register_option_change_callback(gnc_configure_date_format_cb, NULL);
gnc_configure_newacc_currency();
gnc_register_option_change_callback(gnc_configure_newacc_currency_cb,
NULL);
mainWindow();
gnucash_style_init();
@ -118,7 +135,7 @@ gnc_ui_shutdown (void)
if (gnome_is_running && !gnome_is_terminating)
{
gnome_is_terminating = TRUE;
xaccGroupWindowDestroy(gncGetCurrentGroup());
gnc_ui_destroy_all_subwindows();
gtk_widget_hide(app);
gtk_main_quit();
}
@ -126,6 +143,16 @@ gnc_ui_shutdown (void)
/* ============================================================== */
void
gnc_ui_destroy_all_subwindows (void)
{
xaccGroupWindowDestroy(gncGetCurrentGroup());
gnc_ui_destroy_help_windows();
gnc_ui_destroy_report_windows();
}
/* ============================================================== */
void
gnc_ui_destroy (void)
{
@ -133,6 +160,7 @@ gnc_ui_destroy (void)
return;
gnc_options_shutdown();
gnc_extensions_shutdown();
if (app != NULL)
{
@ -182,4 +210,102 @@ gnucash_ui_select_file()
return (1);
}
/* ============================================================== */
/* gnc_configure_date_format_cb
* Callback called when options change - sets dateFormat to the current
* value on the scheme side and refreshes register windows
*
* Args: Nothing
* Returns: Nothing
*/
static void
gnc_configure_date_format_cb(gpointer data)
{
gnc_configure_date_format();
gnc_group_ui_refresh(gncGetCurrentGroup());
}
/* gnc_configure_date_format
* sets dateFormat to the current value on the scheme side
*
* Args: Nothing
* Returns: Nothing
*/
static void
gnc_configure_date_format (void)
{
char *format_code = gnc_lookup_multichoice_option("International",
"Date Format",
"us");
DateFormat df;
if( safe_strcmp(format_code, "us") == 0)
{
df = DATE_FORMAT_US;
}
else if( safe_strcmp(format_code, "uk") == 0)
{
df = DATE_FORMAT_UK;
}
else if( safe_strcmp(format_code, "ce") == 0)
{
df = DATE_FORMAT_CE;
}
else if( safe_strcmp(format_code, "iso") == 0)
{
df = DATE_FORMAT_ISO;
}
else if( safe_strcmp(format_code, "locale") == 0)
{
df = DATE_FORMAT_LOCALE;
}
else
{
PERR("Incorrect date format code");
return;
}
setDateFormat(df);
free(format_code);
}
/* gnc_configure_date_format_cb
* Callback called when options change - sets default currency to
* the current value on the scheme size
*
* Args: Nothing
* Returns: Nothing
*/
static void
gnc_configure_newacc_currency_cb(gpointer data)
{
gnc_configure_newacc_currency();
}
/* gnc_configure_newacc_currency
* sets the default currency for new accounts to the
* current value on the scheme side
*
* Args: Nothing
* Returns: Nothing
*/
static void
gnc_configure_newacc_currency(void)
{
char *newacc_def_currency =
gnc_lookup_string_option("International",
"Default Currency",
"USD");
xaccSetDefaultNewaccountCurrency(newacc_def_currency);
free(newacc_def_currency);
}
/****************** END OF FILE **********************/

View File

@ -33,6 +33,7 @@
#include "AdjBWindow.h"
#include "Refresh.h"
#include "window-reconcile.h"
#include "dialog-utils.h"
#include "query-user.h"
#include "messages.h"
#include "util.h"
@ -141,17 +142,19 @@ adjBWindow(Account *account)
{
GtkWidget *dialog, *frame, *vbox;
AdjBWindow *adjBData;
gchar *title;
gchar *title, *name;
FETCH_FROM_LIST(AdjBWindow, adjBList, account, account, adjBData);
title = g_strconcat(xaccAccountGetName(account), ": ", ADJ_BALN_STR, NULL);
name = gnc_ui_get_account_full_name(account, ":");
title = g_strconcat(name, " - ", ADJ_BALN_STR, NULL);
dialog = gnome_dialog_new(title,
GNOME_STOCK_BUTTON_OK,
GNOME_STOCK_BUTTON_CANCEL,
NULL);
g_free(name);
g_free(title);
adjBData->account = account;

View File

@ -1,5 +1,5 @@
/********************************************************************\
* HelpWindow.c -- a help window for hypertext help. *
* window-help.c -- a help window for hypertext help. *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1998 Linas Vepstas *
* Copyright (C) 1999 Jeremy Collins ( gtk-xmhtml port ) *
@ -24,460 +24,92 @@
* Huntington Beach, CA 92648-4632 *
\********************************************************************/
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <gnome.h>
#include <gtk-xmhtml/gtk-xmhtml.h>
#include <guile/gh.h>
#include "config.h"
#if HAVE_XPM
# include <X11/xpm.h>
#endif
#include "File.h"
#include "window-help.h"
#include "top-level.h"
#include "ui-callbacks.h"
#include "messages.h"
#include "window-html.h"
#include "Sheet.h"
#include "File.h"
#include "messages.h"
#include "util.h"
extern GtkWidget app;
static short module = MOD_HTML;
/********************************************************************\
* HTML History functions *
* hack alert -- these are gui-independent, and should be moved
* to somewhere were the gtk, Qt gui's can make use of them
\********************************************************************/
typedef struct _HTMLHistory {
struct _HTMLHistory *next;
struct _HTMLHistory *last;
char *htmlfile;
char *text;
} HTMLHistory;
static HTMLWindow *helpwindow = NULL;
/* insert an htmlfile into history. Return TRUE if this
* is the first element in the history. If not last element
* in history, all next pages are deleted */
static int
historyInsert( HTMLHistory **history, const char *htmlfile )
typedef struct _HelpData HelpData;
struct _HelpData
{
if( (*history) != NULL )
{
HTMLHistory *temp;
/* delete all next pages: */
temp = (*history)->next;
while( temp )
{
(*history)->next = temp->next;
if (temp->htmlfile) free(temp->htmlfile);
if (temp->text) free(temp->text);
_free(temp);
temp = (*history)->next;
}
/* Add new node to history: */
temp = (HTMLHistory *)_malloc(sizeof(HTMLHistory));
if (htmlfile) {
temp->htmlfile = strdup (htmlfile);
} else {
temp->htmlfile = NULL;
}
temp->text = NULL;
temp->next = NULL;
temp->last = (*history);
(*history)->next = temp;
(*history) = temp;
return FALSE;
}
else
{
/* This must be the first node in the history... */
(*history) = (HTMLHistory *)_malloc(sizeof(HTMLHistory));
if (htmlfile) {
(*history)->htmlfile = strdup (htmlfile);
} else {
(*history)->htmlfile = NULL;
}
(*history)->text = NULL;
(*history)->last = NULL;
(*history)->next = NULL;
/* ...so return TRUE */
return TRUE;
}
}
/* Move forward in history, and return current htmlfile */
static char *
historyFwd( HTMLHistory **history )
{
if( (*history) != NULL ) {
if( (*history)->next != NULL ) {
(*history) = (*history)->next;
}
return (*history)->htmlfile;
}
else
return NULL;
}
/* Move back in history, and return current htmlfile */
static char *
historyBack( HTMLHistory **history )
{
if( (*history) != NULL ) {
if( (*history)->last != NULL ) {
(*history) = (*history)->last;
}
return (*history)->htmlfile;
}
else
return NULL;
}
#ifdef NOT_USED
/* Return current htmlfile */
static char *
historyCurrent( HTMLHistory **history )
{
if( (*history) != NULL )
return (*history)->htmlfile;
else
return NULL;
}
#endif
/* Remove all entries from history: */
#if 0
static void
historyClear( HTMLHistory **history )
{
/* move to beginning of history: */
while( (*history)->last != NULL )
(*history) = (*history)->last;
/* delete all next pages: */
while( (*history)->next != NULL )
{
HTMLHistory *temp = (*history)->next;
(*history)->next = temp->next;
if(temp->htmlfile) free(temp->htmlfile);
if(temp->text) free(temp->text);
_free(temp);
}
/* delete current page: */
if ((*history)->htmlfile) free((*history)->htmlfile);
if ((*history)->text) free((*history)->text);
_free(*history);
(*history) = NULL;
}
#endif
/********************************************************************\
* HTML Window stuff... *
\********************************************************************/
/** GLOBALS *********************************************************/
struct _HTMLWindow {
GtkWidget *htmlwidget;
GtkWidget *data_target_frame;
GtkWidget *menu_target_frame;
short installed_data_frame_form_cb;
short installed_menu_frame_form_cb;
HTMLHistory *history;
gchar *htmlfile;
gchar *label;
gchar *text;
};
typedef struct _HTMLWindow HTMLWindow;
HTMLWindow *helpwindow = NULL;
HTMLWindow *reportwindow = NULL;
/** PROTOTYPES ******************************************************/
static void htmlWindow( GtkWidget *parent, HTMLWindow **hwinp,
const char * const title,
const char * const htmlfile,
const char * const text);
static void htmlBackCB( GtkWidget *widget, gpointer data );
static void htmlFwdCB( GtkWidget *widget, gpointer data );
static void htmlAnchorCB( GtkWidget *widget, XmHTMLAnchorCallbackStruct *acbs, gpointer data );
#if HAVE_LIBXMHTML
static char * xaccJumpToLabel (GtkWidget *widget, const char * jumpfile, char * text);
#endif /* HAVE_XMHTML */
/********************************************************************\
* reportWindow *
* opens up a report window, and displays html *
* *
* Args: parent - the parent widget *
* title - the title of the window *
* htmlfile - the file name of the help file to display *
* Return: none *
\********************************************************************/
void
reportWindow( GtkWidget *parent, const char *title, const char *file)
static HelpData *
help_data_new()
{
if (!reportwindow) {
reportwindow = (HTMLWindow *) g_malloc (sizeof (HTMLWindow));
reportwindow->htmlwidget = 0;
reportwindow->data_target_frame = 0;
reportwindow->menu_target_frame = 0;
reportwindow->installed_data_frame_form_cb = 0;
reportwindow->installed_menu_frame_form_cb = 0;
reportwindow->history = NULL;
HelpData *hd;
hd = g_new0(HelpData, 1);
return hd;
}
htmlWindow (parent, &reportwindow, title, file, NULL);
}
/********************************************************************\
* helpWindow *
* opens up a help window, and displays html *
* *
* Args: parent - the parent widget *
* title - the title of the window *
* htmlfile - the file name of the help file to display *
* Return: none *
\********************************************************************/
void
helpWindow( GtkWidget *parent, const char *title, const char *htmlfile )
{
if (!helpwindow) {
helpwindow = (HTMLWindow *) malloc (sizeof (HTMLWindow));
helpwindow->htmlwidget = 0;
helpwindow->data_target_frame = 0;
helpwindow->menu_target_frame = 0;
helpwindow->installed_data_frame_form_cb = 0;
helpwindow->installed_menu_frame_form_cb = 0;
helpwindow->history = NULL;
}
htmlWindow (parent, &helpwindow, title, htmlfile, NULL);
}
/********************************************************************\
* helpWindow *
* opens up a help window, and displays html *
* *
* Args: parent - the parent widget *
* title - the title of the window *
* htmlfile - the file name of the help file to display *
* Return: none *
\********************************************************************/
static void
htmlWindow( GtkWidget *parent,
HTMLWindow **hwinp,
const char * const title,
const char * const htmlfile,
const char * const htmltext)
help_data_destroy(HTMLHistoryData history_data)
{
HTMLWindow *hw = *hwinp;
char * text=0x0;
HelpData *hd = history_data;
// gnc_set_busy_cursor( parent );
g_free(hd->htmlfile);
hd->htmlfile = NULL;
historyInsert (&(hw->history), htmlfile);
if (htmltext) text = strdup (htmltext);
hw->history->text = text;
g_free(hd->label);
hd->label = NULL;
/* if the help window isn't open, then open it... otherwise, load
* new help page into current help window */
/************ CREATE HTML WINDOW HERE *****************/
/******************************************************/
{
GtkWidget *window;
GtkWidget *html;
GtkWidget *button;
GtkWidget *hbuttonbox1;
GtkWidget *vbox1;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_object_set_data (GTK_OBJECT (window), "window", window);
gtk_window_set_title (GTK_WINDOW (window), "Gnucash Help System");
gtk_window_set_policy (GTK_WINDOW (window), TRUE, TRUE, FALSE);
html = gtk_xmhtml_new();
hw->htmlwidget = html;
text = xaccJumpToLabel( hw->htmlwidget, htmlfile, text);
hw->history->text = text;
vbox1 = gtk_vbox_new (FALSE, 0);
gtk_object_set_data (GTK_OBJECT (window), "vbox1", vbox1);
gtk_widget_show (vbox1);
gtk_container_add (GTK_CONTAINER (window), vbox1);
gtk_container_border_width (GTK_CONTAINER (vbox1), 6);
/* Pack our html widget into the window */
//gtk_container_add(GTK_CONTAINER(window), html);
gtk_box_pack_start(GTK_BOX(vbox1), html, TRUE, TRUE, 5);
hbuttonbox1 = gtk_hbutton_box_new ();
gtk_object_set_data (GTK_OBJECT (window), "hbuttonbox1", hbuttonbox1);
gtk_widget_show (hbuttonbox1);
gtk_box_pack_start (GTK_BOX (vbox1), hbuttonbox1, FALSE, FALSE, 0);
gtk_container_border_width (GTK_CONTAINER (hbuttonbox1), 5);
gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_SPREAD);
button = gtk_button_new_with_label ("Back");
gtk_object_set_data (GTK_OBJECT (window), "back", button);
gtk_widget_show (button);
gtk_container_add (GTK_CONTAINER (hbuttonbox1), button);
gtk_signal_connect(GTK_OBJECT(button), "clicked",
GTK_SIGNAL_FUNC(htmlBackCB),
hw);
button = gtk_button_new_with_label ("Contents");
gtk_object_set_data (GTK_OBJECT (window), "contents", button);
gtk_widget_show (button);
gtk_container_add (GTK_CONTAINER (hbuttonbox1), button);
button = gtk_button_new_with_label ("Forward");
gtk_object_set_data (GTK_OBJECT (window), "forward", button);
gtk_widget_show (button);
gtk_container_add (GTK_CONTAINER (hbuttonbox1), button);
gtk_signal_connect(GTK_OBJECT(button), "clicked",
GTK_SIGNAL_FUNC(htmlFwdCB),
hw);
hw->data_target_frame = hw->htmlwidget;
gtk_widget_show(html);
gtk_widget_show(window);
gtk_widget_set_usize(GTK_WIDGET(window), 675, 400);
gtk_signal_connect(GTK_OBJECT(hw->htmlwidget), "activate",
GTK_SIGNAL_FUNC(htmlAnchorCB),
hw);
g_free(hd->text);
hd->text = NULL;
g_free(hd);
}
}
/********************************************************************\
* callback functions... *
\********************************************************************/
/********************************************************************\
* htmlBackCB - called when user clicks "Back" button... shows last *
* help page in history *
* *
* Args: mw - *
* cd - *
* cb - *
* Return: none *
\********************************************************************/
static void
htmlBackCB( GtkWidget *widget, gpointer data )
help_data_set_file(HelpData *hd, const gchar *htmlfile)
{
HTMLWindow *hw = (HTMLWindow *) data;
char *file = historyBack(&(hw->history));
char *text = hw->history->text;
#if HAVE_LIBXMHTML
text = xaccJumpToLabel( hw->htmlwidget, file, text);
hw->history->text = text;
#endif
g_free(hd->htmlfile);
hd->htmlfile = g_strdup(htmlfile);
}
/********************************************************************\
* htmlFwdCB - called when user clicks "Forward" button... shows *
* next help page in the history *
* *
* Args: mw - *
* cd - *
* cb - *
* Return: none *
\********************************************************************/
static void
htmlFwdCB( GtkWidget *widget, gpointer data )
help_data_set_label(HelpData *hd, const gchar *label)
{
HTMLWindow *hw = (HTMLWindow *) data;
char *file = historyFwd(&(hw->history));
char *text = hw->history->text;
#if HAVE_LIBXMHTML
text = xaccJumpToLabel( hw->htmlwidget, file, text);
hw->history->text = text;
#endif
g_free(hd->label);
hd->label = g_strdup(label);
}
/********************************************************************\
* closeHtmlWin - called when the help window is closed *
* *
* Args: mw - *
* cd - *
* cb - *
* Return: none *
\********************************************************************/
#if 0
static void
closeHtmlWin( GtkWidget *widget, gpointer data )
help_data_set_text(HelpData *hd, const gchar *text)
{
HTMLWindow **hw = (HTMLWindow **) data;
/* Delete the history: */
historyClear (&((*hw)->history));
(*hw)->history=NULL;
(*hw)->htmlwidget=0;
free (*hw);
(*hw) = NULL;
g_free(hd->text);
hd->text = g_strdup(text);
}
#endif
/********************************************************************\
* htmlAnchorCB - called when user clicks on html anchor tag *
* *
* Args: mw - the html widget that called us *
* cd - *
* cb - the anchor call-back struct *
* Return: none *
\********************************************************************/
static void
htmlAnchorCB( GtkWidget *widget, XmHTMLAnchorCallbackStruct *acbs, gpointer data)
static HTMLHistoryData
helpAnchorCB(XmHTMLAnchorCallbackStruct *acbs, HTMLHistoryData history_data)
{
HTMLWindow *hw = (HTMLWindow *) data;
#if HAVE_LIBXMHTML
// XmHTMLAnchorCallbackStruct *acbs = (XmHTMLAnchorCallbackStruct *) data;
if(acbs->reason != XmCR_ACTIVATE) return;
switch(acbs->url_type) {
/* a named anchor on a page that is already displayed */
case ANCHOR_JUMP: {
// XmHTMLAnchorScrollToName(widget, acbs->href);
}
break;
HelpData *hd;
switch(acbs->url_type)
{
/* a local file with a possible jump to label */
case ANCHOR_FILE_LOCAL: {
historyInsert(&(hw->history), acbs->href);
/* hack alert -- the target widget thing is a hack */
hw->history->text = xaccJumpToLabel (hw->data_target_frame, acbs->href, NULL);
}
break;
case ANCHOR_FILE_LOCAL:
hd = help_data_new();
help_data_set_file(hd, acbs->href);
return hd;
/* other types are unsupported, but it would be fun if they were ... */
case ANCHOR_FTP:
@ -495,82 +127,97 @@ htmlAnchorCB( GtkWidget *widget, XmHTMLAnchorCallbackStruct *acbs, gpointer data
break;
}
#endif
return NULL;
}
/********************************************************************\
* utility functions... *
\********************************************************************/
#if HAVE_LIBXMHTML
static char *
xaccJumpToLabel (GtkWidget *widget, const char * jumpfile, char * text)
static void
helpJumpCB(HTMLHistoryData history_data, char **set_text, char **set_label)
{
char *label=0x0, *file=0x0;
HelpData *hd = (HelpData *) history_data;
char *text = NULL;
char *label = NULL;
if (jumpfile) {
file = strdup (jumpfile);
*set_text = NULL;
*set_label = NULL;
if (hd->text != NULL)
{
*set_text = hd->text;
*set_label = hd->label;
return;
}
if (hd->htmlfile == NULL)
return;
/* see if this anchor contains a jump */
label = strpbrk (file, "#?");
if (label) {
file [label - file] = 0x0; /* truncate # from name */
label = strpbrk(hd->htmlfile, "#?");
if (label != NULL)
{
help_data_set_label(hd, label);
/* truncate # from name */
hd->htmlfile[label - hd->htmlfile] = 0x0;
}
/* see if the anchor is an "active gnucash page" */
if (strstr (file, ".phtml")) {
text = gncReport (file);
}
if (strstr(hd->htmlfile, ".phtml"))
text = gncReport(hd->htmlfile);
/* if text to display wasn't specified, use the truncated name to read */
if (!text) text = gncReadFile (file);
}
if (!text) return NULL;
if (text == NULL)
text = gncReadFile(hd->htmlfile);
gtk_xmhtml_source(GTK_XMHTML(widget), text);
#if 0
if (label) {
XmHTMLAnchorScrollToName(mw, label);
} else {
XmHTMLTextScrollToLine(mw, 0);
}
#endif
if (file) free (file);
return text;
}
/********************************************************************\
* HTML functions... *
\********************************************************************/
/********************************************************************\
* htmlReadImageProc *
* *
* Args: file - the name of the html file to read *
* Return: none *
* Global: helpPath - the path to the help files *
\********************************************************************/
#if 0
static XmImageInfo *
htmlReadImageProc (GtkWidget *widget, String file)
if (text != NULL)
{
char *filename;
XmImageInfo *retval = NULL;
/* construct absolute path -- twiddle the relative path we recieved */
filename = gncFindFile (file);
/* use the default proc for the hard work */
// retval = XmHTMLImageDefaultProc(widget, filename, NULL, 0);
free(filename);
return retval;
help_data_set_text(hd, text);
free(text);
}
*set_text = hd->text;
*set_label = hd->label;
}
/********************************************************************\
* helpWindow *
* opens up a help window, and displays html *
* *
* Args: parent - the parent widget *
* title - the title of the window *
* htmlfile - the file name of the help file to display *
* Return: none *
\********************************************************************/
void
helpWindow(GtkWidget *parent, const char *title, const char *htmlfile)
{
HelpData *hd;
if (helpwindow == NULL)
helpwindow = gnc_html_window_new(help_data_destroy, helpAnchorCB,
helpJumpCB);
hd = help_data_new();
help_data_set_file(hd, htmlfile);
htmlWindow(parent, &helpwindow, title, hd, NULL, 0);
}
/********************************************************************\
* gnc_ui_destroy_help_windows *
* destroys any open help windows *
* *
* Args: none *
* Return: none *
\********************************************************************/
void
gnc_ui_destroy_help_windows()
{
gnc_html_window_destroy(helpwindow);
helpwindow = NULL;
DEBUG("help windows destroyed.\n");
}
#endif /* 0 */
#endif /* HAVE_XMHTML */
/* ----------------------- END OF FILE --------------------- */

View File

@ -1,5 +1,5 @@
/********************************************************************\
* HelpWindow.h -- a help window for hypertext help. *
* window-help.h -- a help window for hypertext help. *
* Copyright (C) 1997 Robin D. Clark *
* *
* This program is free software; you can redistribute it and/or *
@ -22,8 +22,8 @@
* Huntington Beach, CA 92648-4632 *
\********************************************************************/
#ifndef __HELPWINDOW_H__
#define __HELPWINDOW_H__
#ifndef __WINDOW_HELP_H__
#define __WINDOW_HELP_H__
#include <gnome.h>
@ -31,8 +31,9 @@
/** PROTOTYPES ******************************************************/
void helpWindow( GtkWidget *parent, const char *title, const char *htmlfile );
void reportWindow( GtkWidget *parent, const char *title, const char *text );
void helpWindow(GtkWidget *parent, const char *title, const char *htmlfile);
void gnc_ui_destroy_help_windows();
#endif

750
src/gnome/window-html.c Normal file
View File

@ -0,0 +1,750 @@
/********************************************************************\
* window-html.c -- an html window for gnucash *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1998 Linas Vepstas *
* Copyright (C) 1999 Jeremy Collins ( gtk-xmhtml port ) *
* *
* 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. *
* *
* Author: Rob Clark *
* Internet: rclark@cs.hmc.edu *
* Address: 609 8th Street *
* Huntington Beach, CA 92648-4632 *
\********************************************************************/
#include <string.h>
#include "top-level.h"
#include "window-html.h"
#include "File.h"
#include "util.h"
static short module = MOD_HTML;
/********************************************************************\
* HTML History functions *
* hack alert -- these are gui-independent, and should be moved *
* to a central location *
\********************************************************************/
typedef struct _HTMLHistoryNode HTMLHistoryNode;
struct _HTMLHistoryNode
{
HTMLHistoryNode *next;
HTMLHistoryNode *last;
HTMLHistoryData history_data;
};
typedef struct _HTMLHistory HTMLHistory;
struct _HTMLHistory
{
HTMLHistoryNode *current_node;
HTMLHistoryDestroyDataFunc destroy;
};
static HTMLHistoryNode *
history_node_new(const HTMLHistoryData history_data)
{
HTMLHistoryNode *new;
new = malloc(sizeof(HTMLHistoryNode));
assert(new != NULL);
new->history_data = history_data;
new->last = NULL;
new->next = NULL;
return new;
}
/* Insert a history element into history. Return TRUE if this
* is the first element in the history. If not last element
* in history, all next pages are deleted */
static gncBoolean
historyInsert(HTMLHistory *history, const HTMLHistoryData history_data)
{
HTMLHistoryNode *new;
HTMLHistoryNode *temp;
assert(history != NULL);
new = history_node_new(history_data);
if (history->current_node == NULL)
{
history->current_node = new;
return GNC_T;
}
/* delete all next pages: */
temp = history->current_node->next;
while(temp != NULL)
{
history->current_node->next = temp->next;
if (temp->history_data != NULL)
(history->destroy)(temp->history_data);
free(temp);
temp = history->current_node->next;
}
new->last = history->current_node;
history->current_node->next = new;
history->current_node = new;
return GNC_F;
}
/* Move forward in history, and return current history data */
static HTMLHistoryData
historyFwd(HTMLHistory *history)
{
if (history == NULL)
return NULL;
if (history->current_node == NULL)
return NULL;
if (history->current_node->next != NULL)
history->current_node = history->current_node->next;
return history->current_node->history_data;
}
/* Move back in history, and return current history data */
static HTMLHistoryData
historyBack(HTMLHistory *history)
{
if (history == NULL)
return NULL;
if (history->current_node == NULL)
return NULL;
if (history->current_node->last != NULL)
history->current_node = history->current_node->last;
return history->current_node->history_data;
}
/* Remove all entries from history: */
static void
historyClear(HTMLHistory *history)
{
if (history == NULL)
return;
if (history->current_node == NULL)
return;
/* move to beginning of history: */
while(history->current_node->last != NULL )
history->current_node = history->current_node->last;
/* delete all next pages: */
while(history->current_node->next != NULL )
{
HTMLHistoryNode *temp = history->current_node->next;
history->current_node->next = temp->next;
if (temp->history_data != NULL)
(history->destroy)(temp->history_data);
free(temp);
}
/* delete current page: */
if (history->current_node->history_data != NULL)
(history->destroy)(history->current_node->history_data);
free(history->current_node);
history->current_node = NULL;
}
static HTMLHistoryData
historyData(HTMLHistory *history)
{
if (history == NULL)
return NULL;
if (history->current_node == NULL)
return NULL;
return history->current_node->history_data;
}
static HTMLHistory *
historyNew(HTMLHistoryDestroyDataFunc destroy)
{
HTMLHistory *history;
history = malloc(sizeof(HTMLHistory));
assert(history != NULL);
history->current_node = NULL;
history->destroy = destroy;
return history;
}
static void
historyDestroy(HTMLHistory *history)
{
if (history == NULL)
return;
historyClear(history);
free(history);
}
/********************************************************************\
* HTML Window stuff... *
\********************************************************************/
struct _HTMLWindow
{
GtkWidget *window;
GtkWidget *htmlwidget;
GtkWidget *forward;
GtkWidget *back;
HTMLHistory *history;
HTMLAnchorCB anchor_cb;
HTMLJumpCB jump_cb;
};
/** PROTOTYPES ******************************************************/
static void htmlBackCB(GtkWidget *widget, gpointer data);
static void htmlFwdCB(GtkWidget *widget, gpointer data);
static void htmlAnchorCB(GtkWidget *widget,
XmHTMLAnchorCallbackStruct *acbs,
gpointer data);
static gboolean htmlKeyCB(GtkWidget *widget, GdkEventKey *event,
gpointer user_data);
static void closeHtmlWinCB(GtkWidget *widget, gpointer data);
static void destroyHtmlWinCB(GtkWidget *widget, gpointer data);
static XmImageInfo * htmlReadImageProc(GtkWidget *widget, String file,
gpointer data);
static void htmlSetButtonStates(HTMLWindow *hw);
/********************************************************************\
* gnc_html_window_history_data *
* return the current history data for the window *
* *
* Args: none *
* Return: history data for the window *
\********************************************************************/
HTMLHistoryData
gnc_html_window_history_data(HTMLWindow *hw)
{
if (hw == NULL)
return NULL;
return historyData(hw->history);
}
/********************************************************************\
* gnc_html_window_new *
* g_malloc and initialize HTMLWindow structure *
* *
* Args: none *
* Return: g_malloc'd initialized HTMLWindow structure *
\********************************************************************/
HTMLWindow *
gnc_html_window_new(HTMLHistoryDestroyDataFunc destroy,
HTMLAnchorCB anchor_cb, HTMLJumpCB jump_cb)
{
HTMLWindow *hw;
hw = g_new0(HTMLWindow, 1);
hw->history = historyNew(destroy);
hw->anchor_cb = anchor_cb;
hw->jump_cb = jump_cb;
return hw;
}
/********************************************************************\
* gnc_html_window_destroy *
* destroy an HTMLWindow structure *
* *
* Args: hw - the htmlwindow structure to destroy *
* Return: none *
\********************************************************************/
void
gnc_html_window_destroy(HTMLWindow *hw)
{
if (hw == NULL)
return;
if (hw->window != NULL)
gtk_widget_destroy(hw->window);
else
{
historyDestroy(hw->history);
g_free(hw);
}
}
static GtkWidget *
create_html_toolbar(HTMLWindow *hw, GnomeUIInfo *user_buttons,
gint num_buttons)
{
GnomeUIInfo *toolbar_info;
GtkWidget *toolbar;
gint num_start, num_end;
gint i;
GnomeUIInfo toolbar_start[] =
{
{ GNOME_APP_UI_ITEM,
"Back",
"Move back one step in the history.",
htmlBackCB, hw,
NULL,
GNOME_APP_PIXMAP_STOCK,
GNOME_STOCK_PIXMAP_BACK,
0, 0, NULL
},
{ GNOME_APP_UI_ITEM,
"Forward",
"Move forward one step in the history.",
htmlFwdCB, hw,
NULL,
GNOME_APP_PIXMAP_STOCK,
GNOME_STOCK_PIXMAP_FORWARD,
0, 0, NULL
}
};
GnomeUIInfo toolbar_end[] =
{
{ GNOME_APP_UI_ITEM,
"Close",
"Close this HTML window.",
closeHtmlWinCB, hw,
NULL,
GNOME_APP_PIXMAP_STOCK,
GNOME_STOCK_PIXMAP_CLOSE,
0, 0, NULL
},
GNOMEUIINFO_END
};
num_start = sizeof(toolbar_start) / sizeof(GnomeUIInfo);
num_end = sizeof(toolbar_end) / sizeof(GnomeUIInfo);
toolbar_info = g_new0(GnomeUIInfo, num_start + num_buttons + num_end);
for (i = 0; i < num_start; i++)
toolbar_info[i] = toolbar_start[i];
for (i = 0; i < num_buttons; i++)
toolbar_info[i + num_start] = user_buttons[i];
for (i = 0; i < num_end; i++)
toolbar_info[i + num_start + num_buttons] = toolbar_end[i];
toolbar = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
gnome_app_fill_toolbar(GTK_TOOLBAR(toolbar), toolbar_info, NULL);
hw->back = toolbar_info[0].widget;
hw->forward = toolbar_info[1].widget;
g_free(toolbar_info);
return toolbar;
}
/********************************************************************\
* htmlWindow *
* opens up an html window, and displays html *
* *
* Args: parent - the parent widget *
* hwp - the htmlwindow structure pointer *
* title - the title of the window *
* history_data - the history data *
* new_buttons - array of buttons to add to icon bar *
* num_buttons - number of buttons in list *
* Return: none *
\********************************************************************/
void
htmlWindow(GtkWidget * parent,
HTMLWindow ** hwp,
const char * const title,
HTMLHistoryData history_data,
GnomeUIInfo *user_buttons,
gint num_buttons)
{
HTMLWindow *hw = *hwp;
historyInsert(hw->history, history_data);
/* If the help window is already created, just load the new
* page into the existing widget and raise the window. */
if (hw->htmlwidget != NULL)
{
gnc_html_load(hw);
if (hw->window->window != NULL)
gdk_window_raise(hw->window->window);
htmlSetButtonStates(hw);
return;
}
/************ CREATE HTML WINDOW HERE *****************/
{
GtkWidget *window;
GtkWidget *html;
GtkWidget *dock;
GtkWidget *dock_item;
GtkWidget *toolbar;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
hw->window = window;
gtk_window_set_title(GTK_WINDOW (window), title);
gtk_window_set_policy(GTK_WINDOW (window), TRUE, TRUE, FALSE);
gtk_window_set_default_size(GTK_WINDOW(window), 675, 400);
dock = gnome_dock_new();
gtk_container_add(GTK_CONTAINER(window), dock);
dock_item = gnome_dock_item_new("toolbar", GNOME_DOCK_ITEM_BEH_EXCLUSIVE);
toolbar = create_html_toolbar(hw, user_buttons, num_buttons);
gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);
gtk_container_add(GTK_CONTAINER(dock_item), toolbar);
gnome_dock_add_item (GNOME_DOCK(dock), GNOME_DOCK_ITEM(dock_item),
GNOME_DOCK_TOP, 0, 0, 0, TRUE);
html = gtk_xmhtml_new();
hw->htmlwidget = html;
gnome_dock_set_client_area(GNOME_DOCK(dock), html);
gtk_xmhtml_set_image_procs(GTK_XMHTML(html), htmlReadImageProc,
NULL, NULL, NULL);
gnc_html_load(hw);
gtk_signal_connect(GTK_OBJECT(hw->htmlwidget), "activate",
GTK_SIGNAL_FUNC(htmlAnchorCB), hw);
gtk_signal_connect(GTK_OBJECT(window), "destroy",
GTK_SIGNAL_FUNC(destroyHtmlWinCB), hwp);
gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
GTK_SIGNAL_FUNC(htmlKeyCB), hw);
gtk_widget_show_all(window);
}
htmlSetButtonStates(hw);
}
/********************************************************************\
* htmlSetButtonStates - set the sensitivity of the back/forward *
* buttons based on the history state *
* *
* Args: hw - the html window structure *
* Return: none *
\********************************************************************/
static void
htmlSetButtonStates(HTMLWindow *hw)
{
gboolean more_to_go;
more_to_go =
(hw->history != NULL) &&
(hw->history->current_node != NULL) &&
(hw->history->current_node->last != NULL);
gtk_widget_set_sensitive(hw->back, more_to_go);
more_to_go =
(hw->history != NULL) &&
(hw->history->current_node != NULL) &&
(hw->history->current_node->next != NULL);
gtk_widget_set_sensitive(hw->forward, more_to_go);
}
/********************************************************************\
* htmlKeyCB - called when user presses a key *
* *
* Args: widget - the back button *
* data - html window structure *
* Return: none *
\********************************************************************/
static gboolean
htmlKeyCB( GtkWidget *widget, GdkEventKey *event, gpointer data )
{
HTMLWindow *hw = (HTMLWindow *) data;
GtkXmHTML *html = GTK_XMHTML(hw->htmlwidget);
GtkAdjustment *adj;
gfloat value;
if (html->vsba == NULL)
return FALSE;
adj = GTK_ADJUSTMENT(html->vsba);
value = adj->value;
switch (event->keyval)
{
case GDK_KP_Up:
case GDK_Up:
value -= adj->step_increment;
break;
case GDK_KP_Down:
case GDK_Down:
value += adj->step_increment;
break;
case GDK_KP_Page_Up:
case GDK_Page_Up:
value -= adj->page_increment;
break;
case GDK_KP_Page_Down:
case GDK_Page_Down:
case GDK_space:
value += adj->page_increment;
break;
case GDK_KP_Home:
case GDK_Home:
value = adj->lower;
break;
case GDK_KP_End:
case GDK_End:
value = adj->upper;
break;
case GDK_Escape:
gtk_widget_destroy(hw->window);
return TRUE;
default:
return FALSE;
}
value = CLAMP(value, adj->lower, adj->upper - adj->page_size);
gtk_adjustment_set_value(adj, value);
return TRUE;
}
/********************************************************************\
* htmlBackCB - called when user clicks "Back" button... shows last *
* help page in history *
* *
* Args: widget - the back button *
* data - html window structure *
* Return: none *
\********************************************************************/
static void
htmlBackCB(GtkWidget *widget, gpointer data)
{
HTMLWindow *hw = (HTMLWindow *) data;
HTMLHistoryData history_data;
history_data = historyBack(hw->history);
gnc_html_load(hw);
htmlSetButtonStates(hw);
}
/********************************************************************\
* htmlFwdCB - called when user clicks "Forward" button... shows *
* next help page in the history *
* *
* Args: widget - the forward button *
* data - html window structure *
* Return: none *
\********************************************************************/
static void
htmlFwdCB(GtkWidget *widget, gpointer data)
{
HTMLWindow *hw = (HTMLWindow *) data;
HTMLHistoryData history_data;
history_data = historyFwd(hw->history);
gnc_html_load(hw);
htmlSetButtonStates(hw);
}
/********************************************************************\
* closeHtmlWinCB - callback for closing html window *
* *
* Args: widget - the widget that called us *
* data - html window structure pointer *
* Return: none *
\********************************************************************/
static void
closeHtmlWinCB(GtkWidget *widget, gpointer data)
{
HTMLWindow *hw = (HTMLWindow *) data;
gtk_widget_destroy(hw->window);
}
/********************************************************************\
* destroyHtmlWinCB - called when the help window is destroyed *
* *
* Args: widget - the widget that called us *
* data - html window structure pointer *
* Return: none *
\********************************************************************/
static void
destroyHtmlWinCB(GtkWidget *widget, gpointer data)
{
HTMLWindow **hwp = (HTMLWindow **) data;
HTMLWindow *hw = *hwp;
/* Delete the history: */
historyDestroy(hw->history);
hw->history = NULL;
hw->htmlwidget = NULL;
g_free(hw);
*hwp = NULL;
DEBUG("HTML window destroyed.\n");
}
/********************************************************************\
* htmlAnchorCB - called when user clicks on html anchor tag *
* *
* Args: widget - the html widget that called us *
* acbs - callback structure *
* data - html window structure *
* Return: none *
\********************************************************************/
static void
htmlAnchorCB(GtkWidget *widget, XmHTMLAnchorCallbackStruct *acbs,
gpointer data)
{
HTMLWindow *hw = (HTMLWindow *) data;
HTMLHistoryData history_data;
if (acbs->reason != XmCR_ACTIVATE) return;
switch(acbs->url_type)
{
/* a named anchor on a page that is already displayed */
case ANCHOR_JUMP:
XmHTMLAnchorScrollToName(widget, acbs->href);
break;
default:
if (hw->anchor_cb == NULL)
return;
history_data = (hw->anchor_cb)(acbs, historyData(hw->history));
if (history_data == NULL)
return;
historyInsert(hw->history, history_data);
gnc_html_load(hw);
break;
}
htmlSetButtonStates(hw);
}
/********************************************************************\
* gnc_html_load - load the current location into the html window *
* *
* Args: hw - the html window structure *
* Return: none *
\********************************************************************/
void
gnc_html_load(HTMLWindow *hw)
{
HTMLHistoryData history_data;
char *label = NULL;
char *text = NULL;
if (hw == NULL)
return;
if (hw->jump_cb == NULL)
return;
history_data = historyData(hw->history);
(hw->jump_cb)(history_data, &text, &label);
if (text == NULL)
return;
gtk_xmhtml_source(GTK_XMHTML(hw->htmlwidget), text);
if (label != NULL)
XmHTMLAnchorScrollToName(hw->htmlwidget, label);
else
XmHTMLTextScrollToLine(hw->htmlwidget, 0);
}
/********************************************************************\
* htmlReadImageProc - callback function for the html widget *
* used to find an image file *
* *
* Args: widget - the html widget *
* file - the name of the image file to read *
* data - some data, not used *
* Return: none *
\********************************************************************/
static XmImageInfo *
htmlReadImageProc (GtkWidget *widget, String file, gpointer data)
{
char *filename;
XmImageInfo *retval = NULL;
/* construct absolute path -- twiddle the relative path we received */
filename = gncFindFile(file);
/* use the default proc for the hard work */
retval = XmHTMLImageDefaultProc(widget, filename, NULL, 0);
free(filename);
return retval;
}
/* ----------------------- END OF FILE --------------------- */

62
src/gnome/window-html.h Normal file
View File

@ -0,0 +1,62 @@
/********************************************************************\
* window-html -- an html window for gnucash. *
* Copyright (C) 1997 Robin D. Clark *
* *
* 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. *
* *
* Author: Rob Clark *
* Internet: rclark@cs.hmc.edu *
* Address: 609 8th Street *
* Huntington Beach, CA 92648-4632 *
\********************************************************************/
#ifndef __WINDOW_HTML_H__
#define __WINDOW_HTML_H__
#include <gnome.h>
#include <gtk-xmhtml/gtk-xmhtml.h>
typedef struct _HTMLWindow HTMLWindow;
typedef void * HTMLHistoryData;
typedef void (*HTMLHistoryDestroyDataFunc)(HTMLHistoryData);
typedef HTMLHistoryData (*HTMLAnchorCB)(XmHTMLAnchorCallbackStruct *acbs,
HTMLHistoryData history_data);
typedef void (*HTMLJumpCB)(HTMLHistoryData history_data,
char **text, char **label);
HTMLHistoryData gnc_html_window_history_data(HTMLWindow *hw);
HTMLWindow * gnc_html_window_new(HTMLHistoryDestroyDataFunc destroy,
HTMLAnchorCB anchor_cb, HTMLJumpCB jump_cb);
void gnc_html_window_destroy(HTMLWindow *hw);
void htmlWindow(GtkWidget * parent,
HTMLWindow ** hwp,
const char * const title,
HTMLHistoryData history_data,
GnomeUIInfo *user_buttons,
gint num_buttons);
void gnc_html_load(HTMLWindow *hw);
#endif

View File

@ -27,7 +27,7 @@
#include "AccWindow.h"
#include "AdjBWindow.h"
#include "dialog-options.h"
#include "global-options.h"
#include "FileDialog.h"
#include "g-wrap.h"
#include "gnucash.h"
@ -35,14 +35,17 @@
#include "Destroy.h"
#include "messages.h"
#include "RegWindow.h"
#include "Refresh.h"
#include "version.h"
#include "window-main.h"
#include "window-mainP.h"
#include "window-reconcile.h"
#include "window-register.h"
#include "window-help.h"
#include "account-tree.h"
#include "dialog-transfer.h"
#include "dialog-edit.h"
#include "Scrub.h"
#include "util.h"
#include "gnc.h"
@ -50,6 +53,7 @@
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_GUI;
enum {
FMB_NEW,
FMB_OPEN,
@ -85,24 +89,6 @@ static GnomeUIInfo filemenu[] = {
GNOMEUIINFO_END
};
static GnomeUIInfo reportsmenu[] = {
{
GNOME_APP_UI_ITEM,
N_("_Balance..."), N_("Balance Report"),
gnc_ui_reports_cb, "report-baln.phtml", NULL,
GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_PREF,
0, 0, NULL
},
{
GNOME_APP_UI_ITEM,
N_("_Profit & Loss..."), N_("Profit & Loss Report"),
gnc_ui_reports_cb, "report-pnl.phtml", NULL,
GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_PREF,
0, 0, NULL
},
GNOMEUIINFO_END
};
static GnomeUIInfo optionsmenu[] = {
{
GNOME_APP_UI_ITEM,
@ -123,17 +109,50 @@ static GnomeUIInfo optionsmenu[] = {
GNOMEUIINFO_END
};
static GnomeUIInfo scrubmenu[] = {
{
GNOME_APP_UI_ITEM,
N_("_Scrub Account"), N_("Scrub the account clean"),
gnc_ui_mainWindow_scrub, NULL, NULL,
GNOME_APP_PIXMAP_NONE, NULL,
0, 0, NULL
},
{
GNOME_APP_UI_ITEM,
N_("Scrub S_ubaccounts"), N_("Scrub the account and all its "
"subaccounts clean"),
gnc_ui_mainWindow_scrub_sub, NULL, NULL,
GNOME_APP_PIXMAP_NONE, NULL,
0, 0, NULL
},
{
GNOME_APP_UI_ITEM,
N_("Scrub _All"), N_("Scrub all the accounts clean"),
gnc_ui_mainWindow_scrub_all, NULL, NULL,
GNOME_APP_PIXMAP_NONE, NULL,
0, 0, NULL
},
GNOMEUIINFO_END
};
static GnomeUIInfo accountsmenu[] = {
{
GNOME_APP_UI_ITEM,
N_("_View..."), N_("View Account"),
N_("_View..."), N_("View account"),
gnc_ui_mainWindow_toolbar_open, NULL, NULL,
GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_OPEN,
'v', GDK_CONTROL_MASK, NULL
},
{
GNOME_APP_UI_ITEM,
N_("_Edit..."), N_("Edit Account"),
N_("View S_ubaccounts..."), N_("View account and subaccounts"),
gnc_ui_mainWindow_toolbar_open_subs, NULL, NULL,
GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_OPEN,
0, 0, NULL
},
{
GNOME_APP_UI_ITEM,
N_("_Edit..."), N_("Edit account information"),
gnc_ui_mainWindow_toolbar_edit, NULL, NULL,
GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_PROP,
'e', GDK_CONTROL_MASK, NULL
@ -175,6 +194,8 @@ static GnomeUIInfo accountsmenu[] = {
GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_PIXMAP_REMOVE,
'r', GDK_CONTROL_MASK, NULL
},
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_SUBTREE(N_("_Scrub"), scrubmenu),
GNOMEUIINFO_END
};
@ -199,7 +220,6 @@ static GnomeUIInfo scriptsmenu[] = {
static GnomeUIInfo mainmenu[] = {
GNOMEUIINFO_MENU_FILE_TREE(filemenu),
GNOMEUIINFO_SUBTREE(N_("_Accounts"), accountsmenu),
GNOMEUIINFO_SUBTREE(N_("_Reports"), reportsmenu),
GNOMEUIINFO_SUBTREE(N_("_Options"), optionsmenu),
GNOMEUIINFO_SUBTREE(N_("_Extensions"), scriptsmenu),
GNOMEUIINFO_MENU_HELP_TREE(helpmenu),
@ -397,13 +417,7 @@ gnc_ui_about_cb (GtkWidget *widget, gpointer data)
static void
gnc_ui_help_cb ( GtkWidget *widget, gpointer data )
{
helpWindow( GTK_WIDGET(gnc_get_ui_data()), HELP_STR, HH_MAIN );
}
static void
gnc_ui_reports_cb(GtkWidget *widget, gpointer report)
{
reportWindow (widget, "duuuude", report);
helpWindow(NULL, HELP_STR, HH_MAIN);
}
static void
@ -425,8 +439,9 @@ gnc_ui_delete_account ( Account *account )
xaccRemoveAccount(account);
xaccFreeAccount(account);
/* Step 4: Refresh the toolbar */
/* Step 4: Refresh things */
gnc_ui_refresh_statusbar();
gnc_group_ui_refresh(gncGetCurrentGroup());
}
static void
@ -447,15 +462,37 @@ gnc_ui_delete_account_cb ( GtkWidget *widget, gpointer data )
static void
gnc_ui_mainWindow_toolbar_open ( GtkWidget *widget, gpointer data )
{
RegWindow *regData;
Account *account = gnc_get_current_account();
if(account)
if (account == NULL)
{
PINFO ("calling regWindowSimple(%p)\n", account);
regWindowSimple ( account );
}
else
gnc_error_dialog("You must select an account to open first.");
return;
}
PINFO ("calling regWindowSimple(%p)\n", account);
regData = regWindowSimple(account);
gnc_register_raise(regData);
}
static void
gnc_ui_mainWindow_toolbar_open_subs(GtkWidget *widget, gpointer data)
{
RegWindow *regData;
Account *account = gnc_get_current_account();
if (account == NULL)
{
gnc_error_dialog("You must select an account to open first.");
return;
}
PINFO ("calling regWindowAccGroup(%p)\n", account);
regData = regWindowAccGroup(account);
gnc_register_raise(regData);
}
static void
@ -505,6 +542,54 @@ gnc_ui_mainWindow_adjust_balance(GtkWidget *widget, gpointer data)
gnc_error_dialog("You must select an account to adjust first.");
}
static void
gnc_ui_mainWindow_scrub(GtkWidget *widget, gpointer data)
{
Account *account = gnc_get_current_account();
if (account == NULL)
{
gnc_error_dialog("You must select an account to scrub first.");
return;
}
xaccAccountScrubOrphans(account);
xaccAccountScrubImbalance(account);
gnc_account_ui_refresh(account);
gnc_refresh_main_window();
}
static void
gnc_ui_mainWindow_scrub_sub(GtkWidget *widget, gpointer data)
{
Account *account = gnc_get_current_account();
if (account == NULL)
{
gnc_error_dialog("You must select an account to scrub first.");
return;
}
xaccAccountTreeScrubOrphans(account);
xaccAccountTreeScrubImbalance(account);
gnc_account_ui_refresh(account);
gnc_refresh_main_window();
}
static void
gnc_ui_mainWindow_scrub_all(GtkWidget *widget, gpointer data)
{
AccountGroup *group = gncGetCurrentGroup();
xaccGroupScrubOrphans(group);
xaccGroupScrubImbalance(group);
gnc_group_ui_refresh(group);
gnc_refresh_main_window();
}
static void
gnc_ui_options_cb(GtkWidget *widget, gpointer data)
{
@ -582,11 +667,14 @@ gnc_account_tree_activate_cb(GNCAccountTree *tree,
Account *account,
gpointer user_data)
{
regWindowSimple(account);
RegWindow *regData;
regData = regWindowSimple(account);
gnc_register_raise(regData);
}
static void
gnc_configure_account_tree()
gnc_configure_account_tree(gpointer data)
{
GtkObject *app;
GNCAccountTree *tree;
@ -713,8 +801,9 @@ mainWindow()
account_tree = gnc_account_tree_new();
gtk_object_set_data (GTK_OBJECT (app), "account_tree", account_tree);
gnc_configure_account_tree();
gnc_register_option_change_callback(gnc_configure_account_tree);
gnc_configure_account_tree(NULL);
gnc_register_option_change_callback(gnc_configure_account_tree, NULL);
gtk_signal_connect(GTK_OBJECT (account_tree), "activate_account",
GTK_SIGNAL_FUNC (gnc_account_tree_activate_cb), NULL);
@ -773,15 +862,8 @@ mainWindow()
gtk_widget_show(scrolled_win);
gnc_refresh_main_window();
gtk_widget_grab_focus(account_tree);
}
/********************* END OF FILE **********************************/
/*
Local Variables:
indent-tabs-mode: nil
mode: c
c-indentation-style: gnu
eval: (c-set-offset 'block-open '-)
End:
*/

View File

@ -25,7 +25,7 @@
#ifndef __WINDOW_MAINP_H__
#define __WINDOW_MAINP_H__
#include <gtk/gtk.h>
#include <gnome.h>
#include "top-level.h"
@ -35,14 +35,18 @@ static void gnc_ui_refresh_statusbar(void);
static void gnc_ui_exit_cb(GtkWidget *widget, gpointer data);
static void gnc_ui_about_cb(GtkWidget *widget, gpointer data);
static void gnc_ui_help_cb(GtkWidget *widget, gpointer data);
static void gnc_ui_reports_cb(GtkWidget *widget, gpointer report);
static void gnc_ui_add_account(GtkWidget *widget, gpointer data);
static void gnc_ui_delete_account_cb(GtkWidget *widget, gpointer data);
static void gnc_ui_mainWindow_toolbar_open(GtkWidget *widget, gpointer data);
static void gnc_ui_mainWindow_toolbar_open_subs(GtkWidget *widget,
gpointer data);
static void gnc_ui_mainWindow_toolbar_edit(GtkWidget *widget, gpointer data);
static void gnc_ui_mainWindow_reconcile(GtkWidget *widget, gpointer data);
static void gnc_ui_mainWindow_transfer(GtkWidget *widget, gpointer data);
static void gnc_ui_mainWindow_adjust_balance(GtkWidget *widget, gpointer data);
static void gnc_ui_mainWindow_scrub(GtkWidget *widget, gpointer data);
static void gnc_ui_mainWindow_scrub_sub(GtkWidget *widget, gpointer data);
static void gnc_ui_mainWindow_scrub_all(GtkWidget *widget, gpointer data);
static void gnc_ui_options_cb(GtkWidget *widget, gpointer data);
static void gnc_ui_filemenu_cb(GtkWidget *widget, gpointer menuItem);

View File

@ -33,7 +33,10 @@
#include "MainWindow.h"
#include "RegWindow.h"
#include "window-reconcile.h"
#include "window-register.h"
#include "dialog-utils.h"
#include "reconcile-list.h"
#include "Refresh.h"
#include "query-user.h"
#include "window-help.h"
#include "messages.h"
@ -58,6 +61,9 @@ struct _RecnWindow
GtkWidget *debit; /* Debit matrix show unreconciled debit */
GtkWidget *credit; /* Credit matrix, shows credits... */
GtkWidget *edit_button; /* Edit transaction button */
GtkWidget *delete_button; /* Delete transaction button */
char * symbol; /* Currency symbol or 's' for shares */
};
@ -68,6 +74,8 @@ static void recnClose(GtkWidget *w, gpointer data);
static void recnOkCB(GtkWidget *w, gpointer data);
static void recnCancelCB(GtkWidget *w, gpointer data);
static void gnc_reconcile_window_set_button_sensitivity(RecnWindow *recnData);
/** GLOBALS *********************************************************/
static RecnWindow **recnList = NULL;
@ -97,6 +105,8 @@ recnRefresh(Account *account)
gnc_reconcile_list_refresh(GNC_RECONCILE_LIST(recnData->debit));
gnc_reconcile_list_refresh(GNC_RECONCILE_LIST(recnData->credit));
gnc_reconcile_window_set_button_sensitivity(recnData);
recnRecalculateBalance(recnData);
}
@ -159,7 +169,7 @@ recnRecalculateBalance(RecnWindow *recnData)
* or "Cancel" *
* *
* Args: parent - the parent of this window *
* acc - the account to reconcile *
* account - the account to reconcile *
* diff - returns the amount from ending balance field *
* Return: True, if the user presses "Ok", else False *
\********************************************************************/
@ -272,15 +282,50 @@ startRecnWindow(GtkWidget *parent, Account *account, double *diff)
}
static void
gnc_reconcile_window_set_button_sensitivity(RecnWindow *recnData)
{
gboolean sensitive = FALSE;
GNCReconcileList *list;
list = GNC_RECONCILE_LIST(recnData->debit);
if (gnc_reconcile_list_get_current_split(list) != NULL)
sensitive = TRUE;
list = GNC_RECONCILE_LIST(recnData->credit);
if (gnc_reconcile_list_get_current_split(list) != NULL)
sensitive = TRUE;
gtk_widget_set_sensitive(recnData->edit_button, sensitive);
gtk_widget_set_sensitive(recnData->delete_button, sensitive);
}
static void
gnc_reconcile_window_list_cb(GNCReconcileList *list, Split *split,
gpointer data)
{
RecnWindow *recnData = (RecnWindow *) data;
gnc_reconcile_window_set_button_sensitivity(recnData);
recnRecalculateBalance(recnData);
}
static void
gnc_reconcile_window_focus_cb(GtkWidget *widget, GdkEventFocus *event,
gpointer data)
{
RecnWindow *recnData = (RecnWindow *) data;
GNCReconcileList *this_list, *debit, *credit;
this_list = GNC_RECONCILE_LIST(widget);
debit = GNC_RECONCILE_LIST(recnData->debit);
credit = GNC_RECONCILE_LIST(recnData->credit);
/* clear the *other* list so we always have no more than one selection */
gnc_reconcile_list_unselect_all((this_list == debit) ? credit : debit);
}
static GtkWidget *
gnc_reconcile_window_create_list_frame(Account *account,
GNCReconcileListType type,
@ -305,6 +350,8 @@ gnc_reconcile_window_create_list_frame(Account *account,
gtk_signal_connect(GTK_OBJECT(list), "toggle_reconciled",
GTK_SIGNAL_FUNC(gnc_reconcile_window_list_cb), recnData);
gtk_signal_connect(GTK_OBJECT(list), "focus_in_event",
GTK_SIGNAL_FUNC(gnc_reconcile_window_focus_cb), recnData);
scrollWin = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrollWin),
@ -331,6 +378,23 @@ gnc_reconcile_window_create_list_frame(Account *account,
}
static Split *
gnc_reconcile_window_get_current_split(RecnWindow *recnData)
{
GNCReconcileList *list;
Split *split;
list = GNC_RECONCILE_LIST(recnData->debit);
split = gnc_reconcile_list_get_current_split(list);
if (split != NULL)
return split;
list = GNC_RECONCILE_LIST(recnData->credit);
split = gnc_reconcile_list_get_current_split(list);
return split;
}
static void
gnc_ui_reconcile_window_help_cb(GtkWidget *widget, gpointer data)
{
@ -350,6 +414,94 @@ gnc_ui_reconcile_window_change_cb(GtkButton *button, gpointer data)
}
}
static void
gnc_ui_reconcile_window_new_cb(GtkButton *button, gpointer data)
{
RecnWindow *recnData = (RecnWindow *) data;
RegWindow *regData;
regData = regWindowSimple(recnData->account);
if (regData == NULL)
return;
gnc_register_raise(regData);
gnc_register_jump_to_blank(regData);
}
static void
gnc_ui_reconcile_window_delete_cb(GtkButton *button, gpointer data)
{
RecnWindow *recnData = (RecnWindow *) data;
Account **affected_accounts;
Transaction *trans;
Split *split, *s;
int i, num_splits;
split = gnc_reconcile_window_get_current_split(recnData);
/* This should never be true, but be paranoid */
if (split == NULL)
return;
{
gchar * buf = "Are you sure you want to delete the current transaction?";
gboolean result;
result = gnc_verify_dialog_parented(GTK_WINDOW(recnData->dialog),
buf, GNC_F);
if (!result)
return;
}
/* make a copy of all of the accounts that will be
* affected by this deletion, so that we can update
* their register windows after the deletion.
*/
trans = xaccSplitGetParent(split);
num_splits = xaccTransCountSplits(trans);
affected_accounts = (Account **) malloc((num_splits + 1) *
sizeof(Account *));
assert(affected_accounts != NULL);
for (i = 0; i < num_splits; i++)
{
s = xaccTransGetSplit(trans, i);
affected_accounts[i] = xaccSplitGetAccount(s);
}
affected_accounts[num_splits] = NULL;
xaccTransBeginEdit(trans, 1);
xaccTransDestroy(trans);
xaccTransCommitEdit(trans);
gnc_account_list_ui_refresh(affected_accounts);
free(affected_accounts);
gnc_refresh_main_window ();
}
static void
gnc_ui_reconcile_window_edit_cb(GtkButton *button, gpointer data)
{
RecnWindow *recnData = (RecnWindow *) data;
RegWindow *regData;
Split *split;
split = gnc_reconcile_window_get_current_split(recnData);
/* This should never be true, but be paranoid */
if (split == NULL)
return;
regData = regWindowSimple(recnData->account);
if (regData == NULL)
return;
gnc_register_raise(regData);
gnc_register_jump_to_split(regData, split);
}
/********************************************************************\
* recnWindow *
* opens up the window to reconcile an account *
@ -437,53 +589,92 @@ recnWindow(GtkWidget *parent, Account *account)
gtk_box_pack_start(GTK_BOX(debcred_area), credits_frame, TRUE, FALSE, 0);
{
GtkWidget *hbox = gtk_hbox_new(FALSE, 5);
GtkWidget *hbox, *title_vbox, *value_vbox, *button;
GtkWidget *totals_hbox, *frame, *title, *value, *bbox;
GtkWidget *prev_title = gtk_label_new(PREV_BALN_C_STR);
GtkWidget *end_title = gtk_label_new(END_BALN_C_STR);
GtkWidget *space = gtk_label_new("");
GtkWidget *prev_value = gtk_label_new("");
GtkWidget *end_value = gtk_label_new("");
/* lower horizontal bar below reconcile lists */
hbox = gtk_hbox_new(FALSE, 5);
gtk_box_pack_start(GTK_BOX(main_area), hbox, FALSE, FALSE, 0);
GtkWidget *change_end = gtk_button_new();
GtkWidget *difference_frame = gtk_frame_new(NULL);
GtkWidget *difference_box = gtk_hbox_new(FALSE, 5);
GtkWidget *difference_label = gtk_label_new(DIFF_C_STR);
GtkWidget *difference_value = gtk_label_new("");
bbox = gtk_hbutton_box_new();
gtk_box_pack_start(GTK_BOX(hbox), bbox, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(change_end), end_value);
gtk_container_set_border_width(GTK_CONTAINER(change_end), 4);
gtk_button_set_relief(GTK_BUTTON(change_end), GTK_RELIEF_HALF);
button = gtk_button_new_with_label("New");
gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
gnc_set_tooltip(button, "Add a new transaction to the account");
gtk_signal_connect(GTK_OBJECT(button), "clicked",
GTK_SIGNAL_FUNC(gnc_ui_reconcile_window_new_cb),
recnData);
gtk_signal_connect(GTK_OBJECT(change_end), "clicked",
button = gtk_button_new_with_label("Edit");
recnData->edit_button = button;
gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
gnc_set_tooltip(button, "Edit the current transaction");
gtk_signal_connect(GTK_OBJECT(button), "clicked",
GTK_SIGNAL_FUNC(gnc_ui_reconcile_window_edit_cb),
recnData);
button = gtk_button_new_with_label("Delete");
recnData->delete_button = button;
gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
gnc_set_tooltip(button, "Delete the current transaction");
gtk_signal_connect(GTK_OBJECT(button), "clicked",
GTK_SIGNAL_FUNC(gnc_ui_reconcile_window_delete_cb),
recnData);
button = gtk_button_new_with_label("Edit Info...");
gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
gnc_set_tooltip(button, "Adjust the ending balance");
gtk_signal_connect(GTK_OBJECT(button), "clicked",
GTK_SIGNAL_FUNC(gnc_ui_reconcile_window_change_cb),
recnData);
gtk_misc_set_alignment(GTK_MISC(prev_title), 0.95, 0.5);
gtk_misc_set_alignment(GTK_MISC(end_title), 0.95, 0.5);
gtk_misc_set_alignment(GTK_MISC(difference_label), 0.95, 0.5);
/* frame to hold totals */
frame = gtk_frame_new(NULL);
gtk_box_pack_end(GTK_BOX(hbox), frame, FALSE, FALSE, 0);
recnData->starting = prev_value;
recnData->ending = end_value;
recnData->difference = difference_value;
/* hbox to hold title/value vboxes */
totals_hbox = gtk_hbox_new(FALSE, 3);
gtk_container_add(GTK_CONTAINER(frame), totals_hbox);
gtk_container_set_border_width(GTK_CONTAINER(totals_hbox), 5);
gtk_box_pack_start(GTK_BOX(main_area), hbox, FALSE, FALSE, 0);
/* vbox to hold titles */
title_vbox = gtk_vbox_new(TRUE, 3);
gtk_box_pack_start(GTK_BOX(totals_hbox), title_vbox, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), prev_title, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), prev_value, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), space, FALSE, FALSE, 3);
gtk_box_pack_start(GTK_BOX(hbox), end_title, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), change_end, FALSE, FALSE, 0);
gtk_box_pack_end(GTK_BOX(hbox), difference_frame, FALSE, FALSE, 0);
/* vbox to hold values */
value_vbox = gtk_vbox_new(TRUE, 3);
gtk_box_pack_start(GTK_BOX(totals_hbox), value_vbox, TRUE, TRUE, 0);
gtk_frame_set_shadow_type(GTK_FRAME(difference_frame), GTK_SHADOW_IN);
gtk_container_add(GTK_CONTAINER(difference_frame), difference_box);
/* previous balance title/value */
title = gtk_label_new(PREV_BALN_C_STR);
gtk_misc_set_alignment(GTK_MISC(title), 0.95, 0.5);
gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(difference_box), 5);
gtk_box_pack_start(GTK_BOX(difference_box), difference_label,
TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(difference_box), difference_value,
FALSE, FALSE, 0);
value = gtk_label_new("");
recnData->starting = value;
gtk_misc_set_alignment(GTK_MISC(value), 0.95, 0.5);
gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 0);
/* ending balance title/value */
title = gtk_label_new(END_BALN_C_STR);
gtk_misc_set_alignment(GTK_MISC(title), 0.95, 0.5);
gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 0);
value = gtk_label_new("");
recnData->ending = value;
gtk_misc_set_alignment(GTK_MISC(value), 0.95, 0.5);
gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 0);
/* difference title/value */
title = gtk_label_new(DIFF_C_STR);
gtk_misc_set_alignment(GTK_MISC(title), 0.95, 0.5);
gtk_box_pack_start(GTK_BOX(title_vbox), title, FALSE, FALSE, 0);
value = gtk_label_new("");
recnData->difference = value;
gtk_misc_set_alignment(GTK_MISC(value), 0.95, 0.5);
gtk_box_pack_start(GTK_BOX(value_vbox), value, FALSE, FALSE, 0);
}
/* Set up the data */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
/*******************************************************************\
* window-register.h -- public GnuCash register functions *
* Copyright (C) 1998,1999 Linas Vepstas *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
\********************************************************************/
#ifndef __WINDOW_REGISTER_H__
#define __WINDOW_REGISTER_H__
#include "top-level.h"
void gnc_register_raise(RegWindow *regData);
void gnc_register_jump_to_blank(RegWindow *regData);
void gnc_register_jump_to_split(RegWindow *regData, Split *split);
#endif

341
src/gnome/window-report.c Normal file
View File

@ -0,0 +1,341 @@
/********************************************************************\
* window-report.c -- a report window for hypertext help. *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1998 Linas Vepstas *
* Copyright (C) 1999 Jeremy Collins ( gtk-xmhtml port ) *
* *
* 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. *
* *
* Author: Rob Clark *
* Internet: rclark@cs.hmc.edu *
* Address: 609 8th Street *
* Huntington Beach, CA 92648-4632 *
\********************************************************************/
#include <gnome.h>
#include "config.h"
#include "window-report.h"
#include "window-html.h"
#include "option-util.h"
#include "guile-util.h"
#include "dialog-options.h"
#include "util.h"
static short module = MOD_HTML;
static HTMLWindow *reportwindow = NULL;
typedef struct _ReportData ReportData;
struct _ReportData
{
gchar *text;
GNCOptionDB *odb;
GtkWidget *option_dialog;
SCM rendering_thunk;
SCM guile_options;
};
static ReportData *
report_data_new()
{
ReportData *rd;
rd = g_new0(ReportData, 1);
rd->guile_options = SCM_UNDEFINED;
rd->rendering_thunk = SCM_UNDEFINED;
return rd;
}
static void
report_data_destroy(HTMLHistoryData history_data)
{
ReportData *rd = history_data;
g_free(rd->text);
rd->text = NULL;
gnc_option_db_destroy(rd->odb);
rd->odb = NULL;
if (rd->option_dialog != NULL)
gtk_widget_destroy(rd->option_dialog);
rd->option_dialog = NULL;
if (rd->guile_options != SCM_UNDEFINED)
gnc_unregister_c_side_scheme_ptr(rd->guile_options);
rd->guile_options = SCM_UNDEFINED;
if (rd->rendering_thunk != SCM_UNDEFINED)
gnc_unregister_c_side_scheme_ptr(rd->rendering_thunk);
rd->rendering_thunk = SCM_UNDEFINED;
g_free(rd);
}
static void
report_data_set_text(ReportData *rd, const gchar *text)
{
g_free(rd->text);
rd->text = g_strdup(text);
}
static void
report_data_set_rendering_thunk(ReportData *rd, const SCM rendering_thunk)
{
if (rd->rendering_thunk != SCM_UNDEFINED)
gnc_unregister_c_side_scheme_ptr(rd->rendering_thunk);
rd->rendering_thunk = rendering_thunk;
}
static void
gnc_options_dialog_apply_cb(GnomePropertyBox *propertybox,
gint arg1, gpointer user_data)
{
ReportData *rd = user_data;
if (arg1 == -1)
gnc_option_db_commit(rd->odb);
}
static void
gnc_options_dialog_help_cb(GnomePropertyBox *propertybox,
gint arg1, gpointer user_data)
{
gnome_ok_dialog("Set the report options you want using this dialog.");
}
static void
report_data_set_guile_options(ReportData *rd, const SCM guile_options)
{
GnomePropertyBox *prop_box;
if (rd->guile_options != SCM_UNDEFINED)
{
gnc_unregister_c_side_scheme_ptr(rd->guile_options);
gnc_option_db_destroy(rd->odb);
}
if (rd->option_dialog != NULL)
gtk_widget_destroy(rd->option_dialog);
if (gh_scm2bool(gh_not(guile_options)))
{
rd->guile_options = SCM_UNDEFINED;
rd->option_dialog = NULL;
return;
}
rd->guile_options = guile_options;
gnc_register_c_side_scheme_ptr(guile_options);
rd->odb = gnc_option_db_new();
gnc_option_db_init(rd->odb, guile_options);
rd->option_dialog = gnome_property_box_new();
gnome_dialog_close_hides(GNOME_DIALOG(rd->option_dialog), TRUE);
prop_box = GNOME_PROPERTY_BOX(rd->option_dialog);
gnc_build_options_dialog_contents(prop_box, rd->odb);
gnc_option_db_clean(rd->odb);
gtk_signal_connect(GTK_OBJECT(rd->option_dialog), "apply",
GTK_SIGNAL_FUNC(gnc_options_dialog_apply_cb), rd);
gtk_signal_connect(GTK_OBJECT(rd->option_dialog), "help",
GTK_SIGNAL_FUNC(gnc_options_dialog_help_cb), rd);
}
static HTMLHistoryData
reportAnchorCB(XmHTMLAnchorCallbackStruct *acbs,
HTMLHistoryData history_data)
{
switch(acbs->url_type)
{
/* a local file with a possible jump to label */
case ANCHOR_FILE_LOCAL:
PERR(" this report window doesn't support ftp: %s\n", acbs->href);
break;
/* other types are unsupported, but it would be fun if they were ... */
case ANCHOR_FTP:
PERR(" this report window doesn't support ftp: %s\n", acbs->href);
break;
case ANCHOR_HTTP:
PERR (" this report window doesn't support http: %s\n", acbs->href);
break;
case ANCHOR_MAILTO:
PERR(" this report window doesn't support email: %s\n", acbs->href);
break;
case ANCHOR_UNKNOWN:
default:
PERR(" don't know this type of url: %s\n", acbs->href);
break;
}
return NULL;
}
static void
reportJumpCB(HTMLHistoryData history_data, char **set_text, char **set_label)
{
ReportData *rd = (ReportData *) history_data;
char *text;
SCM text_scm;
*set_text = NULL;
*set_label = NULL;
if (rd->text != NULL)
{
*set_text = rd->text;
return;
}
if (!gh_procedure_p(rd->rendering_thunk))
return;
text_scm = gh_call0(rd->rendering_thunk);
if (!gh_string_p(text_scm))
return;
text = gh_scm2newstr(text_scm, NULL);
if (text == NULL)
return;
report_data_set_text(rd, text);
free(text);
*set_text = rd->text;
}
static void
gnc_report_options_changed_cb(gpointer data)
{
HTMLWindow *hw = data;
HTMLHistoryData hd;
ReportData *rd;
hd = gnc_html_window_history_data(hw);
if (hd == NULL)
return;
rd = (ReportData *) hd;
report_data_set_text(rd, NULL);
gnc_html_load(hw);
}
static void
gnc_report_properties_cb(GtkWidget *widget, gpointer data)
{
ReportData *rd = data;
if (rd->option_dialog == NULL)
return;
gtk_widget_show_all(rd->option_dialog);
gdk_window_raise(GTK_WIDGET(rd->option_dialog)->window);
}
/********************************************************************\
* reportWindow *
* opens up a report window, and displays html *
* *
* Args: title - the title of the window *
* text - the html text to display *
* Return: none *
\********************************************************************/
void
reportWindow(const char *title, SCM rendering_thunk, SCM guile_options)
{
ReportData *rd;
if (reportwindow == NULL)
reportwindow = gnc_html_window_new(report_data_destroy, reportAnchorCB,
reportJumpCB);
rd = report_data_new();
report_data_set_rendering_thunk(rd, rendering_thunk);
report_data_set_guile_options(rd, guile_options);
if (rd->odb != NULL)
gnc_option_db_register_change_callback(rd->odb,
gnc_report_options_changed_cb,
reportwindow);
if (rd->option_dialog != NULL)
{
gchar *prop_title;
prop_title = g_strconcat(title, " (Parameters)", NULL);
gtk_window_set_title(GTK_WINDOW(rd->option_dialog), prop_title);
g_free(prop_title);
}
{
GnomeUIInfo user_buttons[] =
{
{ GNOME_APP_UI_ITEM,
"Properties",
"Set the properties for this report.",
gnc_report_properties_cb, rd,
NULL,
GNOME_APP_PIXMAP_STOCK,
GNOME_STOCK_PIXMAP_PROPERTIES,
0, 0, NULL
}
};
gint num_buttons = sizeof(user_buttons) / sizeof(GnomeUIInfo);
htmlWindow(NULL, &reportwindow, title, rd, user_buttons, num_buttons);
}
}
/********************************************************************\
* gnc_ui_destroy_report_windows *
* destroys any open report windows *
* *
* Args: none *
* Return: none *
\********************************************************************/
void
gnc_ui_destroy_report_windows()
{
gnc_html_window_destroy(reportwindow);
reportwindow = NULL;
DEBUG("report windows destroyed.\n");
}
/* ----------------------- END OF FILE --------------------- */

40
src/gnome/window-report.h Normal file
View File

@ -0,0 +1,40 @@
/********************************************************************\
* window-report.h -- a report window for hypertext help. *
* Copyright (C) 1997 Robin D. Clark *
* *
* 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. *
* *
* Author: Rob Clark *
* Internet: rclark@cs.hmc.edu *
* Address: 609 8th Street *
* Huntington Beach, CA 92648-4632 *
\********************************************************************/
#ifndef __WINDOW_REPORT_H__
#define __WINDOW_REPORT_H__
#include <gnome.h>
#include <guile/gh.h>
#include "config.h"
/** PROTOTYPES ******************************************************/
void reportWindow(const char *title, SCM rendering_thunk, SCM guile_options);
void gnc_ui_destroy_report_windows();
#endif

View File

@ -45,6 +45,7 @@ COMMON_SRCS := basiccell.c cellblock.c \
MOTIF_SRCS := table-motif.c combocell-motif.c
GNOME_SRCS := table-gnome.c
QT_SRCS := table-qt.cpp combocell-qt.cpp
CLEAN_SUBDIRS := gnome
######################################################################
all:

View File

@ -195,7 +195,8 @@ struct _BasicCell {
const char * (*modify_verify) (BasicCell *,
const char *old_value,
const char *add_str,
const char *new_value);
const char *new_value,
int *cursor_position);
const char * (*leave_cell) (BasicCell *,
const char * current);

View File

@ -47,6 +47,8 @@ CellBlock * xaccMallocCellBlock (int numrows, int numcols)
arr->cell_types = NULL;
arr->right_traverse_r = NULL;
arr->right_traverse_c = NULL;
arr->left_traverse_r = NULL;
arr->left_traverse_c = NULL;
arr->widths = NULL;
arr->alignments = NULL;
xaccInitCellBlock (arr, numrows, numcols);
@ -80,7 +82,7 @@ FreeCellBlockMem (CellBlock *arr)
}
}
/* free traversal chain */
/* free right traversal chain */
if (arr->right_traverse_r) {
for (i=0; i<oldrows; i++) {
if (arr->right_traverse_r[i]) free (arr->right_traverse_r[i]);
@ -92,6 +94,18 @@ FreeCellBlockMem (CellBlock *arr)
}
}
/* free left traversal chain */
if (arr->left_traverse_r) {
for (i=0; i<oldrows; i++) {
if (arr->left_traverse_r[i]) free (arr->left_traverse_r[i]);
}
}
if (arr->left_traverse_c) {
for (i=0; i<oldrows; i++) {
if (arr->left_traverse_c[i]) free (arr->left_traverse_c[i]);
}
}
/* free widths, alignments */
if (arr->widths) free (arr->widths);
if (arr->alignments) free (arr->alignments);
@ -123,7 +137,7 @@ xaccInitCellBlock (CellBlock *arr, int numrows, int numcols)
}
}
/* malloc new traversal arrays */
/* malloc new right traversal arrays */
arr->right_traverse_r = (short **) malloc (numrows * sizeof (short *));
arr->right_traverse_c = (short **) malloc (numrows * sizeof (short *));
for (i=0; i<numrows; i++) {
@ -146,6 +160,29 @@ xaccInitCellBlock (CellBlock *arr, int numrows, int numcols)
arr->last_reenter_traverse_row = numrows-1;
arr->last_reenter_traverse_col = numcols-1;
/* malloc new left traversal arrays */
arr->left_traverse_r = (short **) malloc (numrows * sizeof (short *));
arr->left_traverse_c = (short **) malloc (numrows * sizeof (short *));
for (i=0; i<numrows; i++) {
(arr->left_traverse_r)[i] = (short *) malloc (numcols * sizeof (short));
(arr->left_traverse_c)[i] = (short *) malloc (numcols * sizeof (short));
for (j=0; j<numcols-1; j++) {
/* default traversal is same row, previous column */
(arr->left_traverse_r)[i][j] = i;
(arr->left_traverse_c)[i][j] = j-1;
}
/* at start of row, wrap to previous row */
(arr->left_traverse_r)[i][numcols-1] = i-1;
(arr->left_traverse_c)[i][numcols-1] = numcols-1;
}
/* at start of block, wrap back to end */
(arr->right_traverse_r)[0][0] = numrows-1;
(arr->right_traverse_c)[0][0] = numcols-1;
/* first is last ... */
arr->last_left_reenter_traverse_row = 0;
arr->last_left_reenter_traverse_col = 0;
arr->widths = (short *) malloc (numcols * sizeof(short));
arr->alignments = (Alignments *) malloc (numcols * sizeof(Alignments));
@ -202,4 +239,37 @@ xaccNextRight (CellBlock *arr, int row, int col,
}
void
xaccNextLeft (CellBlock *arr, int row, int col,
int next_row, int next_col)
{
if (!arr) return;
/* avoid embarrasement if cell incorrectly specified */
if ((0 > row) || (0 > col)) return;
if ((row >= arr->numRows) || (col >= arr->numCols)) return;
/* -1 is a valid value for next ... it signifies that traversal
* should go to next tab group, so do not check for neg values.
* if ((0 > next_row) || (0 > next_col)) return;
*/
/* if the "next" location to hop to is larger than the cursor, that
* just means that we should hop to the next cursor. Thus, large
* values for next *are* valid.
* if ((next_row >= arr->numRows) || (next_col >= arr->numCols)) return;
*/
(arr->left_traverse_r)[row][col] = next_row;
(arr->left_traverse_c)[row][col] = next_col;
/* if traversing out (neg values) record this as the last ... */
if ((0 > next_row) || (0 > next_col)) {
arr->last_left_reenter_traverse_row = row;
arr->last_left_reenter_traverse_col = col;
}
}
/* --------------- end of file ----------------- */

View File

@ -99,6 +99,12 @@ struct _CellBlock {
short **right_traverse_r;
short **right_traverse_c;
short **left_traverse_r;
short **left_traverse_c;
short right_exit_r;
short right_exit_c;
short left_exit_r;
short left_exit_c;
/* the above arrays have dimension of numRows*numCols.
* the are automatically created and managed by the routines below.
* The control the tab-traversal order through this cell block.
@ -106,8 +112,7 @@ struct _CellBlock {
* on the keyboard will take input-focus to cell (inext,jnext), where
* inext = right_traverse_r[i][j] and jnext = right_traverse_c[i][j].
*
* Note that left-traversing arrays could be defined (for when
* shift-tab is hit), but we haven't (yet) done so.
* (exit_r, exit_c) is the last cell of this tab group.
*/
/* the last-reneter row and column should contain the very last
@ -118,6 +123,9 @@ struct _CellBlock {
short last_reenter_traverse_row;
short last_reenter_traverse_col;
short last_left_reenter_traverse_row;
short last_left_reenter_traverse_col;
void * user_data;
/* above is a pointer to anything the programmer-user of this struct
* wants it to be. Handy for stuff.

View File

@ -148,6 +148,40 @@ void xaccDestroyComboCell (ComboCell *cell)
/* =============================================== */
void
xaccClearComboCellMenu (ComboCell * cell)
{
int n;
char ** arr;
if (!cell) return;
arr = cell->menuitems;
n = 0;
while (arr[n]) n++;
if (n == 0)
return;
n = 0;
while (arr[n]) {
free (arr[n]);
n++;
}
free (arr);
cell->menuitems = (char **) malloc (sizeof (char *));
cell->menuitems[0] = NULL;
if (!cell->cell.realize) {
PopBox *box;
box = (PopBox *) cell->cell.gui_private;
XmComboBoxDeleteAllItems (box->combobox);
}
}
/* =============================================== */
void
xaccAddComboCellMenuItem (ComboCell *cell, char * menustr)
{

View File

@ -52,6 +52,7 @@ void xaccDestroyComboCell (ComboCell *);
void xaccSetComboCellValue (ComboCell *, const char *);
void xaccClearComboCellMenu (ComboCell *);
void xaccAddComboCellMenuItem (ComboCell *, char * menustr);
#endif /* __XACC_COMBO_CELL_H__ */

View File

@ -141,7 +141,8 @@ static const char *
DateMV (BasicCell *_cell,
const char *oldval,
const char *change,
const char *newval)
const char *newval,
int *cursor_position)
{
DateCell *cell = (DateCell *) _cell;
struct tm *date;

View File

@ -111,6 +111,7 @@ select_item_cb (GNCItemList *item_list, char *item_string, gpointer data)
PopBox *box = (PopBox *) cell->cell.gui_private;
gnucash_sheet_modify_current_cell(box->sheet, item_string);
item_edit_hide_list (box->item_edit);
}
static void
@ -119,7 +120,15 @@ key_press_item_cb (GNCItemList *item_list, GdkEventKey *event, gpointer data)
ComboCell *cell = (ComboCell *) data;
PopBox *box = (PopBox *) cell->cell.gui_private;
gtk_widget_event(GTK_WIDGET(box->sheet), (GdkEvent *) event);
switch(event->keyval) {
case GDK_Escape:
item_edit_hide_list (box->item_edit);
break;
default:
gtk_widget_event(GTK_WIDGET(box->sheet),
(GdkEvent *) event);
break;
}
}
static void
@ -130,6 +139,9 @@ disconnect_list_signals (ComboCell *cell)
if (!box->list_signals_connected)
return;
if (GTK_OBJECT_DESTROYED(GTK_OBJECT(box->item_list)))
return;
gtk_signal_disconnect(GTK_OBJECT(box->item_list),
box->select_item_signal);
@ -147,6 +159,9 @@ connect_list_signals (ComboCell *cell)
if (box->list_signals_connected)
return;
if (GTK_OBJECT_DESTROYED(GTK_OBJECT(box->item_list)))
return;
box->select_item_signal =
gtk_signal_connect(GTK_OBJECT(box->item_list), "select_item",
GTK_SIGNAL_FUNC(select_item_cb),
@ -213,6 +228,32 @@ void xaccDestroyComboCell (ComboCell *cell)
/* =============================================== */
void
xaccClearComboCellMenu (ComboCell * cell)
{
PopBox *box;
if (cell == NULL)
return;
box = (PopBox *) cell->cell.gui_private;
if (box == NULL)
return;
if (box->menustrings == NULL)
return;
g_list_foreach(box->menustrings, (GFunc) g_free, NULL);
g_list_free(box->menustrings);
box->menustrings = NULL;
if (box->item_list != NULL)
gnc_item_list_clear(box->item_list);
box->list_in_sync = TRUE;
}
/* =============================================== */
static void
gnc_append_string_to_list(gpointer _string, gpointer _item_list)
{
@ -266,7 +307,7 @@ xaccSetComboCellValue (ComboCell *cell, const char *str)
static const char *
ComboMV (BasicCell *_cell, const char *oldval, const char *change,
const char *newval)
const char *newval, int *cursor_position)
{
xaccSetBasicCellValue (_cell, newval);
@ -307,6 +348,8 @@ moveCombo (BasicCell *bcell, int phys_row, int phys_col)
{
PopBox *box = (PopBox *) bcell->gui_private;
disconnect_list_signals((ComboCell *) bcell);
gnome_canvas_item_set(GNOME_CANVAS_ITEM(box->item_edit),
"is_combo", FALSE, NULL);

View File

@ -40,13 +40,12 @@ GdkColor gn_white, gn_black, gn_light_gray, gn_dark_gray, gn_red;
static GHashTable *color_hash_table = NULL;
static guint
color_hash (gconstpointer v)
{
const GdkColor *c = (GdkColor *) v;
const uint32 *c = (uint32 *) v;
return (c->red << 16) | (c->green << 8) | (c->blue);
return *c;
}
@ -116,6 +115,7 @@ gnucash_color_argb_to_gdk (uint32 argb)
if (!color) {
color = g_new0(GdkColor, 1);
newkey = g_new0(uint32, 1);
*newkey = key;
color->red = (argb & 0xff0000) >> 8;

View File

@ -39,17 +39,6 @@ enum {
};
#if 0
static void
gnucash_cursor_update (GnomeCanvasItem *item, double *affine,
ArtSVP *clip_path, int flags)
{
if (GNOME_CANVAS_ITEM_CLASS(gnucash_cursor_parent_class)->update)
(*GNOME_CANVAS_ITEM_CLASS(gnucash_cursor_parent_class)->update)
(item, affine, clip_path, flags);
}
#endif
static void
gnucash_cursor_get_pixel_coords (GnucashCursor *cursor, gint *x, gint *y,
gint *w, gint *h)
@ -58,12 +47,12 @@ gnucash_cursor_get_pixel_coords (GnucashCursor *cursor, gint *x, gint *y,
GnucashItemCursor *item_cursor =
GNUCASH_ITEM_CURSOR(cursor->cursor[GNUCASH_CURSOR_BLOCK]);
gnome_canvas_get_scroll_offsets (GNOME_CANVAS(cursor->sheet), x, y);
gnome_canvas_get_scroll_offsets (GNOME_CANVAS(cursor->sheet), NULL, y);
*y += gnucash_sheet_row_get_distance (sheet, sheet->top_block,
item_cursor->row)
+ sheet->top_block_offset;
*x += gnucash_sheet_col_get_distance (sheet, sheet->left_block,
*x = gnucash_sheet_col_get_distance (sheet, sheet->left_block,
item_cursor->col)
+ sheet->left_block_offset;
@ -255,12 +244,17 @@ gnucash_item_cursor_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
dw = item_cursor->w;
dh = item_cursor->h;
gdk_gc_set_foreground (cursor->gc, &gn_light_gray);
gdk_gc_set_line_attributes (cursor->gc, 1,
GDK_LINE_SOLID, -1, -1);
gdk_gc_set_foreground (cursor->gc, &gn_light_gray);
gdk_draw_rectangle (drawable, cursor->gc, FALSE,
dx+1, dy+1, dw-2, dh-2);
gdk_gc_set_foreground (cursor->gc, &gn_black);
gdk_draw_rectangle (drawable, cursor->gc, FALSE,
dx+2, dy+2, dw-4, dh-4);
}
}
@ -333,6 +327,12 @@ gnucash_cursor_set (GnucashCursor *cursor, gint block_row, gint block_col,
gnucash_cursor_configure (cursor);
gnome_canvas_item_set (GNOME_CANVAS_ITEM(sheet->header_item),
"GnucashHeader::cursor_type",
cursor->style->cursor_type,
"GnucashHeader::cursor_row", cell_row,
NULL);
gnucash_cursor_request_redraw (cursor);
}
@ -506,15 +506,8 @@ gnucash_cursor_class_init (GnucashCursorClass *cursor_class)
object_class->destroy = gnucash_cursor_destroy;
/* GnomeCanvasItem method overrides */
item_class->realize = gnucash_cursor_realize;
item_class->unrealize = gnucash_cursor_unrealize;
/*
item_class->update = gnucash_cursor_update;
item_class->point = gnucash_cursor_point;
item_class->translate = gnucash_cursor_translate;
item_class->event = gnucash_cursor_event;
*/
}
@ -589,5 +582,3 @@ gnucash_cursor_new (GnomeCanvasGroup *parent)
c-basic-offset: 8
End:
*/

View File

@ -31,7 +31,9 @@ static GnomeCanvasItem *gnucash_header_parent_class;
enum {
ARG_0,
ARG_SHEET,
ARG_SHEET, /* the sheet this header is associated with */
ARG_CURSOR_TYPE, /* the type of the current cursor */
ARG_CURSOR_ROW, /* the row within the current cursor */
};
@ -71,17 +73,23 @@ gnucash_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
gdk_draw_rectangle (drawable, header->gc, FALSE,
-x, -y, style->width-1, style->height);
gdk_draw_line (drawable, header->gc,
-x, style->height+1, style->width-1, style->height+1);
-x, style->height-1, style->width-1, style->height-1);
gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, -1, -1);
gdk_gc_set_background (header->gc, &gn_white);
gdk_gc_set_foreground (header->gc, &gn_black);
font = gnucash_default_font;
font = style->header_font;
xpaint = -x;
ypaint = -y;
for (i = 0; i < 1; i++) {
i = header->row;
/* TODO: This routine is duplicated in several places. Can we
abstract at least the cell drawing routine? That way we'll be
sure everything is drawn consistently, and cut down on maintenance
issues.
*/
for (j = 0; j < style->ncols; j++) {
w = style->pixel_widths[i][j];
h = style->pixel_heights[i][j];
@ -130,8 +138,6 @@ gnucash_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
xpaint += w;
}
ypaint += h;
}
}
@ -143,6 +149,7 @@ gnucash_header_request_redraw (GnucashHeader *header)
gnome_canvas_request_redraw (canvas, 0, 0, INT_MAX, INT_MAX);
}
static void
gnucash_header_realize (GnomeCanvasItem *item)
{
@ -170,6 +177,9 @@ gnucash_header_unrealize (GnomeCanvasItem *item)
header->gc = NULL;
}
gdk_cursor_destroy (header->resize_cursor);
gdk_cursor_destroy (header->normal_cursor);
if (GNOME_CANVAS_ITEM_CLASS (gnucash_header_parent_class)->unrealize)
(*GNOME_CANVAS_ITEM_CLASS
(gnucash_header_parent_class)->unrealize)(item);
@ -184,10 +194,10 @@ gnucash_header_destroy (GtkObject *object)
if (GTK_OBJECT_CLASS (gnucash_header_parent_class)->destroy)
(*GTK_OBJECT_CLASS
(gnucash_header_parent_class)->destroy)(object);
}
/* FIXME: for now, let's only draw one row, to avoid resizing. */
void
gnucash_header_reconfigure (GnucashHeader *header)
{
@ -195,13 +205,18 @@ gnucash_header_reconfigure (GnucashHeader *header)
int w, h;
double old_w, old_h;
header->style = header->sheet->cursor_style[GNUCASH_CURSOR_HEADER];
header->style = header->sheet->cursor_style[header->type];
if (header->style == NULL)
return;
w = header->style->width + 1;
h = header->style->pixel_heights[0][0] + 2;
/* Check for a valid header row. This can be invalid during
arg setting. */
if (header->row < 0 || header->row >= header->style->nrows)
return;
w = header->style->width;
h = header->style->pixel_heights[header->row][0];
gnome_canvas_get_scroll_region(canvas, NULL, NULL, &old_w, &old_h);
@ -209,24 +224,90 @@ gnucash_header_reconfigure (GnucashHeader *header)
gnome_canvas_set_scroll_region(GNOME_CANVAS(canvas),
0, 0, w, h);
gtk_widget_set_usize (GTK_WIDGET(canvas), w, h);
gtk_widget_set_usize (GTK_WIDGET(canvas), -1, h);
}
gnucash_header_request_redraw (header);
}
static double
gnucash_header_point (GnomeCanvasItem *item,
double x, double y, int cx, int cy,
GnomeCanvasItem **actual_item)
{
*actual_item = item;
return 0.0;
}
static int
pointer_on_resize_line (GnucashHeader *header, int x, int y)
{
SheetBlockStyle *style = header->style;
int i, j;
int pixels = 0;
for (j = 0; j < style->ncols; j++) {
if (x >= pixels - 1 && x <= pixels+1)
return TRUE;
pixels += style->pixel_widths[header->row][j];
}
return FALSE;
}
static gint
gnucash_header_event (GnomeCanvasItem *item, GdkEvent *event)
{
GnucashHeader *header = GNUCASH_HEADER(item);
GnomeCanvas *canvas = item->canvas;
int x, y;
switch (event->type) {
case GDK_MOTION_NOTIFY:
gnome_canvas_w2c (canvas, event->motion.x, event->motion.y,
&x, &y);
if (pointer_on_resize_line(header, x, y))
gdk_window_set_cursor (GTK_WIDGET(canvas)->window,
header->resize_cursor);
else
gdk_window_set_cursor (GTK_WIDGET(canvas)->window,
header->normal_cursor);
break;
}
return TRUE;
}
static void
gnucash_header_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
{
GnucashHeader *header;
GtkLayout *layout;
gint needs_update = FALSE;
header = GNUCASH_HEADER (o);
layout = GTK_LAYOUT(GNOME_CANVAS_ITEM(header)->canvas);
switch (arg_id){
case ARG_SHEET:
header->sheet = GTK_VALUE_POINTER (*arg);
gtk_layout_set_hadjustment (layout, header->sheet->hadj);
needs_update = TRUE;
break;
case ARG_CURSOR_TYPE:
header->type = GTK_VALUE_INT (*arg);
needs_update = TRUE;
break;
case ARG_CURSOR_ROW:
header->row = GTK_VALUE_INT (*arg);
needs_update = TRUE;
break;
default:
@ -242,6 +323,8 @@ static void
gnucash_header_init (GnucashHeader *header)
{
header->sheet = NULL;
header->resize_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
header->normal_cursor = gdk_cursor_new (GDK_X_CURSOR);
}
@ -259,6 +342,10 @@ gnucash_header_class_init (GnucashHeaderClass *header_class)
gtk_object_add_arg_type ("GnucashHeader::sheet", GTK_TYPE_POINTER,
GTK_ARG_WRITABLE, ARG_SHEET);
gtk_object_add_arg_type ("GnucashHeader::cursor_type", GTK_TYPE_INT,
GTK_ARG_WRITABLE, ARG_CURSOR_TYPE);
gtk_object_add_arg_type ("GnucashHeader::cursor_row", GTK_TYPE_INT,
GTK_ARG_WRITABLE, ARG_CURSOR_ROW);
object_class->set_arg = gnucash_header_set_arg;
object_class->destroy = gnucash_header_destroy;
@ -267,6 +354,8 @@ gnucash_header_class_init (GnucashHeaderClass *header_class)
item_class->unrealize = gnucash_header_unrealize;
item_class->update = gnucash_header_update;
item_class->draw = gnucash_header_draw;
item_class->event = gnucash_header_event;
item_class->point = gnucash_header_point;
}
@ -322,6 +411,9 @@ gnucash_header_new (GnucashSheet *sheet)
item = gnome_canvas_item_new (group,
gnucash_header_get_type (),
"GnucashHeader::sheet", sheet,
"GnucashHeader::cursor_type",
GNUCASH_CURSOR_HEADER,
"GnucashHeader::cursor_row", 0,
NULL);
sheet->header_item = item;
@ -336,5 +428,3 @@ gnucash_header_new (GnucashSheet *sheet)
c-basic-offset: 8
End:
*/

View File

@ -31,9 +31,14 @@ typedef struct {
GnomeCanvasItem canvas_item;
GnucashSheet *sheet;
SheetBlockStyle *style;
int type;
int row;
GdkGC *gc;
GdkCursor *normal_cursor;
GdkCursor *resize_cursor;
} GnucashHeader;

View File

@ -80,9 +80,9 @@ item_edit_get_pixel_coords (ItemEdit *item_edit, int *x, int *y,
GnucashSheet *sheet = item_edit->sheet;
int xd, yd;
gnome_canvas_get_scroll_offsets (GNOME_CANVAS(sheet), &xd, &yd);
gnome_canvas_get_scroll_offsets (GNOME_CANVAS(sheet), NULL, &yd);
xd += gnucash_sheet_col_get_distance(sheet, sheet->left_block,
xd = gnucash_sheet_col_get_distance(sheet, sheet->left_block,
item_edit->virt_col)
+ sheet->left_block_offset;
yd += gnucash_sheet_row_get_distance (sheet, sheet->top_block,
@ -566,22 +566,11 @@ static void
item_edit_combo_toggled (GtkToggleButton *button, gpointer data)
{
ItemEdit *item_edit = ITEM_EDIT(data);
GtkArrowType arrow_type;
GtkShadowType shadow_type;
item_edit->show_list = gtk_toggle_button_get_active(button);
if (!item_edit->show_list) {
if (!item_edit->show_list)
item_edit_hide_list(item_edit);
arrow_type = GTK_ARROW_DOWN;
shadow_type = GTK_SHADOW_IN;
}
else {
arrow_type = GTK_ARROW_UP;
shadow_type = GTK_SHADOW_OUT;
}
gtk_arrow_set(item_edit->combo_toggle.arrow, arrow_type, shadow_type);
item_edit_configure (item_edit);
}
@ -655,12 +644,6 @@ item_edit_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
disconnect_combo_signals(item_edit);
gtk_arrow_set(item_edit->combo_toggle.arrow,
GTK_ARROW_DOWN, GTK_SHADOW_IN);
gtk_toggle_button_set_active
(item_edit->combo_toggle.combo_button, FALSE);
item_edit_hide_list(item_edit);
item_edit_hide_combo_toggle(item_edit);
}
@ -800,6 +783,11 @@ item_edit_show_list (ItemEdit *item_edit, gint x, gint y,
"height", (gdouble) height,
"anchor", anchor,
NULL);
gtk_arrow_set(item_edit->combo_toggle.arrow,
GTK_ARROW_UP, GTK_SHADOW_OUT);
gtk_widget_grab_focus(GTK_WIDGET(item_edit->item_list->clist));
}
@ -814,6 +802,14 @@ item_edit_hide_list (ItemEdit *item_edit)
/* safely out of the way */
gnome_canvas_item_set(GNOME_CANVAS_ITEM(item_edit->item_list),
"x", -10000.0, NULL);
gtk_arrow_set(item_edit->combo_toggle.arrow,
GTK_ARROW_DOWN, GTK_SHADOW_IN);
gtk_widget_grab_focus(GTK_WIDGET(item_edit->sheet));
gtk_toggle_button_set_active
(item_edit->combo_toggle.combo_button, FALSE);
}

View File

@ -261,6 +261,7 @@ gnc_item_list_new(GnomeCanvasGroup *parent)
item = gnome_canvas_item_new(parent, gnc_item_list_get_type(),
"widget", hbox,
"size_pixels", TRUE,
"x", -10000.0,
NULL);
item_list = GNC_ITEM_LIST(item);

View File

@ -35,7 +35,8 @@
#include "util.h"
#define DEFAULT_REGISTER_HEIGHT 400
#define DEFAULT_REGISTER_WIDTH 640
#define DEFAULT_REGISTER_WIDTH 630
#define DEFAULT_REGISTER_ROWS 15
static void gnucash_sheet_cell_set_from_table (GnucashSheet *sheet,
@ -50,10 +51,8 @@ static void gnucash_sheet_start_editing_at_cursor (GnucashSheet *sheet,
gboolean blankp,
gboolean cursorp);
static void gnucash_sheet_cursor_move (GnucashSheet *sheet,
gint virt_row, gint virt_col,
gint cell_row, gint cell_col,
gboolean changed_cells);
static gboolean gnucash_sheet_cursor_move (GnucashSheet *sheet,
gint phys_row, gint phys_col);
static void gnucash_sheet_deactivate_cursor_cell (GnucashSheet *sheet);
static void gnucash_sheet_activate_cursor_cell (GnucashSheet *sheet,
@ -65,8 +64,16 @@ static void gnucash_sheet_block_destroy (GnucashSheet *sheet, gint virt_row,
static void gnucash_sheet_update_adjustments (GnucashSheet *sheet);
/* Register signals */
enum
{
ACTIVATE_CURSOR,
LAST_SIGNAL
};
static GnomeCanvasClass *sheet_parent_class;
static GtkTableClass *register_parent_class;
static guint register_signals[LAST_SIGNAL];
gint
@ -124,7 +131,7 @@ gnucash_sheet_cursor_set (GnucashSheet *sheet, int virt_row, int virt_col,
}
void
gnucash_sheet_cursor_set_from_table (GnucashSheet *sheet)
gnucash_sheet_cursor_set_from_table (GnucashSheet *sheet, gncBoolean do_scroll)
{
Table *table;
gint cell_row, cell_col;
@ -139,27 +146,26 @@ gnucash_sheet_cursor_set_from_table (GnucashSheet *sheet)
p_row = table->current_cursor_phys_row;
p_col = table->current_cursor_phys_col;
if (p_row >= 0 && p_row < table->num_phys_rows
&& p_col >= 0 && p_col < table->num_phys_cols) {
if (p_row < 0 || p_row >= table->num_phys_rows ||
p_col < 0 || p_col >= table->num_phys_cols)
return;
cell_row = table->locators[p_row][p_col]->phys_row_offset;
cell_col = table->locators[p_row][p_col]->phys_col_offset;
if (do_scroll)
gnucash_sheet_make_cell_visible(sheet,
table->current_cursor_virt_row,
table->current_cursor_virt_col,
cell_row,
cell_col);
gnucash_sheet_update_adjustments(sheet);
gnucash_sheet_cursor_set(sheet,
table->current_cursor_virt_row,
table->current_cursor_virt_col,
cell_row,
cell_col);
}
}
void
@ -208,19 +214,16 @@ gnucash_sheet_deactivate_cursor_cell (GnucashSheet *sheet)
gnucash_sheet_stop_editing (sheet);
if (gnc_register_cell_valid (table, p_row, p_col)) {
old_text = gnucash_sheet_block_get_text(sheet, virt_row,
virt_col, cell_row,
cell_col);
new_text = gnc_table_leave_update(table, p_row, p_col,
old_text);
new_text = gnc_table_leave_update(table, p_row, p_col, old_text);
if (new_text)
gnucash_sheet_cell_set_from_table (sheet, virt_row,
virt_col, cell_row,
cell_col);
}
gnucash_sheet_redraw_block (sheet, virt_row, virt_col);
}
@ -236,8 +239,7 @@ gnucash_sheet_activate_cursor_cell (GnucashSheet *sheet,
int virt_row, virt_col, cell_row, cell_col;
SheetBlockStyle *style;
/* Hmm, this shouldn't happen, but let's be sure */
/* Sanity check */
if (sheet->editing)
gnucash_sheet_deactivate_cursor_cell (sheet);
@ -246,8 +248,14 @@ gnucash_sheet_activate_cursor_cell (GnucashSheet *sheet,
gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor),
&virt_row, &virt_col, &cell_row, &cell_col);
/* This should be a no-op */
wrapVerifyCursorPosition (table, p_row, p_col);
gnucash_cursor_get_phys (GNUCASH_CURSOR(sheet->cursor),
&p_row, &p_col);
gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor),
&virt_row, &virt_col, &cell_row, &cell_col);
style = gnucash_sheet_get_style (sheet, virt_row, virt_col);
if (style->cursor_type == GNUCASH_CURSOR_HEADER ||
!gnc_register_cell_valid (table, p_row, p_col) )
@ -266,18 +274,48 @@ gnucash_sheet_activate_cursor_cell (GnucashSheet *sheet,
}
static void
gnucash_sheet_cursor_move (GnucashSheet *sheet, gint virt_row, gint virt_col,
gint cell_row, gint cell_col,
gboolean changed_cells)
static gboolean
gnucash_sheet_cursor_move (GnucashSheet *sheet, gint phys_row, gint phys_col)
{
if (!gnucash_sheet_cell_valid (sheet,
virt_row, virt_col,
cell_row, cell_col))
return;
int old_virt_row, old_virt_col, old_cell_row, old_cell_col;
int virt_row, virt_col, cell_row, cell_col;
int old_phys_row, old_phys_col;
gboolean changed_cells;
Table *table;
table = sheet->table;
/* Get the old cursor position */
gnucash_cursor_get_phys (GNUCASH_CURSOR(sheet->cursor),
&old_phys_row, &old_phys_col);
gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor),
&old_virt_row, &old_virt_col,
&old_cell_row, &old_cell_col);
/* Turn off the editing controls */
gnucash_sheet_deactivate_cursor_cell (sheet);
/* Do the move. This may result in table restructuring due to
* commits, auto modes, etc. */
wrapVerifyCursorPosition (table, phys_row, phys_col);
/* A complete reload can leave us with editing back on */
if (sheet->editing)
gnucash_sheet_deactivate_cursor_cell (sheet);
/* Find out where we really landed. We have to get the new
* physical position as well, as the table may have been
* restructured. */
gnucash_cursor_get_phys (GNUCASH_CURSOR(sheet->cursor),
&phys_row, &phys_col);
gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor),
&virt_row, &virt_col,
&cell_row, &cell_col);
/* We should be at our new location now. Show it on screen and
* configure the cursor. */
gnucash_sheet_make_cell_visible (sheet,
virt_row, virt_col,
cell_row, cell_col);
@ -286,14 +324,14 @@ gnucash_sheet_cursor_move (GnucashSheet *sheet, gint virt_row, gint virt_col,
virt_row, virt_col,
cell_row, cell_col);
changed_cells =
(phys_row != old_phys_row) ||
(phys_col != old_phys_col);
/* Now turn on the editing controls. */
gnucash_sheet_activate_cursor_cell (sheet, changed_cells);
}
int
gnucash_sheet_can_move_cursor (GnucashSheet *gsheet)
{
return TRUE;
return changed_cells;
}
@ -440,24 +478,38 @@ gnucash_sheet_make_cell_visible (GnucashSheet *sheet,
}
/* FIXME: do the horizontal adjustment, too? */
static void
gnucash_sheet_update_adjustments (GnucashSheet *sheet)
{
GtkAdjustment *adj;
GtkAdjustment *vadj;
GtkAdjustment *hadj;
GnucashCursor *cursor;
g_return_if_fail (sheet != NULL);
g_return_if_fail (GNUCASH_IS_SHEET (sheet));
g_return_if_fail (sheet->vadj != NULL);
g_return_if_fail (sheet->hadj != NULL);
g_return_if_fail (sheet->cursor != NULL);
adj = sheet->vadj;
vadj = sheet->vadj;
hadj = sheet->hadj;
cursor = GNUCASH_CURSOR(sheet->cursor);
adj->lower = 1;
adj->upper = MAX(sheet->bottom_block, sheet->num_virt_rows);
adj->page_size = sheet->bottom_block - sheet->top_block + 1;
adj->page_increment = adj->page_size - 1;
vadj->lower = 1;
vadj->upper = MAX(sheet->bottom_block, sheet->num_virt_rows);
vadj->page_size = sheet->bottom_block - sheet->top_block + 1;
vadj->page_increment = vadj->page_size - 1;
gtk_signal_emit_by_name (GTK_OBJECT(adj), "changed");
gtk_signal_emit_by_name (GTK_OBJECT(vadj), "changed");
if (cursor->style) {
hadj->lower = 0;
hadj->upper = cursor->style->width;
hadj->page_size = GTK_WIDGET(sheet)->allocation.width;
hadj->page_increment = hadj->page_size;
hadj->step_increment = hadj->page_size / 100.0;
gtk_adjustment_changed (hadj);
}
}
@ -659,16 +711,37 @@ gnucash_sheet_create (Table *table)
* maybe just default the height?
*/
static gint
compute_optimal_width (Table *table)
compute_optimal_width (GnucashSheet *sheet)
{
SheetBlockStyle *style;
if ((sheet == NULL) || (sheet->cursor_style == NULL))
return DEFAULT_REGISTER_WIDTH;
style = sheet->cursor_style[GNUCASH_CURSOR_HEADER];
if ((style == NULL) || (style->widths == NULL))
return DEFAULT_REGISTER_WIDTH;
return DEFAULT_REGISTER_WIDTH;
}
/* Compute the height needed to show DEFAULT_REGISTER_ROWS rows */
static gint
compute_optimal_height (Table *table)
compute_optimal_height (GnucashSheet *sheet)
{
SheetBlockStyle *style;
if ((sheet == NULL) || (sheet->cursor_style == NULL))
return DEFAULT_REGISTER_HEIGHT;
style = sheet->cursor_style[GNUCASH_CURSOR_HEADER];
if ((style == NULL) || (style->pixel_heights == NULL))
return DEFAULT_REGISTER_HEIGHT;
return (style->pixel_heights[0][0] * DEFAULT_REGISTER_ROWS);
}
@ -677,8 +750,8 @@ gnucash_sheet_size_request (GtkWidget *widget, GtkRequisition *requisition)
{
GnucashSheet *sheet = GNUCASH_SHEET(widget);
requisition->width = compute_optimal_width (sheet->table);
requisition->height = compute_optimal_height (sheet->table);
requisition->width = compute_optimal_width (sheet);
requisition->height = compute_optimal_height (sheet);
}
@ -694,6 +767,9 @@ gnucash_sheet_modify_current_cell(GnucashSheet *sheet, const gchar *new_text)
char *newval;
char *change;
int current_position;
int new_position;
gnucash_cursor_get_phys(GNUCASH_CURSOR(sheet->cursor), &p_row, &p_col);
gnucash_cursor_get_virt(GNUCASH_CURSOR(sheet->cursor),
@ -710,8 +786,14 @@ gnucash_sheet_modify_current_cell(GnucashSheet *sheet, const gchar *new_text)
change = strdup(new_text);
assert((newval != NULL) && (change != NULL));
current_position =
gtk_editable_get_position (GTK_EDITABLE(sheet->entry));
new_position = current_position;
retval = gnc_table_modify_update (table, p_row, p_col,
old_text, change, newval);
old_text, change, newval,
&new_position);
gnucash_sheet_cell_set_from_table (sheet, v_row, v_col, c_row, c_col);
@ -721,6 +803,11 @@ gnucash_sheet_modify_current_cell(GnucashSheet *sheet, const gchar *new_text)
gtk_entry_set_text (GTK_ENTRY (sheet->entry), retval);
if(new_position != current_position) {
gtk_editable_set_position (GTK_EDITABLE(sheet->entry),
new_position);
}
gtk_signal_handler_unblock (GTK_OBJECT (sheet->entry),
sheet->insert_signal);
@ -780,7 +867,8 @@ gnucash_sheet_insert_cb (GtkWidget *widget, const gchar *new_text,
strncpy (change, new_text, new_text_length);
retval = gnc_table_modify_update (table, p_row, p_col, old_text,
change, newval);
change, newval,
position);
gnucash_sheet_cell_set_from_table (sheet, v_row, v_col, c_row, c_col);
@ -793,9 +881,9 @@ gnucash_sheet_insert_cb (GtkWidget *widget, const gchar *new_text,
gtk_signal_handler_block(GTK_OBJECT (sheet->entry),
sheet->insert_signal);
gtk_entry_set_text (GTK_ENTRY (sheet->entry), retval);
gtk_editable_set_position (GTK_EDITABLE(sheet->entry),
*position + new_text_length + 1);
gtk_signal_handler_unblock (GTK_OBJECT (sheet->entry),
sheet->insert_signal);
@ -828,6 +916,7 @@ gnucash_sheet_delete_cb (GtkWidget *widget,
const char *old_text;
char *newval = NULL;
const char *retval = NULL;
int new_position = start_pos;
gnucash_cursor_get_phys (GNUCASH_CURSOR(sheet->cursor),
&p_row, &p_col);
@ -850,7 +939,7 @@ gnucash_sheet_delete_cb (GtkWidget *widget,
strcat (newval, &old_text[end_pos]);
retval = gnc_table_modify_update (table, p_row, p_col, old_text,
NULL, newval);
NULL, newval, &new_position);
gnucash_sheet_cell_set_from_table (sheet, v_row, v_col, c_row, c_col);
@ -864,6 +953,12 @@ gnucash_sheet_delete_cb (GtkWidget *widget,
gtk_signal_handler_block ( GTK_OBJECT (sheet->entry),
sheet->insert_signal);
gtk_entry_set_text (GTK_ENTRY (sheet->entry), retval);
if(new_position != start_pos) {
gtk_editable_set_position (GTK_EDITABLE(sheet->entry),
new_position);
}
gtk_signal_handler_unblock ( GTK_OBJECT (sheet->entry),
sheet->insert_signal);
@ -895,6 +990,7 @@ gnucash_sheet_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
sheet->cursor_style[i]);
gnucash_cursor_configure (GNUCASH_CURSOR (sheet->cursor));
item_edit_configure (ITEM_EDIT(sheet->item_editor));
gnucash_header_reconfigure (GNUCASH_HEADER(sheet->header_item));
gnucash_sheet_update_adjustments (sheet);
}
@ -957,18 +1053,15 @@ static gint
gnucash_button_press_event (GtkWidget *widget, GdkEventButton *event)
{
GnucashSheet *sheet;
gboolean changed_cells;
int xoffset, yoffset;
gboolean changed_cells;
int x, y;
/* physical coordinates */
int current_p_row, current_p_col, new_p_row, new_p_col;
/* virtual coordinates */
int current_v_row, current_v_col, new_v_row, new_v_col;
/* cell coordinates */
int current_c_row, current_c_col, new_c_row, new_c_col;
int new_v_row, new_v_col, new_c_row, new_c_col;
Table *table;
gncBoolean exit_register;
@ -978,7 +1071,7 @@ gnucash_button_press_event (GtkWidget *widget, GdkEventButton *event)
g_return_val_if_fail(event != NULL, TRUE);
if (event->type != GDK_BUTTON_PRESS || event->button != 1)
return TRUE;
return FALSE;
sheet = GNUCASH_SHEET (widget);
table = sheet->table;
@ -989,10 +1082,6 @@ gnucash_button_press_event (GtkWidget *widget, GdkEventButton *event)
gnucash_cursor_get_phys (GNUCASH_CURSOR(sheet->cursor),
&current_p_row, &current_p_col);
gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor),
&current_v_row, &current_v_col,
&current_c_row, &current_c_col);
x = xoffset + event->x;
y = yoffset + event->y;
@ -1023,25 +1112,12 @@ gnucash_button_press_event (GtkWidget *widget, GdkEventButton *event)
if (!gnc_register_cell_valid (table, new_p_row, new_p_col))
return TRUE;
new_v_row = table->locators[new_p_row][new_p_col]->virt_row;
new_v_col = table->locators[new_p_row][new_p_col]->virt_col;
new_c_row = table->locators[new_p_row][new_p_col]->phys_row_offset;
new_c_col = table->locators[new_p_row][new_p_col]->phys_col_offset;
changed_cells =
(new_v_row != current_v_row) ||
(new_v_col != current_v_col) ||
(new_c_row != current_c_row) ||
(new_c_col != current_c_col);
gnucash_sheet_cursor_move (sheet,
new_v_row, new_v_col,
new_c_row, new_c_col,
changed_cells);
changed_cells = gnucash_sheet_cursor_move (sheet,
new_p_row, new_p_col);
item_edit_set_cursor_pos (ITEM_EDIT(sheet->item_editor),
x, y, changed_cells);
return TRUE;
}
@ -1049,23 +1125,13 @@ gnucash_button_press_event (GtkWidget *widget, GdkEventButton *event)
static gint
gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event)
{
Table *table;
GnucashSheet *sheet;
CellBlock *header;
int direction = 0;
gboolean pass_on = FALSE;
gboolean changed_cells;
/* physical coordinates */
int current_p_row, current_p_col, new_p_row, new_p_col;
/* virtual coordinates */
int current_v_row, current_v_col, new_v_row, new_v_col;
/* cell coordinates */
int current_c_row, current_c_col, new_c_row, new_c_col;
Table *table;
gncBoolean exit_register;
int current_p_row, current_p_col, new_p_row, new_p_col;
g_return_val_if_fail(widget != NULL, TRUE);
g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE);
@ -1078,18 +1144,23 @@ gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event)
gnucash_cursor_get_phys (GNUCASH_CURSOR(sheet->cursor),
&current_p_row, &current_p_col);
gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor),
&current_v_row, &current_v_col,
&current_c_row, &current_c_col);
/* Calculate tentative physical values */
switch (event->keyval) {
case GDK_Tab:
case GDK_ISO_Left_Tab:
if (event->state & GDK_SHIFT_MASK) {
direction = GNC_TABLE_TRAVERSE_LEFT;
new_p_row = current_p_row;
new_p_col = MAX(current_p_col - 1, 0);
}
else {
direction = GNC_TABLE_TRAVERSE_RIGHT;
new_p_row = current_p_row;
new_p_col = MIN(current_p_col + 1,
table->num_phys_cols - 1);
}
break;
case GDK_KP_Page_Up:
case GDK_Page_Up:
direction = GNC_TABLE_TRAVERSE_UP;
new_p_col = 0;
@ -1097,6 +1168,7 @@ gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event)
sheet->vadj->page_increment,
header->numRows);
break;
case GDK_KP_Page_Down:
case GDK_Page_Down:
direction = GNC_TABLE_TRAVERSE_DOWN;
new_p_col = 0;
@ -1104,12 +1176,14 @@ gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event)
sheet->vadj->page_increment,
table->num_phys_rows - 1);
break;
case GDK_KP_Up:
case GDK_Up:
direction = GNC_TABLE_TRAVERSE_UP;
new_p_col = current_p_col;
new_p_row = MAX(current_p_row - 1,
header->numRows);
break;
case GDK_KP_Down:
case GDK_Down:
direction = GNC_TABLE_TRAVERSE_DOWN;
new_p_col = current_p_col;
@ -1135,48 +1209,92 @@ gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event)
(table, current_p_row, current_p_col,
direction, &new_p_row, &new_p_col);
/* If that would leave the register and we're not going right, abort */
if (exit_register && (direction != GNC_TABLE_TRAVERSE_RIGHT))
return TRUE;
/* Hack: try to wrap around. Shouldn't splitreg.c do this? */
if (exit_register) {
new_p_row = table->rev_locators[current_v_row]
[current_v_col]->phys_row;
new_p_col = 0;
exit_register = gnc_table_traverse_update
(table, current_p_row, current_p_col,
GNC_TABLE_TRAVERSE_POINTER, &new_p_row, &new_p_col);
}
/* Give up */
if (exit_register)
/* If that would leave the register and we're not tabbing, abort */
if (exit_register &&
direction != GNC_TABLE_TRAVERSE_RIGHT &&
direction != GNC_TABLE_TRAVERSE_LEFT)
return TRUE;
/* Shouldn't gnc_table_traverse_update fill in valid cells ? */
if (!gnc_register_cell_valid (table, new_p_row, new_p_col))
return TRUE;
new_v_row = table->locators[new_p_row][new_p_col]->virt_row;
new_v_col = table->locators[new_p_row][new_p_col]->virt_col;
new_c_row = table->locators[new_p_row][new_p_col]->phys_row_offset;
new_c_col = table->locators[new_p_row][new_p_col]->phys_col_offset;
changed_cells =
(new_v_row != current_v_row) ||
(new_v_col != current_v_col) ||
(new_c_row != current_c_row) ||
(new_c_col != current_c_col);
gnucash_sheet_cursor_move(sheet, new_v_row, new_v_col,
new_c_row, new_c_col, changed_cells);
gnucash_sheet_cursor_move (sheet, new_p_row, new_p_col);
/* return true because we handled the key press */
return TRUE;
}
static void
gnucash_sheet_goto_virt_row_col (GnucashSheet *sheet,
int new_v_row, int new_v_col)
{
Table *table;
gncBoolean exit_register;
int current_p_row, current_p_col, new_p_row, new_p_col;
g_return_if_fail(GNUCASH_IS_SHEET(sheet));
table = sheet->table;
gnucash_cursor_get_phys (GNUCASH_CURSOR(sheet->cursor),
&current_p_row, &current_p_col);
new_p_row = table->rev_locators[new_v_row][new_v_col]->phys_row;
new_p_col = table->rev_locators[new_v_row][new_v_col]->phys_col;
/* It's not really a pointer traverse, but it seems the most
* appropriate here. */
exit_register = gnc_table_traverse_update
(table, current_p_row, current_p_col,
GNC_TABLE_TRAVERSE_POINTER, &new_p_row, &new_p_col);
if (exit_register)
return;
/* Shouldn't gnc_table_traverse_update fill in valid cells ? */
if (!gnc_register_cell_valid (table, new_p_row, new_p_col))
return;
gnucash_sheet_cursor_move (sheet, new_p_row, new_p_col);
}
void
gnucash_register_goto_virt_row_col (GnucashRegister *reg, int v_row, int v_col)
{
GnucashSheet *sheet;
g_return_if_fail(GNUCASH_IS_REGISTER(reg));
sheet = GNUCASH_SHEET(reg->sheet);
gnucash_sheet_goto_virt_row_col(sheet, v_row, v_col);
}
void
gnucash_register_goto_next_virt_row (GnucashRegister *reg)
{
GnucashSheet *sheet;
int v_row, v_col, c_row, c_col;
g_return_if_fail(GNUCASH_IS_REGISTER(reg));
sheet = GNUCASH_SHEET(reg->sheet);
gnucash_cursor_get_virt(GNUCASH_CURSOR(sheet->cursor),
&v_row, &v_col, &c_row, &c_col);
v_row++;
if (v_row >= sheet->num_virt_rows)
return;
gnucash_sheet_goto_virt_row_col(sheet, v_row, v_col);
}
SheetBlock *
gnucash_sheet_get_block (GnucashSheet *sheet, gint virt_row, gint virt_col)
{
@ -1213,7 +1331,7 @@ gnucash_sheet_block_get_text (GnucashSheet *sheet, gint virt_row,
}
void
static void
gnucash_sheet_block_clear_entries (SheetBlock *block)
{
gint i,j;
@ -1230,6 +1348,13 @@ gnucash_sheet_block_clear_entries (SheetBlock *block)
g_free (block->fg_colors[i]);
g_free (block->bg_colors[i]);
}
g_free (block->entries);
g_free (block->fg_colors);
g_free (block->bg_colors);
block->entries = NULL;
block->fg_colors = NULL;
block->bg_colors = NULL;
}
}
@ -1288,10 +1413,7 @@ gnucash_sheet_block_set_from_table (GnucashSheet *sheet, gint virt_row,
table = sheet->table;
if (block->entries) {
gnucash_sheet_block_clear_entries (block);
g_free (block->entries);
}
if (block->style)
gnucash_style_unref (block->style);
@ -1364,9 +1486,6 @@ gnucash_sheet_block_destroy (GnucashSheet *sheet, gint virt_row, gint virt_col)
if (block) {
gnucash_sheet_block_clear_entries (block);
g_free (block->entries);
g_free (block->fg_colors);
g_free (block->bg_colors);
if (block->style)
gnucash_style_unref (block->style);
@ -1471,14 +1590,11 @@ gnucash_sheet_table_load (GnucashSheet *sheet)
for (j = 0; j < table->num_virt_cols; j++)
gnucash_sheet_block_set_from_table (sheet, i, j);
/* FIXME: try to keep the cursor row in the same visual position */
/* FIXME: Probably we should set the bottom row instead */
gnucash_sheet_set_top_row (sheet, 0, GNUCASH_ALIGN_TOP);
gnucash_sheet_set_top_row (sheet, sheet->top_block, GNUCASH_ALIGN_TOP);
gnucash_sheet_compute_visible_range(sheet);
gnucash_sheet_cursor_set_from_table (sheet);
gnucash_sheet_cursor_set_from_table (sheet, TRUE);
gnucash_sheet_activate_cursor_cell (sheet, TRUE);
}
@ -1550,6 +1666,7 @@ gnucash_sheet_init (GnucashSheet *sheet)
sheet->item_editor = NULL;
sheet->entry = NULL;
sheet->editing = FALSE;
sheet->width = 0;
sheet->blocks = g_hash_table_new(block_hash, block_compare);
}
@ -1643,6 +1760,18 @@ gnucash_register_class_init (GnucashRegisterClass *class)
table_class = (GtkTableClass *) class;
register_parent_class = gtk_type_class (gtk_table_get_type ());
register_signals[ACTIVATE_CURSOR] =
gtk_signal_new("activate_cursor",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET(GnucashRegisterClass,
activate_cursor),
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
gtk_object_class_add_signals(object_class, register_signals,
LAST_SIGNAL);
}
@ -1685,6 +1814,42 @@ gnucash_register_get_type (void)
}
static gint
gnucash_register_key_press_cb(GtkWidget *widget, GdkEventKey *event,
gpointer data)
{
GnucashRegister *reg = GNUCASH_REGISTER(data);
g_return_val_if_fail(widget != NULL, TRUE);
g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE);
g_return_val_if_fail(event != NULL, TRUE);
switch (event->keyval) {
case GDK_Return:
gtk_signal_emit_by_name(GTK_OBJECT(reg),
"activate_cursor");
return TRUE;
break;
default:
return FALSE;
}
}
void
gnucash_register_attach_popup(GnucashRegister *reg, GtkWidget *popup,
gpointer data)
{
g_return_if_fail(GNUCASH_IS_REGISTER(reg));
g_return_if_fail(GTK_IS_WIDGET(popup));
g_return_if_fail(reg->sheet != NULL);
g_return_if_fail(reg->header_canvas != NULL);
gnome_popup_menu_attach(popup, reg->sheet, data);
gnome_popup_menu_attach(popup, reg->header_canvas, data);
}
GtkWidget *
gnucash_register_new (Table *table)
{
@ -1701,6 +1866,10 @@ gnucash_register_new (Table *table)
reg->sheet = sheet;
GNUCASH_SHEET(sheet)->reg = widget;
gtk_signal_connect (GTK_OBJECT(sheet), "key_press_event",
GTK_SIGNAL_FUNC(gnucash_register_key_press_cb),
reg);
header_canvas = gnucash_header_new (GNUCASH_SHEET(sheet));
reg->header_canvas = header_canvas;
@ -1725,7 +1894,7 @@ gnucash_register_new (Table *table)
reg->vscrollbar = scrollbar;
gtk_widget_show(scrollbar);
scrollbar = gtk_hscrollbar_new(NULL);
scrollbar = gtk_hscrollbar_new(GNUCASH_SHEET(sheet)->hadj);
gtk_table_attach (GTK_TABLE(widget), GTK_WIDGET(scrollbar),
0, 1, 2, 3,
GTK_EXPAND | GTK_SHRINK | GTK_FILL,

View File

@ -56,6 +56,8 @@ typedef enum {
} GnucashSheetAlignment;
typedef struct _CellLayoutInfo CellLayoutInfo;
typedef struct
{
gint nrows;
@ -75,12 +77,16 @@ typedef struct
row/column not displayed */
gint **pixel_widths;
gchar ***labels;
CellLayoutInfo **layout_info;
gchar ***labels; /* for the header */
GdkFont *header_font;
GtkJustification **alignments;
GdkFont ***fonts;
GdkColor ***active_bg_color;
GdkColor ***inactive_bg_color;
@ -141,6 +147,8 @@ typedef struct {
gint top_block_offset;
gint left_block_offset;
gint width; /* the width in pixels of the sheet */
gint alignment;
gint editing;
@ -194,12 +202,10 @@ void gnucash_sheet_cursor_set (GnucashSheet *gsheet,
const char * gnucash_sheet_modify_current_cell(GnucashSheet *sheet,
const gchar *new_text);
void gnucash_sheet_cursor_set_from_table (GnucashSheet *sheet);
void gnucash_sheet_cursor_set_from_table (GnucashSheet *sheet,
gncBoolean do_scroll);
void gnucash_sheet_move_cursor (GnucashSheet *sheet,
int col, int row);
int gnucash_sheet_can_move_cursor (GnucashSheet *sheet);
void gnucash_sheet_move_cursor (GnucashSheet *sheet, int col, int row);
void gnucash_sheet_set_cursor_bounds (GnucashSheet *sheet,
int start_col, int start_row,
@ -211,6 +217,14 @@ void gnucash_sheet_make_cell_visible (GnucashSheet *sheet,
gint virt_row, gint virt_col,
gint cell_row, gint cell_col);
void gnucash_register_goto_virt_row_col (GnucashRegister *reg,
int v_row, int v_col);
void gnucash_register_goto_next_virt_row (GnucashRegister *reg);
void gnucash_register_attach_popup(GnucashRegister *reg, GtkWidget *popup,
gpointer data);
typedef struct {
GnomeCanvasClass parent_class;
@ -230,6 +244,9 @@ typedef struct {
typedef struct {
GtkTableClass parent_class;
void (*activate_cursor) (GnucashRegister *reg);
} GnucashRegisterClass;
#endif

View File

@ -29,6 +29,70 @@
GdkFont *gnucash_default_font = NULL;
GdkFont *gnucash_italic_font = NULL;
#define CHARS_FIXED (1 << 0) /* We use this to set the cell width */
#define PIXELS_FIXED (1 << 1) /* We use this as the cell width */
#define RIGHT_ALIGNED (1 << 2) /* Right align with a specified cell */
#define LEFT_ALIGNED (1 << 3) /* Left align with a specified cell */
#define FILL (1 << 4) /* Allow this cell to expand to fill */
#define CHARS_MIN (1 << 5) /* Cell is no smaller than this,
but won't expand further */
#define PIXELS_MIN (1 << 6)
#define CHARS_MAX (1 << 7) /* Cell is no larger that this,
but won't shrink further */
#define PIXELS_MAX (1 << 8)
#define STRING_FIXED (1 << 9) /* Fit this string */
#define STRING_MIN (1 << 10) /* Fit this string */
#define SAME_SIZE (1 << 11) /* Make the cell the same size
as a specified cell */
struct _CellLayoutInfo
{
unsigned int flags;
int chars_width;
int pixels_width;
int chars_min;
int pixels_min;
int chars_max;
int pixels_max;
short left_align_r; /* the alignment cells */
short left_align_c;
short right_align_r;
short right_align_c;
short size_r; /* same size as this cell */
short size_c;
char *string;
};
#define SET_CELL_PERC(r, n) { \
for (i=0; i<r; i++) \
for (j=0; j<n; j++) \
style->cell_perc[i][j] = perc[i][j]; \
}
#define SET_CELL_DATA(r, n) { \
for (i=0; i<r; i++) \
for (j=0; j<n; j++) {\
style->cell_perc[i][j] = perc[i][j]; \
style->layout_info[i][j].flags = li[i][j].flags; \
style->layout_info[i][j].chars_width = li[i][j].chars_width;\
style->layout_info[i][j].pixels_width = li[i][j].pixels_width;\
style->layout_info[i][j].chars_min = li[i][j].chars_min;\
style->layout_info[i][j].pixels_min = li[i][j].pixels_min;\
style->layout_info[i][j].chars_max = li[i][j].chars_max;\
style->layout_info[i][j].pixels_max = li[i][j].pixels_max;\
style->layout_info[i][j].right_align_r = li[i][j].right_align_r;\
style->layout_info[i][j].right_align_c = li[i][j].right_align_c;\
style->layout_info[i][j].left_align_r = li[i][j].left_align_r;\
style->layout_info[i][j].left_align_c = li[i][j].left_align_c; \
style->layout_info[i][j].size_r = li[i][j].size_r; \
style->layout_info[i][j].size_c = li[i][j].size_c; \
style->layout_info[i][j].string = g_strdup(li[i][j].string); \
} \
}
/* FIXME: read this from a config file */
/* keep this in sync with splitreg.c */
void
@ -49,53 +113,89 @@ gnucash_style_layout_init (SheetBlockStyle *style)
switch (style->cursor_type) {
case GNUCASH_CURSOR_HEADER:
case GNUCASH_CURSOR_SINGLE:
style->cell_perc [0][0] = 0.10;
style->cell_perc [0][1] = 0.07;
style->cell_perc [0][2] = 0.15;
style->cell_perc [0][3] = 0.30;
style->cell_perc [0][4] = 0.02;
style->cell_perc [0][5] = 0.12;
style->cell_perc [0][6] = 0.12;
style->cell_perc [0][7] = 0.12;
{
int i, j;
double perc[1][8] = {{0.10, 0.07, 0.15, 0.30, 0.02, 0.12, 0.12, 0.12}};
CellLayoutInfo li[1][8] =
{{{STRING_FIXED, 0, 0,0,0,0,0,0,0,0,0, 0, 0, " 88/88/8888"},
{CHARS_MIN | CHARS_MAX, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL},
{STRING_MIN | CHARS_MAX | FILL, 0, 0, 0 ,0, 10 ,0,0,0,0,0, 0, 0, "Transfer From"},
{CHARS_MIN | FILL, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL},
{STRING_FIXED, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"},
{CHARS_MIN | CHARS_MAX | FILL, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
}};
SET_CELL_DATA (1, 8);
}
break;
case GNUCASH_CURSOR_DOUBLE:
style->cell_perc [0][0] = 0.10;
style->cell_perc [0][1] = 0.07;
style->cell_perc [0][2] = 0.15;
style->cell_perc [0][3] = 0.30;
style->cell_perc [0][4] = 0.02;
style->cell_perc [0][5] = 0.12;
style->cell_perc [0][6] = 0.12;
style->cell_perc [0][7] = 0.12;
{
int i, j;
double perc[2][8] = {{0.10, 0.07, 0.15, 0.30, 0.02, 0.12, 0.12, 0.12},
{0.10, 0.07, 0.15, 0.68, 0.0, 0.0, 0.0, 0.0}};
CellLayoutInfo li[2][8] =
{{{STRING_FIXED, 0, 0,0,0,0,0,0,0,0,0, 0, 0, " 88/88/8888"},
{CHARS_MIN | CHARS_MAX, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL},
{STRING_MIN | CHARS_MAX | FILL, 0, 0, 0 ,0, 10 ,0,0,0,0,0, 0, 0, "Transfer From"},
{CHARS_MIN | FILL, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL},
{STRING_FIXED, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"},
{CHARS_MIN | CHARS_MAX | FILL, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}},
{{LEFT_ALIGNED|RIGHT_ALIGNED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
{LEFT_ALIGNED|RIGHT_ALIGNED, 0, 0, 0,0,0,0,0,1,0,1, 0,0, NULL},
{LEFT_ALIGNED|RIGHT_ALIGNED, 0, 0, 0,0,0,0,0,2,0,2, 0,0, NULL},
{LEFT_ALIGNED|RIGHT_ALIGNED, 0, 0, 0,0,0,0,0,3,0,7, 0,0, NULL},
{PIXELS_FIXED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
{PIXELS_FIXED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
{PIXELS_FIXED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
{PIXELS_FIXED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
}};
SET_CELL_DATA (2, 8);
}
style->cell_perc [1][0] = 0.10;
style->cell_perc [1][1] = 0.07;
style->cell_perc [1][2] = 0.15;
style->cell_perc [1][3] = 0.68;
style->cell_perc [1][4] = 0.0;
style->cell_perc [1][5] = 0.0;
style->cell_perc [1][6] = 0.0;
style->cell_perc [1][7] = 0.0;
break;
case GNUCASH_CURSOR_TRANS:
style->cell_perc [0][0] = 0.10;
style->cell_perc [0][1] = 0.07;
style->cell_perc [0][2] = 0.15;
style->cell_perc [0][3] = 0.30;
style->cell_perc [0][4] = 0.02;
style->cell_perc [0][5] = 0.12;
style->cell_perc [0][6] = 0.12;
style->cell_perc [0][7] = 0.12;
{
int i, j;
double perc[1][8] = {{0.10, 0.07, 0.15, 0.30, 0.02, 0.12, 0.12, 0.12}};
CellLayoutInfo li[1][8] =
{{{STRING_FIXED, 0, 0,0,0,0,0,0,0,0,0, 0, 0, " 88/88/8888"},
{CHARS_MIN | CHARS_MAX, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL},
{STRING_MIN | CHARS_MAX | FILL, 0, 0, 0 ,0, 10 ,0,0,0,0,0, 0, 0, "Transfer From"},
{CHARS_MIN | FILL, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL},
{STRING_FIXED, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"},
{CHARS_MIN | CHARS_MAX | FILL, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
}};
SET_CELL_DATA (1, 8);
}
break;
case GNUCASH_CURSOR_SPLIT:
style->cell_perc [0][0] = 0.10;
style->cell_perc [0][1] = 0.07;
style->cell_perc [0][2] = 0.15;
style->cell_perc [0][3] = 0.32;
style->cell_perc [0][4] = 0.0;
style->cell_perc [0][5] = 0.12;
style->cell_perc [0][6] = 0.12;
style->cell_perc [0][7] = 0.12;
{
int i, j;
double perc[1][8] = {{0.10, 0.07, 0.15, 0.30, 0.02, 0.12, 0.12, 0.12}};
CellLayoutInfo li[1][8] =
{{{STRING_FIXED, 0, 0,0,0,0,0,0,0,0,0, 0, 0, " 88/88/8888"},
{CHARS_MIN | CHARS_MAX, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL},
{STRING_MIN | CHARS_MAX | FILL, 0, 0, 0 ,0, 10 ,0,0,0,0,0, 0, 0, "Transfer From"},
{CHARS_MIN | FILL, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL},
{STRING_FIXED, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"},
{CHARS_MIN | CHARS_MAX | FILL, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
}};
SET_CELL_DATA (1, 8);
}
break;
default:
break;
@ -108,57 +208,104 @@ gnucash_style_layout_init (SheetBlockStyle *style)
{
case GNUCASH_CURSOR_HEADER:
case GNUCASH_CURSOR_SINGLE:
style->cell_perc [0][0] = 0.09;
style->cell_perc [0][1] = 0.06;
style->cell_perc [0][2] = 0.11;
style->cell_perc [0][3] = 0.23;
style->cell_perc [0][4] = 0.01;
style->cell_perc [0][5] = 0.10;
style->cell_perc [0][6] = 0.10;
style->cell_perc [0][7] = 0.07;
style->cell_perc [0][8] = 0.07;
style->cell_perc [0][9] = 0.07;
style->cell_perc [0][10] = 0.09;
{
int i, j;
double perc[1][11] = {{0.09, 0.06, 0.11, 0.23, 0.01, 0.10, 0.10, 0.07, 0.07, 0.07, 0.09}};
CellLayoutInfo li[1][11] =
{{{STRING_FIXED, 0, 0,0,0,0,0,0,0,0,0, 0, 0, " 88/88/8888"},
{CHARS_MIN | CHARS_MAX, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL},
{STRING_MIN | CHARS_MAX | FILL, 0, 0, 0 ,0, 10 ,0,0,0,0,0, 0, 0, "Transfer From"},
{CHARS_MIN | FILL, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL},
{STRING_FIXED, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"},
{CHARS_MIN | CHARS_MAX | FILL, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
}};
SET_CELL_DATA (1, 11);
}
break;
case GNUCASH_CURSOR_DOUBLE:
style->cell_perc [0][0] = 0.09;
style->cell_perc [0][1] = 0.06;
style->cell_perc [0][2] = 0.11;
style->cell_perc [0][3] = 0.23;
style->cell_perc [0][4] = 0.01;
style->cell_perc [0][5] = 0.10;
style->cell_perc [0][6] = 0.10;
style->cell_perc [0][7] = 0.07;
style->cell_perc [0][8] = 0.07;
style->cell_perc [0][9] = 0.07;
style->cell_perc [0][10] = 0.09;
{
int i, j;
double perc[2][11] = {{0.09, 0.06, 0.11, 0.23, 0.01, 0.10, 0.10, 0.07, 0.07, 0.07, 0.09},
{0.0, 0.15, 0.11, 0.74, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}};
CellLayoutInfo li[2][11] =
{{{STRING_FIXED, 0, 0,0,0,0,0,0,0,0,0, 0, 0, " 88/88/8888"},
{CHARS_MIN | CHARS_MAX, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL},
{STRING_MIN | CHARS_MAX | FILL, 0, 0, 0 ,0, 10 ,0,0,0,0,0, 0, 0, "Transfer From"},
{CHARS_MIN | FILL, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL},
{STRING_FIXED, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"},
{CHARS_MIN | CHARS_MAX | FILL, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}},
{{LEFT_ALIGNED|RIGHT_ALIGNED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
{LEFT_ALIGNED|RIGHT_ALIGNED, 0, 0, 0,0,0,0,0,1,0,1, 0,0, NULL},
{LEFT_ALIGNED|RIGHT_ALIGNED, 0, 0, 0,0,0,0,0,2,0,2, 0,0, NULL},
{LEFT_ALIGNED|RIGHT_ALIGNED, 0, 0, 0,0,0,0,0,3,0,10, 0,0, NULL},
{PIXELS_FIXED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
{PIXELS_FIXED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
{PIXELS_FIXED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
{PIXELS_FIXED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
{PIXELS_FIXED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
{PIXELS_FIXED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
{PIXELS_FIXED, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL},
}};
SET_CELL_DATA (2, 11);
}
style->cell_perc [1][1] = 0.15;
style->cell_perc [1][2] = 0.11;
style->cell_perc [1][3] = 0.74;
break;
case GNUCASH_CURSOR_TRANS:
style->cell_perc [0][0] = 0.09;
style->cell_perc [0][1] = 0.06;
style->cell_perc [0][2] = 0.11;
style->cell_perc [0][3] = 0.23;
style->cell_perc [0][4] = 0.01;
style->cell_perc [0][5] = 0.10;
style->cell_perc [0][6] = 0.10;
style->cell_perc [0][7] = 0.07;
style->cell_perc [0][8] = 0.07;
style->cell_perc [0][9] = 0.07;
style->cell_perc [0][10] = 0.09;
{
int i, j;
double perc[1][11] = {{0.09, 0.06, 0.11, 0.23, 0.01, 0.10, 0.10, 0.07, 0.07, 0.07, 0.09}};
CellLayoutInfo li[1][11] =
{{{STRING_FIXED, 0, 0,0,0,0,0,0,0,0,0, 0, 0, " 88/88/8888"},
{CHARS_MIN | CHARS_MAX, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL},
{STRING_MIN | CHARS_MAX | FILL, 0, 0, 0 ,0, 10 ,0,0,0,0,0, 0, 0, "Transfer From"},
{CHARS_MIN | FILL, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL},
{STRING_FIXED, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"},
{CHARS_MIN | CHARS_MAX | FILL, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
}};
SET_CELL_DATA (1, 11);
}
break;
case GNUCASH_CURSOR_SPLIT:
style->cell_perc [0][0] = 0.0;
style->cell_perc [0][1] = 0.15;
style->cell_perc [0][2] = 0.11;
style->cell_perc [0][3] = 0.24;
style->cell_perc [0][4] = 0.0;
style->cell_perc [0][5] = 0.10;
style->cell_perc [0][6] = 0.10;
style->cell_perc [0][7] = 0.36;
{
int i, j;
double perc[1][11] = {{0.0, 0.15, 0.11, 0.24, 0.0, 0.10, 0.10, 0.36, 0.0, 0.0, 0.0}};
CellLayoutInfo li[1][11] =
{{{STRING_FIXED, 0, 0,0,0,0,0,0,0,0,0, 0, 0, " 88/88/8888"},
{CHARS_MIN | CHARS_MAX, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL},
{STRING_MIN | CHARS_MAX | FILL, 0, 0, 0 ,0, 10 ,0,0,0,0,0, 0, 0, "Transfer From"},
{CHARS_MIN | FILL, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL},
{STRING_FIXED, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"},
{CHARS_MIN | CHARS_MAX | FILL, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
{SAME_SIZE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL},
}};
SET_CELL_DATA (1, 11);
}
break;
}
default:
@ -166,12 +313,289 @@ gnucash_style_layout_init (SheetBlockStyle *style)
}
}
static int
compute_row_width (SheetBlockStyle *style, int row, int col1, int col2)
{
int j;
int width = 0;
col1 = MAX(0, col1);
col2 = MIN(col2, style->ncols-1);
for (j = col1; j <= col2; j++)
width += style->pixel_widths[row][j];
return width;
}
/*
* This sets the initial sizes of the cells, based on cell_perc's and the
* width allocation of the sheet
*/
static void
set_dimensions_pass_one (GnucashSheet *sheet, SheetBlockStyle *style, int row)
{
int i = row, j;
for (j = 0; j < style->ncols; j++) {
style->pixel_widths[i][j] = style->cell_perc[i][j] * style->width + 0.5;
style->pixel_heights[i][j] =
style->fonts[i][j]->ascent +
style->fonts[i][j]->descent +
2*CELL_VPADDING;
}
style->height += style->pixel_heights[i][0];
}
#define C_WIDTH gdk_string_measure (style->fonts[i][j], "X")
/* Now we add/subtract what's required to/from individual cells,
* taking into account min/max char/pixel/string widths
*
* At the end we set all SAME_SIZE cells.
*/
static void
set_dimensions_pass_two (GnucashSheet *sheet, SheetBlockStyle *style, int row)
{
int i = row, j;
unsigned int flags;
for (j = 0; j < style->ncols; j++) {
flags = style->layout_info[i][j].flags;
if (flags & CHARS_FIXED) {
style->pixel_widths[i][j] =
style->layout_info[i][j].chars_width * C_WIDTH + 2*CELL_HPADDING;
style->layout_info[i][j].pixels_width = style->pixel_widths[i][j];
style->layout_info[i][j].flags |= PIXELS_FIXED;
}
else if (flags & PIXELS_FIXED) {
style->pixel_widths[i][j] = style->layout_info[i][j].pixels_width;
}
else if ((flags & STRING_FIXED) && style->layout_info[i][j].string){
style->pixel_widths[i][j] =
gdk_string_measure (style->fonts[i][j], style->layout_info[i][j].string) + 2*CELL_HPADDING;
style->layout_info[i][j].pixels_width = style->pixel_widths[i][j];
style->layout_info[i][j].flags |= PIXELS_FIXED;
}
if (flags & CHARS_MIN) {
style->layout_info[i][j].pixels_min =
style->layout_info[i][j].chars_min * C_WIDTH+ 2*CELL_HPADDING;
style->layout_info[i][j].flags |= PIXELS_MIN;
style->pixel_widths[i][j] =
MAX (style->layout_info[i][j].pixels_min, style->pixel_widths[i][j]);
}
else if (flags & PIXELS_MIN) {
style->pixel_widths[i][j] =
MAX (style->layout_info[i][j].pixels_min, style->pixel_widths[i][j]);
}
else if ((flags & STRING_MIN) && style->layout_info[i][j].string) {
style->layout_info[i][j].pixels_min =
gdk_string_measure (style->fonts[i][j], style->layout_info[i][j].string)+ 2*CELL_HPADDING;
style->layout_info[i][j].flags |= PIXELS_MIN;
style->pixel_widths[i][j] =
MAX (style->layout_info[i][j].pixels_min, style->pixel_widths[i][j]);
}
if (flags & CHARS_MAX) {
style->layout_info[i][j].pixels_max =
style->layout_info[i][j].chars_max * C_WIDTH+ 2*CELL_HPADDING;
style->layout_info[i][j].flags |= PIXELS_MAX;
style->pixel_widths[i][j] =
MIN (style->layout_info[i][j].pixels_max, style->pixel_widths[i][j]);
}
else if (flags & PIXELS_MAX) {
style->pixel_widths[i][j] =
MIN (style->layout_info[i][j].pixels_max, style->pixel_widths[i][j]);
}
}
for (j = 0; j < style->ncols; j++)
if (style->layout_info[i][j].flags & SAME_SIZE) {
int r = style->layout_info[i][j].size_r;
int c = style->layout_info[i][j].size_c;
style->pixel_widths[i][j] = style->pixel_widths[r][c];
}
}
/* This is a subcase of pass_three: we have a very small amount of
* space to reallocate; let's just put it in the largest FILL cell,
* and damn the consequences; in the wierd case that there is no FILL
* cell at all, just stick it in the largest cell.
*/
static void
set_dimensions_plan_b (GnucashSheet *sheet, SheetBlockStyle *style,
int row, int space)
{
int i = row, j;
int fill_col = -1;
int fill_w = 0;
int col = 0;
int w = 0;
for (j = 0; j < style->ncols; j++) {
if (style->layout_info[i][j].flags & FILL)
if (fill_w < style->pixel_widths[i][j]) {
fill_col = j;
fill_w = style->pixel_widths[i][j];
}
if (w < style->pixel_widths[i][j]) {
col = j;
w = style->pixel_widths[i][j];
}
}
if (fill_col > -1)
style->pixel_widths[i][fill_col] -= space;
else
style->pixel_widths[i][col] -= space;
}
/* OK, this is the tricky one: we need to add/subtract left over or
* excess space from the FILL cells. We'll try to adjust each one
* the same amount, but this is subject to any _MIN/_MAX
* restrictions, and we need to be careful with SAME_SIZE cells, too.
*
*/
static void
set_dimensions_pass_three (GnucashSheet *sheet, SheetBlockStyle *style,
int row, int ideal_width)
{
int i = row, j;
int w = 0;
int space = compute_row_width (style, row, 0, style->ncols-1) - ideal_width;
int cellspace;
int nfills;
int *adjustments;
int *done;
adjustments = g_new0 (int, style->ncols);
done = g_new0 (int, style->ncols);
while (space != 0) {
nfills = 0;
/* count the number of fill cells we have left to work with */
for (j = 0; j < style->ncols; j++) {
if ( (style->layout_info[i][j].flags & FILL) && !done[j])
nfills++;
else if (style->layout_info[i][j].flags & SAME_SIZE) {
int r = style->layout_info[i][j].size_r;
int c = style->layout_info[i][j].size_c;
if ((style->layout_info[r][c].flags & FILL) && !done[c])
nfills++;
}
}
/* no more place to put/take away extra space; give up */
if (nfills == 0)
break;
/* We take care of small amounts of space in a special case */
if ( (space < 0 ? -1 : 1)*space < nfills) {
set_dimensions_plan_b (sheet, style, row, space);
break;
}
/* this is how much we should ideally adjust each cell */
cellspace = space/nfills;
/* loop through again and do the best we can */
for (j = 0; j < style->ncols; j++)
if ((style->layout_info[i][j].flags & FILL) && !done[j]) {
if (cellspace > 0) {
if (style->layout_info[i][j].flags & PIXELS_MIN) {
int allowed = style->pixel_widths[i][j]
- style->layout_info[i][j].pixels_min;
adjustments[j] = MIN (allowed, cellspace);
done[j] = (allowed < cellspace);
}
else adjustments[j] = cellspace;
space -= adjustments[j];
}
else {
if (style->layout_info[i][j].flags & PIXELS_MAX) {
int allowed = style->pixel_widths[i][j]
- style->layout_info[i][j].pixels_min;
adjustments[j] = MAX (allowed, cellspace);
done[j] = (allowed >cellspace);
}
else adjustments[j] = cellspace;
space -= adjustments[j];
}
}
/* adjust everything, including SAME_SIZE cells */
for (j = 0; j < style->ncols; j++) {
if (style->layout_info[i][j].flags & SAME_SIZE) {
int r = style->layout_info[i][j].size_r;
int c = style->layout_info[i][j].size_c;
style->pixel_widths[i][j] -= adjustments[c];
space -= adjustments[c];
}
style->pixel_widths[i][j] -= adjustments[j];
}
for (j = 0; j < style->ncols; j++)
adjustments[j] = 0;
}
/* clean up */
g_free (adjustments);
g_free (done);
}
static void
set_dimensions_aligned (GnucashSheet *sheet, SheetBlockStyle *style, int row)
{
int i = row, j;
int width;
int c1, c2;
int r;
for (j = 0; j < style->ncols; j++) {
if (!(style->layout_info[i][j].flags & (LEFT_ALIGNED | RIGHT_ALIGNED))) {
style->layout_info[i][j].pixels_max = 0;
style->layout_info[i][j].flags |= PIXELS_FIXED;
style->pixel_widths[i][j] = 0;
}
else {
r = style->layout_info[i][j].right_align_r;
c1 = style->layout_info[i][j].left_align_c;
c2 = style->layout_info[i][j].right_align_c;
style->layout_info[i][j].pixels_width = compute_row_width (style, r, c1, c2);
style->layout_info[i][j].flags |= PIXELS_FIXED;
style->pixel_widths[i][j] = style->layout_info[i][j].pixels_width;
}
}
}
void
gnucash_sheet_style_set_dimensions (GnucashSheet *sheet,
SheetBlockStyle *style)
{
gint i, j;
int i;
int ideal_width;
g_return_if_fail (sheet != NULL);
g_return_if_fail (GNUCASH_IS_SHEET (sheet));
@ -179,18 +603,20 @@ gnucash_sheet_style_set_dimensions (GnucashSheet *sheet,
style->height = 0;
style->width = GTK_WIDGET (sheet)->allocation.width;
ideal_width = style->width;
for (i = 0; i < style->nrows; i++) {
for (j = 0; j < style->ncols; j++) {
style->pixel_widths[i][j] =
style->cell_perc[i][j] * style->width + 0.5;
/* First set the top rows */
set_dimensions_pass_one (sheet, style, 0);
set_dimensions_pass_two (sheet, style, 0);
set_dimensions_pass_three (sheet, style, 0, ideal_width);
style->pixel_heights[i][j] =
style->fonts[i][j]->ascent +
style->fonts[i][j]->descent +
2*CELL_VPADDING;
}
style->height += style->pixel_heights[i][0];
/* style->width is fixed now */
style->width = compute_row_width (style, 0, 0, style->ncols-1);
/* For now, let's treat the other rows as ALIGNED_ cells only */
for (i = 1; i < style->nrows; i++) {
set_dimensions_pass_one (sheet, style, i);
set_dimensions_aligned (sheet, style, i);
}
}
@ -206,6 +632,9 @@ gnucash_sheet_style_destroy (SheetBlockStyle *style)
g_free(style->widths[i]);
g_free(style->pixel_heights[i]);
g_free(style->pixel_widths[i]);
for (j = 0; j < style->ncols; j++)
g_free (style->layout_info[i][j].string);
g_free(style->layout_info[i]);
g_free(style->alignments[i]);
g_free(style->fonts[i]);
g_free(style->active_bg_color[i]);
@ -219,6 +648,7 @@ gnucash_sheet_style_destroy (SheetBlockStyle *style)
g_free(style->widths);
g_free(style->pixel_heights);
g_free(style->pixel_widths);
g_free(style->layout_info);
g_free(style->alignments);
g_free(style->fonts);
g_free(style->active_bg_color);
@ -255,6 +685,7 @@ gnucash_sheet_style_compile (GnucashSheet *sheet, CellBlock *cellblock,
style->widths = g_new0(gint *, cellblock->numRows);
style->pixel_heights = g_new0(gint *, cellblock->numRows);
style->pixel_widths = g_new0 (gint *, cellblock->numRows);
style->layout_info = g_new0 (CellLayoutInfo *, cellblock->numRows);
style->alignments = g_new0 (GtkJustification *, cellblock->numRows);
style->fonts = g_new0 (GdkFont **, cellblock->numRows);
style->active_bg_color = g_new0 (GdkColor **, cellblock->numRows);
@ -266,6 +697,7 @@ gnucash_sheet_style_compile (GnucashSheet *sheet, CellBlock *cellblock,
style->widths[i] = g_new0(gint, style->ncols);
style->pixel_heights[i] = g_new0(gint , style->ncols);
style->pixel_widths[i] = g_new0 (gint, style->ncols);
style->layout_info[i] = g_new0 (CellLayoutInfo, style->ncols);
style->alignments[i] = g_new0 (GtkJustification, style->ncols);
style->fonts[i] = g_new0 (GdkFont *, style->ncols);
style->active_bg_color[i] = g_new0 (GdkColor *, style->ncols);
@ -284,6 +716,7 @@ gnucash_sheet_style_compile (GnucashSheet *sheet, CellBlock *cellblock,
style->widths[i][j] = cellblock->widths[j];
style->fonts[i][j] = gnucash_default_font;
style->header_font = gnucash_default_font;
if (type > -1)
label = sr->header_label_cells[type]->value;

View File

@ -38,6 +38,7 @@
#define VERY_SMALL (1.0e-20)
static void PriceSetValue (BasicCell *, const char *);
static char * xaccPriceCellPrintValue (PriceCell *cell);
/* set the color of the text to red, if teh value is negative */
/* hack alert -- the actual color should probably be configurable */
@ -66,7 +67,8 @@ static const char *
PriceMV (BasicCell *_cell,
const char * oldval,
const char *change,
const char *newval)
const char *newval,
int *cursor_position)
{
PriceCell *cell = (PriceCell *) _cell;
@ -93,6 +95,25 @@ PriceMV (BasicCell *_cell,
/* ================================================ */
static const char *
PriceLeave (BasicCell *_cell, const char *val)
{
PriceCell *cell = (PriceCell *) _cell;
double amount;
char *newval;
newval = xaccPriceCellPrintValue(cell);
/* If they are identical, return the original */
if (strcmp(newval, val) == 0)
return val;
/* Otherwise, return the new one. */
return strdup(newval);
}
/* ================================================ */
PriceCell *
xaccMallocPriceCell (void)
{
@ -117,6 +138,7 @@ xaccInitPriceCell (PriceCell *cell)
cell->cell.use_fg_color = 1;
cell->cell.modify_verify = PriceMV;
cell->cell.leave_cell = PriceLeave;
cell->cell.set_value = PriceSetValue;
}
@ -132,35 +154,48 @@ xaccDestroyPriceCell (PriceCell *cell)
/* ================================================ */
void xaccSetPriceCellValue (PriceCell * cell, double amt)
static char *
xaccPriceCellPrintValue (PriceCell *cell)
{
char buff[PRTBUF];
cell->amount = amt;
/* if amount is zero, and blanking is set, then print blank */
if (cell->blank_zero && (VERY_SMALL > amt) && ((-VERY_SMALL) < amt)) {
buff[0] = 0x0;
} else {
static char buff[PRTBUF];
char tmpfmt[PRTBUF];
char tmpval[PRTBUF];
char *monet;
if (cell->blank_zero &&
(VERY_SMALL > cell->amount) && ((-VERY_SMALL) < cell->amount)) {
strcpy(buff, "");
return buff;
}
/* check for monetary-style format not natively supported by printf */
/* hack alert -- this type of extended function should be abstracted
* out to a gnc_sprintf type function, however, this is much
* easier said than done */
monet = strstr (cell->prt_format, "%m");
if (monet) {
char tmpfmt[PRTBUF];
char tmpval[PRTBUF];
strcpy (tmpfmt, cell->prt_format);
monet = strstr (tmpfmt, "%m");
*(monet+1) = 's';
xaccSPrintAmount (tmpval, amt, PRTSEP);
xaccSPrintAmount (tmpval, cell->amount, PRTSEP);
snprintf (buff, PRTBUF, tmpfmt, tmpval);
} else {
snprintf (buff, PRTBUF, cell->prt_format, amt);
snprintf (buff, PRTBUF, cell->prt_format, cell->amount);
}
return buff;
}
/* ================================================ */
void xaccSetPriceCellValue (PriceCell * cell, double amt)
{
char *buff;
cell->amount = amt;
buff = xaccPriceCellPrintValue (cell);
SET ( &(cell->cell), buff);
/* set the cell color to red if the value is negative */
@ -183,21 +218,20 @@ void xaccSetPriceCellFormat (PriceCell * cell, char * fmt)
void xaccSetDebCredCellValue (PriceCell * deb,
PriceCell * cred, double amt)
{
deb->amount = -amt;
cred->amount = amt;
deb->cell.fg_color = 0xff0000;
cred->cell.fg_color = 0x0;
if (cred->blank_zero && (VERY_SMALL > amt) && ((-VERY_SMALL) < amt)) {
SET ( &(cred->cell), "");
SET ( &(deb->cell), "");
} else
if (0.0 < amt) {
xaccSetPriceCellValue (cred, amt);
SET ( &(deb->cell), "");
xaccSetPriceCellValue (deb, 0.0);
if (!deb->blank_zero) {
SET(&deb->cell, "");
}
} else {
SET ( &(cred->cell), "");
xaccSetPriceCellValue (cred, 0.0);
if (!cred->blank_zero) {
SET(&deb->cell, "");
}
xaccSetPriceCellValue (deb, -amt);
}
}

View File

@ -66,40 +66,101 @@ static const char *
quick_modify (BasicCell *_cell,
const char *oldval,
const char *change,
const char *newval)
const char *newval,
int *cursor_position)
{
/* The modifications to fix up completion cursor positioning and
* stale match handling below are somewhat tortured. I suspect we
* can do better, but this works for now... */
QuickFillCell *cell = (QuickFillCell *) _cell;
char * retval = (char *) newval;
QuickFill *initial_match_point = cell->qf;
const char * retval = newval;
/* if user typed the very first letter into this
* cell, then make sure that the quick-fill is set to
* the root. Alternately, if user erased all of the
* text in the cell, and has just started typing,
* then make sure that the quick-fill root is also reset
*/
* then make sure that the quick-fill root is also reset */
if (newval) {
if ((0x0 != newval[0]) && (0x0 == newval[1])) {
if (change && (0x0 != newval[0]) && (0x0 == newval[1])) {
/* the first char is being inserted */
cell->qf = cell->qfRoot;
} else if('\0' == newval[0]) {
/* the last char is being deleted */
cell->qf = cell->qfRoot;
}
}
/* if change is null, then user is deleting text;
* otehrwise, they are inserting text. */
* otherwise, they are inserting text. */
if (change) {
int i;
char c;
int previous_match_len = 0;
/* search for best-matching quick-fill string */
i=0;
c = change[i];
while (c) {
while (c && cell->qf) {
cell->qf = xaccGetQuickFill (cell->qf, c);
i++;
c = change[i];
}
/* if a match found, return it */
if (cell->qf) retval = strdup (cell->qf->text);
/* Figure out how long the previous match was. This allows us
* to position the cursor incrementally, or DTRT when the user
* diverges from a quickfill match. */
{
QuickFill *qcursor = cell->qfRoot;
while(qcursor && (qcursor != initial_match_point)) {
qcursor = xaccGetQuickFill (qcursor, oldval[previous_match_len++]);
}
}
if (cell->qf) {
/* we have a match, return it and reposition the cursor */
*cursor_position = previous_match_len + strlen(change);
retval = strdup (cell->qf->text);
} else {
/* do some research to figure out if the failure is because
* the current change just diverged from a match, in which
* case we should return everything that matched before this
* insertion plus the new change, or because the oldval hasn't
* matched for a while (i.e. we're no longer quickfilling), in
* which case we should just return the suggested newval. */
int oldval_match_len = 0;
/* find out how much of oldval is a quickfill entry. */
{
QuickFill *qcursor = cell->qfRoot;
while(qcursor) {
qcursor = xaccGetQuickFill (qcursor, oldval[oldval_match_len]);
oldval_match_len++;
}
oldval_match_len--;
}
if(oldval_match_len == strlen(oldval)) {
/* If oldval was quick-fill match in its entirety, then the
* current "change" is the first divergence. Accordingly,
* we need to remove any "post-divergence", stale quickfill
* bits that reside after the cursor and then append the new
* change text. */
char *unmatched =
(char *) malloc(previous_match_len + strlen(change) + 1);
strncpy(unmatched, oldval, previous_match_len);
unmatched[previous_match_len] = '\0';
strcat(unmatched, change);
*cursor_position = strlen(unmatched);
retval = unmatched;
}
}
}
SET (&(cell->cell), retval);

View File

@ -50,7 +50,6 @@ static short module = MOD_REGISTER;
#define RECN_CELL 3
#define SHRS_CELL 4
#define BALN_CELL 5
#define ACTN_CELL 6
#define XFRM_CELL 7
#define XTO_CELL 8
@ -105,6 +104,13 @@ static short module = MOD_REGISTER;
#define SHRS_CELL_ALIGN ALIGN_RIGHT
#define BALN_CELL_ALIGN ALIGN_RIGHT
/* Use 4 decimal places for everything that isn't a $ value
* Perhaps this should be changed to store precision selection
* in a config file. */
#define SHRS_CELL_FORMAT "%0.4f"
#define PRICE_CELL_FORMAT "%0.4f"
#define DEBIT_CELL_FORMAT "%0.4f"
#define CREDIT_CELL_FORMAT "%0.4f"
/* ============================================== */
@ -128,20 +134,19 @@ configLabels (SplitRegister *reg)
LABEL (DATE, DATE_STR);
LABEL (NUM, NUM_STR);
LABEL (ACTN, NUM_STR);
LABEL (DESC, DESC_STR);
LABEL (RECN, "R");
LABEL (SHRS, TOT_SHRS_STR);
LABEL (BALN, BALN_STR);
LABEL (ACTN, ACTION_STR);
LABEL (XFRM, XFRM_STR);
LABEL (MXFRM, XFRM_STR);
LABEL (XTO, XFTO_STR);
LABEL (DESC, DESC_STR);
LABEL (MEMO, DESC_STR);
LABEL (RECN, "R");
LABEL (DEBT, DEBIT_STR);
LABEL (MEMO, MEMO_STR);
LABEL (CRED, CREDIT_STR);
LABEL (DEBT, DEBIT_STR);
LABEL (PRIC, PRICE_STR);
LABEL (VALU, VALUE_STR);
LABEL (SHRS, TOT_SHRS_STR);
LABEL (BALN, BALN_STR);
/* setup custom labels for the debit/credit columns */
@ -296,9 +301,7 @@ configAction (SplitRegister *reg)
curs->widths[col] = NAME##_CELL_WIDTH; \
curs->alignments[col] = NAME##_CELL_ALIGN; \
if (hcell) { \
if (1 == reg->num_header_rows) { \
header->cells[0][col] = hcell; \
} else { \
if (row < reg->num_header_rows) { \
header->cells[row][col] = hcell; \
} \
} \
@ -351,18 +354,6 @@ configLayout (SplitRegister *reg)
case INCOME_LEDGER: /* hack alert do xto cell too */
case GENERAL_LEDGER: /* hack alert do xto cell too */
{
/* basic common 8 column config */
curs = reg->single_cursor;
FANCY (DATE, date, 0, 0);
BASIC (NUM, num, 1, 0);
FANCY (MXFRM, mxfrm, 2, 0);
FANCY (DESC, desc, 3, 0);
BASIC (RECN, recn, 4, 0);
FANCY (DEBT, debit, 5, 0);
FANCY (CRED, credit, 6, 0);
FANCY (BALN, balance, 7, 0);
curs = reg->double_cursor;
FANCY (DATE, date, 0, 0);
BASIC (NUM, num, 1, 0);
@ -389,15 +380,8 @@ configLayout (SplitRegister *reg)
BASIC (MEMO, memo, 3, 0);
FANCY (NDEBT, ndebit, 5, 0);
FANCY (NCRED, ncredit, 6, 0);
break;
}
/* --------------------------------------------------------- */
case STOCK_REGISTER:
case PORTFOLIO:
case CURRENCY_REGISTER:
{
/* 11 column config */
/* basic common 8 column config */
curs = reg->single_cursor;
FANCY (DATE, date, 0, 0);
BASIC (NUM, num, 1, 0);
@ -406,11 +390,16 @@ configLayout (SplitRegister *reg)
BASIC (RECN, recn, 4, 0);
FANCY (DEBT, debit, 5, 0);
FANCY (CRED, credit, 6, 0);
FANCY (PRIC, price, 7, 0);
FANCY (VALU, value, 8, 0);
FANCY (SHRS, shrs, 9, 0);
FANCY (BALN, balance, 10, 0);
FANCY (BALN, balance, 7, 0);
break;
}
/* --------------------------------------------------------- */
case STOCK_REGISTER:
case PORTFOLIO:
case CURRENCY_REGISTER:
{
/* prep the second row of the double style */
curs = reg->double_cursor;
FANCY (DATE, date, 0, 0);
@ -447,6 +436,21 @@ configLayout (SplitRegister *reg)
BASIC (MEMO, memo, 3, 0);
FANCY (NDEBT, ndebit, 5, 0);
FANCY (NCRED, ncredit, 6, 0);
/* 11 column config */
curs = reg->single_cursor;
FANCY (DATE, date, 0, 0);
BASIC (NUM, num, 1, 0);
FANCY (MXFRM, mxfrm, 2, 0);
FANCY (DESC, desc, 3, 0);
BASIC (RECN, recn, 4, 0);
FANCY (DEBT, debit, 5, 0);
FANCY (CRED, credit, 6, 0);
FANCY (PRIC, price, 7, 0);
FANCY (VALU, value, 8, 0);
FANCY (SHRS, shrs, 9, 0);
FANCY (BALN, balance, 10, 0);
break;
}
/* --------------------------------------------------------- */
@ -464,15 +468,20 @@ configLayout (SplitRegister *reg)
/* hack alert -- if show_tamount or show_samount is set then don't traverse there */
/* hack alert -- fix show_txfrm also ... */
/* Right Traversals */
#define FIRST_RIGHT(r,c) { \
prev_r = r; prev_c = c; \
}
#define NEXT_RIGHT(r,c) { \
xaccNextRight (curs, prev_r, prev_c, (r), (c)); \
prev_r = r; prev_c = c; \
}
#define TRAVERSE_NON_NULL_CELLS() { \
i = prev_r; \
for (j=prev_c+1; j<curs->numCols; j++) { \
@ -495,6 +504,7 @@ configLayout (SplitRegister *reg)
} \
}
#define FIRST_NON_NULL(r,c) { \
i = r; \
for (j=c; j<curs->numCols; j++) { \
@ -508,6 +518,7 @@ configLayout (SplitRegister *reg)
} \
}
#define NEXT_NON_NULL(r,c) { \
i = r; \
for (j=c+1; j<curs->numCols; j++) { \
@ -521,6 +532,11 @@ configLayout (SplitRegister *reg)
} \
}
#define EXIT_RIGHT() { \
curs->right_exit_r = prev_r; curs->right_exit_c = prev_c; \
}
#define NEXT_SPLIT() { \
i = 0; \
for (j=0; j<reg->split_cursor->numCols; j++) { \
@ -534,6 +550,83 @@ configLayout (SplitRegister *reg)
} \
}
/* Left Traversals */
#define LAST_LEFT(r,c) { \
prev_r = r; prev_c = c; \
}
#define NEXT_LEFT(r,c) { \
xaccNextLeft (curs, prev_r, prev_c, (r), (c)); \
prev_r = r; prev_c = c; \
}
#define TRAVERSE_NON_NULL_CELLS_LEFT() { \
i = prev_r; \
for (j=prev_c -1; j>=0; j--) { \
if ((reg->nullCell != curs->cells[i][j]) && \
(reg->recnCell != curs->cells[i][j]) && \
(XACC_CELL_ALLOW_INPUT & curs->cells[i][j]->input_output)) \
{ \
NEXT_LEFT (i, j); \
} \
} \
for (i=prev_r-1; i>=0; i--) { \
for (j=curs->numCols-1; j>=0; j--) { \
if ((reg->nullCell != curs->cells[i][j]) && \
(reg->recnCell != curs->cells[i][j]) && \
(XACC_CELL_ALLOW_INPUT & curs->cells[i][j]->input_output)) \
{ \
NEXT_LEFT (i, j); \
} \
} \
} \
}
#define LAST_NON_NULL(r,c) { \
i = r; \
for (j=c; j>=0; j--) { \
if ((reg->nullCell != curs->cells[i][j]) && \
(reg->recnCell != curs->cells[i][j]) && \
(XACC_CELL_ALLOW_INPUT & curs->cells[i][j]->input_output)) \
{ \
LAST_LEFT (i, j); \
break; \
} \
} \
}
#define PREVIOUS_NON_NULL(r,c) { \
i = r; \
for (j=c-1; j>=0; j--) { \
if ((reg->nullCell != curs->cells[i][j]) && \
(reg->recnCell != curs->cells[i][j]) && \
(XACC_CELL_ALLOW_INPUT & curs->cells[i][j]->input_output)) \
{ \
NEXT_LEFT (i, j); \
break; \
} \
} \
}
#define EXIT_LEFT() { \
curs->left_exit_r = prev_r; curs->left_exit_c = prev_c; \
}
#define PREVIOUS_SPLIT() { \
i = reg->split_cursor->numRows-1; \
for (j=reg->split_cursor->numCols-1; j>=0; j--) { \
if ((reg->nullCell != reg->split_cursor->cells[i][j]) && \
(reg->recnCell != reg->split_cursor->cells[i][j]) && \
(XACC_CELL_ALLOW_INPUT & reg->split_cursor->cells[i][j]->input_output)) \
{ \
NEXT_LEFT (i-1, j); \
break; \
} \
} \
}
static void
configTraverse (SplitRegister *reg)
{
@ -547,28 +640,63 @@ configTraverse (SplitRegister *reg)
FIRST_NON_NULL (0, 0);
first_r = prev_r; first_c = prev_c;
TRAVERSE_NON_NULL_CELLS ();
/* set the exit row, col */
EXIT_RIGHT ();
/* wrap back to start of row after hitting the commit button */
NEXT_RIGHT (-1-first_r, -1-first_c);
NEXT_RIGHT (first_r, first_c);
/* left traverses */
LAST_NON_NULL (curs->numRows-1, curs->numCols - 1);
first_r = prev_r; first_c = prev_c;
TRAVERSE_NON_NULL_CELLS_LEFT();
EXIT_LEFT ();
NEXT_LEFT (first_r, first_c);
curs = reg->double_cursor;
/* lead in with the date cell, return to the date cell */
FIRST_NON_NULL (0, 0);
first_r = prev_r; first_c = prev_c;
TRAVERSE_NON_NULL_CELLS ();
/* set the exit row,col */
EXIT_RIGHT ();
/* for double-line, hop back one row */
NEXT_RIGHT (-1-first_r, -1-first_c);
NEXT_RIGHT (first_r, first_c);
/* left traverses */
LAST_NON_NULL (curs->numRows-1, curs->numCols - 1);
first_r = prev_r; first_c = prev_c;
TRAVERSE_NON_NULL_CELLS_LEFT ();
EXIT_LEFT ();
NEXT_LEFT (first_r, first_c);
curs = reg->trans_cursor;
FIRST_NON_NULL (0,0);
TRAVERSE_NON_NULL_CELLS ();
/* set the exit row, col */
EXIT_RIGHT ();
/* hop to start of next row (the split cursor) */
NEXT_SPLIT();
/* left_traverses */
LAST_NON_NULL (curs->numRows-1, curs->numCols - 1);
TRAVERSE_NON_NULL_CELLS_LEFT ();
EXIT_LEFT ();
PREVIOUS_SPLIT ();
printf ("SPLIT\n");
curs = reg->split_cursor;
FIRST_NON_NULL (0,0);
TRAVERSE_NON_NULL_CELLS ();
/* set the exit row, col */
EXIT_RIGHT ();
/* hop to start of next row (the split cursor) */
NEXT_SPLIT();
/* left_traverses */
LAST_NON_NULL (curs->numRows-1, curs->numCols - 1);
TRAVERSE_NON_NULL_CELLS_LEFT ();
EXIT_LEFT ();
PREVIOUS_SPLIT ();
}
/* ============================================== */
@ -681,9 +809,7 @@ xaccInitSplitRegister (SplitRegister *reg, int type)
CellBlock *header;
int phys_r, phys_c;
reg->user_hook = NULL;
reg->user_hack = NULL;
reg->user_huck = NULL;
reg->user_data = NULL;
reg->destroy = NULL;
reg->type = type;
@ -788,18 +914,18 @@ xaccInitSplitRegister (SplitRegister *reg, int type)
xaccSetPriceCellValue (reg->ndebitCell, 0.0);
xaccSetPriceCellValue (reg->ncreditCell, 0.0);
/* use three decimal places to print share-related info.
* The format is a printf-style format for a double. */
xaccSetPriceCellFormat (reg->shrsCell, "%.3f");
xaccSetPriceCellFormat (reg->priceCell, "%.3f");
/* The format for share-related info is a printf-style
* format string for a double. */
xaccSetPriceCellFormat (reg->shrsCell, SHRS_CELL_FORMAT);
xaccSetPriceCellFormat (reg->priceCell, PRICE_CELL_FORMAT);
/* use three decimal places for share quantities in stock ledgers */
/* number format for share quantities in stock ledgers */
switch (type & REG_TYPE_MASK) {
case STOCK_REGISTER:
case PORTFOLIO:
case CURRENCY_REGISTER:
xaccSetPriceCellFormat (reg->debitCell, "%.3f");
xaccSetPriceCellFormat (reg->creditCell, "%.3f");
xaccSetPriceCellFormat (reg->debitCell, DEBIT_CELL_FORMAT);
xaccSetPriceCellFormat (reg->creditCell, CREDIT_CELL_FORMAT);
break;
default:
break;
@ -874,9 +1000,7 @@ xaccDestroySplitRegister (SplitRegister *reg)
(*(reg->destroy)) (reg);
}
reg->destroy = NULL;
reg->user_hook = NULL;
reg->user_hack = NULL;
reg->user_huck = NULL;
reg->user_data = NULL;
xaccDestroyTable (reg->table);
reg->table = NULL;
@ -966,4 +1090,57 @@ xaccSplitRegisterGetChangeFlag (SplitRegister *reg)
return changed;
}
/* ============================================== */
void
xaccSplitRegisterClearChangeFlag (SplitRegister *reg)
{
reg->dateCell->cell.changed = 0;
reg->numCell->changed = 0;
reg->descCell->cell.changed = 0;
reg->recnCell->changed = 0;
reg->actionCell->cell.changed = 0;
reg->xfrmCell->cell.changed = 0;
reg->mxfrmCell->cell.changed = 0;
reg->xtoCell->cell.changed = 0;
reg->memoCell->changed = 0;
reg->creditCell->cell.changed = 0;
reg->debitCell->cell.changed = 0;
reg->priceCell->cell.changed = 0;
reg->valueCell->cell.changed = 0;
reg->ncreditCell->cell.changed = 0;
reg->ndebitCell->cell.changed = 0;
}
/* ============================================== */
CursorType
xaccSplitRegisterGetCursorType (SplitRegister *reg)
{
Table *table;
CellBlock *cursor;
assert(reg);
table = reg->table;
if (table == NULL)
return CURSOR_NONE;
cursor = table->current_cursor;
if (cursor == NULL)
return CURSOR_NONE;
if ((cursor == reg->single_cursor) ||
(cursor == reg->double_cursor) ||
(cursor == reg->trans_cursor))
return CURSOR_TRANS;
if (cursor == reg->split_cursor)
return CURSOR_SPLIT;
return CURSOR_NONE;
}
/* ============ END OF FILE ===================== */

Some files were not shown because too many files have changed in this diff Show More