git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/abakus@1071969 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
v3.5.13-sru
tpearson 15 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;
typedef struct bc_struct
{
sign n_sign;
int n_len; /* The number of digits before the decimal point. */
int n_scale; /* The number of digits after the decimal point. */
int n_refs; /* The number of pointers to this number. */
bc_num n_next; /* Linked list for available list. */
char *n_ptr; /* The pointer to the actual storage.
If NULL, n_value points to the inside of
another number (bc_multiply...) and should
not be "freed." */
char *n_value; /* The number. Not zero char terminated.
May not point to the same place as n_ptr as
in the case of leading zeros generated. */
} bc_struct;
/* The base used in storing the numbers in n_value above.
Currently this MUST be 10. */
#define BASE 10
/* Some useful macros and constants. */
#define CH_VAL(c) (c - '0')
#define BCD_CHAR(d) (d + '0')
#ifdef MIN
#undef MIN
#undef MAX
#endif
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)>(b)?(b):(a))
#define ODD(a) ((a)&1)
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#ifndef LONG_MAX
#define LONG_MAX 0x7ffffff
#endif
/* Global numbers. */
extern bc_num _zero_;
extern bc_num _one_;
extern bc_num _two_;
/* Function Prototypes */
/* Define the _PROTOTYPE macro if it is needed. */
#ifndef _PROTOTYPE
#ifdef NUMBER__STDC__
#define _PROTOTYPE(func, args) func args
#else
#define _PROTOTYPE(func, args) func()
#endif
#endif
_PROTOTYPE(void bc_init_numbers, (void));
_PROTOTYPE(bc_num bc_new_num, (int length, int scale));
_PROTOTYPE(void bc_free_num, (bc_num *num));
_PROTOTYPE(bc_num bc_copy_num, (bc_num num));
_PROTOTYPE(void bc_init_num, (bc_num *num));
_PROTOTYPE(void bc_str2num, (bc_num *num, char *str, int scale));
_PROTOTYPE(char *bc_num2str, (bc_num num));
_PROTOTYPE(void bc_int2num, (bc_num *num, int val));
_PROTOTYPE(long bc_num2long, (bc_num num));
_PROTOTYPE(int bc_compare, (bc_num n1, bc_num n2));
_PROTOTYPE(char bc_is_zero, (bc_num num));
_PROTOTYPE(char bc_is_near_zero, (bc_num num, int scale));
_PROTOTYPE(char bc_is_neg, (bc_num num));
_PROTOTYPE(void bc_add, (bc_num n1, bc_num n2, bc_num *result, int scale_min));
_PROTOTYPE(void bc_sub, (bc_num n1, bc_num n2, bc_num *result, int scale_min));
_PROTOTYPE(void bc_multiply, (bc_num n1, bc_num n2, bc_num *prod, int scale));
_PROTOTYPE(int bc_divide, (bc_num n1, bc_num n2, bc_num *quot, int scale));
_PROTOTYPE(int bc_modulo, (bc_num num1, bc_num num2, bc_num *result,
int scale));
_PROTOTYPE(int bc_divmod, (bc_num num1, bc_num num2, bc_num *quot,
bc_num *rem, int scale));
_PROTOTYPE(int bc_raisemod, (bc_num base, bc_num expo, bc_num mod,
bc_num *result, int scale));
_PROTOTYPE(void bc_raise, (bc_num num1, bc_num num2, bc_num *result,
int scale));
_PROTOTYPE(int bc_sqrt, (bc_num *num, int scale));
_PROTOTYPE(void bc_out_num, (bc_num num, int o_base, void (* out_char)(int),
int leading_zero));
#ifdef __cplusplus
}
#endif
#endif
// vim: set et sw=2 ts=8:

@ -0,0 +1,205 @@
/*
* numerictypes.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 "hmath.h"
#include <kdebug.h>
#include <kglobal.h>
#include <klocale.h>
Abakus::TrigMode Abakus::m_trigMode = Abakus::Degrees;
int Abakus::m_prec = -1;
#if HAVE_MPFR
namespace Abakus
{
QString convertToString(const mpfr_ptr &number)
{
char *str = 0;
QRegExp zeroKiller ("0*$");
mp_exp_t exp;
int desiredPrecision = Abakus::m_prec;
QString decimalSymbol = KGlobal::locale()->decimalSymbol();
if(desiredPrecision < 0)
desiredPrecision = 8;
// This first call is to see approximately how many digits of precision
// the fractional part has.
str = mpfr_get_str (0, &exp, 10, desiredPrecision, number, GMP_RNDN);
// Check for ginormously small numbers.
if(exp < -74)
return "0";
if(exp < -2 || exp > desiredPrecision)
{
// Use exponential notation.
QString numbers (str);
mpfr_free_str(str);
QString sign, l, r;
if(numbers[0] == '-')
{
sign = "-";
l = numbers[1];
r = numbers.right(numbers.length() - 2);
}
else
{
l = numbers[0];
r = numbers.right(numbers.length() - 1);
}
// Remove trailing zeroes.
if(Abakus::m_prec < 0)
r.replace(zeroKiller, "");
// But don't display numbers like 2.e10 either.
if(r.isEmpty())
r = "0";
r.append(QString("e%1").arg(exp - 1));
return sign + l + decimalSymbol + r;
}
else
{
mpfr_free_str(str);
// This call is to adjust the result so that the fractional part has at
// most m_prec digits of precision.
str = mpfr_get_str (0, &exp, 10, exp + desiredPrecision, number, GMP_RNDN);
}
QString result = str;
mpfr_free_str(str);
str = 0;
int position = exp;
QString l, r, sign;
if(position < 0) { // Number < 0.1
l.fill('0', -position);
if(result[0] == '-') {
sign = "-";
r = result.right(result.length() - 1);
}
else
r = result;
r = l + r;
l = '0';
}
else { // Number >= 0.1
if(result[0] == '-') {
l = result.mid(1, position);
sign = "-";
position++;
}
else
l = result.left(position);
r = result.right(result.length() - position);
}
// Remove trailing zeroes.
r.replace(zeroKiller, "");
// Don't display numbers of the form .23
if(l.isEmpty())
l = "0";
// If we have an integer don't display the decimal part.
if(r.isEmpty())
return sign + l;
return sign + l + decimalSymbol + r;
}
} // namespace Abakus
Abakus::number_t::value_type setupPi()
{
static mpfr_t pi;
mpfr_init2 (pi, 250);
mpfr_const_pi (pi, GMP_RNDN);
return pi;
}
Abakus::number_t::value_type setupExponential()
{
static mpfr_t exponential;
mpfr_t one;
mpfr_init2 (exponential, 250);
mpfr_init_set_ui (one, 1, GMP_RNDN);
mpfr_exp (exponential, one, GMP_RNDN);
mpfr_clear (one);
return exponential;
}
const Abakus::number_t::value_type Abakus::number_t::PI = setupPi();
const Abakus::number_t::value_type Abakus::number_t::E = setupExponential();
#else
// Converts hmath number to a string.
namespace Abakus
{
QString convertToString(const HNumber &num)
{
QString str = HMath::formatGenString(num, m_prec);
QString decimalSymbol = KGlobal::locale()->decimalSymbol();
str.replace('.', decimalSymbol);
QStringList parts = QStringList::split("e", str);
QRegExp zeroKiller("(" + QRegExp::escape(decimalSymbol) +
"\\d*[1-9])0*$"); // Remove trailing zeroes.
QRegExp zeroKiller2("(" + QRegExp::escape(decimalSymbol) + ")0*$");
str = parts[0];
str.replace(zeroKiller, "\\1");
str.replace(zeroKiller2, "\\1");
if(str.endsWith(decimalSymbol))
str.truncate(str.length() - 1); // Remove trailing period.
if(parts.count() > 1 && parts[1] != "0")
str += QString("e%1").arg(parts[1]);
return str;
}
} // namespace Abakus.
const Abakus::number_t::value_type Abakus::number_t::PI = HMath::pi();
const Abakus::number_t::value_type Abakus::number_t::E = HMath::exp(1);
#endif /* HAVE_MPFR */
// vim: set et ts=8 sw=4:

