diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | ce599e4f9f94b4eb00c1b5edb85bce5431ab3df2 (patch) | |
tree | d3bb9f5d25a2dc09ca81adecf39621d871534297 /kturtle/src | |
download | tdeedu-ce599e4f9f94b4eb00c1b5edb85bce5431ab3df2.tar.gz tdeedu-ce599e4f9f94b4eb00c1b5edb85bce5431ab3df2.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdeedu@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kturtle/src')
66 files changed, 7395 insertions, 0 deletions
diff --git a/kturtle/src/Makefile.am b/kturtle/src/Makefile.am new file mode 100644 index 00000000..7ed55e4d --- /dev/null +++ b/kturtle/src/Makefile.am @@ -0,0 +1,37 @@ +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) + +SUBDIRS = pics + +# added from kjots +AM_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +kturtle_LDFLAGS = $(KDE_RPATH) $(all_libraries) +kturtle_LDADD = $(LIB_KDEUI) $(LIB_KIO) $(LIB_KDEPRINT) $(LIB_KPARTS) -lkatepartinterfaces + +KDE_ICON = kturtle + +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = kturtle + +# the application source, library search path, and link libraries +kturtle_SOURCES = main.cpp kturtle.cpp canvas.cpp dialogs.cpp value.cpp lexer.cpp parser.cpp executer.cpp treenode.cpp token.cpp translate.cpp settings.kcfgc + +# let automoc handle all the meta source files (moc) +kturtle_METASOURCES = AUTO + +# these are the headers for your project +noinst_HEADERS = kturtle.h canvas.h dialogs.cpp value.h lexer.h parser.h executer.h treenode.h token.h translate.h + +kde_kcfg_DATA = kturtle.kcfg + +# this is where the shell's XML-GUI resource file goes +shellrcdir = $(kde_datadir)/kturtle +shellrc_DATA = kturtleui.rc + +# place KTurtle in the start menu under Miscellanous +xdg_apps_DATA = kturtle.desktop + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kturtle.pot diff --git a/kturtle/src/canvas.cpp b/kturtle/src/canvas.cpp new file mode 100644 index 00000000..871c1614 --- /dev/null +++ b/kturtle/src/canvas.cpp @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2003 Cies Breijs <cies # kde ! nl> + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + + +// Note on this file: +// It contains 200 lines of code just to make sure very long line are drawn correctly +// till a certain extent... Beyond that extent the code just cuts the crap, since use user +// it then probably not doing anything usefull anymore; so he she will not notice the code +// is cheating a bit in order to prevent CPU hogging. +// If anyone has a good fix for this problem, than please test it with these logo scripts: + +// # bastard script 1 +// reset +// canvassize 350,348 +// center +// for x = 1 to 255 [ +// fw x +// tr x / 65 +// ] + +// # bastard script 2 +// reset +// canvassize 350,350 +// center +// for x = 1 to 255 [ +// fw x*x +// tr x +// ] + +// Thanks for looking at the code of KTurtle! + +// Updated 21.10.2004 by Kiril Jovchev +// -- Changed that position is kept in double values. +// This makes accuracy higher. + + +// BEGIN includes, defines and constants + +#include <cmath> + +#include <qpainter.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> + +#include "settings.h" +#include "canvas.h" + + +// this function is used in executer and canvas: +#define ROUND2INT(x) ( (x) >= 0 ? (int)( (x) + .5 ) : (int)( (x) - .5 ) ) + +const double PI = 3.14159265358979323846; +const double DEG2RAD = 3.14159265358979323846/180; + +// END + + + +// BEGIN public methods + +Canvas::Canvas(QWidget *parent, const char *name) : QCanvasView(0, parent, name) +{ + // Create a new canvas for this view + canvas = new QCanvas(parent); + canvas->setAdvancePeriod(250); // refresh-rate in [ms] + + // set initial values + initValues(); + + // at last we assign the canvas to the view + setCanvas(canvas); +} + +Canvas::~Canvas() +{ + delete sprite; + delete spriteFrames; +} + + +QPixmap* Canvas::canvas2Pixmap() +{ + pixmap = QPixmap( canvas->width(), canvas->height() ); + QPainter painter(&pixmap); + canvas->drawArea(canvas->rect(), &painter); + return &pixmap; +} + +// END + + + +// BEGIN public slots + +void Canvas::slotClear() +{ + QCanvasItemList list = canvas->allItems(); + QCanvasItemList::Iterator it = list.begin(); + for (; it != list.end(); ++it) + { + // kill everything but the turtle (who lives on a seperate layer) + if ( *it && !( (*it)->z() == 250 ) ) delete *it; + } +} + +void Canvas::slotClearSpriteToo() +{ + QCanvasItemList list = canvas->allItems(); + QCanvasItemList::Iterator it = list.begin(); + for (; it != list.end(); ++it) + { + if (*it) delete *it; + } +} + + +void Canvas::slotGo(double x, double y) +{ + int intX = ROUND2INT(x); + int intY = ROUND2INT(y); + + if ( wrap && !canvas->onCanvas(intX, intY) ) + { + QPoint offsetPoint = offset(intX, intY); + posX = x - ( offsetPoint.x() * canvasWidth ); + posY = y - ( offsetPoint.y() * canvasHeight ); + } + else + { + posX = x; + posY = y; + } + updateSpritePos(); +} + +void Canvas::slotGoX(double x) +{ + int intX = ROUND2INT(x); + int intPosY = ROUND2INT(posY); + if ( wrap && !canvas->onCanvas(intX, intPosY) ) + { + QPoint offsetPoint = offset(intX, intPosY); + posX = x - ( offsetPoint.x() * canvasWidth ); + } + else posX = x; + updateSpritePos(); +} + +void Canvas::slotGoY(double y) +{ + int intY = ROUND2INT(y); + int intPosX = ROUND2INT(posX); + if ( wrap && !canvas->onCanvas(intPosX, intY) ) + { + QPoint offsetPoint = offset(intPosX, intY); + posY = y - ( offsetPoint.y() * canvasHeight ); + } + else posY = y; + updateSpritePos(); +} + +void Canvas::slotForward(double x) +{ + double posXnew = posX + ( x * sin(direction * DEG2RAD) ); + double posYnew = posY - ( x * cos(direction * DEG2RAD) ); + if (pen) lineShell(posX, posY, posXnew, posYnew); + slotGo(posXnew, posYnew); +} + +void Canvas::slotBackward(double x) +{ + double posXnew = posX - ( x * sin(direction * DEG2RAD) ); + double posYnew = posY + ( x * cos(direction * DEG2RAD) ); + if (pen) lineShell(posX, posY, posXnew, posYnew); + slotGo(posXnew, posYnew); +} + +void Canvas::slotDirection(double deg) +{ + direction = deg; + updateSpriteAngle(); +} + +void Canvas::slotTurnLeft(double deg) +{ + direction = direction - deg; + updateSpriteAngle(); +} + +void Canvas::slotTurnRight(double deg) +{ + direction = direction + deg; + updateSpriteAngle(); +} + +void Canvas::slotCenter() +{ + posX = canvasWidth / 2; + posY = canvasHeight / 2; + updateSpritePos(); +} + +void Canvas::slotSetPenWidth(int w) +{ + if (w == 1) penWidth = 0; // 0 gives 1 pixel lines using fast algorithem + else penWidth = w; +} + +void Canvas::slotPenUp() +{ + pen = false; +} + +void Canvas::slotPenDown() +{ + pen = true; +} + +void Canvas::slotSetFgColor(int r, int g, int b) +{ + // shouldn't it be checked if: ( 0 =< r, g, b =< 255) ? + fgR = r; + fgG = g; + fgB = b; +} + +void Canvas::slotSetBgColor(int r, int g, int b) +{ + canvas->setBackgroundColor( QColor(r, g, b) ); +} + +void Canvas::slotResizeCanvas(int x, int y) +{ + if (x <= 0 || y <= 0) + { + // TODO put error message + x = 100; + y = 100; + } + canvasWidth = x; + canvasHeight = y; + canvas->resize(x, y); + emit CanvasResized(); +} + + +// I'm having major problems with the canvas and qt-3.2 +// qt-3.3 will fix it and the supposed fix is allready in qt-copy +// i'll not work any further on sprites, while i dont have qt-3.3 or a fresh qt-copy + +void Canvas::slotSpriteShow() +{ + sprite->show(); +} + +void Canvas::slotSpriteHide() +{ + sprite->hide(); +} + +void Canvas::slotSpritePress() +{ +} + +void Canvas::slotSpriteChange(int x) +{ + sprite->setFrame(x); + sprite->move(ROUND2INT(posX - sprite->width()/2), ROUND2INT(posY - sprite->height()/2)); +} + +void Canvas::slotPrint(QString text) +{ + QCanvasText* t = new QCanvasText(text, font, canvas); + // text does not do the wrapping, never... sorry + t->setColor( QColor(fgR, fgG, fgB) ); + t->move(ROUND2INT(posX), ROUND2INT(posY)); + t->show(); +} + +void Canvas::slotFontType(QString family, QString extra) +{ + font.setFamily(family); + font.setBold( extra.contains("bold") > 0 ); + font.setItalic( extra.contains("italic") > 0 ); + font.setUnderline( extra.contains("underline") > 0 ); + font.setOverline( extra.contains("overline") > 0 ); + font.setStrikeOut( extra.contains("strikeout") > 0 ); +} + +void Canvas::slotFontSize(int px) +{ + font.setPixelSize(px); +} + +void Canvas::slotWrapOn() +{ + wrap = true; +} + +void Canvas::slotWrapOff() +{ + wrap = false; +} + +void Canvas::slotReset() +{ + slotClearSpriteToo(); + initValues(); +} + +// END + + + +// BEGIN private methods + +void Canvas::initValues() +{ + // canvas size + slotResizeCanvas( Settings::canvasWidth(), Settings::canvasHeight() ); + canvasWidth = Settings::canvasWidth(); + canvasHeight = Settings::canvasHeight(); + // colors + canvas->setBackgroundColor( QColor(255, 255, 255) ); // background + fgR = 0; // pencolor (forground) + fgG = 0; + fgB = 0; + // pen, wrap, direction and font + penWidth = 0; + pen = true; + wrap = true; + direction = 0; + font = QFont("serif", 18); + // the position + posX = canvasWidth / 2; + posY = canvasHeight / 2; + // construct the default sprite + loadSpriteFrames("turtle"); + updateSpritePos(); + updateSpriteAngle(); + sprite->show(); +} + + +void Canvas::lineShell(double xa, double ya, double xb, double yb) +{ + // line can fallback into this function in case of cutLoop == true + cutLoop = false; + // Reset the loop detection memory + prevStartPos3 = prevStartPos2 = prevStartPos1 = prevEndPos3 = prevEndPos2 = prevEndPos1 = QPoint(0, 0); + // and go! + line(xa, ya, xb, yb); +} + +void Canvas::line(double xa, double ya, double xb, double yb) +{ + QCanvasLine* l = new QCanvasLine(canvas); + int intXa = ROUND2INT(xa); + int intYa = ROUND2INT(ya); + int intXb = ROUND2INT(xb); + int intYb = ROUND2INT(yb); + l->setPoints(intXa, intYa, intXb, intYb); + l->setPen( QPen(QColor(fgR, fgG, fgB), penWidth, SolidLine) ); + l->setZ(1); + l->show(); + // kdDebug(0)<<"Canvas::line(); xa:"<<xa<<", ya:"<<ya<<", xb:"<<xb<<", yb:"<<yb<<endl; + if ( wrap && !canvas->onCanvas( ROUND2INT(xb), ROUND2INT(yb) ) ) + { + if (endlessLoop( QPoint(intXa, intYa), QPoint(intXb, intYb) ) == true) // detect for endless loop + { + slotCenter(); + kdDebug(0)<<"Canvas::line(): ENDLESS LOOP DETECTED, BROKE THE LOOP"<<endl; + cutLoop = true; + return; + } + + QPoint translation = translationFactor(xa, ya, xb, yb); + if (translation == QPoint(0, 0) ) + { + // this could never happen in theory + kdDebug(0)<<"Canvas::line(): ***********ERRORRR***********"<<endl; + return; + } + // kdDebug(0)<<"Canvas::line(); translate by: <<tranlation<<endl; + line (xa + translation.x() * canvasWidth, ya + translation.y() * canvasHeight, + xb + translation.x() * canvasWidth, yb + translation.y() * canvasHeight); + if (cutLoop == true) + { + // kdDebug(0)<<"Canvas::line(): cutLoop is set to TRUE! ABORT LINE MISSION"<<endl; + return; + } + } +} + +bool Canvas::endlessLoop(QPoint begin, QPoint end) +{ + // kdDebug(0)<<"prevStartPos3: "<<prevStartPos3<<", prevStartPos2: "<<prevStartPos2<<", prevStartPos1: "<<prevStartPos1<<", prevStartPos0: "<<begin<<", prevEndPos3: "<<prevEndPos3<<", prevEndPos2: "<<prevEndPos2<<", prevEndPos1: "<<prevEndPos1<<", prevEndPos0: "<<end<<endl; + if ( prevStartPos2 == begin && prevStartPos3 == prevStartPos1 && + prevEndPos2 == end && prevEndPos3 == prevEndPos1 ) + { + // this is to break the horrible endless loop bug that i cannot fix... + // i need more simple reproductions of this bug to really find it + // for now i say it is something with QCanvas but i'm likely wrong on thisone + // kdDebug(0)<<"Canvas::endlessLoop TRUE!!"<<endl; + return true; + } + else + { + // kdDebug(0)<<"Canvas::endlessLoop FASLE!!"<<endl; + prevStartPos3 = prevStartPos2; + prevStartPos2 = prevStartPos1; + prevStartPos1 = begin; + prevEndPos3 = prevEndPos2; + prevEndPos2 = prevEndPos1; + prevEndPos1 = end; + return false; + } + return false; // fallback will not be used +} + +bool Canvas::pointInRange(double px, double py, double xa, double ya, double xb, double yb) +{ + if ( ( ( px >= xa && px <= xb ) || ( px <= xa && px >= xb ) ) && + ( ( py >= ya && py <= yb ) || ( py <= ya && py >= yb ) ) ) return true; + return false; +} + +QPoint Canvas::offset(int x, int y) +{ + // This funktion makes is easy to read since deviding int's is a weird thing: + // int x = 5 / 2, outputs: x = 2, with: 5 % 2 = 1 (the rest value) + if (x < 0) x = x - canvasWidth; + if (y < 0) y = y - canvasHeight; + QPoint offsetPoint( x / canvasWidth, y / canvasHeight); + return offsetPoint; +} + +QPoint Canvas::translationFactor(double xa, double ya, double xb, double yb) +{ + // this class returns a QPoint which can be used to properly 'wrap' a line + QPoint crossPoint[4]; // under wicked circumstances we can need this + // namely when crossing both corners, we have 4 bordercrossings + QPoint translate[4]; + int i = 0; + if ( (xb - xa) > -0.00001 && (xb - xa) < 0.00001 ) // check for an infinite tangent (direction coefficient) + { + i++; + translate[i] = QPoint( 0, 1 ); + crossPoint[i] = QPoint( ROUND2INT(xa), 0 ); + i++; + translate[i] = QPoint(0,-1 ); + crossPoint[i] = QPoint( ROUND2INT(xa), canvasHeight ); + } + else + { + // Here we find out what crossing points the line has with canvas border lines (lines are ENDLESS here) + + // f(t) == (yb - ya) / (xb - xa) * t + ya - (A * xa) == A*t + B + double A = (yb - ya) / (xb - xa); + double B = ya - (A * xa); + + double x_sT = -B / A; // A * x_sT + B == 0 => x_sT == -B / A + double x_sB = ( (double)(canvasHeight) - B ) / A; // A * x_sB + B == CH => x_sB == (CH - B) / A + double y_sL = B; // A * 0 + B == y_sL => y_sL == B + double y_sR = ( A * (double)(canvasWidth) ) + B; // A * CW + B == y_sR + // kdDebug(0)<<"Canvas::translationFactor; rc:"<<A<<", xTop:"<<x_sT<<", xBot:"<<x_sB<<", yLft:"<<y_sL<<", yRft:"<<y_sR<<". "<<endl; + + // Here we find out what crossing points are on the borders AND on the linePIECES + // pointInRange only checks wether the crossing point of the ENDLESS line is on the line PIECE. + if ( 0 <= x_sT && x_sT <= canvasWidth && pointInRange(x_sT, 0, xa, ya, xb, yb) ) + { + i++; + translate[i] = QPoint( 0, 1 ); + crossPoint[i] = QPoint( ROUND2INT(x_sT), 0 ); + } + if ( 0 <= x_sB && x_sB <= canvasWidth && pointInRange(x_sB, canvasHeight, xa, ya, xb, yb) ) + { + i++; + translate[i] = QPoint( 0,-1 ); + crossPoint[i] = QPoint( ROUND2INT(x_sB), ROUND2INT(canvasHeight) ); + } + if ( 0 <= y_sL && y_sL <= canvasHeight && pointInRange(0, y_sL, xa, ya, xb, yb) ) + { + i++; + translate[i] = QPoint( 1, 0 ); + crossPoint[i] = QPoint( 0, ROUND2INT(y_sL) ); + } + if ( 0 <= y_sR && y_sR <= canvasHeight && pointInRange(canvasWidth, y_sR, xa, ya, xb, yb) ) + { + i++; + translate[i] = QPoint(-1, 0 ); + crossPoint[i] = QPoint( ROUND2INT(canvasWidth), ROUND2INT(y_sR) ); + } + + if ( i == 0 ) + { + // kdDebug(0)<<"Canvas::translationFactor: FIRST NO BORDER CROSSINGS DETECTED"<<endl; + QPoint returnValue = QPoint(0, 0); // initiate the returnValue + // Here a fallback if the line has no crossings points with any borders. + // This mostly happens because of unlucky rounding, when this happens the line is nearly + // crossing a corner of the canvas. + // This code make sure the line is tranlated back onto the canvas. + // The -2 and +2 was just something i learnt from examples... I HAVE NO PROOF FOR THIS! + // This, luckily, allmost never happens. + if ( -2 <= x_sT && x_sT <= (canvasWidth + 2) && pointInRange(x_sT, 0, xa, ya, xb, yb) ) + { + returnValue = returnValue + QPoint(0, 1); + } + if ( -2 <= x_sB && x_sB <= (canvasWidth + 2) && pointInRange(x_sB, canvasHeight, xa, ya, xb, yb) ) + { + returnValue = returnValue + QPoint(0,-1); + } + if ( -2 <= y_sL && y_sL <= (canvasHeight + 2) && pointInRange(0, y_sL, xa, ya, xb, yb) ) + { + returnValue = returnValue + QPoint(1, 0); + } + if ( -2 <= y_sR && y_sR <= (canvasHeight + 2) && pointInRange(canvasWidth, y_sR, xa, ya, xb, yb) ) + { + returnValue = returnValue + QPoint(-1, 0); + } + + if ( returnValue == QPoint(0, 0) ) + { + // kdDebug(0)<<"Canvas::translationFactor: *****This shouldn't happen (1) *****"<<endl; + // and this doesnt happen, that why +3 and -3 are ok values and the code above works. + } + return returnValue; + } + } + + QPoint returnValue = QPoint(0, 0); // a new returnValue QPoint gets inited + if (i == 1) + { + // only one border crossing, this is normal when the start point + // is within the canvas and no corners are crossed + // kdDebug(0)<<"***only one border crossing!"<<endl; + return translate[1]; + } + if (i > 1) + { + // more than one border crossing starting point if of the canvas + // we now have to find out which crossing occurs 'first' to know how to translate the line + // NOTE2SELF: the line does NOT have to start on the canvas!! + QPoint endPos( ROUND2INT(xb), ROUND2INT(yb)); + int smallestSize = ( QPoint( ROUND2INT(xa), ROUND2INT(ya) ) - endPos ).manhattanLength(); + // smallestSize is initiated to the total size of the line + for (int ii = 1; ii <= i; ii++) + { + int testSize = ( crossPoint[ii] - endPos ).manhattanLength(); // size till the crosspoint + if (testSize < smallestSize) // if testSize is smaller then... + { + smallestSize = testSize; // ...it becomes smallestSize + returnValue = translate[ii]; + // and the returnValue is updated to the corresponing translaton factors + // kdDebug(0)<<"Canvas::translationFactor: UPDATED"<<endl; + } + else if (testSize == smallestSize) // this only happens on corners + { + // kdDebug(0)<<"Canvas::translationFactor: CORNER EXCEPTION"<<endl; + returnValue = QPoint(0, 0); + if (xb < 0) returnValue = returnValue + QPoint( 1, 0); + else if (xb > canvasWidth) returnValue = returnValue + QPoint(-1, 0); + + if (yb < 0) returnValue = returnValue + QPoint( 0, 1); + else if (yb > canvasHeight) returnValue = returnValue + QPoint( 0,-1); + + return returnValue; + } + } + // kdDebug(0)<<"Canvas::translationFactor: NOT RETURNED YET SO DOING IT NOW"<<endl; + return returnValue; + } + // kdDebug(0)<<"Canvas::translationFactor: *****This shouldn't happen (3) *****"<<endl; + return returnValue; +} + + + +// Sprite related methods: + +void Canvas::loadSpriteFrames(QString name) +{ + // read the pixmaps name.0001.png, name.0002.png, ..., name.0035.png: the different rotations + // #0000 for 0 or 360, #0001 for 10, #0002 for 20, ..., #0018 for 180, etc. + + // WARNING if the dir doesnt exists the app will crash!!! + // This will be fixed in qt3.3 and in the current qt-copy + QPixmap turtlePix = QPixmap(locate("data","kturtle/pics/turtle.0000.png") ); + if ( turtlePix.isNull() ) + { + KMessageBox::sorry( this, + i18n("The turtle picture could not be found. " + "Please check your installation."), i18n("Error") ); + return; + } + QString spritePath = locate("data","kturtle/pics/"+name+".0000.png"); + spritePath.remove(".0000.png"); + spriteFrames = new QCanvasPixmapArray(spritePath+".%1.png", 36); + sprite = new QCanvasSprite(spriteFrames, canvas); + sprite->setZ(250); +} + +void Canvas::updateSpritePos() +{ + sprite->move( posX - ( sprite->width() / 2 ), posY - ( sprite->height() / 2 ), -1 ); +} + +void Canvas::updateSpriteAngle() +{ + // get the direction back on the 1st circle + while (direction < 0 || direction >= 360) + { + if (direction >= 360) direction = direction - 360; + if (direction < 0) direction = direction + 360; + } + // convert to degrees, fix the direction, divide by 10 (for picnr), and round + int i = (int) ( direction / 10 ); + // kdDebug(0)<<"########## direction = "<<direction<<"; int i = "<< i << endl; + // int i = (int) ( ( ( (-direction * 180) / PI ) / 10) + 0.000000001 ); + sprite->setFrame(i); + updateSpritePos(); // pixmaps of different rotations have different sizes, so refresh +} + +// END + + +#include "canvas.moc" diff --git a/kturtle/src/canvas.h b/kturtle/src/canvas.h new file mode 100644 index 00000000..349e610d --- /dev/null +++ b/kturtle/src/canvas.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2003 Cies Breijs <cies # kde ! nl> + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef _CANVAS_H_ +#define _CANVAS_H_ + +#include <qcanvas.h> + + +class Canvas : public QCanvasView +{ + Q_OBJECT + + public: + Canvas(QWidget *parent = 0, const char *name = 0); + ~Canvas(); + + QPixmap* canvas2Pixmap(); + + + public slots: + void slotClear(); + void slotClearSpriteToo(); + void slotGo(double x, double y); + void slotGoX(double x); + void slotGoY(double y); + void slotForward(double x); + void slotBackward(double x); + void slotDirection(double deg); + void slotTurnLeft(double deg); + void slotTurnRight(double deg); + void slotCenter(); + void slotSetPenWidth(int w); + void slotPenUp(); + void slotPenDown(); + void slotSetFgColor(int r, int g, int b); + void slotSetBgColor(int r, int g, int b); + void slotResizeCanvas(int x, int y); + void slotSpriteShow(); + void slotSpriteHide(); + void slotSpritePress(); + void slotSpriteChange(int x); + + void slotPrint(QString text); + void slotFontType(QString family, QString extra); + void slotFontSize(int px); + void slotWrapOn(); + void slotWrapOff(); + void slotReset(); + + + signals: + void CanvasResized(); + + + private: + void initValues(); + + void line(double xa, double ya, double xb, double yb); + void lineShell(double xa, double ya, double xb, double yb); + bool endlessLoop(QPoint begin, QPoint end); + bool pointInRange(double px, double py, double xa, double ya, double xb, double yb); + QPoint offset(int x, int y); + QPoint translationFactor(double xa, double ya, double xb, double yb); + + void loadSpriteFrames(QString name); + void updateSpritePos(); + void updateSpriteAngle(); + + QCanvas *canvas; + QPixmap pixmap; + QCanvasSprite *sprite; + QCanvasPixmapArray *spriteFrames; + QFont font; + double posX, posY; + int canvasWidth, canvasHeight; + int penWidth; + double direction; + int fgR, fgG, fgB; + bool pen; + bool wrap; + bool cutLoop; + QPoint prevStartPos3, prevStartPos2, prevStartPos1, prevEndPos3, prevEndPos2, prevEndPos1; +}; + +#endif // _CANVAS_H_ diff --git a/kturtle/src/dialogs.cpp b/kturtle/src/dialogs.cpp new file mode 100644 index 00000000..044f36f4 --- /dev/null +++ b/kturtle/src/dialogs.cpp @@ -0,0 +1,320 @@ +/* + KTurtle, Copyright (C) 2003-04 Cies Breijs <cies # kde ! nl> + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> + +#include "dialogs.h" + + +// BEGIN class ErrorMessage dialog + +ErrorMessage::ErrorMessage (QWidget *parent) + : KDialogBase (parent, "errorDialog", false, 0, KDialogBase::Close | KDialogBase::Help | KDialogBase::User1, KDialogBase::Close, true, i18n("Help on &Error") ) +{ + connect( this, SIGNAL( user1Clicked() ), this, SLOT( showHelpOnError() ) ); + connect( this, SIGNAL( helpClicked() ), this, SLOT( errorMessageHelp() ) ); + setCaption( i18n("Error Dialog") ); + setButtonWhatsThis( KDialogBase::Close, i18n("Closes this error dialog") ); + setButtonWhatsThis( KDialogBase::Help, i18n("Click here to read more on this error dialog in KTurtle's Handbook.") ); + setButtonTip( KDialogBase::Help, i18n("Click here for help using this error dialog") ); + setButtonWhatsThis( KDialogBase::User1, i18n("Click here for help regarding the error you selected in the list. This button will not work when no error is selected.") ); + setButtonTip( KDialogBase::User1, i18n("Click here for help regarding the error you selected.") ); + + QWidget *baseWidget = new QWidget(this); + setMainWidget(baseWidget); + baseLayout = new QVBoxLayout(baseWidget); + + label = new QLabel(baseWidget); + label->setText( i18n("In this list you find the error(s) that resulted from running your Logo code. \nGood luck!") ); + // \nYou can select an error and click the 'Help on Error' button for help. + label->setScaledContents(true); + baseLayout->addWidget(label); + + spacer = new QSpacerItem( 10, 10, QSizePolicy::Minimum, QSizePolicy::Fixed ); + baseLayout->addItem(spacer); + + errTable = new QTable(0, 3, baseWidget); + errTable->setSelectionMode(QTable::SingleRow); + errTable->setReadOnly(true); + errTable->setShowGrid(false); + errTable->setFocusStyle(QTable::FollowStyle); + errTable->setLeftMargin(0); + + errTable->horizontalHeader()->setLabel( 0, i18n("number") ); + errTable->hideColumn(0); // needed to link with the errorData which stores the tokens, codes, etc. + + errTable->horizontalHeader()->setLabel( 1, i18n("line") ); + errTable->setColumnWidth(1, baseWidget->fontMetrics().width("88888") ); + + errTable->horizontalHeader()->setLabel( 2, i18n("description") ); + errTable->setColumnStretchable(2, true); + + baseLayout->addWidget(errTable); + + // Since both help buttons are not working yet. Doc need a section on the Error Dialog! + enableButton(KDialogBase::Help, false); + enableButton(KDialogBase::User1, false); + + errCount = 1; +} + + +void ErrorMessage::slotAddError(Token& t, const QString& s, uint c) +{ + errorData err; + err.code = c; + err.tok = t; + err.msg = s; + errList.append(err); + + Token currentToken = err.tok; kdDebug(0)<<"ErrorMessage::slotAddError(); >> "<<err.msg<<" <<, token: '"<<currentToken.look<<"', @ ("<<currentToken.start.row<<", "<<currentToken.start.col<<") - ("<<currentToken.end.row<<", "<<currentToken.end.col<<"), tok-number:"<<currentToken.type<<endl; + + errTable->insertRows(0); + errTable->setText( 0, 0, QString::number(errCount) ); // put the count in a hidden field for reference + errTable->setText( 0, 1, QString::number(err.tok.start.row) ); + errTable->setText( 0, 2, err.msg ); + + errCount++; +} + + +bool ErrorMessage::containsErrors() +{ + if (errTable->numRows() != 0) return true; + return false; +} + +void ErrorMessage::display() +{ + errTable->clearSelection(); + enableButton (KDialogBase::User1, false); + errTable->sortColumn(0, true, true); + show(); + connect( errTable, SIGNAL( selectionChanged() ), this, SLOT( updateSelection() ) ); +} + +void ErrorMessage::updateSelection() +{ + int i = errTable->text( errTable->currentRow(), 0 ).toInt(); // get the hidden errCount value + currentError = *errList.at(i - 1); + emit setSelection(currentError.tok.start.row, currentError.tok.start.col, + currentError.tok.end.row, currentError.tok.end.col); +// #if 0 // FIXME +// if ( tokenTypeNames[currentError.tok.type].isEmpty() ) enableButton(KDialogBase::User1, true); +// else enableButton(KDialogBase::User1, false); +// #endif +} + +void ErrorMessage::showHelpOnError() +{ +// #if 0 // FIXME +// kapp->invokeHelp(tokenTypeNames[currentError.tok.type], "", ""); +// #endif +} + +void ErrorMessage::errorMessageHelp() +{ + kapp->invokeHelp("anchorname", "", ""); +} + +// END + + + +// BEGIN class ColorPicker dialog + +ColorPicker::ColorPicker(QWidget *parent) + : KDialogBase(parent, "colorpicker", false, i18n("Color Picker"), KDialogBase::Close | KDialogBase::Help | KDialogBase::User1, KDialogBase::Close, true ) +{ + // connect to help + connect( this, SIGNAL( helpClicked() ), SLOT( slotColorPickerHelp() ) ); + + // for toggling convenience + connect( this, SIGNAL( finished() ), SLOT( slotEmitVisibility() ) ); + + // Create the top level page and its layout + BaseWidget = new QWidget(this); + setMainWidget(BaseWidget); + + // the User1 button: + setButtonText( KDialogBase::User1, i18n("Insert Color Code at Cursor") ); + connect( this, SIGNAL( user1Clicked() ), SLOT( slotEmitColorCode() ) ); + + QVBoxLayout *vlayout = new QVBoxLayout(BaseWidget); + + vlayout->addSpacing(5); // spacing on top + + // the palette and value selector go into a H-box + QHBoxLayout *h1layout = new QHBoxLayout(BaseWidget); + vlayout->addLayout(h1layout); + + h1layout->addSpacing(10); // space on the left border + + hsSelector = new KHSSelector(BaseWidget); // the color (SH) selector + hsSelector->setMinimumSize(300, 150); + h1layout->addWidget(hsSelector); + connect( hsSelector, SIGNAL( valueChanged(int, int) ), SLOT( slotSelectorChanged(int, int) ) ); + + h1layout->addSpacing(5); // space in between + + valuePal = new KValueSelector(BaseWidget); // the darkness (V) pal + valuePal->setFixedWidth(30); + h1layout->addWidget(valuePal); + connect( valuePal, SIGNAL( valueChanged(int) ), SLOT( slotPalChanged(int) ) ); + + vlayout->addSpacing(15); // space in between the top and the bottom widgets + + // the patch and the codegenerator also go into a H-box + QHBoxLayout *h2layout = new QHBoxLayout(BaseWidget); + vlayout->addLayout(h2layout); + + h2layout->addSpacing(10); // space on the left border + + patch = new KColorPatch(BaseWidget); // the patch (color previewer) + patch->setFixedSize(50, 50); + h2layout->addWidget(patch); + + h2layout->addSpacing(10); // space in between + + // the label and the codegenerator go in a V-box + QVBoxLayout *v2layout = new QVBoxLayout(BaseWidget); + h2layout->addLayout(v2layout); + + copyable = new QLabel(i18n("Color code:"), BaseWidget); // tha label + v2layout->addWidget(copyable); + + colorcode = new QLineEdit(BaseWidget); // the code generator + colorcode->setReadOnly(true); + v2layout->addWidget(colorcode); + connect( colorcode, SIGNAL( selectionChanged() ), SLOT( slotReselect() ) ); + + h2layout->addSpacing(5); // spacing on the right border + + vlayout->addSpacing(10); // spacing on the bottom + + h = g = b = 0; // start with red + s = v = r = 255; + + slotSelectorChanged(h, s); // update all at once +} + + + +void ColorPicker::updateSelector() +{ + hsSelector->setValues(h, s); +} + +void ColorPicker::updatePal() +{ + valuePal->setHue(h); + valuePal->setSaturation(s); + valuePal->setValue(v); + valuePal->updateContents(); + valuePal->repaint(false); +} + +void ColorPicker::updatePatch() +{ + patch->setColor(color); +} + +void ColorPicker::updateColorCode() +{ + color.getRgb(&r, &g, &b); + colorcode->setText( QString("%1, %2, %3").arg(r).arg(g).arg(b) ); + colorcode->selectAll(); +} + +void ColorPicker::slotSelectorChanged(int h_, int s_) +{ + h = h_; + s = s_; + color.setHsv(h, s, v); + + //updateSelector(); // updated it self allready + updatePal(); + updatePatch(); + updateColorCode(); +} + +void ColorPicker::slotPalChanged(int v_) +{ + v = v_; + color.setHsv(h, s, v); + + //updateSelector(); // only needed when H or S changes + //updatePal(); // updated it self allready + updatePatch(); + updateColorCode(); +} + +void ColorPicker::slotReselect() +{ + // reselect by selectAll(), but make sure no looping occurs + disconnect( colorcode, SIGNAL( selectionChanged() ), 0, 0 ); + colorcode->selectAll(); + connect( colorcode, SIGNAL( selectionChanged() ), SLOT( slotReselect() ) ); +} + +void ColorPicker::slotEmitVisibility() +{ + // for toggling convenience + emit visible(false); +} + +void ColorPicker::slotEmitColorCode() +{ + // convenience + emit ColorCode( colorcode->text() ); +} + +void ColorPicker::slotColorPickerHelp() +{ + kapp->invokeHelp("tools-color-picker", "", ""); +} + +// END + + + +// BEGIN class RestartOrBack dialog + +RestartOrBack::RestartOrBack (QWidget *parent) + : KDialogBase (parent, "rbDialog", true, 0, KDialogBase::User1 | KDialogBase::User2, KDialogBase::User2, false, i18n("&Restart"), i18n("&Back") ) +{ + setPlainCaption( i18n("Finished Execution") ); + setButtonWhatsThis( KDialogBase::User1, i18n("Click here to restart the current logo program.") ); + setButtonWhatsThis( KDialogBase::User2, i18n("Click here to switch back to the edit mode.") ); + QWidget *baseWidget = new QWidget(this); + setMainWidget(baseWidget); + baseLayout = new QVBoxLayout(baseWidget); + + label = new QLabel(baseWidget); + label->setText( i18n("Execution was finished without errors.\nWhat do you want to do next?") ); + label->setScaledContents(true); + baseLayout->addWidget(label); + disableResize(); +} + +// END + + +#include "dialogs.moc" diff --git a/kturtle/src/dialogs.h b/kturtle/src/dialogs.h new file mode 100644 index 00000000..dd4f2474 --- /dev/null +++ b/kturtle/src/dialogs.h @@ -0,0 +1,165 @@ +/* + KTurtle, Copyright (C) 2003-04 Cies Breijs <cies # kde ! nl> + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +// This is one class for all custom dialogs that KTurtle uses. +// Right now that are: +// - ErrorMessage dialog, used to display the users coding errors +// - ColorPicker dialog, used to easily pick color-codes and put them in the code +// - RestartOrBack dialog, used when executing in fullscreenmode + + +#ifndef _DIALOGS_H_ +#define _DIALOGS_H_ + + +#include <qlayout.h> +#include <qlabel.h> +#include <qlineedit.h> +#include <qstring.h> +#include <qtable.h> +#include <qvaluelist.h> + +#include <kdialogbase.h> +#include <kcolordialog.h> + +#include "token.h" + + +// BEGIN ErrorMessage dialog HEADER + +struct errorData +{ + uint code; + Token tok; + QString msg; +}; + + +class ErrorMessage : public KDialogBase +{ + Q_OBJECT + + public: + ErrorMessage(QWidget *parent); + ~ErrorMessage() {} + + bool containsErrors(); + + void display(); + + public slots: + void slotAddError(Token&, const QString&, uint code); + void updateSelection(); + + private slots: + void showHelpOnError(); + void errorMessageHelp(); + + signals: + void setCursor(uint row, uint column); + void setSelection(uint, uint, uint, uint); + + private: + typedef QValueList<errorData> errorList; + errorList errList; + QTable *errTable; + uint errCount; + errorData currentError; + + protected: + QDialog *dialog; + QVBoxLayout *baseLayout; + QLabel *label; + QSpacerItem *spacer; +}; + +// END + + + +// BEGIN ColorPicker dialog HEADER + +class ColorPicker : public KDialogBase +{ + Q_OBJECT + + public: + ColorPicker(QWidget *parent); + virtual ~ColorPicker() {} + + + signals: + void visible(bool); // for toggling convenience + void ColorCode(QString); + + + private: + void updateSelector(); + void updatePal(); + void updatePatch(); + void updateColorCode(); + + QWidget *BaseWidget; + KHSSelector *hsSelector; + KValueSelector *valuePal; + QVBoxLayout *vlayout; + QHBoxLayout *h1layout; + QHBoxLayout *h2layout; + KColorPatch *patch; + QLabel *copyable; + QLineEdit *colorcode; + QColor color; + int h, s, v, r, g, b; + + + private slots: + void slotColorPickerHelp(); + + + protected slots: + void slotSelectorChanged(int _h, int _s); + void slotPalChanged(int _v); + void slotReselect(); + + void slotEmitVisibility(); // for toggling convenience + void slotEmitColorCode(); // convenience +}; + +// END + + + +// BEGIN RestartOrBack dialog HEADER + +class RestartOrBack : public KDialogBase +{ + Q_OBJECT + + public: + RestartOrBack (QWidget *parent); + ~RestartOrBack() {} + + protected: + QVBoxLayout *baseLayout; + QLabel *label; +}; + +// END + + +#endif // _DIALOGS_H_ diff --git a/kturtle/src/executer.cpp b/kturtle/src/executer.cpp new file mode 100644 index 00000000..f7058146 --- /dev/null +++ b/kturtle/src/executer.cpp @@ -0,0 +1,1116 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// This file is originally written by Walter Scheppers, but almost +// every aspect of it is slightly changed by Cies Breijs. + + +#include <unistd.h> // for usleep(); +#include <stdlib.h> + +#include <qtimer.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> + +#include "executer.h" + +// this function is used in executer and canvas: +#define ROUND2INT(x) ( (x) >= 0 ? (int)( (x) + .5 ) : (int)( (x) - .5 ) ) + + +Executer::Executer(TreeNode* tree) +{ + this->tree = tree; + functionTable.clear(); + bBreak = false; + bReturn = false; +} + +Executer::~Executer() +{ + emit Finished(); +} + +bool Executer::run() +{ + bBreak = false; + bReturn = false; + bPause = false; + bAbort = false; + symtable main; + symbolTables.push(main); // new symbol table for main block + + TreeNode::const_iterator i; + for (i = tree->begin(); i != tree->end(); ++i) + { + if (bAbort) return false; + kapp->processEvents(); + execute(*i); + + symbolTables.pop(); //free up stack + } + return true; +} + +void Executer::slowDown(TreeNode* node) +{ + switch (runSpeed) + { + case 1: // slow + startWaiting(0); + break; + + case 2: // slower + startWaiting(250); + break; + + case 3: // slowest + startWaiting(900); + break; + + default: + kdDebug(0)<<"Executer: Wrong execution speed given"<<endl; + break; + } + Token tok = node->getToken(); + emit setSelection(tok.start.row, tok.start.col, tok.end.row, tok.end.col); +} + +void Executer::slotChangeSpeed(int speed) +{ + runSpeed = speed; +} + +void Executer::pause() +{ + // The next line is within all loops of the Executer + // if (bAbort) return; + // mostly before + // kapp->processEvents(); + // this to keep the GUI of KTurtle accessible while executing the logo code + // so the Abort button can be clicked, and will function + bPause = true; +} + +void Executer::abort() +{ + // The next line is within all loops of the Executer + // if(bAbort) return; + // mostly before + // kapp->processEvents(); + // this to keep the GUI of KTurtle accessible while executing the logo code + // so the Abort button can be clicked, and will function + bAbort = true; +} + + +void Executer::execute(TreeNode* node) +{ + switch ( node->getType() ) + { + case blockNode : execBlock(node); break; + case forNode : execFor(node); break; + case forEachNode : execForEach(node); break; + case whileNode : execWhile(node); break; + case ifNode : execIf(node); break; + case assignNode : execAssign(node); break; + case expressionNode : execExpression(/*node*/); break; + case idNode : execId(node); break; + case constantNode : execConstant(/*node*/); break; // does nothing value allready set + + case addNode : execAdd(node); break; + case mulNode : execMul(node); break; + case divNode : execDiv(node); break; + case subNode : execSub(node); break; + case minusNode : execMinus(node); break; + + case nodeGE : execGE(node); break; + case nodeGT : execGT(node); break; + case nodeLE : execLE(node); break; + case nodeLT : execLT(node); break; + case nodeNE : execNE(node); break; + case nodeEQ : execEQ(node); break; + + case andNode : execAnd(node); break; + case orNode : execOr(node); break; + case notNode : execNot(node); break; + + case functionNode : createFunction(node); break; + case functionCallNode : execFunction(node); break; + case funcReturnNode : execRetFunction(node); break; + case returnNode : execReturn(node); break; + case breakNode : execBreak(/*node*/); break; + + case runNode : execRun(node); break; + + case ClearNode : execClear(node); break; + case GoNode : execGo(node); break; + case GoXNode : execGoX(node); break; + case GoYNode : execGoY(node); break; + case ForwardNode : execForward(node); break; + case BackwardNode : execBackward(node); break; + case DirectionNode : execDirection(node); break; + case TurnLeftNode : execTurnLeft(node); break; + case TurnRightNode : execTurnRight(node); break; + case CenterNode : execCenter(node); break; + case SetPenWidthNode : execSetPenWidth(node); break; + case PenUpNode : execPenUp(node); break; + case PenDownNode : execPenDown(node); break; + case SetFgColorNode : execSetFgColor(node); break; + case SetBgColorNode : execSetBgColor(node); break; + case ResizeCanvasNode : execResizeCanvas(node); break; + case SpriteShowNode : execSpriteShow(node); break; + case SpriteHideNode : execSpriteHide(node); break; + case SpritePressNode : execSpritePress(node); break; + case SpriteChangeNode : execSpriteChange(node); break; + + case MessageNode : execMessage(node); break; + case InputWindowNode : execInputWindow(node); break; + case printNode : execPrint(node); break; + case FontTypeNode : execFontType(node); break; + case FontSizeNode : execFontSize(node); break; + case RepeatNode : execRepeat(node); break; + case RandomNode : execRandom(node); break; + case WaitNode : execWait(node); break; + case WrapOnNode : execWrapOn(node); break; + case WrapOffNode : execWrapOff(node); break; + case ResetNode : execReset(node); break; + + case EndOfFileNode : break; // just do nothing is enough + + case Unknown : // dont break but fallthrough to default + + default: + kdDebug(0)<<"Found unsupported node named '"<<node->getLook()<<"' in the tree @ ("<<node->getRow()<<", "<<node->getCol()<<")"<<endl; + break; + } +} + + +void Executer::createFunction(TreeNode* node) +{ + QString funcname = node->getLook(); + functionTable[funcname] = node; //store for later use +} + + +//execute a function +void Executer::execFunction(TreeNode* node) +{ + QString funcname = node->getLook(); + + // locate function node + functable::iterator p = functionTable.find(funcname); + if ( p == functionTable.end() ) + { + emit ErrorMsg(node->getToken(), i18n("Call to undefined function: %1.").arg(funcname), 5010); + return; + } + + TreeNode* funcnode = p->second; + TreeNode* funcIds = funcnode->firstChild(); + TreeNode* callparams = node->firstChild(); + + // check if number of parameters match + if ( callparams->size() != funcIds->size() ) + { + emit ErrorMsg(node->getToken(), i18n("Call to function '%1' with wrong number of parameters.").arg(funcname), 5020); + return; + } + + // pass parameters to function + // by adding them to it's symboltable and setting the values + TreeNode::iterator pfrom, pto = funcIds->begin(); + symtable funcSymTable; + + for (pfrom = callparams->begin(); pfrom != callparams->end(); ++pfrom ) + { + if (bAbort) return; + if (bPause) startPausing(); + kapp->processEvents(); + + // execute the parameters which can be expressions + execute(*pfrom); + + QString idname = (*pto)->getLook(); + funcSymTable[idname] = (*pfrom)->getValue(); + ++pto; + } + + symbolTables.push(funcSymTable); // use new symboltable for current function + + // execute function statement block + bReturn = false; // set to true when return is called + execute( funcnode->secondChild() ); + bReturn = false; // function execution done + + symbolTables.pop(); // release function symboltable +} + + +// execute a function and expect and get return +// value from stack +// first child = function name +// second child = parameters +void Executer::execRetFunction(TreeNode* node) +{ + execFunction(node); + if (runStack.size() == 0) + { + emit ErrorMsg(node->getToken(), i18n("Function %1 did not return a value.").arg( node->getLook() ), 5030); + return; + } + node->setValue( runStack.top() ); // set return val + runStack.pop(); // remove from stack +} + + +void Executer::execReturn(TreeNode* node) +{ + execute( node->firstChild() ); // execute return expression + runStack.push( node->firstChild()->getValue() ); + bReturn = true; // notify blocks of return +} + + +void Executer::execBreak(/*TreeNode* node*/) +{ + bBreak = true; // stops loop block execution +} + + +void Executer::execBlock(TreeNode* node) +{ + // execute all statements in block + TreeNode::iterator i; + for (i = node->begin(); i != node->end(); ++i) + { + if (runSpeed != 0) slowDown(*i); + if (bAbort) return; + if (bPause) startPausing(); + kapp->processEvents(); + + execute(*i); + + if (bReturn || bBreak) break; //jump out of block + } +} + + +void Executer::execForEach(TreeNode* node) +{ + // sorry, not fully implemented/tested yet + TreeNode* expr1 = node->firstChild(); + TreeNode* expr2 = node->secondChild(); + TreeNode* statements = node->thirdChild(); + + execute(expr1); + execute(expr2); + + QString expStr1 = expr1->getValue().String(); + QString expStr2 = expr2->getValue().String(); + + bBreak = false; + + int i = expStr2.contains(expStr1, false); + for ( ; i > 0; i-- ) + { + if (bAbort) return; + if (bPause) startPausing(); + kapp->processEvents(); + + execute(statements); + if (bBreak || bReturn) break; //jump out loop + } + bBreak = false; +} + + + +void Executer::execFor(TreeNode* node) +{ + TreeNode* id = node->firstChild(); + TreeNode* startNode = node->secondChild(); + TreeNode* stopNode = node->thirdChild(); + TreeNode* statements = node->fourthChild(); + + QString name = id->getLook(); + + execute(startNode); + //assign startval to id + Value startVal = startNode->getValue(); + ( symbolTables.top() )[ name ] = startVal; + + + execute(stopNode); + Value stopVal = stopNode->getValue(); + + if(node->size() == 4 ) //for loop without step part + { + bBreak = false; + for (double d = startVal.Number(); d <= stopVal.Number(); d = d + 1) + { + if (bAbort) return; + if (bPause) startPausing(); + kapp->processEvents(); + ( symbolTables.top() )[name] = d; + execute( statements ); + if (bBreak || bReturn) break; //jump out loop + } + bBreak = false; + } + else //for loop with step part + { + TreeNode* step = node->fourthChild(); + statements = node->fifthChild(); + + execute(step); + Value stepVal = step->getValue(); + bBreak = false; + if ( (stepVal.Number() >= 0.0) && (startVal.Number() <= stopVal.Number() ) ) + { + for ( double d = startVal.Number(); d <= stopVal.Number(); d = d + stepVal.Number() ) + { + if (bAbort) return; + if (bPause) startPausing(); + kapp->processEvents(); + + (symbolTables.top() )[name] = d; + execute( statements ); + if (bBreak || bReturn) break; //jump out loop + } + } + else if ( (stepVal.Number() < 0.0) && (startVal.Number() >= stopVal.Number() ) ) + { + for (double d = startVal.Number(); d >= stopVal.Number(); d = d + stepVal.Number() ) + { + if (bAbort) return; + if (bPause) startPausing(); + kapp->processEvents(); + + ( symbolTables.top() )[name] = d; + execute(statements); + if (bBreak || bReturn) break; //jump out loop + } + } + bBreak = false; + } +} + + + +void Executer::execRepeat(TreeNode* node) +{ + TreeNode* value = node->firstChild(); + TreeNode* statements = node->secondChild(); + + bBreak = false; + execute(value); + for ( int i = ROUND2INT( value->getValue().Number() ); i > 0; i-- ) + { + if (bAbort) return; + if (bPause) startPausing(); + kapp->processEvents(); + + execute(statements); + if (bBreak || bReturn) break; //jump out loop + } + bBreak = false; +} + + +void Executer::execWhile(TreeNode* node) +{ + TreeNode* condition = node->firstChild(); + TreeNode* statements = node->secondChild(); + + bBreak = false; + execute(condition); + while (condition->getValue().Number() != 0) + { + if (bAbort) return; + if (bPause) startPausing(); + kapp->processEvents(); + + execute(statements); + if (bBreak || bReturn) break; //jump out loop + execute(condition); + } + bBreak = false; +} + + +void Executer::execIf(TreeNode* node) +{ + TreeNode* condition = node->firstChild(); + TreeNode* ifblok = node->secondChild(); + + //determine if there is an else part + if (node->size() == 2) // no else + { + execute( condition ); + if( condition->getValue().Number() != 0 ) execute(ifblok); + } + else // else part given + { + TreeNode* elseblok = node->thirdChild(); + execute( condition ); + if( condition->getValue().Number() != 0 ) execute(ifblok); + else execute( elseblok ); + } +} + + + +void Executer::execAssign(TreeNode* node) +{ + TreeNode* expr = node->firstChild(); + + execute(expr); + ( symbolTables.top() )[ node->getLook() ] = expr->getValue(); +} + +void Executer::execExpression(/*TreeNode* node*/) +{ + // execExpression is not implemented, because it should not be needed! +} + +void Executer::execId(TreeNode* node) +{ + node->setValue( ( symbolTables.top() )[ node->getLook() ] ); +} + +void Executer::execConstant(/*TreeNode* node*/) +{ + // do nothing, value is already set +} + +Value Executer::exec2getValue(TreeNode* node) +{ + execute(node); + return node->getValue(); +} + + +void Executer::execAdd(TreeNode* node) +{ + Value left( exec2getValue( node->firstChild() ) ); + Value right( exec2getValue( node->secondChild() ) ); + + if (left.Type() == numberValue && right.Type() == numberValue) node->setValue( left + right ); + else node->setValue( left.String().append( right.String() ) ); +} + + +void Executer::execMul(TreeNode* node) +{ + Value left( exec2getValue( node->firstChild() ) ); + Value right( exec2getValue( node->secondChild() ) ); + + if (left.Type() == numberValue && right.Type() == numberValue) node->setValue( left * right ); + else emit ErrorMsg(node->getToken(), i18n("Can only multiply numbers."), 9000); +} + + +void Executer::execDiv(TreeNode* node) +{ + Value left( exec2getValue( node->firstChild() ) ); + Value right( exec2getValue( node->secondChild() ) ); + + if (left.Type() == numberValue && right.Type() == numberValue) + { + if (right.Number() == 0) emit ErrorMsg(node->getToken(), i18n("Cannot divide by zero."), 9000); + else node->setValue( left / right ); + } + else emit ErrorMsg(node->getToken(), i18n("Can only divide numbers."), 9000); +} + + +void Executer::execSub(TreeNode* node) +{ + Value left( exec2getValue( node->firstChild() ) ); + Value right( exec2getValue( node->secondChild() ) ); + + if (left.Type() == numberValue && right.Type() == numberValue) + node->setValue( left - right ); + + else emit ErrorMsg(node->getToken(), i18n("Can only subtract numbers."), 9000); +} + + + +void Executer::execLT(TreeNode* node) +{ + Value left( exec2getValue( node->firstChild() ) ); + Value right( exec2getValue( node->secondChild() ) ); + + node->setValue( left < right ); +} + +void Executer::execLE(TreeNode* node) +{ + Value left( exec2getValue( node->firstChild() ) ); + Value right( exec2getValue( node->secondChild() ) ); + + node->setValue( left <= right ); +} + +void Executer::execGT(TreeNode* node) +{ + Value left( exec2getValue( node->firstChild() ) ); + Value right( exec2getValue( node->secondChild() ) ); + + node->setValue( left > right ); +} + +void Executer::execGE(TreeNode* node) +{ + Value left( exec2getValue( node->firstChild() ) ); + Value right( exec2getValue( node->secondChild() ) ); + + node->setValue( left >= right ); +} + + +void Executer::execEQ(TreeNode* node) +{ + Value left( exec2getValue( node->firstChild() ) ); + Value right( exec2getValue( node->secondChild() ) ); + + node->setValue( left == right ); +} + + +void Executer::execNE(TreeNode* node) +{ + Value left( exec2getValue( node->firstChild() ) ); + Value right( exec2getValue( node->secondChild() ) ); + + node->setValue( left != right ); +} + + + +void Executer::execAnd(TreeNode* node) +{ + bool nl = exec2getValue( node->firstChild() ).Number() != 0; + bool nr = exec2getValue( node->secondChild() ).Number() != 0; + node->setValue( (double) (nl && nr) ); +} + + +void Executer::execOr(TreeNode* node) +{ + bool nl = exec2getValue( node->firstChild() ).Number() != 0; + bool nr = exec2getValue( node->secondChild() ).Number() != 0; + node->setValue(nl || nr); +} + + +void Executer::execNot(TreeNode* node) +{ + node->setValue( exec2getValue( node->firstChild() ).Number() == 0 ); +} + + +void Executer::execMinus(TreeNode* node) +{ + node->setValue( -exec2getValue( node->firstChild() ).Number() ); +} + + +QString Executer::runCommand(const QString& command) +{ + FILE *pstream; + + if ( ( pstream = popen( command.ascii(), "r" ) ) == NULL ) return (""); + + QString Line; + char buf[100]; + + while( fgets(buf, sizeof(buf), pstream) !=NULL) { + if (bAbort) return (""); + kapp->processEvents(); + + Line += buf; + } + pclose(pstream); + return Line; +} + + +void Executer::execRun(TreeNode* node) +{ + QString cmd = exec2getValue( node->firstChild() ).String(); + node->setValue( runCommand(cmd) ); +} + + + + +void Executer::execClear(TreeNode* node) +{ + if ( checkParameterQuantity(node, 0, 5060) ) emit Clear(); +} + +void Executer::execCenter(TreeNode* node) +{ + if ( checkParameterQuantity(node, 0, 5060) ) emit Center(); +} + +void Executer::execPenUp(TreeNode* node) +{ + if ( checkParameterQuantity(node, 0, 5060) ) emit PenUp(); +} + +void Executer::execPenDown(TreeNode* node) +{ + if ( checkParameterQuantity(node, 0, 5060) ) emit PenDown(); +} + +void Executer::execSpriteShow(TreeNode* node) +{ + if ( checkParameterQuantity(node, 0, 5060) ) emit SpriteShow(); +} + +void Executer::execSpriteHide(TreeNode* node) +{ + if ( checkParameterQuantity(node, 0, 5060) ) emit SpriteHide(); +} + +void Executer::execSpritePress(TreeNode* node) +{ + if ( checkParameterQuantity(node, 0, 5060) ) emit SpritePress(); +} + +void Executer::execWrapOn(TreeNode* node) +{ + if ( checkParameterQuantity(node, 0, 5060) ) emit WrapOn(); +} + +void Executer::execWrapOff(TreeNode* node) +{ + if ( checkParameterQuantity(node, 0, 5060) ) emit WrapOff(); +} + +void Executer::execReset(TreeNode* node) +{ + if ( checkParameterQuantity(node, 0, 5060) ) emit Reset(); +} + +void Executer::execMessage(TreeNode* node) +{ + if ( checkParameterQuantity(node, 1, 5060) && checkParameterType(node, stringValue, 5060) ) + emit MessageDialog( node->firstChild()->getValue().String() ); +} + + + + + + +void Executer::execGoX(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + TreeNode* param1 = node->firstChild(); + execute(param1); + if ( checkParameterType(node, numberValue, 5060) ) + { + emit GoX( param1->getValue().Number() ); + } +} + +void Executer::execGoY(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + TreeNode* param1 = node->firstChild(); + execute(param1); + if ( checkParameterType(node, numberValue, 5060) ) + { + emit GoY( param1->getValue().Number() ); + } +} + +void Executer::execForward(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + TreeNode* param1 = node->firstChild(); + execute(param1); + if ( checkParameterType(node, numberValue, 5060) ) + { + emit Forward( param1->getValue().Number() ); + } +} + +void Executer::execBackward(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + TreeNode* param1 = node->firstChild(); + execute(param1); + if ( checkParameterType(node, numberValue, 5060) ) + { + emit Backward( param1->getValue().Number() ); + } +} + +void Executer::execDirection(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + TreeNode* param1 = node->firstChild(); + execute(param1); + if ( checkParameterType(node, numberValue, 5060) ) + { + emit Direction( param1->getValue().Number() ); + } +} + +void Executer::execTurnLeft(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + TreeNode* param1 = node->firstChild(); + execute(param1); + if ( checkParameterType(node, numberValue, 5060) ) + { + emit TurnLeft( param1->getValue().Number() ); + } +} + +void Executer::execTurnRight(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + TreeNode* param1 = node->firstChild(); + execute(param1); + if ( checkParameterType(node, numberValue, 5060) ) + { + emit TurnRight( param1->getValue().Number() ); + } +} + +void Executer::execSetPenWidth(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + TreeNode* param1 = node->firstChild(); + execute(param1); + if ( checkParameterType(node, numberValue, 5060) ) + { + int x = ROUND2INT( param1->getValue().Number() ); // pull the number value & round it to int + if (x < 1 || x > 10000) + emit ErrorMsg(node->getToken(), i18n("The penwidth cannot be set to something smaller than 1, or bigger than 10000."), 6050); + else + emit SetPenWidth(x); + } +} + +void Executer::execSpriteChange(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + TreeNode* param1 = node->firstChild(); + execute(param1); + if ( checkParameterType(node, numberValue, 5060) ) + { + int x = ROUND2INT( param1->getValue().Number() ); // pull the number value & round it to int + emit SpriteChange(x); + } +} + +void Executer::execFontSize(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + TreeNode* param1 = node->firstChild(); + execute(param1); + if ( checkParameterType(node, numberValue, 5060) ) + { + int x = ROUND2INT( param1->getValue().Number() ); // pull the number value & round it to int + if ( x < 0 || x > 350 ) + emit ErrorMsg(node->getToken(), i18n("The parameters of function %1 must be within range: 0 to 350.").arg( node->getLook() ), 5065); + else + emit FontSize(x); + } +} + + + + + + +void Executer::execGo(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 2, 5060) ) return; + TreeNode* nodeX = node->firstChild(); // getting + TreeNode* nodeY = node->secondChild(); + execute(nodeX); // executing + execute(nodeY); + + if ( checkParameterType(node, numberValue, 5060) ) + { + emit Go( nodeX->getValue().Number(), nodeY->getValue().Number() ); + } +} + +void Executer::execResizeCanvas(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 2, 5060) ) return; + TreeNode* nodeX = node->firstChild(); // getting + TreeNode* nodeY = node->secondChild(); + execute(nodeX); // executing + execute(nodeY); + + if ( checkParameterType(node, numberValue, 5060) ) + { + int x = ROUND2INT( nodeX->getValue().Number() ); // converting & rounding to int + int y = ROUND2INT( nodeY->getValue().Number() ); + if ( ( x < 1 || y < 1 ) || ( x > 10000 || y > 10000 ) ) + emit ErrorMsg(node->getToken(), i18n("The parameters of the %1 command must be numbers in the range: 1 to 10000.").arg( node->getLook() ), 7030); + else + emit ResizeCanvas(x, y); + } +} + +void Executer::execRandom(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 2, 5060) ) return; + TreeNode* nodeX = node->firstChild(); // getting + TreeNode* nodeY = node->secondChild(); + execute(nodeX); // executing + execute(nodeY); + + if ( !checkParameterType(node, numberValue, 5060) ) return; + double x = nodeX->getValue().Number(); + double y = nodeY->getValue().Number(); + double r = (double)( KApplication::random() ) / RAND_MAX; + node->setValue( r * ( y - x ) + x ); +} + + + + + +void Executer::execSetFgColor(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 3, 5060) ) return; + TreeNode* nodeR = node->firstChild(); // getting + TreeNode* nodeG = node->secondChild(); + TreeNode* nodeB = node->thirdChild(); + execute(nodeR); // executing + execute(nodeG); + execute(nodeB); + if ( checkParameterType(node, numberValue, 5060) ) + { + int r = ROUND2INT( nodeR->getValue().Number() ); // converting & rounding to int + int g = ROUND2INT( nodeG->getValue().Number() ); + int b = ROUND2INT( nodeB->getValue().Number() ); + if ( ( r < 0 || g < 0 || b < 0 ) || ( r > 255 || g > 255 || b > 255 ) ) + emit ErrorMsg(node->getToken(), i18n("The parameters of the %1 command must be numbers in the range: 0 to 255.").arg( node->getLook() ), 6090); + else + emit SetFgColor(r, g, b); + } +} + +void Executer::execSetBgColor(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 3, 5060) ) return; + TreeNode* nodeR = node->firstChild(); // getting + TreeNode* nodeG = node->secondChild(); + TreeNode* nodeB = node->thirdChild(); + execute(nodeR); // executing + execute(nodeG); + execute(nodeB); + if ( checkParameterType(node, numberValue, 5060) ) + { + int r = ROUND2INT( nodeR->getValue().Number() ); // converting & rounding to int + int g = ROUND2INT( nodeG->getValue().Number() ); + int b = ROUND2INT( nodeB->getValue().Number() ); + if ( ( r < 0 || g < 0 || b < 0 ) || ( r > 255 || g > 255 || b > 255 ) ) + emit ErrorMsg(node->getToken(), i18n("The parameters of the %1 command must be numbers in the range: 0 to 255.").arg( node->getLook() ), 6090); + else + emit SetBgColor(r, g, b); + } +} + + + + + + + + + + + +void Executer::execInputWindow(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + + QString value = node->firstChild()->getValue().String(); + emit InputDialog(value); + + node->setType(constantNode); + if ( value.isEmpty() ) node->getValue().resetValue(); // set value back to empty + else + { + bool ok = true; + double num = value.toDouble(&ok); // to see if the value from the InpDialog is a float + if (ok) node->setValue(num); + else node->setValue(value); + } +} + +void Executer::execPrint(TreeNode* node) +{ + if (node->size() == 0) + { + emit ErrorMsg(node->getToken(), i18n("The print command needs input"), 5050); + return; + } + TreeNode::iterator i; + QString str = ""; + for (i = node->begin(); i != node->end(); ++i) + { + execute(*i); // execute expression + str = str + (*i)->getValue().String(); + } + emit Print(str); +} + +void Executer::execFontType(TreeNode* node) +{ + // if not 2 params go staight to the checkParam, diplay the error, and return to prevent a crash + if ( !checkParameterQuantity(node, 2, 5060) && !checkParameterType(node, stringValue, 5060) ) return; + + QString extra; + if (node->size() == 2) QString extra = node->secondChild()->getValue().String(); + QString family = node->firstChild()->getValue().String(); + emit FontType(family, extra); +} + + +void Executer::execWait(TreeNode* node) +{ + if ( !checkParameterQuantity(node, 1, 5060) ) return; + TreeNode* param1 = node->firstChild(); + execute(param1); + if ( !checkParameterType(node, numberValue, 5060) ) return; + int msec = (int)( 1000 * param1->getValue().Number() ); + startWaiting(msec); +} + +void Executer::startWaiting(int msec) +{ + bStopWaiting = false; + // call a timer that sets stopWaiting to true when it runs + QTimer::singleShot( msec, this, SLOT( slotStopWaiting() ) ); + while (bStopWaiting == false) + { + if (bAbort) return; // waits need to be interrupted by the stop action + if (bPause) startPausing(); + kapp->processEvents(); + + // only 10 times per second is enough... else the CPU gets 100% loaded ( not nice :) + usleep(100000); + } +} + +void Executer::slotStopWaiting() +{ + bStopWaiting = true; +} + +void Executer::startPausing() +{ + while (bPause == true) + { + if (bAbort) return; // waits need to be interrupted by the stop action + kapp->processEvents(); + // only 10 times per second is enough... else the CPU gets 100% loaded ( not nice :) + usleep(100000); + } +} + +void Executer::slotStopPausing() +{ + bPause = false; +} + + + + +bool Executer::checkParameterQuantity(TreeNode* node, uint quantity, int errorCode) +{ + if (quantity == 0) + { + if (node->size() == 0) return true; // thats easy! + emit ErrorMsg(node->getToken(), i18n("The %1 command accepts no parameters.").arg( node->getLook() ), errorCode); + return false; + } + + uint nodeSize = node->size(); + if (nodeSize != 0) // when all parameters are forgotten the parser puts a Unknown/tokEOL param, catch this: + if (node->firstChild()->getToken().type == tokEOL) nodeSize = 0; + + if (nodeSize != quantity) + { + if (nodeSize < quantity) + { + emit ErrorMsg(node->getToken(), i18n("The %1 command was called with %2 but needs 1 parameter.", "The %1 command was called with %2 but needs %n parameters.", quantity).arg( node->getLook() ).arg(nodeSize), errorCode); + } + else + { + emit ErrorMsg(node->getToken(), i18n("The %1 command was called with %2 but only accepts 1 parameter.", "The %1 command was called with %2 but only accepts %n parameters.", quantity).arg( node->getLook() ).arg(nodeSize), errorCode); + } + return false; + } + return true; // if all tests passed +} + + +bool Executer::checkParameterType(TreeNode* node, int valueType, int errorCode) +{ + uint quantity = node->size(); + uint ii = 1; + TreeNode::iterator i = node->begin(); + while ( i != node->end() && ii <= quantity ) + { + if ( (*i)->getValue().Type() != valueType ) + { + switch (valueType) + { + case stringValue: + if (quantity == 1) + emit ErrorMsg(node->getToken(), i18n("The %1 command only accepts a string as its parameter.").arg( node->getLook() ), errorCode); + else + emit ErrorMsg(node->getToken(), i18n("The %1 command only accepts strings as its parameters.").arg( node->getLook() ), errorCode); + break; + + case numberValue: + if (quantity == 1) + emit ErrorMsg(node->getToken(), i18n("The %1 command only accepts a number as its parameter.").arg( node->getLook() ), errorCode); + else + emit ErrorMsg(node->getToken(), i18n("The %1 command only accepts numbers as its parameters.").arg( node->getLook() ), errorCode); + break; + } + return false; + } + ++i; + ii++; + } + return true; // if all tests passed +} + +#include "executer.moc" diff --git a/kturtle/src/executer.h b/kturtle/src/executer.h new file mode 100644 index 00000000..ac9a1f1b --- /dev/null +++ b/kturtle/src/executer.h @@ -0,0 +1,186 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _EXECUTER_H_ +#define _EXECUTER_H_ + +#include <stack> +#include <map> + +#include "token.h" +#include "treenode.h" + + +typedef map<QString,Value> symtable; +typedef map<QString,TreeNode*> functable; + +typedef stack<Value> runstack; + + +class Executer : public QObject +{ + Q_OBJECT + + public: + Executer(TreeNode*); + virtual ~Executer(); + + bool run(); + void pause(); + void abort(); + + + signals: + void Finished(); + void ErrorMsg(Token&, const QString&, uint code); + void setSelection(uint, uint, uint, uint); + + void InputDialog(QString& value); + void MessageDialog(QString text); + + void Clear(); + void Go(double x, double y); + void GoX(double x); + void GoY(double y); + void Forward(double x); + void Backward(double x); + void Direction(double x); + void TurnLeft(double x); + void TurnRight(double x); + void Center(); + void SetPenWidth(int w); + void PenUp(); + void PenDown(); + void SetFgColor(int r, int g, int b); + void SetBgColor(int r, int g, int b); + void ResizeCanvas(int x, int y); + void SpriteShow(); + void SpriteHide(); + void SpritePress(); + void SpriteChange(int x); + void Print(QString text); + void FontType(QString family, QString extra); + void FontSize(int px); + void WrapOn(); + void WrapOff(); + void Reset(); + + + private: + void execute (TreeNode*); + + void execBlock (TreeNode*); + void execFor (TreeNode*); + void execForEach (TreeNode*); + void execWhile (TreeNode*); + void execIf (TreeNode*); + void execAssign (TreeNode*); + void execExpression (/*TreeNode*/); + void execId (TreeNode*); + void execConstant (/*TreeNode*/); + + void createFunction (TreeNode*); + void execFunction (TreeNode*); + void execRetFunction (TreeNode*); + void execReturn (TreeNode*); + void execBreak (/*TreeNode*/); + + void execAdd (TreeNode*); + void execMul (TreeNode*); + void execDiv (TreeNode*); + void execSub (TreeNode*); + void execNot (TreeNode*); + + void execGE (TreeNode*); + void execGT (TreeNode*); + void execLE (TreeNode*); + void execLT (TreeNode*); + void execNE (TreeNode*); + void execEQ (TreeNode*); + + void execAnd (TreeNode*); + void execOr (TreeNode*); + void execMinus (TreeNode*); + + void execRun (TreeNode*); + + void execClear (TreeNode*); + void execGo (TreeNode*); + void execGoX (TreeNode*); + void execGoY (TreeNode*); + void execForward (TreeNode*); + void execBackward (TreeNode*); + void execDirection (TreeNode*); + void execTurnLeft (TreeNode*); + void execTurnRight (TreeNode*); + void execCenter (TreeNode*); + void execSetPenWidth (TreeNode*); + void execPenUp (TreeNode*); + void execPenDown (TreeNode*); + void execSetFgColor (TreeNode*); + void execSetBgColor (TreeNode*); + void execResizeCanvas (TreeNode*); + void execSpriteShow (TreeNode*); + void execSpriteHide (TreeNode*); + void execSpritePress (TreeNode*); + void execSpriteChange (TreeNode*); + + void execMessage (TreeNode*); + void execInputWindow (TreeNode*); + void execPrint (TreeNode*); + void execFontType (TreeNode*); + void execFontSize (TreeNode*); + void execRepeat (TreeNode*); + void execRandom (TreeNode*); + void execWait (TreeNode*); + void execWrapOn (TreeNode*); + void execWrapOff (TreeNode*); + void execReset (TreeNode*); + + Value exec2getValue (TreeNode*); + + QString runCommand(const QString&); + + bool checkParameterQuantity(TreeNode*, uint quantity, int errorCode); + bool checkParameterType(TreeNode*, int valueType, int errorCode); + + void slowDown(TreeNode*); + void startWaiting(int msec); + void startPausing(); + + // private locals + TreeNode* tree; + stack<symtable> symbolTables; + functable functionTable; // keep track of functionNode's + runstack runStack; // stores parameters and return value of functions + + int runSpeed; + bool bReturn; // used for return statements + bool bBreak; // used for break statement + bool bPause; // used to pause execution + bool bAbort; // used to abort execution + bool bStopWaiting; // used for wait-command + + + private slots: + void slotStopWaiting(); + void slotChangeSpeed(int speed); + void slotStopPausing(); +}; + +#endif // _EXECUTER_H_ diff --git a/kturtle/src/hi16-app-kturtle.png b/kturtle/src/hi16-app-kturtle.png Binary files differnew file mode 100644 index 00000000..dd4a9575 --- /dev/null +++ b/kturtle/src/hi16-app-kturtle.png diff --git a/kturtle/src/hi32-app-kturtle.png b/kturtle/src/hi32-app-kturtle.png Binary files differnew file mode 100644 index 00000000..959c933e --- /dev/null +++ b/kturtle/src/hi32-app-kturtle.png diff --git a/kturtle/src/kturtle.cpp b/kturtle/src/kturtle.cpp new file mode 100644 index 00000000..2cc7c5ff --- /dev/null +++ b/kturtle/src/kturtle.cpp @@ -0,0 +1,1181 @@ +/* + * KTurtle, Copyright (C) 2003-04 Cies Breijs <cies # kde ! nl> + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +// BEGIN includes and defines + +#include <stdlib.h> + +#include <qbutton.h> +#include <qregexp.h> +#include <qpainter.h> +#include <qtooltip.h> +#include <qtimer.h> +#include <qwhatsthis.h> + +#include <kapplication.h> +#include <kconfigdialog.h> +#include <kdebug.h> +#include <kedittoolbar.h> +#include <kfiledialog.h> +#include <kimageio.h> +#include <kinputdialog.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kmenubar.h> +#include <kprinter.h> +#include <ksavefile.h> +#include <kstatusbar.h> + +#include <ktexteditor/clipboardinterface.h> +#include <ktexteditor/cursorinterface.h> +#include <ktexteditor/editorchooser.h> +#include <ktexteditor/editinterface.h> +#include <ktexteditor/highlightinginterface.h> +#include <ktexteditor/printinterface.h> +#include <ktexteditor/selectioninterface.h> +#include <ktexteditor/undointerface.h> +#include <ktexteditor/viewcursorinterface.h> +#include <ktexteditor/encodinginterface.h> + +#include "lexer.h" +#include "settings.h" +#include "translate.h" + +#include "kturtle.h" + +// StatusBar field IDs +#define IDS_STATUS 0 +#define IDS_LINECOLUMN 2 +#define IDS_INS 3 +#define IDS_LANG 4 + +// END + + + +// BEGIN constructor and destructor + +MainWindow::MainWindow(KTextEditor::Document *document) : editor(0) +{ + // the initial values + CurrentFile = KURL(); // fill with empty URL + setCaption( i18n("Untitled") ); + picker = 0; // for the colorpickerdialog + executing = false; + b_fullscreen = false; + + // set the shell's ui resource file + if (!document) + { + if ( !(document = KTextEditor::EditorChooser::createDocument(0,"KTextEditor::Document") ) ) + { + KMessageBox::error( this, i18n("A KDE text-editor component could not be found;\n" + "please check your KDE installation.") ); + kapp->exit(1); + } + // docList.append(doc); + } + doc = document; + + setupCanvas(); + setupEditor(); + + setupStatusBar(); + setupActions(); + createShellGUI(true); + setMinimumSize(200,200); + + // init with more usefull size, stolen from kwite (they stole it from konq) + if ( !initialGeometrySet() && !kapp->config()->hasGroup("MainWindow Settings") ) resize(640, 480); + + KConfig *config = kapp->config(); + readConfig(config); +} + +MainWindow::~MainWindow() +{ + delete editor->document(); +} + +// END + + + +// BEGIN setup's (actions, editor, statusbar, canvas) + +void MainWindow::setupActions() +{ + KActionCollection *ac = actionCollection(); // abbreviation + + // File actions + KStdAction::openNew(this, SLOT(slotNewFile()), ac); + openExAction = new KAction(i18n("Open Exa&mples..."), "bookmark_folder", CTRL+Key_E, this, SLOT(slotOpenExample()), ac, "open_examples"); + KStdAction::open(this, SLOT(slotOpenFile()), ac); + m_recentFiles = KStdAction::openRecent(this, SLOT(slotOpenFile(const KURL&)), ac); + KStdAction::save(this, SLOT(slotSaveFile()), ac); + KStdAction::saveAs(this, SLOT(slotSaveAs()), ac); + new KAction(i18n("Save &Canvas..."), 0, 0, this, SLOT(slotSaveCanvas()), ac, "save_canvas"); + speed = new KSelectAction(i18n("Execution Speed"), 0, ALT+Key_S, this, SLOT( slotChangeSpeed() ), ac, "speed"); + QStringList speeds; speeds << i18n("Full Speed") << i18n("Slow") << i18n("Slower") << i18n("Slowest"); + speed->setItems(speeds); + speed->setCurrentItem(0); + run = new KAction(i18n("&Execute Commands"), "gear", ALT+Key_Return, this, SLOT( slotExecute() ), ac, "run"); + pause = new KToggleAction(i18n("Pause E&xecution"), "player_pause", Key_Pause, this, SLOT( slotPauseExecution() ), ac, "pause"); + pause->setChecked(false); + pause->setEnabled(false); + stop = new KAction(i18n("Stop E&xecution"), "stop", Key_Escape, this, SLOT( slotAbortExecution() ), ac, "stop"); + stop->setEnabled(false); + KStdAction::print(this, SLOT(slotPrint()), ac); + KStdAction::quit(this, SLOT(close()), ac); + + // Edit actions + KStdAction::undo(this, SLOT(slotUndo()), ac); + KStdAction::redo(this, SLOT(slotRedo()), ac); + KStdAction::cut(this, SLOT(slotCut()), ac); + KStdAction::copy(this, SLOT(slotCopy()), ac); + KStdAction::paste(this, SLOT(slotPaste()), ac); + KStdAction::selectAll(this, SLOT(slotSelectAll()), ac); + KStdAction::deselect(this, SLOT(slotClearSelection()), ac); + new KToggleAction(i18n("Toggle Insert"), Key_Insert, this, SLOT(slotToggleInsert()), ac, "set_insert"); + KStdAction::find(this, SLOT(slotFind()), ac); + KStdAction::findNext(this, SLOT(slotFindNext()), ac); + KStdAction::findPrev(this, SLOT(slotFindPrevious()), ac); + KStdAction::replace(this, SLOT(slotReplace()), ac); + + // View actions + new KToggleAction(i18n("Show &Line Numbers"), 0, Key_F11, this, SLOT(slotToggleLineNumbers()), ac, "line_numbers"); + m_fullscreen = KStdAction::fullScreen(this, SLOT(slotToggleFullscreen()), ac, this, "full_screen"); + m_fullscreen->setChecked(b_fullscreen); + + // Tools actions + colorpicker = new KToggleAction(i18n("&Color Picker"), "colorize", ALT+Key_C, this, SLOT(slotColorPicker()), ac, "color_picker"); + new KAction(i18n("&Indent"), "indent", CTRL+Key_I, this, SLOT(slotIndent()), ac, "edit_indent"); + new KAction(i18n("&Unindent"), "unindent", CTRL+SHIFT+Key_I, this, SLOT(slotUnIndent()), ac, "edit_unindent"); + new KAction(i18n("Cl&ean Indentation"), 0, 0, this, SLOT(slotCleanIndent()), ac, "edit_cleanIndent"); + new KAction(i18n("Co&mment"), 0, CTRL+Key_D, this, SLOT(slotComment()), ac, "edit_comment"); + new KAction(i18n("Unc&omment"), 0, CTRL+SHIFT+Key_D, this, SLOT(slotUnComment()), ac, "edit_uncomment"); + + // Settings actions + KStdAction::preferences( this, SLOT(slotSettings()), ac ); + new KAction(i18n("&Configure Editor..."), "configure", 0, this, SLOT(slotEditor()), ac, "set_confdlg"); + + // Help actions + ContextHelp = new KAction(0, 0, Key_F2, this, SLOT(slotContextHelp()), ac, "context_help"); + slotContextHelpUpdate(); // this sets the label of this action + + // other + setXMLFile("kturtleui.rc"); + setupGUI(); +} + +void MainWindow::setupEditor() +{ + editorDock = new QDockWindow(this); + editorDock->setNewLine(true); + editorDock->setFixedExtentWidth(250); + editorDock->setFixedExtentHeight(150); + editorDock->setResizeEnabled(true); + editorDock->setFrameShape(QFrame::ToolBarPanel); + QWhatsThis::add( editorDock, i18n( "This is the code editor, here you type the Logo commands to instruct the turtle. You can also open an existing Logo program with File->Open Examples... or File->Open." ) ); + moveDockWindow(editorDock, Qt::DockLeft); + editor = doc->createView (editorDock, 0L); + // editorInterface is the editor interface which allows us to access the text in the part + editorInterface = dynamic_cast<KTextEditor::EditInterface*>(doc); + dynamic_cast<KTextEditor::EncodingInterface*>(doc)->setEncoding("UTF-8"); + editorDock->setWidget(editor); + + // default the highlightstyle to "logo" using the needed i18n + kdDebug(0)<<"The HighlightStyle for the Code Editor: "<<Settings::logoLanguage()<<endl; + slotSetHighlightstyle( Settings::logoLanguage() ); + + // allow the cursor position to be indicated in the statusbar + connect( editor, SIGNAL(cursorPositionChanged()), this, SLOT(slotCursorStatusBar()) ); + // and update the context help menu item + connect( editor, SIGNAL(cursorPositionChanged()), this, SLOT(slotContextHelpUpdate()) ); + + translate = new Translate(); +} + +void MainWindow::setupStatusBar() +{ + statusBar()->insertItem("", IDS_STATUS, 1, false); + statusBar()->setItemAlignment(IDS_STATUS, AlignLeft); + statusBar()->insertItem("", IDS_LANG, 0, true); + statusBar()->insertItem("", IDS_LINECOLUMN, 0, true); + statusBar()->insertItem("", IDS_INS, 0, true); + + // fill the statusbar + slotStatusBar(i18n("Welcome to KTurtle..."), IDS_STATUS); // the message part + slotStatusBar(i18n("Line: %1 Column: %2").arg(1).arg(1), IDS_LINECOLUMN); + slotStatusBar(i18n("INS"), IDS_INS); +} + +void MainWindow::setupCanvas() +{ + baseWidget = new QWidget(this); + setCentralWidget(baseWidget); + baseLayout = new QGridLayout(baseWidget, 0, 0); + canvasView = new Canvas(baseWidget); + baseLayout->addWidget(canvasView, 0, 0, AlignCenter); + baseLayout->setRowStretch(0, 1); // this apperntly fixes a pre-usefull scrollbars bug + baseLayout->setColStretch(0, 1); + QWhatsThis::add( canvasView, i18n("This is the canvas, here the turtle draws a picture.") ); + canvasView->show(); + connect( canvasView, SIGNAL( CanvasResized() ), this, SLOT( slotUpdateCanvas() ) ); +} + +// END + + + +// BEGIN staturbar related functions + +void MainWindow::slotStatusBar(QString text, int id) +{ + text = " " + text + " "; // help the layout + statusBar()->changeItem(text, id); +} + +void MainWindow::slotCursorStatusBar() +{ + uint cursorLine; + uint cursorCol; + dynamic_cast<KTextEditor::ViewCursorInterface*>(editor)->cursorPositionReal(&cursorLine, &cursorCol); + QString linenumber = i18n(" Line: %1 Column: %2 ").arg(cursorLine + 1).arg(cursorCol + 1); + statusBar()->changeItem(linenumber, IDS_LINECOLUMN); +} + +// END + + + +// BEGIN file menu related fuctions (new, open, save()as, image, print, quit) + +void MainWindow::slotNewFile() +{ + if ( !editor->document()->isModified() && CurrentFile.isEmpty() ) return; // do nothing when nothing can be done + if ( editor->document()->isModified() ) + { + int result = KMessageBox::warningContinueCancel( this, + i18n("The program you are currently working on is not saved. " + "By continuing you may lose the changes you have made."), + i18n("Unsaved File"), i18n("&Discard Changes") ); + if (result != KMessageBox::Continue) return; + } + editorInterface->clear(); // clear the editor + canvasView->slotClear(); // clear the view + editor->document()->setModified(false); + CurrentFile = KURL(); + setCaption( i18n("Untitled") ); + slotStatusBar(i18n("New file... Happy coding!"), IDS_STATUS); +} + + + +void MainWindow::slotOpenFile(const KURL &urlRef) +{ + KURL url = urlRef; + if ( url.isEmpty() ) + { + url = KFileDialog::getOpenURL( QString(":logo_dir"), QString("*.logo|") + i18n("Logo Files"), this, i18n("Open Logo File") ); + } + loadFile(url); +} + +void MainWindow::slotOpenExample() +{ + KURL url; + url.setPath( locate("data", "kturtle/examples/" + Settings::logoLanguage() + "/") ); + url = KFileDialog::getOpenURL( url.path(), QString("*.logo|") + i18n("Logo Examples Files"), this, i18n("Open Logo Example File") ); + loadFile(url); +} + +void MainWindow::loadFile(const KURL &url) +{ + if ( !url.isEmpty() ) + { + QFile file( url.path() ); + if ( file.open(IO_ReadOnly) ) + { + if ( editor->document()->isModified() ) + { + int result = KMessageBox::warningContinueCancel( this, + i18n("The program you are currently working on is not saved. " + "By continuing you may lose the changes you have made."), + i18n("Unsaved File"), i18n("&Discard Changes") ); + if (result != KMessageBox::Continue) + { + slotStatusBar(i18n("Opening aborted, nothing opened."), IDS_STATUS); + return; + } + } + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); + editorInterface->setText( stream.read() ); + file.close(); + m_recentFiles->addURL(url); + setCaption( url.fileName() ); + slotStatusBar(i18n("Opened file: %1").arg( url.fileName() ), IDS_STATUS); + editor->document()->setModified(false); + CurrentFile = url; + return; + } + else + { + KMessageBox::error( this, + i18n("KTurtle was unable to open: \n%1.").arg( url.prettyURL() ), + i18n("Open Error") ); + slotStatusBar(i18n("Opening aborted because of error."), IDS_STATUS); + return; + } + } + slotStatusBar(i18n("Opening aborted."), IDS_STATUS); // fallback +} + +void MainWindow::slotSaveFile() +{ + writeFile(CurrentFile); +} + +void MainWindow::slotSaveAs() +{ + KURL url; + while (true) + { + url = KFileDialog::getSaveURL( QString(":logo_dir"), QString("*.logo|") + i18n("Logo Files"), this, i18n("Save As") ); + if ( url.isEmpty() ) // cancelled the save dialog + { + slotStatusBar(i18n("Saving aborted."), IDS_STATUS); + return; + } + if ( QFile( url.path() ).exists() ) + { + int result = KMessageBox::warningContinueCancel ( this, + i18n("A program named \"%1\" already exists in this folder. " + "Do you want to overwrite it?").arg( url.fileName() ), + i18n("Overwrite?"), i18n("&Overwrite") ); + if (result != KMessageBox::Continue) return; + } + break; + } + writeFile(url); +} + +void MainWindow::writeFile(const KURL &url) +{ + if ( url.isEmpty() ) slotSaveAs(); + else + { + editor->document()->saveAs(url); // use the KateParts method for saving + loadFile(url); // reload the file as utf8 otherwise display weird chars + setCaption( url.fileName() ); + slotStatusBar(i18n("Saved to: %1").arg( url.fileName() ), IDS_STATUS); + m_recentFiles->addURL(url); + editor->document()->setModified(false); + CurrentFile = url; + } +} + + + +void MainWindow::slotSaveCanvas() +{ + KURL url; + while (true) + { + url = KFileDialog::getSaveURL( QString(":logo_dir"), QString("*.png|") + + i18n("Pictures"), this, i18n("Save Canvas as Picture") ); + if ( url.isEmpty() ) return; // when cancelled the KFiledialog? + if ( QFile( url.path() ).exists() ) + { + int result = KMessageBox::warningContinueCancel( this, + i18n("A picture named \"%1\" already in this folder. " + "Do you want to overwrite it?").arg( url.fileName() ), + i18n("Overwrite?"), i18n("&Overwrite") ); + if (result != KMessageBox::Continue) return; + } + break; + } + + QString type( KImageIO::type( url.path() ) ); + if ( type.isNull() ) type = "PNG"; + bool ok = false; + QPixmap* pixmap = canvasView->canvas2Pixmap(); + if ( url.isLocalFile() ) + { + KSaveFile saveFile( url.path() ); + if ( saveFile.status() == 0 ) + { + if ( pixmap->save( saveFile.file(), type.latin1() ) ) ok = saveFile.close(); + } + } + if (!ok) + { + kdWarning() << "KTurtle was unable to save the canvas drawing" << endl; + KMessageBox::error(this, + i18n("KTurtle was unable to save the image to: \n%1.").arg( url.prettyURL() ), + i18n("Unable to Save Image") ); + slotStatusBar(i18n("Could not save image."), IDS_STATUS); + return; + } + slotStatusBar(i18n("Saved canvas to: %1").arg( url.fileName() ), IDS_STATUS); +} + + + +void MainWindow::slotPrint() +{ + int result = KMessageBox::questionYesNoCancel( this, + i18n("Do you want to print the Logo code or the canvas?"), + i18n("What to Print?"), i18n("Print &Logo Code"), i18n("Print &Canvas") ); + if (result == KMessageBox::Yes) + { + dynamic_cast<KTextEditor::PrintInterface*>(doc)->printDialog(); + return; + } + if (result == KMessageBox::No) + { + KPrinter printer; + if ( printer.setup(this) ) + { + QPainter painter(&printer); + QPixmap *CanvasPic = canvasView->canvas2Pixmap(); + painter.drawPixmap(0, 0, *CanvasPic); + } + return; + } + slotStatusBar(i18n("Printing aborted."), IDS_STATUS); +} + + + +bool MainWindow::queryClose() +{ + if ( editor->document()->isModified() ) + { + slotStatusBar(i18n("Quitting KTurtle..."), IDS_STATUS); + // make sure the dialog looks good with new -- unnamed -- files. + int result = KMessageBox::warningYesNoCancel( this, + i18n("The program you are currently working on is not saved. " + "By quitting KTurtle you may lose the changes you have made."), + i18n("Unsaved File"), i18n("&Save"), i18n("Discard Changes && &Quit") ); + if (result == KMessageBox::Cancel) + { + slotStatusBar(i18n("Quitting aborted."), IDS_STATUS); + return false; + } + else if (result == KMessageBox::Yes) + { + slotSaveFile(); + if ( CurrentFile.isEmpty() ) + { + // when saveAs get cancelled or X-ed it should not quit + slotStatusBar(i18n("Quitting aborted."), IDS_STATUS); + return false; + } + } + } + KConfig *config = kapp->config(); + config->setGroup("General Options"); + m_recentFiles->saveEntries(config, "Recent Files"); + config->sync(); + return true; +} + +// END + + + +// BEGIN run related functions + +void MainWindow::slotExecute() +{ + if (b_fullscreen) // check if execution should be execution in 'fullscreen' mode + { + editorDock->hide(); + statusBar()->hide(); + menuBar()->hide(); + toolBar()->hide(); + } + + run->setEnabled(false); // set action indications + pause->setEnabled(true); + stop->setEnabled(true); + + // start paring + slotStatusBar(i18n("Parsing commands..."), IDS_STATUS); + kdDebug(0)<<"############## PARSING STARTED ##############"<<endl; + kapp->processEvents(); + errMsg = new ErrorMessage(this); // create an empty errorDialog + QString txt = editorInterface->text() + "\x0a\x0a"; // parser expects input to have 2 delimiting newlines + QTextIStream in(&txt); // create the stream + Parser parser(in); // pass the reference to the stream to the parse object + connect(&parser, SIGNAL( ErrorMsg(Token&, const QString&, uint) ), + errMsg, SLOT( slotAddError(Token&, const QString&, uint) ) ); + connect( errMsg, SIGNAL(setSelection(uint, uint, uint, uint) ), + this, SLOT(slotSetSelection(uint, uint, uint, uint) ) ); + parser.parse(); // and GO! + TreeNode* root = parser.getTree(); // when finished parsing get the nodeTree + kdDebug(0)<<"############## PARSING FINISHED ##############"<<endl; + + kdDebug(0)<<"TreeNode::showTree():"<<endl; + root->showTree(root); // show nodeTree, this is a DEBUG OPTION (but nice) + + // start execution + slotStatusBar(i18n("Executing commands..."), IDS_STATUS); + kdDebug(0)<<"############## EXECUTION STARTED ##############"<<endl; + exe = new Executer(root); // make Executer object, 'exe', and pass it the nodeTree + + connect(this, SIGNAL( changeSpeed(int) ), exe, SLOT(slotChangeSpeed(int) ) ); + connect(this, SIGNAL( unpauseExecution() ), exe, SLOT( slotStopPausing() ) ); + connect( exe, SIGNAL( setSelection(uint, uint, uint, uint) ), + this, SLOT ( slotSetSelection(uint, uint, uint, uint) ) ); + connect( exe, SIGNAL( ErrorMsg(Token&, const QString&, uint) ), + errMsg, SLOT ( slotAddError(Token&, const QString&, uint) ) ); + connect( exe, SIGNAL( InputDialog(QString&) ), this, SLOT( slotInputDialog(QString&) ) ); + connect( exe, SIGNAL( MessageDialog(QString) ), this, SLOT( slotMessageDialog(QString) ) ); + + // Connect the signals form Executer to the slots from Canvas: + connect( exe, SIGNAL( Clear() ), canvasView, SLOT( slotClear() ) ); + connect( exe, SIGNAL( Go(double, double) ), canvasView, SLOT( slotGo(double, double) ) ); + connect( exe, SIGNAL( GoX(double) ), canvasView, SLOT( slotGoX(double) ) ); + connect( exe, SIGNAL( GoY(double) ), canvasView, SLOT( slotGoY(double) ) ); + connect( exe, SIGNAL( Forward(double) ), canvasView, SLOT( slotForward(double) ) ); + connect( exe, SIGNAL( Backward(double) ), canvasView, SLOT( slotBackward(double) ) ); + connect( exe, SIGNAL( Direction(double) ), canvasView, SLOT( slotDirection(double) ) ); + connect( exe, SIGNAL( TurnLeft(double) ), canvasView, SLOT( slotTurnLeft(double) ) ); + connect( exe, SIGNAL( TurnRight(double) ), canvasView, SLOT( slotTurnRight(double) ) ); + connect( exe, SIGNAL( Center() ), canvasView, SLOT( slotCenter() ) ); + connect( exe, SIGNAL( SetPenWidth(int) ), canvasView, SLOT( slotSetPenWidth(int) ) ); + connect( exe, SIGNAL( PenUp() ), canvasView, SLOT( slotPenUp() ) ); + connect( exe, SIGNAL( PenDown() ), canvasView, SLOT( slotPenDown() ) ); + connect( exe, SIGNAL( SetFgColor(int, int, int) ), canvasView, SLOT( slotSetFgColor(int, int, int) ) ); + connect( exe, SIGNAL( SetBgColor(int, int, int) ), canvasView, SLOT( slotSetBgColor(int, int, int) ) ); + connect( exe, SIGNAL( ResizeCanvas(int, int) ), canvasView, SLOT( slotResizeCanvas(int, int) ) ); + connect( exe, SIGNAL( SpriteShow() ), canvasView, SLOT( slotSpriteShow() ) ); + connect( exe, SIGNAL( SpriteHide() ), canvasView, SLOT( slotSpriteHide() ) ); + connect( exe, SIGNAL( SpritePress() ), canvasView, SLOT( slotSpritePress() ) ); + connect( exe, SIGNAL( SpriteChange(int) ), canvasView, SLOT( slotSpriteChange(int) ) ); + connect( exe, SIGNAL( Print(QString) ), canvasView, SLOT( slotPrint(QString) ) ); + connect( exe, SIGNAL( FontType(QString, QString) ), canvasView, SLOT( slotFontType(QString, QString) ) ); + connect( exe, SIGNAL( FontSize(int) ), canvasView, SLOT( slotFontSize(int) ) ); + connect( exe, SIGNAL( WrapOn() ), canvasView, SLOT( slotWrapOn() ) ); + connect( exe, SIGNAL( WrapOff() ), canvasView, SLOT( slotWrapOff() ) ); + connect( exe, SIGNAL( Reset() ), canvasView, SLOT( slotReset() ) ); + + // START EXECUTION on the selected speed, and use the feedbacked boolean value + slotChangeSpeed(); + if ( exe->run() ) slotStatusBar(i18n("Done."), IDS_STATUS); + else slotStatusBar(i18n("Execution aborted."), IDS_STATUS); + finishExecution(); + + delete exe; // clean-up + + if ( errMsg->containsErrors() ) errMsg->display(); // if errors show them +} + +void MainWindow::slotPauseExecution() +{ + if ( pause->isChecked() ) + { + exe->pause(); + slotStatusBar(i18n("Execution paused."), IDS_STATUS); + } + else + { + emit unpauseExecution(); + slotStatusBar(i18n("Executing commands..."), IDS_STATUS); + } +} + +void MainWindow::slotAbortExecution() +{ + // To abort the executor, this can for instance be handy when the + // executer got stuck in a user made endless loops... + // to keep the program responding to interrupts like this, all the + // loops have possible breaks. This activates them breaks: + exe->abort(); +} + +void MainWindow::finishExecution() +{ + kdDebug(0)<<"############## EXECUTION FINISHED ##############"<<endl; + executing = false; + pause->setEnabled(false); + pause->setChecked(false); + run->setEnabled(true); + stop->setEnabled(false); + + // if execution finished on FullSpeed the selection does not match the last executed command -> clear-it + if (speed->currentItem() == 0) slotClearSelection(); + + // if coming from fullscreen-mode show the editor, menu- and statusbar + if (b_fullscreen) QTimer::singleShot( 1000, this, SLOT( slotFinishedFullScreenExecution() ) ); +} + +void MainWindow::slotChangeSpeed() +{ + emit changeSpeed( speed->currentItem() ); +} + + +// slots for logo functions that need to use the MainWindow class: + +void MainWindow::slotInputDialog(QString& value) +{ + value = KInputDialog::getText(i18n("Input"), value); +} + +void MainWindow::slotMessageDialog(QString text) +{ + KMessageBox::information( this, text, i18n("Message") ); +} + +// END + + + +// BEGIN editor connections (undo, redo, cut, copy, paste, cursor, selections, find, replace, linenumbers etc.) + +void MainWindow::slotEditor() +{ + KAction *a = editor->actionCollection()->action("set_confdlg"); + a->activate(); +} + +void MainWindow::slotSetHighlightstyle(QString langCode) +{ + KTextEditor::HighlightingInterface *hli = dynamic_cast<KTextEditor::HighlightingInterface*>(doc); + for (uint i = 0; i < hli->hlModeCount(); i++) + { + if (hli->hlModeName(i) == langCode) + { + hli->setHlMode(i); + return; + } + } + // and the fallback: + for (uint i = 0; i < hli->hlModeCount(); i++) + { + if(hli->hlModeName(i) == "en_US") hli->setHlMode(i); + } +} + + +void MainWindow::slotUndo() +{ + dynamic_cast<KTextEditor::UndoInterface*>(doc)->undo(); +} + +void MainWindow::slotRedo() +{ + dynamic_cast<KTextEditor::UndoInterface*>(doc)->redo(); +} + + +void MainWindow::slotCut() +{ + dynamic_cast<KTextEditor::ClipboardInterface*>(editor)->cut(); +} + +void MainWindow::slotCopy() +{ + dynamic_cast<KTextEditor::ClipboardInterface*>(editor)->copy(); +} + +void MainWindow::slotPaste() +{ + dynamic_cast<KTextEditor::ClipboardInterface*>(editor)->paste(); +} + + +void MainWindow::slotSelectAll() +{ + dynamic_cast<KTextEditor::SelectionInterface*>(doc)->selectAll(); +} + +void MainWindow::slotClearSelection() +{ + dynamic_cast<KTextEditor::SelectionInterface*>(doc)->clearSelection(); +} + +void MainWindow::slotFind() +{ + KAction *a = editor->actionCollection()->action("edit_find"); + a->activate(); +} + +void MainWindow::slotFindNext() +{ + KAction *a = editor->actionCollection()->action("edit_find_next"); + a->activate(); +} + +void MainWindow::slotFindPrevious() +{ + KAction *a = editor->actionCollection()->action("edit_find_prev"); + a->activate(); +} + +void MainWindow::slotReplace() +{ + KAction* a = editor->actionCollection()->action("edit_replace"); + a->activate(); +} + +void MainWindow::slotToggleInsert() +{ + KToggleAction *a = dynamic_cast<KToggleAction*>(editor->actionCollection()->action("set_insert")); + a->activate(); + if (a) statusBar()->changeItem(a->isChecked() ? i18n(" OVR ") : i18n(" INS "), IDS_INS); +} + +void MainWindow::slotIndent() +{ + KAction *a = editor->actionCollection()->action("tools_indent"); + a->activate(); +} + +void MainWindow::slotUnIndent() +{ + KAction *a = editor->actionCollection()->action("tools_unindent"); + a->activate(); +} + +void MainWindow::slotCleanIndent() +{ + KAction *a = editor->actionCollection()->action("tools_cleanIndent"); + a->activate(); +} + +void MainWindow::slotComment() +{ + KAction *a = editor->actionCollection()->action("tools_comment"); + a->activate(); +} + +void MainWindow::slotUnComment() +{ + KAction *a = editor->actionCollection()->action("tools_uncomment"); + a->activate(); +} + +void MainWindow::slotToggleLineNumbers() +{ + KToggleAction *a = dynamic_cast<KToggleAction*>( editor->actionCollection()->action("view_line_numbers") ); + a->activate(); +} + +void MainWindow::slotInsertText(QString str) +{ + uint StartLine, StartCol, EndLine, EndCol; + dynamic_cast<KTextEditor::ViewCursorInterface*>(editor)->cursorPositionReal(&StartLine, &StartCol); + dynamic_cast<KTextEditor::EditInterface*>(doc)->insertText(StartLine, StartCol, str); + dynamic_cast<KTextEditor::ViewCursorInterface*>(editor)->cursorPositionReal(&EndLine, &EndCol); + dynamic_cast<KTextEditor::SelectionInterface*>(doc)->setSelection(StartLine, StartCol, EndLine, EndCol); +} + +void MainWindow::slotSetCursorPos(uint row, uint col) +{ + dynamic_cast<KTextEditor::ViewCursorInterface*>(editor)->setCursorPositionReal(row - 1, col); + // kdDebug(0)<<"Cursor set to: ("<<row-1<<", "<<col<<")"<<endl; +} + +void MainWindow::slotSetSelection(uint StartLine, uint StartCol, uint EndLine, uint EndCol) +{ + dynamic_cast<KTextEditor::SelectionInterface*>(doc)->setSelection(StartLine - 1, StartCol - 1, EndLine - 1, EndCol - 1); + // kdDebug(0)<<"Selection set to: ("<<StartLine<<", "<<StartCol<<", "<<EndLine<<", "<<EndCol<<")"<<endl; +} +// END + + + +// BEGIN fullscreen functions + +void MainWindow::slotToggleFullscreen() +{ + if (!b_fullscreen) showFullScreen(); // both calls will generate event triggering updateFullScreen() + else if ( isFullScreen() ) showNormal(); +} + +bool MainWindow::event(QEvent* e) +{ + // executes updateFullScreen() after a ShowFullScreen or ShowNormal event got triggered + if (e->type() == QEvent::ShowFullScreen || e->type() == QEvent::ShowNormal) updateFullScreen(); + return KMainWindow::event(e); +} + +void MainWindow::updateFullScreen() +{ + if (isFullScreen() == b_fullscreen) return; + b_fullscreen = isFullScreen(); + if (m_fullscreen) m_fullscreen->setChecked(b_fullscreen); +} + +void MainWindow::slotFinishedFullScreenExecution() +{ + restartOrBackDialog = new RestartOrBack(this); // we have to make some to delete some + if ( errMsg->containsErrors() ) slotBackToFullScreen(); // straight back to edit if there where errors + else + { + connect( restartOrBackDialog, SIGNAL( user1Clicked() ), this, SLOT( slotRestartFullScreen() ) ); + connect( restartOrBackDialog, SIGNAL( user2Clicked() ), this, SLOT( slotBackToFullScreen() ) ); + connect( restartOrBackDialog, SIGNAL( finished() ), this, SLOT( slotBackToFullScreen() ) ); + restartOrBackDialog->show(); + restartOrBackDialog->move(50, 50); + } +} + +void MainWindow::slotBackToFullScreen() +{ + delete restartOrBackDialog; + editorDock->show(); + statusBar()->show(); + menuBar()->show(); + toolBar()->show(); +} + +void MainWindow::slotRestartFullScreen() +{ + delete restartOrBackDialog; + slotExecute(); +} + +// END + + + +// BEGIN configuration related functions + +void MainWindow::slotSettings() +{ + // Check if there is already a dialog, if so bring it to the foreground. + if ( KConfigDialog::showDialog("settings") ) return; + + // Create a new dialog with the same name as the above checking code. + KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self() ); + // connect the help + connect( dialog, SIGNAL( helpClicked() ), this, SLOT( slotSettingsHelp() ) ); + + // making the filling for the 'General' settings dept. + general = new QWidget(); + QGridLayout *generalLayout = new QGridLayout( general, 1, 1, 11, 6, "generalLayout"); + WidthHeightBox = new QGroupBox( i18n("Initial Canvas Size"), general ); + WidthHeightBox->setColumnLayout(0, Qt::Vertical ); + WidthHeightBox->layout()->setSpacing( 6 ); + WidthHeightBox->layout()->setMargin( 11 ); + QVBoxLayout *WidthHeightBoxLayout = new QVBoxLayout( WidthHeightBox->layout() ); + WidthHeightBoxLayout->setAlignment( Qt::AlignTop ); + QHBoxLayout *layout3 = new QHBoxLayout( 0, 0, 6, "layout3"); + QVBoxLayout *layout2 = new QVBoxLayout( 0, 0, 6, "layout2"); + + QVBoxLayout *layout1 = new QVBoxLayout( 0, 0, 6, "layout1"); + + kcfg_CanvasWidth = new KIntNumInput( WidthHeightBox, "kcfg_CanvasWidth" ); + kcfg_CanvasWidth->setValue( 400 ); + kcfg_CanvasWidth->setMinValue( 1 ); + kcfg_CanvasWidth->setReferencePoint( 1 ); + layout1->addWidget( kcfg_CanvasWidth ); + + kcfg_CanvasHeight = new KIntNumInput( WidthHeightBox, "kcfg_CanvasHeight" ); + kcfg_CanvasHeight->setValue( 300 ); + kcfg_CanvasHeight->setMinValue( 1 ); + kcfg_CanvasHeight->setReferencePoint( 1 ); + layout1->addWidget( kcfg_CanvasHeight ); + + WidthLabel = new QLabel( kcfg_CanvasWidth, i18n("Canvas &width:"), WidthHeightBox ); + layout2->addWidget( WidthLabel ); + HeightLabel = new QLabel( kcfg_CanvasHeight, i18n("Ca&nvas height:"), WidthHeightBox ); + layout2->addWidget( HeightLabel ); + layout3->addLayout( layout2 ); + + layout3->addLayout( layout1 ); + WidthHeightBoxLayout->addLayout( layout3 ); + QLabel* WidthHeightLabel = new QLabel(i18n("You need to restart before these settings have effect"), WidthHeightBox); + WidthHeightBoxLayout->addWidget( WidthHeightLabel ); + generalLayout->addWidget( WidthHeightBox, 0, 0 ); + general->resize( QSize(234, 109).expandedTo(minimumSizeHint()) ); + + dialog->addPage( general, i18n("General"), "package_settings", i18n("General Settings") ); + + // making the filling for the 'Language' settings dept. + QWidget *language = new QWidget(); + QGridLayout *languageLayout = new QGridLayout( language, 1, 1, 11, 6, "Form1Layout"); + QGroupBox *groupBox1 = new QGroupBox( language, "groupBox1" ); + groupBox1->setColumnLayout(0, Qt::Vertical ); + groupBox1->layout()->setSpacing( 6 ); + groupBox1->layout()->setMargin( 11 ); + QGridLayout *groupBox1Layout = new QGridLayout( groupBox1->layout() ); + groupBox1Layout->setAlignment( Qt::AlignTop ); + + QVBoxLayout *layout4 = new QVBoxLayout( 0, 0, 6, "layout4"); + + kcfg_LanguageComboBox = new KComboBox(groupBox1, "kcfg_LanguageComboBox"); + kcfg_LanguageComboBox->setEditable(false); + QStringList LogoLanguageList = Settings::logoLanguageList(); + // Add the full language names to the items + for ( QStringList::Iterator it = LogoLanguageList.begin(); it != LogoLanguageList.end(); ++it ) { + *it = KGlobal::locale()->twoAlphaToLanguageName( (*it).left(2) ) + " (" + *it + ")"; + } + kcfg_LanguageComboBox->insertStringList(LogoLanguageList); + + LanguageLabel = new QLabel(kcfg_LanguageComboBox, i18n("&Select the language for the Logo commands:"), groupBox1); + layout4->addWidget( LanguageLabel ); + layout4->addWidget( kcfg_LanguageComboBox ); + LanguageLabel->setBuddy( kcfg_LanguageComboBox ); + + groupBox1Layout->addLayout( layout4, 0, 0 ); + languageLayout->addWidget( groupBox1, 0, 0 ); + language->resize( QSize(373, 80).expandedTo(minimumSizeHint()) ); + + dialog->addPage( language, i18n("Language"), "locale", i18n("Language Settings") ); + + // When the user clicks OK or Apply we want to update our settings. + connect( dialog, SIGNAL( settingsChanged() ), this, SLOT( slotUpdateSettings() ) ); + + // Display the dialog is there where errors. + dialog->setInitialSize( QSize(550, 300) ); + dialog->show(); +} + +void MainWindow::slotUpdateSettings() +{ + // get the selected language as a language code + QString selectedLogoLanguage = kcfg_LanguageComboBox->currentText().section( "(", -1, -1 ).remove(")"); + // update the settings + Settings::setLogoLanguage( selectedLogoLanguage ); + Settings::setLanguageComboBox( kcfg_LanguageComboBox->currentItem() ); + Settings::writeConfig(); + // set the HLstyle + slotSetHighlightstyle( selectedLogoLanguage ); + // set the statusbar to display the language as just updated + // TODO maybe this language name can be more pretty by not using ".left(2)", ie "American English" would than be possible... [if this is possible this should be fixed at more places.] + KConfig entry(locate("locale", "all_languages")); + entry.setGroup(Settings::logoLanguage().left(2)); + slotStatusBar(i18n("Command language: %1").arg( entry.readEntry("Name") ), IDS_LANG); + + delete translate; // to update the currently used language + translate = new Translate(); +} + +void MainWindow::readConfig(KConfig *config) +{ + config->setGroup("General Options"); + m_recentFiles->loadEntries(config, "Recent Files"); + KConfig entry(locate("locale", "all_languages")); + entry.setGroup(Settings::logoLanguage().left(2)); + slotStatusBar(i18n("Command language: %1").arg( entry.readEntry("Name") ), IDS_LANG); +} + +void MainWindow::slotSettingsHelp() +{ + kapp->invokeHelp("settings-configure", "", ""); +} + +// END + + + +// BEGIN help related functions + +void MainWindow::slotContextHelp() +{ +// somehow the 'anchor' parameter of invokeHelp is not working correcctly (yet) + +// this is/was appearently a bug in KHTML that Waldo Bastian kindly and quikly fixed. + +// Ooh... and we also want a DCOPmethod to close the sidebar since it over-informs... +// Ooh3... we want fancy help (using KHTML) @ errormessage dialog +// Ooh4... And we might also have to keep track of the KHelpCenter instance we open so +// we will not end up with loads of them +// + +// IDEA!!: put all the keyword in a i18n("...") this will make translating them a lot easier!!! +// MAYBE THIS IS ALSO POSSIBLE FOR THE INTERPRETER!!!! +// this should be discussed with translators (and please think of the highlight-themes too (since +// right now thay can probably be translated with a simple perl script + + kdDebug(0)<<"help requested on this text: "<<helpKeyword<<endl; + + QString helpWord; + if ( helpKeyword == i18n("<no keyword>") ) + { + KMessageBox::information( this, i18n("There is currently no text under the cursor to get help on."), i18n("Nothing Under Cursor") ); + return; + } + else if ( helpKeyword == i18n("<number>") ) helpWord = "number"; + else if ( helpKeyword == i18n("<string>") ) helpWord = "string"; + else if ( helpKeyword == i18n("<assignment>") ) helpWord = "assignment"; + else if ( helpKeyword == i18n("<question>") ) helpWord = "questions"; + else if ( helpKeyword == i18n("<name>") ) helpWord = "name"; + else if ( helpKeyword == i18n("<comment>") ) helpWord = "comment"; + else + { + // make lowercase + helpWord = helpKeyword.lower(); + // if the key is an alias translate that alias to a key + if ( !translate->alias2key(helpKeyword).isEmpty() ) helpWord = translate->alias2key(helpKeyword); + else if ( !translate->name2key (helpKeyword).isEmpty() ) helpWord = translate->name2key (helpKeyword); + + // at this point helpKeyword should contain a valid + // section/anchor-id that can be used found in the doc + // if not... who cares :)... well let's put an debugMsg for that occasion: + else kdDebug(0)<<"Error in MainWindow::slotContextHelp: could not translate \""<<helpKeyword<<"\""<<endl; + } + + kdDebug(0)<<"trying to open a help page using this keyword: "<<helpWord<<endl; + + kapp->invokeHelp(helpWord, "", ""); + + QString help2statusBar; + if ( helpKeyword.startsWith("<") ) help2statusBar = helpKeyword; + else help2statusBar = i18n("\"%1\"").arg(helpKeyword); + slotStatusBar(i18n("Displaying help on %1").arg(help2statusBar), IDS_STATUS); +} + +void MainWindow::slotContextHelpUpdate() +{ + uint row, col; + dynamic_cast<KTextEditor::ViewCursorInterface*>(editor)->cursorPositionReal(&row, &col); + QString line = dynamic_cast<KTextEditor::EditInterface*>(doc)->textLine(row); + + // two shortcuts so we dont do all the CPU intensive regexp stuff when it not needed + if ( line.stripWhiteSpace().startsWith("#") ) + { + helpKeyword = i18n("<comment>"); + ContextHelp->setText( i18n("Help on: %1").arg(helpKeyword) ); + return; + } + if ( line.stripWhiteSpace().isEmpty() || line.mid(col-1,2).stripWhiteSpace().isEmpty() ) + { + helpKeyword = i18n("<no keyword>"); + ContextHelp->setText( i18n("Help on: %1").arg(helpKeyword) ); + return; + } + + int start, length, pos; + + pos = 0; + if ( line.contains('"') ) + { + QRegExp delimitedStrings("(\"[^\"\\\\\\r\\n]*(\\\\.[^\"\\\\\\r\\n]*)*\")"); + while ( delimitedStrings.search(line, pos) != -1 ) + { + // kdDebug(0)<<"stringsearch: >>"<<pos<<"<<"<<endl; + start = delimitedStrings.search(line, pos); + length = delimitedStrings.matchedLength(); // the length of the last matched string, or -1 if there was no match + if ( col >= (uint)start && col < (uint)(start+length) ) + { + helpKeyword = i18n("<string>"); + ContextHelp->setText( i18n("Help on: %1").arg(helpKeyword) ); + return; + } + pos += (length <= 0 ? 1 : length); + } + } + + + // except for "strings" this regexp effectively separates logo code in 'words' (in a very broad meaning) + QRegExp splitter("(([^ ,+\\-*/()=<>[!]|(?!==|<=|>=|!=))*)|(([ ,+\\-*/()=<>[!]|==|<=|>=|!=))"); + + pos = 0; + while (splitter.search(line, pos) != -1) + { + start = splitter.search(line, pos); + length = splitter.matchedLength(); + if ( col < (uint)start ) break; + if ( col >= (uint)start && col < (uint)(start+length) ) + { + QString cursorWord = line.mid( (uint)start, (uint)length ); + kdDebug(0)<<"cursorWord: '"<<cursorWord<<"'"<<endl; + + kdDebug(0)<<"(translate->name2key( cursorWord.lower() )): '"<<translate->name2key( cursorWord.lower() )<<"'"<<endl; + kdDebug(0)<<"(translate->alias2key( cursorWord.lower() )): '"<<translate->alias2key( cursorWord.lower() )<<"'"<<endl; + + if ( cursorWord.stripWhiteSpace().isEmpty() ) helpKeyword = i18n("<no keyword>"); + + else if ( !translate->name2key( cursorWord.lower() ).isEmpty() ) helpKeyword = cursorWord; + else if ( !translate->alias2key( cursorWord.lower() ).isEmpty() ) helpKeyword = cursorWord; + + else if ( cursorWord.find( QRegExp("[\\d.]+") ) == 0 ) helpKeyword = i18n("<number>"); + + else if ( cursorWord.find( QRegExp("[+\\-*/\\(\\)]") ) == 0 ) helpKeyword = i18n("<math>"); + + else if ( cursorWord == QString("=") ) helpKeyword = i18n("<assignment>"); + + else if ( cursorWord.find( QRegExp("==|<|>|<=|>=|!=") ) == 0 ) helpKeyword = i18n("<question>"); + + // if we come here we either have an ID of some sort or an error + // all we can do is try to catch some errors (TODO) and then... + + else helpKeyword = i18n("<name>"); + + ContextHelp->setText( i18n("Help on: %1").arg(helpKeyword) ); + return; + } + pos += (length <= 0 ? 1 : length); // the pos had to be increased with at least one + } + + // we allready cached some in the beginning of this method; yet its still needed as fall-through + helpKeyword = i18n("<no keyword>"); + ContextHelp->setText( i18n("Help on: %1").arg(helpKeyword) ); +} + +// END + + + +// BEGIN misc. functions + +void MainWindow::slotColorPicker() +{ + // in the constructor picker is initialised as 0 + // if picker is 0 when this funktion is called a colorpickerdialog is created and connected + if (picker == 0) + { + picker = new ColorPicker(this); + if(picker == 0) return; // safety + connect( picker, SIGNAL( visible(bool) ), colorpicker, SLOT( setChecked(bool) ) ); + connect( picker, SIGNAL( ColorCode(QString) ), this, SLOT( slotInsertText(QString) ) ); + } + // if picker is not 0, there is a colorpickerdialog which only needs to be shown OR hidden + if ( picker->isHidden() ) + { + picker->show(); + colorpicker->setChecked(true); + } + else + { + picker->hide(); + colorpicker->setChecked(false); + } +} + +void MainWindow::slotUpdateCanvas() +{ + // fixes a non updateing bug + // I tried doing this by connecting Canvas's resized to baseWidget's update... + // but i had no luck :( ... this worked though :) + canvasView->hide(); + canvasView->show(); +} + +// END + + +#include "kturtle.moc" diff --git a/kturtle/src/kturtle.desktop b/kturtle/src/kturtle.desktop new file mode 100644 index 00000000..0b541e05 --- /dev/null +++ b/kturtle/src/kturtle.desktop @@ -0,0 +1,124 @@ +[Desktop Entry] +DocPath=kturtle/index.html +Name=KTurtle +Name[af]=KSkilpad +Name[bn]=কে-টার্টল +Name[fi]=Konna +Name[hi]=के-टर्टल +Name[hr]=KKornjača +Name[is]=KSkjaldbaka +Name[ne]=केडीई टर्टल +Name[sv]=Kturtle +Name[tg]=KСангпушт +GenericName=Logo Programming Environment +GenericName[af]=Logo Programmeeromgewing +GenericName[be]=Асяроддзе праграмавання на мове Logo +GenericName[bg]=Програмиране на Лого +GenericName[bn]=লোগো প্রোগ্রামিং +GenericName[bs]=Logo programsko okruženje +GenericName[ca]=Entorn de programació de Logo +GenericName[cs]=Logo programovací prostředí +GenericName[csb]=Òkrãżi do programòwania w Logo +GenericName[cy]=Amgylchedd Rhaglennu Logo +GenericName[da]=Logo programmeringsmiljø +GenericName[de]=Logo-Programmierumgebung +GenericName[el]=Περιβάλλον προγραμματισμού σε Logo +GenericName[es]=Entorno de programación de Logo +GenericName[et]=Logo programmeerimise keskkond +GenericName[eu]=Logo programazio ingurunea +GenericName[fa]=محیط برنامهسازی لوگو +GenericName[fi]=Logo-ohjelmointiympäristö +GenericName[fr]=Environnement de programmation en Logo +GenericName[ga]=Timpeallacht Ríomhchláraithe Logo +GenericName[gl]=Ambiente de Programación en Logo +GenericName[he]=סביבת תכנות לשפת לוגו +GenericName[hi]=लोगो डेवलपमेंट माहौल. +GenericName[hr]=Okruženje za programiranje logotipa +GenericName[hu]=Logo-programozás +GenericName[is]=Logo forritunarumhverfi +GenericName[it]=Ambiente di programmazione Logo +GenericName[ja]=ロゴプログラミング環境 +GenericName[ka]=პროგრამირება "ლოგოზე" +GenericName[km]=បរិស្ថានកម្មវិធីរូបសញ្ញា +GenericName[lt]=Logo programavimo aplinka +GenericName[nb]=Programmeringsmiljø for Logo +GenericName[nds]="Logo"-Programmsmeed +GenericName[ne]=लोगो प्रोग्रामिङ परिवेश +GenericName[nl]=Logo programmeeromgeving +GenericName[nn]=Programmeringsmiljø for Logo +GenericName[pl]=Środowisko do programowania w Logo +GenericName[pt]=Ambiente de Programação em Logo +GenericName[pt_BR]=Ambiente de programação Logo +GenericName[ru]=Обучение программированию на языке Лого +GenericName[sk]=Programovacie prostredie pre Logo +GenericName[sl]=Programsko okolje za Logo +GenericName[sr]=Програмерско окружење за Logo +GenericName[sr@Latn]=Programersko okruženje za Logo +GenericName[sv]=Logo-programmeringsmiljö +GenericName[ta]=சிண்ண நிரல் உருவாக சூழல் +GenericName[tg]=Муҳити барноманависии Logo +GenericName[tr]=Logo Programlama Ortamı +GenericName[uk]=Середовище програмування на мові Logo +GenericName[vi]=Môi trường Lập trình Logo +GenericName[zh_CN]=Logo 编程环境 +GenericName[zh_TW]=Logo 程式環境 +Exec=kturtle -caption "%c" %i %m +Icon=kturtle +Type=Application +Comment=A KDE Logo programming environment +Comment[af]='n KDE Logo programmeeromgewing +Comment[be]=Асяроддзе KDE для праграмавання на мове Logo +Comment[bg]=Среда за програмиране на Лого +Comment[bn]=কেডিই'র লোগো প্রোগ্রামিং +Comment[bs]=KDE programsko okruženje za Logo +Comment[ca]=Un entorn de programació de Logo per a KDE +Comment[cs]=KDE Logo programovací prostředí +Comment[csb]=Òkrãżi do programòwania w Logo dla KDE +Comment[cy]=Amgylchedd Rhaglennu Logo KDE +Comment[da]=Et KDE Logo programmeringsmiljø +Comment[de]=Eine KDE-Programmierumgebung für Logo +Comment[el]=Ένα περιβάλλον προγραμματισμού σε Logo για το KDE +Comment[eo]=KDE Logo-lingva programa ĉirkaŭaĵo +Comment[es]=Un entorno de programación de Logo para KDE +Comment[et]=KDE Logo programmeerimise keskkond +Comment[eu]=KDE-ren Logo programazio ingurune bat +Comment[fa]=محیط برنامهسازی لوگوKDE +Comment[fi]=KDE Logo-ohjelmointiympäristö +Comment[fr]=Un environnement de programmation en Logo pour KDE +Comment[ga]=Timpeallacht ríomhchláraithe Logo le haghaidh KDE +Comment[gl]=Ambiente de programación en Logo para KDE +Comment[he]=סביבצ עבודה של KDE לשפת לוגו +Comment[hi]=केडीई हेतु लोगो प्रोग्रामिंग माहौल +Comment[hr]=KDE okruženje za programiranje logotipa +Comment[hu]=KDE-alapú Logo-programozási környezet +Comment[is]=KDE Logo forritunarumhverfi +Comment[it]=Ambiente di programmazione Logo per KDE +Comment[ja]=KDE ロゴプログラミング環境 +Comment[ka]=KDE "ლოგოზე" პროგრამირების გარემო +Comment[km]=បរិស្ថានកម្មវិធីរូបសញ្ញារបស់ KDE +Comment[lt]=KDE Logo programavimo aplinka +Comment[nb]=Et KDE-miljø for Logo-programmering +Comment[nds]=En KDE-Programmsmeed för "Logo" +Comment[ne]= KDE लोगो प्रोग्रामिङ परिवेश +Comment[nl]=Logo-programmeeromgeving voor KDE +Comment[nn]=Eit KDE-miljø for Logo-programmering +Comment[pl]=Środowisko do programowania w Logo dla KDE +Comment[pt]=Ambiente de programação em Logo para o KDE +Comment[pt_BR]=Um ambiente KDE de programação Logo +Comment[ru]=Обучение программированию на языке Лого +Comment[sk]=KDE programovacie prostredie pre Logo +Comment[sl]=Okolje za programiranje v jeziku Logo za KDE +Comment[sr]=KDE програмерско окружење за Logo +Comment[sr@Latn]=KDE programersko okruženje za Logo +Comment[sv]=Logo-programmeringsmiljö för KDE +Comment[ta]=ஒரு KDE சின்னத்தின் நிரலாக்க சூழல் +Comment[tg]=Муҳити барноманависии KDE Logo +Comment[tr]=Bir KDE Logo programlama ortamı +Comment[uk]=KDE середовище програмування на мові Logo +Comment[vi]=Một môi trường lập trình Logo cho KDE +Comment[zh_CN]=KDE Logo 编程环境 +Comment[zh_TW]=KDE 的 Logo 程式設計環境 +Path= +Terminal=false +X-KDE-StartupNotify=true +Categories=Qt;KDE;Education; diff --git a/kturtle/src/kturtle.h b/kturtle/src/kturtle.h new file mode 100644 index 00000000..8ffd55df --- /dev/null +++ b/kturtle/src/kturtle.h @@ -0,0 +1,182 @@ +/* + Copyright (C) 2003-04 Cies Breijs <cies # kde ! nl> + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef _KTURTLE_H_ +#define _KTURTLE_H_ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include <qgroupbox.h> + +#include <kcombobox.h> +#include <klineedit.h> +#include <knuminput.h> + +#include <kparts/mainwindow.h> +#include <ktexteditor/editinterface.h> +#include <ktexteditor/view.h> + +#include "canvas.h" +#include "dialogs.h" +#include "executer.h" +#include "parser.h" + + +class KRecentFilesAction; + + +class MainWindow : public KParts::MainWindow +{ + Q_OBJECT + + public: + MainWindow(KTextEditor::Document* = 0L); + virtual ~MainWindow(); + + + signals: + void changeSpeed(int speed); + void unpauseExecution(); + + + protected slots: + void slotNewFile(); + void slotOpenFile(const KURL& url = NULL); + void slotOpenExample(); + void slotSaveFile(); + void slotSaveAs(); + void slotSaveCanvas(); + void slotPrint(); + + void slotExecute(); + void slotPauseExecution(); + void slotAbortExecution(); + void slotChangeSpeed(); + void slotInputDialog(QString& value); + void slotMessageDialog(QString text); + + void slotEditor(); + void slotSetHighlightstyle(QString langCode); + void slotUndo(); + void slotRedo(); + void slotCut(); + void slotCopy(); + void slotPaste(); + void slotSelectAll(); + void slotClearSelection(); + void slotFind(); + void slotFindNext(); + void slotFindPrevious(); + void slotReplace(); + void slotToggleInsert(); + void slotInsertText(QString); + void slotToggleLineNumbers(); + void slotIndent(); + void slotUnIndent(); + void slotCleanIndent(); + void slotComment(); + void slotUnComment(); + void slotSetCursorPos(uint row, uint column); + void slotSetSelection(uint StartLine, uint StartCol, uint EndLine, uint EndCol); + + void slotSettings(); + void slotUpdateSettings(); + void slotSettingsHelp(); + + void slotContextHelp(); + void slotContextHelpUpdate(); + + void slotStatusBar(QString text, int place); + void slotCursorStatusBar(); + + void slotToggleFullscreen(); + void slotFinishedFullScreenExecution(); + void slotRestartFullScreen(); + void slotBackToFullScreen(); + + void slotColorPicker(); + void slotUpdateCanvas(); + + + protected: + void setupActions(); + void setupEditor(); + void setupCanvas(); + void setupStatusBar(); + + void loadFile(const KURL& url); + void writeFile(const KURL& url); + bool queryClose(); + + void finishExecution(); + void readConfig(KConfig *config); + + void updateFullScreen(); + virtual bool event(QEvent* e); + + Canvas *canvasView; + QWidget *baseWidget; + QGridLayout *baseLayout; + QDockWindow *editorDock; + Executer *exe; + ErrorMessage *errMsg; + ColorPicker *picker; + RestartOrBack *restartOrBackDialog; + + KAction *run; + KAction *stop; + KAction *openExAction; + KAction *openFileAction; + KAction *newAction; + KAction *ContextHelp; + KToggleAction *pause; + KToggleAction *m_fullscreen; + KToggleAction *colorpicker; + KSelectAction *speed; + KRecentFilesAction *m_recentFiles; + + KTextEditor::View *view() const { return editor; } + KTextEditor::EditInterface *editorInterface; + KTextEditor::Document *doc; + KTextEditor::View *editor; + + bool executing; + bool b_fullscreen; + bool b_editorShown; + + QString helpKeyword; + Translate *translate; + KURL CurrentFile; + + // configuration related + QWidget *general; + QWidget *language; + QGroupBox *WidthHeightBox; + QLabel *WidthLabel; + QLabel *HeightLabel; + KIntNumInput *kcfg_CanvasWidth; + KIntNumInput *kcfg_CanvasHeight; + KComboBox *kcfg_LanguageComboBox; + QLabel *LanguageLabel; +}; + + +#endif // _KTURTLE_H_ diff --git a/kturtle/src/kturtle.kcfg b/kturtle/src/kturtle.kcfg new file mode 100644 index 00000000..1dba5092 --- /dev/null +++ b/kturtle/src/kturtle.kcfg @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > +<kcfgfile name="kturtlerc"/> + + <group name="general"> + <entry name="CanvasWidth" type="Int"> + <label>The width of the canvas in pixels</label> + <default>300</default> + </entry> + <entry name="CanvasHeight" type="Int"> + <label>The height of the canvas in pixels</label> + <default>250</default> + </entry> + </group> + + + <group name="language"> + <entry name="LogoLanguageList" type="StringList"> + <label>The list of the available languages for the Logo commands</label> + <code> + + // This StringList (LogoLanguageList) is a list of all possible language codes all possible [en_US, fr, nl, etc.]. It is used to (1) find the LogoLanguage variable (that is defined later on), and (2) in the Settings dialog to fill the language ComboBox. Better not to dupe this code. The LogoLanguageList is made pretty in the slotSettings class of kturtle.cpp. + + // We assume keyword and highlight files come in pairs (if not there will be no highlight style) + + QStringList xmlFiles = KGlobal::dirs()->findAllResources("data", "kturtle/data/*.xml"); + QStringList LogoLanguageList; + QString xmlTranslationFiles; // for debug purposes + for ( QStringList::Iterator it = xmlFiles.begin(); it != xmlFiles.end(); ++it ) { + QString xmlFile(*it); + xmlTranslationFiles += xmlFile.section('.', -2, -2).append(", "); // for debug purposes + // build list of language codes (en_US, nl, etc.) + LogoLanguageList += xmlFile.section('.', -2, -2); + } + kdDebug(0) << "Available translations of Logo: "<< xmlTranslationFiles <<endl; + LogoLanguageList.sort(); + + </code> + <default code="true">LogoLanguageList</default> + </entry> + + + <entry name="LogoLanguage" type="String"> + <label>The language of the Logo commands</label> + <code> + + // This is the String (LogoLanguage) that actually gets picked up by the app to know what XML files (for HLstyle and Logo language) to use. It is not directly changeable by the ComboBox but is translated from the Ints that ComboBox LogoLanguageList emits. It is better to have the logic that changes the Ints to Strings at the settings dept. + + // The KconfigXT framework makes shure the value that is saved in the kturtlerc file will be used in stead of the defaultLanguage that we find here. + + // Get the desktops language + KConfigBase *globalConf = KGlobal::config(); + globalConf->setGroup("Locale"); + QString desktopLanguage = globalConf->readEntry("Language"); + kdDebug(0) << "desktopLanguage: "<< desktopLanguage <<endl; + + QString defaultLanguage = "en_US"; // init to the fall back value + int itemCount = 0; // for the LanguageComboBox + for (QStringList::Iterator it = LogoLanguageList.begin(); it != LogoLanguageList.end(); ++it ) { + QString testLanguage(*it); + if (testLanguage == desktopLanguage) { // exact match + defaultLanguage = testLanguage; + break; + } else if ( testLanguage.left(2) == desktopLanguage.left(2) ) { // close match (different dialect) + // The default language must still be one of the kturtle languages. + // defaultLanguage = testLanguage.left(2); + defaultLanguage = testLanguage; + break; + } + itemCount++; + } + kdDebug(0) << "Logo command language: "<< defaultLanguage <<endl; + + </code> + <default code="true">defaultLanguage</default> + </entry> + + + <entry name="LanguageComboBox" type="Int"> + <label>The value of the ComboBox</label> + <code> + + // This is the parameter that actually gets changed (and emits the signal to the dialog to enable the Apply and Defaults buttons). This value is saved in the kturtlerc file but is never used. + + </code> + <default code="true">itemCount</default> + </entry> + + </group> +</kcfg> diff --git a/kturtle/src/kturtleui.rc b/kturtle/src/kturtleui.rc new file mode 100644 index 00000000..d5180799 --- /dev/null +++ b/kturtle/src/kturtleui.rc @@ -0,0 +1,97 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="kturtle" version="2" > +<MenuBar> + <Menu name="file" noMerge="1"><text>&File</text> + <Action name="file_new" /> + <Action name="file_open" /> + <Action name="file_open_recent" /> + <Action name="open_examples" /> + <Separator /> + <Action name="file_save" /> + <Action name="file_save_as" /> + <Action name="save_canvas" /> + <Separator /> + <Action name="speed" /> + <Action name="run" /> + <Action name="pause" /> + <Action name="stop" /> + <Separator /> + <Action name="file_print" /> + <Separator /> + <Action name="file_quit" /> + </Menu> + + <Menu name="edit" noMerge="1"><text>&Edit</text> + <Action name="edit_undo" /> + <Action name="edit_redo" /> + <Separator /> + <Action name="edit_cut" /> + <Action name="edit_copy" /> + <Action name="edit_paste" /> + <Separator /> + <Action name="edit_find" /> + <Action name="edit_find_next" /> + <Action name="edit_replace" /> + </Menu> + + <Menu name="view" noMerge="1"><text>&View</text> + <Action name="full_screen" /> + <Separator /> + <Action name="line_numbers" /> + </Menu> + + <Menu name="tools" noMerge="1"><text>&Tools</text> + <Action name="color_picker" /> + <Separator /> + <Action name="edit_indent" /> + <Action name="edit_unindent" /> + <Action name="edit_cleanIndent" /> + <Separator /> + <Action name="edit_comment" /> + <Action name="edit_uncomment" /> + </Menu> + + <Menu name="settings" noMerge="1"><text>&Settings</text> + <Merge name="StandardToolBarMenuHandler" /> + <Action name="options_show_statusbar"/> + <Separator /> + <Menu name="Advanced settings"><text>&Advanced Settings</text> + <Action name="set_confdlg" /> + <Action name="options_configure_keybinding"/> + <Action name="options_configure_toolbars"/> + </Menu> + <Action name="options_configure"/> + </Menu> + + <Menu name="help"><text>&Help</text> + <Action name="context_help" /> + </Menu> + </MenuBar> + +<ToolBar name="mainToolBar" noMerge="1" fullWidth="true" > + <Action name="file_new" /> + <Action name="file_open" /> + <Separator /> + <Action name="file_save" /> + <Action name="file_save_as" /> + <Separator /> + <Action name="file_print" /> + <Separator /> + <Action name="edit_undo" /> + <Action name="edit_redo" /> + <Separator /> + <Action name="edit_cut" /> + <Action name="edit_copy" /> + <Action name="edit_paste" /> + <Separator /> + <Action name="color_picker" /> + <Separator /> + <Action name="full_screen" /> + <Separator /> + <Action name="speed" /> + <Action name="run" /> + <Action name="pause" /> + <Action name="stop" /> +</ToolBar> + +</kpartgui> diff --git a/kturtle/src/lexer.cpp b/kturtle/src/lexer.cpp new file mode 100644 index 00000000..1a480641 --- /dev/null +++ b/kturtle/src/lexer.cpp @@ -0,0 +1,411 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <qdom.h> +#include <qfile.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "settings.h" + +#include "lexer.h" + + +Lexer::Lexer(QTextIStream& iStream) +{ + inputStream = &iStream; + row = 1; + col = 1; + prevCol = 1; + translate = new Translate(); +} + + +Token Lexer::lex() +{ + skipSpaces(); // skips the white space that it quite likely (indentation) infront of the Token + + Token currentToken; + currentToken.type = tokNotSet; // not really needed + currentToken.look = ""; + currentToken.value = 0; + currentToken.start.row = row; + currentToken.start.col = col; + + QChar currentChar = getChar(); + + if ( inputStream->atEnd() ) + { + kdDebug(0)<<"Lexer::lex(), got EOF."<<endl; + currentToken.type = tokEOF; + currentToken.look = "EOF"; + ungetChar(currentChar); // unget the currentChar and fix the row/col values + return currentToken; + } + + if (currentChar == '#') + { + while ( !inputStream->atEnd() && !(currentChar == '\x0a' || currentChar == '\n') ) + currentChar = getChar(); + } + + // if (currentChar.category() == QChar::Separator_Line) somehow doesnt work + if (currentChar == '\x0a' || currentChar == '\n') + { + currentToken.type = tokEOL; + currentToken.look = "EOL"; + } + else if (currentChar.isLetter() || currentChar == '[' || currentChar == ']') + { + ungetChar(currentChar); + // sets currentToken.look by reference, and set the currentToken.type to tokUnknown + currentToken.type = getWord(currentToken.look); + setTokenType(currentToken); // gets the actual tokenType + } + else if ( currentChar.isNumber() ) + { + ungetChar(currentChar); + // set currentToken.value/look by reference, and set the currentToken.type to tokNumber + currentToken.type = getNumber(currentToken.value, currentToken.look); + } + else if (currentChar == '>') + { + currentChar = getChar(); + if (currentChar == '=') + { + currentToken.type = tokGe; + currentToken.look = ">="; + } + else + { + ungetChar(currentChar); + currentToken.type = tokGt; + currentToken.look = ">"; + } + } + else if (currentChar == '<') + { + currentChar = getChar(); + if ( currentChar == '=' ) + { + currentToken.type = tokLe; + currentToken.look = "<="; + } + else + { + ungetChar(currentChar); + currentToken.type = tokLt; + currentToken.look = ">"; + } + } + else if (currentChar == '!') + { + currentChar = getChar(); + if (currentChar == '=') + { + currentToken.type = tokNe; + currentToken.look = "!="; + } + else + { + ungetChar(currentChar); + currentToken.type = tokNot; + currentToken.look = "!"; + } + } + else if (currentChar == '=') + { + currentChar = getChar(); + if (currentChar == '=') + { + currentToken.type = tokEq; + currentToken.look = "=="; + } + else + { + ungetChar(currentChar); + currentToken.type = tokAssign; + currentToken.look = "="; + } + } + else if (currentChar == '(') + { + currentToken.type = tokBraceOpen; + currentToken.look = "("; + } + else if (currentChar == ')') + { + currentToken.type = tokBraceClose; + currentToken.look = ")"; + } + else if (currentChar == '+') + { + currentToken.type = tokPlus; + currentToken.look = "+"; + } + else if (currentChar == '-') + { + currentToken.type = tokMinus; + currentToken.look = "-"; + } + else if (currentChar == '*') + { + currentToken.type = tokMul; + currentToken.look = "*"; + } + else if (currentChar == '/') + { + currentToken.type = tokDev; + currentToken.look = "/"; + } + else if (currentChar == ',') + { + currentToken.type = tokComma; + currentToken.look = ","; + } + else if (currentChar == '"') + { + getString(currentToken); + } + else + { + currentToken.type = tokUnknown; + currentToken.look = currentChar; + } + + currentToken.end.row = row; + currentToken.end.col = col; + return currentToken; +} + + + +// PRIVATEs + +QChar Lexer::getChar() +{ + QChar c; + if ( !putBackChar.isNull() ) + { + c = putBackChar; // use the char that is stored to be put back + // kdDebug(0)<<"Lexer::getChar(), restored: '"<<c<<"' @ ("<<row<<", "<<col<<")"<<endl; + putBackChar = QChar(); // and set putBackChar back to NULL + if (c == '\x0a' || c == '\n') + { + row++; + prevCol = col; + col = 1; + } + else + { + col++; + } + } + else + { + *inputStream >> c; // take a QChar of the inputStream + // kdDebug(0)<<"Lexer::getChar(): '"<<c<<"' @ ("<<row<<", "<<col<<")"<<endl; + if (c == '\x0a' || c == '\n') + { + row++; + prevCol = col; + col = 1; + } + else + { + col++; + } + } + return c; +} + +void Lexer::ungetChar(QChar c) +{ + if (c == '\x0a' || c == '\n') + { + row--; + col = prevCol; + } + else + { + col--; + } + putBackChar = c; + // kdDebug(0)<<"Lexer::ungetChar(), saved char: '"<<c<<"' and steped back to ("<<row<<", "<<col<<")"<<endl; +} + +int Lexer::getWord(QString& word) +{ + // kdDebug(0)<<"Lexer::getWord()"<<endl; + QChar currentChar = getChar(); + if ( currentChar.isLetter() || currentChar == '[' || currentChar == ']' ) { + while ( ( currentChar.isLetterOrNumber() || currentChar == '_' || currentChar == '[' || currentChar == ']' ) && !inputStream->atEnd() ) + { + word += currentChar; + currentChar = getChar(); + } + kdDebug(0)<<"Lexer::getWord(), got NAME: '"<<word<<"'"<<endl; + ungetChar(currentChar); //read one too much + return tokUnknown; // returns tokUnknown, actual token is to be determained later in Lexer::setTokenType + } + else return tokError; +} + +void Lexer::setTokenType(Token& currentToken) +{ + if (currentToken.type == tokUnknown) + { + // make lowercase copy of the word as it was found in the inputStream + QString k = currentToken.look.lower(); + // if the key is an alias translate that alias to a key + if ( !translate->alias2key(k).isEmpty() ) k = translate->alias2key(k); + + if (k == translate->name2key("begin") ) currentToken.type = tokBegin; + else if (k == translate->name2key("end") ) currentToken.type = tokEnd; + else if (k == translate->name2key("while") ) currentToken.type = tokWhile; + else if (k == translate->name2key("if") ) currentToken.type = tokIf; + else if (k == translate->name2key("else") ) currentToken.type = tokElse; + else if (k == translate->name2key("for") ) currentToken.type = tokFor; + else if (k == translate->name2key("to") ) currentToken.type = tokTo; + else if (k == translate->name2key("step") ) currentToken.type = tokStep; + else if (k == translate->name2key("and") ) currentToken.type = tokAnd; + else if (k == translate->name2key("or") ) currentToken.type = tokOr; + else if (k == translate->name2key("not") ) currentToken.type = tokNot; + else if (k == translate->name2key("return") ) currentToken.type = tokReturn; + else if (k == translate->name2key("break") ) currentToken.type = tokBreak; + else if (k == translate->name2key("run") ) currentToken.type = tokRun; + else if (k == translate->name2key("foreach") ) currentToken.type = tokForEach; + else if (k == translate->name2key("in") ) currentToken.type = tokIn; + + else if (k == translate->name2key("learn") ) currentToken.type = tokLearn; + + else if (k == translate->name2key("clear") ) currentToken.type = tokClear; + else if (k == translate->name2key("go") ) currentToken.type = tokGo; + else if (k == translate->name2key("gox") ) currentToken.type = tokGoX; + else if (k == translate->name2key("goy") ) currentToken.type = tokGoY; + else if (k == translate->name2key("forward") ) currentToken.type = tokForward; + else if (k == translate->name2key("backward") ) currentToken.type = tokBackward; + else if (k == translate->name2key("direction") ) currentToken.type = tokDirection; + else if (k == translate->name2key("turnleft") ) currentToken.type = tokTurnLeft; + else if (k == translate->name2key("turnright") ) currentToken.type = tokTurnRight; + else if (k == translate->name2key("center") ) currentToken.type = tokCenter; + else if (k == translate->name2key("setpenwidth") ) currentToken.type = tokSetPenWidth; + else if (k == translate->name2key("penup") ) currentToken.type = tokPenUp; + else if (k == translate->name2key("pendown") ) currentToken.type = tokPenDown; + else if (k == translate->name2key("setfgcolor") ) currentToken.type = tokSetFgColor; + else if (k == translate->name2key("setbgcolor") ) currentToken.type = tokSetBgColor; + else if (k == translate->name2key("resizecanvas") ) currentToken.type = tokResizeCanvas; + else if (k == translate->name2key("spriteshow") ) currentToken.type = tokSpriteShow; + else if (k == translate->name2key("spritehide") ) currentToken.type = tokSpriteHide; + else if (k == translate->name2key("spritepress") ) currentToken.type = tokSpritePress; + else if (k == translate->name2key("spritechange") ) currentToken.type = tokSpriteChange; + + else if (k == translate->name2key("do") ) currentToken.type = tokDo; // dummy commands + + else if (k == translate->name2key("message") ) currentToken.type = tokMessage; + else if (k == translate->name2key("inputwindow") ) currentToken.type = tokInputWindow; + else if (k == translate->name2key("print") ) currentToken.type = tokPrint; + else if (k == translate->name2key("fonttype") ) currentToken.type = tokFontType; + else if (k == translate->name2key("fontsize") ) currentToken.type = tokFontSize; + else if (k == translate->name2key("repeat") ) currentToken.type = tokRepeat; + else if (k == translate->name2key("random") ) currentToken.type = tokRandom; + else if (k == translate->name2key("wait") ) currentToken.type = tokWait; + else if (k == translate->name2key("wrapon") ) currentToken.type = tokWrapOn; + else if (k == translate->name2key("wrapoff") ) currentToken.type = tokWrapOff; + else if (k == translate->name2key("reset") ) currentToken.type = tokReset; + else + { + kdDebug(0)<<"Lexer::setTokenType, found UNKNOWN word @ ("<<currentToken.start.row<<", "<<currentToken.start.col<<"), can be anything"<<endl; + // t.type = tokUnknown; is allready + } + + kdDebug(0)<<"Lexer::setTokenType, found tok-number: '"<<currentToken.type<<"' with the key: '"<<k<<"' @ ("<<currentToken.start.row<<", "<<currentToken.start.col<<")"<<endl; + } +} + + +void Lexer::skipSpaces() +{ + // kdDebug(0)<<"Lexer::skipSpaces(), skipping SPACES."<<endl; + QChar currentChar = getChar(); + // when the Separator_* groups can be identified in the QChar thing would be easier + while ( !inputStream->atEnd() && ( currentChar.isSpace() && !(currentChar == '\x0a' || currentChar == '\n') ) ) + { + currentChar = getChar(); + } + ungetChar(currentChar); // unget the tokEOL we likely just found +} + + +int Lexer::getNumber(Value& num, QString& look) +{ + // by reference the value (Value) and look part are set + // kdDebug(0)<<"Lexer::getNumber()"<<endl; + bool hasPoint = false; + QChar currentChar = getChar(); + if ( currentChar.isNumber() ) + { + while ( ( currentChar.isNumber() || (currentChar == '.' && !hasPoint) ) && !inputStream->atEnd() ) + { + if (currentChar == '.') + { + hasPoint = true; + } + look += currentChar; + currentChar = getChar(); + } + ungetChar(currentChar); //read one too much + num.setNumber( look.toDouble() ); + kdDebug(0)<<"Lexer::getNumber(), got NUMBER: '"<<num.Number()<<"'"<<endl; + return tokNumber; + } + else return tokError; +} + +void Lexer::getString(Token& currentToken) +{ + QString str = "\""; // start with a " cauz it just got lost + QChar currentChar = QChar(); // start empty + while ( currentChar != '"' && !(currentChar == '\x0a' || currentChar == '\n') && !inputStream->atEnd() ) + { + currentChar = getChar(); + if (currentChar == '\\') // escape sequence + { + currentChar = getChar(); + switch (currentChar) + { + case 'n': str += '\n'; break; + case 't': str += '\t'; break; + case 'f': str += '\f'; break; + case '"': str += '"'; break; + } + } + else if (currentChar == '\x0a' || currentChar == '\n') // if the user forgot to delimit the string + { + ungetChar(currentChar); + break; + } + else str += currentChar; + } + currentToken.type = tokString; + currentToken.look = str; + + kdDebug(0)<<"Lexer::getStringConstant, got STRINGCONSTANT: "<<currentToken.look<<"'"<<endl; +} + diff --git a/kturtle/src/lexer.h b/kturtle/src/lexer.h new file mode 100644 index 00000000..49317f8e --- /dev/null +++ b/kturtle/src/lexer.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef _LEXER_H_ +#define _LEXER_H_ + +#include <qmap.h> +#include <qstring.h> +#include <qtextstream.h> + +#include "token.h" +#include "translate.h" +#include "value.h" + + +class Lexer +{ + public: + Lexer(QTextIStream&); + ~Lexer() {} + + Token lex(); // returns the next Token, skipping spaces + + + private: + QChar getChar(); + void ungetChar(QChar); + int getWord(QString&); + void skipSpaces(); + void setTokenType(Token&); + int getNumber(Value&, QString&); + void getString(Token&); + + QTextIStream *inputStream; + Translate *translate; + uint row, col, prevCol; + QChar putBackChar; +}; + + +#endif // _LEXER_H_ + + diff --git a/kturtle/src/main.cpp b/kturtle/src/main.cpp new file mode 100644 index 00000000..d9bf7eab --- /dev/null +++ b/kturtle/src/main.cpp @@ -0,0 +1,115 @@ +/* + Copyright (C) 2003 Cies Breijs <cies # kde ! nl> + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <kaboutdata.h> +#include <kapplication.h> +#include <kcmdlineargs.h> + +#include "kturtle.h" + + +static const char description[] = + I18N_NOOP("Educational Programming Environment using the Logo programming language"); + +static const char version[] = "0.6"; + +static const char copyright[] = I18N_NOOP("(C) 2003 The KTurtle Authors"); + +static const char website[] = "http://edu.kde.org/kturtle"; + +static KCmdLineOptions options[] = +{ +/// @todo so some about the command line arguments... Something like: +// { "e <argument>", I18N_NOOP( "sets the folder with the examples to <argument>" ), 0 } +// { "t <argument>", I18N_NOOP( "sets the xml file containing the translatiosns of the Logo commands" ), 0 } +// { "h <argument>", I18N_NOOP( "sets the xml file containing the highlightstyle" ), 0 } +// { "+[URL]", I18N_NOOP( "A Logo file to open" ), 0 }, + KCmdLineLastOption +}; + + +int main(int argc, char **argv) +{ + KAboutData about("kturtle", I18N_NOOP("KTurtle"), version, description, KAboutData::License_GPL, copyright, 0, website); + about.addAuthor("Cies Breijs", + I18N_NOOP("Main developer and initiator"), + "cies # kde.nl"); + about.addAuthor("Anne-Marie Mahfouf", + I18N_NOOP("Big contributor, supporter and fan"), + "annemarie.mahfouf # free.fr"); + about.addAuthor("Walter Schreppers", + I18N_NOOP("Author of \"wsbasic\" (wsbasic.sf.net) the base for the interpreter of KTurtle"), + "Walter.Schreppers # ua.ac.be"); + about.addCredit("Matthias Meßmer", + I18N_NOOP("German Data Files"), + "bmlmessmer # web.de"); + about.addCredit("Burkhard Lück", + I18N_NOOP("German Data Files"), + "lueck # hube-lueck.de"); + about.addCredit("Stefan Asserhäll", + I18N_NOOP("Swedish Data Files"), + "stefan.asserhall # telia.com"); + about.addCredit("Jure Repinc", + I18N_NOOP("Slovenian Data Files"), + "jlp # holodeck1.com"); + about.addCredit("Chusslove Illich", + I18N_NOOP("Serbian (Latin and Cyrillic) Data Files"), + "caslav.ilic # gmx.net"); + about.addCredit("Pino Toscano", + I18N_NOOP("Italian Data Files"), + "toscano.pino # tiscali.it"); + about.addCredit("Andy Potter", + I18N_NOOP("British English Data Files"), + "A.J.Potter # rhul.ac.uk"); + about.addCredit("Rafael Beccar", + I18N_NOOP("Spanish Data Files"), + "rafael.beccar # kdemail.net"); + about.addCredit("Riverson Rios", + I18N_NOOP("Brazilian Portuguese Data Files"), + "riverson # ccv.ufc.br"); + about.addCredit("Karl Ove Hufthammer", + I18N_NOOP("Norwegian Nynorsk and Bokmål Data files"), "karl # huftis.org"); + about.addCredit("Albert Astals Cid", + I18N_NOOP("Parser Cyrillic support"), + "astals11 # terra.es"); + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication app; + MainWindow *mainwindow = 0; + + if ( app.isRestored() ) + { + RESTORE(MainWindow); + } + else + { + // no session... just start up normally + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + /// @todo do something with the command line args here + + mainwindow = new MainWindow(); + app.setMainWidget(mainwindow); + mainwindow->show(); + + args->clear(); + } + + // mainwindow has WDestructiveClose flag by default, so it will delete itself. + return app.exec(); +} diff --git a/kturtle/src/parser.cpp b/kturtle/src/parser.cpp new file mode 100644 index 00000000..ca1fafd7 --- /dev/null +++ b/kturtle/src/parser.cpp @@ -0,0 +1,1084 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// This file is originally written by Walter Scheppers, but allmost +// allmost every aspect of it is heavily changed by Cies Breijs. + + +#include <qstringlist.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "parser.h" + + +Parser::Parser(QTextIStream& in) +{ + lexer = new Lexer(in); + tree = new TreeNode(); +} + +Parser::~Parser() +{ + delete lexer; +} + +void Parser::parse() +{ + tree = Program(); // the first node that keeps the lexer running till finished/error +} + +void Parser::getToken() +{ + currentToken = lexer->lex(); // stores a Token, obtained though the lexer, in 'currentToken' + row = currentToken.start.row; // these will have to leave eventually, all should be passed on by the Token + col = currentToken.start.col; + kdDebug(0)<<"Parser::getToken(), got a token: '"<<currentToken.look<<"', @ ("<<currentToken.start.row<<", "<<currentToken.start.col<<") - ("<<currentToken.end.row<<", "<<currentToken.end.col<<"), tok-number:"<<currentToken.type<<endl; +} + +TreeNode* Parser::Program() +{ + Token emptyToken; + emptyToken.type = tokNotSet; + emptyToken.look = ""; + emptyToken.start.row = 0; + emptyToken.start.col = 0; + emptyToken.end.row = 0; + emptyToken.end.col = 0; + + TreeNode* program = new TreeNode(emptyToken, programNode, "program"); + TreeNode* block = new TreeNode(emptyToken, blockNode, "block"); + + getToken(); + + // this is the main parse loop + kdDebug(0)<<"Parser::Program(): entering main parse loop..."<<endl; + while (currentToken.type != tokEOF) // currentToken.type returns the type of the currentToken + { + kdDebug(0)<<"Parser::Program(), [main parse loop]: looking for next statement..."<<endl; + block->appendChild( Statement() ); + while (currentToken.type == tokEOL) getToken(); // newlines between statements are allowed + // runs statement related code, stores the returned TreeNode* in the nodetree + // note: Statement() allways gets a new Token with getToken() before it returns + } + program->appendChild(block); + kdDebug(0)<<"Parser::Program(): leaving main parse loop..."<<endl; + + return program; +} + +void Parser::matchToken(int expectedToken) +{ + if (currentToken.type == expectedToken) + { + getToken(); // get a new token + return; + } + + switch (expectedToken) + { + case tokEOL: + Error(currentToken, i18n("Unexpected intruction after the '%1' command, please use only one instruction per line").arg(preservedToken.look), 1010); + break; + + case tokBegin: + Error(preservedToken, i18n("Expected '['"), 1010); + break; + + case tokTo: + Error(currentToken, i18n("Expected 'to' after the '%1' command").arg(preservedToken.look), 1010); + break; + + case tokAssign: + Error(currentToken, i18n("Expected '=' after the '%1' command").arg(preservedToken.look), 1010); + break; + + case tokEnd: + Error(currentToken, i18n("Expected ']' after the '%1' command").arg(preservedToken.look), 1010); + break; + + case tokUnknown: + Error(preservedToken, i18n("Expected a name after the '%1' command").arg(preservedToken.look), 1010); + break; + + default: + Error(currentToken, i18n("UNDEFINED ERROR NR %1: please send this Logo script to the KTurtle developers").arg(expectedToken), 1010); + break; + } +} + + +void Parser::appendParameters(TreeNode* node) +{ + node->appendChild( Expression() ); // append the first papameter + while (currentToken.type == tokComma) + { + matchToken(tokComma); // push through the comma + if (currentToken.type == tokEOL) return; // catch forgotten expressions, like "go 10, " + node->appendChild( Expression() ); + } +} + + +TreeNode* Parser::getId() +{ + TreeNode* n = new TreeNode(currentToken, idNode); + n->setLook(currentToken.look); + matchToken(tokUnknown); // Id's are ofcouse not yet known + return n; +} + + + +TreeNode* Parser::FunctionCall(Token maybeFunctionCall) +{ + kdDebug(0)<<"Parser::FunctionCall() [using identifier: '"<<maybeFunctionCall.look<<"']"<<endl; + TreeNode* fcall = new TreeNode(maybeFunctionCall, functionCallNode); + + TreeNode* paramList = new TreeNode(currentToken, idListNode, "idlist"); + // if (currentToken.type != tokEOL && currentToken.type != tokEOF) + if (currentToken.type == tokNumber || + currentToken.type == tokString || + currentToken.type == tokUnknown) // only if there is a possible parameter given after the call... + { + TreeNode* expr = Expression(); + if (expr->getType() == Unknown) Error(currentToken, i18n("Expected an expression"), 1020); + else paramList->appendChild(expr); + while (currentToken.type == tokComma) + { + matchToken(tokComma); + expr = Expression(); + if (expr->getType() == Unknown) Error(currentToken, i18n("Expected an expression"), 1020); + else paramList->appendChild(expr); + } + } + fcall->appendChild(paramList); + + return fcall; +} + + +TreeNode* Parser::Factor() +{ + TreeNode* node; + Token rememberedToken = currentToken; + switch (currentToken.type) + { + case tokBraceOpen: + matchToken(tokBraceOpen); + node = Expression(); + matchToken(tokBraceClose); + break; + + case tokUnknown: + node = getId(); + if (learnedFunctionList.contains(rememberedToken.look) > 0) // is function call + { + delete node; + node = FunctionCall(rememberedToken); + node->setType(funcReturnNode); // expect returned value on stack + } + break; + + case tokString: + node = new TreeNode(currentToken, constantNode); + { // extra scope to localize the QString 'str' + QString str = currentToken.look; + if ( currentToken.look.endsWith("\"") ) + { + // cut off the quotes and store the value + str.remove(0, 1).truncate( currentToken.look.length() - 2 ); + } + else // problems but we need to keep it moving + { + str.remove(0, 1); // cut off the first quote only + Error(currentToken, i18n("String text not properly delimited with a ' \" ' (double quote)"), 1060); + } + node->setValue(str); + } + matchToken(tokString); + break; + + case tokNumber: + node = new TreeNode(currentToken, constantNode); + node->setValue(currentToken.value); + matchToken(tokNumber); + break; + + case tokRun: + node = ExternalRun(); + break; + + case tokInputWindow: + node = InputWindow(); + break; + + case tokRandom: + node = Random(); + break; + + case tokEOL: + node = new TreeNode(currentToken, Unknown); + break; + + default: + QString s = currentToken.look; + if ( s.isEmpty() || currentToken.type == tokEOF ) + { + Error(currentToken, i18n("INTERNAL ERROR NR %1: please sent this Logo script to KTurtle developers").arg(1), 1020); + // if this error occurs the see the Parser::Repeat for the good solution using 'preservedToken' + } + else + { + Error(currentToken, i18n("Cannot understand '%1', expected an expression after the '%2' command").arg(s).arg(preservedToken.look), 1020); + } + node = new TreeNode(currentToken, Unknown); + getToken(); + break; + } + return node; +} + + +TreeNode* Parser::signedFactor() +{ + // see if there is a tokPlus, tokMinus or tokNot infront of the factor + TreeNode* node; + switch (currentToken.type) + { + case tokPlus: + matchToken(tokPlus); + return Factor(); + break; + + case tokMinus: + preservedToken = currentToken; + matchToken(tokMinus); + node = Factor(); + if (node->getType() == constantNode) + { + // in case of just a constant (-3) situation + Value num = node->getValue(); + num.setNumber( -num.Number() ); + node->setValue(num); + return node; + } + else + { + // in case of a variable or other situation (-a) + TreeNode* minus = new TreeNode(preservedToken, minusNode); + minus->appendChild(node); + return minus; + } + break; + + case tokNot: + preservedToken = currentToken; + matchToken(tokNot); + node = Factor(); + { // extra scope needed to localize not_Node + TreeNode* not_Node = new TreeNode(preservedToken, notNode); + not_Node->appendChild(node); + return not_Node; + } + break; + + default: + // fall-through safety + return Factor(); + break; + } +} + + + +TreeNode* Parser::Term() +{ + TreeNode* termNode = signedFactor(); + TreeNode* pos = termNode; + TreeNode* left = NULL; + TreeNode* right = NULL; + + while ( (currentToken.type == tokMul) || (currentToken.type == tokDev) || (currentToken.type == tokAnd) ) + { + // while is is a multiplicative operator do... + left = pos; + pos = new TreeNode(currentToken, Unknown); + pos->appendChild(left); + + switch (currentToken.type) + { + case tokMul: + matchToken(tokMul); + right = signedFactor(); + pos->setType(mulNode); + break; + + case tokDev: + matchToken(tokDev); + right = signedFactor(); + pos->setType(divNode); + break; + + case tokAnd: + matchToken(tokAnd); + right = signedFactor(); + pos->setType(andNode); + break; + + default: + Error(currentToken, i18n("Expected '*' or '/'"), 1030); + getToken(); + return pos; + break; + } + if (right != NULL) pos->appendChild(right); + termNode = pos; + } + return termNode; +} + + +bool Parser::isAddOp(Token t) +{ + return ( (t.type == tokPlus) || + (t.type == tokMinus) || + (t.type == tokGt) || + (t.type == tokGe) || + (t.type == tokLt) || + (t.type == tokLe) || + (t.type == tokEq) || + (t.type == tokNe) || + + (t.type == tokOr) || + (t.type == tokGe) ); +} + + +/*---------------------------------------------------------------*/ +/* Parse and Translate an Expression */ +TreeNode* Parser::Expression() +{ + TreeNode* retExp = Term(); // preset the base-TreeNode as it eventually will be returned + TreeNode* pos = retExp; + TreeNode* left = NULL; + TreeNode* right = NULL; + + while ( isAddOp(currentToken) ) + { + left = pos; + pos = new TreeNode(currentToken, Unknown); + pos->appendChild(left); + switch (currentToken.type) + { + case tokPlus: + matchToken(tokPlus); + right = Term(); + pos->setType(addNode); + break; + + case tokMinus: + matchToken(tokMinus); + right = Term(); + pos->setType(subNode); + break; + + case tokGt: + matchToken(tokGt); + right = Term(); + pos->setType(nodeGT); + break; + + case tokLt: + matchToken(tokLt); + right = Term(); + pos->setType(nodeLT); + break; + + case tokGe: + matchToken(tokGe); + right = Term(); + pos->setType(nodeGE); + break; + + case tokLe: + matchToken(tokLe); + right = Term(); + pos->setType(nodeLE); + break; + + case tokEq: + matchToken(tokEq); + right = Term(); + pos->setType(nodeEQ); + break; + + case tokNe: + matchToken(tokNe); + right = Term(); + pos->setType(nodeNE); + break; + + case tokOr: + matchToken(tokOr); + right = Term(); + pos->setType(orNode); + break; + + default: + Error(currentToken, i18n("Expected '*' or '/'"), 1040); + getToken(); + return pos; + break; + } + if (right != NULL) pos->appendChild(right); + retExp = pos; + } + return retExp; +} + + +TreeNode* Parser::Assignment(Token t) +{ + TreeNode* node = new TreeNode(t, assignNode); + matchToken(tokAssign); // match the '=' + + // the child is the expression or RHV of assignment + TreeNode* expr = NULL; +// if (currentToken.type == tokUnknown) expr = Other(); // in case of an functioncall +// else expr = Expression(); -------> fuctioncalls get caught in Expression() and co. + + expr = Expression(); + + node->appendChild(expr); + + return node; +} + + +TreeNode* Parser::Statement() +{ + kdDebug(0)<<"Parser::Statement()"<<endl; + while (currentToken.type == tokEOL) getToken(); // statements can allways start on newlines + switch (currentToken.type) + { + case tokLearn : return Learn(); break; + + case tokIf : return If(); break; + case tokFor : return For(); break; + case tokForEach : return ForEach(); break; + case tokWhile : return While(); break; + case tokRun : return ExternalRun(); break; + case tokReturn : return Return(); break; + case tokBreak : return Break(); break; + case tokUnknown : return Other(); break; //assignment or function call + + case tokClear : return Clear(); break; + case tokGo : return Go(); break; + case tokGoX : return GoX(); break; + case tokGoY : return GoY(); break; + case tokForward : return Forward(); break; + case tokBackward : return Backward(); break; + case tokDirection : return Direction(); break; + case tokTurnLeft : return TurnLeft(); break; + case tokTurnRight : return TurnRight(); break; + case tokCenter : return Center(); break; + case tokSetPenWidth : return SetPenWidth(); break; + case tokPenUp : return PenUp(); break; + case tokPenDown : return PenDown(); break; + case tokSetFgColor : return SetFgColor(); break; + case tokSetBgColor : return SetBgColor(); break; + case tokResizeCanvas : return ResizeCanvas(); break; + case tokSpriteShow : return SpriteShow(); break; + case tokSpriteHide : return SpriteHide(); break; + case tokSpritePress : return SpritePress(); break; + case tokSpriteChange : return SpriteChange(); break; + + case tokPrint : return Print(); break; + case tokInputWindow : return InputWindow(); break; + case tokMessage : return Message(); break; + case tokFontType : return FontType(); break; + case tokFontSize : return FontSize(); break; + case tokRepeat : return Repeat(); break; + case tokRandom : return Random(); break; + case tokWait : return Wait(); break; + case tokWrapOn : return WrapOn(); break; + case tokWrapOff : return WrapOff(); break; + case tokReset : return Reset(); break; + + case tokEOF : return EndOfFile(); break; + + case tokEnd : Error(currentToken, i18n("Cannot understand ']'"), 1050); + getToken(); + return new TreeNode(currentToken, Unknown); + break; + + case tokBegin : Error(currentToken, i18n("Cannot understand '['"), 1050); + getToken(); + return new TreeNode(currentToken, Unknown); + break; + + default : break; + } + if (currentToken.type != tokEnd) + { + Error(currentToken, i18n("Cannot understand '%1'").arg(currentToken.look), 1060); + } + + getToken(); + return new TreeNode(currentToken, Unknown); // fall-though for unknowns +} + + +TreeNode* Parser::Block() +{ + TreeNode* block = new TreeNode(currentToken, blockNode, "block"); + + while (currentToken.type == tokEOL) getToken(); // skip newlines + matchToken(tokBegin); + while (currentToken.type == tokEOL) getToken(); // skip newlines + while ( (currentToken.type != tokEnd) && (currentToken.type != tokEOF) ) + { + block->appendChild( Statement() ); + while (currentToken.type == tokEOL) getToken(); // blocks can have newlines between their statements + } + matchToken(tokEnd); + return block; +} + + +// +// Turtle Funktions +// + +// Functions that take NO arguments + +TreeNode* Parser::Clear() +{ + TreeNode* node = new TreeNode(currentToken, ClearNode); + preservedToken = currentToken; + getToken(); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::Center() +{ + TreeNode* node = new TreeNode(currentToken, CenterNode); + preservedToken = currentToken; + getToken(); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::PenUp() +{ + TreeNode* node = new TreeNode(currentToken, PenUpNode); + preservedToken = currentToken; + getToken(); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::PenDown() +{ + TreeNode* node = new TreeNode(currentToken, PenDownNode); + preservedToken = currentToken; + getToken(); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::SpriteShow() +{ + TreeNode* node = new TreeNode(currentToken, SpriteShowNode); + preservedToken = currentToken; + getToken(); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::SpriteHide() +{ + TreeNode* node = new TreeNode(currentToken, SpriteHideNode); + preservedToken = currentToken; + getToken(); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::SpritePress() +{ + TreeNode* node = new TreeNode(currentToken, SpritePressNode); + preservedToken = currentToken; + getToken(); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::WrapOn() +{ + TreeNode* node = new TreeNode(currentToken, WrapOnNode); + preservedToken = currentToken; + getToken(); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::WrapOff() +{ + TreeNode* node = new TreeNode(currentToken, WrapOffNode); + preservedToken = currentToken; + getToken(); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::Reset() +{ + TreeNode* node = new TreeNode(currentToken, ResetNode); + preservedToken = currentToken; + getToken(); + matchToken(tokEOL); + return node; +} + + + + +// Functions that take 1 arguments + +TreeNode* Parser::GoX() +{ + TreeNode* node = new TreeNode(currentToken, GoXNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + + +TreeNode* Parser::GoY() +{ + TreeNode* node = new TreeNode(currentToken, GoYNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + + +TreeNode* Parser::Forward() +{ + TreeNode* node = new TreeNode(currentToken, ForwardNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + + +TreeNode* Parser::Backward() +{ + TreeNode* node = new TreeNode(currentToken, BackwardNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::Direction() +{ + TreeNode* node = new TreeNode(currentToken, DirectionNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::TurnLeft() +{ + TreeNode* node = new TreeNode(currentToken, TurnLeftNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::TurnRight() +{ + TreeNode* node = new TreeNode(currentToken, TurnRightNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::SetPenWidth() +{ + TreeNode* node = new TreeNode(currentToken, SetPenWidthNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::Message() +{ + TreeNode* node = new TreeNode(currentToken, MessageNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::InputWindow() +{ + TreeNode* node = new TreeNode(currentToken, InputWindowNode); + preservedToken = currentToken; + getToken(); + node->appendChild( Expression() ); + // matchToken(tokEOL); this command can return values so can be used as expression/parameter + return node; +} + +TreeNode* Parser::SpriteChange() +{ + TreeNode* node = new TreeNode(currentToken, SpriteChangeNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::FontType() +{ + TreeNode* node = new TreeNode(currentToken, FontTypeNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::FontSize() +{ + TreeNode* node = new TreeNode(currentToken, FontSizeNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::Wait() +{ + TreeNode* node = new TreeNode(currentToken, WaitNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::ExternalRun() +{ + TreeNode* node = new TreeNode(currentToken, runNode); + preservedToken = currentToken; + getToken(); + node->appendChild( Expression() ); + // matchToken(tokEOL); this command can return values so can be used as expression/parameter + return node; +} + + + +// Functions that take 2 arguments + +TreeNode* Parser::Go() +{ + TreeNode* node = new TreeNode(currentToken, GoNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::ResizeCanvas() +{ + TreeNode* node = new TreeNode(currentToken, ResizeCanvasNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::Random() +{ + TreeNode* node = new TreeNode(currentToken, RandomNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + // matchToken(tokEOL); this command can return values so can be used as expression/parameter + return node; +} + + + + +// Functions that take 3 arguments + +TreeNode* Parser::SetFgColor() +{ + TreeNode* node = new TreeNode(currentToken, SetFgColorNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::SetBgColor() +{ + TreeNode* node = new TreeNode(currentToken, SetBgColorNode); + preservedToken = currentToken; + getToken(); + appendParameters(node); + matchToken(tokEOL); + return node; +} + + + + +// Weirdo's (learn, execution controllers, print, and Other()s) + +TreeNode* Parser::Learn() +{ + preservedToken = currentToken; + matchToken(tokLearn); // skip the 'dummy' command + TreeNode* func = new TreeNode(currentToken, functionNode); + getToken(); // get the token after the function's name + + TreeNode* idList = new TreeNode(currentToken, idListNode, "idlist"); + if (currentToken.type != tokBegin) + { + if (currentToken.type == tokUnknown) idList->appendChild( getId() ); + else + { + Error(currentToken, "Expected a parameter name or a '[' after the learn command.", 3030); + getToken(); // this recovers from the error + } + + while (currentToken.type == tokComma) + { + matchToken(tokComma); + idList->appendChild( getId() ); + } + } + func->appendChild(idList); + + learnedFunctionList.append( func->getLook() ); // publish the function + + func->appendChild( Block() ); + + return func; +} + + +TreeNode* Parser::If() +{ + TreeNode* node = new TreeNode(currentToken, ifNode); + preservedToken = currentToken; + matchToken(tokIf); + + node->appendChild( Expression() ); + + if (currentToken.type == tokDo) getToken(); // skip dummy word 'do' + + if (currentToken.type == tokBegin) node->appendChild( Block() ); // if followed by a block + else node->appendChild( Statement() ); // if followed by single statement + + while (currentToken.type == tokEOL) getToken(); // allow the else keyword to be on later lines + + if (currentToken.type == tokElse) // else part + { + matchToken(tokElse); + while (currentToken.type == tokEOL) getToken(); + if (currentToken.type == tokDo) getToken(); // next word + + if(currentToken.type == tokBegin) node->appendChild( Block() ); // else is followed by block + else node->appendChild( Statement() ); + } + + return node; +} + + +TreeNode* Parser::While() +{ + TreeNode* node = new TreeNode(currentToken, whileNode); + preservedToken = currentToken; + matchToken(tokWhile); + node->appendChild( Expression() ); + node->appendChild( Block() ); + return node; +} + + +TreeNode* Parser::For() +{ + TreeNode* fNode = new TreeNode(currentToken, forNode); + preservedToken = currentToken; + matchToken(tokFor); + fNode->appendChild( getId() ); // loop id + matchToken(tokAssign); + + fNode->appendChild( Expression() ); // start value expression + matchToken(tokTo); + fNode->appendChild( Expression() ); // stop value expression + + if (currentToken.type == tokStep) + { + matchToken(tokStep); + fNode->appendChild( Expression() ); //step expression + } + + while (currentToken.type == tokEOL) getToken(); // newlines are allowed + if (currentToken.type == tokBegin) fNode->appendChild( Block() ); // for followed by a block + else fNode->appendChild( Statement() ); // while followed by single statement + + return fNode; +} + + +TreeNode* Parser::Repeat() +{ + TreeNode* node = new TreeNode(currentToken, RepeatNode); + preservedToken = currentToken; // preserve token, else Match() will make sure it gets lost + matchToken(tokRepeat); + node->appendChild( Expression() ); + node->appendChild( Block() ); + return node; +} + + + +TreeNode* Parser::ForEach() +{ + TreeNode* fNode = new TreeNode(currentToken, forEachNode); + preservedToken = currentToken; + matchToken(tokForEach); + + fNode->appendChild( Expression() ); + matchToken(tokIn); + fNode->appendChild( Expression() ); + + if (currentToken.type == tokBegin) fNode->appendChild( Block() ); // for followed by a block + else fNode->appendChild( Statement() ); // while followed by single statement + + return fNode; +} + +TreeNode* Parser::Print() +{ + TreeNode* node = new TreeNode(currentToken, printNode); + preservedToken = currentToken; // preserve token, else Match() will make sure it gets lost + getToken(); + if (currentToken.type == tokEOL) return node; // print called without expressions + node->appendChild( Expression() ); // first expression + // following strings or expressions + while (currentToken.type == tokComma) + { + getToken(); // the comma + node->appendChild( Expression() ); + } + matchToken(tokEOL); + return node; +} + +TreeNode* Parser::Return() +{ + TreeNode* ret = new TreeNode(currentToken, returnNode); + matchToken(tokReturn); + + ret->appendChild( Expression() ); + + return ret; +} + +TreeNode* Parser::Break() +{ + TreeNode* brk = new TreeNode(currentToken, breakNode); + matchToken(tokBreak); + + return brk; +} + +TreeNode* Parser::EndOfFile() +{ + TreeNode* node = new TreeNode(currentToken, EndOfFileNode); + return node; +} + +TreeNode* Parser::Other() +{ + // this is either an assignment or a function call! + kdDebug(0)<<"Parser::Other()"<<endl; + Token rememberedToken = currentToken; // preserve token, else Match() will make sure it gets lost + matchToken(tokUnknown); + + if (currentToken.type == tokAssign) return Assignment(rememberedToken); + else if (learnedFunctionList.contains(rememberedToken.look) > 0) + { + TreeNode* node; + node = FunctionCall(rememberedToken); +// node->setType(funcReturnNode); + return node; + } + + Error(rememberedToken, i18n("'%1' is neither a Logo command nor a learned command.").arg(rememberedToken.look), 1020); + TreeNode* errNode = new TreeNode(rememberedToken, Unknown); + + // skip the rest of the line + while (currentToken.type != tokEOL) getToken(); + + return errNode; +} + + +void Parser::Error(Token& t, const QString& s, uint code) +{ + emit ErrorMsg(t, s, code); +} + + +#include "parser.moc" diff --git a/kturtle/src/parser.h b/kturtle/src/parser.h new file mode 100644 index 00000000..28be566f --- /dev/null +++ b/kturtle/src/parser.h @@ -0,0 +1,126 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef _PARSER_H_ +#define _PARSER_H_ + +#include <qobject.h> + +#include "lexer.h" +#include "treenode.h" + + +class Parser : public QObject +{ + Q_OBJECT + + public: + Parser(QTextIStream& in); + virtual ~Parser(); + + void parse(); + TreeNode* getTree() const { return tree; } + + + signals: + void ErrorMsg(Token&, const QString&, uint code); + + + private: + bool isAddOp(Token); + bool isMulOp(Token); + + void getToken(); + void matchToken(int tokenType); + void appendParameters(TreeNode* node); + void Error(Token&, const QString& s, uint code); + + TreeNode* Program(); + TreeNode* ParamList(); + TreeNode* Block(); + TreeNode* Statement(); + + TreeNode* ExternalRun(); + + TreeNode* getId(); + TreeNode* signedFactor(); + TreeNode* Factor(); + TreeNode* Term(); + TreeNode* Expression(); + + TreeNode* Assignment(Token); + TreeNode* FunctionCall(Token); + TreeNode* Other(); + + TreeNode* While(); + TreeNode* For(); + TreeNode* ForEach(); + TreeNode* If(); + TreeNode* Return(); + TreeNode* Break(); + + TreeNode* Clear(); + TreeNode* Go(); + TreeNode* GoX(); + TreeNode* GoY(); + TreeNode* Forward(); + TreeNode* Backward(); + TreeNode* Direction(); + TreeNode* TurnLeft(); + TreeNode* TurnRight(); + TreeNode* Center(); + TreeNode* SetPenWidth(); + TreeNode* PenUp(); + TreeNode* PenDown(); + TreeNode* SetFgColor(); + TreeNode* SetBgColor(); + TreeNode* ResizeCanvas(); + TreeNode* SpriteShow(); + TreeNode* SpriteHide(); + TreeNode* SpritePress(); + TreeNode* SpriteChange(); + + TreeNode* Message(); + TreeNode* InputWindow(); + TreeNode* Print(); + TreeNode* FontType(); + TreeNode* FontSize(); + TreeNode* Repeat(); + TreeNode* Random(); + TreeNode* Wait(); + TreeNode* WrapOn(); + TreeNode* WrapOff(); + TreeNode* Reset(); + + TreeNode* LineBreak(); + TreeNode* EndOfFile(); + + TreeNode* Learn(); + + //private locals + Lexer *lexer; + TreeNode *tree; + Token currentToken; + Token preservedToken; // to preserve the currentToken so it wont get lost + uint row; + uint col; + QStringList learnedFunctionList; +}; + +#endif // _PARSER_H_ diff --git a/kturtle/src/pics/Makefile.am b/kturtle/src/pics/Makefile.am new file mode 100644 index 00000000..2d3c312f --- /dev/null +++ b/kturtle/src/pics/Makefile.am @@ -0,0 +1,8 @@ +picsdir = $(kde_datadir)/kturtle/pics +pics_DATA = turtle.0000.png turtle.0006.png turtle.0012.png turtle.0018.png turtle.0024.png turtle.0030.png \ +turtle.0001.png turtle.0007.png turtle.0013.png turtle.0019.png turtle.0025.png turtle.0031.png \ +turtle.0002.png turtle.0008.png turtle.0014.png turtle.0020.png turtle.0026.png turtle.0032.png \ +turtle.0003.png turtle.0009.png turtle.0015.png turtle.0021.png turtle.0027.png turtle.0033.png \ +turtle.0004.png turtle.0010.png turtle.0016.png turtle.0022.png turtle.0028.png turtle.0034.png \ +turtle.0005.png turtle.0011.png turtle.0017.png turtle.0023.png turtle.0029.png turtle.0035.png +EXTRA_DIST = $(pics_DATA) diff --git a/kturtle/src/pics/turtle.0000.png b/kturtle/src/pics/turtle.0000.png Binary files differnew file mode 100644 index 00000000..5bdf2768 --- /dev/null +++ b/kturtle/src/pics/turtle.0000.png diff --git a/kturtle/src/pics/turtle.0001.png b/kturtle/src/pics/turtle.0001.png Binary files differnew file mode 100644 index 00000000..c6a17d5c --- /dev/null +++ b/kturtle/src/pics/turtle.0001.png diff --git a/kturtle/src/pics/turtle.0002.png b/kturtle/src/pics/turtle.0002.png Binary files differnew file mode 100644 index 00000000..27419fcb --- /dev/null +++ b/kturtle/src/pics/turtle.0002.png diff --git a/kturtle/src/pics/turtle.0003.png b/kturtle/src/pics/turtle.0003.png Binary files differnew file mode 100644 index 00000000..e8949259 --- /dev/null +++ b/kturtle/src/pics/turtle.0003.png diff --git a/kturtle/src/pics/turtle.0004.png b/kturtle/src/pics/turtle.0004.png Binary files differnew file mode 100644 index 00000000..0cb37fcd --- /dev/null +++ b/kturtle/src/pics/turtle.0004.png diff --git a/kturtle/src/pics/turtle.0005.png b/kturtle/src/pics/turtle.0005.png Binary files differnew file mode 100644 index 00000000..b809ef2e --- /dev/null +++ b/kturtle/src/pics/turtle.0005.png diff --git a/kturtle/src/pics/turtle.0006.png b/kturtle/src/pics/turtle.0006.png Binary files differnew file mode 100644 index 00000000..927d4bd8 --- /dev/null +++ b/kturtle/src/pics/turtle.0006.png diff --git a/kturtle/src/pics/turtle.0007.png b/kturtle/src/pics/turtle.0007.png Binary files differnew file mode 100644 index 00000000..18578690 --- /dev/null +++ b/kturtle/src/pics/turtle.0007.png diff --git a/kturtle/src/pics/turtle.0008.png b/kturtle/src/pics/turtle.0008.png Binary files differnew file mode 100644 index 00000000..4015a21e --- /dev/null +++ b/kturtle/src/pics/turtle.0008.png diff --git a/kturtle/src/pics/turtle.0009.png b/kturtle/src/pics/turtle.0009.png Binary files differnew file mode 100644 index 00000000..6d0d55c6 --- /dev/null +++ b/kturtle/src/pics/turtle.0009.png diff --git a/kturtle/src/pics/turtle.0010.png b/kturtle/src/pics/turtle.0010.png Binary files differnew file mode 100644 index 00000000..19d4bbce --- /dev/null +++ b/kturtle/src/pics/turtle.0010.png diff --git a/kturtle/src/pics/turtle.0011.png b/kturtle/src/pics/turtle.0011.png Binary files differnew file mode 100644 index 00000000..e5f08140 --- /dev/null +++ b/kturtle/src/pics/turtle.0011.png diff --git a/kturtle/src/pics/turtle.0012.png b/kturtle/src/pics/turtle.0012.png Binary files differnew file mode 100644 index 00000000..f317f4b5 --- /dev/null +++ b/kturtle/src/pics/turtle.0012.png diff --git a/kturtle/src/pics/turtle.0013.png b/kturtle/src/pics/turtle.0013.png Binary files differnew file mode 100644 index 00000000..cce82a0f --- /dev/null +++ b/kturtle/src/pics/turtle.0013.png diff --git a/kturtle/src/pics/turtle.0014.png b/kturtle/src/pics/turtle.0014.png Binary files differnew file mode 100644 index 00000000..3b3c9687 --- /dev/null +++ b/kturtle/src/pics/turtle.0014.png diff --git a/kturtle/src/pics/turtle.0015.png b/kturtle/src/pics/turtle.0015.png Binary files differnew file mode 100644 index 00000000..08b2e0aa --- /dev/null +++ b/kturtle/src/pics/turtle.0015.png diff --git a/kturtle/src/pics/turtle.0016.png b/kturtle/src/pics/turtle.0016.png Binary files differnew file mode 100644 index 00000000..ad66fc98 --- /dev/null +++ b/kturtle/src/pics/turtle.0016.png diff --git a/kturtle/src/pics/turtle.0017.png b/kturtle/src/pics/turtle.0017.png Binary files differnew file mode 100644 index 00000000..a81d01c8 --- /dev/null +++ b/kturtle/src/pics/turtle.0017.png diff --git a/kturtle/src/pics/turtle.0018.png b/kturtle/src/pics/turtle.0018.png Binary files differnew file mode 100644 index 00000000..f28eb0bd --- /dev/null +++ b/kturtle/src/pics/turtle.0018.png diff --git a/kturtle/src/pics/turtle.0019.png b/kturtle/src/pics/turtle.0019.png Binary files differnew file mode 100644 index 00000000..1307d50b --- /dev/null +++ b/kturtle/src/pics/turtle.0019.png diff --git a/kturtle/src/pics/turtle.0020.png b/kturtle/src/pics/turtle.0020.png Binary files differnew file mode 100644 index 00000000..a2232c03 --- /dev/null +++ b/kturtle/src/pics/turtle.0020.png diff --git a/kturtle/src/pics/turtle.0021.png b/kturtle/src/pics/turtle.0021.png Binary files differnew file mode 100644 index 00000000..bc0f6150 --- /dev/null +++ b/kturtle/src/pics/turtle.0021.png diff --git a/kturtle/src/pics/turtle.0022.png b/kturtle/src/pics/turtle.0022.png Binary files differnew file mode 100644 index 00000000..f65b47b3 --- /dev/null +++ b/kturtle/src/pics/turtle.0022.png diff --git a/kturtle/src/pics/turtle.0023.png b/kturtle/src/pics/turtle.0023.png Binary files differnew file mode 100644 index 00000000..8e5efab1 --- /dev/null +++ b/kturtle/src/pics/turtle.0023.png diff --git a/kturtle/src/pics/turtle.0024.png b/kturtle/src/pics/turtle.0024.png Binary files differnew file mode 100644 index 00000000..396a7ec1 --- /dev/null +++ b/kturtle/src/pics/turtle.0024.png diff --git a/kturtle/src/pics/turtle.0025.png b/kturtle/src/pics/turtle.0025.png Binary files differnew file mode 100644 index 00000000..a1ecd51e --- /dev/null +++ b/kturtle/src/pics/turtle.0025.png diff --git a/kturtle/src/pics/turtle.0026.png b/kturtle/src/pics/turtle.0026.png Binary files differnew file mode 100644 index 00000000..f73f8167 --- /dev/null +++ b/kturtle/src/pics/turtle.0026.png diff --git a/kturtle/src/pics/turtle.0027.png b/kturtle/src/pics/turtle.0027.png Binary files differnew file mode 100644 index 00000000..d55940ce --- /dev/null +++ b/kturtle/src/pics/turtle.0027.png diff --git a/kturtle/src/pics/turtle.0028.png b/kturtle/src/pics/turtle.0028.png Binary files differnew file mode 100644 index 00000000..3e17afee --- /dev/null +++ b/kturtle/src/pics/turtle.0028.png diff --git a/kturtle/src/pics/turtle.0029.png b/kturtle/src/pics/turtle.0029.png Binary files differnew file mode 100644 index 00000000..14d26bb4 --- /dev/null +++ b/kturtle/src/pics/turtle.0029.png diff --git a/kturtle/src/pics/turtle.0030.png b/kturtle/src/pics/turtle.0030.png Binary files differnew file mode 100644 index 00000000..1809eb2e --- /dev/null +++ b/kturtle/src/pics/turtle.0030.png diff --git a/kturtle/src/pics/turtle.0031.png b/kturtle/src/pics/turtle.0031.png Binary files differnew file mode 100644 index 00000000..bc9f2f94 --- /dev/null +++ b/kturtle/src/pics/turtle.0031.png diff --git a/kturtle/src/pics/turtle.0032.png b/kturtle/src/pics/turtle.0032.png Binary files differnew file mode 100644 index 00000000..7b252516 --- /dev/null +++ b/kturtle/src/pics/turtle.0032.png diff --git a/kturtle/src/pics/turtle.0033.png b/kturtle/src/pics/turtle.0033.png Binary files differnew file mode 100644 index 00000000..6b2e84b6 --- /dev/null +++ b/kturtle/src/pics/turtle.0033.png diff --git a/kturtle/src/pics/turtle.0034.png b/kturtle/src/pics/turtle.0034.png Binary files differnew file mode 100644 index 00000000..71ad275f --- /dev/null +++ b/kturtle/src/pics/turtle.0034.png diff --git a/kturtle/src/pics/turtle.0035.png b/kturtle/src/pics/turtle.0035.png Binary files differnew file mode 100644 index 00000000..edee5d05 --- /dev/null +++ b/kturtle/src/pics/turtle.0035.png diff --git a/kturtle/src/pics/turtle.png b/kturtle/src/pics/turtle.png Binary files differnew file mode 100644 index 00000000..5bdf2768 --- /dev/null +++ b/kturtle/src/pics/turtle.png diff --git a/kturtle/src/settings.kcfgc b/kturtle/src/settings.kcfgc new file mode 100644 index 00000000..abbb05b2 --- /dev/null +++ b/kturtle/src/settings.kcfgc @@ -0,0 +1,5 @@ +File=kturtle.kcfg +IncludeFiles=kstandarddirs.h,kdebug.h +ClassName=Settings +Singleton=true +Mutators=true
\ No newline at end of file diff --git a/kturtle/src/token.cpp b/kturtle/src/token.cpp new file mode 100644 index 00000000..b30842a4 --- /dev/null +++ b/kturtle/src/token.cpp @@ -0,0 +1,19 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "token.h" diff --git a/kturtle/src/token.h b/kturtle/src/token.h new file mode 100644 index 00000000..2c0e7622 --- /dev/null +++ b/kturtle/src/token.h @@ -0,0 +1,222 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef _TOKEN_H_ +#define _TOKEN_H_ + +#include <qstring.h> + +#include "value.h" + + +struct Pos // convenience, it codes a bit nicer with this, i.e.: "int row = tok.start.row" +{ + uint row; + uint col; +}; + +struct Token +{ + int type; // filled with enumed tokenTypes + QString look; // the way the token looks in the text editor + Value value; // if the token is a number it can be stored here + Pos start; // row'n'col of the starting/ending point of the token + Pos end; +}; + + +enum tokenTypes +{ + tokNotSet = 0, // inittial type of all tokens + tokError = 1, // when there is an error + tokUnknown = 2, // when no token was found, a tokUnknown is often a variable, function or error + + tokIf, // the execution controlling tokens + tokElse, + tokWhile, + tokFor, + tokTo, + tokStep, + tokForEach, + tokIn, + tokBreak, + tokReturn, + + tokBegin, // the scope delimiting tokens + tokEnd, + + tokNumber, // the containers + tokString, + + tokAssign, // for assignments + + tokAnd, // and, or, not + tokOr, + tokNot, + + tokEq, // ==, !=, >=, >, <=, < + tokNe, + tokGe, + tokGt, + tokLe, + tokLt, + + tokBraceOpen, // (, ), +, -, *, / + tokBraceClose, + tokPlus, + tokMinus, + tokMul, + tokDev, + + tokComma, + + tokEOL, // End Of Line token + tokEOF, // End Of File token + + tokLearn, // learn command + + tokClear, // the 'regular' command tokens + tokGo, + tokGoX, + tokGoY, + tokForward, + tokBackward, + tokDirection, + tokTurnLeft, + tokTurnRight, + tokCenter, + tokSetPenWidth, + tokPenUp, + tokPenDown, + tokSetFgColor, + tokSetBgColor, + tokResizeCanvas, + tokSpriteShow, + tokSpriteHide, + tokSpritePress, + tokSpriteChange, + tokMessage, + tokInputWindow, + tokPrint, + tokFontType, + tokFontSize, + tokRepeat, + tokRandom, + tokWait, + tokWrapOn, + tokWrapOff, + tokReset, + tokRun, + + tokDo // a dummy command +}; + +// const QString tokenTypeNames[] = +// { +// /* tokNotSet = 0 */ "", +// /* tokError = 1 */ "", +// /* tokUnknown = 2 */ "name", +// +// /* tokIf */ "if", +// /* tokElse */ "else", +// /* tokWhile */ "while", +// /* tokFor */ "for", +// /* tokTo */ "to", +// /* tokStep */ "step", +// /* tokForEach */ "each", +// /* tokIn */ "in", +// /* tokBreak */ "break", +// /* tokReturn */ "return", +// +// /* tokBegin */ "", +// /* tokEnd */ "", +// +// /* tokNumber */ "number", +// /* tokString */ "string", +// +// /* tokAssign */ "asignment", +// +// /* tokAnd */ "and", +// /* tokOr */ "or", +// /* tokNot */ "not", +// +// /* tokEq */ "questions", +// /* tokNe */ "questions", +// /* tokGe */ "questions", +// /* tokGt */ "questions", +// /* tokLe */ "questions", +// /* tokLt */ "questions", +// +// /* tokBraceOpen */ "math", +// /* tokBraceClose */ "math", +// /* tokPlus */ "math", +// /* tokMinus */ "math", +// /* tokMul */ "math", +// /* tokDev */ "math", +// +// /* tokComma */ "", +// +// /* tokEOL */ "", +// /* tokEOF */ "", +// +// /* tokLearn */ "learn", +// +// /* tokClear */ "clear", +// /* tokGo */ "go", +// /* tokGoX */ "gox", +// /* tokGoY */ "goy", +// /* tokForward */ "forward", +// /* tokBackward */ "backward", +// /* tokDirection */ "direction", +// /* tokTurnLeft */ "turnleft", +// /* tokTurnRight */ "turnright", +// /* tokCenter */ "center", +// /* tokSetPenWidth */ "setpenwidth", +// /* tokPenUp */ "penup", +// /* tokPenDown */ "pendown", +// /* tokSetFgColor */ "setfgcolor", +// /* tokSetBgColor */ "setbgcolor", +// /* tokResizeCanvas */ "resizecanvas", +// /* tokSpriteShow */ "spriteshow", +// /* tokSpriteHide */ "spritehide", +// /* tokSpritePress */ "", +// /* tokSpriteChange */ "", +// /* tokMessage */ "message", +// /* tokInputWindow */ "inputwindow", +// /* tokPrint */ "print", +// /* tokFontType */ "", +// /* tokFontSize */ "fontsize", +// /* tokRepeat */ "repeat", +// /* tokRandom */ "random", +// /* tokWait */ "wait", +// /* tokWrapOn */ "wrapon", +// /* tokWrapOff */ "wrapoff", +// /* tokReset */ "reset", +// /* tokRun */ "run", +// +// /* tokDo */ "", +// }; +// +// // QString Token::tokenType2name(int tokenType) +// // { +// // if (tokenType >= 0) return tokenTypeNames[tokenType]; +// // else return ""; +// // } + +#endif // _TOKEN_H_ diff --git a/kturtle/src/translate.cpp b/kturtle/src/translate.cpp new file mode 100644 index 00000000..d2c43f16 --- /dev/null +++ b/kturtle/src/translate.cpp @@ -0,0 +1,107 @@ +/* + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <qdom.h> +#include <qfile.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "settings.h" + +#include "translate.h" + + + +Translate::Translate() +{ + loadTranslations(); +} + + +QString Translate::name2fuzzy(const QString &name) +{ + if ( !aliasMap[name].isEmpty() ) // translate the alias if any + { + return QString( i18n("'%1' (%2)").arg(keyMap[name]).arg(reverseAliasMap[name]) ); + } + return QString( "'" + keyMap[name] + "'"); +} + +QString Translate::name2key(const QString &name) +{ + return keyMap[name]; +} + +QString Translate::alias2key(const QString &name) +{ + return aliasMap[name]; +} + + +void Translate::loadTranslations() { + QDomDocument KeywordsXML; + + kdDebug(0) << "Loading translation dictionary: "<< locate("data", "kturtle/data/logokeywords." + Settings::logoLanguage() + ".xml") <<endl; + // Read the specified translation file + QFile xmlfile( locate("data", "kturtle/data/logokeywords." + Settings::logoLanguage() + ".xml") ); + + if ( !xmlfile.open(IO_ReadOnly) ) return; + + if ( !KeywordsXML.setContent(&xmlfile) ) + { + xmlfile.close(); + return; + } + xmlfile.close(); + + // get into the first child of the root element (in our case a <command> tag) + QDomElement rootElement = KeywordsXML.documentElement(); + QDomNode n = rootElement.firstChild(); + + while ( !n.isNull() ) + { + QString name, key, alias; + name = n.toElement().attribute("name"); // get the name attribute of <command> + QDomNode m = n.firstChild(); // get into the first child of a <command> + while (true) + { + if( !m.toElement().text().isEmpty() ) + { + if (m.toElement().tagName() == "keyword") + { + key = m.toElement().text(); + keyMap.insert(name, key); + } + if (m.toElement().tagName() == "alias") + { + alias = m.toElement().text(); + aliasMap.insert(alias, key); + reverseAliasMap.insert(key, alias); + } + } + // break when read the last child of the current <command> + if ( m == n.lastChild() ) break; + m = m.nextSibling(); // goto the next element in the current <command> + } + n = n.nextSibling(); // goto the next <command> + } + + kdDebug(0) << "Translation dictionary loaded" <<endl; +} + diff --git a/kturtle/src/translate.h b/kturtle/src/translate.h new file mode 100644 index 00000000..b40cf133 --- /dev/null +++ b/kturtle/src/translate.h @@ -0,0 +1,49 @@ +/* + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef _TRANSLATE_H_ +#define _TRANSLATE_H_ + +#include <qmap.h> +#include <qstring.h> + + +class Translate +{ + public: + Translate(); + ~Translate() {} + + QString name2fuzzy(const QString&); + QString name2key(const QString&); + QString alias2key(const QString&); + + + private: + void loadTranslations(); + + typedef QMap<QString, QString> StringMap; + StringMap keyMap; + StringMap aliasMap; + StringMap reverseAliasMap; +}; + + +#endif // _TRANSLATE_H_ + + diff --git a/kturtle/src/treenode.cpp b/kturtle/src/treenode.cpp new file mode 100644 index 00000000..d27204ad --- /dev/null +++ b/kturtle/src/treenode.cpp @@ -0,0 +1,255 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// This file is originally written by Walter Scheppers, but allmost +// most aspects of it are slightly changed by Cies Breijs. + + +#include <kdebug.h> + +#include "treenode.h" + + +TreeNode::TreeNode() +{ + init(); + fType = Unknown; +} + +TreeNode::TreeNode(Token t, NodeType nodeType, QString name) +{ + init(); + fType = nodeType; + fTok = t; + if ( !name.isNull() ) fTok.look = name; + kdDebug(0)<<"TreeNode::TreeNode(); new TreeNode created, with look: '"<<fTok.look<<"', from position ("<<fTok.start.row<<", "<<fTok.start.col<<")."<<endl; +} + +TreeNode::TreeNode(TreeNode* p) +{ + init(); + setParent(p); + fType = Unknown; + fTok.value = -1; +} + +TreeNode::~TreeNode() +{ + destroy(this); +} + + + +void TreeNode::init() +{ + clear(); + parent = NULL; + // fTok.look (QString) and fTok.value (Value) are properly init'ed when constructed + fTok.start.row = 0; + fTok.start.col = 0; + fTok.end.row = 0; + fTok.end.col = 0; +} + + + + +void TreeNode::appendChild(TreeNode* node) +{ + node->setParent(this); + push_back(node); +} + +void TreeNode::appendSibling(TreeNode* node) +{ + node->parent = parent; + if (parent != NULL) parent->push_back(node); +} + + +TreeNode* TreeNode::nextSibling() +{ + if (parent) + { + TreeNode::iterator i = lookup(); + if ( i != parent->end() ) + { + ++i; + // Must check after i has been incremented + // to make sure i isn't at the end before + // returning the contained pointer value. + if ( i != parent->end() ) return *i; + } + } + return NULL; +} + +TreeNode* TreeNode::prevSibling() +{ + if (parent) + { + TreeNode::iterator i = lookup(); + // Must make sure we aren't at beginning as well + // or we can crash when decrementing since we shouldn't + // decrement before the start of the list. + if ( ( i != parent->end() ) && ( i != parent->begin() ) ) + { + --i; + return *i; + } + } + return NULL; +} + + + +TreeNode* TreeNode::firstChild() +{ + if ( !empty() ) + { + TreeNode::const_iterator child = begin(); + return *child; + } + return NULL; +} + +TreeNode* TreeNode::secondChild() +{ + if ( !empty() ) + { + TreeNode::const_iterator child = begin(); + return (*child)->nextSibling(); + } + return NULL; +} + +TreeNode* TreeNode::thirdChild() +{ + if ( !empty() ) + { + TreeNode* child = secondChild(); + if (child != NULL) return child->nextSibling(); + } + return NULL; +} + +TreeNode* TreeNode::fourthChild() +{ + if ( !empty() ) + { + TreeNode* child = thirdChild(); + if (child != NULL) return child->nextSibling(); + } + return NULL; +} + +TreeNode* TreeNode::fifthChild() +{ + if ( !empty() ) + { + TreeNode* child = fourthChild(); + if(child != NULL) return child->nextSibling(); + } + return NULL; +} + +TreeNode* TreeNode::lastChild() +{ + // using a reverse iterator... + if ( !empty() ) + { + TreeNode::const_reverse_iterator child = rbegin(); + return *child; + } + return NULL; +} + + + + +TreeNode::iterator TreeNode::lookup() +{ + if (parent != NULL) + { + TreeNode::iterator i = parent->begin(); + while ( (*i != this) && (i != parent->end() ) ) ++i; + return i; + } + return end(); +} + + + +TreeNode& TreeNode::operator= (const TreeNode& t) +{ + if (this != &t) + { + fType = t.fType; + fTok = t.fTok; + parent = t.parent; + + clear(); + for (TreeNode::const_iterator i = t.begin(); i != t.end(); ++i) + { + push_back(*i); + } + } + return *this; +} + + + + +// recursively walk through tree and show node names with indentation +void TreeNode::showTree(TreeNode* node, int indent) const +{ + indent++; + if ( (node != NULL) && (node->size() > 0) ) + { + for ( TreeNode::const_iterator i = node->begin(); i != node->end(); ++i ) + { + (*i)->show(indent); + showTree(*i, indent); + } + } +} + +void TreeNode::show(int indent) +{ + QString s = ""; + for (int i = 0; i < indent; i++) + { + s += "> "; + } + kdDebug(0)<<s<<""<<fTok.look<<" @ ("<<getRow()<<", "<<getCol()<<")"<<endl; +} + + +//recursively walk down tree and delete every node bottom up +void TreeNode::destroy(TreeNode* node) +{ + if ( (node != NULL) && (node->size() > 0) ) + { + for ( TreeNode::iterator i = node->begin(); i != node->end(); ++i ) + { + destroy(*i); + (*i)->clear(); //free children + //delete ( *i ); //free mem + } + } +} diff --git a/kturtle/src/treenode.h b/kturtle/src/treenode.h new file mode 100644 index 00000000..44d64d0a --- /dev/null +++ b/kturtle/src/treenode.h @@ -0,0 +1,276 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// This file is originally written by Walter Scheppers, but allmost +// every aspect of it is slightly changed by Cies Breijs. + + +#ifndef _TREENODE_H_ +#define _TREENODE_H_ + +#include <list> + +#include <qstring.h> + +#include "lexer.h" +#include "token.h" +#include "value.h" + + +using namespace std; // needed because treenode inherites from std::list + +//BUGS: prevSibling and nextSibling sometimes segfault and are not optimal, in short, don't use em! :) + +enum NodeType +{ + Unknown = -1, + programNode, + functionNode, + functionCallNode, + funcReturnNode, + returnNode, + breakNode, + idListNode, + blockNode, + forNode, + forEachNode, + whileNode, + ifNode, + assignNode, + expressionNode, + idNode, + constantNode, + + addNode, + mulNode, + divNode, + subNode, + minusNode, + + nodeGE, + nodeGT, + nodeLE, + nodeLT, + nodeNE, + nodeEQ, + + andNode, + orNode, + notNode, + + runNode, + + ClearNode, + GoNode, + GoXNode, + GoYNode, + ForwardNode, + BackwardNode, + DirectionNode, + TurnLeftNode, + TurnRightNode, + CenterNode, + SetPenWidthNode, + PenUpNode, + PenDownNode, + SetFgColorNode, + SetBgColorNode, + ResizeCanvasNode, + SpriteShowNode, + SpriteHideNode, + SpritePressNode, + SpriteChangeNode, + + MessageNode, + InputWindowNode, + printNode, + FontTypeNode, + FontSizeNode, + RepeatNode, + RandomNode, + WaitNode, + WrapOnNode, + WrapOffNode, + ResetNode, + + LineBreakNode, + EndOfFileNode + + +// HOPEFULLY THIS LIST CAN ONCE LOOK LIKE THIS: +// nodeProgram, // this frirst block has no corresponding token +// nodeFunction, +// nodeFunctionCall, +// nodeReturnFunction, +// nodeParameterList, +// nodeBlock, +// nodeExpression, +// nodeId, // for assignments +// +// nodeNotSet = -1, // inittial type of all tokens +// nodeUnknown = 0, // when no token was found, a tokUnknown is often a variable, function or error +// +// nodeIf, +// nodeElse, +// nodeWhile, +// nodeFor, +// nodeTo, +// nodeStep, +// nodeForEach, +// nodeIn, +// nodeBreak, +// nodeReturn, +// +// nodeBegin, +// nodeEnd, +// +// // nodeNumber, --> constantNode +// // nodeString, +// +// nodeAssign, +// +// nodeAnd, +// nodeOr, +// nodeNot, +// +// nodeEq, +// nodeNe, +// nodeGe, +// nodeGt, +// nodeLe, +// nodeLt, +// +// // nodeBraceOpen, +// // nodeBraceClose, +// // nodePlus, +// nodeAdd, // different from TokenType +// nodeSub, // different from TokenType +// nodeMul, +// nodeDev, +// nodeMinus, // different meaning from TokenType +// +// // nodeComma, +// // nodeEOL, +// // nodeEOF, +// // nodeError, +// +// nodeLearn, +// +// nodeClear, +// nodeGo, +// nodeGoX, +// nodeGoY, +// nodeForward, +// nodeBackward, +// nodeDirection, +// nodeTurnLeft, +// nodeTurnRight, +// nodeCenter, +// nodeSetPenWidth, +// nodePenUp, +// nodePenDown, +// nodeSetFgColor, +// nodeSetBgColor, +// nodeResizeCanvas, +// nodeSpriteShow, +// nodeSpriteHide, +// nodeSpritePress, +// nodeSpriteChange, +// nodeMessage, +// nodeInputWindow, +// nodePrint, +// nodeFontType, +// nodeFontSize, +// nodeRepeat, +// nodeRandom, +// nodeWait, +// nodeWrapOn, +// nodeWrapOff, +// nodeReset, +// nodeRun, +// +// // nodeDo +}; + + + +class TreeNode : public list<TreeNode*> /*, public Value <-- maybe oneday */ +{ + public: + TreeNode(); // used for creation of the first node called 'tree', in the contructor of the parser + TreeNode( TreeNode* ); // give parent + TreeNode( Token, NodeType = Unknown, QString = QString() ); + virtual ~TreeNode(); + + void init(); + + void appendChild(TreeNode*); + void appendSibling(TreeNode*); // works only if it has parent set! + + TreeNode* firstChild(); + TreeNode* secondChild(); + TreeNode* thirdChild(); + TreeNode* fourthChild(); + TreeNode* fifthChild(); + + TreeNode* lastChild(); + TreeNode* nextSibling(); + TreeNode* prevSibling(); + + void setParent(TreeNode* p) { parent = p; } + TreeNode* getParent() const { return parent; } + + void setToken(Token t) { fTok = t; } + Token& getToken() { return fTok; } + + void setType(NodeType t) { fType = t; } + NodeType getType() const { return fType; } + + void setLook(const QString& s) { fTok.look = s; } + QString getLook() const { return fTok.look; } + + void setValue(const Value& n) { fTok.value = n; } + void setValue(double d) { fTok.value = d; } + void setValue(const QString& s) { fTok.value = s; } + void setValue(bool b) { fTok.value.setBool(b); } + Value getValue() const { return fTok.value; } + + uint getRow() const { return fTok.start.row; } + uint getCol() const { return fTok.start.col; } + + bool hasChildren() const { return size() != 0; } + TreeNode::iterator lookup(); // gives location in parent list as iterator (used by prevSibling and nextSibling) + + TreeNode& operator=(const TreeNode&); + + virtual void show(int indent = 0); + void showTree(TreeNode* node, int indent = 0) const; + + + private: + void destroy(TreeNode*); + + NodeType fType; + Token fTok; + + + protected: + TreeNode *parent; +}; + +#endif // _TREENODE_H_ diff --git a/kturtle/src/value.cpp b/kturtle/src/value.cpp new file mode 100644 index 00000000..b529452d --- /dev/null +++ b/kturtle/src/value.cpp @@ -0,0 +1,328 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <kdebug.h> +#include <klocale.h> + +#include "value.h" + + +Value::Value() +{ + init(); +} + +Value::Value(const Value& n) +{ + *this = n; +} + + + +int Value::Type() const +{ + return type; +} + +void Value::setType(int newType) +{ + if (type == newType) return; // dont change values when type is not changing + else if (newType == boolValue) + { + init(); + type = newType; + m_string = i18n("false"); + } + else if (newType == numberValue) + { + init(); + type = newType; + } + else if (newType == stringValue) + { + init(); + type = newType; + } + else if (newType == emptyValue) init(); +} + + +bool Value::Bool() const +{ + if (type == boolValue) return m_bool; + else if (type == numberValue) return (m_double - 0.5 > 0); + return false; +} + +void Value::setBool(bool b) +{ + type = boolValue; + m_bool = b; + if (m_bool) + { + m_double = 1; + m_string = i18n("true"); + } + else + { + m_double = 0; + m_string = i18n("false"); + } +} + + +double Value::Number() const +{ + if (type == boolValue) + { + if (m_bool) return 1; + else return 0; + } + else if (type == numberValue) return m_double; + return 0; // stringValue, emptyValue +} + +void Value::setNumber(double d) +{ + type = numberValue; + m_double = d; + m_string.setNum(d); +} + +bool Value::setNumber(const QString &s) +{ + type = numberValue; + bool ok = true; + double num = s.toDouble(&ok); + if (ok) + { + m_double = num; + m_string.setNum(num); + return true; + } + return false; +} + + +QString Value::String() const +{ + if (type == boolValue) + { + if (m_bool) return QString( i18n("true") ); + else return QString( i18n("false") ); + } + else if (type == numberValue) + { + QString s; + s.setNum(m_double); + return s; + } + return m_string; // stringValue, emptyValue +} + +void Value::setString(double d) +{ + type = stringValue; + m_double = d; + m_string.setNum(d); +} + +void Value::setString(const QString &s) +{ + type = stringValue; + m_string = s; +} + + + +Value& Value::operator=(const Value& n) +{ + if (this != &n) + { + if (type == n.type) + { + type = n.Type(); + m_bool = n.Bool(); + m_double = n.Number(); + m_string = n.String(); + } + else if (n.Type() == boolValue) + { + setBool( n.Bool() ); + } + else if (n.Type() == numberValue) + { + setNumber( n.Number() ); + } + else if (n.Type() == stringValue) + { + setString( n.String() ); + } + else if (n.Type() == emptyValue) + { + init(); + } + } + return *this; +} + + +Value& Value::operator=(const QString& s) +{ + setString(s); + return *this; +} + +Value& Value::operator=(double n) +{ + setNumber(n); + return *this; +} + + + +Value& Value::operator+(const Value& n) +{ + if (type == numberValue && n.Type() == numberValue) + { + m_double += n.Number(); + } + else + { + type = stringValue; + m_string = String() + n.String(); + } + return *this; +} + + +Value& Value::operator-(const Value& n) +{ + if (type == numberValue && n.Type() == numberValue) + { + m_double -= n.Number(); + } + else + { + kdDebug(0)<<"Value::operator; cannot subtract strings"<<endl; + } + return *this; +} + + +Value& Value::operator*(const Value& n) +{ + if (type == numberValue && n.Type() == numberValue) + { + m_double *= n.Number(); + } + else + { + kdDebug(0)<<"Value::operator; cannot multiply strings"<<endl; + } + return *this; +} + + +Value& Value::operator/(const Value& n) +{ + if (type == numberValue && n.Type() == numberValue) + { + m_double /= n.Number(); + } + else + { + kdDebug(0)<<"Value::operator; cannot divide strings"<<endl; + } + return *this; +} + + + +bool Value::operator==(const Value& n) const +{ + if (type == boolValue && n.Type() == boolValue) return m_bool == n.Bool(); + if (type == numberValue && n.Type() == numberValue) return m_double == n.Number(); + if (type == stringValue && n.Type() == stringValue) return m_string == n.String(); + if (type == emptyValue && n.Type() == emptyValue) return true; + return false; +} + + +bool Value::operator!=(const Value& n) const +{ + if (type == boolValue && n.Type() == boolValue) return m_bool != n.Bool(); + if (type == numberValue && n.Type() == numberValue) return m_double != n.Number(); + if (type == stringValue && n.Type() == stringValue) return m_string != n.String(); + // if (type == emptyValue && n.Type() == emptyValue) return false; + return false; +} + + +bool Value::operator<(const Value& n) const +{ + if (type == boolValue && n.Type() == boolValue) return m_bool < n.Bool(); + if (type == numberValue && n.Type() == numberValue) return m_double < n.Number(); + if (type == stringValue && n.Type() == stringValue) return m_string.length() < n.String().length(); + // if (type == emptyValue && n.Type() == emptyValue) return false; + return false; +} + + +bool Value::operator<=(const Value& n) const +{ + if (type == boolValue && n.Type() == boolValue) return m_bool <= n.Bool(); + if (type == numberValue && n.Type() == numberValue) return m_double <= n.Number(); + if (type == stringValue && n.Type() == stringValue) return m_string.length() <= n.String().length(); + if (type == emptyValue && n.Type() == emptyValue) return true; + return false; +} + + +bool Value::operator>(const Value& n) const +{ + if (type == boolValue && n.Type() == boolValue) return m_bool > n.Bool(); + if (type == numberValue && n.Type() == numberValue) return m_double > n.Number(); + if (type == stringValue && n.Type() == stringValue) return m_string.length() > n.String().length(); + // if (type == emptyValue && n.Type() == emptyValue) return false; + return false; +} + + +bool Value::operator>=(const Value& n) const +{ + if (type == boolValue && n.Type() == boolValue) return m_bool >= n.Bool(); + if (type == numberValue && n.Type() == numberValue) return m_double >= n.Number(); + if (type == stringValue && n.Type() == stringValue) return m_string.length() >= n.String().length(); + if (type == emptyValue && n.Type() == emptyValue) return true; + return false; +} + + + +// private + +void Value::init() +{ + type = emptyValue; // init'ed values are empty by default + m_bool = false; + m_double = 0; + m_string = ""; +} + + diff --git a/kturtle/src/value.h b/kturtle/src/value.h new file mode 100644 index 00000000..8c61966e --- /dev/null +++ b/kturtle/src/value.h @@ -0,0 +1,85 @@ +/* + Copyright (C) 2003 by Walter Schreppers + Copyright (C) 2004 by Cies Breijs + + This program is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef _VALUE_H_ +#define _VALUE_H_ + +#include <qstring.h> +#include <qtextstream.h> + + +enum valueType +{ + emptyValue, + boolValue, + numberValue, + stringValue +}; + +class Value +{ + public: + Value(); + Value(const Value&); + ~Value() {} + + + void resetValue() { init(); } + + int Type() const; + void setType(int); + + void setBool(bool); + bool Bool() const; + + double Number() const; + void setNumber(double); + bool setNumber(const QString&); + + QString String() const; + void setString(double); + void setString(const QString&); + + Value& operator=(const Value&); + Value& operator=(const QString&); + Value& operator=(double); + + Value& operator+(const Value&); + Value& operator-(const Value&); + Value& operator*(const Value&); + Value& operator/(const Value&); + + bool operator==(const Value&) const; + bool operator!=(const Value&) const; + bool operator< (const Value&) const; + bool operator<=(const Value&) const; + bool operator> (const Value&) const; + bool operator>=(const Value&) const; + + + private: + void init(); + + int type; + bool m_bool; + double m_double; + QString m_string; +}; + +#endif // _VALUE_H_ |