diff --git a/processing/app/PdeApplet.java b/processing/app/PdeApplet.java new file mode 100644 index 000000000..8b4cce467 --- /dev/null +++ b/processing/app/PdeApplet.java @@ -0,0 +1,369 @@ +#ifndef KVM + + +import java.awt.*; +import java.applet.Applet; +import java.io.*; +import java.net.*; +import java.util.*; + + +public class PdeApplet extends Applet +{ + static PdeApplet applet; + static Properties properties; + boolean errorState; + +#ifndef PLAYER + String encoding; + PdeEnvironment environment; +#endif + + public void init() { + applet = this; + //System.getProperties().list(System.out); + //System.out.println("home = " + System.getProperty("user.home")); + //System.out.println("prefix = " + System.getProperty("sys.prefix")); + +#ifdef PLAYER + // because it's the player version, cut out all the + // other crap, so that this file is as small as possible + + //} else if (mode.equals("player")) { + // could also do a class.forname for jdk11 + //PdePlayerProgram dpp = new PdePlayerProgram(this); + try { + String program = get("program"); + PdePlayer player = + ((PdePlayer) Class.forName(program).newInstance()); + add(player); + //environment = player; + player.init(this); + player.start(); + } catch (Exception e) { + e.printStackTrace(); + errorState = true; + } +#else + encoding = get("encoding"); + +#ifdef DBN + new DbnPreprocessor(this); +#endif + + String mode = get("mode", "editor"); + //System.err.println("mode is " + mode); + if (mode.equals("editor")) { +#ifdef EDITOR + //System.err.println("editor not yet complete"); + //System.err.println("editor dammit"); + //System.exit(0); + boolean beautify = false; + String program = get("program"); + if (program != null) { + program = readFile(program); + } else { + program = get("inline_program"); + } + if (program != null) { + // don't beautify if it's java code + if (program.indexOf("extends PdePlayer") == -1) { + // don't convert ; to \n if scheme + if (program.charAt(0) != ';') { + program = program.replace(';', '\n'); + // not scheme, but don't beautify if it's python + if (program.charAt(0) != '#') + beautify = true; + } + } + } + //add(hostess = new PdeEditor(this, program)); + PdeEditor editor = new PdeEditor(this, program); + if (beautify) editor.doBeautify(); + + setLayout(new BorderLayout()); + add("Center", editor); + environment = editor; + + //convert(); +#endif + + /* + } else if (mode.equals("grid")) { + // read 1 or more programs to be laid out in grid mode + // first count how many programs + int counter = 0; + while (true) { + if (get("program" + counter) == null) + break; + counter++; + } + // next load the programs + // what to do if zero programs in griddify? + String filenames[] = new String[counter]; + String programs[] = new String[counter]; + for (int i = 0; i < counter; i++) { + String filename = get("program" + i); + programs[i] = readFile(filename); + } + PdeGrid grid = new PdeGrid(this, programs); + setLayout(new BorderLayout()); + add("Center", grid); + environment = grid; + */ + + } else if (mode.equals("none")) { + // don't do anything, handled by subclass + } +#endif PLAYER + } + + +#ifndef PLAYER + public void destroy() { + if (environment != null) { + environment.terminate(); + } + } +#endif + + /* +#ifdef EDITOR + // this is used by PdeFancy, but could be useful in other + // contexts as well, i would imagine + public void setProgram(String p) { + if (environment instanceof PdeEditor) { + ((PdeEditor)environment).setProgram(p); + } + } +#endif + */ + + public void paint(Graphics g) { + if (errorState) { + g.setColor(Color.red); + Dimension d = size(); + g.fillRect(0, 0, d.width, d.height); + //} else { + //super(g); + } + } + + + +#ifndef PLAYER + /* loading order: + * 0. if application, a file on the disk + * 1. a file relative to the .html file containing the applet + * 2. a url + * 3. a file relative to the .class files + */ + public String readFile(String filename) { + if (filename.length() == 0) { + return null; + } + URL url; + InputStream stream = null; + String openMe; + byte temp[] = new byte[65536]; // 64k, 16k was too small + + try { + // this is two cases, one is bound to throw (or work) + if (isApplet()) { + // Try to open it relative to the document base + url = new URL(getDocumentBase(), filename); + stream = url.openStream(); + } else { + // if running as an application, get file from disk + stream = new FileInputStream(filename); + } + + } catch (Exception e1) { try { + if (isApplet()) { + // now try to open it relative to the code base + url = new URL(getCodeBase(), filename); + stream = url.openStream(); + } else { + url = getClass().getResource(filename); + stream = url.openStream(); + } + + } catch (Exception e2) { try { + // Try to open the param string as a URL + url = new URL(filename); + stream = url.openStream(); + + } catch (Exception e3) { + //e1.printStackTrace(); + //e2.printStackTrace(); + return null; + } } } + + try { + int offset = 0; + while (true) { + int byteCount = stream.read(temp, offset, 1024); + if (byteCount <= 0) break; + offset += byteCount; + } + byte program[] = new byte[offset]; + System.arraycopy(temp, 0, program, 0, offset); + + //return languageEncode(program); + // convert the bytes based on the current encoding + try { + if (encoding == null) + return new String(program); + return new String(program, encoding); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + encoding = null; + return new String(program); + } + + } catch (Exception e) { + System.err.println("problem during download"); + e.printStackTrace(); + return null; + } + } + +#ifdef EDITOR + static public Image readImage(String name) { + Image image = null; + if (isApplet()) { + image = applet.getImage(applet.getCodeBase(), name); + } else { + Toolkit tk = Toolkit.getDefaultToolkit(); + image = tk.getImage("lib/" + name); + //URL url = PdeApplet.class.getResource(name); + //image = tk.getImage(url); + } + MediaTracker tracker = new MediaTracker(applet); + tracker.addImage(image, 0); + try { + tracker.waitForAll(); + } catch (InterruptedException e) { } + return image; + } +#endif // EDITOR + +#endif // !PLAYER + + // all the information from PdeProperties + + static public String get(String attribute) { + return get(attribute, null); + } + + static public String get(String attribute, String defaultValue) { + String value = (properties != null) ? + properties.getProperty(attribute) : applet.getParameter(attribute); + + return (value == null) ? + defaultValue : value; + } + +#ifndef PLAYER + static public boolean getBoolean(String attribute, boolean defaultValue) { + String value = get(attribute, null); + return (value == null) ? defaultValue : + (new Boolean(value)).booleanValue(); + } + + static public int getInteger(String attribute, int defaultValue) { + String value = get(attribute, null); + return (value == null) ? defaultValue : + Integer.parseInt(value); + } + + static public Color getColor(String name, Color otherwise) { + Color parsed = null; + String s = get(name, null); + if ((s != null) && (s.indexOf("#") == 0)) { + try { + int v = Integer.parseInt(s.substring(1), 16); + parsed = new Color(v); + } catch (Exception e) { + } + } + if (parsed == null) return otherwise; + return parsed; + } + + static public boolean isMacintosh() { + return System.getProperty("os.name").toLowerCase().indexOf("mac") != -1; + } + + static public boolean hasFullPrivileges() { + //if (applet == null) return true; // application + //return false; + return !isApplet(); + } + + static public Font getFont(String which) { + if (which.equals("editor")) { + // 'Monospaced' and 'courier' also caused problems.. ;-/ + //return new Font("monospaced", Font.PLAIN, 12); + return new Font("Monospaced", Font.PLAIN, 12); + } + return null; + } +#endif // PLAYER + + public String getNetServer() { + String host = get("net_server", null); + if (host != null) return host; + + if (isApplet()) { + return getCodeBase().getHost(); + } + return "dbn.media.mit.edu"; + } + + static public boolean isApplet() { + return (properties == null); + } +} + + +#else // if it is the KVM + + +public class PdeApplet { + public PdeApplet() { + } + + String get(String something) { + return get(something, null); + } + + String get(String something, String otherwise) { + return null; + } + + String readFile(String name) { + // grab something out of the database + return null; + } +} + + +#endif + + +/* temporary, a little something for the kids */ +/* + static public void debugString(String s) { + byte output[] = s.getBytes(); + for (int i = 0; i < output.length; i++) { + if (output[i] >= 32) { + System.out.print((char)output[i]); + } else { + System.out.print("\\" + (int)output[i]); + if (output[i] == '\n') System.out.println(); + } + } + System.out.println(); + } +*/ diff --git a/processing/app/PdeApplication.java b/processing/app/PdeApplication.java new file mode 100644 index 000000000..89a703652 --- /dev/null +++ b/processing/app/PdeApplication.java @@ -0,0 +1,85 @@ +import java.awt.*; +import java.applet.Applet; +import java.io.*; +import java.net.*; +import java.util.*; +import java.awt.event.*; + + +public class PdeApplication extends PdeApplet +#ifdef RECORDER +implements ActionListener +#endif +{ + Frame frame; + WindowAdapter windowListener; + + static public void main(String args[]) { + PdeApplication app = new PdeApplication(); + //if (args.length != 0) { + //app.setProgramFile(args[0]); + //} + + app.frame.show(); + } + + public PdeApplication() { + frame = new Frame(" p r o c e s s i n g "); + + windowListener = new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }; + frame.addWindowListener(windowListener); + +#ifdef RECORDER + MenuBar menubar = new MenuBar(); + Menu goodies = new Menu("Processing"); + goodies.add(new MenuItem("Save QuickTime movie...")); + goodies.add(new MenuItem("Quit")); + goodies.addActionListener(this); + menubar.add(goodies); + frame.setMenuBar(menubar); +#endif + + properties = new Properties(); + try { + properties.load(new FileInputStream("lib/pde.properties")); + + } catch (Exception e) { + System.err.println("Error reading pde.properties"); + e.printStackTrace(); + System.exit(1); + } + int width = getInteger("width", 600); + int height = getInteger("height", 350); + // ms jdk requires that BorderLayout is set explicitly + frame.setLayout(new BorderLayout()); + frame.add("Center", this); + init(); + Insets insets = frame.getInsets(); + frame.reshape(50, 50, width + insets.left + insets.right, + height + insets.top + insets.bottom); + + // i don't like this being here, but.. + //((PdeEditor)environment).graphics.frame = frame; + ((PdeEditor)environment).frame = frame; + + frame.pack(); + //frame.show(); + } + +#ifdef RECORDER + public void actionPerformed(ActionEvent event) { + String command = event.getActionCommand(); + if (command.equals("Save QuickTime movie...")) { + ((PdeEditor)environment).doRecord(); + } else if (command.equals("Quit")) { + System.exit(0); + } + } +#endif +} + +#endif diff --git a/processing/app/PdeEditor.java b/processing/app/PdeEditor.java new file mode 100644 index 000000000..8211c7f21 --- /dev/null +++ b/processing/app/PdeEditor.java @@ -0,0 +1,598 @@ +#ifdef EDITOR + +import java.awt.*; +import java.io.*; +import java.net.*; +import java.util.*; + + +// play, stop, open, save, courseware, print, beautify +// height of button panel is 35 + +public class PdeEditor extends Panel implements PdeEnvironment { + static final String DEFAULT_PROGRAM = "// type program here\n"; + + // otherwise, if the window is resized with the message label + // set to blank, it's preferredSize() will be fukered + static final String EMPTY = " "; + PdeApplet app; + + PdeEditorButtons buttons; + //PdeGraphics graphics; + PdeRunner runner; + + Frame frame; + Window fullScreenWindow; + + Label status; + TextArea textarea; + + String lastDirectory; + String lastFile; + + boolean playing; + + + public PdeEditor(PdeApplet app, String program) { + this.app = app; + setLayout(new BorderLayout()); + + Color bgColor = + PdeApplet.getColor("bg_color", new Color(51, 102, 153)); + Color bgStippleColor = + PdeApplet.getColor("bg_stipple_color", null); + Color tickColor = + PdeApplet.getColor("tick_color", new Color(204, 204, 204)); + Color gutterBgColor = + PdeApplet.getColor("gutter_bg_color", new Color(0, 51, 102)); + Color buttonBgColor = + PdeApplet.getColor("button_bg_color", new Color(153, 153, 153)); + Color statusBgColor = + PdeApplet.getColor("status_bg_color", new Color(204, 204, 204)); + + //int gwidth = PdeApplet.getInteger("graphics_width", 101); + //int gheight = PdeApplet.getInteger("graphics_height", 101); + + //add("North", new PdeEditorLicensePlate()); + + //Panel left = new Panel(); + //left.setLayout(new BorderLayout()); + + Panel top = new Panel(); + top.setLayout(new BorderLayout()); + + boolean privileges = PdeApplet.hasFullPrivileges(); + boolean courseware = PdeApplet.get("save_as") != null; + buttons = new PdeEditorButtons(this, privileges, courseware, + (privileges & !courseware), true); + buttons.setBackground(buttonBgColor); + //add("North", buttons); + top.add("North", buttons); + + /* + graphics = new PdeGraphics(gwidth, gheight, bgColor); +#ifndef OPENGL + graphics = new PdeEditorGraphics(gwidth, gheight, tickColor, + bgColor, bgStippleColor, this); +#else + if (PdeApplet.getBoolean("graphics_3D", false)) { + graphics = new PdeEditorGraphics3D(gwidth, gheight, tickColor, + bgColor, bgStippleColor, this); + } else { + graphics = new PdeEditorGraphics(gwidth, gheight, tickColor, + bgColor, bgStippleColor, this); + } +#endif + */ + + //left.add("Center", graphics); + + //left.setBackground(gutterBgColor); + + //Panel right = new Panel(); + //right.setLayout(new BorderLayout()); + + Panel statusPanel = new Panel(); + statusPanel.setBackground(statusBgColor); + statusPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); + statusPanel.add(status = new Label(EMPTY)); + //right.add("North", statusPanel); + top.add("South", statusPanel); + + add("North", top); + + if (program == null) program = DEFAULT_PROGRAM; + textarea = new TextArea(program, 20, 48); + textarea.setFont(PdeApplet.getFont("editor")); + //right.add("Center", textarea); + add("Center", textarea); + + //#ifdef FANCY + // right.add("South", PdeFancy.makeDescription()); + //#endif + + //this.add("West", left); + //this.add("Center", right); + + if (!PdeApplet.isMacintosh()) { // this still relevant? + PdeEditorListener listener = new PdeEditorListener(); + textarea.addKeyListener(listener); + textarea.addFocusListener(listener); + textarea.addKeyListener(new PdeKeyListener(this)); + } + + runner = new PdeRunner(/*graphics,*/ this); + } + + + public void doPlay() { + doStop(); + playing = true; + buttons.play(); + + runner.setProgram(textarea.getText()); + runner.start(); + + // required so that key events go to the panel and works + //graphics.requestFocus(); // removed for pde + } + +#ifdef RECORDER + public void doRecord() { + doStop(); + PdeRecorder.start(this, graphics.width, graphics.height); + doPlay(); + } +#endif + + public void doStop() { +#ifdef RECORDER + if (!playing) return; +#endif + terminate(); + buttons.clear(); + playing = false; + } + + + public void doOpen() { + FileDialog fd = new FileDialog(new Frame(), + "Open a PDE program...", + FileDialog.LOAD); + fd.setDirectory(lastDirectory); + fd.setFile(lastFile); + fd.show(); + + String directory = fd.getDirectory(); + String filename = fd.getFile(); + if (filename == null) { + buttons.clear(); + return; // user cancelled + } + File file = new File(directory, filename); + + try { + FileInputStream input = new FileInputStream(file); + int length = (int) file.length(); + byte data[] = new byte[length]; + + int count = 0; + while (count != length) { + data[count++] = (byte) input.read(); + } + // set the last dir and file, so that they're + // the defaults when you try to save again + lastDirectory = directory; + lastFile = filename; + + // once read all the bytes, convert it to the proper + // local encoding for this system. + //textarea.setText(app.languageEncode(data)); + if (app.encoding == null) + textarea.setText(new String(data)); + else + textarea.setText(new String(data, app.encoding)); + + } catch (FileNotFoundException e1) { + e1.printStackTrace(); + + } catch (IOException e2) { + e2.printStackTrace(); + } + buttons.clear(); + } + + + public void doSave() { + message("Saving file..."); + String s = textarea.getText(); + FileDialog fd = new FileDialog(new Frame(), + "Save PDE program as...", + FileDialog.SAVE); + fd.setDirectory(lastDirectory); + fd.setFile(lastFile); + fd.show(); + + String directory = fd.getDirectory(); + String filename = fd.getFile(); + if (filename == null) { + message(EMPTY); + buttons.clear(); + return; // user cancelled + } + File file = new File(directory, filename); + + try { + FileWriter writer = new FileWriter(file); + writer.write(s); + writer.flush(); + writer.close(); + + lastDirectory = directory; + lastFile = filename; + message("Done saving file."); + + } catch (IOException e) { + e.printStackTrace(); + message("Did not write file."); + } + buttons.clear(); + } + + + public void doSnapshot() { + /* + //dbcp.msg("Sending your file to the server..."); + message("Sending your file to the server..."); + + try { + String programStr = textarea.getText(); + //byte imageData[] = dbrp.runners[dbrp.current].dbg.getPixels(); + //byte imageData[] = graphics.getPixels(); + String imageStr = new String(graphics.makeTiffData()); + + URL appletUrl = app.getDocumentBase(); + String document = appletUrl.getFile(); + document = document.substring(0, document.lastIndexOf("?")); + URL url = new URL("http", appletUrl.getHost(), document); + + URLConnection conn = url.openConnection(); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setUseCaches(false); + conn.setRequestProperty("Content-Type", + "application/x-www-form-urlencoded"); + + DataOutputStream printout = + new DataOutputStream(conn.getOutputStream()); + + String content = + "save_as=" + URLEncoder.encode(PdeApplet.get("save_as")) + + "&save_image=" + URLEncoder.encode(imageStr) + + "&save_program=" + URLEncoder.encode(programStr); + + printout.writeBytes(content); + printout.flush(); + printout.close(); + + // what did they say back? + DataInputStream input = + new DataInputStream(conn.getInputStream()); + String str = null; + while ((str = input.readLine()) != null) { + //System.out.println(str); + } + input.close(); + message("Done saving file."); + + } catch (Exception e) { + e.printStackTrace(); + message("Problem: Your work could not be saved."); + } + buttons.clear(); + */ + } + + + static byte tiffHeader[] = { + 77, 77, 0, 42, 0, 0, 0, 8, 0, 9, 0, -2, 0, 4, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 3, 0, 0, 0, 1, + 0, 0, 0, 0, 1, 2, 0, 3, 0, 0, 0, 3, 0, 0, 0, 122, 1, 6, 0, 3, 0, + 0, 0, 1, 0, 2, 0, 0, 1, 17, 0, 4, 0, 0, 0, 1, 0, 0, 3, 0, 1, 21, + 0, 3, 0, 0, 0, 1, 0, 3, 0, 0, 1, 22, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, + 1, 23, 0, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 8, 0, 8 + }; + + static byte[] makeTiffData(int pixels[], int width, int height) { + byte tiff[] = new byte[768 + width*height*3]; + System.arraycopy(tiffHeader, 0, tiff, 0, tiffHeader.length); + tiff[30] = (byte) ((width >> 8) & 0xff); + tiff[31] = (byte) ((width) & 0xff); + tiff[42] = tiff[102] = (byte) ((height >> 8) & 0xff); + tiff[43] = tiff[103] = (byte) ((height) & 0xff); + int count = width*height*3; + tiff[114] = (byte) ((count >> 24) & 0xff); + tiff[115] = (byte) ((count >> 16) & 0xff); + tiff[116] = (byte) ((count >> 8) & 0xff); + tiff[117] = (byte) ((count) & 0xff); + int index = 768; + for (int i = 0; i < pixels.length; i++) { + tiff[index++] = (byte) ((pixels[i] >> 16) & 0xff); + tiff[index++] = (byte) ((pixels[i] >> 8) & 0xff); + tiff[index++] = (byte) ((pixels[i] >> 0) & 0xff); + } + return tiff; + } + + //public byte[] makeTiffData() { + //return makeTiffData(pixels, width, height); + //} + + public void doSaveTiff() { + /* + message("Saving TIFF image..."); + String s = textarea.getText(); + FileDialog fd = new FileDialog(new Frame(), + "Save image as...", + FileDialog.SAVE); + fd.setDirectory(lastDirectory); + fd.setFile("untitled.tif"); + fd.show(); + + String directory = fd.getDirectory(); + String filename = fd.getFile(); + if (filename == null) return; + + File file = new File(directory, filename); + try { + FileOutputStream fos = new FileOutputStream(file); + byte data[] = graphics.makeTiffData(); + fos.write(data); + fos.flush(); + fos.close(); + + lastDirectory = directory; + message("Done saving image."); + + } catch (IOException e) { + e.printStackTrace(); + message("An error occurred, no image could be written."); + } + */ + } + + + /* + static void writeTiffHeader(OutputStream os, int width, int height) + throws IOException { + byte header[] = new byte[768]; + System.arraycopy(tiffHeader, 0, header, 0, tiffHeader.length); + header[30] = (byte) ((width >> 8) & 0xff); + header[31] = (byte) ((width) & 0xff); + header[42] = header[102] = (byte) ((height >> 8) & 0xff); + header[43] = header[103] = (byte) ((height) & 0xff); + int count = width*height*3; + header[114] = (byte) ((count >> 24) & 0xff); + header[115] = (byte) ((count >> 16) & 0xff); + header[116] = (byte) ((count >> 8) & 0xff); + header[117] = (byte) ((count) & 0xff); + os.write(header); + } + + static void writeTiff(OutputStream os, int width, int height, int pixels[]) { + writeTiffHeader(os, width, height); + for (int i = 0; i < pixels.length; i++) { + write((pixels[i] >> 16) & 0xff); + write((pixels[i] >> 8) & 0xff); + write(pixels[i] & 0xff); + } + os.flush(); + } + */ + + /* + static public byte[] makePgmData(byte inData[], int width, int height) { + String headerStr = "P5 " + width + " " + height + " 255\n"; + byte header[] = headerStr.getBytes(); + int count = width * height; + byte outData[] = new byte[header.length + count]; + System.arraycopy(header, 0, outData, 0, header.length); + System.arraycopy(inData, 0, outData, header.length, count); + return outData; + } + */ + + public void doPrint() { + /* + Frame frame = new Frame(); // bullocks + int screenWidth = getToolkit().getScreenSize().width; + frame.reshape(screenWidth + 20, 100, screenWidth + 100, 200); + frame.show(); + + Properties props = new Properties(); + PrintJob pj = getToolkit().getPrintJob(frame, "PDE", props); + if (pj != null) { + Graphics g = pj.getGraphics(); + // awful way to do printing, but sometimes brute force is + // just the way. java printing across multiple platforms is + // outrageously inconsistent. + int offsetX = 100; + int offsetY = 100; + int index = 0; + for (int y = 0; y < graphics.height; y++) { + for (int x = 0; x < graphics.width; x++) { + g.setColor(new Color(graphics.pixels[index++])); + g.drawLine(offsetX + x, offsetY + y, + offsetX + x, offsetY + y); + } + } + g.dispose(); + g = null; + pj.end(); + } + frame.dispose(); + buttons.clear(); + */ + } + + + public void doBeautify() { + String prog = textarea.getText(); + if ((prog.charAt(0) == '#') || (prog.charAt(0) == ';')) { + message("Only DBN code can be made beautiful."); + buttons.clear(); + return; + } + char program[] = prog.toCharArray(); + StringBuffer buffer = new StringBuffer(); + boolean gotBlankLine = false; + int index = 0; + int level = 0; + + while (index != program.length) { + int begin = index; + while ((program[index] != '\n') && + (program[index] != '\r')) { + index++; + if (program.length == index) + break; + } + int end = index; + if (index != program.length) { + if ((index+1 != program.length) && + // treat \r\n from windows as one line + (program[index] == '\r') && + (program[index+1] == '\n')) { + index += 2; + } else { + index++; + } + } // otherwise don't increment + + String line = new String(program, begin, end-begin); + line = line.trim(); + + if (line.length() == 0) { + if (!gotBlankLine) { + // let first blank line through + buffer.append('\n'); + gotBlankLine = true; + } + } else { + if (line.charAt(0) == '}') { + level--; + } + for (int i = 0; i < level*3; i++) { + buffer.append(' '); + } + buffer.append(line); + buffer.append('\n'); + if (line.charAt(0) == '{') { + level++; + } + gotBlankLine = false; + } + } + textarea.setText(buffer.toString()); + buttons.clear(); + } + + + public void enableFullScreen() { + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + fullScreenWindow = new Window(new Frame()); + fullScreenWindow.setBounds(0, 0, screen.width, screen.height); + fullScreenWindow.setBackground(new Color(102, 102, 102)); + fullScreenWindow.show(); + buttons.clear(); + + if (frame != null) frame.toFront(); + } + + public void disableFullScreen() { + buttons.clear(); + } + + + public void terminate() { // part of PdeEnvironment + runner.stop(); + message(EMPTY); + } + + + // TODO iron out bugs with this code under + // different platforms, especially macintosh + public void highlightLine(int lnum) { + if (lnum < 0) { + textarea.select(0, 0); + return; + } + //System.out.println(lnum); + String s = textarea.getText(); + int len = s.length(); + //int lnum = .line; + int st = -1, end = -1; + int lc = 0; + if (lnum == 0) st = 0; + for (int i = 0; i < len; i++) { + //if ((s.charAt(i) == '\n') || (s.charAt(i) == '\r')) { + boolean newline = false; + if (s.charAt(i) == '\r') { + if ((i != len-1) && (s.charAt(i+1) == '\n')) i++; + lc++; + newline = true; + } else if (s.charAt(i) == '\n') { + lc++; + newline = true; + } + if (newline) { + if (lc == lnum) + st = i+1; + else if (lc == lnum+1) { + end = i; + break; + } + } + } + if (end == -1) end = len; + //System.out.println("st/end: "+st+"/"+end); + textarea.select(st, end+1); + //if (iexplorerp) { + //textarea.invalidate(); + //textarea.repaint(); + //} + } + + + public void error(PdeException e) { // part of PdeEnvironment + if (e.line >= 0) highlightLine(e.line); + //dbcp.repaint(); // button should go back to 'play' + //System.err.println(e.getMessage()); + message("Problem: " + e.getMessage()); + buttons.clearPlay(); + //showStatus(e.getMessage()); + } + + + public void finished() { // part of PdeEnvironment +#ifdef RECORDER + PdeRecorder.stop(); +#endif + playing = false; + buttons.clearPlay(); + message("Done."); + } + + + public void message(String msg) { // part of PdeEnvironment + status.setText(msg); + } + + + public void messageClear(String msg) { + if (status.getText().equals(msg)) status.setText(EMPTY); + } +} + +#endif + diff --git a/processing/app/PdeEditorButtons.java b/processing/app/PdeEditorButtons.java new file mode 100644 index 000000000..71df51960 --- /dev/null +++ b/processing/app/PdeEditorButtons.java @@ -0,0 +1,338 @@ +#ifdef EDITOR + + +import java.awt.*; + + +public class PdeEditorButtons extends Panel { + static final int BUTTON_COUNT = 9; + static final int BUTTON_WIDTH = 24; + static final int BUTTON_HEIGHT = 24; + + static final String title[] = { + "Play", "Stop", + "Open", "Save", "Save on Server", "Print", "Beautify", + "Disable Full Screen", "Full Screen" + }; + + static final int PLAY = 0; + static final int STOP = 1; + static final int OPEN = 2; + static final int SAVE = 3; + static final int SNAPSHOT = 4; // only available is save_as defined + static final int PRINT = 5; + static final int BEAUTIFY = 6; + + static final int DISABLE_FULL_SCREEN = 7; + static final int FULL_SCREEN = 8; + + static final int INACTIVE = 0; + static final int ROLLOVER = 1; + static final int ACTIVE = 2; + + PdeEditor editor; + + Image offscreen; + int width, height; + + Image buttons; + Image inactive[]; + Image rollover[]; + Image active[]; + int currentRollover; + + int buttonCount; + int state[]; + Image stateImage[]; + int which[]; // mapping indices to implementation + + int x1[], x2[]; + int y1, y2; + + + public PdeEditorButtons(PdeEditor editor, boolean useOpenSave, + boolean useCourseware, boolean usePrint, + boolean useBeautify) { + this.editor = editor; + buttons = PdeApplet.readImage("buttons.gif"); + + buttonCount = 0; + which = new int[BUTTON_COUNT]; + + // always include these + which[buttonCount++] = PLAY; + which[buttonCount++] = STOP; + + // the rest are conditional + if (useOpenSave) { + which[buttonCount++] = OPEN; + which[buttonCount++] = SAVE; + } + if (useCourseware) which[buttonCount++] = SNAPSHOT; + if (usePrint) which[buttonCount++] = PRINT; + if (useBeautify) which[buttonCount++] = BEAUTIFY; + + which[buttonCount++] = FULL_SCREEN; + + //buttonX = new int[buttonCount]; + currentRollover = -1; + } + + + public void update() { + //System.out.println(currentRollover); + paint(this.getGraphics()); + } + + public void update(Graphics g) { + paint(g); + } + + public void paint(Graphics screen) { + if (inactive == null) { + inactive = new Image[BUTTON_COUNT]; + rollover = new Image[BUTTON_COUNT]; + active = new Image[BUTTON_COUNT]; + state = new int[BUTTON_COUNT]; + + for (int i = 0; i < BUTTON_COUNT; i++) { + inactive[i] = createImage(BUTTON_WIDTH, BUTTON_HEIGHT); + Graphics g = inactive[i].getGraphics(); + g.drawImage(buttons, -(i*BUTTON_WIDTH), -2*BUTTON_HEIGHT, null); + + rollover[i] = createImage(BUTTON_WIDTH, BUTTON_HEIGHT); + g = rollover[i].getGraphics(); + g.drawImage(buttons, -(i*BUTTON_WIDTH), -1*BUTTON_HEIGHT, null); + + active[i] = createImage(BUTTON_WIDTH, BUTTON_HEIGHT); + g = active[i].getGraphics(); + g.drawImage(buttons, -(i*BUTTON_WIDTH), -0*BUTTON_HEIGHT, null); + } + + state = new int[buttonCount]; + stateImage = new Image[buttonCount]; + for (int i = 0; i < buttonCount; i++) { + setState(i, INACTIVE, false); + } + } + Dimension size = size(); + if ((offscreen == null) || + (size.width != width) || (size.height != height)) { + offscreen = createImage(size.width, size.height); + width = size.width; + height = size.height; + + x1 = new int[buttonCount]; + x2 = new int[buttonCount]; + + y1 = (height - BUTTON_HEIGHT) / 2; + y2 = y1 + BUTTON_HEIGHT; + + int offsetX = 8; + for (int i = 0; i < 2; i++) { + //g.drawImage(stateImage[i], offsetX, offsetY, null); + x1[i] = offsetX; + x2[i] = offsetX + BUTTON_WIDTH; + offsetX += BUTTON_WIDTH + 4; + } + + // start from righthand side and move left + offsetX = width - 8 - BUTTON_WIDTH; + for (int i = buttonCount-1; i >= 2; --i) { + //g.drawImage(stateImage[i], offsetX, offsetY, null); + x1[i] = offsetX; + x2[i] = offsetX + BUTTON_WIDTH; + offsetX -= BUTTON_WIDTH + 4; + } + } + Graphics g = offscreen.getGraphics(); + g.setColor(getBackground()); + g.fillRect(0, 0, width, height); + + for (int i = 0; i < buttonCount; i++) { + g.drawImage(stateImage[i], x1[i], y1, null); + } + //g.drawImage(stateImage[i], offsetX, offsetY, null); + /* + //Dimension dim = size(); + int offsetY = (height - BUTTON_HEIGHT) / 2; + + int offsetX = 8; + for (int i = 0; i < 2; i++) { + g.drawImage(stateImage[i], offsetX, offsetY, null); + offsetX += BUTTON_WIDTH + 4; + } + + // start from righthand side and move left + offsetX = width - 8 - BUTTON_WIDTH; + for (int i = buttonCount-1; i >= 2; --i) { + g.drawImage(stateImage[i], offsetX, offsetY, null); + offsetX -= BUTTON_WIDTH + 4; + } + */ + screen.drawImage(offscreen, 0, 0, null); + //screen.fillRect(0, 0, 10, 10); + } + + + public boolean mouseMove(Event e, int x, int y) { + //System.out.println(x + ", " + y); + if (currentRollover != -1) { + if ((x > x1[currentRollover]) && (y > y1) && + (x < x2[currentRollover]) && (y < y2)) { + //System.out.println("same"); + return true; // no change + + } else { + //state[currentRollover] = INACTIVE_STATE; + //stateImage[currentRollover] = inactive[currentRollover]; + setState(currentRollover, INACTIVE, true); + editor.messageClear(title[currentRollover]); + /* + if (editor.status.getText().equals(title[currentRollover])) { + editor.message(""); + } + */ + currentRollover = -1; + //update(); + } + } + int sel = findSelection(x, y); + if (sel == -1) return true; + + if (state[sel] != ACTIVE) { + //state[sel] = ROLLOVER_STATE; + //stateImage[sel] = rollover[sel]; + setState(sel, ROLLOVER, true); + currentRollover = sel; + } + /* + for (int i = 0; i < buttonCount; i++) { + if ((x > x1[i]) && (y > y1) && + (x < x2[i]) && (y < y2)) { + //System.out.println(i); + if (state[i] != ACTIVE_STATE) { + state[i] = ROLLOVER_STATE; + stateImage[i] = rollover[i]; + currentRollover = i; + } + update(); + return true; + } + } + */ + //update(); + return true; + } + + private int findSelection(int x, int y) { + for (int i = 0; i < buttonCount; i++) { + if ((x > x1[i]) && (y > y1) && + (x < x2[i]) && (y < y2)) { + return i; + } + } + return -1; + } + + private void setState(int slot, int newState, boolean updateAfter) { + //if (inactive == null) return; + state[slot] = newState; + switch (newState) { + case INACTIVE: + stateImage[slot] = inactive[which[slot]]; + break; + case ACTIVE: + stateImage[slot] = active[which[slot]]; + break; + case ROLLOVER: + stateImage[slot] = rollover[which[slot]]; + editor.message(title[which[slot]]); + break; + } + if (updateAfter) update(); + } + + public boolean mouseEnter(Event e, int x, int y) { + return mouseMove(e, x, y); + } + + public boolean mouseExit(Event e, int x, int y) { + // kludge + for (int i = 0; i < BUTTON_COUNT; i++) { + editor.messageClear(title[i]); + } + //if (currentRollover != -1) { + //editor.message(""); + //} + return mouseMove(e, x, y); + } + + int wasDown = -1; + + public boolean mouseDown(Event e, int x, int y) { + int sel = findSelection(x, y); + if (sel == -1) return false; + currentRollover = -1; + + setState(sel, ACTIVE, true); + switch (which[sel]) { + + case PLAY: editor.doPlay(); break; + case STOP: setState(PLAY, INACTIVE, true); editor.doStop(); break; + + case OPEN: editor.doOpen(); break; + case SAVE: editor.doSave(); break; + case SNAPSHOT: editor.doSnapshot(); break; + case PRINT: editor.doPrint(); break; + case BEAUTIFY: editor.doBeautify(); break; + + case FULL_SCREEN: editor.enableFullScreen(); break; + case DISABLE_FULL_SCREEN: editor.disableFullScreen(); break; + } + //update(); + return true; + } + + public void clear() { // (int button) { + if (inactive == null) return; + + //setState(button, INACTIVE); + // skip the play button, do the others + for (int i = 1; i < buttonCount; i++) { + //state[i] = INACTIVE; + //stateImage[i] = inactive[which[i]]; + setState(i, INACTIVE, false); + } + update(); + } + + public void play() { + if (inactive == null) return; + clear(); + setState(0, ACTIVE, true); + } + + public void clearPlay() { + if (inactive == null) return; + setState(0, INACTIVE, true); + } + + /* + public boolean mouseUp(Event e, int x, int y) { + if (wasDown == -1) return true; + if (which[wasDown] == PLAY) return true; + + setState(wasDown, INACTIVE); + wasDown = -1; + //update(); + return true; + } + */ + + public Dimension preferredSize() { + return new Dimension(200, 35); + } +} + +#endif diff --git a/processing/app/PdeEditorGraphics.java.x b/processing/app/PdeEditorGraphics.java.x new file mode 100644 index 000000000..dc8daadb0 --- /dev/null +++ b/processing/app/PdeEditorGraphics.java.x @@ -0,0 +1,103 @@ +#ifdef EDITOR + +import java.awt.*; + + +public class DbnEditorGraphics extends DbnGraphics { + static Font plainFont = new Font("Helvetica", Font.PLAIN, 10); + + Color tickColor; + Color bgStippleColor; + + DbnEditor editor; + + + public DbnEditorGraphics(int width, int height, Color tickColor, + Color bgColor, Color bgStippleColor, + DbnEditor editor) { + super(width, height, bgColor); + this.tickColor = tickColor; + this.bgStippleColor = bgStippleColor; + this.editor = editor; + } + + + public Dimension preferredSize() { + return new Dimension(width1*magnification + 100, + height1*magnification + 100); + } + + + public void base() { + if (baseImage == null) updateBase = true; + + if (updateBase) { + // these few lines completely identical to DbnGraphics.base() + Dimension dim = preferredSize(); + baseImage = createImage(dim.width, dim.height); + baseGraphics = baseImage.getGraphics(); + lastImage = createImage(width, height); + lastGraphics = lastImage.getGraphics(); + + gx = (dim.width - width*magnification) / 2; + gy = (dim.height - height*magnification) / 2; + + // draw background + Graphics g = baseGraphics; + g.setColor(bgColor); + g.fillRect(0, 0, dim.width, dim.height); + if (!bgColor.equals(bgStippleColor)) { + g.setColor(bgStippleColor); + int count = 2 * Math.max(dim.width, dim.height); + for (int i = 0; i < count; i += 2) { + g.drawLine(0, i, i, 0); + } + } + g.setFont(plainFont); + FontMetrics metrics = g.getFontMetrics(); + int lineheight = metrics.getAscent() + + metrics.getDescent(); + + // put ticks around (only if in edit mode) + g.setColor(tickColor); + int increment = (width > 101) ? 25 : 20; + int x, y; + y = gy + height*magnification; + for (x = 0; x <= width; x += increment) { + int xx = x * magnification; + g.drawLine(gx + xx, y, gx + xx, y + 4); + String num = String.valueOf(x); + g.drawString(num, gx + xx - 1, y + 2 + lineheight); + } + for (y = 0; y <= height; y += increment) { + int yy = y * magnification; + g.drawLine(gx - 4, gy + yy, gx, gy + yy); + String num = String.valueOf(y); + int numWidth = metrics.stringWidth(num); + g.drawString(num, gx - 6 - numWidth, + gy + height*magnification - yy); + } + // draw a dark frame around the runner + g.setColor(Color.black); + g.drawRect(gx-1, gy-1, width*magnification+1, height*magnification+1); + } + } + + + public boolean updateMouse(Event e, int x, int y) { + super.updateMouse(e, x, y); + + if (e.controlDown() && (mouse[2] == 100)) { + editor.doSaveTiff(); + } + + if (e.shiftDown()) { + editor.highlightLine(getLine(x, height1 - y)); + } + + return true; + } +} + + +#endif diff --git a/processing/app/PdeEditorListener.java b/processing/app/PdeEditorListener.java new file mode 100644 index 000000000..03e179dd7 --- /dev/null +++ b/processing/app/PdeEditorListener.java @@ -0,0 +1,144 @@ +#ifdef EDITOR + +import java.awt.*; +import java.awt.event.*; + + +public class PdeEditorListener extends KeyAdapter implements FocusListener { + static final String spaces = " "; + String tabString; + String newline = System.getProperty("line.separator"); + + boolean expandTabs; + int tabSize; + boolean autoIndent; + + boolean balanceParens; + boolean balancing = false; + TextArea tc; + int selectionStart, selectionEnd; + int position; + + + public PdeEditorListener() { + expandTabs = PdeApplet.getBoolean("editor.expandTabs", false); + tabSize = PdeApplet.getInteger("editor.tabSize", 2); + tabString = spaces.substring(0, tabSize); + autoIndent = PdeApplet.getBoolean("editor.autoIndent", false); + balanceParens = PdeApplet.getBoolean("editor.balanceParens", false); + } + + + public void keyPressed(KeyEvent event) { + // only works with TextArea, because it needs 'insert' + //TextComponent tc = (TextComponent) event.getSource(); + tc = (TextArea) event.getSource(); + deselect(); + char c = event.getKeyChar(); + + //System.err.println((int)c); + switch ((int) c) { + case ')': + if (balanceParens) { + position = tc.getCaretPosition() + 1; + char contents[] = tc.getText().toCharArray(); + int counter = 1; // char not in the textfield yet + //int index = contents.length-1; + int index = tc.getCaretPosition() - 1; + boolean error = false; + if (index == -1) { // special case for first char + counter = 0; + error = true; + } + while (counter != 0) { + if (contents[index] == ')') counter++; + if (contents[index] == '(') counter--; + index--; + if ((index == -1) && (counter != 0)) { + error = true; + break; + } + } + if (error) { + //System.err.println("mismatched paren"); + Toolkit.getDefaultToolkit().beep(); + tc.select(0, 0); + tc.setCaretPosition(position); + } + tc.insert(")", position-1); + event.consume(); + if (!error) { + selectionStart = index+1; + selectionEnd = index+2; + tc.select(selectionStart, selectionEnd); + balancing = true; + } + } + break; + + case 9: // expand tabs + if (expandTabs) { + //System.out.println("start = " + tc.getSelectionStart()); + //System.out.println("end = " + tc.getSelectionEnd()); + //System.out.println("pos = " + tc.getCaretPosition()); + tc.replaceRange(tabString, tc.getSelectionStart(), + tc.getSelectionEnd()); + event.consume(); + } + break; + + case 10: // auto-indent + if (autoIndent) { + //System.err.println("auto indenting"); + char contents[] = tc.getText().toCharArray(); + // back up until \r \r\n or \n.. @#($* cross platform + //index = contents.length-1; + int index = tc.getCaretPosition() - 1; + int spaceCount = 0; + boolean finished = false; + while ((index != -1) && (!finished)) { + if ((contents[index] == '\r') || + (contents[index] == '\n')) { + finished = true; + } else { + spaceCount = (contents[index] == ' ') ? + (spaceCount + 1) : 0; + } + index--; + } + + // !@#$@#$ MS VM doesn't move the caret position to the + // end of an insertion after it happens, even though sun does + String insertion = newline + spaces.substring(0, spaceCount); + int oldCarrot = tc.getSelectionStart(); + tc.replaceRange(insertion, oldCarrot, tc.getSelectionEnd()); + tc.setCaretPosition(oldCarrot + insertion.length() - 1); + event.consume(); + } + break; + + case 1: tc.selectAll(); break; // control a for select all + } + } + + + protected void deselect() { + if (!balancing || (tc == null)) return; + // bounce back, otherwise will write over stuff + if ((selectionStart == tc.getSelectionStart()) && + (selectionEnd == tc.getSelectionEnd())) + tc.setCaretPosition(position); + balancing = false; + } + + + public void focusGained(FocusEvent event) { } + + + public void focusLost(FocusEvent event) { + deselect(); + } +} + + +#endif diff --git a/processing/app/PdeEngine.java b/processing/app/PdeEngine.java new file mode 100644 index 000000000..7852a7973 --- /dev/null +++ b/processing/app/PdeEngine.java @@ -0,0 +1,4 @@ +public interface PdeEngine { + public void start() throws PdeException; + public void stop(); +} diff --git a/processing/app/PdeEnvironment.java b/processing/app/PdeEnvironment.java new file mode 100644 index 000000000..578f4e483 --- /dev/null +++ b/processing/app/PdeEnvironment.java @@ -0,0 +1,13 @@ +public interface PdeEnvironment { + // stop the currently running thread (called by gui and others) + public void terminate(); + + // error being reported to gui (called by dbn) + public void error(PdeException e); + + // successful finish reported to gui (called by dbn) + public void finished(); + + // message to write to gui (called by dbn) + public void message(String msg); +} diff --git a/processing/app/PdeException.java b/processing/app/PdeException.java new file mode 100644 index 000000000..32a726ffb --- /dev/null +++ b/processing/app/PdeException.java @@ -0,0 +1,23 @@ +public class PdeException extends Exception { + int line = -1; + + public PdeException() { } + + public PdeException(String message) { + super(message); + } + + public PdeException(String message, int line) { + super(message); + this.line = line; + } + +#ifndef PLAYER +#ifdef DBN + public PdeException(String message, DbnToken token) { + super(message + ", token: " + token.toString()); + } +#endif +#endif +} + diff --git a/processing/app/PdeKeyListener.java b/processing/app/PdeKeyListener.java new file mode 100644 index 000000000..b9d9fd916 --- /dev/null +++ b/processing/app/PdeKeyListener.java @@ -0,0 +1,31 @@ +#ifdef EDITOR + + +import java.awt.*; +import java.awt.event.*; + + +public class PdeKeyListener extends KeyAdapter { + PdeEditor editor; + + public PdeKeyListener(PdeEditor editor) { + this.editor = editor; + } + + public void keyPressed(KeyEvent event) { + switch ((int) event.getKeyChar()) { + case 2: editor.doBeautify(); break; // control b for beautify + case 15: editor.doOpen(); break; // control o for open + case 16: editor.doPrint(); break; // control p for print + case 18: editor.doPlay(); break; // control r for run + case 19: editor.doSave(); break; // control s for save + case 20: editor.doSnapshot(); break; // control t for snapshot + // escape only works from the runpanel, because that's + // who's getting all the key events while running + //case 27: editor.terminate(); break; // escape to stop + } + } +} + + +#endif diff --git a/processing/app/PdeRunner.java b/processing/app/PdeRunner.java new file mode 100644 index 000000000..6ab315bc5 --- /dev/null +++ b/processing/app/PdeRunner.java @@ -0,0 +1,145 @@ +public class PdeRunner implements Runnable { + //DbnGraphics graphics; + PdeEnvironment env; + String program; + + PdeEngine engine; + // dbn definitely needs an engine, + // for the others it's just an interface + + static final int RUNNER_STARTED = 0; + static final int RUNNER_FINISHED = 1; + static final int RUNNER_ERROR = -1; + static final int RUNNER_STOPPED = 2; + int state = RUNNER_FINISHED; + + Thread thread; + boolean forceStop; + + + public PdeRunner(PdeEnvironment env) { + this(env, ""); + } + + public PdeRunner(PdeEnvironment env, String program) { + this.program = program; + //this.graphics = graphics; + this.env = env; + } + + + public void setProgram(String program) { + this.program = program; + } + + + public void start() { + if (thread != null) { + try { + thread.stop(); + } catch (Exception e) { } + thread = null; + } + thread = new Thread(this, "PdeRunner"); + thread.start(); + } + + + public void run() { + state = RUNNER_STARTED; + //graphics.reset(); // remove for pde + + try { + if (program.length() == 0) { + + /* + } else if (program.indexOf('#') < 2) { //charAt(0) == '#') { +#ifdef PYTHON + +#ifdef OPENGL + program = "#\r\n" + + "import DbnEditorGraphics3D\r\n" + + "import ExperimentalCanvas\r\n" + + "g = DbnEditorGraphics3D.getCurrentGraphics()\r\n" + + "glc = g.canvas\r\n" + + "gl = glc.getGL()\r\n" + + "glj = glc.getGLJ()\r\n" + program; +#endif + + forceStop = true; + engine = new PythonEngine(program); + engine.start(); + forceStop = false; +#else + throw new Exception("python support not included"); +#endif + */ + + } else if (program.indexOf("extends ProcessingApplet") != -1) { +#ifdef JAVAC + engine = new JavacEngine(program, graphics); + engine.start(); +#else + throw new Exception("javac support not included"); +#endif + + } else if (program.indexOf("// dbn") < 2) { +#ifdef DBN + String pre = "set red 0; set green 1; set blue 2; " + + "set quicktime 0; set tiff 1; set illustrator 2; "; + DbnParser parser = + new DbnParser(DbnPreprocessor.process(pre + program)); + + DbnToken root = parser.getRoot(); + //root.print(); + if (!root.findToken(DbnToken.SIZE)) { + graphics.size(101, 101, 1); + } + if (root.findToken(DbnToken.REFRESH)) { + graphics.aiRefresh = false; + } + engine = new DbnEngine(root, graphics); + engine.start(); +#else + throw new Exception("dbn support not included"); +#endif + + } else { + forceStop = true; + engine = new PythonEngine(program); + engine.start(); + forceStop = false; + + + } + //System.out.println("finished"); + state = RUNNER_FINISHED; + env.finished(); + //graphics.update(); // removed for pde + + } catch (PdeException e) { + state = RUNNER_ERROR; + forceStop = false; + this.stop(); + env.error(e); + + } catch (Exception e) { +#ifndef KVM + e.printStackTrace(); +#endif + this.stop(); + } + } + + + public void stop() { + if (engine != null) { + engine.stop(); + if (forceStop) { + thread.stop(); + thread = null; + } + engine = null; + } + } +} diff --git a/processing/app/ProcessingApplet.java b/processing/app/ProcessingApplet.java new file mode 100644 index 000000000..34a4470bd --- /dev/null +++ b/processing/app/ProcessingApplet.java @@ -0,0 +1,280 @@ +import java.applet.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; + + +public class ProcessingApplet extends Applet + implements BagelConstants, Runnable, + MouseListener, MouseMotionListener, KeyListener { + Bagel g; + + int mouseX, mouseY; + boolean mousePressed; + boolean mousePressedBriefly; + + int key; + boolean keyPressed; + boolean keyPressedBriefly; + + boolean timing; + int millis; + long actualMillis; + long millisOffset; + Calendar calendar; + + boolean drawMethod; + boolean loopMethod; + + boolean finished; + boolean drawn; + Thread thread; + + int width, height; + + + public void init() { + addMouse(); // set basic params + addKeyboard(); + addTime(); + + finished = false; // just for clarity + drawn = false; + + // this will be cleared by loop() if it is not overridden + drawMethod = true; + loopMethod = true; + + // call setup for changed params + setup(); + + // do actual setup calls + if (g == null) { + // if programmer hasn't added a special graphics + // object, then setup a standard 320x240 one + size(320, 240); + } + } + + + // ------------------------------------------------------------ + + + public void setup() { + } + + + public void draw() { + drawMethod = false; + } + + + public void loop() { + loopMethod = false; + } + + + // ------------------------------------------------------------ + + + // this is where screen grab could attach itself + public void update() { + paint(this.getGraphics()); + } + + public void update(Graphics screen) { + paint(screen); + } + + public void paint(Graphics screen) { + if (thread == null) { + // kickstart my heart + thread = new Thread(this); + thread.start(); + + } else { + screen.drawImage(g.image, 0, 0, null); + } + } + + + public void run() { + while ((Thread.currentThread() == thread) && !finished) { + + // setup + if (timing) { + actualMillis = System.currentTimeMillis(); + calendar = null; + } + + // attempt to draw a static image using draw() + if (!drawn) { + // always do this once. empty if not overridden + g.beginFrame(); + draw(); + if (!drawMethod) { + // that frame was bogus, mark it as such + // before ending the frame so that it doesn't get + // saved to a quicktime movie or whatever + + // might be as simple as not calling endFrame? + } + if (drawMethod) { + g.endFrame(); + update(); + finished = true; + } + drawn = true; + } + + // if not a static app, run the loop + if (!drawMethod) { + g.beginFrame(); + loop(); + g.endFrame(); + update(); + } + + // takedown + if (!loopMethod) { + finished = true; + } + + if (mousePressedBriefly) { + mousePressedBriefly = false; + mousePressed = false; + } + + if (keyPressedBriefly) { + keyPressedBriefly = false; + keyPressed = false; + } + + // sleep to make OS happy + try { + thread.sleep(5); + } catch (InterruptedException e) { } + } + } + + + // ------------------------------------------------------------ + + + public void size(int width, int height) { + if (g != null) return; // would this ever happen? + + this.width = width; + this.height = height; + + g = new Bagel(width, height); + } + + + // ------------------------------------------------------------ + + + public void addMouse() { + addMouseListener(this); + addMouseMotionListener(this); + } + + public void mouseClicked(MouseEvent e) { + mousePressedBriefly = true; + mousePressed = true; + } + + public void mousePressed(MouseEvent e) { + mousePressedBriefly = false; + mousePressed = true; + } + + public void mouseReleased(MouseEvent e) { + mousePressed = false; + } + + public void mouseEntered(MouseEvent e) { } + + public void mouseExited(MouseEvent e) { } + + public void mouseDragged(MouseEvent e) { + mouseX = e.getX(); mouseY = e.getY(); + mousePressed = true; + } + + public void mouseMoved(MouseEvent e) { + mouseX = e.getX(); mouseY = e.getY(); + mousePressed = false; + } + + + // ------------------------------------------------------------ + + + public void addKeyboard() { + addKeyListener(this); + } + + public void keyTyped(KeyEvent e) { + keyPressed = true; + keyPressedBriefly = true; + key = e.getKeyChar(); + } + + public void keyPressed(KeyEvent e) { + keyPressed = true; + keyPressedBriefly = false; + key = e.getKeyChar(); + } + + public void keyReleased(KeyEvent e) { + keyPressed = false; + key = e.getKeyChar(); + } + + + // ------------------------------------------------------------ + + + public void addTime() { + timing = true; + //calendar = Calendar.getInstance(); + //calendar.setTimeZone(TimeZone.getDefault()); + millisOffset = System.currentTimeMillis(); + } + + // at the expense of dealing with longs.. hmm.. + public int getMillis() { + return (int) (actualMillis - millisOffset); + } + + public int getSecond() { + //calendar.setTimeInMillis(actualMillis); + if (calendar == null) calendar = Calendar.getInstance(); + return calendar.get(Calendar.SECOND); + } + + public int getMinute() { + if (calendar == null) calendar = Calendar.getInstance(); + //calendar.setTimeInMillis(actualMillis); + return calendar.get(Calendar.MINUTE); + } + + public int getHour() { + if (calendar == null) calendar = Calendar.getInstance(); + //calendar.setTimeInMillis(actualMillis); + return calendar.get(Calendar.HOUR_OF_DAY); + } + + public int getMonth() { + if (calendar == null) calendar = Calendar.getInstance(); + //calendar.setTimeInMillis(actualMillis); + return calendar.get(Calendar.MONTH); + } + + public int getYear() { + if (calendar == null) calendar = Calendar.getInstance(); + //calendar.setTimeInMillis(actualMillis); + return calendar.get(Calendar.YEAR); + } +} diff --git a/processing/app/buzz.pl b/processing/app/buzz.pl new file mode 100644 index 000000000..5e8935191 --- /dev/null +++ b/processing/app/buzz.pl @@ -0,0 +1,217 @@ +#!/perl/bin/perl + +# needs to make a temporary directory, compile into that +# clear out contents of temporary directory at begin of compile +# create temp directory if it doesn't exist + +# should take arguments for the compiler: +# jikes -d classes *.java +# instead looks like +# buzz "jikes -d classes" *.java +# maybe everything always goes in /tmp? +# (no, don't want to leave code around) + +$blank_line = "\n"; + +$temp_dir = "buzztemp"; +if (-d $temp_dir) { + `rm -rf $temp_dir`; +} +mkdir($temp_dir, 0777) || die $!; + +if ($ENV{'WINDIR'} ne '') { + $separator = "\\"; + $windows = 1; +} else { + $separator = '/'; + $unix = 1; +} + +#print "args = @ARGV\n"; +$command = shift(@ARGV); +if ($command eq '') { + print "buzz.pl: perl is misconfigured.. no args passed in.. can't run\n"; + print " cygwin perl seems to have problems, use activestate\n"; + exit; +} + +if ($command =~ /-classpath/) { + die "cannot set classpath using this version of buzz"; +} +$classpath = $ENV{"CLASSPATH"}; +if ($classpath eq "") { + # find java in the path + if ($windows) { + @elements = split(';', $ENV{"PATH"}); + foreach $element (@elements) { + #print "trying $element\\java.exe\n"; + if (-f "$element\\java.exe") { + $classpath = "$element\\..\\lib\\classes.zip"; + print "found java: $element\\java.exe\n"; + last; + } + } + if ($classpath eq "") { + die "java.exe is not in your path, and classpath not set"; + } + } else { + die "code for searching path not written for unix"; + } +} + + +# if target directory, -d, option is used, add it to CLASSPATH +if ($command =~ /\-d\s(\S*)/) { + if ($windows) { + $classpath = "$1;$classpath"; + } else { + $classpath = "$1:$classpath"; + } +} + +foreach $arg (@ARGV) { + if ($arg =~ /^-d(.*)/) { + $params{$1} = 1; + #} elsif ($arg =~/^-c(.*)/) { + #$compiler = $1; + } elsif ($arg =~ /\.java$/) { + if ($arg =~ /(.*)\*\.java$/) { + # gotta expand * to all matching + #print "expanding *.java from \"$1\"\n"; + $dir = $1; + if ($dir eq "") { + $dir = '.'; + } else { + #print "creating dir $temp_dir$separator$dir\n"; + mkdir("$temp_dir$separator$dir", 0777) || die $!; + } + opendir(DIR, $dir) || die $!; + @dcontents = readdir(DIR); + closedir(DIR); + foreach $file (@dcontents) { + if ($file =~ /\.java$/) { + if ($dir eq '.') { + $fullname = "$file"; + } else { + $fullname = "$dir$file"; + } + #print "adding $fullname\n"; + unshift @file_list, "$fullname"; + } + } + + } else { + unshift @file_list, $arg; + } + } +} + +# support: define, ifdef, ifndef, else, endif +# no support: defined(x), elif, #define blah 12, nesting + +print "processing...\n"; +foreach $file (@file_list) { + open(FILE, "$file") || die "error with $file, $!"; + @contents = ; + close(FILE); + + @new_contents = (); + &read_positive; + + open(OUTPUT, ">$temp_dir$separator$file") || die $!; + print OUTPUT reverse(@new_contents); + close(OUTPUT); + unshift(@new_file_list, "$temp_dir$separator$file"); +} + +print "compiling...\n"; +$files = join(' ', @new_file_list); +$compile_command = "$command -classpath $classpath $files"; +#print "$compile_command\n"; +print `$compile_command`; + +# clean up +print "cleaning...\n"; +`rm -rf $temp_dir`; + +# finished +print "done.\n"; + + +# reads until else or endif, adding what it finds +# to the new output file +sub read_positive { + my $line; + while ($line = shift(@contents)) { + if ($line =~ /$\#if(\w*)def\s+(\S+)/) { + unshift(@new_contents, $blank_line); + if ((($1 eq "") && ($params{$2} == 1)) || #ifdef found + (($1 eq "n") && ($params{$2} != 1))) { #ifndef found + # include until endif/else + &read_positive; + #return; + + } else { + # exclude until endif/else + &read_negative(0); + #return; + } + } elsif ($line =~ /$\#else/) { + unshift(@new_contents, $blank_line); + &read_negative(0); + return; + + } elsif ($line =~ /$\#endif/) { + unshift(@new_contents, $blank_line); + return; + + } elsif ($line =~ /$\#define\s+(\S+)/) { + $params{$1} = 1; + unshift(@new_contents, $blank_line); # maintain lf count + + } else { + unshift(@new_contents, $line); # no change + } + } +} + + +# excludes everything until an else or an endif +sub read_negative { + my ($inside_negative) = @_[0]; + my $line; + while ($line = shift(@contents)) { + if ($line =~ /$\#if(\w*)def\s+(\S+)/) { + unshift(@new_contents, $blank_line); + if ((($1 eq "") && ($params{$2} == 1)) || #ifdef found + (($1 eq "n") && ($params{$2} != 1))) { #ifndef found + #&read_positive; + &read_negative(1); + + } else { + # exclude until endif/else + &read_negative(1); + } + } elsif ($line =~ /$\#else/) { + unshift(@new_contents, $blank_line); + if ($inside_negative) { + &read_negative(1); + } else { + &read_positive; + } + return; + + } elsif ($line =~ /$\#endif/) { + unshift(@new_contents, $blank_line); + return; + + #} elsif ($line =~ /$\#define\s+(\S+)/) { + # unshift(@new_contents, $blank_line); # maintain lf count + + } else { + # blank line, maintain lf count + unshift(@new_contents, $blank_line); + } + } +} +