diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java
index 806771478..fe18ab4a2 100644
--- a/app/src/processing/app/Editor.java
+++ b/app/src/processing/app/Editor.java
@@ -1232,6 +1232,11 @@ public abstract class Editor extends JFrame implements RunnerListener {
public void showReference(String filename) {
File file = new File(mode.getReferenceFolder(), filename);
+ try {
+ file = file.getCanonicalFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
// Prepend with file:// and also encode spaces & other characters
Base.openURL(file.toURI().toString());
}
diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java
index 013313bad..78d0b1752 100644
--- a/app/src/processing/app/Sketch.java
+++ b/app/src/processing/app/Sketch.java
@@ -26,6 +26,9 @@ package processing.app;
import processing.core.*;
import java.awt.*;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
import java.io.*;
import javax.swing.*;
@@ -286,7 +289,8 @@ public class Sketch {
}
renamingCode = false;
- editor.status.edit("Name for new file:", "");
+ // editor.status.edit("Name for new file:", "");
+ promptForTabName("Name for new file:", "");
}
@@ -326,8 +330,99 @@ public class Sketch {
"New name for sketch:" : "New name for file:";
String oldName = (current.isExtension(mode.getDefaultExtension())) ?
current.getPrettyName() : current.getFileName();
- editor.status.edit(prompt, oldName);
+ // editor.status.edit(prompt, oldName);
+ promptForTabName(prompt, oldName);
}
+
+ /**
+ * Displays a dialog for renaming or creating a new tab
+ * @param prompt - msg to display
+ * @param oldName
+ */
+ protected void promptForTabName(String prompt, String oldName) {
+ final JTextField txtTabName = new JTextField();
+ txtTabName.addKeyListener(new KeyAdapter() {
+ // Forget ESC, the JDialog should handle it.
+
+ // Use keyTyped to catch when the feller is actually
+ // added to the text field. With keyTyped, as opposed to
+ // keyPressed, the keyCode will be zero, even if it's
+ // enter or backspace or whatever, so the keychar should
+ // be used instead. Grr.
+ public void keyTyped(KeyEvent event) {
+ //System.out.println("got event " + event);
+ char ch = event.getKeyChar();
+ if ((ch == '_') || (ch == '.') || // allow.pde and .java
+ (('A' <= ch) && (ch <= 'Z')) || (('a' <= ch) && (ch <= 'z'))) {
+ // These events are allowed straight through.
+ } else if (ch == ' ') {
+ String t = txtTabName.getText();
+ int start = txtTabName.getSelectionStart();
+ int end = txtTabName.getSelectionEnd();
+ txtTabName.setText(t.substring(0, start) + "_" + t.substring(end));
+ txtTabName.setCaretPosition(start + 1);
+ event.consume();
+ } else if ((ch >= '0') && (ch <= '9')) {
+ // getCaretPosition == 0 means that it's the first char
+ // and the field is empty.
+ // getSelectionStart means that it *will be* the first
+ // char, because the selection is about to be replaced
+ // with whatever is typed.
+ if ((txtTabName.getCaretPosition() == 0)
+ || (txtTabName.getSelectionStart() == 0)) {
+ // number not allowed as first digit
+ //System.out.println("bad number bad");
+ event.consume();
+ }
+ } else if (ch == KeyEvent.VK_ENTER) {
+ // Slightly ugly hack that ensures OK button of the dialog
+ // consumes the Enter key event. Since the text field is the
+ // default component in the dialog(see below), OK button doesn't
+ // consume Enter key event, by default.
+ Container parent = txtTabName.getParent();
+ while (!(parent instanceof JOptionPane)) {
+ parent = parent.getParent();
+ }
+ JOptionPane pane = (JOptionPane) parent;
+ final JPanel pnlBottom = (JPanel) pane.getComponent(pane
+ .getComponentCount() - 1);
+ for (int i = 0; i < pnlBottom.getComponents().length; i++) {
+ Component component = pnlBottom.getComponents()[i];
+ if (component instanceof JButton) {
+ final JButton okButton = ((JButton) component);
+ if (okButton.getText().equalsIgnoreCase("OK")) {
+ ActionListener[] actionListeners = okButton
+ .getActionListeners();
+ if (actionListeners.length > 0) {
+ actionListeners[0].actionPerformed(null);
+ event.consume();
+ }
+ }
+ }
+ }
+ }
+ else {
+ event.consume();
+ }
+ }
+ });
+
+ int userReply = JOptionPane.showOptionDialog(editor, new Object[] {
+ prompt, txtTabName },
+ "Tab Name",
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.PLAIN_MESSAGE,
+ null, new Object[] {
+ Preferences.PROMPT_OK,
+ Preferences.PROMPT_CANCEL },
+ txtTabName);
+
+ if (userReply == JOptionPane.OK_OPTION) {
+ String answer = txtTabName.getText();
+ nameCode(answer);
+ }
+ }
+
/**
@@ -382,7 +477,7 @@ public class Sketch {
if (current == code[0]) { // If this is the main tab, disallow
Base.showWarning("Problem with rename",
"The first tab cannot be a ." + newExtension + " file.\n" +
- "(It may be time for your to graduate to a\n" +
+ "(It may be time for you to graduate to a\n" +
"\"real\" programming environment, hotshot.)");
return;
}
diff --git a/app/src/processing/mode/java/PdeKeyListener.java b/app/src/processing/mode/java/PdeKeyListener.java
index e1acd5fad..c1ba6c30d 100644
--- a/app/src/processing/mode/java/PdeKeyListener.java
+++ b/app/src/processing/mode/java/PdeKeyListener.java
@@ -194,6 +194,9 @@ public class PdeKeyListener {
textarea.setSelectedText(spaces(tabSize));
event.consume();
return true;
+ } else if (!Preferences.getBoolean("editor.tabs.expand")) {
+ textarea.setSelectedText("\t");
+ event.consume();
}
break;
diff --git a/build/shared/revisions.txt b/build/shared/revisions.txt
index d4b60796b..30acaafcc 100644
--- a/build/shared/revisions.txt
+++ b/build/shared/revisions.txt
@@ -1,3 +1,25 @@
+PROCESSING 3.0a2 (REV 0229) - ?? August 2014
+
+
+[ fixes ]
+
++ The Examples and Reference weren't included in 3.0a1. Oops.
+ https://github.com/processing/processing/issues/2652
+
+
+[ changes ]
+
++ Neglected to mention with the previous release that the video library has
+ been removed from the default download. This decreases the size of the
+ Processing download by about 20%. In addition, it was only the video
+ library for the platform being downloaded, and with the return of cross-
+ platform application export, that could cause sadness. To use the video
+ library, use the "Add Library..." menu and select it from the list.
+
+
+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
PROCESSING 3.0a1 (REV 0228) - 26 July 2014
Kicking off the 3.0 release process. The focus for Processing 3 is improving
diff --git a/core/src/processing/core/PShapeSVG.java b/core/src/processing/core/PShapeSVG.java
index 7bac4fb62..aa18b6ed0 100644
--- a/core/src/processing/core/PShapeSVG.java
+++ b/core/src/processing/core/PShapeSVG.java
@@ -518,7 +518,7 @@ public class PShapeSVG extends PShape {
c == 'S' || c == 's' ||
c == 'Q' || c == 'q' || // quadratic beziers
c == 'T' || c == 't' ||
-// c == 'A' || c == 'a' || // elliptical arc
+ c == 'A' || c == 'a' || // elliptical arc
c == 'Z' || c == 'z' || // closepath
c == ',') {
separate = true;
@@ -816,6 +816,40 @@ public class PShapeSVG extends PShape {
}
break;
+ // A - elliptical arc to (absolute)
+ case 'A': {
+ float rx = PApplet.parseFloat(pathTokens[i + 1]);
+ float ry = PApplet.parseFloat(pathTokens[i + 2]);
+ float angle = PApplet.parseFloat(pathTokens[i + 3]);
+ boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
+ boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
+ float endX = PApplet.parseFloat(pathTokens[i + 6]);
+ float endY = PApplet.parseFloat(pathTokens[i + 7]);
+ parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 8;
+ prevCurve = true;
+ }
+ break;
+
+ // a - elliptical arc to (relative)
+ case 'a': {
+ float rx = PApplet.parseFloat(pathTokens[i + 1]);
+ float ry = PApplet.parseFloat(pathTokens[i + 2]);
+ float angle = PApplet.parseFloat(pathTokens[i + 3]);
+ boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
+ boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
+ float endX = cx + PApplet.parseFloat(pathTokens[i + 6]);
+ float endY = cy + PApplet.parseFloat(pathTokens[i + 7]);
+ parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 8;
+ prevCurve = true;
+ }
+ break;
+
case 'Z':
case 'z':
// since closing the path, the 'current' point needs
@@ -924,6 +958,93 @@ public class PShapeSVG extends PShape {
}
+ // Approximates elliptical arc by several bezier segments.
+ // Meets SVG standard requirements from:
+ // http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
+ // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
+ // Based on arc to bezier curve equations from:
+ // http://www.spaceroots.org/documents/ellipse/node22.html
+ private void parsePathArcto(float x1, float y1,
+ float rx, float ry,
+ float angle,
+ boolean fa, boolean fs,
+ float x2, float y2) {
+ if (x1 == x2 && y1 == y2) return;
+ if (rx == 0 || ry == 0) { parsePathLineto(x2, y2); return; }
+
+ rx = PApplet.abs(rx); ry = PApplet.abs(ry);
+
+ float phi = PApplet.radians(((angle % 360) + 360) % 360);
+ float cosPhi = PApplet.cos(phi), sinPhi = PApplet.sin(phi);
+
+ float x1r = ( cosPhi * (x1 - x2) + sinPhi * (y1 - y2)) / 2;
+ float y1r = (-sinPhi * (x1 - x2) + cosPhi * (y1 - y2)) / 2;
+
+ float cxr, cyr;
+ {
+ float A = (x1r*x1r) / (rx*rx) + (y1r*y1r) / (ry*ry);
+ if (A > 1) {
+ // No solution, scale ellipse up according to SVG standard
+ float sqrtA = PApplet.sqrt(A);
+ rx *= sqrtA; cxr = 0;
+ ry *= sqrtA; cyr = 0;
+ } else {
+ float k = ((fa == fs) ? -1f : 1f) *
+ PApplet.sqrt((rx*rx * ry*ry) / ((rx*rx * y1r*y1r) + (ry*ry * x1r*x1r)) - 1f);
+ cxr = k * rx * y1r / ry;
+ cyr = -k * ry * x1r / rx;
+ }
+ }
+
+ float cx = cosPhi * cxr - sinPhi * cyr + (x1 + x2) / 2;
+ float cy = sinPhi * cxr + cosPhi * cyr + (y1 + y2) / 2;
+
+ float phi1, phiDelta;
+ {
+ float sx = ( x1r - cxr) / rx, sy = ( y1r - cyr) / ry;
+ float tx = (-x1r - cxr) / rx, ty = (-y1r - cyr) / ry;
+ phi1 = PApplet.atan2(sy, sx);
+ phiDelta = (((PApplet.atan2(ty, tx) - phi1) % TWO_PI) + TWO_PI) % TWO_PI;
+ if (!fs) phiDelta -= TWO_PI;
+ }
+
+ // One segment can not cover more that PI, less than PI/2 is
+ // recommended to avoid visible inaccuracies caused by rounding errors
+ int segmentCount = PApplet.ceil(PApplet.abs(phiDelta) / TWO_PI * 4);
+
+ float inc = phiDelta / segmentCount;
+ float a = PApplet.sin(inc) *
+ (PApplet.sqrt(4 + 3 * PApplet.sq(PApplet.tan(inc / 2))) - 1) / 3;
+
+ float sinPhi1 = PApplet.sin(phi1), cosPhi1 = PApplet.cos(phi1);
+
+ float p1x = x1;
+ float p1y = y1;
+ float relq1x = a * (-rx * cosPhi * sinPhi1 - ry * sinPhi * cosPhi1);
+ float relq1y = a * (-rx * sinPhi * sinPhi1 + ry * cosPhi * cosPhi1);
+
+ for (int i = 0; i < segmentCount; i++) {
+ float eta = phi1 + (i + 1) * inc;
+ float sinEta = PApplet.sin(eta), cosEta = PApplet.cos(eta);
+
+ float p2x = cx + rx * cosPhi * cosEta - ry * sinPhi * sinEta;
+ float p2y = cy + rx * sinPhi * cosEta + ry * cosPhi * sinEta;
+ float relq2x = a * (-rx * cosPhi * sinEta - ry * sinPhi * cosEta);
+ float relq2y = a * (-rx * sinPhi * sinEta + ry * cosPhi * cosEta);
+
+ if (i == segmentCount - 1) { p2x = x2; p2y = y2; }
+
+ parsePathCode(BEZIER_VERTEX);
+ parsePathVertex(p1x + relq1x, p1y + relq1y);
+ parsePathVertex(p2x - relq2x, p2y - relq2y);
+ parsePathVertex(p2x, p2y);
+
+ p1x = p2x; relq1x = relq2x;
+ p1y = p2y; relq1y = relq2y;
+ }
+ }
+
+
/**
* Parse the specified SVG matrix into a PMatrix2D. Note that PMatrix2D
* is rotated relative to the SVG definition, so parameters are rearranged
diff --git a/core/todo.txt b/core/todo.txt
index a3ff01848..ecf2cc743 100644
--- a/core/todo.txt
+++ b/core/todo.txt
@@ -1,6 +1,14 @@
0229 core (3.0a2)
+pulls
+X implement A and a (elliptical arcs)
+X https://github.com/processing/processing/issues/169
+X http://code.google.com/p/processing/issues/detail?id=130
+X https://github.com/processing/processing/pull/2659
+X done with an approximation, if re-saving this will destroy data (docs)
+
+
applet removal
_ remove Applet as base class
_ performance issues on OS X (might be threading due to Applet)
@@ -418,8 +426,6 @@ _ for PShape, need to be able to set the origin (flash people)
CORE / PShapeSVG
-_ implement A and a (elliptical arcs)
-_ http://code.google.com/p/processing/issues/detail?id=130
_ implement support for SVG gradients from Inkscape
_ http://code.google.com/p/processing/issues/detail?id=1142
_ need to handle
-
+
diff --git a/pdex/build.xml b/pdex/build.xml
index 4cfc10aa0..3d4828f8a 100644
--- a/pdex/build.xml
+++ b/pdex/build.xml
@@ -1,25 +1,55 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/pdex/src/processing/mode/experimental/ASTGenerator.java b/pdex/src/processing/mode/experimental/ASTGenerator.java
index f68f8c2c1..5918e8e4b 100644
--- a/pdex/src/processing/mode/experimental/ASTGenerator.java
+++ b/pdex/src/processing/mode/experimental/ASTGenerator.java
@@ -268,7 +268,7 @@ public class ASTGenerator {
logE("No CU found!");
}
visitRecur((ASTNode) compilationUnit.types().get(0), codeTree);
- SwingWorker worker = new SwingWorker() {
+ SwingWorker