diff --git a/app/Base.java b/app/Base.java
index ff53d2f9b..d646e00de 100644
--- a/app/Base.java
+++ b/app/Base.java
@@ -52,30 +52,63 @@ import com.ice.jni.registry.*;
* files and images, etc) that comes from that.
*/
public class Base {
- static final int VERSION = 1;
+ static final int VERSION = 3;
static final String VERSION_NAME = "0004 Alpha";
- static public int platform;
-
- // platform IDs for platform
+ // platform IDs for PApplet.platform
static final int WINDOWS = 1;
static final int MACOS9 = 2;
static final int MACOSX = 3;
static final int LINUX = 4;
static final int OTHER = 0;
-
-
- // moved from PApplet
- // in preperation of detaching the IDE from the
- // Arduino core classes
-
- /**
+
+ // used by split, all the standard whitespace chars
+ // (uncludes unicode nbsp, that little bostage)
+
+ static final String WHITESPACE = " \t\n\r\f\u00A0";
+
+ /**
+ * Path of filename opened on the command line,
+ * or via the MRJ open document handler.
+ */
+ static String openedAtStartup;
+
+ Editor editor;
+
+ /**
+ * "1.3" or "1.1" or whatever (just the first three chars)
+ */
+ public static final String javaVersionName =
+ System.getProperty("java.version").substring(0,3);
+
+ /**
+ * Version of Java that's in use, whether 1.1 or 1.3 or whatever,
+ * stored as a float.
+ *
+ * Note that because this is stored as a float, the values may
+ * not be exactly 1.3 or 1.4. Instead, make sure you're
+ * comparing against 1.3f or 1.4f, which will have the same amount
+ * of error (i.e. 1.40000001). This could just be a double, but
+ * since Processing only uses floats, it's safer to do this,
+ * because there's no good way to specify a double with the preproc.
+ */
+ public static final float javaVersion =
+ new Float(javaVersionName).floatValue();
+
+ /**
+ * Current platform in use, one of the
+ * PConstants WINDOWS, MACOSX, MACOS9, LINUX or OTHER.
+ */
+ static public int platform;
+
+ /**
* Current platform in use.
*
* Equivalent to System.getProperty("os.name"), just used internally.
*/
- static public String platformName = System.getProperty("os.name");
+ static public String platformName =
+ System.getProperty("os.name");
static {
// figure out which operating system
@@ -105,23 +138,6 @@ public class Base {
}
}
- // used by split, all the standard whitespace chars
- // (uncludes unicode nbsp, that little bostage)
-
- static final String WHITESPACE = " \t\n\r\f\u00A0";
-
-
-
-
- /**
- * Path of filename opened on the command line,
- * or via the MRJ open document handler.
- */
- static String openedAtStartup;
-
- Editor editor;
-
-
static public void main(String args[]) {
// make sure that this is running on java 1.4
@@ -179,6 +195,9 @@ public class Base {
e.printStackTrace();
}
+ // use native popups so they don't look so crappy on osx
+ JPopupMenu.setDefaultLightWeightPopupEnabled(false);
+
// build the editor object
editor = new Editor();
@@ -214,7 +233,6 @@ public class Base {
* returns true if running on windows.
*/
static public boolean isWindows() {
-
return platform == WINDOWS;
}
@@ -223,7 +241,6 @@ public class Base {
* true if running on linux.
*/
static public boolean isLinux() {
-
return platform == LINUX;
}
@@ -363,26 +380,40 @@ public class Base {
}
- static public File getBuildFolder() {
- String buildPath = Preferences.get("build.path");
- if (buildPath != null) return new File(buildPath);
+ static File buildFolder;
- File folder = new File(getTempFolder(), "build");
- if (!folder.exists()) folder.mkdirs();
- return folder;
+ static public File getBuildFolder() {
+ if (buildFolder == null) {
+ String buildPath = Preferences.get("build.path");
+ if (buildPath != null) {
+ buildFolder = new File(buildPath);
+
+ } else {
+ //File folder = new File(getTempFolder(), "build");
+ //if (!folder.exists()) folder.mkdirs();
+ buildFolder = createTempFolder("build");
+ buildFolder.deleteOnExit();
+ }
+ }
+ return buildFolder;
}
/**
* Get the path to the platform's temporary folder, by creating
* a temporary temporary file and getting its parent folder.
+ *
+ * Modified for revision 0094 to actually make the folder randomized
+ * to avoid conflicts in multi-user environments. (Bug 177)
*/
- static public File getTempFolder() {
+ static public File createTempFolder(String name) {
try {
- File ignored = File.createTempFile("ignored", null);
- String tempPath = ignored.getParent();
- ignored.delete();
- return new File(tempPath);
+ File folder = File.createTempFile(name, null);
+ //String tempPath = ignored.getParent();
+ //return new File(tempPath);
+ folder.delete();
+ folder.mkdirs();
+ return folder;
} catch (Exception e) {
e.printStackTrace();
@@ -548,6 +579,55 @@ public class Base {
// .................................................................
+ // someone needs to be slapped
+ //static KeyStroke closeWindowKeyStroke;
+
+ /**
+ * Return true if the key event was a Ctrl-W or an ESC,
+ * both indicators to close the window.
+ * Use as part of a keyPressed() event handler for frames.
+ */
+ /*
+ static public boolean isCloseWindowEvent(KeyEvent e) {
+ if (closeWindowKeyStroke == null) {
+ int modifiers = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
+ closeWindowKeyStroke = KeyStroke.getKeyStroke('W', modifiers);
+ }
+ return ((e.getKeyCode() == KeyEvent.VK_ESCAPE) ||
+ KeyStroke.getKeyStrokeForEvent(e).equals(closeWindowKeyStroke));
+ }
+ */
+
+
+ /**
+ * Registers key events for a Ctrl-W and ESC with an ActionListener
+ * that will take care of disposing the window.
+ */
+ static public void registerWindowCloseKeys(JRootPane root, //Window window,
+ ActionListener disposer) {
+ /*
+ JRootPane root = null;
+ if (window instanceof JFrame) {
+ root = ((JFrame)window).getRootPane();
+ } else if (window instanceof JDialog) {
+ root = ((JDialog)window).getRootPane();
+ }
+ */
+
+ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
+ root.registerKeyboardAction(disposer, stroke,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+
+ int modifiers = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
+ stroke = KeyStroke.getKeyStroke('W', modifiers);
+ root.registerKeyboardAction(disposer, stroke,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ }
+
+
+ // .................................................................
+
+
/**
* Given the reference filename from the keywords list,
* builds a URL and passes it to openURL.
@@ -858,7 +938,9 @@ public class Base {
static public void removeDir(File dir) {
if (dir.exists()) {
removeDescendants(dir);
- dir.delete();
+ if (!dir.delete()) {
+ System.err.println("Could not delete " + dir);
+ }
}
}
@@ -917,6 +999,46 @@ public class Base {
}
+ /**
+ * Gets a list of all files within the specified folder,
+ * and returns a list of their relative paths.
+ * Ignores any files/folders prefixed with a dot.
+ */
+ static public String[] listFiles(String path, boolean relative) {
+ return listFiles(new File(path), relative);
+ }
+
+ static public String[] listFiles(File folder, boolean relative) {
+ String path = folder.getAbsolutePath();
+ Vector vector = new Vector();
+ listFiles(relative ? (path + File.separator) : "", path, vector);
+ String outgoing[] = new String[vector.size()];
+ vector.copyInto(outgoing);
+ return outgoing;
+ }
+
+ static protected void listFiles(String basePath,
+ String path, Vector vector) {
+ File folder = new File(path);
+ String list[] = folder.list();
+ if (list == null) return;
+
+ for (int i = 0; i < list.length; i++) {
+ if (list[i].charAt(0) == '.') continue;
+
+ File file = new File(path, list[i]);
+ String newPath = file.getAbsolutePath();
+ if (newPath.startsWith(basePath)) {
+ newPath = newPath.substring(basePath.length());
+ }
+ vector.add(newPath);
+ if (file.isDirectory()) {
+ listFiles(basePath, newPath, vector);
+ }
+ }
+ }
+
+
/**
* Equivalent to the one in PApplet, but static (die() is removed)
*/
@@ -954,7 +1076,7 @@ public class Base {
return null;
}
- //////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////
// STRINGS
diff --git a/app/Compiler.java b/app/Compiler.java
index 72c959320..d7b627cc2 100644
--- a/app/Compiler.java
+++ b/app/Compiler.java
@@ -76,7 +76,9 @@ public class Compiler implements MessageConsumer {
String userdir = System.getProperty("user.dir") + File.separator;
- String baseCommandCompiler[] = new String[] {
+ LibraryManager libraryManager = new LibraryManager();
+
+ String preCommandCompiler[] = new String[] {
((!Base.isMacOS()) ? "tools/avr/bin/avr-gcc" :
userdir + "tools/avr/bin/avr-gcc"),
"-c", // compile, don't link
@@ -86,11 +88,19 @@ public class Compiler implements MessageConsumer {
"-w", // surpress all warnings
"-mmcu=" + Preferences.get("build.mcu"),
"-DF_CPU=" + Preferences.get("build.f_cpu"),
- " ",
- " "
};
- String baseCommandCompilerCPP[] = new String[] {
+ // use lib directories as include paths
+ String[] libDirs = libraryManager.getFolderPaths();
+
+ // Last two arguments will specify the file being compiled and the output file.
+ String[] baseCommandCompiler = new String[preCommandCompiler.length + libDirs.length + 2];
+ System.arraycopy(preCommandCompiler, 0, baseCommandCompiler, 0, preCommandCompiler.length);
+ for (int i = 0; i < libDirs.length; ++i) {
+ baseCommandCompiler[preCommandCompiler.length + i] = "-I" + libDirs[i];
+ }
+
+ String preCommandCompilerCPP[] = new String[] {
((!Base.isMacOS()) ? "tools/avr/bin/avr-g++" :
userdir + "tools/avr/bin/avr-g++"),
"-c", // compile, don't link
@@ -101,46 +111,33 @@ public class Compiler implements MessageConsumer {
"-fno-exceptions",
"-mmcu=" + Preferences.get("build.mcu"),
"-DF_CPU=" + Preferences.get("build.f_cpu"),
- " ",
- " "
};
- String baseCommandLinker[] = new String[] {
+ // use lib directories as include paths
+ // Last two arguments will specify the file being compiled and the output file.
+ String[] baseCommandCompilerCPP = new String[preCommandCompilerCPP.length + libDirs.length + 2];
+ System.arraycopy(preCommandCompilerCPP, 0, baseCommandCompilerCPP, 0, preCommandCompilerCPP.length);
+ for (int i = 0; i < libDirs.length; ++i) {
+ baseCommandCompilerCPP[preCommandCompilerCPP.length + i] = "-I" + libDirs[i];
+ }
+
+ String preCommandLinker[] = new String[] {
((!Base.isMacOS()) ? "tools/avr/bin/avr-gcc" :
userdir + "tools/avr/bin/avr-gcc"),
" ",
"-mmcu=" + Preferences.get("build.mcu"),
"-o",
" ",
-// ((!Base.isMacOS()) ? "" : userdir) + "lib/uart.o",
-// ((!Base.isMacOS()) ? "" : userdir) + "lib/buffer.o",
-// ((!Base.isMacOS()) ? "" : userdir) + "lib/timer.o",
-// ((!Base.isMacOS()) ? "" : userdir) + "lib/wiring.o",
-// ((!Base.isMacOS()) ? "" : userdir) + "lib/pins_arduino.o",
- //((!Base.isMacOS()) ? "lib/WApplet.o" :
- //userdir + "lib/WApplet.o"),
- //((!Base.isMacOS()) ? "lib/WSerial.o" :
- //userdir + "lib/WSerial.o"),
- //((!Base.isMacOS()) ? "lib/WTimer.o" :
- //userdir + "lib/WTimer.o"),
- //((!Base.isMacOS()) ? "lib/Servo.o" :
- //userdir + "lib/Servo.o"),
- ////((!Base.isMacOS()) ? "lib/Wire.o" :
- //// userdir + "lib/Wire.o"),
- ////((!Base.isMacOS()) ? "lib/WServo.o" :
- //// userdir + "lib/WServo.o"),
- //((!Base.isMacOS()) ? "lib/WDisplay.o" :
- //userdir + "lib/WDisplay.o"),
- //((!Base.isMacOS()) ? "lib/WEncoder.o" :
- //userdir + "lib/WEncoder.o"),
- //((!Base.isMacOS()) ? "lib/WInterrupts.o" :
- //userdir + "lib/WInterrupts.o"),
- //((!Base.isMacOS()) ? "lib/WCounter.o" :
- //userdir + "lib/WCounter.o"),
- //((!Base.isMacOS()) ? "tools/avr/avr/lib/libm.a" :
- //userdir + "tools/avr/avr/lib/libm.a")
};
+ // use lib object files during include
+ String[] libObjectFiles = libraryManager.getObjectFiles();
+ String[] baseCommandLinker = new String[preCommandLinker.length + libObjectFiles.length];
+ System.arraycopy(preCommandLinker, 0, baseCommandLinker, 0, preCommandLinker.length);
+ for (int i = 0; i < libObjectFiles.length; ++i) {
+ baseCommandLinker[preCommandLinker.length + i] = libObjectFiles[i];
+ }
+
String baseCommandObjcopy[] = new String[] {
((!Base.isMacOS()) ? "tools/avr/bin/avr-objcopy" :
userdir + "tools/avr/bin/avr-objcopy"),
@@ -295,8 +292,8 @@ public class Compiler implements MessageConsumer {
Process process;
boolean compiling = true;
for(int i = 0; i < fileCount; i++) {
- baseCommandCompiler[8] = sourceNames[i];
- baseCommandCompiler[9] = "-o"+ objectNames[i];
+ baseCommandCompiler[baseCommandCompiler.length - 2] = sourceNames[i];
+ baseCommandCompiler[baseCommandCompiler.length - 1] = "-o"+ objectNames[i];
//System.arraycopy(baseCommandCompiler.length
//for(int j = 0; j < baseCommandCompiler.length; j++) {
// System.out.println(baseCommandCompiler[j]);
@@ -325,8 +322,8 @@ public class Compiler implements MessageConsumer {
}
for(int i = 0; i < fileCountCPP; i++) {
- baseCommandCompilerCPP[9] = sourceNamesCPP[i];
- baseCommandCompilerCPP[10] = "-o"+ objectNamesCPP[i];
+ baseCommandCompilerCPP[baseCommandCompilerCPP.length - 2] = sourceNamesCPP[i];
+ baseCommandCompilerCPP[baseCommandCompilerCPP.length - 1] = "-o"+ objectNamesCPP[i];
//for(int j = 0; j < baseCommandCompilerCPP.length; j++) {
// System.out.println(baseCommandCompilerCPP[j]);
//}
diff --git a/app/Editor.java b/app/Editor.java
index 8cd23024a..09065a9a6 100644
--- a/app/Editor.java
+++ b/app/Editor.java
@@ -30,6 +30,8 @@ import processing.app.syntax.*;
import processing.app.tools.*;
import java.awt.*;
+import java.awt.datatransfer.*;
+import java.awt.dnd.*;
import java.awt.event.*;
import java.io.*;
import java.lang.reflect.*;
@@ -46,10 +48,8 @@ import javax.swing.undo.*;
import com.oroinc.text.regex.*;
import com.apple.mrj.*;
-
import gnu.io.*;
-
public class Editor extends JFrame
implements MRJAboutHandler, MRJQuitHandler, MRJPrefsHandler,
MRJOpenDocumentHandler //, MRJOpenApplicationHandler
@@ -101,27 +101,27 @@ public class Editor extends JFrame
//Point presentLocation;
//Window presentationWindow;
RunButtonWatcher watcher;
- Runner runtime;
+ //Runner runtime;
- //JMenuItem exportAppItem;
+ JMenuItem exportAppItem;
JMenuItem saveMenuItem;
JMenuItem saveAsMenuItem;
-
- //ButtonGroup serialGroup;
- JMenu serialSubMenu;
- JMenu serialRateSubMenu;
+ JMenu serialMenu;
+ JMenu serialRateMenu;
SerialMenuListener serialMenuListener;
- //
- boolean debugging;
boolean running;
boolean presenting;
+ boolean debugging;
// undo fellers
JMenuItem undoItem, redoItem;
protected UndoAction undoAction;
protected RedoAction redoAction;
UndoManager undo;
+ // used internally, and only briefly
+ CompoundEdit compoundEdit;
+
//static public UndoManager undo = new UndoManager(); // editor needs this guy
//
@@ -129,7 +129,7 @@ public class Editor extends JFrame
//SketchHistory history; // TODO re-enable history
Sketchbook sketchbook;
//Preferences preferences;
- FindReplace find;
+ //FindReplace find;
//static Properties keywords; // keyword -> reference html lookup
@@ -175,7 +175,7 @@ public class Editor extends JFrame
setJMenuBar(menubar);
// doesn't matter when this is created, just make it happen at some point
- find = new FindReplace(Editor.this);
+ //find = new FindReplace(Editor.this);
Container pain = getContentPane();
pain.setLayout(new BorderLayout());
@@ -240,28 +240,87 @@ public class Editor extends JFrame
listener = new EditorListener(this, textarea);
pain.add(box);
- /*
- // set the undo stuff for this feller
- Document document = textarea.getDocument();
- //document.addUndoableEditListener(new PdeUndoableEditListener());
- document.addUndoableEditListener(new UndoableEditListener() {
- public void undoableEditHappened(UndoableEditEvent e) {
- if (undo != null) {
- //System.out.println(e.getEdit());
- undo.addEdit(e.getEdit());
- undoAction.updateUndoState();
- redoAction.updateRedoState();
+ DropTarget dt = new DropTarget(this, new DropTargetListener() {
+
+ public void dragEnter(DropTargetDragEvent event) {
+ // debug messages for diagnostics
+ //System.out.println("dragEnter " + event);
+ event.acceptDrag(DnDConstants.ACTION_COPY);
+ }
+
+ public void dragExit(DropTargetEvent event) {
+ //System.out.println("dragExit " + event);
+ }
+
+ public void dragOver(DropTargetDragEvent event) {
+ //System.out.println("dragOver " + event);
+ event.acceptDrag(DnDConstants.ACTION_COPY);
+ }
+
+ public void dropActionChanged(DropTargetDragEvent event) {
+ //System.out.println("dropActionChanged " + event);
+ }
+
+ public void drop(DropTargetDropEvent event) {
+ //System.out.println("drop " + event);
+ event.acceptDrop(DnDConstants.ACTION_COPY);
+
+ Transferable transferable = event.getTransferable();
+ DataFlavor flavors[] = transferable.getTransferDataFlavors();
+ int successful = 0;
+
+ for (int i = 0; i < flavors.length; i++) {
+ try {
+ //System.out.println(flavors[i]);
+ //System.out.println(transferable.getTransferData(flavors[i]));
+ java.util.List list =
+ (java.util.List) transferable.getTransferData(flavors[i]);
+ for (int j = 0; j < list.size(); j++) {
+ Object item = list.get(j);
+ if (item instanceof File) {
+ File file = (File) item;
+
+ // see if this is a .pde file to be opened
+ String filename = file.getName();
+ if (filename.endsWith(".pde")) {
+ String name = filename.substring(0, filename.length() - 4);
+ File parent = file.getParentFile();
+ if (name.equals(parent.getName())) {
+ handleOpenFile(file);
+ return;
+ }
+ }
+
+ if (sketch.addFile(file)) {
+ successful++;
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (successful == 0) {
+ error("No files were added to the sketch.");
+
+ } else if (successful == 1) {
+ message("One file added to the sketch.");
+
+ } else {
+ message(successful + " files added to the sketch.");
}
}
});
- */
}
/**
- * Hack for #@#)$(* Mac OS X.
- * This appears to only be required on OS X 10.2, and this code
- * isn't even being hit on OS X 10.3 or Windows.
+ * Hack for #@#)$(* Mac OS X 10.2.
+ *
+ * This appears to only be required on OS X 10.2, and is not
+ * even being called on later versions of OS X or Windows.
*/
public Dimension getMinimumSize() {
//System.out.println("getting minimum size");
@@ -435,47 +494,19 @@ public class Editor extends JFrame
JMenuItem item;
JMenu menu = new JMenu("File");
- /*
- menu.add(item = new JMenuItem("do the editor thing"));
+ item = newJMenuItem("New", 'N');
item.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- textarea.getPainter().setFont(new Font("Courier", Font.PLAIN, 36));
- }
- });
- */
-
- if (!Preferences.getBoolean("export.library")) {
- item = newJMenuItem("New", 'N');
- item.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- handleNew(false);
- }
- });
- menu.add(item);
-
- } else {
- item = newJMenuItem("New Sketch", 'N');
- item.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- handleNew(false);
- }
- });
- menu.add(item);
-
- item = new JMenuItem("New Library");
- item.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- handleNewLibrary();
- }
- });
- menu.add(item);
- }
+ public void actionPerformed(ActionEvent e) {
+ handleNew(false);
+ }
+ });
+ menu.add(item);
menu.add(sketchbook.getOpenMenu());
saveMenuItem = newJMenuItem("Save", 'S');
saveMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
- handleSave();
+ handleSave(false);
}
});
menu.add(saveMenuItem);
@@ -496,14 +527,18 @@ public class Editor extends JFrame
});
menu.add(item);
-// exportAppItem = newJMenuItem("Export Application", 'E', true);
-// exportAppItem.addActionListener(new ActionListener() {
-// public void actionPerformed(ActionEvent e) {
-// handleExportApp();
-// }
-// });
-// menu.add(exportAppItem);
-
+ /*exportAppItem = newJMenuItem("Export Application", 'E', true);
+ exportAppItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ //buttons.activate(EditorButtons.EXPORT);
+ //SwingUtilities.invokeLater(new Runnable() {
+ //public void run() {
+ handleExportApplication();
+ //}});
+ }
+ });
+ menu.add(exportAppItem);
+ */
menu.addSeparator();
item = newJMenuItem("Page Setup", 'P', true);
@@ -552,14 +587,14 @@ public class Editor extends JFrame
});
menu.add(item);
- //item = newJMenuItem("Present", 'R', true);
- //item.addActionListener(new ActionListener() {
- // public void actionPerformed(ActionEvent e) {
- // handleRun(true);
- // }
- // });
- //menu.add(item);
-
+ /*item = newJMenuItem("Present", 'R', true);
+ item.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ handleRun(true);
+ }
+ });
+ menu.add(item);
+ */
item = new JMenuItem("Stop");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
@@ -583,7 +618,7 @@ public class Editor extends JFrame
if (Base.isWindows() || Base.isMacOS()) {
// no way to do an 'open in file browser' on other platforms
// since there isn't any sort of standard
- item = new JMenuItem("Show Sketch Folder");
+ item = newJMenuItem("Show Sketch Folder", 'K', false);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//Base.openFolder(sketchDir);
@@ -599,123 +634,79 @@ public class Editor extends JFrame
}
- // taken from an ancient version of processing
- class SerialMenuListener implements ActionListener {
-
- SerialMenuListener() {}
-
- public void actionPerformed(ActionEvent actionevent) {
- int count = serialSubMenu.getItemCount();
- Object to;
-
- for (int i = 0; i < count; i++) {
- to = serialSubMenu.getItem(i);
- if ( to instanceof JCheckBoxMenuItem) ((JCheckBoxMenuItem)serialSubMenu.getItem(i)).setState(false);
- }
-
- JCheckBoxMenuItem item = (JCheckBoxMenuItem)actionevent.getSource();
- item.setState(true);
- String name = item.getLabel();
-
- Preferences.set("serial.port", name);
- //System.out.println("port set to " + name);
- }
-
-
- }
-
- // manages the serial port speed menu
- class SerialRateMenuListener implements ActionListener {
-
- SerialRateMenuListener() {}
-
- public void actionPerformed(ActionEvent actionevent) {
- int count = serialRateSubMenu.getItemCount();
- Object to;
-
- for (int i = 0; i < count; i++) {
- to = serialRateSubMenu.getItem(i);
- if ( to instanceof JCheckBoxMenuItem) ((JCheckBoxMenuItem)serialRateSubMenu.getItem(i)).setState(false);
- }
-
- JCheckBoxMenuItem item = (JCheckBoxMenuItem)actionevent.getSource();
- item.setState(true);
- String name = item.getLabel();
-
- Preferences.set("serial.debug_rate", name);
- //System.out.println("serial port speed set to " + name);
- }
-
-
- }
-
-
-
-
protected JMenu buildToolsMenu() {
- JMenuItem item;
- JMenuItem rbMenuItem;
- JMenuItem cbMenuItem;
- SerialRateMenuListener srml = new SerialRateMenuListener();
- String[] portRates = {
+ JMenuItem item;
+ JMenuItem rbMenuItem;
+ JMenuItem cbMenuItem;
+ SerialRateMenuListener srml = new SerialRateMenuListener();
+ String[] portRates = {
"300","1200","2400","4800","9600","14400",
"19200","28800","38400","57600","115200"
};
- serialMenuListener = new SerialMenuListener();
+ serialMenuListener = new SerialMenuListener();
JMenu menu = new JMenu("Tools");
item = newJMenuItem("Auto Format", 'T', false);
item.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
+ synchronized public void actionPerformed(ActionEvent e) {
new AutoFormat(Editor.this).show();
-
+ //handleBeautify();
}
});
menu.add(item);
- menu.addSeparator();
-
- // The serial options
-
- serialSubMenu = new JMenu("Serial Port");
-// item = newJMenuItem("Update List", 'E', false);
-// item.addActionListener(new ActionListener() {
-// public void actionPerformed(ActionEvent e) {
-// // if (debug) displayResult("Serial Port List Updated");
-// //updateSerial();
-// }
-// });
+ /*item = new JMenuItem("Create Font...");
+ item.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ //new CreateFont().show(sketch.dataFolder);
+ new CreateFont(Editor.this).show();
+ }
+ });
+ menu.add(item);
+ */
+
+ item = new JMenuItem("Archive Sketch");
+ item.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ new Archiver(Editor.this).show();
+ //Archiver archiver = new Archiver();
+ //archiver.setup(Editor.this);
+ //archiver.show();
+ }
+ });
+ menu.add(item);
- //serialGroup = new ButtonGroup();
- populateSerialMenu();
- menu.add(serialSubMenu);
+ item = new JMenuItem("Export Folder...");
+ item.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ new ExportFolder(Editor.this).show();
+ }
+ });
+ menu.add(item);
+ menu.addSeparator();
- // End of The serial options
+ serialMenu = new JMenu("Serial Port");
+ populateSerialMenu();
+ menu.add(serialMenu);
+
+ serialRateMenu = new JMenu("Serial Monitor Baud Rate");
- // menu.addSeparator();
-
- // add the serial speed submenu
-
- serialRateSubMenu = new JMenu("Serial Monitor Baud Rate");
-
- //serialSubMenu.add(item);
- //serialSubMenu.addSeparator();
- ButtonGroup group = new ButtonGroup();
+ ButtonGroup group = new ButtonGroup();
- String curr_rate = Preferences.get("serial.debug_rate");
+ String curr_rate = Preferences.get("serial.debug_rate");
for (int i = 0; i < portRates.length; i++) {
rbMenuItem = new JCheckBoxMenuItem(portRates[i], portRates[i].equals(curr_rate));
rbMenuItem.addActionListener(srml);
group.add(rbMenuItem);
- serialRateSubMenu.add(rbMenuItem);
+ serialRateMenu.add(rbMenuItem);
}
- menu.add(serialRateSubMenu);
+ menu.add(serialRateMenu);
- menu.addSeparator();
+ menu.addSeparator();
item = new JMenuItem("Burn Bootloader");
item.addActionListener(new ActionListener() {
@@ -739,49 +730,114 @@ public class Editor extends JFrame
public void menuCanceled(MenuEvent e) {}
public void menuDeselected(MenuEvent e) {}
public void menuSelected(MenuEvent e) {
+ //System.out.println("Tools menu selected.");
populateSerialMenu();
}
});
return menu;
}
+
+ // taken from an ancient version of processing
+ class SerialMenuListener implements ActionListener {
+ //public SerialMenuListener() { }
+
+ public void actionPerformed(ActionEvent e) {
+ if(serialMenu == null) {
+ System.out.println("serialMenu is null");
+ return;
+ }
+ int count = serialMenu.getItemCount();
+ for (int i = 0; i < count; i++) {
+ ((JCheckBoxMenuItem)serialMenu.getItem(i)).setState(false);
+ }
+
+ JCheckBoxMenuItem item = (JCheckBoxMenuItem)e.getSource();
+ item.setState(true);
+ String name = item.getLabel();
+
+ //System.out.println(item.getLabel());
+ Preferences.set("serial.port", name);
+ //System.out.println("set to " + get("serial.port"));
+ }
+
+ /*
+ public void actionPerformed(ActionEvent e) {
+ System.out.println(e.getSource());
+ String name = e.getActionCommand();
+ PdeBase.properties.put("serial.port", name);
+ System.out.println("set to " + get("serial.port"));
+ //editor.skOpen(path + File.separator + name, name);
+ // need to push "serial.port" into PdeBase.properties
+ }
+ */
+ }
+
+ // manages the serial port speed menu
+ class SerialRateMenuListener implements ActionListener {
+
+ SerialRateMenuListener() {}
+
+ public void actionPerformed(ActionEvent actionevent) {
+ int count = serialRateMenu.getItemCount();
+ Object to;
+
+ for (int i = 0; i < count; i++) {
+ to = serialRateMenu.getItem(i);
+ if ( to instanceof JCheckBoxMenuItem) ((JCheckBoxMenuItem)serialRateMenu.getItem(i)).setState(false);
+ }
+
+ JCheckBoxMenuItem item = (JCheckBoxMenuItem)actionevent.getSource();
+ item.setState(true);
+ String name = item.getLabel();
+
+ Preferences.set("serial.debug_rate", name);
+ //System.out.println("serial port speed set to " + name);
+ }
+
+
+ }
protected void populateSerialMenu() {
- // getting list of ports
+ // getting list of ports
JMenuItem rbMenuItem;
- serialSubMenu.removeAll();
-
- try
- {
- for (Enumeration enumeration = CommPortIdentifier.getPortIdentifiers(); enumeration.hasMoreElements();)
- {
- CommPortIdentifier commportidentifier = (CommPortIdentifier)enumeration.nextElement();
- if (commportidentifier.getPortType() == CommPortIdentifier.PORT_SERIAL)
- {
- String curr_port = commportidentifier.getName();
- rbMenuItem = new JCheckBoxMenuItem(curr_port, curr_port.equals(Preferences.get("serial.port")));
- rbMenuItem.addActionListener(serialMenuListener);
- //serialGroup.add(rbMenuItem);
- serialSubMenu.add(rbMenuItem);
- }
- }
-
- }
-
- catch (Exception exception)
- {
- System.out.println("error retrieving port list");
- exception.printStackTrace();
- }
+ //System.out.println("Clearing serial port menu.");
- if (serialSubMenu.getItemCount() == 0) {
- serialSubMenu.setEnabled(false);
- }
+ serialMenu.removeAll();
- //serialSubMenu.addSeparator();
- //serialSubMenu.add(item);
+ try
+ {
+ for (Enumeration enumeration = CommPortIdentifier.getPortIdentifiers(); enumeration.hasMoreElements();)
+ {
+ CommPortIdentifier commportidentifier = (CommPortIdentifier)enumeration.nextElement();
+ //System.out.println("Found communication port: " + commportidentifier);
+ if (commportidentifier.getPortType() == CommPortIdentifier.PORT_SERIAL)
+ {
+ //System.out.println("Adding port to serial port menu: " + commportidentifier);
+ String curr_port = commportidentifier.getName();
+ rbMenuItem = new JCheckBoxMenuItem(curr_port, curr_port.equals(Preferences.get("serial.port")));
+ rbMenuItem.addActionListener(serialMenuListener);
+ //serialGroup.add(rbMenuItem);
+ serialMenu.add(rbMenuItem);
+ }
+ }
+
+ }
+
+ catch (Exception exception)
+ {
+ System.out.println("error retrieving port list");
+ exception.printStackTrace();
+ }
+
+ if (serialMenu.getItemCount() == 0) {
+ serialMenu.setEnabled(false);
+ }
+
+ //serialMenu.addSeparator();
+ //serialMenu.add(item);
}
@@ -882,7 +938,7 @@ public class Editor extends JFrame
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textarea.cut();
- sketch.setModified();
+ sketch.setModified(true);
}
});
menu.add(item);
@@ -899,7 +955,7 @@ public class Editor extends JFrame
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textarea.paste();
- sketch.setModified();
+ sketch.setModified(true);
}
});
menu.add(item);
@@ -917,7 +973,9 @@ public class Editor extends JFrame
item = newJMenuItem("Find...", 'F');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
- find.show();
+ new FindReplace(Editor.this).show();
+ //find.show();
+ //find.setVisible(true);
}
});
menu.add(item);
@@ -927,6 +985,8 @@ public class Editor extends JFrame
public void actionPerformed(ActionEvent e) {
// TODO find next should only be enabled after a
// search has actually taken place
+ //find.find(true);
+ FindReplace find = new FindReplace(Editor.this); //.show();
find.find(true);
}
});
@@ -986,11 +1046,17 @@ public class Editor extends JFrame
undoItem.setEnabled(true);
undoItem.setText(undo.getUndoPresentationName());
putValue(Action.NAME, undo.getUndoPresentationName());
+ if (sketch != null) {
+ sketch.setModified(true); // 0107
+ }
} else {
this.setEnabled(false);
undoItem.setEnabled(false);
undoItem.setText("Undo");
putValue(Action.NAME, "Undo");
+ if (sketch != null) {
+ sketch.setModified(false); // 0107
+ }
}
}
}
@@ -1015,7 +1081,7 @@ public class Editor extends JFrame
protected void updateRedoState() {
if (undo.canRedo()) {
- this.setEnabled(true);
+ //this.setEnabled(true);
redoItem.setEnabled(true);
redoItem.setText(undo.getRedoPresentationName());
putValue(Action.NAME, undo.getRedoPresentationName());
@@ -1095,64 +1161,24 @@ public class Editor extends JFrame
}
- /**
- * Called to update the text but not switch to a different
- * set of code (which would affect the undo manager).
- */
- //public void setText(String what) { //, boolean discardUndo) {
- //setText(what, 0, 0);
- //}
-
-
/**
* Called to update the text but not switch to a different
* set of code (which would affect the undo manager).
*/
public void setText(String what, int selectionStart, int selectionEnd) {
+ beginCompoundEdit();
textarea.setText(what);
+ endCompoundEdit();
+
+ // make sure that a tool isn't asking for a bad location
+ selectionStart =
+ Math.max(0, Math.min(selectionStart, textarea.getDocumentLength()));
+ selectionEnd =
+ Math.max(0, Math.min(selectionStart, textarea.getDocumentLength()));
textarea.select(selectionStart, selectionEnd);
- textarea.requestFocus(); // get the caret blinking
- }
-
-
- /**
- * Called by Sketch when the tab is changed or a new set of files are opened.
- */
- /*
- public void setText(String currentProgram,
- int selectionStart, int selectionEnd,
- UndoManager currentUndo) {
- //System.out.println("setting text, changing undo");
- this.undo = null;
-
- //if (discardUndo) undo.discardAllEdits();
-
- // don't set the undo object yet otherwise gets hokey
- textarea.setText(currentProgram);
- textarea.select(selectionStart, selectionEnd);
- textarea.requestFocus(); // get the caret blinking
-
- this.undo = currentUndo;
- undoAction.updateUndoState();
- redoAction.updateRedoState();
- }
- */
-
- /*
- public void setDocument(SyntaxDocument document,
- int selectionStart, int selectionStop,
- int scrollPosition, UndoManager undo) {
-
- textarea.setDocument(document, selectionStart, selectionStop,
- scrollPosition);
textarea.requestFocus(); // get the caret blinking
-
- this.undo = undo;
- undoAction.updateUndoState();
- redoAction.updateRedoState();
}
- */
/**
@@ -1179,7 +1205,10 @@ public class Editor extends JFrame
// connect the undo listener to the editor
code.document.addUndoableEditListener(new UndoableEditListener() {
public void undoableEditHappened(UndoableEditEvent e) {
- if (undo != null) {
+ if (compoundEdit != null) {
+ compoundEdit.addEdit(e.getEdit());
+
+ } else if (undo != null) {
undo.addEdit(e.getEdit());
undoAction.updateUndoState();
redoAction.updateRedoState();
@@ -1200,12 +1229,24 @@ public class Editor extends JFrame
redoAction.updateRedoState();
}
+ public void beginCompoundEdit() {
+ compoundEdit = new CompoundEdit();
+ }
+
+ public void endCompoundEdit() {
+ compoundEdit.end();
+ undo.addEdit(compoundEdit);
+ undoAction.updateUndoState();
+ redoAction.updateRedoState();
+ compoundEdit = null;
+ }
+
public void handleRun(boolean present) {
doClose();
running = true;
- buttons.run();
+ buttons.activate(EditorButtons.RUN);
message("Compiling...");
// do this for the terminal window / dos prompt / etc
for (int i = 0; i < 10; i++) System.out.println();
@@ -1291,17 +1332,15 @@ public class Editor extends JFrame
}
public void run() {
- /*
while (Thread.currentThread() == thread) {
- if (runtime == null) {
+ /*if (runtime == null) {
stop();
} else {
- // FIXME remove dependance from core libs
- //if (runtime.applet != null) {
- // if (runtime.applet.finished) {
- // stop();
- //}
+ if (runtime.applet != null) {
+ if (runtime.applet.finished) {
+ stop();
+ }
//buttons.running(!runtime.applet.finished);
} else if (runtime.process != null) {
@@ -1310,13 +1349,12 @@ public class Editor extends JFrame
} else {
stop();
}
- }
+ }*/
try {
Thread.sleep(250);
} catch (InterruptedException e) { }
//System.out.println("still inside runner thread");
}
- */
}
public void stop() {
@@ -1329,6 +1367,7 @@ public class Editor extends JFrame
public void handleSerial() {
if (!debugging) {
console.clear();
+ buttons.activate(EditorButtons.SERIAL);
serialPort = new Serial(true);
debugging = true;
} else {
@@ -1343,6 +1382,7 @@ public class Editor extends JFrame
} else {
doStop();
}
+ buttons.clear();
}
@@ -1350,11 +1390,11 @@ public class Editor extends JFrame
* Stop the applet but don't kill its window.
*/
public void doStop() {
+ //if (runtime != null) runtime.stop();
if (debugging) {
serialPort.dispose();
debugging = false;
}
- if (runtime != null) runtime.stop();
if (watcher != null) watcher.stop();
message(EMPTY);
@@ -1371,32 +1411,31 @@ public class Editor extends JFrame
* mode, this will always be called instead of doStop().
*/
public void doClose() {
- /*
//if (presenting) {
//presentationWindow.hide();
//} else {
- try {
+ //try {
// the window will also be null the process was running
// externally. so don't even try setting if window is null
// since Runner will set the appletLocation when an
// external process is in use.
- //if (runtime.window != null) {
- appletLocation = runtime.window.getLocation();
- }
- } catch (NullPointerException e) { }
+// if (runtime.window != null) {
+// appletLocation = runtime.window.getLocation();
+// }
+ //} catch (NullPointerException e) { }
//}
//if (running) doStop();
doStop(); // need to stop if runtime error
- try {
- if (runtime != null) {
+ //try {
+ /*if (runtime != null) {
runtime.close(); // kills the window
runtime = null; // will this help?
- }
- } catch (Exception e) { }
+ }*/
+ //} catch (Exception e) { }
//buttons.clear(); // done by doStop
- */
+
sketch.cleanup();
// [toxi 030903]
@@ -1450,7 +1489,7 @@ public class Editor extends JFrame
options[0]);
if (result == JOptionPane.YES_OPTION) {
- handleSave();
+ handleSave(true);
checkModified2();
} else if (result == JOptionPane.NO_OPTION) {
@@ -1480,15 +1519,20 @@ public class Editor extends JFrame
/**
* New was called (by buttons or by menu), first check modified
* and if things work out ok, handleNew2() will be called.
- *
+ *
* If shift is pressed when clicking the toolbar button, then
* force the opposite behavior from sketchbook.prompt's setting
*/
- public void handleNew(boolean shift) {
- doStop();
- handleNewShift = shift;
- handleNewLibrary = false;
- checkModified(HANDLE_NEW);
+ public void handleNew(final boolean shift) {
+ buttons.activate(EditorButtons.NEW);
+
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ doStop();
+ handleNewShift = shift;
+ handleNewLibrary = false;
+ checkModified(HANDLE_NEW);
+ }});
}
@@ -1539,6 +1583,7 @@ public class Editor extends JFrame
"An error occurred while creating\n" +
"a new sketch. Arduino must now quit.", e);
}
+ buttons.clear();
}
@@ -1556,15 +1601,22 @@ public class Editor extends JFrame
* Open a sketch given the full path to the .pde file.
* Pass in 'null' to prompt the user for the name of the sketch.
*/
- public void handleOpen(String path) {
- if (path == null) { // "open..." selected from the menu
- path = sketchbook.handleOpen();
- if (path == null) return;
- }
- doClose();
- //doStop();
- handleOpenPath = path;
- checkModified(HANDLE_OPEN);
+ public void handleOpen(final String ipath) {
+ // haven't run across a case where i can verify that this works
+ // because open is usually very fast.
+ buttons.activate(EditorButtons.OPEN);
+
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ String path = ipath;
+ if (path == null) { // "open..." selected from the menu
+ path = sketchbook.handleOpen();
+ if (path == null) return;
+ }
+ doClose();
+ handleOpenPath = path;
+ checkModified(HANDLE_OPEN);
+ }});
}
@@ -1692,7 +1744,32 @@ public class Editor extends JFrame
// there is no handleSave1 since there's never a need to prompt
- public void handleSave() {
+ /**
+ * Actually handle the save command. If 'force' is set to false,
+ * this will happen in another thread so that the message area
+ * will update and the save button will stay highlighted while the
+ * save is happening. If 'force' is true, then it will happen
+ * immediately. This is used during a quit, because invokeLater()
+ * won't run properly while a quit is happening. This fixes
+ * Bug 276.
+ */
+ public void handleSave(boolean force) {
+ doStop();
+ buttons.activate(EditorButtons.SAVE);
+
+ if (force) {
+ handleSave2();
+ } else {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ handleSave2();
+ }
+ });
+ }
+ }
+
+
+ public void handleSave2() {
message("Saving...");
try {
if (sketch.save()) {
@@ -1718,21 +1795,24 @@ public class Editor extends JFrame
public void handleSaveAs() {
doStop();
+ buttons.activate(EditorButtons.SAVE);
- message("Saving...");
- try {
- if (sketch.saveAs()) {
- message("Done Saving.");
- sketchbook.rebuildMenus();
- } else {
- message("Save Cancelled.");
- }
-
- } catch (Exception e) {
- // show the error as a message in the window
- error(e);
- }
- buttons.clear();
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ message("Saving...");
+ try {
+ if (sketch.saveAs()) {
+ message("Done Saving.");
+ sketchbook.rebuildMenus();
+ } else {
+ message("Save Cancelled.");
+ }
+ } catch (Exception e) {
+ // show the error as a message in the window
+ error(e);
+ }
+ buttons.clear();
+ }});
}
@@ -1746,37 +1826,86 @@ public class Editor extends JFrame
synchronized public void handleExport() {
if(debugging)
doStop();
+ buttons.activate(EditorButtons.EXPORT);
console.clear();
//String what = sketch.isLibrary() ? "Applet" : "Library";
//message("Exporting " + what + "...");
message("Uploading to I/O Board...");
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ //boolean success = sketch.isLibrary() ?
+ //sketch.exportLibrary() : sketch.exportApplet();
+ boolean success = sketch.exportApplet(new Target(
+ System.getProperty("user.dir") + File.separator + "lib" +
+ File.separator + "targets", Preferences.get("build.target")));
+ if (success) {
+ message("Done uploading.");
+ } else {
+ // error message will already be visible
+ }
+ } catch (RunnerException e) {
+ message("Error during upload.");
+ //e.printStackTrace();
+ error(e);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ buttons.clear();
+ }});
+ }
+
+
+ synchronized public void handleExportApp() {
+ message("Exporting application...");
try {
- //boolean success = sketch.isLibrary() ?
- //sketch.exportLibrary() : sketch.exportApplet();
- boolean success = sketch.exportApplet(new Target(
- System.getProperty("user.dir") + File.separator + "lib" +
- File.separator + "targets", Preferences.get("build.target")));
- if (success) {
- message("Done uploading.");
+ if (sketch.exportApplication()) {
+ message("Done exporting.");
} else {
// error message will already be visible
}
- } catch (RunnerException e) {
- message("Error during upload.");
- //e.printStackTrace();
- error(e);
} catch (Exception e) {
+ message("Error during export.");
e.printStackTrace();
}
buttons.clear();
}
- synchronized public void handleExportApp() {
- message("Arduino - Export - app");
+ /**
+ * Checks to see if the sketch has been modified, and if so,
+ * asks the user to save the sketch or cancel the export.
+ * This prevents issues where an incomplete version of the sketch
+ * would be exported, and is a fix for
+ * Bug 157
+ */
+ public boolean handleExportCheckModified() {
+ if (!sketch.modified) return true;
+
+ Object[] options = { "OK", "Cancel" };
+ int result = JOptionPane.showOptionDialog(this,
+ "Save changes before export?",
+ "Save",
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.QUESTION_MESSAGE,
+ null,
+ options,
+ options[0]);
+ if (result == JOptionPane.OK_OPTION) {
+ handleSave(true);
+
+ } else {
+ // why it's not CANCEL_OPTION is beyond me (at least on the mac)
+ // but f-- it.. let's get this shite done..
+ //} else if (result == JOptionPane.CANCEL_OPTION) {
+ message("Export canceled, changes must first be saved.");
+ buttons.clear();
+ return false;
+ }
+ return true;
}
-
+
/**
* Quit, but first ask user if it's ok. Also store preferences
* to disk just in case they want to quit. Final exit() happens
@@ -1799,6 +1928,7 @@ public class Editor extends JFrame
Preferences.save();
sketchbook.clean();
+ console.handleQuit();
//System.out.println("exiting here");
System.exit(0);
@@ -1887,6 +2017,14 @@ public class Editor extends JFrame
// ...................................................................
+ /**
+ * Show an error int the status bar.
+ */
+ public void error(String what) {
+ status.error(what);
+ }
+
+
public void error(Exception e) {
if (e == null) {
System.err.println("Editor.error() was passed a null exception.");
@@ -1905,7 +2043,7 @@ public class Editor extends JFrame
if (mess.indexOf(javaLang) == 0) {
mess = mess.substring(javaLang.length());
}
- status.error(mess);
+ error(mess);
}
e.printStackTrace();
}
@@ -1923,37 +2061,23 @@ public class Editor extends JFrame
String rxString = "RuntimeException: ";
if (mess.indexOf(rxString) == 0) {
mess = mess.substring(rxString.length());
+ //System.out.println("MESS3: " + mess);
}
String javaLang = "java.lang.";
if (mess.indexOf(javaLang) == 0) {
mess = mess.substring(javaLang.length());
}
- status.error(mess);
-
- buttons.clearRun();
+ error(mess);
+ buttons.clear();
}
- /*
- public void finished() {
- running = false;
- buttons.clearRun();
- message("Done.");
- }
- */
-
public void message(String msg) {
status.notice(msg);
}
- /*
- public void messageClear(String msg) {
- status.unnotice(msg);
- }
- */
-
// ...................................................................
@@ -1962,7 +2086,6 @@ public class Editor extends JFrame
* Returns the edit popup menu.
*/
class TextAreaPopup extends JPopupMenu {
- //protected ReferenceKeys referenceItems = new ReferenceKeys();
String currentDir = System.getProperty("user.dir");
String referenceFile = null;
@@ -1977,6 +2100,7 @@ public class Editor extends JFrame
cutItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textarea.cut();
+ sketch.setModified(true);
}
});
this.add(cutItem);
@@ -1993,6 +2117,7 @@ public class Editor extends JFrame
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textarea.paste();
+ sketch.setModified(true);
}
});
this.add(item);
diff --git a/app/EditorButtons.java b/app/EditorButtons.java
index 3aa995de3..3867d271a 100644
--- a/app/EditorButtons.java
+++ b/app/EditorButtons.java
@@ -41,9 +41,11 @@ public class EditorButtons extends JComponent implements MouseInputListener {
};
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;
+ /// height, width of the toolbar buttons
+ static final int BUTTON_WIDTH = 27;
+ static final int BUTTON_HEIGHT = 32;
+ /// amount of space between groups of buttons on the toolbar
+ static final int BUTTON_GAP = 15;
static final int RUN = 0;
static final int STOP = 1;
@@ -59,7 +61,7 @@ public class EditorButtons extends JComponent implements MouseInputListener {
static final int ACTIVE = 2;
Editor editor;
- boolean disableRun;
+ //boolean disableRun;
//Label status;
Image offscreen;
@@ -72,7 +74,7 @@ public class EditorButtons extends JComponent implements MouseInputListener {
Image rollover[];
Image active[];
int currentRollover;
- int currentSelection;
+ //int currentSelection;
JPopupMenu popup;
@@ -112,7 +114,7 @@ public class EditorButtons extends JComponent implements MouseInputListener {
// see EditorStatus.java for details.
//bgcolor = Preferences.getColor("buttons.bgcolor");
bgcolor = new Color(0x04, 0x4F, 0x6F);
-
+
status = "";
statusFont = Preferences.getFont("buttons.status.font");
@@ -234,10 +236,10 @@ public class EditorButtons extends JComponent implements MouseInputListener {
if (sel == -1) return;
if (state[sel] != ACTIVE) {
- if (!(disableRun && ((sel == RUN) || (sel == STOP)))) {
- setState(sel, ROLLOVER, true);
- currentRollover = sel;
- }
+ //if (!(disableRun && ((sel == RUN) || (sel == STOP)))) {
+ setState(sel, ROLLOVER, true);
+ currentRollover = sel;
+ //}
}
}
@@ -284,6 +286,10 @@ public class EditorButtons extends JComponent implements MouseInputListener {
public void mouseExited(MouseEvent e) {
+ // if the popup menu for is visible, don't register this,
+ // because the popup being set visible will fire a mouseExited() event
+ if ((popup != null) && popup.isVisible()) return;
+
if (state[OPEN] != INACTIVE) {
setState(OPEN, INACTIVE, true);
}
@@ -295,27 +301,78 @@ public class EditorButtons extends JComponent implements MouseInputListener {
public void mousePressed(MouseEvent e) {
- int x = e.getX();
- int y = e.getY();
+ final int x = e.getX();
+ final 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);
- }
+ //int currentSelection = sel;
+ //if (!(disableRun && ((sel == RUN) || (sel == STOP)))) {
+ // moving the handling of this over into the editor
+ //setState(sel, ACTIVE, true);
+ //}
- if (currentSelection == OPEN) {
+ //if (currentSelection == OPEN) {
+ //switch (currentSelection) {
+ switch (sel) {
+ case RUN:
+ //if (!disableRun) {
+ editor.handleRun(e.isShiftDown());
+ //}
+ break;
+
+ case STOP:
+ //if (!disableRun) {
+ //setState(RUN, INACTIVE, true);
+ //setInactive();
+ editor.handleStop();
+ //}
+ break;
+
+ case OPEN:
if (popup == null) {
//popup = new JPopupMenu();
popup = editor.sketchbook.getPopupMenu();
+ // no events properly being fired, so nevermind
+ /*
+ popup.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ System.out.println("action " + e);
+ }
+ });
+ popup.addComponentListener(new ComponentAdapter() {
+ public void componentHidden(ComponentEvent e) {
+ System.out.println("hidden " + e);
+ }
+ });
+ */
add(popup);
}
- //editor.sketchbook.rebuildPopup(popup);
- popup.show(this, x, y);
- }
+ //activate(OPEN);
+ //SwingUtilities.invokeLater(new Runnable() {
+ //public void run() {
+ popup.show(EditorButtons.this, x, y);
+ //}});
+ break;
+
+ case NEW:
+ editor.handleNew(e.isShiftDown());
+ break;
+
+ case SAVE:
+ editor.handleSave(false);
+ break;
+
+ case EXPORT:
+ editor.handleExport();
+ break;
+
+ case SERIAL:
+ editor.handleSerial();
+ break;
+ }
}
@@ -323,6 +380,7 @@ public class EditorButtons extends JComponent implements MouseInputListener {
public void mouseReleased(MouseEvent e) {
+ /*
switch (currentSelection) {
case RUN:
if (!disableRun) {
@@ -344,14 +402,47 @@ public class EditorButtons extends JComponent implements MouseInputListener {
case SERIAL: editor.handleSerial(); break;
}
currentSelection = -1;
+ */
}
- public void disableRun(boolean what) {
- disableRun = what;
+ //public void disableRun(boolean what) {
+ //disableRun = what;
+ //}
+
+
+ /*
+ public void run() {
+ if (inactive == null) return;
+ clear();
+ setState(RUN, ACTIVE, true);
+ }
+ */
+
+
+ public void running(boolean yesno) {
+ setState(RUN, yesno ? ACTIVE : INACTIVE, true);
}
+ /**
+ * Set a particular button to be active.
+ */
+ public void activate(int what) {
+ if (inactive == null) return;
+ setState(what, ACTIVE, true);
+ }
+
+
+ //public void clearRun() {
+ //if (inactive == null) return;
+ //setState(RUN, INACTIVE, true);
+ //}
+
+
+ /**
+ * Clear all the state of all buttons.
+ */
public void clear() { // (int button) {
if (inactive == null) return;
@@ -363,24 +454,6 @@ public class EditorButtons extends JComponent implements MouseInputListener {
}
- 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;
diff --git a/app/EditorConsole.java b/app/EditorConsole.java
index 5c451295b..27a5abaee 100644
--- a/app/EditorConsole.java
+++ b/app/EditorConsole.java
@@ -28,7 +28,7 @@ import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.text.*;
-
+import java.util.*;
/**
* Message console that sits below the editing area.
@@ -41,7 +41,7 @@ public class EditorConsole extends JScrollPane {
Editor editor;
JTextPane consoleTextPane;
- StyledDocument consoleDoc;
+ BufferedStyledDocument consoleDoc;
MutableAttributeSet stdStyle;
MutableAttributeSet errStyle;
@@ -51,6 +51,10 @@ public class EditorConsole extends JScrollPane {
//int maxCharCount;
int maxLineCount;
+ static File errFile;
+ static File outFile;
+ static File tempFolder;
+
static PrintStream systemOut;
static PrintStream systemErr;
@@ -66,9 +70,9 @@ public class EditorConsole extends JScrollPane {
maxLineCount = Preferences.getInteger("console.length");
- consoleTextPane = new JTextPane();
+ consoleDoc = new BufferedStyledDocument(10000, maxLineCount);
+ consoleTextPane = new JTextPane(consoleDoc);
consoleTextPane.setEditable(false);
- consoleDoc = consoleTextPane.getStyledDocument();
// necessary?
MutableAttributeSet standard = new SimpleAttributeSet();
@@ -115,15 +119,20 @@ public class EditorConsole extends JScrollPane {
systemOut = System.out;
systemErr = System.err;
+ tempFolder = Base.createTempFolder("console");
try {
String outFileName = Preferences.get("console.output.file");
if (outFileName != null) {
- stdoutFile = new FileOutputStream(outFileName);
+ outFile = new File(tempFolder, outFileName);
+ stdoutFile = new FileOutputStream(outFile);
+ //outFile.deleteOnExit();
}
String errFileName = Preferences.get("console.error.file");
if (errFileName != null) {
- stderrFile = new FileOutputStream(outFileName);
+ errFile = new File(tempFolder, errFileName);
+ stderrFile = new FileOutputStream(errFile);
+ //errFile.deleteOnExit();
}
} catch (IOException e) {
Base.showWarning("Console Error",
@@ -151,6 +160,52 @@ public class EditorConsole extends JScrollPane {
if (Base.isMacOS()) {
setBorder(null);
}
+
+ // periodically post buffered messages to the console
+ // should the interval come from the preferences file?
+ new javax.swing.Timer(250, new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ // only if new text has been added
+ if (consoleDoc.hasAppendage) {
+ // insert the text that's been added in the meantime
+ consoleDoc.insertAll();
+ // always move to the end of the text as it's added
+ consoleTextPane.setCaretPosition(consoleDoc.getLength());
+ }
+ }
+ }).start();
+ }
+
+
+ /**
+ * Close the streams so that the temporary files can be deleted.
+ *
+ * File.deleteOnExit() cannot be used because the stdout and stderr
+ * files are inside a folder, and have to be deleted before the
+ * folder itself is deleted, which can't be guaranteed when using
+ * the deleteOnExit() method.
+ */
+ public void handleQuit() {
+ // replace original streams to remove references to console's streams
+ System.setOut(systemOut);
+ System.setErr(systemErr);
+
+ // close the PrintStream
+ consoleOut.close();
+ consoleErr.close();
+
+ // also have to close the original FileOutputStream
+ // otherwise it won't be shut down completely
+ try {
+ stdoutFile.close();
+ stderrFile.close();
+ } catch (IOException e) {
+ e.printStackTrace(systemOut);
+ }
+
+ outFile.delete();
+ errFile.delete();
+ tempFolder.delete();
}
@@ -174,10 +229,8 @@ public class EditorConsole extends JScrollPane {
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) {
@@ -204,66 +257,13 @@ public class EditorConsole extends JScrollPane {
* and eventually leads to EditorConsole.appendText(), which directly
* updates the Swing text components, causing deadlock.
*
- * 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.
- *
- * 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.
+ * Updates are buffered to the console and displayed at regular
+ * intervals on Swing's event-dispatching thread. (patch by David Mellis)
*/
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?
- }
- }
- });
+ consoleDoc.appendString(txt, e ? errStyle : stdStyle);
}
-
public void clear() {
try {
consoleDoc.remove(0, consoleDoc.getLength());
@@ -332,3 +332,87 @@ class EditorConsoleStream extends OutputStream {
}
}
}
+
+
+/**
+ * Buffer updates to the console and output them in batches. For info, see:
+ * http://java.sun.com/products/jfc/tsc/articles/text/element_buffer and
+ * http://javatechniques.com/public/java/docs/gui/jtextpane-speed-part2.html
+ * appendString() is called from multiple threads, and insertAll from the
+ * swing event thread, so they need to be synchronized
+ */
+class BufferedStyledDocument extends DefaultStyledDocument {
+ ArrayList elements = new ArrayList();
+ int maxLineLength, maxLineCount;
+ int currentLineLength = 0;
+ boolean needLineBreak = false;
+ boolean hasAppendage = false;
+
+ public BufferedStyledDocument(int maxLineLength, int maxLineCount) {
+ this.maxLineLength = maxLineLength;
+ this.maxLineCount = maxLineCount;
+ }
+
+ /** buffer a string for insertion at the end of the DefaultStyledDocument */
+ public synchronized void appendString(String str, AttributeSet a) {
+ // do this so that it's only updated when needed (otherwise console
+ // updates every 250 ms when an app isn't even running.. see bug 180)
+ hasAppendage = true;
+
+ // process each line of the string
+ while (str.length() > 0) {
+ // newlines within an element have (almost) no effect, so we need to
+ // replace them with proper paragraph breaks (start and end tags)
+ if (needLineBreak || currentLineLength > maxLineLength) {
+ elements.add(new ElementSpec(a, ElementSpec.EndTagType));
+ elements.add(new ElementSpec(a, ElementSpec.StartTagType));
+ currentLineLength = 0;
+ }
+
+ if (str.indexOf('\n') == -1) {
+ elements.add(new ElementSpec(a, ElementSpec.ContentType,
+ str.toCharArray(), 0, str.length()));
+ currentLineLength += str.length();
+ needLineBreak = false;
+ str = str.substring(str.length()); // eat the string
+ } else {
+ elements.add(new ElementSpec(a, ElementSpec.ContentType,
+ str.toCharArray(), 0, str.indexOf('\n') + 1));
+ needLineBreak = true;
+ str = str.substring(str.indexOf('\n') + 1); // eat the line
+ }
+ }
+ }
+
+ /** insert the buffered strings */
+ public synchronized void insertAll() {
+ ElementSpec[] elementArray = new ElementSpec[elements.size()];
+ elements.toArray(elementArray);
+
+ try {
+ // check how many lines have been used so far
+ // if too many, shave off a few lines from the beginning
+ Element element = super.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
+ super.remove(0, endOffset);
+ }
+ super.insert(super.getLength(), elementArray);
+
+ } catch (BadLocationException e) {
+ // ignore the error otherwise this will cause an infinite loop
+ // maybe not a good idea in the long run?
+ }
+ elements.clear();
+ hasAppendage = false;
+ }
+}
diff --git a/app/EditorHeader.java b/app/EditorHeader.java
index 45e43cc6f..2fc60c90f 100644
--- a/app/EditorHeader.java
+++ b/app/EditorHeader.java
@@ -339,8 +339,10 @@ public class EditorHeader extends JComponent {
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 = new JMenuItem(sketch.hidden[i].name +
+ Sketch.flavorExtensionsShown[sketch.hidden[i].flavor]);
+ item.setActionCommand(sketch.hidden[i].name +
+ Sketch.flavorExtensionsShown[sketch.hidden[i].flavor]);
item.addActionListener(unhideListener);
unhide.add(item);
}
@@ -360,7 +362,8 @@ public class EditorHeader extends JComponent {
}
};
for (int i = 0; i < sketch.codeCount; i++) {
- item = new JMenuItem(sketch.code[i].name);
+ item = new JMenuItem(sketch.code[i].name +
+ Sketch.flavorExtensionsShown[sketch.code[i].flavor]);
item.addActionListener(jumpListener);
menu.add(item);
}
diff --git a/app/EditorListener.java b/app/EditorListener.java
index 53f21d57a..8f6c41ad7 100644
--- a/app/EditorListener.java
+++ b/app/EditorListener.java
@@ -35,13 +35,25 @@ import javax.swing.event.*;
/**
* Filters key events for tab expansion/indent/etc.
+ *
+ * For version 0099, some changes have been made to make the indents
+ * smarter. There are still issues though:
+ * + indent happens when it picks up a curly brace on the previous line,
+ * but not if there's a blank line between them.
+ * + It also doesn't handle single indent situations where a brace
+ * isn't used (i.e. an if statement or for loop that's a single line).
+ * It shouldn't actually be using braces.
+ * Solving these issues, however, would probably best be done by a
+ * smarter parser/formatter, rather than continuing to hack this class.
*/
public class EditorListener {
Editor editor;
JEditTextArea textarea;
boolean externalEditor;
- boolean expandTabs;
+ boolean tabsExpand;
+ boolean tabsIndent;
+ int tabSize;
String tabString;
boolean autoIndent;
@@ -61,8 +73,9 @@ public class EditorListener {
public void applyPreferences() {
- expandTabs = Preferences.getBoolean("editor.tabs.expand");
- int tabSize = Preferences.getInteger("editor.tabs.size");
+ tabsExpand = Preferences.getBoolean("editor.tabs.expand");
+ //tabsIndent = Preferences.getBoolean("editor.tabs.indent");
+ tabSize = Preferences.getInteger("editor.tabs.size");
tabString = Editor.EMPTY.substring(0, tabSize);
autoIndent = Preferences.getBoolean("editor.indent");
externalEditor = Preferences.getBoolean("editor.external");
@@ -74,7 +87,13 @@ public class EditorListener {
//}
- // called by JEditTextArea inside processKeyEvent
+ /**
+ * Intercepts key pressed events for JEditTextArea.
+ *
+ * Called by JEditTextArea inside processKeyEvent(). Note that this
+ * won't intercept actual characters, because those are fired on
+ * keyTyped().
+ */
public boolean keyPressed(KeyEvent event) {
// don't do things if the textarea isn't editable
if (externalEditor) return false;
@@ -92,21 +111,148 @@ public class EditorListener {
}
// TODO i don't like these accessors. clean em up later.
- if (!editor.sketch.current.modified) {
+ if (!editor.sketch.modified) {
if ((code == KeyEvent.VK_BACK_SPACE) || (code == KeyEvent.VK_TAB) ||
(code == KeyEvent.VK_ENTER) || ((c >= 32) && (c < 128))) {
- editor.sketch.setModified();
+ editor.sketch.setModified(true);
}
}
+ if ((code == KeyEvent.VK_UP) &&
+ ((event.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
+ // back up to the last empty line
+ char contents[] = textarea.getText().toCharArray();
+ //int origIndex = textarea.getCaretPosition() - 1;
+ int caretIndex = textarea.getCaretPosition();
+
+ int index = calcLineStart(caretIndex - 1, contents);
+ //System.out.println("line start " + (int) contents[index]);
+ index -= 2; // step over the newline
+ //System.out.println((int) contents[index]);
+ boolean onlySpaces = true;
+ while (index > 0) {
+ if (contents[index] == 10) {
+ if (onlySpaces) {
+ index++;
+ break;
+ } else {
+ onlySpaces = true; // reset
+ }
+ } else if (contents[index] != ' ') {
+ onlySpaces = false;
+ }
+ index--;
+ }
+ // if the first char, index will be -2
+ if (index < 0) index = 0;
+
+ if ((event.getModifiers() & KeyEvent.SHIFT_MASK) != 0) {
+ textarea.setSelectionStart(caretIndex);
+ textarea.setSelectionEnd(index);
+ } else {
+ textarea.setCaretPosition(index);
+ }
+ event.consume();
+ return true;
+
+ } else if ((code == KeyEvent.VK_DOWN) &&
+ ((event.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
+ char contents[] = textarea.getText().toCharArray();
+ int caretIndex = textarea.getCaretPosition();
+
+ int index = caretIndex;
+ int lineStart = 0;
+ boolean onlySpaces = false; // don't count this line
+ while (index < contents.length) {
+ if (contents[index] == 10) {
+ if (onlySpaces) {
+ index = lineStart; // this is it
+ break;
+ } else {
+ lineStart = index + 1;
+ onlySpaces = true; // reset
+ }
+ } else if (contents[index] != ' ') {
+ onlySpaces = false;
+ }
+ index++;
+ }
+ // if the first char, index will be -2
+ //if (index < 0) index = 0;
+
+ //textarea.setSelectionStart(index);
+ //textarea.setSelectionEnd(index);
+ if ((event.getModifiers() & KeyEvent.SHIFT_MASK) != 0) {
+ textarea.setSelectionStart(caretIndex);
+ textarea.setSelectionEnd(index);
+ } else {
+ textarea.setCaretPosition(index);
+ }
+ event.consume();
+ return true;
+ }
+
+
switch ((int) c) {
case 9: // expand tabs
- if (expandTabs) {
- //tc.replaceSelection(tabString);
+ if (tabsExpand) {
textarea.setSelectedText(tabString);
event.consume();
return true;
+
+ } else if (tabsIndent) {
+ // this code is incomplete
+
+ // if this brace is the only thing on the line, outdent
+ //char contents[] = getCleanedContents();
+ char contents[] = textarea.getText().toCharArray();
+ // index to the character to the left of the caret
+ int prevCharIndex = textarea.getCaretPosition() - 1;
+
+ // now find the start of this line
+ int lineStart = calcLineStart(prevCharIndex, contents);
+
+ int lineEnd = lineStart;
+ while ((lineEnd < contents.length - 1) &&
+ (contents[lineEnd] != 10)) {
+ lineEnd++;
+ }
+
+ // get the number of braces, to determine whether this is an indent
+ int braceBalance = 0;
+ int index = lineStart;
+ while ((index < contents.length) &&
+ (contents[index] != 10)) {
+ if (contents[index] == '{') {
+ braceBalance++;
+ } else if (contents[index] == '}') {
+ braceBalance--;
+ }
+ index++;
+ }
+
+ // if it's a starting indent, need to ignore it, so lineStart
+ // will be the counting point. but if there's a closing indent,
+ // then the lineEnd should be used.
+ int where = (braceBalance > 0) ? lineStart : lineEnd;
+ int indent = calcBraceIndent(where, contents);
+ if (indent == -1) {
+ // no braces to speak of, do nothing
+ indent = 0;
+ } else {
+ indent += tabSize;
+ }
+
+ // and the number of spaces it has
+ int spaceCount = calcSpaceCount(prevCharIndex, contents);
+
+ textarea.setSelectionStart(lineStart);
+ textarea.setSelectionEnd(lineStart + spaceCount);
+ textarea.setSelectedText(Editor.EMPTY.substring(0, indent));
+
+ event.consume();
+ return true;
}
break;
@@ -115,13 +261,18 @@ public class EditorListener {
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
+ // this is the previous character
+ // (i.e. when you hit return, it'll be the last character
+ // just before where the newline will be inserted)
int origIndex = textarea.getCaretPosition() - 1;
+ // NOTE all this cursing about CRLF stuff is probably moot
+ // NOTE since the switch to JEditTextArea, which seems to use
+ // NOTE only LFs internally (thank god). disabling for 0099.
// 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++) {
@@ -130,38 +281,140 @@ public class EditorListener {
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
+ int spaceCount = calcSpaceCount(origIndex, contents);
+ //int origCount = spaceCount;
+
+ // now before inserting this many spaces, walk forward from
+ // the caret position, so that the number of spaces aren't
+ // just being duplicated again
+ int index = origIndex + 1;
+ int extraCount = 0;
+ while ((index < contents.length) &&
+ (contents[index] == ' ')) {
+ //spaceCount--;
+ extraCount++;
+ index++;
+ }
+
+ // hitting return on a line with spaces *after* the caret
+ // can cause trouble. for simplicity's sake, just ignore this case.
+ //if (spaceCount < 0) spaceCount = origCount;
+ if (spaceCount - extraCount > 0) {
+ spaceCount -= extraCount;
+ }
+
+ // if the last character was a left curly brace, then indent
+ if (origIndex != -1) {
+ if (contents[origIndex] == '{') {
+ spaceCount += tabSize;
}
}
+
+ String insertion = "\n" + Editor.EMPTY.substring(0, spaceCount);
+ textarea.setSelectedText(insertion);
+
+ // mark this event as already handled
+ event.consume();
+ return true;
+ }
+ break;
+
+ case '}':
+ if (autoIndent) {
+ // first remove anything that was there (in case this multiple
+ // characters are selected, so that it's not in the way of the
+ // spaces for the auto-indent
+ if (textarea.getSelectionStart() != textarea.getSelectionEnd()) {
+ textarea.setSelectedText("");
+ }
+
+ // if this brace is the only thing on the line, outdent
+ char contents[] = textarea.getText().toCharArray();
+ // index to the character to the left of the caret
+ int prevCharIndex = textarea.getCaretPosition() - 1;
+
+ // backup from the current caret position to the last newline,
+ // checking for anything besides whitespace along the way.
+ // if there's something besides whitespace, exit without
+ // messing any sort of indenting.
+ int index = prevCharIndex;
+ boolean finished = false;
+ while ((index != -1) && (!finished)) {
+ if (contents[index] == 10) {
+ finished = true;
+ index++;
+ } else if (contents[index] != ' ') {
+ // don't do anything, this line has other stuff on it
+ return false;
+ } else {
+ index--;
+ }
+ }
+ if (!finished) return false; // brace with no start
+ int lineStartIndex = index;
+
+ /*
+ // now that we know things are ok to be indented, walk
+ // backwards to the last { to see how far its line is indented.
+ // this isn't perfect cuz it'll pick up commented areas,
+ // but that's not really a big deal and can be fixed when
+ // this is all given a more complete (proper) solution.
+ index = prevCharIndex;
+ int braceDepth = 1;
+ finished = false;
+ while ((index != -1) && (!finished)) {
+ if (contents[index] == '}') {
+ // aww crap, this means we're one deeper
+ // and will have to find one more extra {
+ braceDepth++;
+ index--;
+ } else if (contents[index] == '{') {
+ braceDepth--;
+ if (braceDepth == 0) {
+ finished = true;
+ } // otherwise just teasing, keep going..
+ } else {
+ index--;
+ }
+ }
+ // never found a proper brace, be safe and don't do anything
+ if (!finished) return false;
+
+ // check how many spaces on the line with the matching open brace
+ int pairedSpaceCount = calcSpaceCount(index, contents);
+ //System.out.println(pairedSpaceCount);
+ */
+ int pairedSpaceCount = calcBraceIndent(prevCharIndex, contents); //, 1);
+ if (pairedSpaceCount == -1) return false;
+
+ /*
+ // now walk forward and figure out how many spaces there are
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());
+ // number of spaces found on this line
+ //int newSpaceCount = Math.max(0, spaceCount - tabSize);
+ // number of spaces on this current line
+ //int spaceCount = calcSpaces(caretIndex, contents);
+ //System.out.println("spaces is " + spaceCount);
+ //String insertion = "\n" + Editor.EMPTY.substring(0, spaceCount);
+ //int differential = newSpaceCount - spaceCount;
+ //System.out.println("diff is " + differential);
+ //int newStart = textarea.getSelectionStart() + differential;
+ //textarea.setSelectionStart(newStart);
+ //textarea.setSelectedText("}");
+ textarea.setSelectionStart(lineStartIndex);
+ textarea.setSelectedText(Editor.EMPTY.substring(0, pairedSpaceCount));
+
+ // mark this event as already handled
event.consume();
return true;
}
@@ -169,4 +422,143 @@ public class EditorListener {
}
return false;
}
+
+
+ /**
+ * Return the index for the first character on this line.
+ */
+ protected int calcLineStart(int index, char contents[]) {
+ // backup from the current caret position to the last newline,
+ // so that we can figure out how far this line was indented
+ int spaceCount = 0;
+ boolean finished = false;
+ while ((index != -1) && (!finished)) {
+ if ((contents[index] == 10) ||
+ (contents[index] == 13)) {
+ finished = true;
+ //index++; // maybe ?
+ } else {
+ index--; // new
+ }
+ }
+ // add one because index is either -1 (the start of the document)
+ // or it's the newline character for the previous line
+ return index + 1;
+ }
+
+
+ /**
+ * Calculate the number of spaces on this line.
+ */
+ protected int calcSpaceCount(int index, char contents[]) {
+ index = calcLineStart(index, contents);
+
+ int spaceCount = 0;
+ // now walk forward and figure out how many spaces there are
+ while ((index < contents.length) && (index >= 0) &&
+ (contents[index++] == ' ')) {
+ spaceCount++;
+ }
+ return spaceCount;
+ }
+
+
+ /**
+ * Walk back from 'index' until the brace that seems to be
+ * the beginning of the current block, and return the number of
+ * spaces found on that line.
+ */
+ protected int calcBraceIndent(int index, char contents[]) {
+ // now that we know things are ok to be indented, walk
+ // backwards to the last { to see how far its line is indented.
+ // this isn't perfect cuz it'll pick up commented areas,
+ // but that's not really a big deal and can be fixed when
+ // this is all given a more complete (proper) solution.
+ int braceDepth = 1;
+ boolean finished = false;
+ while ((index != -1) && (!finished)) {
+ if (contents[index] == '}') {
+ // aww crap, this means we're one deeper
+ // and will have to find one more extra {
+ braceDepth++;
+ //if (braceDepth == 0) {
+ //finished = true;
+ //}
+ index--;
+ } else if (contents[index] == '{') {
+ braceDepth--;
+ if (braceDepth == 0) {
+ finished = true;
+ }
+ index--;
+ } else {
+ index--;
+ }
+ }
+ // never found a proper brace, be safe and don't do anything
+ if (!finished) return -1;
+
+ // check how many spaces on the line with the matching open brace
+ //int pairedSpaceCount = calcSpaceCount(index, contents);
+ //System.out.println(pairedSpaceCount);
+ return calcSpaceCount(index, contents);
+ }
+
+
+ /**
+ * Get the character array and blank out the commented areas.
+ * This hasn't yet been tested, the plan was to make auto-indent
+ * less gullible (it gets fooled by braces that are commented out).
+ */
+ protected char[] getCleanedContents() {
+ char c[] = textarea.getText().toCharArray();
+
+ int index = 0;
+ while (index < c.length - 1) {
+ if ((c[index] == '/') && (c[index+1] == '*')) {
+ c[index++] = 0;
+ c[index++] = 0;
+ while ((index < c.length - 1) &&
+ !((c[index] == '*') && (c[index+1] == '/'))) {
+ c[index++] = 0;
+ }
+
+ } else if ((c[index] == '/') && (c[index+1] == '/')) {
+ // clear out until the end of the line
+ while ((index < c.length) && (c[index] != 10)) {
+ c[index++] = 0;
+ }
+ if (index != c.length) {
+ index++; // skip over the newline
+ }
+ }
+ }
+ return c;
+ }
+
+ /*
+ protected char[] getCleanedContents() {
+ char c[] = textarea.getText().toCharArray();
+ boolean insideMulti; // multi-line comment
+ boolean insideSingle; // single line double slash
+
+ //for (int i = 0; i < c.length - 1; i++) {
+ int index = 0;
+ while (index < c.length - 1) {
+ if (insideMulti && (c[index] == '*') && (c[index+1] == '/')) {
+ insideMulti = false;
+ index += 2;
+ } else if ((c[index] == '/') && (c[index+1] == '*')) {
+ insideMulti = true;
+ index += 2;
+ } else if ((c[index] == '/') && (c[index+1] == '/')) {
+ // clear out until the end of the line
+ while (c[index] != 10) {
+ c[index++] = 0;
+ }
+ index++;
+ }
+ }
+ }
+ */
}
diff --git a/app/EditorStatus.java b/app/EditorStatus.java
index 191f4b88f..67ededc34 100644
--- a/app/EditorStatus.java
+++ b/app/EditorStatus.java
@@ -422,7 +422,7 @@ public class EditorStatus extends JPanel implements ActionListener {
} else if (e.getSource() == yesButton) {
// answer was in response to "save changes?"
unprompt();
- editor.handleSave();
+ editor.handleSave(true);
editor.checkModified2();
} else if (e.getSource() == cancelButton) {
diff --git a/app/FindReplace.java b/app/FindReplace.java
index 7006a5f45..43f6ce43a 100644
--- a/app/FindReplace.java
+++ b/app/FindReplace.java
@@ -29,10 +29,21 @@ import javax.swing.*;
/**
- * Find & Replace window for the processing editor.
+ * Find & Replace window for the Processing editor.
+ *
+ * One major annoyance in this is that the window is re-created each time
+ * that "Find" is called. This is because Mac OS X has a strange focus
+ * issue with windows that are re-shown with setVisible() or show().
+ * requestFocusInWindow() properly sets the focus to the find field,
+ * however, just a short moment later, the focus is set to null. Even
+ * trying to catch this scenario and request it again doesn't seem to work.
+ * Most likely this is some annoyance buried deep in one of Apple's docs,
+ * or in the doc for the focus stuff (I tend to think the former because
+ * Windows doesn't seem to be quite so beligerent). Filed as
+ * Bug 244
+ * should anyone have clues about how to fix.
*/
-public class FindReplace extends JFrame
- implements ActionListener, KeyListener {
+public class FindReplace extends JFrame implements ActionListener {
static final int BIG = 13;
static final int SMALL = 6;
@@ -41,16 +52,17 @@ public class FindReplace extends JFrame
JTextField findField;
JTextField replaceField;
+ static String findString;
+ static String replaceString;
JButton replaceButton;
JButton replaceAllButton;
JButton findButton;
JCheckBox ignoreCaseBox;
- boolean ignoreCase;
-
- KeyStroke windowClose;
+ static boolean ignoreCase = true;
+ /// true when there's something selected in the editor
boolean found;
@@ -74,6 +86,27 @@ public class FindReplace extends JFrame
pain.add(replaceField = new JTextField(20));
Dimension d2 = findField.getPreferredSize();
+ if (findString != null) findField.setText(findString);
+ if (replaceString != null) replaceField.setText(replaceString);
+ //System.out.println("setting find str to " + findString);
+ //findField.requestFocusInWindow();
+
+ //pain.setDefault
+ /*
+ findField.addFocusListener(new FocusListener() {
+ public void focusGained(FocusEvent e) {
+ System.out.println("Focus gained " + e.getOppositeComponent());
+ }
+
+ public void focusLost(FocusEvent e) {
+ System.out.println("Focus lost "); // + e.getOppositeComponent());
+ if (e.getOppositeComponent() == null) {
+ requestFocusInWindow();
+ }
+ }
+ });
+ */
+
// +1 since it's better to tend downwards
int yoff = (1 + d2.height - d1.height) / 2;
@@ -82,7 +115,7 @@ public class FindReplace extends JFrame
replaceLabel.setBounds(BIG, BIG + d2.height + SMALL + yoff,
d1.width, d1.height);
- ignoreCase = true;
+ //ignoreCase = true;
ignoreCaseBox = new JCheckBox("Ignore Case");
ignoreCaseBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
@@ -110,12 +143,8 @@ public class FindReplace extends JFrame
}
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);
}
@@ -146,7 +175,7 @@ public class FindReplace extends JFrame
replaceButton.setEnabled(false);
// so that typing will go straight to this field
- findField.requestFocus();
+ //findField.requestFocus();
// make the find button the blinky default
getRootPane().setDefaultButton(findButton);
@@ -161,48 +190,62 @@ public class FindReplace extends JFrame
(screen.height - high) / 2, wide, high);
// add key listener to trap esc and ctrl/cmd-w
- findField.addKeyListener(this);
- replaceField.addKeyListener(this);
- addKeyListener(this);
+ /*
+ KeyListener listener = new KeyAdapter() {
+ public void keyPressed(KeyEvent e) {
+ if (Base.isCloseWindowEvent(e)) hide();
+ }
+ };
+ findField.addKeyListener(listener);
+ replaceField.addKeyListener(listener);
+ addKeyListener(listener);
+ */
+ ActionListener disposer = new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ //hide();
+ handleClose();
+ }
+ };
+ setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ handleClose();
+ }
+ });
+ Base.registerWindowCloseKeys(getRootPane(), disposer);
+
+ /*
// 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();
+ //boolean ok = findField.requestFocusInWindow();
+ //System.out.println("got " + ok);
+ //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 handleClose() {
+ //System.out.println("handling close now");
+ findString = findField.getText();
+ replaceString = replaceField.getText();
+
+ // this object should eventually become dereferenced
+ hide();
}
- public void keyReleased(KeyEvent e) { }
-
- public void keyTyped(KeyEvent e) { }
-
/*
public void show() {
+ findField.requestFocusInWindow();
super.show();
- findField.selectAll();
- findField.requestFocus();
+ //findField.selectAll();
+ //findField.requestFocus();
}
*/
@@ -266,9 +309,10 @@ public class FindReplace extends JFrame
}
- // replace the current selection with whatever's in the
- // replacement text field
-
+ /**
+ * Replace the current selection with whatever's in the
+ * replacement text field.
+ */
public void replace() {
if (!found) return; // don't replace if nothing found
@@ -284,15 +328,17 @@ public class FindReplace extends JFrame
editor.textarea.setSelectedText(replaceField.getText());
//editor.setSketchModified(true);
//editor.sketch.setCurrentModified(true);
- editor.sketch.setModified();
+ editor.sketch.setModified(true);
// don't allow a double replace
replaceButton.setEnabled(false);
}
- // keep doing find and replace alternately until nothing more found
-
+ /**
+ * Replace everything that matches by doing find and replace
+ * alternately until nothing more found.
+ */
public void replaceAll() {
// move to the beginning
editor.textarea.select(0, 0);
diff --git a/app/Library.java b/app/Library.java
new file mode 100755
index 000000000..4d3c6974c
--- /dev/null
+++ b/app/Library.java
@@ -0,0 +1,444 @@
+/*
+ Library.java - Library System for Wiring
+ Copyright (c) 2006 Nicholas Zambetti. All right reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+package processing.app;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+import javax.swing.event.*;
+
+/*
+ * Provides information about and builds a library
+ */
+public class Library implements MessageConsumer{
+
+ private File libFolder;
+ private LibraryManager libManager;
+ RunnerException exception;
+
+ static final String BUGS_URL = "https://developer.berlios.de/bugs/?group_id=3590";
+ static final String SUPER_BADNESS = "Compiler error, please submit this code to " + BUGS_URL;
+
+ /*
+ * Create a Library
+ */
+ public Library(LibraryManager manager, File folder)
+ {
+ libFolder = folder;
+ libManager = manager;
+
+ /* for debug output
+ System.out.println("library: " + getName());
+ System.out.println("folder: " + getFolder());
+ System.out.println("built: " + isBuilt());
+ System.out.println("buildable: " + isBuildable());
+ System.out.println("o files: " + getObjectFiles().length);
+ System.out.println("c files: " + getCSourceFiles().length);
+ System.out.println("cpp files: " + getCPPSourceFiles().length);
+ */
+ }
+
+ /*
+ * Directory of library
+ * @return File object of library's folder
+ */
+ public File getFolder()
+ {
+ return libFolder;
+ }
+
+ /*
+ * The name of library
+ * @return String with library name, derived from folder
+ * note: this will be eventually taken from xml description file
+ */
+ public String getName()
+ {
+ return libFolder.getName();
+ }
+
+ /*
+ * Tests if library is built
+ * @return True if library has .o files, false otherwise
+ */
+ public boolean isBuilt()
+ {
+ FileFilter onlyObjectFiles = new FileFilter() {
+ public boolean accept(File file) {
+ return file.getName().endsWith(".o");
+ }
+ };
+ if(0 < (libFolder.listFiles(onlyObjectFiles)).length){
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * Tests if library is buildable
+ * @return True if library has .cpp files, false otherwise
+ */
+ public boolean isBuildable()
+ {
+ FileFilter onlySourceFiles = new FileFilter() {
+ public boolean accept(File file) {
+ return (file.getName()).endsWith(".cpp");
+ }
+ };
+ if(0 < (libFolder.listFiles(onlySourceFiles)).length){
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * Tests if library is unbuilt but buildable
+ * @return True if library has .cpp files but no .o files, false otherwise
+ */
+ public boolean isUnbuiltBuildable()
+ {
+ if(isBuildable()){
+ if(!isBuilt()){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * Finds examples folder
+ * @return "examples" folder as file object or null
+ */
+ private File getExamplesFolder()
+ {
+ FileFilter filter = new FileFilter() {
+ public boolean accept(File file) {
+ if(file.isDirectory()){
+ if((file.getName()).equalsIgnoreCase("examples")){
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ File[] files = libFolder.listFiles(filter);
+ if(files.length > 0){
+ return files[0];
+ }
+ return null;
+ }
+
+ /*
+ * Populates example menu or submenu with files
+ */
+ private void populateWithExamples(File folder, JMenu menu, ActionListener listener) {
+ FileFilter onlyfolders = new FileFilter() {
+ public boolean accept(File file) {
+ return file.isDirectory();
+ }
+ };
+ File[] folders = folder.listFiles(onlyfolders);
+ File file;
+ JMenu submenu;
+ JMenuItem item;
+ for(int i = 0; i < folders.length; ++i){
+ file = new File(folders[i], folders[i].getName() + ".pde");
+ if(file.exists()){
+ item = new JMenuItem(folders[i].getName());
+ item.setActionCommand(file.getAbsolutePath());
+ item.addActionListener(listener);
+ menu.add(item);
+ }else{
+ submenu = new JMenu(folders[i].getName());
+ populateWithExamples(folders[i], submenu, listener);
+ menu.add(submenu);
+ }
+ }
+ }
+
+ /*
+ * Builds and returns an examples menu
+ * @return JMenu object with example files, or null if none
+ */
+ public JMenu getExamplesMenu(ActionListener listener) {
+ JMenu submenu;
+ File examplesFolder = getExamplesFolder();
+ if(null != examplesFolder){
+ submenu = new JMenu("Library-" + getName());
+ populateWithExamples(examplesFolder, submenu, listener);
+ return submenu;
+ }
+ return null;
+ }
+
+ /*
+ * List of object files for linking
+ * @return Array of library's object files as File objects
+ */
+ public File[] getObjectFiles()
+ {
+ FileFilter onlyObjectFiles = new FileFilter() {
+ public boolean accept(File file) {
+ return (file.getName()).endsWith(".o");
+ }
+ };
+ return libFolder.listFiles(onlyObjectFiles);
+ }
+
+ /*
+ * List of header source files for inclusion
+ * @return Array of library's header source files as File objects
+ */
+ public File[] getHeaderFiles()
+ {
+ FileFilter onlyHFiles = new FileFilter() {
+ public boolean accept(File file) {
+ return (file.getName()).endsWith(".h");
+ }
+ };
+ return libFolder.listFiles(onlyHFiles);
+ }
+
+ /*
+ * List of C source files for compiling
+ * @return Array of library's C source files as File objects
+ */
+ private File[] getCSourceFiles()
+ {
+ FileFilter onlyCFiles = new FileFilter() {
+ public boolean accept(File file) {
+ return (file.getName()).endsWith(".c");
+ }
+ };
+ return libFolder.listFiles(onlyCFiles);
+ }
+
+ /*
+ * List of C++ source files for compiling
+ * @return Array of library's C++ source files as File objects
+ */
+ private File[] getCPPSourceFiles()
+ {
+ FileFilter onlyCPPFiles = new FileFilter() {
+ public boolean accept(File file) {
+ return (file.getName()).endsWith(".cpp");
+ }
+ };
+ return libFolder.listFiles(onlyCPPFiles);
+ }
+
+ /*
+ * Attempt to build library
+ * @return true on successful build, false otherwise
+ */
+ public boolean build() throws RunnerException
+ {
+ if(isBuildable()){
+ String userDir = System.getProperty("user.dir") + File.separator;
+
+ String[] baseCompileCommandC = new String[] {
+ ((!Base.isMacOS()) ? "tools/avr/bin/avr-gcc" : userDir + "tools/avr/bin/avr-gcc"),
+ "-c",
+ "-g",
+ "-Os",
+ "-Wall",
+ "-mmcu=" + Preferences.get("build.mcu"),
+ "-DF_CPU=" + Preferences.get("build.f_cpu"),
+ "-Ilib",
+ "-I" + getFolder(),
+ };
+
+ String[] baseCompileCommandCPP = new String[] {
+ ((!Base.isMacOS()) ? "tools/avr/bin/avr-g++" : userDir + "tools/avr/bin/avr-g++"),
+ "-c",
+ "-g",
+ "-Os",
+ "-Wall",
+ "-fno-exceptions",
+ "-mmcu=" + Preferences.get("build.mcu"),
+ "-DF_CPU=" + Preferences.get("build.f_cpu"),
+ "-Ilib",
+ "-I" + getFolder(),
+ };
+
+ // use built lib directories in include paths when searching for headers
+ // this allows libs to use other libs easily
+ String[] libDirs = libManager.getFolderPaths();
+ String[] compileCommandC = new String[baseCompileCommandC.length + libDirs.length + 2];
+ String[] compileCommandCPP = new String[baseCompileCommandCPP.length + libDirs.length + 2];
+ System.arraycopy(baseCompileCommandC, 0, compileCommandC, 0, baseCompileCommandC.length);
+ System.arraycopy(baseCompileCommandCPP, 0, compileCommandCPP, 0, baseCompileCommandCPP.length);
+ for (int i = 0; i < libDirs.length; ++i) {
+ compileCommandC[baseCompileCommandC.length + i] = "-I" + libDirs[i];
+ compileCommandCPP[baseCompileCommandCPP.length + i] = "-I" + libDirs[i];
+ }
+
+ File[] sourcesC = getCSourceFiles();
+ File[] sourcesCPP = getCPPSourceFiles();
+
+ // execute the compiler, and create threads to deal
+ // with the input and error streams
+ //
+ int result = 0;
+ try {
+ String nameSansExtension;
+ Process process;
+ boolean compiling = true;
+
+ // compile c sources
+ for(int i = 0; i < sourcesC.length; ++i) {
+ nameSansExtension = sourcesC[i].getName();
+ nameSansExtension = nameSansExtension.substring(0, nameSansExtension.length() - 2); // -2 because ".c"
+
+ compileCommandC[compileCommandC.length - 2] = sourcesC[i].getPath();
+ compileCommandC[compileCommandC.length - 1] = "-o" + getFolder() + File.separator + nameSansExtension + ".o";
+
+ process = Runtime.getRuntime().exec(compileCommandC);
+ new MessageSiphon(process.getInputStream(), this);
+ new MessageSiphon(process.getErrorStream(), this);
+
+ // wait for the process to finish. if interrupted
+ // before waitFor returns, continue waiting
+ //
+ compiling = true;
+ while (compiling) {
+ try {
+ result = process.waitFor();
+ //System.out.println("result is " + result);
+ compiling = false;
+ } catch (InterruptedException ignored) { }
+ }
+ if (exception != null) {
+ exception.hideStackTrace = true;
+ throw exception;
+ }
+ if(result != 0){
+ return false;
+ }
+ }
+
+ // compile c++ sources
+ for(int i = 0; i < sourcesCPP.length; ++i) {
+ nameSansExtension = sourcesCPP[i].getName();
+ nameSansExtension = nameSansExtension.substring(0, nameSansExtension.length() - 4); // -4 because ".cpp"
+
+ compileCommandCPP[compileCommandCPP.length - 2] = sourcesCPP[i].getPath();
+ compileCommandCPP[compileCommandCPP.length - 1] = "-o" + getFolder() + File.separator + nameSansExtension + ".o";
+
+ process = Runtime.getRuntime().exec(compileCommandCPP);
+ new MessageSiphon(process.getInputStream(), this);
+ new MessageSiphon(process.getErrorStream(), this);
+
+ // wait for the process to finish. if interrupted
+ // before waitFor returns, continue waiting
+ //
+ compiling = true;
+ while (compiling) {
+ try {
+ result = process.waitFor();
+ //System.out.println("result is " + result);
+ compiling = false;
+ } catch (InterruptedException ignored) { }
+ }
+ if (exception != null) {
+ exception.hideStackTrace = true;
+ throw exception;
+ }
+ if(result != 0){
+ return false;
+ }
+ }
+ } catch (Exception e) {
+ String msg = e.getMessage();
+ if ((msg != null) && (msg.indexOf("avr-gcc: not found") != -1)) {
+ Base.showWarning("Compiler error",
+ "Could not find the compiler.\n" +
+ "avr-gcc is missing from your PATH,\n" +
+ "see readme.txt for help.", null);
+ return false;
+
+ } else if ((msg != null) && (msg.indexOf("avr-g++: not found") != -1)) {
+ Base.showWarning("Compiler error",
+ "Could not find the compiler.\n" +
+ "avr-g++ is missing from your PATH,\n" +
+ "see readme.txt for help.", null);
+ return false;
+
+ } else {
+ e.printStackTrace();
+ result = -1;
+ }
+ }
+
+ // an error was queued up by message()
+ if (exception != null) {
+ throw exception;
+ }
+
+ if (result != 0 && result != 1 ) {
+ Base.openURL(BUGS_URL);
+ throw new RunnerException(SUPER_BADNESS);
+ }
+
+ // success would mean that 'result' is set to zero
+ return (result == 0); // ? true : false;
+ }
+ return false; // library is not buildable (contains no sources)
+ }
+
+ /**
+ * 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 inString) {
+ // This receives messages as full lines, so a newline needs
+ // to be added as they're printed to the console.
+
+ // always print all compilation output for library writers!
+ String outString = "";
+
+ // shorten file paths so that they are friendlier
+ int start = 0;
+ int end = 0;
+ String substring = libFolder.getPath() + File.separator;
+ StringBuffer result = new StringBuffer();
+ while ((end = inString.indexOf(substring, start)) >= 0) {
+ result.append(inString.substring(start, end));
+ start = end + substring.length();
+ }
+ result.append(inString.substring(start));
+ outString = result.toString();
+
+ System.err.print(outString);
+
+ // prepare error for throwing
+ if (inString.indexOf("error") != -1){
+ exception = new RunnerException("Error building library \"" + getName() + "\"");
+ }
+ }
+
+}
diff --git a/app/LibraryManager.java b/app/LibraryManager.java
new file mode 100755
index 000000000..f12c2ae2f
--- /dev/null
+++ b/app/LibraryManager.java
@@ -0,0 +1,217 @@
+/*
+ LibraryManager.java - Library System for Wiring
+ Copyright (c) 2006 Nicholas Zambetti. All right reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+package processing.app;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+import java.awt.event.*;
+
+import javax.swing.*;
+
+/*
+ * Provides information about and builds libraries
+ */
+public class LibraryManager {
+
+ private File libDir;
+ private List libraries = new ArrayList();
+
+ /*
+ * Create a LibraryManager.
+ */
+ public LibraryManager()
+ {
+ String userDir = System.getProperty("user.dir") + File.separator;
+ libDir = new File(
+ ((!Base.isMacOS()) ? "" : userDir) + "lib" + File.separator +
+ "targets" + File.separator + "libraries");
+ refreshLibraries();
+ }
+
+ /*
+ * Scans for libraries and refreshes internal list
+ */
+ private void refreshLibraries()
+ {
+ FileFilter onlyDirs = new FileFilter() {
+ public boolean accept(File file) {
+ return file.isDirectory();
+ }
+ };
+ libraries.clear();
+ File[] libs = libDir.listFiles(onlyDirs);
+ for(int i = 0; i < libs.length; ++i){
+ libraries.add(new Library(this, libs[i]));
+ }
+ }
+
+ /*
+ * Returns a collection of all library objects
+ * @return A read-only collection of Library objects
+ */
+ public Collection getAll() {
+ refreshLibraries();
+ return Collections.unmodifiableList(libraries);
+ }
+
+ /*
+ * Returns a collection of all built library objects
+ * @return A read-only collection of built Library objects
+ */
+ public Collection getBuiltLibraries() {
+ refreshLibraries();
+ List builtLibraries = new ArrayList();
+ Library library;
+ ListIterator libIterator = libraries.listIterator();
+ while(libIterator.hasNext()){
+ library = (Library)libIterator.next();
+ if(library.isBuilt()){
+ builtLibraries.add(library);
+ }
+ }
+ return Collections.unmodifiableList(builtLibraries);
+ }
+
+ /*
+ * Returns a collection of all buildable library objects
+ * @return A read-only collection of built Library objects
+ */
+ public Collection getLibrariesToBuild() {
+ refreshLibraries();
+ List buildableLibraries = new ArrayList();
+ Library library;
+ ListIterator libIterator = libraries.listIterator();
+ while(libIterator.hasNext()){
+ library = (Library)libIterator.next();
+ if(library.isUnbuiltBuildable()){
+ buildableLibraries.add(library);
+ }
+ }
+ return Collections.unmodifiableList(buildableLibraries);
+ }
+
+ /*
+ * Gathers paths to object files
+ * @return Array of strings of paths to object files
+ */
+ public String[] getObjectFiles() {
+ ArrayList filesArrayList = new ArrayList();
+ Collection builtLibraries = getBuiltLibraries();
+ Library library;
+ File[] files;
+ Iterator libIterator = builtLibraries.iterator();
+ while(libIterator.hasNext()){
+ library = (Library)libIterator.next();
+ files = library.getObjectFiles();
+ for(int i = 0; i < files.length; ++i){
+ filesArrayList.add(files[i].getPath());
+ }
+ }
+ String[] filesArray = new String[filesArrayList.size()];
+ filesArrayList.toArray(filesArray);
+ return filesArray;
+ }
+
+ /*
+ * Gathers filenames of header files
+ * @return Array of strings of filenames of header files
+ */
+ public String[] getHeaderFiles() {
+ ArrayList filesArrayList = new ArrayList();
+ Collection builtLibraries = getBuiltLibraries();
+ Library library;
+ File[] files;
+ Iterator libIterator = builtLibraries.iterator();
+ while(libIterator.hasNext()){
+ library = (Library)libIterator.next();
+ files = library.getHeaderFiles();
+ for(int i = 0; i < files.length; ++i){
+ filesArrayList.add(files[i].getName());
+ }
+ }
+ String[] filesArray = new String[filesArrayList.size()];
+ filesArrayList.toArray(filesArray);
+ return filesArray;
+ }
+
+ /*
+ * Gathers paths to library folders
+ * @return Array of strings of paths to library folders
+ */
+ public String[] getFolderPaths() {
+ ArrayList foldersArrayList = new ArrayList();
+ Collection builtLibraries = getBuiltLibraries();
+ Library library;
+ Iterator libIterator = builtLibraries.iterator();
+ while(libIterator.hasNext()){
+ library = (Library)libIterator.next();
+ foldersArrayList.add(library.getFolder().getPath());
+ }
+ String[] foldersArray = new String[foldersArrayList.size()];
+ foldersArrayList.toArray(foldersArray);
+ return foldersArray;
+ }
+
+ /*
+ * Builds unbuilt libraries
+ * @return Number of libraries built as int, -1 & exception on error
+ */
+ public int buildAllUnbuilt() throws RunnerException {
+ Collection buildableLibraries = getLibrariesToBuild();
+ Library library;
+ Iterator libIterator = buildableLibraries.iterator();
+ int countBuilt = 0;
+ while(libIterator.hasNext()){
+ library = (Library)libIterator.next();
+ //System.out.println("Building library \"" + library.getName() + "\"");
+ try {
+ if(library.build()){
+ ++countBuilt;
+ }else{
+ return -1;
+ }
+ }catch (RunnerException re) {
+ throw new RunnerException(re.getMessage());
+ } catch (Exception ex) {
+ throw new RunnerException(ex.toString());
+ }
+ }
+ return countBuilt;
+ }
+
+ /*
+ * Populates examples menu with library folders
+ */
+ public void populateExamplesMenu(JMenu examplesMenu, ActionListener listener) {
+ Library library;
+ Collection libraries = getBuiltLibraries();
+ Iterator iterator = libraries.iterator();
+ JMenu libraryExamples;
+ while(iterator.hasNext()){
+ library = (Library)iterator.next();
+ libraryExamples = library.getExamplesMenu(listener);
+ if(null != libraryExamples){
+ examplesMenu.add(libraryExamples);
+ }
+ }
+ }
+}
diff --git a/app/Preferences.java b/app/Preferences.java
index 11dd38069..1672b37d4 100644
--- a/app/Preferences.java
+++ b/app/Preferences.java
@@ -41,6 +41,7 @@ import javax.swing.filechooser.*;
import javax.swing.text.*;
import javax.swing.undo.*;
+//import processing.core.PApplet;
/**
@@ -50,7 +51,7 @@ import javax.swing.undo.*;
* 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 {
+public class Preferences {
// what to call the feller
@@ -72,16 +73,32 @@ public class Preferences extends JComponent {
static final String PROMPT_OK = "OK";
static final String PROMPT_BROWSE = "Browse";
- // mac needs it to be 70, windows needs 66, linux needs 76
+ /**
+ * Standardized width for buttons. Mac OS X 10.3 wants 70 as its default,
+ * Windows XP needs 66, and Linux needs 76, so 76 seems proper.
+ */
+ static public int BUTTON_WIDTH = 76;
- static int BUTTON_WIDTH = 76;
- static int BUTTON_HEIGHT = 24;
+ /**
+ * Standardized button height. Mac OS X 10.3 (Java 1.4) wants 29,
+ * presumably because it now includes the blue border, where it didn't
+ * in Java 1.3. Windows XP only wants 23 (not sure what default Linux
+ * would be). Because of the disparity, on Mac OS X, it will be set
+ * inside a static block.
+ */
+ static public int BUTTON_HEIGHT = 24;
+ static {
+ if (Base.isMacOS()) BUTTON_HEIGHT = 29;
+ }
// value for the size bars, buttons, etc
static final int GRID_SIZE = 33;
- // gui variables
+
+ // indents and spacing standards. these probably need to be modified
+ // per platform as well, since macosx is so huge, windows is smaller,
+ // and linux is all over the map
static final int GUI_BIG = 13;
static final int GUI_BETWEEN = 10;
@@ -89,14 +106,12 @@ public class Preferences extends JComponent {
// gui elements
- //JFrame frame;
- JDialog frame;
+ JDialog dialog;
int wide, high;
JTextField sketchbookLocationField;
JCheckBox sketchPromptBox;
JCheckBox sketchCleanBox;
- //JCheckBox exportLibraryBox;
JCheckBox externalEditorBox;
JCheckBox checkUpdatesBox;
@@ -111,7 +126,6 @@ public class Preferences extends JComponent {
static Hashtable table = new Hashtable();;
static File preferencesFile;
- //boolean firstTime; // first time this feller has been run
static public void init() {
@@ -152,9 +166,6 @@ public class Preferences extends JComponent {
// next load user preferences file
- //File home = new File(System.getProperty("user.home"));
- //File arduinoHome = new File(home, "Arduino");
- //preferencesFile = new File(home, PREFS_FILE);
preferencesFile = Base.getSettingsFile(PREFS_FILE);
if (!preferencesFile.exists()) {
@@ -181,14 +192,12 @@ public class Preferences extends JComponent {
public Preferences() {
- // setup frame for the prefs
+ // setup dialog for the prefs
- //frame = new JFrame("Preferences");
- frame = new JDialog(editor, "Preferences", true);
- //frame.setResizable(false);
+ dialog = new JDialog(editor, "Preferences", true);
+ dialog.setResizable(false);
- //Container pain = this;
- Container pain = frame.getContentPane();
+ Container pain = dialog.getContentPane();
pain.setLayout(null);
int top = GUI_BIG;
@@ -284,20 +293,6 @@ public class Preferences extends JComponent {
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");
@@ -320,21 +315,6 @@ public class Preferences extends JComponent {
// 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);
@@ -362,9 +342,6 @@ public class Preferences extends JComponent {
// [ 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) {
@@ -376,9 +353,6 @@ public class Preferences extends JComponent {
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;
@@ -398,32 +372,39 @@ public class Preferences extends JComponent {
// finish up
wide = right + GUI_BIG;
- high = top + GUI_SMALL; //GUI_BIG;
- setSize(wide, high);
+ high = top + GUI_SMALL;
+ //setSize(wide, high);
// closing the window is same as hitting cancel button
- frame.addWindowListener(new WindowAdapter() {
+ dialog.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
disposeFrame();
}
});
- Container content = frame.getContentPane();
- content.setLayout(new BorderLayout());
- content.add(this, BorderLayout.CENTER);
-
- frame.pack();
+ ActionListener disposer = new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ disposeFrame();
+ }
+ };
+ Base.registerWindowCloseKeys(dialog.getRootPane(), disposer);
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
- frame.setLocation((screen.width - wide) / 2,
+ dialog.setLocation((screen.width - wide) / 2,
(screen.height - high) / 2);
+ dialog.pack(); // get insets
+ Insets insets = dialog.getInsets();
+ dialog.setSize(wide + insets.left + insets.right,
+ high + insets.top + insets.bottom);
+
+
// handle window closing commands for ctrl/cmd-W or hitting ESC.
- addKeyListener(new KeyAdapter() {
+ pain.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
KeyStroke wc = Editor.WINDOW_CLOSE_KEYSTROKE;
if ((e.getKeyCode() == KeyEvent.VK_ESCAPE) ||
@@ -435,6 +416,26 @@ public class Preferences extends JComponent {
}
+ /*
+ protected JRootPane createRootPane() {
+ System.out.println("creating root pane esc received");
+
+ ActionListener actionListener = new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ //setVisible(false);
+ System.out.println("esc received");
+ }
+ };
+
+ JRootPane rootPane = new JRootPane();
+ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
+ rootPane.registerKeyboardAction(actionListener, stroke,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ return rootPane;
+ }
+ */
+
+
public Dimension getPreferredSize() {
return new Dimension(wide, high);
}
@@ -447,7 +448,7 @@ public class Preferences extends JComponent {
* Close the window after an OK or Cancel.
*/
public void disposeFrame() {
- frame.dispose();
+ dialog.dispose();
}
@@ -487,7 +488,7 @@ public class Preferences extends JComponent {
externalEditorBox.setSelected(getBoolean("editor.external"));
checkUpdatesBox.setSelected(getBoolean("update.check"));
- frame.show();
+ dialog.show();
}
diff --git a/app/Sketch.java b/app/Sketch.java
index bd61638d0..56f68250d 100644
--- a/app/Sketch.java
+++ b/app/Sketch.java
@@ -1,7 +1,7 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
- Part of the Arduino project - http://arduino.berlios.de/
+ 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
@@ -19,8 +19,6 @@
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
-
- $Id$
*/
package processing.app;
@@ -67,7 +65,12 @@ public class Sketch {
public File codeFolder;
static final int PDE = 0;
- static final int JAVA = 1;
+ static final int CPP = 1;
+ static final int C = 2;
+ static final int HEADER = 3;
+
+ static final String flavorExtensionsReal[] = new String[] { ".pde", ".cpp", ".c", ".h" };
+ static final String flavorExtensionsShown[] = new String[] { "", ".cpp", ".c", ".h" };
public SketchCode current;
int codeCount;
@@ -104,6 +107,8 @@ public class Sketch {
name = mainFilename.substring(0, mainFilename.length() - 4);
} else if (mainFilename.endsWith(".c")) {
name = mainFilename.substring(0, mainFilename.length() - 2);
+ } else if (mainFilename.endsWith(".h")) {
+ name = mainFilename.substring(0, mainFilename.length() - 2);
} else if (mainFilename.endsWith(".cpp")) {
name = mainFilename.substring(0, mainFilename.length() - 4);
}
@@ -157,9 +162,11 @@ public class Sketch {
for (int i = 0; i < list.length; i++) {
if (list[i].endsWith(".pde")) codeCount++;
else if (list[i].endsWith(".c")) codeCount++;
+ else if (list[i].endsWith(".h")) codeCount++;
else if (list[i].endsWith(".cpp")) codeCount++;
else if (list[i].endsWith(".pde.x")) hiddenCount++;
else if (list[i].endsWith(".c.x")) hiddenCount++;
+ else if (list[i].endsWith(".h.x")) hiddenCount++;
else if (list[i].endsWith(".cpp.x")) hiddenCount++;
}
@@ -180,13 +187,19 @@ public class Sketch {
code[codeCounter++] =
new SketchCode(list[i].substring(0, list[i].length() - 2),
new File(folder, list[i]),
- JAVA);
+ C);
+
+ } else if (list[i].endsWith(".h")) {
+ code[codeCounter++] =
+ new SketchCode(list[i].substring(0, list[i].length() - 2),
+ new File(folder, list[i]),
+ HEADER);
} else if (list[i].endsWith(".cpp")) {
code[codeCounter++] =
new SketchCode(list[i].substring(0, list[i].length() - 4),
new File(folder, list[i]),
- JAVA);
+ CPP);
} else if (list[i].endsWith(".pde.x")) {
hidden[hiddenCounter++] =
@@ -198,12 +211,17 @@ public class Sketch {
hidden[hiddenCounter++] =
new SketchCode(list[i].substring(0, list[i].length() - 4),
new File(folder, list[i]),
- JAVA);
+ C);
+ } else if (list[i].endsWith(".h.x")) {
+ hidden[hiddenCounter++] =
+ new SketchCode(list[i].substring(0, list[i].length() - 4),
+ new File(folder, list[i]),
+ HEADER);
} else if (list[i].endsWith(".cpp.x")) {
hidden[hiddenCounter++] =
new SketchCode(list[i].substring(0, list[i].length() - 6),
new File(folder, list[i]),
- JAVA);
+ CPP);
}
}
@@ -314,8 +332,7 @@ public class Sketch {
renamingCode = true;
String prompt = (current == code[0]) ?
"New name for sketch:" : "New name for file:";
- String oldName =
- (current.flavor == PDE) ? current.name : current.name + ".cpp";
+ String oldName = current.name + flavorExtensionsShown[current.flavor];
editor.status.edit(prompt, oldName);
}
@@ -348,6 +365,7 @@ public class Sketch {
}
if (newName.trim().equals(".c") ||
+ newName.trim().equals(".h") ||
newName.trim().equals(".pde") ||
newName.trim().equals(".cpp")) {
return;
@@ -363,23 +381,28 @@ public class Sketch {
newName = newName.substring(0, newName.length() - 4);
newFlavor = PDE;
- } else if (newName.endsWith(".c") || newName.endsWith(".cpp")) {
+ } else if (newName.endsWith(".c") || newName.endsWith(".cpp") ||
+ newName.endsWith(".h")) {
// don't show this error if creating a new tab
if (renamingCode && (code[0] == current)) {
Base.showWarning("Problem with rename",
- "The main .pde file cannot be .c or .cpp file.\n" +
+ "The main .pde file cannot be .c, .cpp, or .h file.\n" +
"(It may be time for your to graduate to a\n" +
"\"real\" programming environment)", null);
return;
}
newFilename = newName;
- if(newName.endsWith(".c"))
+ if(newName.endsWith(".c")) {
newName = newName.substring(0, newName.length() - 2);
- else if(newName.endsWith(".cpp"))
+ newFlavor = C;
+ } if(newName.endsWith(".h")) {
+ newName = newName.substring(0, newName.length() - 2);
+ newFlavor = HEADER;
+ } else if(newName.endsWith(".cpp")) {
newName = newName.substring(0, newName.length() - 4);
- newFlavor = JAVA;
-
+ newFlavor = CPP;
+ }
} else {
newFilename = newName + ".pde";
newFlavor = PDE;
@@ -538,7 +561,7 @@ public class Sketch {
sortCode();
// set the new guy as current
- setCurrent(newName);
+ setCurrent(newName + flavorExtensionsShown[newFlavor]);
// update the tabs
//editor.header.repaint();
@@ -572,7 +595,8 @@ public class Sketch {
Object[] options = { "OK", "Cancel" };
String prompt = (current == code[0]) ?
"Are you sure you want to delete this sketch?" :
- "Are you sure you want to delete \"" + current.name + "\"?";
+ "Are you sure you want to delete \"" + current.name +
+ flavorExtensionsShown[current.flavor] + "\"?";
int result = JOptionPane.showOptionDialog(editor,
prompt,
"Delete",
@@ -684,9 +708,14 @@ public class Sketch {
public void unhideCode(String what) {
SketchCode unhideCode = null;
-
+ String name = what.substring(0,
+ (what.indexOf(".") == -1 ? what.length() : what.indexOf(".")));
+ String extension = what.indexOf(".") == -1 ? "" :
+ what.substring(what.indexOf("."));
+
for (int i = 0; i < hiddenCount; i++) {
- if (hidden[i].name.equals(what)) {
+ if (hidden[i].name.equals(name) &&
+ Sketch.flavorExtensionsShown[hidden[i].flavor].equals(extension)) {
//unhideIndex = i;
unhideCode = hidden[i];
@@ -730,8 +759,8 @@ public class Sketch {
/**
* Sets the modified value for the code in the frontmost tab.
*/
- public void setModified() {
- current.modified = true;
+ public void setModified(boolean state) {
+ current.modified = state;
calcModified();
}
@@ -996,6 +1025,26 @@ public class Sketch {
// it move instead of copy, they can do it by hand
File sourceFile = new File(directory, filename);
+ // now do the work of adding the file
+ addFile(sourceFile);
+ }
+
+
+ /**
+ * Add a file to the sketch.
+ *
+ * .pde or .java files will be added to the sketch folder.
+ * .jar, .class, .dll, .jnilib, and .so files will all
+ * be added to the "code" folder.
+ * All other files will be added to the "data" folder.
+ *
+ * If they don't exist already, the "code" or "data" folder
+ * will be created.
+ *
+ * @return true if successful.
+ */
+ public boolean addFile(File sourceFile) {
+ String filename = sourceFile.getName();
File destFile = null;
boolean addingCode = false;
@@ -1012,6 +1061,7 @@ public class Sketch {
} else if (filename.toLowerCase().endsWith(".pde") ||
filename.toLowerCase().endsWith(".c") ||
+ filename.toLowerCase().endsWith(".h") ||
filename.toLowerCase().endsWith(".cpp")) {
destFile = new File(this.folder, filename);
addingCode = true;
@@ -1028,7 +1078,7 @@ public class Sketch {
"This file has already been copied to the\n" +
"location where you're trying to add it.\n" +
"I ain't not doin nuthin'.", null);
- return;
+ return false;
}
// in case the user is "adding" the code in an attempt
@@ -1036,10 +1086,12 @@ public class Sketch {
if (!sourceFile.equals(destFile)) {
try {
Base.copyFile(sourceFile, destFile);
+
} catch (IOException e) {
Base.showWarning("Error adding file",
"Could not add '" + filename +
"' to the sketch.", e);
+ return false;
}
}
@@ -1050,9 +1102,15 @@ public class Sketch {
if (newName.toLowerCase().endsWith(".pde")) {
newName = newName.substring(0, newName.length() - 4);
newFlavor = PDE;
- } else {
+ } else if (newName.toLowerCase().endsWith(".c")) {
newName = newName.substring(0, newName.length() - 2);
- newFlavor = JAVA;
+ newFlavor = C;
+ } else if (newName.toLowerCase().endsWith(".h")) {
+ newName = newName.substring(0, newName.length() - 2);
+ newFlavor = HEADER;
+ } else { // ".cpp"
+ newName = newName.substring(0, newName.length() - 4);
+ newFlavor = CPP;
}
// see also "nameCode" for identical situation
@@ -1062,6 +1120,7 @@ public class Sketch {
setCurrent(newName);
editor.header.repaint();
}
+ return true;
}
@@ -1145,8 +1204,15 @@ public class Sketch {
* based on a name (used by codeNew and codeRename).
*/
protected void setCurrent(String findName) {
+ SketchCode unhideCode = null;
+ String name = findName.substring(0,
+ (findName.indexOf(".") == -1 ? findName.length() : findName.indexOf(".")));
+ String extension = findName.indexOf(".") == -1 ? "" :
+ findName.substring(findName.indexOf("."));
+
for (int i = 0; i < codeCount; i++) {
- if (findName.equals(code[i].name)) {
+ if (name.equals(code[i].name) &&
+ Sketch.flavorExtensionsShown[code[i].flavor].equals(extension)) {
setCurrent(i);
return;
}
@@ -1264,6 +1330,20 @@ public class Sketch {
*/
protected String build(Target target, String buildPath, String suggestedClassName)
throws RunnerException {
+
+ // build unbuilt buildable libraries
+ // completely independent from sketch, so run all the time
+ LibraryManager libraryManager = new LibraryManager();
+ try {
+ libraryManager.buildAllUnbuilt();
+ } catch (RunnerException re) {
+ throw new RunnerException(re.getMessage());
+ } catch (Exception ex) {
+ throw new RunnerException(ex.toString());
+ }
+ // update sketchbook menu, this adds examples of any built libs
+ editor.sketchbook.rebuildMenus();
+
// make sure the user didn't hide the sketch folder
ensureExistence();
@@ -1316,7 +1396,7 @@ public class Sketch {
// check to see if multiple files that include a .java file
externalRuntime = false;
for (int i = 0; i < codeCount; i++) {
- if (code[i].flavor == JAVA) {
+ if (code[i].flavor == C || code[i].flavor == CPP) {
externalRuntime = true;
break;
}
@@ -1382,7 +1462,6 @@ public class Sketch {
//System.out.println();
} else {
- //code[0].preprocName = className + "." + Preferences.get("build.extension");
code[0].preprocName = className + ".cpp";
}
@@ -1408,6 +1487,7 @@ public class Sketch {
}
errorLine -= code[errorFile].preprocOffset;
errorLine -= preprocessor.prototypeCount;
+ errorLine -= preprocessor.headerCount;
throw new RunnerException(re.getMessage(), errorFile,
errorLine, re.getColumn());
@@ -1449,6 +1529,7 @@ public class Sketch {
}
errorLine -= code[errorFile].preprocOffset;
errorLine -= preprocessor.prototypeCount;
+ errorLine -= preprocessor.headerCount;
throw new RunnerException(tsre.getMessage(),
errorFile, errorLine, errorColumn);
@@ -1507,13 +1588,12 @@ public class Sketch {
// 3. then loop over the code[] and save each .java file
for (int i = 0; i < codeCount; i++) {
- if (code[i].flavor == JAVA) {
+ if (code[i].flavor == CPP || code[i].flavor == C || code[i].flavor == HEADER) {
// no pre-processing services necessary for java files
// just write the the contents of 'program' to a .java file
// into the build directory. uses byte stream and reader/writer
// shtuff so that unicode bunk is properly handled
- //String filename = code[i].name + "." + Preferences.get("build.extension");
- String filename = code[i].name + ".cpp";
+ String filename = code[i].name + flavorExtensionsReal[code[i].flavor];
try {
Base.saveFile(code[i].program, new File(buildPath, filename));
} catch (IOException e) {
@@ -1538,7 +1618,7 @@ public class Sketch {
} catch (RunnerException re) {
throw new RunnerException(re.getMessage(),
re.file,
- re.line - preprocessor.prototypeCount,
+ re.line - preprocessor.prototypeCount - preprocessor.headerCount,
re.column);
} catch (Exception ex) {
// TODO better method for handling this?
diff --git a/app/Sketchbook.java b/app/Sketchbook.java
index 08c2be03a..460974a4c 100644
--- a/app/Sketchbook.java
+++ b/app/Sketchbook.java
@@ -382,10 +382,18 @@ public class Sketchbook {
} catch (IOException e) {
e.printStackTrace();
}
+
+ LibraryManager libManager = new LibraryManager();
+ ActionListener listener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ editor.handleOpen(e.getActionCommand());
+ }
+ };
try {
JMenu examplesMenu = new JMenu("Examples");
addSketches(examplesMenu, examplesFolder);
+ libManager.populateExamplesMenu(examplesMenu, listener);
menu.add(examplesMenu);
} catch (IOException e) {
e.printStackTrace();
@@ -542,8 +550,10 @@ public class Sketchbook {
list[i].equals("CVS")) continue;
File subfolder = new File(folder, list[i]);
+ if (!subfolder.isDirectory()) continue;
+
File exported = new File(subfolder, "library");
- File entry = new File(exported, list[i] + ".jar");
+ File entry = new File(exported, list[i] + ".o");
// if a .jar file of the same prefix as the folder exists
// inside the 'library' subfolder of the sketch
if (entry.exists()) {
@@ -556,7 +566,7 @@ public class Sketchbook {
Base.showMessage("Ignoring bad sketch name", mess);
continue;
}
-
+/*
// get the path for all .jar files in this code folder
String libraryClassPath =
Compiler.contentsToClassPath(exported);
@@ -565,12 +575,12 @@ public class Sketchbook {
librariesClassPath +=
File.pathSeparatorChar + libraryClassPath;
// need to associate each import with a library folder
- String packages[] = new String[0];
- //Compiler.packageListFromClassPath(libraryClassPath);
+ 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());
@@ -589,7 +599,7 @@ public class Sketchbook {
}
}
return ifound;
- }
+ /*return false;*/ }
/**
diff --git a/app/preproc/PdePreprocessor.java b/app/preproc/PdePreprocessor.java
index 37262f4c9..97d95a802 100755
--- a/app/preproc/PdePreprocessor.java
+++ b/app/preproc/PdePreprocessor.java
@@ -74,6 +74,9 @@ public class PdePreprocessor {
// stores number of built user-defined function prototypes
public int prototypeCount = 0;
+ // stores number of included library headers written
+ public int headerCount = 0;
+
/**
* These may change in-between (if the prefs panel adds this option)
* so grab them here on construction.
@@ -236,24 +239,22 @@ public class PdePreprocessor {
String returntype, functioname, parameterlist, prototype;
java.util.LinkedList prototypes = new java.util.LinkedList();
//System.out.println("prototypes:");
- //if (Preferences.get("build.extension").equals("cpp")) {
- while(matcher.contains(input, pattern)){
- result = matcher.getMatch();
- //System.out.println(result);
- returntype = result.group(1).toString();
- functioname = result.group(2).toString();
- parameterlist = result.group(3).toString().replace('\n', ' ');
- prototype = returntype + " " + functioname + "(" + parameterlist + ");";
- if(0 == functioname.compareTo("setup")){
- continue;
- }
- if(0 == functioname.compareTo("loop")){
- continue;
- }
- prototypes.add(prototype);
- //System.out.println(prototype);
+ while(matcher.contains(input, pattern)){
+ result = matcher.getMatch();
+ //System.out.println(result);
+ returntype = result.group(1).toString();
+ functioname = result.group(2).toString();
+ parameterlist = result.group(3).toString().replace('\n', ' ');
+ prototype = returntype + " " + functioname + "(" + parameterlist + ");";
+ if(0 == functioname.compareTo("setup")){
+ continue;
}
- //}
+ if(0 == functioname.compareTo("loop")){
+ continue;
+ }
+ prototypes.add(prototype);
+ //System.out.println(prototype);
+ }
// store # of prototypes so that line number reporting can be adjusted
prototypeCount = prototypes.size();
@@ -263,6 +264,7 @@ public class PdePreprocessor {
// through so that the line numbers when the compiler reports errors
// match those that will be highlighted in the PDE IDE
//
+ //System.out.println(program);
WLexer lexer = new WLexer(programReader);
//lexer.setTokenObjectClass("antlr.CommonHiddenStreamToken");
lexer.setTokenObjectClass("processing.app.preproc.CToken");
@@ -329,7 +331,6 @@ public class PdePreprocessor {
// output the code
//
WEmitter emitter = new WEmitter(lexer.getPreprocessorInfoChannel());
- //File streamFile = new File(buildPath, name + "." + Preferences.get("build.extension"));
File streamFile = new File(buildPath, name + ".cpp");
PrintStream stream = new PrintStream(new FileOutputStream(streamFile));
@@ -381,6 +382,16 @@ public class PdePreprocessor {
*/
void writeHeader(PrintStream out, String className, java.util.LinkedList prototypes) {
out.print("#include \"WProgram.h\"\n");
+
+ // print library headers
+ LibraryManager libraryManager = new LibraryManager();
+ String[] headerFiles = libraryManager.getHeaderFiles();
+ for(int i = 0; i < headerFiles.length; ++i){
+ out.print("#include \"" + headerFiles[i] + "\"\n");
+ }
+
+ // record number of header lines written for error line adjustment
+ headerCount = headerFiles.length;
// print user defined prototypes
while(0 < prototypes.size()){
diff --git a/app/syntax/JEditTextArea.java b/app/syntax/JEditTextArea.java
index 942039748..ba367d360 100644
--- a/app/syntax/JEditTextArea.java
+++ b/app/syntax/JEditTextArea.java
@@ -1647,7 +1647,7 @@ public class JEditTextArea extends JComponent
inputHandler.keyTyped(evt);
break;
case KeyEvent.KEY_PRESSED:
- if (!editorListener.keyPressed(evt)) {
+ if ((editorListener != null) && !editorListener.keyPressed(evt)) {
inputHandler.keyPressed(evt);
}
break;
diff --git a/app/syntax/TextAreaPainter.java b/app/syntax/TextAreaPainter.java
index fb7eeeef3..b1e4ccc82 100644
--- a/app/syntax/TextAreaPainter.java
+++ b/app/syntax/TextAreaPainter.java
@@ -475,6 +475,42 @@ public class TextAreaPainter extends JComponent implements TabExpander
Token currentLineTokens;
Segment currentLine;
+ /**
+ * Accessor used by tools that want to hook in and grab the formatting.
+ */
+ public int getCurrentLineIndex() {
+ return currentLineIndex;
+ }
+
+ /**
+ * Accessor used by tools that want to hook in and grab the formatting.
+ */
+ public void setCurrentLineIndex(int what) {
+ currentLineIndex = what;
+ }
+
+ /**
+ * Accessor used by tools that want to hook in and grab the formatting.
+ */
+ public Token getCurrentLineTokens() {
+ return currentLineTokens;
+ }
+
+ /**
+ * Accessor used by tools that want to hook in and grab the formatting.
+ */
+ public void setCurrentLineTokens(Token tokens) {
+ currentLineTokens = tokens;
+ }
+
+ /**
+ * Accessor used by tools that want to hook in and grab the formatting.
+ */
+ public Segment getCurrentLine() {
+ return currentLine;
+ }
+
+
// protected members
protected JEditTextArea textArea;
diff --git a/app/tools/Archiver.java b/app/tools/Archiver.java
new file mode 100755
index 000000000..facf49dd5
--- /dev/null
+++ b/app/tools/Archiver.java
@@ -0,0 +1,156 @@
+/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Archiver - plugin tool for archiving sketches
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2004-05 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.tools;
+
+import processing.app.*;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import java.util.zip.*;
+
+
+public class Archiver {
+ Editor editor;
+
+ // someday these will be settable
+ boolean useDate = true; //false;
+ int digits = 3;
+
+ NumberFormat numberFormat;
+ SimpleDateFormat dateFormat;
+
+
+ public Archiver(Editor editor) {
+ this.editor = editor;
+
+ numberFormat = NumberFormat.getInstance();
+ numberFormat.setGroupingUsed(false); // no commas
+ numberFormat.setMinimumIntegerDigits(digits);
+
+ dateFormat = new SimpleDateFormat("yyMMdd");
+ }
+
+
+ public void show() {
+ // first save the sketch so that things don't archive strangely
+ boolean success = false;
+ try {
+ success = editor.sketch.save();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (!success) {
+ Base.showWarning("Couldn't archive sketch",
+ "Archiving the sketch has been canceled because\n" +
+ "the sketch couldn't save properly.", null);
+ return;
+ }
+
+ File location = editor.sketch.folder;
+ String name = location.getName();
+ File parent = new File(location.getParent());
+
+ //System.out.println("loc " + location);
+ //System.out.println("par " + parent);
+
+ File newbie = null;
+ String namely = null;
+ int index = 0;
+ do {
+ if (useDate) {
+ String purty = dateFormat.format(new Date());
+ String stamp = purty + ((char) ('a' + index));
+ namely = name + "-" + stamp;
+ newbie = new File(parent, namely + ".zip");
+
+ } else {
+ String diggie = numberFormat.format(index + 1);
+ namely = name + "-" + diggie;
+ newbie = new File(parent, namely + ".zip");
+ }
+ index++;
+ } while (newbie.exists());
+
+ try {
+ //System.out.println(newbie);
+ FileOutputStream zipOutputFile = new FileOutputStream(newbie);
+ ZipOutputStream zos = new ZipOutputStream(zipOutputFile);
+
+ // recursively fill the zip file
+ buildZip(location, name, zos);
+
+ // close up the jar file
+ zos.flush();
+ zos.close();
+
+ editor.message("Created archive " + newbie.getName() + ".");
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ public void buildZip(File dir, String sofar,
+ ZipOutputStream zos) throws IOException {
+ 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]);
+ String nowfar = (sofar == null) ?
+ files[i] : (sofar + "/" + files[i]);
+
+ if (sub.isDirectory()) {
+ // directories are empty entries and have / at the end
+ ZipEntry entry = new ZipEntry(nowfar + "/");
+ //System.out.println(entry);
+ zos.putNextEntry(entry);
+ zos.closeEntry();
+ buildZip(sub, nowfar, zos);
+
+ } else {
+ ZipEntry entry = new ZipEntry(nowfar);
+ entry.setTime(sub.lastModified());
+ zos.putNextEntry(entry);
+ zos.write(Base.grabFile(sub));
+ zos.closeEntry();
+ }
+ }
+ }
+}
+
+
+ /*
+ 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());
+ */
diff --git a/app/tools/AutoFormat.java b/app/tools/AutoFormat.java
index 2ca38272b..a24bc6021 100644
--- a/app/tools/AutoFormat.java
+++ b/app/tools/AutoFormat.java
@@ -3,7 +3,7 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2005 Ben Fry and Casey Reas
+ Copyright (c) 2005-06 Ben Fry and Casey Reas
Copyright (c) 2003 Martin Gomez, Ateneo de Manila University
This program is free software; you can redistribute it and/or modify
@@ -31,122 +31,20 @@ import java.util.StringTokenizer;
/**
- * Alternate handler for dealing with auto format,
- * contributed by Martin Gomez.
+ * Alternate handler for dealing with auto format.
+ * Contributed by Martin Gomez, additional bug fixes by Ben Fry.
*/
public class AutoFormat {
Editor editor;
-
- public AutoFormat(Editor editor) {
- this.editor = editor;
- }
-
-
- /*
- public void show() {
- String prog = editor.textarea.getText();
-
- // TODO re-enable history
- //history.record(prog, SketchHistory.BEAUTIFY);
-
- //int tabSize = Preferences.getInteger("editor.tabs.size");
-
- char program[] = prog.toCharArray();
- StringBuffer buffer = new StringBuffer();
- boolean gotBlankLine = false;
- int index = 0;
- int level = 0;
-
- while (index != program.length) {
- int begin = index;
- while ((program[index] != '\n') &&
- (program[index] != '\r')) {
- index++;
- if (program.length == index)
- break;
- }
- int end = index;
- if (index != program.length) {
- if ((index+1 != program.length) &&
- // treat \r\n from windows as one line
- (program[index] == '\r') &&
- (program[index+1] == '\n')) {
- index += 2;
- } else {
- index++;
- }
- } // otherwise don't increment
-
- String line = new String(program, begin, end-begin);
- line = line.trim();
-
- if (line.length() == 0) {
- if (!gotBlankLine) {
- // let first blank line through
- buffer.append('\n');
- gotBlankLine = true;
- }
- } else {
- //System.out.println(level);
- int idx = -1;
- String myline = line.substring(0);
- while (myline.lastIndexOf('}') != idx) {
- idx = myline.indexOf('}');
- myline = myline.substring(idx+1);
- level--;
- }
- //for (int i = 0; i < level*2; i++) {
- // TODO i've since forgotten how i made this work (maybe it's even
- // a bug) but for now, level is incrementing/decrementing in
- // steps of two. in the interest of getting a release out,
- // i'm just gonna roll with that since this function will prolly
- // be replaced entirely and there are other things to worry about.
- for (int i = 0; i < tabSize * level / 2; i++) {
- buffer.append(' ');
- }
- buffer.append(line);
- buffer.append('\n');
- //if (line.charAt(0) == '{') {
- //level++;
- //}
- idx = -1;
- myline = line.substring(0);
- while (myline.lastIndexOf('{') != idx) {
- idx = myline.indexOf('{');
- myline = myline.substring(idx+1);
- level++;
- }
- gotBlankLine = false;
- }
- }
-
- // save current (rough) selection point
- int selectionEnd = editor.textarea.getSelectionEnd();
-
- // replace with new bootiful text
- editor.setText(buffer.toString(), false);
-
- // make sure the caret would be past the end of the text
- if (buffer.length() < selectionEnd - 1) {
- selectionEnd = buffer.length() - 1;
- }
-
- // at least in the neighborhood
- editor.textarea.select(selectionEnd, selectionEnd);
-
- editor.sketch.setModified();
- //buttons.clear();
- }
- */
-
+ static final int BLOCK_MAXLEN = 1024;
StringBuffer strOut;
- String formattedText;
+ //String formattedText;
int indentValue;
String indentChar;
- String uhOh = null;
- String theStuff;
+ //String uhOh = null;
+ //String theStuff;
int EOF;
BufferedInputStream bin = null;
int nBytesRead, indexBlock, lineLength, lineNumber;
@@ -167,7 +65,6 @@ public class AutoFormat {
int s_tabs[][];
String w_if_, w_else, w_for, w_ds, w_case, w_cpp_comment, w_jdoc;
int jdoc, j;
- int BLOCK_MAXLEN;
char string[];
byte bstring[];
byte bblank;
@@ -183,10 +80,15 @@ public class AutoFormat {
String line_feed;
- static int outfil; // temporary
+ //static int outfil; // temporary
- public void comment() {
+ public AutoFormat(Editor editor) {
+ this.editor = editor;
+ }
+
+
+ public void comment() throws IOException {
int save_s_flg;
save_s_flg = s_flg;
@@ -194,7 +96,7 @@ public class AutoFormat {
c = string[j++] = getchr(); // extra char
while (done == 0) {
c = string[j++] = getchr();
- while(c != '/') {
+ while ((c != '/') && (j < string.length)) {
if(c == '\n' || c == '\r') {
lineNumber++;
putcoms();
@@ -216,7 +118,7 @@ public class AutoFormat {
}
- public char get_string() {
+ public char get_string() throws IOException {
char ch;
ch = '*';
while (true) {
@@ -275,8 +177,9 @@ public class AutoFormat {
}
- public void fprintf(int outfil, String out_string) {
- int out_len = out_string.length();
+ //public void fprintf(int outfil, String out_string) {
+ public void fprintf(String out_string) {
+ //int out_len = out_string.length();
String j_string = new String(string);
strOut.append(out_string);
}
@@ -287,491 +190,6 @@ public class AutoFormat {
}
- public void setUhOh(String s) {
- uhOh = s;
- }
-
-
- public String grabUhOh() {
- return uhOh;
- }
-
-
- public void show() {
- StringBuffer onechar;
-
- theStuff = editor.textarea.getText();
- strOut = new StringBuffer();
- indentValue = Preferences.getInteger("editor.tabs.size");
- indentChar = new String(" ");
-
- lineNumber = 0;
- BLOCK_MAXLEN = 256;
- c_level = if_lev = level = e_flg = paren = 0;
- a_flg = q_flg = j = b_flg = tabs = 0;
- if_flg = peek = -1;
- peekc = '`';
- s_flg = 1;
- bblank = ' ';
- jdoc = 0;
-
- s_level = new int[10];
- sp_flg = new int[20][10];
- s_ind = new int[20][10];
- s_if_lev = new int[10];
- s_if_flg = new int[10];
- ind = new int[10];
- p_flg = new int[10];
- s_tabs = new int[20][10];
-
- w_else = new String ("else");
- w_if_ = new String ("if");
- w_for = new String ("for");
- w_ds = new String ("default");
- w_case = new String ("case");
- w_cpp_comment = new String ("//");
- w_jdoc = new String ("/**");
- line_feed = new String ("\n");
-
- try { // opening input string
- // open for input
- ByteArrayInputStream in =
- new ByteArrayInputStream(theStuff.getBytes());
-
- // add buffering to that InputStream
- bin = new BufferedInputStream(in);
-
- } catch(Exception e) {
- System.out.println(e.toString());
- }
-
- // read as long as there is something to read
- EOF = 0; // = 1 set in getchr when EOF
-
- bArray = new byte[BLOCK_MAXLEN];
- string = new char[BLOCK_MAXLEN];
- try { // the whole process
- for (int ib = 0; ib < BLOCK_MAXLEN; ib++) bArray[ib] = '\0';
-
- lineLength = nBytesRead = 0;
- // read up a block - remember how many bytes read
- nBytesRead = bin.read(bArray);
- strBlock = new String(bArray);
-
- lineLength = nBytesRead;
- lineNumber = 1;
- indexBlock = -1;
- j = 0;
- while(EOF == 0)
- {
- c = getchr();
- switch(c)
- {
- default:
- string[j++] = c;
- if(c != ',')
- {
- l_char = c;
- }
- break;
- case ' ':
- case '\t':
- if(lookup(w_else) == 1)
- {
- gotelse();
- if(s_flg == 0 || j > 0)string[j++] = c;
- indent_puts();
- s_flg = 0;
- break;
- }
- if(s_flg == 0 || j > 0)string[j++] = c;
- break;
- case '\r': /* for MS Windows 95 */
- case '\n':
- lineNumber++;
- if (EOF==1)
- {
- break;
- }
- String j_string = new String(string);
-
- e_flg = lookup(w_else);
- if(e_flg == 1) gotelse();
- if (lookup_com(w_cpp_comment) == 1)
- {
- if (string[j] == '\n')
- {
- string[j] = '\0';
- j--;
- }
- }
-
- indent_puts();
- fprintf(outfil, line_feed);
- s_flg = 1;
- if(e_flg == 1)
- {
- p_flg[level]++;
- tabs++;
- }
- else
- if(p_char == l_char)
- {
- a_flg = 1;
- }
- break;
- case '{':
- if(lookup(w_else) == 1)gotelse();
- s_if_lev[c_level] = if_lev;
- s_if_flg[c_level] = if_flg;
- if_lev = if_flg = 0;
- c_level++;
- if(s_flg == 1 && p_flg[level] != 0)
- {
- p_flg[level]--;
- tabs--;
- }
- string[j++] = c;
- indent_puts();
- getnl() ;
- indent_puts();
- fprintf(outfil,"\n");
- tabs++;
- s_flg = 1;
- if(p_flg[level] > 0)
- {
- ind[level] = 1;
- level++;
- s_level[level] = c_level;
- }
- break;
- case '}':
- c_level--;
- if (c_level < 0)
- {
- EOF = 1;
- string[j++] = c;
- indent_puts();
- break;
- }
- if((if_lev = s_if_lev[c_level]-1) < 0)if_lev = 0;
- if_flg = s_if_flg[c_level];
- indent_puts();
- tabs--;
- p_tabs();
- peekc = getchr();
- if( peekc == ';')
- {
- onechar = new StringBuffer();
- onechar.append(c); /* } */
- onechar.append(';');
- fprintf(outfil, onechar.toString());
- peek = -1;
- peekc = '`';
- }
- else
- {
- onechar = new StringBuffer();
- onechar.append(c);
- fprintf(outfil, onechar.toString());
- peek = 1;
- }
- getnl();
- indent_puts();
- fprintf(outfil,"\n");
- s_flg = 1;
- if(c_level < s_level[level])
- if(level > 0) level--;
- if(ind[level] != 0)
- {
- tabs -= p_flg[level];
- p_flg[level] = 0;
- ind[level] = 0;
- }
- break;
- case '"':
- case '\'':
- string[j++] = c;
- cc = getchr();
- while(cc != c)
- {
- // max. length of line should be 256
- string[j++] = cc;
-
- if(cc == '\\')
- {
- cc = string[j++] = getchr();
- }
- if(cc == '\n')
- {
- lineNumber++;
- indent_puts();
- s_flg = 1;
- }
- cc = getchr();
-
- }
- string[j++] = cc;
- if(getnl() == 1)
- {
- l_char = cc;
- peek = 1;
- peekc = '\n';
- }
- break;
- case ';':
- string[j++] = c;
- indent_puts();
- if(p_flg[level] > 0 && ind[level] == 0)
- {
- tabs -= p_flg[level];
- p_flg[level] = 0;
- }
- getnl();
- indent_puts();
- fprintf(outfil,"\n");
- s_flg = 1;
- if(if_lev > 0)
- if(if_flg == 1)
- {
- if_lev--;
- if_flg = 0;
- }
- else if_lev = 0;
- break;
- case '\\':
- string[j++] = c;
- string[j++] = getchr();
- break;
- case '?':
- q_flg = 1;
- string[j++] = c;
- break;
- case ':':
- string[j++] = c;
- peekc = getchr();
- if(peekc == ':')
- {
- indent_puts();
- fprintf (outfil,":");
- peek = -1;
- peekc = '`';
- break;
- }
- else
- {
- int double_colon = 0;
- peek = 1;
- }
-
- if(q_flg == 1)
- {
- q_flg = 0;
- break;
- }
- if(lookup(w_ds) == 0 && lookup(w_case) == 0)
- {
- s_flg = 0;
- indent_puts();
- }
- else
- {
- tabs--;
- indent_puts();
- tabs++;
- }
- peekc = getchr();
- if(peekc == ';')
- {
- fprintf(outfil,";");
- peek = -1;
- peekc = '`';
- }
- else
- {
- peek = 1;
- }
- getnl();
- indent_puts();
- fprintf(outfil,"\n");
- s_flg = 1;
- break;
-
- case '/':
- c0 = string[j];
- string[j++] = c;
- peekc = getchr();
-
- if(peekc == '/')
- {
- string[j++] = peekc;
- peekc = '`';
- peek = -1;
- cpp_comment();
- fprintf(outfil,"\n");
- break;
- }
- else
- {
- peek = 1;
- }
-
- if(peekc != '*') {
- break;
- }
- else
- {
- if (j > 0) string[j--] = '\0';
- if (j > 0) indent_puts();
- string[j++] = '/';
- string[j++] = '*';
- peek = -1;
- peekc = '`';
- comment();
- break;
- }
- case '#':
- string[j++] = c;
- cc = getchr();
- while(cc != '\n')
- {
- string[j++] = cc;
- cc = getchr();
- }
- string[j++] = cc;
- s_flg = 0;
- indent_puts();
- s_flg = 1;
- break;
- case ')':
- paren--;
- if (paren < 0)
- {
- EOF = 1;
- }
- string[j++] = c;
- indent_puts();
- if(getnl() == 1)
- {
- peekc = '\n';
- peek = 1;
- if(paren != 0)
- {
- a_flg = 1;
- }
- else if(tabs > 0)
- {
- p_flg[level]++;
- tabs++;
- ind[level] = 0;
- }
- }
- break;
- case '(':
- string[j++] = c;
- paren++;
- if ((lookup(w_for) == 1))
- {
- c = get_string();
- while(c != ';') c = get_string();
- ct=0;
- int for_done = 0;
- while (for_done==0)
- {
- c = get_string();
- while(c != ')')
- {
- if(c == '(') ct++;
- c = get_string();
- }
- if(ct != 0)
- {
- ct--;
- }
- else for_done = 1;
- } /* endwhile for_done */
- paren--;
- if (paren < 0)
- {
- EOF = 1;
- }
- indent_puts();
- if(getnl() == 1)
- {
- peekc = '\n';
- peek = 1;
- p_flg[level]++;
- tabs++;
- ind[level] = 0;
- }
- break;
- }
-
- if(lookup(w_if_) == 1)
- {
- indent_puts();
- s_tabs[c_level][if_lev] = tabs;
- sp_flg[c_level][if_lev] = p_flg[level];
- s_ind[c_level][if_lev] = ind[level];
- if_lev++;
- if_flg = 1;
- }
- } /* end switch */
-
- String j_string = new String(string);
-
- } // end while not EOF
-
- //formattedText = strOut.toString();
-
- // save current (rough) selection point
- int selectionEnd = editor.textarea.getSelectionEnd();
-
- // make sure the caret would be past the end of the text
- if (strOut.length() < selectionEnd - 1) {
- selectionEnd = strOut.length() - 1;
- }
-
- // replace with new bootiful text
- // selectionEnd hopefully at least in the neighborhood
- editor.setText(strOut.toString(), selectionEnd, selectionEnd);
-
- editor.sketch.setModified();
-
- bin.close(); // close buff
-
- } catch (IOException ioe) {
- editor.error(ioe);
- //ioe.printStackTrace();
- }
-
-
- // () {} check
-
- String ck_paren = new String("left");
- if (paren < 0) ck_paren = "right";
-
- if (paren != 0) {
- setUhOh("Uh oh... too many " + ck_paren + " parentheses.");
-
- } else { // check braces only if parens are ok
- ck_paren = "left";
- if (c_level < 0) {
- ck_paren = "right";
- } else if (c_level != 0) {
- setUhOh("Uh oh... too many " + ck_paren + " curled braces.");
- }
- }
- }
-
-
- /* throw back the stuff to the editor */
- public String getFormattedText()
- {
- return formattedText;
- }
-
-
/* special edition of put string for comment processing */
public void putcoms()
{
@@ -793,26 +211,30 @@ public class AutoFormat {
{
if ((last_char != ';') && (sav_s_flg==1) )
{
- fprintf(outfil, strBuffer.substring(i,j));
+ //fprintf(outfil, strBuffer.substring(i,j));
+ fprintf(strBuffer.substring(i,j));
}
else
{
- fprintf(outfil, strBuffer);
+ //fprintf(outfil, strBuffer);
+ fprintf(strBuffer);
}
}
else
{
if (string[i]=='*' || jdoc == 0)
- fprintf (outfil, " "+strBuffer.substring(i,j));
+ //fprintf (outfil, " "+strBuffer.substring(i,j));
+ fprintf (" "+strBuffer.substring(i,j));
else
- fprintf (outfil, " * "+strBuffer.substring(i,j));
+ //fprintf (outfil, " * "+strBuffer.substring(i,j));
+ fprintf (" * "+strBuffer.substring(i,j));
}
j = 0;
string[0] = '\0';
}
}
- public void cpp_comment()
+ public void cpp_comment() throws IOException
{
c = getchr();
while(c != '\n' && c != '\r' && j<133)
@@ -842,7 +264,7 @@ public class AutoFormat {
}
- public char getchr()
+ public char getchr() throws IOException
{
if((peek < 0) && (last_char != ' ') && (last_char != '\t'))
{
@@ -862,8 +284,8 @@ public class AutoFormat {
for (int ib=0; ib 0)
{
nBytesRead = bin.read(bArray);
@@ -876,14 +298,15 @@ public class AutoFormat {
}
else
{
+ //System.out.println("eof a");
EOF = 1;
peekc = '\0';
}
- }
- catch(IOException ioe)
- {
- System.out.println(ioe.toString());
- }
+ //}
+ //catch(IOException ioe)
+ //{
+ //System.out.println(ioe.toString());
+ //}
}
else
{
@@ -909,13 +332,14 @@ public class AutoFormat {
}
/* read to new_line */
- public int getnl()
+ public int getnl() throws IOException
{
int save_s_flg;
save_s_flg = tabs;
peekc = getchr();
- while(peekc == '\t' || peekc == ' ')
- {
+ //while ((peekc == '\t' || peekc == ' ') &&
+ // (j < string.length)) {
+ while (peekc == '\t' || peekc == ' ') {
string[j++] = peekc;
peek = -1;
peekc = '`';
@@ -1008,4 +432,512 @@ public class AutoFormat {
}
return (1);
}
-}
\ No newline at end of file
+
+
+ public void show() {
+ StringBuffer onechar;
+
+ String originalText = editor.textarea.getText();
+ strOut = new StringBuffer();
+ indentValue = Preferences.getInteger("editor.tabs.size");
+ indentChar = new String(" ");
+
+ lineNumber = 0;
+ //BLOCK_MAXLEN = 256;
+ c_level = if_lev = level = e_flg = paren = 0;
+ a_flg = q_flg = j = b_flg = tabs = 0;
+ if_flg = peek = -1;
+ peekc = '`';
+ s_flg = 1;
+ bblank = ' ';
+ jdoc = 0;
+
+ s_level = new int[10];
+ sp_flg = new int[20][10];
+ s_ind = new int[20][10];
+ s_if_lev = new int[10];
+ s_if_flg = new int[10];
+ ind = new int[10];
+ p_flg = new int[10];
+ s_tabs = new int[20][10];
+
+ w_else = new String ("else");
+ w_if_ = new String ("if");
+ w_for = new String ("for");
+ w_ds = new String ("default");
+ w_case = new String ("case");
+ w_cpp_comment = new String ("//");
+ w_jdoc = new String ("/**");
+ line_feed = new String ("\n");
+
+ // read as long as there is something to read
+ EOF = 0; // = 1 set in getchr when EOF
+
+ bArray = new byte[BLOCK_MAXLEN];
+ string = new char[BLOCK_MAXLEN];
+ try { // the whole process
+ // open for input
+ ByteArrayInputStream in =
+ new ByteArrayInputStream(originalText.getBytes());
+
+ // add buffering to that InputStream
+ bin = new BufferedInputStream(in);
+
+ for (int ib = 0; ib < BLOCK_MAXLEN; ib++) bArray[ib] = '\0';
+
+ lineLength = nBytesRead = 0;
+ // read up a block - remember how many bytes read
+ nBytesRead = bin.read(bArray);
+ strBlock = new String(bArray);
+
+ lineLength = nBytesRead;
+ lineNumber = 1;
+ indexBlock = -1;
+ j = 0;
+ while (EOF == 0)
+ {
+ c = getchr();
+ switch(c)
+ {
+ default:
+ string[j++] = c;
+ if(c != ',')
+ {
+ l_char = c;
+ }
+ break;
+
+ case ' ':
+ case '\t':
+ if(lookup(w_else) == 1)
+ {
+ gotelse();
+ if(s_flg == 0 || j > 0)string[j++] = c;
+ indent_puts();
+ s_flg = 0;
+ break;
+ }
+ if(s_flg == 0 || j > 0)string[j++] = c;
+ break;
+
+ case '\r': // for MS Windows 95
+ case '\n':
+ lineNumber++;
+ if (EOF==1)
+ {
+ break;
+ }
+ String j_string = new String(string);
+
+ e_flg = lookup(w_else);
+ if(e_flg == 1) gotelse();
+ if (lookup_com(w_cpp_comment) == 1)
+ {
+ if (string[j] == '\n')
+ {
+ string[j] = '\0';
+ j--;
+ }
+ }
+
+ indent_puts();
+ //fprintf(outfil, line_feed);
+ fprintf(line_feed);
+ s_flg = 1;
+ if(e_flg == 1)
+ {
+ p_flg[level]++;
+ tabs++;
+ }
+ else
+ if(p_char == l_char)
+ {
+ a_flg = 1;
+ }
+ break;
+
+ case '{':
+ if(lookup(w_else) == 1)gotelse();
+ s_if_lev[c_level] = if_lev;
+ s_if_flg[c_level] = if_flg;
+ if_lev = if_flg = 0;
+ c_level++;
+ if(s_flg == 1 && p_flg[level] != 0)
+ {
+ p_flg[level]--;
+ tabs--;
+ }
+ string[j++] = c;
+ indent_puts();
+ getnl() ;
+ indent_puts();
+ //fprintf(outfil,"\n");
+ fprintf("\n");
+ tabs++;
+ s_flg = 1;
+ if(p_flg[level] > 0)
+ {
+ ind[level] = 1;
+ level++;
+ s_level[level] = c_level;
+ }
+ break;
+
+ case '}':
+ c_level--;
+ if (c_level < 0)
+ {
+ EOF = 1;
+ //System.out.println("eof b");
+ string[j++] = c;
+ indent_puts();
+ break;
+ }
+ if ((if_lev = s_if_lev[c_level]-1) < 0)
+ if_lev = 0;
+ if_flg = s_if_flg[c_level];
+ indent_puts();
+ tabs--;
+ p_tabs();
+ peekc = getchr();
+ if( peekc == ';')
+ {
+ onechar = new StringBuffer();
+ onechar.append(c); // the }
+ onechar.append(';');
+ //fprintf(outfil, onechar.toString());
+ fprintf(onechar.toString());
+ peek = -1;
+ peekc = '`';
+ }
+ else
+ {
+ onechar = new StringBuffer();
+ onechar.append(c);
+ //fprintf(outfil, onechar.toString());
+ fprintf(onechar.toString());
+ peek = 1;
+ }
+ getnl();
+ indent_puts();
+ //fprintf(outfil,"\n");
+ fprintf("\n");
+ s_flg = 1;
+ if(c_level < s_level[level])
+ if(level > 0) level--;
+ if(ind[level] != 0)
+ {
+ tabs -= p_flg[level];
+ p_flg[level] = 0;
+ ind[level] = 0;
+ }
+ break;
+
+ case '"':
+ case '\'':
+ string[j++] = c;
+ cc = getchr();
+ while(cc != c)
+ {
+ // max. length of line should be 256
+ string[j++] = cc;
+
+ if(cc == '\\')
+ {
+ cc = string[j++] = getchr();
+ }
+ if(cc == '\n')
+ {
+ lineNumber++;
+ indent_puts();
+ s_flg = 1;
+ }
+ cc = getchr();
+
+ }
+ string[j++] = cc;
+ if(getnl() == 1)
+ {
+ l_char = cc;
+ peek = 1;
+ peekc = '\n';
+ }
+ break;
+
+ case ';':
+ string[j++] = c;
+ indent_puts();
+ if(p_flg[level] > 0 && ind[level] == 0)
+ {
+ tabs -= p_flg[level];
+ p_flg[level] = 0;
+ }
+ getnl();
+ indent_puts();
+ //fprintf(outfil,"\n");
+ fprintf("\n");
+ s_flg = 1;
+ if(if_lev > 0)
+ if(if_flg == 1)
+ {
+ if_lev--;
+ if_flg = 0;
+ }
+ else if_lev = 0;
+ break;
+
+ case '\\':
+ string[j++] = c;
+ string[j++] = getchr();
+ break;
+
+ case '?':
+ q_flg = 1;
+ string[j++] = c;
+ break;
+
+ case ':':
+ string[j++] = c;
+ peekc = getchr();
+ if(peekc == ':')
+ {
+ indent_puts();
+ //fprintf (outfil,":");
+ fprintf(":");
+ peek = -1;
+ peekc = '`';
+ break;
+ }
+ else
+ {
+ int double_colon = 0;
+ peek = 1;
+ }
+
+ if(q_flg == 1)
+ {
+ q_flg = 0;
+ break;
+ }
+ if(lookup(w_ds) == 0 && lookup(w_case) == 0)
+ {
+ s_flg = 0;
+ indent_puts();
+ }
+ else
+ {
+ tabs--;
+ indent_puts();
+ tabs++;
+ }
+ peekc = getchr();
+ if(peekc == ';')
+ {
+ //fprintf(outfil,";");
+ fprintf(";");
+ peek = -1;
+ peekc = '`';
+ }
+ else
+ {
+ peek = 1;
+ }
+ getnl();
+ indent_puts();
+ //fprintf(outfil,"\n");
+ fprintf("\n");
+ s_flg = 1;
+ break;
+
+ case '/':
+ c0 = string[j];
+ string[j++] = c;
+ peekc = getchr();
+
+ if(peekc == '/')
+ {
+ string[j++] = peekc;
+ peekc = '`';
+ peek = -1;
+ cpp_comment();
+ //fprintf(outfil,"\n");
+ fprintf("\n");
+ break;
+ }
+ else
+ {
+ peek = 1;
+ }
+
+ if(peekc != '*') {
+ break;
+ }
+ else
+ {
+ if (j > 0) string[j--] = '\0';
+ if (j > 0) indent_puts();
+ string[j++] = '/';
+ string[j++] = '*';
+ peek = -1;
+ peekc = '`';
+ comment();
+ break;
+ }
+
+ case '#':
+ string[j++] = c;
+ cc = getchr();
+ while(cc != '\n')
+ {
+ string[j++] = cc;
+ cc = getchr();
+ }
+ string[j++] = cc;
+ s_flg = 0;
+ indent_puts();
+ s_flg = 1;
+ break;
+
+ case ')':
+ paren--;
+ if (paren < 0)
+ {
+ EOF = 1;
+ //System.out.println("eof c");
+ }
+ string[j++] = c;
+ indent_puts();
+ if(getnl() == 1)
+ {
+ peekc = '\n';
+ peek = 1;
+ if(paren != 0)
+ {
+ a_flg = 1;
+ }
+ else if(tabs > 0)
+ {
+ p_flg[level]++;
+ tabs++;
+ ind[level] = 0;
+ }
+ }
+ break;
+
+ case '(':
+ string[j++] = c;
+ paren++;
+ if ((lookup(w_for) == 1))
+ {
+ c = get_string();
+ while(c != ';') c = get_string();
+ ct=0;
+ int for_done = 0;
+ while (for_done==0)
+ {
+ c = get_string();
+ while(c != ')')
+ {
+ if(c == '(') ct++;
+ c = get_string();
+ }
+ if(ct != 0)
+ {
+ ct--;
+ }
+ else for_done = 1;
+ } // endwhile for_done
+ paren--;
+ if (paren < 0)
+ {
+ EOF = 1;
+ //System.out.println("eof d");
+ }
+ indent_puts();
+ if(getnl() == 1)
+ {
+ peekc = '\n';
+ peek = 1;
+ p_flg[level]++;
+ tabs++;
+ ind[level] = 0;
+ }
+ break;
+ }
+
+ if(lookup(w_if_) == 1)
+ {
+ indent_puts();
+ s_tabs[c_level][if_lev] = tabs;
+ sp_flg[c_level][if_lev] = p_flg[level];
+ s_ind[c_level][if_lev] = ind[level];
+ if_lev++;
+ if_flg = 1;
+ }
+ } // end switch
+
+ //System.out.println("string len is " + string.length);
+ //if (EOF == 1) System.out.println(string);
+ String j_string = new String(string);
+
+ } // end while not EOF
+
+ /*
+ int bad;
+ while ((bad = bin.read()) != -1) {
+ System.out.print((char) bad);
+ }
+ */
+ /*
+ char bad;
+ //while ((bad = getchr()) != 0) {
+ while (true) {
+ getchr();
+ if (peek != -1) {
+ System.out.print(last_char);
+ } else {
+ break;
+ }
+ }
+ */
+
+ // save current (rough) selection point
+ int selectionEnd = editor.textarea.getSelectionEnd();
+
+ // make sure the caret would be past the end of the text
+ if (strOut.length() < selectionEnd - 1) {
+ selectionEnd = strOut.length() - 1;
+ }
+
+ bin.close(); // close buff
+
+ String formattedText = strOut.toString();
+ if (formattedText.equals(originalText)) {
+ editor.message("No changes necessary for Auto Format.");
+
+ } else {
+ // replace with new bootiful text
+ // selectionEnd hopefully at least in the neighborhood
+ editor.setText(formattedText, selectionEnd, selectionEnd);
+ editor.sketch.setModified(true);
+
+ // warn user if there are too many parens in either direction
+ if (paren != 0) {
+ editor.error("Warning: Too many " +
+ ((paren < 0) ? "right" : "left") +
+ " parentheses.");
+
+ } else if (c_level != 0) { // check braces only if parens are ok
+ editor.error("Warning: Too many " +
+ ((c_level < 0) ? "right" : "left") +
+ " curly braces.");
+ } else {
+ editor.message("Auto Format finished.");
+ }
+ }
+
+ } catch (Exception e) {
+ editor.error(e);
+ }
+ }
+}
diff --git a/app/tools/ExportFolder.java b/app/tools/ExportFolder.java
new file mode 100755
index 000000000..e4e7492bb
--- /dev/null
+++ b/app/tools/ExportFolder.java
@@ -0,0 +1,120 @@
+/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ ExportFolder - tool to export all sketches within a certain folder
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2005-06 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.tools;
+
+import processing.app.*;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import java.util.zip.*;
+
+import javax.swing.*;
+
+
+public class ExportFolder {
+ Editor editor;
+ static JFileChooser fc;
+
+
+ public ExportFolder(Editor editor) {
+ this.editor = editor;
+
+ if (fc == null) {
+ fc = new JFileChooser();
+ fc.setSelectedFile(new File(Sketchbook.getSketchbookPath()));
+ fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ }
+ }
+
+
+ public void show() {
+ if (fc.showOpenDialog(new JDialog()) != JFileChooser.APPROVE_OPTION) {
+ return;
+ }
+
+ File folder = fc.getSelectedFile();
+ // export everything under this folder
+
+ Vector sketches = new Vector();
+ try {
+ addSketches(sketches, folder);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ boolean success = true;
+ int counter = 0;
+
+ try {
+ // iterate through the list
+ Enumeration en = sketches.elements();
+ while (en.hasMoreElements()) {
+ editor.message("Exporting sketch " + (++counter) +
+ " of " + sketches.size());
+ String path = (String) en.nextElement();
+ editor.handleOpen(path);
+ // success may not be that useful, usually an ex is thrown
+ success = editor.sketch.exportApplet(new Target(
+ System.getProperty("user.dir") + File.separator + "lib" +
+ File.separator + "targets", Preferences.get("build.target")));
+ if (!success) break;
+ //System.out.println("success was " + success);
+ }
+ } catch (Exception e) {
+ editor.error(e);
+ success = false;
+ //e.printStackTrace();
+ }
+
+ if (success) {
+ editor.message("Done exporting.");
+ } // else the error message will be visible
+ }
+
+
+ protected void addSketches(Vector sketches, 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;
+
+ for (int i = 0; i < list.length; i++) {
+ if (list[i].charAt(0) == '.') continue;
+
+ File subfolder = new File(folder, list[i]);
+ File entry = new File(subfolder, list[i] + ".pde");
+ // if a .pde file of the same prefix as the folder exists..
+ if (entry.exists()) {
+ sketches.add(entry.getAbsolutePath());
+
+ } else if (subfolder.isDirectory()) { // only follow if a dir
+ addSketches(sketches, subfolder);
+ }
+ }
+ }
+}
diff --git a/build/macosx/Arduino.xcodeproj/project.pbxproj b/build/macosx/Arduino.xcodeproj/project.pbxproj
index ee8d031fe..dd4247912 100644
--- a/build/macosx/Arduino.xcodeproj/project.pbxproj
+++ b/build/macosx/Arduino.xcodeproj/project.pbxproj
@@ -219,6 +219,10 @@
33AF61B30965C54B00B514A9 /* WTreeParser.java in Sources */ = {isa = PBXBuildFile; fileRef = 33FFFE520965BD110016AC38 /* WTreeParser.java */; };
33AF61B40965C54B00B514A9 /* JEditTextArea.java in Sources */ = {isa = PBXBuildFile; fileRef = 33FFFE630965BD110016AC38 /* JEditTextArea.java */; };
33AF61B50965C54B00B514A9 /* Base.java in Sources */ = {isa = PBXBuildFile; fileRef = 33FFFE240965BD100016AC38 /* Base.java */; };
+ 33BEDDB109D6DC1300430D5B /* Library.java in Sources */ = {isa = PBXBuildFile; fileRef = 33BEDDAF09D6DC1300430D5B /* Library.java */; };
+ 33BEDDB209D6DC1300430D5B /* LibraryManager.java in Sources */ = {isa = PBXBuildFile; fileRef = 33BEDDB009D6DC1300430D5B /* LibraryManager.java */; };
+ 33BEDDD509D6E8D800430D5B /* Archiver.java in Sources */ = {isa = PBXBuildFile; fileRef = 33BEDDD309D6E8D800430D5B /* Archiver.java */; };
+ 33BEDDD609D6E8D800430D5B /* ExportFolder.java in Sources */ = {isa = PBXBuildFile; fileRef = 33BEDDD409D6E8D800430D5B /* ExportFolder.java */; };
33CF03B209662CB700F2C9A9 /* arduino.icns in Resources */ = {isa = PBXBuildFile; fileRef = 33CF03B009662CA800F2C9A9 /* arduino.icns */; };
33CF03CC09662DC000F2C9A9 /* mrj.jar in CopyFiles */ = {isa = PBXBuildFile; fileRef = 33AF620C0965D67900B514A9 /* mrj.jar */; settings = {JAVA_ARCHIVE_SUBDIR = ../shared/lib; }; };
33CF03CD09662DC000F2C9A9 /* RXTXcomm.jar in CopyFiles */ = {isa = PBXBuildFile; fileRef = 33AF620F0965D67A00B514A9 /* RXTXcomm.jar */; settings = {JAVA_ARCHIVE_SUBDIR = ../shared/lib; }; };
@@ -371,6 +375,10 @@
33AF620D0965D67900B514A9 /* oro.jar */ = {isa = PBXFileReference; lastKnownFileType = archive.jar; path = oro.jar; sourceTree = ""; };
33AF620E0965D67A00B514A9 /* registry.jar */ = {isa = PBXFileReference; lastKnownFileType = archive.jar; path = registry.jar; sourceTree = ""; };
33AF620F0965D67A00B514A9 /* RXTXcomm.jar */ = {isa = PBXFileReference; lastKnownFileType = archive.jar; path = RXTXcomm.jar; sourceTree = ""; };
+ 33BEDDAF09D6DC1300430D5B /* Library.java */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.java; path = Library.java; sourceTree = ""; };
+ 33BEDDB009D6DC1300430D5B /* LibraryManager.java */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.java; path = LibraryManager.java; sourceTree = ""; };
+ 33BEDDD309D6E8D800430D5B /* Archiver.java */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.java; path = Archiver.java; sourceTree = ""; };
+ 33BEDDD409D6E8D800430D5B /* ExportFolder.java */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.java; path = ExportFolder.java; sourceTree = ""; };
33CF03B009662CA800F2C9A9 /* arduino.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = arduino.icns; path = dist/arduino.icns; sourceTree = ""; };
33DD8FB6096AC8DA0013AF8F /* Arduino.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Arduino.app; sourceTree = BUILT_PRODUCTS_DIR; };
33FF01DC0965BD160016AC38 /* examples.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = examples.zip; sourceTree = ""; };
@@ -738,6 +746,8 @@
33FFFE220965BD100016AC38 /* app */ = {
isa = PBXGroup;
children = (
+ 33BEDDAF09D6DC1300430D5B /* Library.java */,
+ 33BEDDB009D6DC1300430D5B /* LibraryManager.java */,
33FFFE240965BD100016AC38 /* Base.java */,
33FFFE260965BD100016AC38 /* Compiler.java */,
33FFFE270965BD100016AC38 /* Editor.java */,
@@ -847,6 +857,8 @@
33FFFE710965BD110016AC38 /* tools */ = {
isa = PBXGroup;
children = (
+ 33BEDDD309D6E8D800430D5B /* Archiver.java */,
+ 33BEDDD409D6E8D800430D5B /* ExportFolder.java */,
33FFFE720965BD110016AC38 /* AutoFormat.java */,
);
path = tools;
@@ -1126,6 +1138,10 @@
33AF61B40965C54B00B514A9 /* JEditTextArea.java in Sources */,
33AF61B50965C54B00B514A9 /* Base.java in Sources */,
332D4DB609CF147F00BF81F6 /* Sizer.java in Sources */,
+ 33BEDDB109D6DC1300430D5B /* Library.java in Sources */,
+ 33BEDDB209D6DC1300430D5B /* LibraryManager.java in Sources */,
+ 33BEDDD509D6E8D800430D5B /* Archiver.java in Sources */,
+ 33BEDDD609D6E8D800430D5B /* ExportFolder.java in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/readme.txt b/readme.txt
index acd20b3ce..6bd07b97d 100644
--- a/readme.txt
+++ b/readme.txt
@@ -53,8 +53,13 @@ PWM now working on pin 11 (in addition to pins 9 and 10).
Slowed PWM frequency (on all three PWM pins) to 1KHz.
Now give an error if compiled sketch is too big.
Fixed abs(), min(), max(), and constrain() macros.
+Added menu items to the IDE to burn bootloader.
+Now display binary sketch size on upload, and give error if too big.
+Added C++ serial library.
+Resynced with Processing/Wiring IDE code (improved auto-format, faster logging
+to serial monitor console, other bug fixes)
-0003
+0003 - 2006.01.16
API Changes
Reversed the analog input pins to correspond to newer boards. This means