diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4db05adec..4182c98b0 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -67,16 +67,8 @@ jobs:
architecture: ${{ matrix.arch }}
- name: Setup Ant
uses: cedx/setup-ant@v3
- - name: Install Certificates for Code Signing
- if: ${{ matrix.os_prefix == 'macos' }}
- uses: apple-actions/import-codesign-certs@v3
- with:
- p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
- p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
- name: Build Release
run: ant -noinput -buildfile build/build.xml ${{ matrix.os_prefix }}-dist -Dversion="${{ github.sha }}"
- env:
- PROCESSING_APP_SIGNING: true
- name: Add artifact
uses: actions/upload-artifact@v4
with:
diff --git a/app/build.xml b/app/build.xml
index 5ca75a877..0b91c0a14 100644
--- a/app/build.xml
+++ b/app/build.xml
@@ -4,14 +4,14 @@
-
+
+ value="https://dlcdn.apache.org//ant/binaries/${ant.zip}" />
diff --git a/build/shared/lib/languages/PDE.properties b/build/shared/lib/languages/PDE.properties
index fb2042f22..66fcf1729 100644
--- a/build/shared/lib/languages/PDE.properties
+++ b/build/shared/lib/languages/PDE.properties
@@ -25,6 +25,7 @@ menu.file.close = Close
menu.file.save = Save
menu.file.save_as = Save As...
menu.file.export_application = Export Application...
+menu.file.export_pdez = Export as PDEZ...
menu.file.page_setup = Page Setup
menu.file.print = Print...
menu.file.preferences = Preferences...
diff --git a/core/src/processing/awt/PShapeJava2D.java b/core/src/processing/awt/PShapeJava2D.java
index 2b9b968a4..4238ef924 100644
--- a/core/src/processing/awt/PShapeJava2D.java
+++ b/core/src/processing/awt/PShapeJava2D.java
@@ -33,6 +33,9 @@ import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
+import java.util.Arrays;
+import java.awt.Color;
+
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PShapeSVG;
@@ -96,16 +99,29 @@ public class PShapeJava2D extends PShapeSVG {
*/
- static class LinearGradientPaint implements Paint {
+ public static class LinearGradientPaint implements Paint {
float x1, y1, x2, y2;
float[] offset;
int[] color;
+ Color[] colors;
int count;
float opacity;
+ AffineTransform xform;
+
+ public static enum CycleMethod {
+ NO_CYCLE,
+ REFLECT,
+ REPEAT
+ }
+
+ public static enum ColorSpaceType {
+ SRGB,
+ LINEAR_RGB
+ }
public LinearGradientPaint(float x1, float y1, float x2, float y2,
float[] offset, int[] color, int count,
- float opacity) {
+ float opacity, AffineTransform xform) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
@@ -114,6 +130,13 @@ public class PShapeJava2D extends PShapeSVG {
this.color = color;
this.count = count;
this.opacity = opacity;
+ this.xform = xform;
+
+ //set an array of type Color
+ this.colors = new Color[this.color.length];
+ for (int i = 0; i < this.color.length; i++) {
+ this.colors[i] = new Color(this.color[i], true);
+ }
}
public PaintContext createContext(ColorModel cm,
@@ -125,6 +148,35 @@ public class PShapeJava2D extends PShapeSVG {
(float) t2.getX(), (float) t2.getY());
}
+ public Point2D getStartPoint() {
+ return new Point2D.Float(this.x1, this.y1);
+ }
+
+ public Point2D getEndPoint() {
+ return new Point2D.Float(this.x2, this.y2);
+ }
+
+ /* MultipleGradientPaint methods... */
+ public AffineTransform getTransform() {
+ return this.xform;
+ }
+
+ public ColorSpaceType getColorSpace() {
+ return ColorSpaceType.SRGB;
+ }
+
+ public CycleMethod getCycleMethod() {
+ return CycleMethod.NO_CYCLE;
+ }
+
+ public Color[] getColors() {
+ return Arrays.copyOf(this.colors, this.colors.length);
+ }
+
+ public float[] getFractions() {
+ return Arrays.copyOf(this.offset, this.offset.length);
+ }
+
public int getTransparency() {
return TRANSLUCENT; // why not.. rather than checking each color
}
@@ -221,16 +273,29 @@ public class PShapeJava2D extends PShapeSVG {
}
- static class RadialGradientPaint implements Paint {
+ public static class RadialGradientPaint implements Paint {
float cx, cy, radius;
float[] offset;
int[] color;
+ Color[] colors;
int count;
float opacity;
+ AffineTransform xform;
+
+ public static enum CycleMethod {
+ NO_CYCLE,
+ REFLECT,
+ REPEAT
+ }
+
+ public static enum ColorSpaceType {
+ SRGB,
+ LINEAR_RGB
+ }
public RadialGradientPaint(float cx, float cy, float radius,
float[] offset, int[] color, int count,
- float opacity) {
+ float opacity, AffineTransform xform) {
this.cx = cx;
this.cy = cy;
this.radius = radius;
@@ -238,6 +303,13 @@ public class PShapeJava2D extends PShapeSVG {
this.color = color;
this.count = count;
this.opacity = opacity;
+ this.xform = xform;
+
+ //set an array of type Color
+ this.colors = new Color[this.color.length];
+ for (int i = 0; i < this.color.length; i++) {
+ this.colors[i] = new Color(this.color[i], true);
+ }
}
public PaintContext createContext(ColorModel cm,
@@ -246,6 +318,41 @@ public class PShapeJava2D extends PShapeSVG {
return new RadialGradientContext();
}
+ public Point2D getCenterPoint() {
+ return new Point2D.Double(this.cx, this.cy);
+ }
+
+ //TODO: investigate how to change a focus point for 0% x of the gradient
+ //for now default to center x/y
+ public Point2D getFocusPoint() {
+ return new Point2D.Double(this.cx, this.cy);
+ }
+
+ public float getRadius() {
+ return this.radius;
+ }
+
+ /* MultipleGradientPaint methods... */
+ public AffineTransform getTransform() {
+ return this.xform;
+ }
+
+ public ColorSpaceType getColorSpace() {
+ return ColorSpaceType.SRGB;
+ }
+
+ public CycleMethod getCycleMethod() {
+ return CycleMethod.NO_CYCLE;
+ }
+
+ public Color[] getColors() {
+ return Arrays.copyOf(this.colors, this.colors.length);
+ }
+
+ public float[] getFractions() {
+ return Arrays.copyOf(this.offset, this.offset.length);
+ }
+
public int getTransparency() {
return TRANSLUCENT;
}
@@ -305,14 +412,14 @@ public class PShapeJava2D extends PShapeSVG {
LinearGradient grad = (LinearGradient) gradient;
return new LinearGradientPaint(grad.x1, grad.y1, grad.x2, grad.y2,
grad.offset, grad.color, grad.count,
- opacity);
+ opacity, grad.transform);
} else if (gradient instanceof RadialGradient) {
// System.out.println("creating radial gradient");
RadialGradient grad = (RadialGradient) gradient;
return new RadialGradientPaint(grad.cx, grad.cy, grad.r,
grad.offset, grad.color, grad.count,
- opacity);
+ opacity, grad.transform);
}
return null;
}
diff --git a/core/src/processing/awt/PSurfaceAWT.java b/core/src/processing/awt/PSurfaceAWT.java
index 06337ba38..57649d6ba 100644
--- a/core/src/processing/awt/PSurfaceAWT.java
+++ b/core/src/processing/awt/PSurfaceAWT.java
@@ -33,8 +33,7 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
-import javax.swing.JFrame;
-import javax.swing.SwingUtilities;
+import javax.swing.*;
import processing.core.PApplet;
import processing.core.PConstants;
@@ -227,7 +226,7 @@ public class PSurfaceAWT extends PSurfaceNone {
}
- synchronized protected void render() {
+ protected void render() {
if (canvas.isDisplayable() &&
graphics.image != null) {
if (canvas.getBufferStrategy() == null) {
@@ -1021,6 +1020,9 @@ public class PSurfaceAWT extends PSurfaceNone {
//sketch.postWindowMoved(x - currentInsets.left, y - currentInsets.top);
sketch.postWindowMoved(x, y); // presumably user wants drawing area
}
+ }else{
+ // Solves #862 - Grey/White bar on right side of sketches
+ setFrameSize();
}
}
diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java
index ec16e4291..24c0d5d39 100644
--- a/core/src/processing/core/PApplet.java
+++ b/core/src/processing/core/PApplet.java
@@ -9663,7 +9663,7 @@ public class PApplet implements PConstants {
// WINDOW METHODS
- Map windowEventQueue = new ConcurrentHashMap<>();
+ Map windowEventQueue = new ConcurrentHashMap<>();
public void windowTitle(String title) {
@@ -9683,8 +9683,7 @@ public class PApplet implements PConstants {
* only the notification that the resize has happened.
*/
public void postWindowResized(int newWidth, int newHeight) {
- windowEventQueue.put("w", newWidth);
- windowEventQueue.put("h", newHeight);
+ windowEventQueue.put("wh", new WindowEventValuePairs(newWidth, newHeight));
}
@@ -9724,8 +9723,7 @@ public class PApplet implements PConstants {
frameMoved(newX, newY);
}
- windowEventQueue.put("x", newX);
- windowEventQueue.put("y", newY);
+ windowEventQueue.put("xy", new WindowEventValuePairs(newX, newY));
}
@@ -9734,21 +9732,28 @@ public class PApplet implements PConstants {
private void dequeueWindowEvents() {
- if (windowEventQueue.containsKey("x")) {
- windowX = windowEventQueue.remove("x");
- windowY = windowEventQueue.remove("y");
+ if (windowEventQueue.containsKey("xy")) {
+ WindowEventValuePairs xy = windowEventQueue.remove("xy");
+ windowX = xy.num1;
+ windowY = xy.num2;
windowMoved();
}
- if (windowEventQueue.containsKey("w")) {
- // these should already match width/height
- //windowResized(windowEventQueue.remove("w"),
- // windowEventQueue.remove("h"));
- windowEventQueue.remove("w");
- windowEventQueue.remove("h");
+ if (windowEventQueue.containsKey("wh")) {
+ WindowEventValuePairs wh = windowEventQueue.remove("wh");
windowResized();
}
}
+ protected class WindowEventValuePairs {
+
+ public int num1;
+ public int num2;
+
+ public WindowEventValuePairs(int num1, int num2) {
+ this.num1 = num1;
+ this.num2 = num2;
+ }
+ }
/**
* Scale the sketch as if it fits this specific width and height.
diff --git a/core/src/processing/core/PShapeSVG.java b/core/src/processing/core/PShapeSVG.java
index a51e94276..f8aa3400f 100644
--- a/core/src/processing/core/PShapeSVG.java
+++ b/core/src/processing/core/PShapeSVG.java
@@ -1397,7 +1397,8 @@ public class PShapeSVG extends PShape {
}
- void setColor(String colorText, boolean isFill) {
+ //making this public allows us to set gradient fills on a PShape
+ public void setColor(String colorText, boolean isFill) {
colorText = colorText.trim();
int opacityMask = fillColor & 0xFF000000;
boolean visible = true;
@@ -1620,7 +1621,7 @@ public class PShapeSVG extends PShape {
static public class Gradient extends PShapeSVG {
- AffineTransform transform;
+ public AffineTransform transform;
public float[] offset;
public int[] color;
diff --git a/java/libraries/svg/build.xml b/java/libraries/svg/build.xml
index cf82369e8..de0b9e41a 100644
--- a/java/libraries/svg/build.xml
+++ b/java/libraries/svg/build.xml
@@ -3,7 +3,7 @@
-
+
@@ -13,7 +13,7 @@
+ value="https://dlcdn.apache.org//xmlgraphics/batik/binaries/${batik.zip}" />
diff --git a/java/libraries/svg/src/processing/svg/GradientExtensionHandler.java b/java/libraries/svg/src/processing/svg/GradientExtensionHandler.java
new file mode 100644
index 000000000..370adbea9
--- /dev/null
+++ b/java/libraries/svg/src/processing/svg/GradientExtensionHandler.java
@@ -0,0 +1,209 @@
+package processing.svg;
+
+import static org.apache.batik.util.SVGConstants.*;
+
+import processing.awt.PShapeJava2D.LinearGradientPaint;
+import processing.awt.PShapeJava2D.RadialGradientPaint;
+
+import java.awt.Color;
+import java.awt.MultipleGradientPaint;
+import java.awt.Paint;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+
+import java.util.Objects;
+
+import org.apache.batik.svggen.DefaultExtensionHandler;
+import org.apache.batik.svggen.SVGColor;
+import org.apache.batik.svggen.SVGGeneratorContext;
+import org.apache.batik.svggen.SVGPaintDescriptor;
+import org.w3c.dom.Element;
+
+/**
+ * Extension of Batik's {@link DefaultExtensionHandler} which handles different kinds of Paint objects
+ * based on the extenstion by Martin Steiger https://gist.github.com/msteiger/4509119
+ * modified to work with Processing's SVG export library, by Benjamin Fox https://github.com/tracerstar
+ */
+public class GradientExtensionHandler extends DefaultExtensionHandler {
+
+ @Override
+ public SVGPaintDescriptor handlePaint(Paint paint, SVGGeneratorContext genCtx) {
+
+ // Handle LinearGradientPaint
+ if (paint instanceof LinearGradientPaint) {
+ return getLgpDescriptor((LinearGradientPaint) paint, genCtx);
+ }
+
+ // Handle RadialGradientPaint
+ if (paint instanceof RadialGradientPaint) {
+ return getRgpDescriptor((RadialGradientPaint) paint, genCtx);
+ }
+
+ return super.handlePaint(paint, genCtx);
+ }
+
+ private SVGPaintDescriptor getLgpDescriptor(LinearGradientPaint gradient, SVGGeneratorContext genCtx) {
+ Element gradElem = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_LINEAR_GRADIENT_TAG);
+
+ // Create and set unique XML id
+ String id = genCtx.getIDGenerator().generateID("gradient");
+ gradElem.setAttribute(SVG_ID_ATTRIBUTE, id);
+
+ // Set x,y pairs
+ Point2D startPt = gradient.getStartPoint();
+ gradElem.setAttribute("x1", String.valueOf(startPt.getX()));
+ gradElem.setAttribute("y1", String.valueOf(startPt.getY()));
+
+ Point2D endPt = gradient.getEndPoint();
+ gradElem.setAttribute("x2", String.valueOf(endPt.getX()));
+ gradElem.setAttribute("y2", String.valueOf(endPt.getY()));
+
+ //TODO: change this to be: addMgpAttributes after refactoring the paint methods
+ addLgpAttributes(gradElem, genCtx, gradient);
+
+ return new SVGPaintDescriptor("url(#" + id + ")", SVG_OPAQUE_VALUE, gradElem);
+ }
+
+ private SVGPaintDescriptor getRgpDescriptor(RadialGradientPaint gradient, SVGGeneratorContext genCtx) {
+ Element gradElem = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_RADIAL_GRADIENT_TAG);
+
+ // Create and set unique XML id
+ String id = genCtx.getIDGenerator().generateID("gradient");
+ gradElem.setAttribute(SVG_ID_ATTRIBUTE, id);
+
+ // Set x,y pairs
+ Point2D centerPt = gradient.getCenterPoint();
+ gradElem.setAttribute("cx", String.valueOf(centerPt.getX()));
+ gradElem.setAttribute("cy", String.valueOf(centerPt.getY()));
+
+ Point2D focusPt = gradient.getFocusPoint();
+ gradElem.setAttribute("fx", String.valueOf(focusPt.getX()));
+ gradElem.setAttribute("fy", String.valueOf(focusPt.getY()));
+
+ gradElem.setAttribute("r", String.valueOf(gradient.getRadius()));
+
+ //TODO: change this to be: addMgpAttributes after refactoring the paint methods
+ addRgpAttributes(gradElem, genCtx, gradient);
+
+ return new SVGPaintDescriptor("url(#" + id + ")", SVG_OPAQUE_VALUE, gradElem);
+ }
+
+
+ /*
+ Being lazy here to duplicate the methods so we don't have to refactor the two gradient paints
+ to implement java.awt.MultipleGradientPaint
+
+ TODO: make the effort to refactor them to properly implement java.awt.MultipleGradientPaint
+ */
+ private void addLgpAttributes(Element gradElem, SVGGeneratorContext genCtx, LinearGradientPaint gradient) {
+ gradElem.setAttribute(SVG_GRADIENT_UNITS_ATTRIBUTE, SVG_USER_SPACE_ON_USE_VALUE);
+
+ // Set cycle method
+ switch (gradient.getCycleMethod()) {
+ case REFLECT:
+ gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REFLECT_VALUE);
+ break;
+ case REPEAT:
+ gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REPEAT_VALUE);
+ break;
+ case NO_CYCLE:
+ default:
+ gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_PAD_VALUE); // this is the default
+ break;
+ }
+
+ // Set color space
+ switch (gradient.getColorSpace()) {
+ case LINEAR_RGB:
+ gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_LINEAR_RGB_VALUE);
+ break;
+ case SRGB:
+ default:
+ gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_SRGB_VALUE);
+ break;
+ }
+
+ // Set transform matrix if not identity
+ AffineTransform tf = gradient.getTransform();
+ if (!Objects.isNull(tf) && !tf.isIdentity()) {
+ String matrix = "matrix(" +
+ tf.getScaleX() + " " + tf.getShearX() + " " + tf.getTranslateX() + " " +
+ tf.getScaleY() + " " + tf.getShearY() + " " + tf.getTranslateY() + ")";
+ gradElem.setAttribute(SVG_TRANSFORM_ATTRIBUTE, matrix);
+ }
+
+ // Convert gradient stops
+ Color[] colors = gradient.getColors();
+ float[] fracs = gradient.getFractions();
+
+ for (int i = 0; i < colors.length; i++) {
+ Element stop = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_STOP_TAG);
+ SVGPaintDescriptor pd = SVGColor.toSVG(colors[i], genCtx);
+
+ stop.setAttribute(SVG_OFFSET_ATTRIBUTE, (int) (fracs[i] * 100.0f) + "%");
+ stop.setAttribute(SVG_STOP_COLOR_ATTRIBUTE, pd.getPaintValue());
+
+ if (colors[i].getAlpha() != 255) {
+ stop.setAttribute(SVG_STOP_OPACITY_ATTRIBUTE, pd.getOpacityValue());
+ }
+
+ gradElem.appendChild(stop);
+ }
+ }
+
+ private void addRgpAttributes(Element gradElem, SVGGeneratorContext genCtx, RadialGradientPaint gradient) {
+ gradElem.setAttribute(SVG_GRADIENT_UNITS_ATTRIBUTE, SVG_USER_SPACE_ON_USE_VALUE);
+
+ // Set cycle method
+ switch (gradient.getCycleMethod()) {
+ case REFLECT:
+ gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REFLECT_VALUE);
+ break;
+ case REPEAT:
+ gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REPEAT_VALUE);
+ break;
+ case NO_CYCLE:
+ default:
+ gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_PAD_VALUE); // this is the default
+ break;
+ }
+
+ // Set color space
+ switch (gradient.getColorSpace()) {
+ case LINEAR_RGB:
+ gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_LINEAR_RGB_VALUE);
+ break;
+ case SRGB:
+ default:
+ gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_SRGB_VALUE);
+ break;
+ }
+
+ // Set transform matrix if not identity
+ AffineTransform tf = gradient.getTransform();
+ if (!Objects.isNull(tf) && !tf.isIdentity()) {
+ String matrix = "matrix(" +
+ tf.getScaleX() + " " + tf.getShearX() + " " + tf.getTranslateX() + " " +
+ tf.getScaleY() + " " + tf.getShearY() + " " + tf.getTranslateY() + ")";
+ gradElem.setAttribute(SVG_TRANSFORM_ATTRIBUTE, matrix);
+ }
+
+ // Convert gradient stops
+ Color[] colors = gradient.getColors();
+ float[] fracs = gradient.getFractions();
+
+ for (int i = 0; i < colors.length; i++) {
+ Element stop = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_STOP_TAG);
+ SVGPaintDescriptor pd = SVGColor.toSVG(colors[i], genCtx);
+
+ stop.setAttribute(SVG_OFFSET_ATTRIBUTE, (int) (fracs[i] * 100.0f) + "%");
+ stop.setAttribute(SVG_STOP_COLOR_ATTRIBUTE, pd.getPaintValue());
+
+ if (colors[i].getAlpha() != 255) {
+ stop.setAttribute(SVG_STOP_OPACITY_ATTRIBUTE, pd.getOpacityValue());
+ }
+
+ gradElem.appendChild(stop);
+ }
+ }
+}
diff --git a/java/libraries/svg/src/processing/svg/PGraphicsSVG.java b/java/libraries/svg/src/processing/svg/PGraphicsSVG.java
index 3b170353b..857f5d93a 100644
--- a/java/libraries/svg/src/processing/svg/PGraphicsSVG.java
+++ b/java/libraries/svg/src/processing/svg/PGraphicsSVG.java
@@ -87,6 +87,10 @@ public class PGraphicsSVG extends PGraphicsJava2D {
g2 = new SVGGraphics2D(document);
((SVGGraphics2D) g2).setSVGCanvasSize(new Dimension(width, height));
+ //set the extension handler to allow linear and radial gradients to be exported as svg
+ GradientExtensionHandler gradH = new GradientExtensionHandler();
+ ((SVGGraphics2D) g2).setExtensionHandler(gradH);
+
// Done with our work, let's check on defaults and the rest
//super.beginDraw();
// Can't call super.beginDraw() because it'll nuke our g2
diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java
index 9c4bb8435..815792955 100644
--- a/java/src/processing/mode/java/JavaEditor.java
+++ b/java/src/processing/mode/java/JavaEditor.java
@@ -28,9 +28,13 @@ import java.awt.event.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
import javax.swing.*;
import javax.swing.event.*;
@@ -230,7 +234,17 @@ public class JavaEditor extends Editor {
}
});
- return buildFileMenu(new JMenuItem[] { exportApplication });
+ var exportPDEZ = new JMenuItem(Language.text("menu.file.export_pdez"));
+ exportPDEZ.addActionListener(e -> {
+ if (sketch.isUntitled() || sketch.isReadOnly()) {
+ Messages.showMessage("Save First", "Please first save the sketch.");
+ } else {
+ handleExportPDEZ();
+ }
+ });
+
+
+ return buildFileMenu(new JMenuItem[] { exportApplication, exportPDEZ });
}
@@ -491,6 +505,52 @@ public class JavaEditor extends Editor {
}
}
+ /**
+ * Handler for File → Export PDEZ
+ */
+ public void handleExportPDEZ() {
+ if (handleExportCheckModified()) {
+ var sketch = getSketch();
+ var folder = sketch.getFolder().toPath();
+ var target = new File(folder + ".pdez").toPath();
+ if (Files.exists(target)) {
+ try {
+ Platform.deleteFile(target.toFile());
+ } catch (IOException e) {
+ Messages.showError("Export Error", "Could not delete existing file: " + target, e);
+ }
+ }
+
+ try (var zs = new ZipOutputStream(Files.newOutputStream(target))) {
+ Files.walk(folder)
+ .filter(path -> !Files.isDirectory(path))
+ .forEach(path -> {
+ var zipEntry = new ZipEntry(folder.getParent().relativize(path).toString());
+ try {
+ zs.putNextEntry(zipEntry);
+ Files.copy(path, zs);
+ zs.closeEntry();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ if (Desktop.isDesktopSupported()) {
+ var desktop = Desktop.getDesktop();
+ if (desktop.isSupported(Desktop.Action.BROWSE_FILE_DIR)) {
+ desktop.browseFileDirectory(target.toFile());
+ } else {
+ try {
+ desktop.open(target.getParent().toFile());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
/**
* Checks to see if the sketch has been modified, and if so,