summaryrefslogtreecommitdiffstats
path: root/logo/src/xlogo/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'logo/src/xlogo/kernel')
-rw-r--r--logo/src/xlogo/kernel/Affichage.java207
-rw-r--r--logo/src/xlogo/kernel/DrawPanel.java2917
-rw-r--r--logo/src/xlogo/kernel/InstructionBuffer.java206
-rw-r--r--logo/src/xlogo/kernel/Interprete.java981
-rw-r--r--logo/src/xlogo/kernel/Kernel.java211
-rw-r--r--logo/src/xlogo/kernel/LaunchPrimitive.java4751
-rw-r--r--logo/src/xlogo/kernel/LogoError.java89
-rw-r--r--logo/src/xlogo/kernel/LoopFillPolygon.java48
-rw-r--r--logo/src/xlogo/kernel/LoopFor.java94
-rw-r--r--logo/src/xlogo/kernel/LoopForEach.java69
-rw-r--r--logo/src/xlogo/kernel/LoopProperties.java149
-rw-r--r--logo/src/xlogo/kernel/LoopRepeat.java43
-rw-r--r--logo/src/xlogo/kernel/LoopWhile.java44
-rw-r--r--logo/src/xlogo/kernel/MP3Player.java86
-rw-r--r--logo/src/xlogo/kernel/MyCalculator.java1221
-rw-r--r--logo/src/xlogo/kernel/MyFlow.java110
-rw-r--r--logo/src/xlogo/kernel/MyFlowReader.java81
-rw-r--r--logo/src/xlogo/kernel/MyFlowWriter.java74
-rw-r--r--logo/src/xlogo/kernel/Primitive.java573
-rw-r--r--logo/src/xlogo/kernel/Turtle.java434
-rw-r--r--logo/src/xlogo/kernel/genericPrimitive308
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoException.java69
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoList.java93
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoNumber.java76
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoParser.java289
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoPrimitive.java74
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoRightDelimiter.java56
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoTree.java96
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoType.java129
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoTypeNull.java56
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoVariable.java68
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoWord.java68
-rw-r--r--logo/src/xlogo/kernel/gui/GuiButton.java92
-rw-r--r--logo/src/xlogo/kernel/gui/GuiComponent.java95
-rw-r--r--logo/src/xlogo/kernel/gui/GuiMap.java72
-rw-r--r--logo/src/xlogo/kernel/gui/GuiMenu.java101
-rw-r--r--logo/src/xlogo/kernel/network/ChatFrame.java160
-rw-r--r--logo/src/xlogo/kernel/network/NetworkClientChat.java115
-rw-r--r--logo/src/xlogo/kernel/network/NetworkClientExecute.java114
-rw-r--r--logo/src/xlogo/kernel/network/NetworkClientSend.java100
-rw-r--r--logo/src/xlogo/kernel/network/NetworkServer.java194
-rw-r--r--logo/src/xlogo/kernel/perspective/Conic.java214
-rw-r--r--logo/src/xlogo/kernel/perspective/Element3D.java115
-rw-r--r--logo/src/xlogo/kernel/perspective/ElementLine.java374
-rw-r--r--logo/src/xlogo/kernel/perspective/ElementPoint.java129
-rw-r--r--logo/src/xlogo/kernel/perspective/ElementPolygon.java179
-rw-r--r--logo/src/xlogo/kernel/perspective/FogDialog.java256
-rw-r--r--logo/src/xlogo/kernel/perspective/LightDialog.java378
-rw-r--r--logo/src/xlogo/kernel/perspective/MyFog.java134
-rw-r--r--logo/src/xlogo/kernel/perspective/MyLight.java190
-rw-r--r--logo/src/xlogo/kernel/perspective/Viewer3D.java493
-rw-r--r--logo/src/xlogo/kernel/perspective/World3D.java318
-rw-r--r--logo/src/xlogo/kernel/userspace/ErrorManager.java201
-rw-r--r--logo/src/xlogo/kernel/userspace/GlobalVariableTable.java73
-rw-r--r--logo/src/xlogo/kernel/userspace/ProcedureErrorMessage.java106
-rw-r--r--logo/src/xlogo/kernel/userspace/PropertyListTable.java165
-rw-r--r--logo/src/xlogo/kernel/userspace/UserSpace.java790
-rw-r--r--logo/src/xlogo/kernel/userspace/context/ContextManager.java301
-rw-r--r--logo/src/xlogo/kernel/userspace/context/ContextSwitcher.java43
-rw-r--r--logo/src/xlogo/kernel/userspace/context/LogoContext.java312
-rw-r--r--logo/src/xlogo/kernel/userspace/context/NetworkContext.java206
-rw-r--r--logo/src/xlogo/kernel/userspace/context/RecordContext.java146
-rw-r--r--logo/src/xlogo/kernel/userspace/context/UserContext.java191
-rw-r--r--logo/src/xlogo/kernel/userspace/files/LogoFile.java755
-rw-r--r--logo/src/xlogo/kernel/userspace/files/LogoFileContainer.java39
-rw-r--r--logo/src/xlogo/kernel/userspace/files/LogoFilesManager.java560
-rw-r--r--logo/src/xlogo/kernel/userspace/files/RecordFile.java234
-rw-r--r--logo/src/xlogo/kernel/userspace/procedures/ExecutablesContainer.java59
-rw-r--r--logo/src/xlogo/kernel/userspace/procedures/ExecutablesProvider.java36
-rw-r--r--logo/src/xlogo/kernel/userspace/procedures/Procedure.java763
-rw-r--r--logo/src/xlogo/kernel/userspace/procedures/ProcedureErrorType.java80
-rw-r--r--logo/src/xlogo/kernel/userspace/procedures/ProceduresManager.java557
72 files changed, 23210 insertions, 0 deletions
diff --git a/logo/src/xlogo/kernel/Affichage.java b/logo/src/xlogo/kernel/Affichage.java
new file mode 100644
index 0000000..7da2120
--- /dev/null
+++ b/logo/src/xlogo/kernel/Affichage.java
@@ -0,0 +1,207 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo Description : XLogo is an interpreter for the Logo programming
+ * language
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel;
+
+import java.util.Stack;
+import java.util.HashMap;
+import java.awt.event.*;
+
+import javax.swing.SwingUtilities;
+
+import xlogo.Application;
+import xlogo.messages.async.history.HistoryMessenger;
+import xlogo.utils.Utils;
+import xlogo.MemoryChecker;
+import xlogo.Logo;
+
+// Ce thread gère l'animation de la tortue pendant l'exécution
+/**
+ * This Thread is responsible of the turtle animation.
+ * This animation has to be executed in a separated thread, else it will block
+ * the event dispatcher thread
+ */
+public class Affichage extends Thread
+{
+ public static boolean execution_lancee = false;
+ private boolean pause = false;
+ private Application cadre;
+ private StringBuffer instruction;
+ private Souris souris = new Souris();
+ private MemoryChecker memoryChecker = null;
+
+ public Affichage()
+ {
+ }
+
+ public Affichage(Application cadre, StringBuffer instruction)
+ {
+ this.cadre = cadre;
+ this.instruction = instruction;
+ }
+
+ class Souris extends MouseAdapter
+ { // Si on déplace les Scrollbar pendant
+ // le dessin
+ public Souris()
+ {
+ } // Ceci permet d'interrompre momentanément l'exécution
+
+ public void mousePressed(MouseEvent e)
+ {
+ pause = true;
+ }
+
+ public void mouseReleased(MouseEvent e)
+ {
+ pause = false;
+ }
+ }
+
+ public void run()
+ {
+ // currentThread().setPriority(Thread.MIN_PRIORITY);
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run()
+ {
+ cadre.setCommandLine(false);// la ligne de commandes
+ // n'est plus active
+ }
+ });
+ execution_lancee = true;
+ cadre.getDrawPanel().active_souris(); // On active les événements souris
+ // sur
+ // la zone de dessin
+ cadre.scrollArea.getVerticalScrollBar().addMouseListener(souris);
+ cadre.scrollArea.getHorizontalScrollBar().addMouseListener(souris);
+ try
+ {
+ cadre.setCar(-1);
+ cadre.error = false;
+ Interprete.operande = Interprete.operateur = Interprete.drapeau_ouvrante = false;
+ cadre.getKernel().getInstructionBuffer().clear();
+ Interprete.calcul = new Stack<String>();
+ Interprete.nom = new Stack<String>();
+ Interprete.locale = new HashMap<String, String>();
+ Interprete.en_cours = new Stack<String>();
+ memoryChecker = new MemoryChecker(cadre);
+ memoryChecker.start();
+ boolean b = true;
+ while (b)
+ {
+ String st = cadre.getKernel().execute(instruction);
+ if (!st.equals(""))
+ throw new LogoError(Logo.messages.getString("error.whattodo") + " " + st + " ?");
+ if (Interprete.actionInstruction.length() == 0)
+ b = false;
+ else
+ {
+ instruction = Interprete.actionInstruction;
+ Interprete.actionInstruction = new StringBuffer();
+ }
+ }
+ }
+ catch (LogoError e)
+ {
+ // if (st.equals("siwhile")) st=Logo.messages.getString("tantque");
+ while (!Interprete.en_cours.isEmpty() && Interprete.en_cours.peek().equals("("))
+ Interprete.en_cours.pop();
+ if (!cadre.error & !Interprete.en_cours.isEmpty())
+ {
+ HistoryMessenger.getInstance().dispatchError(
+ Logo.messages.getString("dans") + " " + Interprete.en_cours.pop() + ", "
+ + Logo.messages.getString("line") + " " + getLineNumber() + ":\n");
+ }
+ if (!cadre.error)
+ HistoryMessenger.getInstance().dispatchError(Utils.SortieTexte(e.getMessage()) + "\n");
+ abortExecution();
+ }
+ cadre.setCommandLine(true);
+ if (!cadre.viewer3DVisible())
+ cadre.focus_Commande();
+ execution_lancee = false;
+ memoryChecker.kill();
+ cadre.error = false;
+ cadre.scrollArea.getVerticalScrollBar().removeMouseListener(souris);
+ cadre.scrollArea.getHorizontalScrollBar().removeMouseListener(souris);
+ }
+
+ private void abortExecution()
+ {
+ cadre.focus_Commande();
+ cadre.error = true;
+ Interprete.calcul = new Stack<String>();
+ cadre.getKernel().getInstructionBuffer().clear();
+ Primitive.stackLoop = new Stack<LoopProperties>();
+ }
+
+ private int getLineNumber()
+ {
+ String string = Interprete.lineNumber;
+ // System.out.println("bb"+string+"bb");
+ if (string.equals(""))
+ string = cadre.getKernel().getInstructionBuffer().toString();
+ // System.out.println("cc"+string+"cc");
+ int id = string.indexOf("\\l");
+ if (id != -1)
+ {
+ String lineNumber = "";
+ int i = id + 2;
+ char c = string.charAt(i);
+ while (c != ' ')
+ {
+ lineNumber += c;
+ i++;
+ c = string.charAt(i);
+ }
+ // System.out.println(lineNumber);
+ return Integer.parseInt(lineNumber);
+ }
+ return 1;
+ }
+
+ protected boolean isOnPause()
+ {
+ return pause;
+ }
+
+ /**
+ * @param b
+ * @uml.property name="pause"
+ */
+ public void setPause(boolean b)
+ {
+ pause = b;
+ }
+}
diff --git a/logo/src/xlogo/kernel/DrawPanel.java b/logo/src/xlogo/kernel/DrawPanel.java
new file mode 100644
index 0000000..269a0c0
--- /dev/null
+++ b/logo/src/xlogo/kernel/DrawPanel.java
@@ -0,0 +1,2917 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.HeadlessException;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Point;
+
+import javax.swing.JViewport;
+
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.Transparency;
+
+import javax.vecmath.Color3f;
+
+import java.awt.FontMetrics;
+import java.awt.Stroke;
+import java.awt.Toolkit;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.CubicCurve2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.QuadCurve2D;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.font.TextLayout;
+import java.awt.Dimension;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.CropImageFilter;
+import java.awt.image.ReplicateScaleFilter;
+import java.awt.image.FilteredImageSource;
+
+import com.sun.j3d.utils.geometry.Text2D;
+
+import javax.swing.ImageIcon;
+import javax.swing.SwingUtilities;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+
+import javax.swing.JPanel;
+import javax.imageio.ImageIO;
+import javax.media.j3d.*;
+import javax.vecmath.Vector3d;
+import javax.vecmath.Matrix3d;
+import javax.vecmath.Point3d;
+
+import java.util.Stack;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.awt.event.*;
+
+import xlogo.Application;
+import xlogo.storage.WSManager;
+import xlogo.storage.user.DrawQuality;
+import xlogo.storage.user.PenShape;
+import xlogo.storage.user.UserConfig;
+import xlogo.utils.Utils;
+import xlogo.Logo;
+import xlogo.kernel.grammar.LogoNumber;
+import xlogo.kernel.grammar.LogoType;
+import xlogo.kernel.grammar.LogoTypeNull;
+import xlogo.kernel.gui.*;
+import xlogo.kernel.perspective.*;
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+ public class DrawPanel extends JPanel implements MouseMotionListener,MouseListener {
+ public static final LogoTypeNull nullType=new LogoTypeNull();
+ private static final long serialVersionUID = 1L;
+ public Turtle tortue;
+ public Turtle[] tortues;
+
+ /**
+ * When a turtle is active on screen, its number is added to this stack
+ * @uml.property name="tortues_visibles"
+ * @uml.associationEnd multiplicity="(0 -1)" elementType="java.lang.String"
+ */
+ public Stack<String> tortues_visibles;
+
+ /**
+ * this int indicates the window mode, default 0
+ */
+ protected static int WINDOW_MODE = 0;
+ /**
+ * WINDOW MODE: 0 <br>
+ * Turtles can go out the drawing area
+ */
+ protected static final int WINDOW_CLASSIC=0;
+ /**
+ * WRAP MODE: 1 <br>
+ * Turtles can go out the drawing area and reappear on the other side
+ */
+ protected static final int WINDOW_WRAP=1;
+ /**
+ * CLOSE MODE: 2 <br>
+ * Turtles can't go out the drawing area
+ */
+ protected static final int WINDOW_CLOSE=2;
+ /**
+ * Perspective MODE <br>
+ * The screen is a projection of the 3D universe
+ */
+ protected static final int WINDOW_3D=3;
+
+ /** Boolean for animation mode */
+ public static boolean classicMode=true; // true si classique false si animation
+ /** Animation mode: */
+ public final static boolean MODE_ANIMATION=false;
+ /** Classic mode */
+ public final static boolean MODE_CLASSIC=true;
+
+ /**
+ * default predefined colors
+ */
+ public static final Color[] defaultColors={Color.BLACK,Color.RED,Color.GREEN,Color.YELLOW,Color.BLUE,
+ Color.MAGENTA,Color.CYAN,Color.WHITE,Color.GRAY,Color.LIGHT_GRAY,new Color(128,0,0),new Color(0,128,0),
+ new Color(0,0,128),new Color(255,128,0),Color.PINK,new Color(128,0,255),new Color(153,102,0)};
+
+
+ /**
+ * The id for the drawing font (with primitive label)
+ * @uml.property name="police_etiquette"
+ */
+ protected int police_etiquette;
+ /**
+ * The default drawing area color
+ * @uml.property name="couleurfond"
+ */
+ private Color couleurfond = Color.white;
+ private Shape shape=null;
+ private Line2D line;
+ private Rectangle2D rec;
+ private Application cadre;
+ /** This Image is used for Buffering the drawing*/
+ public static BufferedImage dessin;
+ /**
+ * Graphics of the BufferedImage dessin
+ * @uml.property name="g"
+ */
+ private Graphics2D g;
+ /** The scale for the zoom*/
+ public static double zoom=1;
+
+
+ /**
+ * All Gui Objects on the drawing area are stored in the GuiMap gm
+ * @uml.property name="gm"
+ * @uml.associationEnd multiplicity="(1 1)"
+ */
+ private GuiMap gm;
+ /**
+ * The Stroke for the triangle turtle
+ * @uml.property name="crayon_triangle"
+ */
+ private BasicStroke crayon_triangle = new BasicStroke(1);
+ /**
+ * Tools for 3D Mode
+ * @uml.property name="w3d"
+ * @uml.associationEnd
+ */
+ private World3D w3d=null;
+ /**
+ * Boolean that indicates if the interpreter is recording polygon in 3D Mode
+ */
+ protected static int record3D=0;
+ protected final static int record3D_NONE=0;
+ protected final static int record3D_POLYGON=1;
+ protected final static int record3D_LINE=2;
+ protected final static int record3D_POINT=3;
+ protected final static int record3D_TEXT=4;
+
+ /**
+ * Boolean that indicates if the interpreter is recording polygon in 2D Mode
+ */
+ private static int record2D=0;
+ private final static int record2D_NONE=0;
+ private final static int record2D_POLYGON=1;
+ private Vector<Point2D.Double> stackTriangle;
+
+
+ protected static Element3D poly;
+
+ private double[] coords;
+ private double oldx;
+ private double oldy;
+ private double x1;
+ private double y1;
+ private double x2;
+ private double y2;
+ // Were used for clipping
+ // private double nx,ny,vx,vy,factor,length;
+ // private GeneralPath gp;
+ // private Arc2D clipArc;
+ private Arc2D arc;
+ /**
+ * Button number when user click on the drawing area
+ * @uml.property name="bouton_souris"
+ */
+ private int bouton_souris=0; // Numéro du bouton de souris appuyé sur la zone de dessin
+ /**
+ * Last coords for last mouse event
+ * @uml.property name="possouris"
+ */
+ private String possouris="[ 0 0 ] "; // Coordonnées du point du dernier événement souris
+
+ /**
+ * Notify if a mouse event has occured
+ * @uml.property name="lissouris"
+ */
+ private boolean lissouris=false; //Indique si un événement souris est intervenu depuis le debut du programme
+ /**
+ * The rectangular selection zone
+ * @uml.property name="selection"
+ */
+ private Rectangle selection;
+ /**
+ * Color for the rectangular selection
+ * @uml.property name="colorSelection"
+ */
+ private Color colorSelection;
+ /**
+ * The First clicked point when the rectangular selection is created
+ * @uml.property name="origine"
+ */
+ Point origine;
+ public DrawPanel(Application cadre){
+ this.gm= cadre.getKernel().getWorkspace().getGuiMap();
+ setLayout(null);
+ this.setPreferredSize(new Dimension(
+ (int)(WSManager.getUserConfig().getImageWidth()*zoom),
+ (int)(WSManager.getUserConfig().getImageHeight()*zoom)));
+ this.cadre=cadre;
+ addMouseListener(this);
+ addMouseMotionListener(this);
+ initGraphics();
+ }
+ /**
+ * This method is used to draw for primitive "forward"
+ * @param arg LogoType which represents the number of steps to move
+ */
+ protected LogoType av(LogoType number){
+ if (number.isException()) return number;
+ LogoNumber ln=(LogoNumber)number;
+ return av(ln.getValue());
+
+ }
+ /**
+ * This method is used to draw for primitive "backward"
+ * @param arg LogoType which represents the number of steps to move
+ */
+ protected LogoType re(LogoType number){
+ if (number.isException()) return number;
+ LogoNumber ln=(LogoNumber)number;
+ return av(-ln.getValue());
+
+ }
+ /**
+ * This method is used to draw for primitive "right"
+ * @param arg LogoType which represents the number of steps to rotate
+ */
+ protected LogoType td(LogoType number){
+ if (number.isException()) return number;
+ LogoNumber ln=(LogoNumber)number;
+ return td(ln.getValue());
+
+ } /**
+ * This method is used to draw for primitive "left"
+ * @param arg LogoType which represents the number of steps to rotate
+ */
+ protected LogoType tg(LogoType number){
+ if (number.isException()) return number;
+ LogoNumber ln=(LogoNumber)number;
+ return td(-ln.getValue());
+
+ }
+
+ /**
+ * This method is used to draw for primitive "forward" and "backward"
+ * @param arg Number of steps
+ */
+ protected LogoType av(double arg) {
+ // Graphics2D g=(Graphics2D)dessin.getGraphics();
+
+ oldx = tortue.corX;
+ oldy = tortue.corY;
+ if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_CLASSIC) { //mode fenetre
+ montrecacheTortue(false);
+
+ tortue.corX = tortue.corX + arg
+ * Math.cos(tortue.angle);
+ tortue.corY = tortue.corY - arg
+ * Math.sin(tortue.angle);
+ if (tortue.isPenDown()) {
+ g.setStroke(tortue.stroke); // TODO Marko : fix penerase problem. TODO also for other ops, circle etc
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ if (null==line) line=new Line2D.Double();
+ /* if (null==gp) gp=new GeneralPath();
+ else gp.reset();*/
+ if (oldx < tortue.corX){
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ if (oldx>tortue.corX){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else if (oldx==tortue.corX){
+ if (oldy<tortue.corY){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else{
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ }
+
+ line.setLine(x1,y1,x2,y2);
+
+ /*
+ // perpendicular vector
+ nx=y1-y2;
+ ny=x2-x1;
+ length=Math.sqrt(nx*nx+ny*ny);
+ if (length!=0){
+ factor=(1+tortue.getPenWidth())/length;
+ vx=x2-x1;
+ vy=y2-y1;
+ gp.moveTo((float)(x1-vx*factor-nx*factor),
+ (float)(y1-vy*factor-ny*factor));
+ gp.lineTo((float)(x1-vx*factor+nx*factor),
+ (float)(y1-vy*factor+ny*factor));
+ gp.lineTo((float)(x2+vx*factor+nx*factor),
+ (float)(y2+vy*factor+ny*factor));
+ gp.lineTo((float)(x2+vx*factor-nx*factor),
+ (float)(y2+vy*factor-ny*factor));
+ gp.lineTo((float)(x1-vx*factor-nx*factor),
+ (float)(y1-vy*factor-ny*factor));
+ }
+ else{
+ float width=tortue.getPenWidth()+0.5f;
+ gp.moveTo((float)(x1-width),
+ (float)(y1-width));
+ gp.lineTo((float)(x1+width),
+ (float)(y1-width));
+ gp.lineTo((float)(x1+width),
+ (float)(y1+width));
+ gp.lineTo((float)(x1-width),
+ (float)(y1+width));
+ gp.lineTo((float)(x1-width),
+ (float)(y1-width));
+ }
+ shape=gp;*/
+ tryRecord2DMode(tortue.corX,tortue.corY);
+ g.draw(line);
+ //if (!tortue.isVisible())
+ clip();
+ // g.dispose();
+ }
+ montrecacheTortue(true);
+ } else if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_WRAP) { //mode enroule
+ trace_enroule(arg, oldx, oldy);
+ } else if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_CLOSE) { //mode clos
+ try {
+ trace_ferme(oldx, oldy, arg);
+ } catch (LogoError e) {
+ }
+ }
+ else if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_3D){
+ montrecacheTortue(false);
+ tortue.X=tortue.X+arg*tortue.getRotationMatrix()[0][1];
+ tortue.Y=tortue.Y+arg*tortue.getRotationMatrix()[1][1];
+ tortue.Z=tortue.Z+arg*tortue.getRotationMatrix()[2][1];
+
+ double tmp[]=new double[3];
+ tmp[0]=tortue.X;
+ tmp[1]=tortue.Y;
+ tmp[2]=tortue.Z;
+
+ tmp=this.toScreenCoord(tmp,true);
+ tortue.corX = tmp[0];
+ tortue.corY = tmp[1];
+
+
+ if (tortue.isPenDown()) {
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ if (null==line) line=new Line2D.Double();
+
+ if (oldx < tortue.corX){
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ if (oldx>tortue.corX){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else if (oldx==tortue.corX){
+ if (oldy<tortue.corY){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else{
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ }
+
+ line.setLine(x1,y1,x2,y2);
+
+ g.draw(line);
+ clip();
+ }
+ montrecacheTortue(true);
+ }
+ return DrawPanel.nullType;
+ }
+
+ /**
+ * This method is used for drawing with primitive "right" or "left"
+ * @param arg The angle to rotate
+ */
+ protected LogoType td(double arg) {
+// System.out.println(tortue.angle);
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ if (!enabled3D()){
+ tortue.heading = ((tortue.heading + arg) % 360 + 360) % 360;
+ tortue.angle = Math.toRadians(90 - tortue.heading);
+ }
+ else{
+ tortue.setRotationMatrix(w3d.multiply(tortue.getRotationMatrix(),w3d.rotationZ(-arg)));
+ double[] tmp=w3d.rotationToEuler(tortue.getRotationMatrix());
+ tortue.heading=tmp[2];
+ tortue.roll=tmp[1];
+ tortue.pitch=tmp[0];
+ }
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande = false;
+
+ return DrawPanel.nullType;
+ }
+ /**
+ * This method is used for drawing with primitive "rightroll" or "leftroll"
+ * @param arg
+ */
+ protected void rightroll(double arg) {
+// System.out.println(tortue.angle);
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ if (enabled3D()){
+ tortue.setRotationMatrix(w3d.multiply(tortue.getRotationMatrix(),w3d.rotationY(-arg)));
+ double[] tmp=w3d.rotationToEuler(tortue.getRotationMatrix());
+ tortue.heading=tmp[2];
+ tortue.roll=tmp[1];
+ tortue.pitch=tmp[0];
+ }
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande = false;
+ }
+ /**
+ * This method is used for drawing with primitive "uppitch" or "downpitch"
+ * @param arg
+ */
+ protected void uppitch(double arg) {
+// System.out.println(tortue.angle);
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ if (enabled3D()){
+ tortue.setRotationMatrix(w3d.multiply(tortue.getRotationMatrix(),w3d.rotationX(arg)));
+ double[] tmp=w3d.rotationToEuler(tortue.getRotationMatrix());
+ tortue.heading=tmp[2];
+ tortue.roll=tmp[1];
+ tortue.pitch=tmp[0];
+ }
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande = false;
+ }
+ /**
+ * This method set the turtle's Roll
+ * @param arg The new roll
+ */
+ protected void setRoll(double arg){
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ tortue.roll=arg;
+ tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading));
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande=false;
+ }
+ /**
+ * This method set the turtle's heading
+ * @param arg The new heading
+ */
+ protected void setHeading(double arg){
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ tortue.heading=arg;
+ tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading));
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande=false;
+ }
+ /**
+ * This method set the turtle's pitch
+ * @param arg The new pitch
+ */
+ protected void setPitch(double arg){
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ tortue.pitch=arg;
+ tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading));
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande=false;
+ }
+ /**
+ *
+ * This method set the turtle's orientation
+ * @param arg The new orientation
+ * @throws LogoError If the list doesn't contain three numbers
+ */
+ protected void setOrientation(String arg) throws LogoError{
+ initCoords();
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ extractCoords(arg,Utils.primitiveName("3d.setorientation"));
+ tortue.roll = coords[0];
+ tortue.pitch = coords[1];
+ tortue.heading = coords[2];
+ tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading));
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande=false;
+ }
+ /**
+ * Primitive "origine"
+ */
+ protected void origine(){ // primitive origine
+ try {
+ if (!enabled3D())
+ fpos("0 0");
+ else fpos("0 0 0");
+ } catch (LogoError e) {
+ }
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ tortue.heading = 0;
+ tortue.angle = Math.PI / 2;
+ tortue.roll=0;
+ tortue.pitch=0;
+ if (enabled3D())
+ tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading));
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ }
+
+
+ /**
+ * Primitive distance
+ * @param liste The coords
+ * @param nom
+ * @return The distance from the turtle position to this point
+ * @throws LogoError If bad format list
+ */
+ protected double distance(String liste) throws LogoError {
+
+ initCoords();
+ extractCoords(liste,Utils.primitiveName("distance"));
+ double distance;
+ if (!enabled3D()){
+ coords=this.toScreenCoord(coords,false);
+ distance = Math.sqrt(Math.pow(tortue.corX - coords[0], 2)
+ + Math.pow(tortue.corY - coords[1], 2));
+ }
+ else distance= Math.sqrt(Math.pow(tortue.X - coords[0], 2)
+ + Math.pow(tortue.Y - coords[1], 2)+Math.pow(tortue.Z - coords[2], 2));
+ return distance;
+ }
+ protected double[] vers3D(String liste) throws LogoError{
+ double[] tmp=new double [3];
+ initCoords();
+ extractCoords(liste,Utils.primitiveName("vers"));
+ tmp[0]=coords[0]-tortue.X;
+ tmp[1]=coords[1]-tortue.Y;
+ tmp[2]=coords[2]-tortue.Z;
+ double length=Math.sqrt(Math.pow(tmp[0],2)+Math.pow(tmp[1],2)+Math.pow(tmp[2],2));
+ if (length==0) return tmp;
+ tmp[0]=tmp[0]/length;
+ tmp[1]=tmp[1]/length;
+ tmp[2]=tmp[2]/length;
+ double heading=Math.acos(tmp[1]);
+ double f=Math.sin(heading);
+ double tr_x=-tmp[0]/f;
+ double tr_y=-tmp[2]/f;
+ double roll=Math.atan2(tr_y, tr_x);
+ tmp[0]=-Math.toDegrees(roll);
+ tmp[1]=0;
+ tmp[2]=-Math.toDegrees(heading);
+ for (int i=0;i<3;i++){
+ if (tmp[i]<0) tmp[i]+=360;
+ }
+ return tmp;
+ }
+
+ /**
+ * Primitive towards in 2D MODE
+ * @param liste the coordinate for the point
+ * @return the rotation angle
+ * @throws LogoError if Bad format List
+ */
+ protected double vers2D(String liste) throws LogoError{
+ initCoords();
+ extractCoords(liste,Utils.primitiveName("vers"));
+ double angle;
+ coords=this.toScreenCoord(coords, false);
+ if (tortue.corY == coords[1]) {
+ if (coords[0] > tortue.corX)
+ angle = 90;
+ else if (coords[0] == tortue.corX)
+ angle = 0;
+ else
+ angle = 270;
+ }
+ else if (tortue.corX == coords[0]) {
+ if (tortue.corY > coords[1])
+ angle = 0;
+ else
+ angle = 180;
+ }
+ else {
+ angle = Math.toDegrees(Math.atan(Math
+ .abs(coords[0] - tortue.corX)
+ / Math.abs(tortue.corY - coords[1])));
+ // System.out.println(coords[0] - tortue.corX+" "+Math.abs(tortue.corY - coords[1])+" "+angle);
+ if (coords[0] > tortue.corX && coords[1] > tortue.corY)
+ angle = 180 - angle; // 2eme quadrant
+ else if (coords[0] < tortue.corX && coords[1] > tortue.corY)
+ angle = 180 + angle; // 3eme quadrant
+ else if (coords[0] < tortue.corX && coords[1] < tortue.corY)
+ angle = 360 - angle; // 4eme quadrant
+ }
+ return angle;
+ }
+ /**
+ * Draw with the primitive "setposition" in 2D mode or 3D
+ * @param liste The list with the coordinates to move
+ * @throws LogoError If the coordinates are invalid
+ */
+ protected void fpos(String liste) throws LogoError {
+ initCoords();
+ oldx = tortue.corX;
+ oldy = tortue.corY;
+ extractCoords(liste,Utils.primitiveName("drawing.fpos"));
+ montrecacheTortue(false);
+ if (enabled3D()) {
+ tortue.X = coords[0];
+ tortue.Y = coords[1];
+ tortue.Z = coords[2];
+ }
+ coords=toScreenCoord(coords,true);
+
+ tortue.corX=coords[0];
+ tortue.corY=coords[1];
+ if (tortue.isPenDown()) {
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ if (null==line) line=new Line2D.Double();
+ if (oldx < tortue.corX){
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ if (oldx>tortue.corX){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else if (oldx==tortue.corX){
+ if (oldy<tortue.corY){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else{
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ }
+ line.setLine(x1,y1,x2,y2);
+ tryRecord2DMode(tortue.corX,tortue.corY);
+ g.draw(line);
+ clip();
+ }
+ montrecacheTortue(true);
+ }
+ public void drawEllipseArc(double xAxis,double yAxis, double angleRotation,double xCenter,double yCenter, double angleStart, double angleExtent){
+ montrecacheTortue(false);
+ arc=new Arc2D.Double(-xAxis,-yAxis,2*xAxis,2*yAxis,angleStart,angleExtent,Arc2D.Double.OPEN);
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ double tmpx=WSManager.getUserConfig().getImageWidth()/2+xCenter;
+ double tmpy=WSManager.getUserConfig().getImageHeight()/2-yCenter;
+ g.translate(tmpx, tmpy);
+ g.rotate(-angleRotation);
+ g.draw(arc);
+ g.rotate(angleRotation);
+ g.translate(-tmpx, -tmpy);
+ /* if (null==clipArc) clipArc=new Arc2D.Double();
+ clipArc.setArcByCenter(tortue.corX,tortue.corY,
+ rayon+2+tortue.getPenWidth(),0,360, Arc2D.OPEN);*/
+ clip();
+ montrecacheTortue(true); // on efface la tortue si elle st visible
+ }
+ /**
+ * This method draw an arc on the drawing area
+ * @param rayon The radius
+ * @param pangle Starting angle
+ * @param fangle End angle
+ * @throws LogoError
+ */
+ protected void arc(double rayon, double pangle, double fangle) throws LogoError {
+ // Put fangle and pangle between 0 and 360
+ fangle = ((90 - fangle) % 360);
+ pangle = ((90 - pangle) % 360);
+ if (fangle<0) fangle+=360;
+ if (pangle<0) pangle+=360;
+ // Calculate angle extend
+ double angle=pangle-fangle;
+ if (angle<0) angle+=360;
+ montrecacheTortue(false);
+ if (null==arc) arc=new Arc2D.Double();
+ if (!enabled3D()){
+ if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP) centers=new Vector<Point2D.Double>();
+ arc2D(tortue.corX,tortue.corY,rayon,fangle,angle);
+
+ /* if (null==gp) gp=new GeneralPath();
+ else gp.reset();
+ gp.moveTo((float)(tortue.corX-rayon-tortue.getPenWidth()),
+ (float)(tortue.corY-rayon-tortue.getPenWidth());
+ gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()),
+ (float)(tortue.corY-rayon-tortue.getPenWidth()));
+ gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()),
+ (float)(tortue.corY-rayon-tortue.getPenWidth()));
+ gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()),
+ (float)(tortue.corY-rayon-tortue.getPenWidth()));
+ gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()),
+ (float)(tortue.corY-rayon-tortue.getPenWidth()));*/
+/* if (null==rec) rec=new Rectangle2D.Double();
+ rec.setRect(tortue.corX-rayon-tortue.getPenWidth(),
+ tortue.corY-rayon-tortue.getPenWidth(),
+ 2*(rayon+tortue.getPenWidth()),2*(rayon+tortue.getPenWidth()));*/
+ clip();
+
+ }
+ else{
+ arcCircle3D(rayon,fangle,angle);
+ }
+ montrecacheTortue(true);
+ }
+ private void arc2D(double x, double y, double radius,double fangle, double angle){
+ arc.setArcByCenter(x,y,radius,
+ fangle,angle, Arc2D.OPEN);
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ g.draw(arc);
+ clip();
+
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP){
+ if (x+radius>w&& x<=w){
+ pt=new Point2D.Double(-w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ arc2D(-w+x,y,radius,fangle,angle);
+ }
+ }
+ if (x-radius<0&& x>=0){
+ pt=new Point2D.Double(w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ arc2D(w+x,y,radius,fangle,angle);
+ }
+ }
+ if (y-radius<0&& y>=0){
+ pt=new Point2D.Double(x,h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ arc2D(x,h+y,radius,fangle,angle);
+ }
+ }
+ if (y+radius>h&&y<=h){
+ pt=new Point2D.Double(x,-h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ arc2D(x,-h+y,radius,fangle,angle);
+ }
+ }
+ }
+ }
+
+
+ private void arcCircle3D(double radius,double angleStart,double angleExtent) throws LogoError{
+ if (null==arc) arc=new Arc2D.Double();
+ arc.setArcByCenter(0,0,radius,
+ angleStart,angleExtent, Arc2D.OPEN);
+ Shape s=transformShape(arc);
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ g.draw(s);
+ if (DrawPanel.record3D==DrawPanel.record3D_LINE||DrawPanel.record3D==DrawPanel.record3D_POLYGON){
+ recordArcCircle3D(radius,angleStart,angleExtent);
+ }
+ }
+
+
+ /**
+ *
+ * returns the color for the pixel "ob"
+ * @param liste: The list containing the coordinates of the pixel
+ * @return Color of this pixel
+ * @throws LogoError If the list doesn't contain coordinates
+ */
+ protected Color guessColorPoint(String liste) throws LogoError {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ initCoords();
+ extractCoords(liste,Utils.primitiveName("tc"));
+ coords=toScreenCoord(coords,false);
+ int couleur = -1;
+ int x=(int)coords[0];
+ int y=(int)coords[1];
+ if (0 < x && x < w && 0 < y && y < h) {
+ couleur = DrawPanel.dessin.getRGB(x, y);
+ }
+ return new Color(couleur);
+ }
+ /**
+ * This method draw a circle from the turtle position on the drawing area
+ * @param radius The radius of the circle
+ * @throws LogoError
+ */
+ protected void circle(double radius) throws LogoError {
+ montrecacheTortue(false);
+ if (null==arc) arc=new Arc2D.Double();
+ if (!enabled3D()){
+ if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP) centers=new Vector<Point2D.Double>();
+ circle2D(tortue.corX,tortue.corY,radius);
+ /* if (null==clipArc) clipArc=new Arc2D.Double();
+ clipArc.setArcByCenter(tortue.corX,tortue.corY,
+ rayon+2+tortue.getPenWidth(),0,360, Arc2D.OPEN);*/
+ }
+ else{
+ circle3D(radius);
+ }
+ montrecacheTortue(true); // on efface la tortue si elle st visible
+ }
+ /**
+ * This method draws a circle in 2D mode in WRAP mode, makes recursion to draw all circle part on the screen
+ * @param x x circle center
+ * @param y y circle center
+ * @param circle radius
+ * @uml.property name="pt"
+ */
+ private Point2D.Double pt;
+ /**
+ * @uml.property name="centers"
+ */
+ private Vector <Point2D.Double> centers;
+ private void circle2D(double x,double y, double radius){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ arc.setArcByCenter(x,y,radius,
+ 0,360, Arc2D.OPEN);
+
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ g.draw(arc);
+ clip();
+ if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP){
+ if (x+radius>w&& x<=w){
+ pt=new Point2D.Double(-w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ circle2D(-w+x,y,radius);
+ }
+ }
+ if (x-radius<0&& x>=0){
+ pt=new Point2D.Double(w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ circle2D(w+x,y,radius);
+ }
+ }
+ if (y-radius<0&& y>=0){
+ pt=new Point2D.Double(x,h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ circle2D(x,h+y,radius);
+ }
+ }
+ if (y+radius>h&&y<=h){
+ pt=new Point2D.Double(x,-h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ circle2D(x,-h+y,radius);
+ }
+ }
+ }
+ }
+
+ /**
+ * used for drawing with primitive "dot"
+ * @param liste The list with the dot coordinates
+ * @throws LogoError If the list is invalid coordinates
+ */
+ protected void point(String liste) throws LogoError {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ initCoords();
+ extractCoords(liste,Utils.primitiveName("drawing.point"));
+ coords=toScreenCoord(coords,true);
+// System.out.println(coords[0]+" "+coords[1]+" "+h+" "+w);
+ if (coords[0]>0 && coords[1]>0 && coords[0]<w && coords[1] < h) {
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ if (rec==null) rec=new Rectangle2D.Double();
+ // High quality
+ if (uc.getQuality()==DrawQuality.HIGH){
+ double width=tortue.getPenWidth();
+ rec.setRect(coords[0]-width+0.5,coords[1]-width+0.5,
+ 2*width,2*width);
+ }
+ // Normal or Low Quality
+ else{
+ // penWidth is 2k or 2k+1??
+ int intWidth=(int)(2*tortue.getPenWidth()+0.5);
+ if (intWidth%2==1){
+ double width=tortue.getPenWidth()-0.5;
+// System.out.println(coords[0]+" "+coords[1]);
+ rec.setRect(coords[0]-width,coords[1]-width,
+ 2*width+1,2*width+1);
+ }
+ else {
+ double width=tortue.getPenWidth();
+ rec.setRect(coords[0]-width,coords[1]-width,
+ 2*width,2*width);
+ }
+ }
+ if (uc.getPenShape()==PenShape.SQUARE){ // MAKE ENUM
+ g.fill(rec);
+ }
+ else if (uc.getPenShape()==PenShape.OVAL){
+ if (null==arc) arc=new Arc2D.Double();
+ arc.setArcByCenter(coords[0],coords[1],0,0,360,Arc2D.OPEN);
+ g.draw(arc);
+ }
+ clip();
+ }
+ }
+
+
+
+ /**
+ * @throws LogoError
+ *
+ */
+ private void circle3D(double radius) throws LogoError{
+
+ // In camera world,
+ // the circle is the intersection of
+ // - a plane with the following equation: ax+by+cz+d=0 <-> f(x,y,z)=0
+ // - and a sphere with the following equation: (x-tx)^2+(y-ty)^2+(z-tz)^2=R^2 <-> g(x,y,z)=0
+ // I found the cone equation resolving f(x/lambda,y/lambda,z/lambda)=0=g(x/lambda,y/lambda,z/lambda)
+
+ double[] v=new double[3];
+ for(int i=0;i<3;i++){
+ v[i]=tortue.getRotationMatrix()[i][2];
+ }
+ v[0]+=w3d.xCamera;
+ v[1]+=w3d.yCamera;
+ v[2]+=w3d.zCamera;
+ w3d.toCameraWorld(v);
+ // Now v contains coordinates of a normal vector to the plane in camera world coordinates
+ double a=v[0];
+ double b=v[1];
+ double c=v[2];
+
+ // We convert the turtle coordinates
+ v[0]=tortue.X;
+ v[1]=tortue.Y;
+ v[2]=tortue.Z;
+ w3d.toCameraWorld(v);
+
+ double x=v[0];
+ double y=v[1];
+ double z=v[2];
+ // We calculate the number d for the plane equation
+ double d=-a*x-b*y-c*z;
+
+ // We have to work with Bigdecimal because of precision problems
+
+ BigDecimal[] big=new BigDecimal[6];
+ BigDecimal bx=new BigDecimal(x);
+ BigDecimal by=new BigDecimal(y);
+ BigDecimal bz=new BigDecimal(z);
+ BigDecimal ba=new BigDecimal(a);
+ BigDecimal bb=new BigDecimal(b);
+ BigDecimal bc=new BigDecimal(c);
+ BigDecimal bd=new BigDecimal(d);
+ BigDecimal deux=new BigDecimal("2");
+ BigDecimal screenDistance=new BigDecimal(w3d.screenDistance);
+ BigDecimal bradius=new BigDecimal(String.valueOf(radius));
+
+ // Now we calculate the coefficient for the conic ax^2+bxy+cy^2+dx+ey+f=0
+ // Saved in an array
+
+ // lambda=(x*x+y*y+z*z-radius*radius);
+ BigDecimal lambda=bx.pow(2).add(by.pow(2)).add(bz.pow(2)).subtract(bradius.pow(2));
+
+ // x^2 coeff
+ // d*d+2*d*x*a+a*a*lambda;
+ big[0]=bd.pow(2).add(bd.multiply(bx).multiply(ba).multiply(deux)).add(ba.pow(2).multiply(lambda));
+ // xy coeff
+ // 2*d*x*b+2*d*y*a+2*a*b*lambda;
+ big[1]=deux.multiply(bd).multiply(bx).multiply(bb).add(deux.multiply(bd).multiply(by).multiply(ba)).add(deux.multiply(ba).multiply(bb).multiply(lambda));
+ // y^2 coeff
+ // d*d+2*d*y*b+b*b*lambda;
+ big[2]=bd.pow(2).add(bd.multiply(by).multiply(bb).multiply(deux)).add(bb.pow(2).multiply(lambda));
+ // x coeff
+ // 2*w3d.screenDistance*(d*x*c+d*z*a+lambda*a*c);
+ big[3]=deux.multiply(screenDistance).multiply(bd.multiply(bx).multiply(bc).add(bd.multiply(bz).multiply(ba)).add(lambda.multiply(ba).multiply(bc)));
+ // y coeff
+ // 2*w3d.screenDistance*(d*y*c+d*z*b+lambda*b*c);
+ big[4]=deux.multiply(screenDistance).multiply(bd.multiply(by).multiply(bc).add(bd.multiply(bz).multiply(bb)).add(lambda.multiply(bb).multiply(bc)));
+ // Numbers
+ // Math.pow(w3d.screenDistance,2)*(d*d+2*d*z*c+lambda*c*c);
+ big[5]=screenDistance.pow(2).multiply(bd.pow(2).add(deux.multiply(bd).multiply(bz).multiply(bc)).add(lambda.multiply(bc.pow(2))));
+ new Conic(this,big);
+ if (DrawPanel.record3D==DrawPanel.record3D_LINE||DrawPanel.record3D==DrawPanel.record3D_POLYGON){
+ recordArcCircle3D(radius,0,360);
+ }
+ }
+ /**
+ * This method records this circle in the polygon's List
+ * @param radius The circle's radius
+ * @param angleStart The starting Angle
+ * @param angleExtent The angle for the sector
+ * @throws LogoError
+ */
+ public void recordArcCircle3D(double radius,double angleStart,double angleExtent) throws LogoError{
+ double[][] d=tortue.getRotationMatrix();
+ Matrix3d m=new Matrix3d(d[0][0],d[0][1],d[0][2],d[1][0],d[1][1],d[1][2],d[2][0],d[2][1],d[2][2]);
+ // Vector X
+ Point3d v1=new Point3d(radius/1000,0,0);
+ Transform3D t=new Transform3D(m,new Vector3d(),1);
+ t.transform(v1);
+ // Vector Y
+ Point3d v2=new Point3d(0,radius/1000,0);
+ t.transform(v2);
+
+ // Turtle position
+ Point3d pos=new Point3d(tortue.X/1000,tortue.Y/1000,tortue.Z/1000);
+ int indexMax=(int)angleExtent;
+ if (indexMax!=angleExtent) indexMax+=2;
+ else indexMax+=1;
+ if (null!=DrawPanel.poly&&DrawPanel.poly.getVertexCount()>1)
+ DrawPanel.poly.addToScene();
+ if (DrawPanel.record3D==DrawPanel.record3D_POLYGON) {
+ DrawPanel.poly=new ElementPolygon(cadre.getViewer3D());
+ DrawPanel.poly.addVertex(pos, tortue.couleurcrayon);
+ }
+ else {
+ DrawPanel.poly=new ElementLine(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth());
+ }
+
+ for(int i=0;i<indexMax-1;i++){
+ Point3d tmp1=new Point3d(v1);
+ tmp1.scale(Math.cos(Math.toRadians(angleStart+i)));
+ Point3d tmp2=new Point3d(v2);
+ tmp2.scale(Math.sin(Math.toRadians(angleStart+i)));
+ tmp1.add(tmp2);
+ tmp1.add(pos);
+ DrawPanel.poly.addVertex(tmp1, tortue.couleurcrayon);
+ }
+ Point3d tmp1=new Point3d(v1);
+ tmp1.scale(Math.cos(Math.toRadians(angleStart+angleExtent)));
+ Point3d tmp2=new Point3d(v2);
+ tmp2.scale(Math.sin(Math.toRadians(angleStart+angleExtent)));
+ tmp1.add(tmp2);
+ tmp1.add(pos);
+ DrawPanel.poly.addVertex(tmp1, tortue.couleurcrayon);
+ }
+
+/**
+ * Load an image and draw it on the drawing area
+ * @param image The image to draw
+ */
+ protected void chargeimage(BufferedImage image) {
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ g.setPaintMode();
+ g.translate(tortue.corX, tortue.corY);
+ g.rotate(-tortue.angle);
+ g.drawImage(image, null, 0,0);
+ g.rotate(tortue.angle);
+ g.translate(-tortue.corX, -tortue.corY);
+
+ clip();
+// repaint();
+/* if (null==rec) rec=new Rectangle2D.Double();
+ rec.setRect(tortue.corX,tortue.corY,
+ image.getWidth(),image.getHeight());*/
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ }
+ /**
+ * To guess the length before going out the drawing area in WRAP mode
+ * @param mini The minimum distance before leaving
+ * @param maxi The maximum distance before leaving
+ * @param oldx The X turtle location
+ * @param oldy The Y turtle location
+ * @return the number of steps (Recursive dichotomy)
+ */
+ private double trouve_longueur(double mini, double maxi, double oldx, double oldy) {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ // renvoie la longueur dont on peut encore avancer
+ if (Math.abs(maxi - mini) < 0.5){
+ return (mini);}
+ else {
+ double milieu = (mini + maxi) / 2;
+ double nx = oldx + milieu * Math.cos(tortue.angle);
+ double ny = oldy - milieu * Math.sin(tortue.angle);
+ if (nx < 0 || nx > w|| ny < 0 || ny > h)
+ return trouve_longueur(mini, milieu, oldx, oldy);
+ else
+ return trouve_longueur(milieu, maxi, oldx, oldy);
+ }
+ }
+/**
+ * This method is used for drawing with primitive forward, backward in WRAP MODE
+ * @param arg the length to forward
+ * @param oldx X position
+ * @param oldy Y position
+ */
+ private void trace_enroule(double arg, double oldx, double oldy) {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ boolean re = false;
+ if (arg < 0) {
+ re = true;
+ }
+ double diagonale=Math.sqrt(Math.pow(w,2)+Math.pow(h,2))+1;
+ double longueur;
+ if (re)
+ longueur = trouve_longueur(0, -diagonale, oldx, oldy);
+ else
+ longueur = trouve_longueur(0, diagonale, oldx, oldy);
+// System.out.println(diagonale+" "+oldx+" "+oldy);
+ while (Math.abs(longueur) < Math.abs(arg)) {
+ // System.out.println(Math.abs(longueur)+" "+Math.abs(arg));
+ arg -= longueur;
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC;
+ av(longueur);
+ //System.out.println(Math.abs(longueur)+" "+Math.abs(arg));
+ if (cadre.error)
+ break; //permet d'interrompre avec le bouton stop
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_WRAP;
+ if (uc.getTurtleSpeed() != 0) {
+ try {
+ Thread.sleep(uc.getTurtleSpeed() * 5);
+ } catch (InterruptedException e) {
+ }
+ }
+ if (tortue.isVisible())
+ this.montrecacheTortue(false);
+ if (re) tortue.heading=(tortue.heading+180)%360;
+ if (tortue.corX > w-1
+ && (tortue.heading < 180 && tortue.heading != 0)) {
+ tortue.corX = 0;
+ if (tortue.corY > h-1
+ && (tortue.heading > 90 && tortue.heading < 270))
+ tortue.corY = 0;
+ else if (tortue.corY < 1
+ && (tortue.heading < 90 || tortue.heading > 270))
+ tortue.corY = h;
+ } else if (tortue.corX < 1 && tortue.heading > 180) {
+ tortue.corX = w;
+ if (tortue.corY > h-1
+ && (tortue.heading > 90 && tortue.heading < 270))
+ tortue.corY = 0;
+ else if (tortue.corY < 1
+ && (tortue.heading < 90 || tortue.heading > 270))
+ tortue.corY = h;
+ } else if (tortue.corY > h-1)
+ tortue.corY = 0;
+ else if (tortue.corY < 1)
+ tortue.corY = h;
+ if (re) tortue.heading=(tortue.heading+180)%360;
+ if (tortue.isVisible())
+ this.montrecacheTortue(true);
+ if (re)
+ longueur = trouve_longueur(0, -diagonale, tortue.corX,
+ tortue.corY);
+ else
+ longueur = trouve_longueur(0, diagonale, tortue.corX,
+ tortue.corY);
+ }
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC;
+ if (!cadre.error)
+ av(arg);
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_WRAP;
+ }
+/**
+ * This method is used for drawing with primitive forward, backward in CLOSE MODE
+ * @param oldx X position
+ * @param oldy Y position
+ * @param arg The length to forward
+ * @throws LogoError
+ */
+ private void trace_ferme(double oldx, double oldy, double arg) throws LogoError {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ boolean re = false;
+ double longueur;
+ double diagonale=Math.sqrt(Math.pow(w,2)+Math.pow(h,2))+1;
+ if (arg < 0)
+ re = true;
+ if (re)
+ longueur = trouve_longueur(0, -diagonale, oldx, oldy);
+ else
+ longueur = trouve_longueur(0, diagonale, oldx, oldy);
+ if (Math.abs(longueur) < Math.abs(arg))
+ throw new LogoError(Logo.messages
+ .getString("erreur_sortie1")
+ + "\n"
+ + Logo.messages.getString("erreur_sortie2")
+ + Math.abs((int) (longueur)));
+ else {
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC;
+ av(arg);
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLOSE;
+ }
+ }
+ /**
+ * This method extract coords from a list <br>
+ * X is stored in coords(0], Y stored in coords[1], Z Stored in coords[2]
+ * @param liste The list
+ * @param prim The calling primitive
+ * @throws LogoError If List isn't a list coordinate
+ */
+
+ private void extractCoords(String liste,String prim)throws LogoError{
+ StringTokenizer st = new StringTokenizer(liste);
+ try {
+ for(int i=0;i<coords.length;i++){
+ coords[i]=1;
+ if (!st.hasMoreTokens())
+ throw new LogoError(prim
+ + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ String element = st.nextToken();
+ if (element.equals("-")) {
+ if (st.hasMoreTokens())
+ element = st.nextToken();
+ coords[i] = -1;
+ }
+ coords[i] = coords[i] * Double.parseDouble(element);
+ }
+
+ } catch (NumberFormatException e) {
+ throw new LogoError(prim
+ + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ }
+ if (st.hasMoreTokens())
+ throw new LogoError(prim
+ + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ }
+ /**
+ * This method sets the drawing area to perspective mode
+ */
+
+ protected void perspective(){
+ UserConfig uc = WSManager.getUserConfig();
+ if (!enabled3D()) {
+ uc.setDrawXAxis(false);
+ uc.setDrawYAxis(false);
+ uc.setDrawGrid(false);
+ change_image_tortue(cadre,"tortue0.png");
+ montrecacheTortue(false);
+ DrawPanel.WINDOW_MODE=DrawPanel.WINDOW_3D;
+ w3d=new World3D();
+ montrecacheTortue(true);
+ }
+ }
+ /**
+ * This method sets the drawing area to Wrap, Close or Window mode
+ * @param id The window Mode
+ */
+ protected void setWindowMode(int id){
+ if (DrawPanel.WINDOW_MODE!=id) {
+ montrecacheTortue(false);
+ DrawPanel.WINDOW_MODE=id;
+ w3d=null;
+ montrecacheTortue(true);
+ }
+ }
+
+
+ /**
+ * This method converts the coordinates contained in "coords" towards the coords on the drawing area
+ */
+ double[] toScreenCoord(double[] coord,boolean drawPoly){
+ // If Mode perspective is active
+ if (enabled3D()){
+ // w3d.toScreenCoord(coord);
+ // camera world
+ // If we have to record the polygon coordinates
+ if (DrawPanel.record3D!=DrawPanel.record3D_NONE&&DrawPanel.record3D!=DrawPanel.record3D_TEXT&&drawPoly){
+
+ DrawPanel.poly.addVertex(new Point3d(coord[0]/1000,coord[1]/1000,coord[2]/1000),tortue.couleurcrayon);
+ }
+
+ w3d.toCameraWorld(coord);
+
+ // Convert to screen Coordinates
+ w3d.cameraToScreen(coord);
+ }
+ // Mode2D
+ else {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ coord[0]=w/2+coord[0];
+ coord[1]=h/2-coord[1];
+ }
+ return coord;
+ }
+
+
+
+ /**
+ * This method creates an instance of coord with the valid size:<br>
+ * size 2 for 2D coordinates<br>
+ * size 3 for 3D coordinates
+ */
+
+ private void initCoords(){
+
+ if (null==coords) coords=new double[2];
+ if (enabled3D()){
+ if (coords.length!=3) coords=new double[3];
+ }
+ else {
+ if (coords.length!=2) coords=new double[2];
+ }
+ }
+ public boolean enabled3D(){
+ return (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_3D);
+ }
+
+ /**
+ * For hideturtle and showturtle
+ */
+ protected void ct_mt() {
+ if (null == tortue.tort) {
+ g.setXORMode(couleurfond);
+ g.setColor(tortue.couleurcrayon);
+ tortue.drawTriangle();
+ BasicStroke crayon_actuel = (BasicStroke) g.getStroke();
+ if (crayon_actuel.getLineWidth() == 1)
+ g.draw(tortue.triangle);
+ else {
+ g.setStroke(crayon_triangle);
+ g.draw(tortue.triangle);
+ g.setStroke(crayon_actuel);
+ }
+ } else {
+ g.setXORMode(couleurfond);
+ double angle = Math.PI / 2 - tortue.angle;
+ float x = (float) (tortue.corX * Math.cos(angle) + tortue.corY
+ * Math.sin(angle));
+ float y = (float) (-tortue.corX * Math.sin(angle) + tortue.corY
+ * Math.cos(angle));
+ g.rotate(angle);
+ g.drawImage(tortue.tort, (int) x - tortue.largeur / 2,
+ (int) y - tortue.hauteur / 2, this);
+ g.rotate(-angle);
+ }
+/* if (null==rec) rec=new Rectangle2D.Double();
+ rec.setRect(tortue.corX - tortue.gabarit,
+ tortue.corY - tortue.gabarit,
+ tortue.gabarit * 2,
+ tortue.gabarit * 2);
+ */
+ clip();
+
+/* clip((int) (tortue.corX - tortue.gabarit),
+ (int) (tortue.corY - tortue.gabarit),
+ tortue.gabarit * 2, tortue.gabarit * 2);*/
+ }
+ /**
+ * When the turtle has to be redrawn, this method erase the turtle on the drawing screen
+ *
+ */
+ protected void montrecacheTortue(boolean b) {
+ g.setColor(couleurfond);
+ for (int i = 0; i < tortues_visibles.size(); i++) {
+ int id = Integer.parseInt(tortues_visibles.get(i));
+ // Turtle triangle
+ if (null == tortues[id].tort) {
+ g.setXORMode(couleurfond);
+ g.setColor(tortues[id].couleurmodedessin);
+ tortues[id].drawTriangle();
+ BasicStroke crayon_actuel = (BasicStroke) g.getStroke();
+ if (crayon_actuel.getLineWidth() == 1)
+ g.draw(tortues[id].triangle);
+ else {
+ g.setStroke(crayon_triangle);
+ g.draw(tortues[id].triangle);
+ g.setStroke(crayon_actuel);
+ }
+ } else {
+ // Image turtle
+ g.setXORMode(couleurfond);
+ double angle = Math.PI / 2 - tortues[id].angle;
+ float x = (float) (tortues[id].corX * Math.cos(angle) + tortues[id].corY
+ * Math.sin(angle));
+ float y = (float) (-tortues[id].corX * Math.sin(angle) + tortues[id].corY
+ * Math.cos(angle));
+ g.rotate(angle);
+ g.drawImage(tortues[id].tort, (int) x
+ - tortues[id].largeur / 2, (int) y
+ - tortues[id].hauteur / 2, this);
+ g.rotate(-angle);
+ }
+ /*if (null==rec) rec=new Rectangle2D.Double();
+ rec.setRect(tortues[id].corX - tortues[id].gabarit,
+ tortues[id].corY - tortues[id].gabarit,
+ tortues[id].gabarit * 2,
+ tortues[id].gabarit * 2);
+ shape=rec;*/
+ if (b) clip();
+ }
+ }
+
+
+
+/* private void montrecacheTortue() {
+ // Graphics2D g=(Graphics2D)dessin.getGraphics();
+ g.setColor(couleurfond);
+ for (int i = 0; i < tortues_visibles.size(); i++) {
+ int id = Integer.parseInt(String.valueOf(tortues_visibles
+ .get(i)));
+
+ if (null == tortues[id].tort) {
+ g.setXORMode(couleurfond);
+ g.setColor(tortues[id].couleurmodedessin);
+ tortues[id].coord();
+ BasicStroke crayon_actuel = (BasicStroke) g.getStroke();
+ if (crayon_actuel.getLineWidth() == 1)
+ g.draw(tortues[id].triangle);
+ else {
+ g.setStroke(crayon_triangle);
+ g.draw(tortues[id].triangle);
+ g.setStroke(crayon_actuel);
+ }
+ } else {
+ g.setXORMode(couleurfond);
+ double angle = Math.PI / 2 - tortues[id].angle;
+ float x = (float) (tortues[id].corX * Math.cos(angle) + tortues[id].corY
+ * Math.sin(angle));
+ float y = (float) (-tortues[id].corX * Math.sin(angle) + tortues[id].corY
+ * Math.cos(angle));
+ g.rotate(angle);
+ g.drawImage(tortues[id].tort, (int) x
+ - tortues[id].largeur / 2, (int) y
+ - tortues[id].hauteur / 2, cadre.getArdoise());
+ g.rotate(-angle);
+ }
+ /* if (null==rec) rec=new Rectangle2D.Double();
+ rec.setRect(tortues[id].corX - tortues[id].gabarit,
+ tortues[id].corY - tortues[id].gabarit,
+ tortues[id].gabarit * 2,
+ tortues[id].gabarit * 2);
+
+ clip();
+ // g.dispose();
+ }
+ }
+*/
+ /**
+ * Primitive clearscreen
+ */
+ protected void videecran() {
+ // Delete all Gui Component
+ Set<String> set=gm.keySet();
+ Iterator<String> it=set.iterator();
+ while(it.hasNext()){
+ String element=it.next();
+ gui=gm.get(element).getGuiObject();
+ it.remove();
+ if (SwingUtilities.isEventDispatchThread()){
+ remove(gui);
+ validate();
+ }
+ else {
+ try{
+ SwingUtilities.invokeAndWait(new Runnable(){
+ public void run(){
+ remove(gui);
+ validate();
+ }
+ });
+ }
+ catch(Exception e){}
+ }
+ }
+
+
+ // Delete List Polygon in 3D mode
+// DrawPanel.listPoly=new Vector<Shape3D>();
+// DrawPanel.listText=new Vector<TransformGroup>();
+ // Erase the 3d viewer if visible
+ if (null!=cadre.getViewer3D()) {
+ cadre.getViewer3D().clearScreen();
+ System.gc();
+ }
+
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ g.setPaintMode();
+ couleurfond=uc.getScreencolor();
+ g.setColor(uc.getScreencolor());
+ g.fillRect(0, 0, w,h);
+ stopRecord2DPolygon();
+
+ // Draw Grid
+ g.setStroke(new BasicStroke(1));
+ drawGrid();
+ drawXAxis();
+ drawYAxis();
+ // Init Turtles
+ if (null == tortues[0])
+ tortues[0] = new Turtle(cadre);
+ // The active turtle will be the turtle 0
+ tortue = tortues[0];
+ tortue.id = 0;
+ // We delete all other turtles
+ for (int i = 1; i < tortues.length; i++) {
+ tortues[i] = null;
+ }
+ tortues_visibles.removeAllElements();
+ tortues_visibles.push("0");
+ g.setColor(tortue.couleurcrayon);
+ clip();
+ tortue.init();
+ tortue.setVisible(true);
+ g.setStroke(new BasicStroke(1));
+ montrecacheTortue(true);
+ // Update the selection frame
+ updateColorSelection();
+
+ }
+ /**
+ * Primitive wash
+ */
+ protected void nettoie() {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ stopRecord2DPolygon();
+ g.setPaintMode();
+ g.setColor(couleurfond);
+ g.fillRect(0, 0, w,h);
+
+ drawGrid();
+ /* Réinitialiser les tortues
+ if (null == tortues[0])
+ tortues[0] = new Tortue(cadre);
+ tortue = tortues[0]; //la tortue active sera à présent la
+ // numéro 0
+ tortue.id = 0;
+ for (int i = 1; i < tortues.length; i++) { //On élimine les
+ // autres tortues
+ tortues[i] = null;
+ }
+ tortues_visibles.removeAllElements();
+ tortues_visibles.push("0");*/
+ g.setColor(tortue.couleurcrayon);
+ clip();
+
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ else
+ tortues_visibles=new Stack<String>();
+ }
+ /**
+ * Used for primitive fillzone
+ * @param x
+ * @param y
+ * @param increment
+ * @param couleur_frontiere
+ * @return
+ */
+
+ private int bornes_remplis_zone(int x, int y, int increment, int couleur_frontiere) {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+// System.out.println(x+" "+y);
+ while (!meme_couleur(DrawPanel.dessin.getRGB(x, y) ,couleur_frontiere)) {
+ DrawPanel.dessin.setRGB(x, y, couleur_frontiere);
+ x = x + increment;
+ if (!(x > 0 && x < w-1))
+ break;
+ }
+ return x - increment;
+ }
+ /**
+ * Are the two color equals?
+ * @param col1 The first color
+ * @param col2 The second color
+ * @return true or false
+ */
+ private boolean meme_couleur(int col1,int col2){
+/* if (Config.quality==Logo.QUALITY_HIGH){
+ int rouge1 = (col1 >> 16) & 0xFF;
+ int vert1 = (col1 >> 8) & 0xFF;
+ int bleu1 = col1 & 0xFF;
+ int rouge2 = (col2 >> 16) & 0xFF;
+ int vert2 = (col2 >> 8) & 0xFF;
+ int bleu2 = col2 & 0xFF;
+ int tolerance=120;
+ int diff_rouge=rouge1-rouge2;
+ int diff_bleu=bleu1-bleu2;
+ int diff_vert=vert1-vert2;
+ boolean rouge;boolean vert; boolean bleu;
+ if (rouge1>rouge2){
+ if (rouge1-rouge2< 128 -rouge2/2) rouge=true;
+ else rouge=false;
+ }
+ else{
+ if (rouge2-rouge1<rouge2/2) rouge=true;
+ else rouge=false;
+ }
+ if (vert1>vert2){
+ if (vert1-vert2< 128 -vert2/2) vert=true;
+ else vert=false;
+ }
+ else{
+ if (vert2-vert1<vert2/2) vert=true;
+ else vert=false;
+ }
+ if (bleu1>bleu2){
+ if (bleu1-bleu2< 128 -bleu2/2) bleu=true;
+ else bleu=false;
+ }
+ else{
+ if (bleu2-bleu1<bleu2/2) bleu=true;
+ else bleu=false;
+ }
+
+ return rouge&&bleu&&vert;
+// if (Math.abs(rouge1-rouge2)<tolerance&&Math.abs(vert1-vert2)<tolerance&&Math.abs(bleu1-bleu2)<tolerance&&Math.abs(rouge1+bleu1+vert1-rouge2-bleu2-vert2)<450)
+// return true;
+ // else return false;
+ }
+ else{*/
+ return (col1==col2);
+ //}
+ }
+ /**
+ * Primitive fillzone
+ */
+ protected void rempliszone() {
+ montrecacheTortue(false);
+ int x = (int) (tortue.corX + 0.5);
+ int y = (int) (tortue.corY + 0.5);
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ if (x > 0 & x < w & y > 0 & y < h) {
+ int couleur_origine = DrawPanel.dessin.getRGB(x, y);
+ int couleur_frontiere = tortue.couleurcrayon.getRGB();
+ // System.out.println(couleur_origine+" " +couleur_frontiere);
+ Stack<Point> pile_germes = new Stack<Point>();
+ boolean couleurs_differentes = !meme_couleur(couleur_origine,couleur_frontiere);
+ if (couleurs_differentes)
+ pile_germes.push(new Point(x, y));
+ while (!pile_germes.isEmpty()) {
+
+ Point p = pile_germes.pop();
+ int xgerme = p.x;
+ int ygerme = p.y;
+ int xmax = bornes_remplis_zone(xgerme, ygerme, 1,
+ couleur_frontiere);
+ int xmin=0;
+ if (xgerme>0) xmin = bornes_remplis_zone(xgerme - 1, ygerme, -1,
+ couleur_frontiere);
+ boolean ligne_dessus = false;
+ boolean ligne_dessous = false;
+ for (int i = xmin; i < xmax + 1; i++) {
+ //on recherche les germes au dessus et au dessous
+ if (ygerme > 0
+ && meme_couleur(DrawPanel.dessin.getRGB(i, ygerme - 1) ,couleur_frontiere)) {
+ if (ligne_dessus)
+ pile_germes.push(new Point(i - 1, ygerme - 1));
+ ligne_dessus = false;
+ } else {
+ ligne_dessus = true;
+ if (i == xmax && ygerme > 0)
+ pile_germes.push(new Point(xmax, ygerme - 1));
+ }
+ if (ygerme < h-1
+ && meme_couleur(DrawPanel.dessin.getRGB(i, ygerme + 1),couleur_frontiere)) {
+ if (ligne_dessous)
+ pile_germes.push(new Point(i - 1, ygerme + 1));
+ ligne_dessous = false;
+ } else {
+ ligne_dessous = true;
+ if (i == xmax && ygerme < h-1)
+ pile_germes.push(new Point(xmax, ygerme + 1));
+ }
+ }
+ }
+ clip();
+ montrecacheTortue(true);
+ }
+ }
+ /**
+ * Used for primitive "fill"
+ * @param x
+ * @param y
+ * @param increment
+ * @param couleur_crayon
+ * @param couleur_origine
+ * @return
+ */
+ private int bornes_remplis(int x, int y, int increment, int couleur_crayon,
+ int couleur_origine) {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ while (DrawPanel.dessin.getRGB(x, y) == couleur_origine) {
+ DrawPanel.dessin.setRGB(x, y, couleur_crayon);
+ x = x + increment;
+ if (!(x > 0 && x < w-1))
+ break;
+ }
+ return x - increment;
+ }
+ /**
+ * Primitive "fill"
+ */
+ protected void remplis() {
+ montrecacheTortue(false);
+ int x = (int) (tortue.corX + 0.5);
+ int y = (int) (tortue.corY + 0.5);
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (x > 0 & x < w & y > 0 & y < h) {
+ int couleur_origine = DrawPanel.dessin.getRGB(x, y);
+ int couleur_crayon = tortue.couleurcrayon.getRGB();
+ if (x > 0 & x < w & y > 0 & y < h) {
+ Stack<Point> pile_germes = new Stack<Point>();
+ boolean couleurs_differentes = !(couleur_origine == couleur_crayon);
+ if (couleurs_differentes)
+ pile_germes.push(new Point(x, y));
+ while (!pile_germes.isEmpty()) {
+ Point p = pile_germes.pop();
+ int xgerme = p.x;
+ int ygerme = p.y;
+ // System.out.println(xgerme+" "+ygerme);
+ int xmax = bornes_remplis(xgerme, ygerme, 1, couleur_crayon,
+ couleur_origine);
+ int xmin=0;
+ if (xgerme>0) xmin = bornes_remplis(xgerme - 1, ygerme, -1,
+ couleur_crayon, couleur_origine);
+ // System.out.println("xmax "+xmax+"xmin "+xmin);
+ boolean ligne_dessus = false;
+ boolean ligne_dessous = false;
+ for (int i = xmin; i < xmax + 1; i++) {
+ //on recherche les germes au dessus et au dessous
+ if (ygerme > 0
+ && DrawPanel.dessin.getRGB(i, ygerme - 1) != couleur_origine) {
+ if (ligne_dessus)
+ pile_germes.push(new Point(i - 1, ygerme - 1));
+ ligne_dessus = false;
+ } else {
+ ligne_dessus = true;
+ if (i == xmax && ygerme > 0)
+ pile_germes.push(new Point(xmax, ygerme - 1));
+ }
+ if (ygerme < h-1
+ && DrawPanel.dessin.getRGB(i, ygerme + 1) != couleur_origine) {
+ if (ligne_dessous)
+ pile_germes.push(new Point(i - 1, ygerme + 1));
+ ligne_dessous = false;
+ } else {
+ ligne_dessous = true;
+ if (i == xmax && ygerme < h-1)
+ pile_germes.push(new Point(xmax, ygerme + 1));
+ }
+ }
+ }
+ clip();
+ montrecacheTortue(true);
+ }
+ }
+ }
+ /**
+ * Primitive "label"
+ * @param mot The word to write on the drawing area
+ */
+ protected void etiquette(String mot) {
+ // Graphics2D g = (Graphics2D) Ardoise.dessin.getGraphics();
+ montrecacheTortue(false);
+ if (!enabled3D()){
+ double angle = Math.PI / 2 - tortue.angle;
+ if(DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP) centers=new Vector<Point2D.Double>();
+ etiquette2D(tortue.corX,tortue.corY,angle,mot);
+/* g.rotate(angle);
+ g.setPaintMode();
+ g.setColor(tortue.couleurcrayon);
+ float x = (float) (tortue.corX * Math.cos(angle) + tortue.corY
+ * Math.sin(angle));
+ float y = (float) (-tortue.corX * Math.sin(angle) + tortue.corY
+ * Math.cos(angle));
+ g.setFont(Panel_Font.fontes[police_etiquette]
+ .deriveFont((float) tortue.police));
+ g.drawString(mot, x, y);
+ g.rotate(-angle);*/
+ }
+ else{
+ FontRenderContext frc=g.getFontRenderContext();
+ GlyphVector gv=g.getFont().createGlyphVector(frc, mot);
+ Shape outline=gv.getOutline(0, 0);
+ Shape s=transformShape(outline);
+ g.setPaintMode();
+ g.setColor(tortue.couleurcrayon);
+ g.fill(s);
+ if (record3D==DrawPanel.record3D_TEXT){
+ Text2D text=new Text2D(
+ mot,new Color3f(tortue.couleurcrayon), UserConfig.fontes[police_etiquette].getName(),
+ tortue.police,Font.PLAIN);
+
+ text.setRectangleScaleFactor(0.001f);
+ Appearance appear=text.getAppearance();
+ PolygonAttributes pa=new PolygonAttributes();
+ pa.setCullFace(PolygonAttributes.CULL_NONE);
+ pa.setBackFaceNormalFlip(true);
+ appear.setPolygonAttributes(pa);
+ text.setAppearance(appear);
+// if (null==DrawPanel.listText) DrawPanel.listText=new Vector<TransformGroup>();
+ TransformGroup tg=new TransformGroup();
+ double[][] d=tortue.getRotationMatrix();
+ Matrix3d m=new Matrix3d(d[0][0],d[0][1],d[0][2],d[1][0],d[1][1],d[1][2],d[2][0],d[2][1],d[2][2]);
+ Transform3D t=new Transform3D(m,new Vector3d(tortue.X/1000,tortue.Y/1000,tortue.Z/1000),1);
+ tg.setTransform(t);
+ tg.addChild(text);
+ cadre.getViewer3D().add2DText(tg);
+// DrawPanel.listText.add(tg);
+ }
+
+
+ }
+ montrecacheTortue(true);
+ if (classicMode) repaint();
+ }
+ private void etiquette2D(double x,double y, double angle, String word){
+ if (word.length()==0) return;
+
+ g.setPaintMode();
+ g.setColor(tortue.couleurcrayon);
+ Font f= UserConfig.fontes[police_etiquette]
+ .deriveFont((float) tortue.police);
+ g.setFont(f);
+ g.translate(x, y);
+ g.rotate(angle);
+ FontRenderContext frc = g.getFontRenderContext();
+ TextLayout layout = new TextLayout(word, f, frc);
+ Rectangle2D bounds = layout.getBounds();
+ float height=(float)bounds.getHeight();
+ float width=(float)bounds.getWidth();
+ float x1=0,y1=0;
+ switch(tortue.getLabelHorizontalAlignment()){
+ case Turtle.LABEL_HORIZONTAL_ALIGNMENT_LEFT:
+ x1=0;
+ break;
+ case Turtle.LABEL_HORIZONTAL_ALIGNMENT_CENTER:
+ x1=-width/2;
+ break;
+ case Turtle.LABEL_HORIZONTAL_ALIGNMENT_RIGHT:
+ x1=-width;
+ break;
+ }
+ switch(tortue.getLabelVerticalAlignment()){
+ case Turtle.LABEL_VERTICAL_ALIGNMENT_BOTTOM:
+ y1=0;
+ break;
+ case Turtle.LABEL_VERTICAL_ALIGNMENT_CENTER:
+ y1=height/2;
+ break;
+ case Turtle.LABEL_VERTICAL_ALIGNMENT_TOP:
+ y1=height;
+ break;
+ }
+ layout.draw(g, x1, y1);
+ g.drawString(word, x1, y1);
+ g.rotate(-angle);
+ g.translate(-x, -y);
+ if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP){
+ Rectangle2D.Double rec=new Rectangle2D.Double(0,0,width,height);
+ AffineTransform at=new AffineTransform();
+ at.translate(x, y);
+ at.rotate(angle);
+ Rectangle2D bound =at.createTransformedShape(rec).getBounds2D();
+ double right= bound.getX()+bound.getWidth()-x;
+ double left= x-bound.getX();
+ double up=y-bound.getY();
+ double down=bound.getY()+bound.getHeight()-y;
+
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (x+right>w&& x<=w){
+ pt=new Point2D.Double(-w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ etiquette2D(-w+x,y,angle,word);
+ }
+ }
+ if (x-left<0&& x>=0){
+ pt=new Point2D.Double(w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ etiquette2D(w+x,y,angle,word);
+ }
+ }
+ if (y-up<0&& y>=0){
+ pt=new Point2D.Double(x,h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ etiquette2D(x,h+y,angle,word);
+ }
+ }
+ if (y+down>h&&y<=h){
+ pt=new Point2D.Double(x,-h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ etiquette2D(x,-h+y,angle,word);
+ }
+ }
+ }
+ }
+
+ /**
+ * This method transform a plane 2D shape in the shape corresponding to the turtle plane
+ * @param s the first shape
+ * @return the new shape after transformation
+ */
+ private Shape transformShape(Shape s){
+ PathIterator it=s.getPathIterator(null);
+ double[] d=new double[6];
+ double[][] coor=new double[3][1];
+ GeneralPath gp=new GeneralPath();
+ double[] end=new double[3];
+ double[] ctl1=new double[3];
+ double[] ctl2=new double[3];
+ boolean b=false;
+ while(!it.isDone()){
+ it.next();
+ int id=0;
+ if (!it.isDone()) id=it.currentSegment(d);
+ else break;
+ coor[0][0]=d[0];
+ coor[1][0]=-d[1];
+ coor[2][0]=0;
+ coor=w3d.multiply(tortue.getRotationMatrix(), coor);
+
+ end[0]=coor[0][0]+tortue.X;
+ end[1]=coor[1][0]+tortue.Y;
+ end[2]=coor[2][0]+tortue.Z;
+ w3d.toScreenCoord(end);
+
+ if (id==PathIterator.SEG_MOVETO)
+ gp.moveTo((float)end[0], (float)end[1]);
+ else if (id==PathIterator.SEG_LINETO)
+ {
+ if (!b) {
+ b=true;
+ gp.moveTo((float)end[0], (float)end[1]);
+ }
+ else gp.lineTo((float)end[0], (float)end[1]);
+ }
+ else if (id==PathIterator.SEG_CLOSE){
+ gp.closePath();
+ }
+ else {
+ if (!b) {
+ b=true;
+ Point2D p=null;
+ if (s instanceof Arc2D.Double)
+ p=((Arc2D.Double)s).getStartPoint();
+ else if (s instanceof GeneralPath)
+ p=((GeneralPath)s).getCurrentPoint();
+ coor[0][0]=p.getX();
+ coor[1][0]=-p.getY();
+ coor[2][0]=0;
+ coor=w3d.multiply(tortue.getRotationMatrix(), coor);
+ ctl1[0]=coor[0][0]+tortue.X;
+ ctl1[1]=coor[1][0]+tortue.Y;
+ ctl1[2]=coor[2][0]+tortue.Z;
+ w3d.toScreenCoord(ctl1);
+ gp.moveTo((float)ctl1[0], (float)ctl1[1]);
+ }
+ coor[0][0]=d[2];
+ coor[1][0]=-d[3];
+ coor[2][0]=0;
+ coor=w3d.multiply(tortue.getRotationMatrix(), coor);
+ ctl1[0]=coor[0][0]+tortue.X;
+ ctl1[1]=coor[1][0]+tortue.Y;
+ ctl1[2]=coor[2][0]+tortue.Z;
+ w3d.toScreenCoord(ctl1);
+ if(id==PathIterator.SEG_QUADTO){
+ QuadCurve2D qc=new QuadCurve2D.Double(gp.getCurrentPoint().getX(),gp.getCurrentPoint().getY()
+ ,end[0], end[1],ctl1[0], ctl1[1]);
+ gp.append(qc, true);}
+ else if (id==PathIterator.SEG_CUBICTO){
+ coor[0][0]=d[4];
+ coor[1][0]=-d[5];
+ coor[2][0]=0;
+ coor=w3d.multiply(tortue.getRotationMatrix(), coor);
+
+ ctl2[0]=coor[0][0]+tortue.X;
+ ctl2[1]=coor[1][0]+tortue.Y;
+ ctl2[2]=coor[2][0]+tortue.Z;
+
+ w3d.toScreenCoord(ctl2);
+ CubicCurve2D qc=new CubicCurve2D.Double(gp.getCurrentPoint().getX(),gp.getCurrentPoint().getY()
+ ,end[0], end[1],ctl1[0], ctl1[1],ctl2[0], ctl2[1]);
+ gp.append(qc, true);
+ }
+ }
+ }
+ return gp;
+ }
+ public World3D getWorld3D(){
+ return w3d;
+ }
+ /**
+ * primitive setscreencolor
+ * @param color The Color of the nackground screen
+ */
+ protected void fcfg(Color color) {
+ couleurfond=color;
+ updateColorSelection();
+ if (enabled3D()){
+ if (cadre.getViewer3D()!=null){
+ cadre.getViewer3D().updateBackGround(couleurfond);
+ }
+ }
+ nettoie();
+ }
+ /**
+ * Primitive setpencolor
+ * @param color The pen Color
+ */
+ protected void fcc(Color color) {
+ if (tortue.isVisible()&&null==tortue.tort) montrecacheTortue(false);
+ tortue.couleurcrayon = color;
+ tortue.couleurmodedessin = color;
+ if (tortue.isVisible()&&null==tortue.tort) montrecacheTortue(true);
+ }
+
+ /**
+ * Primitive "guiposition"
+ * @param id The id for the gui Object
+ * @param liste The Coordinates list
+ * @param name The translated name for the primitive "guiposition"
+ * @throws LogoError If coordinates list is invalid
+ */
+ protected void guiposition(String id, String liste,String name) throws LogoError{
+ if (guiExist(id)){
+ initCoords();
+ extractCoords(liste,name);
+ coords=toScreenCoord(coords,false);
+ gm.get(id).setLocation((int)coords[0],(int)coords[1]);
+ }
+ }
+ /**
+ * Draw the Gui object refered with "id"
+ * @param id The Gui Object Id
+ * @throws LogoError If this object doesn't exist
+ */
+ protected void guiDraw(String id) throws LogoError{
+ if (guiExist(id)){
+ GuiComponent gc=gm.get(id);
+ add(gc.getGuiObject());
+ validate();
+ repaint();
+ // updateGuiComponents();
+ }
+ }
+ /**
+ * @uml.property name="gui"
+ * @uml.associationEnd
+ */
+ private javax.swing.JComponent gui;
+ /**
+ * This method erases a Gui on the drawing area
+ * @param id The Gui Object id
+ * @throws LogoError
+ */
+
+ protected void guiRemove(String id) throws LogoError{
+ if (guiExist(id)){
+ gui=gm.get(id).getGuiObject();
+ gm.remove(id);
+ if (SwingUtilities.isEventDispatchThread()){
+ remove(gui);
+ validate();
+ }
+ else {
+ try{
+ SwingUtilities.invokeAndWait(new Runnable(){
+ public void run(){
+ remove(gui);
+ validate();
+ }
+ });
+ }
+ catch(Exception e){}
+ }
+ repaint();
+ }
+ }
+ private StringBuffer extractList(String list) throws LogoError{
+ StringBuffer sb=new StringBuffer();
+ int compteur=0;
+ int i=0;
+ while(list.length()!=0){
+ char c=list.charAt(i);
+ if (c=='[') compteur++;
+ else if (c==']') {
+ if (compteur==0) return sb;
+ else compteur--;
+ }
+ sb.append(c);
+ i++;
+ }
+ throw new LogoError("[ "+list+" "+Logo.messages.getString("pas_liste"));
+ }
+
+ protected void guiAction(String id, String liste) throws LogoError{
+ if (guiExist(id)){
+ GuiComponent gc=gm.get(id);
+ // If gc is a JButton
+ if (gc.isButton()){
+ ((GuiButton)gc).setAction(Utils.decoupe(liste));
+ if (!gc.hasAction()){
+ ((javax.swing.JButton)gc.getGuiObject()).addActionListener(gc);
+ gc.hasAction=true;
+ }
+ }
+ // gc is a JcomboBox
+ else if (gc.isMenu()){
+ liste=liste.trim();
+ int i=0;
+ while(liste.length()!=0){
+ if (liste.charAt(0)=='['){
+ liste=liste.substring(1).trim();
+ StringBuffer sb=extractList(liste);
+ liste=liste.substring(sb.length()+1).trim();
+ ((GuiMenu)gc).setAction(sb, i);
+ i++;
+ }
+ else throw new LogoError(liste.charAt(0)+" "+Logo.messages.getString("pas_liste"));
+ }
+ GuiMenu gm=(GuiMenu)gc;
+ if (!gm.hasAction){
+ gm.hasAction=true;
+ ((javax.swing.JComboBox)gc.getGuiObject()).addActionListener(gm);
+ }
+ }
+ }
+ }
+ private boolean guiExist(String id) throws LogoError{
+ if (gm.containsKey(id.toLowerCase())) return true;
+ else throw new LogoError(Logo.messages.getString("no_gui")+" "+id);
+ }
+// boolean access=false;
+ private void clip(){
+ if (classicMode){
+
+ //access=true;
+// refresh();
+
+ repaint();
+ /* if (SwingUtilities.isEventDispatchThread()){
+ repaint();
+ }
+ else {
+ try {
+
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run(){
+ repaint();
+ }
+ });
+ }
+ catch(Exception e2){}
+ }*/
+ }
+ /*Rectangle rec1=cadre.jScrollPane1.getViewport().getViewRect();
+ boolean inter=sh.intersects(rec1);
+ if (inter){
+ if (classicMode){
+ repaint();
+ }
+ }*/
+ }
+ public void setQuality(DrawQuality q){
+ /*
+ * Marko Zivkovic
+ * I improved all the qualities for one level. I introduced a super high quality that is assigned to "high"
+ * and dropped the low quality, which is now the old normal/default quality
+ */
+
+ if (q==DrawQuality.HIGH){
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,RenderingHints.VALUE_STROKE_PURE);
+ }
+ else if(q==DrawQuality.LOW){
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_DEFAULT);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_DEFAULT);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
+ }
+ else { //normal
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ }
+ }
+ public void change_image_tortue(Application cadre, String chemin) {
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ if (chemin.equals("tortue0.png")) {
+ tortue.tort = null;
+ tortue.largeur = 26;
+ tortue.hauteur = 26;
+ } else {
+ //ON teste tout d'abord si le chemin est valide
+ if (null == Utils.class.getResource(chemin))
+ chemin = "tortue1.png";
+ tortue.tort = Toolkit.getDefaultToolkit().getImage(
+ Utils.class.getResource(chemin));
+ MediaTracker tracker = new MediaTracker(cadre.getFrame());
+ tracker.addImage(tortue.tort, 0);
+ try {
+ tracker.waitForID(0);
+ } catch (InterruptedException e1) {
+ }
+ double largeur_ecran = Toolkit.getDefaultToolkit().getScreenSize()
+ .getWidth();
+ // On fait attention à la résolution de l'utilisateur
+ double facteur = largeur_ecran / 1024.0;
+
+ if ((int) (facteur + 0.001) != 1) {
+ tortue.largeur = tortue.tort.getWidth(cadre.getFrame());
+ tortue.hauteur = tortue.tort.getHeight(cadre.getFrame());
+ tortue.tort = tortue.tort.getScaledInstance(
+ (int) (facteur * tortue.largeur),
+ (int) (facteur * tortue.hauteur),
+ Image.SCALE_SMOOTH);
+ tracker = new MediaTracker(cadre.getFrame());
+ tracker.addImage(tortue.tort, 0);
+ try {
+ tracker.waitForID(0);
+ } catch (InterruptedException e1) {
+ }
+ }
+ tortue.largeur = tortue.tort.getWidth(cadre.getFrame());
+ tortue.hauteur = tortue.tort.getHeight(cadre.getFrame());
+ }
+ tortue.gabarit = Math.max(tortue.hauteur,
+ tortue.largeur);
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+
+ }
+ // animation
+ protected void setAnimation(boolean predic){
+ if (predic==classicMode){
+ if (predic) {
+ cadre.getHistoryPanel().active_animation();
+ }
+ else {
+ cadre.getHistoryPanel().stop_animation();
+ repaint();
+ }
+ }
+ }
+
+ protected void setGraphicsFont(Font f){
+ g.setFont(f);
+ }
+ protected Font getGraphicsFont(){
+ return g.getFont();
+ }
+ protected void setStroke(Stroke st){
+ g.setStroke(st);
+ }
+ public Color getBackgroundColor(){
+ return couleurfond;
+ }
+ protected void setBackgroundColor(Color c){
+ couleurfond=c;
+ }
+ protected void updateColorSelection(){
+ float r=(255-couleurfond.getRed())/255;
+ float v=(255-couleurfond.getGreen())/255;
+ float b=(255-couleurfond.getBlue())/255;
+ colorSelection=new Color(r,v,b,0.2f);
+ }
+ public void setNumberOfTurtles(int max){
+ WSManager.getUserConfig().setMaxTurtles(max);
+ Turtle[] tampon = tortues.clone();
+ tortues = new Turtle[max];
+ int borne_sup=Math.min(tampon.length,tortues.length);
+ for(int i=0;i<borne_sup;i++){
+ tortues[i]=tampon[i];
+ }
+ for(int i=tortues_visibles.size()-1;i>-1;i--){
+ int integer=Integer.parseInt(tortues_visibles.get(i));
+ if (integer>=max){
+ tortues_visibles.remove(i);
+ }
+ }
+ }
+ protected void initGraphics(){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ police_etiquette=UserConfig.police_id(WSManager.getWorkspaceConfig().getFont());
+ // init all turtles
+ tortues = new Turtle[uc.getMaxTurtles()];
+ tortues_visibles=new Stack<String>();
+ tortue=new Turtle(cadre);
+ tortues[0] = tortue;
+ tortue.id = 0;
+ tortues_visibles.push("0");
+ for (int i = 1; i < tortues.length; i++) {
+ // All other turtles are null
+ tortues[i] = null;
+ }
+ g=(Graphics2D)dessin.getGraphics();
+ couleurfond=uc.getScreencolor();
+ setQuality(uc.getQuality());
+ g.setColor(uc.getScreencolor());
+ g.fillRect(0,0,w,h);
+ g.setColor(uc.getScreencolor());
+ if (!enabled3D()){
+ drawGrid();
+ drawXAxis();
+ drawYAxis();
+ }
+ MediaTracker tracker;
+ if (0==uc.getActiveTurtle()) {
+ g.setXORMode(couleurfond);
+ tortue.drawTriangle();
+ g.setColor(tortue.couleurcrayon);
+ g.draw(tortue.triangle);
+ }
+ else {
+ g.setXORMode(couleurfond);
+ tracker=new MediaTracker(cadre.getFrame());
+ tracker.addImage(tortue.tort,0);
+ try{tracker.waitForID(0);}
+ catch(InterruptedException e){}
+ if (tracker.checkID(0)) g.drawImage(tortue.tort, w/2 - tortue.largeur / 2,
+ h/2 - tortue.hauteur/2, this);
+ }
+ updateColorSelection();
+ }
+
+ private void resizeAllGuiComponents(double d){
+ // Resize all GuiComponent
+ Set<String> set=gm.keySet();
+ Iterator<String> it=set.iterator();
+ while (it.hasNext()){
+ String element=it.next();
+ GuiComponent gui=gm.get(element);
+ gui.getGuiObject().setSize((int)(gui.getOriginalWidth()*d),
+ (int)(gui.getOriginalHeight()*d) );
+ Font f=gui.getGuiObject().getFont();
+ gui.getGuiObject().setFont(f.deriveFont((float)(WSManager.getWorkspaceConfig().getFont().getSize()*d)));
+ double x=gui.getLocation().x/zoom;
+ double y=gui.getLocation().y/zoom;
+ gui.setLocation((int)(x*d),(int)(y*d));
+
+ }
+
+ }
+
+
+ /**
+ * Make a zoom on the drawing area
+ * @param d The absolute factor
+ */
+ public void zoom(double d, boolean zoomIn){
+ // Disable zoom buttons
+ //cadre.setZoomEnabled(false); // TODO REMOVE ZOOM COMPLETELY?
+
+ javax.swing.JViewport jv=cadre.scrollArea.getViewport();
+ Point p=jv.getViewPosition();
+ Rectangle r=jv.getVisibleRect();
+
+
+ // If a selection rectangle is displaying on the drawing area
+ // And If zoomout has been pressed
+ // Zooming on the rectangular selection
+ if (null!=selection&&cadre.commande_isEditable()&&zoomIn){
+ int originalWidth=jv.getWidth();
+ double width=selection.getWidth();
+ d=zoom*originalWidth/width;
+ p=selection.getLocation();
+ r.width=selection.width;
+ // adjust height in the same ratio as width
+ r.height=r.height*(int)width/originalWidth;
+ // erase selection
+ selection=null;
+ }
+ // Resize all Gui Components on the drawing area
+ resizeAllGuiComponents(d);
+
+ double oldZoom=zoom;
+ zoom=d;
+
+ /*
+ * -------------------------------------
+ * | |
+ * | ------------------------- |
+ * | | | |
+ * | | | |
+ * | | x-- dx----- | --> CenterView Point of the rectangle
+ * | | | | |
+ * | | dy | |
+ * | ------------------------- |
+ * -------------------------------------
+ * */
+
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ double dx=Math.min(r.width,w*oldZoom)/2;
+ double dy=Math.min(r.height,h*oldZoom)/2;
+ Point centerView=new Point((int)(p.x+dx),(int)(p.y+dy));
+
+ // Dynamically modify the drawing Area size
+ setPreferredSize(new java.awt.Dimension(
+ (int)(w*zoom)
+ ,(int)(h*zoom)));
+
+ SwingUtilities.invokeLater(new PositionJViewport(jv,
+ new Point((int)(centerView.x/oldZoom*zoom-dx),
+ (int)(centerView.y/oldZoom*zoom-dy))));
+
+ }
+ private Color getTransparencyColor(int color,int trans){
+ Color c=new Color(color);
+ return new Color(c.getRed(),c.getGreen(),c.getBlue(),trans);
+ }
+ /**
+ * Draw the horizontal axis
+ */
+ private void drawXAxis(){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (uc.isDrawXAxis()){
+ g.setColor(getTransparencyColor(uc.getAxisColor(),128));
+ g.drawLine(0,h/2,w,h/2);
+ for (int i=w/2%uc.getXAxis();i<w;i=i+uc.getXAxis()){
+ g.drawLine(i, h/2-2, i, h/2+2);
+ g.setFont(new Font("Dialog",Font.PLAIN,10));
+ String tick=String.valueOf(i-w/2);
+ FontMetrics fm=g.getFontMetrics();
+ int back=fm.stringWidth(String.valueOf(tick))/2;
+ // if the both axes are drawn, the zero has to translated
+ // So we don't draw the zero
+ if (i!=w/2||!uc.isDrawYAxis()) g.drawString(tick, i-back, h/2+20);
+ }
+ }
+ }
+ /**
+ * Draw the vertical axis
+ */
+ private void drawYAxis(){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (uc.isDrawYAxis()){
+ g.setColor(getTransparencyColor(uc.getAxisColor(),128));
+ g.drawLine(w/2,0,w/2,h);
+ for (int i=h/2%uc.getYAxis();i<h;i=i+uc.getYAxis()){
+ g.drawLine( w/2-2, i, w/2+2,i);
+ g.setFont(new Font("Dialog",Font.PLAIN,10));
+ String tick=String.valueOf(h/2-i);
+ // If both axes are drawn, zero is translated
+ if (i==h/2&&uc.isDrawXAxis()) g.drawString("0", w/2+10, i-5);
+ else g.drawString(tick, w/2+10, i+5);
+ }
+ }
+ }
+ private void drawGrid(){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (uc.isDrawGrid()){
+ g.setStroke(new BasicStroke(1));
+ g.setColor(getTransparencyColor(uc.getGridColor(),100));
+ for (int i=w/2%uc.getXGrid();i<w;i=i+uc.getXGrid())
+ g.drawLine(i, 0, i, h);
+
+ for (int i=h/2%uc.getYGrid();i<h;i=i+uc.getYGrid())
+ g.drawLine(0,i, w, i);
+ }
+ }
+ // In animation mode, we have to wait for the drawing to be finished before modifying graphics.
+ // Thread must be synchronized.
+ protected synchronized void refresh(){
+ repaint();
+ try{
+ wait();
+ }
+ catch(InterruptedException e){}
+
+ }
+
+ protected synchronized void paintComponent(Graphics graph){
+ super.paintComponent(graph);
+ Graphics2D g2d=(Graphics2D)graph;
+ if (null==shape){
+ g2d.setClip(cadre.scrollArea.getViewport().getViewRect());
+ }
+ else {
+ g2d.setClip(shape);
+ shape=null;
+ }
+ g2d.scale(DrawPanel.zoom,DrawPanel.zoom);
+ g2d.drawImage(dessin,0,0,this);
+ g2d.scale(1/DrawPanel.zoom,1/DrawPanel.zoom);
+ if (!Affichage.execution_lancee&&null!=selection&&cadre.commande_isEditable()){
+ g2d.setColor(colorSelection);
+ g2d.fillRect(selection.x, selection.y, selection.width, selection.height);
+ }
+ notify();
+ }
+ public void active_souris(){
+ lissouris=false;
+ }
+ public boolean get_lissouris(){
+ return lissouris;
+ }
+ public int get_bouton_souris(){
+ lissouris=false;
+ return bouton_souris;
+ }
+ public String get_possouris(){
+ lissouris=false;
+ return possouris;
+ }
+ public void mousePressed(MouseEvent e){
+ if (!Affichage.execution_lancee) {
+ selection=new Rectangle();
+ origine=new Point(e.getPoint());
+ selection.setSize(0, 0);
+ }
+ }
+ public void mouseReleased(MouseEvent e){}
+ public void mouseClicked(MouseEvent ev){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ if (!Affichage.execution_lancee){
+ selection=null;
+ origine=null;
+ repaint();
+ }
+ else{
+ lissouris=true;
+ bouton_souris=ev.getButton();
+ Point point=ev.getPoint();
+ possouris="[ "+(point.x-w/2)+" "+(h/2-point.y)+" ] ";
+ }
+ }
+
+ public void mouseExited(MouseEvent e){
+ }
+ public void mouseEntered(MouseEvent e){
+ }
+ // Select an export area
+ public void mouseDragged(MouseEvent e){
+ if (!Affichage.execution_lancee&&null!=selection){
+ // First, we test if we need to move the scrollbars
+ Point pos=e.getPoint();
+ javax.swing.JViewport jv=cadre.scrollArea.getViewport();
+ Point viewPosition=jv.getViewPosition();
+ Rectangle r=jv.getVisibleRect();
+ r.setLocation(viewPosition);
+ // Is the point visible on screen?
+ boolean b=r.contains(pos);
+
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ // Move the scroolPane if necessary
+ if (!b){
+ int x,y;
+ if (pos.x<viewPosition.x) x=Math.max(0,pos.x);
+ else if (pos.x>viewPosition.x+r.width) x=Math.min(pos.x-r.width,(int)(w*zoom-r.width));
+ else x=viewPosition.x;
+ if (pos.y<viewPosition.y) y=Math.max(0,pos.y);
+ else if (pos.y>viewPosition.y+r.height) y=Math.min(pos.y-r.height,(int)(h*zoom-r.height));
+ else y=viewPosition.y;
+ jv.setViewPosition(new Point(x,y));
+ }
+
+ // Then , drawing the selection area
+
+ selection.setFrameFromDiagonal(origine, e.getPoint());
+ repaint();
+ }
+ }
+
+ public void mouseMoved(MouseEvent ev) {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ lissouris = true;
+ bouton_souris = 0;
+ Point point = ev.getPoint();
+ possouris = "[ " + (point.x - w / 2) + " " + (h / 2 - point.y) + " ] ";
+ }
+
+ protected void addToGuiMap(GuiComponent gc) throws xlogo.kernel.LogoError {
+ gm.put(gc);
+ }
+
+ // This method modifies all Shape for any turtle on screen
+ protected void updateAllTurtleShape() {
+ for (int i = 0; i < tortues.length; i++) {
+ if (null != tortues[i]) {
+ tortues[i].fixe_taille_crayon(2 * tortues[i].getPenWidth());
+ }
+ }
+ }
+ /**
+ * Saves the a part of the drawing area as an image
+ * @param name The image name
+ * @param coords The upper left corner and the right bottom corner
+ */
+ protected void saveImage(String name, int[] coords){
+ BufferedImage buffer=getImagePart(coords);
+ String lowerName=name.toLowerCase();
+ String format="png";
+ if (lowerName.endsWith(".jpg")||lowerName.endsWith(".jpeg")) {
+ format="jpg";
+ }
+ else if (!lowerName.endsWith(".png")) {
+ name=name+".png";
+ }
+ name=WSManager.getUserConfig().getDefaultFolder()+File.separator+name;
+ try{
+ File f=new File(name);
+ ImageIO.write(buffer, format, f);
+ }
+ catch(IOException e){}
+
+ }
+ /**
+ * Return a part of the drawing area as an image
+ * @return
+ */
+ private BufferedImage getImagePart(int[] coords){
+ Image pic=DrawPanel.dessin;
+ if (zoom!=1){
+ pic=createImage(new FilteredImageSource(pic.getSource(),
+ new ReplicateScaleFilter((int)(dessin.getWidth()*zoom),(int)(dessin.getHeight()*zoom))));
+ }
+ pic=createImage(new FilteredImageSource(pic.getSource(),
+ new CropImageFilter(coords[0],coords[1],coords[2],coords[3])));
+ return toBufferedImage(pic);
+ }
+
+
+ public BufferedImage getSelectionImage(){
+ Image pic=DrawPanel.dessin;
+ if (zoom!=1){
+ pic=createImage(new FilteredImageSource(pic.getSource(),
+ new ReplicateScaleFilter((int)(dessin.getWidth()*zoom),(int)(dessin.getHeight()*zoom))));
+ }
+ if (null!=selection){
+ int x=(int)(selection.getBounds().getX());
+ int y=(int)(selection.getBounds().getY());
+ int width=(int)(selection.getBounds().getWidth());
+ int height=(int)(selection.getBounds().getHeight());
+ pic=createImage(new FilteredImageSource(pic.getSource(),
+ new CropImageFilter(x,y,width,height)));
+ }
+ return toBufferedImage(pic);
+ }
+// This method returns a buffered image with the contents of an image
+ private BufferedImage toBufferedImage(Image image) {
+ if (image instanceof BufferedImage)
+ return (BufferedImage)image;
+
+ // This code ensures that all the pixels in the image are loaded
+ image = new ImageIcon(image).getImage();
+
+
+ // Create a buffered image with a format that's compatible with the screen
+ BufferedImage bimage = null;
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ try {
+ // Determine the type of transparency of the new buffered image
+ int transparency = Transparency.OPAQUE;
+
+ // Create the buffered image
+ GraphicsDevice gs = ge.getDefaultScreenDevice();
+ GraphicsConfiguration gc = gs.getDefaultConfiguration();
+ bimage = gc.createCompatibleImage(
+ image.getWidth(null), image.getHeight(null), transparency);
+ } catch (HeadlessException e) {
+ // The system does not have a screen
+ }
+
+ if (bimage == null) {
+ // Create a buffered image using the default color model
+ int type = BufferedImage.TYPE_INT_RGB;
+ bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
+ }
+
+ // Copy image to buffered image
+ Graphics g = bimage.createGraphics();
+
+ // Paint the image onto the buffered image
+ g.drawImage(image, 0, 0, null);
+ g.dispose();
+
+ return bimage;
+ }
+ class PositionJViewport implements Runnable{
+ JViewport jv;
+ Point p;
+ PositionJViewport(JViewport jv, Point p){
+ this.jv=jv;
+ this.p=p;
+ }
+ public void run(){
+ revalidate();
+ //cadre.calculateMargin(); // TODO here is a zoom bug TODO maybe return this
+ // I have to add those two lines because of a bug I don't understand
+ // zoom 8 zoom 1 zoom 8
+ // Sometimes after the method revalidate(), the left upper corner position
+ // wasn't correct
+ cadre.scrollArea.invalidate();
+ cadre.scrollArea.validate();
+ // End Bug
+
+ jv.setViewPosition(p);
+ repaint();
+
+ //cadre.setZoomEnabled(true);
+ }
+ }
+ private void tryRecord2DMode(double a, double b){
+ if (DrawPanel.record2D==DrawPanel.record2D_POLYGON){
+ // FillPolygon mode
+ if (stackTriangle.size()==3){
+ stackTriangle.remove(0);
+ stackTriangle.add(new Point2D.Double(a,b));
+ }
+ else{
+ stackTriangle.add(new Point2D.Double(a,b));
+ }
+ if (stackTriangle.size()==3){
+ GeneralPath gp=new GeneralPath();
+ Line2D.Double ld=new Line2D.Double(stackTriangle.get(0),stackTriangle.get(1));
+ gp.append(ld,false);
+ ld=new Line2D.Double(stackTriangle.get(1),stackTriangle.get(2));
+ gp.append(ld,true);
+ ld=new Line2D.Double(stackTriangle.get(2),stackTriangle.get(0));
+ gp.append(ld,true);
+ g.fill(gp);
+ }
+ }
+
+ }
+ protected void startRecord2DPolygon(){
+ DrawPanel.record2D=DrawPanel.record2D_POLYGON;
+ stackTriangle=new Vector<Point2D.Double>();
+ stackTriangle.add(new Point2D.Double(tortue.corX,tortue.corY));
+ }
+ protected void stopRecord2DPolygon(){
+ DrawPanel.record2D=DrawPanel.record2D_NONE;
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/kernel/InstructionBuffer.java b/logo/src/xlogo/kernel/InstructionBuffer.java
new file mode 100644
index 0000000..befe0d9
--- /dev/null
+++ b/logo/src/xlogo/kernel/InstructionBuffer.java
@@ -0,0 +1,206 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+public class InstructionBuffer {
+ /**
+ * This integer represents the max Characters in the StringBuffer
+ */
+
+ private final static int MAX_CHARACTERS=15000;
+ /**
+ * The main StringBuffer containing instruction
+ */
+ private StringBuffer mainBuffer;
+ /**
+ * If the main StringBuffer is very large, Xlogo could become very slow. If main StringBuffer should contain more than MAX_CHARACTERS, those characters are saved in this StringBuffer
+ */
+ private StringBuffer stock;
+
+ InstructionBuffer(){
+ clear();
+ }
+ InstructionBuffer(String s){
+ mainBuffer=new StringBuffer(s);
+ stock=new StringBuffer();
+ }
+ /**
+ * Inserts some instructions at the beginning of the StringBuffer mainBuffer
+ * @param sb: The code to insert
+ */
+ void insertCode(StringBuffer sb){
+ if (sb.length()>InstructionBuffer.MAX_CHARACTERS) {
+ // insert current mainBuffer to stock
+ if (mainBuffer.length()!=0) stock.insert(0,mainBuffer);
+ // Copy MAX_CHARACTERS into mainBuffer
+ mainBuffer=new StringBuffer(sb.substring(0, InstructionBuffer.MAX_CHARACTERS));
+ // All remaining characters into stock
+ stock.insert(0,sb.substring(InstructionBuffer.MAX_CHARACTERS));
+ }
+ else {
+ mainBuffer.insert(0, sb);
+ }
+
+ }
+ /**
+ * returns the total length of the two Buffer
+ */
+ protected int getLength(){
+ return mainBuffer.length()+stock.length();
+ }
+ /**
+ * Inserts the String s at the beginning of the mainBuffer
+ * @param s
+ */
+ protected void insert(String s){
+ mainBuffer.insert(0, s);
+ }
+ /**
+ * Search for the String s , first in mainBuffer and then in stock
+ * if it isn't found in any buffer returns -1
+ * @param s: The String to search
+ */
+ protected int indexOf(String s){
+ int index=mainBuffer.indexOf(s);
+ if (index==-1){
+ index=stock.indexOf(s);
+ if (index==-1) return -1;
+ else return index+mainBuffer.length();
+ }
+ return index;
+
+ }
+ /**
+ * Search for the String s , first in mainBuffer and then in stock
+ * if not found in any buffer returns -1
+ * @param s: The String to search
+ */
+ protected int indexOf(String s, int fromIndex){
+ int index=-1;
+ if (fromIndex<mainBuffer.length()) index=mainBuffer.indexOf(s,fromIndex);
+ if (index==-1){
+ int from=0;
+ if (fromIndex>=mainBuffer.length()) from=fromIndex-mainBuffer.length();
+ index=stock.indexOf(s,from);
+ if (index==-1) return -1;
+ else return index+mainBuffer.length();
+ }
+ return index;
+
+ }
+
+
+ /**
+ * Delete all code from offset start to offset end
+ * @param start Start offset
+ * @param end End offset
+ */
+ protected void delete(int start, int end){
+ if (end<=mainBuffer.length()) mainBuffer.delete(start, end);
+ else {
+ stock.delete(0, end-mainBuffer.length());
+ mainBuffer=new StringBuffer();
+ transferStock();
+ }
+ if (mainBuffer.length()==0){
+ // if there are instruction in stock yet
+ if (stock.length()!=0) transferStock();
+ }
+
+ }
+ /**
+ * Transfers MAX_CHARCATERS from the buffer stock to mainBuffer
+ */
+
+ private void transferStock(){
+ if (stock.length()>InstructionBuffer.MAX_CHARACTERS){
+ mainBuffer.append(stock.substring(0, InstructionBuffer.MAX_CHARACTERS));
+ stock.delete(0, InstructionBuffer.MAX_CHARACTERS);
+ }
+ else {
+ mainBuffer.append(stock);
+ stock=new StringBuffer();
+ }
+ }
+ /**
+ * Returns next Word
+ * @return a String which represents the next word
+ */
+ protected String getNextWord() {
+ StringBuffer mot = new StringBuffer();
+ char caractere;
+ for (int i = 0; i < mainBuffer.length(); i++) {
+ caractere = mainBuffer.charAt(i);
+ if (caractere == ' ') {
+ return mot.toString();
+ } else
+ mot.append(caractere);
+ if (i==mainBuffer.length()-1&& stock.length()!=0){
+ transferStock();
+ }
+ }
+ // System.out.println("mot: "+mot);
+ return mot.toString();
+ }
+ /**
+ * Deletes the String mot from the mainBuffer instructions
+ * @param mot The string to delete
+ */
+ protected void deleteFirstWord(String mot) {
+ if (mainBuffer.length() > mot.length())
+ mainBuffer = mainBuffer.delete(0, mot.length() + 1);
+ else
+ mainBuffer = new StringBuffer();
+ if (mainBuffer.length()==0){
+ // if there are instruction in stock yet
+ if (stock.length()!=0) transferStock();
+ }
+ }
+ /**
+ * Return Character at the chosen index. this methos search first in mainBuffer and then in stock
+ * @param index The chosen index
+ * @return A character
+ */
+ protected char charAt(int index){
+ if (index<mainBuffer.length()) return mainBuffer.charAt(index);
+ return stock.charAt(index-mainBuffer.length());
+
+ }
+ /**
+ * Clear the both buffers
+ */
+ public void clear(){
+ mainBuffer=new StringBuffer();
+ stock=new StringBuffer();
+ }
+ public String toString(){
+ return mainBuffer.toString()+stock.toString();
+// return "MainBuffer:"+mainBuffer.toString()+":stock:"+stock.toString();
+ }
+}
diff --git a/logo/src/xlogo/kernel/Interprete.java b/logo/src/xlogo/kernel/Interprete.java
new file mode 100644
index 0000000..291674f
--- /dev/null
+++ b/logo/src/xlogo/kernel/Interprete.java
@@ -0,0 +1,981 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/** Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq */
+package xlogo.kernel;
+
+import java.util.Stack;
+import java.util.HashMap;
+
+import xlogo.Application;
+import xlogo.Logo;
+import xlogo.kernel.userspace.UserSpace;
+import xlogo.kernel.userspace.procedures.Procedure;
+
+public class Interprete
+{
+
+ private LaunchPrimitive lanceprim;
+
+ private Application app;
+
+ private Kernel kernel;
+
+ private UserSpace wp;
+ protected static boolean renvoi_instruction = false;
+ public static Stack<String> calcul = new Stack<String>();
+ protected static Stack<HashMap<String, String>> stockvariable = new Stack<HashMap<String, String>>();
+ protected static boolean stop = false;
+ protected static Stack<String> nom = new Stack<String>();
+ // procédures attendant
+ // parmamètres à recevoir
+ public static Stack<String> en_cours = new Stack<String>();
+ // actuellement en cours
+ // d'exécution
+ /**
+ * This buffer contains all instructions to execute
+ *
+ * @uml.property name="instructionBuffer"
+ * @uml.associationEnd multiplicity="(1 1)"
+ */
+ private InstructionBuffer instructionBuffer = new InstructionBuffer();
+
+ public static StringBuffer actionInstruction = new StringBuffer();
+
+ protected static HashMap<String, String> locale = new HashMap<String, String>();
+ // noms des variables
+ // locales
+
+ protected static boolean operande = false;
+ protected static boolean operateur = false;
+ protected static boolean drapeau_ouvrante = false;
+ protected static boolean drapeau_fermante = false;
+ public static String lineNumber = "";
+
+ // private TreeParser tp;
+ /*
+ * public Interprete(Application cadre){ this.cadre=cadre;
+ * lanceprim=new LaunchPrimitive(cadre); cadre.error=false; }
+ */
+ public Interprete(Application app)
+ {
+ this.kernel = app.getKernel();
+ this.app = app;
+ wp = kernel.getWorkspace();
+ lanceprim = new LaunchPrimitive(app, wp);
+ app.error = false;
+ }
+
+ String execute(StringBuffer instructions) throws LogoError
+ {
+ if (!instructions.equals(""))
+ {
+ instructionBuffer.insertCode(instructions);
+ }
+
+ // Object obca1,obca2,oban;
+ while (instructionBuffer.getLength() != 0)
+ {
+ if (app.error) // TODO && LogoError.lance : before, lance was always false, thus this branch was never taken. check this.
+ throw new LogoError(Logo.messages.getString("stop"));
+ while (app.affichage.isOnPause())
+ { // Si l'on touche aux scrollbars
+ try
+ {
+ wait();
+ }
+ catch (Exception e)
+ {}
+ }
+ // System.out.println("en_cours d'execution "+"\n"+
+ // en_cours+"\n\n");
+ // System.out.println("nom "+nom);
+ // System.out.println("calcul \n"+calcul+"\n\n");
+ // System.out.println("nom "+nom.toString()+" locale "+locale+ "
+ // "+valeur+" stockvariable "+stockvariable);
+ // System.out.println("operande "+calcul+" "+operande+"debut"+instructionBuffer);
+
+ // Is this line really interesting??
+ if (instructionBuffer.getLength() == 0)
+ break;
+ // System.out.print("debut\n"+instructionBuffer+"\nfin\n------------------\n");
+ String element = instructionBuffer.getNextWord();
+ // System.out.println(app.affichage.getPause()+element);
+
+ // System.out.println("/"+instructionBuffer+"/");
+
+ /*
+ * if (element=="")
+ * break;
+ *//* ***********************************************
+ * // si c'est une primitive ou une procedure *******
+ * ***********************************************
+ */
+ String element_minuscule = element.toLowerCase();
+ /*
+ * Marko Zivkovic: In XLogo4Schools, a procedure is identified by its name, not by its position in a stack.
+ * Now, i denotes only Logo primitives. If element_minuscule is a procedure, then i will be set to -2,
+ * s.t. the interpreter can assume as before that it denotes something other than primitive.
+ * If i >= 0, then the interpreter will work just as before, using i.
+ */
+ boolean isProcedure = wp.isExecutable(element_minuscule);
+ int i;
+
+ if (Primitive.primitives.containsKey(element_minuscule) || isProcedure)
+ {
+
+ // identifiant de la primitive
+ if (!isProcedure)
+ {
+ i = Integer.valueOf(Primitive.primitives.get(element_minuscule)).intValue()
+ % Primitive.PRIMITIVE_NUMBER;
+ }
+ else
+ {
+ //i = -i - 2;
+ i = -2; // Marko Zivkovic : i now denotes something else than a primitive : if i < -1 => isProcedure
+ }
+ // if (!calcul.empty()&&nom.isEmpty())
+ // throw new
+ // monException(cadre,Logo.messages.getString("que_faire")+"
+ // "+calcul.pop() +" gdfdsf");
+ // exécuter la procédure ou la primitive.
+ Stack<String> param = new Stack<String>();
+ if (isInfixedOperator(i))
+ { // Si c'est un opérateur infixé
+ deleteLineNumber();
+ operateur = true;
+ operande = false;
+ /*
+ * if (drapeau_ouvrante) { drapeau_ouvrante=false;
+ * if (i!=32&&i!=33) throw new myException(element+"
+ * "+Logo.messages.getString("ne_peut_etre")); else
+ * param.push("0"); }
+ */
+ // else
+ if (calcul.isEmpty())
+ { // Si le + ou le - représente le
+ // signe négatif ou positif
+ if (i != 32 && i != 33)
+ throw new LogoError(element + " " + Logo.messages.getString("error.ne_peut_etre")); // d'un
+ // nombre
+ if (nom.isEmpty())
+ param.push("0");
+ else
+ {
+ String st = nom.peek();
+ if (!testoperateur(st))
+ param.push("0");
+ else if ("*/".indexOf(st) > -1)
+ { // Si le signe -
+ // ou + suit un
+ // * ou /
+ instructionBuffer.deleteFirstWord(element);
+ if (st.equals("*"))
+ instructionBuffer.insert("* ");
+ else
+ instructionBuffer.insert("/ ");
+ if (i == 32)
+ return ("1"); // Si c'est un plus
+ else
+ return ("-1"); // Si c'est un moins
+ }
+ else
+ param.push("0");
+ }
+ }
+ else if (nom.isEmpty())
+ {
+ param.push(calcul.pop());
+ }
+ else
+ {
+ String st = nom.peek();
+ if (testoperateur(st))
+ {
+ // System.out.println("st "+st+" element "+element+"
+ // "+prioriteinf(st,element));
+ if (prioriteinf(st, element))
+ {
+ param.push(calcul.pop());
+ }
+ else
+ return (calcul.pop());
+ }
+ else
+ param.push(calcul.pop());
+ }
+ }// END: INFIX OPERATOR
+ else if (operande && i != 204)
+ {
+ checkParenthesis();
+ operande = false;
+ break;
+ } // Si ce n'est pas l'opérateur de fin de parenthèse, on sort
+
+ /*
+ * Example:
+ * To test | Formatted Form:
+ * fd 5 | fd 5 \l1 rt \l2
+ * rt | --> The \l2 can't be removed before be
+ * end | sure the rt has noproblem
+ */
+ if (!element.equals("\n"))
+ deleteLineNumber();
+ instructionBuffer.deleteFirstWord(element);
+
+ // Case with parenthensis
+ // eg (sum 3 4 5)
+ // eg (myProcedure 2 3 4 5)
+ if (drapeau_ouvrante)
+ {
+ drapeau_ouvrante = false;
+ int constantNumber = -1;
+ if (!hasGeneralForm(element_minuscule, i))
+ {
+ if (!isProcedure)
+ constantNumber = kernel.primitive.parametres[i];
+ else
+ constantNumber = wp.getExecutable(element_minuscule).nbparametre;
+ }
+ // Looking for all arguments (Number undefined)
+ nom.push(element);
+ int j = 0;
+ while (true)
+ {
+ /*
+ * This line fixed the bug for primitive or procedure
+ * without arguments
+ * eg: pr (pi+2)
+ */
+ if (constantNumber == 0)
+ break;
+ try
+ {
+ operande = operateur = drapeau_ouvrante = false;
+ if (instructionBuffer.getNextWord().equals(")"))
+ {
+ if (constantNumber != -1)
+ {
+ // If the primitive or the procedure doesn't
+ // accept optional parameters
+ if (j > constantNumber)
+ {
+ throw new LogoError(Logo.messages.getString("too_much_arguments"));
+ }
+ else if (j < constantNumber)
+ throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek());
+ }
+ break;
+ }
+ String a = execute(new StringBuffer());
+
+ param.push(a);
+ }
+ catch (LogoError e)
+ {
+ throw e;
+ }
+ j++;
+ }
+ // If It's a procedure
+
+ if (isProcedure)
+ {
+ if (wp.isProcedureAmbiguous(element_minuscule))
+ throw new LogoError(Logo.messages.getString("error.call.ambiguous.procedure.name"));
+
+ Procedure proc = wp.getExecutable(element_minuscule);
+
+ if (j > proc.nbparametre + proc.optVariables.size())
+ throw new LogoError(Logo.messages.getString("too_much_arguments"));
+ else if (j < proc.nbparametre)
+ throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek());
+ // Searching for optional arguments that are not defined
+
+ if (j < proc.optVariables.size() + proc.nbparametre)
+ {
+ j = j - proc.nbparametre;
+ for (int c = j; c < proc.optVariables.size(); c++)
+ {
+ try
+ {
+ operande = operateur = drapeau_ouvrante = false;
+ String a = execute(proc.optVariablesExp.get(c));
+ param.push(a);
+ }
+ catch (LogoError e)
+ {
+ throw e;
+ }
+ }
+ }
+ }
+
+ }
+ // classic case: predefined number of arguments
+ else
+ {
+ drapeau_ouvrante = false;
+ // How many arguments for the procedure or the primitive
+ int nbparametre = 0;
+ // For primitive
+ if (!isProcedure)
+ nbparametre = kernel.primitive.parametres[i];
+ // For procedure
+ else
+ nbparametre = wp.getExecutable(element_minuscule).nbparametre;
+ // Looking for each arguments
+ int j = 0;
+ nom.push(element);
+ while (j < nbparametre)
+ {
+ try
+ {
+ operande = operateur = drapeau_ouvrante = false;
+
+ String a = execute(new StringBuffer());
+ param.push(a);
+ j++;
+ }
+ catch (LogoError e)
+ {
+ throw e;
+ }
+ }
+ // System.out.println(instructionBuffer.toString());
+ // System.out.println(nom+"arguments"+param);
+ // Looking for Optional arguments in case of procedure
+ if (isProcedure)
+ {
+ Procedure proc = wp.getExecutable(element_minuscule);
+ nbparametre = proc.optVariables.size();
+ for (j = 0; j < nbparametre; j++)
+ {
+ try
+ {
+ operande = operateur = drapeau_ouvrante = false;
+ String a = execute(proc.optVariablesExp.get(j));
+ param.push(a);
+ }
+ catch (LogoError e)
+ {
+ throw e;
+ }
+ }
+ }
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ // System.out.println(nom+" "+"debut "+instruction+"
+ // fin\n"+param.toString());
+ // System.out.println(nom);
+ nom.pop();
+ if (!app.error)
+ lanceprim.execute(i, element_minuscule, param);
+ if (app.error)
+ break;
+ if (drapeau_fermante && !calcul.empty())
+ {
+ drapeau_fermante = false;
+ operande = false;
+ return calcul.pop();
+ }
+
+ // Tester si la procédure rend quelque chose lorsqu'attendu
+
+ if (!operande)
+ {
+ // dans le cas des primitives exec ou si
+ if (renvoi_instruction)
+ {
+ renvoi_instruction = false;
+ }
+ else
+ {
+ if (!nom.isEmpty() && !app.error && !nom.peek().equals("\n"))
+ {
+ if (!element.equals("\n"))
+ {
+ // If it's the end of a loop
+ // repeat 2 [fd 90 rt]
+ if (element.equals("\\"))
+ {
+ // The loop had been executed, we have to
+ // remove
+ // the loop instruction
+ int offset = instructionBuffer.indexOf(" \\ ");
+ instructionBuffer.delete(0, offset + 1);
+
+ throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek());
+ }
+ // (av 100) ---> OK
+ // av av 20 ----> Bad
+ if (!nom.peek().equals("("))
+ throw new LogoError(element + " " + Logo.messages.getString("ne_renvoie_pas") + " "
+ + nom.peek());
+
+ }
+ }
+ }
+ }
+ else
+ {
+ // The primitive returns a word or a list.
+ // There's no primitive or procedure waiting for it.
+ if (!nom.isEmpty() && nom.peek().equals("\n"))
+ throw new LogoError(Logo.messages.getString("error.whattodo") + " " + calcul.peek() + " ?");
+ }
+ }
+
+ /* ********************************
+ * / IF element IS A VARIABLE
+ * ********************************
+ */
+ else if (element.substring(0, 1).equals(":") && element.length() > 1)
+ {
+ // System.out.println(operande);
+ if (operande)
+ {
+ checkParenthesis();
+ operande = false;
+ break;
+ }
+ else
+ deleteLineNumber();
+ String value;
+ String variableName = element_minuscule.substring(1, element_minuscule.length());
+ // If the variable isn't local
+ if (!locale.containsKey(variableName))
+ {
+ // check it's a global variable
+ if (!wp.getGlobals().getVariables().contains(variableName)) // TODO
+ // CHECK
+ throw new LogoError(variableName + " " + Logo.messages.getString("error.novalue"));
+ else
+ value = wp.getGlobals().getValue(variableName).toString(); // TODO
+ // CHECK
+ }
+ // If the variable is local
+ else
+ {
+ value = locale.get(variableName);
+ }
+
+ if (null == value)
+ throw new LogoError(variableName + " " + Logo.messages.getString("error.novalue"));
+ calcul.push(value);
+ operande = true;
+ operateur = false;
+ drapeau_ouvrante = false;
+ instructionBuffer.deleteFirstWord(element);
+ }
+ else
+ {
+ /* *****************************
+ * IF element IS A NUMBER ******
+ * **************************
+ */
+ try
+ {
+ Double.parseDouble(element);
+ boolean deleteEndZero = false;
+ if (element.endsWith(".0"))
+ {
+ deleteEndZero = true;
+ element = element.substring(0, element.length() - 2);
+ }
+ /*
+ * boolean addStartZero=false;
+ * if (element.startsWith(".") || element.equals("")){
+ * element = "0" + element;
+ * addStartZero=true;
+ * }
+ */
+ calcul.push(element);
+ if (operande)
+ {
+ checkParenthesis();
+ calcul.pop();
+ operande = false;
+ break;
+ }
+ else
+ deleteLineNumber();
+ operande = true;
+ operateur = false;
+ drapeau_ouvrante = false;
+ // if (addStartZero)
+ // instructionBuffer.deleteFirstWord(element.substring(1));
+ if (deleteEndZero)
+ instructionBuffer.deleteFirstWord(element + ".0");
+ else
+ instructionBuffer.deleteFirstWord(element);
+
+ }
+ catch (NumberFormatException e)
+ {
+ /* *********************************
+ * IF element IS A SQUARE BRACKET [
+ * OPEN
+ * **********************************
+ */
+ if (element.equals("["))
+ {
+
+ // Utilité de cette ligne?
+ // if (!calcul.isEmpty()&&operateur==false) break;
+ if (operande)
+ {
+ checkParenthesis();
+ break;
+ }
+ else
+ deleteLineNumber();
+ operande = true;
+ operateur = false;
+ drapeau_ouvrante = false;
+ instructionBuffer.deleteFirstWord(element);
+ String a = chercheListe();
+ calcul.push(a);
+ }
+ /* ***************************
+ * IF element IS A PARENTHESIS
+ * OPEN
+ * ***********************
+ */
+ else if (element.equals("("))
+ {
+ if (operande)
+ {
+ checkParenthesis();
+ break;
+ }
+ else
+ deleteLineNumber();
+ drapeau_ouvrante = true;
+
+ Interprete.en_cours.push("(");
+ int pos = chercheParenthese();
+ if (pos == -1)
+ {
+ try
+ {
+ throw new LogoError(Logo.messages.getString("parenthese_fermante"));
+ }
+ catch (LogoError e1)
+ {}
+ }
+ instructionBuffer.deleteFirstWord(element);
+ // System.out.println("&&"+instruction);
+ Interprete.nom.push("(");
+ }
+ /* **********************************
+ * IF element IS A WORD
+ * **************************
+ */
+ else if (element.substring(0, 1).equals("\""))
+ {
+ try
+ {
+ String el = element.substring(1);
+ Double.parseDouble(el);
+ calcul.push(el);
+ }
+ catch (NumberFormatException e1)
+ {
+ calcul.push(element);
+ }
+ if (operande)
+ {
+ checkParenthesis();
+ calcul.pop();
+ operande = false;
+ break;
+ }
+ else
+ deleteLineNumber();
+ operande = true;
+ operateur = false;
+ drapeau_ouvrante = false;
+ instructionBuffer.deleteFirstWord(element);
+ }
+ // Si c'est le mot pour
+ else if (element_minuscule.equals(Logo.messages.getString("pour")))
+ {
+ instructionBuffer.deleteFirstWord(element);
+ if (instructionBuffer.getLength() != 0)
+ {
+ element = instructionBuffer.getNextWord();
+ element_minuscule = element.toLowerCase();
+ }
+ else
+ throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + "\""
+ + Logo.messages.getString("pour") + "\"");
+ if (Primitive.primitives.containsKey(element_minuscule) || wp.isExecutable(element_minuscule)) // TODO
+ // check
+ throw new LogoError(element + " " + Logo.messages.getString("existe_deja"));
+ else
+ {
+ String definition = Logo.messages.getString("pour") + " " + element + " ";
+ instructionBuffer.deleteFirstWord(element);
+ while (instructionBuffer.getLength() != 0)
+ {
+ element = instructionBuffer.getNextWord().toLowerCase();
+ if (null == element)
+ break;
+ if (!element.substring(0, 1).equals(":") || element.length() == 1)
+ throw new LogoError(element + " " + Logo.messages.getString("pas_argument"));
+ definition += element + " ";
+ instructionBuffer.deleteFirstWord(element);
+ }
+ if (app.editeur.getComponent().isVisible())
+ throw new LogoError(Logo.messages.getString("ferme_editeur"));
+ else
+ {
+ app.editeur.getComponent().setVisible(true);
+ app.editeur.setEditorStyledText(definition + "\n\n" + Logo.messages.getString("fin"));
+ }
+ }
+ }
+ else if (element.startsWith("\\l"))
+ {
+ if (operande)
+ {
+ break;
+ }
+ instructionBuffer.deleteFirstWord(element);
+ lineNumber = element + " ";
+ element = instructionBuffer.getNextWord();
+
+ }
+ else
+ {
+ deleteLineNumber();
+ throw new LogoError(Logo.messages.getString("je_ne_sais_pas") + " " + element);
+ }
+ }
+ }
+ // System.out.println("instruction "+instruction+" calcul "+calcul);
+ }
+ /* ******************************
+ * **** END OF THE MAIN LOOP
+ * *****************************
+ */
+ // S'il n'y a rien à retourner.
+ if (calcul.isEmpty())
+ {
+ if (!nom.isEmpty())
+ {// &&!nom.peek().equals("\n")) {
+ while ((!nom.isEmpty()) && nom.peek().equals("\n"))
+ nom.pop();
+ if (!nom.isEmpty()) { throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek()); }
+ }
+ }
+ // Sinon on retourne la valeur contenue dans la pile de calcul.
+ if (!calcul.isEmpty())
+ {
+ // S'il y a une procédure de lancer
+ // Ex: pour t -- 6 -- fin . Puis, av t.
+ if ((!nom.isEmpty()) && nom.peek().equals("\n"))
+ {
+ String up = "";
+ int id = 0;
+ while (!nom.isEmpty() && nom.peek().equals("\n"))
+ {
+ nom.pop();
+ id++;
+ }
+ if (!nom.isEmpty())
+ {
+ up = nom.peek().toString();
+ try
+ {
+ throw new LogoError(en_cours.get(en_cours.size() - id) + " "
+ + Logo.messages.getString("ne_renvoie_pas") + " " + up);
+ }
+ catch (LogoError e)
+ {}
+ }
+ else
+ {
+ try
+ {
+ throw new LogoError(Logo.messages.getString("error.whattodo") + " " + calcul.peek() + " ?");
+ }
+ catch (LogoError e)
+ {}
+ }
+ /*
+ * }
+ * if (!nom.isEmpty() && nom.peek().equals("\n")) {
+ * up = en_cours.get(en_cours.size() - 2).toString();
+ * try {
+ * throw new myException( en_cours.peek() + " "
+ * + Logo.messages.getString("ne_renvoie_pas")
+ * + " " + up);
+ * } catch (myException e) {
+ * }
+ * } else if (!nom.isEmpty()) {
+ * up = nom.peek().toString();
+ * try {
+ * throw new myException( en_cours.peek() + " "
+ * + Logo.messages.getString("ne_renvoie_pas")
+ * + " " + up);
+ * } catch (myException e) {
+ * }
+ * } else {
+ * try {
+ * throw new myException( Logo.messages
+ * .getString("que_faire")
+ * + " " + calcul.peek() + " ?");
+ * } catch (myException e) {
+ * }}
+ */
+
+ }
+ // ///////////
+ else
+ {
+ operande = false;
+ return (calcul.pop());
+ }
+ }
+ return ("");
+ }
+
+ private int chercheParenthese()
+ { // position ou s'arrete la prochaine
+ // parenthese
+ boolean continuer = true;
+ int of_ouvrant;
+ int of_fermant = 0;
+ int from_index_ouvrant = 1;
+ int from_index_fermant = 1;
+ while (continuer)
+ {
+ of_ouvrant = instructionBuffer.indexOf("(", from_index_ouvrant);
+ of_fermant = instructionBuffer.indexOf(")", from_index_fermant);
+ if (of_fermant == -1)
+ break;
+ if (of_ouvrant != -1 && of_ouvrant < of_fermant)
+ {
+ from_index_ouvrant = of_ouvrant + 1;
+ from_index_fermant = of_fermant + 1;
+ }
+ else
+ continuer = false;
+ ;
+ }
+ return of_fermant;
+ }
+
+ protected String chercheListe() throws LogoError
+ {
+ String liste = "[ ";
+ String element = "";
+ while (instructionBuffer.getLength() != 0)
+ {
+ element = instructionBuffer.getNextWord();
+ // SI crochet ouvrant, on l'empile dans la pile de calcul
+ if (element.equals("["))
+ {
+ calcul.push("[");
+ instructionBuffer.deleteFirstWord(element);
+ liste += "[ ";
+ }
+
+ else if (element.equals("]"))
+ { // Si on atteint un crochet fermant
+ instructionBuffer.deleteFirstWord(element);
+ // if (((Stack)instruction.peek()).isEmpty()) instruction.pop();
+ liste += "] ";
+ if (calcul.empty())
+ {
+ return (liste);
+ } // 1er cas: rien dans la pile de calcul, on renvoie la liste
+ else if (!calcul.peek().toString().equals("["))
+ {
+ return (liste);
+ } // 2eme cas: pas de crochet ouvrant en haut de la pile, idem
+ else
+ calcul.pop(); // 3eme cas: un crochet ouvrant en haut de
+ // la pile, on l'enleve
+ }
+ else
+ {
+ instructionBuffer.deleteFirstWord(element);
+ liste += element + " ";
+ }
+ }
+ throw new LogoError(Logo.messages.getString("erreur_crochet"));
+ }
+
+ private boolean testoperateur(String st)
+ { // l'élément trouvé est-il un
+ // opérateur
+ int i = "+-*/<>=!&|".indexOf(st);
+ if (i == -1)
+ return (false);
+ return (true);
+ }
+
+ /**
+ * This method compares the two operators op and str.<br>
+ * Cette methode teste si l'operateur op (sommet de la pile d'appel)
+ * est de priorite strictement inferieur a str
+ *
+ * @param op
+ * The first operator
+ * @param str
+ * The second operator
+ * @return true if op has a minor priority than str.
+ */
+
+ private boolean prioriteinf(String op, String str)
+ { /*
+ * if (parenthesefermante>0) return(false); //
+ * else
+ */
+ if (isTimesDiv(str) && !isTimesDiv(op))
+ return (true);
+ else if (isPlusMinus(str) && isLogicOperator(op))
+ return (true);
+ else if (">=<=".indexOf(str) > -1 && "|&".indexOf(op) > -1)
+ return (true);
+ return (false);
+ }
+
+ /*
+ * private int isProcedure(String mot) { // vérifie si mot est une
+ * procédure
+ * for (int i = 0; i < wp.getNumberOfProcedure(); i++) {
+ * if (wp.getProcedure(i).name.equals(mot))
+ * return (i);
+ * }
+ * return (-1);
+ * }
+ */
+
+ protected void setWorkspace(UserSpace workspace)
+ {
+ wp = workspace;
+ lanceprim.setWorkspace(workspace);
+ }
+
+ private boolean hasGeneralForm(String name, int i)
+ {
+ // If it's a procedure
+ if (wp.isExecutable(name))
+ return !wp.getExecutable(name).optVariables.isEmpty();
+ return kernel.primitive.generalForm[i];
+ }
+
+ private void checkParenthesis() throws LogoError
+ {
+ if (!nom.isEmpty())
+ {
+ String name = nom.peek();
+ if (name.equals("(")) { throw new LogoError(Logo.messages.getString("too_much_arguments"));
+
+ }
+ }
+
+ }
+
+ private void deleteLineNumber()
+ {
+ lineNumber = "";
+ }
+
+ /**
+ * This method indicates if a primitive is an infixed operator<br>
+ * Infixed operators are for example: +,-,*-,/,&,>=.....
+ *
+ * @param id
+ * The integer identifiant for the primitive
+ * @return true or false if it's an infixed operator
+ */
+ private boolean isInfixedOperator(int id)
+ {
+ boolean b1 = (29 < id) && (id < 39);
+ boolean b2 = (id == 273) || (id == 274);
+ return b1 || b2;
+ }
+
+ /**
+ * This metods tests if the String op is a logic operator, ie a string like
+ * |,&,<,>,=,>=,<=
+ *
+ * @param op
+ * The operator to test
+ * @return true if op is a logic operator
+ */
+ private boolean isLogicOperator(String op)
+ {
+ return ("|&>=<=".indexOf(op) != -1);
+
+ }
+
+ /**
+ * This metods tests if the String op is + or -
+ *
+ * @param op
+ * The operator to test
+ * @return true if op is + or -
+ */
+ private boolean isPlusMinus(String op)
+ {
+ return (op.equals("+") || op.equals("-"));
+ }
+
+ /**
+ * This metods tests if the String op is / or *
+ *
+ * @param op
+ * The operator to test
+ * @return true if op is * or /
+ */
+ private boolean isTimesDiv(String op)
+ {
+ return (op.equals("*") || op.equals("/"));
+ }
+
+ /**
+ * @return
+ * @uml.property name="instructionBuffer"
+ */
+ protected InstructionBuffer getInstructionBuffer()
+ {
+ return instructionBuffer;
+ }
+}
diff --git a/logo/src/xlogo/kernel/Kernel.java b/logo/src/xlogo/kernel/Kernel.java
new file mode 100644
index 0000000..108fef4
--- /dev/null
+++ b/logo/src/xlogo/kernel/Kernel.java
@@ -0,0 +1,211 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.util.ArrayList;
+import java.awt.Color;
+
+import xlogo.kernel.userspace.UserSpace;
+import xlogo.storage.user.DrawQuality;
+import xlogo.storage.workspace.Language;
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+import xlogo.Application;
+
+public class Kernel
+{
+ protected static long chrono = 0;
+
+ protected ArrayFlow flows = new ArrayFlow(); // Contient les
+ // flux de
+ // lecture ou
+ // d'écriture
+
+ protected static boolean mode_trace = false; // true si le
+ // mode trace
+ // est
+ // enclenchée
+ // (permet de
+ // suivre les
+ // procédures)
+
+ // interprete the user command and launch primitive and procedure
+ private Interprete interprete;
+ // For all drawing operation
+ // protected DrawPanel dg;
+ // For primitive
+ protected Primitive primitive = null;
+ private UserSpace userSpace;
+ private Application app;
+ private MP3Player mp3Player;
+ private MyCalculator myCalculator;
+
+ public Kernel(Application app, UserSpace userSpace)
+ {
+ this.app = app;
+ this.userSpace = userSpace;
+ initCalculator(-1);
+ }
+
+ public UserSpace getWorkspace()
+ {
+ return userSpace;
+ }
+
+ public void setWorkspace(UserSpace workspace)
+ {
+ userSpace = workspace;
+ interprete.setWorkspace(userSpace);
+ }
+
+ protected String listSearch() throws xlogo.kernel.LogoError
+ {
+ return interprete.chercheListe();
+ }
+
+ public void fcfg(Color color)
+ {
+ app.getDrawPanel().fcfg(color);
+ }
+
+ public Turtle getActiveTurtle()
+ {
+ return app.getDrawPanel().tortue;
+ }
+
+ public MyCalculator getCalculator()
+ {
+ return myCalculator;
+ }
+
+ public void fcc(Color color)
+ {
+ app.getDrawPanel().fcc(color);
+ }
+
+ public void vide_ecran()
+ {
+ app.getDrawPanel().videecran();
+ }
+
+ public void setNumberOfTurtles(int i)
+ {
+ app.getDrawPanel().setNumberOfTurtles(i);
+ }
+
+ public void setDrawingQuality(DrawQuality q)
+ {
+ app.getDrawPanel().setQuality(q);
+ }
+
+ public Color getScreenBackground()
+ {
+ return app.getDrawPanel().getBackgroundColor();
+ }
+
+ public void change_image_tortue(String chemin)
+ {
+ app.getDrawPanel().change_image_tortue(app, chemin);
+ }
+
+ public void initGraphics()
+ {
+ app.getDrawPanel().initGraphics();
+ }
+
+ public void buildPrimitiveTreemap(Language lang)
+ {
+ primitive.buildPrimitiveTreemap(lang);
+ }
+
+ public String execute(StringBuffer st) throws LogoError
+ {
+ return interprete.execute(st);
+ }
+
+ protected void initCalculator(int s)
+ {
+ myCalculator = new MyCalculator(s);
+
+ }
+
+ public void initPrimitive()
+ {
+ primitive = new Primitive(app);
+ }
+
+ public void initInterprete()
+ {
+ interprete = new Interprete(app);
+ }
+
+ /**
+ * Returns the InstructionBuffer containing all commands to execute
+ */
+ public InstructionBuffer getInstructionBuffer()
+ {
+ return interprete.getInstructionBuffer();
+ }
+
+ public void setMp3Player(MP3Player mp3Player)
+ {
+ this.mp3Player = mp3Player;
+ }
+
+ public MP3Player getMp3Player()
+ {
+ return mp3Player;
+ }
+
+ class ArrayFlow extends ArrayList<MyFlow>
+ {
+ ArrayFlow()
+ {
+ super();
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ protected int search(int id)
+ {
+ for (int i = 0; i < size(); i++)
+ {
+ if (get(i).getId() == id)
+ return i;
+ }
+ return -1;
+ }
+
+ }
+}
diff --git a/logo/src/xlogo/kernel/LaunchPrimitive.java b/logo/src/xlogo/kernel/LaunchPrimitive.java
new file mode 100644
index 0000000..47827cd
--- /dev/null
+++ b/logo/src/xlogo/kernel/LaunchPrimitive.java
@@ -0,0 +1,4751 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel;
+
+import java.util.Stack;
+import java.util.Vector;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import java.util.Calendar;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+
+import javax.imageio.*;
+
+import java.io.*;
+
+import javax.swing.JOptionPane;
+import javax.swing.ImageIcon;
+import javax.swing.Icon;
+import javax.vecmath.Point3d;
+
+import java.math.BigDecimal;
+
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.user.DrawQuality;
+import xlogo.storage.user.PenShape;
+import xlogo.storage.user.UserConfig;
+import xlogo.storage.workspace.Language;
+import xlogo.utils.Utils;
+import xlogo.gui.Lis;
+import xlogo.gui.MyTextAreaDialog;
+import xlogo.kernel.DrawPanel;
+import xlogo.gui.HistoryPanel;
+import xlogo.Application;
+import xlogo.Logo;
+import xlogo.kernel.network.*;
+import xlogo.kernel.gui.*;
+import xlogo.kernel.perspective.ElementPolygon;
+import xlogo.kernel.perspective.ElementLine;
+import xlogo.kernel.perspective.ElementPoint;
+import xlogo.kernel.userspace.UserSpace;
+import xlogo.kernel.userspace.files.LogoFile;
+import xlogo.kernel.userspace.procedures.Procedure;
+import xlogo.messages.async.history.HistoryMessenger;
+
+/*******************************************************************************
+ * When a primitive or a procedure has all arguments, LauchPrimitive executes
+ * the appropriate code.
+ ******************************************************************************/
+public class LaunchPrimitive
+{
+ /**
+ * Default Application frame
+ */
+ private Application cadre;
+ /**
+ * Default kernel
+ */
+ private Kernel kernel;
+ /**
+ * Default workspace
+ */
+ private UserSpace wp;
+
+ private Procedure procedure;
+ // private MathContext mc=MathContext.DECIMAL64;
+ /**
+ * This is the start for the String returned by primitive or procedure.<br>
+ * It is "\"" for words and "" for numbers. <br>
+ * <br>
+ * Ceci est le début de la chaine générique renvoyé par les primitives<br>
+ * Elle vaut "\"" pour les mots et "" pour les nombres<br>
+ */
+ private String debut_chaine = "";
+ /**
+ * When we launch the primitive "listentcp", we have to save workspaces
+ */
+ //private Stack<LogoContext> savedWorkspace;
+
+ /**
+ * @param cadre
+ * Default frame Application
+ * @param wp
+ * Default workspace
+ */
+ public LaunchPrimitive(Application cadre, UserSpace wp)
+ {
+ this.wp = wp;
+ this.cadre = cadre;
+ this.kernel = cadre.getKernel();
+ }
+
+ /**
+ * Execute the primitive number "id" with the arguments contained in "param"<br>
+ * <ul>
+ * <li>if id<0: it is a procedure. <br>
+ * For example, if id=-3, it is procedure number -i-2=-(-3)-2=1</li>
+ * <li>if d>=0: it is primitive number "id"</li>
+ * </ul>
+ *
+ * @param id
+ * The number representing the procedure or the primitive
+ * @param param
+ * The Stack that contains all arguments
+ */
+ protected void execute(int id, String element_minuscule, Stack<String> param) throws LogoError
+ {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ Font font = WSManager.getWorkspaceConfig().getFont();
+ String defaultFolder;
+
+ // identifiant procédure ou primitive, valeur des paramètres
+ if (id < 0)
+ {
+ procedure = wp.getExecutable(element_minuscule);
+ Interprete.stockvariable.push(Interprete.locale);
+ Interprete.locale = new HashMap<String, String>();
+ // Read local Variable
+ int optSize = procedure.optVariables.size();
+ int normSize = procedure.variable.size();
+ for (int j = 0; j < optSize + normSize; j++)
+ {
+ // Add local Variable
+ if (j < normSize)
+ {
+ Interprete.locale.put(procedure.variable.get(j), param.get(j));
+ } // add optional variables
+ else
+ {
+ String value = "";
+ if (j < param.size())
+ value = param.get(j);
+ else
+ value = procedure.optVariablesExp.get(j - param.size()).toString();
+ Interprete.locale.put(procedure.optVariables.get(j - normSize), value);
+
+ }
+ }
+ // Add Optional variable
+ if (Kernel.mode_trace)
+ {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < Interprete.en_cours.size(); i++)
+ buffer.append(" ");
+ buffer.append(procedure.name);
+ for (int i = 0; i < param.size(); i++)
+ buffer.append(" " + Utils.SortieTexte(param.get(i)));
+ String msg = buffer + "\n";
+ HistoryMessenger.getInstance().dispatchMessage(msg);
+ }
+ Interprete.en_cours.push(procedure.name);
+ procedure.decoupe();
+ // Add Procedure code in Interprete.instruction
+ kernel.getInstructionBuffer().insert("\n ");
+ kernel.getInstructionBuffer().insertCode(procedure.instr);
+ // System.out.println("instr " +Interprete.instruction);
+ // System.out.println("stock "+Interprete.stockInstruction);
+ // System.out.println("a"+Interprete.instruction+"a");
+ Interprete.nom.push("\n");
+ }
+ else
+ {
+ Language lang = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage();
+
+ switch (id)
+ {
+ case 0: // av
+ delay();
+ cadre.getDrawPanel().av(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 1: // re
+ delay();
+ cadre.getDrawPanel().av(-kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 2: // td
+ delay();
+ cadre.getDrawPanel().td(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 3: // tg
+ delay();
+ cadre.getDrawPanel().td(-kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 4: // arithmetic.power puissance
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().power(param.get(0), param.get(1)));
+ break;
+ case 5: // repete controls.repeat
+ String liste = getList(param.get(1));
+ kernel.primitive.repete(kernel.getCalculator().getInteger(param.get(0)), liste);
+ break;
+ case 6: // ve
+ cadre.getDrawPanel().videecran();
+ break;
+ case 7: // ct
+ if (kernel.getActiveTurtle().isVisible())
+ {
+ cadre.getDrawPanel().ct_mt();
+ cadre.getDrawPanel().tortues_visibles.remove(String.valueOf(kernel.getActiveTurtle().id));
+ }
+ kernel.getActiveTurtle().setVisible(false);
+ break;
+ case 8: // mt
+ if (!kernel.getActiveTurtle().isVisible())
+ {
+ cadre.getDrawPanel().ct_mt();
+ cadre.getDrawPanel().tortues_visibles.push(String.valueOf(kernel.getActiveTurtle().id));
+ }
+ kernel.getActiveTurtle().setVisible(true);
+ break;
+ case 9: // ecris, ec
+ int size = param.size();
+ String result = "";
+ String mot;
+ for (int i = 0; i < size; i++)
+ {
+ String par = param.get(i).trim();
+ if (isList(par))
+ par = formatList(par.substring(1, par.length() - 1));
+ mot = getWord(param.get(i));
+ if (null == mot)
+ result += Utils.SortieTexte(par) + " ";
+ else
+ result += Utils.SortieTexte(mot) + " ";
+ }
+ HistoryMessenger.getInstance().dispatchLogoOutput(result + "\n");
+ break;
+ case 10: // si // if
+
+ liste = getList(param.get(1));
+ liste = new String(Utils.decoupe(liste));
+ String liste2 = null;
+ boolean predicat = predicat(param.get(0));
+ InstructionBuffer instruction = cadre.getKernel().getInstructionBuffer();
+ if (instruction.getLength() != 0)
+ {
+ try
+ {
+ String element = instruction.getNextWord();
+ // System.out.println("a"+element+"a");
+ if (element.startsWith("\\l"))
+ {
+ instruction.deleteFirstWord(element);
+ Interprete.lineNumber = element + " ";
+ }
+ if (instruction.charAt(0) == '[')
+ {
+ instruction.deleteFirstWord("[");
+ liste2 = getFinalList(kernel.listSearch());
+ liste2 = new String(Utils.decoupe(liste2));
+ }
+ }
+ catch (Exception e)
+ {}
+ }
+ kernel.primitive.si(predicat, liste, liste2);
+ Interprete.renvoi_instruction = true;
+ break;
+ case 11: // STOP
+ kernel.primitive.stop();
+ break;
+ case 12: // origine
+ delay();
+ cadre.getDrawPanel().origine();
+ break;
+ case 13: // fpos
+ delay();
+ String list = getFinalList(param.get(0));
+ cadre.getDrawPanel().fpos(list);
+ break;
+ case 14: // fixex
+ delay();
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ {
+ double x = kernel.getCalculator().numberDouble(param.get(0));
+ double y = h / 2 - kernel.getActiveTurtle().corY;
+ cadre.getDrawPanel().fpos(x + " " + y);
+ }
+ else
+ cadre.getDrawPanel().fpos(
+ kernel.getCalculator().numberDouble(param.get(0)) + " " + kernel.getActiveTurtle().Y
+ + " " + kernel.getActiveTurtle().Z);
+ break;
+ case 15: // fixey
+ delay();
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ {
+ double y = kernel.getCalculator().numberDouble(param.get(0));
+ double x = kernel.getActiveTurtle().corX - w / 2;
+ cadre.getDrawPanel().fpos(x + " " + y);
+ }
+ else
+ cadre.getDrawPanel().fpos(
+ kernel.getActiveTurtle().X + " " + kernel.getCalculator().numberDouble(param.get(0))
+ + " " + kernel.getActiveTurtle().Z);
+ break;
+ case 16: // fixexy
+ delay();
+ primitive2D("drawing.fixexy");
+ cadre.getDrawPanel().fpos(
+ kernel.getCalculator().numberDouble(param.get(0)) + " "
+ + kernel.getCalculator().numberDouble(param.get(1)));
+ break;
+ case 17: // fixecap
+ delay();
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ cadre.getDrawPanel().td(
+ 360 - kernel.getActiveTurtle().heading
+ + kernel.getCalculator().numberDouble(param.pop()));
+ else
+ {
+ cadre.getDrawPanel().setHeading(kernel.getCalculator().numberDouble(param.pop()));
+ }
+ break;
+ case 18: // lc
+ kernel.getActiveTurtle().setPenDown(false);
+ break;
+ case 19: // bc
+ kernel.getActiveTurtle().setPenDown(true);
+ break;
+ case 20: // gomme
+ kernel.getActiveTurtle().setPenDown(true);
+ // if mode penerase isn't active yet
+ if (kernel.getActiveTurtle().couleurmodedessin.equals(kernel.getActiveTurtle().couleurcrayon))
+ {
+ kernel.getActiveTurtle().couleurmodedessin = kernel.getActiveTurtle().couleurcrayon;
+ kernel.getActiveTurtle().couleurcrayon = cadre.getDrawPanel().getBackgroundColor();
+ kernel.getActiveTurtle().stroke = new BasicStroke(3); // TODO
+ // make
+ // member
+ }
+ break;
+ case 21: // inversecrayon
+ kernel.getActiveTurtle().setPenDown(true);
+ kernel.getActiveTurtle().setPenReverse(true);
+ break;
+ case 22: // dessine
+ kernel.getActiveTurtle().setPenReverse(false);
+ kernel.getActiveTurtle().setPenDown(true);
+ kernel.getActiveTurtle().couleurcrayon = kernel.getActiveTurtle().couleurmodedessin;
+ kernel.getActiveTurtle().stroke = new BasicStroke(1); // TODO
+ // make
+ // member
+ break;
+ case 23: // somme
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().add(param));
+ break;
+
+ case 24: // difference
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().substract(param));
+ break;
+ case 25: // arithmetic.minus moins (opposé)
+ Interprete.calcul.push(kernel.getCalculator().minus(param.get(0)));
+ Interprete.operande = true;
+ break;
+ case 26: // produit
+ Interprete.calcul.push(kernel.getCalculator().multiply(param));
+ Interprete.operande = true;
+ break;
+ case 27: // div
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().divide(param));
+ break;
+ case 28: // reste
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().remainder(param.get(0), param.get(1)));
+ break;
+ case 29: // retourne
+ kernel.primitive.retourne(param.get(0));
+ break;
+ case 30: // *
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().multiply(param));
+ break;
+ case 31: // diviser /
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().divide(param));
+ break;
+ case 32: // +
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().add(param));
+ break;
+ case 33: // -
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().substract(param));
+ break;
+ case 34: // =
+ equal(param);
+ break;
+ case 35: // <
+ inf(param);
+ break;
+ case 36: // >
+ sup(param);
+ break;
+ case 37: // |
+ boolean b1 = predicat(param.get(0));
+ boolean b2 = predicat(param.get(1));
+ b1 = b1 | b2;
+ if (b1)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ Interprete.operande = true;
+ break;
+ case 38: // &
+ b1 = predicat(param.get(0));
+ b2 = predicat(param.get(1));
+ b1 = b1 & b2;
+ if (b1)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ Interprete.operande = true;
+ break;
+ case 39: // opérateur interne \n signalant une fin de
+ // procédure
+ Interprete.locale = Interprete.stockvariable.pop();
+ if (Interprete.nom.peek().equals("\n"))
+ {
+ Interprete.nom.pop();
+ Interprete.lineNumber = "";
+ }
+ else
+ {
+ /*
+ * Example
+ * to bug
+ * av
+ * end
+ */
+ throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + Interprete.nom.peek());
+ }
+ /*
+ * to bug [:a] | (bug 10)
+ * av :a |
+ * end |
+ */
+ if (!Interprete.nom.isEmpty() && !Interprete.nom.peek().equals("\n")
+ && !Interprete.nom.peek().equals("("))
+ {
+ if (!cadre.error)
+ throw new LogoError(Interprete.en_cours.peek() + " "
+ + Logo.messages.getString("ne_renvoie_pas") + " "
+ + Interprete.nom.peek().toString());
+ }
+ if (!Interprete.en_cours.isEmpty())
+ Interprete.en_cours.pop();
+ break;
+ case 40: // opérateur interne \ signalant une fin de boucle
+
+ LoopProperties loop = Primitive.stackLoop.peek();
+ // LOOP REPEAT
+ if (loop.isRepeat())
+ {
+ BigDecimal compteur = loop.getCounter();
+ BigDecimal fin = loop.getEnd();
+ if (compteur.compareTo(fin) < 0)
+ {
+ loop.incremente();
+ Primitive.stackLoop.pop();
+ Primitive.stackLoop.push(loop);
+ cadre.getKernel().getInstructionBuffer().insert(loop.getInstr() + Primitive.END_LOOP + " ");
+ }
+ else if (compteur.compareTo(fin) == 0)
+ {
+ Primitive.stackLoop.pop();
+ }
+ }
+ // LOOP FOR or LOOP FOREACH
+ else if (loop.isFor() || loop.isForEach())
+ {
+ BigDecimal inc = loop.getIncrement();
+ BigDecimal compteur = loop.getCounter();
+ BigDecimal fin = loop.getEnd();
+ if ((inc.compareTo(BigDecimal.ZERO) == 1 && (compteur.add(inc).compareTo(fin) <= 0))
+ || (inc.compareTo(BigDecimal.ZERO) == -1 && (compteur.add(inc).compareTo(fin) >= 0)))
+ {
+ loop.incremente();
+ ((LoopFor) loop).AffecteVar(false);
+ Primitive.stackLoop.pop();
+ Primitive.stackLoop.push(loop);
+ cadre.getKernel().getInstructionBuffer().insert(loop.getInstr() + Primitive.END_LOOP + " ");
+ }
+ else
+ {
+ ((LoopFor) loop).DeleteVar();
+ Primitive.stackLoop.pop();
+ }
+ }
+ // LOOP FOREVER
+ else if (loop.isForEver())
+ {
+ cadre.getKernel().getInstructionBuffer().insert(loop.getInstr() + Primitive.END_LOOP + " ");
+ }
+ // LOOP FILL POLYGON
+ else if (loop.isFillPolygon())
+ {
+ cadre.getDrawPanel().stopRecord2DPolygon();
+ Primitive.stackLoop.pop();
+ }
+ break;
+ case 41: // pos
+ Interprete.operande = true;
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ {
+ double a = kernel.getActiveTurtle().corX - w / 2;
+ double b = h / 2 - kernel.getActiveTurtle().corY;
+ Interprete.calcul.push("[ " + MyCalculator.teste_fin_double(a) + " "
+ + MyCalculator.teste_fin_double(b) + " ] ");
+ }
+ else
+ {
+ Interprete.calcul.push("[ " + kernel.getActiveTurtle().X + " " + kernel.getActiveTurtle().Y
+ + " " + kernel.getActiveTurtle().Z + " ] ");
+
+ }
+ break;
+ case 42: // cap
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().heading));
+ break;
+ case 43: // arrondi
+ Interprete.operande = true;
+ Interprete.calcul
+ .push(String.valueOf(Math.round(kernel.getCalculator().numberDouble(param.get(0)))));
+ break;
+ case 44: // log10
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().log10(param.get(0)));
+ break;
+ case 45: // arithmetic.sin
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().sin(param.get(0)));
+ break;
+ case 46: // arithmetic.cos
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().cos(param.get(0)));
+ break;
+ case 47: // ou
+ ou(param);
+ break;
+ case 48: // et
+ et(param);
+ break;
+ case 49: // non
+ Interprete.operande = true;
+ b1 = predicat(param.get(0));
+ if (b1)
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ break;
+ case 50: // liste
+ liste = "[ ";
+ Interprete.operande = true;
+ String mot2;
+ for (int i = 0; i < param.size(); i++)
+ {
+ mot2 = param.get(i);
+ mot = getWord(param.get(i));
+ if (null == mot)
+ {
+ liste += mot2;
+ // System.out.println("a"+mot2+"a");
+ }
+ else
+ {
+ if (mot.equals(""))
+ mot = "\\v";
+ liste += mot + " ";
+ }
+ }
+ Interprete.calcul.push(liste + "] ");
+ break;
+ case 51: // phrase
+ liste = "[ ";
+ Interprete.operande = true;
+ for (int i = 0; i < param.size(); i++)
+ {
+ mot = getWord(param.get(i));
+ mot2 = param.get(i).trim();
+ if (null == mot)
+ {
+ if (isList(mot2))
+ liste += mot2.substring(1, mot2.length() - 1).trim() + " ";
+ else
+ liste += mot2 + " ";
+ }
+ else
+ {
+ if (mot.equals(""))
+ mot = "\\v";
+ liste += mot + " ";
+ }
+ }
+ Interprete.calcul.push(liste + "] ");
+ break;
+ case 52: // metspremier
+ liste = getFinalList(param.get(1));
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ {
+ if (!liste.equals(""))
+ Interprete.calcul.push("[ " + param.get(0).trim() + " " + liste.trim() + " ] ");
+ else
+ Interprete.calcul.push("[ " + param.get(0).trim() + " ] ");
+ }
+ else
+ {
+ if (!liste.equals(""))
+ Interprete.calcul.push("[ " + mot + " " + liste.trim() + " ] ");
+ else
+ Interprete.calcul.push("[ " + mot + " ] ");
+ }
+ break;
+ case 53: // metsdernier
+ liste = getFinalList(param.get(1)).trim();
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ { // Si c'est une liste
+ Interprete.calcul.push(("[ " + liste).trim() + " " + param.get(0).trim() + " ] ");
+
+ }
+ else
+ Interprete.calcul.push(("[ " + liste).trim() + " " + mot + " ] ");
+
+ break;
+ case 54: // inverse liste
+ liste = getFinalList(param.get(0)).trim();
+ Interprete.operande = true;
+ StringTokenizer st = new StringTokenizer(liste);
+ liste = " ] ";
+ String element = "";
+ while (st.hasMoreTokens())
+ {
+ element = st.nextToken();
+ if (element.equals("["))
+ element = extractList(st);
+ liste = " " + element + liste;
+ }
+ Interprete.calcul.push("[" + liste);
+ break;
+ case 55: // choix
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ liste = getFinalList(param.get(0));
+ int nombre = (int) Math.floor(numberOfElements(liste) * Math.random()) + 1;
+ String tmp = item(liste, nombre);
+ if (tmp.equals("\"\\v"))
+ tmp = "\"";
+ Interprete.calcul.push(tmp);
+ }
+ else
+ {
+ int nombre = (int) Math.floor(Math.random() * getWordLength(mot)) + 1;
+ String str = "";
+ try
+ {
+ str = itemWord(nombre, mot);
+ Double.parseDouble(str);
+ Interprete.calcul.push(str);
+ }
+ catch (NumberFormatException e1)
+ {
+ Interprete.calcul.push("\"" + str);
+ }
+ }
+ break;
+ case 56: // enleve
+ Interprete.operande = true;
+ liste = getFinalList(param.get(1));
+ st = new StringTokenizer(liste);
+ liste = "[ ";
+ mot = getWord(param.get(0));
+ String str;
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ mot = param.get(0).trim();
+
+ while (st.hasMoreTokens())
+ {
+ str = st.nextToken();
+ if (str.equals("["))
+ str = extractList(st);
+ if (!str.equals(mot))
+ liste += str + " ";
+ }
+ Interprete.calcul.push(liste.trim() + " ] ");
+ break;
+ case 57: // item
+ Interprete.operande = true;
+ mot = getWord(param.get(1));
+ if (null == mot)
+ Interprete.calcul.push(item(getFinalList(param.get(1)),
+ kernel.getCalculator().getInteger(param.get(0))));
+ else
+ {
+ int i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 1 || i > getWordLength(mot))
+ throw new LogoError(Utils.primitiveName("item") + " "
+ + Logo.messages.getString("n_aime_pas") + i + " "
+ + Logo.messages.getString("comme_parametre") + ".");
+ else
+ {
+ str = itemWord(i, mot);
+ try
+ {
+ Double.parseDouble(str);
+ Interprete.calcul.push(str);
+ }
+ catch (NumberFormatException e1)
+ {
+ Interprete.calcul.push("\"" + str);
+ }
+ }
+ }
+ break;
+ case 58: // saufdernier
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ liste = getFinalList(param.get(0)).trim();
+ element = item(liste, numberOfElements(liste));
+ int longueur = element.length();
+
+ if (element.startsWith("\"") || element.startsWith("["))
+ longueur--;
+ Interprete.calcul.push("[ " + liste.substring(0, liste.length() - longueur) + "] ");
+ }
+ else if (mot.equals(""))
+ {
+ throw new LogoError(Logo.messages.getString("mot_vide"));
+ }
+ else if (getWordLength(mot) == 1)
+ Interprete.calcul.push("\"");
+ else
+ {
+ String tmp = mot.substring(0, mot.length() - 1);
+ if (tmp.endsWith("\\"))
+ tmp = tmp.substring(0, tmp.length() - 1);
+ try
+ {
+ Double.parseDouble(tmp);
+ Interprete.calcul.push(tmp);
+ }
+ catch (NumberFormatException e)
+ {
+ Interprete.calcul.push(debut_chaine + tmp);
+ }
+ }
+ break;
+ case 59: // saufpremier
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ liste = getFinalList(param.get(0)).trim();
+ element = item(liste, 1);
+ int longueur = element.length();
+ if (element.startsWith("\"") || element.startsWith("["))
+ longueur--;
+ Interprete.calcul.push("[" + liste.substring(longueur, liste.length()) + " ] ");
+ }
+ else if (mot.equals(""))
+ {
+ throw new LogoError(Logo.messages.getString("mot_vide"));
+ }
+ else if (getWordLength(mot) == 1)
+ Interprete.calcul.push("\"");
+ else
+ {
+ if (!mot.startsWith("\\"))
+ mot = mot.substring(1);
+ else
+ mot = mot.substring(2);
+ try
+ {
+ Double.parseDouble(mot);
+ Interprete.calcul.push(mot);
+ }
+ catch (NumberFormatException e)
+ {
+ Interprete.calcul.push(debut_chaine + mot);
+ }
+ }
+
+ break;
+ case 60: // dernier
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ { // Si c'est une liste
+ liste = getFinalList(param.get(0));
+ Interprete.calcul.push(item(liste, numberOfElements(liste)));
+ }
+ else if (getWordLength(mot) == 1)
+ Interprete.calcul.push(debut_chaine + mot);
+ else
+ {
+ str = "";
+ try
+ {
+ str = itemWord(getWordLength(mot), mot);
+ Double.parseDouble(str);
+ Interprete.calcul.push(str);
+ }
+ catch (NumberFormatException e1)
+ {
+ Interprete.calcul.push("\"" + str);
+ }
+ }
+ break;
+ case 61: // premier first
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ { // SI c'est une liste
+ liste = getFinalList(param.get(0));
+ // System.out.println("b"+item(liste, 1)+"b");
+ Interprete.calcul.push(item(liste, 1));
+ }
+ else if (getWordLength(mot) == 1)
+ Interprete.calcul.push(debut_chaine + mot);
+ else
+ {
+ str = "";
+ try
+ {
+ str = itemWord(1, mot);
+ Double.parseDouble(str);
+ Interprete.calcul.push(str);
+ }
+ catch (NumberFormatException e2)
+ {
+ Interprete.calcul.push("\"" + str);
+ }
+ }
+ break;
+ case 62: // compte
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ liste = getFinalList(param.get(0));
+ Interprete.calcul.push(String.valueOf(numberOfElements(liste)));
+ }
+ else
+ Interprete.calcul.push(String.valueOf(getWordLength(mot)));
+ break;
+ case 63: // mot?
+ mot = getWord(param.get(0));
+ if (null == mot)
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ Interprete.operande = true;
+ break;
+ case 64: // nombre?
+ try
+ {
+ Double.parseDouble(param.get(0));
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ }
+ catch (NumberFormatException e)
+ {
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+ Interprete.operande = true;
+ break;
+ case 65: // liste?
+ liste = param.get(0).trim();
+ if (isList(liste))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ Interprete.operande = true;
+ break;
+ case 66: // vide?
+ liste = param.get(0).trim();
+ mot = getWord(param.get(0));
+ if (null == mot)
+ { // si c'est une liste ou un nombre
+ liste = getFinalList(liste).trim();
+ if (liste.equals(""))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+ else
+ { // Si c'est un mot
+ if (mot.equals(""))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+ Interprete.operande = true;
+ break;
+ case 67: // egal?
+ equal(param);
+ break;
+ case 68: // precede?
+ precede(param);
+ break;
+ case 69: // membre ?
+ membre(param, id);
+ break;
+ case 70: // racine arithmetic.sqrt
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().sqrt(param.get(0)));
+ break;
+ case 71: // membre
+ membre(param, id);
+ break;
+ case 72: // donne
+ donne(param);
+ Interprete.operande = false;
+
+ break;
+ case 73: // locale
+ locale(param);
+ Interprete.operande = false;
+ break;
+ case 74: // donnelocale
+ locale(param);
+ donne(param);
+ Interprete.operande = false;
+ break;
+ case 75: // fcc
+ Color color = null;
+ if (isList(param.get(0)))
+ {
+ color = rgb(param.get(0), Utils.primitiveName("fcc"));
+ }
+ else
+ {
+ int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length;
+ if (coul < 0)
+ coul += DrawPanel.defaultColors.length;
+ color = DrawPanel.defaultColors[coul];
+ }
+ cadre.getDrawPanel().fcc(color);
+ break;
+ case 76: // fcfg setscreencolor
+ color = null;
+ if (isList(param.get(0)))
+ {
+ color = rgb(param.get(0), Utils.primitiveName("fcfg"));
+ }
+ else
+ {
+ int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length;
+ if (coul < 0)
+ coul += DrawPanel.defaultColors.length;
+ color = DrawPanel.defaultColors[coul];
+ }
+ cadre.getDrawPanel().fcfg(color);
+ break;
+ case 77: // hasard
+ Interprete.operande = true;
+ int i = kernel.getCalculator().getInteger(param.get(0));
+ i = (int) Math.floor(Math.random() * i);
+ Interprete.calcul.push(String.valueOf(i));
+ break;
+ case 78: // attends
+ try
+ {
+ int temps = kernel.getCalculator().getInteger(param.get(0));
+ if (temps < 0)
+ {
+ String attends = Utils.primitiveName("attends");
+ throw new LogoError(attends + " " + Logo.messages.getString("attend_positif"));
+ }
+ else
+ {
+ int nbsecondes = temps / 60;
+ int reste = temps % 60;
+ for (i = 0; i < nbsecondes; i++)
+ {
+ Thread.sleep(1000);
+ if (cadre.error)
+ break;
+ }
+ if (!cadre.error)
+ Thread.sleep(reste * 50 / 3);
+ }
+
+ }
+ catch (InterruptedException e2)
+ {}
+ break;
+ case 79: // procedures
+ Interprete.operande = true;
+ Interprete.calcul.push(new String(getAllProcedures()));
+ break;
+ case 80: // effaceprocedure efp
+ erase(param.get(0), "procedure");
+ break;
+
+ case 81: // effacevariable
+ erase(param.get(0), "variable");
+ break;
+ case 82: // effacetout erall
+ /*
+ * Marko Zivkovic:
+ * In XLogo4Schools, we delete all files (together with the
+ * procedures) and clear all variables and property lists.
+ */
+ wp.eraseAll();
+ break;
+ case 83: // mot
+ Interprete.operande = true;
+ result = "";
+ for (i = 0; i < param.size(); i++)
+ {
+ mot = getWord(param.get(i));
+ if (null == mot)
+ throw new LogoError(param.get(i) + " " + Logo.messages.getString("error.word"));
+ result += mot;
+ }
+ try
+ {
+ Double.parseDouble(result);
+ }
+ catch (NumberFormatException e)
+ {
+ result = "\"" + result;
+ }
+ Interprete.calcul.push(result);
+ break;
+ case 84: // etiquette
+ String par = param.get(0).trim();
+ if (isList(par))
+ par = formatList(par.substring(1, par.length() - 1));
+ mot = getWord(param.get(0));
+ if (null == mot)
+ cadre.getDrawPanel().etiquette(Utils.SortieTexte(par));
+ else
+ cadre.getDrawPanel().etiquette(Utils.SortieTexte(mot));
+ break;
+ case 85: // /trouvecouleur
+ if (kernel.getActiveTurtle().isVisible())
+ cadre.getDrawPanel().montrecacheTortue(false);
+
+ liste = getFinalList(param.get(0));
+ Color r = cadre.getDrawPanel().guessColorPoint(liste);
+ Interprete.operande = true;
+ Interprete.calcul.push("[ " + r.getRed() + " " + r.getGreen() + " " + r.getBlue() + " ] ");
+ if (kernel.getActiveTurtle().isVisible())
+ cadre.getDrawPanel().montrecacheTortue(true);
+ break;
+ case 86: // fenetre
+ cadre.getDrawPanel().setWindowMode(DrawPanel.WINDOW_CLASSIC);
+ break;
+ case 87: // enroule
+ cadre.getDrawPanel().setWindowMode(DrawPanel.WINDOW_WRAP);
+ break;
+ case 88: // clos
+ cadre.getDrawPanel().setWindowMode(DrawPanel.WINDOW_CLOSE);
+ break;
+ case 89: // videtexte
+ cadre.getHistoryPanel().vide_texte();
+ break;
+ case 90: // chargeimage
+ BufferedImage image = null;
+
+ primitive2D("ci");
+ image = getImage(param.get(0));
+ if (null != image)
+ cadre.getDrawPanel().chargeimage(image);
+ break;
+ case 91: // ftc, fixetaillecrayon
+ double nombre = kernel.getCalculator().numberDouble(param.get(0));
+ if (nombre < 0)
+ nombre = Math.abs(nombre);
+ if (DrawPanel.record3D == DrawPanel.record3D_LINE || DrawPanel.record3D == DrawPanel.record3D_POINT)
+ {
+ if (kernel.getActiveTurtle().getPenWidth() != (float) nombre)
+ DrawPanel.poly.addToScene();
+ }
+ kernel.getActiveTurtle().fixe_taille_crayon((float) nombre);
+ cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon);
+ if (DrawPanel.record3D == DrawPanel.record3D_LINE)
+ {
+ DrawPanel.poly = new ElementLine(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth());
+ DrawPanel.poly.addVertex(
+ new Point3d(kernel.getActiveTurtle().X / 1000, kernel.getActiveTurtle().Y / 1000,
+ kernel.getActiveTurtle().Z / 1000), kernel.getActiveTurtle().couleurcrayon);
+ }
+ else if (DrawPanel.record3D == DrawPanel.record3D_POINT)
+ {
+ DrawPanel.poly = new ElementPoint(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth());
+ }
+ break;
+ case 92: // tantque
+ String li1 = getList(param.get(0));
+ li1 = new String(Utils.decoupe(li1));
+ String li2 = getList(param.get(1));
+ li2 = new String(Utils.decoupe(li2));
+ String instr = "\\siwhile " + li1 + "[ " + li2 + "] ";
+ LoopWhile bp = new LoopWhile(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, instr);
+ Primitive.stackLoop.push(bp);
+ cadre.getKernel().getInstructionBuffer().insert(instr + Primitive.END_LOOP + " ");
+
+ break;
+ case 93: // lis
+ liste = getFinalList(param.get(0));
+ mot = getWord(param.get(1));
+ if (null == mot)
+ throw new LogoError(Logo.messages.getString("error.word"));
+ java.awt.FontMetrics fm = cadre.getFrame().getGraphics().getFontMetrics(font);
+ int longueur = fm.stringWidth(liste) + 100;
+ Lis lis = new Lis(liste, longueur);
+ while (lis.isVisible())
+ {
+ try
+ {
+ Thread.sleep(50);
+ }
+ catch (InterruptedException e)
+ {}
+ }
+ param = new Stack<String>();
+ param.push("\"" + mot);
+ String phrase = lis.getText();
+ // phrase="[ "+Logo.rajoute_backslash(phrase)+" ] ";
+ StringBuffer tampon = new StringBuffer();
+ for (int j = 0; j < phrase.length(); j++)
+ {
+ char c = phrase.charAt(j);
+ if (c == '\\')
+ tampon.append("\\\\");
+ else
+ tampon.append(c);
+ }
+ int offset = tampon.indexOf(" ");
+ if (offset != -1)
+ {
+ tampon.insert(0, "[ ");
+ tampon.append(" ] ");
+ }
+ else
+ {
+ try
+ {
+ Double.parseDouble(phrase);
+ }
+ catch (NumberFormatException e)
+ {
+ tampon.insert(0, "\"");
+ }
+ }
+ phrase = new String(tampon);
+ param.push(phrase);
+ donne(param);
+ String texte = liste + "\n" + phrase;
+ HistoryMessenger.getInstance().dispatchComment(Utils.SortieTexte(texte) + "\n");
+ cadre.focus_Commande();
+ lis.dispose();
+ cadre.focus_Commande();
+ break;
+ case 94: // touche?
+ Interprete.operande = true;
+ if (cadre.getCar() != -1)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 95: // siwhile --> Evalue l'expression test du while
+ liste = getFinalList(param.get(1));
+ boolean pred = predicat(param.get(0));
+ kernel.primitive.whilesi(pred, liste);
+ break;
+ case 96: // liscar
+ while (cadre.getCar() == -1)
+ {
+ try
+ {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e)
+ {}
+ //if (LogoError.lance) //TODO this was always false
+ // break;
+ }
+ Interprete.calcul.push(String.valueOf(cadre.getCar()));
+ Interprete.operande = true;
+ cadre.setCar(-1);
+ break;
+ case 97: // remplis
+ cadre.getDrawPanel().remplis();
+ break;
+ case 98: // point
+ if (kernel.getActiveTurtle().isVisible())
+ cadre.getDrawPanel().montrecacheTortue(false);
+
+ cadre.getDrawPanel().point(getFinalList(param.get(0)));
+ if (kernel.getActiveTurtle().isVisible())
+ cadre.getDrawPanel().montrecacheTortue(true);
+ break;
+ case 99: // vers=towards vers
+
+ Interprete.operande = true;
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ {
+ double angle = cadre.getDrawPanel().vers2D(getFinalList(param.get(0)));
+ Interprete.calcul.push(MyCalculator.teste_fin_double(angle));
+ }
+ else
+ {
+ double[] orientation = cadre.getDrawPanel().vers3D(getFinalList(param.get(0)));
+ Interprete.calcul.push("[ " + orientation[0] + " " + orientation[1] + " " + orientation[2]
+ + " ] ");
+ }
+ break;
+ case 100: // distance
+ Interprete.operande = true;
+ double distance = cadre.getDrawPanel().distance(getFinalList(param.get(0)));
+ Interprete.calcul.push(MyCalculator.teste_fin_double(distance));
+ break;
+ case 101: // couleurcrayon
+ Interprete.operande = true;
+ Interprete.calcul.push("[ " + kernel.getActiveTurtle().couleurcrayon.getRed() + " "
+ + kernel.getActiveTurtle().couleurcrayon.getGreen() + " "
+ + kernel.getActiveTurtle().couleurcrayon.getBlue() + " ] ");
+ break;
+ case 102: // couleurfond
+ Interprete.operande = true;
+ color = cadre.getDrawPanel().getBackgroundColor();
+ Interprete.calcul.push("[ " + color.getRed() + " " + color.getGreen() + " " + color.getBlue()
+ + " ] ");
+ break;
+ case 103: // bc?
+ Interprete.operande = true;
+ if (kernel.getActiveTurtle().isPenDown())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 104: // visible?
+ Interprete.operande = true;
+ if (kernel.getActiveTurtle().isVisible())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 105: // prim?
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ if (Primitive.primitives.containsKey(mot))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 106: // proc?
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (wp.isExecutable(mot))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 107: // exec
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ mot = getList(param.get(0).trim());
+ mot = new String(Utils.decoupe(mot));
+ }
+ else
+ mot = mot + " ";
+ cadre.getKernel().getInstructionBuffer().insert(mot);
+ Interprete.renvoi_instruction = true;
+ break;
+ case 108: // catalogue
+ defaultFolder = uc.getDefaultFolder();
+ str = Utils.SortieTexte(defaultFolder);
+ File f = new File(str);
+ String fichier = "";
+ String dossier = "";
+ int nbdossier = 0;
+ int nbfichier = 0;
+ String[] l = f.list();
+ for (i = 0; i < l.length; i++)
+ {
+ if ((new File(str + File.separator + l[i])).isDirectory())
+ {
+ nbdossier++;
+ if (nbdossier % 5 == 0)
+ dossier += l[i] + "\n";
+ else
+ dossier += l[i] + " ";
+ }
+ else
+ {
+ nbfichier++;
+ if (nbfichier % 5 == 0)
+ fichier += l[i] + "\n";
+ else
+ fichier += l[i] + " ";
+ }
+ }
+ texte = "";
+ if (!dossier.equals(""))
+ texte += Logo.messages.getString("repertoires") + ":\n" + dossier + "\n";
+ if (!fichier.equals(""))
+ texte += Logo.messages.getString("fichiers") + ":\n" + fichier + "\n";
+ HistoryMessenger.getInstance().dispatchComment(texte);
+ break;
+ case 109: // frepertoire
+ liste = getWord(param.get(0));
+ if (null == liste)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ String chemin = Utils.SortieTexte(liste);
+ if ((new File(chemin)).isDirectory() && !chemin.startsWith(".."))
+ {
+ defaultFolder = Utils.rajoute_backslash(chemin);
+ uc.setDefaultFolder(defaultFolder);
+ }
+ else
+ throw new LogoError(liste + " " + Logo.messages.getString("erreur_pas_repertoire"));
+ break;
+ case 110: // repertoire
+ Interprete.operande = true;
+ defaultFolder = uc.getDefaultFolder();
+ Interprete.calcul.push("\"" + defaultFolder);
+ break;
+ case 111: // sauve
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(Logo.messages.getString("error.word"));
+ liste = getFinalList(param.get(1));
+ st = new StringTokenizer(liste);
+ Stack<String> pile = new Stack<String>();
+ while (st.hasMoreTokens())
+ pile.push(st.nextToken());
+ saveProcedures(mot, pile);
+ break;
+ case 112: // sauved
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ saveProcedures(mot, null);
+ break;
+ case 113: // ramene load
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ defaultFolder = uc.getDefaultFolder();
+ String path = Utils.SortieTexte(defaultFolder) + File.separator + mot;
+ String fileName = wp.makeUniqueFileName(mot);
+ try
+ {
+ String txt = Utils.readLogoFile(path); // TODO this
+ // does not
+ // work...
+ // should
+ // load from
+ // path....
+ // later:
+ // looks ok,
+ // not?
+ wp.createFile(fileName);
+ wp.writeFileText(fileName, txt);
+ }
+ catch (IOException e1)
+ {
+ throw new LogoError(Logo.messages.getString("error.iolecture"));
+ }
+
+ break;
+ case 114: // pi
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().pi());
+ break;
+ case 115: // tangente arithmetic.tan
+ Interprete.operande = true;
+
+ Interprete.calcul.push(kernel.getCalculator().tan(param.get(0)));
+
+ break;
+ case 116: // acos
+
+ Interprete.calcul.push(kernel.getCalculator().acos(param.get(0)));
+ Interprete.operande = true;
+ break;
+ case 117: // asin
+
+ Interprete.calcul.push(kernel.getCalculator().asin(param.get(0)));
+ Interprete.operande = true;
+ break;
+ case 118: // atan
+ Interprete.calcul.push(kernel.getCalculator().atan(param.get(0)));
+ Interprete.operande = true;
+ break;
+ case 119: // vrai
+ Interprete.operande = true;
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ break;
+ case 120: // faux
+ Interprete.operande = true;
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 121: // forme
+ primitive2D("turtle.forme");
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(kernel.getActiveTurtle().getShape()));
+ break;
+ case 122: // fixeforme setshape
+ primitive2D("turtle.fforme");
+ i = kernel.getCalculator().getInteger(param.get(0)) % 7;
+ if (kernel.getActiveTurtle().id == 0)
+ {
+ uc.setActiveTurtle(i);
+ }
+ chemin = "tortue" + i + ".png";
+ kernel.change_image_tortue(chemin);
+ break;
+ case 123: // definis workspace.define
+ define(param); // Method extracted by Marko Zivkovic
+ // 21.6.2013
+ break;
+
+ case 124: // tortue
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(kernel.getActiveTurtle().id));
+ break;
+ case 125: // tortues
+ Interprete.operande = true;
+ String li = "[ ";
+ for (i = 0; i < cadre.getDrawPanel().tortues.length; i++)
+ {
+ if (null != cadre.getDrawPanel().tortues[i])
+ li += String.valueOf(i) + " ";
+ }
+ li += "]";
+ Interprete.calcul.push(li);
+ break;
+ case 126: // fixetortue
+ try
+ {
+ i = Integer.parseInt(param.get(0));
+ if (i > -1 && i < uc.getMaxTurtles())
+ {
+ if (null == cadre.getDrawPanel().tortues[i])
+ {
+ cadre.getDrawPanel().tortues[i] = new Turtle(cadre);
+ cadre.getDrawPanel().tortues[i].id = i;
+ cadre.getDrawPanel().tortues[i].setVisible(false);
+ }
+ cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[i];
+ cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon);
+ String police = cadre.getDrawPanel().getGraphicsFont().getName();
+ cadre.getDrawPanel().setGraphicsFont(
+ new java.awt.Font(police, java.awt.Font.PLAIN, kernel.getActiveTurtle().police));
+
+ }
+ else
+ {
+ throw new LogoError(Logo.messages.getString("tortue_inaccessible"));
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ kernel.getCalculator().getInteger(param.get(0));
+ }
+ break;
+ case 127: // police
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(kernel.getActiveTurtle().police));
+ break;
+ case 128: // fixetaillepolice
+ int taille = kernel.getCalculator().getInteger(param.get(0));
+ kernel.getActiveTurtle().police = taille;
+ Font police = font;
+ cadre.getDrawPanel().setGraphicsFont(police.deriveFont((float) kernel.getActiveTurtle().police));
+
+ break;
+ case 129: // tuetortue
+ try
+ {
+ id = Integer.parseInt(param.get(0));
+ if (id > -1 && id < uc.getMaxTurtles())
+ {
+ // On compte le nombre de tortues à l'écran
+ int compteur = 0;
+ int premier_dispo = -1;
+ for (i = 0; i < uc.getMaxTurtles(); i++)
+ {
+ if (null != cadre.getDrawPanel().tortues[i])
+ {
+ if (i != id && premier_dispo == -1)
+ premier_dispo = i;
+ compteur++;
+ }
+ }
+ // On vérifie que ce n'est pas la seule tortue
+ // dispopnible:
+ if (null != cadre.getDrawPanel().tortues[id])
+ {
+ if (compteur > 1)
+ {
+ int tortue_utilisee = kernel.getActiveTurtle().id;
+ cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[id];
+ cadre.getDrawPanel().ct_mt();
+ cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[tortue_utilisee];
+ cadre.getDrawPanel().tortues[id] = null;
+ if (cadre.getDrawPanel().tortues_visibles.search(String.valueOf(id)) > 0)
+ cadre.getDrawPanel().tortues_visibles.remove(String.valueOf(id));
+ if (kernel.getActiveTurtle().id == id)
+ {
+ cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[premier_dispo];
+ cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon); // on
+ // adapte
+ // le
+ // nouveau
+ // crayon
+ str = cadre.getDrawPanel().getGraphicsFont().getName();
+ cadre.getDrawPanel().setFont(
+ new java.awt.Font(str, java.awt.Font.PLAIN,
+ kernel.getActiveTurtle().police));
+
+ }
+ }
+ else
+ {
+ throw new LogoError(Logo.messages.getString("seule_tortue_dispo"));
+ }
+ }
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ kernel.getCalculator().getInteger(param.get(0));
+ }
+ break;
+ case 130: // sequence
+ liste = getFinalList(param.get(0));
+ cadre.getSon().cree_sequence(Utils.decoupe(liste).toString());
+ break;
+ case 131: // instrument
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(cadre.getSon().getInstrument()));
+ break;
+ case 132: // fixeinstrument
+ i = kernel.getCalculator().getInteger(param.get(0));
+ cadre.getSon().setInstrument(i);
+
+ break;
+ case 133: // joue
+ cadre.getSon().joue();
+ break;
+ case 134: // effacesequence
+ cadre.getSon().efface_sequence();
+ break;
+ case 135: // indexsequence
+ Interprete.operande = true;
+ double d = (double) cadre.getSon().getTicks() / 64;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(d));
+
+ break;
+ case 136: // fixeindexsequence
+ i = kernel.getCalculator().getInteger(param.get(0));
+ cadre.getSon().setTicks(i * 64);
+ break;
+ case 137:// fpt
+ i = kernel.getCalculator().getInteger(param.get(0));
+ cadre.getHistoryPanel().getDsd().fixepolice(i);
+ break;
+ case 138: // ptexte
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(cadre.getHistoryPanel().police()));
+ break;
+ case 139: // fct,fixecouleurtexte
+ if (isList(param.get(0)))
+ {
+ cadre.getHistoryPanel().getDsd().fixecouleur(rgb(param.get(0), Utils.primitiveName("fct")));
+ }
+ else
+ {
+ int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length;
+ if (coul < 0)
+ coul += DrawPanel.defaultColors.length;
+ cadre.getHistoryPanel().getDsd().fixecouleur(DrawPanel.defaultColors[coul]);
+ }
+ break;
+ case 140: // couleurtexte
+ Interprete.operande = true;
+ Color c = cadre.getHistoryPanel().getCouleurtexte();
+ Interprete.calcul.push("[ " + c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " ] ");
+ break;
+ case 141: // lissouris readmouse
+ while (!cadre.getDrawPanel().get_lissouris())
+ {
+ try
+ {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e)
+ {}
+ //if (LogoError.lance) //TODO Marko: this was always false
+ // break;
+ }
+ Interprete.calcul.push(String.valueOf(cadre.getDrawPanel().get_bouton_souris()));
+ Interprete.operande = true;
+ break;
+ case 142: // possouris
+ Interprete.calcul.push(cadre.getDrawPanel().get_possouris());
+ Interprete.operande = true;
+ break;
+ case 143: // msg message
+ liste = getFinalList(param.get(0));
+ st = new StringTokenizer(liste); // On
+ // découpe
+ // le
+ // message
+ // en
+ // tranche
+ // de
+ // longueurs
+ // acceptables
+ fm = cadre.getFrame().getGraphics().getFontMetrics(font);
+ liste = "";
+ String buf = "";
+ while (st.hasMoreTokens())
+ {
+ buf += st.nextToken() + " ";
+ if (fm.stringWidth(buf) > 200)
+ {
+ liste += buf + "\n";
+ buf = "";
+ }
+ }
+ liste += buf;
+ liste = Utils.SortieTexte(liste);
+
+ MyTextAreaDialog jt = new MyTextAreaDialog(liste, cadre.getHistoryPanel().getDsd());
+ ImageIcon icone = new ImageIcon(Utils.class.getResource("icone.png"));
+ JOptionPane.showMessageDialog(cadre.getFrame(), jt, "", JOptionPane.INFORMATION_MESSAGE,
+ (Icon) icone);
+ break;
+ case 144: // date
+ Interprete.operande = true;
+ Calendar cal = Calendar.getInstance(lang.getLocale());
+ int jour = cal.get(Calendar.DAY_OF_MONTH);
+ int mois = cal.get(Calendar.MONTH) + 1;
+ int annee = cal.get(Calendar.YEAR);
+ Interprete.calcul.push("[ " + jour + " " + mois + " " + annee + " ] ");
+ break;
+ case 145: // heure
+ Interprete.operande = true;
+ cal = Calendar.getInstance(lang.getLocale());
+ int heure = cal.get(Calendar.HOUR_OF_DAY);
+ int minute = cal.get(Calendar.MINUTE);
+ int seconde = cal.get(Calendar.SECOND);
+ Interprete.calcul.push("[ " + heure + " " + minute + " " + seconde + " ] ");
+ break;
+ case 146: // temps
+ Interprete.operande = true;
+ long heure_actuelle = Calendar.getInstance().getTimeInMillis();
+ Interprete.calcul.push(String.valueOf((heure_actuelle - uc.getHeure_demarrage()) / 1000));
+ break;
+ case 147: // debuttemps
+ int temps = kernel.getCalculator().getInteger(param.get(0));
+ Kernel.chrono = Calendar.getInstance().getTimeInMillis() + 1000 * temps;
+ break;
+ case 148: // fintemps?
+ Interprete.operande = true;
+ if (Calendar.getInstance().getTimeInMillis() > Kernel.chrono)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 149: // fnp fixenompolice
+ int int_police = kernel.getCalculator().getInteger(param.get(0));
+ cadre.getDrawPanel().police_etiquette = int_police % UserConfig.fontes.length;
+ break;
+ case 150: // np nompolice
+ Interprete.operande = true;
+ Interprete.calcul.push("[ " + cadre.getDrawPanel().police_etiquette + " [ "
+ + UserConfig.fontes[cadre.getDrawPanel().police_etiquette].getFontName() + " ] ] ");
+ break;
+ case 151: // fnpt fixenompolicetexte
+ int_police = kernel.getCalculator().getInteger(param.get(0));
+ HistoryPanel.fontPrint = int_police % UserConfig.fontes.length;
+ cadre.getHistoryPanel().getDsd().fixenompolice(int_police);
+
+ break;
+ case 152: // npt nompolicetexte
+ Interprete.operande = true;
+ Interprete.calcul.push("[ " + HistoryPanel.fontPrint + " [ "
+ + UserConfig.fontes[HistoryPanel.fontPrint].getFontName() + " ] ] ");
+ break;
+ case 153: // listeflux
+ liste = "[ ";
+ for (MyFlow flow : kernel.flows)
+ {
+ liste += "[ " + flow.getId() + " " + flow.getPath() + " ] ";
+ }
+ liste += "] ";
+ Interprete.operande = true;
+ Interprete.calcul.push(liste);
+ break;
+ case 154: // lisligneflux
+ try
+ {
+ int ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ MyFlow flow = kernel.flows.get(index);
+ MyFlowReader flowReader;
+ // If the flow is a writable flow, throw error
+ if (flow.isWriter())
+ throw new LogoError(Logo.messages.getString("flux_lecture"));
+ // else if the flow is a readable flow, convert to
+ // MyFlowReader
+ else if (flow.isReader())
+ {
+ flowReader = ((MyFlowReader) flow);
+ }
+ // else the flow isn't yet defined, initialize
+ else
+ flowReader = new MyFlowReader(flow);
+
+ if (flowReader.isFinished())
+ throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident);
+ // Reading line
+ String line = flowReader.readLine();
+ if (null == line)
+ {
+ flow.setFinished(true);
+ throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident);
+ }
+ Interprete.operande = true;
+ Interprete.calcul.push("[ " + Utils.decoupe(line.trim()) + " ] ");
+ kernel.flows.set(index, flowReader);
+ }
+ catch (FileNotFoundException e1)
+ {
+ throw new LogoError(Logo.messages.getString("error.iolecture"));
+ }
+ catch (IOException e2)
+ {}
+ break;
+ case 155: // liscaractereflux
+ try
+ {
+ int ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ MyFlow flow = kernel.flows.get(index);
+ MyFlowReader flowReader;
+ // If the flow is a writable flow, throw error
+ if (flow.isWriter())
+ throw new LogoError(Logo.messages.getString("flux_lecture"));
+ // else if the flow is reader, convert to MyFlowReader
+ else if (flow.isReader())
+ {
+ flowReader = ((MyFlowReader) flow);
+ }
+ // else the flow isn't yet defined, initialize
+ else
+ flowReader = new MyFlowReader(flow);
+
+ if (flowReader.isFinished())
+ throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident);
+
+ int character = ((MyFlowReader) flow).readChar();
+ if (character == -1)
+ {
+ flow.setFinished(true);
+ throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident);
+ }
+ Interprete.operande = true;
+ String car = String.valueOf(character);
+ if (car.equals("\\"))
+ car = "\\\\";
+ Interprete.calcul.push(car);
+ kernel.flows.set(index, flowReader);
+ }
+ catch (FileNotFoundException e1)
+ {
+ throw new LogoError(Logo.messages.getString("error.iolecture"));
+ }
+ catch (IOException e2)
+ {}
+ break;
+ case 156: // ecrisligneflux
+ try
+ {
+ int ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ liste = getFinalList(param.get(1));
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ MyFlow flow = kernel.flows.get(index);
+ MyFlowWriter flowWriter;
+ // If the flow is a readable flow, throw an error
+ if (flow.isReader())
+ throw new LogoError(Logo.messages.getString("flux_ecriture"));
+ // Else if the flow is a writable flow , convert to
+ // MrFlowWriter
+ else if (flow.isWriter())
+ flowWriter = (MyFlowWriter) flow;
+ // Else the flow isn't defined yet, initialize
+ else
+ flowWriter = new MyFlowWriter(flow);
+
+ // System.out.println(flow.isReader()+" "+flow.isWriter());
+ // Write the line
+ flowWriter.write(Utils.SortieTexte(liste));
+ kernel.flows.set(index, flowWriter);
+ }
+ catch (FileNotFoundException e1)
+ {}
+ catch (IOException e2)
+ {}
+ break;
+ case 157: // finficher?
+ try
+ {
+ int ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ else
+ {
+ MyFlow flow = kernel.flows.get(index);
+ MyFlowReader flowReader = null;
+ // If the flow isn't defined yet, initialize
+ if (!flow.isWriter() && !flow.isReader())
+ {
+ flowReader = new MyFlowReader(flow);
+ }
+ else if (flow.isReader())
+ flowReader = (MyFlowReader) flow;
+ if (null != flowReader)
+ {
+ if (flow.isFinished())
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ }
+ else
+ {
+ int read = flowReader.isReadable();
+ if (read == -1)
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ flow.setFinished(true);
+ }
+ else
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+ }
+ }
+ else
+ throw new LogoError(Logo.messages.getString("flux_lecture"));
+ }
+ }
+ catch (FileNotFoundException e1)
+ {}
+ catch (IOException e2)
+ {}
+ break;
+ case 158: // ouvreflux
+ mot = getWord(param.get(1));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ defaultFolder = uc.getDefaultFolder();
+ liste = Utils.SortieTexte(defaultFolder) + File.separator + Utils.SortieTexte(mot);
+ int ident = kernel.getCalculator().getInteger(param.get(0));
+ if (kernel.flows.search(ident) == -1)
+ kernel.flows.add(new MyFlow(ident, liste, false));
+ else
+ throw new LogoError(ident + " " + Logo.messages.getString("flux_existant"));
+ break;
+ case 159: // fermeflux
+ try
+ {
+ ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ MyFlow flow = kernel.flows.get(index);
+ // If the flow is a readable flow
+ if (flow.isReader())
+ ((MyFlowReader) flow).close();
+ // Else if it's a writable flow
+ else if (flow.isWriter())
+ ((MyFlowWriter) flow).close();
+ kernel.flows.remove(index);
+ }
+ catch (IOException e2)
+ {}
+ break;
+ case 160: // ajouteligneflux
+ try
+ {
+ ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ liste = getFinalList(param.get(1));
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ MyFlow flow = kernel.flows.get(index);
+ MyFlowWriter flowWriter;
+ // If the flow is a readable flow, throw an error
+ if (flow.isReader())
+ throw new LogoError(Logo.messages.getString("flux_ecriture"));
+ // Else if the flow is a writable flow , convert to
+ // MrFlowWriter
+ else if (flow.isWriter())
+ flowWriter = (MyFlowWriter) flow;
+ // Else the flow isn't defined yet, initialize
+ else
+ flowWriter = new MyFlowWriter(flow);
+
+ // Write the line
+ flowWriter.append(Utils.SortieTexte(liste));
+ kernel.flows.set(index, flowWriter);
+ }
+ catch (FileNotFoundException e1)
+ {}
+ catch (IOException e2)
+ {}
+ break;
+ case 161: // souris?
+ Interprete.operande = true;
+ if (cadre.getDrawPanel().get_lissouris())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 162: // variables
+ Interprete.operande = true;
+ Interprete.calcul.push(new String(getAllVariables()));
+ break;
+ case 163: // chose thing
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ throw new LogoError(Logo.messages.getString("error.word"));
+ } // si c'est une liste
+ else if (debut_chaine.equals("")) { throw new LogoError(
+ Logo.messages.getString("erreur_variable")); } // si
+ // c'est
+ // un
+ // nombre
+ Interprete.operande = true;
+ String value;
+ mot = mot.toLowerCase();
+ if (!Interprete.locale.containsKey(mot))
+ {
+ if (!wp.getGlobals().getVariables().contains(mot))
+ throw new LogoError(mot + " " + Logo.messages.getString("erreur_variable"));
+ else
+ value = wp.getGlobals().getValue(mot);
+ }
+ else
+ {
+ value = Interprete.locale.get(mot);
+ }
+ if (null == value)
+ throw new LogoError(mot + " " + Logo.messages.getString("erreur_variable"));
+ Interprete.calcul.push(value);
+ break;
+ case 164: // nettoie
+ cadre.getDrawPanel().nettoie();
+ break;
+ case 165: // tape
+ par = param.get(0).trim();
+ if (isList(par))
+ par = formatList(par.substring(1, par.length() - 1));
+ mot = getWord(param.get(0));
+ if (null == mot)
+ HistoryMessenger.getInstance().dispatchLogoOutput(Utils.SortieTexte(par));
+ else
+ HistoryMessenger.getInstance().dispatchLogoOutput(Utils.SortieTexte(mot));
+ break;
+ case 166: // cercle
+ cadre.getDrawPanel().circle((kernel.getCalculator().numberDouble(param.pop())));
+ break;
+ case 167: // arc
+ cadre.getDrawPanel().arc(kernel.getCalculator().numberDouble(param.get(0)),
+ kernel.getCalculator().numberDouble(param.get(1)),
+ kernel.getCalculator().numberDouble(param.get(2)));
+ break;
+ case 168: // rempliszone
+ cadre.getDrawPanel().rempliszone();
+ break;
+ case 169: // animation
+ cadre.getDrawPanel().setAnimation(true);
+ Interprete.operande = false;
+ break;
+ case 170: // rafraichis
+ if (DrawPanel.classicMode == DrawPanel.MODE_ANIMATION)
+ {
+ cadre.getDrawPanel().refresh();
+ }
+ break;
+
+ case 171: // tailledessin
+ Interprete.operande = true;
+ StringBuffer sb = new StringBuffer();
+ sb.append("[ ");
+ sb.append(w);
+ sb.append(" ");
+ sb.append(h);
+ sb.append(" ] ");
+ Interprete.calcul.push(new String(sb));
+ break;
+ case 172: // quotient
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().quotient(param.get(0), param.get(1)));
+ break;
+ case 173: // entier?
+ Interprete.operande = true;
+ double ent = kernel.getCalculator().numberDouble(param.get(0));
+ if ((int) ent == ent)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 174: // fixeseparation
+ nombre = kernel.getCalculator().numberDouble(param.get(0));
+ if (nombre < 0 || nombre > 1)
+ throw new LogoError(nombre + " " + Logo.messages.getString("entre_zero_un"));
+ cadre.drawingAndHistory.setResizeWeight(nombre);
+ cadre.drawingAndHistory.setDividerLocation(nombre);
+ break;
+ case 175: // separation
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(cadre.drawingAndHistory.getResizeWeight()));
+ break;
+ case 176: // tronque
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().truncate(param.get(0)));
+ break;
+ case 177: // trace
+ Kernel.mode_trace = true;
+ Interprete.operande = false;
+ break;
+ case 178:// changedossier
+ Interprete.operande = false;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ chemin = "";
+ defaultFolder = uc.getDefaultFolder();
+ if (defaultFolder.endsWith(File.separator))
+ chemin = Utils.SortieTexte(defaultFolder + mot);
+ else
+ chemin = Utils.SortieTexte(defaultFolder + Utils.rajoute_backslash(File.separator) + mot);
+ if ((new File(chemin)).isDirectory())
+ {
+ try
+ {
+ defaultFolder = Utils.rajoute_backslash((new File(chemin)).getCanonicalPath());
+ uc.setDefaultFolder(defaultFolder);
+ }
+ catch (NullPointerException e1)
+ {}
+ catch (IOException e2)
+ {}
+ }
+ else
+ throw new LogoError(Utils.rajoute_backslash(chemin) + " "
+ + Logo.messages.getString("erreur_pas_repertoire"));
+
+ break;
+ case 179:// unicode
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ else if (getWordLength(mot) != 1)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("un_caractere"));
+ else
+ {
+ Interprete.operande = true;
+ str = String.valueOf((int) Utils.SortieTexte(itemWord(1, mot)).charAt(0));
+ Interprete.calcul.push(str);
+ }
+ break;
+ case 180:// caractere
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 0 || i > 65535)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("nombre_unicode"));
+ else
+ {
+ str = "";
+ Interprete.operande = true;
+ if (i == 92)
+ str = "\"\\\\";
+ else if (i == 10)
+ str = "\"\\n";
+ else if (i == 32)
+ str = "\"\\e";
+ else
+ {
+ str = String.valueOf((char) i);
+ try
+ {
+ Double.parseDouble(str);
+ }
+ catch (NumberFormatException e)
+ {
+ str = "\"" + str;
+ }
+ }
+ Interprete.calcul.push(str);
+ }
+ break;
+ case 181: // stoptout
+ cadre.error = true;
+ break;
+ case 182: // compteur
+ boolean erreur = false;
+ if (!Primitive.stackLoop.isEmpty())
+ {
+ LoopProperties lp = Primitive.stackLoop.peek();
+ if (lp.isRepeat())
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(lp.getCounter().toString());
+ }
+ else
+ erreur = true;
+ }
+ else
+ erreur = true;
+ if (erreur) { throw new LogoError(Logo.messages.getString("erreur_compteur")); }
+ break;
+ case 183: // controls.for repetepour
+ li2 = getList(param.get(1));
+ li2 = new String(Utils.decoupe(li2));
+ li1 = getFinalList(param.get(0));
+ int nb = numberOfElements(li1);
+ if (nb < 3 || nb > 4)
+ throw new LogoError(Logo.messages.getString("erreur_repetepour"));
+ st = new StringTokenizer(li1);
+ String var = st.nextToken().toLowerCase();
+ BigDecimal deb = kernel.getCalculator().numberDecimal(st.nextToken());
+ BigDecimal fin = kernel.getCalculator().numberDecimal(st.nextToken());
+ BigDecimal increment = BigDecimal.ONE;
+ if (nb == 4)
+ increment = kernel.getCalculator().numberDecimal(st.nextToken());
+ if (var.equals(""))
+ throw new LogoError(Logo.messages.getString("variable_vide"));
+ try
+ {
+ Double.parseDouble(var);
+ throw new LogoError(Logo.messages.getString("erreur_nom_nombre_variable"));
+ }
+ catch (NumberFormatException e)
+ {
+ LoopFor lf = new LoopFor(deb, fin, increment, li2, var);
+ lf.AffecteVar(true);
+
+ if ((increment.compareTo(BigDecimal.ZERO) == 1 && fin.compareTo(deb) >= 0)
+ || (increment.compareTo(BigDecimal.ZERO) == -1 && fin.compareTo(deb) <= 0))
+ {
+ cadre.getKernel().getInstructionBuffer().insert(li2 + Primitive.END_LOOP + " ");
+ Primitive.stackLoop.push(lf);
+ }
+ }
+ break;
+ case 184: // absolue
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().abs(param.get(0)));
+ break;
+ case 185: // remplace
+ String reponse = "";
+ liste = getFinalList(param.get(0));
+ int entier = kernel.getCalculator().getInteger(param.get(1));
+ mot = getWord(param.get(2));
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ mot = "[ " + getFinalList(param.get(2)) + "]";
+ char ch;
+ int compteur = 1;
+ boolean espace = true;
+ boolean crochet = false;
+ boolean error = true;
+ for (int j = 0; j < liste.length(); j++)
+ {
+ if (compteur == entier)
+ {
+ error = false;
+ compteur = j;
+ break;
+ }
+ ch = liste.charAt(j);
+ if (ch == '[')
+ {
+ if (espace)
+ crochet = true;
+ espace = false;
+ }
+ if (ch == ' ')
+ {
+ espace = true;
+ if (crochet)
+ {
+ crochet = false;
+ j = extractList(liste, j);
+ }
+ compteur++;
+ }
+ }
+ if (error)
+ throw new LogoError(Logo.messages.getString("y_a_pas") + " " + entier + " "
+ + Logo.messages.getString("element_dans_liste") + liste + "]");
+ reponse = "[ " + liste.substring(0, compteur) + mot;
+ // On extrait le mot suivant
+ if (compteur + 1 < liste.length() && liste.charAt(compteur) == '['
+ && liste.charAt(compteur + 1) == ' ')
+ {
+ compteur = extractList(liste, compteur + 2);
+ reponse += liste.substring(compteur) + "] ";
+
+ }
+ else
+ {
+ for (i = compteur + 1; i < liste.length(); i++)
+ {
+ if (liste.charAt(i) == ' ')
+ {
+ compteur = i;
+ break;
+ }
+ }
+ reponse += liste.substring(compteur) + "] ";
+ }
+ Interprete.operande = true;
+ Interprete.calcul.push(reponse);
+ break;
+ case 186: // ajoute
+ reponse = "";
+ liste = getFinalList(param.get(0));
+ entier = kernel.getCalculator().getInteger(param.get(1));
+ mot = getWord(param.get(2));
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ mot = "[ " + getFinalList(param.get(2)) + "]";
+ compteur = 1;
+ espace = true;
+ crochet = false;
+ error = true;
+ for (int j = 0; j < liste.length(); j++)
+ {
+ if (compteur == entier)
+ {
+ error = false;
+ compteur = j;
+ break;
+ }
+ ch = liste.charAt(j);
+ if (ch == '[')
+ {
+ if (espace)
+ crochet = true;
+ espace = false;
+ }
+ if (ch == ' ')
+ {
+ espace = true;
+ if (crochet)
+ {
+ crochet = false;
+ j = extractList(liste, j);
+ }
+ compteur++;
+ if (j == liste.length() - 1 && compteur == entier)
+ {
+ error = false;
+ compteur = liste.length();
+ }
+ }
+ }
+ if (error && entier != compteur)
+ throw new LogoError(Logo.messages.getString("y_a_pas") + " " + entier + " "
+ + Logo.messages.getString("element_dans_liste") + liste + "]");
+ if (!liste.trim().equals(""))
+ reponse = "[ " + liste.substring(0, compteur) + mot + " " + liste.substring(compteur) + "] ";
+ else
+ reponse = "[ " + mot + " ] ";
+ Interprete.operande = true;
+ Interprete.calcul.push(reponse);
+ break;
+ case 187: // gris
+ colorCode(8);
+ break;
+ case 188: // grisclair
+ colorCode(9);
+ break;
+ case 189: // rougefonce
+ colorCode(10);
+ break;
+ case 190: // vertfonce
+ colorCode(11);
+ break;
+ case 191: // bleufonce
+ colorCode(12);
+ break;
+ case 192: // orange
+ colorCode(13);
+ break;
+ case 193: // rose
+ colorCode(14);
+ break;
+ case 194: // violet
+ colorCode(15);
+ break;
+ case 195: // marron
+ colorCode(16);
+ break;
+ case 196: // noir
+ colorCode(0);
+ break;
+ case 197: // rouge
+ colorCode(1);
+ break;
+ case 198: // vert
+ colorCode(2);
+ break;
+ case 199: // jaune
+ colorCode(3);
+ break;
+ case 200: // bleu
+ colorCode(4);
+ break;
+ case 201: // magenta
+ colorCode(5);
+ break;
+ case 202: // cyan
+ colorCode(6);
+ break;
+ case 203: // blanc
+ colorCode(7);
+ break;
+ case 204: // Parenthese fermante
+ // Distinguons les deux cas : (3)*2 et (4+3)*2
+ // Le 3 est ici a retourner au +
+ boolean a_retourner = true;
+ // On enleve le "(" correspondant a la parenthese ouvrante
+ // de la
+ // pile nom
+ // a condition que l'element attendant de la pile nom ne
+ // soit
+ // pas une procedure
+ boolean est_procedure = false;
+ int pos = Interprete.nom.lastIndexOf("(");
+ if (pos == -1)
+ {
+ // Parenthese fermante sans parenthese ouvrante au
+ // prealable
+ throw new LogoError(Logo.messages.getString("parenthese_ouvrante"));
+ }
+ else
+ { // Evitons l'erreur en cas de par exemple: "ec )"
+ // (parenthese fermante sans ouvrante)--> else a
+ // executer qu'en cas de non erreur
+ if (Interprete.drapeau_ouvrante)
+ {
+ // parenthese vide
+ throw new LogoError(Logo.messages.getString("parenthese_vide"));
+
+ }
+ for (int j = pos; j < Interprete.nom.size(); j++)
+ {
+ String proc = Interprete.nom.get(j).toLowerCase();
+ if (Primitive.primitives.containsKey(proc))
+ est_procedure = true;
+ else
+ {
+ if (wp.isExecutable(proc)) // TODO changed
+ {
+ est_procedure = true;
+ break;
+ }
+ }
+ }
+ }
+ // Si une procedure est presente dans la pile nom, on garde
+ // les parenteses
+ // System.out.println(Primitive.primitives.containsKey("puissance")+"
+ // "+est_procedure);
+ if (est_procedure)
+ {
+ cadre.getKernel().getInstructionBuffer().insert(") ");
+ }
+ // Sinon on les enleve avec leurs imbrications eventuelles
+ else
+ {
+ if (Interprete.en_cours.isEmpty() || !Interprete.en_cours.peek().equals("("))
+ {
+ throw new LogoError(Logo.messages.getString("parenthese_ouvrante"));
+ }
+ else
+ Interprete.en_cours.pop();
+ if (!Interprete.nom.isEmpty())
+ {
+ if (Interprete.nom.peek().equals("("))
+ a_retourner = false;
+ pos = Interprete.nom.lastIndexOf("(");
+ if (pos == -1)
+ {
+ // Parenthese fermante sans parenthese ouvrante
+ // au prelable
+ throw new LogoError(Logo.messages.getString("parenthese_ouvrante"));
+ }
+ else
+ {
+ Interprete.nom.removeElementAt(pos);
+ // S'il y a imbrication de parentheses (((20)))
+ pos--;
+ instruction = cadre.getKernel().getInstructionBuffer();
+ while (instruction.getNextWord().equals(")") && (pos > -1))
+ {
+ if (!Interprete.nom.isEmpty() && Interprete.nom.get(pos).equals("("))
+ {
+ instruction.deleteFirstWord(")");
+ Interprete.nom.removeElementAt(pos);
+ pos--;
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+ if (Interprete.calcul.isEmpty())
+ {
+ Interprete.operande = false;
+ }
+ else
+ {
+ Interprete.operande = true;
+ Interprete.drapeau_fermante = a_retourner;
+ }
+ break;
+ case 205: // fixestyle
+ boolean gras = false;
+ boolean italique = false;
+ boolean souligne = false;
+ boolean exposant = false;
+ boolean indice = false;
+ boolean barre = false;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ liste = getFinalList(param.get(0));
+ else
+ liste = mot;
+ if (liste.trim().equals(""))
+ liste = Logo.messages.getString("style.none");
+ st = new StringTokenizer(liste);
+ while (st.hasMoreTokens())
+ {
+ element = st.nextToken().toLowerCase();
+ if (element.equals(Logo.messages.getString("style.underline").toLowerCase()))
+ {
+ souligne = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.bold").toLowerCase()))
+ {
+ gras = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.italic").toLowerCase()))
+ {
+ italique = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.exposant").toLowerCase()))
+ {
+ exposant = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.subscript").toLowerCase()))
+ {
+ indice = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.strike").toLowerCase()))
+ {
+ barre = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.none").toLowerCase()))
+ {}
+ else
+ throw new LogoError(Logo.messages.getString("erreur_fixestyle"));
+ }
+ cadre.getHistoryPanel().getDsd().fixegras(gras);
+ cadre.getHistoryPanel().getDsd().fixeitalique(italique);
+ cadre.getHistoryPanel().getDsd().fixesouligne(souligne);
+ cadre.getHistoryPanel().getDsd().fixeexposant(exposant);
+ cadre.getHistoryPanel().getDsd().fixeindice(indice);
+ cadre.getHistoryPanel().getDsd().fixebarre(barre);
+ break;
+ case 206: // style
+ StringBuffer buffer = new StringBuffer();
+ compteur = 0;
+ if (cadre.getHistoryPanel().getDsd().estgras())
+ {
+ buffer.append(Logo.messages.getString("style.bold").toLowerCase() + " ");
+ compteur++;
+ }
+ if (cadre.getHistoryPanel().getDsd().estitalique())
+ {
+ buffer.append(Logo.messages.getString("style.italic").toLowerCase() + " ");
+ compteur++;
+ }
+ if (cadre.getHistoryPanel().getDsd().estsouligne())
+ {
+ buffer.append(Logo.messages.getString("style.underline").toLowerCase() + " ");
+ compteur++;
+ }
+ if (cadre.getHistoryPanel().getDsd().estexposant())
+ {
+ buffer.append(Logo.messages.getString("style.exposant").toLowerCase() + " ");
+ compteur++;
+ }
+ if (cadre.getHistoryPanel().getDsd().estindice())
+ {
+ buffer.append(Logo.messages.getString("style.subscript").toLowerCase() + " ");
+ compteur++;
+ }
+ if (cadre.getHistoryPanel().getDsd().estbarre())
+ {
+ buffer.append(Logo.messages.getString("style.strike").toLowerCase() + " ");
+ compteur++;
+ }
+ Interprete.operande = true;
+ if (compteur == 0)
+ Interprete.calcul.push("\"" + Logo.messages.getString("style.none").toLowerCase());
+ else if (compteur == 1)
+ Interprete.calcul.push("\"" + new String(buffer).trim());
+ else if (compteur > 1)
+ Interprete.calcul.push("[ " + new String(buffer) + "]");
+ break;
+ case 207: // listaillefenetre
+ Interprete.operande = true;
+ java.awt.Point p = cadre.scrollArea.getViewport().getViewPosition();
+ Rectangle rec = cadre.scrollArea.getVisibleRect();
+ sb = new StringBuffer();
+ int x1 = p.x - w / 2;
+ int y1 = h / 2 - p.y;
+ int x2 = x1 + rec.width - cadre.scrollArea.getVerticalScrollBar().getWidth();
+ int y2 = y1 - rec.height + cadre.scrollArea.getHorizontalScrollBar().getHeight();
+ sb.append("[ ");
+ sb.append(x1);
+ sb.append(" ");
+ sb.append(y1);
+ sb.append(" ");
+ sb.append(x2);
+ sb.append(" ");
+ sb.append(y2);
+ sb.append(" ] ");
+ Interprete.calcul.push(new String(sb));
+ break;
+ case 208: // LongueurEtiquette
+ mot = getWord(param.get(0));
+ if (null != mot)
+ mot = Utils.SortieTexte(mot);
+ else
+ mot = getFinalList(param.get(0)).trim();
+ Interprete.operande = true;
+ fm = cadre.getDrawPanel().getGraphics().getFontMetrics(cadre.getDrawPanel().getGraphicsFont());
+ longueur = fm.stringWidth(mot);
+ Interprete.calcul.push(String.valueOf(longueur));
+ break;
+ case 209: // envoietcp // sendtcp // enviatcp etcp
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot) { throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); }
+ mot = mot.toLowerCase();
+ liste = "";
+ liste = getFinalList(param.get(1));
+ NetworkClientSend ncs = new NetworkClientSend(mot, liste);
+ Interprete.calcul.push("[ " + ncs.getAnswer() + " ] ");
+ /*
+ * {
+ * liste = "[ "; mot2 =
+ * getFinalList(param.get(0).toString()); liste += mot2 + "
+ * ]"; String rip = liste.substring(2,17); // cadre.ecris("perso
+ * ", rip + "\n");
+ * //para debug String rdat = "_" + liste.substring(18,23) +
+ * "*\n\r"; //
+ * cadre.ecris("perso", rdat + "\n"); //para debug Socket
+ * echoSocket = null;
+ * DataOutputStream tcpout = null; BufferedReader tcpin =
+ * null; String resp =
+ * null; { echoSocket = new Socket(rip, 1948); tcpout =
+ * new
+ * DataOutputStream(echoSocket.getOutputStream()); tcpin=
+ * new BufferedReader(new
+ * InputStreamReader(echoSocket.getInputStream()));
+ * tcpout.writeBytes(rdat);
+ * resp = tcpin.readLine(); // readLine detiene el programa
+ * hasta que recibe una
+ * respuesta del robot. Que hacer si no recibe nada?
+ * tcpout.close();
+ * tcpin.close(); echoSocket.close(); } catch
+ * (UnknownHostException e) { throw
+ * new myException( Logo.messages.getString("erreur_tcp"));
+ * } catch
+ * (IOException e) { throw new myException(
+ * Logo.messages.getString("erreur_tcp")); }
+ * Interprete.calcul.push("[ " + resp + "
+ * ]"); } catch(myException e){}
+ */
+ break;
+ case 210: // ecoutetcp
+ /**
+ * Marko Zivkovic : new implementation of context switch.
+ * saving of workspace (now context) is done in {@link ContextManager}
+ */
+ Interprete.operande = false;
+ new NetworkServer(cadre);
+
+ break;
+ case 211: // executetcp
+ mot = getWord(param.get(0));
+ if (null == mot) { throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); }
+ mot = mot.toLowerCase();
+ liste = "";
+ liste = getFinalList(param.get(1));
+ new NetworkClientExecute(cadre.getKernel(), mot, liste);
+ break;
+ case 212: // \x internal operator to specify
+ // the end of network instructions with
+ // "executetcp"
+ // have to replace workspace
+ Interprete.operande = false;
+
+ /*
+ * Marko Zivkovic : new implementation of network context
+ */
+ wp.popNetworkMode();
+
+ break;
+ case 213: // chattcp
+ Interprete.operande = false;
+ mot = getWord(param.get(0));
+ if (null == mot) { throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); }
+ mot = mot.toLowerCase();
+ liste = "";
+ liste = getFinalList(param.get(1));
+ new NetworkClientChat(cadre, mot, liste);
+ break;
+ case 214: // init resetall
+ Interprete.operande = false;
+ // resize drawing zone if necessary
+ if (h != 1000 || w != 1000)
+ {
+ h = 1000;
+ w = 1000;
+ cadre.resizeDrawingZone();
+ }
+ uc.setDrawGrid(false);
+ uc.setDrawXAxis(false);
+ uc.setDrawYAxis(false);
+ cadre.getDrawPanel().origine();
+ kernel.getActiveTurtle().stroke = new BasicStroke(1);
+ cadre.getDrawPanel().setBackgroundColor(Color.WHITE);
+ if (kernel.getActiveTurtle().id == 0)
+ {
+ uc.setActiveTurtle(0);
+ }
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC;
+ chemin = "tortue0.png";
+ kernel.change_image_tortue(chemin);
+ cadre.getDrawPanel().fcfg(Color.WHITE);
+ cadre.getDrawPanel().fcc(Color.BLACK);
+ cadre.getDrawPanel().setAnimation(false);
+ font = new Font("dialog", Font.PLAIN, 12);
+ WSManager.getWorkspaceConfig().setFont(font);
+ kernel.getActiveTurtle().police = 12;
+ cadre.getDrawPanel().setGraphicsFont(font);
+ HistoryPanel.fontPrint = UserConfig.police_id(font);
+ cadre.getHistoryPanel().getDsd().fixepolice(12);
+ cadre.getHistoryPanel().getDsd().fixenompolice(HistoryPanel.fontPrint);
+ cadre.getHistoryPanel().getDsd().fixecouleur(Color.black);
+ uc.setPenShape(PenShape.SQUARE);
+ uc.setQuality(DrawQuality.NORMAL);
+ kernel.setDrawingQuality(uc.getQuality());
+ kernel.setNumberOfTurtles(16);
+ uc.setTurtleSpeed(0);
+ Kernel.mode_trace = false;
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC;
+ cadre.getDrawPanel().zoom(1, false);
+ break;
+ case 215: // tc taillecrayon
+ Interprete.operande = true;
+ double penwidth = 2 * kernel.getActiveTurtle().getPenWidth();
+ Interprete.calcul.push(String.valueOf(MyCalculator.teste_fin_double(penwidth)));
+ break;
+ case 216: // setpenshape=ffc fixeformecrayon
+ Interprete.operande = false;
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i != PenShape.OVAL.getValue() && i != PenShape.SQUARE.getValue())
+ {
+ str = Utils.primitiveName("setpenshape") + " " + Logo.messages.getString("error_bad_values");
+ str += " " + PenShape.SQUARE.getValue() + " " + PenShape.OVAL.getValue();
+ throw new LogoError(str);
+ }
+ uc.setPenShape(PenShape.getPenShape(i));
+ cadre.getDrawPanel().updateAllTurtleShape();
+ cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon);
+ break;
+ case 217: // penshape=fc formecrayon
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(uc.getPenShape().getValue()));
+ break;
+ case 218: // setdrawingquality=fqd fixequalitedessin
+ Interprete.operande = false;
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i != DrawQuality.NORMAL.getValue() && i != DrawQuality.HIGH.getValue()
+ && i != DrawQuality.LOW.getValue())
+ {
+ str = Utils.primitiveName("setdrawingquality") + " "
+ + Logo.messages.getString("error_bad_values") + " 0 1 2";
+ throw new LogoError(str);
+ }
+ uc.setQuality(DrawQuality.getDrawQuality(i));
+ kernel.setDrawingQuality(uc.getQuality());
+ break;
+ case 219: // drawingquality=qd qualitedessin
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(uc.getQuality().getValue()));
+ break;
+ case 220: // setturtlesnumber=fmt fixemaxtortues
+ Interprete.operande = false;
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 0)
+ {
+ String fmt = Utils.primitiveName("setturtlesnumber");
+ throw new LogoError(fmt + " " + Logo.messages.getString("attend_positif"));
+ }
+ else if (i == 0)
+ i = 1;
+ kernel.setNumberOfTurtles(i);
+ break;
+ case 221: // turtlesnumber=maxtortues
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(uc.getMaxTurtles()));
+
+ break;
+ case 222: // setscreensize=ftd fixetailledessin
+ Interprete.operande = false;
+
+ String prim = Utils.primitiveName("setscreensize");
+ liste = getFinalList(param.get(0));
+ int width,
+ height;
+ st = new StringTokenizer(liste);
+ try
+ {
+ if (!st.hasMoreTokens())
+ throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ width = Integer.parseInt(st.nextToken().toString());
+ if (!st.hasMoreTokens())
+ throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ height = Integer.parseInt(st.nextToken().toString());
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ }
+ if (st.hasMoreTokens())
+ throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ boolean changement = false;
+ if (height != h)
+ changement = true;
+ int tmp_hauteur = h;
+ h = height;
+ if (width != w)
+ changement = true;
+ int tmp_largeur = w;
+ w = width;
+ if (w < 100 || h < 100)
+ {
+ w = 1000;
+ h = 1000;
+ }
+ if (changement)
+ {
+ int memoire_necessaire = w * h * 4 / 1024 / 1024;
+ int memoire_image = tmp_hauteur * tmp_largeur * 4 / 1024 / 1024;
+ long free = Runtime.getRuntime().freeMemory() / 1024 / 1024;
+ long total = Runtime.getRuntime().totalMemory() / 1024 / 1024;
+ /*
+ * System.out.println("memoire nécessaire
+ * "+memoire_necessaire); System.out.println("memoire
+ * image
+ * "+memoire_image); System.out.println("memoire
+ * libre "+free); System.out.println("memoire totale
+ * "+total); System.out.println("memoire envisagee
+ * "+(total-free+memoire_necessaire-memoire_image));
+ * System.out.println();
+ */
+ if (total - free + memoire_necessaire - memoire_image < GlobalConfig.getMaximumMemory() * 0.8)
+ {
+ cadre.resizeDrawingZone();
+ }
+ else
+ {
+ w = tmp_largeur;
+ h = tmp_hauteur;
+ long conseil = 64 * ((total - free + memoire_necessaire - memoire_image) / 64) + 64;
+ if (total - free + memoire_necessaire - memoire_image > 0.8 * conseil)
+ conseil += 64;
+ if (conseil == GlobalConfig.getMaximumMemory())
+ conseil += 64;
+ String message = Logo.messages.getString("erreur_memoire") + " " + conseil + "\n"
+ + Logo.messages.getString("relancer");
+ jt = new MyTextAreaDialog(message);
+ JOptionPane.showMessageDialog(cadre.getFrame(), jt, Logo.messages.getString("erreur"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ break;
+ case 223: // guibutton guibouton
+ String identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot = getWord(param.get(1));
+ if (null == mot)
+ throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word"));
+ GuiButton gb = new GuiButton(identifier.toLowerCase(), mot, cadre);
+ cadre.getDrawPanel().addToGuiMap(gb);
+ break;
+ case 224: // guiaction
+ identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ liste = getFinalList(param.get(1));
+ cadre.getDrawPanel().guiAction(identifier, liste);
+ break;
+ case 225: // guiremove
+ identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ cadre.getDrawPanel().guiRemove(identifier);
+ break;
+ case 226: // guiposition
+ identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ liste = getFinalList(param.get(1));
+ cadre.getDrawPanel().guiposition(identifier, liste, Utils.primitiveName("guiposition"));
+ break;
+ case 227: // guidraw
+ identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ cadre.getDrawPanel().guiDraw(identifier);
+ break;
+ case 228: // zoom
+ Interprete.operande = false;
+ d = kernel.getCalculator().numberDouble(param.get(0));
+ if (d <= 0)
+ {
+ String name = Utils.primitiveName("zoom");
+ throw new LogoError(name + " " + Logo.messages.getString("attend_positif"));
+ }
+ cadre.getDrawPanel().zoom(d, false);
+ break;
+ case 229: // grille
+ Interprete.operande = false;
+ primitive2D("grille");
+ int[] args = new int[2];
+ for (i = 0; i < 2; i++)
+ {
+ args[i] = kernel.getCalculator().getInteger(param.get(i));
+ if (args[i] < 0)
+ {
+ String grille = Utils.primitiveName("grille");
+ throw new LogoError(grille + " " + Logo.messages.getString("attend_positif"));
+ }
+ else if (args[i] == 0)
+ {
+ args[i] = 1;
+ }
+ }
+ uc.setDrawGrid(true);
+ uc.setXGrid(args[0]);
+ uc.setYGrid(args[1]);
+ cadre.getDrawPanel().videecran();
+ break;
+ case 230: // stopgrille
+ Interprete.operande = false;
+ uc.setDrawGrid(false);
+ cadre.getDrawPanel().videecran();
+ break;
+ case 231: // stopanimation
+ cadre.getDrawPanel().setAnimation(false);
+ Interprete.operande = false;
+ break;
+ case 232: // stoptrace
+ Kernel.mode_trace = false;
+ Interprete.operande = false;
+ break;
+ case 233: // guimenu
+ identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ liste = getFinalList(param.get(1));
+ GuiMenu gm = new GuiMenu(identifier.toLowerCase(), liste, cadre);
+ cadre.getDrawPanel().addToGuiMap(gm);
+ break;
+ case 234: // axis
+
+ Interprete.operande = false;
+
+ primitive2D("axis");
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 0)
+ {
+ String name = Utils.primitiveName("axis");
+ throw new LogoError(name + " " + Logo.messages.getString("attend_positif"));
+ }
+ else if (i < 25)
+ i = 25;
+ uc.setDrawXAxis(true);
+ uc.setXAxis(i);
+ uc.setDrawYAxis(true);
+ uc.setYAxis(i);
+ cadre.getDrawPanel().videecran();
+ break;
+ case 235: // xaxis
+ Interprete.operande = false;
+ primitive2D("xaxis");
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 0)
+ {
+ String name = Utils.primitiveName("xaxis");
+ throw new LogoError(name + " " + Logo.messages.getString("attend_positif"));
+ }
+ else if (i < 25)
+ i = 25;
+ uc.setDrawXAxis(true);
+ uc.setXAxis(i);
+ cadre.getDrawPanel().videecran();
+ break;
+ case 236: // yaxis
+ Interprete.operande = false;
+ primitive2D("yaxis");
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 0)
+ {
+ String name = Utils.primitiveName("yaxis");
+ throw new LogoError(name + " " + Logo.messages.getString("attend_positif"));
+ }
+ else if (i < 25)
+ i = 25;
+ uc.setDrawYAxis(true);
+ uc.setYAxis(i);
+ cadre.getDrawPanel().videecran();
+ break;
+ case 237: // stopaxis
+ uc.setDrawXAxis(false);
+ uc.setDrawYAxis(false);
+ Interprete.operande = false;
+ cadre.getDrawPanel().videecran();
+ break;
+ case 238: // bye
+ cadre.closeWindow();
+ break;
+ case 239: // var? variable?
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot = mot.toLowerCase();
+ if (wp.getGlobals().getVariables().contains(mot) || Interprete.locale.containsKey(mot)) // TODO
+ // changed
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 240: // axiscolor= couleuraxes
+ Interprete.operande = true;
+ c = new Color(uc.getAxisColor());
+ Interprete.calcul.push("[ " + c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " ] ");
+
+ break;
+ case 241: // gridcolor=couleurgrille
+ Interprete.operande = true;
+ c = new Color(uc.getGridColor());
+ Interprete.calcul.push("[ " + c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " ] ");
+ break;
+ case 242: // grid?=grille?
+ Interprete.operande = true;
+ if (uc.isDrawGrid())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 243: // xaxis?=axex?
+ Interprete.operande = true;
+ if (uc.isDrawXAxis())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 244: // yaxis?=axey?
+ Interprete.operande = true;
+ if (uc.isDrawYAxis())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 245: // setgridcolor=fcg fixecouleurgrille
+ Interprete.operande = false;
+ if (isList(param.get(0)))
+ {
+ uc.setGridColor(rgb(param.get(0), Utils.primitiveName("setgridcolor")).getRGB());
+ }
+ else
+ {
+ int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length;
+ if (coul < 0)
+ coul += DrawPanel.defaultColors.length;
+ uc.setGridColor(DrawPanel.defaultColors[coul].getRGB());
+ }
+ break;
+ case 246: // setaxiscolor=fca fixecouleuraxes
+ Interprete.operande = false;
+ if (isList(param.get(0)))
+ {
+ uc.setAxisColor(rgb(param.get(0), Utils.primitiveName("setaxiscolor")).getRGB());
+ }
+ else
+ {
+ int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length;
+ if (coul < 0)
+ coul += DrawPanel.defaultColors.length;
+ uc.setAxisColor(DrawPanel.defaultColors[coul].getRGB());
+ }
+ break;
+ case 247: // perspective
+
+ cadre.getDrawPanel().perspective();
+
+ break;
+ case 248:// rightroll=rd roulisdroite
+ delay();
+ primitive3D("3d.rightroll");
+ cadre.getDrawPanel().rightroll(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 249:// uppitch=cabre
+ delay();
+ primitive3D("3d.uppitch");
+ cadre.getDrawPanel().uppitch(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 250:// leftroll=rg roulisgauche
+ delay();
+ primitive3D("3d.leftroll");
+ cadre.getDrawPanel().rightroll(-kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 251:// downpitch=pique
+ delay();
+ primitive3D("3d.downpitch");
+ cadre.getDrawPanel().uppitch(-kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 252:// roll=roulis
+ primitive3D("3d.roll");
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().roll));
+ break;
+ case 253:// pitch=cabrement tangage
+ primitive3D("3d.pitch");
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().pitch));
+ break;
+ case 254:// setroll=fixeroulis
+ primitive3D("3d.setroll");
+ delay();
+ cadre.getDrawPanel().setRoll(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 255:// setpitch=fixetangage
+ primitive3D("3d.setpitch");
+ delay();
+ cadre.getDrawPanel().setPitch(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 256:// setorientation=fixeorientation
+ primitive3D("3d.setorientation");
+ delay();
+ cadre.getDrawPanel().setOrientation(getFinalList(param.pop()));
+ break;
+ case 257: // orientation=orientation
+ primitive3D("3d.orientation");
+ Interprete.operande = true;
+ String pitch = MyCalculator.teste_fin_double(kernel.getActiveTurtle().pitch);
+ String roll = MyCalculator.teste_fin_double(kernel.getActiveTurtle().roll);
+ String heading = MyCalculator.teste_fin_double(kernel.getActiveTurtle().heading);
+ Interprete.calcul.push("[ " + roll + " " + pitch + " " + heading + " ] ");
+ break;
+ case 258: // setxyz=fposxyz
+ primitive3D("3d.setxyz");
+ cadre.getDrawPanel().fpos(
+ kernel.getCalculator().numberDouble(param.get(0)) + " "
+ + kernel.getCalculator().numberDouble(param.get(1)) + " "
+ + kernel.getCalculator().numberDouble(param.get(2)));
+ break;
+ case 259: // setz=fixez
+ delay();
+ primitive3D("3d.setz");
+ cadre.getDrawPanel().fpos(
+ kernel.getActiveTurtle().X + " " + kernel.getActiveTurtle().Y + " "
+ + kernel.getCalculator().numberDouble(param.get(0)));
+ break;
+ case 260: // pprop=dprop
+ Interprete.operande = false;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot2 = getWord(param.get(1));
+ if (null == mot2)
+ throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word"));
+ wp.getPropertyLists().addPropList(mot, mot2, param.get(2)); // TODO
+ // changed
+ break;
+ case 261: // gprop=rprop
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot2 = getWord(param.get(1));
+ if (null == mot2)
+ throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word"));
+ value = wp.getPropertyLists().getPropList(mot, mot2); // TODO
+ // CHANGED
+ if (value.startsWith("["))
+ value += " ";
+ Interprete.calcul.push(value);
+ break;
+ case 262: // remprop=efprop
+ Interprete.operande = false;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot2 = getWord(param.get(1));
+ if (null == mot2)
+ throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word"));
+ wp.getPropertyLists().removePropList(mot, mot2); // TODO
+ // CHANGED
+ break;
+ case 263: // plist=lprop
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ Interprete.calcul.push(wp.getPropertyLists().displayPropList(mot)); // TODO
+ // CHANGED
+
+ break;
+ case 264: // polystart=polydef
+ DrawPanel.record3D = DrawPanel.record3D_POLYGON;
+ cadre.initViewer3D();
+ // if (null==DrawPanel.listPoly) DrawPanel.listPoly=new
+ // java.util.Vector<Shape3D>();
+ DrawPanel.poly = new ElementPolygon(cadre.getViewer3D());
+ break;
+ case 265: // polyend=polyfin
+ DrawPanel.record3D = DrawPanel.record3D_NONE;
+ DrawPanel.poly.addToScene();
+ break;
+ case 266: // polyview=polyaf vue3d
+ primitive3D("3d.polyview");
+ cadre.viewerOpen();
+ break;
+ case 267: // linestart=lignedef
+ DrawPanel.record3D = DrawPanel.record3D_LINE;
+ cadre.initViewer3D();
+ // if (null==DrawPanel.listPoly) DrawPanel.listPoly=new
+ // java.util.Vector<Shape3D>();
+ DrawPanel.poly = new ElementLine(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth());
+ DrawPanel.poly.addVertex(new Point3d(kernel.getActiveTurtle().X / 1000,
+ kernel.getActiveTurtle().Y / 1000, kernel.getActiveTurtle().Z / 1000), kernel
+ .getActiveTurtle().couleurcrayon);
+ break;
+ case 268: // lineend=lignefin
+ DrawPanel.record3D = DrawPanel.record3D_NONE;
+ DrawPanel.poly.addToScene();
+ break;
+ case 269: // pointstart=pointdef
+ DrawPanel.record3D = DrawPanel.record3D_POINT;
+ cadre.initViewer3D();
+ // if (null==DrawPanel.listPoly) DrawPanel.listPoly=new
+ // java.util.Vector<Shape3D>();
+ DrawPanel.poly = new ElementPoint(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth());
+ break;
+ case 270: // pointend=pointfin
+ DrawPanel.record3D = DrawPanel.record3D_NONE;
+ DrawPanel.poly.addToScene();
+ break;
+ case 271: // textstart=textedef
+ DrawPanel.record3D = DrawPanel.record3D_TEXT;
+ cadre.initViewer3D();
+ // if (null==DrawPanel.listText) DrawPanel.listText=new
+ // java.util.Vector<TransformGroup>();
+ DrawPanel.poly = null;
+ break;
+ case 272: // textend=textefin
+ DrawPanel.record3D = DrawPanel.record3D_NONE;
+ break;
+ case 273: // operator <=
+ infequal(param);
+ break;
+ case 274: // operator >=
+ supequal(param);
+ break;
+ case 275: // primitives
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.primitive.getAllPrimitives());
+ break;
+ case 276: // listesproprietes propertylists
+ Interprete.operande = true;
+ Interprete.calcul.push(new String(getAllpropertyLists()));
+ break;
+ case 277: // contenu
+ Interprete.operande = true;
+ sb = new StringBuffer("[ ");
+ sb.append(this.getAllProcedures());
+ sb.append(this.getAllVariables());
+ sb.append(this.getAllpropertyLists());
+ sb.append("] ");
+ Interprete.calcul.push(new String(sb));
+ break;
+ case 278: // erpl=eflp effacelistepropriete
+ Interprete.operande = false;
+ this.erase(param.get(0), "propertylist");
+ break;
+ case 279: // arithmetic.exp
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().exp(param.get(0)));
+ break;
+ case 280: // arithmetic.log
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().log(param.get(0)));
+ break;
+ case 281: // controls.ifelse
+ liste = getList(param.get(1));
+ liste = new String(Utils.decoupe(liste));
+ pred = predicat(param.get(0));
+ liste2 = getList(param.get(2));
+ liste = new String(Utils.decoupe(liste));
+ kernel.primitive.si(pred, liste, liste2);
+ Interprete.renvoi_instruction = true;
+ break;
+ case 282: // workspace.ed
+
+ /*
+ * Marko Zivkovic:
+ * With the new Workspace (UserSpace + LogoFile) ed makes
+ * not so much sense, because we work with
+ * multiple files at the same time.
+ * I therefore restrict this command to allow only a single
+ * word as parameter, to open the file,
+ * where the specified procedure is defined, and scroll to
+ * the procedure's line.
+ */
+
+ mot = this.getWord(param.get(0));
+ if (mot == null)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ if (!wp.isExecutable(mot))
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.proc.does.not.exist"));
+
+ cadre.displayProcedure(mot);
+ break;
+ case 283: // workspace.edall
+ /*
+ * Marko Zivkovic:
+ * Again, it makes no sense to open all files at a time.
+ * Instead, I just open the last edited file.
+ */
+ wp.editAll();
+ break;
+ case 284: // controls.foreach pourchaque
+ // Variable name
+ var = getWord(param.get(0));
+ // If it isn't a word
+ if (null == var)
+ throw new LogoError(param.get(0).toString() + " " + Logo.messages.getString("error.word"));
+ // If it's a number
+ else
+ {
+ try
+ {
+ Double.parseDouble(var);
+ throw new LogoError(Logo.messages.getString("erreur_nom_nombre_variable"));
+ }
+ catch (NumberFormatException e1)
+ {}
+ }
+ li2 = getList(param.get(2));
+ li2 = new String(Utils.decoupe(li2));
+ li1 = getWord(param.get(1));
+ boolean isList = false;
+ if (null == li1)
+ {
+ isList = true;
+ li1 = getFinalList(param.get(1));
+ }
+ Vector<String> elements = new Vector<String>();
+ while (!li1.equals(""))
+ {
+ String character = "";
+ // If it's a list
+ if (isList)
+ {
+ character = this.item(li1, 1);
+ // If it's a number
+ try
+ {
+ // Fix Bug: foreach "i [1 2 3][pr :i]
+ // character=1 , 2 , 3 (without quote)
+ Double.parseDouble(character);
+ li1 = li1.substring(character.length() + 1);
+ }
+ catch (NumberFormatException e)
+ {
+ // Fix Bug: foreach "i [r s t][pr :i]
+ // character="r , "s or "t
+ li1 = li1.substring(character.length());
+ }
+ }
+ // If it's a word
+ else
+ {
+ character = this.itemWord(1, li1);
+ li1 = li1.substring(character.length());
+ // If it isn't a number, adding a quote
+ try
+ {
+ Double.parseDouble(character);
+ }
+ catch (NumberFormatException e)
+ {
+ character = "\"" + character;
+ }
+ }
+
+ elements.add(character);
+ }
+ if (elements.size() > 0)
+ {
+ LoopForEach lfe = new LoopForEach(BigDecimal.ZERO, new BigDecimal(elements.size() - 1),
+ BigDecimal.ONE, li2, var.toLowerCase(), elements);
+ lfe.AffecteVar(true);
+ cadre.getKernel().getInstructionBuffer().insert(li2 + Primitive.END_LOOP + " ");
+ Primitive.stackLoop.push(lfe);
+ }
+ break;
+ case 285: // controls.forever repetetoujours
+ li2 = getList(param.get(0));
+ li2 = new String(Utils.decoupe(li2));
+ LoopProperties lp = new LoopProperties(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, li2);
+ cadre.getKernel().getInstructionBuffer().insert(li2 + Primitive.END_LOOP + " ");
+ Primitive.stackLoop.push(lp);
+ break;
+ case 286: // arithmetic.setdigits
+ Interprete.operande = false;
+ kernel.initCalculator(kernel.getCalculator().getInteger(param.get(0)));
+ break;
+ case 287: // arithmetic.digits
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(kernel.getCalculator().getDigits()));
+ break;
+ case 288: // workspace.text
+ var = getWord(param.get(0));
+ if (null == var)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+
+ Procedure proc = wp.getExecutable(var);
+ if (proc != null)
+ {
+ sb = new StringBuffer();
+ sb.append("[ [ ");
+ // Append variable names
+ for (int j = 0; j < proc.nbparametre; j++)
+ {
+ sb.append(proc.variable.get(j));
+ sb.append(" ");
+ }
+ for (int j = 0; j < proc.optVariables.size(); j++)
+ {
+ sb.append("[ ");
+ sb.append(proc.optVariables.get(j));
+ sb.append(" ");
+ sb.append(proc.optVariablesExp.get(j).toString());
+ sb.append(" ] ");
+ }
+ sb.append("] ");
+ // Append body procedure
+ sb.append(proc.cutInList());
+ sb.append("] ");
+ Interprete.operande = true;
+ Interprete.calcul.push(sb.toString());
+ }
+ else
+ throw new LogoError(var + " " + Logo.messages.getString("error.procedure.must.be"));
+ break;
+ case 289: // workspace.externalcommand
+ Interprete.operande = false;
+
+ {
+ list = getFinalList(param.get(0));
+ int index = numberOfElements(list);
+ String[] cmd = new String[index];
+ for (i = 0; i < index; i++)
+ {
+ String liste1 = item(list, i + 1);
+ cmd[i] = Utils.SortieTexte(getFinalList(liste1).trim());
+ }
+ try
+ {
+ /*
+ * String com="";
+ * for(int i=0;i<cmd.length;i++){
+ * com+=cmd[i]+" ";
+ * }
+ * System.out.println(com);
+ */
+ Runtime.getRuntime().exec(cmd);
+ }
+ catch (IOException e2)
+ {
+ // System.out.println("a");
+ }
+
+ }
+
+ {
+ // System.out.println("coucou");
+ }
+ break;
+ case 290: // drawing.saveimage
+ String word = getWord(param.get(0));
+ if (null == word)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ if (word.equals(""))
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("mot_vide"));
+ // xmin, ymin, width, height
+ int[] coord = new int[4];
+ list = getFinalList(param.get(1));
+ st = new StringTokenizer(list);
+ if (st.countTokens() == 4)
+ {
+ try
+ {
+ int j = 0;
+ while (st.hasMoreTokens())
+ {
+ coord[j] = Integer.parseInt(st.nextToken());
+ j++;
+ }
+ coord[0] += w / 2;
+ coord[2] += w / 2;
+ coord[1] = h / 2 - coord[1];
+ coord[3] = h / 2 - coord[3];
+ if (coord[2] < coord[0])
+ {
+ int tmp = coord[0];
+ coord[0] = coord[2];
+ coord[2] = tmp;
+ }
+ if (coord[3] < coord[1])
+ {
+ int tmp = coord[1];
+ coord[1] = coord[3];
+ coord[3] = tmp;
+ }
+ coord[2] = coord[2] - coord[0];
+ coord[3] = coord[3] - coord[1];
+ }
+ catch (NumberFormatException e)
+ {
+ coord[0] = 0;
+ coord[2] = w;
+ coord[1] = 0;
+ coord[3] = h;
+ }
+ }
+ else
+ {
+ coord[0] = 0;
+ coord[2] = w;
+ coord[1] = 0;
+ coord[3] = h;
+ }
+ if (coord[2] == 0 || coord[3] == 0)
+ {
+ coord[0] = 0;
+ coord[2] = w;
+ coord[1] = 0;
+ coord[3] = h;
+ }
+ cadre.getDrawPanel().saveImage(word, coord);
+ Interprete.operande = false;
+ break;
+ case 291: // sound.mp3play
+ Interprete.operande = false;
+ if (kernel.getMp3Player() != null)
+ kernel.getMp3Player().getPlayer().close();
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(mot + " " + Logo.messages.getString("error.word"));
+ MP3Player player = new MP3Player(cadre, mot);
+ kernel.setMp3Player(player);
+ kernel.getMp3Player().start();
+ break;
+ case 292: // sound.mp3stop
+ Interprete.operande = false;
+ if (null != kernel.getMp3Player())
+ kernel.getMp3Player().getPlayer().close();
+ break;
+ case 293: // zoom
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(DrawPanel.zoom));
+ break;
+ case 294: // drawing.x
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().getX()));
+ break;
+ case 295:// drawing.y
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().getY()));
+ break;
+ case 296: // drawing.z
+ Interprete.operande = true;
+ primitive3D("drawing.z");
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().Z));
+ break;
+ case 297: // drawing.fillpolygon
+ Interprete.operande = false;
+ list = getFinalList(param.get(0));
+ LoopFillPolygon lfp = new LoopFillPolygon();
+ Primitive.stackLoop.push(lfp);
+ cadre.getKernel().getInstructionBuffer().insert(Utils.decoupe(list) + Primitive.END_LOOP + " ");
+ cadre.getDrawPanel().startRecord2DPolygon();
+ break;
+ case 298: // arithmetic.alea
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(Math.random()));
+ break;
+ case 299: // loop.dountil
+ li1 = getList(param.get(0));
+ li1 = new String(Utils.decoupe(li1));
+ li2 = getList(param.get(1));
+ li2 = new String(Utils.decoupe(li2));
+ instr = "\\siwhile " + Utils.primitiveName("non") + " " + li2 + "[ " + li1 + "] ";
+ LoopWhile lw = new LoopWhile(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, instr);
+ Primitive.stackLoop.push(lw);
+ cadre.getKernel().getInstructionBuffer().insert(instr + Primitive.END_LOOP + " ");
+ break;
+ case 300: // loop.dowhile
+ li1 = getList(param.get(0));
+ li1 = new String(Utils.decoupe(li1));
+ li2 = getList(param.get(1));
+ li2 = new String(Utils.decoupe(li2));
+ instr = "\\siwhile " + li2 + "[ " + li1 + "] ";
+ lw = new LoopWhile(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, instr);
+ Primitive.stackLoop.push(lw);
+ cadre.getKernel().getInstructionBuffer().insert(li1 + instr + Primitive.END_LOOP + " ");
+ break;
+ case 301: // arithmetic.modulo
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().modulo(param.get(0), param.get(1)));
+ break;
+ case 302: // drawing.setfontjustify
+ Interprete.operande = false;
+ li1 = getFinalList(param.get(0));
+ kernel.getActiveTurtle().setFontJustify(li1);
+ break;
+ case 303: // drawing.fontjustify
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getActiveTurtle().getFontJustify());
+ break;
+ case 304: // arithmetic.inf
+ inf(param);
+ break;
+ case 305: // arithmetic.sup
+ sup(param);
+ break;
+ case 306: // arithmetic.infequal
+ infequal(param);
+ break;
+ case 307: // arithmetic.supequal
+ supequal(param);
+ break;
+
+ }
+ }
+ }
+
+ /**
+ * This implementation of the Logo command "define" is slightly changed towards the original XLogo
+ * implementation.
+ * In {@link xlogo.gui.Editor#analyseprocedure()} (which is now {@link LogoFile#setText(String)}),
+ * I added the line statement {@code wp.deleteAllProcedures()}
+ * because we don't want the procedures to
+ * be ordered by definition time in the editor.
+ * We want the programmer to have the procedures in the editor exactly in
+ * the same order as the programmer writes them.
+ *
+ * @param params
+ * @author Marko Zivkovic
+ * @throws LogoError
+ */
+ private void define(Stack<String> params) throws LogoError
+ {
+ String procedureName;
+ String procedureText = null;
+ procedureName = getWord(params.get(0));
+ if (null == procedureName)
+ throw new LogoError(params.get(0) + " " + Logo.messages.getString("error.word"));
+ if (procedureName.equals(""))
+ throw new LogoError(Logo.messages.getString("procedure_vide"));
+ String list = getFinalList(params.get(1));
+ StringBuffer sb = new StringBuffer();
+ for (int i = 1; i <= numberOfElements(list); i++)
+ {
+ String liste1 = item(list, i);
+ liste1 = getFinalList(liste1);
+
+ // First line
+ if (i == 1)
+ {
+ StringTokenizer st = new StringTokenizer(liste1);
+ sb.append(Logo.messages.getString("pour"));
+ sb.append(" ");
+ sb.append(procedureName);
+ sb.append(" ");
+
+ while (st.hasMoreTokens())
+ {
+ // Optional variables
+ String token = st.nextToken();
+ if (token.equals("["))
+ {
+ sb.append("[ :");
+ while (st.hasMoreTokens())
+ {
+ token = st.nextToken();
+ if (token.equals("]"))
+ {
+ sb.append("] ");
+ break;
+ }
+ else
+ {
+ sb.append(token);
+ sb.append(" ");
+ }
+ }
+ }
+ else
+ {
+ sb.append(":");
+ sb.append(token);
+ sb.append(" ");
+ }
+ }
+ }
+ // Body of the procedure
+ else if (i > 1)
+ {
+ sb.append("\n");
+ sb.append(liste1);
+ }
+ }
+ sb.append("\n");
+ sb.append(Logo.messages.getString("fin"));
+
+ /*
+ * Marko Zivkovic:
+ * In XLogo4Schools, procedures are not created by setting the text
+ * of the editor,
+ * we directly define them in LogoFiles.
+ * Because we have multiple files, the defined procedure will be put
+ * in a special file for programatically defined procedures.
+ * If the procedure name was already defined, then it will be
+ * redefined, as before in XLogo.
+ * If there is an ambiguity with the procedure name, an error will
+ * be displayed "I don't know which one to redefine..."
+ */
+
+ if (wp.isProcedureAmbiguous(procedureName))
+ throw new LogoError(procedureName + " " + Logo.messages.getString("ws.redefine.ambiguous.procedure"));
+ // procedureText is generated above => it is executable
+ procedureText = "\n" + new String(sb);
+ Procedure procedure = new Procedure(procedureText);
+
+ Procedure other = wp.getExecutable(procedureName);
+ String fileName = other != null ? other.getOwnerName() : Logo.messages.getString("ws.generated.procedure");
+ procedure.setOwnerName(fileName);
+
+ try
+ {
+ wp.defineProcedure(procedure);
+ }
+ catch (IOException e)
+ {
+ throw new LogoError(Logo.messages.getString("ws.could.not.create.file") + " " + fileName);
+ }
+ }
+
+ /**
+ * This method tests if the primitive name exist in 2D mode
+ *
+ * @param name
+ * The primitive name
+ * @throws LogoError
+ */
+ private void primitive2D(String name) throws LogoError
+ {
+ if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_3D)
+ throw new LogoError(Utils.primitiveName(name) + " " + Logo.messages.getString("error.primitive2D"));
+ }
+
+ /**
+ * This method tests if the primitive name exist in 2D mode
+ *
+ * @param name
+ * The primitive name
+ * @throws LogoError
+ */
+ private void primitive3D(String name) throws LogoError
+ {
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ throw new LogoError(Utils.primitiveName(name) + " " + Logo.messages.getString("error.primitive3D"));
+ }
+
+ /**
+ * Returns the code [r g b] for the color i
+ *
+ * @param i
+ * Integer representing the Color
+ */
+ private void colorCode(int i)
+ {
+ Interprete.operande = true;
+ Color co = DrawPanel.defaultColors[i];
+ Interprete.calcul.push("[ " + co.getRed() + " " + co.getGreen() + " " + co.getBlue() + " ] ");
+ }
+
+ /**
+ * Save all procedures whose name are contained in the Stack pile
+ *
+ * @param fichier
+ * The patch to the saved file
+ * @param pile
+ * Stack Stack containing all procedure names
+ */
+ private void saveProcedures(String fichier, Stack<String> pile)
+ {
+ try
+ {
+ String aecrire = "";
+ boolean bool = true;
+ if (!fichier.endsWith(".lgo"))
+ fichier += ".lgo";
+ String path = Utils.SortieTexte(WSManager.getUserConfig().getDefaultFolder()) + File.separator + fichier;
+ try
+ {
+ // TODO CHANGED
+ for (Procedure procedure : wp.getExecutables())
+ {
+ if (null == pile)
+ bool = true;
+ else
+ bool = (pile.search(procedure.name) != -1);
+ if (bool)
+ {
+ aecrire += Logo.messages.getString("pour") + " " + procedure.name;
+ for (int j = 0; j < procedure.nbparametre; j++)
+ {
+ aecrire += " :" + procedure.variable.get(j);
+ }
+ aecrire += "\n" + procedure.instruction + Logo.messages.getString("fin") + "\n\n";
+ }
+ }
+ }
+ catch (NullPointerException ex)
+ {} // Si aucune procédure n'a été définie.
+ Utils.writeLogoFile(path, aecrire);
+ }
+ catch (IOException e2)
+ {
+ HistoryMessenger.getInstance().dispatchError(Logo.messages.getString("error.ioecriture"));
+ }
+ }
+
+ /**
+ * Returns the Image defined by the path "chemin"
+ *
+ * @param chemin
+ * The absolute path for the image
+ * @return BufferedImage defined by the path "chemin"
+ * @throws LogoError
+ * If Image format isn't valid(jpg or png)
+ */
+ private BufferedImage getImage(String path) throws LogoError
+ {
+ BufferedImage image = null;
+ String pathWord = getWord(path);
+ if (null == pathWord)
+ throw new LogoError(path + " " + Logo.messages.getString("error.word"));
+ if (!(pathWord.endsWith(".png") || pathWord.endsWith(".jpg")))
+ throw new LogoError(Logo.messages.getString("erreur_format_image"));
+ else
+ {
+ try
+ {
+ pathWord = Utils.SortieTexte(pathWord);
+ File f = new File(Utils.SortieTexte(WSManager.getUserConfig().getDefaultFolder()) + File.separator
+ + pathWord);
+ image = ImageIO.read(f);
+ }
+ catch (Exception e1)
+ {
+ throw new LogoError(Logo.messages.getString("error.iolecture"));
+ }
+ }
+ return image;
+ }
+
+ /**
+ * Create a local variable called "mot" with no value.
+ *
+ * @param mot
+ * Variable name
+ */
+ private void createLocaleName(String mot)
+ {
+ mot = mot.toLowerCase();
+ if (!Interprete.locale.containsKey(mot))
+ {
+ Interprete.locale.put(mot, null);
+ }
+ }
+
+ /**
+ * Create a new local variable
+ *
+ * @param param
+ * The variable name or a list of variable names
+ * @throws LogoError
+ * If "param" isn't a list containing all variable names, or a
+ * word
+ */
+
+ private void locale(Stack<String> param) throws LogoError
+ {
+ String li = param.get(0);
+ if (LaunchPrimitive.isList(li))
+ {
+ li = getFinalList(li);
+ StringTokenizer st = new StringTokenizer(li);
+ while (st.hasMoreTokens())
+ {
+ String item = st.nextToken();
+ isVariableName(item);
+ createLocaleName(item);
+ }
+ }
+ else
+ {
+ String mot = getWord(param.get(0));
+ if (null != mot)
+ {
+ createLocaleName(mot);
+ }
+ else
+ throw new LogoError(param.get(0) + Logo.messages.getString("error.word"));
+ }
+ }
+
+ /**
+ * returns the color defined by [r g b] contained in "ob"
+ *
+ * @param obj
+ * the list [r g b]
+ * @param name
+ * The name of the calling primitive
+ * @return The Object Color
+ * @throws LogoError
+ * If the list doesn't contain 3 numbers
+ */
+
+ private Color rgb(String obj, String name) throws LogoError
+ {
+ String liste = getFinalList(obj);
+ StringTokenizer st = new StringTokenizer(liste);
+ if (st.countTokens() != 3)
+ throw new LogoError(name + " " + Logo.messages.getString("color_3_arguments"));
+ int[] entier = new int[3];
+ for (int i = 0; i < 3; i++)
+ {
+ String element = st.nextToken();
+ try
+ {
+ entier[i] = (int) (Double.parseDouble(element) + 0.5);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(element + " " + Logo.messages.getString("pas_nombre"));
+ }
+ if (entier[i] < 0)
+ entier[i] = 0;
+ if (entier[i] > 255)
+ entier[i] = 255;
+
+ }
+ return (new Color(entier[0], entier[1], entier[2]));
+ }
+
+ /**
+ * Primitive member or member?
+ *
+ * @param param
+ * Stack that contains arguments for the primitive member
+ * @param id
+ * 69 --> member? or 70--> member
+ * @throws LogoError
+ * Incorrect arguments
+ */
+ private void membre(Stack<String> param, int id) throws LogoError
+ {
+ Interprete.operande = true;
+ String mot_retourne = null;
+ boolean b = false;
+ String mot = getWord(param.get(1));
+ String liste = "[ ";
+ if (null == mot)
+ { // on travaille sur une liste
+
+ liste = getFinalList(param.get(1));
+ StringTokenizer st = new StringTokenizer(liste);
+ liste = "[ ";
+ mot = getWord(param.get(0));
+ String str;
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ mot = param.get(0).trim();
+ while (st.hasMoreTokens())
+ {
+ str = st.nextToken();
+ if (str.equals("["))
+ str = extractList(st);
+ if (!liste.equals("[ "))
+ liste += str + " ";
+ if (str.equals(mot) && liste.equals("[ "))
+ {
+ if (id == 69)
+ {
+ b = true;
+ break;
+ }
+ else
+ liste += str + " ";
+ }
+ }
+ }
+ else
+ { // on travaille sur un mot
+ String mot2 = getWord(param.get(0));
+ if (null != mot2)
+ {
+ boolean backslash = false;
+ for (int i = 0; i < mot.length(); i++)
+ {
+ char c = mot.charAt(i);
+ if (!backslash && c == '\\')
+ backslash = true;
+ else
+ {
+ String tmp = Character.toString(c);
+ if (backslash)
+ tmp = "\\" + tmp;
+ if (tmp.equals(mot2))
+ {
+ if (id == 69)
+ {
+ b = true;
+ break;
+ }
+ else
+ {
+ if (!backslash)
+ mot_retourne = mot.substring(i, mot.length());
+ else
+ mot_retourne = mot.substring(i - 1, mot.length());
+ break;
+ }
+ }
+ backslash = false;
+ }
+ }
+ }
+ }
+ if (!liste.equals("[ "))
+ Interprete.calcul.push(liste + "] ");
+ else if (null != mot_retourne)
+ {
+ try
+ {
+ Double.parseDouble(mot_retourne);
+ Interprete.calcul.push(mot_retourne);
+ }
+ catch (NumberFormatException e)
+ {
+ Interprete.calcul.push(debut_chaine + mot_retourne);
+ }
+ }
+ else if (b)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+
+ /**
+ * Primitive before?
+ *
+ * @param param
+ * Stack that contains all arguments
+ * @throws LogoError
+ * Bad argument type
+ */
+
+ private void precede(Stack<String> param) throws LogoError
+ {
+ Interprete.operande = true;
+ boolean b = false;
+ String ope[] = { "", "" };
+ String mot = "";
+ for (int i = 0; i < 2; i++)
+ {
+ mot = getWord(param.get(i));
+ if (null == mot)
+ throw new LogoError(param.get(i) + " " + Logo.messages.getString("pas_mot"));
+ else
+ ope[i] = mot;
+ }
+ if (ope[1].compareTo(ope[0]) > 0)
+ b = true;
+ if (b)
+ mot = Logo.messages.getString("vrai");
+ else
+ mot = Logo.messages.getString("faux");
+ Interprete.calcul.push(mot);
+ }
+
+ private void infequal(Stack<String> param) throws LogoError
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().infequal(param));
+ }
+
+ private void supequal(Stack<String> param) throws LogoError
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().supequal(param));
+ }
+
+ private void inf(Stack<String> param) throws LogoError
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().inf(param));
+ }
+
+ private void sup(Stack<String> param) throws LogoError
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().sup(param));
+ }
+
+ /**
+ * / Primitive equal?
+ *
+ * @param param
+ * Stack that contains all arguments
+ * @throws LogoError
+ */
+ private void equal(Stack<String> param) throws LogoError
+ {
+ try
+ {
+ Double.parseDouble(param.get(0));
+ Double.parseDouble(param.get(1));
+ Interprete.calcul.push(kernel.getCalculator().equal(param));
+ }
+ catch (NumberFormatException e)
+ {
+ if (param.get(0).toString().equals(param.get(1).toString()))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+ Interprete.operande = true;
+ }
+
+ /**
+ * this method returns the boolean corresponding to the string st
+ *
+ * @param st
+ * true or false
+ * @return The boolean corresponding to the string st
+ * @throws LogoError
+ * If st isn't equal to true or false
+ */
+
+ private boolean predicat(String st) throws LogoError
+ {
+ if (st.toLowerCase().equals(Logo.messages.getString("vrai")))
+ return true;
+ else if (st.toLowerCase().equals(Logo.messages.getString("faux")))
+ return false;
+ else
+ throw new LogoError(st + " " + Logo.messages.getString("pas_predicat"));
+
+ }
+
+ /**
+ * Returns the word contained in st. If it isn't a word, returns null
+ *
+ * @param st
+ * The Object to convert
+ * @return The word corresponding to st
+ */
+ private String getWord(Object st)
+ { // Si c'est un mot
+ String liste = st.toString();
+ if (liste.equals("\""))
+ {
+ debut_chaine = "";
+ return "";
+ }
+ if (liste.length() > 0 && liste.substring(0, 1).equals("\""))
+ {
+ debut_chaine = "\"";
+ return (liste.substring(1, liste.length()));
+ }
+ else
+ try
+ {
+ if (liste == String.valueOf(Double.parseDouble(liste)))
+ debut_chaine = "";
+ else
+ debut_chaine = "\"";
+ return Utils.SortieTexte(liste);
+ }
+ catch (NumberFormatException e)
+ {}
+ return (null);
+ }
+
+ /**
+ * Returns the list contained in the string li without any lineNumber
+ *
+ * @param li
+ * The String corresponding to the list
+ * @return A list without any line Number tag (\0, \1, \2 ...)
+ * @throws LogoError
+ * List bad format
+ */
+
+ private String getFinalList(String li) throws LogoError
+ {
+ // remove line number
+ li = li.replaceAll("\\\\l([0-9])+ ", "");
+ // return list
+ return getList(li);
+ }
+
+ /**
+ * Returns the list contained in the string li
+ *
+ * @param li
+ * The String corresponding to the list
+ * @return A list with line Number tag (\0, \1, \2 ...)
+ * @throws LogoError
+ * List bad format
+ */
+ private String getList(String li) throws LogoError
+ {
+ li = li.trim();
+ // Retourne la liste sans crochets;
+ if (li.substring(0, 1).equals("[") && li.substring(li.length() - 1, li.length()).equals("]"))
+ {
+ li = li.substring(1, li.length() - 1).trim() + " ";
+ if (!li.equals(" "))
+ return li;
+ else
+ return ("");
+ }
+ else
+ throw new LogoError(li + " " + Logo.messages.getString("pas_liste"));
+ }
+
+ /**
+ * Tests if "li" is a list
+ *
+ * @param li
+ * The String to test
+ * @return true if it is a list, else false
+ */
+ //
+ protected static boolean isList(String li)
+ {
+ li = li.trim();
+ if (li.length() > 0 && li.substring(0, 1).equals("[") && li.substring(li.length() - 1, li.length()).equals("]"))
+ return (true);
+ return false;
+ }
+
+ // Format the List (only one space between two elements)
+ private String formatList(String li)
+ {
+ String tampon = "";
+ String precedent = "";
+ StringTokenizer st = new StringTokenizer(li, " []", true);
+ String element = "";
+ while (st.hasMoreTokens())
+ {
+ element = st.nextToken();
+ while (st.hasMoreTokens() && element.equals(" "))
+ {
+ element = st.nextToken();
+ }
+ if (element.equals("]"))
+ tampon = tampon.trim() + "] ";
+ else if (element.equals("["))
+ {
+ if (precedent.equals("["))
+ tampon += "[";
+ else
+ tampon = tampon.trim() + " [";
+ }
+ else
+ tampon += element + " ";
+ precedent = element;
+ }
+ return (tampon.trim());
+ }
+
+ private String extractList(StringTokenizer st)
+ {
+ int compteur = 1;
+ String crochet = "[ ";
+ String element = "";
+ while (st.hasMoreTokens())
+ {
+ element = st.nextToken();
+ if (element.equals("["))
+ {
+ compteur++;
+ crochet += "[ ";
+ }
+ else if (!element.equals("]"))
+ crochet += element + " ";
+ else if (compteur != 1)
+ {
+ compteur--;
+ crochet += "] ";
+ }
+ else
+ {
+ crochet += element + " ";
+ break;
+ }
+ }
+ element = crochet;
+ compteur = 0;
+ return element.trim();
+ }
+
+ private int extractList(String st, int deb)
+ {
+ int compteur = 1;
+ char element;
+ boolean espace = true;
+ boolean crochet_ouvert = false;
+ boolean crochet_ferme = false;
+ for (int i = deb; i < st.length(); i++)
+ {
+ element = st.charAt(i);
+ if (element == '[')
+ {
+ if (espace)
+ crochet_ouvert = true;
+ espace = false;
+ crochet_ferme = false;
+ }
+ else if (element == ']')
+ {
+ if (espace)
+ crochet_ferme = true;
+ espace = false;
+ crochet_ouvert = false;
+ }
+ else if (element == ' ')
+ {
+ espace = true;
+ if (crochet_ouvert)
+ {
+ compteur++;
+ crochet_ouvert = false;
+ }
+ else if (crochet_ferme)
+ {
+ crochet_ferme = false;
+ if (compteur != 1)
+ compteur--;
+ else
+ {
+ compteur = i;
+ break;
+ }
+ }
+ }
+ }
+ return compteur;
+ }
+
+ // returns how many elements contains the list "liste"
+ private int numberOfElements(String liste)
+ { // calcule le nombre
+ // d'éléments dans une
+ // liste
+ StringTokenizer st = new StringTokenizer(liste);
+ int i = 0;
+ String element = "";
+ while (st.hasMoreTokens())
+ {
+ element = st.nextToken();
+ if (element.equals("["))
+ element = extractList(st);
+ i++;
+ }
+ return i;
+ }
+
+ // returns the item "i" from the list "liste"
+ private String item(String liste, int i) throws LogoError
+ { // retourne
+ // l'élément i d'une
+ // liste
+ StringTokenizer st = new StringTokenizer(liste);
+ String element = "";
+ int j = 0;
+ while (st.hasMoreTokens())
+ {
+ j++;
+ element = st.nextToken();
+ if (element.equals("["))
+ element = extractList(st);
+ if (j == i)
+ break;
+ }
+ if (j != i)
+ throw new LogoError(Logo.messages.getString("y_a_pas") + " " + i + " "
+ + Logo.messages.getString("element_dans_liste") + liste + "]");
+ else if (i == 0 && j == 0)
+ throw new LogoError(Logo.messages.getString("liste_vide"));
+ try
+ {
+ Double.parseDouble(element);
+ return element;
+ } // Si c'est un nombre, on le renvoie.
+ catch (Exception e)
+ {}
+ if (element.startsWith("["))
+ return element + " "; // C'est une liste, on la renvoie telle
+ // quelle.
+ if (element.equals("\\v"))
+ element = "";
+ return "\"" + element; // C'est forcément un mot, on le renvoie.
+ }
+
+ // Test if the name of the variable is valid
+ private void isVariableName(String st) throws LogoError
+ {
+ if (st.equals(""))
+ throw new LogoError(Logo.messages.getString("variable_vide"));
+ if (":+-*/() []=<>&|".indexOf(st) > -1)
+ throw new LogoError(st + " " + Logo.messages.getString("erreur_variable"));
+
+ try
+ {
+ Double.parseDouble(st);
+ throw new LogoError(Logo.messages.getString("erreur_nom_nombre_variable"));
+ }
+ catch (NumberFormatException e)
+ {
+
+ }
+
+ }
+
+ // primitve make
+ private void donne(Stack<String> param) throws LogoError
+ {
+ String mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot = mot.toLowerCase();
+ isVariableName(mot);
+ if (Interprete.locale.containsKey(mot))
+ {
+ Interprete.locale.put(mot, param.get(1));
+ }
+ else
+ {
+ wp.getGlobals().define(mot, param.get(1)); // TODO changed
+ }
+ }
+
+ private void delay()
+ {
+ if (WSManager.getUserConfig().getTurtleSpeed() != 0)
+ {
+ try
+ {
+ Thread.sleep((long) (WSManager.getUserConfig().getTurtleSpeed()*3));
+ }
+ catch (InterruptedException e)
+ {}
+ }
+ }
+
+ // How many characters in the word "mot"
+ private int getWordLength(String mot)
+ {// retourne le nombre de caractères
+ // d'un mot
+ int compteur = 0;
+ boolean backslash = false;
+ for (int i = 0; i < mot.length(); i++)
+ {
+ if (!backslash && mot.charAt(i) == '\\')
+ backslash = true;
+ else
+ {
+ backslash = false;
+ compteur++;
+ }
+ }
+ return compteur;
+ }
+
+ // the character number "i" in the word "mot"
+ private String itemWord(int entier, String mot) throws LogoError
+ {
+ String reponse = "";
+ int compteur = 1;
+ boolean backslash = false;
+ if (mot.equals(""))
+ throw new LogoError(Logo.messages.getString("mot_vide"));
+ for (int i = 0; i < mot.length(); i++)
+ {
+ char c = mot.charAt(i);
+ if (!backslash && c == '\\')
+ backslash = true;
+ else
+ {
+ if (compteur == entier)
+ {
+ if (backslash)
+ reponse = "\\" + Character.toString(c);
+ else
+ reponse = Character.toString(c);
+ break;
+ }
+ else
+ {
+ compteur++;
+ backslash = false;
+ }
+ }
+ }
+ return reponse;
+ }
+
+ protected void setWorkspace(UserSpace workspace)
+ {
+ wp = workspace;
+ }
+
+ private void ou(Stack<String> param) throws LogoError
+ {
+ int size = param.size();
+ boolean result = false;
+ boolean b;
+ for (int i = 0; i < size; i++)
+ {
+ b = predicat(param.get(i).toString());
+ result = result | b;
+ }
+ if (result)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ Interprete.operande = true;
+ }
+
+ private void et(Stack<String> param) throws LogoError
+ {
+ int size = param.size();
+ boolean result = true;
+ boolean b;
+ for (int i = 0; i < size; i++)
+ {
+ b = predicat(param.get(i).toString());
+ result = result & b;
+ }
+ Interprete.operande = true;
+ if (result)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+
+ }
+
+ /**
+ * This methods returns a list that contains all procedures name
+ *
+ * @return A list with all procedure names
+ */
+ private StringBuffer getAllProcedures()
+ {
+ StringBuffer sb = new StringBuffer("[ ");
+ // TODO CHANGED
+ for (Procedure proc : wp.getExecutables())
+ {
+ sb.append(proc.getName());
+ sb.append(" ");
+ }
+ sb.append("] ");
+ return sb;
+ }
+
+ /**
+ * This methods returns a list that contains all variables name
+ *
+ * @return A list with all variables names
+ */
+
+ private StringBuffer getAllVariables()
+ {
+ StringBuffer sb = new StringBuffer("[ ");
+ Iterator<String> it = Interprete.locale.keySet().iterator();
+ while (it.hasNext())
+ {
+ String name = it.next();
+ sb.append(name);
+ sb.append(" ");
+ }
+ // TODO changed
+ for (String key : wp.getGlobals().getVariables())
+ {
+ if (!Interprete.locale.containsKey(key))
+ {
+ sb.append(key.toString());
+ sb.append(" ");
+ }
+ }
+ sb.append("] ");
+ return sb;
+ }
+
+ /**
+ * This methods returns a list that contains all Property Lists name
+ *
+ * @return A list with all Property Lists names
+ */
+ private StringBuffer getAllpropertyLists()
+ {
+ StringBuffer sb = new StringBuffer("[ ");
+ // TODO changed
+ for (String propList : wp.getPropertyLists().getPropListKeys())
+ {
+ sb.append(propList);
+ sb.append(" ");
+ }
+ sb.append("] ");
+ return sb;
+ }
+
+ /**
+ * Delete The variable called "name" from the workspace if it exists
+ *
+ * @param name
+ * The variable name
+ */
+ private void deleteVariable(String name)
+ {
+ if (!Interprete.locale.isEmpty())
+ {
+ if (Interprete.locale.containsKey(name))
+ {
+ Interprete.locale.remove(name);
+ }
+ }
+ else
+ {
+ wp.getGlobals().deleteVariable(name.toLowerCase());
+ }
+ }
+
+ /**
+ * Delete the procedure called "name" from the workspace
+ *
+ * @param name
+ * The procedure name
+ */
+ private void deleteProcedure(String name)
+ {
+ wp.eraseProcedure(name.toLowerCase());
+ }
+
+ /**
+ * According to the type of the data, erase from workspace the resource
+ * called "name"
+ *
+ * @param name
+ * The name of the deleted resource, it couls be a list with all
+ * resource names
+ * @param type
+ * The type for the data, it could be "variable", "procedure" or
+ * "propertylist"
+ * @throws LogoError
+ */
+
+ private void erase(String name, String type) throws LogoError
+ {
+ Interprete.operande = false;
+ if (LaunchPrimitive.isList(name))
+ {
+ name = getFinalList(name);
+ StringTokenizer st = new StringTokenizer(name);
+ while (st.hasMoreTokens())
+ {
+ String item = st.nextToken();
+ this.eraseItem(item, type);
+ }
+ }
+ else
+ {
+ name = getWord(name);
+ if (null != name)
+ {
+ this.eraseItem(name, type);
+ }
+ else
+ throw new LogoError(name + Logo.messages.getString("error.word"));
+
+ }
+ }
+
+ /**
+ * According to the type of the data, erase from workspace the resource
+ * called "name"
+ *
+ * @param name
+ * The name of the deleted resource
+ * @param type
+ * The type for the data, it could be "variable", "procedure" or
+ * "propertylist"
+ */
+ private void eraseItem(String name, String type)
+ {
+ if (type.equals("procedure"))
+ {
+ this.deleteProcedure(name.toLowerCase());
+ }
+ else if (type.equals("variable"))
+ {
+ this.deleteVariable(name.toLowerCase());
+ }
+ else if (type.equals("propertylist"))
+ {
+ wp.getPropertyLists().removePropList(name.toLowerCase());
+ }
+
+ }
+}
diff --git a/logo/src/xlogo/kernel/LogoError.java b/logo/src/xlogo/kernel/LogoError.java
new file mode 100644
index 0000000..175c243
--- /dev/null
+++ b/logo/src/xlogo/kernel/LogoError.java
@@ -0,0 +1,89 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+/**
+ * In XLogo, myException was used. My exception was something rather funny.
+ * Actually it was really bad. myException was an exception that handled itself, by showing an error dialog and by aborting execution of the running Logo program.
+ * Thus throwing a myException was more like a function call with the side effect, that execution jumped to some other place, wherever it was caught.
+ * Note that to handle itself, myException had to have a reference to {@link Application}. Thus every part of the interpreter that wanted to throw Logo errors
+ * had to have such a reference, even though Application was not used otherwise.<br>
+ * By moving the exception handling at the root, where interpretation is started, in {@link Affichage}, I managed to decouple several classes from Application.
+ * (Still, many of them are unnecessarily dependent on Application, but I cannot refactor everything in the given time)
+ *
+ * Also note how ugly myException was used before. LaunchPrimitive is the best example. The general pattern was this:<br>
+ *
+ * <pre>
+ * {@code
+ * case i: // i : the id of some Logo primitive
+ * try
+ * {
+ * ...
+ * if (the next token is not as expected)
+ * throw new myException(application, errorMessage);
+ * ...
+ * }
+ * catch (myException)
+ * {}
+ * break; // => end of execute()
+ * // Because myException "handled itself", Affichage and Interprete will not continue execution
+ * }
+ * </pre>
+ *
+ * Note that almost all of the more than 300 Logo primitives contained such a statement, sometimes even more.
+ * That is more than 5x300 = 1500 unnecessary lines of code (after my style guide) that make the reading very hard,
+ * and introducing multiple levels of blocks { } to the structure.
+ *
+ * I took myself the time to remove all these unnecessary try-catches, place only one in Affichage and thus make the core of the application much more readable.
+ * <p><p>
+ * Note that I cannot test all the 300 Logo procedures for every case, but since throwing a myException caused the Interpreter to stop anyway,
+ * Redirecting the exception up to the root in Affichage will not change the flow of correct Logo Execution. Only executions which caused an exception can maybe behave slightly different now.
+ * I will for sure test all the procedures that we use in our school projects and a few more.
+ *
+ * <p><p>
+ * Another interesting thing, or again, really bad.
+ * The {@code static boolean lance} in myException was initialized with false. There are several assignments that set, again, false to it, but there is no single set to true.
+ * In most cases where lance is tested in a if-statement, it is tested in conjunction with Application.error (which can be true or false)
+ *
+ * <p> And then, there was also the nested class Affiche, which was never used.
+ *
+ *@author Marko Zivkovic
+ */
+public class LogoError extends Exception
+{
+ private static final long serialVersionUID = 9184760816698357437L;
+
+ public LogoError()
+ {
+ }
+
+ public LogoError(String st)
+ {
+ super(st);
+ }
+}
diff --git a/logo/src/xlogo/kernel/LoopFillPolygon.java b/logo/src/xlogo/kernel/LoopFillPolygon.java
new file mode 100644
index 0000000..f2cb0fd
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopFillPolygon.java
@@ -0,0 +1,48 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.math.BigDecimal;
+
+public class LoopFillPolygon extends LoopProperties {
+ /**
+ * The super constructor for Fill Polygon Loops
+ * @param instr The instruction to execute each loop
+ */
+
+ LoopFillPolygon(){
+ super(BigDecimal.ONE,BigDecimal.ONE,BigDecimal.ONE,"");
+ }
+ protected boolean isForEver(){
+ return false;
+ }
+ protected boolean isFillPolygon(){
+ return true;
+ }
+}
diff --git a/logo/src/xlogo/kernel/LoopFor.java b/logo/src/xlogo/kernel/LoopFor.java
new file mode 100644
index 0000000..473fd28
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopFor.java
@@ -0,0 +1,94 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+import java.math.BigDecimal;
+
+public class LoopFor extends LoopProperties{
+ /**
+ * This boolean indicates
+ * @uml.property name="conserver"
+ */
+ protected boolean conserver=false;
+
+ /**
+ * The variable name
+ * @uml.property name="var"
+ */
+
+ String var="";
+ /**
+ * Constructor Loop: For
+ * @param counter The beginning integer
+ * @param end The end integer
+ * @param increment The increment between two values
+ * @param instr The instruction to execute between two values
+ * @param var The name of the variable
+ */
+
+ LoopFor(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr,String var){
+ super(counter,end,increment,instr);
+ this.var=var;
+ }
+
+ protected boolean isFor(){
+ return true;
+ }
+ protected boolean isForEver(){
+ return false;
+ }
+ /**
+ * This method affects the variable counter the correct value
+ * @param first boolean that indicates if it is the first affectation
+ */
+ protected void AffecteVar(boolean first){
+ String element=String.valueOf(super.getCounter());
+ if (element.endsWith(".0")) element=element.substring(0,element.length()-2) ;
+ if (element.startsWith(".")||element.equals("")) element="0"+element;
+
+ if (Interprete.locale.containsKey(var)){
+ if (first) conserver=true;
+ Interprete.locale.put(var, element);
+ }
+ else {
+ Interprete.locale.put(var,element);
+ }
+ }
+ /**
+ * This method deletes the variable var from the local stack variable
+ */
+ void DeleteVar(){
+ if (!conserver){
+ if (Interprete.locale.containsKey(var)){
+ Interprete.locale.remove(var);
+ }
+
+ }
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/LoopForEach.java b/logo/src/xlogo/kernel/LoopForEach.java
new file mode 100644
index 0000000..3d0c518
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopForEach.java
@@ -0,0 +1,69 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.math.BigDecimal;
+import java.util.Vector;
+
+public class LoopForEach extends LoopFor{
+ private Vector<String> vec;
+ /**
+ * Constructor Loop: For
+ * @param counter The beginning integer
+ * @param end The end integer
+ * @param increment The increment between two values
+ * @param instr The instruction to execute between two values
+ * @param var The name of the variable
+ * @param vec The Vec with all value for the variable
+ */
+ LoopForEach(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr,String var,Vector<String> vec){
+ super(counter,end,increment,instr,var);
+ this.vec=vec;
+ }
+ protected boolean isForEach(){
+ return true;
+ }
+ protected boolean isForEver(){
+ return false;
+ }
+ /**
+ * This method affects the variable counter the correct value
+ * @param first boolean that indicates if it is the first affectation
+ */
+ protected void AffecteVar(boolean first){
+ String element=vec.get(getCounter().intValue());
+ if (Interprete.locale.containsKey(var)){
+ if (first) conserver=true;
+ Interprete.locale.put(var, element);
+ }
+ else {
+ Interprete.locale.put(var,element);
+ }
+ }
+}
diff --git a/logo/src/xlogo/kernel/LoopProperties.java b/logo/src/xlogo/kernel/LoopProperties.java
new file mode 100644
index 0000000..ac252e4
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopProperties.java
@@ -0,0 +1,149 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel;
+import java.math.BigDecimal;
+/**
+ * This class saves all Loop Properties (repeat, while, for) such as increment, end integer ....
+ * @author loic
+ */
+public class LoopProperties {
+ /**
+ * Counter: The counter value for the current loop End: The end value for the loop Increment the increment between two values
+ * @uml.property name="counter"
+ */
+ private BigDecimal counter;
+ /**
+ * Counter: The counter value for the current loop End: The end value for the loop Increment the increment between two values
+ * @uml.property name="end"
+ */
+ private BigDecimal end;
+ /**
+ * Counter: The counter value for the current loop End: The end value for the loop Increment the increment between two values
+ * @uml.property name="increment"
+ */
+ private BigDecimal increment;
+ /**
+ * The Instruction to execute on each iteration
+ * @uml.property name="instr"
+ */
+ String instr;
+/**
+ * The super constructor for all loops
+ * @param counter The beginning integer
+ * @param fin The end integer
+ * @param increment The increment between two values
+ * @param instr The instruction to execute each loop
+ */
+ LoopProperties(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr){
+ this.counter=counter;
+ this.end=end;
+ this.increment=increment;
+ this.instr=instr;
+ }
+
+
+ /**
+ * Adds the increment to the variable counter
+ */
+
+ protected void incremente(){
+ counter=counter.add(increment);
+ counter=new BigDecimal(MyCalculator.eraseZero(counter));
+ }
+ /**
+ * This method returns the Loop Id
+ * @return the Loop Id (TYPE_FOR, TYPE_WHILE...)
+ */
+/* protected int getId(){
+ return id;
+ }*/
+ /**
+ * This method returns the Loop Id
+ * @return the Loop Id (TYPE_FOR, TYPE_WHILE...)
+ * @uml.property name="counter"
+ */
+ protected BigDecimal getCounter(){
+ return counter;
+ }
+ /**
+ * This method returns the end Value
+ * @return the end value for the loop
+ * @uml.property name="end"
+ */
+ protected BigDecimal getEnd(){
+ return end;
+ }
+ /**
+ * this method returns the increment for the loop
+ * @return The variable increment
+ * @uml.property name="increment"
+ */
+ protected BigDecimal getIncrement(){
+ return increment;
+ }
+ /**
+ * This method returns the instructions to execute each loop
+ * @return the instruction block
+ * @uml.property name="instr"
+ */
+ protected String getInstr(){
+ return instr;
+ }
+ /**
+ * This method returns a loop description
+ */
+ public String toString(){
+ return(counter+" "+end+" "+increment+"\n"+instr+"\n");
+ }
+
+ protected boolean isFor(){
+ return false;
+ }
+ protected boolean isWhile(){
+ return false;
+ }
+ protected boolean isForEach(){
+ return false;
+ }
+ protected boolean isRepeat(){
+ return false;
+ }
+ protected boolean isForEver(){
+ return true;
+ }
+ protected boolean isFillPolygon(){
+ return false;
+ }
+}
diff --git a/logo/src/xlogo/kernel/LoopRepeat.java b/logo/src/xlogo/kernel/LoopRepeat.java
new file mode 100644
index 0000000..5362d46
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopRepeat.java
@@ -0,0 +1,43 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.math.BigDecimal;
+
+public class LoopRepeat extends LoopProperties{
+ LoopRepeat(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr){
+ super(counter,end,increment,instr);
+ }
+ protected boolean isRepeat(){
+ return true;
+ }
+ protected boolean isForEver(){
+ return false;
+ }
+}
diff --git a/logo/src/xlogo/kernel/LoopWhile.java b/logo/src/xlogo/kernel/LoopWhile.java
new file mode 100644
index 0000000..c4451c8
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopWhile.java
@@ -0,0 +1,44 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.math.BigDecimal;
+
+public class LoopWhile extends LoopProperties{
+
+ LoopWhile(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr){
+ super(counter,end,increment,instr);
+ }
+ protected boolean isForEver(){
+ return false;
+ }
+ protected boolean isWhile(){
+ return true;
+ }
+}
diff --git a/logo/src/xlogo/kernel/MP3Player.java b/logo/src/xlogo/kernel/MP3Player.java
new file mode 100644
index 0000000..e996bd2
--- /dev/null
+++ b/logo/src/xlogo/kernel/MP3Player.java
@@ -0,0 +1,86 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+
+import xlogo.Application;
+import xlogo.Logo;
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+import javazoom.jl.decoder.JavaLayerException;
+import javazoom.jl.player.advanced.AdvancedPlayer;
+public class MP3Player extends Thread{
+
+ private AdvancedPlayer player;
+
+ MP3Player(Application app,String path) throws LogoError{
+ try {
+ // Build absolutePath
+ String absolutePath= Utils.SortieTexte(WSManager.getUserConfig().getDefaultFolder())
+ + File.separator + Utils.SortieTexte(path);
+ player=new AdvancedPlayer(new FileInputStream(absolutePath));
+ }
+ catch(FileNotFoundException e){
+ // tentative fichier réseau
+ try{
+ URL url =new java.net.URL(path);
+ java.io.InputStream fr = url.openStream();
+ player=new AdvancedPlayer(fr);
+ }
+ catch( java.net.MalformedURLException e1){
+
+ throw new LogoError(Logo.messages.getString("error.iolecture"));
+
+
+ }
+ catch(IOException e2){}
+ catch(JavaLayerException e3){}
+ }
+ catch(JavaLayerException e4){}
+
+ }
+ public void run(){
+ try{
+ player.play();
+ }
+ catch(JavaLayerException e){}
+ }
+ /**
+ * @return
+ * @uml.property name="player"
+ */
+ protected AdvancedPlayer getPlayer(){
+ return player;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/MyCalculator.java b/logo/src/xlogo/kernel/MyCalculator.java
new file mode 100644
index 0000000..1fde09c
--- /dev/null
+++ b/logo/src/xlogo/kernel/MyCalculator.java
@@ -0,0 +1,1221 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.math.MathContext;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Stack;
+
+import xlogo.Logo;
+import xlogo.utils.Utils;
+
+public class MyCalculator
+{
+ private final BigDecimal tenth = new BigDecimal(0.1);
+
+ private MathContext mc = null;
+
+ // If precision is lesser than 16 (operation for double)
+ /**
+ * @uml.property name="lowPrecision"
+ */
+ private boolean lowPrecision = true;
+
+ /**
+ * Indicates if the log table have been created
+ *
+ * @uml.property name="initLogTable"
+ */
+ private boolean initLogTable;
+ /**
+ * This is a table containing all BigDecimal ln(1+10^ (-k) ), k in
+ * {0,1....,digits-1} This are constants for the Cordic method to calculate
+ * ln, exp
+ *
+ * @uml.property name="logTable" multiplicity="(0 -1)" dimension="1"
+ */
+ private BigDecimal[] logTable;
+ /**
+ * Indicates if the trigonometric table have been created
+ *
+ * @uml.property name="initCosTable"
+ */
+ private boolean initCosTable;
+ /**
+ * This is a table containing all BigDecimal arctan 10^ (-k) , k in
+ * {0,1....,digits-1} This are constants for the Cordic method to calculate
+ * trigonometric functions
+ *
+ * @uml.property name="cosTable" multiplicity="(0 -1)" dimension="1"
+ */
+ private BigDecimal[] cosTable;
+
+ private static int digits;
+
+ protected MyCalculator(int digits)
+ {
+ MyCalculator.digits = digits;
+ initLogTable = false;
+ initCosTable = false;
+ if (digits < 16)
+ {
+ mc = new MathContext(16);
+ lowPrecision = true;
+ logTable = new BigDecimal[16];
+ cosTable = new BigDecimal[16];
+ }
+ else
+ {
+ mc = new MathContext(digits);
+ lowPrecision = false;
+ logTable = new BigDecimal[digits];
+ cosTable = new BigDecimal[digits];
+ }
+ }
+
+ /**
+ * Return The exponential of s according to matContext Precision
+ *
+ * @param s
+ * The number
+ * @return Exp(s)
+ * @throws LogoError
+ * if s isn't a number
+ */
+
+ protected String exp(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double nombre = numberDouble(s);
+ return teste_fin_double(Math.exp(nombre));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return expBD(bd).toPlainString();
+ }
+ }
+
+ /**
+ * Return The logarithm of s according to matContext Precision
+ *
+ * @param s
+ * The number
+ * @return log(s)
+ * @throws LogoError
+ * if s isn't a number or negative
+ */
+
+ protected String log(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double nombre = numberDouble(s);
+ if (nombre < 0 || nombre == 0)
+ {
+ String log = Utils.primitiveName("arithmetic.log");
+ throw new LogoError(log + " " + Logo.messages.getString("attend_positif"));
+ }
+ return teste_fin_double(Math.log(nombre));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ if (bd.signum() != 1)
+ {
+ String log = Utils.primitiveName("arithmetic.log");
+ throw new LogoError(log + " " + Logo.messages.getString("attend_positif"));
+ }
+ return logBD(bd).toPlainString();
+ }
+ }
+
+ /**
+ * Return The square root of s according to matContext Precision
+ *
+ * @param s
+ * The number
+ * @return sqrt(s)
+ * @throws LogoError
+ * if s isn't a number or negative
+ */
+
+ protected String sqrt(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double number = numberDouble(s);
+ if (number < 0)
+ {
+ String sqrt = Utils.primitiveName("arithmetic.racine");
+ throw new LogoError(sqrt + " " + Logo.messages.getString("attend_positif"));
+ }
+ return teste_fin_double(Math.sqrt(number));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ if (bd.signum() == -1)
+ {
+ String sqrt = Utils.primitiveName("arithmetic.racine");
+ throw new LogoError(sqrt + " " + Logo.messages.getString("attend_positif"));
+ }
+ return sqrtBD(bd).toPlainString();
+ }
+ }
+
+ /**
+ * Return the product of all elements in stack param
+ *
+ * @param param
+ * The stack of operands
+ * @return The product
+ * @throws LogoError
+ */
+ protected String multiply(Stack<String> param) throws LogoError
+ {
+ int size = param.size();
+ BigDecimal product = BigDecimal.ONE;
+ BigDecimal a;
+ for (int i = 0; i < size; i++)
+ {
+ a = numberDecimal(param.get(i));
+ product = product.multiply(a, mc);
+ }
+ return product.stripTrailingZeros().toPlainString();
+ }
+
+ protected String divide(Stack<String> param) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double a = numberDouble(param.get(0));
+ double b = numberDouble(param.get(1));
+ if (b == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return (teste_fin_double(a / b));
+ }
+ else
+ {
+ BigDecimal a = new BigDecimal(param.get(0), mc);
+ BigDecimal b = new BigDecimal(param.get(1), mc);
+ if (b.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return (a.divide(b, mc).stripTrailingZeros().toPlainString());
+ }
+ }
+
+ /**
+ * Return the sum of all elements in stack param
+ *
+ * @param param
+ * The stack of operands
+ * @return The sum
+ * @throws LogoError
+ */
+ protected String add(Stack<String> param) throws LogoError
+ {
+ int size = param.size();
+ BigDecimal sum = BigDecimal.ZERO;
+ BigDecimal a;
+ for (int i = 0; i < size; i++)
+ {
+ a = numberDecimal(param.get(i));
+ sum = sum.add(a, mc);
+ }
+
+ return sum.stripTrailingZeros().toPlainString();
+ }
+
+ protected String inf(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) < 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String sup(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) > 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String infequal(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) <= 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String supequal(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) >= 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String equal(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) == 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String substract(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ return a.subtract(b, mc).stripTrailingZeros().toPlainString();
+ }
+
+ /**
+ * Returns the opposite of s
+ *
+ * @param s
+ * @return
+ */
+ protected String minus(String s) throws LogoError
+ {
+ BigDecimal a = numberDecimal(s);
+ return a.negate(mc).stripTrailingZeros().toPlainString();
+ }
+
+ protected String remainder(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ int aa = getInteger(a);
+ int bb = getInteger(b);
+ if (bb == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return teste_fin_double(aa % bb);
+ }
+ else
+ {
+ BigDecimal aa = getBigInteger(a);
+ BigDecimal bb = getBigInteger(b);
+ if (bb.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return aa.remainder(bb, mc).stripTrailingZeros().toPlainString();
+
+ }
+ }
+
+ protected String modulo(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ int aa = getInteger(a);
+ int bb = getInteger(b);
+ if (bb == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ double rem = aa % bb;
+ if (aa * bb < 0 && rem != 0)
+ rem = rem + bb;
+ return teste_fin_double(rem);
+ }
+ else
+ {
+ BigDecimal aa = getBigInteger(a);
+ BigDecimal bb = getBigInteger(b);
+ if (bb.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ BigDecimal rem = aa.remainder(bb, mc);
+ if (aa.multiply(bb).compareTo(BigDecimal.ZERO) == -1 && (!rem.equals(BigDecimal.ZERO)))
+ rem = rem.add(bb);
+ return rem.stripTrailingZeros().toPlainString();
+
+ }
+ }
+
+ protected String quotient(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double aa = numberDouble(a);
+ double bb = numberDouble(b);
+ if (bb == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return String.valueOf((int) (aa / bb));
+ }
+ else
+ {
+ BigDecimal aa = numberDecimal(a);
+ BigDecimal bb = numberDecimal(b);
+ if (bb.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return aa.divideToIntegralValue(bb, mc).stripTrailingZeros().toPlainString();
+
+ }
+ }
+
+ protected String truncate(String a) throws LogoError
+ {
+ BigDecimal ent = numberDecimal(a);
+ return ent.toBigInteger().toString();
+ }
+
+ protected String abs(String a) throws LogoError
+ {
+ BigDecimal e = numberDecimal(a);
+ return e.abs().stripTrailingZeros().toPlainString();
+ }
+
+ protected String power(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double p = Math.pow(numberDouble(a), numberDouble(b));
+ // Bug pr power -1 0.5
+ Double p1 = new Double(p);
+ if (p1.equals(Double.NaN))
+ throw new LogoError(Utils.primitiveName("arithmetic.puissance") + " "
+ + Logo.messages.getString("attend_positif"));
+ // End Bug
+ return teste_fin_double(p);
+ }
+ else
+ {
+ // if the exposant is an integer
+ try
+ {
+ int n = Integer.parseInt(b);
+ BigDecimal aa = numberDecimal(a);
+ return aa.pow(n, mc).toPlainString();
+ }
+ catch (NumberFormatException e)
+ {
+ BigDecimal aa = numberDecimal(a);
+ BigDecimal bb = numberDecimal(b);
+ if (aa.signum() == 1)
+ {
+ return expBD(bb.multiply(logBD(aa), mc)).toPlainString();
+ }
+ else if (aa.signum() == 0)
+ return "0";
+ else
+ return String.valueOf(getInteger(b));
+ }
+ }
+ }
+
+ protected String log10(String s) throws LogoError
+ {
+ Stack<String> tmp = new Stack<String>();
+ tmp.push(log(s));
+ tmp.push(log("10"));
+ return divide(tmp);
+ }
+
+ protected String pi()
+ {
+ if (lowPrecision)
+ {
+ return String.valueOf(Math.PI);
+ }
+ else
+ {
+ return piBD().toPlainString();
+ }
+ }
+
+ protected String sin(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.sin(Math.toRadians(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return sinBD(bd).toPlainString();
+
+ }
+ }
+
+ protected String cos(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.cos(Math.toRadians(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return cosBD(bd).toPlainString();
+
+ }
+ }
+
+ protected String tan(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.tan(Math.toRadians(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return tanBD(bd).toPlainString();
+ }
+ }
+
+ protected String atan(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.toDegrees(Math.atan(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return toDegree(atanBD(bd)).toPlainString();
+ }
+ }
+
+ protected String acos(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.toDegrees(Math.acos(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return toDegree(acosBD(bd)).toPlainString();
+ }
+ }
+
+ protected String asin(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.toDegrees(Math.asin(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return toDegree(asinBD(bd)).toPlainString();
+ }
+ }
+
+ /**
+ * This method returns the exp of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal expBD(BigDecimal bd)
+ {
+ if (!initLogTable)
+ {
+ initLogTable();
+ }
+ int signum = bd.signum();
+ if (signum == -1)
+ {
+ BigDecimal exp = expCordic(bd.negate(mc));
+ exp = BigDecimal.ONE.divide(exp, mc);
+ return exp;
+ }
+ else if (signum == 0)
+ return BigDecimal.ONE;
+ else
+ {
+ return expCordic(bd);
+ }
+ }
+
+ private BigDecimal expCordic(BigDecimal bd)
+ {
+ int i = 0;
+ BigDecimal y = BigDecimal.ONE;
+ while (i < mc.getPrecision())
+ {
+ while (logTable[i].subtract(bd).signum() == -1)
+ {
+ bd = bd.subtract(logTable[i], mc);
+ y = y.add(y.multiply(tenth.pow(i, mc), mc), mc);
+ }
+ i++;
+ }
+ y = y.multiply(bd.add(BigDecimal.ONE, mc), mc);
+ return y;
+
+ }
+
+ /**
+ * This method returns the log of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal logBD(BigDecimal bd)
+ {
+ if (!initLogTable)
+ {
+ initLogTable();
+ }
+ // If bd > 1
+ int signum = bd.subtract(BigDecimal.ONE, mc).signum();
+ if (signum == 1)
+ {
+ bd = bd.subtract(BigDecimal.ONE, mc);
+ return logCordic(bd);
+ }
+ else if (signum == 0)
+ return BigDecimal.ZERO;
+ else
+ {
+ bd = BigDecimal.ONE.divide(bd, mc).subtract(BigDecimal.ONE, mc);
+ return logCordic(bd).negate(mc);
+ }
+ }
+
+ private BigDecimal logCordic(BigDecimal bd)
+ {
+ int i = 0;
+ BigDecimal y = BigDecimal.ZERO;
+ while (i < mc.getPrecision())
+ {
+ BigDecimal tenthi = tenth.pow(i, mc);
+ while (bd.subtract(tenthi, mc).signum() > 0)
+ {
+ bd = bd.subtract(tenthi, mc).divide(BigDecimal.ONE.add(tenthi, mc), mc);
+ y = y.add(logTable[i], mc);
+ }
+ i++;
+ }
+ y = y.add(bd, mc).subtract(bd.pow(2, mc).multiply(new BigDecimal(0.5), mc), mc);
+ return y;
+
+ }
+
+ /**
+ * This method returns the sqrt of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal sqrtBD(BigDecimal bd)
+ {
+ if (bd.signum() == 0)
+ return BigDecimal.ZERO;
+ BigDecimal three = new BigDecimal(3);
+ BigDecimal half = new BigDecimal(0.5);
+ BigDecimal x = BigDecimal.ZERO;
+ BigDecimal y = BigDecimal.ONE.min(BigDecimal.ONE.divide(bd, mc));
+ while (x.compareTo(y) == -1)
+ {
+ x = y;
+ // y=(3x-bd*x^3)/2
+ y = x.multiply(three, mc).subtract(bd.multiply(x.pow(3, mc), mc), mc).multiply(half, mc);
+ }
+ return BigDecimal.ONE.divide(y, mc);
+ }
+
+ /**
+ * This method returns the cos of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal cosBD(BigDecimal bd)
+ {
+ // bd is in degree
+ BigDecimal period = new BigDecimal(360);
+ BigDecimal a90 = new BigDecimal(90);
+ BigDecimal a135 = new BigDecimal(135);
+ BigDecimal a180 = new BigDecimal(180);
+ BigDecimal a225 = new BigDecimal(225);
+ BigDecimal a270 = new BigDecimal(270);
+ BigDecimal a315 = new BigDecimal(315);
+
+ bd = bd.remainder(period, mc);
+ if (bd.signum() == -1)
+ bd = bd.add(period, mc);
+ BigDecimal quarterpi = new BigDecimal(45);
+ // Now bd between 0 and 360.
+ if (bd.compareTo(quarterpi) == -1)
+ {
+ // Between 0 and 45
+ return cosCordic(toRadian(bd));
+ }
+ else if (bd.compareTo(a90) == -1)
+ {
+ // Between 45 and 90
+ return sinCordic(toRadian(a90.subtract(bd, mc)));
+ }
+ else if (bd.compareTo(a135) == -1)
+ {
+ // Between 90 and 135
+ return sinCordic(toRadian(bd.subtract(a90, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a180) == -1)
+ {
+ // Between 135 and 180
+ return cosCordic(toRadian(a180.subtract(bd, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a225) == -1)
+ {
+ // Between 180 and 225
+ return cosCordic(toRadian(bd.subtract(a180, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a270) == -1)
+ {
+ // Between 225 and 270
+ return sinCordic(toRadian(a270.subtract(bd, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a315) == -1)
+ {
+ // Between 270 and 315
+ return sinCordic(toRadian(bd.subtract(a270, mc)));
+ }
+ else
+ {
+ return cosCordic(toRadian(new BigDecimal(360).subtract(bd, mc)));
+ }
+ }
+
+ /**
+ * This method returns the cos of bd with 0<bd<pi/4
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal cosCordic(BigDecimal bd)
+ {
+ return BigDecimal.ONE.divide(sqrtBD(tanCordic(bd).pow(2, mc).add(BigDecimal.ONE, mc)), mc);
+ }
+
+ /**
+ * This method returns the cos of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal sinBD(BigDecimal bd)
+ {
+ BigDecimal a90 = new BigDecimal(90);
+ return cosBD(a90.subtract(bd, mc));
+
+ }
+
+ /**
+ * This method returns the sin of bd with 0<bd<pi/4
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal sinCordic(BigDecimal bd)
+ {
+ BigDecimal tan = tanCordic(bd);
+ return tan.divide(sqrtBD(tan.pow(2, mc).add(BigDecimal.ONE, mc)), mc);
+ }
+
+ /**
+ * This method returns the tan of bd (in degree)
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal tanBD(BigDecimal bd)
+ {
+ // bd is in degree
+ BigDecimal pi = new BigDecimal(180);
+ BigDecimal halfpi = new BigDecimal(90);
+ BigDecimal quarterpi = new BigDecimal(45);
+ bd = bd.remainder(pi, mc);
+ if (bd.compareTo(halfpi.negate(mc)) == -1)
+ bd = bd.add(pi, mc);
+ if (bd.compareTo(halfpi) == 1)
+ bd = bd.subtract(pi, mc);
+ // Now bd is in -90;+90 degrees
+
+ if (bd.compareTo(quarterpi) == 1)
+ {
+ BigDecimal x = toRadian(new BigDecimal(0.5).multiply(bd, mc));
+ return new BigDecimal(2).multiply(tanCordic(x), mc).divide(
+ BigDecimal.ONE.subtract(tanCordic(x).pow(2, mc), mc), mc);
+ }
+ else if (bd.signum() == 1)
+ {
+ return tanCordic(toRadian(bd));
+ }
+ else if (bd.compareTo(quarterpi.negate(mc)) == 1)
+ {
+ return tanCordic(toRadian(bd.negate(mc))).negate(mc);
+ }
+ else
+ {
+ BigDecimal x = toRadian(new BigDecimal(0.5).multiply(bd, mc)).negate(mc);
+ return new BigDecimal(2).multiply(tanCordic(x), mc)
+ .divide(BigDecimal.ONE.subtract(tanCordic(x).pow(2, mc), mc), mc).negate(mc);
+ }
+ }
+
+ /**
+ * This method returns the tan of bd with 0<bd<pi/4
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal tanCordic(BigDecimal bd)
+ {
+ if (!initCosTable)
+ initCosTable();
+ BigDecimal three = new BigDecimal(3);
+ int k = 1;
+ BigDecimal x = BigDecimal.ONE;
+ BigDecimal y = BigDecimal.ZERO;
+ BigDecimal tenthk = tenth;
+ while (k < mc.getPrecision())
+ {
+ while (cosTable[k].compareTo(bd) == -1)
+ {
+ bd = bd.subtract(cosTable[k], mc);
+ BigDecimal tmp = x;
+ x = x.subtract(tenthk.multiply(y, mc), mc);
+ y = y.add(tenthk.multiply(tmp, mc), mc);
+ }
+ tenthk = tenthk.multiply(tenth, mc);
+ k++;
+ }
+ BigDecimal tmp = bd.pow(3, mc).add(three.multiply(bd, mc), mc);
+ // return (3*y+(3t+t^3)*x)/(3x-(3t+t^3)*y
+ return three.multiply(y, mc).add(x.multiply(tmp, mc), mc)
+ .divide(three.multiply(x, mc).subtract(y.multiply(tmp, mc), mc), mc);
+ }
+
+ private BigDecimal piBD()
+ {
+ if (!initCosTable)
+ {
+ initCosTable();
+ }
+ return cosTable[0].multiply(new BigDecimal(4), mc);
+ }
+
+ /**
+ * This method creates the log Table using
+ * log h=(h-1)-(h-1)^2/2+(h-1)^3/3-.....
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private void initLogTable()
+ {
+ initLogTable = true;
+ // calculate ln 2
+ // Using ln 2=2*(x+x^3/3+x^5/5+....) with x=1/3
+
+ BigDecimal sum = BigDecimal.ZERO;
+ BigDecimal previous = BigDecimal.ONE;
+ BigDecimal i = BigDecimal.ONE;
+ BigDecimal nine = new BigDecimal(9);
+ BigDecimal two = new BigDecimal(2);
+ BigDecimal power = new BigDecimal(3);
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ sum = sum.add(BigDecimal.ONE.divide(i.multiply(power, mc), mc), mc);
+ i = i.add(two, mc);
+ power = power.multiply(nine, mc);
+ }
+ logTable[0] = sum.multiply(two, mc);
+
+ // Calculate ln (1+10^-j) j in 1 ... digits-1
+
+ for (int j = 1; j < mc.getPrecision(); j++)
+ {
+ // count=0;
+ sum = BigDecimal.ZERO;
+ previous = BigDecimal.ONE;
+ i = BigDecimal.ONE;
+ // 10^(-j)
+ BigDecimal bd = tenth.pow(j, mc);
+ power = bd;
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ sum = sum.add(power.divide(i, mc), mc);
+ if (i.signum() == 1)
+ i = i.add(BigDecimal.ONE, mc).negate(mc);
+ else
+ i = i.subtract(BigDecimal.ONE, mc).negate(mc);
+ power = power.multiply(bd, mc);
+ // count++;
+ }
+ logTable[j] = sum;
+ }
+ }
+
+ /**
+ * This method creates the cos Table using
+ * arctan h=x-x^3/3+x^5/5-x^7/7...
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private void initCosTable()
+ {
+ initCosTable = true;
+ // calculate pi/4
+
+ cosTable[0] = calcPI().multiply(new BigDecimal(0.25), mc);
+
+ // Calculate arctan (10^-j) j in 1 ... digits-1
+
+ for (int j = 1; j < mc.getPrecision(); j++)
+ {
+ // 10^(-j)
+ BigDecimal bd = tenth.pow(j, mc);
+ cosTable[j] = arctanSE(bd);
+ // System.out.println(cosTable[j].toPlainString());
+ }
+ }
+
+ // Using PI = 16arctg(1/5) - 4arctg(1/239)
+ private BigDecimal calcPI()
+ {
+ return new BigDecimal(16).multiply(arctanSE(new BigDecimal("0.2")), mc).subtract(
+ new BigDecimal(4).multiply(arctanSE(BigDecimal.ONE.divide(new BigDecimal(239), mc)), mc), mc);
+ }
+
+ private BigDecimal arctanSE2(BigDecimal bd)
+ {
+ BigDecimal i = BigDecimal.ONE;
+ // BigDecimal j=new BigDecimal(3);
+ BigDecimal two = new BigDecimal(2);
+ BigDecimal square = bd.multiply(bd, mc);
+ BigDecimal power = bd.divide(square.add(BigDecimal.ONE, mc), mc);
+ BigDecimal cst = new BigDecimal(4).multiply(square, mc).divide(square.add(BigDecimal.ONE, mc), mc);
+ BigDecimal previous = BigDecimal.ZERO;
+ BigDecimal sum = power;
+ int count = 0;
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ power = power.multiply(cst, mc);
+ power = power.multiply(i.pow(2, mc), mc);
+ BigDecimal doublei = two.multiply(i, mc);
+ doublei = doublei.multiply(doublei.add(BigDecimal.ONE, mc), mc);
+ power = power.divide(doublei, mc);
+ sum = sum.add(power, mc);
+ i = i.add(BigDecimal.ONE);
+ count++;
+ }
+ System.out.println("Itérations " + count);
+ return sum;
+
+ }
+
+ private BigDecimal atanBD(BigDecimal bd)
+ {
+ if (bd.signum() == -1)
+ return atanBD(bd.negate(mc)).negate(mc);
+ if (bd.compareTo(BigDecimal.ONE) == 1)
+ // pi/2 -arctan (1/x)
+ return piBD().multiply(new BigDecimal(0.5), mc).subtract(arctanSE(BigDecimal.ONE.divide(bd, mc)), mc);
+ else if (bd.compareTo(BigDecimal.ONE) == 0)
+ return piBD().multiply(new BigDecimal("0.25"), mc);
+ else
+ return arctanSE(bd);
+ }
+
+ private BigDecimal acosBD(BigDecimal bd)
+ {
+ if (bd.compareTo(new BigDecimal("-1")) == 0)
+ {
+ return piBD();
+ }
+ // acos x= 2 atan (sqrt(1-x^2)/1+x
+ else
+ {
+ return new BigDecimal("2").multiply(
+ atanBD(sqrtBD(BigDecimal.ONE.subtract(bd.pow(2, mc), mc)).divide(BigDecimal.ONE.add(bd, mc), mc)),
+ mc);
+
+ }
+ }
+
+ private BigDecimal asinBD(BigDecimal bd)
+ {
+ // acos x= 2 atan (x/(1+sqrt(1-x^2))
+ return new BigDecimal("2").multiply(
+ atanBD(bd.divide(BigDecimal.ONE.add(sqrtBD(BigDecimal.ONE.subtract(bd.pow(2, mc), mc)), mc), mc)), mc);
+ }
+
+ // arctan h=x-x^3/3+x^5/5-x^7/7...
+ private BigDecimal arctanSE(BigDecimal bd)
+ {
+ BigDecimal i = BigDecimal.ONE;
+ BigDecimal two = new BigDecimal(2, mc);
+ BigDecimal square = bd.multiply(bd, mc);
+ BigDecimal sum = BigDecimal.ZERO;
+ BigDecimal previous = BigDecimal.ONE;
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ sum = sum.add(bd.divide(i, mc), mc);
+ if (i.signum() == 1)
+ i = i.add(two, mc).negate(mc);
+ else
+ i = i.subtract(two, mc).negate(mc);
+ bd = bd.multiply(square, mc);
+ }
+ return sum;
+ }
+
+ private BigDecimal toRadian(BigDecimal n)
+ {
+ return n.multiply(piBD(), mc).divide(new BigDecimal(180), mc);
+ }
+
+ private BigDecimal toDegree(BigDecimal n)
+ {
+ return n.multiply(new BigDecimal(180), mc).divide(piBD(), mc);
+ }
+
+ /**
+ * This method converts st to double
+ *
+ * @param st
+ * The String
+ * @return The double corresponding to st
+ * @throws LogoError
+ * If st can't be convert
+ */
+
+ protected double numberDouble(String st) throws LogoError
+ { // Si un nombre est
+ // un double
+ try
+ {
+ return (Double.parseDouble(st));
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_nombre"));
+ }
+ }
+
+ /**
+ * If a double ends with the suffix ".0", remove it
+ *
+ * @param d
+ * @return
+ */
+
+ static protected String teste_fin_double(double d)
+ {
+ String st = String.valueOf(d);
+ if (st.endsWith(".0"))
+ st = st.substring(0, st.length() - 2);
+ return st;
+ }
+
+ /**
+ * Converts st to BigDecimal number
+ *
+ * @param st
+ * The String to convert
+ * @return The BigDecimal Number
+ * @throws LogoError
+ * if st isn't a number
+ */
+
+ protected BigDecimal numberDecimal(String st) throws LogoError
+ {
+ /*
+ * if (null==mc){
+ * try {
+ * BigDecimal bd = new BigDecimal(st).setScale(16,
+ * BigDecimal.ROUND_HALF_EVEN);
+ * return (new BigDecimal(eraseZero(bd)));
+ * } catch (NumberFormatException e) {
+ * throw new myException( st + " "
+ * + Logo.messages.getString("pas_nombre"));
+ * }
+ * }
+ * else {
+ */
+ try
+ {
+ return new BigDecimal(st, mc);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_nombre"));
+ }
+ // }
+
+ }
+
+ /**
+ * Erase unused Zeros in decimal Format
+ *
+ * @param bd
+ * The decimal number
+ * @return The formatted number
+ */
+ static protected String eraseZero(BigDecimal bd)
+ {
+ DecimalFormatSymbols dfs = new DecimalFormatSymbols();
+ dfs.setDecimalSeparator('.');
+ DecimalFormat df = new DecimalFormat("#####.################", dfs);
+ String st = df.format(bd);
+ return st;
+
+ }
+
+ /**
+ * Test if the number contained in st is an integer
+ *
+ * @param st
+ * The Object to convert
+ * @return The integer corresponding to st
+ * @throws LogoError
+ * If it isn't an integer
+ */
+
+ protected int getInteger(String st) throws LogoError
+ { // Si c'est un
+ // entier
+ try
+ {
+ return Integer.parseInt(st);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_entier"));
+ }
+ }
+
+ /**
+ * Test if the number contained in st is an integer
+ *
+ * @param st
+ * The Object to convert
+ * @return The integer corresponding to st
+ * @throws LogoError
+ * If it isn't an integer
+ */
+
+ protected BigDecimal getBigInteger(String st) throws LogoError
+ { // Si c'est un
+ // entier
+ try
+ {
+ return new BigDecimal(new BigInteger(st));
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_entier"));
+ }
+ }
+
+ protected int getDigits()
+ {
+ if (digits < 0)
+ return -1;
+ else
+ return mc.getPrecision();
+ }
+
+ public static String getOutputNumber(String s)
+ {
+ try
+ {
+ if (digits >= 0 && digits < 16)
+ {
+ BigDecimal bd = new BigDecimal(s);
+ s = bd.toPlainString();
+ // is it a decimal number?
+ int index = s.indexOf(".");
+ if (index != -1)
+ {
+ if (digits == 0)
+ return s.substring(0, index);
+ else if (s.length() > index + digits)
+ {
+ s = s.substring(0, index + digits + 1);
+ int a = Integer.parseInt(String.valueOf(s.charAt(s.length() - 1)));
+ if (a > 4)
+ {
+ a++;
+ s = s.substring(0, s.length() - 1) + a;
+ }
+ return s;
+ }
+ }
+ else
+ return s;
+ }
+ }
+ catch (NumberFormatException e)
+ {}
+ return s;
+ }
+}
diff --git a/logo/src/xlogo/kernel/MyFlow.java b/logo/src/xlogo/kernel/MyFlow.java
new file mode 100644
index 0000000..2c7b5e3
--- /dev/null
+++ b/logo/src/xlogo/kernel/MyFlow.java
@@ -0,0 +1,110 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+public class MyFlow
+{
+
+ private String path;
+ private int id;
+ private boolean finished;
+
+ MyFlow(int id, String path, boolean finished)
+ {
+ this.id = id;
+ this.path = path;
+ this.finished = finished;
+ }
+
+ MyFlow(MyFlow flow)
+ {
+ this.id = flow.getId();
+ this.path = flow.getPath();
+ this.finished = flow.isFinished();
+ }
+
+ /**
+ * @return
+ */
+ String getPath()
+ {
+ return path;
+ }
+
+ /**
+ * @param p
+ */
+ void setPath(String p)
+ {
+ path = p;
+ }
+
+ /**
+ * @return
+ */
+ int getId()
+ {
+ return id;
+ }
+
+ /**
+ * @param i
+ */
+ void setId(int i)
+ {
+ id = i;
+ }
+
+ /**
+ * @return
+ */
+ boolean isFinished()
+ {
+ return finished;
+ }
+
+ /**
+ * @param b
+ */
+ void setFinished(boolean b)
+ {
+ finished = b;
+ }
+
+ boolean isReader()
+ {
+ return false;
+ }
+
+ boolean isWriter()
+ {
+ return false;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/MyFlowReader.java b/logo/src/xlogo/kernel/MyFlowReader.java
new file mode 100644
index 0000000..2b3409b
--- /dev/null
+++ b/logo/src/xlogo/kernel/MyFlowReader.java
@@ -0,0 +1,81 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+public class MyFlowReader extends MyFlow
+{
+ BufferedReader bfr;
+
+ boolean isReader()
+ {
+ return true;
+ }
+
+ MyFlowReader(MyFlow flow)
+ {
+ super(flow);
+ }
+
+ String readLine() throws FileNotFoundException, IOException
+ {
+ if (null == bfr)
+ bfr = new BufferedReader(new FileReader(getPath()));
+ String line = bfr.readLine();
+ return line;
+ }
+
+ int readChar() throws FileNotFoundException, IOException
+ {
+ if (null == bfr)
+ bfr = new BufferedReader(new FileReader(getPath()));
+ int character = bfr.read();
+ return character;
+ }
+
+ int isReadable() throws FileNotFoundException, IOException
+ {
+ if (null == bfr)
+ bfr = new BufferedReader(new FileReader(getPath()));
+ bfr.mark(2);
+ int id = bfr.read();
+ bfr.reset();
+ return id;
+ }
+
+ void close() throws IOException
+ {
+ if (null != bfr)
+ bfr.close();
+ }
+}
diff --git a/logo/src/xlogo/kernel/MyFlowWriter.java b/logo/src/xlogo/kernel/MyFlowWriter.java
new file mode 100644
index 0000000..7651aee
--- /dev/null
+++ b/logo/src/xlogo/kernel/MyFlowWriter.java
@@ -0,0 +1,74 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.io.BufferedWriter;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import xlogo.utils.Utils;
+
+public class MyFlowWriter extends MyFlow
+{
+ BufferedWriter bfw;
+
+ MyFlowWriter(MyFlow flow)
+ {
+ super(flow);
+ }
+
+ void append(String line) throws FileNotFoundException, IOException
+ {
+ if (null == bfw)
+ bfw = new BufferedWriter(new FileWriter(getPath(), true));
+ PrintWriter pw = new PrintWriter(bfw);
+ pw.println(Utils.SortieTexte(line));
+ }
+
+ void write(String line) throws FileNotFoundException, IOException
+ {
+ if (null == bfw)
+ bfw = new BufferedWriter(new FileWriter(getPath()));
+ PrintWriter pw = new PrintWriter(bfw);
+ pw.println(Utils.SortieTexte(line));
+ }
+
+ boolean isWriter()
+ {
+ return true;
+ }
+
+ void close() throws IOException
+ {
+ if (null != bfw)
+ bfw.close();
+ }
+}
diff --git a/logo/src/xlogo/kernel/Primitive.java b/logo/src/xlogo/kernel/Primitive.java
new file mode 100644
index 0000000..0d0d8d1
--- /dev/null
+++ b/logo/src/xlogo/kernel/Primitive.java
@@ -0,0 +1,573 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo Description : XLogo is an interpreter for the Logo programming
+ * language
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel;
+
+import java.util.Vector;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.math.BigDecimal;
+
+import xlogo.kernel.LoopProperties;
+import xlogo.messages.async.history.HistoryMessenger;
+import xlogo.storage.WSManager;
+import xlogo.storage.workspace.Language;
+import xlogo.utils.Utils;
+import xlogo.Application;
+
+import java.io.*;
+
+import xlogo.Logo;
+
+import java.util.Enumeration;
+
+public class Primitive
+{
+ /**
+ * This character indicates the end of a procedure in instructionBuffer
+ */
+ protected static final String END_PROCEDURE = "\n";
+ /**
+ * This character indicates the end of a loop in instructionBuffer
+ */
+ protected static final String END_LOOP = "\\";
+
+ // float taille_crayon=(float)0;
+ private Application app;
+ protected static final int PRIMITIVE_NUMBER = 310;
+ protected static int[] parametres = new int[PRIMITIVE_NUMBER];
+ protected static boolean[] generalForm = new boolean[PRIMITIVE_NUMBER];
+
+ // Treemap for primitives (better efficiency in searching)
+ public static TreeMap<String, String> primitives = new TreeMap<String, String>();
+
+ public static Stack<LoopProperties> stackLoop = new Stack<LoopProperties>();
+
+ public Primitive()
+ {
+ }
+
+ public Primitive(Application app)
+ {
+ this.app = app;
+ // build treemap for primitives
+ buildPrimitiveTreemap(WSManager.getInstance().getWorkspaceConfigInstance().getLanguage());
+ }
+
+ /**
+ * This methods returns a list which contains all the primitive for the
+ * current language
+ *
+ * @return The primitives list
+ */
+ protected String getAllPrimitives()
+ {
+ Vector<String> list = new Vector<String>();
+ Locale locale = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage().getLocale();
+ ResourceBundle prim = ResourceBundle.getBundle("primitives", locale);
+ try
+ {
+ BufferedReader bfr = new BufferedReader(new InputStreamReader(
+ Primitive.class.getResourceAsStream("genericPrimitive")));
+ while (bfr.ready())
+ {
+ String line = bfr.readLine();
+ // read the generic keyword for the primitive
+ StringTokenizer cut = new StringTokenizer(line);
+ // read the standard number of arguments for the primitive
+ String cle = cut.nextToken();
+ // Exclude internal primitive \n \x and siwhile
+ if (!cle.equals("\\n") && !cle.equals("\\siwhile") && !cle.equals("\\x"))
+ {
+ // Exclude all arithmetic symbols + - / * & |
+ if (cle.length() != 1)
+ {
+ if (!cle.equals(">=") && !cle.equals("<="))
+ {
+ cle = prim.getString(cle).trim();
+ list.add(cle);
+ }
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ System.out.println("Impossible de lire le fichier d'initialisation des primitives");
+ }
+ Collections.sort(list);
+ StringBuffer sb = new StringBuffer("[ ");
+ for (int i = 0; i < list.size(); i++)
+ {
+ sb.append("[ ");
+ sb.append(list.get(i));
+ sb.append("] ");
+ }
+ sb.append("] ");
+ return (sb.toString());
+ }
+
+ // Exécution des primitives
+ public static void buildPrimitiveTreemap(Language lang)
+ {
+ // this.exportPrimCSV();
+ primitives = new TreeMap<String, String>();
+ Locale locale = lang.getLocale();
+ ResourceBundle prim = ResourceBundle.getBundle("primitives", locale);
+ try
+ {
+ BufferedReader bfr = new BufferedReader(new InputStreamReader(
+ Primitive.class.getResourceAsStream("genericPrimitive")));
+ int i = 0;
+ while (bfr.ready())
+ {
+ String line = bfr.readLine();
+ // read the generic keyword for the primitive
+ StringTokenizer cut = new StringTokenizer(line);
+ // read the standard number of arguments for the primitive
+ String cle = cut.nextToken();
+ parametres[i] = Integer.parseInt(cut.nextToken());
+ // Read if the primitive has a general form
+ // eg (sum 2 3 4 5) --> 14
+ // eg (list 3 4 5) ---> [3 4 5]
+ if (cut.hasMoreTokens())
+ {
+ generalForm[i] = true;
+ }
+ else
+ generalForm[i] = false;
+
+ if (i == 39)
+ primitives.put("\n", "39");
+ // Internal Primitive siwhile
+ else if (i == 95)
+ {
+ primitives.put("\\siwhile", "95");
+ }
+ // Internal Primitive \x
+ else if (i == 212)
+ primitives.put("\\x", "212");
+ else
+ {
+ if (cle.length() != 1)
+ {
+ if (!cle.equals(">=") && !cle.equals("<="))
+ cle = prim.getString(cle);
+ }
+ StringTokenizer st = new StringTokenizer(cle.toLowerCase());
+ int compteur = 0;
+ while (st.hasMoreTokens())
+ {
+ primitives.put(st.nextToken(), String.valueOf(i + Primitive.PRIMITIVE_NUMBER * compteur));
+ compteur++;
+ }
+ }
+ i++;
+ }
+ /*
+ * System.out.println(i+ " primitives");
+ * java.util.Iterator it=primitives.keySet().iterator();
+ * while(it.hasNext()){
+ * String next=it.next().toString();
+ * System.out.println(next+" "+primitives.get(next));
+ * }
+ */
+ }
+ catch (IOException e)
+ {
+ System.out.println("Impossible de lire le fichier d'initialisation des primitives");
+ }
+ }
+
+ /**
+ * This method creates the loop "repeat"
+ *
+ * @param i
+ * The number of iteration
+ * @param st
+ * The instruction to execute
+ * @throws LogoError
+ */
+ protected void repete(int i, String st) throws LogoError
+ {
+ if (i > 0)
+ {
+ st = new String(Utils.decoupe(st));
+ LoopProperties bp = new LoopRepeat(BigDecimal.ONE, new BigDecimal(i), BigDecimal.ONE, st);
+ stackLoop.push(bp);
+ app.getKernel().getInstructionBuffer().insert(st + "\\ ");
+ }
+ else if (i != 0) { throw new LogoError(Utils.primitiveName("controls.repete") + " "
+ + Logo.messages.getString("attend_positif")); }
+ }
+
+ /**
+ * This method is an internal primitive for the primitive "while"
+ *
+ * @param b
+ * Do we still execute the loop body?
+ * @param li
+ * The loop body instructions
+ * @throws LogoError
+ */
+ protected void whilesi(boolean b, String li) throws LogoError
+ {
+ if (b)
+ {
+ app.getKernel().getInstructionBuffer().insert(li + Primitive.stackLoop.peek().getInstr());
+ }
+ else
+ {
+ eraseLevelStop(app);
+ }
+ }
+
+ // primitive if
+ protected void si(boolean b, String li, String li2)
+ {
+ if (b)
+ {
+ app.getKernel().getInstructionBuffer().insert(li);
+ }
+ else if (null != li2)
+ {
+ app.getKernel().getInstructionBuffer().insert(li2);
+ }
+ }
+
+ // primitive stop
+ protected void stop() throws LogoError
+ {
+ Interprete.operande = false;
+ String car = "";
+ car = eraseLevelStop(app);
+
+ // A procedure has been stopped
+ if (car.equals("\n"))
+ {
+ String en_cours = Interprete.en_cours.pop();
+ Interprete.locale = Interprete.stockvariable.pop();
+ // Example: to bug
+ // fd stop
+ // end
+ // --------
+ // bug
+ // stop doesn't output to fd
+ if (!Interprete.nom.isEmpty() && !Interprete.nom.peek().equals("\n"))
+ {
+ // System.out.println(Interprete.nom);
+ throw new LogoError(Utils.primitiveName("controls.stop") + " "
+ + Logo.messages.getString("ne_renvoie_pas") + " " + Interprete.nom.peek());
+ }
+ else if (!Interprete.nom.isEmpty())
+ {
+ // Removing the character "\n"
+ Interprete.nom.pop();
+ // Example: to bug | to bug2
+ // fd bug2 | stop
+ // end | end
+ // ------------------------
+ // bug
+ // bug2 doesn't output to fd
+ if (!Interprete.nom.isEmpty() && !Interprete.nom.peek().equals("\n"))
+ {
+ // System.out.println(Interprete.nom);
+ throw new LogoError(en_cours + " " + Logo.messages.getString("ne_renvoie_pas") + " "
+ + Interprete.nom.peek());
+ }
+ }
+ }
+ }
+
+ // primitive output
+ protected void retourne(String val) throws LogoError
+ {
+ Interprete.calcul.push(val);
+ Interprete.operande = true;
+ if (Kernel.mode_trace)
+ {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < Interprete.en_cours.size() - 1; i++)
+ buffer.append(" ");
+ buffer.append(Interprete.en_cours.peek());
+ buffer.append(" " + Utils.primitiveName("ret") + " " + val);
+ HistoryMessenger.getInstance().dispatchMessage(Utils.SortieTexte(buffer.toString()) + "\n");
+ }
+ Interprete.en_cours.pop();
+ Interprete.locale = Interprete.stockvariable.pop();
+ if ((!Interprete.nom.isEmpty()) && Interprete.nom.peek().equals("\n"))
+ {
+ eraseLevelReturn(app);
+ Interprete.nom.pop();
+ }
+ else if (!Interprete.nom.isEmpty())
+ throw new LogoError(Utils.primitiveName("ret") + " " + Logo.messages.getString("ne_renvoie_pas") + " "
+ + Interprete.nom.peek());
+ else
+ throw new LogoError(Logo.messages.getString("erreur_retourne"));
+ }
+
+ /**
+ * This method deletes all instruction since it encounters the end of a loop
+ * or the end of a procedure
+ *
+ * @param app
+ * The runnning frame Application
+ * @return The specific character \n or \ if found
+ * @throws LogoError
+ */
+ private String eraseLevelStop(Application app) throws LogoError
+ {
+ boolean error = true;
+ String caractere = "";
+ int marqueur = 0;
+ InstructionBuffer instruction = app.getKernel().getInstructionBuffer();
+ for (int i = 0; i < instruction.getLength(); i++)
+ {
+ caractere = String.valueOf(instruction.charAt(i));
+ if (caractere.equals(Primitive.END_LOOP) | caractere.equals(Primitive.END_PROCEDURE))
+ {
+ marqueur = i;
+ if (caractere.equals(Primitive.END_LOOP) && i != instruction.getLength() - 1)
+ {
+ /*
+ * On test si le caractère "\" est bien un caractère de
+ * fin
+ * de boucle et non du style "\e" ou "\#"
+ */
+ if (instruction.charAt(i + 1) == ' ')
+ {
+ error = false;
+ break;
+ }
+ }
+ else
+ {
+ error = false;
+ break;
+ }
+ }
+ }
+
+ if (error) { throw new LogoError(Logo.messages.getString("erreur_stop")); }
+ if (marqueur + 2 > instruction.getLength())
+ instruction = new InstructionBuffer(" ");
+ else
+ instruction.delete(0, marqueur + 2);
+ if (!caractere.equals("\n"))
+ {
+ Primitive.stackLoop.pop();
+ }
+ return (caractere);
+ }
+
+ /**
+ * This method deletes all instruction since it encounters the end of a
+ * procedure
+ *
+ * @param app
+ * The running frame Application
+ * @return an integer that indicates the number of loop to delete from
+ * Primitive.stackLoop
+ * @throws LogoError
+ */
+ private void eraseLevelReturn(Application app) throws LogoError
+ {
+ boolean error = true;
+ String caractere = "";
+ int loopLevel = 0;
+ int marqueur = 0;
+ InstructionBuffer instruction = app.getKernel().getInstructionBuffer();
+ for (int i = 0; i < instruction.getLength(); i++)
+ {
+ caractere = String.valueOf(instruction.charAt(i));
+ if (caractere.equals(Primitive.END_PROCEDURE))
+ {
+ marqueur = i;
+ error = false;
+ break;
+ }
+ else if (caractere.equals(Primitive.END_LOOP))
+ {
+ /*
+ * On test si le caractère "\" est bien un caractère de fin
+ * de boucle et non du style "\e" ou "\#"
+ */
+ if (instruction.charAt(i + 1) == ' ')
+ {
+ loopLevel++;
+ }
+ }
+ }
+ if (error) { throw new LogoError(Logo.messages.getString("erreur_retourne")); }
+ if (marqueur + 2 > instruction.getLength())
+ instruction = new InstructionBuffer(" ");
+ else
+ instruction.delete(0, marqueur + 2);
+ for (int i = 0; i < loopLevel; i++)
+ {
+ Primitive.stackLoop.pop();
+ }
+ }
+
+ private void exportPrimCSV()
+ {
+ StringBuffer sb = new StringBuffer();
+ Locale locale = new Locale("fr", "FR");
+ ResourceBundle prim_fr = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("en", "US");
+ ResourceBundle prim_en = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("ar", "MA");
+ ResourceBundle prim_ar = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("es", "ES");
+ ResourceBundle prim_es = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("pt", "BR");
+ ResourceBundle prim_pt = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("eo", "EO");
+ ResourceBundle prim_eo = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("de", "DE");
+ ResourceBundle prim_de = ResourceBundle.getBundle("primitives", locale);
+ // locale=new Locale("nl","NL");
+ ResourceBundle[] prim = { prim_fr, prim_en, prim_es, prim_pt, prim_ar, prim_eo, prim_de };
+ try
+ {
+ BufferedReader bfr = new BufferedReader(new InputStreamReader(
+ Primitive.class.getResourceAsStream("genericPrimitive")));
+ int i = 0;
+ while (bfr.ready())
+ {
+ String line = bfr.readLine();
+ StringTokenizer cut = new StringTokenizer(line);
+ String cle = cut.nextToken();
+ parametres[i] = Integer.parseInt(cut.nextToken());
+ if (i != 39 && i != 95 && i != 212 && cle.length() != 1)
+ {
+ sb.append("$");
+ sb.append(cle);
+ sb.append("$");
+ sb.append(";");
+ for (int j = 0; j < prim.length; j++)
+ {
+ String txt = cle;
+ txt = prim[j].getString(cle);
+ sb.append("$");
+ sb.append(txt);
+ sb.append("$");
+ sb.append(";");
+ }
+ sb.append("\n");
+ }
+ i++;
+ }
+ Utils.writeLogoFile("/home/loic/primTable.csv", new String(sb));
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void exportMessageCSV()
+ {
+ StringBuffer sb = new StringBuffer();
+ Locale locale = new Locale("fr", "FR");
+ ResourceBundle lang_fr = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("en", "US");
+ ResourceBundle lang_en = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("ar", "MA");
+ ResourceBundle lang_ar = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("es", "ES");
+ ResourceBundle lang_es = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("pt", "BR");
+ ResourceBundle lang_pt = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("eo", "EO");
+ ResourceBundle lang_eo = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("de", "DE");
+ ResourceBundle lang_de = ResourceBundle.getBundle("langage", locale);
+ // locale=new Locale("nl","NL");
+ ResourceBundle[] lang = { lang_fr, lang_en, lang_es, lang_pt, lang_ar, lang_eo, lang_de };
+ try
+ {
+ Enumeration<String> en = lang_fr.getKeys();
+ while (en.hasMoreElements())
+ {
+ String cle = en.nextElement();
+ sb.append("$");
+ sb.append(cle);
+ sb.append("$");
+ sb.append(";");
+ for (int j = 0; j < lang.length; j++)
+ {
+ String txt = lang[j].getString(cle);
+ sb.append("$");
+ sb.append(txt);
+ sb.append("$");
+ if (j != lang.length - 1)
+ sb.append(";");
+ }
+
+ sb.append("\n");
+ }
+ Utils.writeLogoFile("/home/loic/messageTable.csv", new String(sb));
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public static int isPrimitive(String s)
+ {
+ String index = Primitive.primitives.get(s.toLowerCase());
+ if (null == index)
+ return -1;
+ try
+ {
+ int id = Integer.parseInt(index);
+ return id % Primitive.PRIMITIVE_NUMBER;
+ }
+ catch (NumberFormatException e)
+ {
+ System.err.println("!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
+ + "The file xlogo/kernel/genericPrimitive has been corrupted...\n"
+ + " Please, check the integrity of xlogo.jar \n!!!!!!!!!!!!!!!!!!!!!!!!!");
+ }
+ return -1;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/Turtle.java b/logo/src/xlogo/kernel/Turtle.java
new file mode 100644
index 0000000..82f3a76
--- /dev/null
+++ b/logo/src/xlogo/kernel/Turtle.java
@@ -0,0 +1,434 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+
+package xlogo.kernel;
+
+import java.awt.Toolkit;
+import java.awt.MediaTracker;
+import java.awt.geom.GeneralPath;
+import java.awt.Color;
+import java.awt.Image;
+import java.awt.BasicStroke;
+import java.awt.Stroke;
+import java.util.StringTokenizer;
+
+import xlogo.Logo;
+import xlogo.storage.WSManager;
+import xlogo.storage.user.PenShape;
+import xlogo.storage.user.UserConfig;
+import xlogo.utils.Utils;
+import xlogo.Application;
+
+public class Turtle
+{
+
+ private Application app;
+
+ public Color couleurcrayon = Color.black;
+ public Stroke stroke = new BasicStroke(1);
+
+ Color couleurmodedessin = Color.black;
+
+ public int id = -1;
+
+ BasicStroke crayon = null;
+
+ int police = 12;
+
+ private int labelHorizontalAlignment = 0;
+ protected static final int LABEL_HORIZONTAL_ALIGNMENT_LEFT = 0;
+ protected static final int LABEL_HORIZONTAL_ALIGNMENT_CENTER = 1;
+ protected static final int LABEL_HORIZONTAL_ALIGNMENT_RIGHT = 2;
+
+ private int labelVerticalAlignment = 0;
+ protected static final int LABEL_VERTICAL_ALIGNMENT_BOTTOM = 0;
+ protected static final int LABEL_VERTICAL_ALIGNMENT_CENTER = 1;
+ protected static final int LABEL_VERTICAL_ALIGNMENT_TOP = 2;
+
+ // Image for the turtle
+ // If null then draw the triangle
+
+ Image tort = null;
+
+ GeneralPath triangle;
+ /**
+ * The turtle heading (degree)
+ */
+ public double heading;
+ /**
+ * The turtle roll (degree)
+ */
+ public double roll;
+ /**
+ * The turtle pitch (degree)
+ */
+ public double pitch;
+
+ /**
+ * The X coordinates on the screen
+ */
+ public double corX;
+ /**
+ * The Y coordinates on the screen
+ */
+ public double corY;
+
+ public double angle;
+ /**
+ * The X coordinates in real World (3D or 2D)
+ */
+ public double X = 0;
+ /**
+ * The Y coordinates in real World (3D or 2D)
+ */
+ public double Y = 0;
+ /**
+ * The Z coordinates in real World (3D or 2D)
+ */
+ public double Z = 0;
+ /**
+ * Identity Matrix
+ */
+ private final double[][] identity = new double[3][3];
+ {
+ identity[0][0] = identity[1][1] = identity[2][2] = 1;
+ identity[0][1] = identity[0][2] = identity[1][2] = 0;
+ identity[1][0] = identity[2][1] = identity[2][0] = 0;
+ }
+ /**
+ * This is the rotation Matrix (3x3) in 3D world
+ */
+ private double[][] rotationMatrix = identity;
+
+ int largeur = 0;
+
+ int hauteur = 0;
+
+ int gabarit = 0;
+
+ private boolean pendown = true;
+
+ private boolean penReverse = false;
+
+ private boolean visible = true;
+
+ private int shape = WSManager.getUserConfig().getActiveTurtle();
+
+ private float penWidth = 0; // half
+ // of
+ // the
+ // pen
+ // width
+
+ public Turtle(Application app)
+ {
+ UserConfig uc = WSManager.getUserConfig();
+
+ this.app = app;
+ fixe_taille_crayon(1);
+ String chemin = "tortue" + uc.getActiveTurtle() + ".png";
+ couleurcrayon = uc.getPencolor();
+ couleurmodedessin = uc.getPencolor();
+ if (uc.getActiveTurtle() == 0)
+ {
+ tort = null;
+ largeur = 26;
+ hauteur = 26;
+ }
+ else
+ {
+ // ON teste tout d'abord si le chemin est valide
+ if (null == Utils.class.getResource(chemin))
+ chemin = "tortue1.png";
+ tort = Toolkit.getDefaultToolkit().getImage(Utils.class.getResource(chemin));
+ MediaTracker tracker = new MediaTracker(app.getFrame());
+ tracker.addImage(tort, 0);
+ try
+ {
+ tracker.waitForID(0);
+ }
+ catch (InterruptedException e1)
+ {}
+ largeur = tort.getWidth(app.getFrame());
+ hauteur = tort.getHeight(app.getFrame());
+ double largeur_ecran = Toolkit.getDefaultToolkit().getScreenSize().getWidth();
+ // On fait attention à la résolution de l'utilisateur
+ double facteur = largeur_ecran / 1024.0;
+ if ((int) (facteur + 0.001) != 1)
+ {
+ tort = tort.getScaledInstance((int) (facteur * largeur), (int) (facteur * hauteur), Image.SCALE_SMOOTH);
+ tracker = new MediaTracker(app.getFrame());
+ tracker.addImage(tort, 0);
+ try
+ {
+ tracker.waitForID(0);
+ }
+ catch (InterruptedException e1)
+ {}
+ }
+ largeur = tort.getWidth(app.getFrame());
+ hauteur = tort.getHeight(app.getFrame());
+ }
+ gabarit = Math.max(hauteur, largeur);
+ corX = uc.getImageWidth() / 2;
+ corY = uc.getImageHeight() / 2;
+ angle = Math.PI / 2;
+ heading = 0.0;
+ pitch = 0;
+ roll = 0;
+ X = 0;
+ Y = 0;
+ Z = 0;
+ }
+
+ protected void init()
+ {
+ UserConfig uc = WSManager.getUserConfig();
+
+ corX = uc.getImageWidth() / 2;
+ corY = uc.getImageHeight() / 2;
+ X = 0;
+ Y = 0;
+ Z = 0;
+ heading = 0;
+ pitch = 0;
+ roll = 0;
+ rotationMatrix = identity;
+ angle = Math.PI / 2;
+ pendown = true;
+ stroke = new BasicStroke(1);
+ fixe_taille_crayon(1);
+ couleurcrayon = uc.getPencolor();
+ couleurmodedessin = uc.getPencolor();
+ penReverse = false;
+ }
+
+ void drawTriangle()
+ {
+ if (null == tort)
+ {
+ if (null == triangle)
+ {
+ triangle = new GeneralPath();
+ }
+ else
+ triangle.reset();
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ {
+ triangle.moveTo((float) (corX - 10.0 * Math.sin(angle)), (float) (corY - 10.0 * Math.cos(angle)));
+ triangle.lineTo((float) (corX + 24.0 * Math.cos(angle)), (float) (corY - 24.0 * Math.sin(angle)));
+ triangle.lineTo((float) (corX + 10.0 * Math.sin(angle)), (float) (corY + 10.0 * Math.cos(angle)));
+ triangle.lineTo((float) (corX - 10.0 * Math.sin(angle)), (float) (corY - 10.0 * Math.cos(angle)));
+ }
+ else
+ {
+ double[] screenCoord = new double[2];
+ // The triangle has coordinates: (-10,0,0);(0,24,0);(10,0,0)
+ double[] x1 = new double[3];
+ x1[0] = X - 20 * rotationMatrix[0][0];
+ x1[1] = Y - 20 * rotationMatrix[1][0];
+ x1[2] = Z - 20 * rotationMatrix[2][0];
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.moveTo((float) screenCoord[0], (float) screenCoord[1]);
+ x1[0] = X + 48 * rotationMatrix[0][1];
+ x1[1] = Y + 48 * rotationMatrix[1][1];
+ x1[2] = Z + 48 * rotationMatrix[2][1];
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]);
+ x1[0] = X + 20 * rotationMatrix[0][0];
+ x1[1] = Y + 20 * rotationMatrix[1][0];
+ x1[2] = Z + 20 * rotationMatrix[2][0];
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]);
+ triangle.closePath();
+
+ // the "aileron" has coordinates: (0,10,0);(0,0,10);(0,0,0)
+ x1[0] = X + 15 * rotationMatrix[0][1];
+ x1[1] = Y + 15 * rotationMatrix[1][1];
+ x1[2] = Z + 15 * rotationMatrix[2][1];
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.moveTo((float) screenCoord[0], (float) screenCoord[1]);
+ x1[0] = X + 15 * rotationMatrix[0][2];
+ x1[1] = Y + 15 * rotationMatrix[1][2];
+ x1[2] = Z + 15 * rotationMatrix[2][2];
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]);
+ x1[0] = X;
+ x1[1] = Y;
+ x1[2] = Z;
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]);
+ }
+ }
+ }
+
+ public void fixe_taille_crayon(float nb)
+ {
+ UserConfig uc = WSManager.getUserConfig();
+
+ if (nb < 0)
+ nb = 1;
+ else if (uc.getMaxPenWidth() != -1 && nb > uc.getMaxPenWidth())
+ nb = 1;
+ if (uc.getPenShape() == PenShape.SQUARE)
+ crayon = new BasicStroke(nb, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER);
+ else
+ crayon = new BasicStroke(nb, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
+ penWidth = nb / 2;
+ }
+
+ public float getPenWidth()
+ {
+ return penWidth;
+ }
+
+ protected int getShape()
+ {
+ return shape;
+ }
+
+ public void setShape(int id)
+ {
+ shape = id;
+ }
+
+ protected boolean isVisible()
+ {
+ return visible;
+ }
+
+ protected void setVisible(boolean b)
+ {
+ visible = b;
+ }
+
+ protected boolean isPenDown()
+ {
+ return pendown;
+ }
+
+ protected void setPenDown(boolean b)
+ {
+ pendown = b;
+ }
+
+ protected boolean isPenReverse()
+ {
+ return penReverse;
+ }
+
+ protected void setPenReverse(boolean b)
+ {
+ penReverse = b;
+ }
+
+ protected void setRotationMatrix(double[][] m)
+ {
+ rotationMatrix = m;
+ }
+
+ protected double[][] getRotationMatrix()
+ {
+ return rotationMatrix;
+ }
+
+ protected double getX()
+ {
+ if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_3D)
+ return X;
+ return corX - WSManager.getUserConfig().getImageWidth() / 2;
+
+ }
+
+ protected double getY()
+ {
+ if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_3D)
+ return Y;
+ return WSManager.getUserConfig().getImageHeight() / 2 - corY;
+
+ }
+
+ protected int getLabelHorizontalAlignment()
+ {
+ return labelHorizontalAlignment;
+ }
+
+ protected int getLabelVerticalAlignment()
+ {
+ return labelVerticalAlignment;
+ }
+
+ protected void setFontJustify(String list) throws LogoError
+ {
+ StringTokenizer st = new StringTokenizer(list);
+ int i = 0;
+ while (st.hasMoreTokens())
+ {
+ String s = st.nextToken();
+ try
+ {
+ int j = Integer.parseInt(s);
+ if (j < 0 || j > 2)
+ throw new LogoError(list + " " + Logo.messages.getString("pas_argument"));
+ else
+ {
+ if (i == 0)
+ labelHorizontalAlignment = j;
+ else if (i == 1)
+ labelVerticalAlignment = j;
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(list + " " + Logo.messages.getString("pas_argument"));
+ }
+
+ i++;
+ }
+ if (i != 2)
+ throw new LogoError(list + " " + Logo.messages.getString("pas_argument"));
+ }
+
+ public String getFontJustify()
+ {
+ StringBuffer sb = new StringBuffer("[ ");
+ sb.append(labelHorizontalAlignment);
+ sb.append(" ");
+ sb.append(labelVerticalAlignment);
+ sb.append(" ] ");
+ return new String(sb);
+ }
+}
diff --git a/logo/src/xlogo/kernel/genericPrimitive b/logo/src/xlogo/kernel/genericPrimitive
new file mode 100644
index 0000000..bec63b7
--- /dev/null
+++ b/logo/src/xlogo/kernel/genericPrimitive
@@ -0,0 +1,308 @@
+drawing.av 1
+drawing.re 1
+drawing.td 1
+drawing.tg 1
+arithmetic.puissance 2
+controls.repete 2
+ve 0
+ct 0
+mt 0
+ec 1 +
+si 2
+controls.stop 0
+drawing.origine 0
+drawing.fpos 1
+drawing.fixex 1
+drawing.fixey 1
+drawing.fixexy 2
+drawing.fixecap 1
+lc 0
+bc 0
+go 0
+ic 0
+de 0
+arithmetic.somme 2 +
+arithmetic.difference 2
+arithmetic.moins 1
+arithmetic.produit 2 +
+arithmetic.div 2
+arithmetic.reste 2
+ret 1
+* 1
+/ 1
++ 1
+- 1
+= 1
+< 1
+> 1
+| 1
+& 1
+\n 0
+\ 0
+pos 0
+cap 0
+arithmetic.arrondi 1
+arithmetic.log10 1
+arithmetic.sin 1
+arithmetic.cos 1
+ou 2 +
+et 2 +
+non 1
+liste 2 +
+ph 2 +
+mp 2
+md 2
+inverse 1
+choix 1
+enleve 2
+item 2
+sd 1
+sp 1
+dernier 1
+premier 1
+compte 1
+mot? 1
+nombre? 1
+liste? 1
+vide? 1
+egal? 2
+precede? 2
+membre? 2
+arithmetic.racine 1
+membre 2
+workspace.donne 2
+workspace.locale 1
+workspace.donnelocale 2
+fcc 1
+fcfg 1
+arithmetic.hasard 1
+attends 1
+workspace.imts 0
+workspace.efn 1
+workspace.efv 1
+workspace.efns 0
+mot 2 +
+drawing.etiquette 1
+tc 1
+fen 0
+enr 0
+clos 0
+vt 0
+ci 1
+ftc 1
+controls.tantque 2
+lis 2
+touche? 0
+\siwhile 2
+liscar 0
+remplis 0
+drawing.point 1
+vers 1
+distance 1
+cc 0
+cf 0
+bc? 0
+visible? 0
+prim? 1
+proc? 1
+workspace.exec 1
+cat 0
+frep 1
+rep 0
+sauve 2
+sauved 1
+ramene 1
+arithmetic.pi 0
+arithmetic.tan 1
+arithmetic.acos 1
+arithmetic.asin 1
+arithmetic.atan 1
+vrai 0
+faux 0
+turtle.forme 0
+turtle.fforme 1
+workspace.def 2
+tortue 0
+tortues 0
+ftortue 1
+police 0
+fpolice 1
+tuetortue 1
+seq 1
+instr 0
+finstr 1
+joue 0
+efseq 0
+indseq 0
+findseq 1
+fpt 1
+ptexte 0
+fct 1
+couleurtexte 0
+lissouris 0
+possouris 0
+message 1
+date 0
+heure 0
+temps 0
+debuttemps 1
+fintemps? 0
+fixenompolice 1
+nompolice 0
+fixenompolicetexte 1
+nompolicetexte 0
+listeflux 0
+lisligneflux 1
+liscarflux 1
+ecrisligneflux 2
+finflux? 1
+ouvreflux 2
+fermeflux 1
+ajouteligneflux 2
+souris? 0
+workspace.listevariables 0
+workspace.chose 1
+nettoie 0
+tape 1
+drawing.cercle 1
+drawing.arc 3
+rempliszone 0
+animation 0
+rafraichis 0
+tailleimage 0
+arithmetic.quotient 2
+entier? 1
+fixeseparation 1
+separation 0
+arithmetic.tronque 1
+workspace.trace 0
+changedossier 1
+unicode 1
+caractere 1
+controls.stoptout 0
+controls.compteur 0
+controls.repetepour 2
+arithmetic.absolue 1
+remplace 3
+ajoute 3
+color.gris 0
+color.grisclair 0
+color.rougefonce 0
+color.vertfonce 0
+color.bleufonce 0
+color.orange 0
+color.rose 0
+color.violet 0
+color.marron 0
+color.noir 0
+color.rouge 0
+color.vert 0
+color.jaune 0
+color.bleu 0
+color.magenta 0
+color.cyan 0
+color.blanc 0
+) 0
+fixestyle 1
+style 0
+taillefenetre 0
+drawing.longueuretiquette 1
+envoietcp 2
+ecoutetcp 0
+executetcp 2
+\x 0
+chattcp 2
+resetall 0
+penwidth 0
+setpenshape 1
+penshape 0
+setdrawingquality 1
+drawingquality 0
+setturtlesnumber 1
+turtlesnumber 0
+setscreensize 1
+guibutton 2
+guiaction 2
+guiremove 1
+guiposition 2
+guidraw 1
+setzoom 1
+grille 2
+stopgrille 0
+stopanimation 0
+workspace.stoptrace 0
+guimenu 2
+axis 1
+xaxis 1
+yaxis 1
+stopaxis 0
+bye 0
+var? 1
+axiscolor 0
+gridcolor 0
+grid? 0
+xaxis? 0
+yaxis? 0
+setgridcolor 1
+setaxiscolor 1
+3d.perspective 0
+3d.rightroll 1
+3d.uppitch 1
+3d.leftroll 1
+3d.downpitch 1
+3d.roll 0
+3d.pitch 0
+3d.setroll 1
+3d.setpitch 1
+3d.setorientation 1
+3d.orientation 0
+3d.setxyz 3
+3d.setz 1
+workspace.pprop 3
+workspace.gprop 2
+workspace.remprop 2
+workspace.plist 1
+3d.polystart 0
+3d.polyend 0
+3d.polyview 0
+3d.linestart 0
+3d.lineend 0
+3d.pointstart 0
+3d.pointend 0
+3d.textstart 0
+3d.textend 0
+<= 1
+>= 1
+workspace.primitives 0
+workspace.propertylists 0
+workspace.content 0
+workspace.erpl 1
+arithmetic.exp 1
+arithmetic.log 1
+controls.ifelse 3
+workspace.ed 1
+workspace.edall 0
+controls.foreach 3
+controls.forever 1
+arithmetic.setdigits 1
+arithmetic.digits 0
+workspace.text 1
+workspace.externalcommand 1
+drawing.saveimage 2
+sound.mp3play 1
+sound.mp3stop 0
+zoom 0
+drawing.x 0
+drawing.y 0
+drawing.z 0
+drawing.fillpolygon 1
+arithmetic.alea 0
+loop.dountil 2
+loop.dowhile 2
+aritmetic.modulo 2
+drawing.setfontjustify 1
+drawing.fontjustify 0
+arithmetic.inf 2
+arithmetic.sup 2
+arithmetic.infequal 2
+arithmetic.supequal 2 \ No newline at end of file
diff --git a/logo/src/xlogo/kernel/grammar/LogoException.java b/logo/src/xlogo/kernel/grammar/LogoException.java
new file mode 100644
index 0000000..225a677
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoException.java
@@ -0,0 +1,69 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+/**
+ * @author loic
+ *
+ */
+public class LogoException extends LogoType
+{
+
+ private String message;
+
+ public LogoException(String message)
+ {
+ this.message = message;
+ }
+
+ public boolean isException()
+ {
+ return true;
+ }
+
+ public String toString()
+ {
+ return message;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(EXCEPTION) " + message;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoList.java b/logo/src/xlogo/kernel/grammar/LogoList.java
new file mode 100644
index 0000000..1cd6ea5
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoList.java
@@ -0,0 +1,93 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+import java.util.Vector;
+
+public class LogoList extends LogoType
+{
+ private Vector<LogoType> vector;
+
+ LogoList(Vector<LogoType> vector)
+ {
+ this.vector = vector;
+ }
+
+ LogoList()
+ {
+ vector = new Vector<LogoType>();
+ }
+
+ public boolean isList()
+ {
+ return true;
+ }
+
+ public void add(LogoType type)
+ {
+ vector.add(type);
+ }
+
+ public Vector<LogoType> getVector()
+ {
+ return vector;
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[ ");
+ for (int i = 0; i < vector.size(); i++)
+ {
+ sb.append(vector.get(i).toString());
+ sb.append(" ");
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ @Override
+ public String toDebug()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("(LIST) ");
+ sb.append(toString());
+ return sb.toString();
+
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoNumber.java b/logo/src/xlogo/kernel/grammar/LogoNumber.java
new file mode 100644
index 0000000..fd367bb
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoNumber.java
@@ -0,0 +1,76 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+/**
+ * @author loic
+ *
+ */
+public class LogoNumber extends LogoType
+{
+ private double value;
+
+ LogoNumber(double value)
+ {
+ this.value = value;
+ }
+ public double getValue()
+ {
+ return value;
+ }
+
+ public boolean isWord()
+ {
+ return true;
+ }
+
+ public boolean isNumber()
+ {
+ return true;
+ }
+
+ public String toString()
+ {
+ return String.valueOf(value);
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(NUMBER) " + String.valueOf(value);
+ }
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoParser.java b/logo/src/xlogo/kernel/grammar/LogoParser.java
new file mode 100644
index 0000000..ccf3436
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoParser.java
@@ -0,0 +1,289 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+
+package xlogo.kernel.grammar;
+
+import xlogo.Logo;
+import xlogo.kernel.DrawPanel;
+import xlogo.kernel.Primitive;
+import xlogo.storage.global.GlobalConfig;
+
+public class LogoParser
+{
+ private char c;
+ private int cursor;
+ private String text;
+
+ /**
+ *
+ * @param sr
+ * The String input reader
+ */
+ public LogoParser(String text)
+ {
+ this.text = text;
+ cursor = 0;
+ while (cursor < text.length())
+ {
+ LogoType lt = getToken();
+ if (GlobalConfig.DEBUG)
+ System.out.println("[DEBUG] Token " + lt.toDebug() + " cursor " + cursor);
+ }
+ }
+
+ private LogoType getToken()
+ {
+ boolean isQuotedWord = false;
+ boolean isVariable = false;
+
+ StringBuffer sb = new StringBuffer();
+ boolean start = false;
+ boolean backslash = false;
+ do
+ {
+ c = text.charAt(cursor);
+ // Skip White Spaces
+ if (c == ' ' || c == '\t')
+ {
+ if (start)
+ break;
+ else
+ cursor++;
+ }
+ else
+ {
+ if (backslash)
+ {
+ if (c == ' ')
+ sb.append(" ");
+ else if (c == '#')
+ sb.append("#");
+ else if (c == '\\')
+ sb.append("\\");
+ else if (c == '(')
+ sb.append("(");
+ else if (c == ')')
+ sb.append(")");
+ else if (c == '[')
+ sb.append("[");
+ else if (c == ']')
+ sb.append("]");
+ else if (c == 'n')
+ sb.append("\n");
+ else
+ sb.append(c);
+ cursor++;
+ }
+ else
+ {
+ // If it's the first character, check for type
+ if (!start)
+ {
+ if (c == ':')
+ isVariable = true;
+ else if (c == '\"')
+ isQuotedWord = true;
+ }
+ if (c == '\\')
+ backslash = true;
+
+ else if (c == '[')
+ {
+ if (start)
+ break;
+ else
+ {
+ cursor++;
+ return extractList();
+ }
+ }
+ else if (c == '(')
+ {
+ if (start)
+ break;
+ }
+ else if (c == '*' || c == '/' || c == '+' || c == '-' || c == '|' || c == '&' || c == '=')
+ {
+ if (!isQuotedWord)
+ {
+ if (!start)
+ {
+ sb.append(c);
+ cursor++;
+ }
+ break;
+
+ }
+ else
+ cursor++;
+ }
+ else if (c == ')')
+ return new LogoException(Logo.messages.getString("parenthese_ouvrante"));
+ else if (c == ']')
+ return new LogoException(Logo.messages.getString("error.whattodo") + " ]");
+ else
+ {
+ sb.append(c);
+ cursor++;
+ }
+ }
+ start = true;
+ }
+ } while (cursor < text.length());
+ if (sb.length() == 0)
+ return DrawPanel.nullType;
+ else if (isQuotedWord)
+ return new LogoWord(sb.substring(1));
+ else if (isVariable)
+ return new LogoVariable(sb.substring(1));
+ try
+ {
+ double d = Double.parseDouble(sb.toString());
+ return new LogoNumber(d);
+ }
+ catch (NumberFormatException e)
+ {
+ int id = Primitive.isPrimitive(sb.toString());
+ if (id != -1) { return new LogoPrimitive(id, sb.toString()); }
+ return new LogoException(Logo.messages.getString("je_ne_sais_pas") + " " + sb.toString());
+ }
+
+ }
+
+ /**
+ * This method extracts a list.
+ *
+ * @return a LogoList if operation succeed,
+ * a LogoException otherwise
+ */
+
+ private LogoType extractList()
+ {
+ LogoList list = new LogoList();
+ while (cursor < text.length())
+ {
+ LogoType lt = getListToken();
+ if (lt.isNull())
+ {
+ return new LogoException(Logo.messages.getString("erreur_crochet"));
+ }
+ else if (lt.isRightDelimiter())
+ return list;
+ else
+ {
+ list.add(lt);
+ }
+ }
+ return new LogoException(Logo.messages.getString("erreur_crochet"));
+ }
+
+ private LogoType getListToken()
+ {
+ StringBuffer sb = new StringBuffer();
+ boolean start = false;
+ boolean backslash = false;
+ for (int i = cursor; i < text.length(); i++)
+ {
+ cursor = i;
+ c = text.charAt(i);
+ // Skip White Spaces
+ if (c == ' ' || c == '\t')
+ {
+ if (start)
+ break;
+ }
+ else
+ {
+ if (backslash)
+ {
+ if (c == ' ')
+ sb.append(" ");
+ else if (c == '#')
+ sb.append("#");
+ else if (c == '\\')
+ sb.append("\\");
+ else if (c == '(')
+ sb.append("(");
+ else if (c == ')')
+ sb.append(")");
+ else if (c == '[')
+ sb.append("[");
+ else if (c == ']')
+ sb.append("]");
+ else if (c == 'n')
+ sb.append("\n");
+ else
+ sb.append(c);
+ }
+ else
+ {
+ if (c == '\\')
+ {
+ backslash = true;
+ }
+ else if (c == '[')
+ {
+ if (start)
+ break;
+ else
+ {
+ cursor++;
+ return extractList();
+ }
+ }
+ else if (c == ']')
+ {
+ if (start)
+ break;
+ else
+ {
+ System.out.println("coucou");
+ cursor++;
+ return new LogoRightDelimiter();
+ }
+ }
+ else
+ sb.append(c);
+ }
+ start = true;
+ }
+ }
+ if (sb.length() == 0)
+ return DrawPanel.nullType;
+ return new LogoWord(sb.toString());
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoPrimitive.java b/logo/src/xlogo/kernel/grammar/LogoPrimitive.java
new file mode 100644
index 0000000..52044a3
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoPrimitive.java
@@ -0,0 +1,74 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+public class LogoPrimitive extends LogoType
+{
+ private int id;
+ private String name;
+
+ LogoPrimitive(int id, String name)
+ {
+ this.id = id;
+ this.name = name;
+ }
+
+ public boolean isPrimitive()
+ {
+ return true;
+ }
+
+ /**
+ * @return
+ * @uml.property name="id"
+ */
+ public int getId()
+ {
+ return id;
+ }
+
+ public String toString()
+ {
+ return name;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(PRIMITIVE id=" + id + ") " + name;
+ }
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoRightDelimiter.java b/logo/src/xlogo/kernel/grammar/LogoRightDelimiter.java
new file mode 100644
index 0000000..fb46026
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoRightDelimiter.java
@@ -0,0 +1,56 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+/**
+ * @author loic
+ *
+ */
+public class LogoRightDelimiter extends LogoType
+{
+ public boolean isRightDelimiter()
+ {
+ return true;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return null;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoTree.java b/logo/src/xlogo/kernel/grammar/LogoTree.java
new file mode 100644
index 0000000..4e6b66e
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoTree.java
@@ -0,0 +1,96 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.grammar;
+
+import java.util.Vector;
+
+public class LogoTree
+{
+ private Vector<LogoTree> children;
+ private LogoTree parent;
+ private LogoType value;
+ private boolean isRoot = false;
+ private boolean isProcedure = false;
+ private boolean isPrimitive = false;
+ private boolean isLeaf = false;
+
+ LogoTree()
+ {
+ children = new Vector<LogoTree>();
+ }
+
+ protected void setParent(LogoTree lt)
+ {
+ this.parent = lt;
+ }
+
+ protected LogoTree getParent()
+ {
+ return parent;
+ }
+
+ protected boolean isRoot()
+ {
+ return isRoot;
+ }
+
+ protected void addChild(LogoTree child)
+ {
+ children.add(child);
+ }
+
+ protected void setValue(LogoType value)
+ {
+ this.value = value;
+ }
+
+ protected LogoType getValue()
+ {
+ return value;
+ }
+
+ protected boolean isLeaf()
+ {
+ return isLeaf;
+ }
+
+ LogoType evaluate()
+ {
+ Vector<LogoType> args = new Vector<LogoType>();
+ for (int i = 0; i < children.size(); i++)
+ {
+ LogoTree child = children.get(i);
+ if (child.isLeaf())
+ args.add(child.getValue());
+ else
+ args.add(child.evaluate());
+ }
+ return null;
+ }
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoType.java b/logo/src/xlogo/kernel/grammar/LogoType.java
new file mode 100644
index 0000000..d407431
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoType.java
@@ -0,0 +1,129 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+
+package xlogo.kernel.grammar;
+
+public abstract class LogoType
+{
+
+ /**
+ * If this token is a word ?
+ *
+ * @return true for a word, false otherwise
+ */
+ public boolean isWord()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is a list?
+ *
+ * @return true for a list, false otherwise
+ */
+ public boolean isList()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is a number?
+ *
+ * @return true for a number, false otherwise
+ */
+ public boolean isNumber()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is a variable?
+ *
+ * @return true for a variable, false otherwise
+ */
+ public boolean isVariable()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is a primitive?
+ *
+ * @return true for a primitive, false otherwise
+ */
+ public boolean isPrimitive()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is a procedure?
+ *
+ * @return true for a procedure, false otherwise
+ */
+ public boolean isProcedure()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is an exception?
+ *
+ * @return true for an exception, false otherwise
+ */
+ public boolean isException()
+ {
+ return false;
+ }
+
+ public boolean isNull()
+ {
+ return false;
+ }
+
+ public boolean isRightDelimiter()
+ {
+ return false;
+ }
+
+ /**
+ * Util for debugging
+ *
+ * @return the type and value for LogoType
+ */
+ abstract public String toDebug();
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoTypeNull.java b/logo/src/xlogo/kernel/grammar/LogoTypeNull.java
new file mode 100644
index 0000000..b764063
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoTypeNull.java
@@ -0,0 +1,56 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+/**
+ * @author loic
+ *
+ */
+public class LogoTypeNull extends LogoType
+{
+ public boolean isNull()
+ {
+ return true;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(LogoTypeNull)";
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoVariable.java b/logo/src/xlogo/kernel/grammar/LogoVariable.java
new file mode 100644
index 0000000..d2a7e91
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoVariable.java
@@ -0,0 +1,68 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.grammar;
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+public class LogoVariable extends LogoType
+{
+ String name;
+
+ LogoVariable(String name)
+ {
+ this.name = name;
+ }
+
+ public boolean isVariable()
+ {
+ return true;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String toString()
+ {
+ return ":" + name;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(VARIABLE) :" + name;
+ }
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoWord.java b/logo/src/xlogo/kernel/grammar/LogoWord.java
new file mode 100644
index 0000000..e155180
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoWord.java
@@ -0,0 +1,68 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+public class LogoWord extends LogoType
+{
+ private String value;
+
+ LogoWord(String value)
+ {
+ this.value = value;
+ }
+
+ public String getQuotedValue()
+ {
+ return "\"" + value;
+ }
+
+ public String toString()
+ {
+ return value;
+ }
+
+ public boolean isWord()
+ {
+ return true;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(WORD) " + value;
+ }
+}
diff --git a/logo/src/xlogo/kernel/gui/GuiButton.java b/logo/src/xlogo/kernel/gui/GuiButton.java
new file mode 100644
index 0000000..1d440a5
--- /dev/null
+++ b/logo/src/xlogo/kernel/gui/GuiButton.java
@@ -0,0 +1,92 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.gui;
+
+import javax.swing.JButton;
+
+import xlogo.kernel.Interprete;
+
+import java.awt.Font;
+import java.awt.event.*;
+
+import xlogo.Application;
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+
+public class GuiButton extends GuiComponent
+{
+
+ private StringBuffer action;
+
+ private Application app;
+
+ public GuiButton(String id, String text, Application app)
+ {
+ Font font = WSManager.getWorkspaceConfig().getFont();
+ super.setId(id);
+ guiObject = new JButton(Utils.SortieTexte(text));
+ this.app = app;
+ java.awt.FontMetrics fm = app.getFrame().getGraphics().getFontMetrics(font);
+ originalWidth = fm.stringWidth(((JButton) (getGuiObject())).getText()) + 50;
+ originalHeight = font.getSize() + 10;
+ setSize(originalWidth, originalHeight);
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ if (!app.commande_isEditable())
+ {
+ Interprete.actionInstruction.append(action);
+ }
+ else
+ {
+ app.startInterpretation(action);
+ }
+ }
+
+ public boolean isButton()
+ {
+ return true;
+ }
+
+ public boolean isMenu()
+ {
+ return false;
+ }
+
+ /**
+ * @param action
+ * @uml.property name="action"
+ */
+ public void setAction(StringBuffer action)
+ {
+ this.action = action;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/gui/GuiComponent.java b/logo/src/xlogo/kernel/gui/GuiComponent.java
new file mode 100644
index 0000000..c144432
--- /dev/null
+++ b/logo/src/xlogo/kernel/gui/GuiComponent.java
@@ -0,0 +1,95 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.gui;
+
+import java.awt.event.ActionEvent;
+import java.awt.Point;
+import java.awt.event.ActionListener;
+import javax.swing.JComponent;
+
+public abstract class GuiComponent implements ActionListener
+{
+ public boolean hasAction = false;
+ protected int originalWidth;
+ protected int originalHeight;
+ private String id;
+ protected JComponent guiObject;
+ protected boolean actionFinished;
+
+ public String getId()
+ {
+ return id;
+ }
+
+ protected void setId(String id)
+ {
+ this.id = id;
+ }
+
+ public boolean hasAction()
+ {
+ return hasAction;
+ }
+
+ public JComponent getGuiObject()
+ {
+ return guiObject;
+ }
+
+ public void setLocation(int x, int y)
+ {
+ guiObject.setLocation(x, y);
+ }
+
+ public Point getLocation()
+ {
+ return guiObject.getLocation();
+ }
+
+ public void setSize(int width, int height)
+ {
+ guiObject.setSize(width, height);
+ }
+
+ public int getOriginalWidth()
+ {
+ return originalWidth;
+ }
+
+ public int getOriginalHeight()
+ {
+ return originalHeight;
+ }
+
+ public abstract void actionPerformed(ActionEvent e);
+
+ public abstract boolean isButton();
+
+ public abstract boolean isMenu();
+}
diff --git a/logo/src/xlogo/kernel/gui/GuiMap.java b/logo/src/xlogo/kernel/gui/GuiMap.java
new file mode 100644
index 0000000..d2c071e
--- /dev/null
+++ b/logo/src/xlogo/kernel/gui/GuiMap.java
@@ -0,0 +1,72 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.gui;
+
+import xlogo.kernel.LogoError;
+
+import java.util.HashMap;
+
+import xlogo.Logo;
+
+public class GuiMap extends HashMap<String, GuiComponent>
+{
+ private static final long serialVersionUID = 1L;
+
+ public GuiMap()
+ {
+ }
+
+ public void put(GuiComponent gc) throws LogoError
+ {
+ if (this.containsKey(gc.getId()))
+ {
+ throw new LogoError(Logo.messages.getString("gui_exists") + " " + gc.getId());
+ }
+ else
+ this.put(gc.getId(), gc);
+ }
+
+ public GuiComponent get(Object key)
+ {
+ String k = key.toString().toLowerCase();
+ return super.get(k);
+ }
+
+ /*
+ * public void remove(GuiComponent gc) throws myException{
+ * if (this.containsKey(gc.getId())){
+ * this.remove(gc.getId());
+ * }
+ * else{
+ * throw new
+ * myException(app,Logo.messages.getString("no_gui")+" "+gc.getId());
+ * }
+ * }
+ */
+}
diff --git a/logo/src/xlogo/kernel/gui/GuiMenu.java b/logo/src/xlogo/kernel/gui/GuiMenu.java
new file mode 100644
index 0000000..2a424f3
--- /dev/null
+++ b/logo/src/xlogo/kernel/gui/GuiMenu.java
@@ -0,0 +1,101 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.gui;
+
+import javax.swing.JComboBox;
+
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+
+import java.util.StringTokenizer;
+
+import xlogo.kernel.Interprete;
+
+import java.awt.event.*;
+
+import xlogo.Application;
+
+public class GuiMenu extends GuiComponent
+{
+ private Application app;
+ private String[] item;
+ private StringBuffer[] action;
+
+ public GuiMenu(String id, String text, Application app)
+ {
+ this.app = app;
+ setId(id);
+ StringTokenizer st = new StringTokenizer(text);
+ item = new String[st.countTokens()];
+ action = new StringBuffer[st.countTokens()];
+ int i = 0;
+ originalWidth = 0;
+ while (st.hasMoreTokens())
+ {
+ item[i] = Utils.SortieTexte(st.nextToken());
+ java.awt.FontMetrics fm = app.getFrame().getGraphics()
+ .getFontMetrics(WSManager.getWorkspaceConfig().getFont());
+ originalWidth = Math.max(originalWidth, fm.stringWidth(item[i]));
+ i++;
+ }
+ originalWidth += 50;
+ guiObject = new JComboBox(item);
+ originalHeight = WSManager.getWorkspaceConfig().getFont().getSize() + 10;
+ setSize(originalWidth, originalHeight);
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ // System.out.println("coucou");
+ int select = ((JComboBox) guiObject).getSelectedIndex();
+ if (!app.commande_isEditable())
+ {
+ Interprete.actionInstruction.append(action[select]);
+ }
+ else
+ {
+ app.startInterpretation(action[select]);
+ }
+ }
+
+ public boolean isButton()
+ {
+ return false;
+ }
+
+ public boolean isMenu()
+ {
+ return true;
+ }
+
+ public void setAction(StringBuffer action, int id)
+ {
+ this.action[id] = action;
+ }
+}
diff --git a/logo/src/xlogo/kernel/network/ChatFrame.java b/logo/src/xlogo/kernel/network/ChatFrame.java
new file mode 100644
index 0000000..227f4ca
--- /dev/null
+++ b/logo/src/xlogo/kernel/network/ChatFrame.java
@@ -0,0 +1,160 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.network;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Toolkit;
+
+import javax.swing.BorderFactory;
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+import javax.swing.JTextPane;
+import javax.swing.JTextField;
+
+import java.awt.event.*;
+import java.awt.Dimension;
+import java.io.PrintWriter;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultStyledDocument;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+
+import java.awt.BorderLayout;
+
+import xlogo.Application;
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+import xlogo.Logo;
+
+public class ChatFrame extends JFrame implements ActionListener
+{
+ private static final long serialVersionUID = 1L;
+
+ private MutableAttributeSet local;
+
+ private MutableAttributeSet distant;
+
+ private JTextPane textPane;
+
+ private DefaultStyledDocument dsd;
+
+ private JScrollPane scroll;
+
+ private JTextField textField;
+
+ private Application app;
+
+ private PrintWriter out;
+
+ protected ChatFrame(PrintWriter out, Application app)
+ {
+ this.app = app;
+ this.out = out;
+ initStyle();
+ initGui();
+ textField.addActionListener(this);
+ }
+
+ private void initStyle()
+ {
+ Font font = WSManager.getWorkspaceConfig().getFont();
+ local = new SimpleAttributeSet();
+ StyleConstants.setFontFamily(local, font.getFamily());
+ StyleConstants.setForeground(local, Color.black);
+ StyleConstants.setFontSize(local, font.getSize());
+
+ distant = new SimpleAttributeSet();
+ StyleConstants.setFontFamily(distant, font.getFamily());
+ StyleConstants.setForeground(distant, Color.RED);
+ StyleConstants.setFontSize(distant, font.getSize());
+ }
+
+ private void initGui()
+ {
+ setTitle(Logo.messages.getString("chat"));
+ setIconImage(Toolkit.getDefaultToolkit().createImage(Utils.class.getResource("icone.png")));
+ dsd = new DefaultStyledDocument();
+ textPane = new JTextPane();
+ textPane.setDocument(dsd);
+ textPane.setEditable(false);
+ textPane.setBackground(new Color(255, 255, 220));
+ this.getContentPane().setLayout(new BorderLayout());
+ textField = new JTextField();
+ java.awt.FontMetrics fm = app.getFrame().getGraphics().getFontMetrics(WSManager.getWorkspaceConfig().getFont());
+ int width = fm.stringWidth(Logo.messages.getString("stop_chat")) + 30;
+ if (width < 200)
+ width = 200;
+
+ textPane.setPreferredSize(new Dimension(width, 300));
+ textField.setPreferredSize(new Dimension(width, WSManager.getWorkspaceConfig().getFont().getSize() + 10));
+ scroll = new JScrollPane(textPane);
+ getContentPane().add(scroll, BorderLayout.CENTER);
+ getContentPane().add(textField, BorderLayout.SOUTH);
+ textPane.setBorder(BorderFactory.createLineBorder(Color.BLUE, 3));
+ textField.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ pack();
+ setVisible(true);
+ }
+
+ protected void append(String sty, String st)
+ {
+ st += "\n";
+ try
+ {
+ if (sty.equals("local"))
+ dsd.insertString(dsd.getLength(), st, local);
+ if (sty.equals("distant"))
+ dsd.insertString(dsd.getLength(), ">" + st, distant);
+ textPane.setCaretPosition(dsd.getLength());
+
+ }
+ catch (BadLocationException e)
+ {}
+ }
+
+ protected void processWindowEvent(WindowEvent e)
+ {
+ if (e.getID() == WindowEvent.WINDOW_CLOSING)
+ {
+ out.println(NetworkServer.END_OF_FILE);
+ dispose();
+ }
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ String txt = textField.getText();
+ append("local", txt);
+ textField.setText("");
+ out.println(txt);
+ }
+}
diff --git a/logo/src/xlogo/kernel/network/NetworkClientChat.java b/logo/src/xlogo/kernel/network/NetworkClientChat.java
new file mode 100644
index 0000000..cf9ce0b
--- /dev/null
+++ b/logo/src/xlogo/kernel/network/NetworkClientChat.java
@@ -0,0 +1,115 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.network;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import xlogo.Application;
+import xlogo.Logo;
+import xlogo.kernel.LogoError;
+import xlogo.storage.WSManager;
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+
+public class NetworkClientChat
+{
+ private InetAddress ip;
+ private String st;
+ private Application app;
+ private PrintWriter out;
+ private BufferedReader in;
+ private Socket socket;
+ ChatFrame cf;
+
+ public NetworkClientChat(Application app, String ip, String st) throws LogoError
+ {
+ this.app = app;
+ try
+ {
+ this.ip = InetAddress.getByName(ip);
+ }
+ catch (UnknownHostException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + " " + ip);
+ }
+ this.st = st;
+ init();
+ }
+
+ private void init() throws LogoError
+ {
+ try
+ {
+ socket = new Socket(ip, WSManager.getUserConfig().getTcpPort());
+ java.io.OutputStream os = socket.getOutputStream();
+ BufferedOutputStream b = new BufferedOutputStream(os);
+ OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8");
+ out = new PrintWriter(osw, true);
+ in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF8"));
+ String cmd = NetworkServer.CHATTCP + "\n";
+ cmd += st + "\n";
+ cmd += NetworkServer.END_OF_FILE;
+ cf = new ChatFrame(out, app);
+ cf.append("local", st);
+ out.println(cmd);
+ while ((cmd = in.readLine()) != null)
+ {
+ if (cmd.equals(NetworkServer.END_OF_FILE))
+ {
+ cf.append("local", Logo.messages.getString("stop_chat"));
+ break;
+ }
+ cf.append("distant", cmd);
+ }
+ out.close();
+ in.close();
+ socket.close();
+ }
+ catch (IOException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + ip.getHostAddress());
+ }
+
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/network/NetworkClientExecute.java b/logo/src/xlogo/kernel/network/NetworkClientExecute.java
new file mode 100644
index 0000000..22c3753
--- /dev/null
+++ b/logo/src/xlogo/kernel/network/NetworkClientExecute.java
@@ -0,0 +1,114 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.network;
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+
+import java.net.Socket;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.UnknownHostException;
+
+import xlogo.storage.WSManager;
+
+import java.net.InetAddress;
+
+import xlogo.Logo;
+import xlogo.kernel.Kernel;
+import xlogo.kernel.LogoError;
+
+public class NetworkClientExecute
+{
+ private InetAddress ip;
+ private String cmd;
+ private PrintWriter out;
+ private BufferedReader in;
+ private Socket socket;
+ private Kernel kernel;
+
+ public NetworkClientExecute(Kernel kernel, String ip, String cmd) throws LogoError
+ {
+ this.kernel = kernel;
+ try
+ {
+ this.ip = InetAddress.getByName(ip);
+ }
+ catch (UnknownHostException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + " " + ip);
+ }
+ this.cmd = cmd;
+ init();
+ }
+
+ private void init() throws LogoError
+ {
+ try
+ {
+ socket = new Socket(ip, WSManager.getUserConfig().getTcpPort());
+ java.io.OutputStream os = socket.getOutputStream();
+ BufferedOutputStream b = new BufferedOutputStream(os);
+ OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8");
+ out = new PrintWriter(osw, true);
+ in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ String wp = NetworkServer.EXECUTETCP + "\n";
+ wp += kernel.getWorkspace().getSerializedContext(); // Marko : changed
+ wp += NetworkServer.END_OF_FILE;
+ out.println(wp);
+ String input;
+ while ((input = in.readLine()) != null)
+ {
+ // System.out.println("je vais envoyer: OK");
+ if (input.equals("OK"))
+ {
+ // System.out.println("chargement reussi");
+ break;
+ }
+ }
+ out.println(cmd);
+ out.close();
+ in.close();
+ socket.close();
+ }
+ catch (IOException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + ip.getHostAddress());
+ }
+ }
+}
diff --git a/logo/src/xlogo/kernel/network/NetworkClientSend.java b/logo/src/xlogo/kernel/network/NetworkClientSend.java
new file mode 100644
index 0000000..2954436
--- /dev/null
+++ b/logo/src/xlogo/kernel/network/NetworkClientSend.java
@@ -0,0 +1,100 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.network;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import xlogo.Logo;
+import xlogo.kernel.LogoError;
+import xlogo.storage.WSManager;
+
+public class NetworkClientSend
+{
+
+ private InetAddress ip;
+ private String data;
+ private BufferedReader in;
+ private PrintWriter out;
+ private Socket socket;
+ private String answer;
+
+ public NetworkClientSend(String ip, String data) throws LogoError
+ {
+ try
+ {
+ this.ip = InetAddress.getByName(ip);
+ }
+ catch (UnknownHostException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + " " + ip);
+ }
+ this.data = data;
+ init();
+ }
+
+ /**
+ * @return
+ * @uml.property name="answer"
+ */
+ public String getAnswer()
+ {
+ return answer;
+ }
+
+ private void init() throws LogoError
+ {
+ try
+ {
+ socket = new Socket(ip, WSManager.getUserConfig().getTcpPort());
+ in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ java.io.OutputStream os = socket.getOutputStream();
+ BufferedOutputStream b = new BufferedOutputStream(os);
+ OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8");
+ out = new PrintWriter(osw, true);
+ out.println(data);
+ answer = "";
+ answer = in.readLine();
+ out.close();
+ socket.close();
+ }
+ catch (IOException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + ip.getHostAddress());
+ }
+ }
+}
diff --git a/logo/src/xlogo/kernel/network/NetworkServer.java b/logo/src/xlogo/kernel/network/NetworkServer.java
new file mode 100644
index 0000000..f12b383
--- /dev/null
+++ b/logo/src/xlogo/kernel/network/NetworkServer.java
@@ -0,0 +1,194 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.network;
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import xlogo.storage.WSManager;
+import xlogo.Application;
+import xlogo.Logo;
+import xlogo.kernel.LogoError;
+import xlogo.kernel.Kernel;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.history.HistoryMessenger;
+
+public class NetworkServer
+{
+ public static boolean isActive;
+ private ServerSocket serverSocket;
+ private Application app;
+ private PrintWriter out;
+ private BufferedReader in;
+ private ChatFrame cf;
+
+ protected static final String EXECUTETCP = "executetcp" + "\u001B";
+ protected static final String CHATTCP = "chattcp" + "\u001B";
+ protected static final String END_OF_FILE = "*/EOF\\*" + "\u001B";
+ private Kernel kernel;
+
+ public NetworkServer(Application app) throws LogoError
+ {
+ isActive = true;
+ this.app = app;
+ this.kernel = app.getKernel();
+ init();
+
+ }
+
+ private void init() throws LogoError
+ {
+ try
+ {
+ serverSocket = new ServerSocket(WSManager.getUserConfig().getTcpPort());
+ }
+ catch (IOException e)
+ {
+ throw (new LogoError(Logo.messages.getString("pb_port") + WSManager.getUserConfig().getTcpPort()));
+ }
+ Socket clientSocket = null;
+ try
+ {
+ clientSocket = serverSocket.accept();
+ java.io.OutputStream os = clientSocket.getOutputStream();
+ BufferedOutputStream b = new BufferedOutputStream(os);
+ OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8");
+ out = new PrintWriter(osw, true);
+ in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "UTF8"));
+ String inputLine = "";
+ inputLine = in.readLine();
+ // ******************* Executetcp **************************
+ if (inputLine.equals(NetworkServer.EXECUTETCP))
+ {
+ StringBuffer remoteNetworkContext = new StringBuffer();
+ while ((inputLine = in.readLine()) != null)
+ {
+ if (inputLine.equals(NetworkServer.END_OF_FILE))
+ break;
+ remoteNetworkContext.append(inputLine);
+ remoteNetworkContext.append("\n");
+ }
+
+ /*
+ * Marko Zivkovic : New implementation of this context switch
+ */
+
+ try
+ {
+ kernel.getWorkspace().pushNetworkMode(remoteNetworkContext.toString());
+ }
+ catch(Exception e)
+ {
+ throw new LogoError(Logo.messages.getString(MessageKeys.ERROR_NETWORK_CONTEXT));
+ }
+
+ // We say the workspace is fully created
+ out.println("OK");
+ // What is the command to execute?
+ inputLine = in.readLine();
+ // System.out.println("a exécuter: "+inputLine);
+ out.close();
+ in.close();
+ clientSocket.close();
+ serverSocket.close();
+
+ kernel.execute(new StringBuffer(inputLine + "\\x "));
+
+ }
+ // ******************* Chattcp **************************
+ else if (inputLine.equals(NetworkServer.CHATTCP))
+ {
+ String sentence = "";
+ while ((inputLine = in.readLine()) != null)
+ {
+ if (inputLine.equals(NetworkServer.END_OF_FILE))
+ break;
+ sentence = inputLine;
+ }
+ cf = new ChatFrame(out, app);
+ cf.append("distant", sentence);
+ while ((sentence = in.readLine()) != null)
+ {
+ if (sentence.equals(NetworkServer.END_OF_FILE))
+ {
+ cf.append("local", Logo.messages.getString("stop_chat"));
+ break;
+ }
+ cf.append("distant", sentence);
+ }
+ out.close();
+ in.close();
+ clientSocket.close();
+ serverSocket.close();
+ }
+ // ******************* Envoietcp **************************
+ else
+ {
+ HistoryMessenger.getInstance().dispatchMessage(inputLine);
+ out.println(Logo.messages.getString("pref.ok"));
+ out.close();
+ in.close();
+ clientSocket.close();
+ serverSocket.close();
+
+ }
+ }
+ catch (IOException e)
+ {
+ throw new LogoError(Logo.messages.getString("erreur_tcp"));
+ }
+ isActive = false;
+ }
+
+ public static void stopServer()
+ {
+ String st = NetworkServer.EXECUTETCP + "\n" + NetworkServer.END_OF_FILE + "\n\n";
+ try
+ {
+ Socket socket = new Socket("127.0.0.1", WSManager.getUserConfig().getTcpPort());
+ PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
+ out.println(st);
+ }
+ catch (IOException e)
+ {}
+ }
+}
diff --git a/logo/src/xlogo/kernel/perspective/Conic.java b/logo/src/xlogo/kernel/perspective/Conic.java
new file mode 100644
index 0000000..6963b57
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/Conic.java
@@ -0,0 +1,214 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import java.math.BigDecimal;
+
+import xlogo.kernel.DrawPanel;
+
+/**
+ * @author Marko : This is now decoupled from Application
+ *
+ */
+public class Conic
+{
+ private DrawPanel dp;
+ BigDecimal a;
+ BigDecimal b;
+ BigDecimal c;
+ BigDecimal d;
+ BigDecimal e;
+ BigDecimal f;
+ BigDecimal deux = new BigDecimal(2);
+ private final BigDecimal un = BigDecimal.ONE;
+ private final BigDecimal zero = BigDecimal.ZERO;
+ BigDecimal[][] A = new BigDecimal[2][2];
+ private BigDecimal[] eigenValue = new BigDecimal[2];
+ private BigDecimal[][] eigenVect = new BigDecimal[2][2];
+ private BigDecimal[] center = new BigDecimal[2];
+ private double halfXAxis;
+ private double halfYAxis;
+
+ public Conic(DrawPanel dp, BigDecimal[] v)
+ {
+ this.dp = dp;
+ this.a = v[0];
+ this.b = v[1];
+ this.c = v[2];
+ this.d = v[3];
+ this.e = v[4];
+ this.f = v[5];
+ A[0][0] = a;
+ A[1][1] = c;
+ // b/2
+ A[0][1] = b.divide(deux);
+ A[1][0] = b.divide(deux);
+ drawConic();
+ }
+
+ private void drawConic()
+ {
+ calculateEigenVect();
+ // ellipse
+ BigDecimal det = det(A);
+ if (det.compareTo(zero) == 1)
+ {
+ // Looking for center
+ calculateCenter();
+ // The main equation becomes with the center as Origin:
+ // aX^2+bXY+cY^2+omega=0
+
+ // double omega=a*Math.pow(center[0],2)+c*Math.pow(center[1],2)
+ // +b*center[0]*center[1]+d*center[0]+e*center[1]+f;
+
+ BigDecimal omega = a.multiply(center[0].pow(2)).add(c.multiply(center[1].pow(2)))
+ .add(center[0].multiply(center[1]).multiply(b)).add(d.multiply(center[0]))
+ .add(e.multiply(center[1])).add(f);
+
+ // We apply the rotation with the Eigen Vect Matrix
+ // Now the equation becomes: lambda*X^2+mu*Y^2+omega=0
+ // with lambda and mu the eigen Values
+ // 1/sqrt(eigenValue[0]/-omega)
+ halfXAxis = 1 / Math.sqrt(eigenValue[0].divide(omega.negate(), 20, BigDecimal.ROUND_HALF_EVEN)
+ .doubleValue());
+ // 1/sqrt(eigenValue[1]/-omega)
+ halfYAxis = 1 / Math.sqrt(eigenValue[1].divide(omega.negate(), 20, BigDecimal.ROUND_HALF_EVEN)
+ .doubleValue());
+ double angle = Math.atan(eigenVect[1][0].divide(eigenVect[0][0], 20, BigDecimal.ROUND_HALF_EVEN)
+ .doubleValue());
+ // System.out.println(toString());
+ dp.drawEllipseArc(halfXAxis, halfYAxis, angle, center[0].doubleValue(),
+ center[1].doubleValue(), 0, 360);
+
+ }
+ // hyperbola
+ else if (det.compareTo(zero) == -1)
+ {
+ calculateCenter();
+
+ }
+ // parabola
+ else
+ {}
+ }
+
+ private void calculateEigenValue()
+ {
+ // Polynom: acX^2-(a+c)X+A[0][1]^2
+ // tmp=a+c
+ BigDecimal tmp = A[0][0].add(A[1][1]);
+ // Discriminant delta=(a-c)^2+4*A[0][1]^2
+ BigDecimal delta = A[0][0].subtract(A[1][1]).pow(2).add(new BigDecimal(4).multiply(A[0][1].pow(2)));
+ // calculate the eigen Values
+ eigenValue[0] = tmp.subtract(sqrt(delta)).divide(deux);
+ eigenValue[1] = tmp.add(sqrt(delta)).divide(deux);
+ }
+
+ private void calculateEigenVect()
+ {
+ if (A[0][1].compareTo(zero) == 0)
+ {
+ eigenValue[0] = A[0][0];
+ eigenValue[1] = A[1][1];
+ eigenVect[0][0] = un;
+ eigenVect[1][0] = zero;
+ eigenVect[0][1] = zero;
+ eigenVect[1][1] = un;
+ }
+ else
+ {
+ calculateEigenValue();
+ BigDecimal tmp = A[0][0].subtract(eigenValue[0]);
+ eigenVect[0][0] = un;
+ // System.out.println("pb: "+tmp+"\n"+A[0][1]);
+ eigenVect[1][0] = tmp.negate().divide(A[0][1], 20, BigDecimal.ROUND_HALF_EVEN);
+ // vector length and then normalize
+ tmp = sqrt(eigenVect[0][0].pow(2).add(eigenVect[1][0].pow(2)));
+ eigenVect[0][0] = eigenVect[0][0].divide(tmp, 20, BigDecimal.ROUND_HALF_EVEN);
+ eigenVect[1][0] = eigenVect[1][0].divide(tmp, 20, BigDecimal.ROUND_HALF_EVEN);
+ eigenVect[0][1] = eigenVect[1][0].negate();
+ eigenVect[1][1] = un;
+
+ }
+ }
+
+ private void calculateCenter()
+ {
+ // System determinant
+ // df/dx=0 and df/dy=0
+
+ // double det=4*a*c-Math.pow(b,2);
+ BigDecimal det = new BigDecimal(4).multiply(a).multiply(c).subtract(b.pow(2));
+ // xCenter=(-2*c*d+b*e)/det;
+ center[0] = (deux.negate().multiply(c).multiply(d).add(b.multiply(e))).divide(det, 20,
+ BigDecimal.ROUND_HALF_EVEN);
+ // yCenter=(-2*a*e+b*d)/det;
+ center[1] = (deux.negate().multiply(a).multiply(e).add(b.multiply(d))).divide(det, 20,
+ BigDecimal.ROUND_HALF_EVEN);
+ }
+
+ private BigDecimal det(BigDecimal[][] M)
+ {
+ return M[0][0].multiply(M[1][1]).subtract(M[0][1].multiply(M[1][0]));
+ }
+
+ public String toString()
+ {
+ String st = "centre: " + center[0] + " " + center[1] + "\n" + "Valeurs propres: " + eigenValue[0] + " "
+ + eigenValue[1] + "\n" + "Vecteurs propres:\n" + eigenVect[0][0] + " " + eigenVect[0][1] + "\n"
+ + eigenVect[1][0] + " " + eigenVect[1][1] + "\n" + "Demi Axe: X " + halfXAxis + " Y " + halfYAxis;
+ return st;
+ }
+
+ private BigDecimal sqrt(BigDecimal b)
+ {
+ BigDecimal precision = new BigDecimal(0.00000000001);
+ int scale = precision.scale();
+ if (b.scale() < scale)
+ {
+ b = b.setScale(scale);
+ }
+ // Initial guess
+ // G = A/2
+ BigDecimal g = new BigDecimal(Math.sqrt(b.doubleValue()));
+
+ // Iterate until we're done
+ while (true)
+ {
+ // G* = ((A/G)+G)/2
+ BigDecimal gStar = b.divide(g, BigDecimal.ROUND_HALF_EVEN).add(g).divide(deux, BigDecimal.ROUND_HALF_EVEN);
+ BigDecimal delta = gStar.subtract(g);
+ delta = delta.abs();
+ if (delta.compareTo(precision) < 0)
+ break;
+ g = gStar;
+ }
+ return g;
+ }
+}
diff --git a/logo/src/xlogo/kernel/perspective/Element3D.java b/logo/src/xlogo/kernel/perspective/Element3D.java
new file mode 100644
index 0000000..d3473cf
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/Element3D.java
@@ -0,0 +1,115 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import java.awt.Color;
+import java.util.Vector;
+
+import javax.vecmath.Point3d;
+
+import xlogo.kernel.LogoError;
+
+/**
+ * @author Marko - I decoupled this and its subclasses from Application
+ *
+ */
+abstract public class Element3D
+{
+ protected Viewer3D v3d;
+
+ /**
+ * Color for each vertex
+ *
+ * @uml.property name="color"
+ * @uml.associationEnd multiplicity="(0 -1)" elementType="java.awt.Color"
+ */
+ protected Vector<Color> color;
+
+ /**
+ * Vertex coordinates in virtual world
+ *
+ * @uml.property name="vertex"
+ * @uml.associationEnd multiplicity="(0 -1)"
+ * elementType="javax.vecmath.Point3d"
+ */
+ protected Vector<Point3d> vertex;
+
+ public Element3D(Viewer3D v3d)
+ {
+ this.v3d = v3d;
+ vertex = new Vector<Point3d>();
+ color = new Vector<Color>();
+
+ }
+
+ public void addVertex(Point3d p, Color c)
+ {
+ vertex.add(p);
+ color.add(c);
+ }
+
+ /**
+ * This method calculates all attributes for polygon and add it in the 3D
+ * Viewer
+ */
+ abstract public void addToScene() throws LogoError;
+
+ /**
+ * This method indicates if the Element3D is a Point.
+ *
+ * @return true if this Element3D is a Point, false otherwise
+ */
+ abstract public boolean isPoint();
+
+ /**
+ * This method indicates if the Element3D is a Polygon.
+ *
+ * @return true if this Element3D is a Polygon, false otherwise
+ */
+ abstract public boolean isPolygon();
+
+ /**
+ * This method indicates if the Element3D is a line.
+ *
+ * @return true if this Element3D is a line, false otherwise
+ */
+ abstract public boolean isLine();
+
+ /**
+ * This method indicates if the Element3D is a Text2D.
+ *
+ * @return true if this Element3D is a Text2D, false otherwise
+ */
+ abstract public boolean isText();
+
+ public int getVertexCount()
+ {
+ return vertex.size();
+ }
+}
diff --git a/logo/src/xlogo/kernel/perspective/ElementLine.java b/logo/src/xlogo/kernel/perspective/ElementLine.java
new file mode 100644
index 0000000..5aa0a8a
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/ElementLine.java
@@ -0,0 +1,374 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import javax.media.j3d.Appearance;
+import javax.media.j3d.LineStripArray;
+import javax.media.j3d.Material;
+import javax.media.j3d.PolygonAttributes;
+import javax.media.j3d.Shape3D;
+import javax.media.j3d.LineAttributes;
+import javax.media.j3d.TransformGroup;
+
+import com.sun.j3d.utils.geometry.Sphere;
+
+import javax.media.j3d.Transform3D;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3d;
+import javax.vecmath.AxisAngle4d;
+
+import com.sun.j3d.utils.geometry.Cylinder;
+
+import javax.vecmath.Vector3d;
+
+import xlogo.kernel.LogoError;
+
+/**
+ *
+ * @author Marko Zivkovic - I decoupled this from Application
+ *
+ */
+public class ElementLine extends Element3D
+{
+ /**
+ * This float stores the line Width for each 3D line
+ *
+ * @uml.property name="lineWidth"
+ */
+ private float lineWidth;
+
+ public ElementLine(Viewer3D v3d, float lineWidth)
+ {
+ super(v3d);
+ this.lineWidth = lineWidth; //app.getKernel().getActiveTurtle().getPenWidth();
+ }
+
+ public void addToScene() throws LogoError
+ {
+ int size = vertex.size();
+ if (size > 1)
+ {
+ if (lineWidth == 0.5)
+ createSimpleLine(size);
+ else
+ createComplexLine(size);
+ }
+ }
+
+ /**
+ * This method draws a line with width 1
+ *
+ * @param size
+ */
+ private void createSimpleLine(int size)
+ {
+ int[] tab = { size };
+ LineStripArray line = new LineStripArray(size, LineStripArray.COORDINATES | LineStripArray.COLOR_3, tab);
+ for (int i = 0; i < size; i++)
+ {
+ line.setCoordinate(i, vertex.get(i));
+ // System.out.println("sommet "+(2*i-1)+" "+vertex.get(i).x+" "+vertex.get(i).y+" "+vertex.get(i).z+" ");
+ line.setColor(i, new Color3f(color.get(i)));
+ }
+ Shape3D s = new Shape3D(line);
+ Appearance appear = new Appearance();
+ Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), new Color3f(0.0f, 0f, 0f), new Color3f(1f, 1.0f,
+ 1.0f), new Color3f(1f, 1f, 1f), 64);
+ appear.setMaterial(mat);
+ appear.setLineAttributes(new LineAttributes(2 * lineWidth, LineAttributes.PATTERN_SOLID, false));
+ s.setAppearance(appear);
+ // DrawPanel.listPoly.add(s);
+ v3d.add3DObject(s);
+ /*
+ * for (int i=0;i<line.getVertexCount();i++){
+ * double[] d=new double[3];
+ * line.getCoordinate(i, d);
+ * for(int j=0;j<d.length;j++){
+ * System.out.println(i+" "+d[j]);
+ * }
+ * }
+ */
+ }
+
+ /**
+ * This method draws a sequence of lines with a width different from 1
+ * Draws a cylinder around the line
+ *
+ * @param size
+ */
+
+ private void createComplexLine(int size)
+ {
+ for (int i = 0; i < size - 1; i++)
+ {
+ createLine(vertex.get(i), vertex.get(i + 1), new Color3f(color.get(i + 1)));
+ }
+
+ }
+
+ /**
+ * This method creates an elementary line with a width different from 1
+ * It draws a cylinder around the line and two spheres on each extremities
+ *
+ * @param p1
+ * the first point
+ * @param p2
+ * the second point
+ * @param color
+ * the sphere color
+ */
+ private void createLine(Point3d p1, Point3d p2, Color3f color)
+ {
+ // create a new CylinderTransformer between the two atoms
+ CylinderTransformer cT = new CylinderTransformer(p1, p2);
+
+ // get the length
+ double length = cT.getLength();
+ float radius = lineWidth / 1000;
+ // holds the translation
+ Transform3D transform1 = new Transform3D();
+ // ...move the coordinates there
+ transform1.setTranslation(cT.getTranslation());
+
+ // get the axis and angle for rotation
+ AxisAngle4d rotation = cT.getAxisAngle();
+ Transform3D transform2 = new Transform3D();
+ transform2.setRotation(rotation);
+
+ // combine the translation and rotation into transform1
+ transform1.mul(transform2);
+
+ // create a new Cylinder
+ Appearance appear = new Appearance();
+ Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), color, new Color3f(1f, 1.0f, 1.0f), new Color3f(1f,
+ 1f, 1f), 64);
+ appear.setMaterial(mat);
+ PolygonAttributes pa = new PolygonAttributes();
+ pa.setCullFace(PolygonAttributes.CULL_NONE);
+ pa.setBackFaceNormalFlip(true);
+ appear.setPolygonAttributes(pa);
+ Cylinder cyl = new Cylinder(radius, (float) length, appear);
+
+ // and add it to the TransformGroup
+ TransformGroup tg = new TransformGroup();
+ tg.setTransform(transform1);
+ tg.addChild(cyl);
+ v3d.add2DText(tg);
+
+ // Add Sphere to the line extremities.
+ Sphere sphere1 = new Sphere(radius, appear);
+ TransformGroup tg2 = new TransformGroup();
+ Transform3D transform3 = new Transform3D();
+ transform3.setTranslation(new Vector3d(p1));
+ tg2.setTransform(transform3);
+ tg2.addChild(sphere1);
+ v3d.add2DText(tg2);
+
+ Sphere sphere2 = new Sphere(radius, appear);
+ TransformGroup tg3 = new TransformGroup();
+ Transform3D transform4 = new Transform3D();
+ transform4.setTranslation(new Vector3d(p2));
+ tg3.setTransform(transform4);
+ tg3.addChild(sphere2);
+ v3d.add2DText(tg3);
+
+ }
+
+ public boolean isLine()
+ {
+ return true;
+ }
+
+ public boolean isPoint()
+ {
+ return false;
+ }
+
+ public boolean isPolygon()
+ {
+ return false;
+ }
+
+ public boolean isText()
+ {
+ return false;
+ }
+
+ /**
+ * A class to calculate the length and transformations necessary to produce
+ * a cylinder to connect two points. Useful for Java3D and VRML where a
+ * cylinder object is created aligned along the y-axis.
+ *
+ * @author Alastair Hill
+ */
+ class CylinderTransformer
+ {
+
+ /** point A */
+ private final Point3d pointA;
+ /** point B */
+ private final Point3d pointB;
+ /**
+ * the angle through which to rotate the cylinder
+ */
+ private double angle;
+ /**
+ * the axis around which to rotate the cylinder
+ *
+ */
+ private Vector3d axis;
+ /**
+ * The translation required to translate the cylinder to the midpoint of
+ * the two points
+ */
+ private Vector3d translation;
+ /**
+ * The length of the cylinder required to join the two points
+ *
+ */
+ private double length;
+
+ /**
+ * Creates a new instance of CylinderTransformer
+ *
+ * @param a
+ * point a
+ * @param b
+ * point b
+ */
+ CylinderTransformer(Point3d a, Point3d b)
+ {
+ pointA = a;
+ pointB = b;
+
+ // carry out the calculations
+ doCalculations();
+ }
+
+ /**
+ * Carries out the necessary calculations so that values may be returned
+ */
+ private void doCalculations()
+ {
+ length = pointA.distance(pointB);
+
+ double[] arrayA = new double[3];
+ pointA.get(arrayA);
+ double[] arrayB = new double[3];
+ pointB.get(arrayB);
+ double[] arrayMid = new double[3];
+
+ for (int i = 0; i < arrayA.length; i++)
+ {
+ arrayMid[i] = (arrayA[i] + arrayB[i]) / 2f;
+ }
+
+ // the translation needed is
+ translation = new Vector3d(arrayMid);
+
+ // the initial orientation of the bond is in the y axis
+ Vector3d init = new Vector3d(0.0f, 1.0f, 0.0f);
+
+ // the vector needed is the same as that from a to b
+ Vector3d needed = new Vector3d(pointB.x - pointA.x, pointB.y - pointA.y, pointB.z - pointA.z);
+
+ // so the angle to rotate the bond by is:
+ angle = needed.angle(init);
+
+ // and the axis to rotate by is the cross product of the initial and
+ // needed vectors - ie the vector orthogonal to them both
+ axis = new Vector3d();
+ axis.cross(init, needed);
+ }
+
+ /**
+ * Returns the angle (in radians) through which to rotate the cylinder
+ *
+ * @return the angle.
+ */
+ double getAngle()
+ {
+ return angle;
+ }
+
+ /**
+ * The axis around which the cylinder must be rotated
+ *
+ * @return the axis
+ */
+ Vector3d getAxis()
+ {
+ return axis;
+ }
+
+ /**
+ * The length required for the cylinder to join the two points
+ *
+ * @return the length
+ */
+ double getLength()
+ {
+ return length;
+ }
+
+ /**
+ * The translation required to move the cylinder to the midpoint of the
+ * two points
+ *
+ * @return the translation
+ */
+ Vector3d getTranslation()
+ {
+ return translation;
+ }
+
+ /**
+ * Generates a (pretty) string representation of the the
+ * CylinderTransformer
+ *
+ * @return the string representation
+ */
+ public String toString()
+ {
+ return "tr: " + translation + ", ax: " + axis + ", an: " + angle + ", le: " + length;
+ }
+
+ /**
+ * Generates the required axis and angle combined into an AxisAngle4f
+ * object
+ *
+ * @return the axis and angle
+ */
+ public AxisAngle4d getAxisAngle()
+ {
+ return new AxisAngle4d(axis.x, axis.y, axis.z, angle);
+ }
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/ElementPoint.java b/logo/src/xlogo/kernel/perspective/ElementPoint.java
new file mode 100644
index 0000000..c77011a
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/ElementPoint.java
@@ -0,0 +1,129 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import javax.media.j3d.Appearance;
+import javax.media.j3d.Material;
+import javax.media.j3d.PointArray;
+import javax.media.j3d.PolygonAttributes;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3d;
+import javax.vecmath.Color3f;
+import javax.vecmath.Vector3d;
+
+import xlogo.kernel.LogoError;
+
+import javax.media.j3d.Shape3D;
+
+import com.sun.j3d.utils.geometry.Sphere;
+
+/**
+ * @author Marko - I decoupled this from Application
+ */
+public class ElementPoint extends Element3D
+{
+ float pointWidth;
+
+ public ElementPoint(Viewer3D v3d, float pointWidth)
+ {
+ super(v3d);
+ this.pointWidth = pointWidth; //app.getKernel().getActiveTurtle().getPenWidth();
+ }
+
+ public void addToScene() throws LogoError
+ {
+ if (vertex.size() == 0)
+ return;
+ if (pointWidth == 0.5)
+ {
+ int[] tab = new int[1];
+ tab[0] = vertex.size();
+ PointArray point = new PointArray(vertex.size(), PointArray.COORDINATES | PointArray.COLOR_3);
+ for (int i = 0; i < vertex.size(); i++)
+ {
+ point.setCoordinate(i, vertex.get(i));
+ point.setColor(i, new Color3f(color.get(i)));
+ }
+ v3d.add3DObject(new Shape3D(point));
+ }
+ else
+ {
+ for (int i = 0; i < vertex.size(); i++)
+ {
+ createBigPoint(vertex.get(i), new Color3f(color.get(i)));
+ }
+
+ }
+ }
+
+ private void createBigPoint(Point3d p, Color3f color)
+ {
+ // Add a Sphere to main 3D scene.
+ Appearance appear = new Appearance();
+ Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), color,// new
+ // Color3f(0.0f,0f,0f),
+ new Color3f(1f, 1.0f, 1.0f), new Color3f(1f, 1f, 1f), 64);
+ appear.setMaterial(mat);
+ PolygonAttributes pa = new PolygonAttributes();
+ pa.setCullFace(PolygonAttributes.CULL_NONE);
+ pa.setBackFaceNormalFlip(true);
+ appear.setPolygonAttributes(pa);
+
+ Sphere sphere = new Sphere(pointWidth / 1000, appear);
+
+ TransformGroup tg = new TransformGroup();
+ Transform3D transform = new Transform3D();
+ transform.setTranslation(new Vector3d(p));
+ tg.setTransform(transform);
+ tg.addChild(sphere);
+ v3d.add2DText(tg);
+ }
+
+ public boolean isLine()
+ {
+ return false;
+ }
+
+ public boolean isPoint()
+ {
+ return true;
+ }
+
+ public boolean isPolygon()
+ {
+ return false;
+ }
+
+ public boolean isText()
+ {
+ return false;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/ElementPolygon.java b/logo/src/xlogo/kernel/perspective/ElementPolygon.java
new file mode 100644
index 0000000..d0aa85b
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/ElementPolygon.java
@@ -0,0 +1,179 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.perspective;
+
+import javax.media.j3d.Appearance;
+import javax.media.j3d.Material;
+import javax.media.j3d.PolygonAttributes;
+import javax.media.j3d.TriangleFanArray;
+import javax.vecmath.Point3d;
+import javax.media.j3d.Shape3D;
+import javax.vecmath.Vector3f;
+import javax.vecmath.Color3f;
+
+import xlogo.Logo;
+import xlogo.kernel.LogoError;
+
+/**
+ * This class represent A polygon surface in 3D mode
+ *
+ * @author loic
+ *
+ * @author Marko Zivkovic - I decoupled this from Application
+ */
+public class ElementPolygon extends Element3D
+{
+ public ElementPolygon(Viewer3D v3d)
+ {
+ super(v3d);
+ }
+
+ /**
+ * This method calculates all attributes for polygon and add it to the
+ * Polygon's list
+ */
+ public void addToScene() throws LogoError
+ {
+
+ if (vertex.size() < 3)
+ throw new LogoError(Logo.messages.getString("error.3d.3vertex"));
+
+ // Create normal vector
+
+ Point3d origine = vertex.get(0);
+ // System.out.println(" origine "+origine.x+" "+origine.y+" "+origine.z);
+
+ Point3d point1;
+ Vector3f vec1 = null;
+ Vector3f vec2 = null;
+ for (int i = 1; i < vertex.size(); i++)
+ {
+ point1 = vertex.get(i);
+ if (!point1.equals(origine))
+ {
+ // System.out.println(" point1 "+point1.x+" "+point1.y+" "+point1.z);
+ vec1 = new Vector3f((float) (point1.x - origine.x), (float) (point1.y - origine.y),
+ (float) (point1.z - origine.z));
+ break;
+ }
+ }
+ if (null == vec1)
+ throw new LogoError(Logo.messages.getString("error.3d.emptypolygon"));
+ for (int i = 2; i < vertex.size(); i++)
+ {
+ point1 = vertex.get(i);
+ // System.out.println(" point1 "+point1.x+" "+point1.y+" "+point1.z);
+ vec2 = new Vector3f((float) (point1.x - origine.x), (float) (point1.y - origine.y),
+ (float) (point1.z - origine.z));
+ if (vec1.dot(vec2) == 0)
+ vec2 = null;
+ else
+ {
+ // System.out.println(" vec1 "+vec1.x+" "+vec1.y+" "+vec1.z);
+ // System.out.println(" vec2 "+vec2.x+" "+vec2.y+" "+vec2.z);
+ vec2.cross(vec1, vec2);
+ vec2.normalize();
+ vec1 = new Vector3f(vec2);
+ vec1.negate();
+ // System.out.println("Après"+" vec1 "+vec1.x+" "+vec1.y+" "+vec1.z);
+ // System.out.println("Après"+" vec2 "+vec2.x+" "+vec2.y+" "+vec2.z);
+ // if (vec1.x!=0&& vec1.y!=0&& vec1.z!=0)
+ // System.out.println("coucou"+" vec1 "+vec1.x+" "+vec1.y+" "+vec1.z);
+ break;
+ }
+ if (null == vec2)
+ throw new LogoError(Logo.messages.getString("error.3d.emptypolygon"));
+ }
+
+ // Create Geometry
+
+ int[] tab = new int[1];
+ tab[0] = vertex.size();
+ TriangleFanArray tfa = new TriangleFanArray(vertex.size(), TriangleFanArray.COORDINATES
+ | TriangleFanArray.COLOR_3 | TriangleFanArray.NORMALS, tab);
+ // TriangleFanArray tfa2=new
+ // TriangleFanArray(vertex.size(),TriangleFanArray.COORDINATES|TriangleFanArray.COLOR_3|TriangleFanArray.NORMALS,tab);
+ for (int i = 0; i < vertex.size(); i++)
+ {
+
+ tfa.setCoordinate(i, vertex.get(i));
+ // tfa2.setCoordinate(i, vertex.get(vertex.size()-1-i));
+
+ tfa.setColor(i, new Color3f(color.get(i)));
+ // tfa2.setColor(i, new Color3f(color.get(color.size()-i-1)));
+
+ tfa.setNormal(i, vec2);
+ // tfa2.setNormal(i, vec1);
+
+ }
+
+ Shape3D s = new Shape3D(tfa);
+ Appearance appear = new Appearance();
+ Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), new Color3f(0.0f, 0f, 0f), new Color3f(1f, 1.0f,
+ 1.0f), new Color3f(1f, 1f, 1f), 64);
+ appear.setMaterial(mat);
+ PolygonAttributes pa = new PolygonAttributes();
+ pa.setCullFace(PolygonAttributes.CULL_NONE);
+ pa.setBackFaceNormalFlip(true);
+ appear.setPolygonAttributes(pa);
+
+ s.setAppearance(appear);
+ v3d.add3DObject(s);
+ // DrawPanel.listPoly.add(s);
+ // DrawPanel.listPoly.add(new Shape3D(tfa2));
+ // System.out.println(DrawPanel.listPoly.size()+" "+vertex.get(i).x+" "+vertex.get(i).y+" "+vertex.get(i).z);
+ }
+
+ public boolean isPoint()
+ {
+ return false;
+ }
+
+ public boolean isPolygon()
+ {
+ return true;
+ }
+
+ public boolean isLine()
+ {
+ return false;
+ }
+
+ public boolean isText()
+ {
+ return false;
+ }
+}
diff --git a/logo/src/xlogo/kernel/perspective/FogDialog.java b/logo/src/xlogo/kernel/perspective/FogDialog.java
new file mode 100644
index 0000000..24b8bd1
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/FogDialog.java
@@ -0,0 +1,256 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import xlogo.Logo;
+import xlogo.storage.WSManager;
+
+public class FogDialog extends JDialog implements ActionListener
+{
+ private static final long serialVersionUID = 1L;
+
+ private String[] type = { Logo.messages.getString("3d.fog.none"),
+ Logo.messages.getString("3d.fog.linear"), Logo.messages.getString("3d.fog.exponential") };
+
+ private JComboBox comboType;
+
+ private PanelValue panelDensity;
+
+ private PanelValue panelFront;
+
+ private PanelValue panelBack;
+
+ private JLabel labelType;
+
+ private JButton ok;
+
+ private JButton refresh;
+
+ private Viewer3D viewer3d;
+
+ private MyFog fog;
+
+ FogDialog(Viewer3D viewer3d, MyFog fog, String title)
+ {
+ super(viewer3d, title, true);
+ this.viewer3d = viewer3d;
+ this.fog = fog;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ getContentPane().setLayout(new GridBagLayout());
+ setSize(450, 200);
+ labelType = new JLabel(Logo.messages.getString("3d.fog.type"));
+ comboType = new JComboBox(type);
+ comboType.setSelectedIndex(fog.getType());
+
+ panelDensity = new PanelValue(fog.getDensity(), Logo.messages.getString("3d.fog.density"));
+ panelFront = new PanelValue((int) (fog.getFront() * 1000), Logo.messages.getString("3d.fog.frontdistance"));
+ panelBack = new PanelValue((int) (fog.getBack() * 1000), Logo.messages.getString("3d.fog.backdistance"));
+
+ Font font = WSManager.getWorkspaceConfig().getFont();
+
+ ok = new JButton(Logo.messages.getString("pref.ok"));
+ refresh = new JButton(Logo.messages.getString("3d.light.apply"));
+ labelType.setFont(font);
+ comboType.setFont(font);
+ ok.setFont(font);
+ refresh.setFont(font);
+
+ comboType.addActionListener(this);
+ comboType.setActionCommand("combo");
+ ok.addActionListener(this);
+ ok.setActionCommand("ok");
+ refresh.addActionListener(this);
+ refresh.setActionCommand("refresh");
+
+ getContentPane().add(
+ labelType,
+ new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ comboType,
+ new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelFront,
+ new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelBack,
+ new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelDensity,
+ new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ refresh,
+ new GridBagConstraints(0, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ ok,
+ new GridBagConstraints(1, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ selectComponents();
+ setVisible(true);
+
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ String cmd = e.getActionCommand();
+ // The selected item in the combo Box has changed
+ if (cmd.equals("combo"))
+ selectComponents();
+ // Button Ok pressed
+ else if (cmd.equals("ok"))
+ {
+ updateFog();
+ dispose();
+ }
+ // Button Apply pressed
+ else if (cmd.equals("refresh"))
+ {
+ updateFog();
+ }
+ }
+
+ private void updateFog()
+ {
+ int t = comboType.getSelectedIndex();
+ float d = panelDensity.getTextValue();
+ float back = panelBack.getTextValue();
+ float front = panelFront.getTextValue();
+ fog.setType(t);
+ fog.setDensity(d);
+ fog.setBack(back / 1000);
+ fog.setFront(front / 1000);
+ fog.detach();
+ fog.removeAllChildren();
+ fog.createFog();
+ viewer3d.addNode(fog);
+ }
+
+ private void selectComponents()
+ {
+ int id = comboType.getSelectedIndex();
+ // None
+ if (id == MyFog.FOG_OFF)
+ {
+ panelDensity.setEnabled(false);
+ panelBack.setEnabled(false);
+ panelFront.setEnabled(false);
+ }
+ // Linear Fog
+ else if (id == MyFog.FOG_LINEAR)
+ {
+ panelDensity.setEnabled(false);
+ panelBack.setEnabled(true);
+ panelFront.setEnabled(true);
+ }
+ // Exponential Fog
+ else if (id == MyFog.FOG_EXPONENTIAL)
+ {
+ panelBack.setEnabled(false);
+ panelFront.setEnabled(false);
+ panelDensity.setEnabled(true);
+ }
+ }
+
+ class PanelValue extends JPanel
+ {
+ private static final long serialVersionUID = 1L;
+ private JLabel label;
+ private JTextField text;
+ private String title;
+ private float textValue;
+
+ PanelValue(float textValue, String title)
+ {
+ this.textValue = textValue;
+ this.title = title;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ label = new JLabel(title);
+ label.setFont(WSManager.getWorkspaceConfig().getFont());
+ String st;
+ int i = (int) textValue;
+ if (i == textValue)
+ {
+ st = String.valueOf(i);
+
+ }
+ else
+ st = String.valueOf(textValue);
+ text = new JTextField(st, 4);
+ add(label);
+ add(text);
+ }
+
+ public void setEnabled(boolean b)
+ {
+ super.setEnabled(b);
+ label.setEnabled(b);
+ text.setEnabled(b);
+ }
+
+ float getTextValue()
+ {
+ try
+ {
+ float f = Float.parseFloat(text.getText());
+ return f;
+ }
+ catch (NumberFormatException e)
+ {}
+ return 0;
+ }
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/LightDialog.java b/logo/src/xlogo/kernel/perspective/LightDialog.java
new file mode 100644
index 0000000..13aa671
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/LightDialog.java
@@ -0,0 +1,378 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Color;
+
+import xlogo.Logo;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Tuple3f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+import javax.swing.BorderFactory;
+import javax.swing.JDialog;
+import javax.swing.JComboBox;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.JLabel;
+import javax.swing.JButton;
+import javax.swing.border.TitledBorder;
+
+import java.awt.event.*;
+
+import xlogo.gui.preferences.PanelColor;
+import xlogo.storage.WSManager;
+
+public class LightDialog extends JDialog implements ActionListener
+{
+ private static final long serialVersionUID = 1L;
+ private String[] type = { Logo.messages.getString("3d.light.none"),
+ Logo.messages.getString("3d.light.ambient"), Logo.messages.getString("3d.light.directional"),
+ Logo.messages.getString("3d.light.point"), Logo.messages.getString("3d.light.spot") };
+
+ private JComboBox comboType;
+
+ PanelColor panelColor;
+
+ private PanelPosition panelPosition;
+
+ private PanelPosition panelDirection;
+
+ private PanelAngle panelAngle;
+
+ private JLabel labelType;
+
+ private JButton ok;
+
+ private JButton refresh;
+
+ private Viewer3D viewer3d;
+
+ private MyLight light;
+
+ LightDialog(Viewer3D viewer3d, MyLight light, String title)
+ {
+ super(viewer3d, title, true);
+ this.viewer3d = viewer3d;
+ this.light = light;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ getContentPane().setLayout(new GridBagLayout());
+ setSize(500, 200);
+ labelType = new JLabel(Logo.messages.getString("3d.light.type"));
+ comboType = new JComboBox(type);
+ comboType.setSelectedIndex(light.getType());
+
+ Color3f col = light.getColor();
+ if (null != col)
+ panelColor = new PanelColor(col.get());
+ else
+ panelColor = new PanelColor(Color.white);
+ panelColor.setBackground(comboType.getBackground());
+
+ panelPosition = new PanelPosition(Logo.messages.getString("3d.light.position"), light.getPosition());
+ panelDirection = new PanelPosition(Logo.messages.getString("3d.light.direction"), light.getDirection());
+ panelAngle = new PanelAngle(light.getAngle());
+ ok = new JButton(Logo.messages.getString("pref.ok"));
+ refresh = new JButton(Logo.messages.getString("3d.light.apply"));
+
+ Font font = WSManager.getWorkspaceConfig().getFont();
+
+ labelType.setFont(font);
+ comboType.setFont(font);
+ ok.setFont(font);
+ refresh.setFont(font);
+
+ comboType.addActionListener(this);
+ comboType.setActionCommand("combo");
+ ok.addActionListener(this);
+ ok.setActionCommand("ok");
+ refresh.addActionListener(this);
+ refresh.setActionCommand("refresh");
+
+ getContentPane().add(
+ labelType,
+ new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ comboType,
+ new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelColor,
+ new GridBagConstraints(0, 1, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelPosition,
+ new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelDirection,
+ new GridBagConstraints(1, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelAngle,
+ new GridBagConstraints(0, 3, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ refresh,
+ new GridBagConstraints(0, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ ok,
+ new GridBagConstraints(1, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ selectComponents();
+ setVisible(true);
+
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ String cmd = e.getActionCommand();
+ // The selected item in the combo Box has changed
+ if (cmd.equals("combo"))
+ selectComponents();
+ // Button Ok pressed
+ else if (cmd.equals("ok"))
+ {
+ updateLight();
+ dispose();
+ }
+ // Button Apply pressed
+ else if (cmd.equals("refresh"))
+ {
+ updateLight();
+ }
+ }
+
+ private void updateLight()
+ {
+ int t = comboType.getSelectedIndex();
+ Color3f c = new Color3f(panelColor.getValue());
+ Point3f p = panelPosition.getPosition();
+ Vector3f d = panelDirection.getDirection();
+ float a = panelAngle.getAngleValue();
+ light.setType(t);
+ light.setColor(c);
+ light.setPosition(p);
+ light.setDirection(d);
+ light.setAngle(a);
+ light.detach();
+ light.removeAllChildren();
+ light.createLight();
+ // System.out.println(c+" "+" "+p+" "+d);
+ viewer3d.addNode(light);
+
+ }
+
+ private void selectComponents()
+ {
+ int id = comboType.getSelectedIndex();
+ // None
+ if (id == MyLight.LIGHT_OFF)
+ {
+ panelColor.setEnabled(false);
+ panelPosition.setEnabled(false);
+ panelDirection.setEnabled(false);
+ panelAngle.setEnabled(false);
+ }
+ // Ambient
+ else if (id == MyLight.LIGHT_AMBIENT)
+ {
+ panelColor.setEnabled(true);
+ panelPosition.setEnabled(false);
+ panelDirection.setEnabled(false);
+ panelAngle.setEnabled(false);
+ }
+ // Directional
+ else if (id == MyLight.LIGHT_DIRECTIONAL)
+ {
+ panelColor.setEnabled(true);
+ panelPosition.setEnabled(false);
+ panelDirection.setEnabled(true);
+ panelAngle.setEnabled(false);
+ }
+ // PointLight
+ else if (id == MyLight.LIGHT_POINT)
+ {
+ panelColor.setEnabled(true);
+ panelPosition.setEnabled(true);
+ panelDirection.setEnabled(false);
+ panelAngle.setEnabled(false);
+ }
+ // Spot
+ else if (id == MyLight.LIGHT_SPOT)
+ {
+ panelColor.setEnabled(true);
+ panelPosition.setEnabled(true);
+ panelDirection.setEnabled(true);
+ panelAngle.setEnabled(true);
+ }
+ }
+
+ class PanelAngle extends JPanel
+ {
+ private static final long serialVersionUID = 1L;
+ private JLabel label;
+ private JTextField angle;
+ private float angleValue;
+
+ PanelAngle(float angleValue)
+ {
+ this.angleValue = angleValue;
+
+ initGui();
+ }
+
+ private void initGui()
+ {
+ label = new JLabel(Logo.messages.getString("3d.light.angle"));
+ label.setFont(WSManager.getWorkspaceConfig().getFont());
+ angle = new JTextField(String.valueOf(angleValue));
+ angle.setSize(30, WSManager.getWorkspaceConfig().getFont().getSize() + 10);
+ add(label);
+ add(angle);
+ }
+
+ public void setEnabled(boolean b)
+ {
+ super.setEnabled(b);
+ label.setEnabled(b);
+ angle.setEnabled(b);
+ }
+
+ /**
+ * @return
+ * @uml.property name="angleValue"
+ */
+ float getAngleValue()
+ {
+ try
+ {
+ float f = Float.parseFloat(angle.getText());
+ return f;
+ }
+ catch (NumberFormatException e)
+ {}
+ return MyLight.DEFAULT_ANGLE;
+ }
+ }
+
+ class PanelPosition extends JPanel
+ {
+ private static final long serialVersionUID = 1L;
+ private String title;
+ private JTextField Xpos;
+ private JTextField Ypos;
+ private JTextField Zpos;
+ private JLabel sep1;
+ private JLabel sep2;
+ Tuple3f tuple;
+
+ PanelPosition(String title, Tuple3f tuple)
+ {
+ this.title = title;
+ this.tuple = tuple;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ TitledBorder tb = BorderFactory.createTitledBorder(title);
+ tb.setTitleFont(WSManager.getWorkspaceConfig().getFont());
+ setBorder(tb);
+ sep1 = new JLabel("x");
+ sep2 = new JLabel("x");
+ Xpos = new JTextField(4);
+ Ypos = new JTextField(4);
+ Zpos = new JTextField(4);
+ if (null != tuple)
+ {
+ Xpos.setText(String.valueOf((int) (tuple.x * 1000)));
+ Ypos.setText(String.valueOf((int) (tuple.y * 1000)));
+ Zpos.setText(String.valueOf((int) (tuple.z * 1000)));
+ }
+ add(Xpos);
+ add(sep1);
+ add(Ypos);
+ add(sep2);
+ add(Zpos);
+ }
+
+ Point3f getPosition()
+ {
+ try
+ {
+ float x = Float.parseFloat(Xpos.getText());
+ float y = Float.parseFloat(Ypos.getText());
+ float z = Float.parseFloat(Zpos.getText());
+ return new Point3f(x / 1000, y / 1000, z / 1000);
+ }
+ catch (NumberFormatException e)
+ {}
+ return (new Point3f(0, 0, 0));
+ }
+
+ Vector3f getDirection()
+ {
+ try
+ {
+ float x = Float.parseFloat(Xpos.getText());
+ float y = Float.parseFloat(Ypos.getText());
+ float z = Float.parseFloat(Zpos.getText());
+ return new Vector3f(x / 1000, y / 1000, z / 1000);
+ }
+ catch (NumberFormatException e)
+ {}
+ return (new Vector3f(0, 0, 0));
+
+ }
+
+ public void setEnabled(boolean b)
+ {
+ super.setEnabled(b);
+ sep1.setEnabled(b);
+ sep2.setEnabled(b);
+ Xpos.setEnabled(b);
+ Ypos.setEnabled(b);
+ Zpos.setEnabled(b);
+ }
+
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/MyFog.java b/logo/src/xlogo/kernel/perspective/MyFog.java
new file mode 100644
index 0000000..1961fcc
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/MyFog.java
@@ -0,0 +1,134 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.LinearFog;
+import javax.media.j3d.ExponentialFog;
+import javax.media.j3d.Fog;
+import javax.media.j3d.BoundingSphere;
+
+import javax.vecmath.Color3f;
+
+public class MyFog extends BranchGroup
+{
+ protected final static int FOG_OFF = 0;
+ protected final static int FOG_LINEAR = 1;
+ protected final static int FOG_EXPONENTIAL = 2;
+ private int type = FOG_OFF;
+ private float density = 1;
+ private float backDistance = 3.5f;
+ private float frontDistance = 0.5f;
+ private Fog fog;
+ Color3f color;
+
+ MyFog(int type, Color3f color)
+ {
+ super();
+ setCapability(BranchGroup.ALLOW_DETACH);
+ setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
+ this.type = type;
+ this.color = color;
+ }
+
+ /**
+ * This method creates a light according to each parameter:<br>
+ * type, color, position, direction and angle
+ */
+ void createFog()
+ {
+ this.setCapability(BranchGroup.ALLOW_DETACH);
+ this.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
+ switch (type)
+ {
+ case FOG_OFF:
+ fog = null;
+ break;
+ case FOG_LINEAR:
+ fog = new LinearFog(color);
+ ((LinearFog) fog).setBackDistance(backDistance);
+ ((LinearFog) fog).setFrontDistance(frontDistance);
+ fog.setInfluencingBounds(new BoundingSphere());
+ break;
+ case FOG_EXPONENTIAL:
+ fog = new ExponentialFog(color, density);
+ fog.setInfluencingBounds(new BoundingSphere());
+ break;
+ }
+ if (null != fog)
+ addChild(fog);
+ }
+
+ /**
+ * This method returns the light type
+ *
+ * @return an integer which represents the light type
+ * @uml.property name="type"
+ */
+ int getType()
+ {
+ return type;
+ }
+
+ void setType(int t)
+ {
+ type = t;
+ }
+
+ float getDensity()
+ {
+ return density;
+ }
+
+ void setDensity(float f)
+ {
+ density = f;
+ }
+
+ float getBack()
+ {
+ return backDistance;
+ }
+
+ void setBack(float f)
+ {
+ backDistance = f;
+ }
+
+ float getFront()
+ {
+ return frontDistance;
+ }
+
+ void setFront(float f)
+ {
+ frontDistance = f;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/MyLight.java b/logo/src/xlogo/kernel/perspective/MyLight.java
new file mode 100644
index 0000000..9c5c054
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/MyLight.java
@@ -0,0 +1,190 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import javax.media.j3d.AmbientLight;
+import javax.media.j3d.BoundingSphere;
+import javax.media.j3d.DirectionalLight;
+import javax.media.j3d.Light;
+import javax.media.j3d.PointLight;
+import javax.media.j3d.SpotLight;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3d;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+import javax.media.j3d.BranchGroup;
+
+public class MyLight extends BranchGroup
+{
+ protected final static int LIGHT_OFF = 0;
+ protected final static int LIGHT_AMBIENT = 1;
+ protected final static int LIGHT_DIRECTIONAL = 2;
+ protected final static int LIGHT_POINT = 3;
+ protected final static int LIGHT_SPOT = 4;
+ protected final static float DEFAULT_ANGLE = 15;
+ private int type = LIGHT_OFF;
+ private Color3f color;
+ private Point3f position;
+ private Vector3f direction;
+ private float angle = DEFAULT_ANGLE;
+ private Light light;
+
+ MyLight(int type)
+ {
+ super();
+ this.type = type;
+ }
+
+ MyLight(int type, Color3f color, Point3f position)
+ {
+ super();
+ this.type = type;
+ this.color = color;
+ this.position = position;
+ }
+
+ /**
+ * This method creates a light according to each parameter:<br>
+ * type, color, position, direction and angle
+ */
+ void createLight()
+ {
+ this.setCapability(BranchGroup.ALLOW_DETACH);
+ this.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
+ switch (type)
+ {
+ case LIGHT_OFF:
+ light = null;
+ break;
+ case LIGHT_AMBIENT:
+ light = new AmbientLight(color);
+ light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE));
+ break;
+ case LIGHT_DIRECTIONAL:
+ light = new DirectionalLight(color, direction);
+ light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE));
+ break;
+ case LIGHT_POINT:
+ light = new PointLight(color, position, new Point3f(1, 0, 0));
+ light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE));
+ break;
+ case LIGHT_SPOT:
+ light = new SpotLight(color, position, new Point3f(1, 0, 0), direction, (float) Math.toRadians(angle),
+ 64);
+ light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE));
+ break;
+ }
+ if (null != light)
+ addChild(light);
+ }
+
+ /**
+ * This method returns the light type
+ *
+ * @return an integer which represents the light type
+ */
+ int getType()
+ {
+ return type;
+ }
+
+ /**
+ * @param t
+ */
+ void setType(int t)
+ {
+ type = t;
+ }
+
+ /**
+ * @return
+ */
+ Color3f getColor()
+ {
+ return color;
+ }
+
+ /**
+ * @param c
+ */
+ void setColor(Color3f c)
+ {
+ color = c;
+ }
+
+ /**
+ * @return
+ */
+ Point3f getPosition()
+ {
+ return position;
+ }
+
+ /**
+ * @param p
+ */
+ void setPosition(Point3f p)
+ {
+
+ position = p;
+ }
+
+ /**
+ * @return
+ */
+ Vector3f getDirection()
+ {
+ return direction;
+ }
+
+ /**
+ * @param v
+ */
+ void setDirection(Vector3f v)
+ {
+ direction = v;
+ }
+
+ /**
+ * @return
+ */
+ float getAngle()
+ {
+ return angle;
+ }
+
+ /**
+ * @param f
+ */
+ void setAngle(float f)
+ {
+ angle = f;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/Viewer3D.java b/logo/src/xlogo/kernel/perspective/Viewer3D.java
new file mode 100644
index 0000000..e7bce56
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/Viewer3D.java
@@ -0,0 +1,493 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.Insets;
+import java.awt.Toolkit;
+
+import javax.media.j3d.BoundingSphere;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.Canvas3D;
+
+import java.awt.image.BufferedImage;
+
+import javax.media.j3d.ImageComponent2D;
+import javax.media.j3d.ImageComponent;
+import javax.media.j3d.GraphicsContext3D;
+import javax.media.j3d.Shape3D;
+import javax.media.j3d.Raster;
+import javax.vecmath.Point3f;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.Node;
+import javax.media.j3d.Background;
+
+import java.awt.GridBagLayout;
+import java.awt.Color;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.border.TitledBorder;
+
+import java.awt.GridLayout;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3d;
+import javax.vecmath.Vector3d;
+
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+import xlogo.utils.WriteImage;
+import xlogo.Logo;
+
+import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
+import com.sun.j3d.utils.universe.SimpleUniverse;
+
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+/**
+ * This Frame displays the 3D main Scene
+ *
+ * BG scene
+ *
+ * BG backBranchGroup | BG Mylight (4) | BG
+ * Background Light 1,2,3,4 Other objects
+ */
+
+public class Viewer3D extends JFrame implements ActionListener
+{
+ private final static String ACTION_SCREENSHOT = "screenshot";
+ private final static String ACTION_LIGHT0 = "light0";
+ private final static String ACTION_LIGHT1 = "light1";
+ private final static String ACTION_LIGHT2 = "light2";
+ private final static String ACTION_LIGHT3 = "light3";
+ private final static String ACTION_FOG = "fog";
+
+ // To store the Light attributes
+ private MyLight[] myLights;
+ // To store the Fog attributes
+ private MyFog myFog;
+ // Gui Components
+ private ImageIcon iscreenshot = Utils.dimensionne_image("screenshot.png", this);
+ private JButton screenshot;
+ private static final long serialVersionUID = 1L;
+ private World3D w3d;
+ private Canvas3D canvas3D;
+ /**
+ * Main scene's Branchgroup
+ */
+ private BranchGroup scene;
+ private BranchManager branchManager;
+ private SimpleUniverse universe;
+ private Color3f backgroundColor;
+ private BranchGroup backBranchgroup;
+ private Background back;
+ private PanelLight panelLight;
+ private PanelFog panelFog;
+
+ public Viewer3D(World3D w3d, Color c)
+ {
+ this.w3d = w3d;
+ this.backgroundColor = new Color3f(c);
+ initGui();
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ String cmd = e.getActionCommand();
+ // Take a screenshot
+ if (cmd.equals(Viewer3D.ACTION_SCREENSHOT))
+ {
+ WriteImage wi = new WriteImage(this, null);
+ int id = wi.chooseFile();
+ if (id == JFileChooser.APPROVE_OPTION)
+ {
+ GraphicsContext3D ctx = canvas3D.getGraphicsContext3D();
+ java.awt.Dimension scrDim = canvas3D.getSize();
+
+ // setting raster component
+ Raster ras = new Raster(new Point3f(-1.0f, -1.0f, -1.0f), Raster.RASTER_COLOR, 0, 0, scrDim.width,
+ scrDim.height, new ImageComponent2D(ImageComponent.FORMAT_RGB, new BufferedImage(scrDim.width,
+ scrDim.height, java.awt.image.BufferedImage.TYPE_INT_RGB)), null);
+ ctx.readRaster(ras);
+ BufferedImage img = ras.getImage().getImage();
+ wi.setImage(img);
+ wi.start();
+ }
+ }
+ // Click on Button Light1
+ else if (cmd.equals(Viewer3D.ACTION_LIGHT0))
+ {
+ new LightDialog(this, myLights[0], Logo.messages.getString("3d.light") + " 1");
+ }
+ // Click on Button Light2
+ else if (cmd.equals(Viewer3D.ACTION_LIGHT1))
+ {
+ new LightDialog(this, myLights[1], Logo.messages.getString("3d.light") + " 2");
+ }
+ // Click on Button Light3
+ else if (cmd.equals(Viewer3D.ACTION_LIGHT2))
+ {
+ new LightDialog(this, myLights[2], Logo.messages.getString("3d.light") + " 3");
+ }
+ // Click on Button Light4
+ else if (cmd.equals(Viewer3D.ACTION_LIGHT3))
+ {
+ new LightDialog(this, myLights[3], Logo.messages.getString("3d.light") + " 4");
+ }
+ // Click on the Fog Button
+ else if (cmd.equals(Viewer3D.ACTION_FOG))
+ {
+ new FogDialog(this, myFog, Logo.messages.getString("3d.fog"));
+
+ }
+ }
+
+ public void setText()
+ {
+ setTitle(Logo.messages.getString("3d.viewer"));
+ panelFog.setText();
+ panelLight.setText();
+
+ }
+
+ private void initGui()
+ {
+ this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ setIconImage(Toolkit.getDefaultToolkit().createImage(Utils.class.getResource("icone.png")));
+
+ // Creation d'un composant de classe Canvas3D permettant de visualiser
+ // une scène 3D
+ canvas3D = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
+ // Création d'un univers 3D rattaché au composant 3D
+ universe = new SimpleUniverse(canvas3D);
+
+ // Install the camera at the valid position with correct orientation
+ TransformGroup tg = universe.getViewingPlatform().getViewPlatformTransform();
+ Transform3D trans = new Transform3D();
+ if (null == w3d)
+ {
+ w3d = new World3D();
+ }
+ trans.setTranslation(new Vector3d(-w3d.xCamera / 1000, -w3d.yCamera / 1000, -w3d.zCamera / 1000));
+ Transform3D rot = new Transform3D();
+ rot.lookAt(new Point3d(-w3d.xCamera / 1000, -w3d.yCamera / 1000, -w3d.zCamera / 1000), new Point3d(0, 0, 0),
+ new Vector3d(0, 1, 0));
+ rot.invert();
+ trans.mul(rot);
+ tg.setTransform(trans);
+ OrbitBehavior ob = new OrbitBehavior(canvas3D, OrbitBehavior.REVERSE_ALL);
+ ob.setRotationCenter(new Point3d(0, 0, 0));
+ ob.setSchedulingBounds(new BoundingSphere(new Point3d(0, 0, 0), 2 * w3d.r / 1000));
+ universe.getViewingPlatform().setViewPlatformBehavior(ob);
+
+ // Create the root of the branch graph
+ scene = new BranchGroup();
+ branchManager = new BranchManager();
+ // scene.setName("Main Branch");
+ // We can add New BranchGroup dynamically in the main scene
+ scene.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
+ // We can remove BranchGroup dynamically in the main scene
+ scene.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
+
+ // Configure and create background
+ createBackground();
+
+ // Configure Lights
+ initLights();
+
+ // Configure Fog
+ myFog = new MyFog(MyFog.FOG_OFF, backgroundColor);
+ scene.addChild(myFog);
+
+ // Rattachement de la scène 3D à l'univers
+ universe.addBranchGraph(scene);
+
+ Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
+ int width = d.width * 9 / 10;
+ int height = d.height * 9 / 10;
+ setSize(width, height);
+ getContentPane().setLayout(new GridBagLayout());
+ screenshot = new JButton(iscreenshot);
+ screenshot.addActionListener(this);
+ screenshot.setMaximumSize(new Dimension(100, 100));
+ screenshot.setActionCommand(Viewer3D.ACTION_SCREENSHOT);
+
+ panelLight = new PanelLight(this);
+ panelFog = new PanelFog(this);
+
+ getContentPane().add(
+ canvas3D,
+ new GridBagConstraints(0, 0, 1, 3, 2.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ screenshot,
+ new GridBagConstraints(1, 0, 1, 1, 0.1, 0.8, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(10, 10, 10, 10), 0, 0));
+ getContentPane().add(
+ panelLight,
+ new GridBagConstraints(1, 1, 1, 1, 0.1, 0.1, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(10, 10, 10, 10), 0, 0));
+ getContentPane().add(
+ panelFog,
+ new GridBagConstraints(1, 2, 1, 1, 0.1, 0.1, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(10, 10, 10, 10), 0, 0));
+ setText();
+ getContentPane().validate();
+ }
+
+ /**
+ * This method adds a shape3D to the main scene
+ *
+ * @param s
+ * The Shape to add
+ */
+ public void add3DObject(Shape3D s)
+ {
+ branchManager.add3DObject(s);
+ }
+
+ public void add2DText(TransformGroup tg)
+ {
+ branchManager.add2DText(tg);
+ }
+
+ public void insertBranch()
+ {
+ branchManager.insertBranch();
+ branchManager = new BranchManager();
+ }
+
+ /**
+ * This methods adds two default PointLight int the 3d Scene
+ */
+ private void initLights()
+ {
+ myLights = new MyLight[4];
+ // First Default Point Light
+ Color3f color = new Color3f(1f, 1f, 1f);
+ Point3f pos = new Point3f((float) w3d.xCamera / 1000, (float) w3d.yCamera / 1000, (float) w3d.zCamera / 1000);
+ myLights[0] = new MyLight(MyLight.LIGHT_POINT, color, pos);
+
+ // Second default Point Light
+ pos = new Point3f(-(float) w3d.xCamera / 1000, -(float) w3d.yCamera / 1000, -(float) w3d.zCamera / 1000);
+ myLights[1] = new MyLight(MyLight.LIGHT_POINT, color, pos);
+
+ myLights[2] = new MyLight(MyLight.LIGHT_OFF);
+ myLights[3] = new MyLight(MyLight.LIGHT_OFF);
+ for (int i = 0; i < 4; i++)
+ {
+ myLights[i].createLight();
+ }
+ addAllLights(scene);
+ }
+
+ /**
+ * Add all lights in the main 3D scene
+ */
+ private void addAllLights(BranchGroup bg)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ bg.addChild(myLights[i]);
+ }
+ }
+
+ /**
+ * add a light in the main scene
+ */
+ protected void addNode(Node l)
+ {
+ scene.addChild(l);
+ }
+
+ /**
+ * This methods erase all drawings on the 3D Viewer Drawing Area
+ */
+ public void clearScreen()
+ {
+ scene.removeAllChildren();
+ createBackground();
+ initLights();
+ /*
+ * Enumeration<Locale> locales=universe.getAllLocales();
+ * while(locales.hasMoreElements()){
+ * Locale lo=locales.nextElement();
+ * Enumeration<BranchGroup> en=lo.getAllBranchGraphs();
+ * while(en.hasMoreElements()){
+ * BranchGroup bg=en.nextElement();
+ * // System.out.println(bg.getName());
+ * if (null!=bg.getName()&&bg.getName().equals("Main Branch")){
+ * // Detach scene
+ * bg.detach();
+ * // Delete all nodes
+ * bg.removeAllChildren();
+ * // create background
+ * createBackground(bg);
+ * // Add lights
+ * addAllLights(bg);
+ * // Attach scene
+ * lo.addBranchGraph(bg);
+ * }
+ * }
+ * }
+ */
+
+ }
+
+ public void updateBackGround(Color c)
+ {
+ backgroundColor = new Color3f(c);
+ backBranchgroup.detach();
+ createBackground();
+ }
+
+ /**
+ * This method creates the Background with the defined color
+ */
+ public void createBackground()
+ {
+ backBranchgroup = new BranchGroup();
+ backBranchgroup.setCapability(BranchGroup.ALLOW_DETACH);
+ backBranchgroup.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
+
+ back = new Background(backgroundColor);
+ back.setApplicationBounds(new BoundingSphere());
+ back.setCapability(Background.ALLOW_COLOR_WRITE);
+ backBranchgroup.addChild(back);
+ scene.addChild(backBranchgroup);
+ }
+
+ class BranchManager
+ {
+ BranchGroup bg;
+
+ BranchManager()
+ {
+ bg = new BranchGroup();
+ bg.setCapability(BranchGroup.ALLOW_DETACH);
+ }
+
+ /**
+ * This method adds a shape3D to the main scene
+ *
+ * @param s
+ * The Shape to add
+ */
+ void add3DObject(Shape3D s)
+ {
+ bg.addChild(s);
+ }
+
+ void add2DText(TransformGroup tg)
+ {
+ bg.addChild(tg);
+ }
+
+ void insertBranch()
+ {
+ bg.compile();
+ scene.addChild(bg);
+ }
+ }
+
+ class PanelLight extends JPanel
+ {
+ private static final long serialVersionUID = 1L;
+ private JButton[] buttonLights;
+ private Viewer3D viewer3d;
+
+ PanelLight(Viewer3D viewer3d)
+ {
+ this.viewer3d = viewer3d;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ buttonLights = new JButton[4];
+ setLayout(new GridLayout(2, 2, 10, 10));
+ for (int i = 0; i < 4; i++)
+ {
+ ImageIcon ilight = Utils.dimensionne_image("light" + i + ".png", viewer3d);
+ buttonLights[i] = new JButton(ilight);
+ add(buttonLights[i]);
+ buttonLights[i].addActionListener(viewer3d);
+ buttonLights[i].setActionCommand("light" + i);
+ }
+ setText();
+ }
+
+ void setText()
+ {
+ TitledBorder tb = BorderFactory.createTitledBorder(Logo.messages.getString("3d.light"));
+ tb.setTitleFont(WSManager.getWorkspaceConfig().getFont());
+ setBorder(tb);
+ }
+ }
+
+ class PanelFog extends JPanel
+ {
+
+ private static final long serialVersionUID = 1L;
+ private JButton buttonFog;
+ private Viewer3D viewer3d;
+
+ PanelFog(Viewer3D viewer3d)
+ {
+ this.viewer3d = viewer3d;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ ImageIcon ifog =Utils.dimensionne_image("fog.png", viewer3d);
+ buttonFog = new JButton(ifog);
+ buttonFog.setActionCommand(Viewer3D.ACTION_FOG);
+ buttonFog.addActionListener(viewer3d);
+ add(buttonFog);
+ setText();
+ }
+
+ void setText()
+ {
+ TitledBorder tb = BorderFactory.createTitledBorder(Logo.messages.getString("3d.fog"));
+ tb.setTitleFont(WSManager.getWorkspaceConfig().getFont());
+ setBorder(tb);
+ }
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/World3D.java b/logo/src/xlogo/kernel/perspective/World3D.java
new file mode 100644
index 0000000..d3ffcc5
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/World3D.java
@@ -0,0 +1,318 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import xlogo.storage.WSManager;
+import xlogo.storage.user.UserConfig;
+
+public class World3D
+{
+
+ double theta = Math.toRadians(45);
+
+ double phi = Math.toRadians(30);
+
+ public double r = 1500;
+
+ public double xCamera = r * Math.cos(theta) * Math.cos(phi);
+
+ public double yCamera = r * Math.sin(theta) * Math.cos(phi);
+
+ public double zCamera = r * Math.sin(phi); ;
+
+ public double screenDistance = 1000;
+
+ private double[][] array3D;
+
+ public World3D()
+ {
+ initArray3D();
+
+ }
+
+ /**
+ * This method converts the coordinates in coord to the screen coord
+ */
+ public void toScreenCoord(double[] coord)
+ {
+ // Coord in the camera world
+ toCameraWorld(coord);
+ /*
+ * double x=array3D[0][0]*coord[0]+array3D[1][0]*coord[1];
+ * double
+ * y=array3D[0][1]*coord[0]+array3D[1][1]*coord[1]+array3D[2][1]*coord
+ * [2];
+ * double
+ * z=array3D[0][2]*coord[0]+array3D[1][2]*coord[1]+array3D[2][2]*coord
+ * [2]+r;
+ */
+ cameraToScreen(coord);
+ }
+
+ /**
+ * This method converts coordinates conatined in coord from camera world to
+ * screen
+ */
+ public void cameraToScreen(double[] coord)
+ {
+ UserConfig uc = WSManager.getUserConfig();
+
+ double x = coord[0];
+ double y = coord[1];
+ double z = coord[2];
+ coord[0] = screenDistance * x / z + uc.getImageWidth() / 2;
+ coord[1] = uc.getImageHeight() / 2 - screenDistance * y / z;
+ }
+
+ /**
+ * This method initializes the 3D array
+ */
+ private void initArray3D()
+ {
+ double cost = Math.cos(theta);
+ double sint = Math.sin(theta);
+ double cosp = Math.cos(phi);
+ double sinp = Math.sin(phi);
+ array3D = new double[4][4];
+ array3D[0][0] = -sint;
+ array3D[0][1] = -cost * sinp;
+ array3D[0][2] = -cost * cosp;
+ array3D[0][3] = 0;
+ array3D[1][0] = cost;
+ array3D[1][1] = -sint * sinp;
+ array3D[1][2] = -sint * cosp;
+ array3D[1][3] = 0;
+ array3D[2][0] = 0;
+ array3D[2][1] = cosp;
+ array3D[2][2] = -sinp;
+ array3D[2][3] = 0;
+ array3D[3][0] = 0;
+ array3D[3][1] = 0;
+ array3D[3][2] = r;
+ array3D[3][3] = 1;
+ }
+
+ /**
+ * This method converts coordinates from Real world to coodinates in camera
+ * world
+ *
+ * @param coord
+ * The real World coordinates
+ * @return Array that contains coordinates in camera world
+ */
+
+ public void toCameraWorld(double[] coord)
+ {
+ double x = array3D[0][0] * coord[0] + array3D[1][0] * coord[1];
+ double y = array3D[0][1] * coord[0] + array3D[1][1] * coord[1] + array3D[2][1] * coord[2];
+ double z = array3D[0][2] * coord[0] + array3D[1][2] * coord[1] + array3D[2][2] * coord[2] + r;
+ coord[0] = x;
+ coord[1] = y;
+ coord[2] = z;
+ }
+
+ /**
+ * This method multiply two matrices
+ *
+ * @param a
+ * The first matrix
+ * @param b
+ * The second matrix
+ * @return The matrix product
+ */
+ public double[][] multiply(double[][] a, double[][] b)
+ {
+ int n = a.length;
+ int p = b[0].length;
+ int s = a[0].length;
+ double[][] m = new double[n][p];
+ for (int i = 0; i < n; i++)
+ {
+ for (int j = 0; j < p; j++)
+ {
+ m[i][j] = 0;
+ for (int k = 0; k < s; k++)
+ {
+ m[i][j] += a[i][k] * b[k][j];
+ }
+ }
+ }
+ return m;
+ }
+
+ /**
+ * This method returns a matrix for a Z Axis rotation
+ *
+ * @param angle
+ * The rotation angle
+ * @return The rotation matrix
+ */
+ public double[][] rotationZ(double angle)
+ {
+ double[][] m = new double[3][3];
+ double cos = Math.cos(Math.toRadians(angle));
+ double sin = Math.sin(Math.toRadians(angle));
+ m[0][0] = cos;
+ m[1][0] = sin;
+ m[0][1] = -sin;
+ m[1][1] = cos;
+ m[2][2] = 1;
+ m[2][1] = m[2][0] = m[0][2] = m[1][2] = 0;
+ return m;
+ }
+
+ /**
+ * This method returns a matrix for a Y Axis rotation
+ *
+ * @param angle
+ * The rotation angle
+ * @return The rotation matrix
+ */
+ public double[][] rotationY(double angle)
+ {
+ double[][] m = new double[3][3];
+ double cos = Math.cos(Math.toRadians(angle));
+ double sin = Math.sin(Math.toRadians(angle));
+ m[0][0] = cos;
+ m[2][0] = sin;
+ m[0][2] = -sin;
+ m[2][2] = cos;
+ m[1][1] = 1;
+ m[0][1] = m[1][0] = m[1][2] = m[2][1] = 0;
+ return m;
+ }
+
+ /**
+ * This method returns a matrix for a X Axis rotation
+ *
+ * @param angle
+ * The rotation angle
+ * @return The rotation matrix
+ */
+ public double[][] rotationX(double angle)
+ {
+ double[][] m = new double[3][3];
+ double cos = Math.cos(Math.toRadians(angle));
+ double sin = Math.sin(Math.toRadians(angle));
+ m[0][0] = 1;
+ m[1][1] = cos;
+ m[2][1] = sin;
+ m[1][2] = -sin;
+ m[2][2] = cos;
+ m[1][0] = m[2][0] = m[0][1] = m[0][2] = 0;
+ return m;
+ }
+
+ /**
+ * This method with the 3 Euler's angle builds a rotation matrix
+ *
+ * @param heading
+ * The turtle heading
+ * @param roll
+ * The turtle roll
+ * @param pitch
+ * The turtle pitch
+ * @return The rotation Matrix
+ */
+
+ public double[][] EulerToRotation(double roll, double pitch, double heading)
+ {
+ double[][] m = new double[3][3];
+ double rpitch = Math.toRadians(pitch);
+ double rheading = Math.toRadians(heading);
+ double rroll = Math.toRadians(roll);
+ double a = Math.cos(rpitch);
+ double b = Math.sin(rpitch);
+ double c = Math.cos(rroll);
+ double d = Math.sin(rroll);
+ double e = Math.cos(rheading);
+ double f = Math.sin(rheading);
+ double bd = b * d;
+ double bc = b * c;
+ m[0][0] = c * e - bd * f;
+ m[0][1] = -c * f - bd * e;
+ m[0][2] = -a * d;
+ m[1][0] = a * f;
+ m[1][1] = a * e;
+ m[1][2] = -b;
+ m[2][0] = d * e + bc * f;
+ m[2][1] = -d * f + bc * e;
+ m[2][2] = a * c;
+ return m;
+ }
+
+ /**
+ *
+ */
+ public double[] rotationToEuler(double[][] m)
+ {
+ double[] v = new double[3];
+
+ double angle_x, angle_y, angle_z;
+ double a, tr_x, tr_y;
+ // Angle x
+ angle_x = -Math.asin(m[1][2]);
+
+ a = Math.cos(angle_x);
+ // Gimbal Lock?
+ if (Math.abs(a) > 0.005)
+ {
+ // No gimbal Lock
+ // Angle z
+ tr_x = m[1][1] / a;
+ tr_y = m[1][0] / a;
+ angle_z = Math.atan2(tr_y, tr_x);
+ // Angle y
+ tr_x = m[2][2] / a;
+ tr_y = -m[0][2] / a;
+ angle_y = Math.atan2(tr_y, tr_x);
+ v[0] = Math.toDegrees(angle_x);
+ v[1] = -Math.toDegrees(angle_y);
+ v[2] = -Math.toDegrees(angle_z);
+ }
+ else
+ { // Gimbal Lock
+ angle_y = 0;
+ // Angle z
+ tr_x = m[0][0];
+ tr_y = m[2][0];
+ angle_z = Math.atan2(tr_y, tr_x);
+ v[0] = 270;
+ v[1] = 0;
+ v[2] = Math.toDegrees(angle_z);
+ }
+
+ for (int i = 0; i < v.length; i++)
+ {
+ if (v[i] < 0)
+ v[i] += 360;
+ }
+ return v;
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/ErrorManager.java b/logo/src/xlogo/kernel/userspace/ErrorManager.java
new file mode 100644
index 0000000..f1b18ec
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/ErrorManager.java
@@ -0,0 +1,201 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import xlogo.interfaces.ErrorDetector.AmbiguityDetector.AmbiguityListener;
+import xlogo.interfaces.ErrorDetector.FileErrorCollector;
+import xlogo.kernel.userspace.procedures.Procedure;
+
+public class ErrorManager implements FileErrorCollector
+{
+ private final FileErrorCollector fileErrorDetector;
+ private final AmbiguityDetector ambiguityDetector;
+
+ private final HashMap<String, Void> errorFiles = new HashMap<String, Void>();
+ private final HashMap<String, Collection<String>> ambiguousProcToFiles = new HashMap<String, Collection<String>>();
+
+ private final ArrayList<ErrorListener> errorListeners = new ArrayList<ErrorListener>();
+
+ public ErrorManager(FileErrorCollector fileErrorDetector, AmbiguityDetector ambiguityDetector)
+ {
+ this.fileErrorDetector = fileErrorDetector;
+ this.ambiguityDetector = ambiguityDetector;
+
+ initListeners();
+ }
+
+ private void initListeners()
+ {
+ fileErrorDetector.addErrorListener(new ErrorListener(){
+
+ @Override
+ public void errorsDetected(String fileName)
+ {
+ errorFiles.put(fileName, null);
+ notifyErrorDetected(fileName);
+ }
+
+ @Override
+ public void allErrorsCorrected(String fileName)
+ {
+ errorFiles.remove(fileName);
+
+ for (Collection<String> fileNames : ambiguousProcToFiles.values())
+ {
+ if (fileNames.contains(fileName))
+ return;
+ }
+ // no more errors or ambiguities found
+ notifyAllErrorsCorrected(fileName);
+ }
+ });
+
+ ambiguityDetector.addAmbiguityListener(new AmbiguityListener(){
+
+ @Override
+ public void ambiguityResolved(String procedureName, String fileName)
+ {
+ Collection<String> ambigFiles = ambiguousProcToFiles.get(procedureName);
+
+ ambigFiles.remove(fileName);
+
+ if (ambigFiles.size() == 0)
+ ambiguousProcToFiles.remove(procedureName);
+
+ // [this check is not necessary. if it was ambiguous, it did not have errors.]
+ //if (errorFiles.containsKey(fileName))
+ // return;
+ // No more file errors
+
+ for (Collection<String> fileNames : ambiguousProcToFiles.values())
+ {
+ if (fileNames.contains(fileName))
+ return;
+ }
+ // No more ambiguities for file
+
+ notifyAllErrorsCorrected(fileName);
+ }
+
+ @Override
+ public void ambiguityDetected(String procedureName, Map<String, Procedure> fileToProcedure)
+ {
+ ambiguousProcToFiles.put(procedureName, new ArrayList<String>(fileToProcedure.keySet()));
+ for (String fileName : fileToProcedure.keySet())
+ notifyErrorDetected(fileName);
+ }
+ });
+ }
+
+ @Override
+ public boolean hasErrors()
+ {
+ return errorFiles.size() > 0 || ambiguousProcToFiles.size() > 0;
+ }
+
+ @Override
+ public boolean hasErrors(String fileName)
+ {
+ if (errorFiles.containsKey(fileName))
+ return true;
+
+ for (Collection<String> conflictingFiles : ambiguousProcToFiles.values())
+ if (conflictingFiles.contains(fileName))
+ return true;
+ return false;
+ }
+
+ @Override
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> allErrorMessages = new ArrayList<ProcedureErrorMessage>();
+ // Not the most efficient impl possible
+ allErrorMessages.addAll(fileErrorDetector.getAllErrors());
+ allErrorMessages.addAll(ambiguityDetector.getAllErrors());
+ return allErrorMessages;
+ }
+
+ @Override
+ public Collection<String> getAllErroneousFiles()
+ {
+ TreeMap<String, Void> allErrorFiles = new TreeMap<String, Void>();
+
+ for(String fileName : errorFiles.keySet())
+ allErrorFiles.put(fileName, null);
+
+ for(Collection<String> files : ambiguousProcToFiles.values())
+ for(String fileName : files)
+ allErrorFiles.put(fileName, null);
+
+ return allErrorFiles.keySet();
+ }
+
+ public Collection<String> getErrorMessages(String fileName)
+ {
+ ArrayList<String> messages = new ArrayList<String>();
+
+ for(ProcedureErrorMessage pem : getAllErrors())
+ if(pem.fileNames.contains(fileName))
+ messages.add(pem.toString());
+
+ return messages;
+ }
+
+ // These error event will directly update the gui => run on event dispatcher thread
+
+ @Override
+ public void addErrorListener(ErrorListener listener)
+ {
+ errorListeners.add(listener);
+ }
+
+ @Override
+ public void removeErrorListener(ErrorListener listener)
+ {
+ errorListeners.remove(listener);
+ }
+
+ private void notifyErrorDetected(final String fileName)
+ {
+ for (ErrorListener listener : errorListeners)
+ listener.errorsDetected(fileName);
+ }
+
+ private void notifyAllErrorsCorrected(final String fileName)
+ {
+ for (ErrorListener listener : errorListeners)
+ listener.allErrorsCorrected(fileName);
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/GlobalVariableTable.java b/logo/src/xlogo/kernel/userspace/GlobalVariableTable.java
new file mode 100644
index 0000000..7d74560
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/GlobalVariableTable.java
@@ -0,0 +1,73 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.HashMap;
+import java.util.Set;
+
+public class GlobalVariableTable
+{
+ /**
+ * All defined variables with their current value.
+ */
+ protected HashMap<String, String> globale;
+
+ public GlobalVariableTable()
+ {
+ globale = new HashMap<String, String>();
+ }
+
+ public Set<String> getVariables()
+ {
+ return globale.keySet();
+ }
+
+ public String getValue(String var)
+ {
+ return globale.get(var.toLowerCase());
+ }
+
+ public void define(String var, String value)
+ {
+ globale.put(var.toLowerCase(), value);
+ }
+
+ public void deleteVariable(String st)
+ {
+ globale.remove(st.toLowerCase());
+ }
+
+ /**
+ * Delete all Variables from the workspace
+ */
+ public void deleteAllVariables()
+ {
+ globale.clear();
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/ProcedureErrorMessage.java b/logo/src/xlogo/kernel/userspace/ProcedureErrorMessage.java
new file mode 100644
index 0000000..2e2afc8
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/ProcedureErrorMessage.java
@@ -0,0 +1,106 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import xlogo.Logo;
+import xlogo.kernel.userspace.procedures.ProcedureErrorType;
+
+/**
+ * This is used to report document structure errors
+ * or ambiguity errors, either within or among files.
+ * @author Marko Zivkovic
+ *
+ */
+public class ProcedureErrorMessage
+{
+ ProcedureErrorType type;
+ String procedureDescription;
+ Collection <String> fileNames;
+
+ /**
+ * The description may be either the name (if known) or the line, where the error was found.
+ * @param type
+ * @param procedureDescription
+ * @param fileName
+ */
+ public ProcedureErrorMessage(ProcedureErrorType type, String procedureDescription, String fileName)
+ {
+ this.type = type;
+ this.procedureDescription = procedureDescription;
+ this.fileNames = new ArrayList<String>();
+ this.fileNames.add(fileName);
+ }
+
+ /**
+ * This can be used for ambiguity messages
+ * @see #ProcedureErrorMessage(ProcedureErrorType, String, String)
+ */
+ public ProcedureErrorMessage(ProcedureErrorType type, String procedureDescription, Collection<String> fileNames)
+ {
+ this.type = type;
+ this.procedureDescription = procedureDescription;
+ this.fileNames = fileNames;
+ }
+
+ public ProcedureErrorType getErrorType()
+ {
+ return type;
+ }
+
+ public String getProcedureDescription()
+ {
+ return procedureDescription;
+ }
+
+ public Collection<String> getFileNames()
+ {
+ return fileNames;
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ for (String fileName : fileNames)
+ {
+ sb.append(fileName);
+ sb.append(", ");
+ }
+ sb.delete(sb.length()-2, sb.length()-1);
+
+ sb.append(procedureDescription);
+ sb.append(": ");
+ sb.append(Logo.messages.getString(type.getDescription()));
+
+ return sb.toString();
+ }
+
+} \ No newline at end of file
diff --git a/logo/src/xlogo/kernel/userspace/PropertyListTable.java b/logo/src/xlogo/kernel/userspace/PropertyListTable.java
new file mode 100644
index 0000000..7764d60
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/PropertyListTable.java
@@ -0,0 +1,165 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.HashMap;
+import java.util.Set;
+
+public class PropertyListTable
+{
+
+ /**
+ * For all Property Lists
+ */
+ private HashMap<String, HashMap<String, String>> propList;
+
+ public PropertyListTable()
+ {
+ propList = new HashMap<String, HashMap<String,String>>();
+ }
+
+
+ /**
+ * This method adds in the property List called "name" a value for the
+ * corresponding key
+ *
+ * @param name
+ * The property List 's name
+ * @param key
+ * The key for the value to add
+ * @param value
+ * The value to add
+ */
+ public void addPropList(String name, String key, String value)
+ {
+ if (!propList.containsKey(name))
+ {
+ propList.put(name, new HashMap<String, String>());
+ }
+ propList.get(name).put(key, value);
+ }
+
+ /**
+ * This method removes a Property List
+ *
+ * @param name
+ * The property List 's name
+ */
+ public void removePropList(String name)
+ {
+ if (propList.containsKey(name))
+ {
+ propList.remove(name);
+ }
+ }
+
+ /**
+ * This method removes a couple (key, value) from a Property List
+ *
+ * @param name
+ * The property List 's name
+ * @param key
+ * The key to delete
+ */
+ public void removePropList(String name, String key)
+ {
+ if (propList.containsKey(name))
+ {
+ if (propList.get(name).containsKey(key))
+ propList.get(name).remove(key);
+ if (propList.get(name).isEmpty())
+ propList.remove(name);
+ }
+ }
+
+ /**
+ * This method returns a list that contains all couple key value
+ *
+ * @param name
+ * The Property List's name
+ * @return A list with all keys-values
+ */
+ public String displayPropList(String name)
+ {
+ if (propList.containsKey(name))
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[ ");
+ Set<String> set = propList.get(name).keySet();
+ for (String key : set)
+ {
+ sb.append(key);
+ sb.append(" ");
+ String value = propList.get(name).get(key);
+ if (value.startsWith("\""))
+ value = value.substring(1);
+ sb.append(value);
+ sb.append(" ");
+ }
+ sb.append("] ");
+ return sb.toString();
+ }
+ else
+ return "[ ] ";
+ }
+
+ /**
+ * This method return a value from a Property List
+ *
+ * @param name
+ * The Property List's name
+ * @param key
+ * The key for the chosen value
+ * @return The value for this key
+ */
+ public String getPropList(String name, String key)
+ {
+ if (!propList.containsKey(name)) { return "[ ]"; }
+ if (!propList.get(name).containsKey(key))
+ return "[ ]";
+ return propList.get(name).get(key);
+ }
+
+ /**
+ * Returns all defined Property List names
+ *
+ * @return A list with all Property List names
+ */
+ public Set<String> getPropListKeys()
+ {
+ return propList.keySet();
+ }
+
+ /**
+ * Delete all property Lists from the workspace
+ */
+ public void deleteAllPropertyLists()
+ {
+ propList.clear();
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/UserSpace.java b/logo/src/xlogo/kernel/userspace/UserSpace.java
new file mode 100644
index 0000000..d3b79d6
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/UserSpace.java
@@ -0,0 +1,790 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import xlogo.Logo;
+import xlogo.interfaces.BroadcasterErrorFileContainer;
+import xlogo.interfaces.X4SModeSwitcher;
+import xlogo.interfaces.ErrorDetector.FileErrorCollector;
+import xlogo.kernel.gui.GuiMap;
+import xlogo.kernel.userspace.context.ContextManager;
+import xlogo.kernel.userspace.context.LogoContext;
+import xlogo.kernel.userspace.files.LogoFileContainer;
+import xlogo.kernel.userspace.files.LogoFilesManager;
+import xlogo.kernel.userspace.procedures.ExecutablesProvider;
+import xlogo.kernel.userspace.procedures.Procedure;
+import xlogo.kernel.userspace.procedures.ProceduresManager;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * This is a facade for what was before called Workspace in XLogo, and much more.
+ * XLogo's Workspace had:
+ * <p>
+ * - All Defined Procedures <br>
+ * - All Global variables <br>
+ * - All GUI Objects (generated by a Logo program)<br>
+ * - All Property lists
+ * <p>
+ * The new "Workspace" does not resemble the old workspace at all, although it still provides access to these symbol tables.
+ * <p>
+ * New in XLogo4Schools: multiple Files. Procedures must be unique within all files of a context.
+ * If multiple procedures with the same name are defined <br>
+ * - within one file : The file will not be usable, until errors and ambiguities are fixed (like in XLogo), <br>
+ * - among multiple files : All of these files will not be executable, <br> //TODO or the ambiguous procedures will not be executed
+ * unless the ambiguity is resolved.
+ * <p>
+ * New are also the Contexts for user mode, contest mode, network mode.
+ * The contest mode is completely new. In XLogo, Network mode was implemented by replacing the whole workspace.
+ * Now only the Contexts with symbol tables are replaced. Why? Because we have an event driven architecture now.
+ * If the whole workspace (referring to XLogo's workspace, including event dispatchers) would be replaced, then all subscribers to the workspace would have to be re-mapped.
+ * By keeping the LogoFileManager and the ProceduresManager, subscribers to the user space (which are redirected to the managers) must not be re-mapped when a context switch happens.
+ * <p>
+ * (Option for future: Procedures can be qualified with
+ * {@code fileName.procedureName}. But : A dot is not a legal character for a
+ * name anymore)
+ *
+ * @author Marko Zivkovic
+ *
+ */
+public class UserSpace implements X4SModeSwitcher, LogoFileContainer, BroadcasterErrorFileContainer,
+ ExecutablesProvider, FileErrorCollector
+{
+
+ /**
+ * This is included for every call that is delegated. This way all errors are caught and if we're lucky,
+ * The application can still be used or terminated normally. At least the user has discovered a new bug and he or she
+ * can report it.
+ *
+ * TODO write a log file?
+ *
+ * @param e
+ */
+ public static void showErrorDialog(Exception e)
+ {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+
+ DialogMessenger.getInstance().dispatchError(Logo.messages.getString(MessageKeys.WS_ERROR_TITLE),
+ sw.toString());
+ }
+
+ private final ContextManager contextManager; // Switches context when modes change, notifies the other managers. & causes mode and clock events
+ private final LogoFilesManager filesManager; // handles file specific requests & causes file events
+ private final ProceduresManager proceduresManager; // handles procedure specific requests & causes defined/undefined events
+ private final ErrorManager errorManager; // handles error specific requests & causes error events
+
+ public UserSpace()
+ {
+ contextManager = new ContextManager();
+ proceduresManager = new ProceduresManager(contextManager);
+ filesManager = new LogoFilesManager(contextManager);
+ errorManager = new ErrorManager(filesManager, proceduresManager);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * X4S MODE SWITCHER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public String getSerializedContext()
+ {
+ try
+ {
+ return contextManager.getSerializedContext();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return "";
+ }
+
+ @Override
+ public boolean isNetworkMode()
+ {
+ try
+ {
+ return contextManager.isNetworkMode();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public boolean isRecordMode()
+ {
+ try
+ {
+ return contextManager.isRecordMode();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public boolean isUserMode()
+ {
+ try
+ {
+ return contextManager.isUserMode();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return true;
+ }
+
+ @Override
+ public void startRecordMode(String[] fileNames) throws IllegalArgumentException, IOException
+ {
+ try
+ {
+ contextManager.startRecordMode(fileNames);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void stopRecordMode()
+ {
+ try
+ {
+ contextManager.stopRecordMode();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ public void pushNetworkMode(String networkContext)
+ {
+ try
+ {
+ contextManager.pushNetworkMode(networkContext);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void popNetworkMode()
+ {
+ try
+ {
+ contextManager.popNetworkMode();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void addModeChangedListener(ModeChangeListener listener)
+ {
+ try
+ {
+ contextManager.addModeChangedListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeModeChangedListener(ModeChangeListener listener)
+ {
+ try
+ {
+ contextManager.removeModeChangedListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void addBroadcastListener(MessageListener listener)
+ {
+ try
+ {
+ contextManager.addBroadcastListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeBroadcastListener(MessageListener listener)
+ {
+ try
+ {
+ contextManager.removeBroadcastListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * LOGO FILE CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public String[] getFileNames()
+ {
+ try
+ {
+ return filesManager.getFileNames();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new String[0];
+ }
+
+ @Override
+ public String readFile(String name)
+ {
+ try
+ {
+ return filesManager.readFile(name);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return "";
+ }
+
+ @Override
+ public String getOpenFileName()
+ {
+ try
+ {
+ return filesManager.getOpenFileName();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return null;
+ }
+
+ @Override
+ public boolean existsFile(String name)
+ {
+ try
+ {
+ return filesManager.existsFile(name);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public void createFile(String fileName) throws IOException
+ {
+ try
+ {
+ filesManager.createFile(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeFile(String fileName)
+ {
+ try
+ {
+ filesManager.removeFile(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /**
+ * The file is expected to exist on the file system and to have a .lgo extension
+ * @param filePath
+ * @throws IOException
+ */
+ public void importFile(File filePath) throws IOException
+ {
+ try
+ {
+ filesManager.importFile(filePath);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /**
+ * @throws IOException
+ * @see {@link LogoFilesManager#exportFile(String, File)}
+ */
+ public void exportFile(String fileName, File dest) throws IOException
+ {
+ try
+ {
+ filesManager.exportFile(fileName, dest);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /**
+ * @throws IOException
+ * @see {@link LogoFilesManager#exportFile(String, File, String)}
+ */
+ public void exportFile(String fileName, File location, String targetName) throws IOException
+ {
+ try
+ {
+ filesManager.exportFile(fileName, location, targetName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public String makeUniqueFileName(String base)
+ {
+ try
+ {
+ return filesManager.makeUniqueFileName(base);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return null;
+ }
+
+ @Override
+ public void renameFile(String oldName, String newName)
+ {
+ try
+ {
+ filesManager.renameFile(oldName, newName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void openFile(String fileName)
+ {
+ try
+ {
+ filesManager.openFile(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void closeFile(String fileName)
+ {
+ try
+ {
+ filesManager.closeFile(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void writeFileText(String fileName, String content) throws IOException
+ {
+ try
+ {
+ filesManager.writeFileText(fileName, content);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void storeFile(String fileName) throws IOException
+ {
+ try
+ {
+ filesManager.storeFile(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void addFileListener(FileContainerChangeListener listener)
+ {
+ try
+ {
+ filesManager.addFileListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeFileListener(FileContainerChangeListener listener)
+ {
+ try
+ {
+ filesManager.removeFileListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ public String getLastEditedFileName()
+ {
+ try
+ {
+ return filesManager.getLastEditedFileName();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return null;
+ }
+
+ public boolean isFilesListEditable()
+ {
+ try
+ {
+ return filesManager.isFilesListEditable();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return true;
+ }
+
+ public boolean hasErrors(String fileName)
+ {
+ try
+ {
+ return filesManager.hasErrors(fileName) || proceduresManager.hasAmbiguousProcedures(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public void editAll()
+ {
+ try
+ {
+ filesManager.editAll();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /**
+ * Resets the UserSpace by deleting all files (and procedures), all
+ * variables and all property lists.
+ * <p>
+ * Note : deleting all files will cause a chain of events.
+ */
+ public void eraseAll()
+ {
+ try
+ {
+ filesManager.eraseAll(); // should remove all files and procedures and fire the right events
+ LogoContext context = contextManager.getContext();
+ context.getGlobals().deleteAllVariables();
+ context.getPropertyLists().deleteAllPropertyLists();
+ context.getGuiMap().clear();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PROCEDURE CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * This is the implementation of the Logo Command define.
+ * Its effect has changed since we have multiple files now, but the semantics are preserved.
+ * Logo programs will behave exactly as before. <p>
+ * If the procedure is ambiguous, cannot decide which one to redefine => IllegalArgumentException <br>
+ * If the procedure is already defined once, that definition will be redefined in its original LogoFile. <br>
+ * If the procedure is not yet defined, a new File will be created "Generated Procedures" if does not yet exist,
+ * and the new procedure will be put there.
+ *
+ * @throws IllegalArgumentException if the procedure's name is ambiguous in the current context: I Don't know which one to redefine.
+ * @throws IllegalArgumentException if the procedure is not executable or it's owner does not exist in this context.
+ * @throws IOException - if the file "gerated procedure" could not be created in the context.
+ */
+ @Override
+ public void defineProcedure(Procedure procedure) throws IOException
+ {
+ try
+ {
+ String procedureName = procedure.getName();
+ if (proceduresManager.isProcedureAmbiguous(procedureName))
+ throw new IllegalArgumentException("Attempt to redefine ambiguous procedure.");
+
+ if (!isExecutable(procedureName))
+ {
+ String fileName = Logo.messages.getString("ws.generated.procedure");
+ if (!existsFile(fileName))
+ createFile(fileName);
+ }
+
+ proceduresManager.defineProcedure(procedure);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public Collection<Procedure> getExecutables()
+ {
+ try
+ {
+ return proceduresManager.getExecutables();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<Procedure>();
+ }
+
+ @Override
+ public Procedure getExecutable(String procedureName)
+ {
+ try
+ {
+ return proceduresManager.getExecutable(procedureName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return null;
+ }
+
+ @Override
+ public boolean isExecutable(String procedureName)
+ {
+ try
+ {
+ return proceduresManager.isExecutable(procedureName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public void eraseProcedure(String procedureName)
+ {
+ try
+ {
+ proceduresManager.eraseProcedure(procedureName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * The following symbol tables do not need a manager and no events
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ public GuiMap getGuiMap()
+ {
+ try
+ {
+ return contextManager.getContext().getGuiMap();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new GuiMap();
+ }
+
+ public PropertyListTable getPropertyLists()
+ {
+ try
+ {
+ return contextManager.getContext().getPropertyLists();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new PropertyListTable();
+ }
+
+ public GlobalVariableTable getGlobals()
+ {
+ try
+ {
+ return contextManager.getContext().getGlobals();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new GlobalVariableTable();
+ }
+
+ @Override
+ public Collection<String> getAllProcedureNames()
+ {
+ try
+ {
+ return proceduresManager.getAllProcedureNames();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<String>();
+ }
+
+ @Override
+ public Collection<String> getAllProcedureNames(String fileName)
+ {
+ try
+ {
+ return proceduresManager.getAllProcedureNames(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<String>();
+ }
+
+ @Override
+ public String getProcedureOwner(String procedureName)
+ {
+ try
+ {
+ return proceduresManager.getProcedureOwner(procedureName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return null;
+ }
+
+ @Override
+ public void addProcedureMapListener(ProcedureMapListener listener)
+ {
+ try
+ {
+ proceduresManager.addProcedureMapListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeProcedureMapListener(ProcedureMapListener listener)
+ {
+ try
+ {
+ proceduresManager.removeProcedureMapListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /*
+ * Errors & Ambiguities
+ */
+
+ /**
+ * conflicting w.r.t. ambiguity of their procedures
+ */
+ @Override
+ public Collection<String> getAllConflictingFiles()
+ {
+ try
+ {
+ return proceduresManager.getAllConflictingFiles();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<String>();
+ }
+
+ @Override
+ public boolean hasAmbiguousProcedures(String fileName)
+ {
+ try
+ {
+ return proceduresManager.hasAmbiguousProcedures(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public boolean isProcedureAmbiguous(String procedureName)
+ {
+ try
+ {
+ return proceduresManager.isProcedureAmbiguous(procedureName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public void addAmbiguityListener(AmbiguityListener listener)
+ {
+ try
+ {
+ proceduresManager.addAmbiguityListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeAmbiguityListener(AmbiguityListener listener)
+ {
+ try
+ {
+ proceduresManager.removeAmbiguityListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public boolean hasErrors()
+ {
+ try
+ {
+ return errorManager.hasErrors();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ try
+ {
+ return errorManager.getAllErrors();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<ProcedureErrorMessage>();
+ }
+
+ @Override
+ public Collection<String> getAllErroneousFiles()
+ {
+ try
+ {
+ return errorManager.getAllErroneousFiles();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<String>();
+
+ }
+
+ @Override
+ public void addErrorListener(ErrorListener listener)
+ {
+ try
+ {
+ errorManager.addErrorListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeErrorListener(ErrorListener listener)
+ {
+ try
+ {
+ errorManager.removeErrorListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ public Collection<String> getErrorMessages(String fileName)
+ {
+ try
+ {
+ return errorManager.getErrorMessages(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<String>();
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/ContextManager.java b/logo/src/xlogo/kernel/userspace/context/ContextManager.java
new file mode 100644
index 0000000..fe5dab7
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/ContextManager.java
@@ -0,0 +1,301 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Stack;
+
+import xlogo.interfaces.MessageBroadcaster;
+import xlogo.interfaces.X4SModeSwitcher;
+
+/**
+ * One of the four main roles in the {@link xlogo.kernel.userspace.UserSpace} <br>
+ * <b> The Contexts and Priorities and multiplicity</b>
+ * 1. UserContext [1] : default context, lowest priority, cannot be killed.
+ * 2. RecordContext [0..1] : medium priority, killing it will kill all networking contexts too.
+ * 3. NetworkContext [0..*] : highest priority, can be stacked.
+ *
+ * @author Marko
+ */
+public class ContextManager implements X4SModeSwitcher, ContextSwitcher, MessageBroadcaster
+{
+ private LogoContext currentContext;
+
+ private final UserContext userContext = new UserContext();
+ private RecordContext recordContext;
+ private final Stack<NetworkContext> networkStack = new Stack<NetworkContext>();
+
+ private MessageListener contextMessageListener;
+
+ public ContextManager()
+ {
+ currentContext = userContext;
+ initContextMessageListener();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * CONTEXT PROVIDER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * After a context has been removed by stopXXXMode(), contextSwitch() will determine the next current context.
+ * It only peeks at the context collections, but does not modify them. Calling this does never harm,
+ * but after one of the context collections has been changed, it should be called.<br>
+ * It notifies ContextProviderListeners if the context has actually changed after calling this.
+ * <p>
+ * The policy for context switches is defines in the interface here : {@link X4SModeSwitcher}
+ * <p>
+ * Note, this should be called before the mode switch events, because we first want to completely context switch, before we change the mode.
+ * Generally always publish events after the event has actually occurred.
+ *
+ */
+ protected void contextSwitch()
+ {
+ LogoContext old = currentContext;
+
+ if (networkStack.size() > 0)
+ currentContext = networkStack.peek();
+ else
+ currentContext = recordContext != null ? recordContext : userContext;
+
+ if(old != currentContext)
+ notifyContextSwitched(currentContext);
+ }
+
+ /**
+ * returns the current context after the policy described in the in the interface {@link X4SModeSwitcher}
+ */
+ @Override
+ public LogoContext getContext()
+ {
+ return currentContext;
+ }
+
+ // Context switch : internal communication : no direct gui update, must not run on event dispatcher thread
+
+ private final ArrayList<ContextSwitchListener> contextSwitchListeners = new ArrayList<ContextSwitcher.ContextSwitchListener>();
+
+ @Override
+ public void addContextSwitchListener(ContextSwitchListener listener)
+ {
+ contextSwitchListeners.add(listener);
+ }
+
+ @Override
+ public void removeContextSwitchListener(ContextSwitchListener listener)
+ {
+ contextSwitchListeners.remove(listener);
+ }
+
+ public void notifyContextSwitched(LogoContext newContext)
+ {
+ for (ContextSwitchListener listener : contextSwitchListeners)
+ listener.contextSwitched(newContext);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * MODE SWITCHER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public String getSerializedContext()
+ {
+ return currentContext.toString();
+ }
+
+ @Override
+ public boolean isUserMode()
+ {
+ return currentContext instanceof UserContext;
+ }
+
+ @Override
+ public boolean isRecordMode()
+ {
+ return currentContext instanceof RecordContext;
+ }
+
+ @Override
+ public boolean isNetworkMode()
+ {
+ return currentContext instanceof NetworkContext;
+ }
+
+ /**
+ * A record mode can only be started from user mode. In other words,
+ * a record mode cannot be started, if there is another <b>record or network</b> mode active.
+ * @throws IllegalStateException If current mode is not user mode.
+ * @throws IllegalArgumentException If the fileNames are not well formed, null, or ambiguous
+ * @throws IOException If the record files could not be created => recording is impossible => record mode is impossible
+ */
+ @Override
+ public void startRecordMode(String[] fileNames) throws IllegalArgumentException, IOException
+ {
+ if (!isUserMode())
+ throw new IllegalStateException();
+
+ recordContext = new RecordContext(fileNames);
+ recordContext.addBroadcastListener(contextMessageListener);
+
+ contextSwitch();
+
+ notifyContestModeStarted();
+ }
+
+ /**
+ * This will first regularly kill all network contexts, one after the other. Listeners will be notified for every kill. <br>
+ * Afterwards it will kill the current record context. <br>
+ * When this call returns, current mode is user mode.
+ *
+ * @throws IllegalStateException If there is no recordContext available
+ */
+ @Override
+ public void stopRecordMode() throws IllegalStateException
+ {
+ if (recordContext == null)
+ return; // might be a quick double klick that causes this
+
+ while(isNetworkMode())
+ popNetworkMode();
+
+ recordContext = null;
+
+ contextSwitch();
+
+ notifyRecordModeStopped();
+ }
+
+ /**
+ * @throws IllegalArgumentException If serializedContext is corrupted
+ */
+ @Override
+ public void pushNetworkMode(String serializedContext) throws IllegalArgumentException
+ {
+ NetworkContext nc = new NetworkContext(serializedContext);
+ networkStack.push(nc);
+
+ contextSwitch();
+
+ if (networkStack.size() == 1)
+ notifyNetworkModeStarted();
+ }
+
+ /**
+ * This will kill the current network context.
+ * @throws IllegalStateException - if there is no network context to kill.
+ */
+ @Override
+ public void popNetworkMode() throws IllegalStateException
+ {
+ if (!isNetworkMode())
+ throw new IllegalStateException("There is no network context to kill.");
+
+ networkStack.pop();
+
+ contextSwitch();
+
+ if (!isNetworkMode())
+ notifyNetworkModeStopped();
+ }
+
+ // Mode Change Listeners : update GUI => run on event dispatcher thread
+
+ private ArrayList<ModeChangeListener> modeChangeListeners = new ArrayList<ModeChangeListener>();
+
+ @Override
+ public void addModeChangedListener(ModeChangeListener listener)
+ {
+ modeChangeListeners.add(listener);
+ }
+
+ @Override
+ public void removeModeChangedListener(ModeChangeListener listener)
+ {
+ modeChangeListeners.remove(listener);
+ }
+
+ private void notifyContestModeStarted()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.recordModeStarted();
+ }
+
+ private void notifyRecordModeStopped()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.recordModeStopped();
+ }
+
+ private void notifyNetworkModeStarted()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.networkModeStarted();
+ }
+
+ private void notifyNetworkModeStopped()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.networkModeStopped();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * CONTEXT MESSAGE LISTENERS (used to broadcast clock events from record mode)
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ private void initContextMessageListener()
+ {
+ contextMessageListener = new MessageListener(){
+
+ @Override
+ public void messageEvent(String source, String message)
+ {
+ for (MessageListener listener : broadcastListeners)
+ listener.messageEvent(source, message);
+ }
+ };
+ }
+
+ private final ArrayList<MessageListener> broadcastListeners = new ArrayList<MessageListener>();
+
+ @Override
+ public void addBroadcastListener(MessageListener listener)
+ {
+ broadcastListeners.add(listener);
+ }
+
+ @Override
+ public void removeBroadcastListener(MessageListener listener)
+ {
+ broadcastListeners.remove(listener);
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/ContextSwitcher.java b/logo/src/xlogo/kernel/userspace/context/ContextSwitcher.java
new file mode 100644
index 0000000..e38c645
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/ContextSwitcher.java
@@ -0,0 +1,43 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+
+public interface ContextSwitcher
+{
+ public LogoContext getContext();
+
+ public void addContextSwitchListener(ContextSwitchListener listener);
+
+ public void removeContextSwitchListener(ContextSwitchListener listener);
+
+ public interface ContextSwitchListener
+ {
+ public void contextSwitched(LogoContext newContext);
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/LogoContext.java b/logo/src/xlogo/kernel/userspace/context/LogoContext.java
new file mode 100644
index 0000000..15bdc1f
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/LogoContext.java
@@ -0,0 +1,312 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import xlogo.Logo;
+import xlogo.interfaces.ProcedureMapper.ProcedureMapListener;
+import xlogo.kernel.gui.GuiMap;
+import xlogo.kernel.userspace.GlobalVariableTable;
+import xlogo.kernel.userspace.PropertyListTable;
+import xlogo.kernel.userspace.files.LogoFile;
+import xlogo.kernel.userspace.procedures.Procedure;
+import xlogo.storage.global.GlobalConfig;
+/**
+ * A LogoContext contains all the symbol tables for execution of Logo programs <p>
+ * Parts of this resemble the old Workspace class, but this has a new purpose and it is used differently.
+ * In XLogo4Schools, a LogoContext is only a container for a Logo environment - all the symbol tables.
+ * <p>
+ * Note: To be consistent, the execution stack should be included in the context too, but this would (for now) need too much work
+ * to refactor the existing interpreter. It is for sure something that should be done in the future, when the entire interpreter is refactored.
+ *
+ * @author Marko Zivkovic -
+ * @author Lo�c Le Coq - methods inherited from XLogo are marked with author-tag
+ */
+public class LogoContext
+{
+ private LogoFile openFile;
+
+ private final GlobalVariableTable globals = new GlobalVariableTable();
+
+ private final PropertyListTable propertyLists = new PropertyListTable();
+
+ private final Map<String, LogoFile> files = new HashMap<String, LogoFile>();
+
+ private final HashMap<String, HashMap<String, Procedure>> procedureTable = new HashMap<String, HashMap<String, Procedure>>();
+
+ // private Map<String, Procedure> executables = new HashMap<String,
+ // Procedure>();
+
+ private final GuiMap guiMap = new GuiMap();
+
+ public LogoContext() { }
+
+ /*
+ * Symbol table getters.
+ */
+
+ /**
+ * All the files in this context
+ * @return
+ */
+ public Map<String, LogoFile> getFilesTable()
+ {
+ return files;
+ }
+
+ /**
+ * All Executable procedures from all files. Procedures with equal names,
+ * from different files, will be included in the same list. <br>
+ * This Map is used to keep track of all defined procedures and to resolve
+ * name conflicts.
+ */
+ public HashMap<String, HashMap<String, Procedure>> getProcedureTable()
+ {
+ return procedureTable;
+ }
+
+ /**
+ * Global Logo variables
+ * @return
+ */
+
+ public GlobalVariableTable getGlobals()
+ {
+ return globals;
+ }
+
+ /**
+ * Logo property lists
+ * @return
+ */
+ public PropertyListTable getPropertyLists()
+ {
+ return propertyLists;
+ }
+
+ /**
+ * For all Gui Objects (Buttons, ComboBoxes...)
+ */
+ public GuiMap getGuiMap()
+ {
+ return guiMap;
+ }
+
+ /*
+ * Context dependent operations
+ */
+
+ public String[] getFileOrder()
+ {
+ return files.keySet().toArray(new String[files.size()]);
+ }
+
+ public void openFile(String fileName)
+ {
+ openFile = files.get(fileName);
+ }
+
+ public void closeFile()
+ {
+ openFile = null;
+ }
+
+ public LogoFile getOpenFile()
+ {
+ return openFile;
+ }
+
+ /**
+ * This is the preferred method to create a file within a context, because different contexts prefer different LogoFile configurations.
+ * @throws IllegalStateException if fileName already exists in the files table
+ * @throws IOException If the file could not be created on the file system and the text not written.
+ */
+ public void createFile(String fileName, String text) throws IOException
+ {
+ if (files.containsKey(fileName))
+ throw new IllegalStateException("Attempt to create already existing file.");
+
+ LogoFile file = LogoFile.createNewFile(fileName);
+ file.setText(text);
+ file.store();
+
+ files.put(fileName, file);
+ installListeners(file);
+ }
+
+ public void importFile(File path, String newFileName) throws IOException
+ {
+ if (files.containsKey(newFileName))
+ throw new IllegalStateException("Attempt to create already existing file.");
+
+ if (!path.isFile())
+ throw new IllegalArgumentException("The specified file does not exist : " + path.toString());
+
+ String extension = GlobalConfig.LOGO_FILE_EXTENSION;
+ if(!path.getName().endsWith(extension))
+ throw new IllegalArgumentException("Only accept " + extension + " files, but received : " + path.toString());
+
+ String fileName = path.getName();
+ fileName = path.getName().substring(0, fileName.length() - extension.length());
+
+ LogoFile file = LogoFile.importFile(path, newFileName);
+ files.put(newFileName, file);
+ installListeners(file);
+ }
+
+ /**
+ * Note: does not perform checks on validity of file names.
+ * @param oldName
+ * @param newName
+ */
+ public void renameFile(String oldName, String newName)
+ {
+ if (oldName.equals(newName))
+ return;
+
+ LogoFile file = files.get(oldName);
+
+ // must first re-map in files table because file.setFileName fires events
+ files.remove(oldName);
+ files.put(newName, file);
+
+ file.setFileName(newName);
+ }
+
+ private final ArrayList<ProcedureMapListener> procedureMapListener = new ArrayList<ProcedureMapListener>();
+
+ /**
+ * This should be used when a file is created or added to this context.
+ * Listeners that were previously added with {@link #addProcedureMapListener(ProcedureMapListener)} will be installed.
+ * @param file
+ */
+ public void installListeners(LogoFile file)
+ {
+ for (ProcedureMapListener listener : procedureMapListener)
+ file.addProcedureMapListener(listener);
+ }
+
+ /**
+ * This helps the Procedure Manager to register to ProcedureMap events from the logo files.
+ * The ProcedureManager does not need to be notified explicitly when a new file is created.
+ * @see #installListeners(LogoFile)
+ * @param listener
+ */
+ public void addProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListener.add(listener);
+ for (LogoFile file : files.values())
+ file.addProcedureMapListener(listener);
+ }
+
+ public void removeProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListener.remove(listener);
+ for (LogoFile file : files.values())
+ file.removeProcedureMapListener(listener);
+ }
+
+ /*
+ * MISC
+ */
+
+ /**
+ * That's the String that is sent via TCP, and interpreted by the receiver in {@link NetworkContext},
+ * using {@link #setWorkspace(String)}
+ * @author Lo�c Le Coq
+ * @author Marko Zivkovic
+ * refactored using the new data structures.
+ */
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ for (String key : getGlobals().getVariables())
+ {
+ sb.append("-");
+ sb.append(key);
+ sb.append("\n");
+ sb.append(getGlobals().getValue(key));
+ sb.append("\n");
+ }
+
+ for (HashMap<String, Procedure> fileToProc : procedureTable.values())
+ {
+ if (fileToProc.size() != 1)
+ continue;
+
+ Procedure procedure = null;
+ for (Procedure uniqueProc : fileToProc.values())
+ procedure = uniqueProc; // retrieve the only procedure in fileToProc
+
+ sb.append(Logo.messages.getString("pour") + " " + procedure.name);
+ for (int j = 0; j < procedure.nbparametre; j++)
+ {
+ sb.append(" :" + procedure.variable.get(j));
+ }
+ sb.append("\n");
+ sb.append(procedure.instruction);
+ sb.append(Logo.messages.getString("fin"));
+ sb.append("\n\n");
+ }
+ return (new String(sb));
+ }
+
+ /**
+ * @return true : {@link FileContainerChangeListener} should be fired.
+ */
+ public boolean fireFileEvents()
+ {
+ return true;
+ }
+
+ /**
+ * @return true : {@link AmbiguityListener},
+ * {@link ExecutablesChangedListener},
+ * {@link ProceduresDefinedListener} should be fired.
+ */
+ public boolean fireProcedureEvents()
+ {
+ return true;
+ }
+
+ /**
+ * Default is true. Other contexts might override this.
+ * @return Whether it is allowed to create, delete, or rename files.
+ * @note : this is only a suggestion for the files manager and the gui, modification of the table is still possible
+ */
+ public boolean isFilesListEditAllowed()
+ {
+ return true;
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/NetworkContext.java b/logo/src/xlogo/kernel/userspace/context/NetworkContext.java
new file mode 100644
index 0000000..937e513
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/NetworkContext.java
@@ -0,0 +1,206 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+
+import xlogo.Logo;
+import xlogo.kernel.userspace.files.LogoFile;
+
+/**
+ * The network context is very different compared to the other contexts. <br>
+ * 1. It parses its contents from a serialized context string ({@link #toString()}) <br>
+ * 2. Its files are created virtually ({@link xlogo.storage.Storable#isVirtual()}). They are gone when the network mode stops, leaving no trace. <br>
+ * 3. {@link #fireFileEvents()}} and {@link fireProcedureEvents()}} suggest the managers to not fire events,
+ * because the network files are not meant to be displayed in the editor.
+ * In XLogo this effect was achieved by setting the property affichable of procedures.
+ * @author Marko Zivkovic
+ */
+public class NetworkContext extends LogoContext
+{
+
+ public NetworkContext(String networkContext) throws IllegalArgumentException
+ {
+ super();
+ setContext(networkContext);
+ }
+
+ /**
+ * This is used to include a remote context into XLogo4Schools and allow remote
+ * procedure calls via tcp. XLogo had this implemented in the Workspace class.
+ * <p>
+ * Initially, in remote mode, the workspace was temporarily replaced by a new one.
+ * To indicate that the workspace with its procedures should not be displayed
+ * in the editor, Lo�c had added setAffichable(false), meaning not displayable.<br>
+ *
+ * Note that in XLogo, the workspace contained exactly one file. Therefore
+ * a simple swap in swap would do. However, in XLogo4Schools, we need more
+ * state information, and some of the state must be preserved.
+ * For example, in network mode, I do not want the user's procedures
+ * to be removed from the procedure list. I just want to disable the list.<br>
+ * <p>
+ * With this Network context, no events will be fired that would indicate,
+ * that some procedures or files have changed. But the ContextChangedListeners
+ * will be notified that we are now in network mode, thus they can disable
+ * the files list.
+ * <p>
+ * <p>
+ * Note that in XLogo, the workspace text has to be set in the editor just
+ * because analysProcedure() reads the Logo source code from there.
+ * In the network mode, It was actually not necessary to have it in the editor,
+ * because they didn't intend do display the text anyway (setAffichable(false)).
+ * setWorkspace and toString have probably been added late, so they just "hacked"
+ * that new property in.
+ * The existing "architecture" did not allow a cleaner extension of the
+ * system, so they must have added "affichable" for this only purpose.
+ * <p>
+ * In the current implementation, "affichable" is completely removed. <br>
+ * I regulate this effect by either firing events or not.
+ * <p>
+ * Note, parsing of the context is inherited from XLogo. Its implementation is very optimistic.
+ * It assumes that only well-formed strings are provided. Therefore it throws no actual exceptions.
+ * I added IllegalArgumentException to provide a more general interface,
+ * and to prepare clients for the future (?) when this parsing is re-implemented.
+ *
+ * @param app
+ * @param wp
+ * @author Marko Zivkovic, Loic
+ */
+ protected void setContext(String wp) throws IllegalArgumentException
+ {
+ StringReader sr = new StringReader(wp);
+ BufferedReader bfr = new BufferedReader(sr);
+ try
+ {
+ String input = parseVariables(bfr);
+
+ if (input != null)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(input);
+ sb.append("\n");
+ readerToBuilder(sb, bfr);
+
+ String vFileName = "netowork_file";
+ createFile(vFileName, "");
+ LogoFile file = getFilesTable().get(vFileName);
+ file.setText(sb.toString());
+ }
+ }
+ catch (IOException e)
+ {}
+ finally
+ {
+ try
+ {
+ bfr.close();
+ }
+ catch (IOException e)
+ {}
+ sr.close();
+ }
+ }
+
+ /**
+ * append all lines of br to sb
+ */
+ private void readerToBuilder(StringBuilder sb, BufferedReader br) throws IOException
+ {
+ String line = null;
+ while((line = br.readLine()) != null)
+ {
+ sb.append(line);
+ sb.append("\n");
+ }
+ sb.deleteCharAt(sb.length()-1);
+ }
+
+ private String parseVariables(BufferedReader bfr) throws IOException, IllegalArgumentException
+ {
+ String input = "";
+ while ((input = bfr.readLine()) != null)
+ {
+ if (!input.startsWith(Logo.messages.getString("pour")))
+ {
+ String var = input.substring(1); // - expected
+ String value = bfr.readLine();
+ getGlobals().define(var, value);
+ }
+ else
+ break;
+ }
+ return input;
+ }
+
+ /**
+ * Files created in network mode are virtual.
+ */
+ @Override
+ public void createFile(String fileName, String text) throws IllegalArgumentException
+ {
+ LogoFile vFile = LogoFile.createNewVirtualFile(fileName);
+ getFilesTable().put(fileName, vFile);
+
+ LogoFile file = LogoFile.createNewVirtualFile(fileName);
+ file.setText(text);
+
+ getFilesTable().put(fileName, file);
+
+ installListeners(file);
+ }
+
+ /**
+ * @return In network mode : false : {@link FileContainerChangeListener} events will not be fired.
+ */
+ public boolean fireFileEvents()
+ {
+ return false;
+ }
+
+ /**
+ * @return In network mode : false : {@link AmbiguityListener},
+ * {@link xlogo.interfaces.ExecutablesChangedListener},
+ * {@link xlogo.interfaces.ProceduresDefinedListener} events will not be fired.
+ */
+ public boolean fireProcedureEvents()
+ {
+ return false;
+ }
+
+ /**
+ * The Network context suggest that the user should not be allowed to create files from the gui.
+ * @see LogoContext#isFilesListEditAllowed()
+ */
+ @Override
+ public boolean isFilesListEditAllowed()
+ {
+ return false;
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/RecordContext.java b/logo/src/xlogo/kernel/userspace/context/RecordContext.java
new file mode 100644
index 0000000..48237de
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/RecordContext.java
@@ -0,0 +1,146 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import xlogo.interfaces.MessageBroadcaster;
+import xlogo.kernel.userspace.files.RecordFile;
+
+/**
+ * The context for contest/record mode.
+ * When creating a new RecordContext, a number of RecordFiles are created, for each name provided in the constructor
+ * <p>
+ * Note that a RecordContext only works correctly with RecordFiles.
+ * Therefore FileManagers should always use context.createFile(); to add new files, and never put them directly into the files table.
+ * <p>
+ * Besides, RecordContext suggests to not create new files ({@link #isFilesListEditAllowed()}),
+ * but files can still be created by using Logo commands, such as define or load.
+ *
+ * @author Marko
+ */
+public class RecordContext extends LogoContext implements MessageBroadcaster
+{
+ private String[] fileOrder;
+
+ private MessageListener fileTimerListener;
+ private ArrayList<MessageListener> timerEventListeners = new ArrayList<MessageListener>();
+
+ public RecordContext(final String[] recordModeFileNames) throws IOException
+ {
+ super();
+ this.fileOrder = recordModeFileNames;
+ initFileTimerListener();
+ setupRecordFiles();
+ }
+
+ protected void setupRecordFiles() throws IOException
+ {
+ for(String fileName : fileOrder)
+ createFile(fileName, "");
+ }
+
+ private void initFileTimerListener()
+ {
+ fileTimerListener = new MessageListener(){
+
+ @Override
+ public void messageEvent(String source, String message)
+ {
+ for (MessageListener listener : timerEventListeners)
+ listener.messageEvent(source, message);
+ }
+ };
+ }
+
+ @Override
+ public void createFile(String fileName, String text) throws IOException
+ {
+ RecordFile file = RecordFile.createNewFile(fileName);
+
+ if (text != null && text.length() > 0)
+ {
+ file.setText(text);
+ file.store();
+ }
+
+ installListeners(file);
+ file.addBroadcastListener(fileTimerListener);
+
+ getFilesTable().put(fileName, file);
+ }
+
+ public void openFile(String fileName)
+ {
+ super.openFile(fileName);
+ RecordFile file = (RecordFile) getFilesTable().get(fileName);
+ file.startRecord();
+ }
+
+ @Override
+ public void closeFile()
+ {
+ RecordFile file = (RecordFile) getOpenFile();
+ file.pauseRecord();
+ super.closeFile();
+ }
+
+ @Override
+ public String[] getFileOrder()
+ {
+ // TODO must extend file order for command "define" [otherwise constant number of files in record mode]
+ // - although it's not really correct, no visible consequences in app => no priority ... program works just as well
+ return fileOrder;
+ }
+
+ /**
+ * The Record context suggest that the user should not be allowed to create files from the gui.
+ * @see LogoContext#isFilesListEditAllowed()
+ */
+ @Override
+ public boolean isFilesListEditAllowed()
+ {
+ return false;
+ }
+
+
+ @Override
+ public void addBroadcastListener(MessageListener listener)
+ {
+ timerEventListeners.add(listener);
+ }
+
+ @Override
+ public void removeBroadcastListener(MessageListener listener)
+ {
+ timerEventListeners.add(listener);
+ }
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/UserContext.java b/logo/src/xlogo/kernel/userspace/context/UserContext.java
new file mode 100644
index 0000000..064e665
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/UserContext.java
@@ -0,0 +1,191 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+import xlogo.Logo;
+import xlogo.kernel.userspace.files.LogoFile;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.user.UserConfig;
+
+public class UserContext extends LogoContext
+{
+
+ /**
+ * Load and parse all the files in current user's source directory. <br>
+ * This happens only once, in the constructor. <br>
+ * Refresh is currently not planned, but it would be easy to add.
+ *
+ * @param userDir
+ */
+ public UserContext()
+ {
+ loadUserFiles();
+ }
+
+ private void loadUserFiles()
+ {
+ UserConfig userConfig = WSManager.getUserConfig();
+
+ if (userConfig.isVirtual())
+ return;
+
+ File sourceDir = userConfig.getSourceDirectory();
+ if (!sourceDir.exists())
+ sourceDir.mkdirs();
+
+ if (!sourceDir.isDirectory())
+ {
+ DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("ws.error.title"),
+ Logo.messages.getString("ws.error.userdir.not.dir"));
+ return;
+ }
+
+ StringBuilder ioErrors = new StringBuilder();
+
+ for (String fileName : getLogoFileNamesFromDirectory(sourceDir))
+ {
+ String name = fileName.substring(0, fileName.length() - GlobalConfig.LOGO_FILE_EXTENSION.length());
+ userConfig.addFile(name);
+ try
+ {
+ LogoFile file = LogoFile.loadFile(name);
+ getFilesTable().put(file.getPlainName(), file);
+
+ }
+ catch (IOException e)
+ {
+ ioErrors.append(e.toString());
+ ioErrors.append("\n\n");
+ }
+ }
+
+ // must remove files from fileOrder that could not be found anymore.
+ for (String fileName : new ArrayList<String>(userConfig.getFileOrder()))
+ {
+ if (!getFilesTable().containsKey(fileName))
+ userConfig.getFileOrder().remove(fileName);
+ }
+
+ if (ioErrors.length() > 0)
+ {
+ DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("ws.error.title"),
+ Logo.messages.getString("ws.error.could.not.load.logo.files") + "\n" + ioErrors.toString());
+ }
+ }
+
+ /**
+ * Caller must make sure that newName does not already exist.
+ */
+ @Override
+ public void renameFile(String oldName, String newName)
+ {
+ super.renameFile(oldName, newName);
+ WSManager.getUserConfig().renameFile(oldName, newName);
+ }
+
+ @Override
+ public void createFile(String fileName, String text) throws IOException
+ {
+ /*
+ * Eager creation of files in file order list in user config.
+ */
+
+ if (!WSManager.getUserConfig().isVirtual())
+ super.createFile(fileName, text);
+ else
+ {
+ LogoFile file = LogoFile.createNewVirtualFile(fileName);
+ file.setText(text);
+ getFilesTable().put(fileName, file);
+ installListeners(file);
+ }
+ WSManager.getUserConfig().addFile(fileName);
+ }
+
+ @Override
+ public void importFile(File path, String newFileName)
+ {
+ try
+ {
+ super.importFile(path, newFileName);
+ }
+ catch (IOException e)
+ {
+ DialogMessenger.getInstance().dispatchError(MessageKeys.GENERAL_ERROR_TITLE, "Could not import file : \n" + e.toString());
+ }
+ WSManager.getUserConfig().addFile(newFileName);
+ }
+
+ private String[] getLogoFileNamesFromDirectory(File dir)
+ {
+ return dir.list(new FilenameFilter(){
+ public boolean accept(File file, String name)
+ {
+ return name.endsWith(".lgo");
+ }
+ });
+ }
+
+ @Override
+ public String[] getFileOrder()
+ {
+ /*
+ * Lazy deletion from file order list in user config.
+ */
+ ArrayList<String> list = new ArrayList<String>(WSManager.getUserConfig().getFileOrder());
+ Map<String,LogoFile> filesTable = getFilesTable();
+
+ if (filesTable.size() != list.size())
+ {
+ Iterator<String> iter = list.iterator();
+ String current;
+
+ while(iter.hasNext())
+ {
+ current = iter.next();
+ if(!filesTable.containsKey(current))
+ iter.remove();
+ }
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/LogoFile.java b/logo/src/xlogo/kernel/userspace/files/LogoFile.java
new file mode 100644
index 0000000..92fe23c
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/files/LogoFile.java
@@ -0,0 +1,755 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+
+import xlogo.Logo;
+import xlogo.interfaces.ErrorDetector;
+import xlogo.interfaces.ProcedureMapper;
+import xlogo.kernel.userspace.ProcedureErrorMessage;
+import xlogo.kernel.userspace.procedures.ExecutablesContainer;
+import xlogo.kernel.userspace.procedures.Procedure;
+import xlogo.kernel.userspace.procedures.Procedure.State;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.Storable;
+import xlogo.storage.StorableDocument;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.user.UserConfig;
+import xlogo.storage.workspace.NumberOfBackups;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.utils.Utils;
+
+/**
+ * This class holds the text file a user entered in the editor.
+ * It analyzes the text and maintains a symbol table for all defined procedures that live within it.
+ * <p>
+ * The file does never store itself implicitly, except for when it is created using {@link #createNewFile(String)} or renamed using {@link #setFileName(String)}
+ * In every other case, {@link #store()}} or {@link #storeCopyToFile(File)}} must be invoked explicitly.
+ * <p>
+ * The file's text can be set using {@link #setTextFromReader(BufferedReader)}} (preferred) or {@link #setText(String)}}.
+ * Both will try to parse the signature of all procedures using the constructor of {@link xlogo.kernel.userspace.procedures.Procedure}
+ *
+ * @author Marko Zivkovic, (Lo�c Le Coq's parsing of procedures is not recognizable anymore.)
+ *
+ */
+public class LogoFile extends StorableDocument implements ExecutablesContainer, ProcedureMapper, ErrorDetector
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1117062836862782516L;
+
+ /**
+ * UserConfig of the owner of this file
+ */
+ private UserConfig userConfig;
+
+ /**
+ * Contains only executable procedures
+ */
+ private Map<String, Procedure> executables;
+
+ /**
+ * Contains all procedures, no matter what the state is.
+ * The order of the list is relevant to reproduce the editor text after the Logo command 'eraseprocedure'
+ * (after {@link #deleteProcedure(String)}})
+ */
+ private ArrayList<Procedure> allProcedures;
+
+ /**
+ * A flag that indicated whether the last parsing ended with errors or ambiguities
+ */
+ private boolean hasError;
+
+ /*
+ * CONSTRUCTOR & STATIC CONSTRUCTORS, FILE LOADERS
+ */
+
+ /**
+ * The LogoFile automatically sets its location to the current user's src directory, if that user is not virtual.
+ * @param fileName
+ * @throws IllegalArgumentException see : {@link Storable#setFileName()}
+ */
+ protected LogoFile(String fileName) throws IllegalArgumentException
+ {
+ super();
+ this.userConfig = WSManager.getUserConfig();
+ if (!userConfig.isVirtual())
+ setLocation(userConfig.getSourceDirectory());
+ setFileName(fileName);
+ executables = new HashMap<String, Procedure>();
+ allProcedures = new ArrayList<Procedure>();
+ }
+
+ public static LogoFile createNewVirtualFile(String fileName)
+ {
+ LogoFile file = null;
+ try
+ {
+ file = new LogoFile(fileName);
+ file.makeVirtual();
+ }
+ catch (IllegalArgumentException ignore) { }
+ return file;
+ }
+ /**
+ * Create a new file and store it in the user's source directory.
+ * @throws IOException
+ * @throws IllegalArgumentException
+ */
+ public static LogoFile createNewFile(String fileName) throws IOException, IllegalArgumentException
+ {
+ LogoFile file = new LogoFile(fileName);
+ file.setupFileSystem();
+ return file;
+ }
+
+ /**
+ * Load the specified file from the user's source directory and parse procedure structures.
+ * @param fileName - without extension
+ * @return
+ * @throws IOException
+ */
+ public static LogoFile loadFile(String fileName) throws IOException
+ {
+ UserConfig userConfig = WSManager.getUserConfig();
+ File path = userConfig.getLogoFilePath(fileName);
+ String text = Utils.readLogoFile(path.toString());
+ LogoFile file = new LogoFile(fileName);
+ file.setText(text);
+ if (userConfig.isVirtual())
+ file.makeVirtual();
+ return file;
+ }
+
+ /**
+ * Open any file on the file system and integrate it in the UserSpace.
+ * The file will be stored and made visible under the specified newFileName
+ * @param file
+ * @param newFileName
+ * @throws IOException
+ */
+ public static LogoFile importFile(File path, String newFileName) throws IOException
+ {
+ String text = Utils.readLogoFile(path.toString());
+ LogoFile file = new LogoFile(newFileName);
+ file.setText(text);
+ if (WSManager.getUserConfig().isVirtual())
+ file.makeVirtual();
+ file.store();
+ return file;
+ }
+
+ protected UserConfig getUserConfig()
+ {
+ return userConfig;
+ }
+ /**
+ * This assumes that the file name is well formed. No additional checks are performed
+ * Rename this LogoFile and the file on the file system, if it exists there. Notify all FileChangeListeners.
+ * This accepts name with or without .lgo extension.
+ * @param newFileName - without extension
+ */
+ @Override
+ public void setFileName(String newFileName)
+ {
+ if (newFileName == null || newFileName.length() == 0)
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.EMPTY_NAME));
+ return;
+ }
+
+ if (!Storable.checkLegalName(newFileName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.ILLEGAL_NAME) + " : " + newFileName);
+ return;
+ }
+
+ String oldPlainName = getPlainName();
+ super.setFileName(newFileName);
+ String newPlainName = getPlainName();
+
+ if (oldPlainName != null)
+ notifyRenamed(oldPlainName, newPlainName);
+ }
+
+ @Override
+ public String getFileNameExtension()
+ {
+ return GlobalConfig.LOGO_FILE_EXTENSION;
+ }
+
+ private void notifyRenamed(String oldName, String newName)
+ {
+ for(ProcedureMapListener listener : procedureMapListeners)
+ listener.ownerRenamed(oldName, newName);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * STORE & LOAD FILE
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * If this is not virtual, create this file on the file system and create a backup folder for it.
+ * @throws IOException
+ */
+ protected void setupFileSystem() throws IOException
+ {
+ if (isVirtual())
+ return;
+
+ File source = getFilePath();
+ File backupFolder = userConfig.getFileBackupDir(getPlainName());
+
+ if (!source.getParentFile().exists())
+ source.getParentFile().mkdirs();
+
+ if (!backupFolder.exists())
+ backupFolder.mkdirs();
+
+ storeCopyToFile(source);
+ }
+
+ /**
+ * If this is not virtual, store the file in the source folder of the UserSpace, <br>
+ * and another copy in the backup folder, if this is required by {@link WorkspaceConfig#getNumberOfBackups()}.
+ */
+ @Override
+ public void store() throws IOException
+ {
+ super.store();
+ if (isVirtual())
+ return;
+ doBackup();
+ }
+
+ @Override
+ public void delete()
+ {
+ super.delete();
+ Collection<String> procedures = new ArrayList<String>(executables.keySet());
+ executables.clear();
+ allProcedures.clear();
+ notifyDeleted(procedures);
+ }
+
+
+ /**
+ * Store a backup copy of this file.
+ * If the number of maximally allowed backups is exceeded,
+ * delete the oldest copies until the number of backups equals the limit
+ * defined by {@link WorkspaceConfig#getNumberOfBackups()}}
+ * @throws IOException
+ */
+ private void doBackup() throws IOException
+ {
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+ NumberOfBackups nob = wc.getNumberOfBackups();
+
+ File backupFile = userConfig.getBackupFilePath(getPlainName());
+ File backupFolder = backupFile.getParentFile();
+ if (!backupFolder.exists())
+ backupFolder.mkdirs();
+
+ if (nob != NumberOfBackups.NO_BACKUPS)
+ storeCopyToFile(backupFile);
+
+ if (nob == NumberOfBackups.INFINITE)
+ return;
+
+ int max = nob.getNumber(); // max is >= 0
+ // Assume no outer manipulation of that directory
+ File[] backups = backupFolder.listFiles();
+
+ int actual = backups.length;
+ if (actual <= max)
+ return;
+
+ // must delete the oldest backups
+ Arrays.sort(backups, new Comparator<File>(){
+ public int compare(File f1, File f2)
+ {
+ return f2.getName().compareTo(f1.getName().toString());
+ }
+ });
+
+ while (actual > max)
+ {
+ actual--;
+ backups[actual].delete();
+ }
+ }
+
+ /**
+ * The file path of this LogoFile in the source directory.
+ */
+ @Override
+ public File getFilePath()
+ {
+ if (super.getFilePath() != null)
+ return super.getFilePath();
+ return userConfig.getLogoFilePath(getPlainName());
+ }
+
+ @Override
+ public File getLocation()
+ {
+ if (super.getLocation() != null)
+ return super.getLocation();
+ return userConfig.getSourceDirectory();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * SERIALIZATION AND DESERIALIZATION
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ protected String generateText()
+ {
+ StringBuilder text = new StringBuilder();
+
+ for(Procedure proc : allProcedures)
+ {
+ text.append(proc.getText());
+ text.append("\n");
+ }
+
+ return text.toString();
+ }
+
+ /**
+ * Changes made 21.6.2013 and July 2013
+ * <p>
+ * In XLogo, this was {@code Editor.analyseprocedure()}
+ * <p><p>
+ * <b>Refactored:</b><br>
+ * Initially all that happens in
+ * <li> {@link #setText(String)}
+ * <li> {@link #parseText(BufferedReader)}
+ * <li> {@link #nextProcedure(BufferedReader)}
+ * <li> {@link Procedure#Procedure() }
+ * <li> {@link #untilEnd() }<br>
+ * was composed into one big and unreadable procedure {@code Editor.analyseprocedure()}.
+ * Note that the Editor (a GUI Controller) was responsible to parse text.
+ * {@code Editor.analyseprocedure()} took the text it was analyzing from the Editor's text component directly.
+ * Thus, whenever a procedure was programmatically defined, or if a workspace was received from the network,
+ * the text had to be written to the editor first before {@code Editor.analyseprocedure()} was called.<p>
+ * Note that in the networking case, the received text was never meant to be displayed.
+ * In that case the Editor served merely as a temporary container such that analyseProcedure() could read the text from it.
+ * This was the only reason why the property "affichable" (displayable) was added to so many classes.
+ *
+ * <p><p>
+ * <b>New Mechanism:</b><br>
+ * In XLogo, as soon as an error was found in the document, an exception was thrown and displayed to the user. <br>
+ * The new approach is to first split the document wherever a line starts with a token 'end'.
+ * <p>
+ * [#belongs to procedure 1<br>
+ * ... <br>
+ * end][#belongs to procedure 2 <br>
+ * ... <br>
+ * end] ...
+ * <p>
+ * These parts of the document are given to the constructor {@code Procedure#Procedure(String)},
+ * so the procedure can maintain its own state
+ * <br>
+ * Based on the type of errors, a Procedure can now detect several errors at a time and report them.
+ * The LogoFile can then report all errors that have been collected from its procedures.
+ * This approach allows to give more precise error messages to the user.
+ * Example: It is now possible to say which procedure is missing an 'end'
+ * <br>
+ * In the new implementation, a Procedure is not necessarily executable.
+ * Whether it is executable, can be read from its state {@link Procedure.State}.
+ * Its state can be
+ * <li> UNINITIALIZED
+ * <li> EXECUTABLE
+ * <li> COMMENT_ONLY (for white space and comments at the end of the document)
+ * <li> ERROR
+ * <li> AMBIGUOUS_NAME <br>
+ * <p>
+ * Only EXECUTABLE procedures are included in the procedureTable of the {@link xlogo.kernel.userspace.context.LogoContext},
+ * but all procedures are maintained by LogoFile.
+ * @param str
+ * @throws DocumentStructureException
+ * @throws IOException
+ */
+ @Override
+ protected void parseText(BufferedReader br)
+ {
+ /*
+ * Keep old procedures before reset of procedure tables.
+ * procedures that remain in the end, will count as deleted.
+ * procedures that existed before, but have errors now, count as deleted.
+ */
+ HashMap<String, Void> deleted = new HashMap<String, Void>();
+ for (Procedure proc : executables.values())
+ deleted.put(proc.getName(), null);
+
+ /*
+ * Must notify that all old executables are deleted as soon as a single procedure has an error,
+ * We want the whole file to be not executable when there exists an error
+ */
+ Collection<String> oldExecutables = new ArrayList<String>(executables.keySet());
+
+ /*
+ * We don't want the procedures to become ordered by creation time in the editor.
+ * The Logo command "define" was affected by this change, hence it was adapted to work as before.
+ * <p>
+ * Because we delete all the procedures from the tables [unlike XLogo] every time before reading the file,
+ * the procedures will be stored in the order in which the user defined them last.
+ */
+ resetProcedureTables(); // Added by Marko Zivkovic, 21.6.2013
+
+ // When the file is empty, it has no errors...
+ hasError = false;
+
+ try
+ {
+ while (br.ready()) // next procedure
+ {
+ Procedure proc;
+ String procedureText = untilEnd(br);
+ if (procedureText.equals(""))
+ break;
+ proc = new Procedure(procedureText);
+ proc.setOwnerName(getPlainName());
+
+ if (proc.getState() == State.EXECUTABLE)
+ {
+ deleted.remove(proc.getName());
+ }
+ addProcedure(proc);
+
+ if(proc.getState() == State.ERROR || proc.getState() == State.AMBIGUOUS_NAME)
+ hasError = true;
+ }
+ }
+ catch (IOException e){} // This should not happen, because no actual IO happens
+ finally { try { br.close(); } catch (IOException e) { } }
+
+ if(hasError)
+ {
+ notifyDeleted(oldExecutables);
+ return;
+ }
+
+ if (deleted.size() > 0)
+ notifyDeleted(deleted.keySet());
+
+ if (executables.size() > 0)
+ notifyDefined(executables.keySet());
+ }
+
+ /**
+ * @return String until the token 'end' is found on a line, or until the end of the BufferedReader
+ * @throws IOException
+ */
+ private static String untilEnd(BufferedReader br) throws IOException
+ {
+ String end = Logo.messages.getString("fin").toLowerCase();
+ StringBuffer text = new StringBuffer();
+ String line;
+
+ while (br.ready())
+ {
+ line = br.readLine();
+ if (line == null)
+ break;
+ else if (line.trim().toLowerCase().equals(end))
+ {
+ text.append(end);
+ break;
+ }
+ else
+ text.append(line + "\n");
+ }
+
+ return text.toString();
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PROCEDURE CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * Note: does not notify <br>
+ * Delete all procedures
+ */
+ private void resetProcedureTables()
+ {
+ allProcedures.clear();
+ executables.clear();
+ invalidateText();
+ setText(null);
+ }
+
+ /**
+ * Implementation of the Logo Command "define". <br>
+ * FileChangeListeners are notified.
+ * <p>
+ * In XLogo, the command define had no effect, when an error was detected while parsing.
+ * The same is true here, because an IllegalStateException is thrown if procedure is not executable.
+ * <p>
+ * In XLogo4Schools, to preserve semantics, we create a {@link Procedure} using its normal constructor and then check for errors.
+ * If errors exist in the procedure text, the procedure should not be defined in its destined file either.
+ * The responsibility whether a procedure is added to a file lies therefore in the interpreter.
+ * <p>
+ * Existing procedures with the same name are just redefined, as in XLogo.
+ * <p>
+ * @param procedure Expects an executable procedure.
+ * @throws IllegalStateException - if procedure is not Executable or its name is ambiguous in this file.
+ */
+ @Override
+ public void defineProcedure(Procedure procedure)
+ {
+ if (procedure.getState() != State.EXECUTABLE)
+ throw new IllegalStateException("Attempt to define procedure which is not executable.");
+
+ Procedure other = executables.get(procedure.name);
+
+ invalidateText();
+
+ if (other != null)
+ {
+ if (other.getState() == State.AMBIGUOUS_NAME)
+ throw new IllegalStateException("Attempt to redefine ambiguous procedure.");
+
+ other.redefine(procedure);
+
+ }else
+ {
+ allProcedures.add(procedure);
+ executables.put(procedure.name, procedure);
+ }
+ notifyDefined(procedure.getName());
+ }
+
+ /**
+ * This is for the Logo command 'eraseprocedure'
+ * @param name
+ * @throws IllegalArgumentException
+ */
+ @Override
+ public void eraseProcedure(String name)
+ {
+ Procedure proc = getExecutable(name);
+ if(proc == null)
+ throw new IllegalStateException("Attempt to erase procedure which exists not.");
+ allProcedures.remove(proc);
+ executables.remove(name);
+ invalidateText();
+ notifyDeleted(proc.getName());
+ }
+
+ /**
+ * Note: Does not notify listeners! <br>
+ * Semantics: If more than one procedures with the same name are defined in a document,
+ * all are marked ambiguous. The first one is kept in the executables list to track ambiguity.
+ * @param pr
+ */
+ protected void addProcedure(Procedure pr)
+ {
+ if (pr.getState() == State.EXECUTABLE)
+ {
+ Procedure other = executables.get(pr.name);
+
+ if(other != null)
+ {
+ other.makeAmbiguous();
+ pr.makeAmbiguous();
+ }
+ else
+ executables.put(pr.name, pr);
+ }
+ allProcedures.add(pr);
+ invalidateText();
+ }
+
+ @Override
+ public Procedure getExecutable(String name)
+ {
+ return executables.get(name);
+ }
+
+ /**
+ * @param name
+ * @return Whether an executable procedure with the specified name exists
+ */
+ @Override
+ public boolean isExecutable(String name)
+ {
+ return executables.get(name) != null;
+ }
+
+ @Override
+ public Collection<Procedure> getExecutables()
+ {
+ if (hasErrors())
+ return new ArrayList<Procedure>();
+ return executables.values();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * ERROR DETECTOR
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public boolean hasErrors()
+ {
+ return hasError;
+ }
+
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> allErrors = new ArrayList<ProcedureErrorMessage>();
+ for(Procedure proc : allProcedures)
+ {
+ for (xlogo.kernel.userspace.procedures.ProcedureErrorType e : proc.getErrors())
+ {
+ String description = proc.getName();
+ if (description == null)
+ {
+ description = proc.getText().length() < 100 ?
+ proc.getText() :
+ proc.getText().substring(0, 100) + "...";
+ }
+
+ allErrors.add(new ProcedureErrorMessage(e, description, getPlainName()));
+ }
+ }
+ return allErrors;
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PROCEDURE MAPPER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * Only executables.
+ * If the file has errors, no procedure is returned.
+ */
+ @Override
+ public Collection<String> getAllProcedureNames()
+ {
+ if (hasErrors())
+ return new ArrayList<String>();
+
+ ArrayList<String> procedureNames = new ArrayList<String>();
+
+ for (Procedure p : executables.values())
+ procedureNames.add(p.getName());
+
+ return procedureNames;
+ }
+
+ @Override
+ public Collection<String> getAllProcedureNames(String fileName)
+ {
+ if (fileName.equals(getPlainName()))
+ return getAllProcedureNames();
+ return null;
+ }
+
+ /**
+ * Behaves similar like contains(). If the procedure is in the file's executable list, then returns this file's plainName. Otherwise null.
+ */
+ @Override
+ public String getProcedureOwner(String procedureName)
+ {
+ if (executables.containsKey(procedureName))
+ return getPlainName();
+ return null;
+ }
+
+ // Procedure Map Listeners
+
+ private final ArrayList<ProcedureMapListener> procedureMapListeners = new ArrayList<ProcedureMapListener>();
+
+ @Override
+ public void addProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.add(listener);
+
+ if(executables.size() > 0)
+ notifyDefined(executables.keySet()); // TODO hmmm
+ }
+
+ @Override
+ public void removeProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.remove(listener);
+ }
+
+ protected void notifyDefined(Collection<String> procedures)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(getPlainName(), procedures);
+ }
+
+ protected void notifyDefined(String procedure)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(getPlainName(), procedure);
+ }
+
+ protected void notifyDeleted(Collection<String> collection)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(getPlainName(), collection);
+ }
+
+ protected void notifyDeleted(String procedure)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(getPlainName(), procedure);
+ }
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/LogoFileContainer.java b/logo/src/xlogo/kernel/userspace/files/LogoFileContainer.java
new file mode 100644
index 0000000..9c077a9
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/files/LogoFileContainer.java
@@ -0,0 +1,39 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import xlogo.interfaces.BasicFileContainer;
+
+
+public interface LogoFileContainer extends BasicFileContainer
+{
+ /**
+ * Logo Command implementation
+ */
+ public void editAll();
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/LogoFilesManager.java b/logo/src/xlogo/kernel/userspace/files/LogoFilesManager.java
new file mode 100644
index 0000000..7a54902
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/files/LogoFilesManager.java
@@ -0,0 +1,560 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+
+import xlogo.Logo;
+import xlogo.interfaces.ErrorDetector.FileErrorCollector;
+import xlogo.kernel.userspace.ProcedureErrorMessage;
+import xlogo.kernel.userspace.context.ContextSwitcher;
+import xlogo.kernel.userspace.context.LogoContext;
+import xlogo.kernel.userspace.context.ContextSwitcher.ContextSwitchListener;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.Storable;
+import xlogo.storage.global.GlobalConfig;
+
+/**
+ * This Manager is completely new, because XLogo did not support multiple files. <br>
+ * During the requirements analysis, we have decided to maintain a global scope for procedures.
+ * That means a procedure defined in file A is visible in file B.
+ * <p>
+ * If we find during testing that the global scope is confusing for children and it leads to many ambiguity conflicts,
+ * then the current architecture allows to easily switch to file-wide scope. Instead of retrieving executables from the context's procedure table,
+ * we can directly retrieve them from the currently open/active file.
+ *
+ * @author Marko Zivkovic
+ */
+public class LogoFilesManager implements LogoFileContainer, FileErrorCollector
+{
+ private final ContextSwitcher contextProvider;
+ private LogoContext context;
+
+ private final ArrayList<FileContainerChangeListener> fileListeners = new ArrayList<FileContainerChangeListener>();
+
+ public LogoFilesManager(ContextSwitcher contextProvider)
+ {
+ this.contextProvider = contextProvider;
+ initContextSwitchListener();
+ setContext(contextProvider.getContext());
+ }
+
+ private void initContextSwitchListener()
+ {
+ contextProvider.addContextSwitchListener(new ContextSwitchListener(){
+ @Override
+ public void contextSwitched(LogoContext newContext)
+ {
+ setContext(newContext);
+ }
+ });
+ }
+
+ private void setContext(LogoContext newContext)
+ {
+ LogoContext old = context;
+ context = newContext;
+
+ LogoFile openFile = newContext.getOpenFile();
+ if (openFile != null)
+ closeFile(openFile.getPlainName());
+
+ if (newContext.fireFileEvents()) // Example : Network context does not change GUI, only internal change => no events
+ {
+ if (old != null && old.fireFileEvents())
+ for(LogoFile file : old.getFilesTable().values())
+ notifyFileRemoved(file.getPlainName());
+
+ for (String fileName : newContext.getFileOrder())
+ {
+ notifyFileAdded(fileName);
+ if (context.getFilesTable().get(fileName).hasErrors())
+ notifyErrorsDetected(fileName);
+ }
+ }
+
+ if (old == null || old.isFilesListEditAllowed() != newContext.isFilesListEditAllowed())
+ notifyRightsChanged();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * X4S Specific features and Logo command implementations
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * The implementation of the Logo command {@code editall} or {@code edall} <br>
+ * In XLogo4Schools, we cannot open all files simultaneously to show all procedures. Instead, editall opens the file that was edited last.
+ */
+ public void editAll()
+ {
+ String fileName = getLastEditedFileName();
+ if (fileName == null)
+ return;
+ openFile(fileName);
+ }
+
+ @Override
+ public void importFile(File filePath) throws IOException
+ {
+ String name = filePath.getName().substring(0,
+ filePath.getName().length()
+ - GlobalConfig.LOGO_FILE_EXTENSION.length());
+
+ if(existsFile(name))
+ name = makeUniqueFileName(name);
+ context.importFile(filePath, name);
+ notifyFileAdded(name);
+ }
+
+ /**
+ * If file is a directory, the exported file will be named fileName.
+ * Otherwise the Logo-file will be exported to the file specified by dest
+ * @param fileName
+ * @param dest
+ * @throws IOException
+ */
+ public void exportFile(String fileName, File dest) throws IOException
+ {
+
+ if (dest.isDirectory())
+ exportFile(fileName, dest, fileName);
+ else
+ {
+ File parent = dest.getParentFile();
+ String targetName = dest.getName();
+ exportFile(fileName, parent, targetName);
+ }
+ }
+
+ /**
+ * @param fileName - of a file in the current context
+ * @param location - an existing directory on the file system
+ * @param targetName - the exported file's name
+ * @throws IOException
+ */
+ public void exportFile(String fileName, File location, String targetName) throws IOException
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+
+ if(file == null)
+ throw new IllegalArgumentException("The specified fileName does not exist in the context.");
+
+ if (!location.isDirectory())
+ throw new IllegalArgumentException("The specified location does not exist : " + location.toString());
+
+ String extendedName = targetName;
+
+ if(extendedName == null || extendedName.length() == 0)
+ extendedName = fileName;
+
+ String extension = GlobalConfig.LOGO_FILE_EXTENSION;
+
+ if(!extendedName.endsWith(extension))
+ extendedName += extension;
+
+ File target = new File(location.toString() + File.separator + extendedName);
+ file.storeCopyToFile(target);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * LOGO FILE CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public String[] getFileNames()
+ {
+ return context.getFileOrder();
+ }
+
+ @Override
+ public void createFile(String fileName) throws IOException
+ {
+ context.createFile(fileName, "");
+ notifyFileAdded(fileName);
+ }
+
+ @Override
+ public void writeFileText(String fileName, String content)
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+
+ if (file == null)
+ throw new IllegalStateException("Attempt to write to inexistent file.");
+
+ boolean hadErrors = file.hasErrors();
+
+ file.setText(content);
+
+ if (file.hasErrors())
+ notifyErrorsDetected(fileName); // notify anyway
+ else if (hadErrors)
+ notifyErrorsCorrected(fileName);
+ }
+
+ @Override
+ public void storeFile(String fileName) throws IOException
+ {
+ context.getFilesTable().get(fileName).store();
+ }
+
+ /**
+ * The file is also deleted from the file system
+ */
+ @Override
+ public void removeFile(String fileName)
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+ file.delete();
+ context.getFilesTable().remove(fileName);
+ notifyFileRemoved(fileName);
+ }
+
+ /**
+ * Deletes all files from the context and removes them from the context's tables. <br>
+ * Note: The events caused be deleting the files should cause all the procedures to disappear from the tables as well.
+ * [But the files manager doesn't care about procedures]
+ */
+ @Override
+ public void eraseAll()
+ {
+ Collection<LogoFile> files = context.getFilesTable().values();
+
+ while (!files.isEmpty())
+ {
+ LogoFile nextVictim = null;
+ for (LogoFile file : files)
+ {
+ nextVictim = file;
+ break;
+ }
+ nextVictim.delete();
+ context.getFilesTable().remove(nextVictim.getPlainName());
+ notifyFileRemoved(nextVictim.getPlainName());
+ }
+ context.getFilesTable().clear();
+ }
+
+ @Override
+ public boolean existsFile(String name)
+ {
+ return context.getFilesTable().containsKey(name);
+ }
+
+ @Override
+ public String readFile(String name)
+ {
+ return context.getFilesTable().get(name).getText();
+ }
+
+ /**
+ * Please make sure the renaming makes sense, otherwise an IllegalStateException is thrown at you.
+ */
+ @Override
+ public void renameFile(String oldName, String newName)
+ {
+ if (oldName.equals(newName))
+ return;
+
+ if(!existsFile(oldName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.RENAME_INEXISTENT_FILE));
+ return;
+ }
+
+ if (existsFile(newName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.WS_FILENAME_EXISTS_ALREADY));
+ return;
+ }
+
+ if (newName == null || newName.length() == 0)
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.EMPTY_NAME));
+ return;
+ }
+
+ if (!Storable.checkLegalName(newName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.ILLEGAL_NAME) + " : " + newName);
+ return;
+ }
+
+ context.renameFile(oldName, newName);
+ notifyFileRenamed(oldName, newName);
+ }
+
+ @Override
+ public String makeUniqueFileName(String base)
+ {
+ int i = 0;
+ String name = null;
+ do
+ {
+ name = base + i;
+ i++;
+ } while (existsFile(name));
+ return name;
+ }
+
+ /**
+ * @throws IllegalArgumentException if the specified file does not exist in the current context.
+ */
+ @Override
+ public void openFile(String fileName)
+ {
+ if(!existsFile(fileName))
+ throw new IllegalStateException("The specified file to open does not exist in the current context.");
+
+ LogoFile openFile = context.getOpenFile();
+ if(openFile != null)
+ closeFile(openFile.getPlainName());
+
+ context.openFile(fileName);
+ notifyFileOpened(fileName);
+ }
+
+ /**
+ * This can handle only one open file.
+ * If the wrong filename is closed, nothing happens<p>
+ * @throws IllegalStateException
+ */
+ @Override
+ public void closeFile(String fileName)
+ {
+ LogoFile openFile = context.getOpenFile();
+ if (openFile == null || !openFile.getPlainName().equals(fileName))
+ throw new IllegalStateException("Attempting to close a file that was not opened.");
+ context.closeFile();
+ notifyFileClosed(openFile.getPlainName());
+ }
+
+ /**
+ * returns null if no file is open.
+ */
+ @Override
+ public String getOpenFileName()
+ {
+ LogoFile file = context.getOpenFile();
+ if (file == null)
+ return null;
+ return file.getPlainName();
+ }
+
+ public boolean isFilesListEditable()
+ {
+ return context.isFilesListEditAllowed();
+ }
+
+ /**
+ * the name of the file that was edited last in this context.
+ */
+ @Override
+ public String getLastEditedFileName()
+ {
+ Calendar latest = Calendar.getInstance();
+ latest.setTimeInMillis(0);
+
+ LogoFile result = null;
+ for (LogoFile file : context.getFilesTable().values())
+ {
+ Calendar fileDefinedAt = file.getLastSync();
+ if (latest.before(fileDefinedAt))
+ {
+ result = file;
+ latest = fileDefinedAt;
+ }
+ }
+ if (result == null)
+ return null;
+
+ return result.getPlainName();
+ }
+
+ // Change listeners : these event update the gui, they must run on the event dispatcher thread
+
+ @Override
+ public void addFileListener(FileContainerChangeListener listener)
+ {
+ if (listener == null)
+ throw new IllegalArgumentException("listener must not be null.");
+ fileListeners.add(listener);
+ }
+
+ @Override
+ public void removeFileListener(FileContainerChangeListener listener)
+ {
+ fileListeners.remove(listener);
+ }
+
+ private void notifyFileAdded(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileAdded(fileName);
+ }
+
+ private void notifyFileRemoved(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileRemoved(fileName);
+ }
+
+ private void notifyFileRenamed(final String oldName, final String newName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileRenamed(oldName, newName);
+ }
+
+ private void notifyFileOpened(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileOpened(fileName);
+ }
+
+ private void notifyFileClosed(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileClosed(fileName);
+ }
+
+ private void notifyRightsChanged()
+ {
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.editRightsChanged(context.isFilesListEditAllowed());
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * ERROR COLLECTOR : these events do not update the gui directly, they must not run on the event dispatcher thread
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public Collection<String> getAllErroneousFiles()
+ {
+ ArrayList<String> erroneousFiles = new ArrayList<String>();
+
+ for(LogoFile file : context.getFilesTable().values())
+ if(file.hasErrors())
+ erroneousFiles.add(file.getPlainName());
+
+ return erroneousFiles;
+ }
+
+ @Override
+ public boolean hasErrors()
+ {
+ for(LogoFile file : context.getFilesTable().values())
+ if(file.hasErrors())
+ return true;
+ return false;
+ }
+
+ @Override
+ public boolean hasErrors(String fileName)
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+ if (file == null)
+ throw new IllegalStateException("The specified fileName does not exist in this context.");
+
+ return file.hasErrors();
+ }
+
+ @Override
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> allErrors = new ArrayList<ProcedureErrorMessage>();
+ for (LogoFile file : context.getFilesTable().values())
+ allErrors.addAll(file.getAllErrors());
+ return allErrors;
+ }
+
+ // Error listeners
+
+ private final ArrayList<ErrorListener> errorListeners = new ArrayList<ErrorListener>();
+
+ @Override
+ public void addErrorListener(ErrorListener listener)
+ {
+ errorListeners.add(listener);
+ }
+
+ @Override
+ public void removeErrorListener(ErrorListener listener)
+ {
+ errorListeners.add(listener);
+ }
+
+
+ private void notifyErrorsDetected(String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+ for (ErrorListener listener : errorListeners)
+ listener.errorsDetected(fileName);
+ }
+
+ private void notifyErrorsCorrected(String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+ for (ErrorListener listener : errorListeners)
+ listener.allErrorsCorrected(fileName);
+ }
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/RecordFile.java b/logo/src/xlogo/kernel/userspace/files/RecordFile.java
new file mode 100644
index 0000000..9220f28
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/files/RecordFile.java
@@ -0,0 +1,234 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+
+import javax.swing.Timer;
+
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+import xlogo.Logo;
+import xlogo.interfaces.MessageBroadcaster;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.WSManager;
+import xlogo.storage.user.UserConfig;
+import xlogo.utils.Utils;
+
+/**
+ * This is a {@link LogoFile} which is used in contest/record mode.
+ * @author Marko
+ */
+public class RecordFile extends LogoFile implements MessageBroadcaster
+{
+ private static final long serialVersionUID = -9137220313285199168L;
+
+ private Timer timer; // the SWING Timer dispatchers on the EventDispatcher Thread => update GUI ok
+ private Date started;
+ private Date last;
+ private long totalMillis;
+
+ /**
+ * @param fileName
+ */
+ protected RecordFile(String fileName)
+ {
+ super(fileName);
+ }
+
+ public static RecordFile createNewFile(String fileName) throws IOException
+ {
+ RecordFile file = new RecordFile(fileName);
+ file.setupFileSystem();
+ return file;
+ }
+
+
+ /**
+ * @throws NotImplementedException A virtual contest/record mode makes no sense.
+ */
+ public static RecordFile createNewVirtualFile(UserConfig userConfig, String fileName)
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ protected void setupFileSystem() throws IOException
+ {
+ File contestFileDir = getUserConfig().getContestFileDir(getPlainName());
+
+ if (!contestFileDir.exists())
+ contestFileDir.mkdirs();
+ }
+
+ @Override
+ public File getFilePath()
+ {
+ return getUserConfig().getContestFilePath(getPlainName());
+ }
+
+ @Override
+ public void store()
+ {
+ long now = Calendar.getInstance().getTime().getTime();
+ recordFile(getTimeStampHeader(totalMillis, started.getTime(), now));
+ //pauseRecord(); // This is already called by Context at open/close.
+ // We actually never store normally, and we don't export these files.
+ }
+
+ /**
+ * Set the timer
+ */
+ public void startRecord()
+ {
+ this.started = Calendar.getInstance().getTime();
+ this.last = Calendar.getInstance().getTime();
+
+ timer = new Timer(1000,
+ new ActionListener()
+ {
+ public void actionPerformed(ActionEvent arg0)
+ {
+ Date now = Calendar.getInstance().getTime();
+ totalMillis += now.getTime() - last.getTime();
+ last = now;
+
+ String time = UserConfig.getMinSec(totalMillis);
+ String fileName = getPlainName();
+
+ for(MessageListener listener : timerEventListeners)
+ listener.messageEvent(fileName, time);
+ }
+ }
+ );
+ timer.setRepeats(true);
+ timer.start();
+ }
+
+ /**
+ * Stop the timer and record recent changes with time stamp in contest directory.
+ * (Make sure the recent changes from the editor are before calling this)
+ */
+ public void pauseRecord()
+ {
+ timer.stop();
+ }
+
+
+ private void recordFile(final String header)
+ {
+ new Thread(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ // Write to file's folder
+ File recordFile = getUserConfig().getRecordFilePath(getPlainName());
+ File recordFolder = recordFile.getParentFile();
+ if (!recordFolder.exists())
+ recordFolder.mkdirs();
+
+ String content = header + getText();
+
+ try
+ {
+ Utils.writeLogoFile(recordFile.toString(), content);
+ }
+ catch (IOException e)
+ {
+ DialogMessenger.getInstance().dispatchMessage(
+ Logo.messages.getString("contest.error.title"),
+ Logo.messages.getString("contest.error.could.not.record.file") + "\n\n " + e.toString());
+ }
+
+ // append to command line too ...
+ PrintWriter out = null;
+ File logoFile = WSManager.getUserConfig().getCommandLineContestFile();
+ try
+ {
+ out = new PrintWriter(new BufferedWriter(new FileWriter(logoFile, true)));
+ out.println("");
+ out.println(getPlainName());
+ out.println(content);
+ out.println("\n");
+ }
+ catch (Exception e)
+ {
+ DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("contest.error.title"),
+ Logo.messages.getString("contest.could.not.store") + "\n" + e.toString());
+ }
+ finally
+ {
+ if (out != null)
+ out.close();
+ }
+ }
+ }).run();
+
+ }
+
+
+ private String getTimeStampHeader(long totalTime, long lastEditStarted, long lastEditEnded)
+ {
+ String tot = UserConfig.getMinSec(totalTime);
+ String lastStart = UserConfig.getTimeString(lastEditStarted);
+ String now = UserConfig.getTimeString(lastEditEnded);
+
+ return "# Total Time : " + tot + "\n# Edited from : " + lastStart + "\n# Until : " + now + "\n\n";
+ }
+
+ /*
+ * Timer Listeners
+ */
+
+ private final ArrayList<MessageListener> timerEventListeners = new ArrayList<MessageListener>();
+
+ @Override
+ public void addBroadcastListener(MessageListener listener)
+ {
+ if(listener == null)
+ throw new IllegalArgumentException("Listener must not be null.");
+ timerEventListeners.add(listener);
+ listener.messageEvent(getPlainName(), UserConfig.getMinSec(totalMillis));
+ }
+
+ @Override
+ public void removeBroadcastListener(MessageListener listener)
+ {
+ timerEventListeners.remove(listener);
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/procedures/ExecutablesContainer.java b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesContainer.java
new file mode 100644
index 0000000..73c7295
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesContainer.java
@@ -0,0 +1,59 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import java.io.IOException;
+import java.util.Collection;
+
+public interface ExecutablesContainer
+{
+
+ public Collection<Procedure> getExecutables();
+
+ public Procedure getExecutable(String procedureName);
+
+ public boolean isExecutable(String name);
+
+ /**
+ * The Logo command {@code eraseProcedure} <p>
+ * Delete the specified procedure. <br>
+ * If there exists an ambiguity and multiple procedures with the same name
+ * are defined, remove all of them.
+ *
+ * @param name
+ */
+ public void eraseProcedure(String procedureName);
+
+ /**
+ * The Logo command {@code define}
+ * @param procedure
+ * @throws IOException
+ */
+ public void defineProcedure(Procedure procedure) throws IOException;
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/procedures/ExecutablesProvider.java b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesProvider.java
new file mode 100644
index 0000000..6ae20ea
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesProvider.java
@@ -0,0 +1,36 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import xlogo.interfaces.ProcedureMapper;
+import xlogo.interfaces.ErrorDetector.AmbiguityDetector;
+
+public interface ExecutablesProvider extends ExecutablesContainer, ProcedureMapper, AmbiguityDetector
+{
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/procedures/Procedure.java b/logo/src/xlogo/kernel/userspace/procedures/Procedure.java
new file mode 100644
index 0000000..c485432
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/procedures/Procedure.java
@@ -0,0 +1,763 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written Lo�c Le Coq.
+ * The were heavily refactored, changed and extended by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.userspace.procedures;
+
+import java.util.Calendar;
+import java.util.Stack;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+import java.io.*;
+
+import xlogo.Logo;
+import xlogo.kernel.Primitive;
+import xlogo.utils.Utils;
+
+/**
+ * The Procedure in XLogo4Schools receives pieces of text, where a single procedure is expected to be defined.
+ * It parses the text and sets its fields accordingly.
+ * It maintains states, so we know whether a procedure is executable or whether it has errors and what type of error.
+ * With the new implementation, multiple types of errors can be detected at the same time.
+ *
+ * <p>
+ * Affichable (displayable) is removed. In XLogo4Schools, every procedure is displayable.
+ * In XLogo, affichable was added to mark procedures that have been received over the network.
+ * They should only be running while the TCPConnection is open.
+ * They won't ever get displayed in the editor and they are removed, after the TCP connection is closed.
+ * In the new implementation, I can treat the received workspace as a virtual file,
+ * add its procedures to the UserSpace, without adding the file to the file list.
+ * Hence it cannot be opened while the procedures can still be executed.
+ *
+ * @author Marko Zivkovic, Loic
+ */
+public class Procedure
+{
+ public static final String specialCharacters = ":+-*/() []=<>&|";
+
+ /* Marko : TODO the following properties need to be encapsulated completely (Legacy from XLogo)
+ * I removed those properties that were named _sauve etc. which was used to temporarily redefine procedures,
+ * and then restore them in case there was an error while parsing.
+ */
+
+ // false lorsque c'est une procédure d'un fichier de démarrage
+ /**
+ * Whitespace and comments above a procedure in the editor
+ */
+ public String comment;
+ public int nbparametre;
+ public String name;
+ public ArrayList<String> variable = new ArrayList<String>();
+ public Stack<String> optVariables = new Stack<String>(); // Marko : why Stack??? [so bad]
+ public Stack<StringBuffer> optVariablesExp = new Stack<StringBuffer>(); // Marko : why Stack?? [so bad]
+ public String instruction = "";
+ public StringBuffer instr = null;
+
+ // Marko : I added these
+ private String text = null;
+ private String ownerName = null;
+ private Calendar defineTime = null;
+
+ /**
+ * Create a procedure from a piece of text from the editor.
+ * The expected structure is as follows.
+ * <p>
+ * [leading <b>empty lines</b> and <b>comments</b> => stored into comment] <br>
+ * <b>to procedureName</b> [<b>variables</b>] [<b>optional variables</b>] <br>
+ * [<b>body</b>] <br>
+ * <b>end</b><br>
+ *
+ * @param text
+ * @throws IOException
+ */
+ public Procedure(String text)
+ {
+ try
+ {
+ defineTime = Calendar.getInstance();
+ this.text = text;
+ StringReader sr = new StringReader(text);
+ BufferedReader br = new BufferedReader(sr);
+ String line = parseComments(br);
+ if (state == State.COMMENT_ONLY)
+ return;
+ StringTokenizer st = new StringTokenizer(line);
+ parseName(st);
+ if (errors.contains(ProcedureErrorType.MISSING_TO))
+ return;
+ parseVariables(st);
+ parseBody(br);
+
+ if (state == State.UNINITIALIZED)
+ setState(State.EXECUTABLE);
+ }
+ catch(IOException ignore) {
+ /* this should not happen, no actual IO */
+ }
+ }
+
+ /**
+ * Create a procedure with all necessary values.<br>
+ * If name is not legal, the Procedure state will switch to error,
+ * otherwise it will be executable
+ */
+ public void redefine(Procedure newDefinition)
+ {
+ defineTime = Calendar.getInstance();
+ this.name = newDefinition.name;
+ this.nbparametre = newDefinition.nbparametre;
+ this.variable = newDefinition.variable;
+ this.optVariables = newDefinition.optVariables;
+ this.optVariablesExp = newDefinition.optVariablesExp;
+ this.text = newDefinition.text;
+ this.errors = newDefinition.errors;
+
+ ProcedureErrorType e = checkName(name);
+ if (e != ProcedureErrorType.NO_ERROR)
+ addError(e);
+ else
+ setState(State.EXECUTABLE);
+ }
+
+ public Calendar getDefineTime()
+ {
+ return defineTime;
+ }
+
+ public String getText()
+ {
+ return text;
+ }
+
+ /*
+ * PROCEDURE STATE
+ */
+
+ private State state = State.UNINITIALIZED;
+
+ private ArrayList<ProcedureErrorType> errors = new ArrayList<ProcedureErrorType>();
+
+ /**
+ * @see {@link State}
+ * @return
+ */
+ public State getState()
+ {
+ return state;
+ }
+
+ private void setState(State state)
+ {
+ this.state = state;
+ }
+
+ /**
+ * Use this to indicate that this procedure is not executable because there is an ambiguity within its file.
+ */
+ public void makeAmbiguous()
+ {
+ this.state = State.AMBIGUOUS_NAME;
+ if (!errors.contains(ProcedureErrorType.AMBIGUOUS))
+ errors.add(ProcedureErrorType.AMBIGUOUS);
+ }
+
+ /**
+ * States are: UNINITIALIZED, EXECUTABLE, COMMENT_ONLY, ERROR
+ * @author Marko
+ */
+ public enum State
+ {
+ /**
+ * No values are set, or no check has been performed.
+ */
+ UNINITIALIZED("procedure.unititialized"),
+ /**
+ * The procedure has a correct structure and can be executed.
+ */
+ EXECUTABLE("procedure.executable"),
+ /**
+ * The procedure contains only a comment, no procedure definition. It is not executable
+ */
+ COMMENT_ONLY("procedure.not.executable"),
+ /**
+ * The procedure structure could no be parsed entirely because it contains errors.
+ */
+ ERROR("procedure.error"),
+ /**
+ * There is another procedures with the same name in the same file.
+ */
+ AMBIGUOUS_NAME("procedure.ambiguous");
+
+ private String description;
+
+ private State(String description)
+ {
+ this.description = description;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @return {@link #getDescription()}
+ */
+ public String toString()
+ {
+ return getDescription();
+ }
+ }
+
+ /*
+ * ERROR REPORTING
+ */
+
+ public ArrayList<ProcedureErrorType> getErrors()
+ {
+ return errors;
+ }
+
+ private void addError(ProcedureErrorType e)
+ {
+ errors.add(e);
+ state = State.ERROR;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * OLD XLOGO FEATURES ... TODO behavior not always clear
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /**
+ * @author Lo�c Le Coq
+ */
+ public void decoupe()
+ {
+ // Only cut procedures which are visible in the editor
+ if (null == instr)
+ {
+ instr = new StringBuffer();
+ try
+ {
+ String line = "";
+ StringReader sr = new StringReader(instruction);
+ BufferedReader bfr = new BufferedReader(sr);
+ int lineNumber = 0;
+ // Append the number of the line
+ instr.append("\\l");
+ instr.append(lineNumber);
+ instr.append(" ");
+ while (bfr.ready())
+ {
+ lineNumber++;
+ // read the line
+ try
+ {
+ line = bfr.readLine().trim();
+ }
+ catch (NullPointerException e1)
+ {
+ break;
+ }
+ // delete comments
+ line = deleteComments(line);
+ line = Utils.decoupe(line).toString().trim();
+ instr.append(line);
+ if (!line.equals(""))
+ {
+ instr.append(" ");
+ // Append the number of the line
+ instr.append("\\l");
+ instr.append(lineNumber);
+ instr.append(" ");
+ }
+ }
+ }
+ catch (IOException e)
+ {}
+ // System.out.println("****************************"+name+"\n"+instr+"\n\n");
+ }
+ }
+
+ /**
+ * @author Lo�c Le Coq
+ */
+ private String deleteComments(String line)
+ {
+ int index = line.indexOf("#");
+ while (index != -1)
+ {
+ if (index == 0)
+ {
+ return "";
+ }
+ else if (line.charAt(index - 1) != '\\')
+ {
+ return line.substring(0, index);
+ }
+ else
+ index = line.indexOf("#", index + 1);
+ }
+ return line;
+ }
+
+ /**
+ * @author Lo�c Le Coq
+ */
+ public String toString()
+ {
+ // return("nom "+name+" nombre paramètres "+nbparametre+" identifiant "+id+"\n"+variable.toString()+"\n"+instr+"\ninstrction_sauve"+instruction_sauve+"\ninstr_sauve\n"+instr_sauve);
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(comment);
+ sb.append(Logo.messages.getString("pour") + " " + name);
+ for (int j = 0; j < nbparametre; j++)
+ {
+ sb.append(" :");
+ sb.append(variable.get(j));
+ }
+ for (int j = 0; j < optVariables.size(); j++)
+ {
+ sb.append(" [ :");
+ sb.append(optVariables.get(j));
+ sb.append(" ");
+ sb.append(optVariablesExp.get(j));
+ sb.append(" ]");
+ }
+ sb.append("\n");
+ sb.append(instruction);
+ sb.append(Logo.messages.getString("fin"));
+ sb.append("\n");
+ // System.out.println("a"+sb+"a");
+ return new String(sb);
+ }
+
+ /**
+ * @author Lo�c Le Coq
+ */
+ public StringBuffer cutInList()
+ {
+ // Only cut procedures which are visible in the editor
+ StringBuffer sb = new StringBuffer();
+ try
+ {
+ String line = "";
+ StringReader sr = new StringReader(instruction);
+ BufferedReader bfr = new BufferedReader(sr);
+ while (bfr.ready())
+ {
+ try
+ {
+ line = bfr.readLine().trim();
+ }
+ catch (NullPointerException e1)
+ {
+ break;
+ }
+ // delete comments
+ // line=deleteComments(line);
+ line = Utils.decoupe(line).toString().trim();
+ sb.append("[ ");
+ sb.append(line);
+ sb.append(" ] ");
+ }
+ }
+ catch (IOException e)
+ {}
+ return sb;
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PARSING
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /**
+ * Read and save the comments that appear before the procedure
+ * @param br
+ * @return the first line that (probably) contains a procedure definition,
+ * or null if there are no more procedures
+ * @throws IOException
+ * @throws DocumentStructureException
+ */
+ String parseComments(BufferedReader br) throws IOException
+ {
+ String line = null;
+ comment = "";
+ while (br.ready())
+ {
+ line = br.readLine();
+ if (line == null)
+ break;
+
+ if (isComment(line))
+ {
+ comment += line + "\n";
+ line = null;
+ }
+ else if (line.trim().equals(""))
+ {
+ comment += "\n";
+ line = null;
+ }
+ else
+ break;
+ }
+
+ if (line == null)
+ {
+ setState(State.COMMENT_ONLY);
+ }
+
+ return line;
+ }
+
+ /**
+ * Expects a line that starts with "to procedName"
+ * @return Error, NO_ERROR if name is ok
+ * @throws DocumentStructureException
+ */
+ void parseName(StringTokenizer st)
+ {
+ String token = st.nextToken();
+
+ if (!token.toLowerCase().equals(Logo.messages.getString("pour").toLowerCase()))
+ {
+ addError(ProcedureErrorType.MISSING_TO);
+ return;
+ }
+
+ if (!st.hasMoreTokens())
+ {
+ addError(ProcedureErrorType.MISSING_NAME);
+ return;
+ }
+
+ name = st.nextToken().toLowerCase();
+
+ ProcedureErrorType e = checkName(name);
+ if (!e.equals(ProcedureErrorType.NO_ERROR))
+ addError(e);
+ }
+
+ void parseVariables(StringTokenizer st)
+ {
+ // Then, We read the variables
+ // :a :b :c :d .....
+ String token = null;
+ while (st.hasMoreTokens())
+ {
+ token = st.nextToken();
+ if (token.startsWith(":"))
+ {
+ String var = token.substring(1);
+ ProcedureErrorType e = checkValidVariable(var);
+ if (e != ProcedureErrorType.NO_ERROR)
+ {
+ addError(e);
+ return;
+ }
+ var = var.toLowerCase();
+ variable.add(var);
+ }
+ else
+ break;
+ }
+
+ nbparametre = variable.size();
+
+ if (token == null || token.startsWith(":"))
+ return;
+
+ // read all remaining tokens into string buffer
+ StringBuffer sb = new StringBuffer();
+ sb.append(token);
+ //sb.append(token);
+ while (st.hasMoreTokens())
+ {
+ sb.append(" ");
+ sb.append(st.nextToken());
+ }
+ // And finally, optional variables if there are some.
+ // [:a 100] [:b 20] [:c 234] ...........
+
+ while (sb.length() > 0)
+ {
+ if (sb.indexOf("[") != 0)
+ {
+ addError(ProcedureErrorType.VAR_EXTRA_CHARS);
+ return;
+ }
+
+ sb.deleteCharAt(0);
+ String[] arg = new String[2];
+ extractOptionalVariable(sb, arg);
+ optVariables.push(arg[0].toLowerCase());
+ /* Bug Fixed: list as Optionnal arguments
+ ** Eg:
+ ** to a [:var [a b c]]
+ * end
+ * when the string is formatted, we check that a white space
+ * is needed at the end of the argument
+ */
+
+ StringBuffer exp = Utils.decoupe(arg[1]);
+ if (exp.charAt(exp.length() - 1) != ' ')
+ exp.append(" ");
+ optVariablesExp.push(exp);
+ }
+ }
+
+ /**
+ * Reads from sb into args the name and the default value of an optional variable [:a value]. <br>
+ * value can be expression: number, word, list, ...
+ * @author Lo�c Le Coq
+ * @author Marko Zivkovic refactored
+ */
+ void extractOptionalVariable(StringBuffer sb, String[] args)
+ {
+
+ String variable="";
+ String expression="";
+ int compteur=1;
+ int id=0;
+ int id2=0;
+ boolean espace = false;
+ for (int i = 0; i < sb.length(); i++)
+ {
+ char ch = sb.charAt(i);
+ if (ch == '[')
+ compteur++;
+ else if (ch == ']')
+ {
+ if (id == 0)
+ {
+ addError(ProcedureErrorType.OPT_VAR_BRACKET);
+ return;
+ }
+ compteur--;
+ }
+ else if (ch == ' ')
+ {
+ if (!variable.equals(""))
+ {
+ if (!espace)
+ id = i;
+ espace = true;
+ }
+ }
+ else
+ {
+ if (!espace)
+ variable += ch;
+ }
+ if (compteur == 0)
+ {
+ id2 = i;
+ break;
+ }
+ }
+ if (!variable.startsWith(":"))
+ {
+ addError(ProcedureErrorType.VAR_COLON_EXPECTED);
+ return;
+ }
+
+ variable = variable.substring(1, variable.length()).toLowerCase();
+ ProcedureErrorType pet = checkValidVariable(variable);
+
+ if (pet != ProcedureErrorType.NO_ERROR)
+ {
+ addError(pet);
+ return;
+ }
+
+
+ if (compteur != 0)
+ {
+ addError(ProcedureErrorType.OPT_VAR_BRACKET);
+ return;
+ }
+
+ expression = sb.substring(id + 1, id2).trim();
+
+ if (expression.equals(""))
+ {
+ addError(ProcedureErrorType.VAR_MISSING_EXPRESSION);
+ return;
+ }
+ /* System.out.println(id+" "+id2);
+ System.out.println("a"+expression+"a");
+ System.out.println("a"+variable+"a");*/
+ sb.delete(0, id2 + 1);
+ // delete unnecessary space
+ while (sb.length() != 0 && sb.charAt(0) == ' ')
+ sb.deleteCharAt(0);
+ args[0] = variable;
+ args[1] = expression;
+ }
+
+ /**
+ * Sets as body everything up the 'end' key word.
+ */
+ private void parseBody(BufferedReader br) throws IOException
+ {
+ StringBuffer body = new StringBuffer();
+ String to = Logo.messages.getString("pour").toLowerCase() + " ";
+ String end = Logo.messages.getString("fin").toLowerCase();
+ instruction = null;
+ String line;
+ String lower;
+
+ while (br.ready())
+ {
+ line = br.readLine();
+ if (null == line)
+ break;
+ lower = line.toLowerCase();
+ if (lower.trim().equals(end)) // end of procedure
+ {
+ instruction = body.toString();
+ if (br.ready() && br.readLine() != null) // Additional characters after end
+ addError(ProcedureErrorType.CHARS_AFTER_END);
+ return; // We are done
+ }
+ if (lower.startsWith(to)) // new procedure definition before end
+ {
+ addError(ProcedureErrorType.TO_BEFORE_END);
+ return;
+ }
+ body.append(line);
+ body.append("\n");
+ }
+
+ // no end was found
+ addError(ProcedureErrorType.MISSING_END);
+
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * UTILITY
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /**
+ * @return true if and only if line starts with '#'
+ */
+ static boolean isComment(String line)
+ {
+ if (line.trim().startsWith("#"))
+ return true;
+ else
+ return false;
+ }
+
+ static boolean isNumber(String token)
+ {
+ try
+ {
+ Double.parseDouble(token);
+ return true;
+ }
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * This method is essentially changed to fit with the new workspace (=UserSpace & LogoFile).<br>
+ * On the fly it was rewritten completely, because of its initial programming style and inefficiency. <br>
+ * (note that it was very long, and now it is only 3 lines)
+ *
+ * @param token
+ * @return Error - NO_ERROR if token is a legal procedure name
+ * @author Marko Zivkovic
+ */
+ static ProcedureErrorType checkName(String token)
+ {
+ if (isNumber(token))
+ return ProcedureErrorType.NAME_IS_NUMBER;
+ ProcedureErrorType e = checkSpecialCharacter(token);
+ if (e != ProcedureErrorType.NO_ERROR)
+ return e;
+
+ if(Primitive.primitives.containsKey(token))
+ return ProcedureErrorType.NAME_IS_KEY_WORD;
+
+ return ProcedureErrorType.NO_ERROR;
+ }
+
+ /**
+ * @param token
+ * @return null if a valid variable name, error message otherwise
+ */
+ static ProcedureErrorType checkValidVariable(String token)
+ {
+ if (token.length() == 0)
+ return ProcedureErrorType.VAR_WHITESPACE_AFTER_COLON;
+
+ if (isNumber(token))
+ return ProcedureErrorType.VAR_IS_NUMBER;
+
+ return checkSpecialCharacter(token);
+ }
+
+ static ProcedureErrorType checkSpecialCharacter(String var)
+ {
+ StringTokenizer check = new StringTokenizer(var, specialCharacters, true);
+
+ if ((check.countTokens() > 1) || ":+-*/() []=<>&|".indexOf(check.nextToken()) > -1)
+ return ProcedureErrorType.VAR_HAS_SPECIAL;
+ return ProcedureErrorType.NO_ERROR;
+ }
+
+ /*
+ * OWNER : This is typically a file, but a procedure must not know whether it is owned by a file or something else
+ */
+
+ public String getOwnerName()
+ {
+ return ownerName;
+ }
+
+ public void setOwnerName(String ownerName)
+ {
+ this.ownerName = ownerName;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/procedures/ProcedureErrorType.java b/logo/src/xlogo/kernel/userspace/procedures/ProcedureErrorType.java
new file mode 100644
index 0000000..3029320
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/procedures/ProcedureErrorType.java
@@ -0,0 +1,80 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import xlogo.messages.MessageKeys;
+
+public enum ProcedureErrorType
+{
+ NO_ERROR(MessageKeys.LOGO_ERROR_NO_ERROR),
+ MISSING_TO(MessageKeys.LOGO_ERROR_MISSING_TO),
+ MISSING_NAME(MessageKeys.LOGO_ERROR_MISSING_NAME),
+ NAME_IS_NUMBER(MessageKeys.LOGO_ERROR_NAME_IS_NUMBER), //Logo.messages.getString("erreur_nom_nombre_procedure")
+ /*
+ * Logo.messages.getString("caractere_special1") + "\n" + Logo.messages.getString("caractere_special2")
+ * + "\n" + Logo.messages.getString("caractere_special3") + " " + token
+ */
+ NAME_HAS_SPECIAL_CHAR(MessageKeys.LOGO_ERROR_NAME_SPECIAL),
+ VAR_WHITESPACE_AFTER_COLON(MessageKeys.LOGO_ERROR_VAR_WHITE_AFTER_COLON),
+ /*
+ * Logo.messages.getString("caractere_special_variable") + "\n"
+ + Logo.messages.getString("caractere_special2") + "\n"
+ + Logo.messages.getString("caractere_special3") + " :" + var
+ */
+ VAR_HAS_SPECIAL(MessageKeys.LOGO_ERROR_VAR_SPECIAL),
+ VAR_IS_NUMBER(MessageKeys.LOGO_ERROR_VAR_IS_NUMBER),
+ VAR_COLON_EXPECTED(MessageKeys.LOGO_ERROR_VAR_MISSING_COLON),
+ OPT_VAR_BRACKET(MessageKeys.LOGO_ERROR_OPT_VAR_BRACKET),
+ VAR_EXTRA_CHARS(MessageKeys.LOGO_ERROR_VAR_EXTRA_CHARS),
+ VAR_MISSING_EXPRESSION(MessageKeys.LOGO_ERROR_MISSING_EXPR),
+ CHARS_AFTER_END(MessageKeys.LOGO_ERROR_MORE_CHARS_AFTER_END),
+ MISSING_END(MessageKeys.LOGO_ERROR_MISSING_END),
+ TO_BEFORE_END(MessageKeys.LOGO_ERROR_TO_BEFORE_END),
+ NAME_IS_KEY_WORD(MessageKeys.LOGO_ERROR_NAME_IS_KEY_WORD),
+ AMBIGUOUS(MessageKeys.LOGO_ERROR_AMBIGUOUS);
+
+ private String description;
+
+ private ProcedureErrorType(String description)
+ {
+ this.description = description;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @return {@link #getDescription()}
+ */
+ public String toString()
+ {
+ return getDescription();
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/kernel/userspace/procedures/ProceduresManager.java b/logo/src/xlogo/kernel/userspace/procedures/ProceduresManager.java
new file mode 100644
index 0000000..bcaea16
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/procedures/ProceduresManager.java
@@ -0,0 +1,557 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import xlogo.kernel.userspace.ProcedureErrorMessage;
+import xlogo.kernel.userspace.context.ContextSwitcher;
+import xlogo.kernel.userspace.context.LogoContext;
+import xlogo.kernel.userspace.context.ContextSwitcher.ContextSwitchListener;
+import xlogo.kernel.userspace.files.LogoFile;
+import xlogo.kernel.userspace.procedures.Procedure.State;
+
+/**
+ * This class maintains the procedure table of all files in a context, and it reports ambiguity among them.
+ * @author Marko Zivkovic
+ */
+public class ProceduresManager implements ExecutablesProvider
+{
+ private LogoContext context;
+
+ private final ArrayList<AmbiguityListener> ambiguityListeners = new ArrayList<AmbiguityListener>();
+
+ private ProcedureMapListener procedureMapListener;
+
+ public ProceduresManager(ContextSwitcher contextProvider)
+ {
+ initProcedureMapListener();
+ installContextSwitchListener(contextProvider);
+ setContext(contextProvider.getContext());
+ }
+
+ private void setContext(LogoContext newContext)
+ {
+ LogoContext old = this.context;
+ this.context = newContext;
+
+ if (old != null)
+ old.removeProcedureMapListener(procedureMapListener);
+
+ mapProcedures(newContext);
+
+ /* Record Mode => new files => events
+ // Network Mode => only virtual files, invisible => no events
+ // that was achieved with the boolean "affichable" (displayable) in XLogo
+ if (!newContext.fireProcedureEvents())
+ {
+
+ return;
+ }
+ */
+ if (old != null && old.fireProcedureEvents() && newContext.fireProcedureEvents()) // TODO changed
+ for(LogoFile file : old.getFilesTable().values())
+ notifyDeleted(file.getPlainName(), file.getAllProcedureNames());
+
+ // Note : context will immediately fire event to notify what his contents are.
+ // Then these events are forwarded here, in procedureMapListener.
+ newContext.addProcedureMapListener(procedureMapListener);
+ }
+
+ private void mapProcedures(LogoContext context)
+ {
+ context.getProcedureTable().clear();
+ for (LogoFile file : context.getFilesTable().values())
+ for (Procedure procedure : file.getExecutables())
+ addProcedure(procedure);
+ }
+
+ private void installContextSwitchListener(ContextSwitcher provider)
+ {
+ provider.addContextSwitchListener(new ContextSwitchListener(){
+
+ @Override
+ public void contextSwitched(LogoContext newContext)
+ {
+ setContext(newContext);
+ }
+ });
+ }
+
+ private void initProcedureMapListener()
+ {
+ // This one listens for changes in files.
+ procedureMapListener = new ProcedureMapListener()
+ {
+ /**
+ * This event is received when a document is assigned a new text and it contains no errors,
+ * or when this ProcedureManager is assigned a new context that already contains files.
+ */
+ @Override
+ public void defined(String fileName, Collection<String> procedures)
+ {
+ for (String procedureName : procedures)
+ addProcedure(fileName, procedureName);
+
+ if (procedures.size() > 0)
+ notifyDefined(fileName, procedures);
+
+ for (String procedureName : procedures)
+ {
+ Map<String, Procedure> fileNameToProcedure = context.getProcedureTable().get(procedureName);
+ if (fileNameToProcedure.size() > 1)
+ {
+ notifyAmbiguityDetected(procedureName, context.getProcedureTable().get(procedureName));
+ }
+ }
+
+ }
+
+ /**
+ * This event is received when the Logo command "define" (re-)defined a command in a file (which already exists!)
+ */
+ @Override
+ public void defined(String fileName, String procedureName)
+ {
+ addProcedure(fileName, procedureName);
+
+ notifyDefined(fileName, procedureName);
+
+ // Check ambiguity
+ Map<String, Procedure> fileNameToProcedure = context.getProcedureTable().get(procedureName);
+ if (fileNameToProcedure.size() > 1)
+ {
+ notifyAmbiguityDetected(procedureName, fileNameToProcedure);
+ return;
+ }
+ }
+
+ /**
+ * This event is received when a file is deleted or when it is assigned a new text in which old procedures are missing.
+ */
+ @Override
+ public void undefined(String fileName, Collection<String> procedures)
+ {
+ for (String procedure : procedures)
+ undefined(fileName, procedure);
+ }
+
+ /**
+ * This event is received when the Logo command "eraseProcedure" removes a single procedure from a document
+ * Depending on how many procedures with the same name are left across files after this procedure is removed,
+ * this will fire ExecutableChanged events or AmbiguityResolved events.
+ */
+ @Override
+ public void undefined(String fileName, String procedureName)
+ {
+ Map<String,HashMap<String, Procedure>> procedureTable = context.getProcedureTable();
+
+ Map<String, Procedure> fileToProc = procedureTable.get(procedureName);
+
+ if (fileToProc == null)
+ return;
+
+ // remove from fileToProc entry
+ fileToProc.remove(fileName);
+
+ notifyDeleted(fileName, procedureName);
+
+ if (fileToProc.size() == 0)
+ procedureTable.remove(procedureName);
+ else
+ notifyAmbiguityResolved(procedureName, fileName);
+
+ if (fileToProc.size() == 1)
+ {
+ String winnerFile = null;
+ for (String wf : fileToProc.keySet())
+ winnerFile = wf; // size == 1 => only 1 iteration
+
+ notifyAmbiguityResolved(procedureName, winnerFile);
+ }
+ }
+
+ @Override
+ public void ownerRenamed(String oldName, String newName)
+ {
+ // Note : very important that this event is catched after filemanager has renamed files
+ // very critical. but implementation guarantees this order.
+ LogoFile file = context.getFilesTable().get(newName);
+ // Rename keys in procedureTable
+ Map<String, Procedure> fileToProc;
+ for (Procedure proc : file.getExecutables())
+ {
+ fileToProc = context.getProcedureTable().get(proc.getName());
+ fileToProc.remove(oldName);
+ fileToProc.put(newName, proc);
+ }
+ }
+ };
+ }
+
+ /**
+ * @see #addProcedure(Procedure)
+ */
+ protected void addProcedure(String fileName, String procedureName) throws IllegalArgumentException
+ {
+ Procedure procedure = context.getFilesTable().get(fileName).getExecutable(procedureName.toLowerCase());
+ addProcedure(procedure);
+ }
+
+ /**
+ * Adds the procedure to the procedureTable without firing events.
+ * Use {@link #defineProcedure(Procedure)}} if you want to notify listeners
+ * @param procedure
+ * @throws IllegalArgumentException
+ */
+ protected void addProcedure(Procedure procedure) throws IllegalArgumentException
+ {
+ String procedureName = procedure.getName();
+
+ if (procedure.getState() != State.EXECUTABLE && procedure.getState() != State.AMBIGUOUS_NAME)
+ throw new IllegalArgumentException("Only executable procedures should be added to the context.");
+
+ HashMap<String, HashMap<String, Procedure>> procedureTable = context.getProcedureTable();
+ HashMap<String, Procedure> fileToProc = procedureTable.get(procedureName);
+
+ if (fileToProc == null) // The procedure name is not yet defined in the context.
+ {
+ // Must create fileToProc entry in procedureTable
+ fileToProc = new HashMap<String, Procedure>();
+ procedureTable.put(procedureName, fileToProc);
+ }
+
+ fileToProc.put(procedure.getOwnerName(), procedure);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * EXECUTABLES CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * @param {@link Procedure#getOwnerName()} is supposed to be already mapped in the filesTable, and the procedure should be executable
+ * @throws IllegalArgumentException if the procedure's owner name is not mapped;
+ * @see {@link LogoFile#defineProcedure(Procedure)}
+ */
+ @Override
+ public void defineProcedure(Procedure procedure)
+ {
+ LogoFile file = context.getFilesTable().get(procedure.getOwnerName());
+ if (file == null)
+ throw new IllegalArgumentException("The file name \""
+ + procedure.getOwnerName()
+ + "\" specified in procedure is not mapped to a file.");
+
+ //addProcedure(procedure);
+ file.defineProcedure(procedure); // this will cause the necessary event cascade
+ }
+
+ /**
+ * Erase
+ */
+ @Override
+ public void eraseProcedure(String procedureName)
+ {
+ String lower = procedureName.toLowerCase();
+ while(context.getProcedureTable().containsKey(lower))
+ {
+ // contains key => there must be one
+ Procedure nextVictim = null;
+ for (Procedure proc : context.getProcedureTable().get(lower).values())
+ {
+ // iterate only once : otherwise concurrent modification exception
+ nextVictim = proc;
+ break;
+ }
+ context.getFilesTable().get(nextVictim.getOwnerName()).eraseProcedure(nextVictim.getName());
+ }
+ }
+
+ /**
+ * Executable procedures of all files. Ambiguous procedures are not included. <br>
+ * This list should not be used to do computations on it, because it must be created every time.
+ * Its purpose is to feed GUI lists
+ */
+ @Override
+ public ArrayList<Procedure> getExecutables()
+ {
+ ArrayList<Procedure> executables = new ArrayList<Procedure>();
+ for(Map<String, Procedure> fileToProc : context.getProcedureTable().values())
+ if (fileToProc.size() == 1) // only one exists
+ for(Procedure proc : fileToProc.values())
+ executables.add(proc); // only one is added
+ return executables;
+ }
+
+ /**
+ * @return The specified procedure if and only if there is exactly one
+ * procedure definition for this name in the context, otherwise null.
+ */
+ @Override
+ public Procedure getExecutable(String procedureName)
+ {
+ Map<String, Procedure> fileToProc = context.getProcedureTable().get(procedureName.toLowerCase());
+
+ if (fileToProc == null || fileToProc.size() > 1)
+ return null;
+ // There is exactly 1 procedure
+ for (Procedure proc : fileToProc.values())
+ return proc;
+ // Code won't reach here
+ return null;
+ }
+
+ /**
+ * @return true if and only if there exists exactly one procedure definition for the specified name in this context
+ */
+ @Override
+ public boolean isExecutable(String procedureName)
+ {
+ Map<String, Procedure> fileToProc = context.getProcedureTable().get(procedureName.toLowerCase());
+ return (fileToProc != null && fileToProc.size() == 1);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PROCEDURE MAPPER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * Get all executable procedure names
+ * @see #getExecutables()
+ */
+ @Override
+ public Collection<String> getAllProcedureNames()
+ {
+ ArrayList<String> executables = new ArrayList<String>();
+ for(Map<String, Procedure> fileToProc : context.getProcedureTable().values())
+ if (fileToProc.size() == 1) // only one exists
+ for(Procedure proc : fileToProc.values())
+ executables.add(proc.getName()); // only one is added
+ return executables;
+ }
+
+ /**
+ * Get all procedures of that file, if its procedures are not conflicting with other files' procedures.
+ */
+ @Override
+ public Collection<String> getAllProcedureNames(String fileName)
+ {
+ if (!hasAmbiguousProcedures(fileName))
+ return context.getFilesTable().get(fileName).getAllProcedureNames();
+ return null;
+ }
+
+ /**
+ * @return null if procedure does not exist or if it is ambiguous
+ */
+ @Override
+ public String getProcedureOwner(String procedureName)
+ {
+ Procedure procedure = getExecutable(procedureName.toLowerCase());
+ return procedure == null ? null : procedure.getOwnerName();
+ }
+
+ // Procedure Map Listeners : update gui => run on event dispatcher thread
+
+ private final ArrayList<ProcedureMapListener> procedureMapListeners = new ArrayList<ProcedureMapListener>();
+
+ @Override
+ public void addProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.add(listener);
+ }
+
+ @Override
+ public void removeProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.remove(listener);
+ }
+
+ protected void notifyDefined(final String fileName, final Collection<String> procedures)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ if (procedures.size() == 0)
+ return;
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(fileName, procedures);
+ }
+
+ protected void notifyDefined(final String fileName, final String procedure)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(fileName, procedure);
+
+ }
+
+ protected void notifyDeleted(final String fileName, final Collection<String> procedures)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ if (procedures.size() == 0)
+ return;
+
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(fileName, procedures);
+ }
+
+ protected void notifyDeleted(final String fileName, final String procedure)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(fileName, procedure);
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * AMBIGUITY DETECTOR
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * @return Whether there exists a procedure name that is defined in more
+ * than one file in the UserSpace
+ */
+ @Override
+ public boolean hasErrors()
+ {
+ for (Entry<String, ? extends Map<String, Procedure>> entry : context.getProcedureTable().entrySet())
+ {
+ if (entry.getValue().size() > 1)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns all ambiguity problems
+ */
+ @Override
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> ambiguities = new ArrayList<ProcedureErrorMessage>();
+
+ for (Entry<String, HashMap<String, Procedure>> entry : context.getProcedureTable().entrySet())
+ {
+ HashMap<String, Procedure> fileToProc = entry.getValue();
+
+ if (fileToProc.size() < 2)
+ continue;
+
+ ambiguities.add(
+ new ProcedureErrorMessage(
+ ProcedureErrorType.AMBIGUOUS,
+ entry.getKey(),
+ fileToProc.keySet()
+ )
+ );
+ }
+
+ return ambiguities;
+ }
+
+ @Override
+ public Collection<String> getAllConflictingFiles()
+ {
+ ArrayList<String> conflictingFiles = new ArrayList<String>();
+
+ for (Entry<String, HashMap<String, Procedure>> entry : context.getProcedureTable().entrySet())
+ {
+ HashMap<String, Procedure> fileToProc = entry.getValue();
+
+ if (fileToProc.size() < 2)
+ continue;
+
+ conflictingFiles.addAll(fileToProc.keySet());
+ }
+
+ return conflictingFiles;
+ }
+
+ @Override
+ public boolean hasAmbiguousProcedures(String fileName)
+ {
+ for (Procedure proc : context.getFilesTable().get(fileName).getExecutables())
+ {
+ if(isProcedureAmbiguous(proc.getName()))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isProcedureAmbiguous(String name)
+ {
+ Map<String, Procedure> ambigs = context.getProcedureTable().get(name.toLowerCase());
+ if (ambigs == null)
+ return false;
+ return ambigs.size() > 1;
+ }
+
+
+ @Override
+ public void addAmbiguityListener(AmbiguityListener listener)
+ {
+ ambiguityListeners.add(listener);
+ }
+
+ @Override
+ public void removeAmbiguityListener(AmbiguityListener listener)
+ {
+ ambiguityListeners.remove(listener);
+ }
+
+ private void notifyAmbiguityDetected(String procedureName, Map<String, Procedure> fileToProcedure)
+ {
+ if(!context.fireProcedureEvents())
+ return;
+ for (AmbiguityListener listener : ambiguityListeners)
+ listener.ambiguityDetected(procedureName, fileToProcedure);
+ }
+
+ private void notifyAmbiguityResolved(String procedureName, String winnerFile)
+ {
+ if(!context.fireProcedureEvents())
+ return;
+ for (AmbiguityListener listener : ambiguityListeners)
+ listener.ambiguityResolved(procedureName, winnerFile);
+ }
+
+
+
+
+}