Add abakus

git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/abakus@1071969 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
v3.5.13-sru
tpearson 16 years ago
commit 8bec1dda93

@ -0,0 +1 @@
Michael Pyne <michael.pyne@kdemail.net>

@ -0,0 +1,341 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
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.
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.
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.
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.
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.
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.
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.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
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".
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.
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.
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.
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:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
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.
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.)
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.
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.
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.
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:
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,
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,
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.)
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.
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.
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.
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.
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.
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.
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.
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.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
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.
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.
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.
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.
NO WARRANTY
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.
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.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

183
README

@ -0,0 +1,183 @@
abakus, by Michael Pyne <michael.pyne@kdemail.net>
Version: 0.91
This is my attempt at creating a light-calculator based on Roberto Alsina's
initial usability suggestions, and based on the ideas of a few other KDE
hackers.
This software is distributed under the terms of the GNU GPL v2.
Synopsis:
$ tar xvjf abakus-0.91.tar.bz2
$ cd abakus-0.91
$ ./configure && make && make install
$ abakus
Type away, and press Enter to see the result.
Changes since 0.90:
* Add ability to remove all functions and variables to the context menus of
their respective list boxes.
* Convert out-of-range numbers to NaN.
* Accept "," as a decimal separator for the benefit of European users.
* Use correct decimal separator (per KLocale settings) in output.
* For long results, show the beginning instead of the end of the result,
since the beginning is the most significant part of the result.
Changes since 0.85:
* You now have the option of using the GNU Multiple Precision library for
high-precision mathematics. It requires the MPFR library to also be
installed (normally comes with GNU MP 4.x). It is used automatically if
detected.
* Jes Hall has contributed DocBook documentation to make Abakus integrate into
KDE even more tightly. Thanks, Jes!
* User defined functions can now be defined over.
* Error handling with deriv() function improved.
* Ariya Hidayat's name was misspelled everywhere in Abakus. Sorry, Ariya. :(
* Speaking of Ariya, Abakus now uses the impressive user interface code from
his SpeedCrunch calculator (http://speedcrunch.berlios.de/). This includes
the Calc-as-you-Type tooltip, the function and variable dropdown, and
syntax highlighting. It's not configurable at this point, expect that in
the next release.
* You can use the F6 key to select the expression editor if you're a big fan
of the keyboard.
* Raising negative numbers to integral powers should work with the internal
high-precision library now.
* You can no longer deselect the current precision menu item.
* Fix crash bug when a user-defined function refers to another user-defined
function, and then you remove or edit the function it referred to.
* Add exact numerical derivatives for all functions supported.
* Added the asinh, acosh, and atanh functions.
* Fixed bug with loading of prior compact mode status.
* Fixed bug where result text had wrong precision when you changed the
precision and tried drag-and-drop.
* Drag-and-drop improvements.
* Fixed bug where Custom Precision menu entry was checked even if you canceled
the dialog.
* Made hyperbolic functions consistently ignore trigonometric mode. (Both
Degrees and Radians make no sense for hyperbolic trig).
* Whew! :)
v0.85 adds a lot:
* Improvements to the configure script. Since I didn't end up using libCLN it
was mostly for naught, but the changes will be useful for the future.
* abakus now uses the high-precision math routines from Ariya Hidayat's
SpeedCrunch program. Thanks, Ariya!
* High precision arithmetic can have between 0 and 75 digits of precision.
* Support for approximate derivatives. For most functions the derivatives will
be numerically accurate. For those functions where I didn't feel like typing
in the exact form of the derivative an approximation is used instead.
* Implicit multiplication has been added to the parser. That means you can
type stuff like "3 sin pi" without having to manually add the * in between
3 and sin. This also works with numbers and variables, and numbers and
parenthesized expressions.
* GUI changes. The main result view now uses KListView, so it gains tooltip
support for long answers for free, along with a bevy of other improvements.
* You can right-click on an answer and copy it to the clipboard.
* Corrected information in the about box.
* Restarting abakus with compact mode enabled should is much improved.
v0.80.2 fixed an issue with the configure script for people who don't have
exactly the same version of Python I do, and forcibly prevents flex/bison
errors.
v0.80.1 fixed an issue with the configure script for people who don't already
have scons installed.
Major changes since 0.76:
* There is no more C code to interface between the parser and program.
* RPN mode is improved. Now the stack is retained between calls, and there
are a few commands only in RPN mode:
1. pop - Return the top of the stack.
2. clear - Clear the stack.
* bksys is used instead of the custom Makefiles.
* Lots of code cleanups, including license headers.
* The nifty drag-and-drop image looks more rectangular, and is used with the
two listviews on the right as well.
* Improved error checking, with messages that should hopefully be more
descriptive.
Major changes since 0.75:
* Reorder internal macro so that functions are declared *before* they're
referenced, which helps build the program on systems with math.h files that
don't export the long double version of their math functions.
* Hitting a number or letter key right after evaluating an expression in RPN
mode automatically prepends the 'ans' variable, which was a feature of the
Normal mode.
Major changes since 0.70:
* Build system switched (somehow) to using qmake. The parser and lexer are
still included, so bison and flex are still not required. Hopefully this
will improve the ease of building. Of course, this means no more colored
make output.
* Changed most of the keyboard shortcuts to use Shift + Alt + foo instead of
Alt + foo since that was interfering with the menu bar.
* RPN mode!! If you enable RPN mode, then your expressions will be evaluated
using the Reverse Polish Notation popular with users of HP calculators. Note
that although you can use values and functions while in RPN mode, you cannot
set or remove them from the expression editor like you can in normal mode.
* abakus will display a small token starting with a dollar sign ($) in italics
next to results. You can use these tokens to quickly reference a result in
your expression. The most recent result is always $0, with the number
increasing from most recent to least recent result. For example, typing
2 <Enter> 3 <Enter> $0 ^ $1 <Enter> would give a result of 9.
* You can right click on functions and values in the list views to remove them
from the GUI.
* Changed the result items to use word wrapping when needed to fit all the
text.
* Very small DCOP interface.
* More code cleanup.
* Added a .desktop file.
* Test client removed again.
* Double-clicking on an error message (or OK message) no longer inserts them
into the edit box.
Major changes since 0.61:
* User defined Functions.
* Save state of program between runs.
* Miscellaneous fun stuff.
Currently implemented features:
* Parser built using flex and bison. The generated files are included so it
should compile fine for you.
* Fully C++. The parser and lexer code require C++ to compile.
* Supports several built-in functions:
- sin, cos, tan, sinh, cosh, tanh, asin, acos, atan in either radian or
degree mode.
abs, exp (e raised to the given power), ln, log (base 10),
sqrt, ceil, floor
* Supported operators: +, -, *, /, ^ (or **).
* Includes a window showing the values and user-defined functions you have.
* Predefined constants: pi, and e (Euler' constant).
* You can assign to variables by using an expression of the form:
identifier = expression. You can then reuse these variables later.
* You can create user-defined functions of one variable using the syntax
set foo(var) = <expr>, where <expr> calculates the value in terms of var.
* You can delete user-defined variables by doing: remove var
* You can delete user-defined functions by doing: remove foo(). Notice that
the variable is NOT included in that expression.
* Functions and variables are saved on exit, and then loaded when abakus is
started again.
* The ans variable contains the result of the last computation.
* Pressing +, -, *, or / immediately after your last computation automatically
inserts ans for you, saving you typing.
* A compact mode for the program.
* Operator precedence should be correct, including the right association of
the power operator. So, 2 ^ 3 ^ 2 == 512, just as it does when you write
it out. You can use parentheses to force precedence.
* Parentheses are not required around functions. So, sin 3 is a valid
expression. Note that sin 3 + cos 4 translates internally as (sin 3) +
(cos 4), not as sin (3 + cos (4)).
* I took some pains to try to make things like 3 + -2 work right.
* inf and nan are accepted as numeric input for completeness.
* abakus will automatically add ) characters to the end of the expression as
needed to balance your expression. This means that expressions like
sin (cos (2 + 3 will evaluate with no error.
* A rudimentary RPN mode is included. Most everything works, except for
derivatives and creating functions or new variables.
Bugs:
* More functions would be nice.
* The lexer assumes that the decimal marker is a period. (.) I'm not exactly
sure how to cleanly solve this problem with flex. :-(
* Documentation could be better.

@ -0,0 +1,36 @@
#! /usr/bin/env python
###################################################################
# LOAD THE ENVIRONMENT AND SET UP THE TOOLS
###################################################################
## Load the builders in config
tools = [ 'default', 'help', 'generic', 'kde', 'abakus' ]
toolpath = [ './', './bksys' ]
# Required as part of SCons
env = Environment(tools = tools, toolpath = toolpath)
# Pull in some default settings.
env.KDEuse("environ rpath nohelp")
#env.KDEuse("environ rpath lang_qt thread nohelp")
# If we're asking for help just go ahead and exit now.
if env['HELP']:
print env.helpText()
Exit()
if env['flex'] and env['bison']:
env['PARSER_INCLUDED'] = True
# Export the environment so that SConscript files in subdirs can access it.
Export('env')
###################################################################
# SCRIPTS FOR BUILDING THE TARGETS
###################################################################
env.subdirs('src')
env.docfolder('doc/en', 'en', 'abakus/')
env.SConscript('doc/en/SConscript')

@ -0,0 +1,178 @@
#!/usr/bin/env python
"""
Run scons -h to display the associated help, or look below ..
"""
BOLD ="\033[1m"
RED ="\033[91m"
GREEN ="\033[92m"
YELLOW ="\033[1m" #"\033[93m" # unreadable on white backgrounds
CYAN ="\033[96m"
NORMAL ="\033[0m"
def exists(env):
return true
def printColorCoded(msg):
msg = msg.replace(']', NORMAL)
msg = msg.replace('b[', BOLD)
msg = msg.replace('g[', GREEN)
msg = msg.replace('r[', RED)
msg = msg.replace('c[', CYAN)
msg = msg.replace('y[', YELLOW)
print msg
def generate(env):
import SCons.Util, os
env.addHelpText("""b[hi]
b[*** abakus options ***
----------------------]
b[* bison=(no|yes): Enable parser support. Only needed for developers.
b[* flex=(no|yes): Enable lexer support. Only needed for developers.
b[* mpfr=(no|yes|check): Enable the MPFR library, which is faster and more
precise than abakus's high-precision code.
ie: b[scons configure]
""")
if env['HELP']:
# Don't even bother.
return env
from SCons.Options import Options, PackageOption, EnumOption
import os
def CheckFlags(context):
context.Message('Checking if ld supports --as-needed... ')
lastLINKFLAGS = context.env['LINKFLAGS']
context.env.Append(LINKFLAGS = '-Wl,--as-needed')
ret = context.TryLink("""
#include <iostream>
using namespace std;
int main()
{
cout << "Test" << endl;
}
""", ".cpp")
if not ret:
context.env.Replace(LINKFLAGS = lastLINKFLAGS)
context.Result(ret)
return ret
def CheckPath(context, prog, versionFlag = ''):
if context.env[prog] == 'yes':
context.env[prog] = prog
context.Message('Checking for %s... ' % prog)
ret = True
# If absolute path, just try this one.
if prog[0] == '/':
ret = context.TryAction('%s %s' % (context.env[prog], versionFlag))[0]
if ret:
context.Result(ret)
return True
path = context.env.WhereIs(prog)
if ret and path != None:
context.env[prog] = path
context.Result(1)
else:
context.env[prog] = False
context.Result(0)
print """
The $foo program was not found! You asked to use it so we will stop here. It
is not required, you may use $foo=no on the command line to go without it.""".replace('$foo', prog)
Exit(1)
return False
context.Result(1)
return True
cachefile = env['CACHEDIR'] + '/abakus.cache.py'
fixup = lambda x: "%s installed here (yes = search)" % x
opts = None
if env.doConfigure():
opts = Options(None, env['ARGS'])
else:
opts = Options(cachefile, env['ARGS'])
opts.AddOptions(
PackageOption('bison', fixup('use the Bison parser generator'), 'yes'),
PackageOption('flex', fixup('use the Flex scanner generator'), 'yes'),
EnumOption ('mpfr', 'use the MPFR high-precision library', 'check',
allowed_values=('yes', 'no', 'check'), map={}, ignorecase=1),
('ABAKUS_CONFIGURED', '', 0),
('HAVE_ASNEEDED', '', 0)
)
# We must manually pass the ARGS in.
opts.Update(env, env['ARGS'])
if env.doConfigure() or not env['ABAKUS_CONFIGURED']:
# Configure stuff
conf = env.Configure(custom_tests = {'CheckPath': CheckPath, 'CheckFlags' : CheckFlags})
if env['bison'] and env['bison'] != 'no':
conf.CheckPath('bison', '-V')
if env['flex'] and env['flex'] != 'no':
conf.CheckPath('flex', '-V')
if env['mpfr'] != 'no':
oldLibs = conf.env.get('LIBS', '')
conf.env.AppendUnique(LIBS = 'gmp')
if conf.CheckLibWithHeader('mpfr', 'mpfr.h', 'c++', '''
mpfr_t a;
mpfr_ptr ptr;
__mpfr_struct debug;
mpfr_init(a);
''', autoadd = True):
env['mpfr'] = 'yes'
else:
conf.env.Replace(LIBS = oldLibs)
if env['mpfr'] == 'yes':
print "Unable to find requested library mpfr!"
env.Exit(1)
else:
env['mpfr'] = 'no'
env['HAVE_ASNEEDED'] = 0
if conf.CheckFlags():
env['HAVE_ASNEEDED'] = 1
env['ABAKUS_CONFIGURED'] = 1
env = conf.Finish()
try:
f = open("config.h", "w+")
f.write("""/* config.h -- Automatically generated by abakus.py
* Any changes you make to this file will be overwritten!
*/
""")
f.write("/* HAVE_MPFR -- Defined if the MPFR library is being used. */\n")
if env['mpfr'] == 'yes':
f.write ("#define HAVE_MPFR 1\n")
else:
f.write ("/* #undef HAVE_MPFR */\n")
f.close()
except IOError:
print "Unable to write config.h!"
opts.Save(cachefile, env)
# vim: set et ts=8 sw=4:

@ -0,0 +1,498 @@
## Thomas Nagy, 2005
"""
Detect and store the most common options
* kdecxxflags : debug=1 (-g) or debug=full (-g3, slower)
else use the user CXXFLAGS if any, - or -O2 by default
* prefix : the installation path
* extraincludes : a list of paths separated by ':'
ie: scons configure debug=full prefix=/usr/local extraincludes=/tmp/include:/usr/local
"""
BOLD ="\033[1m"
RED ="\033[91m"
GREEN ="\033[92m"
YELLOW ="\033[1m" #"\033[93m" # unreadable on white backgrounds
CYAN ="\033[96m"
NORMAL ="\033[0m"
import os, re, types, sys, string, shutil, stat
import SCons.Defaults
import SCons.Tool
import SCons.Util
from SCons.Script.SConscript import SConsEnvironment
from SCons.Options import Options, PathOption
class genobj:
def __init__(self, val, env):
if not val in "program shlib kioslave staticlib".split():
print "unknown genobj given: "+val
env.Exit(1)
self.type = val
self.orenv = env
self.env = None
self.executed = 0
self.target=''
self.src=None
self.cxxflags=''
self.cflags=''
self.includes=''
self.linkflags=''
self.libpaths=''
self.libs=''
# vars used by shlibs
self.vnum=''
self.libprefix=''
# a directory where to install the targets (optional)
self.instdir=''
# ignore the DESTDIR (optional)
self.nodestdir=''
# change the working directory before reading the targets
self.chdir=''
# these members are private
self.chdir_lock=None
self.old_os_dir=''
self.old_fs_dir=''
self.p_local_shlibs=[]
self.p_local_staticlibs=[]
self.p_global_shlibs=[]
#if not env.has_key('USE_THE_FORCE_LUKE'): env['USE_THE_FORCE_LUKE']=[self]
#else: env['USE_THE_FORCE_LUKE'].append(self)
def lockchdir(self):
if not self.chdir: return
self.chdir_lock=1
SConfFS=SCons.Node.FS.default_fs
self.old_fs_dir=SConfFS.getcwd()
self.old_os_dir=os.getcwd()
#os.chdir(old_os_dir+'/'+self.chdir)
SConfFS.chdir( SConfFS.Dir('#/'+self.chdir), change_os_dir=1)
def unlockchdir(self):
if not self.chdir: return
if self.chdir_lock:
#os.chdir(self.old_os_dir)
SCons.Node.FS.default_fs.chdir(self.old_fs_dir, change_os_dir=0)
self.chdir_lock=None
def execute(self):
if self.orenv.has_key('DUMPCONFIG'):
print self.xml()
return
self.lockchdir()
self.env = self.orenv.Copy()
if not self.src or len(self.src) == 0:
print RED+"no source file given to object - self.src"+NORMAL
self.env.Exit(1)
if not self.env.has_key('nosmart_includes'): self.env.AppendUnique(CPPPATH=['./'])
if self.type == "kioslave": self.libprefix=''
if len(self.includes)>0: self.env.AppendUnique(CPPPATH=self.env.make_list(self.includes))
if len(self.cxxflags)>0: self.env.AppendUnique(CXXFLAGS=self.env.make_list(self.cxxflags))
if len(self.cflags)>0: self.env.AppendUnique(CCFLAGS=self.env.make_list(self.cflags))
llist=self.env.make_list(self.libs)
lext='.so .la'.split()
sext='.a'.split()
for l in llist:
sal=SCons.Util.splitext(l)
if len(sal)>1:
if sal[1] in lext: self.p_local_shlibs.append(sal[0]+'.so')
elif sal[1] in sext: self.p_local_staticlibs.append(sal[0]+'.a')
else: self.p_global_shlibs.append(l)
if len(self.p_global_shlibs)>0: self.env.AppendUnique(LIBS=self.p_global_shlibs)
if len(self.libpaths)>0: self.env.PrependUnique(LIBPATH=self.env.make_list(self.libpaths))
if len(self.linkflags)>0: self.env.PrependUnique(LINKFLAGS=self.env.make_list(self.linkflags))
# the target to return
ret=None
if self.type=='shlib' or self.type=='kioslave':
ret=self.env.bksys_shlib(self.target, self.src, self.instdir,
self.libprefix, self.vnum, nodestdir=self.nodestdir)
elif self.type=='program':
ret=self.env.Program(self.target, self.src)
if not self.env.has_key('NOAUTOINSTALL'):
self.env.bksys_install(self.instdir, ret, nodestdir=self.nodestdir)
elif self.type=='staticlib':
ret=self.env.StaticLibrary(self.target, self.src)
# we link the program against a shared library made locally, add the dependency
if len(self.p_local_shlibs)>0:
self.env.link_local_shlib(self.p_local_shlibs)
if ret: self.env.Depends( ret, self.p_local_shlibs )
if len(self.p_local_staticlibs)>0:
self.env.link_local_staticlib(self.p_local_staticlibs)
if ret: self.env.Depends( ret, self.p_local_staticlibs )
self.unlockchdir()
## Copy function that honors symlinks
def copy_bksys(dest, source, env):
if os.path.islink(source):
#print "symlinking "+source+" "+dest
if os.path.islink(dest):
os.unlink(dest)
os.symlink(os.readlink(source), dest)
else:
shutil.copy2(source, dest)
st=os.stat(source)
os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
return 0
## Return a list of things
def make_list(env, s):
if type(s) is types.ListType:
return s
else:
return s.split()
def exists(env):
return true
def generate(env):
## Bksys requires scons 0.96
env.EnsureSConsVersion(0, 96)
SConsEnvironment.make_list = make_list
def doConfigure(env):
return not env['HELP'] and (env['_CONFIGURE'] or not env.has_key('ISCONFIGURED'))
SConsEnvironment.doConfigure = doConfigure
env['HELP']=0
if '--help' in sys.argv or '-h' in sys.argv or 'help' in sys.argv:
env['HELP']=1
env.addHelpText("""
b[*** Generic options ***
-----------------------]
b[* debug ]: debug=1 (-g) or debug=full (-g3, slower), otherwise use
environment CXXFLAGS, or -O2 by default.
b[* prefix ]: the installation path
b[* extraincludes ]: a list of paths separated by ':'
ie: b[scons configure debug=full prefix=/usr/local extraincludes=/tmp/include:/usr/local]
""")
## Global cache directory
# Put all project files in it so a rm -rf cache will clean up the config
if not env.has_key('CACHEDIR'):
env['CACHEDIR'] = os.getcwd()+'/cache/'
if not os.path.isdir(env['CACHEDIR']):
os.mkdir(env['CACHEDIR'])
## SCons cache directory
# This avoids recompiling the same files over and over again:
# very handy when working with cvs
if os.getuid() != 0:
env.CacheDir(os.getcwd()+'/cache/objects')
# Avoid spreading .sconsign files everywhere - keep this line
env.SConsignFile(env['CACHEDIR']+'/scons_signatures')
def makeHashTable(args):
table = { }
for arg in args:
if len(arg) > 1:
lst=arg.split('=')
if len(lst) < 2:
continue
key=lst[0]
value=lst[1]
if len(key) > 0 and len(value) >0:
table[key] = value
return table
env['ARGS']=makeHashTable(sys.argv)
## Special trick for installing rpms ...
env['DESTDIR']=''
if 'install' in sys.argv:
dd=''
if os.environ.has_key('DESTDIR'):
dd=os.environ['DESTDIR']
if not dd:
if env['ARGS']: dd=env['ARGS']['DESTDIR']
if dd:
env['DESTDIR']=dd+'/'
print CYAN+'** Enabling DESTDIR for the project ** ' + NORMAL + env['DESTDIR']
## install symlinks for shared libraries properly
env['INSTALL'] = copy_bksys
## Use the same extension .o for all object files
env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
## load the options
cachefile=env['CACHEDIR']+'generic.cache.py'
opts = Options(cachefile)
opts.AddOptions(
( 'GENCCFLAGS', 'C flags' ),
( 'GENCXXFLAGS', 'debug level for the project : full or just anything' ),
( 'GENLINKFLAGS', 'additional link flags' ),
( 'PREFIX', 'prefix for installation' ),
( 'EXTRAINCLUDES', 'extra include paths for the project' ),
( 'ISCONFIGURED', 'is the project configured' ),
)
opts.Update(env)
# Use this to avoid an error message 'how to make target configure ?'
env.Alias('configure', None)
# Check if the following command line arguments have been given
# and set a flag in the environment to show whether or not it was
# given.
if 'install' in sys.argv:
env['_INSTALL']=1
else:
env['_INSTALL']=0
if 'configure' in sys.argv:
env['_CONFIGURE']=1
else:
env['_CONFIGURE']=0
# Configure the environment if needed
if doConfigure(env):
# be paranoid, unset existing variables
for var in "GENCXXFLAGS GENCCFLAGS GENLINKFLAGS PREFIX EXTRAINCLUDES ISCONFIGURED EXTRAINCLUDES".split():
if env.has_key(var): env.__delitem__(var)
if env['ARGS'].get('debug', None):
debuglevel = env['ARGS'].get('debug', None)
print CYAN+'** Enabling debug for the project **' + NORMAL
if (debuglevel == "full"):
env['GENCXXFLAGS'] = ['-DDEBUG', '-g3', '-Wall']
else:
env['GENCXXFLAGS'] = ['-DDEBUG', '-g', '-Wall']
else:
if os.environ.has_key('CXXFLAGS'):
# user-defined flags (gentooers will be elighted)
env['GENCXXFLAGS'] = SCons.Util.CLVar( os.environ['CXXFLAGS'] )
env.Append( GENCXXFLAGS = ['-DNDEBUG', '-DNO_DEBUG'] )
else:
env.Append(GENCXXFLAGS = ['-O2', '-DNDEBUG', '-DNO_DEBUG'])
if os.environ.has_key('CFLAGS'):
env['GENCCFLAGS'] = SCons.Util.CLVar( os.environ['CFLAGS'] )
## FreeBSD settings (contributed by will at freebsd dot org)
if os.uname()[0] == "FreeBSD":
if os.environ.has_key('PTHREAD_LIBS'):
env.AppendUnique( GENLINKFLAGS = SCons.Util.CLVar( os.environ['PTHREAD_LIBS'] ) )
else:
syspf = os.popen('/sbin/sysctl kern.osreldate')
osreldate = int(syspf.read().split()[1])
syspf.close()
if osreldate < 500016:
env.AppendUnique( GENLINKFLAGS = ['-pthread'])
env.AppendUnique( GENCXXFLAGS = ['-D_THREAD_SAFE'])
elif osreldate < 502102:
env.AppendUnique( GENLINKFLAGS = ['-lc_r'])
env.AppendUnique( GENCXXFLAGS = ['-D_THREAD_SAFE'])
else:
env.AppendUnique( GENLINKFLAGS = ['-pthread'])
# User-specified prefix
if env['ARGS'].has_key('prefix'):
env['PREFIX'] = os.path.abspath( env['ARGS'].get('prefix', '') )
print (CYAN+'** installation prefix for the project set to : ' +
env['PREFIX'] +' **'+ NORMAL)
# User-specified include paths
env['EXTRAINCLUDES'] = env['ARGS'].get('extraincludes', None)
if env['EXTRAINCLUDES']:
print (CYAN+'** extra include paths for the project set to: ' +
env['EXTRAINCLUDES'] +' **'+ NORMAL)
env['ISCONFIGURED']=1
# And finally save the options in the cache
opts.Save(cachefile, env)
def bksys_install(lenv, subdir, files, destfile=None, nodestdir=None):
""" Install files on "scons install"
If the DESTDIR env variable has been set, (e.g. by
"scons install DESTDIR=$CURDIR/debian) then install files to that
directory, regardless of where the configure stage showed that
files should be installed.
This feature is useful for packagers, and users of GNU stow.
NB. The DESTDIR will be ignored if NODESTDIR is also set, although
the same effect can be acheived by not setting DESTDIR in the first
place."""
if not env['_INSTALL']:
return
basedir = env['DESTDIR']
if nodestdir or env.has_key('NODESTDIR') : basedir = "/"
install_list = None
if not destfile:
install_list = env.Install(basedir+subdir+'/', files)
else:
if subdir:
install_list = env.InstallAs(basedir+subdir+'/'+destfile, files)
else:
install_list = env.InstallAs(basedir+'/'+destfile, files)
env.Alias('install', install_list)
return install_list
def build_la_file(target, source, env):
""" Action for building libtool files.
Writes a .la file, as used by libtool."""
dest=open(target[0].path, 'w')
sname=source[0].name
dest.write("dlname='%s'\n" % sname)
if len(env['BKSYS_VNUM'])>0:
vnum=env['BKSYS_VNUM']
nums=vnum.split('.')
src=source[0].name
name = src.split('so.')[0] + 'so'
strn = src+" "+name+"."+str(nums[0])+" "+name
dest.write("library_names='%s'\n" % (strn) )
else:
dest.write("library_names='%s %s %s'\n" % (sname, sname, sname) )
dest.write("old_library=''\ndependency_libs=''\ncurrent=0\n")
dest.write("age=0\nrevision=0\ninstalled=yes\nshouldnotlink=no\n")
dest.write("dlopen=''\ndlpreopen=''\n")
dest.write("libdir='%s'" % env['BKSYS_DESTDIR'])
dest.close()
return 0
def string_la_file(target, source, env):
print "building '%s' from '%s'" % (target[0].name, source[0].name)
la_file = env.Action(build_la_file, string_la_file, ['BKSYS_VNUM', 'BKSYS_DESTDIR'])
env['BUILDERS']['LaFile'] = env.Builder(action=la_file,suffix='.la',src_suffix=env['SHLIBSUFFIX'])
## Function for building shared libraries
def bksys_shlib(lenv, target, source, libdir, libprefix='lib', vnum='', noinst=None, nodestdir=None):
""" Install a shared library.
Installs a shared library, with or without a version number, and create a
.la file for use by libtool.
If library version numbering is to be used, the version number
should be passed as a period-delimited version number (e.g.
vnum = '1.2.3'). This causes the library to be installed
with its full version number, and with symlinks pointing to it.
For example, for libfoo version 1.2.3, install the file
libfoo.so.1.2.3, and create symlinks libfoo.so and
libfoo.so.1 that point to it.
"""
thisenv = lenv.Copy() # copying an existing environment is cheap
thisenv['BKSYS_DESTDIR']=libdir
thisenv['BKSYS_VNUM']=vnum
thisenv['SHLIBPREFIX']=libprefix
if len(vnum)>0:
thisenv['SHLIBSUFFIX']='.so.'+vnum
thisenv.Depends(target, thisenv.Value(vnum))
# Fix against a scons bug - shared libs and ordinal out of range(128)
if type(source) is types.ListType:
src2=[]
for i in source:
src2.append( str(i) )
source=src2
library_list = thisenv.SharedLibrary(target, source)
lafile_list = thisenv.LaFile(target, library_list)
## Install the libraries automatically
if not thisenv.has_key('NOAUTOINSTALL') and not noinst:
thisenv.bksys_install(libdir, library_list, nodestdir=nodestdir)
thisenv.bksys_install(libdir, lafile_list, nodestdir=nodestdir)
## Handle the versioning
if len(vnum)>0:
nums=vnum.split('.')
symlinkcom = ('cd $TARGET.dir && ' +
'rm -f $TARGET.name && ' +
'ln -s $SOURCE.name $TARGET.name')
tg = target+'.so.'+vnum
nm1 = target+'.so'
nm2 = target+'.so.'+nums[0]
thisenv.Command(nm1, tg, symlinkcom)
thisenv.Command(nm2, tg, symlinkcom)
#base=env['DESTDIR']+libdir+'/'
thisenv.bksys_install(libdir, nm1, nodestdir=nodestdir)
thisenv.bksys_install(libdir, nm2, nodestdir=nodestdir)
# Declare scons scripts to process
def subdirs(lenv, folderlist):
flist=[]
if type(folderlist) is types.ListType: flist = folderlist
else: flist = folderlist.split()
for i in flist:
lenv.SConscript(i+"/SConscript")
def link_local_shlib(lenv, str):
""" Links against a shared library made in the project """
lst = lenv.make_list(str)
for file in lst:
import re
reg = re.compile("(.*)/lib(.*).(la|so)")
result = reg.match(file)
if not result:
print "Unknown la file given "+file
continue
dir = result.group(1)
link = result.group(2)
lenv.AppendUnique(LIBS = [link])
lenv.PrependUnique(LIBPATH = [dir])
def link_local_staticlib(lenv, str):
""" Links against a shared library made in the project """
lst = lenv.make_list(str)
for file in lst:
import re
reg = re.compile("(.*)/(lib.*.a)")
result = reg.match(file)
if not result:
print "Unknown archive file given "+file
continue
f=SCons.Node.FS.default_fs.File(file)
lenv.Append(LINKFLAGS=[f.path])
#valid_targets = "program shlib kioslave staticlib".split()
SConsEnvironment.bksys_install = bksys_install
SConsEnvironment.bksys_shlib = bksys_shlib
SConsEnvironment.subdirs = subdirs
SConsEnvironment.link_local_shlib = link_local_shlib
SConsEnvironment.link_local_staticlib = link_local_staticlib
SConsEnvironment.genobj=genobj
if env.has_key('GENCXXFLAGS'):
env.PrependUnique( CXXFLAGS = env['GENCXXFLAGS'] )
if env.has_key('GENCCFLAGS'):
env.AppendUnique( CCFLAGS = env['GENCCFLAGS'] )
if env.has_key('GENLINKFLAGS'):
env.AppendUnique( LINKFLAGS = env['GENLINKFLAGS'] )
if env.has_key('EXTRAINCLUDES'):
if env['EXTRAINCLUDES']:
incpaths = []
for dir in str(env['EXTRAINCLUDES']).split(':'):
incpaths.append( dir )
env.Append(CPPPATH = incpaths)
env.Export('env')

@ -0,0 +1,43 @@
## Thomas Nagy, 2005
"""
Detect and store the most common options
* kdecxxflags : debug=1 (-g) or debug=full (-g3, slower)
else use the user CXXFLAGS if any, - or -O2 by default
* prefix : the installation path
* extraincludes : a list of paths separated by ':'
ie: scons configure debug=full prefix=/usr/local extraincludes=/tmp/include:/usr/local
"""
BOLD ="\033[1m"
RED ="\033[91m"
GREEN ="\033[92m"
YELLOW ="\033[1m" #"\033[93m" # unreadable on white backgrounds
CYAN ="\033[96m"
NORMAL ="\033[0m"
def exists(env):
return true
def generate(env):
## Bksys requires scons 0.96
env.EnsureSConsVersion(0, 96)
env._help = ''
def addHelpText(env, text):
env._help = env._help + text
def helpText(env):
text = env._help.replace(']', NORMAL)
text = text.replace('b[', BOLD)
text = text.replace('g[', GREEN)
text = text.replace('r[', RED)
text = text.replace('y[', YELLOW)
text = text.replace('c[', CYAN)
return text
from SCons.Script.SConscript import SConsEnvironment
SConsEnvironment.addHelpText = addHelpText
SConsEnvironment.helpText = helpText

@ -0,0 +1,820 @@
# Made from scons qt.py and (heavily) modified into kde.py
# Thomas Nagy, 2004, 2005 <tnagy2^8@yahoo.fr>
"""
Run scons -h to display the associated help, or look below ..
"""
BOLD ="\033[1m"
RED ="\033[91m"
GREEN ="\033[92m"
YELLOW ="\033[1m" #"\033[93m" # unreadable on white backgrounds
CYAN ="\033[96m"
NORMAL ="\033[0m"
import os, re, types
from SCons.Script.SConscript import SConsEnvironment
# Returns the name of the shared object (i.e. libkdeui.so.4)
# referenced by a libtool archive (like libkdeui.la)
def getSOfromLA(lafile):
contents = open(lafile, 'r').read()
match = re.search("^dlname='([^']*)'$", contents, re.M)
if match:
return match.group(1)
return None
# A helper, needed .. everywhere
def KDEuse(lenv, flags):
if lenv['HELP']: lenv.Exit(0)
_flags=lenv.make_list(flags)
if 'environ' in _flags:
## The scons developers advise against using this but it is mostly innocuous :)
lenv.AppendUnique( ENV = os.environ )
if not 'lang_qt' in _flags:
## Use this define if you are using the kde translation scheme (.po files)
lenv.Append( CPPFLAGS = '-DQT_NO_TRANSLATION' )
if 'rpath' in _flags:
## Use this to set rpath - this may cause trouble if folders are moved (chrpath)
lenv.Append( RPATH = [lenv['QTLIBPATH'], lenv['KDELIBPATH'], lenv['KDEMODULE']] )
kdelibpaths=[]
if lenv['KDELIBPATH'] == lenv['KDELIB']:
kdelibpaths = [lenv['KDELIB']]
else:
kdelibpaths = [lenv['KDELIBPATH'], lenv['KDELIB']]
lenv.Append( RPATH = [lenv['QTLIBPATH'], lenv['KDEMODULE']]+kdelibpaths )
if 'thread' in _flags:
## Uncomment the following if you need threading support
lenv.KDEaddflags_cxx( ['-DQT_THREAD_SUPPORT', '-D_REENTRANT'] )
if 'fastmoc' in _flags:
lenv['BKSYS_FASTMOC']=1
if not 'nohelp' in _flags:
if lenv['_CONFIGURE'] or lenv['HELP']:
lenv.Exit(0)
if not 'nosmart' or not lenv.has_key('nosmart_includes'):
lenv.AppendUnique(CPPPATH=['#/'])
lst=[]
if lenv.has_key('USE_THE_FORCE_LUKE'):
lst=lenv['USE_THE_FORCE_LUKE']
lenv.__delitem__('USE_THE_FORCE_LUKE')
for v in lst:
v.execute()
else:
lenv['nosmart_includes']=1
## To use kdDebug(intvalue)<<"some trace"<<endl; you need to define -DDEBUG
## it is done in admin/generic.py automatically when you do scons configure debug=1
def exists(env):
return True
def detect_kde(env):
""" Detect the qt and kde environment using kde-config mostly """
def getpath(varname):
if not env.has_key('ARGS'): return None
v=env['ARGS'].get(varname, None)
if v: v=os.path.abspath(v)
return v
prefix = getpath('prefix')
execprefix = getpath('execprefix')
datadir = getpath('datadir')
libdir = getpath('libdir')
kdeincludes = getpath('kdeincludes')
kdelibs = getpath('kdelibs')
qtincludes = getpath('qtincludes')
qtlibs = getpath('qtlibs')
libsuffix = ''
if env.has_key('ARGS'): libsuffix=env['ARGS'].get('libsuffix', '')
if libdir: libdir = libdir+libsuffix
## Detect the kde libraries
print "Checking for kde-config : ",
kde_config = os.popen("which kde-config 2>/dev/null").read().strip()
if len(kde_config):
print GREEN+"kde-config was found"+NORMAL
else:
print RED+"kde-config was NOT found in your PATH"+NORMAL
print "Make sure kde is installed properly"
print "(missing package kdebase-devel?)"
env.Exit(1)
env['KDEDIR'] = os.popen('kde-config -prefix').read().strip()
print "Checking for kde version : ",
kde_version = os.popen("kde-config --version|grep KDE").read().strip().split()[1]
if int(kde_version[0]) != 3 or int(kde_version[2]) < 2:
print RED+kde_version
print RED+"Your kde version can be too old"+NORMAL
print RED+"Please make sure kde is at least 3.2"+NORMAL
else:
print GREEN+kde_version+NORMAL
## Detect the qt library
print "Checking for the qt library : ",
qtdir = os.getenv("QTDIR")
if qtdir:
print GREEN+"qt is in "+qtdir+NORMAL
else:
try:
tmplibdir = os.popen('kde-config --expandvars --install lib').read().strip()
libkdeuiSO = tmplibdir+'/'+getSOfromLA(tmplibdir+'/libkdeui.la')
m = re.search('(.*)/lib/libqt.*', os.popen('ldd ' + libkdeuiSO + ' | grep libqt').read().strip().split()[2])
except:
m=None
if m:
qtdir = m.group(1)
print YELLOW+"qt was found as "+m.group(1)+NORMAL
else:
print RED+"qt was not found"+NORMAL
print RED+"Please set QTDIR first (/usr/lib/qt3?) or try scons -h for more options"+NORMAL
env.Exit(1)
env['QTDIR'] = qtdir.strip()
## Find the necessary programs uic and moc
print "Checking for uic : ",
uic = qtdir + "/bin/uic"
if os.path.isfile(uic):
print GREEN+"uic was found as "+uic+NORMAL
else:
uic = os.popen("which uic 2>/dev/null").read().strip()
if len(uic):
print YELLOW+"uic was found as "+uic+NORMAL
else:
uic = os.popen("which uic 2>/dev/null").read().strip()
if len(uic):
print YELLOW+"uic was found as "+uic+NORMAL
else:
print RED+"uic was not found - set QTDIR put it in your PATH ?"+NORMAL
env.Exit(1)
env['QT_UIC'] = uic
print "Checking for moc : ",
moc = qtdir + "/bin/moc"
if os.path.isfile(moc):
print GREEN + "moc was found as " + moc + NORMAL
else:
moc = os.popen("which moc 2>/dev/null").read().strip()
if len(moc):
print YELLOW + "moc was found as " + moc + NORMAL
elif os.path.isfile("/usr/share/qt3/bin/moc"):
moc = "/usr/share/qt3/bin/moc"
print YELLOW + "moc was found as " + moc + NORMAL
else:
print RED + "moc was not found - set QTDIR or put it in your PATH ?" + NORMAL
env.Exit(1)
env['QT_MOC'] = moc
## check for the qt and kde includes
print "Checking for the qt includes : ",
if qtincludes and os.path.isfile(qtincludes + "/qlayout.h"):
# The user told where to look for and it looks valid
print GREEN + "ok " + qtincludes + NORMAL
else:
if os.path.isfile(qtdir + "/include/qlayout.h"):
# Automatic detection
print GREEN + "ok " + qtdir + "/include/ " + NORMAL
qtincludes = qtdir + "/include/"
elif os.path.isfile("/usr/include/qt3/qlayout.h"):
# Debian probably
print YELLOW + "the qt headers were found in /usr/include/qt3/ " + NORMAL
qtincludes = "/usr/include/qt3"
else:
print RED + "the qt headers were not found" + NORMAL
env.Exit(1)
print "Checking for the kde includes : ",
kdeprefix = os.popen("kde-config --prefix").read().strip()
if not kdeincludes:
kdeincludes = kdeprefix+"/include/"
if os.path.isfile(kdeincludes + "/klineedit.h"):
print GREEN + "ok " + kdeincludes + NORMAL
else:
if os.path.isfile(kdeprefix+"/include/kde/klineedit.h"):
# Debian, Fedora probably
print YELLOW + "the kde headers were found in " + kdeprefix + "/include/kde/" + NORMAL
kdeincludes = kdeprefix + "/include/kde/"
else:
print RED + "The kde includes were NOT found" + NORMAL
env.Exit(1)
# kde-config options
kdec_opts = {'KDEBIN' : 'exe', 'KDEAPPS' : 'apps',
'KDEDATA' : 'data', 'KDEICONS' : 'icon',
'KDEMODULE' : 'module', 'KDELOCALE' : 'locale',
'KDEKCFG' : 'kcfg', 'KDEDOC' : 'html',
'KDEMENU' : 'apps', 'KDEXDG' : 'xdgdata-apps',
'KDEMIME' : 'mime', 'KDEXDGDIR' : 'xdgdata-dirs',
'KDESERV' : 'services','KDESERVTYPES' : 'servicetypes',
'KDEINCLUDE': 'include'
}
if prefix:
## use the user-specified prefix
if not execprefix:
execprefix = prefix
if not datadir:
datadir=prefix+"/share"
if not libdir:
libdir=execprefix+"/lib"+libsuffix
subst_vars = lambda x: x.replace('${exec_prefix}', execprefix)\
.replace('${datadir}', datadir)\
.replace('${libdir}', libdir)
debian_fix = lambda x: x.replace('/usr/share', '${datadir}')
env['PREFIX'] = prefix
env['KDELIB'] = libdir
for (var, option) in kdec_opts.items():
dir = os.popen('kde-config --install ' + option).read().strip()
if var == 'KDEDOC': dir = debian_fix(dir)
env[var] = subst_vars(dir)
else:
env['PREFIX'] = os.popen('kde-config --expandvars --prefix').read().strip()
env['KDELIB'] = os.popen('kde-config --expandvars --install lib').read().strip()
for (var, option) in kdec_opts.items():
dir = os.popen('kde-config --expandvars --install ' + option).read().strip()
env[var] = dir
env['QTPLUGINS']=os.popen('kde-config --expandvars --install qtplugins').read().strip()
## kde libs and includes
env['KDEINCLUDEPATH']=kdeincludes
if not kdelibs:
kdelibs=os.popen('kde-config --expandvars --install lib').read().strip()
env['KDELIBPATH']=kdelibs
## qt libs and includes
env['QTINCLUDEPATH']=qtincludes
if not qtlibs:
qtlibs=qtdir+"/lib"+libsuffix
env['QTLIBPATH']=qtlibs
def generate(env):
""""Set up the qt and kde environment and builders - the moc part is difficult to understand """
# attach this function immediately
SConsEnvironment.KDEuse = KDEuse
env.addHelpText("""
b[*** KDE options ***
-------------------]
b[* prefix ]: base install path, ie: /usr/local
b[* execprefix ]: install path for binaries, ie: /usr/bin
b[* datadir ]: install path for the data, ie: /usr/local/share
b[* libdir ]: install path for the libs, ie: /usr/lib
b[* libsuffix ]: suffix of libraries on amd64, ie: 64, 32
b[* kdeincludes]: path to the kde includes (/usr/include/kde on debian, ...)
b[* qtincludes ]: path to the for qt includes (/usr/include/qt on debian, ...)
b[* kdelibs ]: path to the kde libs, for linking the programs
b[* qtlibs ]: path to the qt libs, for linking the programs
ie: b[scons configure libdir=/usr/local/lib qtincludes=/usr/include/qt]
""")
import SCons.Defaults
import SCons.Tool
import SCons.Util
import SCons.Node
CLVar = SCons.Util.CLVar
splitext = SCons.Util.splitext
Builder = SCons.Builder.Builder
# Detect the environment - replaces ./configure implicitely and store the options into a cache
from SCons.Options import Options
cachefile=env['CACHEDIR']+'kde.cache.py'
opts = Options(cachefile)
opts.AddOptions(
('PREFIX', 'root of the program installation'),
('QTDIR', ''),
('QTLIBPATH', 'path to the qt libraries'),
('QTINCLUDEPATH', 'path to the qt includes'),
('QT_UIC', 'uic command'),
('QT_MOC', 'moc command'),
('QTPLUGINS', 'uic executable command'),
('KDEDIR', ''),
('KDELIBPATH', 'path to the installed kde libs'),
('KDEINCLUDEPATH', 'path to the installed kde includes'),
('KDEBIN', 'inst path of the kde binaries'),
('KDEINCLUDE', 'inst path of the kde include files'),
('KDELIB', 'inst path of the kde libraries'),
('KDEMODULE', 'inst path of the parts and libs'),
('KDEDATA', 'inst path of the application data'),
('KDELOCALE', ''), ('KDEDOC', ''), ('KDEKCFG', ''),
('KDEXDG', ''), ('KDEXDGDIR', ''), ('KDEMENU', ''),
('KDEMIME', ''), ('KDEICONS', ''), ('KDESERV', ''),
('KDESERVTYPES', ''), ('KDEAPPS', ''),
)
opts.Update(env)
def getInstDirForResType(lenv,restype):
if len(restype) == 0 or not lenv.has_key(restype):
print RED+"unknown resource type "+restype+NORMAL
lenv.Exit(1)
else:
instdir = lenv[restype]
basedir=lenv['DESTDIR']
## support for installing into other folders when PREFIX is set - used by gnu stow
if basedir: instdir = instdir.replace(lenv['PREFIX'], basedir)
return instdir
# reconfigure when things are missing
if not env['HELP'] and (env['_CONFIGURE'] or not env.has_key('QTDIR') or not env.has_key('KDEDIR')):
detect_kde(env)
opts.Save(cachefile, env)
## set default variables, one can override them in sconscript files
env.Append(CXXFLAGS = ['-I'+env['KDEINCLUDEPATH'], '-I'+env['QTINCLUDEPATH'] ],
LIBPATH = [env['KDELIBPATH'], env['QTLIBPATH'] ])
env['QT_AUTOSCAN'] = 1
env['QT_DEBUG'] = 0
env['MEINPROC'] = 'meinproc'
env['MSGFMT'] = 'msgfmt'
## ui file processing
def uic_processing(target, source, env):
inc_kde ='#include <klocale.h>\n#include <kdialog.h>\n'
inc_moc ='#include "%s"\n' % target[2].name
comp_h ='$QT_UIC -L $QTPLUGINS -nounload -o %s %s' % (target[0].path, source[0].path)
comp_c ='$QT_UIC -L $QTPLUGINS -nounload -tr tr2i18n -impl %s %s' % (target[0].path, source[0].path)
comp_moc ='$QT_MOC -o %s %s' % (target[2].path, target[0].path)
if env.Execute(comp_h):
return ret
dest = open( target[1].path, "w" )
dest.write(inc_kde)
dest.close()
if env.Execute( comp_c+" >> "+target[1].path ):
return ret
dest = open( target[1].path, "a" )
dest.write(inc_moc)
dest.close()
ret = env.Execute( comp_moc )
return ret
def uicEmitter(target, source, env):
adjustixes = SCons.Util.adjustixes
bs = SCons.Util.splitext(str(source[0].name))[0]
bs = os.path.join(str(target[0].get_dir()),bs)
target.append(bs+'.cpp')
target.append(bs+'.moc')
return target, source
env['BUILDERS']['Uic']=Builder(action=uic_processing,emitter=uicEmitter,suffix='.h',src_suffix='.ui')
def kcfg_buildit(target, source, env):
comp='kconfig_compiler -d%s %s %s' % (str(source[0].get_dir()), source[1].path, source[0].path)
return env.Execute(comp)
def kcfg_stringit(target, source, env):
print "processing %s to get %s and %s" % (source[0].name, target[0].name, target[1].name)
def kcfgEmitter(target, source, env):
adjustixes = SCons.Util.adjustixes
bs = SCons.Util.splitext(str(source[0].name))[0]
bs = os.path.join(str(target[0].get_dir()),bs)
# .h file is already there
target.append(bs+'.cpp')
if not os.path.isfile(str(source[0])):
print RED+'kcfg file given '+str(source[0])+' does not exist !'+NORMAL
print os.popen('pwd').read()
return target, source
kfcgfilename=""
kcfgFileDeclRx = re.compile("^[fF]ile\s*=\s*(.+)\s*$")
for line in file(str(source[0]), "r").readlines():
match = kcfgFileDeclRx.match(line.strip())
if match:
kcfgfilename = match.group(1)
break
if not kcfgfilename:
print 'invalid kcfgc file'
return 0
source.append(str(source[0].get_dir())+'/'+kcfgfilename)
return target, source
env['BUILDERS']['Kcfg']=Builder(action=env.Action(kcfg_buildit, kcfg_stringit),
emitter=kcfgEmitter, suffix='.h', src_suffix='.kcfgc')
## MOC processing
env['BUILDERS']['Moc']=Builder(action='$QT_MOC -o $TARGET $SOURCE',suffix='.moc',src_suffix='.h')
env['BUILDERS']['Moccpp']=Builder(action='$QT_MOC -o $TARGET $SOURCE',suffix='_moc.cpp',src_suffix='.h')
## KIDL file
env['BUILDERS']['Kidl']=Builder(action= 'dcopidl $SOURCE > $TARGET || (rm -f $TARGET ; false)',
suffix='.kidl', src_suffix='.h')
## DCOP
env['BUILDERS']['Dcop']=Builder(action='dcopidl2cpp --c++-suffix cpp --no-signals --no-stub $SOURCE',
suffix='_skel.cpp', src_suffix='.kidl')
## STUB
env['BUILDERS']['Stub']=Builder(action= 'dcopidl2cpp --c++-suffix cpp --no-signals --no-skel $SOURCE',
suffix='_stub.cpp', src_suffix='.kidl')
## DOCUMENTATION
env['BUILDERS']['Meinproc']=Builder(action='$MEINPROC --check --cache $TARGET $SOURCE',suffix='.cache.bz2')
## TRANSLATIONS
env['BUILDERS']['Transfiles']=Builder(action='$MSGFMT $SOURCE -o $TARGET',suffix='.gmo',src_suffix='.po')
## Handy helpers for building kde programs
## You should not have to modify them ..
ui_ext = [".ui"]
kcfg_ext = ['.kcfgc']
header_ext = [".h", ".hxx", ".hpp", ".hh"]
cpp_ext = [".cpp", ".cxx", ".cc"]
skel_ext = [".skel", ".SKEL"]
stub_ext = [".stub", ".STUB"]
def KDEfiles(lenv, target, source):
""" Returns a list of files for scons (handles kde tricks like .skel)
It also makes custom checks against double includes like : ['file.ui', 'file.cpp']
(file.cpp is already included because of file.ui) """
q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]')
def scan_moc(bs, file_cpp):
addfile=None
# try to find the header
h_ext=''
for n_h_ext in header_ext:
if os.path.isfile(bs+n_h_ext):
h_ext=n_h_ext
break
# We have the header corresponding to the cpp file
if h_ext:
needscan=0
# User asked for fastmoc, try to avoid scanning
if env.has_key('BKSYS_FASTMOC'):
if os.path.isfile(bs+'.moc'):
lenv.Moc(bs+h_ext)
elif os.path.isfile(bs+'_moc.cpp'):
lenv.Moccpp(bs+h_ext)
addfile=bs+'_moc.cpp'
else:
#print "need scanning "+os.getcwd()+'/'+bs+".moc"
needscan=1
else:
needscan=1
# We cannot avoid scanning the files ...
if needscan:
file_h=bs+h_ext
h_contents = open(file_h, 'rb').read()
if q_object_search.search(h_contents):
# we know now there is Q_OBJECT macro
lst = bs.split('/')
val = lst[ len(lst) - 1 ]
reg = '\n\s*#include\s*("|<)'+val+'.moc("|>)'
meta_object_search = re.compile(reg)
cpp_contents = open(file_cpp, 'rb').read()
if meta_object_search.search(cpp_contents):
lenv.Moc(file_h)
else:
lenv.Moccpp(file_h)
addfile=bs+'_moc.cpp'
print "WARNING: moc.cpp for "+bs+h_ext+" consider using #include <file.moc> instead"
return addfile
src=[]
ui_files=[]
kcfg_files=[]
other_files=[]
kidl=[]
source_=lenv.make_list(source)
# For each file, check wether it is a dcop file or not, and create the complete list of sources
for file in source_:
bs = SCons.Util.splitext(file)[0]
ext = SCons.Util.splitext(file)[1]
if ext in skel_ext:
if not bs in kidl:
kidl.append(bs)
lenv.Dcop(bs+'.kidl')
src.append(bs+'_skel.cpp')
elif ext in stub_ext:
if not bs in kidl:
kidl.append(bs)
lenv.Stub(bs+'.kidl')
src.append(bs+'_stub.cpp')
elif ext == ".moch":
lenv.Moccpp(bs+'.h')
src.append(bs+'_moc.cpp')
elif ext in cpp_ext:
src.append(file)
if not env.has_key('NOMOCFILE'):
ret = scan_moc(bs, file)
if ret:
src.append( ret )
elif ext in ui_ext:
lenv.Uic(file)
src.append(bs+'.cpp')
elif ext in kcfg_ext:
lenv.Kcfg(file)
src.append(bs+'.cpp')
else:
src.append(file)
for base in kidl:
lenv.Kidl(base+'.h')
# Now check against typical newbie errors
for file in ui_files:
for ofile in other_files:
if ofile == file:
print RED+"WARNING: You have included "+file+".ui and another file of the same prefix"+NORMAL
print "Files generated by uic (file.h, file.cpp must not be included"
for file in kcfg_files:
for ofile in other_files:
if ofile == file:
print RED+"WARNING: You have included "+file+".kcfg and another file of the same prefix"+NORMAL
print "Files generated by kconfig_compiler (settings.h, settings.cpp) must not be included"
return src
""" In the future, these functions will contain the code that will dump the
configuration for re-use from an IDE """
import glob
def KDEinstall(lenv, restype, subdir, files):
if env.has_key('DUMPCONFIG'):
print "<install type=\"%s\" subdir=\"%s\">" % (restype, subdir)
for i in lenv.make_list(files):
print " <file name=\"%s\"/>" % i
print "</install>"
return
if not env['_INSTALL']:
return
dir = getInstDirForResType(lenv, restype)
install_list = lenv.bksys_install(dir+'/'+subdir, files, nodestdir=1)
return install_list
def KDEinstallas(lenv, restype, destfile, file):
if not env['_INSTALL']:
return
dir = getInstDirForResType(lenv, restype)
install_list = lenv.InstallAs(dir+'/'+destfile, file)
env.Alias('install', install_list)
return install_list
def KDEprogram(lenv, target, source,
includes='', localshlibs='', globallibs='', globalcxxflags=''):
""" Makes a kde program
The program is installed except if one sets env['NOAUTOINSTALL'] """
src = KDEfiles(lenv, target, source)
program_list = lenv.Program(target, src)
# we link the program against a shared library done locally, add the dependency
if not lenv.has_key('nosmart_includes'):
lenv.AppendUnique(CPPPATH=['./'])
if len(localshlibs)>0:
lst=lenv.make_list(localshlibs)
lenv.link_local_shlib(lst)
lenv.Depends( program_list, lst )
if len(includes)>0:
lenv.KDEaddpaths_includes(includes)
if len(globallibs)>0:
lenv.KDEaddlibs(globallibs)
if len(globalcxxflags)>0:
lenv.KDEaddflags_cxx(globalcxxflags)
if not lenv.has_key('NOAUTOINSTALL'):
KDEinstall(lenv, 'KDEBIN', '', target)
return program_list
def KDEshlib(lenv, target, source, kdelib=0, libprefix='lib',
includes='', localshlibs='', globallibs='', globalcxxflags='', vnum=''):
""" Makes a shared library for kde (.la file for klibloader)
The library is installed except if one sets env['NOAUTOINSTALL'] """
src = KDEfiles(lenv, target, source)
if not lenv.has_key('nosmart_includes'):
lenv.AppendUnique(CPPPATH=['./'])
# we link the program against a shared library done locally, add the dependency
lst=[]
if len(localshlibs)>0:
lst=lenv.make_list(localshlibs)
lenv.link_local_shlib(lst)
if len(includes)>0:
lenv.KDEaddpaths_includes(includes)
if len(globallibs)>0:
lenv.KDEaddlibs(globallibs)
if len(globalcxxflags)>0:
lenv.KDEaddflags_cxx(globalcxxflags)
restype = 'KDEMODULE'
if kdelib==1:
restype = 'KDELIB'
library_list = lenv.bksys_shlib(target, src, getInstDirForResType(lenv, restype), libprefix, vnum, nodestdir=1)
if len(lst)>0: lenv.Depends( library_list, lst )
return library_list
def KDEstaticlib(lenv, target, source):
""" Makes a static library for kde - in practice you should not use static libraries
1. they take more memory than shared ones
2. makefile.am needed it because of limitations
(cannot handle sources in separate folders - takes extra processing) """
if not lenv.has_key('nosmart_includes'):
lenv.AppendUnique(CPPPATH=['./'])
src = KDEfiles(lenv, target, source)
return lenv.StaticLibrary(target, src)
# do not install static libraries by default
def KDEaddflags_cxx(lenv, fl):
""" Compilation flags for C++ programs """
lenv.AppendUnique(CXXFLAGS = lenv.make_list(fl))
def KDEaddflags_c(lenv, fl):
""" Compilation flags for C programs """
lenv.AppendUnique(CFLAGS = lenv.make_list(fl))
def KDEaddflags_link(lenv, fl):
""" Add link flags - Use this if KDEaddlibs below is not enough """
lenv.PrependUnique(LINKFLAGS = lenv.make_list(fl))
def KDEaddlibs(lenv, libs):
""" Helper function """
lenv.AppendUnique(LIBS = lenv.make_list(libs))
def KDEaddpaths_includes(lenv, paths):
""" Add new include paths """
lenv.AppendUnique(CPPPATH = lenv.make_list(paths))
def KDEaddpaths_libs(lenv, paths):
""" Add paths to libraries """
lenv.PrependUnique(LIBPATH = lenv.make_list(paths))
def KDElang(lenv, folder, appname):
""" Process translations (.po files) in a po/ dir """
transfiles = glob.glob(folder+'/*.po')
for lang in transfiles:
result = lenv.Transfiles(lang)
country = SCons.Util.splitext(result[0].name)[0]
KDEinstallas(lenv, 'KDELOCALE', country+'/LC_MESSAGES/'+appname+'.mo', result)
def KDEicon(lenv, icname='*', path='./', restype='KDEICONS', subdir=''):
"""Contributed by: "Andrey Golovizin" <grooz()gorodok()net>
modified by "Martin Ellis" <m.a.ellis()ncl()ac()uk>
Installs icons with filenames such as cr22-action-frame.png into
KDE icon hierachy with names like icons/crystalsvg/22x22/actions/frame.png.
Global KDE icons can be installed simply using env.KDEicon('name').
The second parameter, path, is optional, and specifies the icons
location in the source, relative to the SConscript file.
To install icons that need to go under an applications directory (to
avoid name conflicts, for example), use e.g.
env.KDEicon('name', './', 'KDEDATA', 'appname/icons')"""
if env.has_key('DUMPCONFIG'):
print "<icondirent subdir=\"%s\">" % (path+subdir)
return
type_dic = { 'action' : 'actions', 'app' : 'apps', 'device' :
'devices', 'filesys' : 'filesystems', 'mime' : 'mimetypes' }
dir_dic = {
'los' :'locolor/16x16',
'lom' :'locolor/32x32',
'him' :'hicolor/32x32',
'hil' :'hicolor/48x48',
'lo16' :'locolor/16x16',
'lo22' :'locolor/22x22',
'lo32' :'locolor/32x32',
'hi16' :'hicolor/16x16',
'hi22' :'hicolor/22x22',
'hi32' :'hicolor/32x32',
'hi48' :'hicolor/48x48',
'hi64' :'hicolor/64x64',
'hi128':'hicolor/128x128',
'hisc' :'hicolor/scalable',
'cr16' :'crystalsvg/16x16',
'cr22' :'crystalsvg/22x22',
'cr32' :'crystalsvg/32x32',
'cr48' :'crystalsvg/48x48',
'cr64' :'crystalsvg/64x64',
'cr128':'crystalsvg/128x128',
'crsc' :'crystalsvg/scalable'
}
iconfiles = []
for ext in "png xpm mng svg svgz".split():
files = glob.glob(path+'/'+'*-*-%s.%s' % (icname, ext))
iconfiles += files
for iconfile in iconfiles:
lst = iconfile.split('/')
filename = lst[ len(lst) - 1 ]
tmp = filename.split('-')
if len(tmp)!=3:
print RED+'WARNING: icon filename has unknown format: '+iconfile+NORMAL
continue
[icon_dir, icon_type, icon_filename]=tmp
try:
basedir=getInstDirForResType(lenv, restype)
destfile = '%s/%s/%s/%s/%s' % (basedir, subdir, dir_dic[icon_dir], type_dic[icon_type], icon_filename)
except KeyError:
print RED+'WARNING: unknown icon type: '+iconfile+NORMAL
continue
## Do not use KDEinstallas here, as parsing from an ide will be necessary
if env['_INSTALL']:
env.Alias('install', env.InstallAs( destfile, iconfile ) )
## This function uses env imported above
def docfolder(lenv, folder, lang, destination=""):
# folder is the folder to process
# lang is the language
# destination is the subdirectory in KDEDOC
docfiles = glob.glob(folder+"/???*.*") # file files that are at least 4 chars wide :)
# warn about errors
#if len(lang) != 2:
# print "error, lang must be a two-letter string, like 'en'"
# when the destination is not given, use the folder
if len(destination) == 0:
destination=folder
docbook_list = []
for file in docfiles:
# do not process folders
if not os.path.isfile(file):
continue
# do not process the cache file
if file == 'index.cache.bz2':
continue
# ignore invalid files (TODO??)
if len( SCons.Util.splitext( file ) ) <= 1 :
continue
ext = SCons.Util.splitext( file )[1]
# docbook files are processed by meinproc
if ext != '.docbook':
continue
docbook_list.append( file )
lenv.KDEinstall('KDEDOC', lang+'/'+destination, file)
# Now process the index.docbook files ..
if len(docbook_list) == 0:
return
if not os.path.isfile( folder+'/index.docbook' ):
print "Error, index.docbook was not found in "+folder+'/index.docbook'
return
## Define this to 1 if you are writing documentation else to 0 :)
if env.has_key('i_am_a_documentation_writer'):
for file in docbook_list:
lenv.Depends( folder+'index.cache.bz2', file )
lenv.Meinproc( folder+'/index.cache.bz2', folder+'/index.docbook' )
lenv.KDEinstall( 'KDEDOC', lang+'/'+destination, folder+'/index.cache.bz2' )
#valid_targets = "program shlib kioslave staticlib".split()
import generic
class kobject(generic.genobj):
def __init__(self, val, senv=None):
if senv: generic.genobj.__init__(self, val, senv)
else: generic.genobj.__init__(self, val, env)
self.iskdelib=0
def it_is_a_kdelib(self): self.iskdelib=1
def execute(self):
self.lockchdir()
if self.orenv.has_key('DUMPCONFIG'):
print self.xml()
return
if (self.type=='shlib' or self.type=='kioslave'):
install_dir = 'KDEMODULE'
if self.iskdelib==1: install_dir = 'KDELIB'
self.instdir=getInstDirForResType(self.orenv, install_dir)
self.nodestdir=1
elif self.type=='program':
self.instdir=getInstDirForResType(self.orenv, 'KDEBIN')
self.nodestdir=1
self.src=KDEfiles(env, self.target, self.source)
generic.genobj.execute(self)
self.unlockchdir()
def xml(self):
ret= '<compile type="%s" chdir="%s" target="%s" cxxflags="%s" cflags="%s" includes="%s" linkflags="%s" libpaths="%s" libs="%s" vnum="%s" iskdelib="%s" libprefix="%s">\n' % (self.type, self.chdir, self.target, self.cxxflags, self.cflags, self.includes, self.linkflags, self.libpaths, self.libs, self.vnum, self.iskdelib, self.libprefix)
if self.source:
for i in self.orenv.make_list(self.source):
ret += ' <source file="%s"/>\n' % i
ret += "</compile>"
return ret
# Attach the functions to the environment so that SConscripts can use them
SConsEnvironment.KDEprogram = KDEprogram
SConsEnvironment.KDEshlib = KDEshlib
SConsEnvironment.KDEstaticlib = KDEstaticlib
SConsEnvironment.KDEinstall = KDEinstall
SConsEnvironment.KDEinstallas = KDEinstallas
SConsEnvironment.KDElang = KDElang
SConsEnvironment.KDEicon = KDEicon
SConsEnvironment.KDEaddflags_cxx = KDEaddflags_cxx
SConsEnvironment.KDEaddflags_c = KDEaddflags_c
SConsEnvironment.KDEaddflags_link = KDEaddflags_link
SConsEnvironment.KDEaddlibs = KDEaddlibs
SConsEnvironment.KDEaddpaths_includes = KDEaddpaths_includes
SConsEnvironment.KDEaddpaths_libs = KDEaddpaths_libs
SConsEnvironment.docfolder = docfolder
SConsEnvironment.kobject = kobject

Binary file not shown.

@ -0,0 +1,6 @@
/* config.h -- Automatically generated by abakus.py
* Any changes you make to this file will be overwritten!
*/
/* HAVE_MPFR -- Defined if the MPFR library is being used. */
/* #undef HAVE_MPFR */

164
configure vendored

@ -0,0 +1,164 @@
#!/usr/bin/env python
# Configure script for abakus. I think it's better than the sample that came
# with bksys. Feel free to do with it what you want.
# By Michael Pyne <michael.pyne@kdemail.net>
import sys
import re
BOLD="\033[1m"
RED="\033[91m"
GREEN="\033[92m"
YELLOW="\033[93m"
CYAN="\033[96m"
NORMAL="\033[0m"
PROGRAM_NAME='abakus'
# First let's see if they asked for help.
if '--help' in sys.argv:
print '''This is a configure script to prepare %s for building.
You can pass the command line argument debug=1 to enable debugging for the
application, which can be useful if you have problems.
Otherwise, just run ./configure, and if you don't already have scons, a mini
version will be installed suitable for building %s.
'''.replace('%s', PROGRAM_NAME)
sys.exit(0)
# Check that we have the minimum version of Python needs to run SCons.
if sys.hexversion < 0x02030000:
# Use regexp for compatibility with ancient Python
version = re.split(' ', sys.version)[0]
print RED + 'Sorry, your version of Python is too old.' + NORMAL
print PROGRAM_NAME + ' requires Python 2.3 or greater to build.'
print "\nYou have Python " + version
sys.exit(1)
import os
# Check if scons is installed. We can use cool Python features now that we
# know we aren't using an ancient version of Python.
result = os.system('scons -v > /dev/null 2>&1')
scons = 'scons'
if os.WEXITSTATUS(result) == 0:
print GREEN + "scons already installed." + NORMAL
else:
# If we didn't find scons, don't whine to the user about it, just fix it.
print YELLOW + 'scons not installed, installing local copy.' + NORMAL
# Split this into two steps since some tars don't use j to mean bzip2
# compressed.
result = os.system('bzcat bksys/scons-mini.tar.bz2 | tar x')
if os.WEXITSTATUS(result) != 0:
print RED + 'Unable to extract scons' + NORMAL
sys.exit(2)
scons = '%s/scons' % os.getcwd()
# Now we now where scons is. Let's create a Makefile (after we configure) so
# that all the user has to do is type 'make'. Allow the user to pass command
# line arguments, which will be passed to the configure process.
if len(sys.argv) < 2:
options = ''
else:
options = " ".join(sys.argv[1:])
# reduce is pretty cool
# options = reduce(lambda x, y: x + '\n' + y, sys.argv[1:])
result = os.system(scons + ' configure ' + options)
if os.WEXITSTATUS(result) != 0:
print RED + 'Unable to configure scons' + NORMAL
sys.exit(3)
# Recursive generates a makefile for a directory. If topDir is True, the
# Makefile is slightly different.
def generate_makefile(dir, topDir = False):
file = name + "/Makefile"
# Write out Makefile.
try:
makefile = open(file, 'w')
except IOError:
print RED + "Unable to open " + file + NORMAL
sys.exit(4)
text = '''
## Makefile automatically generated by configure
SCONS=$scons
# $scons : compile
# $scons -c : clean
# $scons install : install
# $scons -c install : uninstall and clean
# Default target: use scons to build the program
all:
@$(SCONS) -Q
# Debugging possibilies:
# $scons --debug=explain, $scons --debug=tree
# To optimize the runtime:
# $scons --max-drift=1 --implicit-deps-unchanged
debug:
@$(SCONS) -Q --debug=tree
clean:
@$(SCONS) -c
install:
@$(SCONS) install
uninstall:
@$(SCONS) uninstall
# This target creates a tarball of the project (in theory)
dist:
@$(SCONS) dist
'''
if topDir:
text = text.replace('$scons', scons)
else:
text = text.replace('$scons', scons + ' -u')
try:
print "Generating " + GREEN + file + NORMAL
makefile.write(text)
makefile.close()
except IOError:
print RED + "Unable to write to the Makefile!" + NORMAL
sys.exit(5)
# Recursively generate Makefiles for convienience.
for name, dirs, files in os.walk('.'):
# Don't try to build hidden directories.
remove = filter(lambda x: x[0] == '.', dirs)
for i in remove:
dirs.remove(i)
if 'SConstruct' in files:
# We're in the very top directory.
generate_makefile(name, topDir = True)
for dir in ['cache', 'bksys']:
if dir in dirs:
dirs.remove(dir)
elif 'SConscript' in files:
generate_makefile(name)
# The Makefile has been written, we're pretty much done.
message = '''
The Makefile(s) have been generated. Type:
`make' to build %s, and
`make install' to install %s.
'''.replace('%s', PROGRAM_NAME)
print GREEN + message + NORMAL

@ -0,0 +1,12 @@
#!/usr/bin/python
Import('env')
import glob
sources = glob.glob("*.png")
destination = 'abakus'
for lang in ['en']:
for pic in sources:
env.KDEinstall('KDEDOC', "%s/%s" % (lang, destination), pic)

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

@ -0,0 +1,463 @@
<?xml version="1.0" ?>
<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
<!ENTITY abakus "<application>abakus</application>">
<!ENTITY kappname "&abakus;">
<!ENTITY % addindex "IGNORE">
<!ENTITY % English "INCLUDE">
]>
<book lang="&language;">
<bookinfo>
<title>The &abakus; handbook</title>
<authorgroup>
<author>
<firstname>Michael</firstname>
<surname>Pyne</surname>
<email>michael.pyne@kdemail.net</email>
</author>
<author>&J.Hall; &J.Hall.mail;</author>
<!-- TRANS:ROLES_OF_TRANSLATORS -->
</authorgroup>
<legalnotice>&FDLNotice;</legalnotice>
<date>2005-07-06</date>
<releaseinfo>0.90</releaseinfo>
<abstract>
<para>&abakus; is a calculator designed with computer usability in mind</para>
<para>Please contact <email>michael.pyne@kdemail.net</email> for bug reports and feature requests</para>
</abstract>
<keywordset>
<keyword>KDE</keyword>
<keyword>Calculator</keyword>
</keywordset>
</bookinfo>
<chapter id="introduction">
<title>What is abakus?</title>
<para>&abakus; is a calculator designed with computer usability in mind, as opposed to just being a clone of your desktop calculator. Instead of using your powerful computer to put a limited simulation of a calculator on-screen, &abakus; instead allows you to use your computer to its greater potential.</para>
<para>Enough of the metaphysics, how about an example of what I'm talking about? Let's use&kcalc;, the normal &kde; calculator program as an example, trying to calculate 3 multiplied by the sine of 50 degrees. First you would do something like the following:</para>
<para>1. Make sure you're in Degrees mode:</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="kcalc-degrees-mode.png" format="PNG"/></imageobject>
</inlinemediaobject></para>
<para>2. Then, click on the <guibutton>5</guibutton> and the <guibutton>0</guibutton> buttons:</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="kcalc-fifty.png" format="PNG"/></imageobject>
</inlinemediaobject></para>
<para>3. You would then search for the <guibutton>Sin</guibutton> button and click it, which would immediately calculate a result:</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="kcalc-sine.png" format="PNG"/></imageobject>
</inlinemediaobject></para>
<para>4. Then, click the <guibutton>X</guibutton> button, and notice that there is no user feedback whatsoever. Even real calculators temporarily blank the display:</para>
<para>
<inlinemediaobject>
<imageobject>
<imagedata fileref="kcalc-sine.png" format="PNG"/></imageobject>
</inlinemediaobject></para>
<para>5. Then, we click the <guibutton>3</guibutton> button and watch as our previous result suddenly disappears. This isn't &kcalc;s fault: It has nowhere else to display the <guilabel>3</guilabel>:</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="kcalc-three.png" format="PNG"/></imageobject>
</inlinemediaobject></para>
<para>6. Proceeding boldly forward with the faith that our previous result wasn't lost, we click the <guibutton>=</guibutton> button to perform the multiplication:</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="kcalc-result.png" format="PNG"/></imageobject>
</inlinemediaobject></para>
<para>Although we did get the result, this is unsatisfactory for several reasons. A computer monitor has plenty of room to display text, there's no reason why you should ever be confused about what step your calculator is about to make, or whether it remembered an intermediate result. Computers are also much more powerful than your $10 desktop calculator, so there's no reason that you should be forced to type your expression in a form suitable for your calculator. Roberto Alsina picked up on this in a rant he published, <ulink url="http://www.pycs.net/lateral/stories/33.html">A Modest Usability Improvement
</ulink></para>
<para>Now let's try the same thing in &abakus;, which is designed to help you with your calculating, instead of bending you to its will. As with &kcalc;, extraneous portions of the GUI have been hidden.</para>
<para>1. We still need to make sure we're in <guilabel>Degrees</guilabel> mode:</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="abakus-degrees-mode.png" format="PNG"/></imageobject>
</inlinemediaobject></para>
<para>
2. Now we can type <quote>3sin 50</quote>, just as we'd write it on paper. Sometimes it's better to clarify things, both for reading and to clarify your intentions to &abakus;. So you can also use the parentheses to group operations like you would on paper, and the '*' operator to explicitly multiply, like this: "3 * sin (50)":</para>
<para><inlinemediaobject>
<imageobject>
<imagedata fileref="abakus-result.png" format="PNG"/></imageobject>
</inlinemediaobject></para>
<para> And we're all done! We even typed in the same expression two different ways to demonstrate how &abakus; will try very hard to guess what you're trying to calculate. You'll notice that &kcalc; and &abakus; both agree on the answer.</para>
<para>If you're still reading you've probably been sold by Roberto's argument, just as I was when I started writing &abakus;. So read on, if you want to find out all that &abakus; can do for you.</para>
</chapter>
<chapter id="abakus-usage">
<title>How to use &abakus;</title>
<sect1 id="basics">
<title>Basic usage</title>
<para>The basic synposis is: Type your expression, and hit Enter. &abakus; will calculate the value of what you typed and display it on the screen. You can use many functions from mathematics, and even define your own functions. You can also define and use variables.</para>
<para>You can define your own functions in abakus. To do so, at the expression prompt, you would type something like: <userinput>set funcname(var) = expr</userinput> and hit Enter. If all went well &abakus; will simply output <guilabel>OK</guilabel>, and you'll see your function appear in the user-defined function list. Now you can use your function as normal. If you'd like to remove your function, you can either right-click on it in the user function list and select <guilabel>Remove Function</guilabel>, or enter <userinput>remove funcname()</userinput> in the expression prompt and hit Enter. Note that you don't enter the variable name in the parentheses since only the function name is needed. (The reason you still need the parentheses is because your variables can have the same name as a function).</para>
<para>You can also define your own variables. &abakus; comes with the basic mathematical constants pi (&pi;) and e (Euler's Constant) defined by default. To define your own variable, at the expression prompt you would type: <userinput><replaceable>name</replaceable> = <replaceable>value</replaceable></userinput>, or <userinput>set <replaceable>name</replaceable> = <replaceable>value</replaceable></userinput>. You will then see your variable in the list of variables. To remove your variable, either right-click on it in the list and select <guilabel>Remove Variable</guilabel>, or enter <userinput>remove varname</userinput> in the expression prompt. Notice that there are no parentheses this time. ;-)</para>
<sect2 id="variables">
<title>Placeholder Variables</title>
<para>You may have noticed that when you type in expressions, &abakus; will show a value beginning with $ (such as $0) after the result. This is a placeholder variable. What happens is that the most recent result is always $0. The result directly before is $1, and so on. You may use the placeholder values in your expression to avoid having to re-type it or use the drag-and-drop. Note that there is a special variable defined called ans, which is the same as $0. In other words, whenever you want to reference the last expression's result, you can use $0 or ans.</para>
</sect2>
<sect2 id="precision">
<title>Decimal Precision</title>
<para>&abakus; supports high-precision arithmetic using Ariya Hidayat's hmath code from his excellent calculator <ulink url="http://speedcrunch.berlios.de/">SpeedCrunch</ulink>. You can change the displayed precision by using the <guilabel>View Menu</guilabel>, where you can select between <guilabel>Automatic precision</guilabel>, or some pre-defined precision levels. You can also select <guilabel>Custom precision</guilabel> to select your own precision (between 0-75 digits).</para>
</sect2>
<sect2>
<title>Operators</title>
<para>&abakus; supports all the standard operators like -, +, *, and /. It also supports both the ^ and ** symbols to mean exponentiation. Exponentiation is right-associative in &abakus;, meaning that 2^3^2 will return 512 instead of 64. (2^(3^2)). Operator precedence is performed as in mathematics as well (e.g. 2 + 3 * 2 and 3 * 2 + 2 both return the same answer). &abakus; also supports parentheses to group expressions for when the default operator precedence is invalid.</para>
</sect2>
<sect2>
<title>Functions</title>
<para>&abakus; has quite a few functions built-in:</para>
<itemizedlist>
<listitem><para>sin, cos, tan: Trigonometric functions. Supports Degrees and Radians mode.</para></listitem>
<listitem><para>asin, acos, atan: Inverse trigonometric functions. Supports Degrees and Radians mode.</para></listitem>
<listitem><para>abs: The absolute value of a number.</para></listitem>
<listitem><para>sqrt: Square root of a number.</para></listitem>
<listitem><para>ln / log: Logarithms. ln uses the "natural base", e, which log uses base 10.</para></listitem>
<listitem><para>exp: Exponential. Returns e to the given power. exp(x) is equivalent to e^x.</para></listitem>
<listitem><para>round, ceil, floor, int: Converts an answer to an integer. ceil rounds to the next highest integer, while floor rounds to the next lower. int simply drops the fractional part. round rounds to the nearest integer.</para></listitem>
<listitem><para>frac: Returns the fractional part of a number.</para></listitem>
<listitem><para>sinh, cosh, tanh: Hyperbolic trigonometric functions.</para></listitem>
<listitem><para>deriv: Returns the numerical derivative of the given expression. The graphical interpretation of a derivative is the slope of the given function, at the given point. It is used like this: deriv(exp, pt). Note that since deriv takes two arguments that the parentheses are required to avoid ambiguity. For most functions, the value that deriv returns will be exact (at least within the bounds allowed by the underlying decimal representation).</para></listitem>
</itemizedlist>
</sect2>
</sect1>
</chapter>
<chapter id="abakus-advanced">
<title>Advanced Features</title>
<para>&abakus; supports some features not typically seen in computer
calculators.</para>
<sect1 id="advanced-rpn-mode">
<title>RPN Mode</title>
<para>RPN Mode is a different input method for &abakus;, designed to emulate the
input style of various old calculators which are still popular. If you do not
already know what RPN Mode is, &abakus; is not the best way to find out.
However, I will give a brief description of it.
</para>
<para>In RPN Mode, the calculator operates from what is called the <interface>stack</interface>.
Number are always added to the end of the <interface>stack</interface>, and operators always work from
the end of the <interface>stack</interface>. One nice thing about RPN (and the
reason it was developed in the first place) is that RPN expressions don't
require parentheses, since the order of operations is completely unambiguous.
</para>
<para>So the way things work is that in RPN mode, you type in numbers and operators separated by
spaces. For each number you type, &abakus; will add to the end of the stack.
Every time &abakus; encounters an operator, &abakus; will remove the appropriate
number of values from the end of the stack, apply the operator, and then place
the result back on the end of the stack. &abakus; continues in this fashion
until it reaches the end of your expression, and then returns whatever value
is left at the top of the stack as its result.</para>
<para>Let's see how this works with an example:</para>
<informalexample>
<para>
<userinput>2 3 4 * + 2 /</userinput> would return
<computeroutput>7</computeroutput>
</para>
<para>The way that this works is that first <userinput>2</userinput>, and then
<userinput>3</userinput> are added to the end of the
<interface>stack</interface>. <userinput>4</userinput> is read and is also
added to the end. So at this point there are 3 numbers on the
<interface>stack</interface>. When &abakus; reads the operator
<userinput>*</userinput>, it removes 3 and 4 from the end of the <interface>stack</interface> and
multiplies them, resulting in 12. 12 is then added to the end of the <interface>stack</interface>, and
the <interface>stack</interface> has 2 numbers (2 and 12).
</para>
<para>&abakus; then reads the <userinput>+</userinput> and performs the same
process, adding 12 and 2, and replacing them in the <interface>stack</interface> with 14.
<userinput>2</userinput> is then added to the end, and then &abakus; performs
the division of 14 by 2, leaving 7 on the <interface>stack</interface>, which becomes the final
result.
</para>
</informalexample>
<para>You can also use functions in place of operators. For example,
<userinput>pi 2 / sin</userinput> would calculate the value of sin(pi / 2).
</para>
<para>If the <interface>stack</interface> has more than one element by the end
of the expression, &abakus; will only remove the value at the end. The values
left in the <interface>stack</interface> will be retained for later
calculations. You can see how many values are on the
<interface>stack</interface> using the special variable
<guilabel>stackCount</guilabel> which appears in the
<interface>Variable List</interface> while in RPN mode.
</para>
<para>&abakus; supports a few special commands while in RPN mode, that affect
the <interface>stack</interface> as follows:
<itemizedlist>
<listitem><para><userinput>pop</userinput>, which removes the value on the end
of the <interface>stack</interface>.</para></listitem>
<listitem><para><userinput>clear</userinput>, which clears the
<interface>stack</interface> completely. </para></listitem>
</itemizedlist>
</para>
<para>The <userinput>set</userinput> and <userinput>remove</userinput> commands
are currently unsupported in RPN Mode.</para>
</sect1>
<sect1 id="advanced-dnd">
<title>Drag and Drop</title>
<para>You can drag and drop results to other applications. When doing so,
&abakus; will construct an image for the mouse cursor which includes the text
you are dragging so that you always can tell exactly what you're about to drop
into an application.</para>
<screenshot>
<screeninfo>Demonstration of &abakus; drag and drop</screeninfo>
<mediaobject>
<imageobject>
<imagedata fileref="abakus-dnd.png" format="PNG" />
</imageobject>
<textobject>
<phrase>Demonstration of &abakus; drag and drop</phrase>
</textobject>
</mediaobject>
</screenshot>
</sect1>
</chapter>
<chapter id="command-reference">
<title>Command Reference</title>
<para>Here is a brief description of the commands in the &abakus; interface.</para>
<sect1 id="command-menu">
<title>Menu Commands</title>
<variablelist>
<varlistentry>
<term><menuchoice>
<shortcut><keycombo action="simul">&Ctrl;<keycap>Q</keycap></keycombo></shortcut>
<guimenu>File</guimenu><guimenuitem>Quit</guimenuitem>
</menuchoice></term>
<listitem><para>Quit &abakus;</para></listitem>
</varlistentry>
</variablelist>
<variablelist>
<varlistentry>
<term><menuchoice>
<shortcut><keycombo action="simul">&Alt;&Shift;<keycap>H</keycap></keycombo></shortcut>
<guimenu>View</guimenu><guimenuitem>Show History List</guimenuitem>
</menuchoice></term>
<listitem><para>If checked, &abakus; will show the list of previous results.
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<shortcut><keycombo action="simul">&Alt;&Shift;<keycap>V</keycap></keycombo></shortcut>
<guimenu>View</guimenu><guimenuitem>Show Variable List</guimenuitem>
</menuchoice></term>
<listitem><para>If checked, &abakus; will show the list of variables.
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<shortcut><keycombo action="simul">&Alt;&Shift;<keycap>F</keycap></keycombo></shortcut>
<guimenu>View</guimenu><guimenuitem>Show Function List</guimenuitem>
</menuchoice></term>
<listitem><para>If checked, &abakus; will show the list of functions.
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<guimenu>View</guimenu><guimenuitem>Automatic Precision</guimenuitem>
</menuchoice></term>
<listitem><para>If checked, &abakus; will select a precision automatically.
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<guimenu>View</guimenu><guimenuitem>3 Decimal Digits</guimenuitem>
</menuchoice></term>
<listitem><para>If checked, &abakus; will 8 decimal digits of precision (e.g.
1 / 3 = 0.333).
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<guimenu>View</guimenu><guimenuitem>8 Decimal Digits</guimenuitem>
</menuchoice></term>
<listitem><para>If checked, &abakus; will show 8 decimal digits of precision (e.g.
1 / 3 = 0.33333333).
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<guimenu>View</guimenu><guimenuitem>15 Decimal Digits</guimenuitem>
</menuchoice></term>
<listitem><para>If checked, &abakus; will show 15 decimal digits of precision.
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<guimenu>View</guimenu><guimenuitem>50 Decimal Digits</guimenuitem>
</menuchoice></term>
<listitem><para>If checked, &abakus; will show 50 decimal digits of precision.
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<guimenu>View</guimenu><guimenuitem>Custom Precision...</guimenuitem>
</menuchoice></term>
<listitem><para>This command will bring open a dialog allowing you to enter a
custom precision level. &abakus; supports precision levels from 0 to 75
decimal digits.
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<shortcut><keycombo action="simul">&Alt;&Shift;<keycap>C</keycap></keycombo></shortcut>
<guimenu>View</guimenu><guimenuitem>Activate Compact Mode</guimenuitem>
</menuchoice></term>
<listitem><para>This command is a shortcut to disable the Result, Function,
and Variable views, so that &abakus; shows just the input line. In this mode,
the answer is given in the input line instead of the Result list.
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<shortcut><keycombo action="simul">&Alt;&Shift;<keycap>L</keycap></keycombo></shortcut>
<guimenu>View</guimenu><guimenuitem>Clear History</guimenuitem>
</menuchoice></term>
<listitem><para>This command clears the Result view.
</para></listitem>
</varlistentry>
</variablelist>
<variablelist>
<varlistentry>
<term><menuchoice>
<shortcut><keycombo action="simul">&Alt;&Shift;<keycap>D</keycap></keycombo></shortcut>
<guimenu>Mode</guimenu><guimenuitem>Degrees</guimenuitem>
</menuchoice></term>
<listitem><para>This command causes &abakus; to treat values for the
trigonometric functions as angles measured in degrees (360 degrees make up a
full circle).
<important><para>The <userinput>deriv()</userinput> function will not return correct
results for trigonometric functions in this mode.</para></important>
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<shortcut><keycombo action="simul">&Alt;&Shift;<keycap>R</keycap></keycombo></shortcut>
<guimenu>Mode</guimenu><guimenuitem>Radians</guimenuitem>
</menuchoice></term>
<listitem><para>This command causes &abakus; to treat values for the
trigonometric functions as angles measured in radians (2 * &pi; radians make up
a full circle).
<important><para>The <userinput>deriv()</userinput> function only returns correct
results for trigonometric functions when in this mode.</para></important>
</para></listitem>
</varlistentry>
<varlistentry>
<term><menuchoice>
<shortcut><keycombo action="simul">&Alt;&Shift;<keycap>P</keycap></keycombo></shortcut>
<guimenu>Mode</guimenu><guimenuitem>Use RPN Mode</guimenuitem>
</menuchoice></term>
<listitem><para>This command enables <link linkend="advanced-rpn-mode">RPN Mode</link>.
</para></listitem>
</varlistentry>
</variablelist>
</sect1>
</chapter>
<chapter id="limitations">
<title>Limitations</title>
<para>There are many cool things yet to add to &abakus;. Here's a partial list of things that &abakus; doesn't support:</para>
<itemizedlist>
<listitem><para>Complex numbers.</para></listitem>
<listitem><para>User-defined functions of more than one variable.</para></listitem>
<listitem><para>Unit analysis (for example: 3 A / 1.5 ohm -> 1.5 V)</para></listitem>
<listitem><para>Advanced input as in SpeedCrunch.</para></listitem>
<listitem><para>Numerical integration (finding the area under a given curve).</para></listitem>
<listitem><para>Graphing (? - I'll admit I'm not sure if this would be a great fit for &abakus;)</para></listitem>
<listitem><para>Matrices</para></listitem>
<listitem><para>Functions on lists (e.g. sin {0, pi / 2} -> {0, 1})</para></listitem>
<listitem><para>Session export/import (the session is still saved/loaded automatically).</para></listitem>
<listitem><para>More functions. Although many functions that aren't built-in can be simulated. For instance, to take the logarithm of x to a different base (b), you could do (ln x / ln b). And the x<superscript>th</superscript> root of a number is just that number raised to the (1 / x) power.</para></listitem>
</itemizedlist>
</chapter>
<chapter id="credits">
<title>Credits and License</title>
<para>&abakus;</para>
<para>Program copyright 2005 Michael Pyne</para>
<para>Original documentation copyright 2005 Michael Pyne</para>
<para>Docbook conversion by &J.Hall; &J.Hall.mail;</para>
&underFDL; <!-- FDL: do not remove -->
</chapter>
</book>

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@ -0,0 +1,81 @@
#! /usr/bin/env python
## This script demonstrates how to build and install
## a simple kde program having KconfigXT settings
## with scons
##
## Thomas Nagy, 2004, 2005
## This file can be reused freely for any project (see COPYING)
############################
## load the config
## Use the environment and the tools set in the top-level
## SConstruct file (set with 'Export') - this is very important
Import( 'env' )
myenv=env.Copy()
#############################
## the programs to build
# The sources for our program - only .ui, .skel and .cpp are accepted
abakus_sources = """
abakus.cpp
abakuslistview.cpp
dragsupport.cpp
editor.cpp
evaluator.cpp
function.cpp
lexer_lex.cpp
mainwindow.cpp
node.cpp
numerictypes.cpp
parser_yacc.cpp
result.cpp
resultlistview.cpp
resultlistviewtext.cpp
rpnmuncher.cpp
valuemanager.cpp
dcopIface.skel
"""
if myenv.get('mpfr', 'no') == 'yes':
myenv.Append(LIBS = ['mpfr', 'gmp'])
else:
abakus_sources = abakus_sources + " hmath.cpp number.c"
myenv.KDEprogram( "abakus", abakus_sources )
myenv.KDEicon( 'abakus' )
# Mark these as being created by flex and bison if it's installed.
if myenv.Dictionary().has_key('PARSER_INCLUDED'):
myenv.CXXFile( "lexer_lex.cpp", "lexer.ll" )
myenv.CXXFile( "parser_yacc.cpp", "parser.yy", YACCFLAGS="-d" )
if myenv['HAVE_ASNEEDED']:
myenv.Append(LINKFLAGS = '-Wl,--as-needed')
myenv.Append(CXXFLAGS = '-Wno-non-virtual-dtor')
############################
## Customization
## Additional include paths for compiling the source files
## Always add '../' (top-level directory) because moc makes code that needs it
myenv.KDEaddpaths_includes('#/src/ #/')
## Necessary libraries to link against
myenv.KDEaddlibs( 'qt-mt kio kdecore kdeprint kdeui' )
#############################
## Data to install
## The ui.rc file and the tips go into datadir/appname/
myenv.KDEinstall( 'KDEDATA', 'abakus', 'abakusui.rc' )
## Warning : there is a difference between the normal destop file used for the menu
## and the servicetype desktop file, so they go in different directories
## you will find more information in 'test3'
myenv.KDEinstall( 'KDEMENU', 'Utilities', 'abakus.desktop')

@ -0,0 +1,74 @@
/*
* abakus.cpp - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
#include <kaboutdata.h>
#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kdebug.h>
#include <config.h>
#if HAVE_MPFR
#include <mpfr.h>
#endif
#include "mainwindow.h"
const char *const version = "0.91";
int main(int argc, char **argv)
{
KAboutData *about = new KAboutData("abakus", I18N_NOOP("abakus"), version,
I18N_NOOP("A simple keyboard-driven calculator"), KAboutData::License_GPL,
"(c) 2004, 2005 Michael Pyne", 0 /* text */, "http://grammarian.homelinux.net/abakus/",
"michael.pyne@kdemail.net");
about->addAuthor("Michael Pyne",
I18N_NOOP("Developer"),
"michael.pyne@kdemail.net",
"http://grammarian.homelinux.net/");
about->addCredit("Ariya Hidayat",
I18N_NOOP("High precision math routines, and inspiration for the new design came from his C++ implementation (SpeedCrunch)"),
"ariya@kde.org",
"http://speedcrunch.berlios.de/");
about->addCredit("Roberto Alsina",
I18N_NOOP("Came up with the initial idea, along with a Python implementation."),
"ralsina@netline.com.ar",
"http://dot.kde.org/1096309744/");
about->addCredit("Zack Rusin",
I18N_NOOP("Inspiration/code for the initial design came from his Ruby implementation."),
"zack@kde.org");
#if HAVE_MPFR
mpfr_set_default_prec(6 * 78); // 78 digits, figure about 6 bits needed.
kdDebug() << "Using the MPFR high-precision library.\n";
#else
kdDebug() << "Using the internal high-precision library.\n";
#endif
KCmdLineArgs::init(argc, argv, about);
KApplication app;
MainWindow *win = new MainWindow;
app.setMainWidget(win);
app.connect(&app, SIGNAL(lastWindowClosed()), SLOT(quit()));
win->show();
win->resize(500, 300);
return app.exec();
}