@ -0,0 +1,693 @@
#ifndef ABAKUS_NUMERICTYPES_H
#define ABAKUS_NUMERICTYPES_H
/*
* numerictypes.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 <sstream>
#include <string>
#include <qstring.h>
#include <qstringlist.h>
#include <qregexp.h>
#include "hmath.h"
#include "config.h"
#if HAVE_MPFR
#include <mpfr.h>
#endif
namespace Abakus
{
/* What trigonometric mode we're in. */
typedef enum { Degrees, Radians } TrigMode;
/* Shared application-wide */
extern TrigMode m_trigMode;
/* Precision to display at. */
extern int m_prec;
/**
* Representation of a number type. Includes the basic operators, along with
* built-in functions such as abs() and mod().
*
* You need to actually define it using template specializations though. You
* can add functions in a specialization, it may be worth it to have the
* functions declared here as well so that you get a compiler error if you
* forget to implement it.
*
* Note that since we're using a specialization, and then typedef'ing the
* new specialized class to number_t, that means we only support one type of
* number at a time, and the choice is made at compile-time.
*/
template <typename T>
class number
{
public:
/// Default ctor and set-and-assign ctor wrapped in one.
number(const T& t = T());
/// Copy constructor.
number(const number &other);
/// Create number from textual representation, useful for ginormously
/// precise numbers.
number(const char *str);
/// Convienience constructor to create a number from an integer.
explicit number(int i);
/// Assignment operator. Be sure to check for &other == this if necessary!
number<T> &operator =(const number<T> &other);
// You need to implement the suite of comparison operators as well, along
// with the negation operator. Sorry.
bool operator!=(const number<T> &other) const;
bool operator==(const number<T> &other) const;
bool operator<(const number<T> &other) const;
bool operator>(const number<T> &other) const;
bool operator<=(const number<T> &other) const;
bool operator>=(const number<T> &other) const;
number<T> operator -() const;
// These functions must be implemented by all specializations to be used.
// Note that when implementing these functions, the implicit value is the
// value that this object is wrapping. E.g. you'd call the function on
// a number object, kind of like 3.sin() if you were using Ruby.
// Trigonometric, must accept values in degrees.
number<T> sin() const;
number<T> cos() const;
number<T> tan() const;
// Inverse trigonometric, must return result in Degrees if necessary.
number<T> asin() const;
number<T> acos() const;
number<T> atan() const;
// Hyperbolic trigonometric (doesn't use Degrees).
number<T> sinh() const;
number<T> cosh() const;
number<T> tanh() const;
// Inverse hyperbolic trigonometric (doesn't use degrees).
number<T> asinh() const;
number<T> acosh() const;
number<T> atanh() const;
/// @return Number rounded to closest integer less than or equal to value.
number<T> floor() const;
/// @return Number rounded to closest integer greater than or equal to value.
number<T> ceil() const;
/// @return Number with only integer component of result.
number<T> integer() const;
/// @return Number with only fractional component of result.
number<T> frac() const;
/**
* @return Number rounded to nearest integer. What to do in 'strange'
* situations is specialization-dependant, I don't really care enough to
* mandate one or the other.
*/
number<T> round() const;
/// @return Absolute value of number.
number<T> abs() const;
/// @return Square root of number.
number<T> sqrt() const;
/// @return Natural-base logarithm of value.
number<T> ln() const;
/// @return base-10 logarithm of value.
number<T> log() const;
/// @return Natural base raised to the power given by our value.
number<T> exp() const;
/// @return Our value raised to the \p exponent power. Would be nice if
/// it supported even exponents on negative numbers correctly.
number<T> pow(const number<T> &exponent);
/// @return value rounded to double precision.
double asDouble() const;
/// @return Textual representation of the number, adjusted to the user's
/// current precision.
QString toString() const;
/// @return Our value.
T value() const;
};
// You should also remember to overload the math operators for your
// specialization. These generic ones should work for templates wrapping a
// type that C++ already has operators for.
template<typename T>
inline number<T> operator+(const number<T> &l, const number<T> &r)
{
return number<T>(l.value() + r.value());
}
template<typename T>
inline number<T> operator-(const number<T> &l, const number<T> &r)
{
return number<T>(l.value() - r.value());
}
template<typename T>
inline number<T> operator*(const number<T> &l, const number<T> &r)
{
return number<T>(l.value() * r.value());
}
template<typename T>
inline number<T> operator/(const number<T> &l, const number<T> &r)
{
return number<T>(l.value() / r.value());
}
#if HAVE_MPFR
/**
* Utility function to convert a MPFR number to a string. This is declared
* this way so that when it changes we don't have to recompile all of Abakus.
*
* This function obeys the precision settings of the user. This means that if
* you change the precision between function calls, you may get different
* results, even on the same number!
*
* But, don't use this directly, you should be using
* number<mpfr_ptr>::toString() instead!
*
* @param number MPFR number to convert to string.
* @return The number converted to a string, in US Decimal format at this time.
* @see number<>::toString()
*/
QString convertToString(const mpfr_ptr &number);
/**
* This is a specialization of the number<> template for the MPFR numeric type.
* It uses a weird hack in that it is declared as specializing mpfr_ptr instead
* of mpfr_t like is used everywhere in MPFR's public API.
*
* This is because mpfr_t does not seem to play well with C++ templates (it
* is implemented internally as a 1-length array to get pointer semantics
* while also allocating memory.
*
* What this means is that should you ever have to deal with allocating
* memory, you need to use allocate space for it (mpfr_ptr is a pointer to
* __mpfr_struct).
*
* I don't like using the internal API this way, but I have little choice.
*
* @author Michael Pyne <michael.pyne@kdemail.net>
*/
template<>
class number<mpfr_ptr>
{
public:
typedef mpfr_ptr value_type;
static const mp_rnd_t RoundDirection = GMP_RNDN;
number(const value_type& t)
{
m_t = (mpfr_ptr) new __mpfr_struct;
mpfr_init_set(m_t, t, RoundDirection);
}
number(const number<value_type> &other)
{
m_t = (mpfr_ptr) new __mpfr_struct;
mpfr_init_set(m_t, other.m_t, RoundDirection);
}
number(const char *str)
{
m_t = (mpfr_ptr) new __mpfr_struct;
mpfr_init_set_str (m_t, str, 10, RoundDirection);
}
explicit number(int i)
{
m_t = (mpfr_ptr) new __mpfr_struct;
mpfr_init_set_si(m_t, (signed long int) i, RoundDirection);
}
/// Construct a number with a value of NaN.
number()
{
m_t = (mpfr_ptr) new __mpfr_struct;
mpfr_init(m_t);
}
~number()
{
mpfr_clear(m_t);
delete (__mpfr_struct *) m_t;
}
number<value_type> &operator=(const number<value_type> &other)
{
if(&other == this)
return *this;
mpfr_clear (m_t);
mpfr_init_set (m_t, other.m_t, RoundDirection);
return *this;
}
bool operator!=(const number<value_type> &other) const
{
return mpfr_equal_p(m_t, other.m_t) == 0;
}
bool operator==(const number<value_type> &other) const
{
return mpfr_equal_p(m_t, other.m_t) != 0;
}
bool operator<(const number<value_type> &other) const
{
return mpfr_less_p(m_t, other.m_t) != 0;
}
bool operator>(const number<value_type> &other) const
{
return mpfr_greater_p(m_t, other.m_t) != 0;
}
bool operator<=(const number<value_type> &other) const
{
return mpfr_lessequal_p(m_t, other.m_t) != 0;
}
bool operator>=(const number<value_type> &other) const
{
return mpfr_greaterequal_p(m_t, other.m_t) != 0;
}
number<value_type> operator -() const
{
number<value_type> result(m_t);
mpfr_neg(result.m_t, result.m_t, RoundDirection);
return result;
}
// internal
number<value_type> asRadians() const
{
if(m_trigMode == Degrees)
{
number<value_type> result(m_t);
mpfr_t pi;
mpfr_init (pi);
mpfr_const_pi (pi, RoundDirection);
mpfr_mul (result.m_t, result.m_t, pi, RoundDirection);
mpfr_div_ui (result.m_t, result.m_t, 180, RoundDirection);
mpfr_clear (pi);
return result;
}
else
return m_t;
}
// internal
number<value_type> toTrig() const
{
// Assumes num is in radians.
if(m_trigMode == Degrees)
{
number<value_type> result(m_t);
mpfr_t pi;
mpfr_init (pi);
mpfr_const_pi (pi, RoundDirection);
mpfr_mul_ui (result.m_t, result.m_t, 180, RoundDirection);
mpfr_div (result.m_t, result.m_t, pi, RoundDirection);
mpfr_clear (pi);
return result;
}
else
return m_t;
}
/* There is a lot of boilerplate ahead, so define a macro to declare and
* define some functions for us to forward the call to MPFR.
*/
#define DECLARE_IMPL_BASE(name, func, in, out) number<value_type> name() const \
{ \
number<value_type> result = in; \
mpfr_##func (result.m_t, result.m_t, RoundDirection); \
\
return out; \
}
// Normal function, uses 2 rather than 3 params
#define DECLARE_NAMED_IMPL2(name, func) number<value_type> name() const \
{ \
number<value_type> result = m_t; \
mpfr_##func (result.m_t, result.m_t); \
\
return result; \
}
// Normal function, but MPFL uses a different name than abakus.
#define DECLARE_NAMED_IMPL(name, func) DECLARE_IMPL_BASE(name, func, m_t, result)
// Normal function, just routes call to MPFR.
#define DECLARE_IMPL(name) DECLARE_NAMED_IMPL(name, name)
// Trig function, degrees in
#define DECLARE_TRIG_IN_IMPL(name) DECLARE_IMPL_BASE(name, name, asRadians(), result)
// Trig function, degrees out
#define DECLARE_TRIG_OUT_IMPL(name) DECLARE_IMPL_BASE(name, name, m_t, result.toTrig())
// Now declare our functions.
DECLARE_TRIG_IN_IMPL(sin)
DECLARE_TRIG_IN_IMPL(cos)
DECLARE_TRIG_IN_IMPL(tan)
DECLARE_IMPL(sinh)
DECLARE_IMPL(cosh)
DECLARE_IMPL(tanh)
DECLARE_TRIG_OUT_IMPL(asin)
DECLARE_TRIG_OUT_IMPL(acos)
DECLARE_TRIG_OUT_IMPL(atan)
DECLARE_IMPL(asinh)
DECLARE_IMPL(acosh)
DECLARE_IMPL(atanh)
DECLARE_NAMED_IMPL2(floor, floor)
DECLARE_NAMED_IMPL2(ceil, ceil)
DECLARE_NAMED_IMPL(integer, rint)
DECLARE_IMPL(frac)
DECLARE_NAMED_IMPL2(round, round)
DECLARE_IMPL(abs)
DECLARE_IMPL(sqrt)
DECLARE_NAMED_IMPL(ln, log)
DECLARE_NAMED_IMPL(log, log10)
DECLARE_IMPL(exp)
// Can't use macro for this one, it's sorta weird.
number<value_type> pow(const number<value_type> &exponent)
{
number<value_type> result = m_t;
mpfr_pow(result.m_t, result.m_t, exponent.m_t, RoundDirection);
return result;
}
double asDouble() const
{
return mpfr_get_d(m_t, RoundDirection);
}
// Note that this can be used dangerously, be careful.
value_type value() const { return m_t; }
QString toString() const
{
// Move this to .cpp to avoid recompiling as I fix it.
return convertToString(m_t);
}
static number<value_type> nan()
{
// Doesn't apply, but the default value when initialized happens
// to be nan.
return number<value_type>();
}
static const value_type PI;
static const value_type E;
private:
mpfr_ptr m_t;
};
// Specializations of math operators for mpfr.
template<>
inline number<mpfr_ptr> operator+(const number<mpfr_ptr> &l, const number<mpfr_ptr> &r)
{
number<mpfr_ptr> result;
mpfr_add(result.value(), l.value(), r.value(), GMP_RNDN);
return result;
}
template<>
inline number<mpfr_ptr> operator-(const number<mpfr_ptr> &l, const number<mpfr_ptr> &r)
{
number<mpfr_ptr> result;
mpfr_sub(result.value(), l.value(), r.value(), GMP_RNDN);
return result;
}
template<>
inline number<mpfr_ptr> operator*(const number<mpfr_ptr> &l, const number<mpfr_ptr> &r)
{
number<mpfr_ptr> result;
mpfr_mul(result.value(), l.value(), r.value(), GMP_RNDN);
return result;
}
template<>
inline number<mpfr_ptr> operator/(const number<mpfr_ptr> &l, const number<mpfr_ptr> &r)
{
number<mpfr_ptr> result;
mpfr_div(result.value(), l.value(), r.value(), GMP_RNDN);
return result;
}
// Abakus namespace continues.
typedef number<mpfr_ptr> number_t;
#else
// Defined in numerictypes.cpp for ease of reimplementation.
QString convertToString(const HNumber &num);
/**
* Specialization for internal HMath library, used if MPFR isn't usable.
*
* @author Michael Pyne <michael.pyne@kdemail.net>
*/
template<>
class number<HNumber>
{
public:
typedef HNumber value_type;
number(const HNumber& t = HNumber()) : m_t(t)
{
}
explicit number(int i) : m_t(i) { }
number(const number<HNumber> &other) : m_t(other.m_t) { }
number(const char *s) : m_t(s) { }
bool operator!=(const number<HNumber> &other) const
{
return m_t != other.m_t;
}
bool operator==(const number<HNumber> &other) const
{
return m_t == other.m_t;
}
bool operator<(const number<HNumber> &other) const
{
return m_t < other.m_t;
}
bool operator>(const number<HNumber> &other) const
{
return m_t > other.m_t;
}
bool operator<=(const number<HNumber> &other) const
{
return m_t <= other.m_t;
}
bool operator>=(const number<HNumber> &other) const
{
return m_t >= other.m_t;
}
number<HNumber> &operator=(const number<HNumber> &other)
{
m_t = other.m_t;
return *this;
}
HNumber asRadians() const
{
if(m_trigMode == Degrees)
return m_t * PI / HNumber("180.0");
else
return m_t;
}
HNumber toTrig(const HNumber &num) const
{
// Assumes num is in radians.
if(m_trigMode == Degrees)
return num * HNumber("180.0") / PI;
else
return num;
}
number<HNumber> sin() const
{
return HMath::sin(asRadians());
}
number<HNumber> cos() const
{
return HMath::cos(asRadians());
}
number<HNumber> tan() const
{
return HMath::tan(asRadians());
}
number<HNumber> asin() const
{
return toTrig(HMath::asin(m_t));
}
number<HNumber> acos() const
{
return toTrig(HMath::acos(m_t));
}
number<HNumber> atan() const
{
return toTrig(HMath::atan(m_t));
}
number<HNumber> floor() const
{
if(HMath::frac(m_t) == HNumber("0.0"))
return integer();
if(HMath::integer(m_t) < HNumber("0.0"))
return HMath::integer(m_t) - 1;
return integer();
}
number<HNumber> ceil() const
{
return floor().value() + HNumber(1);
}
/* There is a lot of boilerplate ahead, so define a macro to declare and
* define some functions for us to forward the call to HMath.
*/
#define DECLARE_IMPL(name) number<value_type> name() const \
{ return HMath::name(m_t); }
DECLARE_IMPL(frac)
DECLARE_IMPL(integer)
DECLARE_IMPL(round)
DECLARE_IMPL(abs)
DECLARE_IMPL(sqrt)
DECLARE_IMPL(ln)
DECLARE_IMPL(log)
DECLARE_IMPL(exp)
DECLARE_IMPL(sinh)
DECLARE_IMPL(cosh)
DECLARE_IMPL(tanh)
DECLARE_IMPL(asinh)
DECLARE_IMPL(acosh)
DECLARE_IMPL(atanh)
HNumber value() const { return m_t; }
double asDouble() const { return toString().toDouble(); }
number<HNumber> operator-() const { return HMath::negate(m_t); }
// TODO: I believe this doesn't work for negative numbers with even
// exponents. Which breaks simple stuff like (-2)^2. :(
number<HNumber> pow(const number<HNumber> &exponent)
{
return HMath::raise(m_t, exponent.m_t);
}
QString toString() const
{
return convertToString(m_t);
}
static number<HNumber> nan()
{
return HNumber::nan();
}
static const HNumber PI;
static const HNumber E;
private:
HNumber m_t;
};
// Abakus namespace continues.
typedef number<HNumber> number_t;
#endif /* HAVE_MPFR */
}; // namespace Abakus
#endif /* ABAKUS_NUMERICTYPES_H */
// vim: set et ts=8 sw=4:

