git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/abakus@1071969 283d02a7-25f6-0310-bc7c-ecb5cbfe19dav3.5.13-sru
@ -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
|
||||
|
@ -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 */
|
@ -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)
|
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 30 KiB |
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 (π) 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 * π 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>
|
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 27 KiB |
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>&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>&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( ¶, &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( ¶, &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( ¶, &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( ¶, &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( ¶, &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( ¶, &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
|
After Width: | Height: | Size: 6.9 KiB |
@ -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:
|
@ -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:
|