@ -0,0 +1,38 @@
# KDE Config File
[Desktop Entry]
Encoding=UTF-8
Type=Application
X-KDE-StartupNotify=true
Exec=abakus -caption "%c" %i %m
Icon=abakus
Comment=
Comment[xx]=xxxx
Terminal=0
Name=Abakus
Name[tr]=Abaküs
Name[xx]=xxAbakusxx
GenericName=Calculator
GenericName[bg]=Калкулатор
GenericName[br]=Jederez
GenericName[cs]=Kalkulátor
GenericName[cy]=Cyfrifiannell
GenericName[da]=Lommeregner
GenericName[el]=Υπολογιστής Τσέπης
GenericName[es]=Calculadora
GenericName[et]=Kalkulaator
GenericName[fr]=Calculateur
GenericName[ga]=Áireamhán
GenericName[gl]=Calculadora
GenericName[it]=Calcolatrice
GenericName[ka]=კალკულატორი
GenericName[lt]=Skaičiuotuvas
GenericName[nl]=Rekenmachine
GenericName[pt]=Calculadora
GenericName[rw]=Mubazi
GenericName[sk]=Kalkulačka
GenericName[sr]=Рачунаљка
GenericName[sr@Latn]=Računaljka
GenericName[sv]=Miniräknare
GenericName[tr]=Hesap Makinesi
GenericName[xx]=xxCalculatorxx
Categories=Qt;KDE;Utility

