Add abakus
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;
|
||||
|
||||