import java.applet.Applet; import java.awt.Panel; import java.awt.Button; import java.awt.GridLayout; import java.awt.BorderLayout; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Event; import java.awt.Color; import java.awt.Insets; import java.lang.Integer; import logo.ui.TurtleGraphicsPanel; import logo.ui.ToggleButton; import logo.ui.CommandBox; import logo.ui.ColorChooser; import logo.lang.Interpreter; import logo.lang.LogoSub; /** TurtleControlPanel allows the user to execute simple rLogo commands by pushing buttons, and provides a Command Box for serious programming, debugging, and learning. */ //this class was originally named "TurtleControlPanel," so some //variable names might still reflect this. public class rLogo extends Applet { String action; //holds a pending action, based on the user pushing a button boolean actionRequiresParameter; String mode; //tracks the action that occurred before the pending action Integer value = new Integer(0); // Integer increment=new Integer(10); //used to increment commands that require numeric parameters private int currentLayout=0; private final static int NORMAL_LAYOUT=1; private final static int SMALL_LAYOUT=2; LocalInterpreter i = null; TurtleGraphicsPanel tg = null; Panel movement = new Panel(); Panel environment = new Panel(); Panel controls = new Panel(); Button leftButton = new Button("Left");; Button rightButton = new Button("Right"); Button forwardButton = new Button("Forward"); Button backwardButton = new Button("Back"); public ToggleButton homeButton = new ToggleButton("Home","Clear",false); public ToggleButton penButton = new ToggleButton("Pen Now Down","Pen Now Up",false); public ToggleButton turtleButton = new ToggleButton("Turtle is Showing","Turtle is Hidden",false); Button breakButton = new Button("Break!"); Button colorButton = new Button("Change Color"); public CommandBox commandBox; public ColorChooser c = new ColorChooser(); public void init() { resize(600,600); commandBox.outString("> Welcome to rLogo!"); commandBox.outString("> For help, type:"); commandBox.outString("help"); } public rLogo() { tg=new TurtleGraphicsPanel(); i=new LocalInterpreter(tg,this); commandBox = new CommandBox(i); i.start(); } public final void start() { i.resume(); } public final void stop() { i.suspend(); } //The layout goals are to maximize the height of the //CommandBox, and to maximize the area of the //TurtleGraphicsPanel. The ControlPanel has a pretty much //fixed size. For smaller heights, it is necessary //to allow the Control Panel to span the area adjacent to //both the CommandBox & TurtleGraphicsPanel. For larger //heights, it is better to simply place it to the right //of the CommandBox, and let the TurtleGraphicsPanel //consume more area: private void doLayout(int style) { removeAll(); environment.setLayout(new GridLayout(3,1)); environment.add(penButton); environment.add(turtleButton); environment.add(breakButton); environment.add(colorButton); setupMovement(); GridBagLayout gridBag = new GridBagLayout(); GridBagConstraints gbc = new GridBagConstraints(); controls.setLayout(gridBag); gbc.gridwidth=GridBagConstraints.REMAINDER; gbc.gridx=0; gbc.gridy=0; gridBag.setConstraints(movement,gbc); gbc.gridx=0; gbc.gridy=1; gridBag.setConstraints(environment,gbc); gbc.fill=GridBagConstraints.HORIZONTAL; gbc.weightx=3.0; gbc.gridx=0; gbc.gridy=2; gridBag.setConstraints(c,gbc); controls.add(movement); controls.add(environment); controls.add(c); if (style==SMALL_LAYOUT) { Panel centerPanel=new Panel(); centerPanel.setLayout(new GridLayout(2,1)); centerPanel.add(tg); centerPanel.add(commandBox); setLayout(new BorderLayout()); add("East",controls); add("Center",centerPanel); } else { Panel bottomPanel=new Panel(); bottomPanel.setLayout(new BorderLayout()); bottomPanel.add("Center",commandBox); bottomPanel.add("East",controls); setLayout(new GridLayout(2,1)); add(tg); add(bottomPanel); } } //If the user dynamically resizes the window (e.g. in the //AppletViewer), this code will adjust the layout //appropriately: public void reshape(int x, int y, int width, int height) { super.reshape(x,y,width,height); if (height<400) { if (currentLayout!=SMALL_LAYOUT) { doLayout(SMALL_LAYOUT); currentLayout=SMALL_LAYOUT; } } else { if (currentLayout!=NORMAL_LAYOUT) { doLayout(NORMAL_LAYOUT); currentLayout=NORMAL_LAYOUT; } } } //set up the movement & state buttons: void setupMovement() { GridBagLayout gridBag = new GridBagLayout(); movement.setLayout(gridBag); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(5,5,5,5); gbc.gridx=1; gbc.gridy=0; gridBag.setConstraints(forwardButton,gbc); movement.add(forwardButton); gbc.gridx=0; gbc.gridy=1; gridBag.setConstraints(leftButton,gbc); movement.add(leftButton); gbc.gridx=1; gbc.gridy=1; gridBag.setConstraints(homeButton,gbc); movement.add(homeButton); gbc.gridx=2; gbc.gridy=1; gridBag.setConstraints(rightButton,gbc); movement.add(rightButton); gbc.gridx=1; gbc.gridy=2; gridBag.setConstraints(backwardButton,gbc); movement.add(backwardButton); } //The next three methods are used to track the //current action as opposed to the previous action; //this helps "cumulative" commands to be built //(e.g. forward 10..forward 20..forward 30): private void setAction(String a) { action=a; actionRequiresParameter=false; } private void setAction(String a, boolean p) { action=a; actionRequiresParameter=p; } void setMode(String m) { mode=m; } //Here is the main event handling loop. Most of the //events generate an rLogo command; some of these //are cumulative. public boolean action(Event e, Object o) { setAction(""); if (e.target==breakButton) { //lets user cancel a program that gets out of control... i.Break(); } else if (e.target==rightButton) { setAction("right",true); } else if (e.target==leftButton) { setAction("left",true); } else if (e.target==forwardButton) { setAction("forward",true); } else if (e.target==backwardButton) { setAction("back",true); } else if (e.target==homeButton) { if (homeButton.getState()) { setAction("cs"); } else { setAction("home"); } } else if (e.target==colorButton) { Color l=c.getColor(); setAction("setpencolor "+ Integer.toString(l.getRed())+" "+ Integer.toString(l.getGreen())+" "+ Integer.toString(l.getBlue())); } else if (e.target==penButton) { if (penButton.getState()) { setAction("pendown"); } else { setAction("penup"); } } else if(e.target==turtleButton) { if (turtleButton.getState()) { setAction("showturtle"); } else { setAction("hideturtle"); } } if (action.length()>0) { if ( action==mode ) { //revise cumulative command if ( actionRequiresParameter ) { value=new Integer(value.intValue()+increment.intValue()); commandBox.replaceLastLine(action+" "+value.toString()); } else { commandBox.replaceLastLine(action); } } else { //new command; reset cumulative variables setMode(action); if (actionRequiresParameter ) { value=increment; commandBox.outString(action+" "+value.toString()); } else { commandBox.outString(action); } } //Note that all commands are queued, not executed immediately if ( actionRequiresParameter ) { i.queue(action+" "+increment.toString()); } else { i.queue(action); } } return true; } } /* LocalInterpreter extends Interpreter by invoking appropriate TurtleGraphicsPanel methods. Notice that many of the methods update the TurtleControlPanel, in order to reflect state changes. @see logo.lang.Interpreter This class can be contrasted with the RuntimeInterpreter class in rLogoRuntime.java */ final class LocalInterpreter extends Interpreter { TurtleGraphicsPanel tg; rLogo cp; // TurtleControlPanel cp; public LocalInterpreter(TurtleGraphicsPanel t,/*TurtleControlPanel*/rLogo controlPanel) { tg=t; cp=controlPanel; } public void wrapString(String s) { cp.commandBox.wrapString(s); } public void forward(int i) { tg.forward(i); cp.homeButton.setState(false); } public void backward(int i) { tg.backward(i); cp.homeButton.setState(false); } public void turnLeft(int i) { tg.turnLeft(i); cp.homeButton.setState(false); } public void turnRight(int i) { tg.turnRight(i); cp.homeButton.setState(false); } public void penUp() { tg.penUp(); cp.penButton.setState(true); } public void penDown() { tg.penDown(); cp.penButton.setState(false); } public void showTurtle() { tg.showTurtle(); cp.turtleButton.setState(false); } public void hideTurtle() { tg.hideTurtle(); cp.turtleButton.setState(true); } public void clearScreen() { tg.clearScreen(); cp.homeButton.setState(false); } public void home() { tg.home(); cp.homeButton.setState(true); } public void setColor(Color c) { tg.setColor(c); cp.c.setColor(c); } public void setBackColor(Color c) { tg.setBackColor(c); } public void edit(LogoSub s) { cp.commandBox.edit(s); } public void drawString(String s) { tg.drawString(s); } public void setRepaint(boolean b) { tg.setRepaint(b); } public void paint() { tg.repaint(); } public void resetMode() { // This is called to reset the cumulative variables whenever the // user types in a command, so that the TurtleControlPanel::action() // loop doesn't get confused. cp.setMode(""); } }