@ -0,0 +1,386 @@
/*
* parser.yy - 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
*/
%{
/* Add necessary includes here. */
#include <kdebug.h>
#include <klocale.h>
#include <kglobal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "result.h"
#include "node.h"
#include "function.h"
#include "valuemanager.h"
extern char *yytext;
extern int gCheckIdents;
int yylex(void);
int yyerror(const char *);
%}
%union {
Node *node;
NumericValue *value;
UnaryFunction *fn;
Identifier *ident;
}
%token <value> NUM
%type <node> EXP FACTOR TERM S EXPONENT NUMBER VALUE FINAL
%type <fn> FUNC
%token <fn> FN
%token <ident> ID
%type <ident> IDENT ASSIGN
%token POWER "**"
%token SET "set"
%token REMOVE "remove"
%token DERIV "deriv"
%%
/**
* Parser design:
*
* This is pretty standard stuff for the calculator part (read in tokens from
* the lexer, and form a syntax tree using the Node* objects). The unusual
* part is that due to the design of bison, we don't actually return a value
* normally to the calling function.
*
* Instead, we make use of the static Result::setLastResult() call in order
* to notify the calling function of the result of the parse. There are
* different statuses you can set, including Error (with a message), Null
* (which indicates that some action happened that doesn't generate a result),
* and Value (with a Node* that holds the result).
*
* If you are done parsing before reaching the FINAL token, you can call:
* YYACCEPT: Done, parsed successfully.
* YYERROR : Done, there was an error.
*
* Note that if you let the parse bubble back up to FINAL, then the result
* will always be a Value.
*/
FINAL: { gCheckIdents = 1; } S {
Result::setLastResult(NodePtr($2));
$$ = 0;
}
S: EXP { $$ = $1; }
// Rudimentary error handling
S: error '=' {
Result::setLastResult(i18n("This is an invalid assignment."));
YYABORT;
}
// Can't assign to a function.
S: FUNC '=' {
QString s(i18n("You can't assign to function %1").arg($1->name()));
Result::setLastResult(s);
YYABORT;
}
// This is a function prototype. abakus currently only supports one-argument
// functions.
ASSIGN: '(' { --gCheckIdents; } IDENT ')' '=' {
$$ = $3;
}
// Blocking a variable with the name deriv is a slight feature regression
// since normally functions and variables with the same name can coexist, but
// I don't want to duplicate code all over the place.
S: SET DERIV {
QString s(i18n("Function %1 is built-in and cannot be overridden.").arg("deriv"));
Result::setLastResult(s);
YYABORT;
}
S: DERIV '=' {
QString s(i18n("Function %1 is built-in and cannot be overridden.").arg("deriv"));
Result::setLastResult(s);
YYABORT;
}
S: SET FUNC ASSIGN EXP {
++gCheckIdents;
// We're trying to reassign an already defined function, make sure it's
// not a built-in.
QString funcName = $2->name();
QString ident = $3->name();
FunctionManager *manager = FunctionManager::instance();
if(manager->isFunction(funcName) && !manager->isFunctionUserDefined(funcName)) {
QString s(i18n("Function %1 is built-in and cannot be overridden.").arg(funcName));
Result::setLastResult(s);
YYABORT;
}
if(manager->isFunction(funcName))
manager->removeFunction(funcName);
BaseFunction *newFn = new UserDefinedFunction(funcName, $4);
if(!manager->addFunction(newFn, ident)) {
QString s(i18n("Unable to define function %1 because it is recursive.").arg(funcName));
Result::setLastResult(s);
YYABORT;
}
Result::setLastResult(Result::Null);
YYACCEPT;
}
// IDENT is the same as FUNC, except that the lexer has determined that IDENT
// is not already a FUNC.
S: SET IDENT ASSIGN EXP {
++gCheckIdents;
QString funcName = $2->name();
QString ident = $3->name();
// No need to check if the function is already defined, because the
// lexer checked for us before returning the IDENT token.
BaseFunction *newFn = new UserDefinedFunction(funcName, $4);
FunctionManager::instance()->addFunction(newFn, ident);
Result::setLastResult(Result::Null);
YYACCEPT;
}
// Remove a defined function.
S: REMOVE FUNC '(' ')' {
FunctionManager::instance()->removeFunction($2->name());
Result::setLastResult(Result::Null);
YYACCEPT;
}
// Can't remove an ident using remove-func syntax.
S: REMOVE IDENT '(' ')' {
// This is an error
Result::setLastResult(Result(i18n("Function %1 is not defined.").arg($2->name())));
YYABORT;
}
// This happens when the user tries to remove a function that's not defined.
S: REMOVE IDENT '(' IDENT ')' {
// This is an error
Result::setLastResult(Result(i18n("Function %1 is not defined.").arg($2->name())));
YYABORT;
}
S: REMOVE IDENT {
ValueManager *manager = ValueManager::instance();
if(manager->isValueSet($2->name()) && !manager->isValueReadOnly($2->name())) {
manager->removeValue($2->name());
Result::setLastResult(Result::Null);
YYACCEPT;
}
else {
QString s;
if(manager->isValueSet($2->name()))
s = i18n("Can't remove predefined variable %1.").arg($2->name());
else
s = i18n("Can't remove undefined variable %1.").arg($2->name());
Result::setLastResult(s);
YYABORT;
}
}
S: SET IDENT '=' EXP {
ValueManager *vm = ValueManager::instance();
if(vm->isValueReadOnly($2->name())) {
if($2->name() == "pi" && $4->value() == Abakus::number_t("3.0"))
Result::setLastResult(i18n("This isn't Indiana, you can't just change pi"));
else
Result::setLastResult(i18n("%1 is a constant").arg($2->name()));
YYABORT;
}
ValueManager::instance()->setValue($2->name(), $4->value());
Result::setLastResult(Result::Null);
YYACCEPT;
}
// Set a variable.
S: IDENT '=' EXP {
ValueManager *vm = ValueManager::instance();
if(vm->isValueReadOnly($1->name())) {
if($1->name() == "pi" && $3->value() == Abakus::number_t("3.0"))
Result::setLastResult(i18n("This isn't Indiana, you can't just change pi"));
else
Result::setLastResult(i18n("%1 is a constant").arg($1->name()));
YYABORT;
}
ValueManager::instance()->setValue($1->name(), $3->value());
Result::setLastResult(Result::Null);
YYACCEPT;
}
S: NUMBER '=' {
Result::setLastResult(i18n("Can't assign to %1").arg($1->value().toString()));
YYABORT;
}
// Can't call this as a function.
TERM: IDENT '(' {
Result::setLastResult(i18n("%1 isn't a function (or operator expected)").arg($1->name()));
YYABORT;
}
// Can't do this either.
TERM: IDENT IDENT {
Result::setLastResult(i18n("Missing operator"));
YYABORT;
}
TERM: IDENT NUMBER {
Result::setLastResult(i18n("Missing operator"));
YYABORT;
}
TERM: NUMBER NUMBER {
Result::setLastResult(i18n("Missing operator"));
YYABORT;
}
S: error {
Result::setLastResult(i18n("Sorry, I can't figure it out."));
YYABORT;
}
/**
* Here be the standard calculator-parsing part. Nothing here should be too
* fancy.
*/
EXP: EXP '+' FACTOR { $$ = new BinaryOperator(BinaryOperator::Addition, $1, $3); }
EXP: EXP '-' FACTOR { $$ = new BinaryOperator(BinaryOperator::Subtraction, $1, $3); }
EXP: FACTOR { $$ = $1; }
FACTOR: FACTOR '*' EXPONENT { $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $3); }
FACTOR: FACTOR '/' EXPONENT { $$ = new BinaryOperator(BinaryOperator::Division, $1, $3); }
FACTOR: EXPONENT { $$ = $1; }
EXPONENT: TERM POWER EXPONENT { $$ = new BinaryOperator(BinaryOperator::Exponentiation, $1, $3); }
EXPONENT: TERM { $$ = $1; }
TERM: '+' VALUE { $$ = $2; }
TERM: '-' VALUE { $$ = new UnaryOperator(UnaryOperator::Negation, $2); }
TERM: '(' EXP ')' { $$ = $2; }
TERM: '-' '(' EXP ')' { $$ = new UnaryOperator(UnaryOperator::Negation, $3); }
TERM: VALUE { $$ = $1; }
VALUE: NUMBER { $$ = $1; }
NUMBER: NUM {
KLocale *locale = KGlobal::locale();
QChar decimal = locale->decimalSymbol()[0];
// Replace current decimal separator with US Decimal separator to be
// evil.
unsigned len = strlen(yytext);
for(unsigned i = 0; i < len; ++i)
if(yytext[i] == decimal)
yytext[i] = '.';
Abakus::number_t value(yytext);
$$ = new NumericValue(value);
}
TERM: DERIV { --gCheckIdents; } '(' EXP ',' { ++gCheckIdents; } EXP ')' {
$$ = new DerivativeFunction($4, $7);
}
TERM: FUNC TERM {
$1->setOperand($2);
$$ = $1;
}
/* Handle implicit multiplication */
TERM: NUMBER FUNC TERM {
$2->setOperand($3);
$$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $2);
}
TERM: NUMBER '(' EXP ')' {
$$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $3);
}
TERM: NUMBER IDENT {
if(gCheckIdents > 0 && !ValueManager::instance()->isValueSet($2->name())) {
Result::setLastResult(i18n("Unknown variable %1").arg($2->name()));
YYABORT;
}
$$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $2);
}
VALUE: IDENT {
if(gCheckIdents <= 0 || ValueManager::instance()->isValueSet($1->name()))
$$ = $1;
else {
Result::setLastResult(i18n("Unknown variable %1").arg($1->name()));
YYABORT;
}
}
IDENT: ID {
$$ = new Identifier(yytext);
}
FUNC: FN {
/* No check necessary, the lexer has already checked for us. */
$$ = new BuiltinFunction(yytext, 0);
}
%%
int gCheckIdents = 0;
int yyerror(const char *)
{
return 0;
}

