Initial Arduino IDE based on Processing.

This commit is contained in:
David A. Mellis 2005-08-25 21:06:28 +00:00
commit 9fc5aa63f6
373 changed files with 71081 additions and 0 deletions

1111
app/Base.java Normal file

File diff suppressed because it is too large Load Diff

455
app/Compiler.java Normal file
View File

@ -0,0 +1,455 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Compiler - default compiler class that connects to jikes
Part of the Processing project - http://processing.org
Copyleft 2005 Massimo Banzi (arduino modifications)
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
//import processing.core.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.swing.*;
public class Compiler implements MessageConsumer {
static final String BUGS_URL = "http://arduino.berlios.de";
static final String SUPER_BADNESS = "Compiler error, please submit this code to " + BUGS_URL;
Sketch sketch;
String buildPath;
//String buildPath;
//String className;
//File includeFolder;
RunnerException exception;
//Editor editor;
/*
public Compiler(String buildPath, String className,
File includeFolder, Editor editor) {
this.buildPath = buildPath;
this.includeFolder = includeFolder;
this.className = className;
this.editor = editor;
}
public boolean compile(PrintStream leechErr) {
*/
public Compiler() { } // null constructor
public boolean compile(Sketch sketch, String buildPath)
throws RunnerException {
this.sketch = sketch;
this.buildPath = buildPath;
// the pms object isn't used for anything but storage
MessageStream pms = new MessageStream(this);
String userdir = System.getProperty("user.dir") + File.separator;
System.out.println("Compiling Arduino program");
Process process;
int result = 0;
try {
process = Runtime.getRuntime().exec(userdir + "tools/gnumake -s -C " + userdir + "lib compile");
// System.err.println(userdir + "lib/wiringlite/bin/gnumake -s -C " + userdir + "lib compile");
new MessageSiphon(process.getInputStream(), this);
new MessageSiphon(process.getErrorStream(), this);
boolean compiling = true;
while (compiling) {
try {
result = process.waitFor();
compiling = false;
} catch (InterruptedException ignored) { }
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("Error: GNUMake probably couldn't be found");
result = 99;
}
if(0 == result){
System.out.println("Arduino Compilation Successful");
}else{
System.out.println("Arduino Compilation Unsuccessful (error: " + result + ")");
}
return (result == 0);
}
boolean firstErrorFound;
boolean secondErrorFound;
/**
* Part of the MessageConsumer interface, this is called
* whenever a piece (usually a line) of error message is spewed
* out from the compiler. The errors are parsed for their contents
* and line number, which is then reported back to Editor.
*/
public void message(String s) {
// This receives messages as full lines, so a newline needs
// to be added as they're printed to the console.
System.err.println(s);
// ignore cautions
if (s.indexOf("Caution") != -1) return;
// jikes always uses a forward slash character as its separator,
// so replace any platform-specific separator characters before
// attemping to compare
//
String buildPathSubst = buildPath.replace(File.separatorChar, '/') + "/";
String partialTempPath = null;
int partialStartIndex = -1; //s.indexOf(partialTempPath);
int fileIndex = -1; // use this to build a better exception
// iterate through the project files to see who's causing the trouble
for (int i = 0; i < sketch.codeCount; i++) {
if (sketch.code[i].preprocName == null) continue;
partialTempPath = buildPathSubst + sketch.code[i].preprocName;
partialStartIndex = s.indexOf(partialTempPath);
if (partialStartIndex != -1) {
fileIndex = i;
//System.out.println("fileIndex is " + fileIndex);
break;
}
}
//+ className + ".java";
// if the partial temp path appears in the error message...
//
//int partialStartIndex = s.indexOf(partialTempPath);
if (partialStartIndex != -1) {
// skip past the path and parse the int after the first colon
//
String s1 = s.substring(partialStartIndex +
partialTempPath.length() + 1);
int colon = s1.indexOf(':');
int lineNumber = Integer.parseInt(s1.substring(0, colon));
//System.out.println("pde / line number: " + lineNumber);
if (fileIndex == 0) { // main class, figure out which tab
for (int i = 1; i < sketch.codeCount; i++) {
if (sketch.code[i].flavor == Sketch.PDE) {
if (sketch.code[i].preprocOffset < lineNumber) {
fileIndex = i;
//System.out.println("i'm thinkin file " + i);
}
}
}
if (fileIndex != 0) { // if found another culprit
lineNumber -= sketch.code[fileIndex].preprocOffset;
//System.out.println("i'm sayin line " + lineNumber);
}
}
//String s2 = s1.substring(colon + 2);
int err = s1.indexOf("Error:");
if (err != -1) {
// if the first error has already been found, then this must be
// (at least) the second error found
if (firstErrorFound) {
secondErrorFound = true;
return;
}
// if executing at this point, this is *at least* the first error
firstErrorFound = true;
//err += "error:".length();
String description = s1.substring(err + "Error:".length());
description = description.trim();
String hasLoop = "The method \"void loop();\" with default access";
if (description.indexOf(hasLoop) != -1) {
description =
"Rename loop() to draw() in Processing 0070 and higher";
}
String constructorProblem =
"No applicable overload was found for a constructor of type";
if (description.indexOf(constructorProblem) != -1) {
//"simong.particles.ParticleSystem". Perhaps you wanted the overloaded version "ParticleSystem();" instead?
int nextSentence = description.indexOf("\".") + 3;
description = description.substring(nextSentence);
}
String overloadProblem = "No applicable overload";
if (description.indexOf(overloadProblem) != -1) {
int nextSentence = description.indexOf("\".") + 3;
description = description.substring(nextSentence);
}
// c:/fry/processing/build/windows/work/lib/build/Temporary_6858_2476.java:1:34:1:41: Semantic Error: You need to modify your classpath, sourcepath, bootclasspath, and/or extdirs setup. Package "poo/shoe" could not be found in:
String classpathProblem = "You need to modify your classpath";
if (description.indexOf(classpathProblem) != -1) {
if (description.indexOf("quicktime/std") != -1) {
// special case for the quicktime libraries
description =
"To run sketches that use the Processing video library, " +
"you must first install QuickTime for Java.";
} else {
int nextSentence = description.indexOf(". Package") + 2;
description =
description.substring(nextSentence, description.indexOf(':')) +
" the code folder or in any libraries.";
}
}
//System.out.println("description = " + description);
//System.out.println("creating exception " + exception);
exception = new RunnerException(description, fileIndex, lineNumber-1, -1);
// NOTE!! major change here, this exception will be queued
// here to be thrown by the compile() function
//editor.error(exception);
} else {
System.err.println("i suck: " + s);
}
} else {
// this isn't the start of an error line, so don't attempt to parse
// a line number out of it.
// if the second error hasn't been discovered yet, these lines
// are probably associated with the first error message,
// which is already in the status bar, and are likely to be
// of interest to the user, so spit them to the console.
//
if (!secondErrorFound) {
System.err.println(s);
}
}
}
static String bootClassPath;
static public String calcBootClassPath() {
if (bootClassPath == null) {
String additional = "";
if (Base.isMacOS()) {
additional =
contentsToClassPath(new File("/System/Library/Java/Extensions/"));
}
bootClassPath = System.getProperty("sun.boot.class.path") + additional;
}
return bootClassPath;
}
///
/**
* Return the path for a folder, with appended paths to
* any .jar or .zip files inside that folder.
* This will prepend a colon (or whatever the path separator is)
* so that it can be directly appended to another path string.
*
* This will always add the root folder as well, and doesn't bother
* checking to see if there are any .class files in the folder or
* within a subfolder.
*/
static public String contentsToClassPath(File folder) {
if (folder == null) return "";
StringBuffer abuffer = new StringBuffer();
String sep = System.getProperty("path.separator");
try {
// add the folder itself in case any unzipped files
String path = folder.getCanonicalPath();
abuffer.append(sep);
abuffer.append(path);
if (!path.endsWith(File.separator)) {
path += File.separator;
}
//System.out.println("path is " + path);
String list[] = folder.list();
for (int i = 0; i < list.length; i++) {
if (list[i].toLowerCase().endsWith(".jar") ||
list[i].toLowerCase().endsWith(".zip")) {
abuffer.append(sep);
abuffer.append(path);
abuffer.append(list[i]);
}
}
} catch (IOException e) {
e.printStackTrace(); // this would be odd
}
//System.out.println("included path is " + abuffer.toString());
//packageListFromClassPath(abuffer.toString()); // WHY?
return abuffer.toString();
}
/**
* A classpath, separated by the path separator, will contain
* a series of .jar/.zip files or directories containing .class
* files, or containing subdirectories that have .class files.
*
* @param path the input classpath
* @return array of possible package names
*/
static public String[] packageListFromClassPath(String path) {
Hashtable table = new Hashtable();
String pieces[] =
Base.split(path, File.pathSeparatorChar);
for (int i = 0; i < pieces.length; i++) {
//System.out.println("checking piece '" + pieces[i] + "'");
if (pieces[i].length() == 0) continue;
if (pieces[i].toLowerCase().endsWith(".jar") ||
pieces[i].toLowerCase().endsWith(".zip")) {
packageListFromZip(pieces[i], table);
} else { // it's another type of file or directory
File dir = new File(pieces[i]);
if (dir.exists() && dir.isDirectory()) {
packageListFromFolder(dir, null, table);
//importCount = magicImportsRecursive(dir, null,
// table);
//imports, importCount);
}
}
}
int tableCount = table.size();
String output[] = new String[tableCount];
int index = 0;
Enumeration e = table.keys();
while (e.hasMoreElements()) {
output[index++] = ((String) e.nextElement()).replace('/', '.');
}
//System.arraycopy(imports, 0, output, 0, importCount);
//PApplet.printarr(output);
return output;
}
static private void packageListFromZip(String filename, Hashtable table) {
try {
ZipFile file = new ZipFile(filename);
Enumeration entries = file.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
if (!entry.isDirectory()) {
String name = entry.getName();
if (name.endsWith(".class")) {
int slash = name.lastIndexOf('/');
if (slash == -1) continue;
String pname = name.substring(0, slash);
if (table.get(pname) == null) {
table.put(pname, new Object());
}
}
}
}
} catch (IOException e) {
System.err.println("Ignoring " + filename + " (" + e.getMessage() + ")");
//e.printStackTrace();
}
}
/**
* Make list of package names by traversing a directory hierarchy.
* Each time a class is found in a folder, add its containing set
* of folders to the package list. If another folder is found,
* walk down into that folder and continue.
*/
static private void packageListFromFolder(File dir, String sofar,
Hashtable table) {
//String imports[],
//int importCount) {
//System.err.println("checking dir '" + dir + "'");
boolean foundClass = false;
String files[] = dir.list();
for (int i = 0; i < files.length; i++) {
if (files[i].equals(".") || files[i].equals("..")) continue;
File sub = new File(dir, files[i]);
if (sub.isDirectory()) {
String nowfar =
(sofar == null) ? files[i] : (sofar + "." + files[i]);
packageListFromFolder(sub, nowfar, table);
//System.out.println(nowfar);
//imports[importCount++] = nowfar;
//importCount = magicImportsRecursive(sub, nowfar,
// imports, importCount);
} else if (!foundClass) { // if no classes found in this folder yet
if (files[i].endsWith(".class")) {
//System.out.println("unique class: " + files[i] + " for " + sofar);
table.put(sofar, new Object());
foundClass = true;
}
}
}
//return importCount;
}
/*
static public int magicImportsRecursive(File dir, String sofar,
Hashtable table) {
//String imports[],
//int importCount) {
System.err.println("checking dir '" + dir + "'");
String files[] = dir.list();
for (int i = 0; i < files.length; i++) {
if (files[i].equals(".") || files[i].equals("..")) continue;
File sub = new File(dir, files[i]);
if (sub.isDirectory()) {
String nowfar = (sofar == null) ?
files[i] : (sofar + "." + files[i]);
//System.out.println(nowfar);
imports[importCount++] = nowfar;
importCount = magicImportsRecursive(sub, nowfar,
imports, importCount);
}
}
return importCount;
}
*/
}

268
app/Downloader.java Executable file
View File

@ -0,0 +1,268 @@
/*
Downloader - default downloader class that connects to uisp
Part of the Arduino project http://arduino.berlios.de
Based on PdeDownloader by
Copyright (c) 2005
Hernando Barragan, Interaction Design Institute Ivrea
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.swing.*;
import gnu.io.*;
public class Downloader implements MessageConsumer {
static final String BUGS_URL =
"http://arduino.berlios.de";
static final String SUPER_BADNESS =
"Compiler error, please submit this code to " + BUGS_URL;
String buildPath;
String className;
File includeFolder;
RunnerException exception;
Sketch sketch;
//Preferences preferences;
//static SerialPort serialPort;
static InputStream serialInput;
static OutputStream serialOutput;
//int serial; // last byte of data received
private String serial_port = "COM1";
private int serial_rate = 9600;
private char serial_parity = 'N';
private int serial_databits = 8;
private float serial_stopbits = 1;
public void serialPreferences() {
// System.out.println("setting serial properties");
serial_port = Preferences.get("serial.port");
//serial_rate = 9600; //Preferences.getInteger("serial.download_rate");
//serial_parity = Preferences.get("serial.parity").charAt(0);
//serial_databits = Preferences.getInteger("serial.databits");
//serial_stopbits = new Float(Preferences.get("serial.stopbits")).floatValue();
}
public Downloader() {
}
private boolean downloadMake(String userdir) {
System.out.println("Downloading - makefile");
Process process;
int result = 0;
try {
serialPreferences();
String command = userdir + "tools/gnumake SERIAL=" + serial_port + " -C " + userdir + "lib program";
System.out.println(command);
process = Runtime.getRuntime().exec(command);
new MessageSiphon(process.getInputStream(), this);
new MessageSiphon(process.getErrorStream(), this);
boolean compiling = true;
while (compiling) {
try {
result = process.waitFor();
compiling = false;
} catch (InterruptedException ignored) { }
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("Error: GNUMake probably couldn't be found");
result = 99;
}
if(0 == result){
System.out.println("Arduino Download Successful");
}else{
System.out.println("Arduino Download Unsuccessful (error: " + result + ")");
}
return (result == 0);
}
public boolean downloadJava() {
String userdir = System.getProperty("user.dir") + File.separator;
return downloadMake(userdir);
}
public boolean downloadNative(String userdir) {
System.out.println("Downloading - native");
String commandDownloader[] = new String[] {
(( Base.isMacOS()) ? "tools/avr/bin/uisp" :
userdir + "tools/avr/bin/uisp"),
//[2] Serial port
//[6] hex class file
"-dprog=stk500",
" ",
"-dspeed=9600",
"-dpart=atmega8",
"--upload",
" "
};
firstErrorFound = false; // haven't found any errors yet
secondErrorFound = false;
int result=0; // pre-initialized to quiet a bogus warning from gcc
try {
serialPreferences();
commandDownloader[2] = ((Base.isMacOS()) ? "-dserial=" + serial_port.toLowerCase() : "-dserial=" + serial_port );
commandDownloader[6] = "if=" + buildPath + File.separator + className + ".hex";
// for(int i = 0; i < commandDownloader.length; i++) {
// System.out.println(commandDownloader[i]);
// }
Process process = Runtime.getRuntime().exec(commandDownloader);
new MessageSiphon(process.getInputStream(), this);
new MessageSiphon(process.getErrorStream(), this);
// wait for the process to finish. if interrupted
// before waitFor returns, continue waiting
//
boolean compiling = true;
while (compiling) {
try {
result = process.waitFor();
compiling = false;
} catch (InterruptedException intExc) {
}
}
} catch (Exception e) {
String msg = e.getMessage();
if ((msg != null) && (msg.indexOf("uisp: not found") != -1)) {
//System.err.println("gcc is missing");
//JOptionPane.showMessageDialog(editor.base,
// "Could not find the downloader.\n" +
// "uisp is missing from your PATH,\n" +
// "see readme.txt for help.",
// "Downloade error",
// JOptionPane.ERROR_MESSAGE);
return false;
}
e.printStackTrace();
result = -1;
}
// if the result isn't a known, expected value it means that something
// is fairly wrong, one possibility is that gcc has crashed.
//
if (result != 0 && result != 1 ) {
exception = new RunnerException(SUPER_BADNESS);
//editor.error(exception);
//Base.openURL(BUGS_URL);
}
return (result == 0) ? true : false;
}
boolean firstErrorFound;
boolean secondErrorFound;
// part of the PdeMessageConsumer interface
//
public void message(String s) {
//System.err.println("MSG: " + s);
//System.err.print(s);
// ignore cautions
if (s.indexOf("Caution") != -1) return;
// gcc always uses a forward slash character as its separator, so
// we need to replace any platform-specific separator characters before
// attemping to compare
//
String partialTempPath = buildPath.replace(File.separatorChar, '/')
+ "/" + className + ".c";
// if the partial temp path appears in the error message...
//
int partialStartIndex = s.indexOf(partialTempPath);
//System.out.println(partialStartIndex);
if (partialStartIndex != -1) {
// skip past the path and parse the int after the first colon
//
String s1 = s.substring(partialStartIndex + partialTempPath.length()
+ 1);
int colon = s1.indexOf(':');
int lineNumber = Integer.parseInt(s1.substring(0, colon));
//System.out.println("pde / line number: " + lineNumber);
//String s2 = s1.substring(colon + 2);
int err = s1.indexOf("Error:");
if (err != -1) {
// if the first error has already been found, then this must be
// (at least) the second error found
if (firstErrorFound) {
secondErrorFound = true;
return;
}
// if we're here at all, this is at least the first error
firstErrorFound = true;
//err += "error:".length();
String description = s1.substring(err + "Error:".length());
description = description.trim();
//System.out.println("description = " + description);
exception = new RunnerException(description, lineNumber-1);
//editor.error(exception);
} else {
System.err.println("i suck: " + s);
}
} else {
// this isn't the start of an error line, so don't attempt to parse
// a line number out of it.
// if we're not yet at the second error, these lines are probably
// associated with the first error message, which is already in the
// status bar, and are likely to be of interest to the user, so
// spit them to the console.
//
if (!secondErrorFound) {
System.err.println(s);
}
}
}
}

1903
app/Editor.java Normal file

File diff suppressed because it is too large Load Diff

404
app/EditorButtons.java Normal file
View File

@ -0,0 +1,404 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;
/**
* run/stop/etc buttons for the ide
*/
public class EditorButtons extends JComponent implements MouseInputListener {
static final String title[] = {
"Compile", "Stop", "New", "Open", "Save", "Export"
};
static final int BUTTON_COUNT = title.length;
static final int BUTTON_WIDTH = 27; //Preferences.GRID_SIZE;
static final int BUTTON_HEIGHT = 32; //Preferences.GRID_SIZE;
static final int BUTTON_GAP = 15; //BUTTON_WIDTH / 2;
static final int RUN = 0;
static final int STOP = 1;
static final int NEW = 2;
static final int OPEN = 3;
static final int SAVE = 4;
static final int EXPORT = 5;
static final int INACTIVE = 0;
static final int ROLLOVER = 1;
static final int ACTIVE = 2;
Editor editor;
boolean disableRun;
//Label status;
Image offscreen;
int width, height;
Color bgcolor;
Image buttons;
Image inactive[];
Image rollover[];
Image active[];
int currentRollover;
int currentSelection;
JPopupMenu popup;
int buttonCount;
int state[] = new int[BUTTON_COUNT];
Image stateImage[];
int which[]; // mapping indices to implementation
int x1[], x2[];
int y1, y2;
String status;
Font statusFont;
Color statusColor;
//int statusY;
public EditorButtons(Editor editor) {
this.editor = editor;
buttons = Base.getImage("buttons.gif", this);
buttonCount = 0;
which = new int[BUTTON_COUNT];
//which[buttonCount++] = NOTHING;
which[buttonCount++] = RUN;
which[buttonCount++] = STOP;
which[buttonCount++] = NEW;
which[buttonCount++] = OPEN;
which[buttonCount++] = SAVE;
which[buttonCount++] = EXPORT;
currentRollover = -1;
bgcolor = Preferences.getColor("buttons.bgcolor");
status = "";
statusFont = Preferences.getFont("buttons.status.font");
statusColor = Preferences.getColor("buttons.status.color");
//statusY = (BUTTON_COUNT + 1) * BUTTON_HEIGHT;
addMouseListener(this);
addMouseMotionListener(this);
}
public void paintComponent(Graphics screen) {
if (inactive == null) {
inactive = new Image[BUTTON_COUNT];
rollover = new Image[BUTTON_COUNT];
active = new Image[BUTTON_COUNT];
int IMAGE_SIZE = 33;
for (int i = 0; i < BUTTON_COUNT; i++) {
inactive[i] = createImage(BUTTON_WIDTH, BUTTON_HEIGHT);
Graphics g = inactive[i].getGraphics();
g.drawImage(buttons, -(i*IMAGE_SIZE) - 3, -2*IMAGE_SIZE, null);
rollover[i] = createImage(BUTTON_WIDTH, BUTTON_HEIGHT);
g = rollover[i].getGraphics();
g.drawImage(buttons, -(i*IMAGE_SIZE) - 3, -1*IMAGE_SIZE, null);
active[i] = createImage(BUTTON_WIDTH, BUTTON_HEIGHT);
g = active[i].getGraphics();
g.drawImage(buttons, -(i*IMAGE_SIZE) - 3, -0*IMAGE_SIZE, 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;
y1 = 0;
y2 = BUTTON_HEIGHT;
x1 = new int[buttonCount];
x2 = new int[buttonCount];
int offsetX = 3;
for (int i = 0; i < buttonCount; i++) {
x1[i] = offsetX;
if (i == 2) x1[i] += BUTTON_GAP;
x2[i] = x1[i] + BUTTON_WIDTH;
offsetX = x2[i];
}
}
Graphics g = offscreen.getGraphics();
g.setColor(bgcolor); //getBackground());
g.fillRect(0, 0, width, height);
for (int i = 0; i < buttonCount; i++) {
g.drawImage(stateImage[i], x1[i], y1, null);
}
g.setColor(statusColor);
g.setFont(statusFont);
/*
// if i ever find the guy who wrote the java2d api, i will hurt him.
Graphics2D g2 = (Graphics2D) g;
FontRenderContext frc = g2.getFontRenderContext();
float statusW = (float) statusFont.getStringBounds(status, frc).getWidth();
float statusX = (getSize().width - statusW) / 2;
g2.drawString(status, statusX, statusY);
*/
//int statusY = (BUTTON_HEIGHT + statusFont.getAscent()) / 2;
int statusY = (BUTTON_HEIGHT + g.getFontMetrics().getAscent()) / 2;
g.drawString(status, buttonCount * BUTTON_WIDTH + 2 * BUTTON_GAP, statusY);
screen.drawImage(offscreen, 0, 0, null);
}
public void mouseMoved(MouseEvent e) {
// mouse events before paint();
if (state == null) return;
if (state[OPEN] != INACTIVE) {
// avoid flicker, since there will probably be an update event
setState(OPEN, INACTIVE, false);
}
//System.out.println(e);
//mouseMove(e);
handleMouse(e.getX(), e.getY());
}
public void mouseDragged(MouseEvent e) { }
public void handleMouse(int x, int y) {
if (currentRollover != -1) {
if ((x > x1[currentRollover]) && (y > y1) &&
(x < x2[currentRollover]) && (y < y2)) {
return;
} else {
setState(currentRollover, INACTIVE, true);
messageClear(title[currentRollover]);
currentRollover = -1;
}
}
int sel = findSelection(x, y);
if (sel == -1) return;
if (state[sel] != ACTIVE) {
if (!(disableRun && ((sel == RUN) || (sel == STOP)))) {
setState(sel, ROLLOVER, true);
currentRollover = sel;
}
}
}
private int findSelection(int x, int y) {
// if app loads slowly and cursor is near the buttons
// when it comes up, the app may not have time to load
if ((x1 == null) || (x2 == null)) return -1;
for (int i = 0; i < buttonCount; i++) {
if ((y > y1) && (x > x1[i]) &&
(y < y2) && (x < x2[i])) {
//System.out.println("sel is " + i);
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]];
message(title[which[slot]]);
break;
}
if (updateAfter) repaint(); // changed for swing from update();
}
public void mouseEntered(MouseEvent e) {
//mouseMove(e);
handleMouse(e.getX(), e.getY());
}
public void mouseExited(MouseEvent e) {
if (state[OPEN] != INACTIVE) {
setState(OPEN, INACTIVE, true);
}
status = "";
handleMouse(e.getX(), e.getY());
}
int wasDown = -1;
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
int sel = findSelection(x, y);
///if (sel == -1) return false;
if (sel == -1) return;
currentRollover = -1;
currentSelection = sel;
if (!(disableRun && ((sel == RUN) || (sel == STOP)))) {
setState(sel, ACTIVE, true);
}
if (currentSelection == OPEN) {
if (popup == null) {
//popup = new JPopupMenu();
popup = editor.sketchbook.getPopupMenu();
add(popup);
}
//editor.sketchbook.rebuildPopup(popup);
popup.show(this, x, y);
}
}
public void mouseClicked(MouseEvent e) { }
public void mouseReleased(MouseEvent e) {
switch (currentSelection) {
case RUN:
if (!disableRun) {
editor.handleRun(e.isShiftDown());
}
break;
case STOP:
if (!disableRun) {
setState(RUN, INACTIVE, true);
editor.handleStop();
}
break;
case OPEN: setState(OPEN, INACTIVE, true); break;
case NEW: editor.handleNew(e.isShiftDown()); break;
case SAVE: editor.handleSave(); break;
case EXPORT: editor.handleExport(); break;
}
currentSelection = -1;
}
public void disableRun(boolean what) {
disableRun = what;
}
public void clear() { // (int button) {
if (inactive == null) return;
// skip the run button, do the others
for (int i = 1; i < buttonCount; i++) {
setState(i, INACTIVE, false);
}
repaint(); // changed for swing from update();
}
public void run() {
if (inactive == null) return;
clear();
setState(RUN, ACTIVE, true);
}
public void running(boolean yesno) {
setState(RUN, yesno ? ACTIVE : INACTIVE, true);
}
public void clearRun() {
if (inactive == null) return;
setState(RUN, INACTIVE, true);
}
public void message(String msg) {
//status.setText(msg + " "); // don't mind the hack
status = msg;
}
public void messageClear(String msg) {
//if (status.getText().equals(msg + " ")) status.setText(Editor.EMPTY);
if (status.equals(msg)) status = "";
}
public Dimension getPreferredSize() {
return new Dimension((BUTTON_COUNT + 1)*BUTTON_WIDTH, BUTTON_HEIGHT);
//return new Dimension(BUTTON_WIDTH, (BUTTON_COUNT + 1)*BUTTON_HEIGHT);
}
public Dimension getMinimumSize() {
return getPreferredSize();
}
public Dimension getMaximumSize() {
return new Dimension(3000, BUTTON_HEIGHT);
}
}

334
app/EditorConsole.java Normal file
View File

@ -0,0 +1,334 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.text.*;
/**
* Message console that sits below the editing area.
* <P>
* Debugging this class is tricky... If it's throwing exceptions,
* don't take over System.err, and debug while watching just System.out
* or just write println() or whatever directly to systemOut or systemErr.
*/
public class EditorConsole extends JScrollPane {
Editor editor;
JTextPane consoleTextPane;
StyledDocument consoleDoc;
MutableAttributeSet stdStyle;
MutableAttributeSet errStyle;
boolean cerror;
//int maxCharCount;
int maxLineCount;
static PrintStream systemOut;
static PrintStream systemErr;
static PrintStream consoleOut;
static PrintStream consoleErr;
static OutputStream stdoutFile;
static OutputStream stderrFile;
public EditorConsole(Editor editor) {
this.editor = editor;
maxLineCount = Preferences.getInteger("console.length");
consoleTextPane = new JTextPane();
consoleTextPane.setEditable(false);
consoleDoc = consoleTextPane.getStyledDocument();
// necessary?
MutableAttributeSet standard = new SimpleAttributeSet();
StyleConstants.setAlignment(standard, StyleConstants.ALIGN_LEFT);
consoleDoc.setParagraphAttributes(0, 0, standard, true);
// build styles for different types of console output
Color bgColor = Preferences.getColor("console.color");
Color fgColorOut = Preferences.getColor("console.output.color");
Color fgColorErr = Preferences.getColor("console.error.color");
Font font = Preferences.getFont("console.font");
stdStyle = new SimpleAttributeSet();
StyleConstants.setForeground(stdStyle, fgColorOut);
StyleConstants.setBackground(stdStyle, bgColor);
StyleConstants.setFontSize(stdStyle, font.getSize());
StyleConstants.setFontFamily(stdStyle, font.getFamily());
StyleConstants.setBold(stdStyle, font.isBold());
StyleConstants.setItalic(stdStyle, font.isItalic());
errStyle = new SimpleAttributeSet();
StyleConstants.setForeground(errStyle, fgColorErr);
StyleConstants.setBackground(errStyle, bgColor);
StyleConstants.setFontSize(errStyle, font.getSize());
StyleConstants.setFontFamily(errStyle, font.getFamily());
StyleConstants.setBold(errStyle, font.isBold());
StyleConstants.setItalic(errStyle, font.isItalic());
consoleTextPane.setBackground(bgColor);
// add the jtextpane to this scrollpane
this.setViewportView(consoleTextPane);
// calculate height of a line of text in pixels
// and size window accordingly
FontMetrics metrics = this.getFontMetrics(font);
int height = metrics.getAscent() + metrics.getDescent();
int lines = Preferences.getInteger("console.lines"); //, 4);
int sizeFudge = 6; //10; // unclear why this is necessary, but it is
setPreferredSize(new Dimension(1024, (height * lines) + sizeFudge));
setMinimumSize(new Dimension(1024, (height * 4) + sizeFudge));
if (systemOut == null) {
systemOut = System.out;
systemErr = System.err;
try {
String outFileName = Preferences.get("console.output.file");
if (outFileName != null) {
stdoutFile = new FileOutputStream(outFileName);
}
String errFileName = Preferences.get("console.error.file");
if (errFileName != null) {
stderrFile = new FileOutputStream(outFileName);
}
} catch (IOException e) {
Base.showWarning("Console Error",
"A problem occurred while trying to open the\n" +
"files used to store the console output.", e);
}
consoleOut =
new PrintStream(new EditorConsoleStream(this, false, stdoutFile));
consoleErr =
new PrintStream(new EditorConsoleStream(this, true, stderrFile));
if (Preferences.getBoolean("console")) {
try {
System.setOut(consoleOut);
System.setErr(consoleErr);
} catch (Exception e) {
e.printStackTrace(systemOut);
}
}
}
// to fix ugliness.. normally macosx java 1.3 puts an
// ugly white border around this object, so turn it off.
if (Base.isMacOS()) {
setBorder(null);
}
}
public void write(byte b[], int offset, int length, boolean err) {
if (err != cerror) {
// advance the line because switching between err/out streams
// potentially, could check whether we're already on a new line
message("", cerror, true);
}
// we could do some cross platform CR/LF mangling here before outputting
// add text to output document
message(new String(b, offset, length), err, false);
// set last error state
cerror = err;
}
// added sync for 0091.. not sure if it helps or hinders
synchronized public void message(String what, boolean err, boolean advance) {
if (err) {
systemErr.print(what);
//systemErr.print("CE" + what);
} else {
systemOut.print(what);
//systemOut.print("CO" + what);
}
if (advance) {
appendText("\n", err);
if (err) {
systemErr.println();
} else {
systemOut.println();
}
}
// to console display
appendText(what, err);
// moved down here since something is punting
}
/**
* append a piece of text to the console.
* <P>
* Swing components are NOT thread-safe, and since the MessageSiphon
* instantiates new threads, and in those callbacks, they often print
* output to stdout and stderr, which are wrapped by EditorConsoleStream
* and eventually leads to EditorConsole.appendText(), which directly
* updates the Swing text components, causing deadlock.
* <P>
* A quick hack from Francis Li (who found this to be a problem)
* wraps the contents of appendText() into a Runnable and uses
* SwingUtilities.invokeLater() to ensure that the updates only
* occur on the main event dispatching thread, and that appears
* to have solved the problem.
* <P>
* unfortunately this is probably extremely slow and helping cause
* some of the general print() and println() mess.. need to fix
* up so that it's using a proper queue instead.
*/
synchronized private void appendText(String txt, boolean e) {
final String text = txt;
final boolean err = e;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
// check how many lines have been used so far
// if too many, shave off a few lines from the beginning
Element element = consoleDoc.getDefaultRootElement();
int lineCount = element.getElementCount();
int overage = lineCount - maxLineCount;
if (overage > 0) {
// if 1200 lines, and 1000 lines is max,
// find the position of the end of the 200th line
//systemOut.println("overage is " + overage);
Element lineElement = element.getElement(overage);
if (lineElement == null) return; // do nuthin
int endOffset = lineElement.getEndOffset();
// remove to the end of the 200th line
consoleDoc.remove(0, endOffset);
}
// make sure this line doesn't go over 32k chars
lineCount = element.getElementCount(); // may have changed
Element currentElement = element.getElement(lineCount-1);
int currentStart = currentElement.getStartOffset();
int currentEnd = currentElement.getEndOffset();
//systemOut.println(currentEnd - currentStart);
if (currentEnd - currentStart > 10000) { // force a newline
consoleDoc.insertString(consoleDoc.getLength(), "\n",
err ? errStyle : stdStyle);
}
// add the text to the end of the console,
consoleDoc.insertString(consoleDoc.getLength(), text,
err ? errStyle : stdStyle);
// always move to the end of the text as it's added
consoleTextPane.setCaretPosition(consoleDoc.getLength());
} catch (BadLocationException e) {
// ignore the error otherwise this will cause an infinite loop
// maybe not a good idea in the long run?
}
}
});
}
public void clear() {
try {
consoleDoc.remove(0, consoleDoc.getLength());
} catch (BadLocationException e) {
// ignore the error otherwise this will cause an infinite loop
// maybe not a good idea in the long run?
}
}
}
class EditorConsoleStream extends OutputStream {
EditorConsole parent;
boolean err; // whether stderr or stdout
byte single[] = new byte[1];
OutputStream echo;
public EditorConsoleStream(EditorConsole parent,
boolean err, OutputStream echo) {
this.parent = parent;
this.err = err;
this.echo = echo;
}
public void close() { }
public void flush() { }
public void write(byte b[]) { // appears never to be used
parent.write(b, 0, b.length, err);
if (echo != null) {
try {
echo.write(b); //, 0, b.length);
echo.flush();
} catch (IOException e) {
e.printStackTrace();
echo = null;
}
}
}
public void write(byte b[], int offset, int length) {
parent.write(b, offset, length, err);
if (echo != null) {
try {
echo.write(b, offset, length);
echo.flush();
} catch (IOException e) {
e.printStackTrace();
echo = null;
}
}
}
public void write(int b) {
single[0] = (byte)b;
parent.write(single, 0, 1, err);
if (echo != null) {
try {
echo.write(b);
echo.flush();
} catch (IOException e) {
e.printStackTrace();
echo = null;
}
}
}
}

389
app/EditorHeader.java Normal file
View File

@ -0,0 +1,389 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
/**
* Sketch tabs at the top of the editor window.
*/
public class EditorHeader extends JComponent {
static Color backgroundColor;
static Color textColor[] = new Color[2];
Editor editor;
int tabLeft[];
int tabRight[];
Font font;
FontMetrics metrics;
int fontAscent;
JMenu menu;
JPopupMenu popup;
JMenuItem hideItem;
int menuLeft;
int menuRight;
//
static final String STATUS[] = { "unsel", "sel" };
static final int UNSELECTED = 0;
static final int SELECTED = 1;
static final String WHERE[] = { "left", "mid", "right", "menu" };
static final int LEFT = 0;
static final int MIDDLE = 1;
static final int RIGHT = 2;
static final int MENU = 3;
static final int PIECE_WIDTH = 4;
Image[][] pieces;
//
Image offscreen;
int sizeW, sizeH;
int imageW, imageH;
public EditorHeader(Editor eddie) {
this.editor = eddie; // weird name for listener
pieces = new Image[STATUS.length][WHERE.length];
for (int i = 0; i < STATUS.length; i++) {
for (int j = 0; j < WHERE.length; j++) {
pieces[i][j] = Base.getImage("tab-" + STATUS[i] + "-" +
WHERE[j] + ".gif", this);
}
}
if (backgroundColor == null) {
backgroundColor =
Preferences.getColor("header.bgcolor");
textColor[SELECTED] =
Preferences.getColor("header.text.selected.color");
textColor[UNSELECTED] =
Preferences.getColor("header.text.unselected.color");
}
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
if ((x > menuLeft) && (x < menuRight)) {
popup.show(EditorHeader.this, x, y);
} else {
for (int i = 0; i < editor.sketch.codeCount; i++) {
if ((x > tabLeft[i]) && (x < tabRight[i])) {
editor.sketch.setCurrent(i);
repaint();
}
}
}
}
});
}
public void paintComponent(Graphics screen) {
if (screen == null) return;
Sketch sketch = editor.sketch;
if (sketch == null) return; // ??
Dimension size = getSize();
if ((size.width != sizeW) || (size.height != sizeH)) {
// component has been resized
if ((size.width > imageW) || (size.height > imageH)) {
// nix the image and recreate, it's too small
offscreen = null;
} else {
// who cares, just resize
sizeW = size.width;
sizeH = size.height;
//userLeft = 0; // reset
}
}
if (offscreen == null) {
sizeW = size.width;
sizeH = size.height;
imageW = sizeW;
imageH = sizeH;
offscreen = createImage(imageW, imageH);
}
Graphics g = offscreen.getGraphics();
if (font == null) {
font = Preferences.getFont("header.text.font");
}
g.setFont(font); // need to set this each time through
metrics = g.getFontMetrics();
fontAscent = metrics.getAscent();
//}
//Graphics2D g2 = (Graphics2D) g;
//g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
// RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// set the background for the offscreen
g.setColor(backgroundColor);
g.fillRect(0, 0, imageW, imageH);
if ((tabLeft == null) ||
(tabLeft.length < sketch.codeCount)) {
tabLeft = new int[sketch.codeCount];
tabRight = new int[sketch.codeCount];
}
// disable hide on the first tab
hideItem.setEnabled(sketch.current != sketch.code[0]);
//int x = 0; //Preferences.GUI_SMALL;
//int x = (Base.platform == Base.MACOSX) ? 0 : 1;
int x = 6; // offset from left edge of the component
for (int i = 0; i < sketch.codeCount; i++) {
SketchCode code = sketch.code[i];
String codeName = (code.flavor == Sketch.PDE) ?
code.name : code.file.getName();
// if modified, add the li'l glyph next to the name
String text = " " + codeName + (code.modified ? " \u00A7" : " ");
//int textWidth = metrics.stringWidth(text);
Graphics2D g2 = (Graphics2D) g;
int textWidth = (int)
font.getStringBounds(text, g2.getFontRenderContext()).getWidth();
int pieceCount = 2 + (textWidth / PIECE_WIDTH);
int pieceWidth = pieceCount * PIECE_WIDTH;
int state = (code == sketch.current) ? SELECTED : UNSELECTED;
g.drawImage(pieces[state][LEFT], x, 0, null);
x += PIECE_WIDTH;
int contentLeft = x;
tabLeft[i] = x;
for (int j = 0; j < pieceCount; j++) {
g.drawImage(pieces[state][MIDDLE], x, 0, null);
x += PIECE_WIDTH;
}
tabRight[i] = x;
int textLeft = contentLeft + (pieceWidth - textWidth) / 2;
g.setColor(textColor[state]);
int baseline = (sizeH + fontAscent) / 2;
//g.drawString(sketch.code[i].name, textLeft, baseline);
g.drawString(text, textLeft, baseline);
g.drawImage(pieces[state][RIGHT], x, 0, null);
x += PIECE_WIDTH - 1; // overlap by 1 pixel
}
menuLeft = sizeW - (16 + pieces[0][MENU].getWidth(this));
menuRight = sizeW - 16;
// draw the dropdown menu target
g.drawImage(pieces[popup.isVisible() ? SELECTED : UNSELECTED][MENU],
menuLeft, 0, null);
screen.drawImage(offscreen, 0, 0, null);
}
/**
* Called when a new sketch is opened.
*/
public void rebuild() {
//System.out.println("rebuilding editor header");
rebuildMenu();
repaint();
}
public void rebuildMenu() {
if (menu != null) {
menu.removeAll();
} else {
menu = new JMenu();
popup = menu.getPopupMenu();
add(popup);
popup.addPopupMenuListener(new PopupMenuListener() {
public void popupMenuCanceled(PopupMenuEvent e) {
// on redraw, the isVisible() will get checked.
// actually, a repaint may be fired anyway, so this
// may be redundant.
repaint();
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { }
public void popupMenuWillBecomeVisible(PopupMenuEvent e) { }
});
}
JMenuItem item;
// maybe this shouldn't have a command key anyways..
// since we're not trying to make this a full ide..
//item = Editor.newJMenuItem("New", 'T');
/*
item = Editor.newJMenuItem("Previous", KeyEvent.VK_PAGE_UP);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("prev");
}
});
if (editor.sketch != null) {
item.setEnabled(editor.sketch.codeCount > 1);
}
menu.add(item);
item = Editor.newJMenuItem("Next", KeyEvent.VK_PAGE_DOWN);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("ext");
}
});
if (editor.sketch != null) {
item.setEnabled(editor.sketch.codeCount > 1);
}
menu.add(item);
menu.addSeparator();
*/
item = new JMenuItem("New Tab");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.sketch.newCode();
}
});
menu.add(item);
item = new JMenuItem("Rename");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.sketch.renameCode();
if (editor.sketch.current == editor.sketch.code[0]) {
editor.sketchbook.rebuildMenus();
}
}
});
menu.add(item);
item = new JMenuItem("Delete");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.sketch.deleteCode();
}
});
menu.add(item);
item = new JMenuItem("Hide");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.sketch.hideCode();
}
});
menu.add(item);
hideItem = item;
JMenu unhide = new JMenu("Unhide");
ActionListener unhideListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
String which = (String) e.getActionCommand();
editor.sketch.unhideCode(which);
rebuildMenu();
}
};
Sketch sketch = editor.sketch;
if (sketch != null) {
for (int i = 0; i < sketch.hiddenCount; i++) {
item = new JMenuItem(sketch.hidden[i].name);
item.setActionCommand(sketch.hidden[i].name);
item.addActionListener(unhideListener);
unhide.add(item);
}
}
if (unhide.getItemCount() == 0) {
unhide.setEnabled(false);
}
menu.add(unhide);
if (sketch != null) {
menu.addSeparator();
ActionListener jumpListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.sketch.setCurrent(e.getActionCommand());
}
};
for (int i = 0; i < sketch.codeCount; i++) {
item = new JMenuItem(sketch.code[i].name);
item.addActionListener(jumpListener);
menu.add(item);
}
}
}
public void deselectMenu() {
repaint();
}
public Dimension getPreferredSize() {
return getMinimumSize();
}
public Dimension getMinimumSize() {
if (Base.isMacOS()) {
return new Dimension(300, Preferences.GRID_SIZE);
}
return new Dimension(300, Preferences.GRID_SIZE - 1);
}
public Dimension getMaximumSize() {
if (Base.isMacOS()) {
return new Dimension(3000, Preferences.GRID_SIZE);
}
return new Dimension(3000, Preferences.GRID_SIZE - 1);
}
}

119
app/EditorLineStatus.java Normal file
View File

@ -0,0 +1,119 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2005 Ben Fry and Casey Reas
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import processing.app.syntax.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/**
* Li'l status bar fella that shows the line number.
*/
public class EditorLineStatus extends JComponent {
JEditTextArea textarea;
int start = -1, stop;
Image resize;
Color foreground;
Color background;
Font font;
int high;
String text = "";
public EditorLineStatus(JEditTextArea textarea) {
this.textarea = textarea;
textarea.editorLineStatus = this;
background = Preferences.getColor("linestatus.bgcolor");
font = Preferences.getFont("linestatus.font");
foreground = Preferences.getColor("linestatus.color");
high = Preferences.getInteger("linestatus.height");
if (Base.isMacOS()) {
resize = Base.getImage("resize.gif", this);
}
//linestatus.bgcolor = #000000
//linestatus.font = SansSerif,plain,10
//linestatus.color = #FFFFFF
}
public void set(int newStart, int newStop) {
if ((newStart == start) && (newStop == stop)) return;
start = newStart;
stop = newStop;
/*
if (start == stop) {
text = "Line " + (start + 1);
} else {
text = "Lines " + (start + 1) + " to " + (stop + 1);
}
*/
if (start == stop) {
text = String.valueOf(start+1);
} else {
text = (start+1) + " - " + (stop+1);
}
repaint();
}
public void paintComponent(Graphics g) {
g.setColor(background);
Dimension size = getSize();
g.fillRect(0, 0, size.width, size.height);
g.setFont(font);
g.setColor(foreground);
int baseline = (high + g.getFontMetrics().getAscent()) / 2;
g.drawString(text, 6, baseline);
if (Base.isMacOS()) {
g.drawImage(resize, size.width - 20, 0, this);
}
}
public Dimension getPreferredSize() {
return new Dimension(300, high);
}
public Dimension getMinimumSize() {
return getPreferredSize();
}
public Dimension getMaximumSize() {
return new Dimension(3000, high);
}
}

172
app/EditorListener.java Normal file
View File

@ -0,0 +1,172 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import processing.app.syntax.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
/**
* Filters key events for tab expansion/indent/etc.
*/
public class EditorListener {
Editor editor;
JEditTextArea textarea;
boolean externalEditor;
boolean expandTabs;
String tabString;
boolean autoIndent;
int selectionStart, selectionEnd;
int position;
public EditorListener(Editor editor, JEditTextArea textarea) {
this.editor = editor;
this.textarea = textarea;
// let him know that i'm leechin'
textarea.editorListener = this;
applyPreferences();
}
public void applyPreferences() {
expandTabs = Preferences.getBoolean("editor.tabs.expand");
int tabSize = Preferences.getInteger("editor.tabs.size");
tabString = Editor.EMPTY.substring(0, tabSize);
autoIndent = Preferences.getBoolean("editor.indent");
externalEditor = Preferences.getBoolean("editor.external");
}
//public void setExternalEditor(boolean externalEditor) {
//this.externalEditor = externalEditor;
//}
// called by JEditTextArea inside processKeyEvent
public boolean keyPressed(KeyEvent event) {
// don't do things if the textarea isn't editable
if (externalEditor) return false;
//deselect(); // this is for paren balancing
char c = event.getKeyChar();
int code = event.getKeyCode();
//System.out.println(c + " " + code + " " + event);
//System.out.println();
if ((event.getModifiers() & KeyEvent.META_MASK) != 0) {
//event.consume(); // does nothing
return false;
}
// TODO i don't like these accessors. clean em up later.
if (!editor.sketch.current.modified) {
if ((code == KeyEvent.VK_BACK_SPACE) || (code == KeyEvent.VK_TAB) ||
(code == KeyEvent.VK_ENTER) || ((c >= 32) && (c < 128))) {
editor.sketch.setModified();
}
}
switch ((int) c) {
case 9: // expand tabs
if (expandTabs) {
//tc.replaceSelection(tabString);
textarea.setSelectedText(tabString);
event.consume();
return true;
}
break;
case 10: // auto-indent
case 13:
if (autoIndent) {
char contents[] = textarea.getText().toCharArray();
// this is the position of the caret, if the textarea
// only used a single kind of line ending
int origIndex = textarea.getCaretPosition() - 1;
// walk through the array to the current caret position,
// and count how many weirdo windows line endings there are,
// which would be throwing off the caret position number
int offset = 0;
int realIndex = origIndex;
for (int i = 0; i < realIndex-1; i++) {
if ((contents[i] == 13) && (contents[i+1] == 10)) {
offset++;
realIndex++;
}
}
// back up until \r \r\n or \n.. @#($* cross platform
//System.out.println(origIndex + " offset = " + offset);
origIndex += offset; // ARGH!#(* WINDOWS#@($*
int index = origIndex;
int spaceCount = 0;
boolean finished = false;
while ((index != -1) && (!finished)) {
if ((contents[index] == 10) ||
(contents[index] == 13)) {
finished = true;
index++; // maybe ?
} else {
index--; // new
}
}
while ((index < contents.length) && (index >= 0) &&
(contents[index++] == ' ')) {
spaceCount++;
}
// seems that \r is being inserted anyway
// so no need to insert the platform's line separator
String insertion = "\n" + Editor.EMPTY.substring(0, spaceCount);
//tc.replaceSelection(insertion);
textarea.setSelectedText(insertion);
// microsoft vm version:
//tc.setCaretPosition(oldCarrot + insertion.length() - 1);
// sun vm version:
// tc.setCaretPosition(oldCarrot + insertion.length());
event.consume();
return true;
}
break;
}
return false;
}
}

430
app/EditorStatus.java Normal file
View File

@ -0,0 +1,430 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Panel just below the editing area that contains status messages.
*/
public class EditorStatus extends JPanel implements ActionListener {
static Color bgcolor[];
static Color fgcolor[];
static final int NOTICE = 0;
static final int ERR = 1;
static final int PROMPT = 2;
static final int EDIT = 3;
static final int YES = 1;
static final int NO = 2;
static final int CANCEL = 3;
static final int OK = 4;
static final String NO_MESSAGE = "";
Editor editor;
int mode;
String message;
Font font;
FontMetrics metrics;
int ascent;
Image offscreen;
int sizeW, sizeH;
int imageW, imageH;
JButton yesButton;
JButton noButton;
JButton cancelButton;
JButton okButton;
JTextField editField;
//Thread promptThread;
int response;
public EditorStatus(Editor editor) {
this.editor = editor;
empty();
if (bgcolor == null) {
bgcolor = new Color[4];
bgcolor[0] = Preferences.getColor("status.notice.bgcolor");
bgcolor[1] = Preferences.getColor("status.error.bgcolor");
bgcolor[2] = Preferences.getColor("status.prompt.bgcolor");
bgcolor[3] = Preferences.getColor("status.prompt.bgcolor");
fgcolor = new Color[4];
fgcolor[0] = Preferences.getColor("status.notice.fgcolor");
fgcolor[1] = Preferences.getColor("status.error.fgcolor");
fgcolor[2] = Preferences.getColor("status.prompt.fgcolor");
fgcolor[3] = Preferences.getColor("status.prompt.fgcolor");
}
}
public void empty() {
mode = NOTICE;
message = NO_MESSAGE;
//update();
repaint();
}
public void notice(String message) {
mode = NOTICE;
this.message = message;
//update();
repaint();
}
public void unnotice(String unmessage) {
if (message.equals(unmessage)) empty();
}
public void error(String message) {
mode = ERR;
this.message = message;
repaint();
}
public void prompt(String message) {
mode = PROMPT;
this.message = message;
response = 0;
yesButton.setVisible(true);
noButton.setVisible(true);
cancelButton.setVisible(true);
yesButton.requestFocus();
repaint();
}
// prompt has been handled, re-hide the buttons
public void unprompt() {
yesButton.setVisible(false);
noButton.setVisible(false);
cancelButton.setVisible(false);
empty();
}
public void edit(String message, String dflt) {
mode = EDIT;
this.message = message;
response = 0;
okButton.setVisible(true);
cancelButton.setVisible(true);
editField.setVisible(true);
editField.setText(dflt);
editField.selectAll();
editField.requestFocus();
repaint();
}
public void unedit() {
okButton.setVisible(false);
cancelButton.setVisible(false);
editField.setVisible(false);
empty();
}
/*
public void update() {
Graphics g = this.getGraphics();
try {
setBackground(bgcolor[mode]);
} catch (NullPointerException e) { } // if not ready yet
if (g != null) paint(g);
}
public void update(Graphics g) {
paint(g);
}
*/
public void paintComponent(Graphics screen) {
//if (screen == null) return;
if (yesButton == null) setup();
//System.out.println("status.paintComponent");
Dimension size = getSize();
if ((size.width != sizeW) || (size.height != sizeH)) {
// component has been resized
if ((size.width > imageW) || (size.height > imageH)) {
// nix the image and recreate, it's too small
offscreen = null;
} else {
// who cares, just resize
sizeW = size.width;
sizeH = size.height;
setButtonBounds();
}
}
if (offscreen == null) {
sizeW = size.width;
sizeH = size.height;
setButtonBounds();
imageW = sizeW;
imageH = sizeH;
offscreen = createImage(imageW, imageH);
}
Graphics g = offscreen.getGraphics();
if (font == null) {
font = Preferences.getFont("status.font");
//new Font("SansSerif", Font.PLAIN, 12));
g.setFont(font);
metrics = g.getFontMetrics();
ascent = metrics.getAscent();
}
//setBackground(bgcolor[mode]); // does nothing
g.setColor(bgcolor[mode]);
g.fillRect(0, 0, imageW, imageH);
g.setColor(fgcolor[mode]);
g.setFont(font); // needs to be set each time on osx
g.drawString(message, Preferences.GUI_SMALL, (sizeH + ascent) / 2);
screen.drawImage(offscreen, 0, 0, null);
}
protected void setup() {
if (yesButton == null) {
yesButton = new JButton(Preferences.PROMPT_YES);
noButton = new JButton(Preferences.PROMPT_NO);
cancelButton = new JButton(Preferences.PROMPT_CANCEL);
okButton = new JButton(Preferences.PROMPT_OK);
// !@#(* aqua ui #($*(( that turtle-neck wearing #(** (#$@)(
// os9 seems to work if bg of component is set, but x still a bastard
if (Base.isMacOS()) {
yesButton.setBackground(bgcolor[PROMPT]);
noButton.setBackground(bgcolor[PROMPT]);
cancelButton.setBackground(bgcolor[PROMPT]);
okButton.setBackground(bgcolor[PROMPT]);
}
setLayout(null);
yesButton.addActionListener(this);
noButton.addActionListener(this);
cancelButton.addActionListener(this);
okButton.addActionListener(this);
add(yesButton);
add(noButton);
add(cancelButton);
add(okButton);
yesButton.setVisible(false);
noButton.setVisible(false);
cancelButton.setVisible(false);
okButton.setVisible(false);
editField = new JTextField();
editField.addActionListener(this);
//if (Base.platform != Base.MACOSX) {
editField.addKeyListener(new KeyAdapter() {
// no-op implemented because of a jikes bug
//protected void noop() { }
//public void keyPressed(KeyEvent event) {
//System.out.println("pressed " + event + " " + KeyEvent.VK_SPACE);
//}
// use keyTyped to catch when the feller is actually
// added to the text field. with keyTyped, as opposed to
// keyPressed, the keyCode will be zero, even if it's
// enter or backspace or whatever, so the keychar should
// be used instead. grr.
public void keyTyped(KeyEvent event) {
//System.out.println("got event " + event + " " +
// KeyEvent.VK_SPACE);
int c = event.getKeyChar();
if (c == KeyEvent.VK_ENTER) { // accept the input
String answer = editField.getText();
editor.sketch.nameCode(answer);
unedit();
event.consume();
// easier to test the affirmative case than the negative
} else if ((c == KeyEvent.VK_BACK_SPACE) ||
(c == KeyEvent.VK_DELETE) ||
(c == KeyEvent.VK_RIGHT) ||
(c == KeyEvent.VK_LEFT) ||
(c == KeyEvent.VK_UP) ||
(c == KeyEvent.VK_DOWN) ||
(c == KeyEvent.VK_HOME) ||
(c == KeyEvent.VK_END) ||
(c == KeyEvent.VK_SHIFT)) {
//System.out.println("nothing to see here");
//noop();
} else if (c == KeyEvent.VK_ESCAPE) {
unedit();
editor.buttons.clear();
event.consume();
} else if (c == KeyEvent.VK_SPACE) {
//System.out.println("got a space");
// if a space, insert an underscore
//editField.insert("_", editField.getCaretPosition());
/* tried to play nice and see where it got me
editField.dispatchEvent(new KeyEvent(editField,
KeyEvent.KEY_PRESSED,
System.currentTimeMillis(),
0, 45, '_'));
*/
//System.out.println("start/end = " +
// editField.getSelectionStart() + " " +
// editField.getSelectionEnd());
String t = editField.getText();
//int p = editField.getCaretPosition();
//editField.setText(t.substring(0, p) + "_" + t.substring(p));
//editField.setCaretPosition(p+1);
int start = editField.getSelectionStart();
int end = editField.getSelectionEnd();
editField.setText(t.substring(0, start) + "_" +
t.substring(end));
editField.setCaretPosition(start+1);
//System.out.println("consuming event");
event.consume();
} else if ((c == '_') || (c == '.') || // allow .pde and .java
((c >= 'A') && (c <= 'Z')) ||
((c >= 'a') && (c <= 'z'))) {
// everything fine, catches upper and lower
//noop();
} else if ((c >= '0') && (c <= '9')) {
// getCaretPosition == 0 means that it's the first char
// and the field is empty.
// getSelectionStart means that it *will be* the first
// char, because the selection is about to be replaced
// with whatever is typed.
if ((editField.getCaretPosition() == 0) ||
(editField.getSelectionStart() == 0)) {
// number not allowed as first digit
//System.out.println("bad number bad");
event.consume();
}
} else {
event.consume();
//System.out.println("code is " + code + " char = " + c);
}
//System.out.println("code is " + code + " char = " + c);
}
});
add(editField);
editField.setVisible(false);
}
}
protected void setButtonBounds() {
int top = (sizeH - Preferences.BUTTON_HEIGHT) / 2;
int eachButton = Preferences.GUI_SMALL + Preferences.BUTTON_WIDTH;
int cancelLeft = sizeW - eachButton;
int noLeft = cancelLeft - eachButton;
int yesLeft = noLeft - eachButton;
yesButton.setLocation(yesLeft, top);
noButton.setLocation(noLeft, top);
cancelButton.setLocation(cancelLeft, top);
editField.setLocation(yesLeft - Preferences.BUTTON_WIDTH, top);
okButton.setLocation(noLeft, top);
yesButton.setSize( Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT);
noButton.setSize( Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT);
cancelButton.setSize(Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT);
okButton.setSize( Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT);
editField.setSize( 2*Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT);
}
public Dimension getPreferredSize() {
return getMinimumSize();
}
public Dimension getMinimumSize() {
return new Dimension(300, Preferences.GRID_SIZE);
}
public Dimension getMaximumSize() {
return new Dimension(3000, Preferences.GRID_SIZE);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == noButton) {
// shut everything down, clear status, and return
unprompt();
// don't need to save changes
editor.checkModified2();
} else if (e.getSource() == yesButton) {
// answer was in response to "save changes?"
unprompt();
editor.handleSave();
editor.checkModified2();
} else if (e.getSource() == cancelButton) {
// don't do anything, don't continue with checkModified2
if (mode == PROMPT) unprompt();
else if (mode == EDIT) unedit();
editor.buttons.clear();
} else if (e.getSource() == okButton) {
// answering to "save as..." question
String answer = editField.getText();
//editor.handleSaveAs2(answer);
editor.sketch.nameCode(answer);
unedit();
}
}
}

305
app/FindReplace.java Normal file
View File

@ -0,0 +1,305 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Find & Replace window for the processing editor.
*/
public class FindReplace extends JFrame
implements ActionListener, KeyListener {
static final int BIG = 13;
static final int SMALL = 6;
Editor editor;
JTextField findField;
JTextField replaceField;
JButton replaceButton;
JButton replaceAllButton;
JButton findButton;
JCheckBox ignoreCaseBox;
boolean ignoreCase;
KeyStroke windowClose;
boolean found;
public FindReplace(Editor editor) {
super("Find");
setResizable(false);
this.editor = editor;
Container pain = getContentPane();
pain.setLayout(null);
JLabel findLabel = new JLabel("Find:");
Dimension d0 = findLabel.getPreferredSize();
JLabel replaceLabel = new JLabel("Replace with:");
Dimension d1 = replaceLabel.getPreferredSize();
pain.add(findLabel);
pain.add(replaceLabel);
pain.add(findField = new JTextField(20));
pain.add(replaceField = new JTextField(20));
Dimension d2 = findField.getPreferredSize();
// +1 since it's better to tend downwards
int yoff = (1 + d2.height - d1.height) / 2;
findLabel.setBounds(BIG + (d1.width-d0.width) + yoff, BIG,
d1.width, d1.height);
replaceLabel.setBounds(BIG, BIG + d2.height + SMALL + yoff,
d1.width, d1.height);
ignoreCase = true;
ignoreCaseBox = new JCheckBox("Ignore Case");
ignoreCaseBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ignoreCase = ignoreCaseBox.isSelected();
}
});
ignoreCaseBox.setSelected(ignoreCase);
pain.add(ignoreCaseBox);
//
JPanel buttons = new JPanel();
buttons.setLayout(new FlowLayout());
// ordering is different on mac versus pc
if (Base.isMacOS()) {
buttons.add(replaceButton = new JButton("Replace"));
buttons.add(replaceAllButton = new JButton("Replace All"));
buttons.add(findButton = new JButton("Find"));
} else {
buttons.add(findButton = new JButton("Find"));
buttons.add(replaceButton = new JButton("Replace"));
buttons.add(replaceAllButton = new JButton("Replace All"));
}
pain.add(buttons);
// 0069 TEMPORARILY DISABLED!
//replaceAllButton.setEnabled(false);
// to fix ugliness.. normally macosx java 1.3 puts an
// ugly white border around this object, so turn it off.
//if (Base.platform == Base.MACOSX) {
if (Base.isMacOS()) {
buttons.setBorder(null);
}
Dimension d3 = buttons.getPreferredSize();
//buttons.setBounds(BIG, BIG + d2.height*2 + SMALL + BIG,
buttons.setBounds(BIG, BIG + d2.height*3 + SMALL*2 + BIG,
d3.width, d3.height);
//
findField.setBounds(BIG + d1.width + SMALL, BIG,
d3.width - (d1.width + SMALL), d2.height);
replaceField.setBounds(BIG + d1.width + SMALL, BIG + d2.height + SMALL,
d3.width - (d1.width + SMALL), d2.height);
ignoreCaseBox.setBounds(BIG + d1.width + SMALL,
BIG + d2.height*2 + SMALL*2,
d3.width, d2.height);
//
replaceButton.addActionListener(this);
replaceAllButton.addActionListener(this);
findButton.addActionListener(this);
// you mustn't replace what you haven't found, my son
replaceButton.setEnabled(false);
// so that typing will go straight to this field
findField.requestFocus();
// make the find button the blinky default
getRootPane().setDefaultButton(findButton);
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
int wide = d3.width + BIG*2;
Rectangle butt = buttons.getBounds(); // how big is your butt?
int high = butt.y + butt.height + BIG*2 + SMALL;
setBounds((screen.width - wide) / 2,
(screen.height - high) / 2, wide, high);
// add key listener to trap esc and ctrl/cmd-w
findField.addKeyListener(this);
replaceField.addKeyListener(this);
addKeyListener(this);
// hack to to get first field to focus properly on osx
// though this still doesn't seem to work
addWindowListener(new WindowAdapter() {
public void windowActivated(WindowEvent e) {
//System.out.println("activating");
findField.requestFocus();
findField.selectAll();
}
});
}
/**
* Handle window closing commands for ctrl/cmd-W or hitting ESC.
*/
public void keyPressed(KeyEvent e) {
if (windowClose == null) {
int modifiers = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
windowClose = KeyStroke.getKeyStroke('W', modifiers);
}
if ((e.getKeyCode() == KeyEvent.VK_ESCAPE) ||
(KeyStroke.getKeyStrokeForEvent(e).equals(windowClose))) {
hide();
//} else {
//System.out.println("event " + e);
}
}
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) { }
/*
public void show() {
super.show();
findField.selectAll();
findField.requestFocus();
}
*/
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == findButton) {
find(true);
} else if (source == replaceButton) {
replace();
} else if (source == replaceAllButton) {
replaceAll();
}
}
// look for the next instance of the find string
// to be found later than the current caret selection
// once found, select it (and go to that line)
public void find(boolean wrap) {
// in case search len is zero,
// otherwise replace all will go into an infinite loop
found = false;
String search = findField.getText();
// this will catch "find next" being called when no search yet
if (search.length() == 0) return;
String text = editor.textarea.getText();
if (ignoreCase) {
search = search.toLowerCase();
text = text.toLowerCase();
}
//int selectionStart = editor.textarea.getSelectionStart();
int selectionEnd = editor.textarea.getSelectionEnd();
int nextIndex = text.indexOf(search, selectionEnd);
if (nextIndex == -1) {
if (wrap) {
// if wrapping, a second chance is ok, start from beginning
nextIndex = text.indexOf(search, 0);
}
if (nextIndex == -1) {
found = false;
replaceButton.setEnabled(false);
//Toolkit.getDefaultToolkit().beep();
return;
}
}
found = true;
replaceButton.setEnabled(true);
editor.textarea.select(nextIndex, nextIndex + search.length());
}
// replace the current selection with whatever's in the
// replacement text field
public void replace() {
if (!found) return; // don't replace if nothing found
// check to see if the document has wrapped around
// otherwise this will cause an infinite loop
String sel = editor.textarea.getSelectedText();
if (sel.equals(replaceField.getText())) {
found = false;
replaceButton.setEnabled(false);
return;
}
editor.textarea.setSelectedText(replaceField.getText());
//editor.setSketchModified(true);
//editor.sketch.setCurrentModified(true);
editor.sketch.setModified();
// don't allow a double replace
replaceButton.setEnabled(false);
}
// keep doing find and replace alternately until nothing more found
public void replaceAll() {
// move to the beginning
editor.textarea.select(0, 0);
do {
find(false);
replace();
} while (found);
}
}

42
app/MessageConsumer.java Normal file
View File

@ -0,0 +1,42 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
/**
* Interface for dealing with parser/compiler output.
* <P>
* Different instances of MessageStream need to do different things with
* messages. In particular, a stream instance used for parsing output from
* the compiler compiler has to interpret its messages differently than one
* parsing output from the runtime.
* <P>
* Classes which consume messages and do something with them
* should implement this interface.
*/
public interface MessageConsumer {
public void message(String s);
}

86
app/MessageSiphon.java Normal file
View File

@ -0,0 +1,86 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import java.io.*;
/**
* Slurps up messages from compiler.
*/
class MessageSiphon implements Runnable {
BufferedReader streamReader;
Thread thread;
MessageConsumer consumer;
public MessageSiphon(InputStream stream, MessageConsumer consumer) {
this.streamReader = new BufferedReader(new InputStreamReader(stream));
this.consumer = consumer;
thread = new Thread(this);
// don't set priority too low, otherwise exceptions won't
// bubble up in time (i.e. compile errors have a weird delay)
//thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
public void run() {
try {
// process data until we hit EOF; this will happily block
// (effectively sleeping the thread) until new data comes in.
// when the program is finally done, null will come through.
//
String currentLine;
while ((currentLine = streamReader.readLine()) != null) {
// \n is added again because readLine() strips it out
//EditorConsole.systemOut.println("messaging in");
consumer.message(currentLine + "\n");
//EditorConsole.systemOut.println("messaging out");
}
//EditorConsole.systemOut.println("messaging thread done");
thread = null;
} catch (NullPointerException npe) {
// Fairly common exception during shutdown
thread = null;
} catch (Exception e) {
// On Linux and sometimes on Mac OS X, a "bad file descriptor"
// message comes up when closing an applet that's run externally.
// That message just gets supressed here..
String mess = e.getMessage();
if ((mess != null) &&
(mess.indexOf("Bad file descriptor") != -1)) {
//if (e.getMessage().indexOf("Bad file descriptor") == -1) {
//System.err.println("MessageSiphon err " + e);
//e.printStackTrace();
} else {
e.printStackTrace();
}
thread = null;
}
}
}

62
app/MessageStream.java Normal file
View File

@ -0,0 +1,62 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import java.io.*;
/**
* OutputStream to handle stdout/stderr messages.
* <P>
* This is used by Editor, System.err is set to
* new PrintStream(new MessageStream()).
* It's also used by Compiler.
*/
class MessageStream extends OutputStream {
MessageConsumer messageConsumer;
public MessageStream(MessageConsumer messageConsumer) {
this.messageConsumer = messageConsumer;
}
public void close() { }
public void flush() { }
public void write(byte b[]) {
// this never seems to get called
System.out.println("leech1: " + new String(b));
}
public void write(byte b[], int offset, int length) {
//System.out.println("leech2: " + new String(b));
this.messageConsumer.message(new String(b, offset, length));
}
public void write(int b) {
// this never seems to get called
System.out.println("leech3: '" + ((char)b) + "'");
}
}

793
app/Preferences.java Normal file
View File

@ -0,0 +1,793 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import processing.app.syntax.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.zip.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.filechooser.*;
import javax.swing.text.*;
import javax.swing.undo.*;
//import processing.core.PApplet;
/**
* Storage class for user preferences and environment settings.
* <P>
* This class no longer uses the Properties class, since
* properties files are iso8859-1, which is highly likely to
* be a problem when trying to save sketch folders and locations.
*/
public class Preferences extends JComponent {
// what to call the feller
static final String PREFS_FILE = "preferences.txt";
// platform strings (used to get settings for specific platforms)
static final String platforms[] = {
"other", "windows", "macos9", "macosx", "linux"
};
// prompt text stuff
static final String PROMPT_YES = "Yes";
static final String PROMPT_NO = "No";
static final String PROMPT_CANCEL = "Cancel";
static final String PROMPT_OK = "OK";
static final String PROMPT_BROWSE = "Browse";
// mac needs it to be 70, windows needs 66, linux needs 76
static int BUTTON_WIDTH = 76;
static int BUTTON_HEIGHT = 24;
// value for the size bars, buttons, etc
static final int GRID_SIZE = 33;
// gui variables
static final int GUI_BIG = 13;
static final int GUI_BETWEEN = 10;
static final int GUI_SMALL = 6;
// gui elements
//JFrame frame;
JDialog frame;
int wide, high;
JTextField sketchbookLocationField;
JCheckBox sketchPromptBox;
JCheckBox sketchCleanBox;
//JCheckBox exportLibraryBox;
JCheckBox externalEditorBox;
JCheckBox checkUpdatesBox;
JTextField fontSizeField;
// the calling editor, so updates can be applied
Editor editor;
// data model
static Hashtable table = new Hashtable();;
static File preferencesFile;
//boolean firstTime; // first time this feller has been run
static public void init() {
// start by loading the defaults, in case something
// important was deleted from the user prefs
try {
load(Base.getStream("preferences.txt"));
} catch (Exception e) {
Base.showError(null, "Could not read default settings.\n" +
"You'll need to reinstall Processing.", e);
}
// check for platform-specific properties in the defaults
String platformExtension = "." +
platforms[Base.platform];
int extensionLength = platformExtension.length();
Enumeration e = table.keys(); //properties.propertyNames();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
if (key.endsWith(platformExtension)) {
// this is a key specific to a particular platform
String actualKey = key.substring(0, key.length() - extensionLength);
String value = get(key);
table.put(actualKey, value);
}
}
// other things that have to be set explicitly for the defaults
setColor("run.window.bgcolor", SystemColor.control);
// next load user preferences file
//File home = new File(System.getProperty("user.home"));
//File processingHome = new File(home, "Processing");
//preferencesFile = new File(home, PREFS_FILE);
preferencesFile = Base.getSettingsFile(PREFS_FILE);
if (!preferencesFile.exists()) {
// create a new preferences file if none exists
// saves the defaults out to the file
save();
} else {
// load the previous preferences file
try {
load(new FileInputStream(preferencesFile));
} catch (Exception ex) {
Base.showError("Error reading preferences",
"Error reading the preferences file. " +
"Please delete (or move)\n" +
preferencesFile.getAbsolutePath() +
" and restart Processing.", ex);
}
}
}
public Preferences() {
// setup frame for the prefs
//frame = new JFrame("Preferences");
frame = new JDialog(editor, "Preferences", true);
//frame.setResizable(false);
//Container pain = this;
Container pain = frame.getContentPane();
pain.setLayout(null);
int top = GUI_BIG;
int left = GUI_BIG;
int right = 0;
JLabel label;
JButton button, button2;
JComboBox combo;
Dimension d, d2, d3;
int h, v, vmax;
// [ ] Prompt for name and folder when creating new sketch
sketchPromptBox =
new JCheckBox("Prompt for name when opening or creating a sketch");
pain.add(sketchPromptBox);
d = sketchPromptBox.getPreferredSize();
sketchPromptBox.setBounds(left, top, d.width, d.height);
right = Math.max(right, left + d.width);
top += d.height + GUI_BETWEEN;
// [ ] Delete empty sketches on Quit
sketchCleanBox = new JCheckBox("Delete empty sketches on Quit");
pain.add(sketchCleanBox);
d = sketchCleanBox.getPreferredSize();
sketchCleanBox.setBounds(left, top, d.width, d.height);
right = Math.max(right, left + d.width);
top += d.height + GUI_BETWEEN;
// Sketchbook location:
// [...............................] [ Browse ]
label = new JLabel("Sketchbook location:");
pain.add(label);
d = label.getPreferredSize();
label.setBounds(left, top, d.width, d.height);
top += d.height; // + GUI_SMALL;
sketchbookLocationField = new JTextField(40);
pain.add(sketchbookLocationField);
d = sketchbookLocationField.getPreferredSize();
button = new JButton(PROMPT_BROWSE);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
fc.setSelectedFile(new File(sketchbookLocationField.getText()));
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int returned = fc.showOpenDialog(new JDialog());
if (returned == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
sketchbookLocationField.setText(file.getAbsolutePath());
}
}
});
pain.add(button);
d2 = button.getPreferredSize();
// take max height of all components to vertically align em
vmax = Math.max(d.height, d2.height);
//label.setBounds(left, top + (vmax-d.height)/2,
// d.width, d.height);
//h = left + d.width + GUI_BETWEEN;
sketchbookLocationField.setBounds(left, top + (vmax-d.height)/2,
d.width, d.height);
h = left + d.width + GUI_SMALL; //GUI_BETWEEN;
button.setBounds(h, top + (vmax-d2.height)/2,
d2.width, d2.height);
right = Math.max(right, h + d2.width + GUI_BIG);
top += vmax + GUI_BETWEEN;
// Editor font size [ ]
Container box = Box.createHorizontalBox();
label = new JLabel("Editor font size: ");
box.add(label);
fontSizeField = new JTextField(4);
box.add(fontSizeField);
pain.add(box);
d = box.getPreferredSize();
box.setBounds(left, top, d.width, d.height);
Font editorFont = Preferences.getFont("editor.font");
fontSizeField.setText(String.valueOf(editorFont.getSize()));
top += d.height + GUI_BETWEEN;
// [ ] Enable export to "Library"
/*
exportLibraryBox = new JCheckBox("Enable advanced \"Library\" features" +
" (requires restart)");
exportLibraryBox.setEnabled(false);
pain.add(exportLibraryBox);
d = exportLibraryBox.getPreferredSize();
exportLibraryBox.setBounds(left, top, d.width, d.height);
right = Math.max(right, left + d.width);
top += d.height + GUI_BETWEEN;
*/
// [ ] Use external editor
externalEditorBox = new JCheckBox("Use external editor");
pain.add(externalEditorBox);
d = externalEditorBox.getPreferredSize();
externalEditorBox.setBounds(left, top, d.width, d.height);
right = Math.max(right, left + d.width);
top += d.height + GUI_BETWEEN;
// [ ] Check for updates on startup
checkUpdatesBox = new JCheckBox("Check for updates on startup");
pain.add(checkUpdatesBox);
d = checkUpdatesBox.getPreferredSize();
checkUpdatesBox.setBounds(left, top, d.width, d.height);
right = Math.max(right, left + d.width);
top += d.height + GUI_BETWEEN;
// More preferences are in the ...
/*
String blather =
"More preferences can be edited directly\n" +
"in the file " + preferencesFile.getAbsolutePath();
//"More preferences are in the 'lib' folder inside text files\n" +
//"named preferences.txt and pde_" +
//Base.platforms[Base.platform] + ".properties";
JTextArea textarea = new JTextArea(blather);
textarea.setEditable(false);
textarea.setBorder(new EmptyBorder(0, 0, 0, 0));
textarea.setBackground(null);
textarea.setFont(new Font("Dialog", Font.PLAIN, 12));
pain.add(textarea);
*/
label = new JLabel("More preferences can be edited directly in the file");
pain.add(label);
d = label.getPreferredSize();
label.setForeground(Color.gray);
label.setBounds(left, top, d.width, d.height);
right = Math.max(right, left + d.width);
top += d.height; // + GUI_SMALL;
label = new JLabel(preferencesFile.getAbsolutePath());
pain.add(label);
d = label.getPreferredSize();
label.setBounds(left, top, d.width, d.height);
right = Math.max(right, left + d.width);
top += d.height;
label = new JLabel("(edit only when Processing is not running)");
pain.add(label);
d = label.getPreferredSize();
label.setForeground(Color.gray);
label.setBounds(left, top, d.width, d.height);
right = Math.max(right, left + d.width);
top += d.height; // + GUI_SMALL;
// [ OK ] [ Cancel ] maybe these should be next to the message?
//right = Math.max(right, left + d.width + GUI_BETWEEN +
// BUTTON_WIDTH + GUI_SMALL + BUTTON_WIDTH);
button = new JButton(PROMPT_OK);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
applyFrame();
disposeFrame();
}
});
pain.add(button);
d2 = button.getPreferredSize();
BUTTON_HEIGHT = d2.height;
// smoosh up to the line before
//top -= BUTTON_HEIGHT;
h = right - (BUTTON_WIDTH + GUI_SMALL + BUTTON_WIDTH);
button.setBounds(h, top, BUTTON_WIDTH, BUTTON_HEIGHT);
h += BUTTON_WIDTH + GUI_SMALL;
button = new JButton(PROMPT_CANCEL);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
disposeFrame();
}
});
pain.add(button);
button.setBounds(h, top, BUTTON_WIDTH, BUTTON_HEIGHT);
top += BUTTON_HEIGHT + GUI_BETWEEN;
// finish up
wide = right + GUI_BIG;
high = top + GUI_SMALL; //GUI_BIG;
setSize(wide, high);
// closing the window is same as hitting cancel button
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
disposeFrame();
}
});
Container content = frame.getContentPane();
content.setLayout(new BorderLayout());
content.add(this, BorderLayout.CENTER);
frame.pack();
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
frame.setLocation((screen.width - wide) / 2,
(screen.height - high) / 2);
// handle window closing commands for ctrl/cmd-W or hitting ESC.
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
KeyStroke wc = Editor.WINDOW_CLOSE_KEYSTROKE;
if ((e.getKeyCode() == KeyEvent.VK_ESCAPE) ||
(KeyStroke.getKeyStrokeForEvent(e).equals(wc))) {
disposeFrame();
}
}
});
}
public Dimension getPreferredSize() {
return new Dimension(wide, high);
}
// .................................................................
/**
* Close the window after an OK or Cancel.
*/
public void disposeFrame() {
frame.dispose();
}
/**
* Change internal settings based on what was chosen in the prefs,
* then send a message to the editor saying that it's time to do the same.
*/
public void applyFrame() {
// put each of the settings into the table
setBoolean("sketchbook.prompt", sketchPromptBox.isSelected());
setBoolean("sketchbook.auto_clean", sketchCleanBox.isSelected());
set("sketchbook.path", sketchbookLocationField.getText());
setBoolean("editor.external", externalEditorBox.isSelected());
setBoolean("update.check", checkUpdatesBox.isSelected());
String newSizeText = fontSizeField.getText();
try {
int newSize = Integer.parseInt(newSizeText.trim());
String pieces[] = Base.split(get("editor.font"), ',');
pieces[2] = String.valueOf(newSize);
set("editor.font", Base.join(pieces, ','));
} catch (Exception e) {
System.err.println("ignoring invalid font size " + newSizeText);
}
editor.applyPreferences();
}
public void showFrame(Editor editor) {
this.editor = editor;
// set all settings entry boxes to their actual status
sketchPromptBox.setSelected(getBoolean("sketchbook.prompt"));
sketchCleanBox.setSelected(getBoolean("sketchbook.auto_clean"));
sketchbookLocationField.setText(get("sketchbook.path"));
externalEditorBox.setSelected(getBoolean("editor.external"));
checkUpdatesBox.setSelected(getBoolean("update.check"));
frame.show();
}
// .................................................................
static public void load(InputStream input) throws IOException {
BufferedReader reader =
new BufferedReader(new InputStreamReader(input));
//table = new Hashtable();
String line = null;
while ((line = reader.readLine()) != null) {
if ((line.length() == 0) ||
(line.charAt(0) == '#')) continue;
// this won't properly handle = signs being in the text
int equals = line.indexOf('=');
if (equals != -1) {
String key = line.substring(0, equals).trim();
String value = line.substring(equals + 1).trim();
table.put(key, value);
}
}
reader.close();
}
// .................................................................
static public void save() {
try {
FileOutputStream output = new FileOutputStream(preferencesFile);
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output));
Enumeration e = table.keys(); //properties.propertyNames();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
writer.println(key + "=" + ((String) table.get(key)));
}
writer.flush();
writer.close();
/*
FileOutputStream output = null;
if ((Base.platform == Base.MACOSX) ||
(Base.platform == Base.MACOS9)) {
output = new FileOutputStream("lib/preferences.txt");
} else { // win95/98/ME doesn't set cwd properly
URL url = getClass().getResource("buttons.gif");
String urlstr = url.getFile();
urlstr = urlstr.substring(0, urlstr.lastIndexOf("/") + 1) +
".properties";
output = new FileOutputStream(URLDecoder.decode(urlstr));
}
*/
/*
//base.storePreferences();
Properties skprops = new Properties();
//Rectangle window = Base.frame.getBounds();
Rectangle window = editor.getBounds();
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
skprops.put("last.window.x", String.valueOf(window.x));
skprops.put("last.window.y", String.valueOf(window.y));
skprops.put("last.window.w", String.valueOf(window.width));
skprops.put("last.window.h", String.valueOf(window.height));
skprops.put("last.screen.w", String.valueOf(screen.width));
skprops.put("last.screen.h", String.valueOf(screen.height));
skprops.put("last.sketch.name", sketchName);
skprops.put("last.sketch.directory", sketchDir.getAbsolutePath());
//skprops.put("user.name", userName);
skprops.put("last.divider.location",
String.valueOf(splitPane.getDividerLocation()));
//
skprops.put("editor.external", externalEditor ? "true" : "false");
//skprops.put("serial.port", Preferences.get("serial.port", "unspecified"));
// save() is deprecated, and didn't properly
// throw exceptions when it wasn't working
skprops.store(output, "Settings for processing. " +
"See lib/preferences.txt for defaults.");
// need to close the stream.. didn't do this before
skprops.close();
*/
} catch (IOException ex) {
Base.showWarning(null, "Error while saving the settings file", ex);
//e.printStackTrace();
}
}
// .................................................................
// all the information from preferences.txt
//static public String get(String attribute) {
//return get(attribute, null);
//}
static public String get(String attribute /*, String defaultValue */) {
return (String) table.get(attribute);
/*
//String value = (properties != null) ?
//properties.getProperty(attribute) : applet.getParameter(attribute);
String value = properties.getProperty(attribute);
return (value == null) ?
defaultValue : value;
*/
}
static public void set(String attribute, String value) {
//preferences.put(attribute, value);
table.put(attribute, value);
}
static public boolean getBoolean(String attribute) {
String value = get(attribute); //, null);
return (new Boolean(value)).booleanValue();
/*
supposedly not needed, because anything besides 'true'
(ignoring case) will just be false.. so if malformed -> false
if (value == null) return defaultValue;
try {
return (new Boolean(value)).booleanValue();
} catch (NumberFormatException e) {
System.err.println("expecting an integer: " + attribute + " = " + value);
}
return defaultValue;
*/
}
static public void setBoolean(String attribute, boolean value) {
set(attribute, value ? "true" : "false");
}
static public int getInteger(String attribute /*, int defaultValue*/) {
return Integer.parseInt(get(attribute));
/*
String value = get(attribute, null);
if (value == null) return defaultValue;
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
// ignored will just fall through to returning the default
System.err.println("expecting an integer: " + attribute + " = " + value);
}
return defaultValue;
//if (value == null) return defaultValue;
//return (value == null) ? defaultValue :
//Integer.parseInt(value);
*/
}
static public void setInteger(String key, int value) {
set(key, String.valueOf(value));
}
static public Color getColor(String name /*, Color otherwise*/) {
Color parsed = null;
String s = get(name); //, null);
//System.out.println(name + " = " + s);
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 void setColor(String attr, Color what) {
String r = Integer.toHexString(what.getRed());
String g = Integer.toHexString(what.getGreen());
String b = Integer.toHexString(what.getBlue());
set(attr, "#" + r.substring(r.length() - 2) +
g.substring(g.length() - 2) + b.substring(b.length() - 2));
}
static public Font getFont(String which /*, Font otherwise*/) {
//System.out.println("getting font '" + which + "'");
String str = get(which);
//if (str == null) return otherwise; // ENABLE LATER
StringTokenizer st = new StringTokenizer(str, ",");
String fontname = st.nextToken();
String fontstyle = st.nextToken();
return new Font(fontname,
((fontstyle.indexOf("bold") != -1) ? Font.BOLD : 0) |
((fontstyle.indexOf("italic") != -1) ? Font.ITALIC : 0),
Integer.parseInt(st.nextToken()));
}
static public SyntaxStyle getStyle(String what /*, String dflt*/) {
String str = get("editor." + what + ".style"); //, dflt);
StringTokenizer st = new StringTokenizer(str, ",");
String s = st.nextToken();
if (s.indexOf("#") == 0) s = s.substring(1);
Color color = new Color(Integer.parseInt(s, 16));
s = st.nextToken();
boolean bold = (s.indexOf("bold") != -1);
boolean italic = (s.indexOf("italic") != -1);
//System.out.println(what + " = " + str + " " + bold + " " + italic);
return new SyntaxStyle(color, italic, bold);
}
}
// Default serial port: [ COM1 + ]
/*
label = new JLabel("Default serial port:");
pain.add(label);
d = label.getPreferredSize();
Vector list = buildPortList();
combo = new JComboBox(list);
pain.add(combo);
d2 = combo.getPreferredSize();
if (list.size() == 0) {
label.setEnabled(false);
combo.setEnabled(false);
} else {
String defaultName = Preferences.get("serial.port", "unspecified");
combo.setSelectedItem(defaultName);
}
vmax = Math.max(d.height, d2.height);
label.setBounds(left, top + (vmax-d.height)/2,
d.width, d.height);
h = left + d.width + BETWEEN;
combo.setBounds(h, top + (vmax-d2.height)/2,
d2.width, d2.height);
right = Math.max(right, h + d2.width + BIG);
top += vmax + BETWEEN;
*/
// open the last-used sketch, etc
//public void init() {
//String what = path + File.separator + name + ".pde";
///String serialPort = skprops.getProperty("serial.port");
//if (serialPort != null) {
// properties.put("serial.port", serialPort);
//}
//boolean ee = new Boolean(skprops.getProperty("editor.external", "false")).booleanValue();
//editor.setExternalEditor(ee);
///} catch (Exception e) {
// this exception doesn't matter, it's just the normal course of things
// the app reaches here when no sketch.properties file exists
//e.printStackTrace();
// indicator that this is the first time this feller has used p5
//firstTime = true;
// even if folder for 'default' user doesn't exist, or
// sketchbook itself is missing, mkdirs() will make it happy
//userName = "default";
// doesn't exist, not available, make my own
//skNew();
//}
//}

128
app/PresentMode.java Normal file
View File

@ -0,0 +1,128 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2005- Ben Fry and Casey Reas
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
import javax.swing.*;
/**
* Helper class for full-screen presentation mode.
*/
public class PresentMode {
static GraphicsDevice devices[];
/**
* Index of the default display device, probably the one that p5 was
* started from.
*/
static int defaultIndex;
/**
* Menu object for preferences window
*/
//JMenu preferencesMenu;
static JComboBox selector;
/**
* Index of the currently selected display to be used for present mode.
*/
static GraphicsDevice device;
static {
GraphicsEnvironment environment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
devices = environment.getScreenDevices();
GraphicsDevice defaultDevice = environment.getDefaultScreenDevice();
Vector names = new Vector();
for (int i = 0; i < devices.length; i++) {
String name = String.valueOf(i + 1);
if (devices[i] == defaultDevice) {
defaultIndex = i;
name += " (default)";
}
names.add(name);
}
selector = new JComboBox(names);
selector.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int index = selector.getSelectedIndex();
//device = devices[index];
Preferences.setInteger("run.present.display", index + 1);
}
});
}
static public JComboBox getSelector() {
int deviceIndex = Preferences.getInteger("run.present.display") - 1;
selector.setSelectedIndex(deviceIndex);
return selector;
}
/*
static public JFrame create() {
int deviceIndex = PrePreferences.getInteger("run.present.display") - 1;
if ((deviceIndex < 0) || (deviceIndex >= devices.length)) {
Base.showWarning("Display doesn't exist",
"Present Mode is set to use display " +
(deviceIndex+1) +
" but that doesn't seem to exist. \n" +
"This preference will be reset to " +
"use the default display.", null);
deviceIndex = defaultIndex;
}
//GraphicsDevice device = devices[
//JFrame frame = new JFrame(devices[deviceIndex]);
PresentFrame frame = new PresentFrame(devices[deviceIndex]);
}
public void show() {
setUndecorated(true);
setResizable(false);
device.setFullScreenWindow(this);
}
public Window getWindow() {
return device.getFullScreenWindow(); // isn't this just me?
}
public void dispose() {
Window window = device.getFullScreenWindow();
if (window != null) {
window.dispose();
}
device.setFullScreenWindow(null);
}
*/
}

645
app/Runner.java Normal file
View File

@ -0,0 +1,645 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
//import processing.core.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import com.oroinc.text.regex.*;
/**
* Runs a compiled java applet, whether as an external application
* or internally as an applet object in a window.
*/
public class Runner implements MessageConsumer {
//PApplet applet;
RunnerException exception;
Window window;
PrintStream leechErr;
//String className;
Editor editor;
Sketch sketch;
boolean newMessage;
int messageLineCount;
boolean foundMessageSource;
Process process;
SystemOutSiphon processInput;
OutputStream processOutput;
MessageSiphon processError;
public Runner(Sketch sketch, Editor editor) {
this.sketch = sketch;
this.editor = editor;
}
public void start(Point windowLocation) throws RunnerException {
//System.out.println(" externalRuntime is " + sketch.externalRuntime);
/* this.leechErr = new PrintStream(new MessageStream(this));
try {
if (editor.presenting) {
startPresenting();
} else if (sketch.externalRuntime) {
startExternalRuntime(windowLocation);
} else {
startInternal(windowLocation);
}
} catch (Exception e) {
// this will pass through to the first part of message
// this handles errors that happen inside setup()
e.printStackTrace();
// make sure applet is in use
if (applet != null) applet.finished = true;
leechErr.println(PApplet.LEECH_WAKEUP);
e.printStackTrace(this.leechErr);
}
*/
}
public void startPresenting() throws Exception {
Vector params = new Vector();
params.add("java");
String options = Preferences.get("run.options");
/* if (options.length() > 0) {
String pieces[] = PApplet.split(options, ' ');
for (int i = 0; i < pieces.length; i++) {
String p = pieces[i].trim();
if (p.length() > 0) {
params.add(p);
}
}
}
params.add("-Djava.library.path=" +
sketch.libraryPath +
File.pathSeparator +
System.getProperty("java.library.path"));
params.add("-cp");
params.add(sketch.classPath + Sketchbook.librariesClassPath);
params.add("processing.core.PApplet");
params.add(PApplet.ARGS_EXTERNAL);
params.add(PApplet.ARGS_PRESENT);
params.add(PApplet.ARGS_PRESENT_STOP_COLOR + "=" +
Preferences.get("run.present.stop.color"));
params.add(PApplet.ARGS_BGCOLOR + "=" +
Preferences.get("run.present.bgcolor"));
params.add(PApplet.ARGS_DISPLAY + "=" +
Preferences.get("run.display"));
params.add(PApplet.ARGS_SKETCH_FOLDER + "=" +
sketch.folder.getAbsolutePath());
params.add(sketch.mainClassName);
String command[] = new String[params.size()];
params.copyInto(command);
//PApplet.println(command);
process = Runtime.getRuntime().exec(command);
processInput = new SystemOutSiphon(process.getInputStream());
processError = new MessageSiphon(process.getErrorStream(), this);
processOutput = process.getOutputStream();
*/
}
public void startExternalRuntime(Point windowLocation) throws Exception {
// if there was a saved location (this guy has been run more than
// once) then windowLocation will be set to the last position of
// the sketch window. this will be passed to the PApplet runner
// using something like --external=e30,20 where the e stands for
// exact. otherwise --external=x,y for just the regular positioning.
/* Point editorLocation = editor.getLocation();
String location =
(windowLocation != null) ?
(PApplet.ARGS_LOCATION + "=" +
windowLocation.x + "," + windowLocation.y) :
(PApplet.ARGS_EDITOR_LOCATION + "=" +
editorLocation.x + "," + editorLocation.y);
// this as prefix made the code folder bug go away, but killed stdio
//"cmd", "/c", "start",
// all params have to be stored as separate items,
// so a growable array needs to be used. i.e. -Xms128m -Xmx1024m
// will throw an error if it's shoved into a single array element
Vector params = new Vector();
params.add("java");
String options = Preferences.get("run.options");
if (options.length() > 0) {
String pieces[] = PApplet.split(options, ' ');
for (int i = 0; i < pieces.length; i++) {
String p = pieces[i].trim();
if (p.length() > 0) {
params.add(p);
}
}
}
// sketch.libraryPath might be ""
// librariesClassPath will always have sep char prepended
params.add("-Djava.library.path=" +
sketch.libraryPath +
File.pathSeparator +
System.getProperty("java.library.path"));
params.add("-cp");
params.add(sketch.classPath + Sketchbook.librariesClassPath);
params.add("processing.core.PApplet");
params.add(location);
params.add(PApplet.ARGS_EXTERNAL);
params.add(PApplet.ARGS_DISPLAY + "=" +
Preferences.get("run.display"));
params.add(PApplet.ARGS_SKETCH_FOLDER + "=" +
sketch.folder.getAbsolutePath());
params.add(sketch.mainClassName);
String command[] = new String[params.size()];
params.copyInto(command);
//PApplet.println(command);
process = Runtime.getRuntime().exec(command);
processInput = new SystemOutSiphon(process.getInputStream());
processError = new MessageSiphon(process.getErrorStream(), this);
processOutput = process.getOutputStream();
*/ }
public void startInternal(Point windowLocation) throws Exception {
/* Point editorLocation = editor.getLocation();
//Insets editorInsets = editor.getInsets();
int windowX = editorLocation.x;
int windowY = editorLocation.y + editor.getInsets().top;
RunnerClassLoader loader = new RunnerClassLoader();
Class c = loader.loadClass(sketch.mainClassName);
applet = (PApplet) c.newInstance();
window = new Frame(sketch.name); // use ugly window
((Frame)window).setResizable(false);
if (editor.icon != null) {
((Frame)window).setIconImage(editor.icon);
}
window.pack(); // to get a peer, size set later, need for insets
applet.leechErr = leechErr;
applet.folder = sketch.folder.getAbsolutePath();
applet.frame = (Frame) window;
applet.init();
//applet.start();
while ((applet.width == 0) && !applet.finished) {
try {
if (applet.exception != null) {
throw new RunnerException(applet.exception.getMessage());
}
Thread.sleep(5);
} catch (InterruptedException e) { }
}
window.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
stop();
editor.doClose();
}
});
applet.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
stop();
editor.doClose();
}
}
});
window.add(applet);
Dimension screen =
Toolkit.getDefaultToolkit().getScreenSize();
window.setLayout(null);
Insets insets = window.getInsets();
int minW = Preferences.getInteger("run.window.width.minimum");
int minH = Preferences.getInteger("run.window.height.minimum");
int windowW =
Math.max(applet.width, minW) + insets.left + insets.right;
int windowH =
Math.max(applet.height, minH) + insets.top + insets.bottom;
if (windowX - windowW > 10) { // if it fits to the left of the window
window.setBounds(windowX - windowW, windowY, windowW, windowH);
} else { // if it fits inside the editor window
windowX = editorLocation.x + Preferences.GRID_SIZE * 2; // 66
windowY = editorLocation.y + Preferences.GRID_SIZE * 2; // 66
if ((windowX + windowW > screen.width - Preferences.GRID_SIZE) ||
(windowY + windowH > screen.height - Preferences.GRID_SIZE)) {
// otherwise center on screen
windowX = (screen.width - windowW) / 2;
windowY = (screen.height - windowH) / 2;
}
window.setBounds(windowX, windowY, windowW, windowH); //ww, wh);
}
Color windowBgColor = Preferences.getColor("run.window.bgcolor");
window.setBackground(windowBgColor);
int usableH = windowH - insets.top - insets.bottom;
applet.setBounds((windowW - applet.width)/2,
insets.top + (usableH - applet.height) / 2,
windowW, windowH);
applet.setVisible(true); // no effect
if (windowLocation != null) {
window.setLocation(windowLocation);
}
window.show();
applet.requestFocus(); // necessary for key events
*/
}
public void stop() {
/* // check for null in case stop is called during compilation
if (applet != null) {
applet.stop();
// above avoids NullPointerExceptions
// but still threading is too complex, and so
// some boogers are being left behind
applet = null;
} else if (process != null) { // running externally
try {
processOutput.write(PApplet.EXTERNAL_STOP);
processOutput.flush();
} catch (IOException e) {
close();
}
}
*/
}
public void close() {
/* //if (window != null) window.hide();
if (window != null) {
//System.err.println("disposing window");
window.dispose();
window = null;
}
if (process != null) {
try {
process.destroy();
} catch (Exception e) {
//System.err.println("(ignored) error while destroying");
//e.printStackTrace();
}
process = null;
}
*/
}
// made synchronized for rev 87
synchronized public void message(String s) {
//System.out.println("M" + s.length() + ":" + s.trim()); // + "MMM" + s.length());
// this eats the CRLFs on the lines.. oops.. do it later
//if (s.trim().length() == 0) return;
// this is PApplet sending a message (via System.out.println)
// that signals that the applet has been quit.
// if (s.indexOf(PApplet.EXTERNAL_QUIT) == 0) {
//System.out.println("external: quit");
// editor.doClose();
// return;
// }
// this is the PApplet sending us a message that the applet
// is being moved to a new window location
// if (s.indexOf(PApplet.EXTERNAL_MOVE) == 0) {
// String nums = s.substring(s.indexOf(' ') + 1).trim();
// int space = nums.indexOf(' ');
// int left = Integer.parseInt(nums.substring(0, space));
// int top = Integer.parseInt(nums.substring(space + 1));
// editor.appletLocation = new Point(left, top);
// //System.out.println("external: move to " + left + " " + top);
//// return;
// }
// this is PApplet sending a message saying "i'm about to spew
// a stack trace because an error occurred during PApplet.run()"
// if (s.indexOf(PApplet.LEECH_WAKEUP) == 0) {
// newMessage being set to 'true' means that the next time
// message() is called, expect the first line of the actual
// // error message & stack trace to be sent from the applet.
// newMessage = true;
// return; // this line ignored
// }
// these are used for debugging, in case there are concerns
// that some errors aren't coming through properly
/*
if (s.length() > 2) {
System.err.println(newMessage);
System.err.println("message " + s.length() + ":" + s);
}
*/
// always shove out the mesage, since it might not fall under
// the same setup as we're expecting
System.err.print(s);
//System.err.println("[" + s.length() + "] " + s);
System.err.flush();
// exit here because otherwise the exception name
// may be titled with a blank string
if (s.trim().length() == 0) return;
// annoying, because it seems as though the terminators
// aren't being sent properly
//System.err.println(s);
//if (newMessage && s.length() > 2) {
if (newMessage) {
exception = new RunnerException(s); // type of java ex
exception.hideStackTrace = true;
//System.out.println("setting ex type to " + s);
newMessage = false;
foundMessageSource = false;
messageLineCount = 0;
} else {
messageLineCount++;
/*
java.lang.NullPointerException
at javatest.<init>(javatest.java:5)
at Temporary_2425_1153.draw(Temporary_2425_1153.java:11)
at PApplet.nextFrame(PApplet.java:481)
at PApplet.run(PApplet.java:428)
at java.lang.Thread.run(Unknown Source)
*/
if (!foundMessageSource) {
// " at javatest.<init>(javatest.java:5)"
// -> "javatest.<init>(javatest.java:5)"
int afterAt = s.indexOf("at") + 3;
//if (afterAt == -1) {
if (afterAt == 2) { // means indexOf was -1
//System.err.println(s); // stop double-printing exceptions
return;
}
s = s.substring(afterAt + 1);
// "javatest.<init>(javatest.java:5)"
// -> "javatest.<init>" and "(javatest.java:5)"
int startParen = s.indexOf('(');
// at javatest.<init>(javatest.java:5)
String pkgClassFxn = null;
//String fileLine = null;
int codeIndex = -1;
int lineNumber = -1;
if (startParen == -1) {
pkgClassFxn = s;
} else {
pkgClassFxn = s.substring(0, startParen);
// "(javatest.java:5)"
String fileAndLine = s.substring(startParen + 1);
int stopParen = fileAndLine.indexOf(')');
//fileAndLine = fileAndLine.substring(0, fileAndLine.length() - 1);
fileAndLine = fileAndLine.substring(0, stopParen);
//System.out.println("file 'n line " + fileAndLine);
//if (!fileAndLine.equals("Unknown Source")) {
// "javatest.java:5"
int colonIndex = fileAndLine.indexOf(':');
if (colonIndex != -1) {
String filename = fileAndLine.substring(0, colonIndex);
// "javatest.java" and "5"
//System.out.println("filename = " + filename);
//System.out.println("pre0 = " + sketch.code[0].preprocName);
//for (int i = 0; i < sketch.codeCount; i++) {
//System.out.println(i + " " + sketch.code[i].lineOffset + " " +
// sketch.code[i].preprocName);
//}
lineNumber =
Integer.parseInt(fileAndLine.substring(colonIndex + 1)) - 1;
for (int i = 0; i < sketch.codeCount; i++) {
SketchCode code = sketch.code[i];
//System.out.println(code.preprocName + " " + lineNumber + " " +
// code.preprocOffset);
if (((code.preprocName == null) &&
(lineNumber >= code.preprocOffset)) ||
((code.preprocName != null) &&
code.preprocName.equals(filename))) {
codeIndex = i;
//System.out.println("got codeindex " + codeIndex);
//break;
//} else if (
}
}
if (codeIndex != -1) {
// in case this was a tab that got embedded into the main .java
lineNumber -= sketch.code[codeIndex].preprocOffset;
// this may have a paren on the end, if so need to strip
// down to just the digits
/*
int lastNumberIndex = colonIndex + 1;
while ((lastNumberIndex < fileAndLine.length()) &&
Character.isDigit(fileAndLine.charAt(lastNumberIndex))) {
lastNumberIndex++;
}
*/
// lineNumber is 1-indexed, but editor wants zero-indexed
// getMessage() will be what's shown in the editor
exception =
new RunnerException(exception.getMessage(),
codeIndex, lineNumber, -1);
exception.hideStackTrace = true;
foundMessageSource = true;
}
}
}
editor.error(exception);
/*
int index = s.indexOf(className + ".java");
if (index != -1) {
int len = (className + ".java").length();
String lineNumberStr = s.substring(index + len + 1);
index = lineNumberStr.indexOf(')');
lineNumberStr = lineNumberStr.substring(0, index);
try {
exception.line = Integer.parseInt(lineNumberStr) - 1; //2;
} catch (NumberFormatException e) { }
//e.printStackTrace(); // a recursive error waiting to happen?
// if nfe occurs, who cares, still send the error on up
editor.error(exception);
*/
/*
// WARNING THESE ARE DISABLED!!
} else if ((index = s.indexOf(className + ".class")) != -1) {
// code to check for:
// at Temporary_484_3845.loop(Compiled Code)
// would also probably get:
// at Temporary_484_3845.loop
// which (i believe) is used by the mac and/or jview
String functionStr = s.substring(index +
(className + ".class").length() + 1);
index = functionStr.indexOf('(');
if (index != -1) {
functionStr = functionStr.substring(0, index);
}
exception = new RunnerException(//"inside \"" + functionStr + "()\": " +
exception.getMessage() +
" inside " + functionStr + "() " +
"[add Compiler.disable() to setup()]");
editor.error(exception);
// this will fall through in tihs example:
// at Temporary_4636_9696.pootie(Compiled Code)
// at Temporary_4636_9696.loop(Temporary_4636_9696.java:24)
// because pootie() (re)sets the exception title
// and throws it, but then the line number gets set
// because of the line that comes after
*/
} else if (messageLineCount > 10) { // 5 -> 10 for 0088
// this means the class name may not be mentioned
// in the stack trace.. this is just a general purpose
// error, but needs to make it through anyway.
// so if five lines have gone past, might as well signal
messageLineCount = -100;
exception = new RunnerException(exception.getMessage());
exception.hideStackTrace = true;
editor.error(exception);
} else {
//System.err.print(s);
}
//System.out.println("got it " + s);
}
}
//////////////////////////////////////////////////////////////
/**
* Siphons from an InputStream of System.out (from a Process)
* and sends it to the real System.out.
*/
class SystemOutSiphon implements Runnable {
InputStream input;
Thread thread;
public SystemOutSiphon(InputStream input) {
this.input = input;
thread = new Thread(this);
// unless this is set to min, it seems to hork the app
// since it's in charge of stuffing the editor console with strings
// maybe it's time to get rid of/fix that friggin console
// ...disabled for 0075, with 0074's fix for code folder hanging
// this only seems to make the console unresponsive
//thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
public void run() {
byte boofer[] = new byte[256];
while (Thread.currentThread() == thread) {
try {
// can't use a buffered reader here because incremental
// print statements are interesting too.. causes some
// disparity with how System.err gets spewed, oh well.
int count = input.read(boofer, 0, boofer.length);
if (count == -1) {
thread = null;
} else {
System.out.print(new String(boofer, 0, count));
//System.out.flush();
}
} catch (IOException e) {
// this is prolly because the app was quit & the stream broken
//e.printStackTrace(System.out);
//e.printStackTrace();
thread = null;
} catch (Exception e) {
//System.out.println("SystemOutSiphon: i just died in your arms tonight");
// on mac os x, this will spew a "Bad File Descriptor" ex
// each time an external app is shut down.
//e.printStackTrace();
thread = null;
//System.out.println("");
}
//System.out.println("SystemOutSiphon: out");
//thread = null;
}
}
}
}

128
app/RunnerClassLoader.java Normal file
View File

@ -0,0 +1,128 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Based on Simple1.2ClassLoader.java - simple Java 1.2 class loader
Copyright (c) 1999 Ken McCrary, All Rights Reserved.
Permission to use, copy, modify, and distribute this software
and its documentation for NON-COMMERCIAL purposes and without
fee is hereby granted provided that this copyright notice
appears in all copies.
KEN MCCRARY MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. KEN MCCRARY
SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT
OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
package processing.app;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.jar.*;
/**
* Simple class loader adapted for Processing.
* <P>
* Based on code from Ken McCrary.
*/
public class RunnerClassLoader extends ClassLoader {
String buildFolderPath;
RunnerClassLoader() {
buildFolderPath = Base.getBuildFolder().getAbsolutePath();
}
/**
* This is the method where the task of class loading
* is delegated to our custom loader.
*
* @param name the name of the class
* @return the resulting <code>Class</code> object
* @exception ClassNotFoundException if the class could not be found
*/
protected Class findClass(String name) throws ClassNotFoundException {
FileInputStream fi = null;
try {
String path =
buildFolderPath + File.separator + name.replace('.', '/');
//System.out.println("(from " + path + ")");
fi = new FileInputStream(path + ".class");
byte[] classBytes = new byte[fi.available()];
fi.read(classBytes);
//definePackage(name);
return defineClass(name, classBytes, 0, classBytes.length);
} catch (Exception e) {
// could not find the class, so indicate the problem with an exception
throw new ClassNotFoundException(name);
} finally {
if (fi != null) {
try {
fi.close();
} catch (Exception e) { }
}
}
}
/**
* Identify where to load a resource from, resources for
* this simple ClassLoader are in a directory name "store"
*
* @param name the resource name
* @return URL for resource or null if not found
*/
protected URL findResource(String name) {
String path =
buildFolderPath + File.separator + name.replace('.', '/');
File searchResource = new File(path, name);
//URL result = null;
if (searchResource.exists()) {
try {
return searchResource.toURL();
} catch (MalformedURLException mfe) { }
}
//return result;
return null;
}
/**
* Used for identifying resources from multiple URLS
* Since our simple Classloader only has one repository
* the returned Enumeration contains 0 to 1 items
*
* @param name the resource name
* @return Enumeration of one URL
*/
protected Enumeration findResources(final String name) throws IOException {
// Since we only have a single repository we will only have one
// resource of a particular name, the Enumeration will just return
// this single URL
return new Enumeration() {
URL resource = findResource(name);
public boolean hasMoreElements() {
return ( resource != null ? true : false);
}
public Object nextElement() {
if (!hasMoreElements()) {
throw new NoSuchElementException();
} else {
URL result = resource;
resource = null;
return result;
}
}
};
}
}

83
app/RunnerException.java Normal file
View File

@ -0,0 +1,83 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
/**
* An exception with a line number attached that occurs
* during either compile time or run time.
*/
public class RunnerException extends Exception {
public int file = -1;
public int line = -1;
public int column = -1;
public boolean hideStackTrace;
public RunnerException() { }
public RunnerException(String message) {
super(massage(message));
}
public RunnerException(String message, int line) {
super(massage(message));
this.line = line;
}
public RunnerException(String message, int line, int column) {
super(massage(message));
this.line = line;
this.column = column;
}
public RunnerException(String message, int file, int line, int column) {
super(massage(message));
this.file = file;
this.line = line;
this.column = column;
}
/**
* Nix the java.lang crap out of an exception message
* because it scares the children.
* <P>
* This function must be static to be used with super()
* in each of the constructors above.
*/
static public final String massage(String msg) {
if (msg.indexOf("java.lang.") == 0) {
//int dot = msg.lastIndexOf('.');
msg = msg.substring("java.lang.".length());
}
return msg;
//return (dot == -1) ? msg : msg.substring(dot+1);
}
public void printStackTrace() {
if (!hideStackTrace) {
super.printStackTrace();
}
}
}

1804
app/Sketch.java Normal file

File diff suppressed because it is too large Load Diff

105
app/SketchCode.java Normal file
View File

@ -0,0 +1,105 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
SketchCode - data class for a single file inside a sketch
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import processing.app.syntax.*;
import java.io.*;
import javax.swing.text.*;
import javax.swing.undo.*;
public class SketchCode {
/** Pretty name (no extension), not the full file name */
public String name;
/** File object for where this code is located */
public File file;
/** Type of code in this tab, Sketch.PDE or Sketch.JAVA */
public int flavor;
/** Text of the program text for this tab */
public String program;
/** Document object for this tab */
public SyntaxDocument document;
/** Undo Manager for this tab, each tab keeps track of their own */
public UndoManager undo; // = new UndoManager();
// saved positions from last time this tab was used
public int selectionStart;
public int selectionStop;
public int scrollPosition;
public boolean modified;
//SketchHistory history; // TODO add history information
String preprocName; // name of .java file after preproc
int preprocOffset; // where this code starts relative to the concat'd code
public SketchCode(String name, File file, int flavor) {
this.name = name;
this.file = file;
this.flavor = flavor;
try {
load();
} catch (IOException e) {
System.err.println("error while loading code " + name);
}
}
/**
* Load this piece of code from a file.
*/
public void load() throws IOException {
program = Base.loadFile(file);
modified = false;
}
/**
* Save this piece of code, regardless of whether the modified
* flag is set or not.
*/
public void save() throws IOException {
// TODO re-enable history
//history.record(s, SketchHistory.SAVE);
Base.saveFile(program, file);
modified = false;
}
/**
* Save this file to another location, used by Sketch.saveAs()
*/
public void saveAs(File newFile) throws IOException {
Base.saveFile(program, newFile);
}
}

357
app/SketchHistory.java Normal file
View File

@ -0,0 +1,357 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
SketchHistory - handler for storing history information about a project
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.swing.*;
public class SketchHistory {
Editor editor;
// why things have been saved for history
static final int RUN = 5;
static final int SAVE = 6;
static final int AUTOSAVE = 7;
static final int BEAUTIFY = 8;
static final String HISTORY_SEPARATOR =
"#################################################";
JMenu menu;
// true if the sketch is read-only,
// meaning that no history will be recorded
boolean readOnlySketch;
File historyFile;
String lastRecorded;
ActionListener menuListener;
//public SketchHistory(Editor editor) {
//this.editor = editor;
//}
public SketchHistory(Sketch sketch) {
menu = new JMenu("History");
menuListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
retrieve(e.getActionCommand());
}
};
}
/// Set the path for the current sketch
public void setPath(String path, boolean readOnlySketch) {
this.readOnlySketch = true;
if (readOnlySketch) return;
historyFile = new File(path, "history.gz");
}
public void attachMenu(JMenu parent) {
//if (Preferences.getBoolean("history.recording")) {
parent.add(menu);
// should leave enabled, since can still get old history
// even if the new stuff isn't being recorded
//menu.setEnabled(Preferences.getBoolean("history.recording"));
//}
}
/// Check to see if history should be recorded.
/// mode is RUN, SAVE, AUTOSAVE, or BEAUTIFY
public void record(String program, int mode) {
if (readOnlySketch) return;
if (!Preferences.getBoolean("history.recording")) return;
if ((lastRecorded != null) &&
(lastRecorded.equals(program))) return;
String modeStr = null;
switch (mode) {
case RUN: modeStr = "run"; break;
case SAVE: modeStr = "save"; break;
case AUTOSAVE: modeStr = "autosave"; break;
case BEAUTIFY: modeStr = "beautify"; break;
}
try {
boolean noPreviousHistory = false;
ByteArrayOutputStream old = null;
if (historyFile.exists()) {
InputStream oldStream = new GZIPInputStream(new BufferedInputStream(new FileInputStream(historyFile)));
old = new ByteArrayOutputStream();
int c = oldStream.read();
while (c != -1) {
old.write(c);
c = oldStream.read();
}
//return out.toByteArray();
oldStream.close();
} else {
noPreviousHistory = true; // rebuild menu
}
OutputStream historyStream =
new GZIPOutputStream(new FileOutputStream(historyFile));
if (old != null) {
historyStream.write(old.toByteArray());
}
PrintWriter historyWriter =
new PrintWriter(new OutputStreamWriter(historyStream));
historyWriter.println();
historyWriter.println(HISTORY_SEPARATOR);
Calendar now = Calendar.getInstance();
// 2002 06 18 11 43 29
// when listing, study for descrepancies.. if all are
// 2002, then don't list the year and soforth.
// for the other end, if all minutes are unique,
// then don't show seconds
int year = now.get(Calendar.YEAR);
int month = now.get(Calendar.MONTH) + 1;
int day = now.get(Calendar.DAY_OF_MONTH);
int hour = now.get(Calendar.HOUR_OF_DAY);
int minute = now.get(Calendar.MINUTE);
int second = now.get(Calendar.SECOND);
String parseDate = year + " " + month + " " + day + " " +
hour + " " + minute + " " + second;
String readableDate = now.getTime().toString();
// increment this so sketchbook won't be mangled
// each time this format has to change
String historyVersion = "1";
//Date date = new Date();
//String datestamp = date.toString();
historyWriter.println(historyVersion + " " + modeStr + " - " +
parseDate + " - " + readableDate);
historyWriter.println();
historyWriter.println(program);
historyWriter.flush(); // ??
lastRecorded = program;
//JMenuItem menuItem = new JMenuItem(modeStr + " - " + readableDate);
JMenuItem menuItem = new JMenuItem(modeStr + " - " + readableDate);
menuItem.addActionListener(menuListener);
menu.insert(menuItem, 2);
historyWriter.flush();
historyWriter.close();
if (noPreviousHistory) {
// to get add the actual menu, to get the 'clear' item in there
//rebuildMenu(historyFile.getPath());
rebuildMenu();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void retrieve(String selection) {
//System.out.println("sel '" + selection + "'");
String readableDate =
selection.substring(selection.indexOf("-") + 2);
// make history for the current guy
record(editor.textarea.getText(), AUTOSAVE);
// mark editor text as having been edited
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(historyFile))));
String line = null;
int historyCount = 0;
String historyList[] = new String[100];
try {
boolean found = false;
while ((line = reader.readLine()) != null) {
//System.out.println("->" + line);
if (line.equals(HISTORY_SEPARATOR)) {
line = reader.readLine();
if (line.indexOf(readableDate) != -1) { // this is the one
found = true;
break;
}
}
}
if (found) {
// read lines until the next separator
line = reader.readLine(); // ignored
//String sep = System.getProperty("line.separator");
StringBuffer buffer = new StringBuffer();
while ((line = reader.readLine()) != null) {
if (line.equals(HISTORY_SEPARATOR)) break;
//textarea.append(line + sep);
//buffer.append(line + sep); // JTextPane wants only \n going in
buffer.append(line + "\n");
//System.out.println("'" + line + "'");
}
//textarea.editorSetText(buffer.toString());
editor.changeText(buffer.toString(), true);
lastRecorded = editor.textarea.getText();
editor.setSketchModified(false);
} else {
System.err.println("couldn't find history entry for " +
"'" + readableDate + "'");
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// class HistoryMenuListener implements ActionListener {
// public void actionPerformed(ActionEvent e) {
// editor.selectHistory(e.getActionCommand);
// }
// }
//public void rebuildHistoryMenu(String path) {
//rebuildHistoryMenu(historyMenu, path);
//}
//public void rebuildHistoryMenu(Menu menu, String path) {
public void rebuildMenu() { //String path) {
//if (!recordingHistory) return;
//if (!Preferences.getBoolean("history.recording")) return;
menu.removeAll();
//File hfile = new File(path);
//if (!hfile.exists()) return; // no history yet
if (!historyFile.exists()) return;
JMenuItem item = new JMenuItem("Clear History");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!historyFile.delete()) {
//System.err.println("couldn't erase history");
Base.showWarning("History Problem",
"Could not erase history", null);
}
rebuildMenu();
//SketchHistory.this.rebuildMenu(historyFile.getPath());
}
});
menu.add(item);
menu.addSeparator();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(historyFile))));
String line = null;
int historyCount = 0;
String historyList[] = new String[100];
try {
while ((line = reader.readLine()) != null) {
//while (line = reader.readLine()) {
//while (true) { line = reader.readLine();
//if (line == null) continue;
//System.out.println("line: " + line);
if (line.equals(HISTORY_SEPARATOR)) {
// next line is the good stuff
line = reader.readLine();
int version =
Integer.parseInt(line.substring(0, line.indexOf(' ')));
if (version == 1) {
String whysub = line.substring(2); // after "1 "
String why = whysub.substring(0, whysub.indexOf(" -"));
//System.out.println("'" + why + "'");
String readable = line.substring(line.lastIndexOf("-") + 2);
if (historyList.length == historyCount) {
String temp[] = new String[historyCount*2];
System.arraycopy(historyList, 0, temp, 0, historyCount);
historyList = temp;
}
historyList[historyCount++] = why + " - " + readable;
} // otherwise don't know what to do
}
}
//System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
// add the items to the menu in reverse order
//ActionListener historyMenuListener =
// new ActionListener() {
// public void actionPerformed(ActionEvent e) {
// editor.retrieveHistory(e.getActionCommand());
//}
//};
for (int i = historyCount-1; i >= 0; --i) {
JMenuItem mi = new JMenuItem(historyList[i]);
mi.addActionListener(menuListener);
menu.add(mi);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
*/

647
app/Sketchbook.java Normal file
View File

@ -0,0 +1,647 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;
import java.util.zip.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.undo.*;
import com.apple.mrj.*;
/**
* Handles sketchbook mechanics for the sketch menu and file I/O.
*/
public class Sketchbook {
Editor editor;
JMenu openMenu;
JMenu popupMenu;
//JMenu examples;
JMenu importMenu;
// set to true after the first time it's built.
// so that the errors while building don't show up again.
boolean builtOnce;
//File sketchbookFolder;
//String sketchbookPath; // canonical path
// last file/directory used for file opening
//String handleOpenDirectory;
// opted against this.. in imovie, apple always goes
// to the "Movies" folder, even if that wasn't the last used
// these are static because they're used by Sketch
static File examplesFolder;
static String examplesPath; // canonical path (for comparison)
static File librariesFolder;
static String librariesPath;
// maps imported packages to their library folder
static Hashtable importToLibraryTable = new Hashtable();
// classpath for all known libraries for p5
// (both those in the p5/libs folder and those with lib subfolders
// found in the sketchbook)
static String librariesClassPath;
public Sketchbook(Editor editor) {
this.editor = editor;
// this shouldn't change throughout.. it may as well be static
// but only one instance of sketchbook will be built so who cares
examplesFolder = new File(System.getProperty("user.dir"), "examples");
examplesPath = examplesFolder.getAbsolutePath();
librariesFolder = new File(System.getProperty("user.dir"), "libraries");
librariesPath = librariesFolder.getAbsolutePath();
String sketchbookPath = Preferences.get("sketchbook.path");
// if a value is at least set, first check to see if the
// folder exists. if it doesn't, warn the user that the
// sketchbook folder is being reset.
if (sketchbookPath != null) {
File skechbookFolder = new File(sketchbookPath);
if (!skechbookFolder.exists()) {
Base.showWarning("Sketchbook folder disappeared",
"The sketchbook folder no longer exists,\n" +
"so a new sketchbook will be created in the\n" +
"default location.", null);
sketchbookPath = null;
}
}
if (sketchbookPath == null) {
// by default, set default sketchbook path to the user's
// home folder with 'sketchbook' as a subdirectory of that
/*
File home = new File(System.getProperty("user.home"));
if (Base.platform == Base.MACOSX) {
// on macosx put the sketchbook in the "Documents" folder
home = new File(home, "Documents");
} else if (Base.platform == Base.WINDOWS) {
// on windows put the sketchbook in the "My Documents" folder
home = new File(home, "My Documents");
}
*/
// use a subfolder called 'sketchbook'
//File home = Preferences.getProcessingHome();
//String folderName = Preferences.get("sketchbook.name.default");
//File sketchbookFolder = new File(home, folderName);
//System.out.println("resetting sketchbook path");
File sketchbookFolder = Base.getDefaultSketchbookFolder();
Preferences.set("sketchbook.path",
sketchbookFolder.getAbsolutePath());
if (!sketchbookFolder.exists()) sketchbookFolder.mkdirs();
}
openMenu = new JMenu("Sketchbook");
popupMenu = new JMenu("Sketchbook");
importMenu = new JMenu("Import Library");
}
static public String getSketchbookPath() {
return Preferences.get("sketchbook.path");
}
/**
* Handle creating a sketch folder, return its base .pde file
* or null if the operation was cancelled.
*/
public String handleNew(boolean noPrompt,
boolean shift,
boolean library) throws IOException {
File newbieDir = null;
String newbieName = null;
boolean prompt = Preferences.getBoolean("sketchbook.prompt");
if (shift) prompt = !prompt; // reverse behavior if shift is down
// no sketch has been started, don't prompt for the name if it's
// starting up, just make the farker. otherwise if the person hits
// 'cancel' i'd have to add a thing to make p5 quit, which is silly.
// instead give them an empty sketch, and they can look at examples.
// i hate it when imovie makes you start with that goofy dialog box.
// unless, ermm, they user tested it and people preferred that as
// a way to get started. shite. now i hate myself.
//
if (noPrompt) prompt = false;
if (prompt) {
//if (!startup) {
// prompt for the filename and location for the new sketch
FileDialog fd = new FileDialog(editor, //new Frame(),
//"Create new sketch named",
"Create sketch folder named:",
FileDialog.SAVE);
fd.setDirectory(getSketchbookPath());
fd.show();
String newbieParentDir = fd.getDirectory();
newbieName = fd.getFile();
if (newbieName == null) return null;
newbieName = sanitizeName(newbieName);
newbieDir = new File(newbieParentDir, newbieName);
} else {
// use a generic name like sketch_031008a, the date plus a char
String newbieParentDir = getSketchbookPath();
int index = 0;
SimpleDateFormat formatter = new SimpleDateFormat("yyMMdd");
String purty = formatter.format(new Date());
do {
newbieName = "sketch_" + purty + ((char) ('a' + index));
newbieDir = new File(newbieParentDir, newbieName);
index++;
} while (newbieDir.exists());
}
// make the directory for the new sketch
newbieDir.mkdirs();
// if it's a library, make a library subfolder to tag it as such
if (library) {
new File(newbieDir, "library").mkdirs();
}
// make an empty pde file
File newbieFile = new File(newbieDir, newbieName + ".pde");
new FileOutputStream(newbieFile); // create the file
// TODO this wouldn't be needed if i could figure out how to
// associate document icons via a dot-extension/mime-type scenario
// help me steve jobs, you're my only hope.
// jdk13 on osx, or jdk11
// though apparently still available for 1.4
if (Base.isMacOS()) {
MRJFileUtils.setFileTypeAndCreator(newbieFile,
MRJOSType.kTypeTEXT,
new MRJOSType("Pde1"));
// thank you apple, for changing this @#$)(*
//com.apple.eio.setFileTypeAndCreator(String filename, int, int)
}
// make a note of a newly added sketch in the sketchbook menu
rebuildMenus();
// now open it up
//handleOpen(newbieName, newbieFile, newbieDir);
//return newSketch;
return newbieFile.getAbsolutePath();
}
/**
* Convert to sanitized name and alert the user
* if changes were made.
*/
static public String sanitizeName(String origName) {
String newName = sanitizedName(origName);
if (!newName.equals(origName)) {
Base.showMessage("Naming issue",
"The sketch name had to be modified.\n" +
"You can only use basic letters and numbers\n" +
"to name a sketch (ascii only and no spaces,\n" +
"it can't start with a number, and should be\n" +
"less than 64 characters long)");
}
return newName;
}
/**
* Java classes are pretty limited about what you can use
* for their naming. This helper function replaces everything
* but A-Z, a-z, and 0-9 with underscores. Also disallows
* starting the sketch name with a digit.
*/
static public String sanitizedName(String origName) {
char c[] = origName.toCharArray();
StringBuffer buffer = new StringBuffer();
// can't lead with a digit, so start with an underscore
if ((c[0] >= '0') && (c[0] <= '9')) {
buffer.append('_');
}
for (int i = 0; i < c.length; i++) {
if (((c[i] >= '0') && (c[i] <= '9')) ||
((c[i] >= 'a') && (c[i] <= 'z')) ||
((c[i] >= 'A') && (c[i] <= 'Z'))) {
buffer.append(c[i]);
} else {
buffer.append('_');
}
}
// let's not be ridiculous about the length of filenames
if (buffer.length() > 63) {
buffer.setLength(63);
}
return buffer.toString();
}
public String handleOpen() {
// swing's file choosers are ass ugly, so we use the
// native (awt peered) dialogs instead
FileDialog fd = new FileDialog(editor, //new Frame(),
"Open a Processing sketch...",
FileDialog.LOAD);
//fd.setDirectory(Preferences.get("sketchbook.path"));
fd.setDirectory(getSketchbookPath());
// only show .pde files as eligible bachelors
// TODO this doesn't seem to ever be used. AWESOME.
fd.setFilenameFilter(new FilenameFilter() {
public boolean accept(File dir, String name) {
//System.out.println("check filter on " + dir + " " + name);
return name.toLowerCase().endsWith(".pde");
}
});
// gimme some money
fd.show();
// what in the hell yu want, boy?
String directory = fd.getDirectory();
String filename = fd.getFile();
// user cancelled selection
if (filename == null) return null;
// this may come in handy sometime
//handleOpenDirectory = directory;
File selection = new File(directory, filename);
return selection.getAbsolutePath();
}
/**
* Rebuild the menu full of sketches based on the
* contents of the sketchbook.
*
* Creates a separate JMenu object for the popup,
* because it seems that after calling "getPopupMenu"
* the menu will disappear from its original location.
*/
public void rebuildMenus() {
try {
// rebuild file/open and the toolbar popup menus
buildMenu(openMenu);
builtOnce = true; // disable error messages while loading
buildMenu(popupMenu);
// rebuild the "import library" menu
librariesClassPath = "";
importMenu.removeAll();
if (addLibraries(importMenu, new File(getSketchbookPath()))) {
importMenu.addSeparator();
}
if (addLibraries(importMenu, examplesFolder)) {
importMenu.addSeparator();
}
addLibraries(importMenu, librariesFolder);
//System.out.println("libraries cp is now " + librariesClassPath);
} catch (IOException e) {
Base.showWarning("Problem while building sketchbook menu",
"There was a problem with building the\n" +
"sketchbook menu. Things might get a little\n" +
"kooky around here.", e);
}
}
public void buildMenu(JMenu menu) {
JMenuItem item;
// rebuild the popup menu
menu.removeAll();
//item = new JMenuItem("Open...");
item = Editor.newJMenuItem("Open...", 'O', false);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.handleOpen(null);
}
});
menu.add(item);
menu.addSeparator();
try {
boolean sketches =
addSketches(menu, new File(getSketchbookPath()));
if (sketches) menu.addSeparator();
} catch (IOException e) {
e.printStackTrace();
}
try {
JMenu examplesMenu = new JMenu("Examples");
addSketches(examplesMenu, examplesFolder);
menu.add(examplesMenu);
} catch (IOException e) {
e.printStackTrace();
}
/*
// don't do this until it's finished
// libraries don't show up as proper sketches anyway
try {
if (Preferences.getBoolean("export.library")) {
JMenu librariesMenu = new JMenu("Libraries");
addSketches(librariesMenu, librariesFolder);
menu.add(librariesMenu);
}
} catch (IOException e) {
e.printStackTrace();
}
*/
}
public JMenu getOpenMenu() {
if (openMenu == null) rebuildMenus();
return openMenu;
}
public JPopupMenu getPopupMenu() {
if (popupMenu == null) rebuildMenus();
return popupMenu.getPopupMenu();
}
public JMenu getImportMenu() {
return importMenu;
}
protected boolean addSketches(JMenu menu, File folder) throws IOException {
// skip .DS_Store files, etc
if (!folder.isDirectory()) return false;
String list[] = folder.list();
// if a bad folder or something like that, this might come back null
if (list == null) return false;
// alphabetize list, since it's not always alpha order
// use cheapie bubble-style sort which should be fine
// since not a tone of files, and things will mostly be sorted
// or may be completely sorted already by the os
for (int i = 0; i < list.length; i++) {
int who = i;
for (int j = i+1; j < list.length; j++) {
if (list[j].compareToIgnoreCase(list[who]) < 0) {
who = j; // this guy is earlier in the alphabet
}
}
if (who != i) { // swap with someone if changes made
String temp = list[who];
list[who] = list[i];
list[i] = temp;
}
}
//SketchbookMenuListener listener =
//new SketchbookMenuListener(folder.getAbsolutePath());
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.handleOpen(e.getActionCommand());
}
};
boolean ifound = false;
for (int i = 0; i < list.length; i++) {
if ((list[i].charAt(0) == '.') ||
list[i].equals("CVS")) continue;
File subfolder = new File(folder, list[i]);
File lib = new File(subfolder, "library");
File entry = new File(subfolder, list[i] + ".pde");
// if a .pde file of the same prefix as the folder exists..
if (entry.exists()) {
String sanityCheck = sanitizedName(list[i]);
if (!sanityCheck.equals(list[i])) {
if (!builtOnce) {
String mess =
"The sketch \"" + list[i] + "\" cannot be used.\n" +
"Sketch names must contain only basic letters and numbers.\n" +
"(ascii only and no spaces, and it cannot start with a number)";
Base.showMessage("Ignoring bad sketch name", mess);
}
continue;
}
JMenuItem item = new JMenuItem(list[i]);
item.addActionListener(listener);
item.setActionCommand(entry.getAbsolutePath());
menu.add(item);
ifound = true;
} else { // might contain other dirs, get recursive
JMenu submenu = new JMenu(list[i]);
// needs to be separate var
// otherwise would set ifound to false
boolean found = addSketches(submenu, subfolder); //, false);
if (found) {
menu.add(submenu);
ifound = true;
}
}
}
return ifound; // actually ignored, but..
}
protected boolean addLibraries(JMenu menu, File folder) throws IOException {
// skip .DS_Store files, etc
if (!folder.isDirectory()) return false;
String list[] = folder.list();
// if a bad folder or something like that, this might come back null
if (list == null) return false;
// alphabetize list, since it's not always alpha order
// use cheapie bubble-style sort which should be fine
// since not a tone of files, and things will mostly be sorted
// or may be completely sorted already by the os
for (int i = 0; i < list.length; i++) {
int who = i;
for (int j = i+1; j < list.length; j++) {
if (list[j].compareToIgnoreCase(list[who]) < 0) {
who = j; // this guy is earlier in the alphabet
}
}
if (who != i) { // swap with someone if changes made
String temp = list[who];
list[who] = list[i];
list[i] = temp;
}
}
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.sketch.importLibrary(e.getActionCommand());
}
};
boolean ifound = false;
for (int i = 0; i < list.length; i++) {
if ((list[i].charAt(0) == '.') ||
list[i].equals("CVS")) continue;
File subfolder = new File(folder, list[i]);
File exported = new File(subfolder, "library");
File entry = new File(exported, list[i] + ".jar");
// if a .jar file of the same prefix as the folder exists
// inside the 'library' subfolder of the sketch
if (entry.exists()) {
String sanityCheck = sanitizedName(list[i]);
if (!sanityCheck.equals(list[i])) {
String mess =
"The library \"" + list[i] + "\" cannot be used.\n" +
"Library names must contain only basic letters and numbers.\n" +
"(ascii only and no spaces, and it cannot start with a number)";
Base.showMessage("Ignoring bad sketch name", mess);
continue;
}
// get the path for all .jar files in this code folder
String libraryClassPath =
Compiler.contentsToClassPath(exported);
// grab all jars and classes from this folder,
// and append them to the library classpath
librariesClassPath +=
File.pathSeparatorChar + libraryClassPath;
// need to associate each import with a library folder
String packages[] =
Compiler.packageListFromClassPath(libraryClassPath);
for (int k = 0; k < packages.length; k++) {
importToLibraryTable.put(packages[k], exported);
}
JMenuItem item = new JMenuItem(list[i]);
item.addActionListener(listener);
item.setActionCommand(entry.getAbsolutePath());
menu.add(item);
ifound = true;
} else { // might contain other dirs, get recursive
JMenu submenu = new JMenu(list[i]);
// needs to be separate var
// otherwise would set ifound to false
boolean found = addLibraries(submenu, subfolder); //, false);
if (found) {
menu.add(submenu);
ifound = true;
}
}
}
return ifound;
}
/**
* Clear out projects that are empty.
*/
public void clean() {
//if (!Preferences.getBoolean("sketchbook.auto_clean")) return;
File sketchbookFolder = new File(getSketchbookPath());
if (!sketchbookFolder.exists()) return;
//String entries[] = new File(userPath).list();
String entries[] = sketchbookFolder.list();
if (entries != null) {
for (int j = 0; j < entries.length; j++) {
//System.out.println(entries[j] + " " + entries.length);
if (entries[j].charAt(0) == '.') continue;
//File prey = new File(userPath, entries[j]);
File prey = new File(sketchbookFolder, entries[j]);
File pde = new File(prey, entries[j] + ".pde");
// make sure this is actually a sketch folder with a .pde,
// not a .DS_Store file or another random user folder
if (pde.exists() &&
(Base.calcFolderSize(prey) == 0)) {
//System.out.println("i want to remove " + prey);
if (Preferences.getBoolean("sketchbook.auto_clean")) {
Base.removeDir(prey);
} else { // otherwise prompt the user
String prompt =
"Remove empty sketch titled \"" + entries[j] + "\"?";
Object[] options = { "Yes", "No" };
int result =
JOptionPane.showOptionDialog(editor,
prompt,
"Housekeeping",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
if (result == JOptionPane.YES_OPTION) {
Base.removeDir(prey);
}
}
}
}
}
}
}

131
app/SwingWorker.java Normal file
View File

@ -0,0 +1,131 @@
package processing.app;
import javax.swing.SwingUtilities;
/**
* This is the 3rd version of SwingWorker (also known as
* SwingWorker 3), an abstract class that you subclass to
* perform GUI-related work in a dedicated thread. For
* instructions on and examples of using this class, see:
*
* http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
*
* Note that the API changed slightly in the 3rd version:
* You must now invoke start() on the SwingWorker after
* creating it.
*/
public abstract class SwingWorker {
private Object value; // see getValue(), setValue()
/**
* Class to maintain reference to current worker thread
* under separate synchronization control.
*/
private static class ThreadVar {
private Thread thread;
ThreadVar(Thread t) { thread = t; }
synchronized Thread get() { return thread; }
synchronized void clear() { thread = null; }
}
private ThreadVar threadVar;
/**
* Get the value produced by the worker thread, or null if it
* hasn't been constructed yet.
*/
protected synchronized Object getValue() {
return value;
}
/**
* Set the value produced by worker thread
*/
private synchronized void setValue(Object x) {
value = x;
}
/**
* Compute the value to be returned by the <code>get</code> method.
*/
public abstract Object construct();
/**
* Called on the event dispatching thread (not on the worker thread)
* after the <code>construct</code> method has returned.
*/
public void finished() {
}
/**
* A new method that interrupts the worker thread. Call this method
* to force the worker to stop what it's doing.
*/
public void interrupt() {
Thread t = threadVar.get();
if (t != null) {
t.interrupt();
}
threadVar.clear();
}
/**
* Return the value created by the <code>construct</code> method.
* Returns null if either the constructing thread or the current
* thread was interrupted before a value was produced.
*
* @return the value created by the <code>construct</code> method
*/
public Object get() {
while (true) {
Thread t = threadVar.get();
if (t == null) {
return getValue();
}
try {
t.join();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt(); // propagate
return null;
}
}
}
/**
* Start a thread that will call the <code>construct</code> method
* and then exit.
*/
public SwingWorker() {
final Runnable doFinished = new Runnable() {
public void run() { finished(); }
};
Runnable doConstruct = new Runnable() {
public void run() {
try {
setValue(construct());
}
finally {
threadVar.clear();
}
SwingUtilities.invokeLater(doFinished);
}
};
Thread t = new Thread(doConstruct);
threadVar = new ThreadVar(t);
}
/**
* Start the worker thread.
*/
public void start() {
Thread t = threadVar.get();
if (t != null) {
t.start();
}
}
}

143
app/UpdateCheck.java Executable file
View File

@ -0,0 +1,143 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2005 Ben Fry and Casey Reas
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import java.util.zip.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.undo.*;
import com.apple.mrj.*;
import com.ice.jni.registry.*;
//import processing.core.*;
/**
* Threaded class to check for updates in the background.
* <P>
* This is the class that handles the mind control and stuff for
* spying on our users and stealing their personal information.
* A random ID number is generated for each user, and hits the server
* to check for updates. Also included is the operating system and
* its version and the version of Java being used to run Processing.
* <P>
* The ID number also helps provide us a general idea of how many
* people are using Processing, which helps us when writing grant
* proposals and that kind of thing so that we can keep Processing free.
*/
public class UpdateCheck implements Runnable {
Editor editor;
String downloadURL = "http://arduino.berlios.de/latest.txt";
static final long ONE_DAY = 24 * 60 * 60 * 1000;
public UpdateCheck(Editor editor) {
this.editor = editor;
Thread thread = new Thread(this);
thread.start();
}
public void run() {
//System.out.println("checking for updates...");
// generate a random id in case none exists yet
Random r = new Random();
long id = r.nextLong();
String idString = Preferences.get("update.id");
if (idString != null) {
id = Long.parseLong(idString);
} else {
Preferences.set("update.id", String.valueOf(id));
}
String info =
URLEncoder.encode(id + "\t" +
Base.VERSION+ "\t" +
System.getProperty("java.version") + "\t" +
System.getProperty("java.vendor") + "\t" +
System.getProperty("os.name") + "\t" +
System.getProperty("os.version") + "\t" +
System.getProperty("os.arch"));
try {
int latest = readInt(downloadURL + "?" + info);
String lastString = Preferences.get("update.last");
long now = System.currentTimeMillis();
if (lastString != null) {
long when = Long.parseLong(lastString);
if (now - when < ONE_DAY) {
// don't annoy the shit outta people
return;
}
}
Preferences.set("update.last", String.valueOf(now));
String prompt =
"A new version of Processing is available,\n" +
"would you like to visit the Processing download page?";
if (latest > Base.VERSION) {
Object[] options = { "Yes", "No" };
int result = JOptionPane.showOptionDialog(editor,
prompt,
"Update",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
if (result == JOptionPane.YES_OPTION) {
Base.openURL("http://processing.org/download/");
//} else if (result == JOptionPane.NO_OPTION) {
}
}
} catch (Exception e) {
//e.printStackTrace();
//System.err.println("Error while trying to check for an update.");
}
}
protected int readInt(String filename) throws Exception {
URL url = new URL(filename);
InputStream stream = url.openStream();
InputStreamReader isr = new InputStreamReader(stream);
BufferedReader reader = new BufferedReader(isr);
return Integer.parseInt(reader.readLine());
}
}

300
app/exportapplet.txt Normal file
View File

@ -0,0 +1,300 @@
/*
// make sure the user didn't hide the sketch folder
ensureExistence();
zipFileContents = new Hashtable();
// nuke the old applet folder because it can cause trouble
File appletFolder = new File(folder, "applet");
Base.removeDir(appletFolder);
appletFolder.mkdirs();
// build the sketch
String foundName = build(appletFolder.getPath(), name);
// (already reported) error during export, exit this function
if (foundName == null) return false;
// if name != exportSketchName, then that's weirdness
// BUG unfortunately, that can also be a bug in the preproc :(
if (!name.equals(foundName)) {
Base.showWarning("Error during export",
"Sketch name is " + name + " but the sketch\n" +
"name in the code was " + foundName, null);
return false;
}
int wide = PApplet.DEFAULT_WIDTH;
int high = PApplet.DEFAULT_HEIGHT;
PatternMatcher matcher = new Perl5Matcher();
PatternCompiler compiler = new Perl5Compiler();
// this matches against any uses of the size() function,
// whether they contain numbers of variables or whatever.
// this way, no warning is shown if size() isn't actually
// used in the applet, which is the case especially for
// beginners that are cutting/pasting from the reference.
// modified for 83 to match size(XXX, ddd so that it'll
// properly handle size(200, 200) and size(200, 200, P3D)
String sizing =
"[\\s\\;]size\\s*\\(\\s*(\\S+)\\s*,\\s*(\\d+)";
Pattern pattern = compiler.compile(sizing);
// adds a space at the beginning, in case size() is the very
// first thing in the program (very common), since the regexp
// needs to check for things in front of it.
PatternMatcherInput input =
new PatternMatcherInput(" " + code[0].program);
if (matcher.contains(input, pattern)) {
MatchResult result = matcher.getMatch();
try {
wide = Integer.parseInt(result.group(1).toString());
high = Integer.parseInt(result.group(2).toString());
} catch (NumberFormatException e) {
// found a reference to size, but it didn't
// seem to contain numbers
final String message =
"The size of this applet could not automatically be\n" +
"determined from your code. You'll have to edit the\n" +
"HTML file to set the size of the applet.";
Base.showWarning("Could not find applet size", message, null);
}
} // else no size() command found
// originally tried to grab this with a regexp matcher,
// but it wouldn't span over multiple lines for the match.
// this could prolly be forced, but since that's the case
// better just to parse by hand.
StringBuffer dbuffer = new StringBuffer();
String lines[] = PApplet.split(code[0].program, '\n');
for (int i = 0; i < lines.length; i++) {
if (lines[i].trim().startsWith("/**")) { // this is our comment
// some smartass put the whole thing on the same line
//if (lines[j].indexOf("*/") != -1) break;
for (int j = i+1; j < lines.length; j++) {
if (lines[j].trim().endsWith("*/")) {
// remove the */ from the end, and any extra *s
// in case there's also content on this line
// nah, don't bother.. make them use the three lines
break;
}
int offset = 0;
while ((offset < lines[j].length()) &&
((lines[j].charAt(offset) == '*') ||
(lines[j].charAt(offset) == ' '))) {
offset++;
}
// insert the return into the html to help w/ line breaks
dbuffer.append(lines[j].substring(offset) + "\n");
}
}
}
String description = dbuffer.toString();
StringBuffer sources = new StringBuffer();
for (int i = 0; i < codeCount; i++) {
sources.append("<a href=\"" + code[i].file.getName() + "\">" +
code[i].name + "</a> ");
}
File htmlOutputFile = new File(appletFolder, "index.html");
FileOutputStream fos = new FileOutputStream(htmlOutputFile);
PrintStream ps = new PrintStream(fos);
// @@sketch@@, @@width@@, @@height@@, @@archive@@, @@source@@
// and now @@description@@
InputStream is = null;
// if there is an applet.html file in the sketch folder, use that
File customHtml = new File(folder, "applet.html");
if (customHtml.exists()) {
is = new FileInputStream(customHtml);
}
if (is == null) {
is = Base.getStream("applet.html");
}
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = reader.readLine()) != null) {
if (line.indexOf("@@") != -1) {
StringBuffer sb = new StringBuffer(line);
int index = 0;
while ((index = sb.indexOf("@@sketch@@")) != -1) {
sb.replace(index, index + "@@sketch@@".length(),
name);
}
while ((index = sb.indexOf("@@source@@")) != -1) {
sb.replace(index, index + "@@source@@".length(),
sources.toString());
}
while ((index = sb.indexOf("@@archive@@")) != -1) {
sb.replace(index, index + "@@archive@@".length(),
name + ".jar");
}
while ((index = sb.indexOf("@@width@@")) != -1) {
sb.replace(index, index + "@@width@@".length(),
String.valueOf(wide));
}
while ((index = sb.indexOf("@@height@@")) != -1) {
sb.replace(index, index + "@@height@@".length(),
String.valueOf(high));
}
while ((index = sb.indexOf("@@description@@")) != -1) {
sb.replace(index, index + "@@description@@".length(),
description);
}
line = sb.toString();
}
ps.println(line);
}
reader.close();
ps.flush();
ps.close();
// copy the loading gif to the applet
String LOADING_IMAGE = "loading.gif";
File loadingImage = new File(folder, LOADING_IMAGE);
if (!loadingImage.exists()) {
loadingImage = new File("lib", LOADING_IMAGE);
}
Base.copyFile(loadingImage, new File(appletFolder, LOADING_IMAGE));
// copy the source files to the target, since we like
// to encourage people to share their code
for (int i = 0; i < codeCount; i++) {
try {
Base.copyFile(code[i].file,
new File(appletFolder, code[i].file.getName()));
} catch (IOException e) {
e.printStackTrace();
}
}
// create new .jar file
FileOutputStream zipOutputFile =
new FileOutputStream(new File(appletFolder, name + ".jar"));
ZipOutputStream zos = new ZipOutputStream(zipOutputFile);
ZipEntry entry;
// add the manifest file
addManifest(zos);
// add the contents of the code folder to the jar
// unpacks all jar files
//File codeFolder = new File(folder, "code");
if (codeFolder.exists()) {
String includes = Compiler.contentsToClassPath(codeFolder);
packClassPathIntoZipFile(includes, zos);
}
// add contents of 'library' folders to the jar file
// if a file called 'export.txt' is in there, it contains
// a list of the files that should be exported.
// otherwise, all files are exported.
Enumeration en = importedLibraries.elements();
while (en.hasMoreElements()) {
// in the list is a File object that points the
// library sketch's "library" folder
File libraryFolder = (File)en.nextElement();
File exportSettings = new File(libraryFolder, "export.txt");
String exportList[] = null;
if (exportSettings.exists()) {
String info[] = Base.loadStrings(exportSettings);
for (int i = 0; i < info.length; i++) {
if (info[i].startsWith("applet")) {
int idx = info[i].indexOf('='); // get applet= or applet =
String commas = info[i].substring(idx+1).trim();
exportList = PApplet.split(commas, ", ");
}
}
} else {
exportList = libraryFolder.list();
}
for (int i = 0; i < exportList.length; i++) {
if (exportList[i].equals(".") ||
exportList[i].equals("..")) continue;
exportList[i] = PApplet.trim(exportList[i]);
if (exportList[i].equals("")) continue;
File exportFile = new File(libraryFolder, exportList[i]);
if (!exportFile.exists()) {
System.err.println("File " + exportList[i] + " does not exist");
} else if (exportFile.isDirectory()) {
System.err.println("Ignoring sub-folder \"" + exportList[i] + "\"");
} else if (exportFile.getName().toLowerCase().endsWith(".zip") ||
exportFile.getName().toLowerCase().endsWith(".jar")) {
packClassPathIntoZipFile(exportFile.getAbsolutePath(), zos);
} else { // just copy the file over.. prolly a .dll or something
Base.copyFile(exportFile,
new File(appletFolder, exportFile.getName()));
}
}
}
String bagelJar = "lib/core.jar";
packClassPathIntoZipFile(bagelJar, zos);
// files to include from data directory
// TODO this needs to be recursive
if (dataFolder.exists()) {
String dataFiles[] = dataFolder.list();
for (int i = 0; i < dataFiles.length; i++) {
// don't export hidden files
// skipping dot prefix removes all: . .. .DS_Store
if (dataFiles[i].charAt(0) == '.') continue;
entry = new ZipEntry(dataFiles[i]);
zos.putNextEntry(entry);
zos.write(Base.grabFile(new File(dataFolder, dataFiles[i])));
zos.closeEntry();
}
}
// add the project's .class files to the jar
// just grabs everything from the build directory
// since there may be some inner classes
// (add any .class files from the applet dir, then delete them)
// TODO this needs to be recursive (for packages)
String classfiles[] = appletFolder.list();
for (int i = 0; i < classfiles.length; i++) {
if (classfiles[i].endsWith(".class")) {
entry = new ZipEntry(classfiles[i]);
zos.putNextEntry(entry);
zos.write(Base.grabFile(new File(appletFolder, classfiles[i])));
zos.closeEntry();
}
}
// remove the .class files from the applet folder. if they're not
// removed, the msjvm will complain about an illegal access error,
// since the classes are outside the jar file.
for (int i = 0; i < classfiles.length; i++) {
if (classfiles[i].endsWith(".class")) {
File deadguy = new File(appletFolder, classfiles[i]);
if (!deadguy.delete()) {
Base.showWarning("Could not delete",
classfiles[i] + " could not \n" +
"be deleted from the applet folder. \n" +
"You'll need to remove it by hand.", null);
}
}
}
// close up the jar file
zos.flush();
zos.close();
Base.openFolder(appletFolder);
return true;
*/

9
app/preproc/.cvsignore Normal file
View File

@ -0,0 +1,9 @@
*Lexer.java
*Recognizer.java
*TokenTypes.java
*TokenTypes.txt
*TreeParser.java
*TreeParserTokenTypes.java
*TreeParserTokenTypes.txt
expanded*.g

View File

@ -0,0 +1,132 @@
package antlr;
/* ANTLR Translator Generator
* Project led by Terence Parr at http://www.jGuru.com
* Software rights: http://www.antlr.org/RIGHTS.html
*
* $Id: ExtendedCommonASTWithHiddenTokens.java,v 1.1 2005/04/09 02:30:36 benfry Exp $
*/
import java.io.*;
import antlr.*;
import antlr.collections.*;
import antlr.collections.impl.*;
/** A CommonAST whose initialization copies hidden token
* information from the Token used to create a node.
*/
public class ExtendedCommonASTWithHiddenTokens
extends CommonASTWithHiddenTokens {
public ExtendedCommonASTWithHiddenTokens() {
super();
}
public ExtendedCommonASTWithHiddenTokens(Token tok) {
super(tok);
}
public void initialize(AST ast) {
ExtendedCommonASTWithHiddenTokens a =
(ExtendedCommonASTWithHiddenTokens)ast;
super.initialize(a);
hiddenBefore = a.getHiddenBefore();
hiddenAfter = a.getHiddenAfter();
}
public String getHiddenAfterString() {
CommonHiddenStreamToken t;
StringBuffer hiddenAfterString = new StringBuffer(100);
for ( t = hiddenAfter ; t != null ; t = t.getHiddenAfter() ) {
hiddenAfterString.append(t.getText());
}
return hiddenAfterString.toString();
}
public String getHiddenBeforeString() {
antlr.CommonHiddenStreamToken
child = null,
parent = hiddenBefore;
// if there aren't any hidden tokens here, quietly return
//
if (parent == null) {
return "";
}
// traverse back to the head of the list of tokens before this node
do {
child = parent;
parent = child.getHiddenBefore();
} while (parent != null);
// dump that list
StringBuffer hiddenBeforeString = new StringBuffer(100);
for ( CommonHiddenStreamToken t = child; t != null ;
t = t.getHiddenAfter() ) {
hiddenBeforeString.append(t.getText());
}
return hiddenBeforeString.toString();
}
public void xmlSerializeNode(Writer out)
throws IOException {
StringBuffer buf = new StringBuffer(100);
buf.append("<");
buf.append(getClass().getName() + " ");
buf.append("hiddenBeforeString=\"" +
encode(getHiddenBeforeString()) +
"\" text=\"" + encode(getText()) + "\" type=\"" +
getType() + "\" hiddenAfterString=\"" +
encode(getHiddenAfterString()) + "\"/>");
out.write(buf.toString());
}
public void xmlSerializeRootOpen(Writer out)
throws IOException {
StringBuffer buf = new StringBuffer(100);
buf.append("<");
buf.append(getClass().getName() + " ");
buf.append("hiddenBeforeString=\"" +
encode(getHiddenBeforeString()) +
"\" text=\"" + encode(getText()) + "\" type=\"" +
getType() + "\" hiddenAfterString=\"" +
encode(getHiddenAfterString()) + "\">\n");
out.write(buf.toString());
}
public void xmlSerializeRootClose(Writer out)
throws IOException {
out.write("</" + getClass().getName() + ">\n");
}
public void xmlSerialize(Writer out) throws IOException {
// print out this node and all siblings
for (AST node = this;
node != null;
node = node.getNextSibling()) {
if (node.getFirstChild() == null) {
// print guts (class name, attributes)
((BaseAST)node).xmlSerializeNode(out);
}
else {
((BaseAST)node).xmlSerializeRootOpen(out);
// print children
((BaseAST)node.getFirstChild()).xmlSerialize(out);
// print end tag
((BaseAST)node).xmlSerializeRootClose(out);
}
}
}
}

1899
app/preproc/JavaLexer.java Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,157 @@
// $ANTLR 2.7.2: "java.g" -> "JavaLexer.java"$
package antlr.java;
public interface JavaTokenTypes {
int EOF = 1;
int NULL_TREE_LOOKAHEAD = 3;
int BLOCK = 4;
int MODIFIERS = 5;
int OBJBLOCK = 6;
int SLIST = 7;
int CTOR_DEF = 8;
int METHOD_DEF = 9;
int VARIABLE_DEF = 10;
int INSTANCE_INIT = 11;
int STATIC_INIT = 12;
int TYPE = 13;
int CLASS_DEF = 14;
int INTERFACE_DEF = 15;
int PACKAGE_DEF = 16;
int ARRAY_DECLARATOR = 17;
int EXTENDS_CLAUSE = 18;
int IMPLEMENTS_CLAUSE = 19;
int PARAMETERS = 20;
int PARAMETER_DEF = 21;
int LABELED_STAT = 22;
int TYPECAST = 23;
int INDEX_OP = 24;
int POST_INC = 25;
int POST_DEC = 26;
int METHOD_CALL = 27;
int EXPR = 28;
int ARRAY_INIT = 29;
int IMPORT = 30;
int UNARY_MINUS = 31;
int UNARY_PLUS = 32;
int CASE_GROUP = 33;
int ELIST = 34;
int FOR_INIT = 35;
int FOR_CONDITION = 36;
int FOR_ITERATOR = 37;
int EMPTY_STAT = 38;
int FINAL = 39;
int ABSTRACT = 40;
int STRICTFP = 41;
int SUPER_CTOR_CALL = 42;
int CTOR_CALL = 43;
int LITERAL_package = 44;
int SEMI = 45;
int LITERAL_import = 46;
int LBRACK = 47;
int RBRACK = 48;
int LITERAL_void = 49;
int LITERAL_boolean = 50;
int LITERAL_byte = 51;
int LITERAL_char = 52;
int LITERAL_short = 53;
int LITERAL_int = 54;
int LITERAL_float = 55;
int LITERAL_long = 56;
int LITERAL_double = 57;
int IDENT = 58;
int DOT = 59;
int STAR = 60;
int LITERAL_private = 61;
int LITERAL_public = 62;
int LITERAL_protected = 63;
int LITERAL_static = 64;
int LITERAL_transient = 65;
int LITERAL_native = 66;
int LITERAL_threadsafe = 67;
int LITERAL_synchronized = 68;
int LITERAL_volatile = 69;
int LITERAL_class = 70;
int LITERAL_extends = 71;
int LITERAL_interface = 72;
int LCURLY = 73;
int RCURLY = 74;
int COMMA = 75;
int LITERAL_implements = 76;
int LPAREN = 77;
int RPAREN = 78;
int LITERAL_this = 79;
int LITERAL_super = 80;
int ASSIGN = 81;
int LITERAL_throws = 82;
int COLON = 83;
int LITERAL_if = 84;
int LITERAL_else = 85;
int LITERAL_for = 86;
int LITERAL_while = 87;
int LITERAL_do = 88;
int LITERAL_break = 89;
int LITERAL_continue = 90;
int LITERAL_return = 91;
int LITERAL_switch = 92;
int LITERAL_throw = 93;
int LITERAL_assert = 94;
int LITERAL_case = 95;
int LITERAL_default = 96;
int LITERAL_try = 97;
int LITERAL_finally = 98;
int LITERAL_catch = 99;
int PLUS_ASSIGN = 100;
int MINUS_ASSIGN = 101;
int STAR_ASSIGN = 102;
int DIV_ASSIGN = 103;
int MOD_ASSIGN = 104;
int SR_ASSIGN = 105;
int BSR_ASSIGN = 106;
int SL_ASSIGN = 107;
int BAND_ASSIGN = 108;
int BXOR_ASSIGN = 109;
int BOR_ASSIGN = 110;
int QUESTION = 111;
int LOR = 112;
int LAND = 113;
int BOR = 114;
int BXOR = 115;
int BAND = 116;
int NOT_EQUAL = 117;
int EQUAL = 118;
int LT = 119;
int GT = 120;
int LE = 121;
int GE = 122;
int LITERAL_instanceof = 123;
int SL = 124;
int SR = 125;
int BSR = 126;
int PLUS = 127;
int MINUS = 128;
int DIV = 129;
int MOD = 130;
int INC = 131;
int DEC = 132;
int BNOT = 133;
int LNOT = 134;
int LITERAL_true = 135;
int LITERAL_false = 136;
int LITERAL_null = 137;
int LITERAL_new = 138;
int NUM_INT = 139;
int CHAR_LITERAL = 140;
int STRING_LITERAL = 141;
int NUM_FLOAT = 142;
int NUM_LONG = 143;
int NUM_DOUBLE = 144;
int WS = 145;
int SL_COMMENT = 146;
int ML_COMMENT = 147;
int ESC = 148;
int HEX_DIGIT = 149;
int VOCAB = 150;
int EXPONENT = 151;
int FLOAT_SUFFIX = 152;
}

View File

@ -0,0 +1,151 @@
// $ANTLR 2.7.2: java.g -> JavaTokenTypes.txt$
Java // output token vocab name
BLOCK=4
MODIFIERS=5
OBJBLOCK=6
SLIST=7
CTOR_DEF=8
METHOD_DEF=9
VARIABLE_DEF=10
INSTANCE_INIT=11
STATIC_INIT=12
TYPE=13
CLASS_DEF=14
INTERFACE_DEF=15
PACKAGE_DEF=16
ARRAY_DECLARATOR=17
EXTENDS_CLAUSE=18
IMPLEMENTS_CLAUSE=19
PARAMETERS=20
PARAMETER_DEF=21
LABELED_STAT=22
TYPECAST=23
INDEX_OP=24
POST_INC=25
POST_DEC=26
METHOD_CALL=27
EXPR=28
ARRAY_INIT=29
IMPORT=30
UNARY_MINUS=31
UNARY_PLUS=32
CASE_GROUP=33
ELIST=34
FOR_INIT=35
FOR_CONDITION=36
FOR_ITERATOR=37
EMPTY_STAT=38
FINAL="final"=39
ABSTRACT="abstract"=40
STRICTFP="strictfp"=41
SUPER_CTOR_CALL=42
CTOR_CALL=43
LITERAL_package="package"=44
SEMI=45
LITERAL_import="import"=46
LBRACK=47
RBRACK=48
LITERAL_void="void"=49
LITERAL_boolean="boolean"=50
LITERAL_byte="byte"=51
LITERAL_char="char"=52
LITERAL_short="short"=53
LITERAL_int="int"=54
LITERAL_float="float"=55
LITERAL_long="long"=56
LITERAL_double="double"=57
IDENT=58
DOT=59
STAR=60
LITERAL_private="private"=61
LITERAL_public="public"=62
LITERAL_protected="protected"=63
LITERAL_static="static"=64
LITERAL_transient="transient"=65
LITERAL_native="native"=66
LITERAL_threadsafe="threadsafe"=67
LITERAL_synchronized="synchronized"=68
LITERAL_volatile="volatile"=69
LITERAL_class="class"=70
LITERAL_extends="extends"=71
LITERAL_interface="interface"=72
LCURLY=73
RCURLY=74
COMMA=75
LITERAL_implements="implements"=76
LPAREN=77
RPAREN=78
LITERAL_this="this"=79
LITERAL_super="super"=80
ASSIGN=81
LITERAL_throws="throws"=82
COLON=83
LITERAL_if="if"=84
LITERAL_else="else"=85
LITERAL_for="for"=86
LITERAL_while="while"=87
LITERAL_do="do"=88
LITERAL_break="break"=89
LITERAL_continue="continue"=90
LITERAL_return="return"=91
LITERAL_switch="switch"=92
LITERAL_throw="throw"=93
LITERAL_assert="assert"=94
LITERAL_case="case"=95
LITERAL_default="default"=96
LITERAL_try="try"=97
LITERAL_finally="finally"=98
LITERAL_catch="catch"=99
PLUS_ASSIGN=100
MINUS_ASSIGN=101
STAR_ASSIGN=102
DIV_ASSIGN=103
MOD_ASSIGN=104
SR_ASSIGN=105
BSR_ASSIGN=106
SL_ASSIGN=107
BAND_ASSIGN=108
BXOR_ASSIGN=109
BOR_ASSIGN=110
QUESTION=111
LOR=112
LAND=113
BOR=114
BXOR=115
BAND=116
NOT_EQUAL=117
EQUAL=118
LT=119
GT=120
LE=121
GE=122
LITERAL_instanceof="instanceof"=123
SL=124
SR=125
BSR=126
PLUS=127
MINUS=128
DIV=129
MOD=130
INC=131
DEC=132
BNOT=133
LNOT=134
LITERAL_true="true"=135
LITERAL_false="false"=136
LITERAL_null="null"=137
LITERAL_new="new"=138
NUM_INT=139
CHAR_LITERAL=140
STRING_LITERAL=141
NUM_FLOAT=142
NUM_LONG=143
NUM_DOUBLE=144
WS=145
SL_COMMENT=146
ML_COMMENT=147
ESC=148
HEX_DIGIT=149
VOCAB=150
EXPONENT=151
FLOAT_SUFFIX=152

922
app/preproc/PdeEmitter.java Normal file
View File

@ -0,0 +1,922 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
package processing.app.preproc;
import processing.app.*;
/* Based on original code copyright (c) 2003 Andy Tripp <atripp@comcast.net>.
* shipped under GPL with permission.
*/
import antlr.*;
import antlr.collections.*;
import antlr.collections.impl.*;
import java.io.*;
import java.util.*;
/**
* PDEEmitter: A class that can take an ANTLR Java AST and produce
* reasonably formatted Java code from it. To use it, create a
* PDEEmitter object, call setOut() if you want to print to something
* other than System.out, and then call print(), passing the
* AST. Typically, the AST node that you pass would be the root of a
* tree - the ROOT_ID node that represents a Java file.
*/
public class PdeEmitter implements PdeTokenTypes
{
private PrintStream out = System.out;
private PrintStream debug = System.err;
private static int ALL = -1;
private java.util.Stack stack = new java.util.Stack();
private static String[] tokenNames;
private final static int ROOT_ID = 0;
static {
setupTokenNames();
}
/*
private static Hashtable publicMethods;
private static final String publicMethodList[] = {
"setup", "draw", //"loop",
"mousePressed", "mouseReleased", "mouseClicked",
"mouseEntered", "mouseExited",
"mouseMoved", "mouseDragged",
"keyPressed", "keyReleased", "keyTyped"
};
static {
publicMethods = new Hashtable();
for (int i = 0; i < publicMethodList.length; i++) {
publicMethods.put(publicMethodList[i], new Object());
}
}
*/
// Map each AST token type to a String
private static void setupTokenNames() {
tokenNames = new String[200];
for (int i=0; i<tokenNames.length; i++) {
tokenNames[i] = "ERROR:" + i;
}
tokenNames[POST_INC]="++";
tokenNames[POST_DEC]="--";
tokenNames[UNARY_MINUS]="-";
tokenNames[UNARY_PLUS]="+";
tokenNames[STAR]="*";
tokenNames[ASSIGN]="=";
tokenNames[PLUS_ASSIGN]="+=";
tokenNames[MINUS_ASSIGN]="-=";
tokenNames[STAR_ASSIGN]="*=";
tokenNames[DIV_ASSIGN]="/=";
tokenNames[MOD_ASSIGN]="%=";
tokenNames[SR_ASSIGN]=">>=";
tokenNames[BSR_ASSIGN]=">>>=";
tokenNames[SL_ASSIGN]="<<=";
tokenNames[BAND_ASSIGN]="&=";
tokenNames[BXOR_ASSIGN]="^=";
tokenNames[BOR_ASSIGN]="|=";
tokenNames[QUESTION]="?";
tokenNames[LOR]="||";
tokenNames[LAND]="&&";
tokenNames[BOR]="|";
tokenNames[BXOR]="^";
tokenNames[BAND]="&";
tokenNames[NOT_EQUAL]="!=";
tokenNames[EQUAL]="==";
tokenNames[LT]="<";
tokenNames[GT]=">";
tokenNames[LE]="<=";
tokenNames[GE]=">=";
tokenNames[SL]="<<";
tokenNames[SR]=">>";
tokenNames[BSR]=">>>";
tokenNames[PLUS]="+";
tokenNames[MINUS]="-";
tokenNames[DIV]="/";
tokenNames[MOD]="%";
tokenNames[INC]="++";
tokenNames[DEC]="--";
tokenNames[BNOT]="~";
tokenNames[LNOT]="!";
tokenNames[FINAL]="final";
tokenNames[ABSTRACT]="abstract";
tokenNames[LITERAL_package]="package";
tokenNames[LITERAL_import]="import";
tokenNames[LITERAL_void]="void";
tokenNames[LITERAL_boolean]="boolean";
tokenNames[LITERAL_byte]="byte";
tokenNames[LITERAL_char]="char";
tokenNames[LITERAL_short]="short";
tokenNames[LITERAL_int]="int";
tokenNames[LITERAL_float]="float";
tokenNames[LITERAL_long]="long";
tokenNames[LITERAL_double]="double";
tokenNames[LITERAL_private]="private";
tokenNames[LITERAL_public]="public";
tokenNames[LITERAL_protected]="protected";
tokenNames[LITERAL_static]="static";
tokenNames[LITERAL_transient]="transient";
tokenNames[LITERAL_native]="native";
tokenNames[LITERAL_threadsafe]="threadsafe";
tokenNames[LITERAL_synchronized]="synchronized";
tokenNames[LITERAL_volatile]="volatile";
tokenNames[LITERAL_class]="class";
tokenNames[LITERAL_extends]="extends";
tokenNames[LITERAL_interface]="interface";
tokenNames[LITERAL_implements]="implements";
tokenNames[LITERAL_throws]="throws";
tokenNames[LITERAL_if]="if";
tokenNames[LITERAL_else]="else";
tokenNames[LITERAL_for]="for";
tokenNames[LITERAL_while]="while";
tokenNames[LITERAL_do]="do";
tokenNames[LITERAL_break]="break";
tokenNames[LITERAL_continue]="continue";
tokenNames[LITERAL_return]="return";
tokenNames[LITERAL_switch]="switch";
tokenNames[LITERAL_throw]="throw";
tokenNames[LITERAL_case]="case";
tokenNames[LITERAL_default]="default";
tokenNames[LITERAL_try]="try";
tokenNames[LITERAL_finally]="finally";
tokenNames[LITERAL_catch]="catch";
tokenNames[LITERAL_instanceof]="instanceof";
tokenNames[LITERAL_this]="this";
tokenNames[LITERAL_super]="super";
tokenNames[LITERAL_true]="true";
tokenNames[LITERAL_false]="false";
tokenNames[LITERAL_null]="null";
tokenNames[LITERAL_new]="new";
tokenNames[LITERAL_color]="int"; // PDE specific alias
}
/**
* Specify a PrintStream to print to. System.out is the default.
* @param out the PrintStream to print to
*/
public void setOut(PrintStream out) {
this.out = out;
}
private String name(AST ast) {
return tokenNames[ast.getType()];
}
private String name(int type) {
return tokenNames[type];
}
/**
* Find a child of the given AST that has the given type
* @returns a child AST of the given type. If it can't find a child of the
* given type, return null.
*/
private AST getChild(AST ast, int childType) {
AST child = ast.getFirstChild();
while (child != null) {
if (child.getType() == childType) {
// debug.println("getChild: found:" + name(ast));
return child;
}
child = child.getNextSibling();
}
return null;
}
/**
* Dump the list of hidden tokens linked to after the AST node passed in.
* Most hidden tokens are dumped from this function.
*/
private void dumpHiddenAfter(AST ast) {
dumpHiddenTokens(((antlr.CommonASTWithHiddenTokens)ast).getHiddenAfter());
}
/**
* Dump the list of hidden tokens linked to before the AST node passed in.
* The only time hidden tokens need to be dumped with this function is when
* dealing parts of the tree where automatic tree construction was
* turned off with the ! operator in the grammar file and the nodes were
* manually constructed in such a way that the usual tokens don't have the
* necessary hiddenAfter links.
*/
private void dumpHiddenBefore(AST ast) {
antlr.CommonHiddenStreamToken
child = null,
parent = ((antlr.CommonASTWithHiddenTokens)ast).getHiddenBefore();
// if there aren't any hidden tokens here, quietly return
//
if (parent == null) {
return;
}
// traverse back to the head of the list of tokens before this node
do {
child = parent;
parent = child.getHiddenBefore();
} while (parent != null);
// dump that list
dumpHiddenTokens(child);
}
/**
* Dump the list of hidden tokens linked to from the token passed in.
*/
private void dumpHiddenTokens(antlr.CommonHiddenStreamToken t) {
for ( ; t != null ; t=PdePreprocessor.filter.getHiddenAfter(t) ) {
out.print(t.getText());
}
}
/**
* Print the children of the given AST
* @param ast The AST to print
* @returns true iff anything was printed
*/
private boolean printChildren(AST ast) throws RunnerException {
boolean ret = false;
AST child = ast.getFirstChild();
while (child != null) {
ret = true;
print(child);
child = child.getNextSibling();
}
return ret;
}
/**
* Tells whether an AST has any children or not.
* @return true iff the AST has at least one child
*/
private boolean hasChildren(AST ast) {
return (ast.getFirstChild() != null);
}
/**
* Gets the best node in the subtree for printing. This really means
* the next node which could potentially have hiddenBefore data. It's
* usually the first printable leaf, but not always.
*
* @param includeThisNode Should this node be included in the search?
* If false, only descendants are searched.
*
* @return the first printable leaf node in an AST
*/
private AST getBestPrintableNode(AST ast, boolean includeThisNode) {
AST child;
if (includeThisNode) {
child = ast;
} else {
child = ast.getFirstChild();
}
if (child != null) {
switch (child.getType()) {
// the following node types are printing nodes that print before
// any children, but then also recurse over children. So they
// may have hiddenBefore chains that need to be printed first. Many
// statements and all unary expression types qualify. Return these
// nodes directly
case CLASS_DEF:
case LITERAL_if:
case LITERAL_for:
case LITERAL_while:
case LITERAL_do:
case LITERAL_break:
case LITERAL_continue:
case LITERAL_return:
case LITERAL_switch:
case LITERAL_try:
case LITERAL_throw:
case LITERAL_synchronized:
case LITERAL_assert:
case BNOT:
case LNOT:
case INC:
case DEC:
case UNARY_MINUS:
case UNARY_PLUS:
return child;
// Some non-terminal node types (at the moment, I only know of
// MODIFIERS, but there may be other such types), can be
// leaves in the tree but not have any children. If this is
// such a node, move on to the next sibling.
case MODIFIERS:
if (child.getFirstChild() == null ) {
return getBestPrintableNode(child.getNextSibling(), false);
}
// new jikes doesn't like fallthrough, so just duplicated here:
return getBestPrintableNode(child, false);
default:
return getBestPrintableNode(child, false);
}
}
return ast;
}
/**
* Prints a binary operator
*/
private void printBinaryOperator(AST ast) throws RunnerException {
print(ast.getFirstChild());
out.print(name(ast));
dumpHiddenAfter(ast);
print(ast.getFirstChild().getNextSibling());
}
/**
* Print the given AST. Call this function to print your PDE code.
*
* It works by making recursive calls to print children.
* So the code below is one big "switch" statement on the passed AST type.
*/
public void print (AST ast) throws RunnerException {
if (ast == null) {
return;
}
AST parent = null;
if (!stack.isEmpty()) {
parent = (AST) stack.peek();
}
stack.push(ast);
AST child1 = ast.getFirstChild();
AST child2 = null;
AST child3 = null;
if (child1 != null) {
child2 = child1.getNextSibling();
if (child2 != null) {
child3 = child2.getNextSibling();
}
}
switch(ast.getType()) {
// The top of the tree looks like this:
// ROOT_ID "Whatever.java"
// package
// imports
// class definition
case ROOT_ID:
dumpHiddenTokens(PdePreprocessor.filter.getInitialHiddenToken());
printChildren(ast);
break;
// supporting a "package" statement in a PDE program has
// a bunch of issues with it that need to dealt in the compilation
// code too, so this isn't actually tested.
case PACKAGE_DEF:
out.print("package");
dumpHiddenAfter(ast);
print(ast.getFirstChild());
break;
// IMPORT has exactly one child
case IMPORT:
out.print("import");
dumpHiddenAfter(ast);
print(ast.getFirstChild());
break;
case CLASS_DEF:
case INTERFACE_DEF:
print(getChild(ast, MODIFIERS));
if (ast.getType() == CLASS_DEF) {
out.print("class");
} else {
out.print("interface");
}
dumpHiddenBefore(getChild(ast, IDENT));
print(getChild(ast, IDENT));
print(getChild(ast, EXTENDS_CLAUSE));
print(getChild(ast, IMPLEMENTS_CLAUSE));
print(getChild(ast, OBJBLOCK));
break;
case EXTENDS_CLAUSE:
if (hasChildren(ast)) {
out.print("extends");
dumpHiddenBefore(getBestPrintableNode(ast, false));
printChildren(ast);
}
break;
case IMPLEMENTS_CLAUSE:
if (hasChildren(ast)) {
out.print("implements");
dumpHiddenBefore(getBestPrintableNode(ast, false));
printChildren(ast);
}
break;
// DOT always has exactly two children.
case DOT:
print(child1);
out.print(".");
dumpHiddenAfter(ast);
print(child2);
break;
case MODIFIERS:
case OBJBLOCK:
case CTOR_DEF:
//case METHOD_DEF:
case PARAMETERS:
case PARAMETER_DEF:
case VARIABLE_DEF:
case TYPE:
case SLIST:
case ELIST:
case ARRAY_DECLARATOR:
case TYPECAST:
case EXPR:
case ARRAY_INIT:
case FOR_INIT:
case FOR_CONDITION:
case FOR_ITERATOR:
case METHOD_CALL:
case INSTANCE_INIT:
case INDEX_OP:
case SUPER_CTOR_CALL:
case CTOR_CALL:
printChildren(ast);
break;
case METHOD_DEF:
// kids seem to be: MODIFIERS TYPE setup PARAMETERS
//AST parent = (AST) stack.peek();
AST modifiersChild = ast.getFirstChild();
AST typeChild = modifiersChild.getNextSibling();
AST methodNameChild = typeChild.getNextSibling();
AST parametersChild = methodNameChild.getNextSibling();
// to output, use print(child) on each of the four
/*
// 1. figure out if this is setup, draw, or loop
String methodName = methodNameChild.getText();
if (publicMethods.get(methodName) != null) {
// make sure this feller is public
boolean foundPublic = false;
AST child = modifiersChild.getFirstChild();
while (child != null) {
if (child.getText().equals("public")) {
foundPublic = true;
child = null;
} else {
//out.print("." + child.getText() + ".");
child = child.getNextSibling();
}
}
if (!foundPublic) {
out.print("public ");
}
*/
// if this method doesn't have a specifier, make it public
// (useful for setup/keyPressed/etc)
boolean foundSpecifier = false;
AST child = modifiersChild.getFirstChild();
while (child != null) {
String childText = child.getText();
if (childText.equals("public") ||
childText.equals("protected") ||
childText.equals("private")) {
foundSpecifier = true;
child = null;
} else {
//out.print("." + child.getText() + ".");
child = child.getNextSibling();
}
}
if (!foundSpecifier) {
out.print("public ");
}
printChildren(ast); // everything is fine
break;
// if we have two children, it's of the form "a=0"
// if just one child, it's of the form "=0" (where the
// lhs is above this AST).
case ASSIGN:
if (child2 != null) {
print(child1);
out.print("=");
dumpHiddenAfter(ast);
print(child2);
}
else {
out.print("=");
dumpHiddenAfter(ast);
print(child1);
}
break;
// binary operators:
case PLUS:
case MINUS:
case DIV:
case MOD:
case NOT_EQUAL:
case EQUAL:
case LT:
case GT:
case LE:
case GE:
case LOR:
case LAND:
case BOR:
case BXOR:
case BAND:
case SL:
case SR:
case BSR:
case LITERAL_instanceof:
case PLUS_ASSIGN:
case MINUS_ASSIGN:
case STAR_ASSIGN:
case DIV_ASSIGN:
case MOD_ASSIGN:
case SR_ASSIGN:
case BSR_ASSIGN:
case SL_ASSIGN:
case BAND_ASSIGN:
case BXOR_ASSIGN:
case BOR_ASSIGN:
printBinaryOperator(ast);
break;
case LITERAL_for:
out.print(name(ast));
dumpHiddenAfter(ast);
printChildren(ast);
break;
case POST_INC:
case POST_DEC:
print(child1);
out.print(name(ast));
dumpHiddenAfter(ast);
break;
// unary operators:
case BNOT:
case LNOT:
case INC:
case DEC:
case UNARY_MINUS:
case UNARY_PLUS:
out.print(name(ast));
dumpHiddenAfter(ast);
print(child1);
break;
case LITERAL_new:
out.print("new");
dumpHiddenAfter(ast);
print(child1);
print(child2);
// "new String[] {...}": the stuff in {} is child3
if (child3 != null) {
print(child3);
}
break;
case LITERAL_return:
out.print("return");
dumpHiddenAfter(ast);
print(child1);
break;
case STATIC_INIT:
out.print("static");
dumpHiddenBefore(getBestPrintableNode(ast, false));
print(child1);
break;
case LITERAL_switch:
out.print("switch");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case CASE_GROUP:
printChildren(ast);
break;
case LITERAL_case:
out.print("case");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_default:
out.print("default");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case NUM_INT:
case CHAR_LITERAL:
case STRING_LITERAL:
case NUM_FLOAT:
out.print(ast.getText());
dumpHiddenAfter(ast);
break;
case LITERAL_private:
case LITERAL_public:
case LITERAL_protected:
case LITERAL_static:
case LITERAL_transient:
case LITERAL_native:
case LITERAL_threadsafe:
case LITERAL_synchronized:
case LITERAL_volatile:
case FINAL:
case ABSTRACT:
case LITERAL_package:
case LITERAL_void:
case LITERAL_boolean:
case LITERAL_byte:
case LITERAL_char:
case LITERAL_short:
case LITERAL_int:
case LITERAL_float:
case LITERAL_long:
case LITERAL_double:
case LITERAL_true:
case LITERAL_false:
case LITERAL_null:
case SEMI:
case LITERAL_this:
case LITERAL_super:
case LITERAL_continue:
case LITERAL_break:
out.print(name(ast));
dumpHiddenAfter(ast);
break;
case EMPTY_STAT:
case EMPTY_FIELD:
break;
// yuck: Distinguish between "import x.y.*" and "x = 1 * 3"
case STAR:
if (hasChildren(ast)) { // the binary mult. operator
printBinaryOperator(ast);
}
else { // the special "*" in import:
out.print("*");
dumpHiddenAfter(ast);
}
break;
case LITERAL_throws:
out.print("throws");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_if:
out.print("if");
dumpHiddenAfter(ast);
print(child1); // the "if" condition: an EXPR
print(child2); // the "then" clause is an SLIST
if (child3 != null) {
out.print("else");
dumpHiddenBefore(getBestPrintableNode(child3, true));
print(child3); // optional "else" clause: an SLIST
}
break;
case LITERAL_while:
out.print("while");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_do:
out.print("do");
dumpHiddenAfter(ast);
print(child1); // an SLIST
out.print("while");
dumpHiddenBefore(getBestPrintableNode(child2, false));
print(child2); // an EXPR
break;
case LITERAL_try:
out.print("try");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_catch:
out.print("catch");
dumpHiddenAfter(ast);
printChildren(ast);
break;
// the first child is the "try" and the second is the SLIST
case LITERAL_finally:
out.print("finally");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_throw:
out.print("throw");
dumpHiddenAfter(ast);
print(child1);
break;
// the dreaded trinary operator
case QUESTION:
print(child1);
out.print("?");
dumpHiddenAfter(ast);
print(child2);
print(child3);
break;
// pde specific or modified tokens start here
// Image -> BImage, Font -> BFont as appropriate
case IDENT:
/*
if (ast.getText().equals("Image") &&
Preferences.getBoolean("preproc.substitute_image")) { //, true)) {
out.print("BImage");
} else if (ast.getText().equals("Font") &&
Preferences.getBoolean("preproc.substitute_font")) { //, true)) {
out.print("BFont");
} else {
*/
out.print(ast.getText());
//}
dumpHiddenAfter(ast);
break;
// the color datatype is just an alias for int
case LITERAL_color:
out.print("int");
dumpHiddenAfter(ast);
break;
case WEBCOLOR_LITERAL:
if (ast.getText().length() != 6) {
System.err.println("Internal error: incorrect length of webcolor " +
"literal should have been detected sooner.");
break;
}
out.print("0xff" + ast.getText());
dumpHiddenAfter(ast);
break;
// allow for stuff like int(43.2).
case CONSTRUCTOR_CAST:
AST nonTerminalTypeNode = child1;
AST terminalTypeNode = child1.getFirstChild();
AST exprToCast = child2;
/*
// if this is a string type, add .valueOf()
if (nonTerminalTypeNode.getType() == PdeRecognizer.TYPE &&
terminalTypeNode.getText().equals("String")) {
out.print(terminalTypeNode.getText() + ".valueOf");
dumpHiddenAfter(terminalTypeNode);
print(exprToCast);
// if the expresion to be cast is a string literal, try and parse it.
//
// ideally, we'd be able to do this for all expressions with a
// string type, not just string literals. however, the parser
// doesn't currently track expression type, and for full
// functionality, we'd need to do semantic analysis to handle
// imports so that we could know the return types of method calls.
//
} else if (exprToCast.getFirstChild().getType() == STRING_LITERAL ) {
switch (terminalTypeNode.getType()) {
case PdeRecognizer.LITERAL_byte:
out.print("Byte.parseByte");
dumpHiddenAfter(terminalTypeNode);
print(exprToCast);
break;
case PdeRecognizer.LITERAL_double:
out.print("(new Double");
dumpHiddenAfter(terminalTypeNode);
out.print(exprToCast.getFirstChild().getText() + ").doubleValue()");
dumpHiddenAfter(exprToCast.getFirstChild());
break;
case PdeRecognizer.LITERAL_float:
out.print("(new Float");
dumpHiddenAfter(terminalTypeNode);
out.print(exprToCast.getFirstChild().getText() + ").floatValue()");
dumpHiddenAfter(exprToCast.getFirstChild());
break;
case PdeRecognizer.LITERAL_int:
case PdeRecognizer.LITERAL_color:
out.print("Integer.parseInt");
dumpHiddenAfter(terminalTypeNode);
print(exprToCast);
break;
case PdeRecognizer.LITERAL_long:
out.print("Long.parseLong");
break;
case PdeRecognizer.LITERAL_short:
out.print("Short.parseShort");
break;
default:
throw new RunnerException(Compiler.SUPER_BADNESS);
}
// for builtin types, use regular casting syntax
} else {
*/
// result of below is (int)(4.0
//out.print("(");
//out.print(terminalTypeNode.getText() + ")"); // typename
//dumpHiddenAfter(terminalTypeNode);
//print(exprToCast);
//}
//out.print("(");
String pooType = terminalTypeNode.getText();
out.print("PApplet.to" +
Character.toUpperCase(pooType.charAt(0)) +
pooType.substring(1));
dumpHiddenAfter(terminalTypeNode); // the left paren
print(exprToCast);
//out.print("x)");
break;
// making floating point literals default to floats, not doubles
case NUM_DOUBLE:
out.print(ast.getText());
if (Preferences.getBoolean("preproc.substitute_floats")) { //, true) ) {
out.print("f");
}
dumpHiddenAfter(ast);
break;
default:
debug.println("Invalid type:" + ast.getType());
break;
/* The following are tokens, but I don't think JavaRecognizer
ever produces an AST with one of these types:
case COMMA:
case LITERAL_implements:
case LITERAL_class:
case LITERAL_extends:
case EOF:
case NULL_TREE_LOOKAHEAD:
case BLOCK:
case LABELED_STAT: // refuse to implement on moral grounds :)
case LITERAL_import:
case LBRACK:
case RBRACK:
case LCURLY:
case RCURLY:
case LPAREN:
case RPAREN:
case LITERAL_else: // else is a child of "if" AST
case COLON: // part of the trinary operator
case WS: // whitespace
case ESC:
case HEX_DIGIT:
case VOCAB:
case EXPONENT: // exponents and float suffixes are left in the NUM_FLOAT
case FLOAT_SUFFIX
*/
}
stack.pop();
}
}

1935
app/preproc/PdeLexer.java Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,163 @@
// $ANTLR 2.7.2: "expandedpde.g" -> "PdeRecognizer.java"$
package processing.app.preproc;
import processing.app.*;
public interface PdePartialTokenTypes {
int EOF = 1;
int NULL_TREE_LOOKAHEAD = 3;
int BLOCK = 4;
int MODIFIERS = 5;
int OBJBLOCK = 6;
int SLIST = 7;
int CTOR_DEF = 8;
int METHOD_DEF = 9;
int VARIABLE_DEF = 10;
int INSTANCE_INIT = 11;
int STATIC_INIT = 12;
int TYPE = 13;
int CLASS_DEF = 14;
int INTERFACE_DEF = 15;
int PACKAGE_DEF = 16;
int ARRAY_DECLARATOR = 17;
int EXTENDS_CLAUSE = 18;
int IMPLEMENTS_CLAUSE = 19;
int PARAMETERS = 20;
int PARAMETER_DEF = 21;
int LABELED_STAT = 22;
int TYPECAST = 23;
int INDEX_OP = 24;
int POST_INC = 25;
int POST_DEC = 26;
int METHOD_CALL = 27;
int EXPR = 28;
int ARRAY_INIT = 29;
int IMPORT = 30;
int UNARY_MINUS = 31;
int UNARY_PLUS = 32;
int CASE_GROUP = 33;
int ELIST = 34;
int FOR_INIT = 35;
int FOR_CONDITION = 36;
int FOR_ITERATOR = 37;
int EMPTY_STAT = 38;
int FINAL = 39;
int ABSTRACT = 40;
int STRICTFP = 41;
int SUPER_CTOR_CALL = 42;
int CTOR_CALL = 43;
int LITERAL_package = 44;
int SEMI = 45;
int LITERAL_import = 46;
int LBRACK = 47;
int RBRACK = 48;
int LITERAL_void = 49;
int LITERAL_boolean = 50;
int LITERAL_byte = 51;
int LITERAL_char = 52;
int LITERAL_short = 53;
int LITERAL_int = 54;
int LITERAL_float = 55;
int LITERAL_long = 56;
int LITERAL_double = 57;
int IDENT = 58;
int DOT = 59;
int STAR = 60;
int LITERAL_private = 61;
int LITERAL_public = 62;
int LITERAL_protected = 63;
int LITERAL_static = 64;
int LITERAL_transient = 65;
int LITERAL_native = 66;
int LITERAL_threadsafe = 67;
int LITERAL_synchronized = 68;
int LITERAL_volatile = 69;
int LITERAL_class = 70;
int LITERAL_extends = 71;
int LITERAL_interface = 72;
int LCURLY = 73;
int RCURLY = 74;
int COMMA = 75;
int LITERAL_implements = 76;
int LPAREN = 77;
int RPAREN = 78;
int LITERAL_this = 79;
int LITERAL_super = 80;
int ASSIGN = 81;
int LITERAL_throws = 82;
int COLON = 83;
int LITERAL_if = 84;
int LITERAL_else = 85;
int LITERAL_for = 86;
int LITERAL_while = 87;
int LITERAL_do = 88;
int LITERAL_break = 89;
int LITERAL_continue = 90;
int LITERAL_return = 91;
int LITERAL_switch = 92;
int LITERAL_throw = 93;
int LITERAL_assert = 94;
int LITERAL_case = 95;
int LITERAL_default = 96;
int LITERAL_try = 97;
int LITERAL_finally = 98;
int LITERAL_catch = 99;
int PLUS_ASSIGN = 100;
int MINUS_ASSIGN = 101;
int STAR_ASSIGN = 102;
int DIV_ASSIGN = 103;
int MOD_ASSIGN = 104;
int SR_ASSIGN = 105;
int BSR_ASSIGN = 106;
int SL_ASSIGN = 107;
int BAND_ASSIGN = 108;
int BXOR_ASSIGN = 109;
int BOR_ASSIGN = 110;
int QUESTION = 111;
int LOR = 112;
int LAND = 113;
int BOR = 114;
int BXOR = 115;
int BAND = 116;
int NOT_EQUAL = 117;
int EQUAL = 118;
int LT = 119;
int GT = 120;
int LE = 121;
int GE = 122;
int LITERAL_instanceof = 123;
int SL = 124;
int SR = 125;
int BSR = 126;
int PLUS = 127;
int MINUS = 128;
int DIV = 129;
int MOD = 130;
int INC = 131;
int DEC = 132;
int BNOT = 133;
int LNOT = 134;
int LITERAL_true = 135;
int LITERAL_false = 136;
int LITERAL_null = 137;
int LITERAL_new = 138;
int NUM_INT = 139;
int CHAR_LITERAL = 140;
int STRING_LITERAL = 141;
int NUM_FLOAT = 142;
int NUM_LONG = 143;
int NUM_DOUBLE = 144;
int WS = 145;
int SL_COMMENT = 146;
int ML_COMMENT = 147;
int ESC = 148;
int HEX_DIGIT = 149;
int VOCAB = 150;
int EXPONENT = 151;
int FLOAT_SUFFIX = 152;
int CONSTRUCTOR_CAST = 153;
int EMPTY_FIELD = 154;
int WEBCOLOR_LITERAL = 155;
int LITERAL_color = 156;
}

View File

@ -0,0 +1,155 @@
// $ANTLR 2.7.2: expandedpde.g -> PdePartialTokenTypes.txt$
PdePartial // output token vocab name
BLOCK=4
MODIFIERS=5
OBJBLOCK=6
SLIST=7
CTOR_DEF=8
METHOD_DEF=9
VARIABLE_DEF=10
INSTANCE_INIT=11
STATIC_INIT=12
TYPE=13
CLASS_DEF=14
INTERFACE_DEF=15
PACKAGE_DEF=16
ARRAY_DECLARATOR=17
EXTENDS_CLAUSE=18
IMPLEMENTS_CLAUSE=19
PARAMETERS=20
PARAMETER_DEF=21
LABELED_STAT=22
TYPECAST=23
INDEX_OP=24
POST_INC=25
POST_DEC=26
METHOD_CALL=27
EXPR=28
ARRAY_INIT=29
IMPORT=30
UNARY_MINUS=31
UNARY_PLUS=32
CASE_GROUP=33
ELIST=34
FOR_INIT=35
FOR_CONDITION=36
FOR_ITERATOR=37
EMPTY_STAT=38
FINAL="final"=39
ABSTRACT="abstract"=40
STRICTFP="strictfp"=41
SUPER_CTOR_CALL=42
CTOR_CALL=43
LITERAL_package="package"=44
SEMI=45
LITERAL_import="import"=46
LBRACK=47
RBRACK=48
LITERAL_void="void"=49
LITERAL_boolean="boolean"=50
LITERAL_byte="byte"=51
LITERAL_char="char"=52
LITERAL_short="short"=53
LITERAL_int="int"=54
LITERAL_float="float"=55
LITERAL_long="long"=56
LITERAL_double="double"=57
IDENT=58
DOT=59
STAR=60
LITERAL_private="private"=61
LITERAL_public="public"=62
LITERAL_protected="protected"=63
LITERAL_static="static"=64
LITERAL_transient="transient"=65
LITERAL_native="native"=66
LITERAL_threadsafe="threadsafe"=67
LITERAL_synchronized="synchronized"=68
LITERAL_volatile="volatile"=69
LITERAL_class="class"=70
LITERAL_extends="extends"=71
LITERAL_interface="interface"=72
LCURLY=73
RCURLY=74
COMMA=75
LITERAL_implements="implements"=76
LPAREN=77
RPAREN=78
LITERAL_this="this"=79
LITERAL_super="super"=80
ASSIGN=81
LITERAL_throws="throws"=82
COLON=83
LITERAL_if="if"=84
LITERAL_else="else"=85
LITERAL_for="for"=86
LITERAL_while="while"=87
LITERAL_do="do"=88
LITERAL_break="break"=89
LITERAL_continue="continue"=90
LITERAL_return="return"=91
LITERAL_switch="switch"=92
LITERAL_throw="throw"=93
LITERAL_assert="assert"=94
LITERAL_case="case"=95
LITERAL_default="default"=96
LITERAL_try="try"=97
LITERAL_finally="finally"=98
LITERAL_catch="catch"=99
PLUS_ASSIGN=100
MINUS_ASSIGN=101
STAR_ASSIGN=102
DIV_ASSIGN=103
MOD_ASSIGN=104
SR_ASSIGN=105
BSR_ASSIGN=106
SL_ASSIGN=107
BAND_ASSIGN=108
BXOR_ASSIGN=109
BOR_ASSIGN=110
QUESTION=111
LOR=112
LAND=113
BOR=114
BXOR=115
BAND=116
NOT_EQUAL=117
EQUAL=118
LT=119
GT=120
LE=121
GE=122
LITERAL_instanceof="instanceof"=123
SL=124
SR=125
BSR=126
PLUS=127
MINUS=128
DIV=129
MOD=130
INC=131
DEC=132
BNOT=133
LNOT=134
LITERAL_true="true"=135
LITERAL_false="false"=136
LITERAL_null="null"=137
LITERAL_new="new"=138
NUM_INT=139
CHAR_LITERAL=140
STRING_LITERAL=141
NUM_FLOAT=142
NUM_LONG=143
NUM_DOUBLE=144
WS=145
SL_COMMENT=146
ML_COMMENT=147
ESC=148
HEX_DIGIT=149
VOCAB=150
EXPONENT=151
FLOAT_SUFFIX=152
CONSTRUCTOR_CAST=153
EMPTY_FIELD=154
WEBCOLOR_LITERAL=155
LITERAL_color="color"=156

View File

@ -0,0 +1,419 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
PdePreprocessor - wrapper for default ANTLR-generated parser
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
ANTLR-generated parser and several supporting classes written
by Dan Mosedale via funding from the Interaction Institute IVREA.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.preproc;
import processing.app.*;
//import processing.core.*;
import java.io.*;
import antlr.*;
import antlr.collections.*;
import antlr.collections.impl.*;
import com.oroinc.text.regex.*;
public class PdePreprocessor {
static final int JDK11 = 0;
static final int JDK13 = 1;
static final int JDK14 = 2;
static String defaultImports[][] = new String[3][];
// these ones have the .* at the end, since a class name
// might be at the end instead of .* whcih would make trouble
// other classes using this can lop of the . and anything after
// it to produce a package name consistently.
public String extraImports[];
// imports just from the code folder, treated differently
// than the others, since the imports are auto-generated.
public String codeFolderImports[];
static public final int STATIC = 0; // formerly BEGINNER
static public final int ACTIVE = 1; // formerly INTERMEDIATE
static public final int JAVA = 2; // formerly ADVANCED
// static to make it easier for the antlr preproc to get at it
static public int programType = -1;
Reader programReader;
String buildPath;
// used for calling the ASTFactory to get the root node
private static final int ROOT_ID = 0;
/**
* These may change in-between (if the prefs panel adds this option)
* so grab them here on construction.
*/
public PdePreprocessor() {
defaultImports[JDK11] =
Base.split(Preferences.get("preproc.imports.jdk11"), ',');
defaultImports[JDK13] =
Base.split(Preferences.get("preproc.imports.jdk13"), ',');
defaultImports[JDK14] =
Base.split(Preferences.get("preproc.imports.jdk14"), ',');
}
/**
* Used by PdeEmitter.dumpHiddenTokens()
*/
public static TokenStreamCopyingHiddenTokenFilter filter;
/**
* preprocesses a pde file and write out a java file
* @return the classname of the exported Java
*/
//public String write(String program, String buildPath, String name,
// String extraImports[]) throws java.lang.Exception {
public String write(String program, String buildPath,
String name, String codeFolderPackages[])
throws java.lang.Exception {
// if the program ends with no CR or LF an OutOfMemoryError will happen.
// not gonna track down the bug now, so here's a hack for it:
if ((program.length() > 0) &&
program.charAt(program.length()-1) != '\n') {
program += "\n";
}
if (Preferences.getBoolean("preproc.substitute_unicode")) {
// check for non-ascii chars (these will be/must be in unicode format)
char p[] = program.toCharArray();
int unicodeCount = 0;
for (int i = 0; i < p.length; i++) {
if (p[i] > 127) unicodeCount++;
}
// if non-ascii chars are in there, convert to unicode escapes
if (unicodeCount != 0) {
// add unicodeCount * 5.. replacing each unicode char
// with six digit uXXXX sequence (xxxx is in hex)
// (except for nbsp chars which will be a replaced with a space)
int index = 0;
char p2[] = new char[p.length + unicodeCount*5];
for (int i = 0; i < p.length; i++) {
if (p[i] < 128) {
p2[index++] = p[i];
} else if (p[i] == 160) { // unicode for non-breaking space
p2[index++] = ' ';
} else {
int c = p[i];
p2[index++] = '\\';
p2[index++] = 'u';
char str[] = Integer.toHexString(c).toCharArray();
// add leading zeros, so that the length is 4
//for (int i = 0; i < 4 - str.length; i++) p2[index++] = '0';
for (int m = 0; m < 4 - str.length; m++) p2[index++] = '0';
System.arraycopy(str, 0, p2, index, str.length);
index += str.length;
}
}
program = new String(p2, 0, index);
}
}
// if this guy has his own imports, need to remove them
// just in case it's not an advanced mode sketch
PatternMatcher matcher = new Perl5Matcher();
PatternCompiler compiler = new Perl5Compiler();
//String mess = "^\\s*(import\\s+\\S+\\s*;)";
String mess = "^\\s*(import\\s+)(\\S+)(\\s*;)";
java.util.Vector imports = new java.util.Vector();
Pattern pattern = null;
try {
pattern = compiler.compile(mess);
} catch (MalformedPatternException e) {
e.printStackTrace();
return null;
}
do {
PatternMatcherInput input = new PatternMatcherInput(program);
if (!matcher.contains(input, pattern)) break;
MatchResult result = matcher.getMatch();
String piece1 = result.group(1).toString();
String piece2 = result.group(2).toString(); // the package name
String piece3 = result.group(3).toString();
String piece = piece1 + piece2 + piece3;
int len = piece.length();
//imports.add(piece);
imports.add(piece2);
int idx = program.indexOf(piece);
// just remove altogether?
program = program.substring(0, idx) + program.substring(idx + len);
//System.out.println("removing " + piece);
} while (true);
extraImports = new String[imports.size()];
imports.copyInto(extraImports);
// if using opengl, add it to the special imports
/*
if (Preferences.get("renderer").equals("opengl")) {
extraImports = new String[imports.size() + 1];
imports.copyInto(extraImports);
extraImports[extraImports.length - 1] = "processing.opengl.*";
}
*/
/*
if (codeFolderPackages != null) {
extraImports = new String[importsCount + codeFolderPackages.length];
imports.copyInto(extraImports);
for (int i = 0; i < codeFolderPackages.length; i++) {
extraImports[importsCount + i] = codeFolderPackages[i] + ".*";
}
codeFolderImports = null;
}
*/
if (codeFolderPackages != null) {
codeFolderImports = new String[codeFolderPackages.length];
for (int i = 0; i < codeFolderPackages.length; i++) {
codeFolderImports[i] = codeFolderPackages[i] + ".*";
}
} else {
codeFolderImports = null;
}
// do this after the program gets re-combobulated
this.programReader = new StringReader(program);
this.buildPath = buildPath;
// create a lexer with the stream reader, and tell it to handle
// hidden tokens (eg whitespace, comments) since we want to pass these
// through so that the line numbers when the compiler reports errors
// match those that will be highlighted in the PDE IDE
//
PdeLexer lexer = new PdeLexer(programReader);
lexer.setTokenObjectClass("antlr.CommonHiddenStreamToken");
// create the filter for hidden tokens and specify which tokens to
// hide and which to copy to the hidden text
//
filter = new TokenStreamCopyingHiddenTokenFilter(lexer);
filter.hide(PdeRecognizer.SL_COMMENT);
filter.hide(PdeRecognizer.ML_COMMENT);
filter.hide(PdeRecognizer.WS);
filter.copy(PdeRecognizer.SEMI);
filter.copy(PdeRecognizer.LPAREN);
filter.copy(PdeRecognizer.RPAREN);
filter.copy(PdeRecognizer.LCURLY);
filter.copy(PdeRecognizer.RCURLY);
filter.copy(PdeRecognizer.COMMA);
filter.copy(PdeRecognizer.RBRACK);
filter.copy(PdeRecognizer.LBRACK);
filter.copy(PdeRecognizer.COLON);
// create a parser and set what sort of AST should be generated
//
PdeRecognizer parser = new PdeRecognizer(filter);
// use our extended AST class
//
parser.setASTNodeClass("antlr.ExtendedCommonASTWithHiddenTokens");
// start parsing at the compilationUnit non-terminal
//
parser.pdeProgram();
// set up the AST for traversal by PdeEmitter
//
ASTFactory factory = new ASTFactory();
AST parserAST = parser.getAST();
AST rootNode = factory.create(ROOT_ID, "AST ROOT");
rootNode.setFirstChild(parserAST);
// unclear if this actually works, but it's worth a shot
//
//((CommonAST)parserAST).setVerboseStringConversion(
// true, parser.getTokenNames());
// (made to use the static version because of jikes 1.22 warning)
CommonAST.setVerboseStringConversion(true, parser.getTokenNames());
// if this is an advanced program, the classname is already defined.
//
if (programType == JAVA) {
name = getFirstClassName(parserAST);
}
// if 'null' was passed in for the name, but this isn't
// a 'java' mode class, then there's a problem, so punt.
//
if (name == null) return null;
// output the code
//
PdeEmitter emitter = new PdeEmitter();
File streamFile = new File(buildPath, name + ".java");
PrintStream stream = new PrintStream(new FileOutputStream(streamFile));
//writeHeader(stream, extraImports, name);
writeHeader(stream, name);
emitter.setOut(stream);
emitter.print(rootNode);
writeFooter(stream);
stream.close();
// if desired, serialize the parse tree to an XML file. can
// be viewed usefully with Mozilla or IE
if (Preferences.getBoolean("preproc.output_parse_tree")) {
stream = new PrintStream(new FileOutputStream("parseTree.xml"));
stream.println("<?xml version=\"1.0\"?>");
stream.println("<document>");
OutputStreamWriter writer = new OutputStreamWriter(stream);
if (parserAST != null) {
((CommonAST)parserAST).xmlSerialize(writer);
}
writer.flush();
stream.println("</document>");
writer.close();
}
return name;
}
/**
* Write any required header material (eg imports, class decl stuff)
*
* @param out PrintStream to write it to.
* @param exporting Is this being exported from PDE?
* @param name Name of the class being created.
*/
void writeHeader(PrintStream out, String className) {
// must include processing.core
out.print("import processing.core.*; ");
// emit emports that are needed for classes from the code folder
if (extraImports != null) {
for (int i = 0; i < extraImports.length; i++) {
out.print("import " + extraImports[i] + "; ");
}
}
if (codeFolderImports != null) {
for (int i = 0; i < codeFolderImports.length; i++) {
out.print("import " + codeFolderImports[i] + "; ");
}
}
// emit standard imports (read from pde.properties)
// for each language level that's being used.
String jdkVersionStr = Preferences.get("preproc.jdk_version");
int jdkVersion = JDK11; // default
if (jdkVersionStr.equals("1.3")) { jdkVersion = JDK13; };
if (jdkVersionStr.equals("1.4")) { jdkVersion = JDK14; };
for (int i = 0; i <= jdkVersion; i++) {
for (int j = 0; j < defaultImports[i].length; j++) {
out.print("import " + defaultImports[i][j] + ".*; ");
}
}
//boolean opengl = Preferences.get("renderer").equals("opengl");
//if (opengl) {
//out.println("import processing.opengl.*; ");
//}
if (programType < JAVA) {
// open the class definition
out.print("public class " + className + " extends ");
//if (opengl) {
//out.print("PAppletGL");
//} else {
out.print("PApplet");
//}
out.print(" {");
if (programType == STATIC) {
// now that size() and background() can go inside of draw()
// actually, use setup(), because when running externally
// the applet size needs to be set before the window is drawn,
// meaning that after setup() things need to be ducky.
//out.print("public void draw() {");
out.print("public void setup() {");
}
}
}
/**
* Write any necessary closing text.
*
* @param out PrintStream to write it to.
*/
void writeFooter(PrintStream out) {
if (programType == STATIC) {
// close off draw() definition
out.print("noLoop(); ");
out.print("}");
}
if (programType < JAVA) {
// close off the class definition
out.print("}");
}
}
static String advClassName = "";
/**
* Find the first CLASS_DEF node in the tree, and return the name of the
* class in question.
*
* XXXdmose right now, we're using a little hack to the grammar to get
* this info. In fact, we should be descending the AST passed in.
*/
String getFirstClassName(AST ast) {
String t = advClassName;
advClassName = "";
return t;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,163 @@
// $ANTLR 2.7.2: "expandedpde.g" -> "PdeRecognizer.java"$
package processing.app.preproc;
import processing.app.*;
public interface PdeTokenTypes {
int EOF = 1;
int NULL_TREE_LOOKAHEAD = 3;
int BLOCK = 4;
int MODIFIERS = 5;
int OBJBLOCK = 6;
int SLIST = 7;
int CTOR_DEF = 8;
int METHOD_DEF = 9;
int VARIABLE_DEF = 10;
int INSTANCE_INIT = 11;
int STATIC_INIT = 12;
int TYPE = 13;
int CLASS_DEF = 14;
int INTERFACE_DEF = 15;
int PACKAGE_DEF = 16;
int ARRAY_DECLARATOR = 17;
int EXTENDS_CLAUSE = 18;
int IMPLEMENTS_CLAUSE = 19;
int PARAMETERS = 20;
int PARAMETER_DEF = 21;
int LABELED_STAT = 22;
int TYPECAST = 23;
int INDEX_OP = 24;
int POST_INC = 25;
int POST_DEC = 26;
int METHOD_CALL = 27;
int EXPR = 28;
int ARRAY_INIT = 29;
int IMPORT = 30;
int UNARY_MINUS = 31;
int UNARY_PLUS = 32;
int CASE_GROUP = 33;
int ELIST = 34;
int FOR_INIT = 35;
int FOR_CONDITION = 36;
int FOR_ITERATOR = 37;
int EMPTY_STAT = 38;
int FINAL = 39;
int ABSTRACT = 40;
int STRICTFP = 41;
int SUPER_CTOR_CALL = 42;
int CTOR_CALL = 43;
int LITERAL_package = 44;
int SEMI = 45;
int LITERAL_import = 46;
int LBRACK = 47;
int RBRACK = 48;
int LITERAL_void = 49;
int LITERAL_boolean = 50;
int LITERAL_byte = 51;
int LITERAL_char = 52;
int LITERAL_short = 53;
int LITERAL_int = 54;
int LITERAL_float = 55;
int LITERAL_long = 56;
int LITERAL_double = 57;
int IDENT = 58;
int DOT = 59;
int STAR = 60;
int LITERAL_private = 61;
int LITERAL_public = 62;
int LITERAL_protected = 63;
int LITERAL_static = 64;
int LITERAL_transient = 65;
int LITERAL_native = 66;
int LITERAL_threadsafe = 67;
int LITERAL_synchronized = 68;
int LITERAL_volatile = 69;
int LITERAL_class = 70;
int LITERAL_extends = 71;
int LITERAL_interface = 72;
int LCURLY = 73;
int RCURLY = 74;
int COMMA = 75;
int LITERAL_implements = 76;
int LPAREN = 77;
int RPAREN = 78;
int LITERAL_this = 79;
int LITERAL_super = 80;
int ASSIGN = 81;
int LITERAL_throws = 82;
int COLON = 83;
int LITERAL_if = 84;
int LITERAL_else = 85;
int LITERAL_for = 86;
int LITERAL_while = 87;
int LITERAL_do = 88;
int LITERAL_break = 89;
int LITERAL_continue = 90;
int LITERAL_return = 91;
int LITERAL_switch = 92;
int LITERAL_throw = 93;
int LITERAL_assert = 94;
int LITERAL_case = 95;
int LITERAL_default = 96;
int LITERAL_try = 97;
int LITERAL_finally = 98;
int LITERAL_catch = 99;
int PLUS_ASSIGN = 100;
int MINUS_ASSIGN = 101;
int STAR_ASSIGN = 102;
int DIV_ASSIGN = 103;
int MOD_ASSIGN = 104;
int SR_ASSIGN = 105;
int BSR_ASSIGN = 106;
int SL_ASSIGN = 107;
int BAND_ASSIGN = 108;
int BXOR_ASSIGN = 109;
int BOR_ASSIGN = 110;
int QUESTION = 111;
int LOR = 112;
int LAND = 113;
int BOR = 114;
int BXOR = 115;
int BAND = 116;
int NOT_EQUAL = 117;
int EQUAL = 118;
int LT = 119;
int GT = 120;
int LE = 121;
int GE = 122;
int LITERAL_instanceof = 123;
int SL = 124;
int SR = 125;
int BSR = 126;
int PLUS = 127;
int MINUS = 128;
int DIV = 129;
int MOD = 130;
int INC = 131;
int DEC = 132;
int BNOT = 133;
int LNOT = 134;
int LITERAL_true = 135;
int LITERAL_false = 136;
int LITERAL_null = 137;
int LITERAL_new = 138;
int NUM_INT = 139;
int CHAR_LITERAL = 140;
int STRING_LITERAL = 141;
int NUM_FLOAT = 142;
int NUM_LONG = 143;
int NUM_DOUBLE = 144;
int WS = 145;
int SL_COMMENT = 146;
int ML_COMMENT = 147;
int ESC = 148;
int HEX_DIGIT = 149;
int VOCAB = 150;
int EXPONENT = 151;
int FLOAT_SUFFIX = 152;
int CONSTRUCTOR_CAST = 153;
int EMPTY_FIELD = 154;
int WEBCOLOR_LITERAL = 155;
int LITERAL_color = 156;
}

View File

@ -0,0 +1,155 @@
// $ANTLR 2.7.2: expandedpde.g -> PdeTokenTypes.txt$
Pde // output token vocab name
BLOCK=4
MODIFIERS=5
OBJBLOCK=6
SLIST=7
CTOR_DEF=8
METHOD_DEF=9
VARIABLE_DEF=10
INSTANCE_INIT=11
STATIC_INIT=12
TYPE=13
CLASS_DEF=14
INTERFACE_DEF=15
PACKAGE_DEF=16
ARRAY_DECLARATOR=17
EXTENDS_CLAUSE=18
IMPLEMENTS_CLAUSE=19
PARAMETERS=20
PARAMETER_DEF=21
LABELED_STAT=22
TYPECAST=23
INDEX_OP=24
POST_INC=25
POST_DEC=26
METHOD_CALL=27
EXPR=28
ARRAY_INIT=29
IMPORT=30
UNARY_MINUS=31
UNARY_PLUS=32
CASE_GROUP=33
ELIST=34
FOR_INIT=35
FOR_CONDITION=36
FOR_ITERATOR=37
EMPTY_STAT=38
FINAL="final"=39
ABSTRACT="abstract"=40
STRICTFP="strictfp"=41
SUPER_CTOR_CALL=42
CTOR_CALL=43
LITERAL_package="package"=44
SEMI=45
LITERAL_import="import"=46
LBRACK=47
RBRACK=48
LITERAL_void="void"=49
LITERAL_boolean="boolean"=50
LITERAL_byte="byte"=51
LITERAL_char="char"=52
LITERAL_short="short"=53
LITERAL_int="int"=54
LITERAL_float="float"=55
LITERAL_long="long"=56
LITERAL_double="double"=57
IDENT=58
DOT=59
STAR=60
LITERAL_private="private"=61
LITERAL_public="public"=62
LITERAL_protected="protected"=63
LITERAL_static="static"=64
LITERAL_transient="transient"=65
LITERAL_native="native"=66
LITERAL_threadsafe="threadsafe"=67
LITERAL_synchronized="synchronized"=68
LITERAL_volatile="volatile"=69
LITERAL_class="class"=70
LITERAL_extends="extends"=71
LITERAL_interface="interface"=72
LCURLY=73
RCURLY=74
COMMA=75
LITERAL_implements="implements"=76
LPAREN=77
RPAREN=78
LITERAL_this="this"=79
LITERAL_super="super"=80
ASSIGN=81
LITERAL_throws="throws"=82
COLON=83
LITERAL_if="if"=84
LITERAL_else="else"=85
LITERAL_for="for"=86
LITERAL_while="while"=87
LITERAL_do="do"=88
LITERAL_break="break"=89
LITERAL_continue="continue"=90
LITERAL_return="return"=91
LITERAL_switch="switch"=92
LITERAL_throw="throw"=93
LITERAL_assert="assert"=94
LITERAL_case="case"=95
LITERAL_default="default"=96
LITERAL_try="try"=97
LITERAL_finally="finally"=98
LITERAL_catch="catch"=99
PLUS_ASSIGN=100
MINUS_ASSIGN=101
STAR_ASSIGN=102
DIV_ASSIGN=103
MOD_ASSIGN=104
SR_ASSIGN=105
BSR_ASSIGN=106
SL_ASSIGN=107
BAND_ASSIGN=108
BXOR_ASSIGN=109
BOR_ASSIGN=110
QUESTION=111
LOR=112
LAND=113
BOR=114
BXOR=115
BAND=116
NOT_EQUAL=117
EQUAL=118
LT=119
GT=120
LE=121
GE=122
LITERAL_instanceof="instanceof"=123
SL=124
SR=125
BSR=126
PLUS=127
MINUS=128
DIV=129
MOD=130
INC=131
DEC=132
BNOT=133
LNOT=134
LITERAL_true="true"=135
LITERAL_false="false"=136
LITERAL_null="null"=137
LITERAL_new="new"=138
NUM_INT=139
CHAR_LITERAL=140
STRING_LITERAL=141
NUM_FLOAT=142
NUM_LONG=143
NUM_DOUBLE=144
WS=145
SL_COMMENT=146
ML_COMMENT=147
ESC=148
HEX_DIGIT=149
VOCAB=150
EXPONENT=151
FLOAT_SUFFIX=152
CONSTRUCTOR_CAST=153
EMPTY_FIELD=154
WEBCOLOR_LITERAL=155
LITERAL_color="color"=156

106
app/preproc/README.txt Normal file
View File

@ -0,0 +1,106 @@
The PDE Preprocessor is based on the Java Grammar that comes with
ANTLR 2.7.2. Moving it forward to a new version of the grammar
shouldn't be too difficult.
Here's some info about the various files in this directory:
java.g: this is the ANTLR grammar for Java 1.3/1.4 from the ANTLR
distribution. It is in the public domain. The only change to this
file from the original this file is the uncommenting of the clauses
required to support assert().
java.tree.g: this describes the Abstract Syntax Tree (AST) generated
by java.g. It is only here as a reference for coders hacking on the
preprocessor, it is not built or used at all. Note that pde.g
overrides some of the java.g rules so that in PDE ASTs, there are a
few minor differences. Also in the public domain.
pde.g: this is the grammar and lexer for the PDE language itself. It
subclasses the java.g grammar and lexer. There are a couple of
overrides to java.g that I hope to convince the ANTLR folks to fold
back into their grammar, but most of this file is highly specific to
PDE itself.
PdeEmitter.java: this class traverses the AST generated by the PDE
Recognizer, and emits it as Java code, doing any necessary
transformations along the way. It is based on JavaEmitter.java,
available from antlr.org, written by Andy Tripp <atripp@comcast.net>,
who has given permission for it to be distributed under the GPL.
ExtendedCommonASTWithHiddenTokens.java: this adds a necessary
initialize() method, as well as a number of methods to allow for XML
serialization of the parse tree in a such a way that the hidden tokens
are visible. Much of the code is taken from the original
CommonASTWithHiddenTokens class. I hope to convince the ANTLR folks
to fold these changes back into that class so that this file will be
unnecessary.
TokenStreamCopyingHiddenTokenFilter.java: this class provides
TokenStreamHiddenTokenFilters with the concept of tokens which can be
copied so that they are seen by both the hidden token stream as well
as the parser itself. This is useful when one wants to use an
existing parser (like the Java parser included with ANTLR) that throws
away some tokens to create a parse tree which can be used to spit out
a copy of the code with only minor modifications. Partially derived
from ANTLR code. I hope to convince the ANTLR folks to fold this
functionality back into ANTLR proper as well.
whitespace_test.pde: a torture test to ensure that the preprocessor is
correctly preserving whitespace, comments, and other hidden tokens
correctly. See the comments in the code for details about how to run
the test.
All other files in this directory are generated at build time by ANTLR
itself. The ANTLR manual goes into a fair amount of detail about the
what each type of file is for.
....
Current Preprocessor Subsitutions:
"compiler.substitute_floats" (currently "substitute_f")
- treat doubles as floats, i.e. 12.3 becomes 12.3f so that people
don't have to add f after their numbers all the time. this is
confusing for beginners.
"compiler.enhanced_casting"
- byte(), char(), int(), float() works for casting. this is basic in
the current implementation, but should be expanded as described
above. color() works similarly to int(), however there is also a
*function* called color(r, g, b) in p5. will this cause trouble?
"compiler.color_datattype"
- 'color' is aliased to 'int' as a datatype to represent ARGB packed
into a single int, commonly used in p5 for pixels[] and other color
operations. this is just a search/replace type thing, and it can be
used interchangeably with int.
"compiler.web_colors" (currently "inline_web_colors")
- color c = #cc0080; should unpack to 0xffcc0080 (the ff at the top is
so that the color is opaque), which is just an int.
Other preprocessor functionality
- detects what 'mode' the program is in: static (no function brackets
at all, just assumes everything is in draw), active (setup plus draw
or loop), and java mode (full java support).
http://proce55ing.net/reference/environment/index.html
- size and background are pulled from draw mode programs and placed
into setup(). this has a problem if size() is based on a variable,
which we try to avoid people doing, but would like to be able to
support it (perhaps by requiring the size() to be final?)
- currently does a godawful scrambling of the comments so that the
substitution doesn't try to run on them. this also causes lots of
bizarro bugs.
Possible?
- would be nice to just type code wherever, mixing a 'static' style
app with a few functions. would be simpler for starting out. but it
seems that the declarations would have to be pulled out, but that
all seems problematic. or maybe it could all be inside a static { }
block. but that wouldn't seem to work either.

View File

@ -0,0 +1,221 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
package antlr;
//package processing.app.preproc;
import antlr.*;
import antlr.collections.impl.BitSet;
/**
* This class provides TokenStreamHiddenTokenFilters with the concept of
* tokens which can be copied so that they are seen by both the hidden token
* stream as well as the parser itself. This is useful when one wants to use
* an existing parser (like the Java parser included with ANTLR) that throws
* away some tokens to create a parse tree which can be used to spit out
* a copy of the code with only minor modifications.
*
* This code is partially derived from the public domain ANLTR TokenStream
*/
public class TokenStreamCopyingHiddenTokenFilter
extends TokenStreamHiddenTokenFilter
implements TokenStream {
protected BitSet copyMask;
CommonHiddenStreamToken hiddenCopy = null;
public TokenStreamCopyingHiddenTokenFilter(TokenStream input) {
super(input);
copyMask = new BitSet();
}
/**
* Indicate that all tokens of type tokenType should be copied. The copy
* is put in the stream of hidden tokens, and the original is returned in the
* stream of normal tokens.
*
* @param tokenType integer representing the token type to copied
*/
public void copy(int tokenType) {
copyMask.add(tokenType);
}
/**
* Create a clone of the important parts of the given token. Note that this
* does NOT copy the hiddenBefore and hiddenAfter fields.
*
* @param t token to partially clone
* @return newly created partial clone
*/
public CommonHiddenStreamToken partialCloneToken(CommonHiddenStreamToken t) {
CommonHiddenStreamToken u = new CommonHiddenStreamToken(t.getType(),
t.getText());
u.setColumn(t.getColumn());
u.setLine(t.getLine());
u.setFilename(t.getFilename());
return u;
}
public void linkAndCopyToken(CommonHiddenStreamToken prev,
CommonHiddenStreamToken monitored) {
// create a copy of the token in the lookahead for use as hidden token
hiddenCopy = partialCloneToken(LA(1));
// attach copy to the previous token, whether hidden or monitored
prev.setHiddenAfter(hiddenCopy);
// if previous token was hidden, set the hiddenBefore pointer of the
// copy to point back to it
if (prev != monitored) {
hiddenCopy.setHiddenBefore(prev);
}
// we don't want the non-hidden copy to link back to the hidden
// copy on the next pass through this function, so we leave
// lastHiddenToken alone
//System.err.println("hidden copy: " + hiddenCopy.toString());
return;
}
private void consumeFirst() throws TokenStreamException {
consume(); // get first token of input stream
// Handle situation where hidden or discarded tokens
// appear first in input stream
CommonHiddenStreamToken p=null;
// while hidden, copied, or discarded scarf tokens
while ( hideMask.member(LA(1).getType()) ||
discardMask.member(LA(1).getType()) ||
copyMask.member(LA(1).getType()) ) {
// if we've hit one of the tokens that needs to be copied, we copy it
// and then break out of the loop, because the parser needs to see it
// too
//
if (copyMask.member(LA(1).getType())) {
// copy the token in the lookahead
hiddenCopy = partialCloneToken(LA(1));
// if there's an existing token before this, link that and the
// copy together
if (p != null) {
p.setHiddenAfter(hiddenCopy);
hiddenCopy.setHiddenBefore(p); // double-link
}
lastHiddenToken = hiddenCopy;
if (firstHidden == null) {
firstHidden = hiddenCopy;
}
// we don't want to consume this token, because it also needs to
// be passed through to the parser, so break out of the while look
// entirely
//
break;
} else if (hideMask.member(LA(1).getType())) {
if (p != null) {
p.setHiddenAfter(LA(1));
LA(1).setHiddenBefore(p); // double-link
}
p = LA(1);
lastHiddenToken = p;
if (firstHidden == null) {
firstHidden = p; // record hidden token if first
}
}
consume();
}
}
/** Return the next monitored token.
* Test the token following the monitored token.
* If following is another monitored token, save it
* for the next invocation of nextToken (like a single
* lookahead token) and return it then.
* If following is unmonitored, nondiscarded (hidden)
* channel token, add it to the monitored token.
*
* Note: EOF must be a monitored Token.
*/
public Token nextToken() throws TokenStreamException {
// handle an initial condition; don't want to get lookahead
// token of this splitter until first call to nextToken
if (LA(1) == null) {
consumeFirst();
}
//System.err.println();
// we always consume hidden tokens after monitored, thus,
// upon entry LA(1) is a monitored token.
CommonHiddenStreamToken monitored = LA(1);
// point to hidden tokens found during last invocation
monitored.setHiddenBefore(lastHiddenToken);
lastHiddenToken = null;
// Look for hidden tokens, hook them into list emanating
// from the monitored tokens.
consume();
CommonHiddenStreamToken prev = monitored;
// deal with as many not-purely-monitored tokens as possible
while ( hideMask.member(LA(1).getType()) ||
discardMask.member(LA(1).getType()) ||
copyMask.member(LA(1).getType()) ) {
if (copyMask.member(LA(1).getType())) {
// copy the token and link it backwards
if (hiddenCopy != null) {
linkAndCopyToken(hiddenCopy, monitored);
} else {
linkAndCopyToken(prev, monitored);
}
// we now need to parse it as a monitored token, so we return, which
// avoids the consume() call at the end of this loop. the next call
// will parse it as a monitored token.
//System.err.println("returned: " + monitored.toString());
return monitored;
} else if (hideMask.member(LA(1).getType())) {
// attach the hidden token to the monitored in a chain
// link forwards
prev.setHiddenAfter(LA(1));
// link backwards
if (prev != monitored) { //hidden cannot point to monitored tokens
LA(1).setHiddenBefore(prev);
} else if (hiddenCopy != null) {
hiddenCopy.setHiddenAfter(LA(1));
LA(1).setHiddenBefore(hiddenCopy);
hiddenCopy = null;
}
//System.err.println("hidden: " + prev.getHiddenAfter().toString() + "\" after: " + prev.toString());
prev = lastHiddenToken = LA(1);
}
consume();
}
// remember the last hidden token for next time around
if (hiddenCopy != null) {
lastHiddenToken = hiddenCopy;
hiddenCopy = null;
}
//System.err.println("returned: " + monitored.toString());
return monitored;
}
}

11
app/preproc/clean.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
rm -f *Lexer.java
rm -f *Recognizer.java
rm -f *TokenTypes.java
rm -f *TokenTypes.txt
rm -f *TreeParser.java
rm -f *TreeParserTokenTypes.java
rm -f *TreeParserTokenTypes.txt
rm -f expanded*.g

1112
app/preproc/expandedpde.g Normal file

File diff suppressed because it is too large Load Diff

1276
app/preproc/java.g Normal file

File diff suppressed because it is too large Load Diff

326
app/preproc/java.tree.g Normal file
View File

@ -0,0 +1,326 @@
package antlr;
/** Java 1.3 AST Recognizer Grammar
*
* Author: (see java.g preamble)
*
* This grammar is in the PUBLIC DOMAIN
*/
class JavaTreeParser extends TreeParser;
options {
importVocab = Java;
}
compilationUnit
: (packageDefinition)?
(importDefinition)*
(typeDefinition)*
;
packageDefinition
: #( PACKAGE_DEF identifier )
;
importDefinition
: #( IMPORT identifierStar )
;
typeDefinition
: #(CLASS_DEF modifiers IDENT extendsClause implementsClause objBlock )
| #(INTERFACE_DEF modifiers IDENT extendsClause interfaceBlock )
;
typeSpec
: #(TYPE typeSpecArray)
;
typeSpecArray
: #( ARRAY_DECLARATOR typeSpecArray )
| type
;
type: identifier
| builtInType
;
builtInType
: "void"
| "boolean"
| "byte"
| "char"
| "short"
| "int"
| "float"
| "long"
| "double"
;
modifiers
: #( MODIFIERS (modifier)* )
;
modifier
: "private"
| "public"
| "protected"
| "static"
| "transient"
| "final"
| "abstract"
| "native"
| "threadsafe"
| "synchronized"
| "const"
| "volatile"
| "strictfp"
;
extendsClause
: #(EXTENDS_CLAUSE (identifier)* )
;
implementsClause
: #(IMPLEMENTS_CLAUSE (identifier)* )
;
interfaceBlock
: #( OBJBLOCK
( methodDecl
| variableDef
)*
)
;
objBlock
: #( OBJBLOCK
( ctorDef
| methodDef
| variableDef
| typeDefinition
| #(STATIC_INIT slist)
| #(INSTANCE_INIT slist)
)*
)
;
ctorDef
: #(CTOR_DEF modifiers methodHead (slist)?)
;
methodDecl
: #(METHOD_DEF modifiers typeSpec methodHead)
;
methodDef
: #(METHOD_DEF modifiers typeSpec methodHead (slist)?)
;
variableDef
: #(VARIABLE_DEF modifiers typeSpec variableDeclarator varInitializer)
;
parameterDef
: #(PARAMETER_DEF modifiers typeSpec IDENT )
;
objectinitializer
: #(INSTANCE_INIT slist)
;
variableDeclarator
: IDENT
| LBRACK variableDeclarator
;
varInitializer
: #(ASSIGN initializer)
|
;
initializer
: expression
| arrayInitializer
;
arrayInitializer
: #(ARRAY_INIT (initializer)*)
;
methodHead
: IDENT #( PARAMETERS (parameterDef)* ) (throwsClause)?
;
throwsClause
: #( "throws" (identifier)* )
;
identifier
: IDENT
| #( DOT identifier IDENT )
;
identifierStar
: IDENT
| #( DOT identifier (STAR|IDENT) )
;
slist
: #( SLIST (stat)* )
;
stat: typeDefinition
| variableDef
| expression
| #(LABELED_STAT IDENT stat)
| #("if" expression stat (stat)? )
| #( "for"
#(FOR_INIT (variableDef | elist)?)
#(FOR_CONDITION (expression)?)
#(FOR_ITERATOR (elist)?)
stat
)
| #("while" expression stat)
| #("do" stat expression)
| #("break" (IDENT)? )
| #("continue" (IDENT)? )
| #("return" (expression)? )
| #("switch" expression (caseGroup)*)
| #("throw" expression)
| #("synchronized" expression stat)
| tryBlock
| slist // nested SLIST
// uncomment to make assert JDK 1.4 stuff work
| #("assert" expression (expression)?)
| EMPTY_STAT
;
caseGroup
: #(CASE_GROUP (#("case" expression) | "default")+ slist)
;
tryBlock
: #( "try" slist (handler)* (#("finally" slist))? )
;
handler
: #( "catch" parameterDef slist )
;
elist
: #( ELIST (expression)* )
;
expression
: #(EXPR expr)
;
expr: #(QUESTION expr expr expr) // trinary operator
| #(ASSIGN expr expr) // binary operators...
| #(PLUS_ASSIGN expr expr)
| #(MINUS_ASSIGN expr expr)
| #(STAR_ASSIGN expr expr)
| #(DIV_ASSIGN expr expr)
| #(MOD_ASSIGN expr expr)
| #(SR_ASSIGN expr expr)
| #(BSR_ASSIGN expr expr)
| #(SL_ASSIGN expr expr)
| #(BAND_ASSIGN expr expr)
| #(BXOR_ASSIGN expr expr)
| #(BOR_ASSIGN expr expr)
| #(LOR expr expr)
| #(LAND expr expr)
| #(BOR expr expr)
| #(BXOR expr expr)
| #(BAND expr expr)
| #(NOT_EQUAL expr expr)
| #(EQUAL expr expr)
| #(LT expr expr)
| #(GT expr expr)
| #(LE expr expr)
| #(GE expr expr)
| #(SL expr expr)
| #(SR expr expr)
| #(BSR expr expr)
| #(PLUS expr expr)
| #(MINUS expr expr)
| #(DIV expr expr)
| #(MOD expr expr)
| #(STAR expr expr)
| #(INC expr)
| #(DEC expr)
| #(POST_INC expr)
| #(POST_DEC expr)
| #(BNOT expr)
| #(LNOT expr)
| #("instanceof" expr expr)
| #(UNARY_MINUS expr)
| #(UNARY_PLUS expr)
| primaryExpression
;
primaryExpression
: IDENT
| #( DOT
( expr
( IDENT
| arrayIndex
| "this"
| "class"
| #( "new" IDENT elist )
| "super"
)
| #(ARRAY_DECLARATOR typeSpecArray)
| builtInType ("class")?
)
)
| arrayIndex
| #(METHOD_CALL primaryExpression elist)
| ctorCall
| #(TYPECAST typeSpec expr)
| newExpression
| constant
| "super"
| "true"
| "false"
| "this"
| "null"
| typeSpec // type name used with instanceof
;
ctorCall
: #( CTOR_CALL elist )
| #( SUPER_CTOR_CALL
( elist
| primaryExpression elist
)
)
;
arrayIndex
: #(INDEX_OP expr expression)
;
constant
: NUM_INT
| CHAR_LITERAL
| STRING_LITERAL
| NUM_FLOAT
| NUM_DOUBLE
| NUM_LONG
;
newExpression
: #( "new" type
( newArrayDeclarator (arrayInitializer)?
| elist (objBlock)?
)
)
;
newArrayDeclarator
: #( ARRAY_DECLARATOR (newArrayDeclarator)? (expression)? )
;

3
app/preproc/make.sh Executable file
View File

@ -0,0 +1,3 @@
java -cp ../../build/macosx/work/lib/antlr.jar antlr.Tool java.g
# now build the pde stuff that extends the java classes
java -cp ../../build/macosx/work/lib/antlr.jar antlr.Tool -glib java.g pde.g

304
app/preproc/pde.g Normal file
View File

@ -0,0 +1,304 @@
/* -*- mode: antlr; c-basic-offset: 4; indent-tabs-mode: nil -*- */
header {
package processing.app.preproc;
import processing.app.*;
}
class PdeRecognizer extends JavaRecognizer;
options {
importVocab = Java;
exportVocab = PdePartial;
codeGenMakeSwitchThreshold=10; // this is set high for debugging
codeGenBitsetTestThreshold=10; // this is set high for debugging
// developers may to want to set this to true for better
// debugging messages, however, doing so disables highlighting errors
// in the editor.
defaultErrorHandler = false; //true;
}
tokens {
CONSTRUCTOR_CAST; EMPTY_FIELD;
}
pdeProgram
// only java mode programs will have their own public classes or
// imports (and they must have at least one)
: ( "public" "class" | "import" ) => javaProgram
{ PdePreprocessor.programType = PdePreprocessor.JAVA; }
// the syntactic predicate here looks for any minimal (thus
// the non-greedy qualifier) number of fields, followed by
// the tokens that represent the definition of loop() or
// some other member function. java mode programs may have such
// definitions, but they won't reach this point, having already been
// selected in the previous alternative. static mode programs
// don't have member functions.
//
| ( ( options {greedy=false;}: possiblyEmptyField)* "void" IDENT LPAREN )
=> activeProgram
{ PdePreprocessor.programType = PdePreprocessor.ACTIVE; }
| staticProgram
{ PdePreprocessor.programType = PdePreprocessor.STATIC; }
;
// advanced mode is really just a normal java file
javaProgram
: compilationUnit
;
activeProgram
: (possiblyEmptyField)+
;
staticProgram
: (statement)*
;
// copy of the java.g rule with WEBCOLOR_LITERAL added
constant
: NUM_INT
| CHAR_LITERAL
| STRING_LITERAL
| NUM_FLOAT
| NUM_LONG
| NUM_DOUBLE
| webcolor_literal
;
// of the form #cc008f in PDE
webcolor_literal
: w:WEBCOLOR_LITERAL
{ Preferences.getBoolean("preproc.web_colors") &&
w.getText().length() == 6 }? // must be exactly 6 hex digits
;
// copy of the java.g builtInType rule
builtInConsCastType
: "void"
| "boolean"
| "byte"
| "char"
| "short"
| "int"
| "float"
| "long"
| "double"
;
// our types include the java types and "color". this is separated into two
// rules so that constructor casts can just use the original typelist, since
// we don't want to support the color type as a constructor cast.
//
builtInType
: builtInConsCastType
| "color" // aliased to an int in PDE
{ Preferences.getBoolean("preproc.color_datatype") }?
;
// constructor style casts.
constructorCast!
: t:consCastTypeSpec[true]
LPAREN!
e:expression
RPAREN!
// if this is a string literal, make sure the type we're trying to cast
// to is one of the supported ones
//
{ #e.getType() != STRING_LITERAL ||
( #t.getType() == LITERAL_byte ||
#t.getType() == LITERAL_double ||
#t.getType() == LITERAL_float ||
#t.getType() == LITERAL_int ||
#t.getType() == LITERAL_long ||
#t.getType() == LITERAL_short ) }?
// create the node
//
{#constructorCast = #(#[CONSTRUCTOR_CAST,"CONSTRUCTOR_CAST"], t, e);}
;
// A list of types that be used as the destination type in a constructor-style
// cast. Ideally, this would include all class types, not just "String".
// Unfortunately, it's not possible to tell whether Foo(5) is supposed to be
// a method call or a constructor cast without have a table of all valid
// types or methods, which requires semantic analysis (eg processing of import
// statements). So we accept the set of built-in types plus "String".
//
consCastTypeSpec[boolean addImagNode]
// : stringTypeSpec[addImagNode]
// | builtInConsCastTypeSpec[addImagNode]
: builtInConsCastTypeSpec[addImagNode]
// trying to remove String() cast [fry]
;
//stringTypeSpec[boolean addImagNode]
// : id:IDENT { #id.getText().equals("String") }?
// {
// if ( addImagNode ) {
// #stringTypeSpec = #(#[TYPE,"TYPE"],
// #stringTypeSpec);
// }
// }
// ;
builtInConsCastTypeSpec[boolean addImagNode]
: builtInConsCastType
{
if ( addImagNode ) {
#builtInConsCastTypeSpec = #(#[TYPE,"TYPE"],
#builtInConsCastTypeSpec);
}
}
;
// Since "color" tokens are lexed as LITERAL_color now, we need to have a rule
// that can generate a method call from an expression that starts with this
// token
//
colorMethodCall
: c:"color" {#c.setType(IDENT);} // this would default to LITERAL_color
lp:LPAREN^ {#lp.setType(METHOD_CALL);}
argList
RPAREN!
;
// copy of the java.g rule with added constructorCast and colorMethodCall
// alternatives
primaryExpression
: (consCastTypeSpec[false] LPAREN) => constructorCast
{ Preferences.getBoolean("preproc.enhanced_casting") }?
| identPrimary ( options {greedy=true;} : DOT^ "class" )?
| constant
| "true"
| "false"
| "null"
| newExpression
| "this"
| "super"
| LPAREN! assignmentExpression RPAREN!
| colorMethodCall
// look for int.class and int[].class
| builtInType
( lbt:LBRACK^ {#lbt.setType(ARRAY_DECLARATOR);} RBRACK! )*
DOT^ "class"
;
// the below variable rule hacks are needed so that it's possible for the
// emitter to correctly output variable declarations of the form "float a, b"
// from the AST. This means that our AST has a somewhat different form in
// these rules than the java one does, and this new form may have its own
// semantic issues. But it seems to fix the comma declaration issues.
//
variableDefinitions![AST mods, AST t]
: vd:variableDeclarator[getASTFactory().dupTree(mods),
getASTFactory().dupTree(t)]
{#variableDefinitions = #(#[VARIABLE_DEF,"VARIABLE_DEF"], mods,
t, vd);}
;
variableDeclarator[AST mods, AST t]
: ( id:IDENT (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
v:varInitializer (COMMA!)? )+
;
// java.g builds syntax trees with an inconsistent structure. override one of
// the rules there to fix this.
//
explicitConstructorInvocation!
: t:"this" LPAREN a1:argList RPAREN SEMI
{#explicitConstructorInvocation = #(#[CTOR_CALL, "CTOR_CALL"],
#t, #a1);}
| s:"super" LPAREN a2:argList RPAREN SEMI
{#explicitConstructorInvocation = #(#[SUPER_CTOR_CALL,
"SUPER_CTOR_CALL"],
#s, #a2);}
;
// quick-n-dirty hack to the get the advanced class name. we should
// really be getting it from the AST and not forking this rule from
// the java.g copy at all. Since this is a recursive descent parser, we get
// the last class name in the file so that we don't end up with the classname
// of an inner class. If there is more than one "outer" class in a file,
// this heuristic will fail.
//
classDefinition![AST modifiers]
: "class" i:IDENT
// it _might_ have a superclass...
sc:superClassClause
// it might implement some interfaces...
ic:implementsClause
// now parse the body of the class
cb:classBlock
{#classDefinition = #(#[CLASS_DEF,"CLASS_DEF"],
modifiers,i,sc,ic,cb);
PdePreprocessor.advClassName = i.getText();}
;
possiblyEmptyField
: field
| s:SEMI {#s.setType(EMPTY_FIELD);}
;
class PdeLexer extends JavaLexer;
options {
importVocab=PdePartial;
exportVocab=Pde;
}
// We need to preserve whitespace and commentary instead of ignoring
// like the supergrammar does. Otherwise Jikes won't be able to give
// us error messages that point to the equivalent PDE code.
// WS, SL_COMMENT, ML_COMMENT are copies of the original productions,
// but with the SKIP assigment removed.
WS : ( ' '
| '\t'
| '\f'
// handle newlines
| ( options {generateAmbigWarnings=false;}
: "\r\n" // Evil DOS
| '\r' // Macintosh
| '\n' // Unix (the right way)
)
{ newline(); }
)+
;
// Single-line comments
SL_COMMENT
: "//"
(~('\n'|'\r'))* ('\n'|'\r'('\n')?)
{newline();}
;
// multiple-line comments
ML_COMMENT
: "/*"
( /* '\r' '\n' can be matched in one alternative or by matching
'\r' in one iteration and '\n' in another. I am trying to
handle any flavor of newline that comes in, but the language
that allows both "\r\n" and "\r" and "\n" to all be valid
newline is ambiguous. Consequently, the resulting grammar
must be ambiguous. I'm shutting this warning off.
*/
options {
generateAmbigWarnings=false;
}
:
{ LA(2)!='/' }? '*'
| '\r' '\n' {newline();}
| '\r' {newline();}
| '\n' {newline();}
| ~('*'|'\n'|'\r')
)*
"*/"
;
WEBCOLOR_LITERAL
: '#'! (HEX_DIGIT)+
;

150
app/preproc/whitespace_test.pde Executable file
View File

@ -0,0 +1,150 @@
// this is a whitespace and other invisible token torture test for the ANTLR-based
// preprocessor. edit pde.properties and set "editor.save_build_files" to true.
// then, build this in processing. next, use
//
// diff -u --strip-trailing-cr \
// work/sketchbook/default/whitespace_test/whitespace_test.pde \
// work/lib/build/MyDemo.java
//
// to compare the files before and after preprocessing. There should not be
// any differences.
import // comment test
java.io.*;
// comment 1
public class // post-class comment
// comment2
MyDemo extends BApplet implements // foo
java.lang. // bar
Cloneable {
//argh
public // foo
String // bar
fff = /*rheet */ "stuff";
static /*a*/ {
/*foo*/
/*bar*/
six = 6;
} /* b*/
static /*a*/ final /*b*/ int six;
void setup()
{
size(200, 200);
background(255);
this . fff = /* ook */ (String)/*foo*/"yo";
rectMode(CENTER_DIAMETER); // comment 1a
noStroke();
fill(255, 204, 0);
int q = /*a*/ - /*b*/ 1;
boolean c = /*a*/ ! /*b*/ true;
}
int foo() /*a*/ throws /*b*/ java.lang.Exception /*c*/
{
int b = 7;
switch /*a*/ ( /*b*/ b /*c*/ ) {
case /*d*/ 1 /*e*/: /*f*/
int c=9;
/*g*/
break; /*h*/
default /*i*/ :
int d=9;
break;
}
try { /* qq */
loop(); /* rr */
} catch /*ss*/ (java.lang.Exception ex) /*tt*/ {
b = 8; /*tut*/
throw /*utu*/ ex;
} /*uu*/ finally /*vv*/ {
b = 9;
} /*ww*/
b /*aaa*/ = /*bbb*/ true /*ccc*/ ? /*ddd*/ 0 /*eee*/
: /* fff*/ 1 /*ggg*/;
return /*a*/ 5 /*b*/;
}
// comment 2
void loop()
{
int arr1 /* VVV */ [ /* XXX */] /*YYY*/ ;
int[] arr2 = { /*a*/ 2, 3 /*b*/ } /*c*/ ;
for /*a*/ (/*b*/ int j=0 /*c*/; /*d*/ j<2/*e*/ ; /*f*/ j++ /*g*/)
/*h*/
arr2[1] = 6;
/*foo*/
;
/*bar*/
rect(width-mouseX, height-mouseY, 50, 50);
rect(mouseX, mouseY, 50, 50);
if (/*a*/ arr2[1] == 6/*b*/) {
/*c*/
int d=7;
} /*d*/else /*e*/{
int e=8;
/*f*/
}
int f;
if (/*aa*/ arr2[1] ==6 /*bb*/ )
/*cc*/
f=8; /*dd*/
else /*ee*/
f=10; /*ff*/
while ( /*aaa*/ f < 15) /*bbb*/ {
f ++;
} /*ggg*/
do /* aaaa */ {
f++;
} /*bbbb*/ while /*cccc*/ ( /*a*/ - /*b*/ 20 > f) /*dddd*/;
f = 2 * 3 + 4;
f = ( 2 * 3 ) + 4 + /*aa*/ -/*bb*/1;
f = 2 * ( 3 + 4 ) ;
fff = /*a*/ new /*b*/ String(/*c*/"foo"/*d*/) /*e*/;
int arr3[] = /*a*/ new /*b*/ int/*c*/[/*d*/] /*e*/ {1/*f*/,2};
int arr4[][] = new /*a*/int/*b*/[1][2]/*c*/;
}
class Shoe
{
Shoe(String brand)
{
println(brand);
}
}
class NikeAir extends Shoe
{
NikeAir()
{
/*a*/ super /*b*/ ( /*c*/ "Nike" /*d*/ ) /*e*/ ;
/*aa*/ ( /*bb*/ new /*cc*/ MyDemo /*dd*/ (/*ee*/)/*ff*/)/*gg*/./*hh*/super/*ii*/(/*jj*/5/*kk*/);
}
}
}

View File

@ -0,0 +1,274 @@
/*
* CTokenMarker.java - C token marker
* Copyright (C) 1998, 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.text.Segment;
/**
* C token marker.
*
* @author Slava Pestov
* @version $Id: CTokenMarker.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
*/
public class CTokenMarker extends TokenMarker
{
public CTokenMarker()
{
this(true,getKeywords());
}
public CTokenMarker(boolean cpp, KeywordMap keywords)
{
this.cpp = cpp;
this.keywords = keywords;
}
public byte markTokensImpl(byte token, Segment line, int lineIndex)
{
char[] array = line.array;
int offset = line.offset;
lastOffset = offset;
lastKeyword = offset;
int mlength = line.count + offset;
boolean backslash = false;
loop: for(int i = offset; i < mlength; i++)
{
int i1 = (i+1);
char c = array[i];
if(c == '\\')
{
backslash = !backslash;
continue;
}
switch(token)
{
case Token.NULL:
switch(c)
{
case '#':
if(backslash)
backslash = false;
else if(cpp)
{
if(doKeyword(line,i,c))
break;
addToken(i - lastOffset,token);
addToken(mlength - i,Token.KEYWORD2);
lastOffset = lastKeyword = mlength;
break loop;
}
break;
case '"':
doKeyword(line,i,c);
if(backslash)
backslash = false;
else
{
addToken(i - lastOffset,token);
token = Token.LITERAL1;
lastOffset = lastKeyword = i;
}
break;
case '\'':
doKeyword(line,i,c);
if(backslash)
backslash = false;
else
{
addToken(i - lastOffset,token);
token = Token.LITERAL2;
lastOffset = lastKeyword = i;
}
break;
case ':':
if(lastKeyword == offset)
{
if(doKeyword(line,i,c))
break;
backslash = false;
addToken(i1 - lastOffset,Token.LABEL);
lastOffset = lastKeyword = i1;
}
else if(doKeyword(line,i,c))
break;
break;
case '/':
backslash = false;
doKeyword(line,i,c);
if(mlength - i > 1)
{
switch(array[i1])
{
case '*':
addToken(i - lastOffset,token);
lastOffset = lastKeyword = i;
if(mlength - i > 2 && array[i+2] == '*')
token = Token.COMMENT2;
else
token = Token.COMMENT1;
break;
case '/':
addToken(i - lastOffset,token);
addToken(mlength - i,Token.COMMENT1);
lastOffset = lastKeyword = mlength;
break loop;
}
}
break;
default:
backslash = false;
if(!Character.isLetterOrDigit(c)
&& c != '_')
doKeyword(line,i,c);
break;
}
break;
case Token.COMMENT1:
case Token.COMMENT2:
backslash = false;
if(c == '*' && mlength - i > 1)
{
if(array[i1] == '/')
{
i++;
addToken((i+1) - lastOffset,token);
token = Token.NULL;
lastOffset = lastKeyword = i+1;
}
}
break;
case Token.LITERAL1:
if(backslash)
backslash = false;
else if(c == '"')
{
addToken(i1 - lastOffset,token);
token = Token.NULL;
lastOffset = lastKeyword = i1;
}
break;
case Token.LITERAL2:
if(backslash)
backslash = false;
else if(c == '\'')
{
addToken(i1 - lastOffset,Token.LITERAL1);
token = Token.NULL;
lastOffset = lastKeyword = i1;
}
break;
default:
throw new InternalError("Invalid state: "
+ token);
}
}
if(token == Token.NULL)
doKeyword(line,mlength,'\0');
switch(token)
{
case Token.LITERAL1:
case Token.LITERAL2:
addToken(mlength - lastOffset,Token.INVALID);
token = Token.NULL;
break;
case Token.KEYWORD2:
addToken(mlength - lastOffset,token);
if (!backslash) token = Token.NULL;
addToken(mlength - lastOffset,token);
break;
default:
addToken(mlength - lastOffset,token);
break;
}
return token;
}
public static KeywordMap getKeywords()
{
if(cKeywords == null)
{
cKeywords = new KeywordMap(false);
cKeywords.add("char",Token.KEYWORD3);
cKeywords.add("double",Token.KEYWORD3);
cKeywords.add("enum",Token.KEYWORD3);
cKeywords.add("float",Token.KEYWORD3);
cKeywords.add("int",Token.KEYWORD3);
cKeywords.add("long",Token.KEYWORD3);
cKeywords.add("short",Token.KEYWORD3);
cKeywords.add("signed",Token.KEYWORD3);
cKeywords.add("struct",Token.KEYWORD3);
cKeywords.add("typedef",Token.KEYWORD3);
cKeywords.add("union",Token.KEYWORD3);
cKeywords.add("unsigned",Token.KEYWORD3);
cKeywords.add("void",Token.KEYWORD3);
cKeywords.add("auto",Token.KEYWORD1);
cKeywords.add("const",Token.KEYWORD1);
cKeywords.add("extern",Token.KEYWORD1);
cKeywords.add("register",Token.KEYWORD1);
cKeywords.add("static",Token.KEYWORD1);
cKeywords.add("volatile",Token.KEYWORD1);
cKeywords.add("break",Token.KEYWORD1);
cKeywords.add("case",Token.KEYWORD1);
cKeywords.add("continue",Token.KEYWORD1);
cKeywords.add("default",Token.KEYWORD1);
cKeywords.add("do",Token.KEYWORD1);
cKeywords.add("else",Token.KEYWORD1);
cKeywords.add("for",Token.KEYWORD1);
cKeywords.add("goto",Token.KEYWORD1);
cKeywords.add("if",Token.KEYWORD1);
cKeywords.add("return",Token.KEYWORD1);
cKeywords.add("sizeof",Token.KEYWORD1);
cKeywords.add("switch",Token.KEYWORD1);
cKeywords.add("while",Token.KEYWORD1);
cKeywords.add("asm",Token.KEYWORD2);
cKeywords.add("asmlinkage",Token.KEYWORD2);
cKeywords.add("far",Token.KEYWORD2);
cKeywords.add("huge",Token.KEYWORD2);
cKeywords.add("inline",Token.KEYWORD2);
cKeywords.add("near",Token.KEYWORD2);
cKeywords.add("pascal",Token.KEYWORD2);
cKeywords.add("true",Token.LITERAL2);
cKeywords.add("false",Token.LITERAL2);
cKeywords.add("NULL",Token.LITERAL2);
}
return cKeywords;
}
// private members
private static KeywordMap cKeywords;
private boolean cpp;
private KeywordMap keywords;
private int lastOffset;
private int lastKeyword;
private boolean doKeyword(Segment line, int i, char c)
{
int i1 = i+1;
int len = i - lastKeyword;
byte id = keywords.lookup(line,lastKeyword,len);
if(id != Token.NULL)
{
if(lastKeyword != lastOffset)
addToken(lastKeyword - lastOffset,Token.NULL);
addToken(len,id);
lastOffset = i;
}
lastKeyword = i1;
return false;
}
}

View File

@ -0,0 +1,374 @@
/*
* DefaultInputHandler.java - Default implementation of an input handler
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.KeyStroke;
import java.awt.event.*;
import java.awt.Toolkit;
import java.util.Hashtable;
import java.util.StringTokenizer;
/**
* The default input handler. It maps sequences of keystrokes into actions
* and inserts key typed events into the text area.
* @author Slava Pestov
* @version $Id: DefaultInputHandler.java,v 1.2 2005/05/11 08:34:16 benfry Exp $
*/
public class DefaultInputHandler extends InputHandler
{
/**
* Creates a new input handler with no key bindings defined.
*/
public DefaultInputHandler()
{
bindings = currentBindings = new Hashtable();
}
/**
* Sets up the default key bindings.
*/
public void addDefaultKeyBindings()
{
addKeyBinding("BACK_SPACE",BACKSPACE);
addKeyBinding("C+BACK_SPACE",BACKSPACE_WORD);
addKeyBinding("DELETE",DELETE);
addKeyBinding("C+DELETE",DELETE_WORD);
addKeyBinding("ENTER",INSERT_BREAK);
addKeyBinding("TAB",INSERT_TAB);
addKeyBinding("INSERT",OVERWRITE);
addKeyBinding("C+\\",TOGGLE_RECT);
addKeyBinding("HOME",HOME);
addKeyBinding("END",END);
addKeyBinding("S+HOME",SELECT_HOME);
addKeyBinding("S+END",SELECT_END);
addKeyBinding("C+HOME",DOCUMENT_HOME);
addKeyBinding("C+END",DOCUMENT_END);
addKeyBinding("CS+HOME",SELECT_DOC_HOME);
addKeyBinding("CS+END",SELECT_DOC_END);
addKeyBinding("PAGE_UP",PREV_PAGE);
addKeyBinding("PAGE_DOWN",NEXT_PAGE);
addKeyBinding("S+PAGE_UP",SELECT_PREV_PAGE);
addKeyBinding("S+PAGE_DOWN",SELECT_NEXT_PAGE);
addKeyBinding("LEFT",PREV_CHAR);
addKeyBinding("S+LEFT",SELECT_PREV_CHAR);
addKeyBinding("C+LEFT",PREV_WORD);
addKeyBinding("CS+LEFT",SELECT_PREV_WORD);
addKeyBinding("RIGHT",NEXT_CHAR);
addKeyBinding("S+RIGHT",SELECT_NEXT_CHAR);
addKeyBinding("C+RIGHT",NEXT_WORD);
addKeyBinding("CS+RIGHT",SELECT_NEXT_WORD);
addKeyBinding("UP",PREV_LINE);
addKeyBinding("S+UP",SELECT_PREV_LINE);
addKeyBinding("DOWN",NEXT_LINE);
addKeyBinding("S+DOWN",SELECT_NEXT_LINE);
addKeyBinding("C+ENTER",REPEAT);
}
/**
* Adds a key binding to this input handler. The key binding is
* a list of white space separated key strokes of the form
* <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
* or S for Shift, and key is either a character (a-z) or a field
* name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
* @param keyBinding The key binding
* @param action The action
*/
public void addKeyBinding(String keyBinding, ActionListener action)
{
Hashtable current = bindings;
StringTokenizer st = new StringTokenizer(keyBinding);
while(st.hasMoreTokens())
{
KeyStroke keyStroke = parseKeyStroke(st.nextToken());
if(keyStroke == null)
return;
if(st.hasMoreTokens())
{
Object o = current.get(keyStroke);
if(o instanceof Hashtable)
current = (Hashtable)o;
else
{
o = new Hashtable();
current.put(keyStroke,o);
current = (Hashtable)o;
}
}
else
current.put(keyStroke,action);
}
}
/**
* Removes a key binding from this input handler. This is not yet
* implemented.
* @param keyBinding The key binding
*/
public void removeKeyBinding(String keyBinding)
{
throw new InternalError("Not yet implemented");
}
/**
* Removes all key bindings from this input handler.
*/
public void removeAllKeyBindings()
{
bindings.clear();
}
/**
* Returns a copy of this input handler that shares the same
* key bindings. Setting key bindings in the copy will also
* set them in the original.
*/
public InputHandler copy()
{
return new DefaultInputHandler(this);
}
/**
* Handle a key pressed event. This will look up the binding for
* the key stroke and execute it.
*/
public void keyPressed(KeyEvent evt)
{
int keyCode = evt.getKeyCode();
int modifiers = evt.getModifiers();
// moved this earlier so it doesn't get random meta clicks
if (keyCode == KeyEvent.VK_CONTROL ||
keyCode == KeyEvent.VK_SHIFT ||
keyCode == KeyEvent.VK_ALT ||
keyCode == KeyEvent.VK_META) {
return;
}
// don't get command-s or other menu key equivs on mac
// unless it's something that's specifically bound (cmd-left or right)
//if ((modifiers & KeyEvent.META_MASK) != 0) return;
if ((modifiers & KeyEvent.META_MASK) != 0) {
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers);
if (currentBindings.get(keyStroke) == null) {
return;
}
}
/*
char keyChar = evt.getKeyChar();
System.out.println("code=" + keyCode + " char=" + keyChar +
" charint=" + ((int)keyChar));
System.out.println("other codes " + KeyEvent.VK_ALT + " " +
KeyEvent.VK_META);
*/
if((modifiers & ~KeyEvent.SHIFT_MASK) != 0
|| evt.isActionKey()
|| keyCode == KeyEvent.VK_BACK_SPACE
|| keyCode == KeyEvent.VK_DELETE
|| keyCode == KeyEvent.VK_ENTER
|| keyCode == KeyEvent.VK_TAB
|| keyCode == KeyEvent.VK_ESCAPE)
{
if(grabAction != null)
{
handleGrabAction(evt);
return;
}
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
modifiers);
Object o = currentBindings.get(keyStroke);
if(o == null)
{
// Don't beep if the user presses some
// key we don't know about unless a
// prefix is active. Otherwise it will
// beep when caps lock is pressed, etc.
if(currentBindings != bindings)
{
Toolkit.getDefaultToolkit().beep();
// F10 should be passed on, but C+e F10
// shouldn't
repeatCount = 0;
repeat = false;
evt.consume();
}
currentBindings = bindings;
return;
}
else if(o instanceof ActionListener)
{
currentBindings = bindings;
executeAction(((ActionListener)o),
evt.getSource(),null);
evt.consume();
return;
}
else if(o instanceof Hashtable)
{
currentBindings = (Hashtable)o;
evt.consume();
return;
}
}
}
/**
* Handle a key typed event. This inserts the key into the text area.
*/
public void keyTyped(KeyEvent evt)
{
int modifiers = evt.getModifiers();
char c = evt.getKeyChar();
// this is the apple/cmd key on macosx.. so menu commands
// were being passed through as legit keys.. added this line
// in an attempt to prevent.
if ((modifiers & KeyEvent.META_MASK) != 0) return;
if (c != KeyEvent.CHAR_UNDEFINED) // &&
// (modifiers & KeyEvent.ALT_MASK) == 0)
{
if(c >= 0x20 && c != 0x7f)
{
KeyStroke keyStroke = KeyStroke.getKeyStroke(
Character.toUpperCase(c));
Object o = currentBindings.get(keyStroke);
if(o instanceof Hashtable)
{
currentBindings = (Hashtable)o;
return;
}
else if(o instanceof ActionListener)
{
currentBindings = bindings;
executeAction((ActionListener)o,
evt.getSource(),
String.valueOf(c));
return;
}
currentBindings = bindings;
if(grabAction != null)
{
handleGrabAction(evt);
return;
}
// 0-9 adds another 'digit' to the repeat number
if(repeat && Character.isDigit(c))
{
repeatCount *= 10;
repeatCount += (c - '0');
return;
}
executeAction(INSERT_CHAR,evt.getSource(),
String.valueOf(evt.getKeyChar()));
repeatCount = 0;
repeat = false;
}
}
}
/**
* Converts a string to a keystroke. The string should be of the
* form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
* is any combination of A for Alt, C for Control, S for Shift
* or M for Meta, and <i>shortcut</i> is either a single character,
* or a keycode name from the <code>KeyEvent</code> class, without
* the <code>VK_</code> prefix.
* @param keyStroke A string description of the key stroke
*/
public static KeyStroke parseKeyStroke(String keyStroke)
{
if(keyStroke == null)
return null;
int modifiers = 0;
int index = keyStroke.indexOf('+');
if(index != -1)
{
for(int i = 0; i < index; i++)
{
switch(Character.toUpperCase(keyStroke
.charAt(i)))
{
case 'A':
modifiers |= InputEvent.ALT_MASK;
break;
case 'C':
modifiers |= InputEvent.CTRL_MASK;
break;
case 'M':
modifiers |= InputEvent.META_MASK;
break;
case 'S':
modifiers |= InputEvent.SHIFT_MASK;
break;
}
}
}
String key = keyStroke.substring(index + 1);
if(key.length() == 1)
{
char ch = Character.toUpperCase(key.charAt(0));
if(modifiers == 0)
return KeyStroke.getKeyStroke(ch);
else
return KeyStroke.getKeyStroke(ch,modifiers);
}
else if(key.length() == 0)
{
System.err.println("Invalid key stroke: " + keyStroke);
return null;
}
else
{
int ch;
try
{
ch = KeyEvent.class.getField("VK_".concat(key))
.getInt(null);
}
catch(Exception e)
{
System.err.println("Invalid key stroke: "
+ keyStroke);
return null;
}
return KeyStroke.getKeyStroke(ch,modifiers);
}
}
// private members
private Hashtable bindings;
private Hashtable currentBindings;
private DefaultInputHandler(DefaultInputHandler copy)
{
bindings = currentBindings = copy.bindings;
}
}

1071
app/syntax/InputHandler.java Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

140
app/syntax/KeywordMap.java Normal file
View File

@ -0,0 +1,140 @@
/*
* KeywordMap.java - Fast keyword->id map
* Copyright (C) 1998, 1999 Slava Pestov
* Copyright (C) 1999 Mike Dillon
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.text.Segment;
/**
* A <code>KeywordMap</code> is similar to a hashtable in that it maps keys
* to values. However, the `keys' are Swing segments. This allows lookups of
* text substrings without the overhead of creating a new string object.
* <p>
* This class is used by <code>CTokenMarker</code> to map keywords to ids.
*
* @author Slava Pestov, Mike Dillon
* @version $Id: KeywordMap.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
*/
public class KeywordMap
{
/**
* Creates a new <code>KeywordMap</code>.
* @param ignoreCase True if keys are case insensitive
*/
public KeywordMap(boolean ignoreCase)
{
this(ignoreCase, 52);
this.ignoreCase = ignoreCase;
}
/**
* Creates a new <code>KeywordMap</code>.
* @param ignoreCase True if the keys are case insensitive
* @param mapLength The number of `buckets' to create.
* A value of 52 will give good performance for most maps.
*/
public KeywordMap(boolean ignoreCase, int mapLength)
{
this.mapLength = mapLength;
this.ignoreCase = ignoreCase;
map = new Keyword[mapLength];
}
/**
* Looks up a key.
* @param text The text segment
* @param offset The offset of the substring within the text segment
* @param length The length of the substring
*/
public byte lookup(Segment text, int offset, int length)
{
if(length == 0)
return Token.NULL;
Keyword k = map[getSegmentMapKey(text, offset, length)];
while(k != null)
{
if(length != k.keyword.length)
{
k = k.next;
continue;
}
if(SyntaxUtilities.regionMatches(ignoreCase,text,offset,
k.keyword))
return k.id;
k = k.next;
}
return Token.NULL;
}
/**
* Adds a key-value mapping.
* @param keyword The key
* @Param id The value
*/
public void add(String keyword, byte id)
{
int key = getStringMapKey(keyword);
map[key] = new Keyword(keyword.toCharArray(),id,map[key]);
}
/**
* Returns true if the keyword map is set to be case insensitive,
* false otherwise.
*/
public boolean getIgnoreCase()
{
return ignoreCase;
}
/**
* Sets if the keyword map should be case insensitive.
* @param ignoreCase True if the keyword map should be case
* insensitive, false otherwise
*/
public void setIgnoreCase(boolean ignoreCase)
{
this.ignoreCase = ignoreCase;
}
// protected members
protected int mapLength;
protected int getStringMapKey(String s)
{
return (Character.toUpperCase(s.charAt(0)) +
Character.toUpperCase(s.charAt(s.length()-1)))
% mapLength;
}
protected int getSegmentMapKey(Segment s, int off, int len)
{
return (Character.toUpperCase(s.array[off]) +
Character.toUpperCase(s.array[off + len - 1]))
% mapLength;
}
// private members
class Keyword
{
public Keyword(char[] keyword, byte id, Keyword next)
{
this.keyword = keyword;
this.id = id;
this.next = next;
}
public char[] keyword;
public byte id;
public Keyword next;
}
private Keyword[] map;
private boolean ignoreCase;
}

122
app/syntax/PdeKeywords.java Normal file
View File

@ -0,0 +1,122 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
PdeKeywords - handles text coloring and links to html reference
Part of the Processing project - http://processing.org
Copyright (c) 2004-05 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.syntax;
import processing.app.*;
import java.io.*;
import java.util.*;
public class PdeKeywords extends CTokenMarker {
// lookup table for the TokenMarker subclass, handles coloring
static KeywordMap keywordColoring;
// lookup table that maps keywords to their html reference pages
static Hashtable keywordToReference;
public PdeKeywords() {
super(false, getKeywords());
}
/**
* Handles loading of keywords file.
* <P>
* Uses getKeywords() method because that's part of the
* TokenMarker classes.
* <P>
* It is recommended that a # sign be used for comments
* inside keywords.txt.
*/
static public KeywordMap getKeywords() {
if (keywordColoring == null) {
try {
keywordColoring = new KeywordMap(false);
keywordToReference = new Hashtable();
InputStream input = Base.getStream("keywords.txt");
InputStreamReader isr = new InputStreamReader(input);
BufferedReader reader = new BufferedReader(isr);
String line = null;
while ((line = reader.readLine()) != null) {
//System.out.println("line is " + line);
// in case there's any garbage on the line
//if (line.trim().length() == 0) continue;
String pieces[] = Base.split(line, '\t');
if (pieces.length >= 2) {
//int tab = line.indexOf('\t');
// any line with no tab is ignored
// meaning that a comment is any line without a tab
//if (tab == -1) continue;
String keyword = pieces[0].trim();
//String keyword = line.substring(0, tab).trim();
//String second = line.substring(tab + 1);
//tab = second.indexOf('\t');
//String coloring = second.substring(0, tab).trim();
//String htmlFilename = second.substring(tab + 1).trim();
String coloring = pieces[1].trim();
if (coloring.length() > 0) {
// text will be KEYWORD or LITERAL
boolean isKey = (coloring.charAt(0) == 'K');
// KEYWORD1 -> 0, KEYWORD2 -> 1, etc
int num = coloring.charAt(coloring.length() - 1) - '1';
byte id = (byte)
((isKey ? Token.KEYWORD1 : Token.LITERAL1) + num);
//System.out.println("got " + (isKey ? "keyword" : "literal") +
// (num+1) + " for " + keyword);
keywordColoring.add(keyword, id);
}
if (pieces.length >= 3) {
String htmlFilename = pieces[2].trim();
if (htmlFilename.length() > 0) {
keywordToReference.put(keyword, htmlFilename);
}
}
}
}
reader.close();
} catch (Exception e) {
Base.showError("Problem loading keywords",
"Could not load keywords.txt,\n" +
"please re-install Arduino.", e);
System.exit(1);
}
}
return keywordColoring;
}
static public String getReference(String keyword) {
return (String) keywordToReference.get(keyword);
}
}

View File

@ -0,0 +1,162 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
PdeTextAreaDefaults - grabs font/color settings for the editor
Part of the Processing project - http://Proce55ing.net
Except where noted, code is written by Ben Fry
Copyright (c) 2001-03 Massachusetts Institute of Technology
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.syntax;
import processing.app.*;
public class PdeTextAreaDefaults extends TextAreaDefaults {
public PdeTextAreaDefaults() {
inputHandler = new DefaultInputHandler();
inputHandler.addDefaultKeyBindings();
// use option on mac for things that are ctrl on windows/linux
String mod = Base.isMacOS() ? "A" : "C";
inputHandler.addKeyBinding("S+BACK_SPACE", InputHandler.BACKSPACE);
inputHandler.addKeyBinding("S+DELETE", InputHandler.DELETE);
inputHandler.addKeyBinding("BACK_SPACE", InputHandler.BACKSPACE);
inputHandler.addKeyBinding("C+BACK_SPACE", InputHandler.BACKSPACE_WORD);
inputHandler.addKeyBinding("DELETE", InputHandler.DELETE);
inputHandler.addKeyBinding("C+DELETE", InputHandler.DELETE_WORD);
inputHandler.addKeyBinding("ENTER", InputHandler.INSERT_BREAK);
inputHandler.addKeyBinding("TAB", InputHandler.INSERT_TAB);
inputHandler.addKeyBinding("INSERT", InputHandler.OVERWRITE);
inputHandler.addKeyBinding("C+\\", InputHandler.TOGGLE_RECT);
// beginning and ending of the current line
inputHandler.addKeyBinding("HOME", InputHandler.HOME);
inputHandler.addKeyBinding("END", InputHandler.END);
if (Base.isMacOS()) {
inputHandler.addKeyBinding("M+LEFT", InputHandler.HOME);
inputHandler.addKeyBinding("M+RIGHT", InputHandler.END);
}
inputHandler.addKeyBinding("S+HOME", InputHandler.SELECT_HOME);
inputHandler.addKeyBinding("S+END", InputHandler.SELECT_END);
inputHandler.addKeyBinding(mod + "+HOME", InputHandler.DOCUMENT_HOME);
inputHandler.addKeyBinding(mod + "+END", InputHandler.DOCUMENT_END);
inputHandler.addKeyBinding(mod + "S+HOME", InputHandler.SELECT_DOC_HOME);
inputHandler.addKeyBinding(mod + "S+END", InputHandler.SELECT_DOC_END);
inputHandler.addKeyBinding("PAGE_UP", InputHandler.PREV_PAGE);
inputHandler.addKeyBinding("PAGE_DOWN", InputHandler.NEXT_PAGE);
inputHandler.addKeyBinding("S+PAGE_UP", InputHandler.SELECT_PREV_PAGE);
inputHandler.addKeyBinding("S+PAGE_DOWN", InputHandler.SELECT_NEXT_PAGE);
inputHandler.addKeyBinding("LEFT", InputHandler.PREV_CHAR);
inputHandler.addKeyBinding("S+LEFT", InputHandler.SELECT_PREV_CHAR);
inputHandler.addKeyBinding(mod + "+LEFT", InputHandler.PREV_WORD);
inputHandler.addKeyBinding(mod + "S+LEFT", InputHandler.SELECT_PREV_WORD);
inputHandler.addKeyBinding("RIGHT", InputHandler.NEXT_CHAR);
inputHandler.addKeyBinding("S+RIGHT", InputHandler.SELECT_NEXT_CHAR);
inputHandler.addKeyBinding(mod + "+RIGHT", InputHandler.NEXT_WORD);
inputHandler.addKeyBinding(mod + "S+RIGHT", InputHandler.SELECT_NEXT_WORD);
inputHandler.addKeyBinding("UP", InputHandler.PREV_LINE);
inputHandler.addKeyBinding(mod + "+UP", InputHandler.PREV_LINE); // p5
inputHandler.addKeyBinding("S+UP", InputHandler.SELECT_PREV_LINE);
inputHandler.addKeyBinding("DOWN", InputHandler.NEXT_LINE);
inputHandler.addKeyBinding(mod + "+DOWN", InputHandler.NEXT_LINE); // p5
inputHandler.addKeyBinding("S+DOWN", InputHandler.SELECT_NEXT_LINE);
inputHandler.addKeyBinding(mod + "+ENTER", InputHandler.REPEAT);
document = new SyntaxDocument();
editable = true;
electricScroll = 3;
cols = 80;
rows = 15;
// moved from SyntaxUtilities
//DEFAULTS.styles = SyntaxUtilities.getDefaultSyntaxStyles();
styles = new SyntaxStyle[Token.ID_COUNT];
// comments
styles[Token.COMMENT1] = Preferences.getStyle("comment1");
styles[Token.COMMENT2] = Preferences.getStyle("comment2");
// abstract, final, private
styles[Token.KEYWORD1] = Preferences.getStyle("keyword1");
// beginShape, point, line
styles[Token.KEYWORD2] = Preferences.getStyle("keyword2");
// byte, char, short, color
styles[Token.KEYWORD3] = Preferences.getStyle("keyword3");
// constants: null, true, this, RGB, TWO_PI
styles[Token.LITERAL1] = Preferences.getStyle("literal1");
// p5 built in variables: mouseX, width, pixels
styles[Token.LITERAL2] = Preferences.getStyle("literal2");
// ??
styles[Token.LABEL] = Preferences.getStyle("label");
// + - = /
styles[Token.OPERATOR] = Preferences.getStyle("operator");
// area that's not in use by the text (replaced with tildes)
styles[Token.INVALID] = Preferences.getStyle("invalid");
// moved from TextAreaPainter
font = Preferences.getFont("editor.font");
fgcolor = Preferences.getColor("editor.fgcolor");
bgcolor = Preferences.getColor("editor.bgcolor");
caretVisible = true;
caretBlinks = Preferences.getBoolean("editor.caret.blink");
caretColor = Preferences.getColor("editor.caret.color");
selectionColor = Preferences.getColor("editor.selection.color");
lineHighlight =
Preferences.getBoolean("editor.linehighlight");
lineHighlightColor =
Preferences.getColor("editor.linehighlight.color");
bracketHighlight =
Preferences.getBoolean("editor.brackethighlight");
bracketHighlightColor =
Preferences.getColor("editor.brackethighlight.color");
eolMarkers = Preferences.getBoolean("editor.eolmarkers");
eolMarkerColor = Preferences.getColor("editor.eolmarkers.color");
paintInvalid = Preferences.getBoolean("editor.invalid");
}
}

View File

@ -0,0 +1,166 @@
/*
* SyntaxDocument.java - Document that can be tokenized
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.undo.UndoableEdit;
/**
* A document implementation that can be tokenized by the syntax highlighting
* system.
*
* @author Slava Pestov
* @version $Id: SyntaxDocument.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
*/
public class SyntaxDocument extends PlainDocument
{
/**
* Returns the token marker that is to be used to split lines
* of this document up into tokens. May return null if this
* document is not to be colorized.
*/
public TokenMarker getTokenMarker()
{
return tokenMarker;
}
/**
* Sets the token marker that is to be used to split lines of
* this document up into tokens. May throw an exception if
* this is not supported for this type of document.
* @param tm The new token marker
*/
public void setTokenMarker(TokenMarker tm)
{
tokenMarker = tm;
if(tm == null)
return;
tokenMarker.insertLines(0,getDefaultRootElement()
.getElementCount());
tokenizeLines();
}
/**
* Reparses the document, by passing all lines to the token
* marker. This should be called after the document is first
* loaded.
*/
public void tokenizeLines()
{
tokenizeLines(0,getDefaultRootElement().getElementCount());
}
/**
* Reparses the document, by passing the specified lines to the
* token marker. This should be called after a large quantity of
* text is first inserted.
* @param start The first line to parse
* @param len The number of lines, after the first one to parse
*/
public void tokenizeLines(int start, int len)
{
if(tokenMarker == null || !tokenMarker.supportsMultilineTokens())
return;
Segment lineSegment = new Segment();
Element map = getDefaultRootElement();
len += start;
try
{
for(int i = start; i < len; i++)
{
Element lineElement = map.getElement(i);
int lineStart = lineElement.getStartOffset();
getText(lineStart,lineElement.getEndOffset()
- lineStart - 1,lineSegment);
tokenMarker.markTokens(lineSegment,i);
}
}
catch(BadLocationException bl)
{
bl.printStackTrace();
}
}
/**
* Starts a compound edit that can be undone in one operation.
* Subclasses that implement undo should override this method;
* this class has no undo functionality so this method is
* empty.
*/
public void beginCompoundEdit() {}
/**
* Ends a compound edit that can be undone in one operation.
* Subclasses that implement undo should override this method;
* this class has no undo functionality so this method is
* empty.
*/
public void endCompoundEdit() {}
/**
* Adds an undoable edit to this document's undo list. The edit
* should be ignored if something is currently being undone.
* @param edit The undoable edit
*
* @since jEdit 2.2pre1
*/
public void addUndoableEdit(UndoableEdit edit) {}
// protected members
protected TokenMarker tokenMarker;
/**
* We overwrite this method to update the token marker
* state immediately so that any event listeners get a
* consistent token marker.
*/
protected void fireInsertUpdate(DocumentEvent evt)
{
if(tokenMarker != null)
{
DocumentEvent.ElementChange ch = evt.getChange(
getDefaultRootElement());
if(ch != null)
{
tokenMarker.insertLines(ch.getIndex() + 1,
ch.getChildrenAdded().length -
ch.getChildrenRemoved().length);
}
}
super.fireInsertUpdate(evt);
}
/**
* We overwrite this method to update the token marker
* state immediately so that any event listeners get a
* consistent token marker.
*/
protected void fireRemoveUpdate(DocumentEvent evt)
{
if(tokenMarker != null)
{
DocumentEvent.ElementChange ch = evt.getChange(
getDefaultRootElement());
if(ch != null)
{
tokenMarker.deleteLines(ch.getIndex() + 1,
ch.getChildrenRemoved().length -
ch.getChildrenAdded().length);
}
}
super.fireRemoveUpdate(evt);
}
}

137
app/syntax/SyntaxStyle.java Normal file
View File

@ -0,0 +1,137 @@
/*
* SyntaxStyle.java - A simple text style class
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import java.awt.*;
import java.util.StringTokenizer;
/**
* A simple text style class. It can specify the color, italic flag,
* and bold flag of a run of text.
* @author Slava Pestov
* @version $Id: SyntaxStyle.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
*/
public class SyntaxStyle
{
/**
* Creates a new SyntaxStyle.
* @param color The text color
* @param italic True if the text should be italics
* @param bold True if the text should be bold
*/
public SyntaxStyle(Color color, boolean italic, boolean bold)
{
this.color = color;
this.italic = italic;
this.bold = bold;
}
/**
* Returns the color specified in this style.
*/
public Color getColor()
{
return color;
}
/**
* Returns true if no font styles are enabled.
*/
public boolean isPlain()
{
return !(bold || italic);
}
/**
* Returns true if italics is enabled for this style.
*/
public boolean isItalic()
{
return italic;
}
/**
* Returns true if boldface is enabled for this style.
*/
public boolean isBold()
{
return bold;
}
/**
* Returns the specified font, but with the style's bold and
* italic flags applied.
*/
public Font getStyledFont(Font font)
{
if(font == null)
throw new NullPointerException("font param must not"
+ " be null");
if(font.equals(lastFont))
return lastStyledFont;
lastFont = font;
lastStyledFont = new Font(font.getFamily(),
(bold ? Font.BOLD : 0)
| (italic ? Font.ITALIC : 0),
font.getSize());
return lastStyledFont;
}
/**
* Returns the font metrics for the styled font.
*/
public FontMetrics getFontMetrics(Font font)
{
if(font == null)
throw new NullPointerException("font param must not"
+ " be null");
if(font.equals(lastFont) && fontMetrics != null)
return fontMetrics;
lastFont = font;
lastStyledFont = new Font(font.getFamily(),
(bold ? Font.BOLD : 0)
| (italic ? Font.ITALIC : 0),
font.getSize());
fontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(
lastStyledFont);
return fontMetrics;
}
/**
* Sets the foreground color and font of the specified graphics
* context to that specified in this style.
* @param gfx The graphics context
* @param font The font to add the styles to
*/
public void setGraphicsFlags(Graphics gfx, Font font)
{
Font _font = getStyledFont(font);
gfx.setFont(_font);
gfx.setColor(color);
}
/**
* Returns a string representation of this object.
*/
public String toString()
{
return getClass().getName() + "[color=" + color +
(italic ? ",italic" : "") +
(bold ? ",bold" : "") + "]";
}
// private members
private Color color;
private boolean italic;
private boolean bold;
private Font lastFont;
private Font lastStyledFont;
private FontMetrics fontMetrics;
}

View File

@ -0,0 +1,163 @@
/*
* SyntaxUtilities.java - Utility functions used by syntax colorizing
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.text.*;
import java.awt.*;
/**
* Class with several utility functions used by jEdit's syntax colorizing
* subsystem.
*
* @author Slava Pestov
* @version $Id: SyntaxUtilities.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
*/
public class SyntaxUtilities
{
/**
* Checks if a subregion of a <code>Segment</code> is equal to a
* string.
* @param ignoreCase True if case should be ignored, false otherwise
* @param text The segment
* @param offset The offset into the segment
* @param match The string to match
*/
public static boolean regionMatches(boolean ignoreCase, Segment text,
int offset, String match)
{
int length = offset + match.length();
char[] textArray = text.array;
if(length > text.offset + text.count)
return false;
for(int i = offset, j = 0; i < length; i++, j++)
{
char c1 = textArray[i];
char c2 = match.charAt(j);
if(ignoreCase)
{
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
}
if(c1 != c2)
return false;
}
return true;
}
/**
* Checks if a subregion of a <code>Segment</code> is equal to a
* character array.
* @param ignoreCase True if case should be ignored, false otherwise
* @param text The segment
* @param offset The offset into the segment
* @param match The character array to match
*/
public static boolean regionMatches(boolean ignoreCase, Segment text,
int offset, char[] match)
{
int length = offset + match.length;
char[] textArray = text.array;
if(length > text.offset + text.count)
return false;
for(int i = offset, j = 0; i < length; i++, j++)
{
char c1 = textArray[i];
char c2 = match[j];
if(ignoreCase)
{
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
}
if(c1 != c2)
return false;
}
return true;
}
/**
* Returns the default style table. This can be passed to the
* <code>setStyles()</code> method of <code>SyntaxDocument</code>
* to use the default syntax styles.
*/
public static SyntaxStyle[] getDefaultSyntaxStyles()
{
SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT];
styles[Token.COMMENT1] = new SyntaxStyle(Color.black,true,false);
styles[Token.COMMENT2] = new SyntaxStyle(new Color(0x990033),true,false);
styles[Token.KEYWORD1] = new SyntaxStyle(Color.black,false,true);
styles[Token.KEYWORD2] = new SyntaxStyle(Color.magenta,false,false);
styles[Token.KEYWORD3] = new SyntaxStyle(new Color(0x009600),false,false);
styles[Token.LITERAL1] = new SyntaxStyle(new Color(0x650099),false,false);
styles[Token.LITERAL2] = new SyntaxStyle(new Color(0x650099),false,true);
styles[Token.LABEL] = new SyntaxStyle(new Color(0x990033),false,true);
styles[Token.OPERATOR] = new SyntaxStyle(Color.black,false,true);
styles[Token.INVALID] = new SyntaxStyle(Color.red,false,true);
return styles;
}
/**
* Paints the specified line onto the graphics context. Note that this
* method munges the offset and count values of the segment.
* @param line The line segment
* @param tokens The token list for the line
* @param styles The syntax style list
* @param expander The tab expander used to determine tab stops. May
* be null
* @param gfx The graphics context
* @param x The x co-ordinate
* @param y The y co-ordinate
* @return The x co-ordinate, plus the width of the painted string
*/
public static int paintSyntaxLine(Segment line, Token tokens,
SyntaxStyle[] styles,
TabExpander expander, Graphics gfx,
int x, int y)
{
Font defaultFont = gfx.getFont();
Color defaultColor = gfx.getColor();
int offset = 0;
for(;;)
{
byte id = tokens.id;
if(id == Token.END)
break;
int length = tokens.length;
if(id == Token.NULL)
{
if(!defaultColor.equals(gfx.getColor()))
gfx.setColor(defaultColor);
if(!defaultFont.equals(gfx.getFont()))
gfx.setFont(defaultFont);
}
else
styles[id].setGraphicsFlags(gfx,defaultFont);
line.count = length;
x = Utilities.drawTabbedText(line,x,y,gfx,expander,0);
line.offset += length;
offset += length;
tokens = tokens.next;
}
return x;
}
// private members
private SyntaxUtilities() {}
}

View File

@ -0,0 +1,90 @@
/*
* TextAreaDefaults.java - Encapsulates default values for various settings
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import java.awt.*;
//import javax.swing.JPopupMenu;
/**
* Encapsulates default settings for a text area. This can be passed
* to the constructor once the necessary fields have been filled out.
* The advantage of doing this over calling lots of set() methods after
* creating the text area is that this method is faster.
*/
public class TextAreaDefaults
{
private static TextAreaDefaults DEFAULTS;
public InputHandler inputHandler;
public SyntaxDocument document;
public boolean editable;
public boolean caretVisible;
public boolean caretBlinks;
public boolean blockCaret;
public int electricScroll;
public int cols;
public int rows;
public SyntaxStyle[] styles;
public Color caretColor;
public Color selectionColor;
public Color lineHighlightColor;
public boolean lineHighlight;
public Color bracketHighlightColor;
public boolean bracketHighlight;
public Color eolMarkerColor;
public boolean eolMarkers;
public boolean paintInvalid;
// moved from TextAreaPainter [fry]
public Font font;
public Color fgcolor;
public Color bgcolor;
//public JPopupMenu popup;
/**
* Returns a new TextAreaDefaults object with the default values filled
* in.
*/
public static TextAreaDefaults getDefaults()
{
if (DEFAULTS == null) {
DEFAULTS = new TextAreaDefaults();
DEFAULTS.inputHandler = new DefaultInputHandler();
DEFAULTS.inputHandler.addDefaultKeyBindings();
DEFAULTS.document = new SyntaxDocument();
DEFAULTS.editable = true;
DEFAULTS.caretVisible = true;
DEFAULTS.caretBlinks = true;
DEFAULTS.electricScroll = 3;
DEFAULTS.cols = 80;
DEFAULTS.rows = 25;
DEFAULTS.styles = SyntaxUtilities.getDefaultSyntaxStyles();
DEFAULTS.caretColor = Color.red;
DEFAULTS.selectionColor = new Color(0xccccff);
DEFAULTS.lineHighlightColor = new Color(0xe0e0e0);
DEFAULTS.lineHighlight = true;
DEFAULTS.bracketHighlightColor = Color.black;
DEFAULTS.bracketHighlight = true;
DEFAULTS.eolMarkerColor = new Color(0x009999);
DEFAULTS.eolMarkers = true;
DEFAULTS.paintInvalid = true;
}
return DEFAULTS;
}
}

View File

@ -0,0 +1,688 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* TextAreaPainter.java - Paints the text area
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import processing.app.*;
import javax.swing.ToolTipManager;
import javax.swing.text.*;
import javax.swing.JComponent;
import java.awt.event.MouseEvent;
import java.awt.*;
/**
* The text area repaint manager. It performs double buffering and paints
* lines of text.
* @author Slava Pestov
* @version $Id: TextAreaPainter.java,v 1.3 2005/05/10 01:17:21 benfry Exp $
*/
public class TextAreaPainter extends JComponent implements TabExpander
{
/**
* Creates a new repaint manager. This should be not be called
* directly.
*/
public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults)
{
this.textArea = textArea;
setAutoscrolls(true);
setDoubleBuffered(true);
setOpaque(true);
ToolTipManager.sharedInstance().registerComponent(this);
currentLine = new Segment();
currentLineIndex = -1;
setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
setFont(defaults.font);
setForeground(defaults.fgcolor);
setBackground(defaults.bgcolor);
blockCaret = defaults.blockCaret;
styles = defaults.styles;
cols = defaults.cols;
rows = defaults.rows;
caretColor = defaults.caretColor;
selectionColor = defaults.selectionColor;
lineHighlightColor = defaults.lineHighlightColor;
lineHighlight = defaults.lineHighlight;
bracketHighlightColor = defaults.bracketHighlightColor;
bracketHighlight = defaults.bracketHighlight;
paintInvalid = defaults.paintInvalid;
eolMarkerColor = defaults.eolMarkerColor;
eolMarkers = defaults.eolMarkers;
}
/**
* Returns if this component can be traversed by pressing the
* Tab key. This returns false.
*/
public final boolean isManagingFocus()
{
return false;
}
/**
* Returns the syntax styles used to paint colorized text. Entry <i>n</i>
* will be used to paint tokens with id = <i>n</i>.
* @see org.gjt.sp.jedit.syntax.Token
*/
public final SyntaxStyle[] getStyles()
{
return styles;
}
/**
* Sets the syntax styles used to paint colorized text. Entry <i>n</i>
* will be used to paint tokens with id = <i>n</i>.
* @param styles The syntax styles
* @see org.gjt.sp.jedit.syntax.Token
*/
public final void setStyles(SyntaxStyle[] styles)
{
this.styles = styles;
repaint();
}
/**
* Returns the caret color.
*/
public final Color getCaretColor()
{
return caretColor;
}
/**
* Sets the caret color.
* @param caretColor The caret color
*/
public final void setCaretColor(Color caretColor)
{
this.caretColor = caretColor;
invalidateSelectedLines();
}
/**
* Returns the selection color.
*/
public final Color getSelectionColor()
{
return selectionColor;
}
/**
* Sets the selection color.
* @param selectionColor The selection color
*/
public final void setSelectionColor(Color selectionColor)
{
this.selectionColor = selectionColor;
invalidateSelectedLines();
}
/**
* Returns the line highlight color.
*/
public final Color getLineHighlightColor()
{
return lineHighlightColor;
}
/**
* Sets the line highlight color.
* @param lineHighlightColor The line highlight color
*/
public final void setLineHighlightColor(Color lineHighlightColor)
{
this.lineHighlightColor = lineHighlightColor;
invalidateSelectedLines();
}
/**
* Returns true if line highlight is enabled, false otherwise.
*/
public final boolean isLineHighlightEnabled()
{
return lineHighlight;
}
/**
* Enables or disables current line highlighting.
* @param lineHighlight True if current line highlight
* should be enabled, false otherwise
*/
public final void setLineHighlightEnabled(boolean lineHighlight)
{
this.lineHighlight = lineHighlight;
invalidateSelectedLines();
}
/**
* Returns the bracket highlight color.
*/
public final Color getBracketHighlightColor()
{
return bracketHighlightColor;
}
/**
* Sets the bracket highlight color.
* @param bracketHighlightColor The bracket highlight color
*/
public final void setBracketHighlightColor(Color bracketHighlightColor)
{
this.bracketHighlightColor = bracketHighlightColor;
invalidateLine(textArea.getBracketLine());
}
/**
* Returns true if bracket highlighting is enabled, false otherwise.
* When bracket highlighting is enabled, the bracket matching the
* one before the caret (if any) is highlighted.
*/
public final boolean isBracketHighlightEnabled()
{
return bracketHighlight;
}
/**
* Enables or disables bracket highlighting.
* When bracket highlighting is enabled, the bracket matching the
* one before the caret (if any) is highlighted.
* @param bracketHighlight True if bracket highlighting should be
* enabled, false otherwise
*/
public final void setBracketHighlightEnabled(boolean bracketHighlight)
{
this.bracketHighlight = bracketHighlight;
invalidateLine(textArea.getBracketLine());
}
/**
* Returns true if the caret should be drawn as a block, false otherwise.
*/
public final boolean isBlockCaretEnabled()
{
return blockCaret;
}
/**
* Sets if the caret should be drawn as a block, false otherwise.
* @param blockCaret True if the caret should be drawn as a block,
* false otherwise.
*/
public final void setBlockCaretEnabled(boolean blockCaret)
{
this.blockCaret = blockCaret;
invalidateSelectedLines();
}
/**
* Returns the EOL marker color.
*/
public final Color getEOLMarkerColor()
{
return eolMarkerColor;
}
/**
* Sets the EOL marker color.
* @param eolMarkerColor The EOL marker color
*/
public final void setEOLMarkerColor(Color eolMarkerColor)
{
this.eolMarkerColor = eolMarkerColor;
repaint();
}
/**
* Returns true if EOL markers are drawn, false otherwise.
*/
public final boolean getEOLMarkersPainted()
{
return eolMarkers;
}
/**
* Sets if EOL markers are to be drawn.
* @param eolMarkers True if EOL markers should be drawn, false otherwise
*/
public final void setEOLMarkersPainted(boolean eolMarkers)
{
this.eolMarkers = eolMarkers;
repaint();
}
/**
* Returns true if invalid lines are painted as red tildes (~),
* false otherwise.
*/
public boolean getInvalidLinesPainted()
{
return paintInvalid;
}
/**
* Sets if invalid lines are to be painted as red tildes.
* @param paintInvalid True if invalid lines should be drawn, false otherwise
*/
public void setInvalidLinesPainted(boolean paintInvalid)
{
this.paintInvalid = paintInvalid;
}
/**
* Adds a custom highlight painter.
* @param highlight The highlight
*/
public void addCustomHighlight(Highlight highlight)
{
highlight.init(textArea,highlights);
highlights = highlight;
}
/**
* Highlight interface.
*/
public interface Highlight
{
/**
* Called after the highlight painter has been added.
* @param textArea The text area
* @param next The painter this one should delegate to
*/
void init(JEditTextArea textArea, Highlight next);
/**
* This should paint the highlight and delgate to the
* next highlight painter.
* @param gfx The graphics context
* @param line The line number
* @param y The y co-ordinate of the line
*/
void paintHighlight(Graphics gfx, int line, int y);
/**
* Returns the tool tip to display at the specified
* location. If this highlighter doesn't know what to
* display, it should delegate to the next highlight
* painter.
* @param evt The mouse event
*/
String getToolTipText(MouseEvent evt);
}
/**
* Returns the tool tip to display at the specified location.
* @param evt The mouse event
*/
public String getToolTipText(MouseEvent evt)
{
if(highlights != null)
return highlights.getToolTipText(evt);
else
return null;
}
/**
* Returns the font metrics used by this component.
*/
public FontMetrics getFontMetrics()
{
return fm;
}
/**
* Sets the font for this component. This is overridden to update the
* cached font metrics and to recalculate which lines are visible.
* @param font The font
*/
public void setFont(Font font)
{
super.setFont(font);
fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
textArea.recalculateVisibleLines();
}
/**
* Repaints the text.
* @param g The graphics context
*/
public void paint(Graphics gfx)
{
if (Base.isMacOS()) {
Graphics2D g2 = (Graphics2D) gfx;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
}
tabSize = fm.charWidth(' ') * ((Integer)textArea.getDocument().getProperty(PlainDocument.tabSizeAttribute)).intValue();
Rectangle clipRect = gfx.getClipBounds();
gfx.setColor(getBackground());
gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
// We don't use yToLine() here because that method doesn't
// return lines past the end of the document
int height = fm.getHeight();
int firstLine = textArea.getFirstLine();
int firstInvalid = firstLine + clipRect.y / height;
// Because the clipRect's height is usually an even multiple
// of the font height, we subtract 1 from it, otherwise one
// too many lines will always be painted.
int lastInvalid = firstLine + (clipRect.y + clipRect.height - 1) / height;
try {
TokenMarker tokenMarker = textArea.getDocument().getTokenMarker();
int x = textArea.getHorizontalOffset();
for (int line = firstInvalid; line <= lastInvalid; line++) {
paintLine(gfx,tokenMarker,line,x);
}
if (tokenMarker != null && tokenMarker.isNextLineRequested()) {
int h = clipRect.y + clipRect.height;
repaint(0,h,getWidth(),getHeight() - h);
}
} catch (Exception e) {
System.err.println("Error repainting line"
+ " range {" + firstInvalid + ","
+ lastInvalid + "}:");
e.printStackTrace();
}
}
/**
* Marks a line as needing a repaint.
* @param line The line to invalidate
*/
public final void invalidateLine(int line)
{
repaint(0,textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(),
getWidth(),fm.getHeight());
}
/**
* Marks a range of lines as needing a repaint.
* @param firstLine The first line to invalidate
* @param lastLine The last line to invalidate
*/
public final void invalidateLineRange(int firstLine, int lastLine)
{
repaint(0,textArea.lineToY(firstLine) +
fm.getMaxDescent() + fm.getLeading(),
getWidth(),(lastLine - firstLine + 1) * fm.getHeight());
}
/**
* Repaints the lines containing the selection.
*/
public final void invalidateSelectedLines()
{
invalidateLineRange(textArea.getSelectionStartLine(),
textArea.getSelectionEndLine());
}
/**
* Implementation of TabExpander interface. Returns next tab stop after
* a specified point.
* @param x The x co-ordinate
* @param tabOffset Ignored
* @return The next tab stop after <i>x</i>
*/
public float nextTabStop(float x, int tabOffset)
{
int offset = textArea.getHorizontalOffset();
int ntabs = ((int)x - offset) / tabSize;
return (ntabs + 1) * tabSize + offset;
}
/**
* Returns the painter's preferred size.
*/
public Dimension getPreferredSize()
{
Dimension dim = new Dimension();
dim.width = fm.charWidth('w') * cols;
dim.height = fm.getHeight() * rows;
return dim;
}
/**
* Returns the painter's minimum size.
*/
public Dimension getMinimumSize()
{
return getPreferredSize();
}
// package-private members
int currentLineIndex;
Token currentLineTokens;
Segment currentLine;
// protected members
protected JEditTextArea textArea;
protected SyntaxStyle[] styles;
protected Color caretColor;
protected Color selectionColor;
protected Color lineHighlightColor;
protected Color bracketHighlightColor;
protected Color eolMarkerColor;
protected boolean blockCaret;
protected boolean lineHighlight;
protected boolean bracketHighlight;
protected boolean paintInvalid;
protected boolean eolMarkers;
protected int cols;
protected int rows;
protected int tabSize;
protected FontMetrics fm;
protected Highlight highlights;
protected void paintLine(Graphics gfx, TokenMarker tokenMarker,
int line, int x)
{
Font defaultFont = getFont();
Color defaultColor = getForeground();
currentLineIndex = line;
int y = textArea.lineToY(line);
if (line < 0 || line >= textArea.getLineCount()) {
if (paintInvalid) {
paintHighlight(gfx,line,y);
styles[Token.INVALID].setGraphicsFlags(gfx,defaultFont);
gfx.drawString("~",0,y + fm.getHeight());
}
} else if(tokenMarker == null) {
paintPlainLine(gfx,line,defaultFont,defaultColor,x,y);
} else {
paintSyntaxLine(gfx,tokenMarker,line,defaultFont,
defaultColor,x,y);
}
}
protected void paintPlainLine(Graphics gfx, int line, Font defaultFont,
Color defaultColor, int x, int y)
{
paintHighlight(gfx,line,y);
textArea.getLineText(line,currentLine);
gfx.setFont(defaultFont);
gfx.setColor(defaultColor);
y += fm.getHeight();
x = Utilities.drawTabbedText(currentLine,x,y,gfx,this,0);
if (eolMarkers) {
gfx.setColor(eolMarkerColor);
gfx.drawString(".",x,y);
}
}
protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker,
int line, Font defaultFont,
Color defaultColor, int x, int y)
{
textArea.getLineText(currentLineIndex,currentLine);
currentLineTokens = tokenMarker.markTokens(currentLine,
currentLineIndex);
paintHighlight(gfx,line,y);
gfx.setFont(defaultFont);
gfx.setColor(defaultColor);
y += fm.getHeight();
x = SyntaxUtilities.paintSyntaxLine(currentLine,
currentLineTokens,
styles, this, gfx, x, y);
if (eolMarkers) {
gfx.setColor(eolMarkerColor);
gfx.drawString(".",x,y);
}
}
protected void paintHighlight(Graphics gfx, int line, int y)
{
if (line >= textArea.getSelectionStartLine()
&& line <= textArea.getSelectionEndLine())
paintLineHighlight(gfx,line,y);
if (highlights != null)
highlights.paintHighlight(gfx,line,y);
if (bracketHighlight && line == textArea.getBracketLine())
paintBracketHighlight(gfx,line,y);
if (line == textArea.getCaretLine())
paintCaret(gfx,line,y);
}
protected void paintLineHighlight(Graphics gfx, int line, int y)
{
int height = fm.getHeight();
y += fm.getLeading() + fm.getMaxDescent();
int selectionStart = textArea.getSelectionStart();
int selectionEnd = textArea.getSelectionEnd();
if (selectionStart == selectionEnd) {
if (lineHighlight) {
gfx.setColor(lineHighlightColor);
gfx.fillRect(0,y,getWidth(),height);
}
} else {
gfx.setColor(selectionColor);
int selectionStartLine = textArea.getSelectionStartLine();
int selectionEndLine = textArea.getSelectionEndLine();
int lineStart = textArea.getLineStartOffset(line);
int x1, x2;
if (textArea.isSelectionRectangular()) {
int lineLen = textArea.getLineLength(line);
x1 = textArea._offsetToX(line,Math.min(lineLen, selectionStart - textArea.getLineStartOffset(selectionStartLine)));
x2 = textArea._offsetToX(line,Math.min(lineLen, selectionEnd - textArea.getLineStartOffset(selectionEndLine)));
if (x1 == x2)
x2++;
} else if(selectionStartLine == selectionEndLine) {
x1 = textArea._offsetToX(line, selectionStart - lineStart);
x2 = textArea._offsetToX(line, selectionEnd - lineStart);
} else if(line == selectionStartLine) {
x1 = textArea._offsetToX(line, selectionStart - lineStart);
x2 = getWidth();
} else if(line == selectionEndLine) {
//x1 = 0;
// hack from stendahl to avoid doing weird side selection thing
x1 = textArea._offsetToX(line, 0);
// attempt at getting the gutter too, but doesn't seem to work
//x1 = textArea._offsetToX(line, -textArea.getHorizontalOffset());
x2 = textArea._offsetToX(line, selectionEnd - lineStart);
} else {
//x1 = 0;
// hack from stendahl to avoid doing weird side selection thing
x1 = textArea._offsetToX(line, 0);
// attempt at getting the gutter too, but doesn't seem to work
//x1 = textArea._offsetToX(line, -textArea.getHorizontalOffset());
x2 = getWidth();
}
// "inlined" min/max()
gfx.fillRect(x1 > x2 ? x2 : x1,y,x1 > x2 ?
(x1 - x2) : (x2 - x1),height);
}
}
protected void paintBracketHighlight(Graphics gfx, int line, int y)
{
int position = textArea.getBracketPosition();
if(position == -1)
return;
y += fm.getLeading() + fm.getMaxDescent();
int x = textArea._offsetToX(line,position);
gfx.setColor(bracketHighlightColor);
// Hack!!! Since there is no fast way to get the character
// from the bracket matching routine, we use ( since all
// brackets probably have the same width anyway
gfx.drawRect(x,y,fm.charWidth('(') - 1,
fm.getHeight() - 1);
}
protected void paintCaret(Graphics gfx, int line, int y)
{
//System.out.println("painting caret " + line + " " + y);
if (textArea.isCaretVisible()) {
//System.out.println("caret is visible");
int offset =
textArea.getCaretPosition() - textArea.getLineStartOffset(line);
int caretX = textArea._offsetToX(line, offset);
int caretWidth = ((blockCaret ||
textArea.isOverwriteEnabled()) ?
fm.charWidth('w') : 1);
y += fm.getLeading() + fm.getMaxDescent();
int height = fm.getHeight();
//System.out.println("caretX, width = " + caretX + " " + caretWidth);
gfx.setColor(caretColor);
if (textArea.isOverwriteEnabled()) {
gfx.fillRect(caretX,y + height - 1, caretWidth,1);
} else {
// some machines don't like the drawRect for the single
// pixel caret.. this caused a lot of hell because on that
// minority of machines, the caret wouldn't show up past
// the first column. the fix is to use drawLine() in
// those cases, as a workaround.
if (caretWidth == 1) {
gfx.drawLine(caretX, y, caretX, y + height - 1);
} else {
gfx.drawRect(caretX, y, caretWidth - 1, height - 1);
}
//gfx.drawRect(caretX, y, caretWidth, height - 1);
}
}
}
}

View File

@ -0,0 +1,184 @@
/*
* TextUtilities.java - Utility functions used by the text area classes
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.text.*;
/**
* Class with several utility functions used by the text area component.
* @author Slava Pestov
* @version $Id: TextUtilities.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
*/
public class TextUtilities
{
/**
* Returns the offset of the bracket matching the one at the
* specified offset of the document, or -1 if the bracket is
* unmatched (or if the character is not a bracket).
* @param doc The document
* @param offset The offset
* @exception BadLocationException If an out-of-bounds access
* was attempted on the document text
*/
public static int findMatchingBracket(Document doc, int offset)
throws BadLocationException
{
if(doc.getLength() == 0)
return -1;
char c = doc.getText(offset,1).charAt(0);
char cprime; // c` - corresponding character
boolean direction; // true = back, false = forward
switch(c)
{
case '(': cprime = ')'; direction = false; break;
case ')': cprime = '('; direction = true; break;
case '[': cprime = ']'; direction = false; break;
case ']': cprime = '['; direction = true; break;
case '{': cprime = '}'; direction = false; break;
case '}': cprime = '{'; direction = true; break;
default: return -1;
}
int count;
// How to merge these two cases is left as an exercise
// for the reader.
// Go back or forward
if(direction)
{
// Count is 1 initially because we have already
// `found' one closing bracket
count = 1;
// Get text[0,offset-1];
String text = doc.getText(0,offset);
// Scan backwards
for(int i = offset - 1; i >= 0; i--)
{
// If text[i] == c, we have found another
// closing bracket, therefore we will need
// two opening brackets to complete the
// match.
char x = text.charAt(i);
if(x == c)
count++;
// If text[i] == cprime, we have found a
// opening bracket, so we return i if
// --count == 0
else if(x == cprime)
{
if(--count == 0)
return i;
}
}
}
else
{
// Count is 1 initially because we have already
// `found' one opening bracket
count = 1;
// So we don't have to + 1 in every loop
offset++;
// Number of characters to check
int len = doc.getLength() - offset;
// Get text[offset+1,len];
String text = doc.getText(offset,len);
// Scan forwards
for(int i = 0; i < len; i++)
{
// If text[i] == c, we have found another
// opening bracket, therefore we will need
// two closing brackets to complete the
// match.
char x = text.charAt(i);
if(x == c)
count++;
// If text[i] == cprime, we have found an
// closing bracket, so we return i if
// --count == 0
else if(x == cprime)
{
if(--count == 0)
return i + offset;
}
}
}
// Nothing found
return -1;
}
/**
* Locates the start of the word at the specified position.
* @param line The text
* @param pos The position
*/
public static int findWordStart(String line, int pos, String noWordSep)
{
char ch = line.charAt(pos - 1);
if(noWordSep == null)
noWordSep = "";
boolean selectNoLetter = (!Character.isLetterOrDigit(ch)
&& noWordSep.indexOf(ch) == -1);
int wordStart = 0;
for(int i = pos - 1; i >= 0; i--)
{
ch = line.charAt(i);
if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) &&
noWordSep.indexOf(ch) == -1))
{
wordStart = i + 1;
break;
}
}
return wordStart;
}
/**
* Locates the end of the word at the specified position.
* @param line The text
* @param pos The position
*/
public static int findWordEnd(String line, int pos, String noWordSep)
{
char ch = line.charAt(pos);
if(noWordSep == null)
noWordSep = "";
boolean selectNoLetter = (!Character.isLetterOrDigit(ch)
&& noWordSep.indexOf(ch) == -1);
int wordEnd = line.length();
for(int i = pos; i < line.length(); i++)
{
ch = line.charAt(i);
if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) &&
noWordSep.indexOf(ch) == -1))
{
wordEnd = i;
break;
}
}
return wordEnd;
}
}

149
app/syntax/Token.java Normal file
View File

@ -0,0 +1,149 @@
/*
* Token.java - Generic token
* Copyright (C) 1998, 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
/**
* A linked list of tokens. Each token has three fields - a token
* identifier, which is a byte value that can be looked up in the
* array returned by <code>SyntaxDocument.getColors()</code>
* to get a color value, a length value which is the length of the
* token in the text, and a pointer to the next token in the list.
*
* @author Slava Pestov
* @version $Id: Token.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
*/
public class Token
{
/**
* Normal text token id. This should be used to mark
* normal text.
*/
public static final byte NULL = 0;
/**
* Comment 1 token id. This can be used to mark a comment.
*/
public static final byte COMMENT1 = 1;
/**
* Comment 2 token id. This can be used to mark a comment.
*/
public static final byte COMMENT2 = 2;
/**
* Literal 1 token id. This can be used to mark a string
* literal (eg, C mode uses this to mark "..." literals)
*/
public static final byte LITERAL1 = 3;
/**
* Literal 2 token id. This can be used to mark an object
* literal (eg, Java mode uses this to mark true, false, etc)
*/
public static final byte LITERAL2 = 4;
/**
* Label token id. This can be used to mark labels
* (eg, C mode uses this to mark ...: sequences)
*/
public static final byte LABEL = 5;
/**
* Keyword 1 token id. This can be used to mark a
* keyword. This should be used for general language
* constructs.
*/
public static final byte KEYWORD1 = 6;
/**
* Keyword 2 token id. This can be used to mark a
* keyword. This should be used for preprocessor
* commands, or variables.
*/
public static final byte KEYWORD2 = 7;
/**
* Keyword 3 token id. This can be used to mark a
* keyword. This should be used for data types.
*/
public static final byte KEYWORD3 = 8;
/**
* Operator token id. This can be used to mark an
* operator. (eg, SQL mode marks +, -, etc with this
* token type)
*/
public static final byte OPERATOR = 9;
/**
* Invalid token id. This can be used to mark invalid
* or incomplete tokens, so the user can easily spot
* syntax errors.
*/
public static final byte INVALID = 10;
/**
* The total number of defined token ids.
*/
public static final byte ID_COUNT = 11;
/**
* The first id that can be used for internal state
* in a token marker.
*/
public static final byte INTERNAL_FIRST = 100;
/**
* The last id that can be used for internal state
* in a token marker.
*/
public static final byte INTERNAL_LAST = 126;
/**
* The token type, that along with a length of 0
* marks the end of the token list.
*/
public static final byte END = 127;
/**
* The length of this token.
*/
public int length;
/**
* The id of this token.
*/
public byte id;
/**
* The next token in the linked list.
*/
public Token next;
/**
* Creates a new token.
* @param length The length of the token
* @param id The id of the token
*/
public Token(int length, byte id)
{
this.length = length;
this.id = id;
}
/**
* Returns a string representation of this token.
*/
public String toString()
{
return "[id=" + id + ",length=" + length + "]";
}
}

345
app/syntax/TokenMarker.java Normal file
View File

@ -0,0 +1,345 @@
/*
* TokenMarker.java - Generic token marker
* Copyright (C) 1998, 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package processing.app.syntax;
import javax.swing.text.Segment;
import java.util.*;
/**
* A token marker that splits lines of text into tokens. Each token carries
* a length field and an indentification tag that can be mapped to a color
* for painting that token.<p>
*
* For performance reasons, the linked list of tokens is reused after each
* line is tokenized. Therefore, the return value of <code>markTokens</code>
* should only be used for immediate painting. Notably, it cannot be
* cached.
*
* @author Slava Pestov
* @version $Id: TokenMarker.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
*
* @see org.gjt.sp.jedit.syntax.Token
*/
public abstract class TokenMarker
{
/**
* A wrapper for the lower-level <code>markTokensImpl</code> method
* that is called to split a line up into tokens.
* @param line The line
* @param lineIndex The line number
*/
public Token markTokens(Segment line, int lineIndex)
{
if(lineIndex >= length)
{
throw new IllegalArgumentException("Tokenizing invalid line: "
+ lineIndex);
}
lastToken = null;
LineInfo info = lineInfo[lineIndex];
LineInfo prev;
if(lineIndex == 0)
prev = null;
else
prev = lineInfo[lineIndex - 1];
byte oldToken = info.token;
byte token = markTokensImpl(prev == null ?
Token.NULL : prev.token,line,lineIndex);
info.token = token;
/*
* This is a foul hack. It stops nextLineRequested
* from being cleared if the same line is marked twice.
*
* Why is this necessary? It's all JEditTextArea's fault.
* When something is inserted into the text, firing a
* document event, the insertUpdate() method shifts the
* caret (if necessary) by the amount inserted.
*
* All caret movement is handled by the select() method,
* which eventually pipes the new position to scrollTo()
* and calls repaint().
*
* Note that at this point in time, the new line hasn't
* yet been painted; the caret is moved first.
*
* scrollTo() calls offsetToX(), which tokenizes the line
* unless it is being called on the last line painted
* (in which case it uses the text area's painter cached
* token list). What scrollTo() does next is irrelevant.
*
* After scrollTo() has done it's job, repaint() is
* called, and eventually we end up in paintLine(), whose
* job is to paint the changed line. It, too, calls
* markTokens().
*
* The problem was that if the line started a multiline
* token, the first markTokens() (done in offsetToX())
* would set nextLineRequested (because the line end
* token had changed) but the second would clear it
* (because the line was the same that time) and therefore
* paintLine() would never know that it needed to repaint
* subsequent lines.
*
* This bug took me ages to track down, that's why I wrote
* all the relevant info down so that others wouldn't
* duplicate it.
*/
if(!(lastLine == lineIndex && nextLineRequested))
nextLineRequested = (oldToken != token);
lastLine = lineIndex;
addToken(0,Token.END);
return firstToken;
}
/**
* An abstract method that splits a line up into tokens. It
* should parse the line, and call <code>addToken()</code> to
* add syntax tokens to the token list. Then, it should return
* the initial token type for the next line.<p>
*
* For example if the current line contains the start of a
* multiline comment that doesn't end on that line, this method
* should return the comment token type so that it continues on
* the next line.
*
* @param token The initial token type for this line
* @param line The line to be tokenized
* @param lineIndex The index of the line in the document,
* starting at 0
* @return The initial token type for the next line
*/
protected abstract byte markTokensImpl(byte token, Segment line,
int lineIndex);
/**
* Returns if the token marker supports tokens that span multiple
* lines. If this is true, the object using this token marker is
* required to pass all lines in the document to the
* <code>markTokens()</code> method (in turn).<p>
*
* The default implementation returns true; it should be overridden
* to return false on simpler token markers for increased speed.
*/
public boolean supportsMultilineTokens()
{
return true;
}
/**
* Informs the token marker that lines have been inserted into
* the document. This inserts a gap in the <code>lineInfo</code>
* array.
* @param index The first line number
* @param lines The number of lines
*/
public void insertLines(int index, int lines)
{
if(lines <= 0)
return;
length += lines;
ensureCapacity(length);
int len = index + lines;
System.arraycopy(lineInfo,index,lineInfo,len,
lineInfo.length - len);
for(int i = index + lines - 1; i >= index; i--)
{
lineInfo[i] = new LineInfo();
}
}
/**
* Informs the token marker that line have been deleted from
* the document. This removes the lines in question from the
* <code>lineInfo</code> array.
* @param index The first line number
* @param lines The number of lines
*/
public void deleteLines(int index, int lines)
{
if (lines <= 0)
return;
int len = index + lines;
length -= lines;
System.arraycopy(lineInfo,len,lineInfo,
index,lineInfo.length - len);
}
/**
* Returns the number of lines in this token marker.
*/
public int getLineCount()
{
return length;
}
/**
* Returns true if the next line should be repainted. This
* will return true after a line has been tokenized that starts
* a multiline token that continues onto the next line.
*/
public boolean isNextLineRequested()
{
return nextLineRequested;
}
// protected members
/**
* The first token in the list. This should be used as the return
* value from <code>markTokens()</code>.
*/
protected Token firstToken;
/**
* The last token in the list. New tokens are added here.
* This should be set to null before a new line is to be tokenized.
*/
protected Token lastToken;
/**
* An array for storing information about lines. It is enlarged and
* shrunk automatically by the <code>insertLines()</code> and
* <code>deleteLines()</code> methods.
*/
protected LineInfo[] lineInfo;
/**
* The number of lines in the model being tokenized. This can be
* less than the length of the <code>lineInfo</code> array.
*/
protected int length;
/**
* The last tokenized line.
*/
protected int lastLine;
/**
* True if the next line should be painted.
*/
protected boolean nextLineRequested;
/**
* Creates a new <code>TokenMarker</code>. This DOES NOT create
* a lineInfo array; an initial call to <code>insertLines()</code>
* does that.
*/
protected TokenMarker()
{
lastLine = -1;
}
/**
* Ensures that the <code>lineInfo</code> array can contain the
* specified index. This enlarges it if necessary. No action is
* taken if the array is large enough already.<p>
*
* It should be unnecessary to call this under normal
* circumstances; <code>insertLine()</code> should take care of
* enlarging the line info array automatically.
*
* @param index The array index
*/
protected void ensureCapacity(int index)
{
if(lineInfo == null)
lineInfo = new LineInfo[index + 1];
else if(lineInfo.length <= index)
{
LineInfo[] lineInfoN = new LineInfo[(index + 1) * 2];
System.arraycopy(lineInfo,0,lineInfoN,0,
lineInfo.length);
lineInfo = lineInfoN;
}
}
/**
* Adds a token to the token list.
* @param length The length of the token
* @param id The id of the token
*/
protected void addToken(int length, byte id)
{
if(id >= Token.INTERNAL_FIRST && id <= Token.INTERNAL_LAST)
throw new InternalError("Invalid id: " + id);
if(length == 0 && id != Token.END)
return;
if(firstToken == null)
{
firstToken = new Token(length,id);
lastToken = firstToken;
}
else if(lastToken == null)
{
lastToken = firstToken;
firstToken.length = length;
firstToken.id = id;
}
else if(lastToken.next == null)
{
lastToken.next = new Token(length,id);
lastToken = lastToken.next;
}
else
{
lastToken = lastToken.next;
lastToken.length = length;
lastToken.id = id;
}
}
/**
* Inner class for storing information about tokenized lines.
*/
public class LineInfo
{
/**
* Creates a new LineInfo object with token = Token.NULL
* and obj = null.
*/
public LineInfo()
{
}
/**
* Creates a new LineInfo object with the specified
* parameters.
*/
public LineInfo(byte token, Object obj)
{
this.token = token;
this.obj = obj;
}
/**
* The id of the last token of the line.
*/
public byte token;
/**
* This is for use by the token marker implementations
* themselves. It can be used to store anything that
* is an object and that needs to exist on a per-line
* basis.
*/
public Object obj;
}
}

46
app/syntax/readme.txt Normal file
View File

@ -0,0 +1,46 @@
OLDSYNTAX PACKAGE README
I am placing the jEdit 2.2.1 syntax highlighting package in the public
domain. This means it can be integrated into commercial programs, etc.
This package requires at least Java 1.1 and Swing 1.1. Syntax
highlighting for the following file types is supported:
- C++, C
- CORBA IDL
- Eiffel
- HTML
- Java
- Java properties
- JavaScript
- MS-DOS INI
- MS-DOS batch files
- Makefile
- PHP
- Perl
- Python
- TeX
- Transact-SQL
- Unix patch/diff
- Unix shell script
- XML
This package is undocumented; read the source (start by taking a look at
JEditTextArea.java) to find out how to use it; it's really simple. Feel
free to e-mail questions, queries, etc. to me, but keep in mind that
this code is very old and I no longer maintain it. So if you find a bug,
don't bother me about it; fix it yourself.
* Copyright
The jEdit 2.2.1 syntax highlighting package contains code that is
Copyright 1998-1999 Slava Pestov, Artur Biesiadowski, Clancy Malcolm,
Jonathan Revusky, Juha Lindfors and Mike Dillon.
You may use and modify this package for any purpose. Redistribution is
permitted, in both source and binary form, provided that this notice
remains intact in all source distributions of this package.
-- Slava Pestov
25 September 2000
<sp@gjt.org>

1011
app/tools/AutoFormat.java Normal file

File diff suppressed because it is too large Load Diff

168
build/howto.txt Executable file
View File

@ -0,0 +1,168 @@
HOW TO BUILD PROCESSING ON YOUR FAVORITE PLATFORM
With frequently asked questions, scroll to the end of the file.
////////////////////////////////////////////////////////////////////
//// Steps for First Time Setup
1. INSTALL DEVELOPMENT TOOLS
1a. On Windows, install Cygwin. It's downloadable from
www.cygwin.com or specifically: www.cygwin.com/setup.exe
** of the packages, begin with the defaults, and add:
+ cvs - used for version control
+ make, gcc-mingw, and g++ - used to build processing.exe
(this will also pull in gcc-core)
+ perl - use this version, activestate or other distros have trouble
+ unzip, zip - for dealing with archives
+ included in the defaults, but make sure:
coreutils (or textutils), gzip, tar
+ not required but useful:
openssh - command line ssh client
nano - handy/simple text editor
** and be sure to leave the option selected for 'unix line endings'
the cygwin installer is sometimes a little flakey, so it may take more
than one try to get everything in there. in fact, it's often best to
run the installer once, and let it install all its defaults, then run
it again, and select the items above. it's also useful to run the
installer every few months to keep things fresh.
1b. On Mac OS X, install Apple's Developer Tools. Should work with
everything from the December 2002 Tools for Jaguar on OS X 10.2,
up through the more recent Xcode stuff.
1c. On Linux, you're pretty much on your own.. You need a pretty
standard development setup.
2. GRAB THE CODE FROM SOURCEFORGE
* this grabs the code as an anonymous user. if you have a sourceforge
account, you should know how to grab the code as yourself.
# first do this
cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/processing login
# just hit enter when it asks for a password
# then do this (may take a while)
cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/processing co processing
# (maybe even a long while for you dialup and international folks)
# gonna do a few things in the p5 folder
cd processing
# a quick cleanup, removes empty (dead/old) folders
cvs update -P
# get back to the processing/ folder
cd ..
3. INSTALL QUICKTIME FOR JAVA (windows users only)
* you'll also need to install quicktime for java. grab a quicktime
installer from: http://www.apple.com/quicktime/download/
don't try to be sly & use the itunes installer. we all love itunes
but it doesn't include quicktime for java by default. or if you were
sly, you might need to read the point below about using the updater:
* if you already have quicktime installed, just make sure that
quicktime for java has already been installed, use the quicktime
updater (Program Files -> QuickTime -> QuickTime Updater) hit the
'Details' button in the updater, and if it lists "Not installed"
for "QuickTime for Java", well, take care of that.
4. BUILD IT
# now to build for the first time:
cd /path/to/processing/build/windows
# or if you're on linux
cd /path/to/processing/build/linux
# for the followers of goatee man
cd /path/to/processing/build/macosx
# and then..
./make.sh
# if everything went well, you'll have no errors. (feel free to make
# suggestions for things to include here for common problems)
# then to run it
./run.sh
# each time you make a change, use make to build the thing
# and run to get it up and running.
////////////////////////////////////////////////////////////////////
//// Updating to the Latest Version
5a. Each time you want to update to latest version from cvs:
cd /path/to/processing
cvs -z3 update
# -z3 means make it snappy (using compression)
5b. If new folders have been added, or you're gettin odd errors, use:
# get to the processing folder
cd /path/to/processing
# remove the work directory
rm -rf work
# -d grabs new directories and -P cleans out old (empty) ones
# cvs is a little brain dead about this stuff
cvs -z3 update -d -P
Unfortunately there isn't a way to know (?) if new folders have
since been added. but if you're getting "class not found" errors
while building, then that's a good indicator that something is
missing from a subfolder.
If there have been significant changes, or you get weird build
errors, try deleting your 'work' folder. This will create a
fresh build. This includes any changes to the reference,
the examples, the libraries, jikes, or just about any time you
have to use -d -P with the update.
////////////////////////////////////////////////////////////////////
//// Frequently Asked Questions
- What about eclipse? What about ant? The command line is frightening
and gives me nightmares!
In a basic sense, the command line stuff isn't as scary as it might
seem. Hopefully it's just a matter of following the instructions
above (and being patient). If not, let us know where you have trouble
so we can fix things.
Conceivably, it wouldn't take much to make Processing build under
Eclipse or any other IDE, but we don't do it by default. Same goes for
ANT. We don't use it, but if someone were to make build scripts that
emulated everything that the current build scripts do (not just build
the code, but all the other annoying crap that the build scripts do)
then maybe we could switch to it. It's all about reaching some kind
of critical mass.

5
build/linux/.cvsignore Normal file
View File

@ -0,0 +1,5 @@
work
processing*

6
build/linux/CVS/Entries Normal file
View File

@ -0,0 +1,6 @@
/.cvsignore/1.3/Mon Jul 29 06:07:10 2002//
D/dist////
/run.sh/1.1/Wed Aug 6 02:42:46 2003//
/dist.sh/1.22/Tue Jun 7 13:00:22 2005//
/jre.tgz/1.4/Tue Jun 7 13:05:29 2005/-kb/
/make.sh/1.39/Tue Jun 7 13:05:30 2005//

View File

@ -0,0 +1 @@
/cvsroot/processing/processing/build/linux

1
build/linux/CVS/Root Normal file
View File

@ -0,0 +1 @@
:pserver:anonymous@cvs.sourceforge.net:/cvsroot/processing

72
build/linux/dist.sh Executable file
View File

@ -0,0 +1,72 @@
#!/bin/sh
REVISION=`head -c 4 ../../todo.txt`
./make.sh
echo Creating linux distribution for revision $REVISION...
# remove any old boogers
rm -rf processing
rm -rf processing-*
# use 'shared' files as starting point
cp -r ../shared processing
# add the libraries folder with source
cp -r ../../net processing/libraries/
cp -r ../../opengl processing/libraries/
cp -r ../../serial processing/libraries/
# new style examples thing ala reas
cd processing
unzip -q examples.zip
rm examples.zip
cd ..
cd processing
unzip -q reference.zip
rm reference.zip
cd ..
# add java (jre) files
#tar --extract --verbose --file=jre.tgz --ungzip --directory=processing
tar --extract --file=jre.tgz --ungzip --directory=processing
# directories used by the app
#mkdir processing/lib/build
# grab pde.jar and export from the working dir
cp work/lib/pde.jar processing/lib/
cp work/lib/core.jar processing/lib/
# get platform-specific goodies from the dist dir
install -m 755 dist/processing processing/processing
cp dist/jikes processing/
chmod +x processing/jikes
# make sure notes.txt is unix LFs
# the 2> is because the app is a little chatty
dos2unix processing/revisions.txt 2> /dev/null
dos2unix processing/lib/preferences.txt 2> /dev/null
# remove boogers
find processing -name "*~" -exec rm -f {} ';'
find processing -name ".DS_Store" -exec rm -f {} ';'
find processing -name "._*" -exec rm -f {} ';'
find processing -name "Thumbs.db" -exec rm -f {} ';'
# clean out the cvs entries
find processing -name "CVS" -exec rm -rf {} ';' 2> /dev/null
#find processing -name "CVS" -exec echo {} ';'
# zip it all up for release
echo Creating tarball and finishing...
P5=processing-$REVISION
mv processing $P5
tar cfz $P5.tgz $P5
# nah, keep the new directory around
#rm -rf $P5
#echo Done.

0
build/linux/dist/.cvsignore vendored Normal file
View File

4
build/linux/dist/CVS/Entries vendored Normal file
View File

@ -0,0 +1,4 @@
/.cvsignore/1.1/Thu Jul 25 19:51:59 2002//
D/lib////
/jikes/1.6/Tue Jun 7 13:05:39 2005/-kb/
/processing/1.7/Tue Jun 7 13:05:39 2005//

1
build/linux/dist/CVS/Repository vendored Normal file
View File

@ -0,0 +1 @@
/cvsroot/processing/processing/build/linux/dist

1
build/linux/dist/CVS/Root vendored Normal file
View File

@ -0,0 +1 @@
:pserver:anonymous@cvs.sourceforge.net:/cvsroot/processing

BIN
build/linux/dist/jikes vendored Executable file

Binary file not shown.

1
build/linux/dist/lib/CVS/Entries vendored Normal file
View File

@ -0,0 +1 @@
D

1
build/linux/dist/lib/CVS/Repository vendored Normal file
View File

@ -0,0 +1 @@
/cvsroot/processing/processing/build/linux/dist/lib

1
build/linux/dist/lib/CVS/Root vendored Normal file
View File

@ -0,0 +1 @@
:pserver:anonymous@cvs.sourceforge.net:/cvsroot/processing

47
build/linux/dist/processing vendored Executable file
View File

@ -0,0 +1,47 @@
#!/bin/sh
CLASSPATH=java/lib/rt.jar:lib:lib/build:lib/pde.jar:lib/core.jar:lib/antlr.jar:lib/oro.jar:lib/registry.jar:lib/mrj.jar
export CLASSPATH
# put the directory where this file lives in the front of the path, because
# that directory also contains jikes, which we will need at runtime.
#
PATH=`pwd`/`dirname $0`:`pwd`/java/bin:${PATH}
export PATH
# test to see if jikes is operable. i'm a crappy bash scripter
# so if someone knows a more elegant way to do this, let me know.
#
jikes -version 1> /dev/null 2> /dev/null
if [ $? == 0 ]
then
java processing.app.Base
else
echo
echo It appears that the version of Jikes distributed with Processing
echo cannot properly run on this system.
echo
echo Possible solutions:
echo
echo + If you already have Jikes installed on your system, you may
echo just need to remove the version that is included with Processing.
echo
echo + You probably just need to track down a version of Jikes that will
echo work with your distribution.
echo
echo + You may need to install the rpm/package for compat-libstdc++
echo This is what it takes to get things running on most versions
echo of RedHat Linux or Fedora Core.
echo
echo + If all else fails, or if you just like building stuff yourself,
echo you can download the source for Jikes from SourceForge:
echo http://sourceforge.net/project/showfiles.php?group_id=128803
echo And it just takes a simple ./configure and make, followed by
echo copying src/jikes to the processing-XXXX folder and you should
echo be all set.
echo
echo If you get stuck, ask questions online from the helpful folks via
echo the Processing discussion board: http://processing.org/discourse/
echo
echo Good luck!
fi

159
build/linux/make.sh Executable file
View File

@ -0,0 +1,159 @@
#!/bin/sh
### -- SETUP WORK DIR -------------------------------------------
if test -d work
then
BUILD_PREPROC=false
else
echo Setting up directories to build for linux...
BUILD_PREPROC=true
cp -r ../shared work
# needs to make the dir because of packaging goofiness
mkdir -p work/classes/processing/app/preproc
mkdir -p work/classes/processing/app/syntax
mkdir -p work/classes/processing/app/tools
#cp -r ../../lib work/libraries
cp -r ../../net work/libraries/
cp -r ../../opengl work/libraries/
cp -r ../../serial work/libraries/
cp -r ../../video work/libraries/
cd work
unzip -q examples.zip
rm examples.zip
cd ..
cd work
unzip -q reference.zip
rm reference.zip
cd ..
tar --extract --file=jre.tgz --ungzip --directory=work
#mkdir work/lib/export
mkdir work/lib/build
#mkdir work/classes
# get the serial stuff
#echo Copying serial support from bagel dir
#cp ../../bagel/serial/RXTXcomm.jar work/lib/
#mkdir work/lib/i386
#cp ../../bagel/serial/librxtxSerial.so work/lib/i386/libSerial.so
#chmod +x work/librxtxSerial.so
# get jikes and depedencies
cp dist/jikes work/
chmod +x work/jikes
install -m 755 dist/processing work/processing
fi
cd ../..
### -- BUILD CORE ----------------------------------------------
echo Building processing.core
# move to bagel inside base 'processing' directory
cd core
# new regular version
CLASSPATH="../build/linux/work/java/lib/rt.jar"
export CLASSPATH
perl preproc.pl
../build/linux/work/jikes -d . +D -target 1.1 *.java
zip -rq ../build/linux/work/lib/core.jar processing
rm -rf processing
# back to base processing dir
cd ..
### -- BUILD PREPROC ------------------------------------------------
echo Building PDE for JDK 1.3
cd app/preproc
# first build the default java goop
# long path is to avoid requiring java to be in your PATH
../../build/linux/work/java/bin/java \
-cp ../../build/linux/work/lib/antlr.jar antlr.Tool java.g
# now build the pde stuff that extends the java classes
../../build/linux/work/java/bin/java \
-cp ../../build/linux/work/lib/antlr.jar antlr.Tool -glib java.g pde.g
cd ../..
### -- BUILD PDE ------------------------------------------------
cd app
CLASSPATH="../build/linux/work/lib/core.jar:../build/linux/work/lib/mrj.jar:../build/linux/work/lib/antlr.jar:../build/linux/work/lib/oro.jar:../build/linux/work/lib/registry.jar:../build/linux/work/java/lib/rt.jar"
../build/linux/work/jikes -target 1.3 +D -classpath $CLASSPATH:../build/linux/work/classes -d ../build/linux/work/classes *.java preproc/*.java syntax/*.java tools/*.java
cd ../build/linux/work/classes
rm -f ../lib/pde.jar
zip -0rq ../lib/pde.jar .
cd ../../../..
### -- BUILD LIBRARIES ------------------------------------------------
cd build/linux
PLATFORM=linux
#CLASSPATH="../../build/linux/work/lib/core.jar:../../build/linux/work/java/lib/rt.jar"
CLASSPATH=../build/$PLATFORM/work/lib/core.jar:$CLASSPATH
JIKES=../build/$PLATFORM/work/jikes
CORE=../build/$PLATFORM/work/lib/core.jar
LIBRARIES=../build/$PLATFORM/work/libraries
# move to processing/build
cd ..
# SERIAL LIBRARY
echo Building serial library...
cd ../serial
$JIKES -target 1.1 +D -classpath "code/RXTXcomm.jar:$CORE:$CLASSPATH" -d . *.java
rm -f library/serial.jar
zip -r0q library/serial.jar processing
rm -rf processing
mkdir -p $LIBRARIES/serial/library/
cp library/serial.jar $LIBRARIES/serial/library/
# NET LIBRARY
echo Building net library...
cd ../net
$JIKES -target 1.1 +D -d . *.java
rm -f library/net.jar
zip -r0q library/net.jar processing
rm -rf processing
mkdir -p $LIBRARIES/net/library/
cp library/net.jar $LIBRARIES/net/library/
# OPENGL LIBRARY
echo Building OpenGL library...
cd ../opengl
$JIKES -target 1.1 +D -classpath "library/jogl.jar:$CLASSPATH" -d . *.java
rm -f library/opengl.jar
zip -r0q library/opengl.jar processing
rm -rf processing
mkdir -p $LIBRARIES/opengl/library/
cp library/opengl.jar $LIBRARIES/opengl/library/

3
build/linux/run.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
cd work && ./processing && cd ..

2
build/macosx/.cvsignore Normal file
View File

@ -0,0 +1,2 @@
work*
processing-0000-macosx

132
build/macosx/dist.sh Executable file
View File

@ -0,0 +1,132 @@
#!/bin/sh
# part of the arduino project http://arduino.berlios.de
# take from processing http://www.processing.org
# by Ben Fry, Casey Reas et al
#
# the power of open source
# prefers that fink is intalled, but not required
if test -f /sw/bin/head
then
# old 4 char version.. osx only uses the two chars
#REVISION=`head -c 4 ../../todo.txt`
# a more useful version of head than what's included with osx
SHORT_REVISION=`/sw/bin/head -c 4 ../../todo.txt | tail -c 2`
REVISION=`/sw/bin/head -c 4 ../../todo.txt`
VERSIONED=`cat ../../app/Base.java | grep $REVISION`
if [ -z "$VERSIONED" ]
then
echo Fix the revision number in Base.java
exit
fi
else
# can't get four bytes of head (osx doesn't support -c)
SHORT_REVISION=00
REVISION=0000
fi
./make.sh
echo Creating Arduino distribution for revision $REVISION...
# remove any old boogers
rm -rf arduino
rm -rf Arduino*
rm -rf arduino-*
mkdir arduino
# use 'shared' files as starting point
cp -r ../shared arduino
# add the libraries folder with source
#cp -r ../../lib arduino/libraries
# new style examples thing ala reas
# not there yet in arduino
# cd arduino
# unzip -q examples.zip
# rm examples.zip
# cd ..
# new style reference
# not there yet in arduino
# cd arduino
# unzip -q reference.zip
# rm reference.zip
# cd ..
# get ds_store file (!)
cp dist/DS_Store arduino/.DS_Store
# get package from the dist dir
cp -R dist/Arduino.app arduino/
chmod +x arduino/Arduino.app/Contents/MacOS/JavaApplicationStub
# put jar files into the resource dir, leave the rest in lib
RES=arduino/Arduino.app/Contents/Resources/Java
mkdir -p $RES
mv work/lib/*.jar $RES/
# directories used by the app
#mkdir arduino/lib/build
# grab pde.jar and export from the working dir
cp work/Arduino.app/Contents/Resources/Java/pde.jar $RES/
# removed dependecies from the processing core
#cp work/lib/core.jar arduino/lib/
# get platform-specific goodies from the dist dir
#cp `which jikes` arduino
#gunzip < dist/jikes.gz > arduino/jikes
# not needed in arduino
# cp dist/jikes arduino/
# chmod a+x arduino /jikes
chmod a+x arduino/Arduino.app/Contents/MacOS/JavaApplicationStub
#cd ../..
#javadoc -public -d doc app/*.java app/preproc/*.java app/syntax/*.java core/*.java opengl/*.java net/*.java video/*.java serial/*.java
#cd build/macosx
# remove boogers
find arduino -name "*~" -exec rm -f {} ';'
# need to leave ds store stuff cuz one of those is important
#find arduino -name ".DS_Store" -exec rm -f {} ';'
find arduino -name "._*" -exec rm -f {} ';'
find arduino -name "Thumbs.db" -exec rm -f {} ';'
# clean out the cvs entries
find arduino -name "CVS" -exec rm -rf {} ';' 2> /dev/null
find arduino -name ".cvsignore" -exec rm -rf {} ';'
mv arduino/Arduino.app "arduino/Arduino $SHORT_REVISION.app"
mv arduino arduino-$REVISION
# don't have deluxe on my laptop right now
#stuff -f sitx arduino-$REVISION
# zip it all up for release
#NICE_FOLDER="Arduino $SHORT_REVISION"
#DMG_NAME="arduino-$REVISION"
#mv arduino "$NICE_FOLDER"
#chmod +x mkdmg
#./mkdmg "$NICE_FOLDER" "Arduino"
#mv "$NICE_FOLDER.dmg" "$DMG_NAME.dmg"
# actually, could probably use:
# open arduino-uncomp.dmg
# rm -rf /Volumes/Arduino/Arduino*
# mv "Arduino $REVISION" /Volumes/Arduino
# umount /Volumes/Arduino
echo Done.

View File

@ -0,0 +1 @@
D/Contents////

View File

@ -0,0 +1 @@
/cvsroot/processing/processing/build/macosx/dist/Processing.app

View File

@ -0,0 +1 @@
:pserver:anonymous@cvs.sourceforge.net:/cvsroot/processing

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleAllowMixedLocalizations</key>
<string>true</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>arduino</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>arduino.icns</string>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>text/plain</string>
</array>
<key>CFBundleTypeName</key>
<string>Arduino Source File</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>TEXT</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>JavaApplicationStub</string>
<key>CFBundleIconFile</key>
<string>arduino.icns</string>
<key>CFBundleIdentifier</key>
<string>de.berlios.arduino</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Arduino</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>Pde1</string>
<key>CFBundleVersion</key>
<string>0.3</string>
<key>Java</key>
<dict>
<key>ClassPath</key>
<string>$JAVAROOT/pde.jar:lib/core.jar:$JAVAROOT/antlr.jar:$JAVAROOT/oro.jar:$JAVAROOT/registry.jar:lib/build:$JAVAROOT/RXTXcomm.jar</string>
<key>JVMVersion</key>
<string>1.4+</string>
<key>MainClass</key>
<string>processing.app.Base</string>
<key>Properties</key>
<dict>
<key>apple.awt.Antialiasing</key>
<string>true</string>
<key>apple.awt.TextAntialiasing</key>
<string>true</string>
<key>apple.awt.showGrowBox</key>
<string>true</string>
<key>apple.awt.use-file-dialog-packages</key>
<string>false</string>
<key>apple.laf.useScreenMenuBar</key>
<string>true</string>
<key>com.apple.hwaccel</key>
<string>true</string>
<key>com.apple.smallTabs</key>
<string>true</string>
</dict>
<key>VMOptions</key>
<string>-Xms128M -Xmx256M</string>
</dict>
</dict>
</plist>

Binary file not shown.

View File

@ -0,0 +1 @@
APPLsnip

View File

@ -0,0 +1,3 @@
D/Java////
/processing.icns/1.2/Sun Dec 5 00:45:14 2004/-kb/
/pde.icns/1.1/Sun Jan 30 03:41:54 2005/-kb/

View File

@ -0,0 +1 @@
/cvsroot/processing/processing/build/macosx/dist/Processing.app/Contents/Resources

View File

@ -0,0 +1 @@
:pserver:anonymous@cvs.sourceforge.net:/cvsroot/processing

View File

@ -0,0 +1 @@
D

View File

@ -0,0 +1 @@
/cvsroot/processing/processing/build/macosx/dist/Processing.app/Contents/Resources/Java

View File

@ -0,0 +1 @@
:pserver:anonymous@cvs.sourceforge.net:/cvsroot/processing

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More