/* Spiro by Jimmc * Copyright 1996 Jim McBeath * * Permission to use, copy, and distribute Spiro, in source or binary form, * for personal non-commercial use is hereby granted without fee, provided * that this copyright notice appears in all copies, and that no charge is * associated with such copies. * * Permission to modify Spiro and make derivative works for personal * non-commercial use, and to distribute such modified versions, is hereby * granted without fee, provided that the users of such a modified version * are clearly notified that the work is a modified version of Spiro and * not the original Spiro. As with unmodified distributions, this copyight * notice must appear in all modified copies, and no charge may be associated * with such copies. * * The author makes no representations about the suitability of this software * for any purpose. It is provided "as-is", without any express or implied * warranty. In no event will the author be held liable for any damages * arising from the use of this software. */ /* SpiroControls - the control panel for Spiro * * Jim McBeath, May 1996 */ import java.awt.*; import java.applet.Applet; import java.applet.AppletContext; import java.util.Properties; import java.io.*; class SpiroControls extends Panel { SpiroCanvas canvas; ButtonPanel buttonPanel; WheelPanel wheelPanel; MiscPanel miscPanel; SpiroAdvanced advancedDialog; public SpiroControls(SpiroCanvas c, boolean isApplication, boolean canLoadFiles) { canvas = c; GridBagConstraints gbc = new GridBagConstraints(); GridBagLayout gb = new GridBagLayout(); setLayout(gb); wheelPanel = new WheelPanel(this); miscPanel = new MiscPanel(this,canLoadFiles); buttonPanel = new ButtonPanel(this,isApplication,canLoadFiles); gbc.gridx = 0; gbc.gridy = 0; gb.setConstraints(buttonPanel,gbc); add(buttonPanel); gbc.gridy = 1; gb.setConstraints(miscPanel,gbc); add(miscPanel); gbc.gridx = 1; gbc.gridy = 0; gbc.gridheight = GridBagConstraints.REMAINDER; gb.setConstraints(wheelPanel,gbc); add(wheelPanel); //canvas.repaint(); //draw the canvas outline //doesn't work } protected SpiroState getState() { /* return a SpiroState with all of the info * from the control panel */ SpiroState state; state = wheelPanel.getState(); miscPanel.getState(state); if (advancedDialog!=null) advancedDialog.getState(state); return state; } protected void setState(SpiroState state) { miscPanel.setState(state); wheelPanel.setState(state); if (advancedDialog!=null) advancedDialog.setState(state); layout(); /* in case num wheels changed */ //TBD - need to update screen? } protected void editAdvanced() { if (advancedDialog==null) { advancedDialog = new SpiroAdvanced(getFrame(),this); } advancedDialog.show(); } protected Frame getFrame() { return canvas.spiro.getFrame(); } protected boolean hasFrame() { return canvas.spiro.hasFrame(); } //Action callbacks for buttons, etc. protected void drawCallback() { canvas.defaultState = getState(); int lcm = canvas.defaultState.getLcmSteps(); int n = canvas.defaultState.steps; miscPanel.setActualSteps(lcm,n); if (advancedDialog!=null) advancedDialog.setActualSteps(lcm,n); canvas.runFlag = true; canvas.draw(); } protected void stopCallback() { canvas.runFlag = false; } protected void addWheelCallback() { wheelPanel.addWheel(); updateLayout(); } protected void removeWheelCallback() { wheelPanel.removeWheel(); updateLayout(); } protected void updateLayout() { if (hasFrame()) { getFrame().pack(); } layout(); //repaint(); } protected void toggleAnimateCallback() { //if animage was turned on, puts up the animate outline //if animate was turned off, this clears the screen canvas.defaultState = getState(); canvas.defaultState.steps = -1; canvas.draw(); } protected void saveFile() { showStatus(""); //String homedir; //homedir = System.getProperty("user.home"); Properties p = getState().getProperties(); //p.list(System.out);//debug //String defaultDirectory = homedir; String defaultDirectory = "."; String defaultFileName = "default.spi"; //"spiro" + File.separator + "default.spi"; String outFileName = getFileName(defaultDirectory, defaultFileName,"Save As", FileDialog.SAVE); if (outFileName==null) { showStatus("Cancelled"); return; } System.out.println("Output file is " + outFileName); FileOutputStream outFile; try { outFile = new FileOutputStream(outFileName); } catch (IOException e) { showStatus("Can't open output file"); return; } String fileComment = "Spiro Parameters"; p.save(outFile,fileComment); try { outFile.close(); showStatus("Saved file"); } catch (IOException e) { showStatus("Error closing file"); } } protected void loadFile() { showStatus(""); //String homedir; //homedir = System.getProperty("user.home"); String defaultDirectory = "."; String defaultFileName = "default.spi"; //"spiro" + File.separator + "default.spi"; String inFileName = getFileName(defaultDirectory, defaultFileName,"Load", FileDialog.LOAD); if (inFileName==null) { showStatus("Cancelled"); return; } //System.out.println("Input file is " + inFileName); SpiroState state = SpiroState.loadFile(inFileName); if (state==null) { showStatus("Can't load file"); return; } setState(state); } protected void loadURL() { showStatus(""); String inFileName = getURLName(); if (inFileName==null) { showStatus("Cancelled"); return; } //System.out.println("Input file is " + inFileName); SpiroState state = SpiroState.loadURL(inFileName); if (state==null) { showStatus("Can't load URL"); return; } setState(state); } //misc utility routines public void errorMessage(String s) { canvas.spiro.errorMessage(s); } public void showStatus(String s) { miscPanel.showStatus(s); /* //TBD - not very elegant try { AppletContext c; c = canvas.spiro.getAppletContext(); c.showStatus(s); } catch (Exception e) { System.out.println(s); } */ } private String getFileName(String defaultDirectory, String defaultFileName, String title, int mode) { Frame f = canvas.spiro.getFrame(); FileDialog dialog = new FileDialog(f,title,mode); if (defaultDirectory!=null) dialog.setDirectory(defaultDirectory); if (defaultFileName!=null) dialog.setFile(defaultFileName); dialog.show(); return (dialog.getDirectory() + File.separator + dialog.getFile()); } private String getURLName() { //TBD - use a simpler dialog Frame f = canvas.spiro.getFrame(); String msg = "Enter URL to load:"; String title = "Load URL"; SpiroStringDialog dialog = new SpiroStringDialog(f,title,msg); dialog.show(); Thread.yield(); String s = dialog.getText(); //Unfortunately, the dialog does not block for input (but FileDialog does), //so the control flow is not right here. return s; } private void beep() { // canvas.spiro.play(canvas.spiro.getCodeBase(),"beep.au"); //TBD - to be reenabled when audio is working on my system! } } class ButtonPanel extends Panel { SpiroControls controls; Checkbox advancedCheckbox; public ButtonPanel(SpiroControls c, boolean isApplication, boolean canLoadFiles) { controls = c; setLayout(new FlowLayout()); addButton("Draw"); addButton("Stop"); if (canLoadFiles) { addButton("Save As"); addButton("Load File"); } //addButton("Load URL"); //skip the Load URL button, it's not quite working //addButton("Advanced"); //advancedCheckbox = addCheckbox("Advanced"); if (isApplication) { addButton("Exit"); } } private Button addButton(String label) { Button button = new Button(label); add(button); return button; } private Checkbox addCheckbox(String label) { Checkbox cb = new Checkbox(label); add(cb); return cb; } public boolean action(Event ev, Object arg) { if (ev.target instanceof Button) { Button btn = (Button)(ev.target); if (ev.arg.equals("Draw")) { controls.drawCallback(); return true; } if (ev.arg.equals("Stop")) { controls.stopCallback(); return true; } if (ev.arg.equals("Advanced")) { controls.editAdvanced(); return true; } if (ev.arg.equals("Save As")) { controls.saveFile(); return true; } if (ev.arg.equals("Load File")) { controls.loadFile(); return true; } if (ev.arg.equals("Load URL")) { controls.loadURL(); return true; } if (ev.arg.equals("Exit")) { System.exit(0); return true; } } return false; } } class MiscPanel extends Panel { SpiroControls controls; TextField colorW; TextField stepsW; TextField dashW; Label actualStepsW; Checkbox animateW; Checkbox animateCountW; TextField chainW; //Choice drawModeW; ButtonPanel buttons; Label statusW; GridBuilder gb; public MiscPanel(SpiroControls c, boolean canLoadFiles) { controls = c; gb = new GridBuilder(this); gb.addLabel("Color:"); colorW = gb.addTextField("red",8); gb.addLabel("Dashes:"); dashW = gb.addTextField("",8); gb.nextRow(); gb.addLabel("Steps:"); stepsW = gb.addTextField("",8); actualStepsW = gb.addLabel(" "); gb.nextRow(); if (canLoadFiles) { gb.addLabel("Chain:"); chainW = gb.addTextField("",8); /* gb.addLabel("Draw Mode:"); drawModeW = gb.addChoice(); drawModeW.addItem("Backing"); drawModeW.addItem("Direct"); drawModeW.addItem("Synchronous"); drawModeW.select("Direct"); //set the default */ gb.nextRow(); } gb.constraints.gridwidth = 2; animateW = gb.addCheckbox("Animate"); animateCountW = gb.addCheckbox("Animate Count"); gb.nextRow(); statusW = gb.addLabel(" "); } protected void getState(SpiroState state) { state.color = colorW.getText().trim(); state.animate = animateW.getState(); state.animateCount = animateCountW.getState(); String stepStr = stepsW.getText().trim(); if (stepStr.length()>0) state.steps = Integer.parseInt(stepStr); else state.steps = 0; String dashStr = dashW.getText().trim(); if (dashStr.length()>0) { IntVector v = new IntVector(dashStr); int a[] = v.getArray(); state.dashPattern = a; } else state.dashPattern = null; /* if (drawModeW!=null) { state.drawMode = drawModeW.getSelectedItem(); } else { state.drawMode = "Direct"; } */ //state.drawMode = state.animate?"Backing":"Direct"; state.drawMode = "Direct"; if (chainW!=null) { state.chain = chainW.getText().trim(); } } protected void setState(SpiroState state) { colorW.setText(state.color); animateW.setState(state.animate); animateCountW.setState(state.animateCount); } public void showStatus(String s) { statusW.setText(s); } protected void setActualSteps(int lcmsteps, int usersteps) { String lcmstr = Integer.toString(lcmsteps); if (usersteps==0) actualStepsW.setText(lcmstr); else actualStepsW.setText("("+lcmstr+")"); } public boolean action(Event ev, Object arg) { if (ev.target instanceof Checkbox) { Checkbox ckb = (Checkbox)(ev.target); if (ckb.getLabel()=="Animate") { controls.toggleAnimateCallback(); return true; } } return false; } } class WheelPanel extends Panel { SpiroControls controls; protected static final int MAXNUMWHEELS = 10; protected int numwheels = 0; WheelControl wheels[] = new WheelControl[MAXNUMWHEELS]; GridBuilder gb; public WheelPanel(SpiroControls c) { controls = c; gb = new GridBuilder(this); addWheelLabels(); /* Put in the first two wheel panels */ addWheel(true,"100","1000","0"); addWheel(true,"20","100","0"); addWheel(false,"10","25","10"); addWheel(false,"3","2","1"); } private void addWheelLabels() { gb.addLabel("Wheel"); gb.addLabel("Enable"); gb.addLabel("Radius"); gb.addLabel("Teeth"); gb.addLabel("Offset"); } private void addWheelFields(WheelControl wheelinfo, boolean enabled, String defaultRadius, String defaultTeeth, String defaultOffset) { gb.nextRow(); gb.addLabel("" + (wheelinfo.num+1)); wheelinfo.enableW = gb.addCheckbox(""); wheelinfo.enableW.setState(enabled); wheelinfo.radiusW = gb.addTextField(defaultRadius,5); wheelinfo.teethW = gb.addTextField(defaultTeeth,5); wheelinfo.offsetW = gb.addTextField(defaultOffset,5); } private void addWheel(boolean enabled, String defaultRadius, String defaultTeeth, String defaultOffset) { if (numwheels>=MAXNUMWHEELS) { // controls.beep(); return; /* don't do anything */ } wheels[numwheels] = new WheelControl(numwheels); addWheelFields(wheels[numwheels],enabled, defaultRadius,defaultTeeth,defaultOffset); numwheels++; layout(); /* TBD - we need to make the parent re-layout, but I can't figure out how * to do that. So the user just has to resize his window to make it relayout */ // canvas.draw(); } protected void addWheel() { addWheel(false,"0","0","0"); } protected void removeWheel() { if (numwheels<=1) { // controls.beep(); return; /* ignore it */ } Component[] complist = getComponents(); int n = complist.length; remove(complist[n-1]); /* offset */ remove(complist[n-2]); /* teeth */ remove(complist[n-3]); /* radius */ remove(complist[n-4]); /* enable */ remove(complist[n-5]); /* wheel number */ numwheels--; layout(); } protected SpiroState getState() { int wheelsEnabled; for (wheelsEnabled=0; wheelsEnabled=MAXNUMWHEELS) state.numwheels = MAXNUMWHEELS-1; //silent limit if (state.numwheels<1) state.numwheels = 1; while (numwheelsstate.numwheels) removeWheel(); for (int i=0; i=0; i--) { if (wheels[i].enableW.getState()) enabled = true; if (enabled && !wheels[i].enableW.getState()) wheels[i].enableW.setState(enabled); } } else { //Just turned off, make sure all wheels after //it are also turned on boolean enabled = true; for (int i=0; i0) { String rstr0 = rstr.substring(0,cpos).trim(); String rstr1 = rstr.substring(cpos+1).trim(); state.radiusX = Integer.parseInt(rstr0); state.radiusY = Integer.parseInt(rstr1); } else { state.radiusX = Integer.parseInt(rstr); state.radiusY = state.radiusX; if (state.radiusX==0) state.teeth = 0; //disabled } } } /* end */