@ -0,0 +1,33 @@
/*
* result.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 "result.h"
Result *Result::m_lastResult = new Result;
Result::Result(const QString &message) : m_type(Error), m_message(message)
{
}
Result::Result(NodePtr node) : m_node(node), m_type(Value)
{
}
Result::Result(Type type) : m_type(type)
{
}

@ -0,0 +1,77 @@
#ifndef ABAKUS_RESULT_H
#define ABAKUS_RESULT_H
/*
* result.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 "node.h"
#include "sharedptr.h"
/**
* A conceptual result from an expression parse. Used to determine if the
* parse succeeded or failed. If it succeeded it will have a node value you
* can query as the answer.
*/
class Result
{
public:
typedef enum { Error, Null, Value } Type;
/**
* Default constructor, which constructs a "failed" Result.
*/
Result(const QString &message = "");
/**
* Node constructor, which constructs a "succeeded" result.
*/
Result(NodePtr node);
/**
* Constructor, constructs a "null" result. This means that the
* operation was successful, but did not result in a normal value.
*/
Result(Type type);
bool failed() const { return m_type == Error; }
Type type() const { return m_type; }
QString message() const { return m_message; }
const NodePtr result() const { return m_node; }
NodePtr result() { return m_node; }
static Result *lastResult() { return m_lastResult; }
static void setLastResult(const Result &result)
{
*m_lastResult = result;
}
private:
NodePtr m_node;
Type m_type;
QString m_message;
static Result *m_lastResult;
};
#endif /* ABAKUS_RESULT_H */
// vim: set et ts=8 sw=4:

@ -0,0 +1,149 @@
/*
* resultlistview.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 <kpopupmenu.h>
#include <klocale.h>
#include <qclipboard.h>
#include <qapplication.h>
#include <qevent.h>
#include <qcursor.h>
#include <qdragobject.h>
#include <qheader.h>
#include "resultlistview.h"
#include "resultlistviewtext.h"
#include "dragsupport.h"
using DragSupport::makePixmap;
using namespace ResultList;
ResultListView::ResultListView(QWidget *parent, const char *name) :
KListView(parent, name), m_itemRightClicked(0)
{
connect(this, SIGNAL(doubleClicked(QListViewItem *, const QPoint &, int)),
SLOT(slotDoubleClicked(QListViewItem *, const QPoint &, int)));
addColumn(i18n("Expression"));
addColumn(i18n("Result"));
addColumn(i18n("Shortcut"));
header()->hide(); // I hate that header
header()->setStretchEnabled(ResultColumn, true);
setDragEnabled(true);
setItemMargin(2);
setColumnAlignment(ResultColumn, AlignLeft);
setColumnAlignment(ShortcutColumn, AlignHCenter);
setColumnWidthMode(ResultColumn, Maximum);
setSortColumn(-1);
}
bool ResultListView::getStackValue(unsigned stackPosition, Abakus::number_t &result)
{
QListViewItem *it = firstChild();
for(; it; it = it->itemBelow()) {
ResultListViewText *resultItem = dynamic_cast<ResultListViewText *>(it);
if(!resultItem->wasError() && resultItem->stackPosition() == stackPosition) {
result = Abakus::number_t(resultItem->resultText().latin1());
return true;
}
}
return false;
}
QDragObject *ResultListView::dragObject()
{
QPoint viewportPos = viewport()->mapFromGlobal(QCursor::pos());
ResultListViewText *item = itemUnderCursor();
if(item) {
QString text = item->resultText();
int column = header()->sectionAt(viewportPos.x());
if(column == ExpressionColumn)
text = item->expressionText();
QDragObject *drag = new QTextDrag(text, this);
drag->setPixmap(makePixmap(text, font()));
return drag;
}
return 0;
}
void ResultListView::contextMenuEvent(QContextMenuEvent *e)
{
m_itemRightClicked = itemUnderCursor();
KPopupMenu *menu = constructPopupMenu(m_itemRightClicked);
menu->popup(e->globalPos());
}
void ResultListView::slotDoubleClicked(QListViewItem *item, const QPoint &, int c)
{
ResultListViewText *textItem = dynamic_cast<ResultListViewText *>(item);
if(!textItem)
return;
if(c == ExpressionColumn)
emit signalEntrySelected(textItem->expressionText());
else if(c == ResultColumn)
emit signalResultSelected(textItem->resultText());
}
KPopupMenu *ResultListView::constructPopupMenu(const ResultListViewText *item)
{
KPopupMenu *menu = new KPopupMenu(this, "list view context menu");
menu->insertItem(i18n("Clear &History"), this, SLOT(clear()), ALT+Key_R);
int id = menu->insertItem(i18n("Copy Result to Clipboard"), this, SLOT(slotCopyResult()));
if(!item || item->wasError())
menu->setItemEnabled(id, false);
return menu;
}
void ResultListView::slotCopyResult()
{
if(!m_itemRightClicked)
return;
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(m_itemRightClicked->resultText(), QClipboard::Clipboard);
}
ResultListViewText *ResultListView::lastItem() const
{
return static_cast<ResultListViewText *>(KListView::lastItem());
}
ResultListViewText *ResultListView::itemUnderCursor() const
{
QPoint viewportPos = viewport()->mapFromGlobal(QCursor::pos());
QListViewItem *underCursor = itemAt(viewportPos);
return static_cast<ResultListViewText *>(underCursor);
}
#include "resultlistview.moc"

@ -0,0 +1,64 @@
#ifndef ABAKUS_RESULTLISTVIEW_H
#define ABAKUS_RESULTLISTVIEW_H
/*
* resultlistview.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 QLabel;
class QDragObject;
class ResultListViewText;
namespace ResultList {
enum { ExpressionColumn = 0, ResultColumn, ShortcutColumn };
}
class ResultListView : public KListView
{
Q_OBJECT
public:
ResultListView(QWidget *parent = 0, const char *name = "result list view");
bool getStackValue(unsigned stackPosition, Abakus::number_t &result);
ResultListViewText *lastItem() const;
protected:
virtual void contextMenuEvent(QContextMenuEvent *e);
virtual QDragObject *dragObject();
signals:
void signalEntrySelected(const QString &text);
void signalResultSelected(const QString &text);
private slots:
void slotDoubleClicked(QListViewItem *item, const QPoint & /* Ignored */, int c);
void slotCopyResult();
private:
KPopupMenu *constructPopupMenu(const ResultListViewText *item);
ResultListViewText *itemUnderCursor() const;
ResultListViewText *m_itemRightClicked;
};
#endif