@ -0,0 +1,22 @@
// header file for pch support.
#if defined __cplusplus
#include <kapplication.h>
#include <kdebug.h>
#include <kpushbutton.h>
#include <kconfig.h>
#include <kglobal.h>
#include <klocale.h>
#include <kcombobox.h>
#include <kpopupmenu.h>
#include <kaction.h>
#include <qlabel.h>
#include <qregexp.h>
#include <qtimer.h>
#include "function.h"
#include "node.h"
#include "valuemanager.h"
#endif

@ -0,0 +1,232 @@
/*
* abakuslistview.cpp - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
#include <klocale.h>
#include <kpopupmenu.h>
#include <kdebug.h>
#include <qdragobject.h>
#include <qcursor.h>
#include <qheader.h>
#include "dragsupport.h"
#include "abakuslistview.h"
#include "valuemanager.h"
#include "function.h"
ListView::ListView(QWidget *parent, const char *name) :
KListView(parent, name), m_menu(0), m_usePopup(false), m_removeSingleId(0),
m_removeAllId(0)
{
setResizeMode(LastColumn);
setDragEnabled(true);
connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)),
SLOT(rightClicked(QListViewItem *, const QPoint &)));
}
QDragObject *ListView::dragObject()
{
QPoint viewportPos = viewport()->mapFromGlobal(QCursor::pos());
QListViewItem *item = itemAt(viewportPos);
if(!item)
return 0;
int column = header()->sectionAt(viewportPos.x());
QString dragText = item->text(column);
QDragObject *drag = new QTextDrag(dragText, this, "list item drag");
drag->setPixmap(DragSupport::makePixmap(dragText, font()));
return drag;
}
void ListView::enablePopupHandler(bool enable)
{
if(enable == m_usePopup)
return;
m_usePopup = enable;
if(m_usePopup) {
if(m_menu)
kdError() << "ListView menu shouldn't exist here!\n";
m_menu = new KPopupMenu(this);
m_removeSingleId = m_menu->insertItem(removeItemString(), this, SLOT(removeSelected()));
m_removeAllId = m_menu->insertItem("Placeholder", this, SLOT(removeAllItems()));
}
else {
delete m_menu;
m_menu = 0;
}
}
QString ListView::removeItemString() const
{
return QString();
}
QString ListView::removeAllItemsString(unsigned count) const
{
Q_UNUSED(count);
return QString();
}
void ListView::removeSelectedItem(QListViewItem *item)
{
Q_UNUSED(item);
}
void ListView::removeAllItems()
{
}
bool ListView::isItemRemovable(QListViewItem *item) const
{
Q_UNUSED(item);
return false;
}
void ListView::rightClicked(QListViewItem *item, const QPoint &pt)
{
if(!m_usePopup)
return;
m_menu->setItemEnabled(m_removeSingleId, item && isItemRemovable(item));
m_menu->changeItem(m_removeAllId, removeAllItemsString(childCount()));
m_menu->popup(pt);
}
void ListView::removeSelected()
{
removeSelectedItem(selectedItem());
}
ValueListViewItem::ValueListViewItem(QListView *listView, const QString &name,
const Abakus::number_t &value) :
KListViewItem(listView, name), m_value(value)
{
valueChanged();
}
void ValueListViewItem::valueChanged()
{
setText(1, m_value.toString());
repaint();
}
void ValueListViewItem::valueChanged(const Abakus::number_t &newValue)
{
m_value = newValue;
valueChanged();
}
Abakus::number_t ValueListViewItem::itemValue() const
{
return m_value;
}
VariableListView::VariableListView(QWidget *parent, const char *name) :
ListView(parent, name)
{
enablePopupHandler(true);
}
QString VariableListView::removeItemString() const
{
return i18n("Remove selected variable");
}
QString VariableListView::removeAllItemsString(unsigned count) const
{
// count is unreliable because not all of the elements in the list view
// can be removed.
count = 0;
QStringList values = ValueManager::instance()->valueNames();
for(QStringList::ConstIterator value = values.constBegin(); value != values.constEnd(); ++value)
if(!ValueManager::instance()->isValueReadOnly(*value))
++count;
return i18n("Remove all variables (1 variable)",
"Remove all variables (%n variables)",
count);
}
bool VariableListView::isItemRemovable(QListViewItem *item) const
{
return !ValueManager::instance()->isValueReadOnly(item->text(0));
}
void VariableListView::removeSelectedItem(QListViewItem *item)
{
ValueManager::instance()->removeValue(item->text(0));
}
void VariableListView::removeAllItems()
{
ValueManager::instance()->slotRemoveUserVariables();
}
FunctionListView::FunctionListView(QWidget *parent, const char *name) :
ListView(parent, name)
{
enablePopupHandler(true);
}
QString FunctionListView::removeItemString() const
{
return i18n("Remove selected function");
}
QString FunctionListView::removeAllItemsString(unsigned count) const
{
return i18n("Remove all functions (1 function)",
"Remove all functions (%n functions)",
count);
}
bool FunctionListView::isItemRemovable(QListViewItem *item) const
{
return true;
}
void FunctionListView::removeSelectedItem(QListViewItem *item)
{
// Use section to get the beginning of the string up to (and not
// including) the first (
QString name = item->text(0).section('(', 0, 0);
FunctionManager::instance()->removeFunction(name);
}
void FunctionListView::removeAllItems()
{
QStringList fns = FunctionManager::instance()->functionList(FunctionManager::UserDefined);
for(QStringList::ConstIterator fn = fns.constBegin(); fn != fns.constEnd(); ++fn)
FunctionManager::instance()->removeFunction(*fn);
}
#include "abakuslistview.moc"

@ -0,0 +1,147 @@
#ifndef ABAKUS_LISTVIEW_H
#define ABAKUS_LISTVIEW_H
/*
* abakuslistview.h - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
#include <klistview.h>
#include "numerictypes.h"
class KPopupMenu;
class ListView : public KListView
{
Q_OBJECT
public:
ListView(QWidget *parent, const char *name = 0);
protected:
virtual QDragObject *dragObject();
/**
* Used to enable fancy popup handling support in subclasses. Subclasses
* also need to reimplement a few functions if they want to use this.
*
* This should be called in the subclass's constructor.
*/
void enablePopupHandler(bool enable);
/**
* If using the popup menu handling, the subclass needs to return a
* translated string of the form "Remove selected <itemtype>".
*/
virtual QString removeItemString() const;
/**
* If using the popup menu handling, the subclass needs to return a
* translated string of the form "Remove all <itemtype>s." I recommend
* also appending a " (%n <itemtype>s), which you can use the @p count
* parameter for.
*/
virtual QString removeAllItemsString(unsigned count) const;
protected slots:
/**
* If using the popup menu handing, the subclass needs to reimplement this
* function to remove the selected item, which is passed in as a
* parameter.
*/
virtual void removeSelectedItem(QListViewItem *item);
/**
* If using the popup menu handling, the subclass needs to reimplement this
* function to remove all items.
*/
virtual void removeAllItems();
/**
* If using the popup menu handling, this function may be called to
* determine whether the selected item given by @p item is removable.
*/
virtual bool isItemRemovable(QListViewItem *item) const;
private slots:
void rightClicked(QListViewItem *item, const QPoint &pt);
void removeSelected();
private:
KPopupMenu *m_menu;
bool m_usePopup;
int m_removeSingleId;
int m_removeAllId;
};
class ValueListViewItem : public KListViewItem
{
public:
ValueListViewItem (QListView *listView, const QString &name, const Abakus::number_t &value);
// Will cause the list item to rethink the text.
void valueChanged();
void valueChanged(const Abakus::number_t &newValue);
Abakus::number_t itemValue() const;
private:
Abakus::number_t m_value;
};
/**
* Subclass used for the list of variables.
*/
class VariableListView : public ListView
{
Q_OBJECT
public:
VariableListView(QWidget *parent, const char *name = 0);
protected:
virtual QString removeItemString() const;
virtual QString removeAllItemsString(unsigned count) const;
virtual bool isItemRemovable(QListViewItem *item) const;
protected slots:
virtual void removeSelectedItem(QListViewItem *item);
virtual void removeAllItems();
};
/**
* Subclass used for the list of functions.
*/
class FunctionListView : public ListView
{
Q_OBJECT
public:
FunctionListView(QWidget *parent, const char *name = 0);
protected:
virtual QString removeItemString() const;
virtual QString removeAllItemsString(unsigned count) const;
virtual bool isItemRemovable(QListViewItem *item) const;
protected slots:
virtual void removeSelectedItem(QListViewItem *item);
virtual void removeAllItems();
};
#endif