@ -0,0 +1,135 @@
/*
* resultlistviewtext.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 <qregexp.h>
#include <qpainter.h>
#include <qfontmetrics.h>
#include <qfont.h>
#include <qpalette.h>
#include "resultlistviewtext.h"
using namespace ResultList;
ResultListViewText::ResultListViewText(KListView *listView,
const QString &text,
const QString &result,
ResultListViewText *after,
bool isError)
: KListViewItem(listView, after, text, result), m_text(text),
m_result(result), m_wasError(isError), m_stackPosition(0)
{
// This is some kind of non-result answer, don't bother worrying about the
// stack status, it hasn't changed.
}
ResultListViewText::ResultListViewText(KListView *listView,
const QString &text,
const Abakus::number_t &result,
ResultListViewText *after,
bool isError)
: KListViewItem(listView, after, text), m_text(text),
m_result(result.toString()), m_wasError(isError), m_stackPosition(0),
m_value(result)
{
if(after) {
ResultListViewText *item = static_cast<ResultListViewText *>(listView->firstChild());
for (; item && item != this; item = static_cast<ResultListViewText *>(item->itemBelow())) {
if(!item->wasError()) {
item->setStackPosition(item->stackPosition() + 1);
item->repaint();
}
}
}
setStackPosition(0);
// Call this manually to be rid of trailing zeroes.
setText(ResultColumn, m_value.toString());
}
void ResultListViewText::setStackPosition(unsigned pos)
{
setText(ShortcutColumn, QString("$%1").arg(pos));
m_stackPosition = pos;
}
void ResultListViewText::precisionChanged()
{
if(m_wasError)
return;
m_result = m_value.toString();
setText(ResultColumn, m_result);
}
void ResultListViewText::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align)
{
QColorGroup group(cg);
// XXX: The Qt::red may not provide good contrast with weird color schemes.
// If so I apologize.
if(m_wasError && column == ResultColumn)
group.setColor(QColorGroup::Text, m_result == "OK" ? cg.link() : Qt::red);
if(column == ResultColumn) {
QFont f = p->font();
f.setBold(true);
p->setFont(f);
}
if(column == ShortcutColumn) {
QFont f = p->font();
f.setItalic(true);
f.setPointSize(QMIN(f.pointSize() * 9 / 11, 10));
p->setFont(f);
}
KListViewItem::paintCell(p, group, column, width, align);
}
int ResultListViewText::width(const QFontMetrics &fm, const QListView *lv, int c) const
{
// Simulate painting the text to get accurate results.
if(c == ResultColumn) {
QFont f = lv->font();
f.setBold(true);
return KListViewItem::width(QFontMetrics(f), lv, c);
}
if(c == ShortcutColumn) {
QFont f = lv->font();
f.setItalic(true);
f.setPointSize(QMIN(f.pointSize() * 9 / 11, 10));
return KListViewItem::width(QFontMetrics(f), lv, c);
}
return KListViewItem::width(fm, lv, c);
}
void ResultListViewText::setText(int column, const QString &text)
{
if(!m_wasError && column == ResultColumn) {
KListViewItem::setText(column, m_value.toString());
return;
}
KListViewItem::setText(column, text);
}

@ -0,0 +1,70 @@
#ifndef ABAKUS_RESULTLISTVIEWTEXT_H
#define ABAKUS_RESULTLISTVIEWTEXT_H
/*
* resultlistviewtext.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 "resultlistview.h"
#include "numerictypes.h"
class QPainter;
class QColorGroup;
class QFontMetrics;
// This class shows the results shown in the MainWindow result pane.
class ResultListViewText : public KListViewItem
{
public:
ResultListViewText(KListView *listView,
const QString &text,
const QString &result,
ResultListViewText *after,
bool isError = false);
ResultListViewText(KListView *listView,
const QString &text,
const Abakus::number_t &result,
ResultListViewText *after,
bool isError = false);
QString expressionText() const { return m_text; }
QString resultText() const { return m_result; }
bool wasError() const { return m_wasError; }
unsigned stackPosition() const { return m_stackPosition; }
void setStackPosition(unsigned pos);
// Redisplays the text by calling value.toString again.
void precisionChanged();
// Reimplemented from KListViewItem
virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align);
virtual int width(const QFontMetrics &fm, const QListView *lv, int c) const;
// Reimplemented to remove trailing zeroes from results.
virtual void setText(int column, const QString &text);
private:
QString m_text, m_result;
bool m_wasError;
unsigned m_stackPosition;
Abakus::number_t m_value;
};
#endif

@ -0,0 +1,267 @@
/*
* rpnmuncher.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 <math.h>
#include <kdebug.h>
#include <klocale.h>
#include <qvaluestack.h>
#include <qregexp.h>
#include <qstring.h>
#include <qstringlist.h>
#include "rpnmuncher.h"
#include "valuemanager.h"
#include "function.h"
/**
* Holds either a textual identifier, or a numeric value.
*/
class Operand
{
public:
Operand() : m_isValue(true), m_value(0) { }
Operand(const QString &ident) : m_isValue(false), m_text(ident) { }
Operand(Abakus::number_t value) : m_isValue(true), m_value(value) { }
Abakus::number_t value() const
{
if(m_isValue)
return m_value;
return ValueManager::instance()->value(m_text);
}
operator Abakus::number_t() const
{
return value();
}
QString text() const { return m_text; }
private:
bool m_isValue;
QString m_text;
Abakus::number_t m_value;
};
typedef enum { Number = 256, Func, Ident, Power, Set, Remove, Pop, Clear, Unknown } Token;
static int tokenize (const QString &token);
QString RPNParser::m_errorStr;
bool RPNParser::m_error(false);
OperandStack RPNParser::m_stack;
struct Counter
{
~Counter() {
Abakus::number_t count( static_cast<int>(RPNParser::stack().count()) );
ValueManager::instance()->setValue("stackCount", count);
}
};
Abakus::number_t RPNParser::rpnParseString(const QString &text)
{
QStringList tokens = QStringList::split(QRegExp("\\s"), text);
Counter counter; // Will update stack count when we leave proc.
(void) counter; // Avoid warnings about it being unused.
// Used in the case statements below
Operand l, r;
FunctionManager *manager = FunctionManager::instance();
Function *fn = 0;
m_error = false;
m_errorStr = QString::null;
for(QStringList::ConstIterator it = tokens.begin(); it != tokens.end(); ++it) {
switch(tokenize(*it))
{
case Number:
m_stack.push(Abakus::number_t((*it).latin1()));
break;
case Pop:
if(m_stack.isEmpty()) {
m_error = true;
m_errorStr = i18n("Can't pop from an empty stack.");
return Abakus::number_t::nan();
}
m_stack.pop();
break;
case Clear:
m_stack.clear();
break;
case Func:
if(m_stack.count() < 1) {
m_error = true;
m_errorStr = i18n("Insufficient operands for function %1").arg(*it);
return Abakus::number_t::nan();
}
fn = manager->function(*it);
l = m_stack.pop();
m_stack.push(evaluateFunction(fn, l));
break;
case Ident:
m_stack.push(*it);
break;
case Set:
case Remove:
m_error = true;
m_errorStr = i18n("The set and remove commands can only be used in normal mode.");
return Abakus::number_t::nan();
break;
case Power:
if(m_stack.count() < 2) {
m_error = true;
m_errorStr = i18n("Insufficient operands for exponentiation operator.");
return Abakus::number_t::nan();
}
r = m_stack.pop();
l = m_stack.pop();
m_stack.push(l.value().pow(r));
break;
case Unknown:
m_error = true;
m_errorStr = i18n("Unknown token %1").arg(*it);
return Abakus::number_t::nan();
break;
case '=':
r = m_stack.pop();
l = m_stack.pop();
ValueManager::instance()->setValue(l.text(), r);
m_stack.push(l);
break;
case '+':
if(m_stack.count() < 2) {
m_error = true;
m_errorStr = i18n("Insufficient operands for addition operator.");
return Abakus::number_t::nan();
}
r = m_stack.pop();
l = m_stack.pop();
m_stack.push(l.value() + r.value());
break;
case '-':
if(m_stack.count() < 2) {
m_error = true;
m_errorStr = i18n("Insufficient operands for subtraction operator.");
return Abakus::number_t::nan();
}
r = m_stack.pop();
l = m_stack.pop();
m_stack.push(l.value() - r.value());
break;
case '*':
if(m_stack.count() < 2) {
m_error = true;
m_errorStr = i18n("Insufficient operands for multiplication operator.");
return Abakus::number_t::nan();
}
r = m_stack.pop();
l = m_stack.pop();
m_stack.push(l.value() * r.value());
break;
case '/':
if(m_stack.count() < 2) {
m_error = true;
m_errorStr = i18n("Insufficient operands for division operator.");
return Abakus::number_t::nan();
}
r = m_stack.pop();
l = m_stack.pop();
m_stack.push(l.value() / r.value());
break;
default:
// Impossible case happened.
kdError() << "Impossible case happened in " << k_funcinfo << endl;
m_error = true;
m_errorStr = "Bug found in program, please report.";
return Abakus::number_t::nan();
}
}
// TODO: Should this be an error?
if(m_stack.isEmpty())
return Abakus::number_t::nan();
return m_stack.top();
}
static int tokenize (const QString &token)
{
bool isOK;
token.toDouble(&isOK);
if(isOK)
return Number;
if(token == "**" || token == "^")
return Power;
if(FunctionManager::instance()->isFunction(token))
return Func;
if(token.lower() == "set")
return Set;
if(token.lower() == "pop")
return Pop;
if(token.lower() == "clear")
return Clear;
if(token.lower() == "remove")
return Remove;
if(QRegExp("^\\w+$").search(token) != -1 &&
QRegExp("\\d").search(token) == -1)
{
return Ident;
}
if(QRegExp("^[-+*/=]$").search(token) != -1)
return token[0];
return Unknown;
}
// vim: set et sw=4 ts=8:

@ -0,0 +1,44 @@
#ifndef ABAKUS_RPNMUNCHER_H
#define ABAKUS_RPNMUNCHER_H
/*
* rpnmuncher.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 QString;
class Operand;
template<class T> class QValueStack;
typedef QValueStack<Operand> OperandStack;
#include "numerictypes.h"
class RPNParser
{
public:
static Abakus::number_t rpnParseString(const QString &text);
static bool wasError() { return m_error; }
static QString errorString() { return m_errorStr; }
static OperandStack &stack() { return m_stack; }
private:
static QString m_errorStr;
static bool m_error;
static OperandStack m_stack;
};
#endif

@ -0,0 +1,122 @@
#ifndef ABAKUS_SHARED_PTR_H
#define ABAKUS_SHARED_PTR_H
/*
* sharedptr.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>
template <class T>
class SharedPtr
{
public:
typedef T value_type;
class Data
{
public:
Data(T *ptr = 0, unsigned refCount = 1) : m_ptr(ptr), m_refCount(refCount)
{
}
void deref()
{
--m_refCount;
if(!m_refCount) {
delete m_ptr;
m_ptr = 0;
}
}
void ref()
{
++m_refCount;
}
T *ptr() { return m_ptr; }
const T* ptr() const { return m_ptr; }
unsigned refCount() const { return m_refCount; }
private:
T *m_ptr;
unsigned m_refCount;
};
SharedPtr() : m_data(new Data)
{
}
SharedPtr(T* ptr) : m_data(new Data(ptr))
{
}
SharedPtr(const SharedPtr<T> &other) : m_data(other.m_data)
{
m_data->ref();
}
~SharedPtr()
{
m_data->deref();
}
SharedPtr<T> &operator=(const SharedPtr<T> &other)
{
if(&other == this)
return *this;
m_data->deref();
m_data = other.m_data;
m_data->ref();
return *this;
}
T *operator ->()
{
return m_data->ptr();
}
const T *operator ->() const
{
return m_data->ptr();
}
T &operator *()
{
return *m_data->ptr();
}
const T& operator *() const
{
return *m_data->ptr();
}
bool isNull() const { return m_data->ptr() == 0; }
unsigned refCount() const { return m_data->refCount(); }
private:
Data *m_data;
};
#endif /* ABAKUS_SHARED_PTR_H */
// vim: set et ts=8 sw=4:

@ -0,0 +1,105 @@
/*
* valuemanager.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 <klocale.h>
#include <qregexp.h>
#include "numerictypes.h"
#include "valuemanager.h"
ValueManager *ValueManager::m_manager = 0;
ValueManager *ValueManager::instance()
{
if(!m_manager)
m_manager = new ValueManager;
return m_manager;
}
ValueManager::ValueManager(QObject *parent, const char *name) :
QObject(parent, name)
{
m_values.insert("pi", Abakus::number_t::PI);
m_values.insert("e", Abakus::number_t::E);
}
Abakus::number_t ValueManager::value(const QString &name) const
{
return m_values[name];
}
bool ValueManager::isValueSet(const QString &name) const
{
return m_values.contains(name);
}
bool ValueManager::isValueReadOnly(const QString &name) const
{
QRegExp readOnlyValues("^(ans|pi|e|stackCount)$");
return name.find(readOnlyValues) != -1;
}
void ValueManager::setValue(const QString &name, const Abakus::number_t value)
{
if(m_values.contains(name) && this->value(name) != value)
emit signalValueChanged(name, value);
else if(!m_values.contains(name))
emit signalValueAdded(name, value);
m_values.replace(name, value);
}
void ValueManager::removeValue(const QString &name)
{
if(m_values.contains(name))
emit signalValueRemoved(name);
m_values.remove(name);
}
void ValueManager::slotRemoveUserVariables()
{
QStringList vars = valueNames();
for(QStringList::ConstIterator var = vars.constBegin(); var != vars.constEnd(); ++var)
if(!isValueReadOnly(*var))
removeValue(*var);
}
QStringList ValueManager::valueNames() const
{
return m_values.keys();
}
QString ValueManager::description(const QString &valueName)
{
if(valueName == "e")
return i18n("Natural exponential base - 2.7182818");
if(valueName == "pi")
return i18n("pi (π) - 3.1415926");
return QString();
}
#include "valuemanager.moc"
// vim: set et ts=8 sw=4 encoding=utf-8:

@ -0,0 +1,69 @@
#ifndef ABAKUS_VALUEMANAGER_H
#define ABAKUS_VALUEMANAGER_H
/*
* valuemanager.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 <qobject.h>
#include <qmap.h>
#include <qstring.h>
#include <qstringlist.h>
#include "numerictypes.h"
class ValueManager : public QObject
{
Q_OBJECT
public:
typedef QMap<QString, Abakus::number_t> valueMap;
static ValueManager *instance();
Abakus::number_t value(const QString &name) const;
bool isValueSet(const QString &name) const;
bool isValueReadOnly(const QString &name) const;
void setValue(const QString &name, const Abakus::number_t value);
void removeValue(const QString &name);
QStringList valueNames() const;
/**
* Returns a textual description of a constant built-into abakus.
*/
static QString description(const QString &valueName);
signals:
void signalValueAdded(const QString &name, Abakus::number_t value);
void signalValueRemoved(const QString &name);
void signalValueChanged(const QString &name, Abakus::number_t newValue);
public slots:
void slotRemoveUserVariables();
private:
ValueManager(QObject *parent = 0, const char *name = "value manager");
static ValueManager *m_manager;
valueMap m_values;
};
#endif
// vim: set et sw=4 ts=8:
Loading…
Cancel
Save