@ -0,0 +1,35 @@
<!DOCTYPE kpartgui>
<kpartgui name="abakus" version="2">
<MenuBar>
<Menu name="view"><text>&amp;View</text>
<Action name="toggleHistoryList"/>
<Action name="toggleVariableList"/>
<Action name="toggleFunctionList"/>
<Separator/>
<Action name="precisionAuto"/>
<Action name="precision3"/>
<Action name="precision8"/>
<Action name="precision15"/>
<Action name="precision50"/>
<Action name="precisionCustom"/>
<Separator/>
<Action name="toggleCompactMode"/>
<Separator/>
<Action name="clearHistory"/>
</Menu>
<Menu name="mode"><text>&amp;Mode</text>
<Action name="setDegreesMode"/>
<Action name="setRadiansMode"/>
<Separator/>
<Action name="toggleExpressionMode"/>
</Menu>
</MenuBar>
</kpartgui>

@ -0,0 +1,47 @@
#ifndef ABAKUS_DCOP_IFACE
#define ABAKUS_DCOP_IFACE
/*
* dcopIface.h - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
#include <kdebug.h>
#include <dcopobject.h>
#include <qstring.h>
#include "mainwindow.h"
#include "numerictypes.h"
#include "function.h"
class AbakusIface : virtual public DCOPObject
{
K_DCOP
public:
AbakusIface() : DCOPObject("Calculator")
{
}
k_dcop:
virtual double evaluate(const QString &expr)
{
Abakus::number_t result = parseString(expr.latin1());
return result.asDouble();
}
};
#endif

@ -0,0 +1,87 @@
/*
* dragsupport.cpp - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
#include <qstring.h>
#include <qpixmap.h>
#include <qimage.h>
#include <qpainter.h>
#include <qcolor.h>
#include <qfont.h>
#include <qbrush.h>
#include <qfontmetrics.h>
#include "dragsupport.h"
namespace DragSupport
{
QPixmap makePixmap(const QString &text, const QFont &font)
{
QColor background(234, 178, 230);
QFontMetrics fm(font);
int height = 2 * fm.height();
QSize bonusSize (height, 0);
QSize size(fm.width(text), height);
QImage image(size + bonusSize, 32);
image.setAlphaBuffer(false);
image.fill(0); // All transparent pixels
image.setAlphaBuffer(true);
QPixmap pix(size + bonusSize);
pix.fill(Qt::magenta); // Watch for incoming hacks
QPainter painter(&pix);
painter.setFont(font);
// Outline black, background white
painter.setPen(Qt::black);
painter.setBrush(background);
// roundRect is annoying in that the four "pies" in each corner aren't
// circular, they're elliptical. Try to make the radii force it circular
// again.
painter.drawRoundRect(pix.rect(), 75 * pix.height() / pix.width(), 75);
// Alias better names for some constants.
int textLeft = height / 2;
// Draw text
painter.setPen(Qt::black);
painter.drawText(textLeft, height / 4, size.width(), size.height(), 0, text);
QImage overlay(pix.convertToImage());
// The images should have the same size, copy pixels from overlay to the
// bottom unless the pixel is called magenta. The pixels we don't copy
// are transparent in the QImage, and will remain transparent when
// converted to a QPixmap.
for(int i = 0; i < image.width(); ++i)
for(int j = 0; j < image.height(); ++j) {
if(QColor(overlay.pixel(i, j)) != Qt::magenta)
image.setPixel(i, j, overlay.pixel(i, j));
}
pix.convertFromImage(image);
return pix;
}
} // DragSupport

@ -0,0 +1,33 @@
#ifndef ABAKUS_DRAGSUPPORT_H
#define ABAKUS_DRAGSUPPORT_H
/*
* dragsupport.h - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
class QPixmap;
class QString;
class QFont;
namespace DragSupport {
QPixmap makePixmap(const QString &text, const QFont &font);
}
#endif
// vim: set et ts=8 sw=4:

@ -0,0 +1,892 @@
/* This file was part of the SpeedCrunch project
Copyright (C) 2004,2005 Ariya Hidayat <ariya@kde.org>
And is now part of abakus.
Copyright (c) 2005 Michael Pyne <michael.pyne@kdemail.net>
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., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.
*/
#include "function.h"
#include "valuemanager.h"
#include "editor.h"
#include "evaluator.h"
#include "result.h"
#include <qapplication.h>
#include <qlabel.h>
#include <qlineedit.h>
#include <qlistbox.h>
#include <qpainter.h>
#include <qregexp.h>
#include <qstringlist.h>
#include <qstyle.h>
#include <qsyntaxhighlighter.h>
#include <qtimer.h>
#include <qtooltip.h>
#include <qmessagebox.h>
#include <qvbox.h>
#include <netwm.h>
#include <fixx11h.h> // netwm.h includes X11 headers which conflict with qevent
#include <qevent.h>
#include <kdebug.h>
#include <algorithm>
// XXX: QT 4: Replace this with qBinaryFind().
using std::binary_search;
class CalcResultLabel : public QLabel
{
public:
CalcResultLabel(QWidget *parent, const char *name, int WFlags) :
QLabel(parent, name, WFlags)
{
}
protected:
virtual void mousePressEvent(QMouseEvent *)
{
hide();
}
};
class EditorHighlighter : public QSyntaxHighlighter
{
public:
EditorHighlighter( Editor* );
int highlightParagraph ( const QString & text, int );
private:
Editor* editor;
};
class Editor::Private
{
public:
Evaluator* eval;
QStringList history;
int index;
bool autoCompleteEnabled;
EditorCompletion* completion;
QTimer* completionTimer;
bool autoCalcEnabled;
char format;
int decimalDigits;
QTimer* autoCalcTimer;
QLabel* autoCalcLabel;
bool syntaxHighlightEnabled;
EditorHighlighter* highlighter;
QMap<ColorType,QColor> highlightColors;
QTimer* matchingTimer;
};
class EditorCompletion::Private
{
public:
Editor* editor;
QVBox *completionPopup;
QListBox *completionListBox;
};
class ChoiceItem: public QListBoxText
{
public:
ChoiceItem( QListBox*, const QString& );
void setMinNameWidth (int w) { minNameWidth = w; }
int nameWidth() const;
protected:
void paint( QPainter* p );
private:
QString item;
QString desc;
int minNameWidth;
};
ChoiceItem::ChoiceItem( QListBox* listBox, const QString& text ):
QListBoxText( listBox, text ), minNameWidth(0)
{
QStringList list = QStringList::split( ':', text );
if( list.count() ) item = list[0];
if( list.count()>1 ) desc = list[1];
}
// Returns width of this particular list item's name.
int ChoiceItem::nameWidth() const
{
if(item.isEmpty())
return 0;
QFontMetrics fm = listBox()->fontMetrics();
return fm.width( item );
}
void ChoiceItem::paint( QPainter* painter )
{
int itemHeight = height( listBox() );
QFontMetrics fm = painter->fontMetrics();
int yPos = ( ( itemHeight - fm.height() ) / 2 ) + fm.ascent();
painter->drawText( 3, yPos, item );
//int xPos = fm.width( item );
int xPos = QMAX(fm.width(item), minNameWidth);
if( !isSelected() )
painter->setPen( listBox()->palette().disabled().text().dark() );
painter->drawText( 10 + xPos, yPos, desc );
}
EditorHighlighter::EditorHighlighter( Editor* e ):
QSyntaxHighlighter( e )
{
editor = e;
}
int EditorHighlighter::highlightParagraph ( const QString & text, int )
{
if( !editor->isSyntaxHighlightEnabled() )
{
setFormat( 0, text.length(), editor->colorGroup().text() );
return 0;
}
QStringList fnames = FunctionManager::instance()->functionList(FunctionManager::All);
fnames.sort(); // Sort list so we can bin search it.
Tokens tokens = Evaluator::scan( text );
for( unsigned i = 0; i < tokens.count(); i++ )
{
Token& token = tokens[i];
QString text = token.text().lower();
QFont font = editor->font();
QColor color = Qt::black;
switch( token.type() )
{
case Token::Number:
color = editor->highlightColor( Editor::Number );
break;
case Token::Identifier:
{
color = editor->highlightColor( Editor::Variable );
if( binary_search( fnames.constBegin(), fnames.constEnd(), text) ) {
color = editor->highlightColor( Editor::FunctionName );
}
}
break;
case Token::Operator:
break;
default: break;
};
if( token.pos() >= 0 ) {
setFormat( token.pos(), token.text().length(), font, color );
}
}
return 0;
}
Editor::Editor( QWidget* parent, const char* name ):
QTextEdit( parent, name )
{
d = new Private;
d->eval = 0;
d->index = 0;
d->autoCompleteEnabled = true;
d->completion = new EditorCompletion( this );
d->completionTimer = new QTimer( this );
d->autoCalcEnabled = true;
d->syntaxHighlightEnabled = true;
d->highlighter = new EditorHighlighter( this );
d->autoCalcTimer = new QTimer( this );
d->matchingTimer = new QTimer( this );
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
setWordWrap( NoWrap );
setHScrollBarMode( AlwaysOff );
setVScrollBarMode( AlwaysOff );
setTextFormat( PlainText );
setAutoFormatting( AutoNone );
setTabChangesFocus( true );
setLinkUnderline( false );
connect( d->completion, SIGNAL( selectedCompletion( const QString& ) ),
SLOT( autoComplete( const QString& ) ) );
connect( this, SIGNAL( textChanged() ), SLOT( checkAutoComplete() ) );
connect( d->completionTimer, SIGNAL( timeout() ), SLOT( triggerAutoComplete() ) );
connect( this, SIGNAL( textChanged() ), SLOT( checkMatching() ) );
connect( d->matchingTimer, SIGNAL( timeout() ), SLOT( doMatchingLeft() ) );
connect( d->matchingTimer, SIGNAL( timeout() ), SLOT( doMatchingRight() ) );
connect( this, SIGNAL( textChanged() ), SLOT( checkAutoCalc() ) );
connect( d->autoCalcTimer, SIGNAL( timeout() ), SLOT( autoCalc() ) );
d->autoCalcLabel = new CalcResultLabel( 0, "autocalc", WStyle_StaysOnTop |
WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WX11BypassWM );
d->autoCalcLabel->setFrameStyle( QFrame::Plain | QFrame::Box );
d->autoCalcLabel->setPalette( QToolTip::palette() );
d->autoCalcLabel->hide();
setHighlightColor( Number, QColor(0,0,127) );
setHighlightColor( FunctionName, QColor(85,0,0) );
setHighlightColor( Variable, QColor(0,85,0) );
setHighlightColor( MatchedPar, QColor(255,255,183) );
}
Editor::~Editor()
{
d->autoCalcLabel->hide();
delete d;
}
QSize Editor::sizeHint() const
{
constPolish();
QFontMetrics fm = fontMetrics();
int h = QMAX(fm.lineSpacing(), 14);
int w = fm.width( 'x' ) * 20;
int m = frameWidth() * 2;
return( style().sizeFromContents(QStyle::CT_LineEdit, this,
QSize( w + m, h + m + 4 ).
expandedTo(QApplication::globalStrut())));
}
QStringList Editor::history() const
{
return d->history;
}
void Editor::setHistory( const QStringList& h )
{
d->history = h;
d->index = d->history.count();
}
bool Editor::autoCompleteEnabled() const
{
return d->autoCompleteEnabled;
}
void Editor::setAutoCompleteEnabled( bool enable )
{
d->autoCompleteEnabled = enable;
}
bool Editor::autoCalcEnabled() const
{
return d->autoCalcEnabled;
}
void Editor::setAutoCalcEnabled( bool enable )
{
d->autoCalcEnabled = enable;
}
void Editor::setFormat( char format )
{
d->format = format;
}
void Editor::setDecimalDigits( int digits )
{
d->decimalDigits = digits;
}
void Editor::appendHistory( const QString& text )
{
if( text.isEmpty() ) return;
QString lastText;
if( d->history.count() )
lastText = d->history[ d->history.count()-1 ];
if( text == lastText ) return;
d->history.append( text );
d->index = d->history.count()-1;
}
void Editor::clearHistory()
{
d->history.clear();
d->index = 0;
}
void Editor::squelchNextAutoCalc()
{
d->autoCalcTimer->stop();
}
void Editor::setText(const QString &txt)
{
QTextEdit::setText(txt);
squelchNextAutoCalc();
}
void Editor::checkAutoComplete()
{
if( !d->autoCompleteEnabled ) return;
d->completionTimer->stop();
d->completionTimer->start( 500, true );
}
void Editor::checkMatching()
{
if( !d->syntaxHighlightEnabled ) return;
d->matchingTimer->stop();
d->matchingTimer->start( 200, true );
}
void Editor::checkAutoCalc()
{
// Calc-As-You-Type
if( !d->autoCalcEnabled ) return;
d->autoCalcTimer->stop();
d->autoCalcTimer->start( 1000, true );
d->autoCalcLabel->hide();
}
void Editor::doMatchingLeft()
{
if( !d->syntaxHighlightEnabled ) return;
// tokenize the expression
int para = 0, curPos = 0;
getCursorPosition( &para, &curPos );
// check for right par
QString subtext = text().left( curPos );
Tokens tokens = Evaluator::scan( subtext );
if( !tokens.valid() ) return;
if( tokens.count()<1 ) return;
Token lastToken = tokens[ tokens.count()-1 ];
// right par ?
if( lastToken.isOperator() )
if( lastToken.asOperator() == Token::RightPar )
if( lastToken.pos() == curPos-1 )
{
// find the matching left par
unsigned par = 1;
int k = 0;
Token matchToken;
int matchPos = -1;
for( k = tokens.count()-2; k >= 0; k-- )
{
if( par < 1 ) break;
Token matchToken = tokens[k];
if( matchToken.isOperator() )
{
if( matchToken.asOperator() == Token::RightPar )
par++;
if( matchToken.asOperator() == Token::LeftPar )
par--;
if( par == 0 ) matchPos = matchToken.pos();
}
}
if( matchPos >= 0 )
{
setSelection( 0, matchPos, 0, matchPos+1, 2 );
setSelection( 0, lastToken.pos(), 0, lastToken.pos()+1, 1 );
setCursorPosition( para, curPos );
}
}
}
void Editor::doMatchingRight()
{
if( !d->syntaxHighlightEnabled ) return;
// tokenize the expression
int para = 0, curPos = 0;
getCursorPosition( &para, &curPos );
// check for left par
QString subtext = text().right( text().length() - curPos );
Tokens tokens = Evaluator::scan( subtext );
if( !tokens.valid() ) return;
if( tokens.count()<1 ) return;
Token firstToken = tokens[ 0 ];
// left par ?
if( firstToken.isOperator() )
if( firstToken.asOperator() == Token::LeftPar )
if( firstToken.pos() == 0 )
{
// find the matching right par
unsigned par = 1;
unsigned int k = 0;
Token matchToken;
int matchPos = -1;
for( k = 1; k < tokens.count(); k++ )
{
if( par < 1 ) break;
Token matchToken = tokens[k];
if( matchToken.isOperator() )
{
if( matchToken.asOperator() == Token::LeftPar )
par++;
if( matchToken.asOperator() == Token::RightPar )
par--;
if( par == 0 ) matchPos = matchToken.pos();
}
}
if( matchPos >= 0 )
{
setSelection( 0, curPos+matchPos, 0, curPos+matchPos+1, 2 );
setSelection( 0, curPos+firstToken.pos(), 0, curPos+firstToken.pos()+1, 1 );
setCursorPosition( para, curPos );
}
}
}
void Editor::triggerAutoComplete()
{
if( !d->autoCompleteEnabled ) return;
// tokenize the expression (don't worry, this is very fast)
// faster now that it uses flex. ;)
int para = 0, curPos = 0;
getCursorPosition( &para, &curPos );
QString subtext = text().left( curPos );
Tokens tokens = Evaluator::scan( subtext );
if(!tokens.valid())
{
kdWarning() << "invalid tokens.\n";
return;
}
if(tokens.isEmpty() || subtext.endsWith(" "))
return;
Token lastToken = tokens[ tokens.count()-1 ];
// last token must be an identifier
if( !lastToken.isIdentifier() )
return;
QString id = lastToken.text();
if( id.isEmpty() )
return;
// find matches in function names
QStringList fnames = FunctionManager::instance()->functionList(FunctionManager::All);
QStringList choices;
for( unsigned i=0; i<fnames.count(); i++ )
if( fnames[i].startsWith( id, false ) )
{
QString str = fnames[i];
::Function* f = FunctionManager::instance()->function( str );
if( f && !f->description.isEmpty() )
str.append( ':' ).append( f->description );
choices.append( str );
}
choices.sort();
// find matches in variables names
QStringList vchoices;
QStringList values = ValueManager::instance()->valueNames();
for(QStringList::ConstIterator it = values.begin(); it != values.end(); ++it)
if( (*it).startsWith( id, false ) )
{
QString choice = ValueManager::description(*it);
if(choice.isEmpty())
choice = ValueManager::instance()->value(*it).toString();
vchoices.append( QString("%1:%2").arg( *it, choice ) );
}
vchoices.sort();
choices += vchoices;
// no match, don't bother with completion
if( !choices.count() ) return;
// one match, complete it for the user
if( choices.count()==1 )
{
QString str = QStringList::split( ':', choices[0] )[0];
// single perfect match, no need to give choices.
if(str == id.lower())
return;
str = str.remove( 0, id.length() );
int para = 0, curPos = 0;
getCursorPosition( &para, &curPos );
blockSignals( true );
insert( str );
setSelection( 0, curPos, 0, curPos+str.length() );
blockSignals( false );
return;
}
// present the user with completion choices
d->completion->showCompletion( choices );
}
void Editor::autoComplete( const QString& item )
{
if( !d->autoCompleteEnabled || item.isEmpty() )
return;
int para = 0, curPos = 0;
getCursorPosition( &para, &curPos );
QString subtext = text().left( curPos );
Tokens tokens = Evaluator::scan( subtext );
if( !tokens.valid() || tokens.count() < 1 )
return;
Token lastToken = tokens[ tokens.count()-1 ];
if( !lastToken.isIdentifier() )
return;
QStringList str = QStringList::split( ':', item );
blockSignals( true );
setSelection( 0, lastToken.pos(), 0, lastToken.pos()+lastToken.text().length() );
insert( str[0] );
blockSignals( false );
}
void Editor::autoCalc()
{
if( !d->autoCalcEnabled )
return;
QString str = Evaluator::autoFix( text() );
if( str.isEmpty() )
return;
// too short? do not bother...
Tokens tokens = Evaluator::scan( str );
if( tokens.count() < 2 )
return;
// If we're using set for a function don't try.
QRegExp setFn("\\s*set.*\\(.*=");
if( str.find(setFn) != -1 )
return;
// strip off assignment operator, e.g. "x=1+2" becomes "1+2" only
// the reason is that we want only to evaluate (on the fly) the expression,
// not to update (put the result in) the variable
if( tokens.count() > 2 && tokens[0].isIdentifier() &&
tokens[1].asOperator() == Token::Equal )
{
Tokens::const_iterator it = tokens.begin();
++it;
++it; // Skip first two tokens.
// Reconstruct string to evaluate using the tokens.
str = "";
while(it != tokens.end())
{
str += (*it).text();
str += ' ';
++it;
}
}
Abakus::number_t result = parseString(str.latin1());
if( Result::lastResult()->type() == Result::Value )
{
QString ss = QString("Result: <b>%2</b>").arg(result.toString());
d->autoCalcLabel->setText( ss );
d->autoCalcLabel->adjustSize();
// reposition nicely
QPoint pos = mapToGlobal( QPoint( 0, 0 ) );
pos.setY( pos.y() - d->autoCalcLabel->height() - 1 );
d->autoCalcLabel->move( pos );
d->autoCalcLabel->show();
d->autoCalcLabel->raise();
// do not show it forever
QTimer::singleShot( 5000, d->autoCalcLabel, SLOT( hide()) );
}
else
{
// invalid expression
d->autoCalcLabel->hide();
}
}
QString Editor::formatNumber( const Abakus::number_t &value ) const
{
return value.toString();
}
void Editor::historyBack()
{
if( d->history.isEmpty() )
return;
d->index--;
if( d->index < 0 )
d->index = 0;
setText( d->history[ d->index ] );
setCursorPosition( 0, text().length() );
ensureCursorVisible();
}
void Editor::historyForward()
{
if( d->history.isEmpty() )
return;
d->index++;
if( d->index >= (int) d->history.count() )
d->index = d->history.count() - 1;
setText( d->history[ d->index ] );
setCursorPosition( 0, text().length() );
ensureCursorVisible();
}
void Editor::keyPressEvent( QKeyEvent* e )
{
if( e->key() == Key_Up )
{
historyBack();
e->accept();
return;
}
if( e->key() == Key_Down )
{
historyForward();
e->accept();
return;
}
if( e->key() == Key_Enter || e->key() == Key_Return )
{
emit returnPressed();
return;
}
if( e->key() == Key_Left ||
e->key() == Key_Right ||
e->key() == Key_Home ||
e->key() == Key_End )
{
checkMatching();
}
QTextEdit::keyPressEvent( e );
}
void Editor::wheelEvent( QWheelEvent *e )
{
if( e->delta() > 0 )
historyBack();
else if( e->delta() < 0 )
historyForward();
e->accept();
}
void Editor::setSyntaxHighlight( bool enable )
{
d->syntaxHighlightEnabled = enable;
d->highlighter->rehighlight();
}
bool Editor::isSyntaxHighlightEnabled() const
{
return d->syntaxHighlightEnabled;
}
void Editor::setHighlightColor( ColorType type, QColor color )
{
d->highlightColors[ type ] = color;
setSelectionAttributes( 1, highlightColor( Editor::MatchedPar ), false );
setSelectionAttributes( 2, highlightColor( Editor::MatchedPar ), false );
d->highlighter->rehighlight();
}
QColor Editor::highlightColor( ColorType type )
{
return d->highlightColors[ type ];
}
EditorCompletion::EditorCompletion( Editor* editor ): QObject( editor )
{
d = new Private;
d->editor = editor;
d->completionPopup = new QVBox( editor->topLevelWidget(), 0, WType_Popup );
d->completionPopup->setFrameStyle( QFrame::Box | QFrame::Plain );
d->completionPopup->setLineWidth( 1 );
d->completionPopup->installEventFilter( this );
d->completionPopup->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum);
d->completionListBox = new QListBox( d->completionPopup );
d->completionPopup->setFocusProxy( d->completionListBox );
d->completionListBox->setFrameStyle( QFrame::NoFrame );
d->completionListBox->setVariableWidth( true );
d->completionListBox->installEventFilter( this );
}
EditorCompletion::~EditorCompletion()
{
delete d;
}
bool EditorCompletion::eventFilter( QObject *obj, QEvent *ev )
{
if ( obj == d->completionPopup || obj == d->completionListBox )
{
if ( ev->type() == QEvent::KeyPress )
{
QKeyEvent *ke = (QKeyEvent*)ev;
if ( ke->key() == Key_Enter || ke->key() == Key_Return )
{
doneCompletion();
return true;
}
else if ( ke->key() == Key_Left || ke->key() == Key_Right ||
ke->key() == Key_Up || ke->key() == Key_Down ||
ke->key() == Key_Home || ke->key() == Key_End ||
ke->key() == Key_Prior || ke->key() == Key_Next )
return false;
d->completionPopup->close();
d->editor->setFocus();
QApplication::sendEvent( d->editor, ev );
return true;
}
if ( ev->type() == QEvent::MouseButtonDblClick )
{
doneCompletion();
return true;
}
}
return false;
}
void EditorCompletion::doneCompletion()
{
d->completionPopup->close();
d->editor->setFocus();
emit selectedCompletion( d->completionListBox->currentText() );
}
void EditorCompletion::showCompletion( const QStringList &choices )
{
static bool shown = false;
if( !choices.count() ) return;
d->completionListBox->clear();
int maxWidth = 0;
for( unsigned i = 0; i < choices.count(); i++ ) {
ChoiceItem *item = new ChoiceItem( d->completionListBox, choices[i] );
int itemMaxWidth = item->nameWidth();
if(itemMaxWidth > maxWidth)
maxWidth = itemMaxWidth;
}
for(unsigned i = 0; i < d->completionListBox->count(); ++i) {
ChoiceItem *item = static_cast<ChoiceItem *>(d->completionListBox->item(i));
item->setMinNameWidth(maxWidth);
}
d->completionListBox->setCurrentItem( 0 );
// size of the pop-up
d->completionPopup->setMaximumHeight( 120 );
d->completionPopup->resize( d->completionListBox->sizeHint() +
QSize( d->completionListBox->verticalScrollBar()->width() + 4,
d->completionListBox->horizontalScrollBar()->height() + 4 ) );
if(!shown)
{
d->completionPopup->show();
QTimer::singleShot ( 0, this, SLOT(moveCompletionPopup()) );
}
else
{
moveCompletionPopup();
d->completionPopup->show();
}
}
void EditorCompletion::moveCompletionPopup()
{
int h = d->completionListBox->height();
int w = d->completionListBox->width();
// position, reference is editor's cursor position in global coord
QFontMetrics fm( d->editor->font() );
int para = 0, curPos = 0;
d->editor->getCursorPosition( &para, &curPos );
int pixelsOffset = fm.width( d->editor->text(), curPos );
pixelsOffset -= d->editor->contentsX();
QPoint pos = d->editor->mapToGlobal( QPoint( pixelsOffset, d->editor->height() ) );
// if popup is partially invisible, move to other position
NETRootInfo info(d->completionPopup->x11Display(),
NET::CurrentDesktop | NET::WorkArea | NET::NumberOfDesktops,
-1, false);
info.activate(); // wtf is this needed for?
NETRect NETarea = info.workArea(info.currentDesktop());
QRect area(NETarea.pos.x, NETarea.pos.y, NETarea.size.width, NETarea.size.height);
if( pos.y() + h > area.y() + area.height() )
pos.setY( pos.y() - h - d->editor->height() );
if( pos.x() + w > area.x() + area.width() )
pos.setX( area.x() + area.width() - w );
d->completionPopup->move( pos );
d->completionListBox->setFocus();
}
#include "editor.moc"
// vim: set et sw=2 ts=8:

@ -0,0 +1,131 @@
/* This file was part of the SpeedCrunch project
Copyright (C) 2004,2005 Ariya Hidayat <ariya@kde.org>
And is now part of abakus.
Copyright (c) 2005 Michael Pyne <michael.pyne@kdemail.net>
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., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.
*/
#ifndef ABAKUS_EDITOR_H
#define ABAKUS_EDITOR_H
#include <qobject.h>
#include <qstringlist.h>
#include <qtextedit.h>
#include "hmath.h"
class QEvent;
class QKeyEvent;
class QWidget;
class Evaluator;
class Editor : public QTextEdit
{
Q_OBJECT
public:
typedef enum
{
Number, FunctionName, Variable, MatchedPar
} ColorType;
Editor( QWidget* parent = 0, const char* name = 0 );
~Editor();
QSize sizeHint() const;
QSize xminimumSizeHint() const;
QStringList history() const;
void setHistory( const QStringList& history );
bool autoCompleteEnabled() const;
void setAutoCompleteEnabled( bool enable );
bool autoCalcEnabled() const;
void setAutoCalcEnabled( bool enable );
void setFormat( char format );
void setDecimalDigits( int digits );
void setSyntaxHighlight( bool enable );
bool isSyntaxHighlightEnabled() const;
void setHighlightColor( ColorType type, QColor color );
QColor highlightColor( ColorType type );
public slots:
void appendHistory( const QString& text );
void clearHistory();
// Stop the timer from going off.
void squelchNextAutoCalc();
void setText(const QString &txt);
protected slots:
void checkAutoComplete();
void triggerAutoComplete();
void autoComplete( const QString& item );
void checkAutoCalc();
void autoCalc();
void checkMatching();
void doMatchingLeft();
void doMatchingRight();
void historyBack();
void historyForward();
protected:
void keyPressEvent( QKeyEvent* );
void wheelEvent( QWheelEvent* );
QString formatNumber( const Abakus::number_t &value ) const;
private:
class Private;
Private* d;
Editor( const Editor& );
Editor& operator=( const Editor& );
};
class EditorCompletion : public QObject
{
Q_OBJECT
public:
EditorCompletion( Editor* editor );
~EditorCompletion();
bool eventFilter( QObject *o, QEvent *e );
void doneCompletion();
void showCompletion( const QStringList &choices );
protected slots:
void moveCompletionPopup();
signals:
void selectedCompletion( const QString& item );
private:
class Private;
Private* d;
EditorCompletion( const EditorCompletion& );
EditorCompletion& operator=( const EditorCompletion& );
};
#endif // ABAKUS_EDITOR_H
// vim: set et ts=8 sw=4:

@ -0,0 +1,261 @@
/* This file was part of the SpeedCrunch project
Copyright (C) 2004 Ariya Hidayat <ariya@kde.org>
And is now part of abakus.
Copyright (c) 2005 Michael Pyne <michael.pyne@kdemail.net>
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., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.
*/
#include "evaluator.h"
#include "function.h"
#include "node.h" // For parser_yacc.hpp below
#include "parser_yacc.hpp"
#include <qapplication.h>
#include <qmap.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qvaluevector.h>
#include <kdebug.h>
//
// Reimplementation of goodies from Evaluator follows.
//
Evaluator::Evaluator()
{
}
Evaluator::~Evaluator()
{
}
void Evaluator::setExpression(const QString &expr)
{
kdError() << k_funcinfo << " not implemented.\n";
}
QString Evaluator::expression() const
{
kdError() << k_funcinfo << " not implemented.\n";
return QString();
}
void Evaluator::clear()
{
kdError() << k_funcinfo << " not implemented.\n";
// Yeah, whatever.
}
bool Evaluator::isValid() const
{
return true;
}
Tokens Evaluator::tokens() const
{
kdError() << k_funcinfo << " not implemented.\n";
return Tokens();
}
Tokens Evaluator::scan(const QString &expr)
{
Lexer l(expr);
Tokens tokens;
while(l.hasNext())
{
int t = l.nextType();
Token::Type type = Token::Unknown;
switch(t)
{
case POWER:
case '*':
case '(':
case ')':
case '-':
case '+':
case ',':
case '=':
type = Token::Operator;
break;
case NUM:
type = Token::Number;
break;
case SET:
case REMOVE:
case DERIV:
case FN:
case ID:
type = Token::Identifier;
break;
default:
type = Token::Unknown;
break;
}
tokens.append(Token(type, l.tokenValue(), l.tokenPos()));
}
return tokens;
}
QString Evaluator::error() const
{
kdError() << k_funcinfo << " not implemented.\n";
return "No Error Yet";
}
///
/// ARIYA'S CLASS CODE FOLLOWS
///
// for null token
const Token Token::null;
// helper function: return operator of given token text
// e.g. "*" yields Operator::Asterisk, and so on
static Token::Op matchOperator( const QString& text )
{
Token::Op result = Token::InvalidOp;
if( text.length() == 1 )
{
QChar p = text[0];
switch( p.unicode() )
{
case '+': result = Token::Plus; break;
case '-': result = Token::Minus; break;
case '*': result = Token::Asterisk; break;
case '/': result = Token::Slash; break;
case '^': result = Token::Caret; break;
case ',': result = Token::Comma; break;
case '(': result = Token::LeftPar; break;
case ')': result = Token::RightPar; break;
case '%': result = Token::Percent; break;
case '=': result = Token::Equal; break;
default : result = Token::InvalidOp; break;
}
}
if( text.length() == 2 )
{
if( text == "**" ) result = Token::Caret;
}
return result;
}
// creates a token
Token::Token( Type type, const QString& text, int pos )
{
m_type = type;
m_text = text;
m_pos = pos;
}
// copy constructor
Token::Token( const Token& token )
{
m_type = token.m_type;
m_text = token.m_text;
m_pos = token.m_pos;
}
// assignment operator
Token& Token::operator=( const Token& token )
{
m_type = token.m_type;
m_text = token.m_text;
m_pos = token.m_pos;
return *this;
}
Abakus::number_t Token::asNumber() const
{
if( isNumber() )
return Abakus::number_t( m_text.latin1() );
else
return Abakus::number_t();
}
Token::Op Token::asOperator() const
{
if( isOperator() ) return matchOperator( m_text );
else return InvalidOp;
}
QString Token::description() const
{
QString desc;
switch (m_type )
{
case Number: desc = "Number"; break;
case Identifier: desc = "Identifier"; break;
case Operator: desc = "Operator"; break;
default: desc = "Unknown"; break;
}
while( desc.length() < 10 ) desc.prepend( ' ' );
desc.prepend( " " );
desc.prepend( QString::number( m_pos ) );
desc.append( " : " ).append( m_text );
return desc;
}
QString Evaluator::autoFix( const QString& expr )
{
int par = 0;
QString result;
// strip off all funny characters
for( unsigned c = 0; c < expr.length(); c++ )
if( expr[c] >= QChar(32) )
result.append( expr[c] );
// automagically close all parenthesis
Tokens tokens = Evaluator::scan( result );
for( unsigned i=0; i<tokens.count(); i++ )
if( tokens[i].asOperator() == Token::LeftPar ) par++;
else if( tokens[i].asOperator() == Token::RightPar ) par--;
for(; par > 0; par-- )
result.append( ')' );
// special treatment for simple function
// e.g. "cos" is regarded as "cos(ans)"
if( !result.isEmpty() )
{
Tokens tokens = Evaluator::scan( result );
if( (tokens.count() == 1) &&
FunctionManager::instance()->isFunction(tokens[0].text())
)
{
result.append( "(ans)" );
}
}
return result;
}
// vim: set et ts=8 sw=4:

@ -0,0 +1,131 @@
/* This file was part of the SpeedCrunch project
Copyright (C) 2004 Ariya Hidayat <ariya@kde.org>
And is now part of abakus.
Copyright (c) 2005 Michael Pyne <michael.pyne@kdemail.net>
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., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.
*/
#ifndef ABAKUS_EVALUATOR_H
#define ABAKUS_EVALUATOR_H
#include <qstring.h>
#include <qvaluevector.h>
#include "numerictypes.h"
class Token
{
public:
typedef enum
{
Unknown,
Number,
Operator,
Identifier
} Type;
typedef enum
{
InvalidOp = 0,
Plus, // + (addition)
Minus, // - (substraction, negation)
Asterisk, // * (multiplication)
Slash, // / (division)
Caret, // ^ (power) or **.
LeftPar, // (
RightPar, // )
Comma, // argument separator
Percent,
Equal // variable assignment
} Op;
Token( Type type = Unknown, const QString& text = QString::null, int pos = -1 );
Token( const Token& );
Token& operator=( const Token& );
Type type() const { return m_type; }
QString text() const { return m_text; }
int pos() const { return m_pos; };
bool isNumber() const { return m_type == Number; }
bool isOperator() const { return m_type == Operator; }
bool isIdentifier() const { return m_type == Identifier; }
Abakus::number_t asNumber() const;
Op asOperator() const;
QString description() const;
static const Token null;
protected:
Type m_type;
QString m_text;
int m_pos;
};
class Tokens: public QValueVector<Token>
{
public:
Tokens(): QValueVector<Token>(), m_valid(true) {};
bool valid() const { return m_valid; }
void setValid( bool v ) { m_valid = v; }
protected:
bool m_valid;
};
class Variable
{
public:
QString name;
Abakus::number_t value;
};
class Evaluator
{
public:
Evaluator();
~Evaluator();
void setExpression( const QString& expr );
QString expression() const;
void clear();
bool isValid() const;
Tokens tokens() const;
static Tokens scan( const QString& expr );
QString error() const;
// Abakus::number_t eval();
static QString autoFix( const QString& expr );
private:
Evaluator( const Evaluator& );
Evaluator& operator=( const Evaluator& );
};
#endif // EVALUATOR
// vim: set et ts=8 sw=4:

@ -0,0 +1,298 @@
/*
* function.cpp - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
#include "numerictypes.h"
#include <kdebug.h>
#include <qvaluevector.h>
#include <qstring.h>
#include <qregexp.h>
#include <math.h>
#include "function.h"
#include "node.h"
#include "valuemanager.h"
#include "hmath.h"
// Used to try and avoid recursive function definitions
class DupFinder : public NodeFunctor
{
public:
DupFinder(const QString &nameToFind) :
m_name(nameToFind), m_valid(true)
{
}
virtual ~DupFinder() { }
bool isValid() const { return m_valid; }
virtual void operator()(const Node *node)
{
if(!m_valid)
return;
const BaseFunction *fn = dynamic_cast<const BaseFunction *>(node);
if(fn && fn->name() == m_name)
m_valid = false; // Duplicate detected
}
private:
QString m_name;
bool m_valid;
};
// Define static member for FunctionManager
FunctionManager *FunctionManager::m_manager = 0;
FunctionManager *FunctionManager::instance()
{
if(!m_manager)
m_manager = new FunctionManager;
return m_manager;
}
FunctionManager::FunctionManager(QObject *parent, const char *name) :
QObject(parent, name)
{
m_dict.setAutoDelete(true);
}
// Dummy return value to enable static initialization in the DECL_*()
// macros.
bool FunctionManager::addFunction(const QString &name, function_t fn, const QString &desc)
{
Function *newFn = new Function;
QRegExp returnTrigRE("^a(cos|sin|tan)");
QRegExp needsTrigRE("^(cos|sin|tan)");
QString fnName(name);
newFn->name = name;
newFn->description = desc;
newFn->fn = fn;
newFn->userDefined = false;
newFn->returnsTrig = fnName.contains(returnTrigRE);
newFn->needsTrig = fnName.contains(needsTrigRE);
m_dict.insert(name, newFn);
return false;
}
#define DECLARE_FUNC(name, fn, desc) bool dummy##name = FunctionManager::instance()->addFunction(#name, fn, desc)
// Declares a function name that is implemented by the function of a different
// name. e.g. atan -> Abakus::number_t::arctan()
#define DECLARE_FUNC2(name, fnName, desc) DECLARE_FUNC(name, &Abakus::number_t::fnName, desc)
// Declares a function name that is implemented by the function of the
// same base name.
#define DECLARE_FUNC1(name, desc) DECLARE_FUNC2(name, name, desc)
DECLARE_FUNC1(sin, "Trigonometric sine");
DECLARE_FUNC1(cos, "Trigonometric cosine");
DECLARE_FUNC1(tan, "Trigonometric tangent");
DECLARE_FUNC1(sinh, "Hyperbolic sine");
DECLARE_FUNC1(cosh, "Hyperbolic cosine");
DECLARE_FUNC1(tanh, "Hyperbolic tangent");
DECLARE_FUNC1(atan, "Inverse tangent");
DECLARE_FUNC1(acos, "Inverse cosine");
DECLARE_FUNC1(asin, "Inverse sine");
DECLARE_FUNC1(asinh, "Inverse hyperbolic sine");
DECLARE_FUNC1(acosh, "Inverse hyperbolic cosine");
DECLARE_FUNC1(atanh, "Inverse hyperbolic tangent");
DECLARE_FUNC1(abs, "Absolute value of number");
DECLARE_FUNC1(sqrt, "Square root");
DECLARE_FUNC1(ln, "Natural logarithm (base e)");
DECLARE_FUNC1(log, "Logarithm (base 10)");
DECLARE_FUNC1(exp, "Natural exponential function");
DECLARE_FUNC1(round, "Round to nearest number");
DECLARE_FUNC1(ceil, "Nearest greatest integer");
DECLARE_FUNC1(floor, "Nearest lesser integer");
DECLARE_FUNC2(int, integer, "Integral part of number");
DECLARE_FUNC1(frac, "Fractional part of number");
Function *FunctionManager::function(const QString &name)
{
return m_dict[name];
}
// Returns true if the named identifier is a function, false otherwise.
bool FunctionManager::isFunction(const QString &name)
{
return function(name) != 0;
}
bool FunctionManager::isFunctionUserDefined(const QString &name)
{
const Function *fn = function(name);
return (fn != 0) && (fn->userDefined);
}
bool FunctionManager::addFunction(BaseFunction *fn, const QString &dependantVar)
{
// First see if this function is recursive
DupFinder dupFinder(fn->name());
UnaryFunction *unFunction = dynamic_cast<UnaryFunction *>(fn);
if(unFunction && unFunction->operand()) {
unFunction->operand()->applyMap(dupFinder);
if(!dupFinder.isValid())
return false;
}
// Structure holds extra data needed to call the user defined
// function.
UserFunction *newFn = new UserFunction;
newFn->sequenceNumber = m_dict.count();
newFn->fn = fn;
newFn->varName = QString(dependantVar);
// Now setup the Function data structure that holds the information
// we need to access and call the function later.
Function *fnTabEntry = new Function;
fnTabEntry->name = fn->name();
fnTabEntry->userFn = newFn;
fnTabEntry->returnsTrig = false;
fnTabEntry->needsTrig = false;
fnTabEntry->userDefined = true;
if(m_dict.find(fn->name()))
emit signalFunctionRemoved(fn->name());
m_dict.replace(fn->name(), fnTabEntry);
emit signalFunctionAdded(fn->name());
return true;
}
void FunctionManager::removeFunction(const QString &name)
{
Function *fn = function(name);
// If we remove a function, we need to decrement the sequenceNumber of
// functions after this one.
if(fn && fn->userDefined) {
int savedSeqNum = fn->userFn->sequenceNumber;
// Emit before we actually remove it so that the info on the function
// can still be looked up.
emit signalFunctionRemoved(name);
delete fn->userFn;
fn->userFn = 0;
m_dict.remove(name);
QDictIterator<Function> it(m_dict);
for (; it.current(); ++it) {
UserFunction *userFn = it.current()->userDefined ? it.current()->userFn : 0;
if(userFn && userFn->sequenceNumber > savedSeqNum)
--it.current()->userFn->sequenceNumber;
}
}
}
QStringList FunctionManager::functionList(FunctionManager::FunctionType type)
{
QDictIterator<Function> it(m_dict);
QStringList functions;
switch(type) {
case Builtin:
for(; it.current(); ++it)
if(!it.current()->userDefined)
functions += it.current()->name;
break;
case UserDefined:
// We want to return the function names in the order they were
// added.
{
QValueVector<Function *> fnTable(m_dict.count(), 0);
QValueVector<int> sequenceNumberTable(m_dict.count(), -1);
// First find out what sequence numbers we have.
for(; it.current(); ++it)
if(it.current()->userDefined) {
int id = it.current()->userFn->sequenceNumber;
fnTable[id] = it.current();
sequenceNumberTable.append(id);
}
// Now sort the sequence numbers and return the ordered list
qHeapSort(sequenceNumberTable.begin(), sequenceNumberTable.end());
for(unsigned i = 0; i < sequenceNumberTable.count(); ++i)
if(sequenceNumberTable[i] >= 0)
functions += fnTable[sequenceNumberTable[i]]->name;
}
break;
case All:
functions += functionList(Builtin);
functions += functionList(UserDefined);
break;
}
return functions;
}
// Applies the function identified by func, using value as a parameter.
Abakus::number_t evaluateFunction(const Function *func, const Abakus::number_t value)
{
if(func->userDefined) {
// Pull real entry from userFunctionTable
UserFunction *realFunction = func->userFn;
bool wasSet = ValueManager::instance()->isValueSet(realFunction->varName);
Abakus::number_t oldValue;
if(wasSet)
oldValue = ValueManager::instance()->value(realFunction->varName);
ValueManager::instance()->setValue(realFunction->varName, value);
Abakus::number_t result = realFunction->fn->value();
if(wasSet)
ValueManager::instance()->setValue(realFunction->varName, oldValue);
else
ValueManager::instance()->removeValue(realFunction->varName);
return result;
}
return (value.*(func->fn))();
}
void setTrigMode(Abakus::TrigMode mode)
{
Abakus::m_trigMode = mode;
}
Abakus::TrigMode trigMode()
{
return Abakus::m_trigMode;
}
#include "function.moc"

@ -0,0 +1,122 @@
#ifndef ABAKUS_FUNCTION_H
#define ABAKUS_FUNCTION_H
/*
* function.h - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
#include "numerictypes.h"
#include <qobject.h>
#include <qstringlist.h>
#include <qstring.h>
#include <qmap.h>
#include <qdict.h>
class BaseFunction;
struct UserFunction
{
int sequenceNumber;
BaseFunction *fn;
QString varName;
};
// Ugly pointer-to-member typedef ahead
typedef Abakus::number_t (Abakus::number_t::*function_t)() const;
struct Function {
QString name;
QString description;
// A function is either builtin or user defined, this union is
// used for both cases.
union {
function_t fn; // Builtin.
UserFunction *userFn; // User defined
};
bool returnsTrig;
bool needsTrig;
bool userDefined;
};
void setTrigMode(Abakus::TrigMode mode);
Abakus::TrigMode trigMode();
class FunctionManager : public QObject
{
Q_OBJECT
public:
typedef QDict<Function> functionDict;
static FunctionManager *instance();
Function *function(const QString &name);
bool isFunction(const QString &name);
bool isFunctionUserDefined(const QString &name);
bool addFunction(BaseFunction *fn, const QString &dependantVar);
bool addFunction(const QString &name, function_t fn, const QString &desc);
void removeFunction(const QString &name);
typedef enum { Builtin, UserDefined, All } FunctionType;
QStringList functionList(FunctionType type);
signals:
void signalFunctionAdded(const QString &name);
void signalFunctionRemoved(const QString &name);
private:
FunctionManager(QObject *parent = 0, const char *name = "function manager");
static FunctionManager *m_manager;
functionDict m_dict;
};
Abakus::number_t evaluateFunction(const Function *func, const Abakus::number_t value);
// Implemented in lexer.l due to prototype issues.
Abakus::number_t parseString(const char *str);
// Implemented in lexer.l due to prototype issues.
class Lexer
{
public:
Lexer(const QString &expr);
~Lexer();
bool hasNext() const;
int nextType();
int tokenPos() const;
// Can call this after nextType to find the associated string value of the
// token.
QString tokenValue() const;
private:
class Private;
Private *m_private;
};
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

File diff suppressed because it is too large Load Diff

@ -0,0 +1,359 @@
/* HMath: C++ high precision math routines
Copyright (C) 2004 Ariya Hidayat <ariya.hidayat@gmail.com>
This file was copied from the SpeedCrunch program. Please visit
http://speedcrunch.berlios.de/ for more information.
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.
59 Temple Place, Suite 330
Boston, MA 02110-1301 USA.
*/
#ifndef HMATH_H
#define HMATH_H
#include <iostream>
class HMath;
class HNumber
{
friend class HMath;
public:
/*!
* Creates a new number.
*/
HNumber();
/*!
* Destroys this number.
*/
~HNumber();
/*!
* Copies from another number.
*/
HNumber( const HNumber& );
/*!
* Assigns from another number.
*/
HNumber& operator=( const HNumber& );
/*!
* Creates a new number from an integer value.
*/
HNumber( int i );
/*!
* Creates a new number from a string.
*/
HNumber( const char* );
/*!
* Returns true if this number is Not a Number (NaN).
*/
bool isNan() const;
/*!
* Returns true if this number is zero.
*/
bool isZero() const;
/*!
* Returns true if this number is more than zero.
*/
bool isPositive() const;
/*!
* Returns true if this number is less than zero.
*/
bool isNegative() const;
/*!
* Adds another number.
*/
HNumber operator+( const HNumber& ) const;
/*!
* Adds another number.
*/
HNumber& operator+=( const HNumber& );
/*!
* Subtract from another number.
*/
HNumber operator-( const HNumber& ) const;
/*!
* Subtract from another number.
*/
HNumber& operator-=( const HNumber& );
/*!
* Multiplies with another number.
*/
HNumber operator*( const HNumber& ) const;
/*!
* Multiplies with another number.
*/
HNumber& operator*=( const HNumber& );
/*!
* Divides with another number.
*/
HNumber operator/( const HNumber& ) const;
/*!
* Divides with another number.
*/
HNumber& operator/=( const HNumber& );
/*!
* Returns true if this number is greater than n.
*/
bool operator>( const HNumber& n ) const;
/*!
* Returns true if this number is less than n.
*/
bool operator<( const HNumber& n ) const;
/*!
* Returns true if this number is greater than or equal to n.
*/
bool operator>=( const HNumber& n ) const;
/*!
* Returns true if this number is less than or equal to n.
*/
bool operator<=( const HNumber& n ) const;
/*!
* Returns true if this number is equal to n.
*/
bool operator==( const HNumber& n ) const;
/*!
* Returns true if this number is not equal to n.
*/
bool operator!=( const HNumber& n ) const;
/*!
* Returns a NaN (Not a Number).
*/
static HNumber nan();
private:
class Private;
Private* d;
};
class QString;
class HMath
{
public:
/*!
* Formats the given number as string, using specified decimal digits.
* Note that the returned string must be freed.
*/
static char* format( const HNumber&n, char format = 'g', int prec = -1 );
/*!
* Formats the given number as string, using specified decimal digits.
* Note that the returned string must be freed.
*/
static char* formatFixed( const HNumber&n, int prec = -1 );
/*!
* Formats the given number as string, in exponential format.
* Note that the returned string must be freed.
*/
static char* formatExp( const HNumber&n, int prec = -1 );
/*!
* Formats the given number as string, using specified decimal digits.
* Note that the returned string must be freed.
*/
static char* formatGeneral( const HNumber&n, int prec = -1 );
static QString formatGenString( const HNumber &n, int prec = -1 );
/*!
* Returns the constant pi.
*/
static HNumber pi();
/*!
* Adds two numbers.
*/
static HNumber add( const HNumber& n1, const HNumber& n2 );
/*!
* Subtracts two numbers.
*/
static HNumber sub( const HNumber& n1, const HNumber& n2 );
/*!
* Multiplies two numbers.
*/
static HNumber mul( const HNumber& n1, const HNumber& n2 );
/*!
* Divides two numbers.
*/
static HNumber div( const HNumber& n1, const HNumber& n2 );
/*!
* Returns -1, 0, 1 if n1 is less than, equal to, or more than n2.
*/
static int compare( const HNumber& n1, const HNumber& n2 );
/*!
* Returns the absolute value of n.
*/
static HNumber abs( const HNumber& n );
/*!
* Returns the negative of n.
*/
static HNumber negate( const HNumber& n );
/*!
* Returns the integer part of n.
*/
static HNumber integer( const HNumber& n );
/*!
* Returns the fraction part of n.
*/
static HNumber frac( const HNumber& n );
/*!
* Rounds n to the specified decimal digits.
*/
static HNumber round( const HNumber&n, int prec = 0 );
/*!
* Returns the square root of n. If n is negative, returns NaN.
*/
static HNumber sqrt( const HNumber& n );
/*!
* Raises n1 to an integer n.
*/
static HNumber raise( const HNumber& n1, int n );
/*!
* Raises n1 to n2.
*/
static HNumber raise( const HNumber& n1, const HNumber& n2 );
/*!
* Returns e raised to x.
*/
static HNumber exp( const HNumber& x );
/*!
* Returns the natural logarithm of x.
* If x is non positive, returns NaN.
*/
static HNumber ln( const HNumber& x );
/*!
* Returns the base-10 logarithm of x.
* If x is non positive, returns NaN.
*/
static HNumber log( const HNumber& x );
/*!
* Returns the sine of x. Note that x must be in radians.
*/
static HNumber sin( const HNumber& x );
/*!
* Returns the cosine of x. Note that x must be in radians.
*/
static HNumber cos( const HNumber& x );
/*!
* Returns the tangent of x. Note that x must be in radians.
*/
static HNumber tan( const HNumber& x );
/*!
* Returns the arc sine of x.
*/
static HNumber asin( const HNumber& x );
/*!
* Returns the arc cosine of x.
*/
static HNumber acos( const HNumber& x );
/*!
* Returns the arc tangent of x.
*/
static HNumber atan( const HNumber& x );
/*!
* Returns the hyperbolic sine of x. Note that x must be in radians.
*/
static HNumber sinh( const HNumber& x );
/*!
* Returns the arc hyperbolic sine of x. The result is in radians.
*/
static HNumber asinh( const HNumber & x );
/*!
* Returns the hyperbolic cosine of x. Note that x must be in radians.
*/
static HNumber cosh( const HNumber& x );
/*!
* Returns the arc hyperbolic cosine of x. The result is in radians.
*/
static HNumber acosh( const HNumber & x );
/*!
* Returns the hyperbolic tangent of x. Note that x must be in radians.
*/
static HNumber tanh( const HNumber& x );
/*!
* Returns the arc hyperbolic tangent of x. The result is in radians.
*/
static HNumber atanh( const HNumber & x );
/*!
* Releases all resources. After calling this function, you can not use
* any other functions as well as class HNumber.
*/
static void finalize();
};
std::ostream& operator<<( std::ostream& s, HNumber );
#endif // HMATH_H
// vim: set et sw=2 ts=8:

@ -0,0 +1,223 @@
/*
* lexer.ll - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
%option noyywrap
%{
#define YY_NO_UNPUT
#include <kdebug.h>
#include "node.h"
#include "function.h"
#include "parser_yacc.hpp"
#include "result.h"
int yyCurTokenPos;
int yyThisTokenLength;
int yyparse(void);
%}
DIGITS [0-9]+
HEX [0-9A-Fa-f]+
%%
/* Always skip whitespace */
[ \t]* { yyCurTokenPos += yyThisTokenLength; yyThisTokenLength = yyleng; }
/* Power operator */
"**" {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = 2;
return POWER;
}
"^" {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = 1;
return POWER;
}
[sS][eE][tT] {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = 3;
return SET;
}
[rR][eE][mM][oO][vV][eE] {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = 6;
return REMOVE;
}
[dD][eE][rR][iI][vV] {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = 5;
return DERIV;
}
/* Read numbers of the form with at least the decimal point and trailing
* digits, such as .32, -234.45, .0, etc. Numbers are only read in the BEGIN
* state.
*/
{DIGITS}*([\.,]{DIGITS}+)(e[-+]?{DIGITS}+)? {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = yyleng;
return NUM;
}
/* Read Hex */
0x({HEX}+)? {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = yyleng;
return NUM;
}
/* Read numbers with at least the integral part, such as +4234, -34e8, etc.
* Numbers are only read in the BEGIN state.
*/
{DIGITS}+([\.,]{DIGITS}*)?(e[-+]?{DIGITS}+)? {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = yyleng;
return NUM;
}
[nN][aA][nN] {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = yyleng;
return NUM;
}
[iI][nN][fF] {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = yyleng;
return NUM;
}
/* This detects textual input, and if it isn't pre-declared by the parser (in
* other words, if it isn't a function), then it is returned as an identifier.
*/
[a-zA-Z_][a-zA-Z_0-9]* {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = yyleng;
if(FunctionManager::instance()->isFunction(yytext))
return FN;
else {
return ID;
}
}
/* All other characters are returned as-is to the parser, who can accept or
* reject it as needed.
*/
. {
yyCurTokenPos += yyThisTokenLength;
yyThisTokenLength = 1;
return *yytext;
}
%%
class Lexer::Private
{
public:
YY_BUFFER_STATE buffer;
int lastToken, thisToken;
int lastPos, thisPos;
QString lastTokenData, thisTokenData;
};
/* Declared in function.h, implemented here in lexer.l since this is where
* all the yy_*() functions and types are defined.
*/
Lexer::Lexer(const QString &expr) :
m_private(new Private)
{
const char *exprString = expr.latin1();
yyCurTokenPos = 0;
yyThisTokenLength = 0;
m_private->buffer = yy_scan_string(exprString ? exprString : "");
m_private->lastToken = -1;
m_private->lastPos = -1;
m_private->thisToken = yylex();
m_private->thisTokenData = QString(yytext);
if(yyCurTokenPos != 0)
{
kdError() << "yyCurTokenPos should be 0!!\n";
}
m_private->thisPos = yyCurTokenPos;
}
Lexer::~Lexer()
{
yy_delete_buffer(m_private->buffer);
delete m_private;
}
bool Lexer::hasNext() const
{
return m_private->thisToken > 0;
}
int Lexer::nextType()
{
m_private->lastTokenData = m_private->thisTokenData;
m_private->lastPos = m_private->thisPos;
m_private->lastToken = m_private->thisToken;
m_private->thisToken = yylex();
m_private->thisTokenData = QString(yytext);
m_private->thisPos = yyCurTokenPos;
return m_private->lastToken;
}
QString Lexer::tokenValue() const
{
return m_private->lastTokenData;
}
int Lexer::tokenPos() const
{
return m_private->lastPos;
}
/* Declared in function.h, implemented here in lexer.l since this is where
* all the yy_*() functions and types are defined.
*/
Abakus::number_t parseString(const char *str)
{
YY_BUFFER_STATE buffer = yy_scan_string(str);
yyCurTokenPos = 0;
yyThisTokenLength = 0;
yyparse();
yy_delete_buffer(buffer);
if(Result::lastResult()->type() != Result::Value)
return Abakus::number_t();
return Result::lastResult()->result()->value();
}

@ -0,0 +1,825 @@
/*
* mainwindow.cpp - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
#include "mainwindow.h"
#include "abakuscommon.h"
#include <kaccel.h>
#include <kmenubar.h>
#include <kaction.h>
#include <kstdaction.h>
#include <kshortcut.h>
#include <kdebug.h>
#include <kconfig.h>
#include <kglobal.h>
#include <kconfigbase.h>
#include <kactionclasses.h>
#include <kinputdialog.h>
#include <qlayout.h>
#include <qvbox.h>
#include <qhbox.h>
#include <qradiobutton.h>
#include <qbuttongroup.h>
#include <qsplitter.h>
#include "editor.h"
#include "evaluator.h"
#include "function.h"
#include "resultlistview.h"
#include "resultlistviewtext.h"
#include "valuemanager.h"
#include "node.h"
#include "rpnmuncher.h"
#include "dcopIface.h"
#include "abakuslistview.h"
#include "result.h"
MainWindow::MainWindow() : KMainWindow(0, "abakus-mainwindow"), m_popup(0), m_insert(false)
{
m_mainSplitter = new QSplitter(this);
QWidget *box = new QWidget(m_mainSplitter);
QVBoxLayout *layout = new QVBoxLayout(box);
m_layout = layout;
layout->setSpacing(6);
layout->setMargin(6);
QWidget *configBox = new QWidget(box);
layout->addWidget(configBox);
QHBoxLayout *configLayout = new QHBoxLayout(configBox);
configLayout->addWidget(new QWidget(configBox));
QLabel *label = new QLabel(i18n("History: "), configBox);
label->setAlignment(AlignCenter);
configLayout->addWidget(label);
QButtonGroup *buttonGroup = new QButtonGroup(0);
QWidget *buttonGroupBox = new QWidget(configBox);
QHBoxLayout *buttonGroupLayout = new QHBoxLayout(buttonGroupBox);
buttonGroupLayout->addStretch(0);
configLayout->addWidget(buttonGroupBox);
m_degrees = new QRadioButton(i18n("&Degrees"), buttonGroupBox);
buttonGroup->insert(m_degrees);
buttonGroupLayout->addWidget(m_degrees);
slotDegrees();
connect(m_degrees, SIGNAL(clicked()), SLOT(slotDegrees()));
m_radians = new QRadioButton(i18n("&Radians"), buttonGroupBox);
buttonGroup->insert(m_radians);
buttonGroupLayout->addWidget(m_radians);
connect(m_radians, SIGNAL(clicked()), SLOT(slotRadians()));
m_history = new QVBox(box);
layout->addWidget(m_history);
m_history->setSpacing(6);
m_history->setMargin(0);
m_result = new ResultListView(m_history);
m_result->setSelectionMode(QListView::NoSelection);
m_result->setHScrollBarMode(ResultListView::AlwaysOff);
connect(m_result, SIGNAL(signalEntrySelected(const QString &)),
SLOT(slotEntrySelected(const QString &)));
connect(m_result, SIGNAL(signalResultSelected(const QString &)),
SLOT(slotResultSelected(const QString &)));
m_history->setStretchFactor(m_result, 1);
layout->setStretchFactor(m_history, 1);
QHBox *editBox = new QHBox(box);
layout->addWidget(editBox);
editBox->setSpacing(6);
m_edit = new Editor(editBox);
m_edit->setFocus();
editBox->setStretchFactor(m_edit, 1);
KPushButton *evalButton = new KPushButton(i18n("&Evaluate"), editBox);
connect(evalButton, SIGNAL(clicked()), SLOT(slotEvaluate()));
connect(m_edit, SIGNAL(returnPressed()), SLOT(slotReturnPressed()));
connect(m_edit, SIGNAL(textChanged()), SLOT(slotTextChanged()));
m_listSplitter = new QSplitter(Vertical, m_mainSplitter);
m_fnList = new FunctionListView(m_listSplitter);
m_fnList->addColumn("Functions");
m_fnList->addColumn("Value");
m_varList = new VariableListView(m_listSplitter);
m_varList->addColumn("Variables");
m_varList->addColumn("Value");
connect(FunctionManager::instance(), SIGNAL(signalFunctionAdded(const QString &)),
this, SLOT(slotNewFunction(const QString &)));
connect(FunctionManager::instance(), SIGNAL(signalFunctionRemoved(const QString &)),
this, SLOT(slotRemoveFunction(const QString &)));
connect(ValueManager::instance(), SIGNAL(signalValueAdded(const QString &, Abakus::number_t)),
this, SLOT(slotNewValue(const QString &, Abakus::number_t)));
connect(ValueManager::instance(), SIGNAL(signalValueChanged(const QString &, Abakus::number_t)),
this, SLOT(slotChangeValue(const QString &, Abakus::number_t)));
connect(ValueManager::instance(), SIGNAL(signalValueRemoved(const QString &)),
this, SLOT(slotRemoveValue(const QString &)));
setupLayout();
setCentralWidget(m_mainSplitter);
#if KDE_IS_VERSION(3,4,89)
setupGUI(QSize(450, 400), Keys | StatusBar | Save | Create);
#else
setupGUI(Keys | StatusBar | Save | Create);
#endif
m_dcopInterface = new AbakusIface();
}
bool MainWindow::inRPNMode() const
{
return action<KToggleAction>("toggleExpressionMode")->isChecked();
}
bool MainWindow::eventFilter(QObject *o, QEvent *e)
{
return KMainWindow::eventFilter(o, e);
}
bool MainWindow::queryExit()
{
saveConfig();
return KMainWindow::queryExit();
}
void MainWindow::contextMenuEvent(QContextMenuEvent *e)
{
static KPopupMenu *popup = 0;
if(!popup) {
popup = new KPopupMenu(this);
action("options_show_menubar")->plug(popup);
}
if(!action<KToggleAction>("options_show_menubar")->isChecked())
popup->popup(e->globalPos());
}
void MainWindow::polish()
{
KMainWindow::polish();
loadConfig();
}
void MainWindow::slotReturnPressed()
{
QString text = m_edit->text();
text.replace("\n", "");
m_edit->appendHistory(text);
// Item to insert after
ResultListViewText *after = m_result->lastItem();
// Expand $foo references.
QString str = interpolateExpression(text, after);
QString resultVal;
ResultListViewText *item;
if(str.isNull())
return; // Error already has been posted
m_insert = false; // Assume we failed
if(inRPNMode()) {
// We're in RPN mode.
Abakus::number_t result = RPNParser::rpnParseString(str);
if(!RPNParser::wasError()) {
resultVal = result.toString();
ValueManager::instance()->setValue("ans", result);
m_insert = true;
}
else {
m_insert = false;
resultVal = i18n("Error: %1").arg(RPNParser::errorString());
}
// Skip creating list view items if in compact mode.
if(!m_history->isShown()) {
m_edit->setText(resultVal);
QTimer::singleShot(0, m_edit, SLOT(selectAll()));
return;
}
item = new ResultListViewText(m_result, str, resultVal, after, false);
}
else {
// Check to see if it's just a function name, if so, add (ans).
if(FunctionManager::instance()->isFunction(str))
str += " ans";
// Add right parentheses as needed to balance out the expression.
int parenLevel = getParenthesesLevel(str);
for(int i = 0; i < parenLevel; ++i)
str += ')';
Abakus::number_t result = parseString(str.latin1());
bool compact = !m_history->isShown();
switch(Result::lastResult()->type()) {
case Result::Value:
resultVal = result.toString();
ValueManager::instance()->setValue("ans", result);
if(!compact)
item = new ResultListViewText(m_result, str, result, after, false);
m_insert = true;
break;
case Result::Null: // OK, no result to speak of
resultVal = "OK";
if(!compact)
item = new ResultListViewText(m_result, str, resultVal, after, true);
break;
default:
resultVal = Result::lastResult()->message();
if(!compact)
item = new ResultListViewText(m_result, str, resultVal, after, true);
}
// Skip creating list view items if in compact mode.
if(compact) {
m_edit->setText(resultVal);
QTimer::singleShot(0, m_edit, SLOT(selectAll()));
return;
}
}
m_edit->setText(text);
m_result->setCurrentItem(item);
m_result->ensureItemVisible(item);
QTimer::singleShot(0, m_edit, SLOT(selectAll()));
}
void MainWindow::slotTextChanged()
{
QString str = m_edit->text();
if(str.length() == 1 && m_insert) {
m_insert = false;
// Don't do anything if in RPN Mode.
if(inRPNMode())
return;
if(str.find(QRegExp("^[-+*/^]")) != -1) {
m_edit->setText("ans " + str + " ");
m_edit->moveCursor(QTextEdit::MoveEnd, false);
}
}
}
void MainWindow::slotEvaluate()
{
slotReturnPressed();
}
void MainWindow::slotUpdateSize()
{
if(m_newSize != QSize(0, 0))
resize(m_newSize);
else
resize(width(), minimumSize().height());
}
void MainWindow::slotDegrees()
{
setTrigMode(Abakus::Degrees);
m_degrees->setChecked(true);
if(action("setDegreesMode"))
action<KToggleAction>("setDegreesMode")->setChecked(true);
}
void MainWindow::slotRadians()
{
setTrigMode(Abakus::Radians);
m_radians->setChecked(true);
if(action("setRadiansMode"))
action<KToggleAction>("setRadiansMode")->setChecked(true);
}
int MainWindow::getParenthesesLevel(const QString &str)
{
int level = 0;
for(unsigned i = 0; i < str.length(); ++i)
if(str[i] == '(')
++level;
else if(str[i] == ')')
--level;
return level;
}
void MainWindow::loadConfig()
{
{
KConfigGroup config(KGlobal::config(), "Settings");
QString mode = config.readEntry("Trigonometric mode", "Degrees");
if(mode == "Degrees") {
setTrigMode(Abakus::Degrees);
m_degrees->setChecked(true);
}
else {
setTrigMode(Abakus::Radians);
m_radians->setChecked(true);
}
bool useRPN = config.readBoolEntry("Use RPN Mode", false);
action<KToggleAction>("toggleExpressionMode")->setChecked(useRPN);
int precision = config.readNumEntry("Decimal Precision", -1);
if(precision < -1 || precision > 75)
precision = -1;
Abakus::m_prec = precision;
selectCorrectPrecisionAction();
}
{
KConfigGroup config(KGlobal::config(), "Variables");
QStringList list = config.readListEntry("Saved Variables");
for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
QStringList values = QStringList::split('=', *it);
if(values.count() != 2) {
kdWarning() << "Your configuration file has somehow been corrupted!\n";
continue;
}
ValueManager::instance()->setValue(values[0], Abakus::number_t(values[1].latin1()));
}
}
{
KConfigGroup config(KGlobal::config(), "GUI");
bool showHistory = config.readBoolEntry("ShowHistory", true);
action<KToggleAction>("toggleHistoryList")->setChecked(showHistory);
m_history->setShown(showHistory);
bool showFunctions = config.readBoolEntry("ShowFunctions", true);
action<KToggleAction>("toggleFunctionList")->setChecked(showFunctions);
m_fnList->setShown(showFunctions);
bool showVariables = config.readBoolEntry("ShowVariables", true);
action<KToggleAction>("toggleVariableList")->setChecked(showVariables);
m_varList->setShown(showVariables);
bool compactMode = config.readBoolEntry("InCompactMode", false);
compactMode = compactMode || !showHistory;
action<KToggleAction>("toggleCompactMode")->setChecked(compactMode);
if(compactMode)
QTimer::singleShot(0, this, SLOT(slotToggleCompactMode()));
}
{
KConfigGroup config(KGlobal::config(), "Functions");
QStringList fnList = config.readListEntry("FunctionList");
for(QStringList::ConstIterator it = fnList.begin(); it != fnList.end(); ++it)
parseString(*it); // Run the function definitions through the parser
}
populateListViews();
}
void MainWindow::saveConfig()
{
{
KConfigGroup config(KGlobal::config(), "Settings");
config.writeEntry("Trigonometric mode",
trigMode() == Abakus::Degrees
? "Degrees"
: "Radians");
config.writeEntry("Use RPN Mode", inRPNMode());
config.writeEntry("Decimal Precision", Abakus::m_prec);
}
{
KConfigGroup config(KGlobal::config(), "Variables");
QStringList list;
QStringList values = ValueManager::instance()->valueNames();
QStringList::ConstIterator it = values.begin();
// Set precision to max for most accuracy
Abakus::m_prec = 75;
for(; it != values.end(); ++it) {
if(ValueManager::instance()->isValueReadOnly(*it))
continue;
list += QString("%1=%2")
.arg(*it)
.arg(ValueManager::instance()->value(*it).toString());
}
config.writeEntry("Saved Variables", list);
}
{
KConfigGroup config(KGlobal::config(), "GUI");
bool inCompactMode = action<KToggleAction>("toggleCompactMode")->isChecked();
config.writeEntry("InCompactMode", inCompactMode);
if(!inCompactMode) {
config.writeEntry("ShowHistory", m_history->isShown());
config.writeEntry("ShowFunctions", m_fnList->isShown());
config.writeEntry("ShowVariables", m_varList->isShown());
}
else {
config.writeEntry("ShowHistory", m_wasHistoryShown);
config.writeEntry("ShowFunctions", m_wasFnShown);
config.writeEntry("ShowVariables", m_wasVarShown);
}
}
{
KConfigGroup config(KGlobal::config(), "Functions");
FunctionManager *manager = FunctionManager::instance();
QStringList userFunctions = manager->functionList(FunctionManager::UserDefined);
QStringList saveList;
for(QStringList::ConstIterator it = userFunctions.begin(); it != userFunctions.end(); ++it) {
UnaryFunction *fn = dynamic_cast<UnaryFunction *>(manager->function(*it)->userFn->fn);
QString var = manager->function(*it)->userFn->varName;
QString expr = fn->operand()->infixString();
saveList += QString("set %1(%2) = %3").arg(*it).arg(var).arg(expr);
}
config.writeEntry("FunctionList", saveList);
}
}
void MainWindow::setupLayout()
{
KActionCollection *ac = actionCollection();
KStdAction::quit(kapp, SLOT(quit()), ac);
KStdAction::showMenubar(this, SLOT(slotToggleMenuBar()), ac);
KToggleAction *ta = new KToggleAction(i18n("&Degrees"), SHIFT + ALT + Key_D, this, SLOT(slotDegrees()), ac, "setDegreesMode");
ta->setExclusiveGroup("TrigMode");
ta->setChecked(trigMode() == Abakus::Degrees);
ta = new KToggleAction(i18n("&Radians"), SHIFT + ALT + Key_R, this, SLOT(slotRadians()), ac, "setRadiansMode");
ta->setExclusiveGroup("TrigMode");
ta->setChecked(trigMode() == Abakus::Radians);
ta = new KToggleAction(i18n("Show &History List"), SHIFT + ALT + Key_H, this, SLOT(slotToggleHistoryList()), ac, "toggleHistoryList");
ta->setChecked(true);
ta = new KToggleAction(i18n("Show &Variables"), SHIFT + ALT + Key_V, this, SLOT(slotToggleVariableList()), ac, "toggleVariableList");
ta->setChecked(true);
ta = new KToggleAction(i18n("Show &Functions"), SHIFT + ALT + Key_F, this, SLOT(slotToggleFunctionList()), ac, "toggleFunctionList");
ta->setChecked(true);
ta = new KToggleAction(i18n("Activate &Compact Mode"), SHIFT + ALT + Key_C, this, SLOT(slotToggleCompactMode()), ac, "toggleCompactMode");
ta->setChecked(false);
ta = new KToggleAction(i18n("Use R&PN Mode"), SHIFT + ALT + Key_P, this, SLOT(slotToggleExpressionMode()), ac, "toggleExpressionMode");
ta->setChecked(false);
// Precision actions.
ta = new KToggleAction(i18n("&Automatic Precision"), 0, this, SLOT(slotPrecisionAuto()), ac, "precisionAuto");
ta->setExclusiveGroup("Precision");
ta->setChecked(true);
ta = new KToggleAction(i18n("&3 Decimal Digits"), 0, this, SLOT(slotPrecision3()), ac, "precision3");
ta->setExclusiveGroup("Precision");
ta->setChecked(false);
ta = new KToggleAction(i18n("&8 Decimal Digits"), 0, this, SLOT(slotPrecision8()), ac, "precision8");
ta->setExclusiveGroup("Precision");
ta->setChecked(false);
ta = new KToggleAction(i18n("&15 Decimal Digits"), 0, this, SLOT(slotPrecision15()), ac, "precision15");
ta->setExclusiveGroup("Precision");
ta->setChecked(false);
ta = new KToggleAction(i18n("&50 Decimal Digits"), 0, this, SLOT(slotPrecision50()), ac, "precision50");
ta->setExclusiveGroup("Precision");
ta->setChecked(false);
ta = new KToggleAction(i18n("C&ustom Precision..."), 0, this, SLOT(slotPrecisionCustom()), ac, "precisionCustom");
ta->setExclusiveGroup("Precision");
ta->setChecked(false);
new KAction(i18n("Clear &History"), "editclear", SHIFT + ALT + Key_L, m_result, SLOT(clear()), ac, "clearHistory");
new KAction(i18n("Select Editor"), "goto", Key_F6, m_edit, SLOT(setFocus()), ac, "select_edit");
}
void MainWindow::populateListViews()
{
QStringList values = ValueManager::instance()->valueNames();
Abakus::number_t value = ValueManager::instance()->value("pi");
new ValueListViewItem(m_varList, "pi", value);
value = ValueManager::instance()->value("e");
new ValueListViewItem(m_varList, "e", value);
}
KAction *MainWindow::action(const char *key) const
{
return actionCollection()->action(key);
}
void MainWindow::slotEntrySelected(const QString &text)
{
m_edit->setText(text);
m_edit->moveCursor(QTextEdit::MoveEnd, false);
}
void MainWindow::slotResultSelected(const QString &text)
{
m_edit->insert(text);
}
void MainWindow::slotToggleMenuBar()
{
menuBar()->setShown(!menuBar()->isShown());
}
void MainWindow::slotToggleFunctionList()
{
bool show = action<KToggleAction>("toggleFunctionList")->isChecked();
m_fnList->setShown(show);
if(!m_history->isShown()) {
m_history->setShown(true);
action<KToggleAction>("toggleHistoryList")->setChecked(true);
slotToggleHistoryList();
}
action<KToggleAction>("toggleCompactMode")->setChecked(false);
}
void MainWindow::slotToggleVariableList()
{
bool show = action<KToggleAction>("toggleVariableList")->isChecked();
m_varList->setShown(show);
if(!m_history->isShown()) {
m_history->setShown(true);
action<KToggleAction>("toggleHistoryList")->setChecked(true);
slotToggleHistoryList();
}
action<KToggleAction>("toggleCompactMode")->setChecked(false);
}
void MainWindow::slotToggleHistoryList()
{
bool show = action<KToggleAction>("toggleHistoryList")->isChecked();
m_history->setShown(show);
action<KToggleAction>("toggleCompactMode")->setChecked(false);
}
void MainWindow::slotNewFunction(const QString &name)
{
UserFunction *userFn = FunctionManager::instance()->function(name)->userFn;
UnaryFunction *fn = dynamic_cast<UnaryFunction *>(userFn->fn);
QString fnName = QString("%1(%2)").arg(name, userFn->varName);
QString expr = fn->operand()->infixString();
new KListViewItem(m_fnList, fnName, expr);
}
void MainWindow::slotRemoveFunction(const QString &name)
{
UserFunction *userFn = FunctionManager::instance()->function(name)->userFn;
QString fnName = QString("%1(%2)").arg(name, userFn->varName);
QListViewItem *item = 0;
while((item = m_fnList->findItem(fnName, 0)) != 0)
delete item;
}
void MainWindow::slotNewValue(const QString &name, Abakus::number_t value)
{
new ValueListViewItem(m_varList, name, value);
}
void MainWindow::slotChangeValue(const QString &name, Abakus::number_t value)
{
ValueListViewItem *item = static_cast<ValueListViewItem *>(m_varList->findItem(name, 0));
if(item)
item->valueChanged(value);
}
void MainWindow::slotRemoveValue(const QString &name)
{
delete m_varList->findItem(name, 0);
}
void MainWindow::slotToggleCompactMode()
{
if(action<KToggleAction>("toggleCompactMode")->isChecked()) {
m_wasFnShown = m_fnList->isShown();
m_wasVarShown = m_varList->isShown();
m_wasHistoryShown = m_history->isShown();
m_fnList->setShown(false);
m_varList->setShown(false);
m_history->setShown(false);
action<KToggleAction>("toggleFunctionList")->setChecked(false);
action<KToggleAction>("toggleVariableList")->setChecked(false);
action<KToggleAction>("toggleHistoryList")->setChecked(false);
m_oldSize = size();
m_newSize = QSize(0, 0);
QTimer::singleShot(0, this, SLOT(slotUpdateSize()));
}
else {
m_fnList->setShown(m_wasFnShown);
m_varList->setShown(m_wasVarShown);
m_history->setShown(m_wasHistoryShown);
action<KToggleAction>("toggleFunctionList")->setChecked(m_wasFnShown);
action<KToggleAction>("toggleVariableList")->setChecked(m_wasVarShown);
action<KToggleAction>("toggleHistoryList")->setChecked(m_wasHistoryShown);
m_newSize = m_oldSize;
QTimer::singleShot(0, this, SLOT(slotUpdateSize()));
}
}
void MainWindow::slotToggleExpressionMode()
{
}
QString MainWindow::interpolateExpression(const QString &text, ResultListViewText *after)
{
QString str(text);
QRegExp stackRE("\\$\\d+");
int pos;
while((pos = stackRE.search(str)) != -1) {
QString stackStr = stackRE.cap();
Abakus::number_t value;
unsigned numPos = stackStr.mid(1).toUInt();
if(!m_result->getStackValue(numPos, value)) {
new ResultListViewText(m_result, text, i18n("Marker %1 isn't set").arg(stackStr), after, true);
return QString::null;
}
str.replace(pos, stackStr.length(), value.toString());
}
return str;
}
void MainWindow::slotPrecisionAuto()
{
Abakus::m_prec = -1;
redrawResults();
}
void MainWindow::slotPrecision3()
{
Abakus::m_prec = 3;
redrawResults();
}
void MainWindow::slotPrecision8()
{
Abakus::m_prec = 8;
redrawResults();
}
void MainWindow::slotPrecision15()
{
Abakus::m_prec = 15;
redrawResults();
}
void MainWindow::slotPrecision50()
{
Abakus::m_prec = 50;
redrawResults();
}
void MainWindow::slotPrecisionCustom()
{
bool ok = false;
int precision = KInputDialog::getInteger(i18n("Select number of decimal digits to display"),
i18n("Decimal precision:"), Abakus::m_prec, 0, 75, 1, &ok, this);
if(ok) {
Abakus::m_prec = precision;
redrawResults();
}
selectCorrectPrecisionAction();
}
void MainWindow::redrawResults()
{
QListViewItemIterator it(m_result);
while(it.current()) {
static_cast<ResultListViewText *>(it.current())->precisionChanged();
++it;
}
it = QListViewItemIterator (m_varList);
while(it.current()) {
static_cast<ValueListViewItem *>(it.current())->valueChanged();
++it;
}
// Because of the way we implemented the menu, it is possible to deselect
// every possibility, so make sure we have at least one selected.
selectCorrectPrecisionAction();
}
// This function selects the correct precision action based on the value of
// Abakus::m_prec. Useful for loading settings, or for custom precision
// selection.
void MainWindow::selectCorrectPrecisionAction()
{
switch(Abakus::m_prec) {
case 3:
action<KToggleAction>("precision3")->setChecked(true);
break;
case 8:
action<KToggleAction>("precision8")->setChecked(true);
break;
case 15:
action<KToggleAction>("precision15")->setChecked(true);
break;
case 50:
action<KToggleAction>("precision50")->setChecked(true);
break;
case -1:
action<KToggleAction>("precisionAuto")->setChecked(true);
break;
default:
action<KToggleAction>("precisionCustom")->setChecked(true);
}
}
#include "mainwindow.moc"
// vim: set et ts=8 sw=4:

@ -0,0 +1,135 @@
#ifndef ABAKUS_MAINWINDOW_H
#define ABAKUS_MAINWINDOW_H
/*
* mainwindow.h - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
#include <kmainwindow.h>
#include "numerictypes.h"
class QPoint;
class QVBox;
class QCheckBox;
class QRadioButton;
class QBoxLayout;
class QListViewItem;
class QSplitter;
class QTimer;
//class KComboBox;
class Editor;
class KPopupMenu;
class KAction;
class KListView;
class ResultListView;
class ResultListViewText;
class AbakusIface;
// Main window class, handles events and layout and stuff
class MainWindow : public KMainWindow
{
Q_OBJECT
public:
MainWindow();
bool inRPNMode() const;
protected:
virtual void contextMenuEvent(QContextMenuEvent *e);
virtual bool eventFilter(QObject *o, QEvent *e);
virtual bool queryExit();
virtual void polish();
private slots:
void slotReturnPressed();
void slotTextChanged();
void slotEvaluate();
void slotPrecisionAuto();
void slotPrecision3();
void slotPrecision8();
void slotPrecision15();
void slotPrecision50();
void slotPrecisionCustom();
void slotUpdateSize();
void slotDegrees();
void slotRadians();
void slotEntrySelected(const QString &text);
void slotResultSelected(const QString &text);
void slotToggleMenuBar();
void slotToggleFunctionList();
void slotToggleVariableList();
void slotToggleHistoryList();
void slotToggleCompactMode();
void slotToggleExpressionMode();
void slotNewValue(const QString &name, Abakus::number_t value);
void slotChangeValue(const QString &name, Abakus::number_t value);
void slotRemoveValue(const QString &name);
void slotNewFunction(const QString &name);
void slotRemoveFunction(const QString &name);
private:
int getParenthesesLevel(const QString &str);
void redrawResults();
void selectCorrectPrecisionAction();
void loadConfig();
void saveConfig();
void setupLayout();
void populateListViews();
QString interpolateExpression(const QString &text, ResultListViewText *after);
// Donated via JuK
KAction *action(const char *key) const;
template <class T> T *action(const char *key) const
{
return dynamic_cast<T *>(action(key));
}
private:
QVBox *m_history;
QRadioButton *m_degrees;
QRadioButton *m_radians;
Editor *m_edit;
KPopupMenu *m_popup;
ResultListView *m_result;
QString m_lastError;
QBoxLayout *m_layout;
KListView *m_fnList, *m_varList;
QSplitter *m_mainSplitter, *m_listSplitter;
QSize m_newSize, m_oldSize;
AbakusIface *m_dcopInterface;
bool m_wasFnShown, m_wasVarShown, m_wasHistoryShown;
bool m_compactMode;
bool m_insert;
};
#endif

@ -0,0 +1,419 @@
/*
* node.cpp - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
#include <kdebug.h>
#include <math.h>
#include "node.h"
#include "valuemanager.h"
#include "function.h"
void Node::deleteNode(Node *node)
{
if(dynamic_cast<BaseFunction *>(node) != 0)
delete node;
}
BaseFunction::BaseFunction(const char *name) :
m_name(name)
{
}
const Function *BaseFunction::function() const
{
return FunctionManager::instance()->function(m_name);
}
UnaryFunction::UnaryFunction(const char *name, Node *operand) :
BaseFunction(name), m_node(operand)
{
}
UnaryFunction::~UnaryFunction()
{
deleteNode(m_node);
m_node = 0;
}
void UnaryFunction::setOperand(Node *operand)
{
m_node = operand;
}
void UnaryFunction::applyMap(NodeFunctor &fn) const
{
fn(operand());
fn(this);
}
QString UnaryFunction::infixString() const
{
return QString("%1(%2)").arg(name(), operand()->infixString());
}
BuiltinFunction::BuiltinFunction(const char *name, Node *operand) :
UnaryFunction(name, operand)
{
}
Abakus::number_t BuiltinFunction::value() const
{
if(function() && operand()) {
Abakus::number_t fnValue = operand()->value();
return evaluateFunction(function(), fnValue);
}
return Abakus::number_t(0);
}
Abakus::number_t BuiltinFunction::derivative() const
{
Abakus::number_t du = operand()->derivative();
Abakus::number_t value = operand()->value();
Abakus::number_t one(1), zero(0);
if(du == zero)
return du;
// In case these functions get added later, these derivatives may
// be useful:
// d/dx(asinh u) = (du/dx * 1 / sqrt(x^2 + 1))
// d/dx(acosh u) = (du/dx * 1 / sqrt(x^2 - 1))
// d/dx(atanh u) = (du/dx * 1 / (1 - x^2))
// This is very unfortunate duplication.
if(name() == "sin")
return value.cos() * du;
else if(name() == "cos")
return -value.sin() * du;
else if(name() == "tan") {
Abakus::number_t cosResult;
cosResult = value.cos();
cosResult = cosResult * cosResult;
return one / cosResult;
}
else if(name() == "asinh") {
value = value * value + one;
return du / value.sqrt();
}
else if(name() == "acosh") {
value = value * value - one;
return du / value.sqrt();
}
else if(name() == "atanh") {
value = one - value * value;
return du / value;
}
else if(name() == "sinh") {
return du * value.cosh();
}
else if(name() == "cosh") {
return du * value.sinh(); // Yes the sign is correct.
}
else if(name() == "tanh") {
Abakus::number_t tanh = value.tanh();
return du * (one - tanh * tanh);
}
else if(name() == "atan") {
return one * du / (one + value * value);
}
else if(name() == "acos") {
// Same as asin but with inverted sign.
return -(one / (value * value - one).sqrt() * du);
}
else if(name() == "asin") {
return one / (value * value - one).sqrt() * du;
}
else if(name() == "ln") {
return du / value;
}
else if(name() == "exp") {
return du * value.exp();
}
else if(name() == "log") {
return du / value / Abakus::number_t(10).ln();
}
else if(name() == "sqrt") {
Abakus::number_t half("0.5");
return half * value.pow(-half) * du;
}
else if(name() == "abs") {
return (value / value.abs()) * du;
}
// Approximate it.
Abakus::number_t epsilon("1e-15");
Abakus::number_t fxh = evaluateFunction(function(), value + epsilon);
Abakus::number_t fx = evaluateFunction(function(), value);
return (fxh - fx) / epsilon;
}
DerivativeFunction::~DerivativeFunction()
{
deleteNode(m_operand);
m_operand = 0;
}
Abakus::number_t DerivativeFunction::value() const
{
ValueManager *vm = ValueManager::instance();
Abakus::number_t result;
if(vm->isValueSet("x")) {
Abakus::number_t oldValue = vm->value("x");
vm->setValue("x", m_where->value());
result = m_operand->derivative();
vm->setValue("x", oldValue);
}
else {
vm->setValue("x", m_where->value());
result = m_operand->derivative();
vm->removeValue("x");
}
return result;
}
Abakus::number_t DerivativeFunction::derivative() const
{
kdError() << k_funcinfo << endl;
kdError() << "This function is never supposed to be called!\n";
return m_operand->derivative();
}
void DerivativeFunction::applyMap(NodeFunctor &fn) const
{
fn(m_operand);
fn(this);
}
QString DerivativeFunction::infixString() const
{
return QString("deriv(%1, %2)").arg(m_operand->infixString(), m_where->infixString());
}
UnaryOperator::UnaryOperator(Type type, Node *operand)
: m_type(type), m_node(operand)
{
}
UnaryOperator::~UnaryOperator()
{
deleteNode(m_node);
m_node = 0;
}
void UnaryOperator::applyMap(NodeFunctor &fn) const
{
fn(operand());
fn(this);
}
QString UnaryOperator::infixString() const
{
if(dynamic_cast<BinaryOperator *>(operand()))
return QString("-(%1)").arg(operand()->infixString());
return QString("-%1").arg(operand()->infixString());
}
Abakus::number_t UnaryOperator::derivative() const
{
switch(type()) {
case Negation:
return -(operand()->derivative());
default:
kdError() << "Impossible case encountered for UnaryOperator!\n";
return Abakus::number_t(0);
}
}
Abakus::number_t UnaryOperator::value() const
{
switch(type()) {
case Negation:
return -(operand()->value());
default:
kdError() << "Impossible case encountered for UnaryOperator!\n";
return Abakus::number_t(0);
}
}
BinaryOperator::BinaryOperator(Type type, Node *left, Node *right) :
m_type(type), m_left(left), m_right(right)
{
}
BinaryOperator::~BinaryOperator()
{
deleteNode(m_left);
m_left = 0;
deleteNode(m_right);
m_right = 0;
}
void BinaryOperator::applyMap(NodeFunctor &fn) const
{
fn(leftNode());
fn(rightNode());
fn(this);
}
QString BinaryOperator::infixString() const
{
QString op;
switch(type()) {
case Addition:
op = "+";
break;
case Subtraction:
op = "-";
break;
case Multiplication:
op = "*";
break;
case Division:
op = "/";
break;
case Exponentiation:
op = "^";
break;
default:
op = "Error";
}
QString left = QString(isSimpleNode(leftNode()) ? "%1" : "(%1)").arg(leftNode()->infixString());
QString right = QString(isSimpleNode(rightNode()) ? "%1" : "(%1)").arg(rightNode()->infixString());
return QString("%1 %2 %3").arg(left, op, right);
}
Abakus::number_t BinaryOperator::derivative() const
{
if(!leftNode() || !rightNode()) {
kdError() << "Can't evaluate binary operator!\n";
return Abakus::number_t(0);
}
Abakus::number_t f = leftNode()->value();
Abakus::number_t fPrime = leftNode()->derivative();
Abakus::number_t g = rightNode()->value();
Abakus::number_t gPrime = rightNode()->derivative();
switch(type()) {
case Addition:
return fPrime + gPrime;
case Subtraction:
return fPrime - gPrime;
case Multiplication:
return f * gPrime + fPrime * g;
case Division:
return (g * fPrime - f * gPrime) / (g * g);
case Exponentiation:
return f.pow(g) * ((g / f) * fPrime + gPrime * f.ln());
default:
kdError() << "Impossible case encountered evaluating binary operator!\n";
return Abakus::number_t(0);
}
}
Abakus::number_t BinaryOperator::value() const
{
if(!leftNode() || !rightNode()) {
kdError() << "Can't evaluate binary operator!\n";
return Abakus::number_t(0);
}
Abakus::number_t lValue = leftNode()->value();
Abakus::number_t rValue = rightNode()->value();
switch(type()) {
case Addition:
return lValue + rValue;
case Subtraction:
return lValue - rValue;
case Multiplication:
return lValue * rValue;
case Division:
return lValue / rValue;
case Exponentiation:
return lValue.pow(rValue);
default:
kdError() << "Impossible case encountered evaluating binary operator!\n";
return Abakus::number_t(0);
}
}
bool BinaryOperator::isSimpleNode(Node *node) const
{
if(dynamic_cast<Identifier *>(node) ||
dynamic_cast<NumericValue *>(node) ||
dynamic_cast<UnaryOperator *>(node) ||
dynamic_cast<BaseFunction *>(node))
{
return true;
}
return false;
}
Identifier::Identifier(const char *name) : m_name(name)
{
}
Abakus::number_t Identifier::value() const
{
return ValueManager::instance()->value(name());
}
void Identifier::applyMap(NodeFunctor &fn) const
{
fn(this);
}
QString NumericValue::infixString() const
{
return value().toString();
}
// vim: set et ts=8 sw=4:

@ -0,0 +1,219 @@
#ifndef ABAKUS_NODE_H
#define ABAKUS_NODE_H
/*
* node.h - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
*/
#include <qstring.h>
#include "numerictypes.h"
class Node;
class Function;
template <class T> class SharedPtr;
typedef SharedPtr<Node> NodePtr;
/**
* A class that operates on a Node. Called recursively on a node and all
* of its children.
*/
class NodeFunctor
{
public:
virtual void operator()(const Node *node) = 0;
virtual ~NodeFunctor() { }
};
class Node
{
public:
virtual ~Node() { }
virtual Abakus::number_t value() const = 0;
// Deletes a node only if it isn't a function, since those are
// typically read-only.
virtual void deleteNode(Node *node);
// Calls functor() on all subchildren and this.
virtual void applyMap(NodeFunctor &fn) const = 0;
// Returns an infix representation of the expression.
virtual QString infixString() const = 0;
// Returns the derivative of the node.
virtual Abakus::number_t derivative() const = 0;
};
class BaseFunction : public Node
{
public:
BaseFunction(const char *name);
virtual QString name() const { return m_name; }
virtual const Function *function() const;
private:
QString m_name;
const Function *m_function;
};
class UnaryFunction : public BaseFunction
{
public:
UnaryFunction(const char *name, Node *operand);
virtual ~UnaryFunction();
virtual Node *operand() const { return m_node; }
virtual void setOperand(Node *operand);
virtual void applyMap(NodeFunctor &fn) const;
virtual QString infixString() const;
private:
Node *m_node;
};
// Calculates the approximate derivative of its operand.
class DerivativeFunction : public Node
{
public:
DerivativeFunction(Node *operand, Node *where) :
m_operand(operand), m_where(where) { }
~DerivativeFunction();
virtual Abakus::number_t value() const;
virtual void applyMap(NodeFunctor &fn) const;
// Returns an infix representation of the expression.
virtual QString infixString() const;
virtual Abakus::number_t derivative() const;
private:
Node *m_operand;
Node *m_where;
};
class UserDefinedFunction : public UnaryFunction
{
public:
UserDefinedFunction(const char *name, Node *operand) : UnaryFunction(name, operand) { };
virtual Abakus::number_t value() const { return operand()->value(); }
virtual Abakus::number_t derivative() const { return operand()->derivative(); }
};
class BuiltinFunction : public UnaryFunction
{
public:
BuiltinFunction(const char *name, Node *operand);
virtual Abakus::number_t value() const;
virtual Abakus::number_t derivative() const;
};
class UnaryOperator : public Node
{
public:
typedef enum { Negation } Type;
UnaryOperator(Type type, Node *operand);
virtual ~UnaryOperator();
virtual void applyMap(NodeFunctor &fn) const;
virtual QString infixString() const;
virtual Abakus::number_t value() const;
virtual Abakus::number_t derivative() const;
virtual Type type() const { return m_type; }
virtual Node *operand() const { return m_node; }
private:
Type m_type;
Node *m_node;
};
class BinaryOperator : public Node
{
public:
typedef enum { Addition, Subtraction, Multiplication, Division, Exponentiation } Type;
BinaryOperator(Type type, Node *left, Node *right);
virtual ~BinaryOperator();
virtual void applyMap(NodeFunctor &fn) const;
virtual QString infixString() const;
virtual Abakus::number_t value() const;
virtual Abakus::number_t derivative() const;
virtual Type type() const { return m_type; }
virtual Node *leftNode() const { return m_left; }
virtual Node *rightNode() const { return m_right; }
private:
bool isSimpleNode(Node *node) const;
Type m_type;
Node *m_left, *m_right;
};
class Identifier : public Node
{
public:
Identifier(const char *name);
virtual void applyMap(NodeFunctor &fn) const;
virtual QString infixString() const { return name(); }
virtual Abakus::number_t value() const;
virtual Abakus::number_t derivative() const
{
if(name() == "x")
return "1";
else
return "0";
}
virtual QString name() const { return m_name; }
private:
QString m_name;
};
class NumericValue : public Node
{
public:
NumericValue(const Abakus::number_t value) : m_value(value) { }
virtual void applyMap(NodeFunctor &fn) const { fn(this); }
virtual QString infixString() const;
virtual Abakus::number_t value() const { return m_value; }
virtual Abakus::number_t derivative() const { return "0"; }
private:
Abakus::number_t m_value;
};
#endif
// vim: set et sw=4 ts=8:

File diff suppressed because it is too large Load Diff

@ -0,0 +1,169 @@
/* number.h: Arbitrary precision numbers header file. */
/*
Copyright (C) 1991, 1992, 1993, 1994, 1997, 2000 Free Software Foundation, Inc.
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; see the file COPYING. If not, write to:
The Free Software Foundation, Inc.
59 Temple Place, Suite 330
Boston, MA 02110-1301 USA.
You may contact the author by:
e-mail: philnelson@acm.org
us-mail: Philip A. Nelson
Computer Science Department, 9062
Western Washington University
Bellingham, WA 98226-9062
*************************************************************************/
#ifndef _NUMBER_H_
#define _NUMBER_H_
#ifdef __cplusplus
extern "C" {
#endif
#undef _PROTOTYPE
#ifndef NUMBER__STDC__
#define NUMBER__STDC__
#endif
typedef enum {PLUS, MINUS} sign;
typedef struct bc_struct *bc_num;