From a8c9830f9a601dbb537f8ff927f45bb0cd53a7f5 Mon Sep 17 00:00:00 2001 From: Akarshit Wal Date: Mon, 28 Sep 2015 19:43:40 +0530 Subject: [PATCH 0001/1035] Added the remove filter feature --- .../app/contrib/ContributionTab.java | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/app/src/processing/app/contrib/ContributionTab.java b/app/src/processing/app/contrib/ContributionTab.java index 9e005bc408..08f3655b44 100644 --- a/app/src/processing/app/contrib/ContributionTab.java +++ b/app/src/processing/app/contrib/ContributionTab.java @@ -23,6 +23,7 @@ package processing.app.contrib; import java.awt.Color; +import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.event.*; @@ -350,9 +351,9 @@ protected void setFilterText(String filter) { } - //TODO: this is causing a lot of bugs as the hint is wrongly firing applyFilter() class FilterField extends JTextField { Icon searchIcon; + JButton removeFilter; List filters; JLabel filterLabel; @@ -366,19 +367,40 @@ public FilterField () { setFont(Toolkit.getSansFont(14, Font.PLAIN)); searchIcon = Toolkit.getLibIconX("manager/search"); filterLabel.setIcon(searchIcon); + removeFilter = new JButton("X"); // TODO icon should be initialized here + removeFilter.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2)); + removeFilter.setBorderPainted(false); + removeFilter.setContentAreaFilled(false); + removeFilter.setCursor(Cursor.getDefaultCursor()); + removeFilter.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + setText(""); + filterField.requestFocusInWindow(); + } + }); //searchIcon = new ImageIcon(java.awt.Toolkit.getDefaultToolkit().getImage("NSImage://NSComputerTemplate")); setOpaque(false); //setBorder(BorderFactory.createMatteBorder(0, 33, 0, 0, searchIcon)); GroupLayout fl = new GroupLayout(this); setLayout(fl); - fl.setHorizontalGroup(fl.createSequentialGroup().addComponent(filterLabel)); + fl.setHorizontalGroup(fl + .createSequentialGroup() + .addComponent(filterLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, + GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addComponent(removeFilter)); + fl.setVerticalGroup(fl.createSequentialGroup() .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addGroup(fl.createParallelGroup() .addComponent(filterLabel) + .addComponent(removeFilter)) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)); + removeFilter.setVisible(false); filters = new ArrayList(); @@ -398,14 +420,17 @@ public void focusGained(FocusEvent focusEvent) { getDocument().addDocumentListener(new DocumentListener() { public void removeUpdate(DocumentEvent e) { + removeFilter.setVisible(!getText().isEmpty()); applyFilter(); } public void insertUpdate(DocumentEvent e) { + removeFilter.setVisible(!getText().isEmpty()); applyFilter(); } public void changedUpdate(DocumentEvent e) { + removeFilter.setVisible(!getText().isEmpty()); applyFilter(); } }); From 84e5ca16e6497affd0a9602a5d93487c3f933e2c Mon Sep 17 00:00:00 2001 From: Akarshit Wal Date: Thu, 1 Oct 2015 22:29:10 +0530 Subject: [PATCH 0002/1035] Core libraries added to the count of updates --- app/src/processing/app/contrib/ContributionListing.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/processing/app/contrib/ContributionListing.java b/app/src/processing/app/contrib/ContributionListing.java index cf35ff628c..bd3460d645 100644 --- a/app/src/processing/app/contrib/ContributionListing.java +++ b/app/src/processing/app/contrib/ContributionListing.java @@ -580,6 +580,11 @@ public int countUpdates(Base base) { count++; } } + for (Library lib : base.getActiveEditor().getMode().coreLibraries) { + if (hasUpdates(lib)) { + count++; + } + } for (ToolContribution tc : base.getToolContribs()) { if (hasUpdates(tc)) { count++; From 9aaba9717780155346b4baeed0f667d4b914e197 Mon Sep 17 00:00:00 2001 From: Akarshit Wal Date: Sun, 1 Nov 2015 04:20:53 +0530 Subject: [PATCH 0003/1035] fixes the removal of redundant contribution and update related issues --- app/src/processing/app/contrib/ListPanel.java | 17 +++++++++--- .../app/contrib/UpdateListPanel.java | 27 +++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/app/src/processing/app/contrib/ListPanel.java b/app/src/processing/app/contrib/ListPanel.java index 3d9a4fa874..5d41c96989 100644 --- a/app/src/processing/app/contrib/ListPanel.java +++ b/app/src/processing/app/contrib/ListPanel.java @@ -488,7 +488,6 @@ public void run() { }); } - public void contributionChanged(final Contribution oldContrib, final Contribution newContrib) { // TODO: this should already be on EDT, check it [jv] @@ -515,15 +514,25 @@ public void run() { }); } - public void filterLibraries(List filteredContributions) { synchronized (visibleContributions) { visibleContributions.clear(); - for (Contribution contribution : filteredContributions) { + for (Contribution contribution : panelByContribution.keySet()) { if (contribution.getType() == contributionTab.contribType) { - visibleContributions.add(contribution); + if (filteredContributions.contains(contribution)) { + if (panelByContribution.keySet().contains(contribution)) { + visibleContributions.add(contribution); + } + } } } +// for (Contribution contribution : filteredContributions) { +// if (contribution.getType() == contributionTab.contribType) { +// if(panelByContribution.keySet().contains(contribution)){ +// visibleContributions.add(contribution); +// } +// } +// } updatePanelOrdering(visibleContributions); } } diff --git a/app/src/processing/app/contrib/UpdateListPanel.java b/app/src/processing/app/contrib/UpdateListPanel.java index f5d292fac0..f60fb9702e 100644 --- a/app/src/processing/app/contrib/UpdateListPanel.java +++ b/app/src/processing/app/contrib/UpdateListPanel.java @@ -229,6 +229,9 @@ public void run() { if (!panelByContribution.containsKey(contribution)) { panelByContribution.put(contribution, newPanel); } + synchronized (visibleContributions) { + visibleContributions.add(contribution); + } if (newPanel != null) { newPanel.setContribution(contribution); add(newPanel); @@ -240,4 +243,28 @@ public void run() { }); } } + + @Override + public void contributionChanged(final Contribution oldContrib, + final Contribution newContrib) { + // TODO: this should already be on EDT, check it [jv] + EventQueue.invokeLater(new Runnable() { + public void run() { + synchronized (panelByContribution) { + DetailPanel panel = panelByContribution.get(oldContrib); + if (panel == null) { + contributionAdded(newContrib); + } else { + panelByContribution.remove(oldContrib); + } + } + synchronized (visibleContributions) { + if (visibleContributions.contains(oldContrib)) { + visibleContributions.remove(oldContrib); + } + updatePanelOrdering(visibleContributions); + } + } + }); + } } \ No newline at end of file From cdce741623c8423101cd3ba77b6304378c18d264 Mon Sep 17 00:00:00 2001 From: Akarshit Wal Date: Sun, 1 Nov 2015 04:23:48 +0530 Subject: [PATCH 0004/1035] added few todos for future --- app/src/processing/app/contrib/ContributionListing.java | 2 ++ app/src/processing/app/contrib/ListPanel.java | 1 + 2 files changed, 3 insertions(+) diff --git a/app/src/processing/app/contrib/ContributionListing.java b/app/src/processing/app/contrib/ContributionListing.java index cf35ff628c..d24b71a5b6 100644 --- a/app/src/processing/app/contrib/ContributionListing.java +++ b/app/src/processing/app/contrib/ContributionListing.java @@ -51,6 +51,8 @@ public class ContributionListing { List advertisedContributions; Map> librariesByCategory; Map librariesByImportHeader; + // TODO: Every contribution is getting added twice + // and nothing is replaced ever. List allContributions; boolean listDownloaded; boolean listDownloadFailed; diff --git a/app/src/processing/app/contrib/ListPanel.java b/app/src/processing/app/contrib/ListPanel.java index 5d41c96989..e0b60650d4 100644 --- a/app/src/processing/app/contrib/ListPanel.java +++ b/app/src/processing/app/contrib/ListPanel.java @@ -526,6 +526,7 @@ public void filterLibraries(List filteredContributions) { } } } + // TODO: Make the following loop work for optimization // for (Contribution contribution : filteredContributions) { // if (contribution.getType() == contributionTab.contribType) { // if(panelByContribution.keySet().contains(contribution)){ From 0eb5ba518ac40343a68cef09a13ecf9c9e5ce0fe Mon Sep 17 00:00:00 2001 From: Jeremy Laviole Date: Tue, 24 Nov 2015 17:55:40 +0100 Subject: [PATCH 0005/1035] Support of embedded images in SVGs, linked images are supported but should not be avoided if possible. Support of Text in SVGs (partial). Multiline text are also displayed. Text limitations : Inline styles are not supported. Not all fonts are supported (depending on your OS). Tested only on Inkscape SVGs. --- core/src/processing/core/PShape.java | 111 +++++++++++-- core/src/processing/core/PShapeSVG.java | 199 +++++++++++++++++++++++- 2 files changed, 291 insertions(+), 19 deletions(-) diff --git a/core/src/processing/core/PShape.java b/core/src/processing/core/PShape.java index 44ff15115c..03de66203f 100644 --- a/core/src/processing/core/PShape.java +++ b/core/src/processing/core/PShape.java @@ -22,8 +22,23 @@ package processing.core; +import java.awt.Image; +import java.awt.color.ColorSpace; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; +import java.util.Base64.Decoder; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.nio.charset.StandardCharsets; +import javax.swing.ImageIcon; +import javax.xml.bind.DatatypeConverter; + + import processing.core.PApplet; @@ -106,6 +121,7 @@ public class PShape implements PConstants { /** Texture or image data associated with this shape. */ protected PImage image; + protected String imagePath = null; public static final String OUTSIDE_BEGIN_END_ERROR = "%1$s can only be called between beginShape() and endShape()"; @@ -1645,6 +1661,10 @@ protected void drawPrimitive(PGraphics g) { params[6], params[7]); } else if (kind == RECT) { + + if (imagePath != null){ + loadImage(g); + } if (image != null) { int oldMode = g.imageMode; g.imageMode(CORNER); @@ -1879,6 +1899,63 @@ protected void drawPath(PGraphics g) { g.endShape(close ? CLOSE : OPEN); } + private void loadImage(PGraphics g){ + + if(this.imagePath.startsWith("data:image")){ + loadBase64Image(); + } + + if(this.imagePath.startsWith("file://")){ + loadFileSystemImage(g); + } + this.imagePath = null; + } + + private void loadFileSystemImage(PGraphics g){ + imagePath = imagePath.substring(7); + PImage loadedImage = g.parent.loadImage(imagePath); + if(loadedImage == null){ + System.err.println("Error loading image file: " + imagePath); + }else{ + setTexture(loadedImage); + } + } + + private void loadBase64Image(){ + String[] parts = this.imagePath.split(";base64,"); + String extension = parts[0].substring(11); + String encodedData = parts[1]; + + byte[] decodedBytes = DatatypeConverter.parseBase64Binary(encodedData); + + if(decodedBytes == null){ + System.err.println("Decode Error on image: " + imagePath.substring(0, 20)); + return; + } + + Image awtImage = new ImageIcon(decodedBytes).getImage(); + + if (awtImage instanceof BufferedImage) { + BufferedImage buffImage = (BufferedImage) awtImage; + int space = buffImage.getColorModel().getColorSpace().getType(); + if (space == ColorSpace.TYPE_CMYK) { + return; + } + } + + PImage loadedImage = new PImage(awtImage); + if (loadedImage.width == -1) { + // error... + } + + // if it's a .gif image, test to see if it has transparency + if (extension.equals("gif") || extension.equals("png") || + extension.equals("unknown")) { + loadedImage.checkAlpha(); + } + + setTexture(loadedImage); + } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -2387,14 +2464,14 @@ public void setFill(boolean fill) { /** * ( begin auto-generated from PShape_setFill.xml ) * - * The setFill() method defines the fill color of a PShape. - * This method is used after shapes are created or when a shape is defined explicitly - * (e.g. createShape(RECT, 20, 20, 80, 80)) as shown in the above example. - * When a shape is created with beginShape() and endShape(), its - * attributes may be changed with fill() and stroke() within - * beginShape() and endShape(). However, after the shape is - * created, only the setFill() method can define a new fill value for - * the PShape. + * The setFill() method defines the fill color of a PShape. + * This method is used after shapes are created or when a shape is defined explicitly + * (e.g. createShape(RECT, 20, 20, 80, 80)) as shown in the above example. + * When a shape is created with beginShape() and endShape(), its + * attributes may be changed with fill() and stroke() within + * beginShape() and endShape(). However, after the shape is + * created, only the setFill() method can define a new fill value for + * the PShape. * * ( end auto-generated ) * @@ -2543,14 +2620,14 @@ public void setStroke(boolean stroke) { /** * ( begin auto-generated from PShape_setStroke.xml ) * - * The setStroke() method defines the outline color of a PShape. - * This method is used after shapes are created or when a shape is defined - * explicitly (e.g. createShape(RECT, 20, 20, 80, 80)) as shown in - * the above example. When a shape is created with beginShape() and - * endShape(), its attributes may be changed with fill() and - * stroke() within beginShape() and endShape(). - * However, after the shape is created, only the setStroke() method - * can define a new stroke value for the PShape. + * The setStroke() method defines the outline color of a PShape. + * This method is used after shapes are created or when a shape is defined + * explicitly (e.g. createShape(RECT, 20, 20, 80, 80)) as shown in + * the above example. When a shape is created with beginShape() and + * endShape(), its attributes may be changed with fill() and + * stroke() within beginShape() and endShape(). + * However, after the shape is created, only the setStroke() method + * can define a new stroke value for the PShape. * * ( end auto-generated ) * @@ -3442,4 +3519,4 @@ protected void colorCalcARGB(int argb, float alpha) { calcAlpha = (calcAi != 255); } -} \ No newline at end of file +} diff --git a/core/src/processing/core/PShapeSVG.java b/core/src/processing/core/PShapeSVG.java index bf5cdc2f1f..adc1763bb0 100644 --- a/core/src/processing/core/PShapeSVG.java +++ b/core/src/processing/core/PShapeSVG.java @@ -24,6 +24,9 @@ package processing.core; +import static java.awt.Font.BOLD; +import static java.awt.Font.ITALIC; +import static java.awt.Font.PLAIN; import processing.data.*; // TODO replace these with PMatrix2D @@ -329,6 +332,10 @@ protected PShape parseChild(XML elem) { } else if (name.equals("rect")) { shape = createShape(this, elem, true); shape.parseRect(); + + } else if (name.equals("image")) { + shape = createShape(this, elem, true); + shape.parseImage(); } else if (name.equals("polygon")) { shape = createShape(this, elem, true); @@ -358,8 +365,10 @@ protected PShape parseChild(XML elem) { // return new FontGlyph(this, elem); } else if (name.equals("text")) { // || name.equals("font")) { - PGraphics.showWarning("Text and fonts in SVG files are " + - "not currently supported, convert text to outlines instead."); + return new Text(this, elem); + + } else if (name.equals("tspan")) { + return new LineOfText(this, elem); } else if (name.equals("filter")) { PGraphics.showWarning("Filters are not supported."); @@ -439,8 +448,23 @@ protected void parseRect() { getFloatWithUnit(element, "height", svgHeight) }; } + + + protected void parseImage() { + kind = RECT; + textureMode = NORMAL; + family = PRIMITIVE; + params = new float[] { + getFloatWithUnit(element, "x", svgWidth), + getFloatWithUnit(element, "y", svgHeight), + getFloatWithUnit(element, "width", svgWidth), + getFloatWithUnit(element, "height", svgHeight) + }; + this.imagePath = element.getString("xlink:href"); + } + /** * Parse a polyline or polygon from an SVG file. * Syntax defined at http://www.w3.org/TR/SVG/shapes.html#PointsBNF @@ -1557,8 +1581,179 @@ public RadialGradient(PShapeSVG parent, XML properties) { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + public static float TEXT_QUALITY = 1; + protected PFont parseFont(XML properties) { + +// FontFace fontFace = new FontFace(this, properties); + String fontFamily = null; + float size = 10; + int weight = PLAIN; // 0 + int italic = 0; + + if (properties.hasAttribute("style")) { + String styleText = properties.getString("style"); + String[] styleTokens = PApplet.splitTokens(styleText, ";"); + + //PApplet.println(styleTokens); + for (int i = 0; i < styleTokens.length; i++) { + + String[] tokens = PApplet.splitTokens(styleTokens[i], ":"); + //PApplet.println(tokens); + + tokens[0] = PApplet.trim(tokens[0]); + + if (tokens[0].equals("font-style")) { + // PApplet.println("font-style: " + tokens[1]); + if (tokens[1].contains("italic")) { + italic = ITALIC; + } + } else if (tokens[0].equals("font-variant")) { + // PApplet.println("font-variant: " + tokens[1]); + // setFillOpacity(tokens[1]); + + } else if (tokens[0].equals("font-weight")) { + // PApplet.println("font-weight: " + tokens[1]); + + if (tokens[1].contains("bold")) { + weight = BOLD; + // PApplet.println("Bold weight ! "); + } + + + } else if (tokens[0].equals("font-stretch")) { + // not supported. + + } else if (tokens[0].equals("font-size")) { + // PApplet.println("font-size: " + tokens[1]); + size = Float.parseFloat(tokens[1].split("px")[0]); + // PApplet.println("font-size-parsed: " + size); + } else if (tokens[0].equals("line-height")) { + // not supported + + } else if (tokens[0].equals("font-family")) { + // PApplet.println("Font-family: " + tokens[1]); + fontFamily = tokens[1]; + + } else if (tokens[0].equals("text-align")) { + // not supported + + } else if (tokens[0].equals("letter-spacing")) { + // not supported + + } else if (tokens[0].equals("word-spacing")) { + // not supported + + } else if (tokens[0].equals("writing-mode")) { + // not supported + + } else if (tokens[0].equals("text-anchor")) { + // not supported + + } else { + // Other attributes are not yet implemented + } + } + } + if (fontFamily == null) { + return null; + } + size = size * TEXT_QUALITY; + + return createFont(fontFamily, weight | italic, size, true); + } + + protected PFont createFont(String name, int weight, float size, boolean smooth) { + +// System.out.println("Try to create a font of " + name + " family, " + weight); + java.awt.Font baseFont = new java.awt.Font(name, weight, (int) size); // PFont.findFont(name);ç + +// System.out.println("Resulting family : " + baseFont.getFamily() + " " + baseFont.getStyle()); + PFont outputPFont = new PFont(baseFont.deriveFont(size), smooth, null); + +// System.out.println("Resulting PFont family : " + outputPFont.getName()); + return outputPFont; + } + + public static class Text extends PShapeSVG { + + protected PFont font; + + public Text(PShapeSVG parent, XML properties) { + super(parent, properties, true); + + // get location + float x = Float.parseFloat(properties.getString("x")); + float y = Float.parseFloat(properties.getString("y")); + + if (matrix == null) { + matrix = new PMatrix2D(); + } + matrix.translate(x, y); + + family = GROUP; + + font = parseFont(properties); + } + +// @Override +// public void drawImpl(PGraphics g){ +// } + } + + public static class LineOfText extends PShapeSVG { + String textToDisplay; + PFont font; + + public LineOfText(PShapeSVG parent, XML properties) { + + // TODO: child should ideally be parsed too... + // for inline content. + super(parent, properties, false); + +// // get location + float x = Float.parseFloat(properties.getString("x")); + float y = Float.parseFloat(properties.getString("y")); + + float parentX = Float.parseFloat(parent.element.getString("x")); + float parentY = Float.parseFloat(parent.element.getString("y")); + + if (matrix == null) matrix = new PMatrix2D(); + matrix.translate(x - parentX, (y - parentY) / 2f); + + // get the first properties + parseColors(properties); + font = parseFont(properties); + + // It is a line.. + boolean isALine = properties.getString("role") == "line"; + // NO inline content yet. + if (this.childCount > 0) { + } + + String text = properties.getContent(); + textToDisplay = text; + } + + @Override + public void drawImpl(PGraphics g) { + if (font == null) { + font = ((Text) parent).font; + if (font == null) { + return; + } + } + + pre(g); + g.textFont(font, font.size / TEXT_QUALITY); + g.text(textToDisplay, 0, 0); + post(g); + } + + } + public static class Font extends PShapeSVG { public FontFace face; From 9c04369f789b80dee0f8105ecb88cd2ab1e1e479 Mon Sep 17 00:00:00 2001 From: Akarshit Wal Date: Fri, 11 Dec 2015 11:21:26 +0530 Subject: [PATCH 0006/1035] Added the cross icon --- app/src/processing/app/contrib/ContributionTab.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/processing/app/contrib/ContributionTab.java b/app/src/processing/app/contrib/ContributionTab.java index 08f3655b44..129da5cda1 100644 --- a/app/src/processing/app/contrib/ContributionTab.java +++ b/app/src/processing/app/contrib/ContributionTab.java @@ -367,7 +367,7 @@ public FilterField () { setFont(Toolkit.getSansFont(14, Font.PLAIN)); searchIcon = Toolkit.getLibIconX("manager/search"); filterLabel.setIcon(searchIcon); - removeFilter = new JButton("X"); // TODO icon should be initialized here + removeFilter = new JButton(Toolkit.getLibIconX("manager/remove")); removeFilter.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2)); removeFilter.setBorderPainted(false); removeFilter.setContentAreaFilled(false); From 1d0ea607d653c7347e6ed7caf4ac94f22a380116 Mon Sep 17 00:00:00 2001 From: Akarshit Wal Date: Wed, 3 Feb 2016 22:06:20 +0530 Subject: [PATCH 0007/1035] Changed the error message in JSON functions --- core/src/processing/core/PApplet.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 88ef59d166..1ca5e91cbf 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -6509,7 +6509,10 @@ static public String checkExtension(String filename) { public BufferedReader createReader(String filename) { InputStream is = createInput(filename); if (is == null) { - System.err.println(filename + " does not exist or could not be read"); + System.err.println("The file \"" + filename + "\" " + + "is missing or inaccessible, make sure " + + "the URL is valid or that the file has been " + + "added to your sketch and is readable."); return null; } return createReader(is); From 3bccc174b8f80f53fe08feb4e0b03eb975a591ec Mon Sep 17 00:00:00 2001 From: Suhaib Khan Date: Sat, 27 Feb 2016 21:51:32 +0530 Subject: [PATCH 0008/1035] Adding missing docs and keywords for TableRow. Signed-off-by: Suhaib Khan --- core/src/processing/data/TableRow.java | 79 +++++++++++++++++++++++--- java/keywords.txt | 9 +++ 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/core/src/processing/data/TableRow.java b/core/src/processing/data/TableRow.java index 87e288825f..9071c2af83 100644 --- a/core/src/processing/data/TableRow.java +++ b/core/src/processing/data/TableRow.java @@ -36,8 +36,17 @@ public interface TableRow { * @param columnName title of the column to reference */ public int getInt(String columnName); - + /** + * @webref tablerow:method + * @brief Get a long value from the specified column + * @param column ID number of the column to reference + * @see TableRow#getFloat(int) + * @see TableRow#getString(int) + */ public long getLong(int column); + /** + * @param columnName title of the column to reference + */ public long getLong(String columnName); /** @@ -52,8 +61,17 @@ public interface TableRow { * @param columnName title of the column to reference */ public float getFloat(String columnName); - + /** + * @webref tablerow:method + * @brief Get a double value from the specified column + * @param column ID number of the column to reference + * @see TableRow#getInt(int) + * @see TableRow#getString(int) + */ public double getDouble(int column); + /** + * @param columnName title of the column to reference + */ public double getDouble(String columnName); /** @@ -83,8 +101,18 @@ public interface TableRow { * @param columnName title of the target column */ public void setInt(String columnName, int value); - + /** + * @webref tablerow:method + * @brief Store a long value in the specified column + * @param column ID number of the target column + * @param value value to assign + * @see TableRow#setFloat(int, float) + * @see TableRow#setString(int, String) + */ public void setLong(int column, long value); + /** + * @param columnName title of the target column + */ public void setLong(String columnName, long value); /** @@ -100,16 +128,53 @@ public interface TableRow { * @param columnName title of the target column */ public void setFloat(String columnName, float value); - + /** + * @webref tablerow:method + * @brief Store a double value in the specified column + * @param column ID number of the target column + * @param value value to assign + * @see TableRow#setFloat(int, float) + * @see TableRow#setString(int, String) + */ public void setDouble(int column, double value); + /** + * @param columnName title of the target column + */ public void setDouble(String columnName, double value); - + /** + * @webref tablerow:method + * @brief Get the column count. + * @return count of all columns + */ public int getColumnCount(); + /** + * @webref tablerow:method + * @brief Get the column type. + * @param columnName title of the target column + * @return type of the column + */ public int getColumnType(String columnName); + /** + * @param column ID number of the target column + */ public int getColumnType(int column); - + /** + * @webref tablerow:method + * @brief Get the all column types. + * @return list of all column types + */ public int[] getColumnTypes(); - + /** + * @webref tablerow:method + * @brief Get the column title. + * @param column ID number of the target column + * @return title of the column + */ public String getColumnTitle(int column); + /** + * @webref tablerow:method + * @brief Get the all column titles. + * @return list of all column titles + */ public String[] getColumnTitles(); } diff --git a/java/keywords.txt b/java/keywords.txt index fb8da82ade..ad9bcdb183 100644 --- a/java/keywords.txt +++ b/java/keywords.txt @@ -866,9 +866,18 @@ TableRow KEYWORD5 TableRow getFloat FUNCTION2 TableRow_getFloat_ getInt FUNCTION2 TableRow_getInt_ getString FUNCTION2 TableRow_getString_ +getLong FUNCTION2 TableRow_getLong_ +getDouble FUNCTION2 TableRow_getDouble_ setFloat FUNCTION2 TableRow_setFloat_ setInt FUNCTION2 TableRow_setInt_ setString FUNCTION2 TableRow_setString_ +setLong FUNCTION2 TableRow_setLong_ +setDouble FUNCTION2 TableRow_setDouble_ +getColumnCount FUNCTION2 TableRow_getColumnCount_ +getColumnType FUNCTION2 TableRow_getColumnType_ +getColumnTypes FUNCTION2 TableRow_getColumnTypes_ +getColumnTitle FUNCTION2 TableRow_getColumnTitle_ +getColumnTitles FUNCTION2 TableRow_getColumnTitles_ tan FUNCTION1 tan_ TAU LITERAL2 TAU text FUNCTION1 text_ From 552650264e955cdc58691e754f9228946bb7ad30 Mon Sep 17 00:00:00 2001 From: Akarshit Wal Date: Sun, 28 Feb 2016 17:09:41 +0530 Subject: [PATCH 0009/1035] Changed the access modifier of get() --- core/src/processing/data/JSONArray.java | 2 +- core/src/processing/data/JSONObject.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/processing/data/JSONArray.java b/core/src/processing/data/JSONArray.java index 0ec016d3f9..b1c155f857 100644 --- a/core/src/processing/data/JSONArray.java +++ b/core/src/processing/data/JSONArray.java @@ -263,7 +263,7 @@ private Object opt(int index) { * @return An object value. * @throws JSONException If there is no value for the index. */ - private Object get(int index) { + public Object get(int index) { Object object = opt(index); if (object == null) { throw new RuntimeException("JSONArray[" + index + "] not found."); diff --git a/core/src/processing/data/JSONObject.java b/core/src/processing/data/JSONObject.java index 8b8d2f170d..cedf11e864 100644 --- a/core/src/processing/data/JSONObject.java +++ b/core/src/processing/data/JSONObject.java @@ -540,7 +540,7 @@ static protected String doubleToString(double d) { * @return The object associated with the key. * @throws JSONException if the key is not found. */ - private Object get(String key) { + public Object get(String key) { if (key == null) { throw new RuntimeException("Null key."); } From 14a1ba979199fe0ad2ddd6acda9c580886d316f7 Mon Sep 17 00:00:00 2001 From: Akarshit Wal Date: Sun, 28 Feb 2016 21:28:50 +0530 Subject: [PATCH 0010/1035] Changed the access modifier of put() and putOnce() --- core/src/processing/data/JSONObject.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/processing/data/JSONObject.java b/core/src/processing/data/JSONObject.java index cedf11e864..2d27c8f1fe 100644 --- a/core/src/processing/data/JSONObject.java +++ b/core/src/processing/data/JSONObject.java @@ -1302,7 +1302,7 @@ public JSONObject setJSONArray(String key, JSONArray value) { * @throws JSONException If the value is non-finite number * or if the key is null. */ - private JSONObject put(String key, Object value) { + public JSONObject put(String key, Object value) { String pooled; if (key == null) { throw new RuntimeException("Null key."); @@ -1335,7 +1335,7 @@ private JSONObject put(String key, Object value) { * @return his. * @throws JSONException if the key is a duplicate */ - private JSONObject putOnce(String key, Object value) { + public JSONObject putOnce(String key, Object value) { if (key != null && value != null) { if (this.opt(key) != null) { throw new RuntimeException("Duplicate key \"" + key + "\""); From d46e1b60c27f5fabaafc853ee5c672efef499ad4 Mon Sep 17 00:00:00 2001 From: George Bateman Date: Wed, 23 Mar 2016 18:33:36 +0000 Subject: [PATCH 0011/1035] Use HTML to print Allows multiple tabs at once and lifts responsibility for print rendering from TextAreaPainter. Fixes #50, closes #213. --- .../processing/app/syntax/JEditTextArea.java | 76 ++++++++++--------- .../app/syntax/TextAreaPainter.java | 43 +---------- app/src/processing/app/ui/Editor.java | 41 +++++++++- 3 files changed, 81 insertions(+), 79 deletions(-) diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java index 5fb7392db5..544978d76a 100644 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ b/app/src/processing/app/syntax/JEditTextArea.java @@ -258,11 +258,6 @@ public final TextAreaPainter getPainter() { } - public final Printable getPrintable() { - return painter.getPrintable(); - } - - /** * Returns the input handler. */ @@ -1724,7 +1719,25 @@ public void copy() { * specific to any language or version of the PDE. */ public void copyAsHTML() { - StringBuilder cf = new StringBuilder("
\n");
+    HtmlSelection formatted = new HtmlSelection("
\n"
+        + getTextAsHtml(null) + "\n
"); + + Clipboard clipboard = processing.app.ui.Toolkit.getSystemClipboard(); + clipboard.setContents(formatted, new ClipboardOwner() { + public void lostOwnership(Clipboard clipboard, Transferable contents) { + // I don't care about ownership + } + }); + } + + + /** + * Guts of copyAsHTML, minus the pre, body, and html blocks surrounding. + * @param doc If null, read only the selection if any, and use the active + * document. Otherwise, the whole of doc is used. + */ + public String getTextAsHtml(SyntaxDocument doc) { + StringBuilder cf = new StringBuilder(); int selStart = getSelectionStart(); int selStop = getSelectionStop(); @@ -1732,8 +1745,12 @@ public void copyAsHTML() { int startLine = getSelectionStartLine(); int stopLine = getSelectionStopLine(); + if (doc != null) { + startLine = 0; + stopLine = doc.getDefaultRootElement().getElementCount() - 1; + } // If no selection, convert all the lines - if (selStart == selStop) { + else if (selStart == selStop) { startLine = 0; stopLine = getLineCount() - 1; } else { @@ -1742,55 +1759,45 @@ public void copyAsHTML() { stopLine--; } } + if (doc == null) { + doc = getDocument(); + } // Read the code line by line for (int i = startLine; i <= stopLine; i++) { - emitAsHTML(cf, i); + emitAsHTML(cf, i, doc); } - cf.append("\n
"); - - HtmlSelection formatted = new HtmlSelection(cf.toString()); - - Clipboard clipboard = processing.app.ui.Toolkit.getSystemClipboard(); - clipboard.setContents(formatted, new ClipboardOwner() { - public void lostOwnership(Clipboard clipboard, Transferable contents) { - // i don't care about ownership - } - }); + return cf.toString(); } - private void emitAsHTML(StringBuilder cf, int line) { + private void emitAsHTML(StringBuilder cf, int line, SyntaxDocument doc) { + // Almost static; only needs the painter for a color scheme. Segment segment = new Segment(); - getLineText(line, segment); + try { + Element element = doc.getDefaultRootElement().getElement(line); + int start = element.getStartOffset(); + int stop = element.getEndOffset(); + doc.getText(start, stop - start - 1, segment); + } catch (BadLocationException e) { return; } char[] segmentArray = segment.array; int limit = segment.getEndIndex(); int segmentOffset = segment.offset; int segmentCount = segment.count; - TokenMarker tokenMarker = getTokenMarker(); + TokenMarker tokenMarker = doc.getTokenMarker(); // If syntax coloring is disabled, do simple translation if (tokenMarker == null) { for (int j = 0; j < segmentCount; j++) { char c = segmentArray[j + segmentOffset]; - //cf = cf.append(c); appendAsHTML(cf, c); } } else { // If syntax coloring is enabled, we have to do this // because tokens can vary in width - Token tokens; - if ((painter.getCurrentLineIndex() == line) && - (painter.getCurrentLineTokens() != null)) { - tokens = painter.getCurrentLineTokens(); - - } else { - painter.setCurrentLineIndex(line); - painter.setCurrentLineTokens(tokenMarker.markTokens(segment, line)); - tokens = painter.getCurrentLineTokens(); - } + Token tokens = tokenMarker.markTokens(segment, line); int offset = 0; SyntaxStyle[] styles = painter.getStyles(); @@ -1798,10 +1805,8 @@ private void emitAsHTML(StringBuilder cf, int line) { for (;;) { byte id = tokens.id; if (id == Token.END) { - char c = segmentArray[segmentOffset + offset]; if (segmentOffset + offset < limit) { - //cf.append(c); - appendAsHTML(cf, c); + appendAsHTML(cf, segmentArray[segmentOffset + offset]); } else { cf.append('\n'); } @@ -1824,7 +1829,6 @@ private void emitAsHTML(StringBuilder cf, int line) { cf.append(" "); } else { appendAsHTML(cf, c); - //cf.append(c); } // Place close tags [/] if (j == (length - 1) && id != Token.NULL && styles[id].isBold()) diff --git a/app/src/processing/app/syntax/TextAreaPainter.java b/app/src/processing/app/syntax/TextAreaPainter.java index bdfded94d5..95482b124d 100644 --- a/app/src/processing/app/syntax/TextAreaPainter.java +++ b/app/src/processing/app/syntax/TextAreaPainter.java @@ -12,7 +12,6 @@ import java.awt.event.MouseEvent; import java.awt.*; -import java.awt.print.*; import javax.swing.ToolTipManager; import javax.swing.text.*; @@ -28,9 +27,6 @@ * @author Slava Pestov */ public class TextAreaPainter extends JComponent implements TabExpander { - /** True if inside printing, will handle disabling the highlight */ - boolean printing; - /** A specific painter composed by the InputMethod.*/ protected CompositionTextPainter compositionTextPainter; @@ -502,38 +498,6 @@ public void paint(Graphics gfx) { } - public Printable getPrintable() { - return new Printable() { - - @Override - public int print(Graphics graphics, PageFormat pageFormat, - int pageIndex) throws PrinterException { - int lineHeight = fm.getHeight(); - int linesPerPage = (int) (pageFormat.getImageableHeight() / lineHeight); - int lineCount = textArea.getLineCount(); - int lastPage = lineCount / linesPerPage; - - if (pageIndex > lastPage) { - return NO_SUCH_PAGE; - - } else { - Graphics2D g2 = (Graphics2D) graphics; - TokenMarker tokenMarker = textArea.getDocument().getTokenMarker(); - int firstLine = pageIndex*linesPerPage; - g2.translate(Math.max(54, pageFormat.getImageableX()), - pageFormat.getImageableY() - firstLine*lineHeight); - printing = true; - for (int line = firstLine; line < firstLine + linesPerPage; line++) { - paintLine(g2, line, 0, tokenMarker); - } - printing = false; - return PAGE_EXISTS; - } - } - }; - } - - /** * Marks a line as needing a repaint. * @param line The line to invalidate @@ -661,9 +625,7 @@ protected void paintLine(Graphics gfx, int line, int x, // protected void paintPlainLine(Graphics gfx, int line, Font defaultFont, // Color defaultColor, int x, int y) { protected void paintPlainLine(Graphics gfx, int line, int x, int y) { - if (!printing) { - paintHighlight(gfx,line,y); - } + paintHighlight(gfx,line,y); textArea.getLineText(line, currentLine); // gfx.setFont(plainFont); @@ -786,8 +748,7 @@ protected int paintSyntaxLine(Graphics gfx, Segment line, int x, int y, } - protected void paintHighlight(Graphics gfx, int line, int y) {//, boolean printing) { -// if (!printing) { + protected void paintHighlight(Graphics gfx, int line, int y) { if (line >= textArea.getSelectionStartLine() && line <= textArea.getSelectionStopLine()) { paintLineHighlight(gfx, line, y); diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 47081b99e1..83a134351f 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -64,6 +64,7 @@ import javax.swing.event.*; import javax.swing.plaf.basic.*; import javax.swing.text.*; +import javax.swing.text.html.*; import javax.swing.undo.*; @@ -2610,15 +2611,51 @@ public void handlePageSetup() { */ public void handlePrint() { statusNotice(Language.text("editor.status.printing")); + + StringBuilder html = new StringBuilder(""); + for (SketchCode tab : sketch.getCode()) { + html.append("" + tab.getPrettyName() + "
"); + html.append(textarea.getTextAsHtml((SyntaxDocument)tab.getDocument())); + html.append("
"); + } + html.setLength(html.length() - 4); // Don't want last
. + html.append(""); + JTextPane jtp = new JTextPane(); + // Needed for good line wrapping; otherwise one very long word breaks + // wrapping for the whole document. + jtp.setEditorKit(new HTMLEditorKit() { + public ViewFactory getViewFactory() { + return new HTMLFactory() { + public View create(Element e) { + View v = super.create(e); + if (!(v instanceof javax.swing.text.html.ParagraphView)) + return v; + else + return new javax.swing.text.html.ParagraphView(e) { + protected SizeRequirements calculateMinorAxisRequirements( + int axis, SizeRequirements r) { + r = super.calculateMinorAxisRequirements(axis, r); + r.minimum = 1; + return r; + } + }; + } + }; + } + }); + jtp.setFont(new Font(Preferences.get("editor.font.family"), Font.PLAIN, 10)); + jtp.setText(html.toString().replace("\n", "
") // Not in a
.
+        .replaceAll("(?
Date: Sun, 25 Oct 2015 21:12:46 +0900
Subject: [PATCH 0012/1035] Update Japanese translation

---
 build/shared/lib/languages/PDE_ja.properties | 237 +++++++++++++++----
 1 file changed, 185 insertions(+), 52 deletions(-)

diff --git a/build/shared/lib/languages/PDE_ja.properties b/build/shared/lib/languages/PDE_ja.properties
index 94050a6473..6ed12034d5 100644
--- a/build/shared/lib/languages/PDE_ja.properties
+++ b/build/shared/lib/languages/PDE_ja.properties
@@ -29,7 +29,7 @@ menu.file.quit = 終了
 # | File | Edit | Sketch | Debug | Tools | Help |
 #        | Edit |
 menu.edit = 編集
-menu.edit.undo = 戻す
+menu.edit.undo = 元に戻す
 menu.edit.redo = やり直し
 menu.edit.action.addition = addition
 menu.edit.action.deletion = deletion
@@ -179,14 +179,13 @@ preferences.cmd_space = space
 preferences.suggest_imports = import 宣言をサジェストする
 preferences.increase_max_memory = 有効な最大メモリを増やす
 preferences.delete_previous_folder_on_export = エクスポート時に以前のフォルダーを削除する
-preferences.hide_toolbar_background_image = タブ/ツールバーの背景画像を隠す
 preferences.check_for_updates_on_startup = 起動時にアップデートをチェックする
 preferences.run_sketches_on_display = スケッチを実行するディスプレイ
 preferences.run_sketches_on_display.tip = スケッチが最初に置かれるディスプレイをセットして下さい。
通常、スケッチウィンドウを動かすと、同じ位置に再び開かれますが、
プレゼンテーション(フルスクリーン)モードで実行している場合、
このディスプレイが常に使用されます。 preferences.automatically_associate_pde_files = 自動的に .pde ファイルを Processing に関連付ける preferences.launch_programs_in = プログラムを起動する preferences.launch_programs_in.mode = モード -preferences.file = さらなる設定は次のファイルを直接編集することで可能です +preferences.file = 詳細な設定は次のファイルを直接編集することで可能です preferences.file.hint = Processing が起動していない時のみ編集できます # Sketchbook Location (Frame) @@ -276,13 +275,10 @@ debugger.name = 名前 debugger.value = 値 debugger.type = 型 - # --------------------------------------- # Toolbars # [Run/Present] [Stop] [New] [Open] [Save] -# FIXME: スケッチのメニュー部分は日本語が表示できないため、英語のままにする -# https://github.com/processing/processing/issues/2886 toolbar.run = 実行 toolbar.present = プレゼンテーション toolbar.stop = 停止 @@ -315,6 +311,10 @@ editor.header.next_tab = 次のタブ editor.header.delete.warning.title = いや、うん。 editor.header.delete.warning.text = 開いただけのスケッチのメインタブは削除できません。 +# PopUp menu +editor.popup.show_usage = 用法の表示... +editor.popup.rename = 名前の変更... + # Tabs editor.tab.new = 新規名 editor.tab.new.description = 新しいファイルの名前 @@ -324,60 +324,54 @@ editor.tab.rename.description = ファイルの新しい名前 # Sketch editor.sketch.rename.description = スケッチの新しい名前 -# FIXME: この部分は文字化けするため、英語のままにする -# https://github.com/processing/processing/issues/2886 -#editor.status.autoformat.no_changes = No changes necessary for Auto Format. -#editor.status.autoformat.finished = 自動フォーマットが完了しました。 -#editor.status.find_reference.select_word_first = First select a word to find in the reference. -#editor.status.find_reference.not_available = No reference available for "%s". -#editor.status.drag_and_drop.files_added.0 = No files were added to the sketch. -#editor.status.drag_and_drop.files_added.1 = One file added to the sketch. -#editor.status.drag_and_drop.files_added.n = %d files added to the sketch. +editor.status.autoformat.no_changes = No changes necessary for Auto Format. +editor.status.autoformat.finished = 自動フォーマットが完了しました。 +editor.status.find_reference.select_word_first = First select a word to find in the reference. +editor.status.find_reference.not_available = No reference available for "%s". +editor.status.drag_and_drop.files_added.0 = No files were added to the sketch. +editor.status.drag_and_drop.files_added.1 = One file added to the sketch. +editor.status.drag_and_drop.files_added.n = %d files added to the sketch. editor.status.saving = 保存しています... editor.status.saving.done = 保存が完了しました。 -#editor.status.saving.canceled = 保存がキャンセルされました。 -#editor.status.printing = 印刷しています... -#editor.status.printing.done = 印刷が完了しました。 -#editor.status.printing.error = 印刷中にエラーが発生しました。 -#editor.status.printing.canceled = 印刷がキャンセルされました。 -#editor.status.copy_as_html = Code formatted as HTML has been copied to the clipboard. -#editor.status.debug.busy = Debugger busy... -#editor.status.debug.halt = Debugger halted. -#editor.status.archiver.create = アーカイブ "%s" を作成しました。 -#editor.status.archiver.cancel = スケッチのアーカイブがキャンセルされました。 +editor.status.saving.canceled = 保存がキャンセルされました。 +editor.status.printing = 印刷しています... +editor.status.printing.done = 印刷が完了しました。 +editor.status.printing.error = 印刷中にエラーが発生しました。 +editor.status.printing.canceled = 印刷がキャンセルされました。 +editor.status.copy_as_html = Code formatted as HTML has been copied to the clipboard. +editor.status.debug.busy = Debugger busy... +editor.status.debug.halt = Debugger halted. +editor.status.archiver.create = アーカイブ "%s" を作成しました。 +editor.status.archiver.cancel = スケッチのアーカイブがキャンセルされました。 # Errors -# FIXME: この部分は文字化けするため、英語のままにする -# https://github.com/processing/processing/issues/2886 editor.status.warning = 警告 editor.status.error = エラー editor.status.error_on = "%s" でエラー -#editor.status.missing.default = "%c" がありません -#editor.status.missing.semicolon = セミコロン ";" がありません -#editor.status.missing.left_sq_bracket = Missing left square bracket "[" -#editor.status.missing.right_sq_bracket = Missing right square bracket "]" -#editor.status.missing.left_paren = Missing left parenthesis "(" -#editor.status.missing.right_paren = Missing right parenthesis ")" -#editor.status.missing.left_curly_bracket = Missing left curly bracket "{" -#editor.status.missing.right_curly_bracket = Missing right curly bracket "}" -#editor.status.missing.add = Consider adding "%s" -#editor.status.reserved_words = "color" and "int" are reserved words & cannot be used as variable names -#editor.status.undefined_method = The function "%s(%s)" does not exist -#editor.status.undefined_constructor = The constructor "%s(%s)" does not exist -#editor.status.empty_param = The function "%s()" does not expect any parameters -#editor.status.wrong_param = The function "%s()" expects parameters like: "%s(%s)" -#editor.status.undef_global_var = The global variable "%s" does not exist -#editor.status.undef_class = The class "%s" does not exist -#editor.status.undef_var = The variable "%s" does not exist -#editor.status.undef_name = The name "%s" cannot be recognized -#editor.status.type_mismatch = Type mismatch, "%s" does not match with "%s" -#editor.status.unused_variable = The value of the local variable "%s" is not used -#editor.status.uninitialized_variable = The local variable "%s" may not have been initialized -#editor.status.no_effect_assignment = The assignment to variable "%s" has no effect +editor.status.missing.default = "%c" がありません +editor.status.missing.semicolon = セミコロン ";" がありません +editor.status.missing.left_sq_bracket = Missing left square bracket "[" +editor.status.missing.right_sq_bracket = Missing right square bracket "]" +editor.status.missing.left_paren = Missing left parenthesis "(" +editor.status.missing.right_paren = Missing right parenthesis ")" +editor.status.missing.left_curly_bracket = Missing left curly bracket "{" +editor.status.missing.right_curly_bracket = Missing right curly bracket "}" +editor.status.missing.add = Consider adding "%s" +editor.status.reserved_words = "color" and "int" are reserved words & cannot be used as variable names +editor.status.undefined_method = 関数 "%s(%s)" は存在しません +editor.status.undefined_constructor = The constructor "%s(%s)" does not exist +editor.status.empty_param = The function "%s()" does not expect any parameters +editor.status.wrong_param = The function "%s()" expects parameters like: "%s(%s)" +editor.status.undef_global_var = グローバル変数 "%s" は存在しません +editor.status.undef_class = クラス "%s" は存在しません +editor.status.undef_var = 変数 "%s" は存在しません +editor.status.undef_name = The name "%s" cannot be recognized +editor.status.type_mismatch = Type mismatch, "%s" does not match with "%s" +editor.status.unused_variable = The value of the local variable "%s" is not used +editor.status.uninitialized_variable = The local variable "%s" may not have been initialized +editor.status.no_effect_assignment = The assignment to variable "%s" has no effect # Footer buttons -# FIXME: この部分は文字化けするため、英語のままにする -# https://github.com/processing/processing/issues/2886 editor.footer.errors = エラー editor.footer.errors.problem = 問題 editor.footer.errors.tab = タブ @@ -389,7 +383,7 @@ new.messages.is_read_only = スケッチが読込み専用です new.messages.is_read_only.description = Some files are marked "read-only", so you will\nneed to re-save the sketch in another location,\nand try again. # Rename handler -rename.messages.is_untitled = Sketch is Untitled +rename.messages.is_untitled = スケッチが無題です rename.messages.is_untitled.description = How about saving the sketch first\nbefore trying to rename it? rename.messages.is_modified = 名前の変更の前にスケッチを保存して下さい。 rename.messages.is_read_only = スケッチが読込み専用です @@ -437,6 +431,19 @@ add_file.messages.cannot_add.description = Could not add '%s' to the sketch. add_file.messages.same_file = You can't fool me add_file.messages.same_file.description = This file has already been copied to the\nlocation from which where you're trying to add it.\nI ain't not doin nuthin'. +# Temp folder creator +temp_dir.messages.bad_build_folder = Build folder bad +temp_dir.messages.bad_build_folder.description = Could not find a place to build the sketch. + +# Ensure Existance +ensure_exist.messages.missing_sketch = Sketch Disappeared +ensure_exist.messages.missing_sketch.description = The sketch folder has disappeared.\nWill attempt to re-save in the same location,\nbut anything besides the code will be lost. +ensure_exist.messages.unrecoverable = Could not re-save sketch +ensure_exist.messages.unrecoverable.description = Could not properly re-save the sketch. You may be in trouble at this point,\nand it might be time to copy and paste your code to another text editor. + +# Check name +check_name.messages.is_name_modified = The sketch name had to be modified. Sketch names can only consist\nof ASCII characters and numbers (but cannot start with a number).\nThey should also be less than 64 characters long. + # --------------------------------------- # Contributions @@ -449,3 +456,129 @@ contrib.manager_title.library = ライブラリマネージャー contrib.manager_title.examples = サンプルマネージャー contrib.category = カテゴリ: contrib.filter_your_search = 検索をフィルタ... +contrib.show_only_compatible.mode = Show Only Compatible Modes +contrib.show_only_compatible.tool = Show Only Compatible Tools +contrib.show_only_compatible.library = Show Only Compatible Libraries +contrib.show_only_compatible.examples = Show Only Compatible Examples +contrib.show_only_compatible.update = Show Only Compatible Updates +contrib.restart = Restart Processing +contrib.unsaved_changes = Unsaved changes have been found +contrib.unsaved_changes.prompt = Are you sure you want to restart Processing without saving first? +contrib.messages.remove_restart = Please restart Processing to finish removing this item. +contrib.messages.install_restart = Please restart Processing to finish installing this item. +contrib.messages.update_restart = Please restart Processing to finish updating this item. +contrib.errors.list_download = Could not download the list of available contributions. +contrib.errors.list_download.timeout = Connection timed out while downloading the contribution list. +contrib.errors.download_and_install = Error during download and install of %s. +contrib.errors.description_unavailable = Description unavailable. +contrib.errors.malformed_url = The link fetched from Processing.org is not valid.\nYou can still install this library manually by visiting\nthe library\'s website. +contrib.errors.needs_repackage = %s needs to be repackaged according to the %s guidelines. +contrib.errors.no_contribution_found = Could not find a %s in the downloaded file. +contrib.errors.overwriting_properties = Error overwriting .properties file. +contrib.errors.install_failed = Install failed. +contrib.errors.update_on_restart_failed = Update on restart of %s failed. +contrib.errors.temporary_directory = Could not write to temporary directory. +contrib.errors.contrib_download.timeout = Connection timed out while downloading %s. +contrib.errors.no_internet_connection = You do not seem to be connected to the Internet. +contrib.status.downloading_list = Downloading contribution list... +contrib.status.connecting = Connecting... +contrib.status.done = Done. +#contrib.all = すぺて +#contrib.undo = 元に戻す +#contrib.remove = 削除 +#contrib.install = インストール +contrib.progress.installing = Installing +contrib.progress.starting = Starting +contrib.progress.downloading = Downloading +contrib.download_error = An error occured while downloading the contribution. +contrib.unsupported_operating_system = Your operating system does not appear to be supported. You should visit the %s\'s library for more info. +contrib.category.3d = 3D +contrib.category.animation = Animation +contrib.category.data = Data +contrib.category.geometry = Geometry +contrib.category.gui = GUI +contrib.category.hardware = Hardware +contrib.category.i_o = I/O +contrib.category.math = Math +contrib.category.simulation = Simulation +contrib.category.sound = Sound +contrib.category.typography = Typography +contrib.category.utilities = Utilities +contrib.category.video_vision = Video & Vision +contrib.category.other = Other + +# Install on Startup +contrib.startup.errors.download_install = Error during download and install of %s +contrib.startup.errors.temp_dir = Could not write to temporary directory during download and install of %s +contrib.startup.errors.new_marker = The unupdated contribution marker seems to not like %s. You may have to install it manually to update... + +# Install on Import +contrib.import.dialog.title = Missing Libraries Available +contrib.import.dialog.primary_text = The following imported libraries are available for download, but have not been installed. +contrib.import.dialog.secondary_text = Would you like to install them now? +contrib.import.progress.download = Downloading %s... +contrib.import.progress.install = Installing %s... +contrib.import.progress.done = %s has been installed. +contrib.import.progress.final_list = The following libraries have been installed: +contrib.import.errors.link = Error: The library %s has a strange looking download link. + +# --------------------------------------- +# Warnings + +warn.delete = Delete +warn.delete.sketch = Are you sure you want to delete this sketch? +warn.delete.file = Are you sure you want to delete "%s"? + + +# --------------------------------------- +# Update Check + +update_check = Update +update_check.updates_available.core = A new version of Processing is available,\nwould you like to visit the Processing download page? +update_check.updates_available.contributions = There are updates available for some of the installed contributions,\nwould you like to open the the Contribution Manager now? + + +# --------------------------------------- +# Color Chooser + +color_chooser = Color Selector +color_chooser.select = Select + +# --------------------------------------- +# Movie Maker + +movie_maker = ムービーメーカー +movie_maker.title = QuickTime ムービーメーカー +movie_maker.blurb = This tool creates a QuickTime movie from a sequence of images.

To avoid artifacts caused by re-compressing images as video,
use TIFF, TGA (from Processing), or PNG images as the source.

TIFF and TGA images will write more quickly, but require more disk:
saveFrame("frames/####.tif");
saveFrame("frames/####.tga");

PNG images are smaller, but your sketch will run more slowly:
saveFrame("frames/####.png");

This code is based on QuickTime Movie Maker 1.5.1 2011-01-17.
Copyright © 2010-2011 Werner Randelshofer. All rights reserved.
+movie_maker.image_folder_help_label = Drag a folder with image files into the field below: +movie_maker.choose_button = 選択... +movie_maker.select_image_folder = Select image folder... +movie_maker.sound_file_help_label = Drag a sound file into the field below (.au, .aiff, .wav, .mp3): +movie_maker.select_sound_file = Select sound file... + +movie_maker.create_movie_button = Create movie... +movie_maker.save_dialog_prompt = Save movie as... +movie_maker.width = 幅: +movie_maker.height = 高さ: +movie_maker.compression = Compression: +movie_maker.compression.animation = Animation +movie_maker.compression.jpeg = JPEG +movie_maker.compression.png = PNG +movie_maker.framerate = Framerate: +movie_maker.orig_size_button = Same size as originals +movie_maker.orig_size_tooltip = Check this box if the folder contains already encoded video frames in the desired size. + +movie_maker.error.avoid_tiff = Try TGA or PNG images instead of TIFF. +movie_maker.error.badnumbers = Width and height must be whole numbers greater than zero; framerate must be a number greater than zero. +movie_maker.error.cannot_read = Could not read %s. +movie_maker.error.cannot_read_maybe_bad = Could not read %s; it may be bad. +movie_maker.error.movie_failed = Creating the QuickTime movie failed. +movie_maker.error.need_input = You need to specify the folder with image files, the sound file, or both. +movie_maker.error.no_images_found = No image files found. +movie_maker.error.sorry = Sorry +movie_maker.error.unknown_tga_format = Unknown .tga file format for %s. + +movie_maker.progress.creating_file_name = Creating %s. +movie_maker.progress.creating_output_file = Creating output file +movie_maker.progress.initializing = Initializing... +movie_maker.progress.processing = Processing %s. From ad192385ef51c722dc0f1b1fec07e38fa65c9d30 Mon Sep 17 00:00:00 2001 From: gohai Date: Sat, 21 May 2016 21:54:35 +0200 Subject: [PATCH 0013/1035] IO: We want motors, they said (implements SoftwareServo) Some measurements with a logic analyzer and the Raspberry Pi 2 sleep: 0.001500, measured avg: 0.0015357, measured 0.95 perc.: 0.0015573 sleep: 0.0185, measured avg: 0.0186177, measured 0.95 perc.: 0.0186345 servo_pulse_oversleep was set to account for the (expected) overhead of waking up and toggling the pin with help from the numbers above. --- .../io/examples/ServoSweep/ServoSweep.pde | 34 ++++ .../io/examples/ServoSweep/setup.png | Bin 0 -> 63723 bytes .../io/examples/ServoSweep/setup_better.png | Bin 0 -> 72837 bytes .../library/linux-armv6hf/libprocessing-io.so | Bin 30068 -> 35000 bytes .../io/library/linux32/libprocessing-io.so | Bin 32120 -> 35304 bytes .../io/library/linux64/libprocessing-io.so | Bin 33624 -> 37032 bytes java/libraries/io/src/native/Makefile | 3 +- java/libraries/io/src/native/iface.h | 24 +++ java/libraries/io/src/native/impl.c | 103 +++++++++++ .../io/src/processing/io/NativeInterface.java | 4 + .../io/src/processing/io/SoftwareServo.java | 163 ++++++++++++++++++ 11 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 java/libraries/io/examples/ServoSweep/ServoSweep.pde create mode 100644 java/libraries/io/examples/ServoSweep/setup.png create mode 100644 java/libraries/io/examples/ServoSweep/setup_better.png create mode 100644 java/libraries/io/src/processing/io/SoftwareServo.java diff --git a/java/libraries/io/examples/ServoSweep/ServoSweep.pde b/java/libraries/io/examples/ServoSweep/ServoSweep.pde new file mode 100644 index 0000000000..a09d52ab1b --- /dev/null +++ b/java/libraries/io/examples/ServoSweep/ServoSweep.pde @@ -0,0 +1,34 @@ +import processing.io.*; + +// see setup.png in the sketch folder for wiring details +// for more reliable operation it is recommended to power +// the servo from an external power source, see setup_better.png + +SoftwareServo servo1; +SoftwareServo servo2; + +void setup() { + size(400, 300); + servo1 = new SoftwareServo(this); + servo1.attach(17); + servo2 = new SoftwareServo(this); + servo2.attach(4); +} + +void draw() { + background(0); + stroke(255); + strokeWeight(3); + + // we don't go right to the edge to prevent + // making the servo unhappy + float angle = 90 + sin(frameCount / 100.0)*85; + servo1.write(angle); + float y = map(angle, 0, 180, 0, height); + line(0, y, width/2, y); + + angle = 90 + cos(frameCount / 100.0)*85; + servo2.write(90 + cos(frameCount / 100.0)*85); + y = map(angle, 0, 180, 0, height); + line(width/2, y, width, y); +} diff --git a/java/libraries/io/examples/ServoSweep/setup.png b/java/libraries/io/examples/ServoSweep/setup.png new file mode 100644 index 0000000000000000000000000000000000000000..2370f37575354be62ac192a6fc3df626e8ca33b0 GIT binary patch literal 63723 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 Y>2+P-HM{kQ{%fm}oVsk~hp+zs2S0(l?EnA( literal 0 HcmV?d00001 diff --git a/java/libraries/io/examples/ServoSweep/setup_better.png b/java/libraries/io/examples/ServoSweep/setup_better.png new file mode 100644 index 0000000000000000000000000000000000000000..7931e0db2be82921f0280843ed743015a375b3ff GIT binary patch literal 72837 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 literal 30068 zcmcJ24}4VBmH)eUUXlq3IL-qSA>yE@sN|1GT@-W^{$u!8LBwhuCYebxVUme6lK_f> z7OmEQU}@K_Sa8*Dt*)htR;spkrT*WoUEOLewE>L9e=SzpYPItFe(!yE=FMc-&F*J^ z58k|c-ud2h&OP_s`|i7Ua%cVQ1#^TDmN|UZM2l37-?FCQn(47DXztdLa#UdTFD z@eQ8W{-8cf)J>uI`knw!Skob}5of8(m$<%(g;N{`}r`Pu+0-tSiEYEbBo8K94$e_HN8>cdF>YcEu48eI!+9Le5KbQB^~Dn3pK{>6SN<@f zZ`t%MyKeq)#IG(}QS+nyx2;?8hksaG+xFqKnX~`%{R@BHTKUUI+NX}W_@rCY&py2K z>n(rV{z%%);ae0?DG#ZD`Dp%J7@S~;!*7+)>KaR(x#zTjY zUaX<&F3b2t0e!H59#BaALA-};g+Sk}S?ZJg?-tOHcruWGbpajm;6q4HEufDrz<&h+ z6dI{-22j(in^!3qN6@OYGT_&E_>2kTuOR%@QNaIZfxKG^=o6r-f0$)8;2N|ltVmvd z$NDPnHx9R~B`*K>(6kbDs%aN3VPEB%i8MVdx=LiZgKH{#{Ee!dXGzA zQ((_7+@ApX370<+d_MyAC0Cvm4!3tDyTj>BB$Ww=t#G_Mp0UDhxLD!&%NB;Cu~e)* zp3cNl%NEW|B)enFA}xuS<`w6JTh~M=Ad-k*4&Zrx;qzSyGZT??I+nK1i}XdpJ*i}C zES-*bw}<1&@S;d2-WQwS4Lxm<)>t^%6YHK8>x;L>EGA)QES2g`h7-wFAd}rgkT! zSXVlhv3O@FIWwt9ce*W>nm@VKiYHq$L&^jNOMBu=W0?$UXh>CN)F7Lch_|%XrIU4M zTH#nUl8K=HTGDA9W!^L+PH|%0QI&SDxL9)+%%3qcJh^TPLRTFKT}!(EyeX#nBpJ%T z3n7GUoX5y`94%2=cTg}6K7XokeapR8qhBCx<2nit2tTiQ2vqWlHW=U)ZF3~AM_JbE zj<7}pw})3e;QD#RgXS$>(FY&k70=4|c}4z%yrOL$;plO5ClA>DwF7qlwt*4-xAyxF{&-+&B^vTGx9$tPaMQl2pFJ^P(hZ*UL{B>8 zNmqN)0Z-cRNn4)u$DiipAM~UTAU(2S+rY@swgF$mt^G3e_1{_6`TNHmt`~0~KK$yUfNJh-Ui57hqDIfJjkPu z=OO>RY#AW8;T1g2mhOk&t7*Hh;WlLx!e!V*($==$Z99DUz2|V&fEJERsJw3p> zngrRe4-@(sh4ev{-Zns8wAl)68lcVe74xAc$M_qcJn$@h=C%2g!-t2R0U!JY_qUV_ zs{?Xo!Dee-?K}34oA*_}yl`K@su&m>+`(Dbn8d~?kf_KUHS=;y8 zFE{VAu7C2tk!-4V>7%j;*d8{(!bY>4Yv^_ZQO@K=~=F66FuD z{3zdr##Yz%^9=2={YdgckXH?PHINsAya^5PEA4cBS`GOj*k*i6A6DX?1)tVJ59`9< z=~IKRbbYGI>Do1+z%I1&cTL`YH}3?Gu8|(u4|w>jEBc9L9|^oKw0-ZVpXd9Sb?7h9 z=l7J&dRp5`eIsEj{WBeMDbt61{)Xap{lE2>fWP*_#{Y@GUI(7#G%@&p>91#t`0I%R zegBoeq{m-Bq^u+OYa-$+gnFES_?j51Kp%iO1J=Y>2>zakIxz8-ZL?#y@BaqmGTscH z@n!JE+U!jQ_Sdl;q5U1*(7cZ^>kDk(%YLiJ;1HMSXI`iQpKXmX?Fau#>V=Ipuyq*9 z`Pk{rdku#5I0^C60u1Yta#u6WGP!ZyY2>@@50ANb4WIoUV-0=4yN{lJ@!oX9;kRXj z5BE?PLFsCpYh% zO1oeO+s{(JP; z6Y^Q6hdg?WEzD!uva%Z(#u5F__!@~TeDW^yGhK~xHlhq019%>x&D*Re=<@d46@|V> zK4UA}yeVG*b!cMPl#BgxA(l2W*62_64~5TwGGsE(fnt3FWv3hd@OF7a^8ik?g|P#- zyY|2OxBNZ}W&Iw@k9X&H?IQW2ZoX%ZkPpMCcPAi!0A&uix~8~!b{!!PV~u6=K5N7( z(I|1+tL(-Ap1XcLcV{8)8ds!ZLn#Li65{9{LbyV=aKln5rPw$#=uz3D>Wj` zxh0zR`QgLKr*Qu;t`KV-#PxE}f5nwDhk9@>%^Vw{Z>XoqrPs0Jy^=3`b)+GS_q9SxT$ zyol1I;+6azD%EF~#Je+=^p`w?`jP&!dW9@`k(6CF9>Q(A#J(I?2<=10(!@h03U zy9jk_msocr?f++_Q%InM_I>y)XP(lFaaXon&Ex|}`c7TU4$mBLuxPA5{CLh6Traf&QlZ}|dvZt4#`lM|)fhheAD&9V)ns@y6n;=R@!EpQU zIrmdKACYUvz5r&4Jq@43=HW)^JBU@g?NrdEeF!zXT_q3lZih;)fg|m>N?rr+*_{z$ zhf(e-mD~Xb*a?;V6|r3^`L8s&TP2sHOmIRy9jzp>}B{Ib_#Bk*3g&NN7da<-W`h@ z!>i~-`^GJ}G2$Imn>FH57%xVggy4}QPGjv^r8k3XZ%X3xs7ib1OSlI5>Dxy#$Y2FB z)b}XUcHrkM`VSc>eyjA4wCS;Jz?a@Y?$7oiSz;f+XZZ*CIQmv}LihOWU$#MWpn_sv zQ!y48#q{e;+kxYl{*@9LxRV*zGO&PkviAafRtJ6y zf7ov&k**2&pu~Rr2BbrQ)s+0szaTv!uoafr?>~$5#K7f@li$CM^rXNo)b-&o6tp4m zTime^O+b1|;2G-r^Gu|t2JT_{;}u9x4=ksgzs8Yn42)w8ea7^xK!UnHUyt;>;5>$z z5dR2bL2x1)gikySVoC6CaD`o}h-JY6#+_e056+5UAImvR5tjrR?sla(0M5$b2x=Um z@&&x(t)GI@gRDFcT-Q5m;)jc>?U!H=moJRxQ(J~#H}FK z1;54^A159Ju_3sZ?PR?84-gxJchN-?#2-Oi9~?;mCyCL}a8vMA7H*aT|!og7eU8+B3wXAf5}-vn_exR2D9N&Ftv0^yuZ>RwTW$L12@OlElAC#Hj1CY)ck9ZH%J;Irfc3{6LDvm`<6V4pgSid+8)B~`SR__zdpmqr7B>M9$u?o~q z;e1R_ye)17^_Xx@rUws*`$0W{dPiW|?}`^eJtdqvQXi-eWVdkGjoJrQfApMi)>Gdf zRfqJFaF&ufqQiwBs68kj%ka5a3aTIN zfqjQ1SA%*Bb}}ZUyaUt$#5JiB`8cTeg)^QJStfrA>Y#95rI*U359^qRg!4tlS*1Jy z)W^c%T9!RR&IR?EaLQOK0lA7Rp3*5}jXCm0P_}e-)AOU`_d)rkvxrocd<9gcbk1PC zkC7jN3P@*$>T+dO2<=Zg&$5k;lhZ&|OXoMF#>Z_M0gPJ0p&*{O_C$Oty`L!=yN%L$m1UnQuLEzEbpAq_XUls*&6Cc3q^8T=pcY8y66QNs?gzC* zIuFxJjZz+u_9vZd7`rp%cu*^(Gn~;gOE!YKL^{>XH%Ep+t(4BCM9z~}fr?7!P4b%M zw?TDC=M_fCLiuA*3F(|k%NENwLG?)ICED^u`8lYJblOc`m4R z(&3}XzEEBYYJ+sHX4x;2SAp6nomSS^#q!&ru1AeAmM)dgfx1aLAJLYTav!Ko(rKsX zTV&Y;v_I*5ff}Oncu==XX8~ikT}}tJRXT?xsJP0vT{NoO(rd4(K)BHEvHHjsCv zJQ>te(wRm(*UKhQyQR~?(q1hu2KAhDmX?6}itGjTl5~awUc^{}( zrE{tU>IV5VsMk?{Y;iZqeW3P8=P1g&MV6g}_9vaUnQxPv1nMp6+-QTkO@0y70n{e@ zf-SNS)cewTu>{mNe9o1$NEv5WO9so7P=N!X)Psrh?p#Axr2PyL>@*Ggpea`p&pnfV_ zKsEXtA3gZA{3@tfKIdoD@H6>CQ1g7wKd?PKD}M)SfzK&n^gJ)CP*+QQ&X?(>7v)?~ z%Y4p7EYZKo)u2}RoPVeGm*sarUE*`fNWCJT0=3fTOrt-4Dc=AU^*K$9*4Gu);d6dX zDZiH1X=r~wX9Vl-O?e`y9-lLscJ|BjKxKT+R@UEs83(n-=iEpQ1M&t?>wL~KA`hsR zv%%*)M&7%sq~-ClUJ!v`2g7NV;*1Q= zp)b11wt!O|{HYH_Qo(AhQMro6=OPH^tB~fX*Txe2Vsd7J!xy7GPA@q`@ij?vXwPVh z8T|$nLhh}^@Gf7Uau{$PCWk1#OyxME2?qP$$fj_hT+7Ga~qM=&?n` zs_W97#IU4%XY`0*HkRjg!{=*c0pFD8V}s$_Wg*5bPYqbgRK9c;V5q0j@cHmB;2S+F z4Ti6$g&0@QMq=3F_|{s0p`Py=K3`-D_(soj2E+H;LX4~DePRZnhp)W_80rb&w&L?m zxPWi;G#CtDkP9)co@K-wgdV;(7htGojp6fEx`1!=+-@*@!!E?QdY&MLjfpSb1sLk- zH+;U97VwQ83oIprujqvsSI-1uc0&)}+zT+&Gtcn(GGD+qdO8e-@A!onSI*5UHVD@O-)vcf?`wL_BYK79qh{et`oOzsu(=`8R+B>%J`e0mG~qrs|bF2bj~z(1Y*RYmx_ zH2;g_-&llChavxEi}$jayyx9744+e)+6P&_iQx|o zh7+BI7&nARfu#bB)1L(xHkPvtpA(`5e52GzQTW)Z&IrSB&H^CEn=OMi>}aVHG2e6-u8+hA3lTZHd+=@ZDmvRS~}1rANvCb`ieYrEdgl)N;7*$t*^t+{w77akrdBDY5S%lDD~5pzDG8!1;G_ zm`hCXaz>K`o1Vm%B*3rYQbI24XyyTHIPNMNxVE6#cY^`WL~@ws%7Q1OD|rU)R&klZ z!*X**n3L9W-9fYY{T0YuN6v1fxdx#*#Hk4&6BXYih9e@bOlS;qjCQ^9BiyaxGDQK) zWd9YjCy;@Q7daJB{s}p&!Qnzi9>>9rQP+UOMU6a8Ejc%X!v&5!&RlYC2ZxItd7KD2 zcZ0))k37y5ky!4`lhu0m>gP|3&h-wB#wjYE|mPZLaST#1$tEQcm!I;0|~)0Dcu0 zqBKZ(A_t;R0Qv$!T%Xb)<&GSPjsfUP1aa9)gOpElAbJO&^#pM>OM{eSav-`0pnC}7 zB9{g!@8m%A6F|=s#I-LCQZCAY=q!MKPtfEnNX1DGL$=K;gv>Y?WF$pFqXhI1&(VG5kT;!XO_Wlkbt~1-F?I}47oU|IJo%}-efkeQEIr-!Ab@DRkC@5i_+=$-%)XcC zz~7Z2J%?#|Oag!WT(+2L`(d9Ab9S0*Spk~;~ z@4$#-+M@W+Z9}Nn9e9n-gPO6KPOvadLCV*l=4nD7F;2Ia)(vB8PQybLQBqVXjc7 z$jv?0n2jj8ID<&@r}JX#j;#18dE%$I;>W!+44J0TCW`M$w_D;&HZjPz#vBEUg!)3Z z`D1^v^-xw~qn4P_1~+PnJ>%BH(V|gHOju+1w-w?%lKoJ{4F<&4b@isu&00oO%V^dz zR_a1G>q1{L7JuDuiDvfqa`Ou1f`!@zJ=z6}SR3$M4ROmj@PgxN7=q#g%|cmb_@H?u z?Jye^h^-|J#*Rpy9g#dcBCZ|d1}GxpwWBr9j;OIC*_*LMXPzOevW9T2RrJviv!jC8 zx;1OaYHi3R+99j8AuF^)R%=6+jeDdLB35e`FHsIzs||5u;&QF5rzX?YV~MLTSMFI= z(-uWwZKN)_d1Xxse~T}^=9+9B#|HKQSfN!wBUnQWuC3HzHgXVKBc~gG-Kou-rOmuk zo7t%A{!V7{ZGK|?xH0HIgiAiQUiR$r(psW9r1;AER|M8y(hI{mmH)Kei-W4vDNaQ}W1TLAo}R zi@zuElE(?5jR=j05&TZ@_#U-0vdhBRX-`?6< zA4zr9CgR<_YiirOd-(@V9c}gaYp42ncWa_I8nd$C&c3et&giPzskO;eq%{$%WzN2~ zp4yJKM5ewa-d$f;XL3P0B>wO6>Rhh+`bfH~wmsGzOT}CB86FJ=t8Ue&v0aBU+T1Qy z!t~m79tfkwuAW3)D?EhVRicqpdtC>8)!7{ncSVtgH<3mHJ^l2S-gqKY8}B}saQvlJ zZ6a~z6!7qDk56tz0wIJ%Z8X-W3D~65X!_jc1b6Z9gCb-`8tn#6M)KM3J4rnL(N|t; z-`=q5@yFW_;d2|3ukqGhANhORw;yV*FG}8RNn4cGmHug4^TNj$v~TNZ-oB8x8dm*! zL3^`*{7230AMx7UvG8$-YX4LHSKnxVynP|hW}L*<|Kzog4#~+*ZO5dz|32Q~ZCUz9 zeA=b2@N)`3p5Vtv{5Zsqdg(`aVd#fa;P3bj@v}Yoj1iR^eOV8@SvedIn;A(Y7A3p;;;Bq;Br#)cCblq^=}1PKOhBfw z=}=~EPs~~rTRkh;i@l1PQmM#Vt262$=3-|cj1+P4KxQXmU9s*=8k_j^sp}kUT$9i{ z2rWyVA8Sj*S~Ibz)@Pl!Xg)byu&c^XfR7Z+IeC8B35?#EaLY9*c?<#j%i2qC~xt1}qe4*9b%>u`wA+$g2 zqSh0OMZ+DJ52Y(=H(RfDGOLzN&lDI>ZzyJ-`*NV=xt2MzBi6bKCBmqyHH_`!64;ZD zRZyfgQ<17T?b0SIudku6b?fwCRL7v}0>01{9`1^)S;{i!It|UJ??`sV>U-k#xh?EC zxU27k*Emj0MN;utx;~z)Pp4YzyVcHi1>x*b-Q=m3TpZ}Txw!F$R3XnJtawl@x7rdB zY~q&R1iT_u6nI>TKp50aO?Rhj)Aebw=Vvww7q_;uonuG5Me|KRt$Iqd`{ZiGoz>R8 zB$I+NY!?{QkBlL7Pubn}RQQ{R=#YCivriqj!((@@`>tG!=A7<%kfhdxyIbNI9JFzp zKMYpGoZ;~d3$%I_?ifI%W2wGa^bAH$_Hp61(M~k*V#>TxubN6;p9(>2ELsc7O>~yf zm5?RX!y~_0A;+wE3WJmxy=~YDuqhgajczMY9UY?Hw3ck==M~!-QKP6K)Q)j4w*h4F zJ?xYH&Y+5GOlR*alYTH__{k9Q^kr`C0^xRwEOwDlhR+LkE@e@5rsWoT4bItT z;$1NgX5dH`IoBvaWCdAacPOfQ*Iag8qFnB56=gFh z=M4&AUCA7HFh|;LkfsYKH&>aitK72X?q>@}&zVql!O>VEvNqhCjF6XA$BTSG@l`<=;{=9zr~iUVYN$Yw(IrgR%Wp-+;Zf- zBjwD@H4S>(bzvgg(6VDWwm9g?FQd2r(OK2YmHW(qJpJ78M2ogiV@-qeR!Dy3d4?!f zCylaL-o+{GF6`-?bx)~_qt&+oE1=( zSQ*{3pg`HSm2Ggj_V2AEB{df$`2mXwH^yPuJiOV7sXEizIx`%wsg0krA?k5teqDPd zd&i@BeVKREn;T{|S>1eG=L(RQ$15zCC;$DJVU6927tPOhl&VWqgNqPD-#i7^#4}b` z5)-ymmZzq0c%A1&IGi0(uG!@bQ7z`eSY%aJ^-#i-PhNLh=QS9aDRXi%mlu0qwmMfs zOLmmmkWHPQO-)7z-^RHgcQQZ+tEUTfBik)_esp(ZXq(PG#w_*Ln?c*lHPw6{XzAX1 z_w=rePwg%StG9_~L#bF_nJs6gQB^%;}#e$BR;wM=zM zI@I;pGcThz9&=sm{Pg_pbSBc>8e81PhnRPKo$a6SlLDccdJnWvZC51K+Hnrv6#LEu zp&D~777)%s$VPdir}rGJ4OpisL zp0zM=3!)k}DzL5$-6Hay3g}bxJ5GLk&J)9Nr)Na34dFx0?J)*1FgM(B`H)##2hSY$Qs&x+K2s4_eET(|HvSrx;}U1wh; z(W@u&vZH@j6fMq=Nw*0dVagZrrQzD$E?AE@v-3=p&8_u4g&6d#$qq4H4_kHVwOyG= z3$B@zzIK>YcQO;JL&Vf^#U)xBk6McAh@?BLy6D<&URO^GO;yCt6vAGi*ROXsoNu@4n$BNX7s+6rtED%CUk9j*##(yYA%keOxiU_ zOMqA06@~oEt1ju|iWQEJvouz%aNMJBW9bTuS2(zjBM%=JtZ;C@MIJt`Tj6*rOY1~` zmd2tLj*z|puNJPdzb|2ak7Hw&kA)i?{5=&OKDApU^kb!2(_As){#oh*KGtw?a6d{O zJ_~EhIJkd351)lKb{yW{`yg%he)OqbCb4RceJFYO)NYelqs4xXn#O`H4)gmMJ`3N# z@f<$>nX%{a<^A#r?uJk8vgxI3+#1vOZ0p0r=OJnQ zg+*{{p@){BzXW=!>re8DH^1+JMRY|Q|8hl+-^((Gk-x#MAA{##;_IW*`i@EBO8gIS zHU1XZ>67{zJ^Fv_p^bg(JbdDB^3bn%XoKgTWBSnLGxfz?LYd#}VaoEp?V(Nn2CSRv zqr&>$9`~P5EYnP0F7PzM+l$jUM@wXW|pvCR*nLZ)gFU zd=p>z9)ZQZSj=Jk&-GA!=<-g)7ds41d$^uxXv*VulZGar-wZJ{`7dYdPs_`~5pRX{ zNxW%q_~tz}I?K@lH2J2!@r5WW1-_wK|K5Y;xe9z!Ujp@YBWTke1^8P)8-I|$9kgkm zRN*_#CF{=XMYMDDs=*cqCr-&>_(8gEs5A{Jk1}(a5Ynlb#3K*u&q? z;r5N@pc~sjdw*Z&vOK(lEv0h(7k_W(ssjAiK%0Z|zEwcq588ZXj`q9(+N>Xwu0-E# z)~iXM<@VK-NqGxFoAq+?*LwIN@VW1wIf%c#fZka^KLgrZ-WLn_dkXk(fi~;m)bHRC z;`Qfo`jMhc2;uV#(B_*=#J7Mp>-D5>DZqahwCT_Idqod=_$-h9*2aJL1a9`b|;O( zvo2cHv=D>$a2P+OWDs)~ES}M{AiQ|aoTamug_kwWSTLKviR1kZrN+kGPvQJmzsWR& z$$8J)<`B@HFBEuwzXxUH3QV3={Ba-i(>-d4=l+6JILDxBZyq)6<-IWkgS>M&Lx89A zCs&4$Be(L6!K3=ksQ;mb?PlS#;CHp?O0THG-?Q@a^v^qa@6flFvMukf>+&I#%gpO= z&C64j|Jz^ZGUB+#GX(9~1v?eJrv2vso|t;SQ1$CAB73kQ9bvyj_-L+z-bQC zE#f=Z)}OIgI&mEL^LzlO>BfoUITa_qFsS75eSl{JGLc6tuY9^U;v}u~BEffsdr7#5 zdECwe%{|-6;`;;7IY=`v58mf_0Z#7m&NSaEc!rRsKFZ>iM-(TAMa1<3!?O`-Bab)^ z6T5H{Hv?w~4&u22Y2t?AG`K#bDIa3g^Mub7{kDM{IE>!QkTy7uLwMe<5c-2Sp6hTL z9LMK8AEQF|F!HVeZEzfi^Q7J8Fu3b+gE*F#<1wCwN32QThBV)TSU!$3`;UP8Hu!uy zB97zTw~m1O9{AKt9LIGBj)40<`1Bod9MADLEBlc)cJaz`J%-cBubQ>HQ5Duczh4>;!c}uSr2(`nB*`dOqx=DGY{WW zXHd9@;f*f9EosJPl7m@j;H-M9E(kpN0D$DwbF$z2*@BabgSp6^n diff --git a/java/libraries/io/library/linux32/libprocessing-io.so b/java/libraries/io/library/linux32/libprocessing-io.so index c988fcb7cc6b95eb47eaa7be651b6268d578db77..32a231170c3ac0a174b63c4aee275e4b31946a87 100755 GIT binary patch literal 35304 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

@ZdihOc~i*2XlcUwE%SLmoC0dQ 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 h!U@zH42xhiYI$3$yh$($|w;3Y9-}s;VRE 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 MgsB5x<@@#j4?)Al*8l(j diff --git a/java/libraries/io/src/native/Makefile b/java/libraries/io/src/native/Makefile index 101a20ee5a..f33218a6c4 100644 --- a/java/libraries/io/src/native/Makefile +++ b/java/libraries/io/src/native/Makefile @@ -2,7 +2,8 @@ TARGET := libprocessing-io.so OBJS := impl.o CC := gcc -CFLAGS := -std=c99 -fPIC -g +# prefix with -m32 to compile for linux32 +CFLAGS := -std=gnu99 -fPIC -g CFLAGS += -I$(shell dirname $(shell realpath $(shell which javac)))/../include CFLAGS += -I$(shell dirname $(shell realpath $(shell which javac)))/../include/linux LDFLAGS := -shared diff --git a/java/libraries/io/src/native/iface.h b/java/libraries/io/src/native/iface.h index 0cda66685a..32e235e1e1 100644 --- a/java/libraries/io/src/native/iface.h +++ b/java/libraries/io/src/native/iface.h @@ -63,6 +63,30 @@ JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_pollDevice JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferI2c (JNIEnv *, jclass, jint, jint, jbyteArray, jbyteArray); +/* + * Class: processing_io_NativeInterface + * Method: servoStartThread + * Signature: (III)J + */ +JNIEXPORT jlong JNICALL Java_processing_io_NativeInterface_servoStartThread + (JNIEnv *, jclass, jint, jint, jint); + +/* + * Class: processing_io_NativeInterface + * Method: servoUpdateThread + * Signature: (JII)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoUpdateThread + (JNIEnv *, jclass, jlong, jint, jint); + +/* + * Class: processing_io_NativeInterface + * Method: servoStopThread + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoStopThread + (JNIEnv *, jclass, jlong); + /* * Class: processing_io_NativeInterface * Method: setSpiSettings diff --git a/java/libraries/io/src/native/impl.c b/java/libraries/io/src/native/impl.c index b2748f7d16..0f9402ab92 100644 --- a/java/libraries/io/src/native/impl.c +++ b/java/libraries/io/src/native/impl.c @@ -25,15 +25,20 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include "iface.h" +static const int servo_pulse_oversleep = 35; // amount of uS to account for when sleeping + + JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_openDevice (JNIEnv *env, jclass cls, jstring _fn) { @@ -192,6 +197,104 @@ JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferI2c } +typedef struct { + int fd; + pthread_t thread; + int pulse; + int period; +} SERVO_STATE_T; + + +static void* servoThread(void *ptr) { + SERVO_STATE_T *state = (SERVO_STATE_T*)ptr; + struct timespec on, off; + on.tv_sec = 0; + off.tv_sec = 0; + + do { + write(state->fd, "1", 1); + + on.tv_nsec = state->pulse * 1000; + nanosleep(&on, NULL); + + write(state->fd, "0", 1); + + off.tv_nsec = (state->period - state->pulse) * 1000; + nanosleep(&off, NULL); + } while (1); +} + + +JNIEXPORT jlong JNICALL Java_processing_io_NativeInterface_servoStartThread + (JNIEnv *env, jclass cls, jint gpio, jint pulse, jint period) +{ + char path[26 + 19 + 1]; + int fd; + pthread_t thread; + + // setup struct holding our state + SERVO_STATE_T *state = malloc(sizeof(SERVO_STATE_T)); + if (!state) { + return -ENOMEM; + } + memset(state, 0, sizeof(*state)); + state->pulse = (pulse - servo_pulse_oversleep > 0) ? pulse - servo_pulse_oversleep : 0; + // we're obviously also oversleeping in the general period case + // but other than the pulse, this doesn't seem to be crucial with servos + state->period = period; + + // open gpio + sprintf(path, "/sys/class/gpio/gpio%d/value", gpio); + state->fd = open(path, O_WRONLY); + if (state->fd < 0) { + free(state); + return -errno; + } + + // start thread + int ret = pthread_create(&state->thread, NULL, servoThread, state); + if (ret != 0) { + free(state); + return -ret; + } + + // set scheduling policy and priority + struct sched_param param; + param.sched_priority = 75; + ret = pthread_setschedparam(state->thread, SCHED_FIFO, ¶m); + if (ret != 0) { + fprintf(stderr, "Error setting thread policy: %s\n", strerror(ret)); + } + + return (intptr_t)state; +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoUpdateThread + (JNIEnv *env, jclass cls, jlong handle, jint pulse, jint period) +{ + SERVO_STATE_T *state = (SERVO_STATE_T*)(intptr_t)handle; + state->pulse = (pulse - servo_pulse_oversleep > 0) ? pulse - servo_pulse_oversleep : 0; + state->period = period; + return 0; +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoStopThread + (JNIEnv *env, jclass cls, jlong handle) +{ + SERVO_STATE_T *state = (SERVO_STATE_T*)(intptr_t)handle; + + // signal thread to stop + pthread_cancel(state->thread); + pthread_join(state->thread, NULL); + + close(state->fd); + free(state); + return 0; +} + + JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_setSpiSettings (JNIEnv *env, jclass cls, jint handle, jint _maxSpeed, jint dataOrder, jint mode) { diff --git a/java/libraries/io/src/processing/io/NativeInterface.java b/java/libraries/io/src/processing/io/NativeInterface.java index ea32292c0b..3b1b3e0c32 100644 --- a/java/libraries/io/src/processing/io/NativeInterface.java +++ b/java/libraries/io/src/processing/io/NativeInterface.java @@ -65,6 +65,10 @@ public static int writeFile(String fn, String out) { public static native int pollDevice(String fn, int timeout); /* I2C */ public static native int transferI2c(int handle, int slave, byte[] out, byte[] in); + /* SoftwareServo */ + public static native long servoStartThread(int gpio, int pulse, int period); + public static native int servoUpdateThread(long handle, int pulse, int period); + public static native int servoStopThread(long handle); /* SPI */ public static native int setSpiSettings(int handle, int maxSpeed, int dataOrder, int mode); public static native int transferSpi(int handle, byte[] out, byte[] in); diff --git a/java/libraries/io/src/processing/io/SoftwareServo.java b/java/libraries/io/src/processing/io/SoftwareServo.java new file mode 100644 index 0000000000..dc516a7758 --- /dev/null +++ b/java/libraries/io/src/processing/io/SoftwareServo.java @@ -0,0 +1,163 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Copyright (c) The Processing Foundation 2015 + Hardware I/O library developed by Gottfried Haider + + 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., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.io; + +import processing.core.*; + + +/** + * @webref + */ +public class SoftwareServo { + + public static final int DEFAULT_MIN_PULSE = 544; + public static final int DEFAULT_MAX_PULSE = 2400; + + protected int pin = -1; // gpio number (-1 .. not attached) + protected int handle = -1; // native thread id (<0 .. not started) + protected int period = 20000; // 20 ms (50 Hz) + protected int minPulse = 0; // minimum pulse width in microseconds + protected int maxPulse = 0; // maximum pulse width in microseconds + protected int pulse = 0; // current pulse in microseconds + + + /** + * Opens a servo motor + * @param parent typically use "this" + * @webref + */ + public SoftwareServo(PApplet parent) { + NativeInterface.loadLibrary(); + } + + + /** + * Closes a servo motor + * @webref + */ + public void close() { + detach(); + } + + + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + + /** + * Attaches a servo motor to a GPIO pin + * @param pin GPIO pin + * @webref + */ + public void attach(int pin) { + detach(); + this.pin = pin; + this.minPulse = DEFAULT_MIN_PULSE; + this.maxPulse = DEFAULT_MAX_PULSE; + } + + + /** + * Attaches a servo motor to a GPIO pin + * @param pin GPIO pin + * @param minPulse minimum pulse width in microseconds (default: 544, same as on Arduino) + * @param maxPulse maximum pulse width in microseconds (default: 2400, same as on Arduino) + * @webref + */ + public void attach(int pin, int minPulse, int maxPulse) { + detach(); + this.pin = pin; + this.minPulse = minPulse; + this.maxPulse = maxPulse; + } + + + /** + * Move a servo motor to a given orientation + * @param angle angle in degrees (controls speed and direction on continuous-rotation servos) + * @webref + */ + public void write(float angle) { + if (attached() == false) { + System.err.println("You need to call attach(pin) before write(angle)."); + throw new RuntimeException("Servo is not attached"); + } + + if (angle < 0 || 180 < angle) { + System.err.println("Only degree values between 0 and 180 can be used."); + throw new IllegalArgumentException("Illegal value"); + } + pulse = (int)(minPulse + (angle/180.0) * (maxPulse-minPulse)); + + if (handle < 0) { + // start a new thread + GPIO.pinMode(pin, GPIO.OUTPUT); + if (NativeInterface.isSimulated()) { + return; + } + handle = NativeInterface.servoStartThread(pin, pulse, period); + if (handle < 0) { + throw new RuntimeException(NativeInterface.getError(handle)); + } + } else { + // thread already running + int ret = NativeInterface.servoUpdateThread(handle, pulse, period); + if (ret < 0) { + throw new RuntimeException(NativeInterface.getError(ret)); + } + } + } + + + /** + * Returns whether a servo motor is attached to a pin + * @return true if attached, false is not + * @webref + */ + public boolean attached() { + return (pin != -1); + } + + + /** + * Detatches a servo motor from a GPIO pin + * @webref + */ + public void detach() { + if (0 <= handle) { + // stop thread + int ret = NativeInterface.servoStopThread(handle); + GPIO.pinMode(pin, GPIO.INPUT); + handle = -1; + pin = -1; + if (ret < 0) { + throw new RuntimeException(NativeInterface.getError(ret)); + } + } + } +} From 9f7e52b03a024302b5f394c8ebec1807bc178ec4 Mon Sep 17 00:00:00 2001 From: gohai Date: Fri, 1 Jul 2016 14:18:09 +0200 Subject: [PATCH 0014/1035] Add a temporary workaround for the CHIP Reported this to the CHIP team: Seeing some odd exception from deep within Java's awt classes ("Width (0) and height (0) must be non-zero"). This is triggered because the libX11 function XQueryBestCursor is returning a width and height of zero, which seems to be a bug. I am guessing that this will also pop up with other Java applications. This makes things work on the CHIP w/ OS image 4.4 and latest packages. I also tested on OS X, but I am not familiar enough with the Tweak Mode to say for sure. --- core/src/processing/awt/PSurfaceAWT.java | 17 +++++++++++++++-- .../mode/java/pdex/JavaTextAreaPainter.java | 13 +++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/core/src/processing/awt/PSurfaceAWT.java b/core/src/processing/awt/PSurfaceAWT.java index 1b40ea274c..cd4649436a 100644 --- a/core/src/processing/awt/PSurfaceAWT.java +++ b/core/src/processing/awt/PSurfaceAWT.java @@ -1482,6 +1482,13 @@ public void setCursor(PImage img, int x, int y) { // Don't set cursorType, instead use cursorType to save the last // regular cursor type used for when cursor() is called. //cursor_type = Cursor.CUSTOM_CURSOR; + + // this is a temporary workaround for the CHIP, will be removed + Dimension cursorSize = Toolkit.getDefaultToolkit().getBestCursorSize(img.width, img.height); + if (cursorSize.width == 0 || cursorSize.height == 0) { + return; + } + Cursor cursor = canvas.getToolkit().createCustomCursor((Image) img.getNative(), new Point(x, y), @@ -1511,8 +1518,14 @@ public void hideCursor() { if (invisibleCursor == null) { BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); - invisibleCursor = - canvas.getToolkit().createCustomCursor(cursorImg, new Point(8, 8), "blank"); + // this is a temporary workaround for the CHIP, will be removed + Dimension cursorSize = Toolkit.getDefaultToolkit().getBestCursorSize(16, 16); + if (cursorSize.width == 0 || cursorSize.height == 0) { + invisibleCursor = Cursor.getDefaultCursor(); + } else { + invisibleCursor = + canvas.getToolkit().createCustomCursor(cursorImg, new Point(8, 8), "blank"); + } } canvas.setCursor(invisibleCursor); cursorVisible = false; diff --git a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java index 115355ed61..27e08c2e45 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -25,6 +25,7 @@ import java.awt.Color; import java.awt.Cursor; +import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; @@ -401,8 +402,16 @@ public String getToolTipText(MouseEvent evt) { int cursorType; BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); - Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImg, new Point(0, 0), "blank cursor"); - + Cursor blankCursor; + // this is a temporary workaround for the CHIP, will be removed + { + Dimension cursorSize = Toolkit.getDefaultToolkit().getBestCursorSize(16, 16); + if (cursorSize.width == 0 || cursorSize.height == 0) { + blankCursor = Cursor.getDefaultCursor(); + } else { + blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImg, new Point(0, 0), "blank cursor"); + } + } @Override synchronized public void paint(Graphics gfx) { From a29b8f034319f74f7a11c2189a478b5001f0a23c Mon Sep 17 00:00:00 2001 From: tyfkda Date: Tue, 12 Jul 2016 09:35:43 +0900 Subject: [PATCH 0015/1035] Fix typo in comment --- java/src/processing/mode/java/pdex/CompletionPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/processing/mode/java/pdex/CompletionPanel.java b/java/src/processing/mode/java/pdex/CompletionPanel.java index 41c1eed495..d28699b3a2 100644 --- a/java/src/processing/mode/java/pdex/CompletionPanel.java +++ b/java/src/processing/mode/java/pdex/CompletionPanel.java @@ -64,7 +64,7 @@ public class CompletionPanel { private String subWord; /** - * Postion where the completion has to be inserted + * Position where the completion has to be inserted */ private int insertionPosition; From bde79b835a03a8781a74af77772a4a127c5ea57b Mon Sep 17 00:00:00 2001 From: tyfkda Date: Sun, 3 Jul 2016 08:13:02 +0900 Subject: [PATCH 0016/1035] Make preference button wider for Japanese MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preference window, [cancel] button is too narrow for Japanese "キャンセル" so make it wider. --- build/shared/lib/languages/PDE_ja.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/shared/lib/languages/PDE_ja.properties b/build/shared/lib/languages/PDE_ja.properties index 94050a6473..cf112839b5 100644 --- a/build/shared/lib/languages/PDE_ja.properties +++ b/build/shared/lib/languages/PDE_ja.properties @@ -157,7 +157,7 @@ close.unsaved_changes = %s への変更を保存しますか? # Preferences (Frame) preferences = 設定 -preferences.button.width = 80 +preferences.button.width = 120 preferences.requires_restart = Processing の再起動が必要です preferences.sketchbook_location = スケッチブックの場所 preferences.sketchbook_location.popup = スケッチブックの場所 From 36bb1d3f2bf6dce29da28dfb994f45b782053034 Mon Sep 17 00:00:00 2001 From: tyfkda Date: Wed, 13 Jul 2016 05:12:41 +0900 Subject: [PATCH 0017/1035] Fix typo in comment --- java/src/processing/mode/java/pdex/JavaTextArea.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/processing/mode/java/pdex/JavaTextArea.java b/java/src/processing/mode/java/pdex/JavaTextArea.java index fc6215de68..eb08813666 100644 --- a/java/src/processing/mode/java/pdex/JavaTextArea.java +++ b/java/src/processing/mode/java/pdex/JavaTextArea.java @@ -634,7 +634,7 @@ public int xToOffset(int line, int x) { * Sets default cursor (instead of text cursor) in the gutter area. */ protected final MouseMotionAdapter gutterCursorMouseAdapter = new MouseMotionAdapter() { - private int lastX; // previous horizontal positon of the mouse cursor + private int lastX; // previous horizontal position of the mouse cursor @Override public void mouseMoved(MouseEvent me) { From 05068c94f76646b89d5fe36a95ca504f0a188ef5 Mon Sep 17 00:00:00 2001 From: gohai Date: Fri, 15 Jul 2016 17:30:18 +0200 Subject: [PATCH 0018/1035] IO: Fix compilation on arm Error introduced in previous commit. --- java/libraries/io/src/processing/io/SoftwareServo.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/libraries/io/src/processing/io/SoftwareServo.java b/java/libraries/io/src/processing/io/SoftwareServo.java index dc516a7758..feece76e2a 100644 --- a/java/libraries/io/src/processing/io/SoftwareServo.java +++ b/java/libraries/io/src/processing/io/SoftwareServo.java @@ -34,7 +34,7 @@ public class SoftwareServo { public static final int DEFAULT_MAX_PULSE = 2400; protected int pin = -1; // gpio number (-1 .. not attached) - protected int handle = -1; // native thread id (<0 .. not started) + protected long handle = -1; // native thread id (<0 .. not started) protected int period = 20000; // 20 ms (50 Hz) protected int minPulse = 0; // minimum pulse width in microseconds protected int maxPulse = 0; // maximum pulse width in microseconds @@ -122,7 +122,7 @@ public void write(float angle) { } handle = NativeInterface.servoStartThread(pin, pulse, period); if (handle < 0) { - throw new RuntimeException(NativeInterface.getError(handle)); + throw new RuntimeException(NativeInterface.getError((int)handle)); } } else { // thread already running From 8acd9e37fef8425bfbe5e2f4721e26b5d9b57fe8 Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Fri, 22 Jul 2016 18:00:52 +0200 Subject: [PATCH 0019/1035] Fix running PDE from network locations Java has problems dealing with UNC paths. See http://wiki.eclipse.org/Eclipse/UNC_Paths#Programming_with_UNC_paths to get an idea how bad it is. Scheme specific part should contain the whole decoded URI except the protocol name and following colon, which should shield us from Java parsing URL parts incorrectly. --- app/src/processing/app/Platform.java | 2 +- core/src/processing/core/PApplet.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/processing/app/Platform.java b/app/src/processing/app/Platform.java index 3578e0a7d7..91ea3c3d12 100644 --- a/app/src/processing/app/Platform.java +++ b/app/src/processing/app/Platform.java @@ -288,7 +288,7 @@ static public File getContentFile(String name) { // Decode URL String decodedPath; try { - decodedPath = pathURL.toURI().getPath(); + decodedPath = pathURL.toURI().getSchemeSpecificPart(); } catch (URISyntaxException e) { e.printStackTrace(); return null; diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 8c268921fb..cb84f67c86 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -7413,7 +7413,7 @@ static protected String calcSketchPath() { URL jarURL = PApplet.class.getProtectionDomain().getCodeSource().getLocation(); // Decode URL - String jarPath = jarURL.toURI().getPath(); + String jarPath = jarURL.toURI().getSchemeSpecificPart(); // Workaround for bug in Java for OS X from Oracle (7u51) // https://github.com/processing/processing/issues/2181 From 16f4508c4aabb6cb6c47170ecfad5c9e1caac4ef Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Fri, 22 Jul 2016 18:02:32 +0200 Subject: [PATCH 0020/1035] Report file path when we can't load language --- app/src/processing/app/Language.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/processing/app/Language.java b/app/src/processing/app/Language.java index 149f163e08..5b155477a5 100644 --- a/app/src/processing/app/Language.java +++ b/app/src/processing/app/Language.java @@ -316,6 +316,9 @@ static class LanguageBundle { void read(File additions) { String[] lines = PApplet.loadStrings(additions); + if (lines == null) { + throw new NullPointerException("File not found:\n" + additions.getAbsolutePath()); + } //for (String line : lines) { for (int i = 0; i < lines.length; i++) { String line = lines[i]; From 2c7bdaa5a9c2db5afbcfdccfbc5885a445aac794 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Mon, 25 Jul 2016 21:52:08 -0400 Subject: [PATCH 0021/1035] rewrite csv handling, fix parsing bugs, remove newlines option, improve performance --- core/src/processing/data/Table.java | 298 ++++++++++++++++++++++++++-- 1 file changed, 282 insertions(+), 16 deletions(-) diff --git a/core/src/processing/data/Table.java b/core/src/processing/data/Table.java index dd93f8a999..9f6191013f 100644 --- a/core/src/processing/data/Table.java +++ b/core/src/processing/data/Table.java @@ -325,9 +325,7 @@ static public String extensionOptions(boolean loading, String filename, String o protected void parse(InputStream input, String options) throws IOException { - //init(); - - boolean awfulCSV = false; +// boolean awfulCSV = false; boolean header = false; String extension = null; boolean binary = false; @@ -347,8 +345,9 @@ protected void parse(InputStream input, String options) throws IOException { } else if (opt.equals("ods")) { extension = "ods"; } else if (opt.equals("newlines")) { - awfulCSV = true; - extension = "csv"; + //awfulCSV = true; + //extension = "csv"; + throw new IllegalArgumentException("The 'newlines' option is no longer necessary."); } else if (opt.equals("bin")) { binary = true; extension = "bin"; @@ -379,13 +378,16 @@ protected void parse(InputStream input, String options) throws IOException { } else { InputStreamReader isr = new InputStreamReader(input, encoding); BufferedReader reader = new BufferedReader(isr); - if (awfulCSV) { + /* + if (awfulCSV) { parseAwfulCSV(reader, header); } else if ("tsv".equals(extension)) { parseBasic(reader, header, true); } else if ("csv".equals(extension)) { parseBasic(reader, header, false); } + */ + parseBasic(reader, header, "tsv".equals(extension)); } } @@ -404,16 +406,16 @@ protected void parseBasic(BufferedReader reader, setRowCount(row << 1); } if (row == 0 && header) { - setColumnTitles(tsv ? PApplet.split(line, '\t') : splitLineCSV(line)); + setColumnTitles(tsv ? PApplet.split(line, '\t') : splitLineCSV(line, reader)); header = false; } else { - setRow(row, tsv ? PApplet.split(line, '\t') : splitLineCSV(line)); + setRow(row, tsv ? PApplet.split(line, '\t') : splitLineCSV(line, reader)); row++; } - // this is problematic unless we're going to calculate rowCount first if (row % 10000 == 0) { /* + // this is problematic unless we're going to calculate rowCount first if (row < rowCount) { int pct = (100 * row) / rowCount; if (pct != prev) { // also prevents "0%" from showing up @@ -445,6 +447,7 @@ protected void parseBasic(BufferedReader reader, // } + /* protected void parseAwfulCSV(BufferedReader reader, boolean header) throws IOException { char[] c = new char[100]; @@ -542,8 +545,187 @@ protected void parseAwfulCSV(BufferedReader reader, setRowCount(row); // shrink to the actual size } } + */ + class CommaSeparatedLine { + char[] c; + String[] pieces; + int pieceCount; + +// int offset; + int start; //, stop; + + String[] handle(String line, BufferedReader reader) throws IOException { +// PApplet.println("handle() called for: " + line); + start = 0; + pieceCount = 0; + c = line.toCharArray(); + + // get tally of number of columns and allocate the array + int cols = 1; // the first comma indicates the second column + boolean quote = false; + for (int i = 0; i < c.length; i++) { + if (!quote && (c[i] == ',')) { + cols++; + } else if (c[i] == '\"') { + // double double quotes (escaped quotes like "") will simply toggle + // this back and forth, so it should remain accurate + quote = !quote; + } + } + pieces = new String[cols]; + +// while (offset < c.length) { +// start = offset; + while (start < c.length) { + boolean enough = ingest(); + while (!enough) { + // found a newline inside the quote, grab another line + String nextLine = reader.readLine(); +// System.out.println("extending to " + nextLine); + if (nextLine == null) { +// System.err.println(line); + throw new IOException("Found a quoted line that wasn't terminated properly."); + } + // for simplicity, not bothering to skip what's already been read + // from c (and reset the offset to 0), opting to make a bigger array + // with both lines. + char[] temp = new char[c.length + 1 + nextLine.length()]; + PApplet.arrayCopy(c, temp, c.length); + // NOTE: we're converting to \n here, which isn't perfect + temp[c.length] = '\n'; + nextLine.getChars(0, nextLine.length(), temp, c.length + 1); +// c = temp; + return handle(new String(temp), reader); + //System.out.println(" full line is now " + new String(c)); + //stop = nextComma(c, offset); + //System.out.println("stop is now " + stop); + //enough = ingest(); + } + } + + // Make any remaining entries blanks instead of nulls. Empty columns from + // CSV are always "" not null, so this handles successive commas in a line + for (int i = pieceCount; i < pieces.length; i++) { + pieces[i] = ""; + } +// PApplet.printArray(pieces); + return pieces; + } + + protected void addPiece(int start, int stop, boolean quotes) { + if (quotes) { + int dest = start; + for (int i = start; i < stop; i++) { + if (c[i] == '\"') { + ++i; // step over the quote + } + if (i != dest) { + c[dest] = c[i]; + } + dest++; + } + pieces[pieceCount++] = new String(c, start, dest - start); + + } else { + pieces[pieceCount++] = new String(c, start, stop - start); + } + } + + /** + * Returns the next comma (not inside a quote) in the specified array. + * @param c array to search + * @param index offset at which to start looking + * @return index of the comma, or -1 if line ended inside an unclosed quote + */ + protected boolean ingest() { + boolean hasEscapedQuotes = false; + // not possible +// if (index == c.length) { // we're already at the end +// return c.length; +// } + boolean quoted = c[start] == '\"'; + if (quoted) { + start++; // step over the quote + } + int i = start; + while (i < c.length) { +// PApplet.println(c[i] + " i=" + i); + if (c[i] == '\"') { + // if this fella started with a quote + if (quoted) { + if (i == c.length-1) { + // closing quote for field; last field on the line + addPiece(start, i, hasEscapedQuotes); + start = c.length; + return true; + + } else if (c[i+1] == '\"') { + // an escaped quote inside a quoted field, step over it + hasEscapedQuotes = true; + i += 2; + + } else if (c[i+1] == ',') { + // that was our closing quote, get outta here + addPiece(start, i, hasEscapedQuotes); + start = i+2; + return true; + } + + } else { // not a quoted line + if (i == c.length-1) { + // we're at the end of the line, can't have an unescaped quote + throw new RuntimeException("Unterminated quote at end of line"); + + } else if (c[i+1] == '\"') { + // step over this crummy quote escape + hasEscapedQuotes = true; + i += 2; + + } else { + throw new RuntimeException("Unterminated quoted field mid-line"); + } + } + } else if (!quoted && c[i] == ',') { + addPiece(start, i, hasEscapedQuotes); + start = i+1; + return true; + + } else if (!quoted && i == c.length-1) { + addPiece(start, c.length, hasEscapedQuotes); + start = c.length; + return true; + + } else { // nothing all that interesting + i++; + } + } +// if (!quote && (c[i] == ',')) { +// // found a comma, return this location +// return i; +// } else if (c[i] == '\"') { +// // if it's a quote, then either the next char is another quote, +// // or if this is a quoted entry, it better be a comma +// quote = !quote; +// } +// } + + // if still inside a quote, indicate that another line should be read + if (quoted) { + return false; + } + +// // made it to the end of the array with no new comma +// return c.length; + + throw new RuntimeException("not sure how..."); + } + } + + + CommaSeparatedLine csl; + /** * Parse a line of text as comma-separated values, returning each value as * one entry in an array of String objects. Remove quotes from entries that @@ -551,23 +733,53 @@ protected void parseAwfulCSV(BufferedReader reader, * @param line line of text to be parsed * @return an array of the individual values formerly separated by commas */ - static protected String[] splitLineCSV(String line) { + protected String[] splitLineCSV(String line, BufferedReader reader) throws IOException { + if (csl == null) { + csl = new CommaSeparatedLine(); + } + return csl.handle(line, reader); + } + /* + static protected String[] splitLineCSV(String line, BufferedReader reader) throws IOException { char[] c = line.toCharArray(); - int rough = 1; // at least one + + // get tally of number of columns and allocate the array + int cols = 1; // the first comma indicates the second column boolean quote = false; for (int i = 0; i < c.length; i++) { if (!quote && (c[i] == ',')) { - rough++; + cols++; } else if (c[i] == '\"') { + // double double quotes (escaped quotes like "") will simply toggle + // this back and forth, so it should remain accurate quote = !quote; } } - String[] pieces = new String[rough]; + String[] pieces = new String[cols]; + + // now do actual parsing int pieceCount = 0; int offset = 0; while (offset < c.length) { int start = offset; int stop = nextComma(c, offset); + while (stop == -1) { + // found a newline inside the quote, grab another line + String nextLine = reader.readLine(); + System.out.println("extending to " + nextLine); + if (nextLine == null) { + System.err.println(line); + throw new IOException("Found a quoted line that wasn't terminated properly."); + } + char[] temp = new char[c.length + 1 + nextLine.length()]; + PApplet.arrayCopy(c, temp, c.length); + // NOTE: we're converting to \n here, which isn't perfect + temp[c.length] = '\n'; + line.getChars(0, nextLine.length(), temp, c.length + 1); + c = temp; + stop = nextComma(c, offset); + System.out.println("stop is now " + stop); + } offset = stop + 1; // next time around, need to step over the comment if (c[start] == '\"' && c[stop-1] == '\"') { start++; @@ -588,26 +800,80 @@ static protected String[] splitLineCSV(String line) { String s = new String(c, start, ii - start); pieces[pieceCount++] = s; } - // make any remaining entries blanks instead of nulls + // Make any remaining entries blanks instead of nulls. Empty columns from + // CSV are always "" not null, so this handles successive commas in a line for (int i = pieceCount; i < pieces.length; i++) { pieces[i] = ""; } return pieces; } + */ + /** + * Returns the next comma (not inside a quote) in the specified array. + * @param c array to search + * @param index offset at which to start looking + * @return index of the comma, or -1 if line ended inside an unclosed quote + */ + /* static protected int nextComma(char[] c, int index) { - boolean quote = false; + if (index == c.length) { // we're already at the end + return c.length; + } + boolean quoted = c[index] == '\"'; + if (quoted) { + index++; // step over the quote + } for (int i = index; i < c.length; i++) { + if (c[i] == '\"') { + // if this fella started with a quote + if (quoted) { + if (i == c.length-1) { + //return -1; // ran out of chars + // closing quote for field; last field on the line + return c.length; + } else if (c[i+1] == '\"') { + // an escaped quote inside a quoted field, step over it + i++; + } else if (c[i+1] == ',') { + // that's our closing quote, get outta here + return i+1; + } + + } else { // not a quoted line + if (i == c.length-1) { + // we're at the end of the line, can't have an unescaped quote + //return -1; // ran out of chars + throw new RuntimeException("Unterminated quoted field at end of line"); + } else if (c[i+1] == '\"') { + // step over this crummy quote escape + ++i; + } else { + throw new RuntimeException("Unterminated quoted field mid-line"); + } + } + } else if (!quoted && c[i] == ',') { + return i; + } if (!quote && (c[i] == ',')) { + // found a comma, return this location return i; } else if (c[i] == '\"') { + // if it's a quote, then either the next char is another quote, + // or if this is a quoted entry, it better be a comma quote = !quote; } } + // if still inside a quote, indicate that another line should be read + if (quote) { + return -1; + } + // made it to the end of the array with no new comma return c.length; } + */ /** @@ -4429,7 +4695,7 @@ protected void convertBasic(BufferedReader reader, boolean tsv, int prev = -1; int row = 0; while ((line = reader.readLine()) != null) { - convertRow(output, tsv ? PApplet.split(line, '\t') : splitLineCSV(line)); + convertRow(output, tsv ? PApplet.split(line, '\t') : splitLineCSV(line, reader)); row++; if (row % 10000 == 0) { From f1d3f24afa8cfce47a1ae5d4d63df3965b37b31a Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Mon, 25 Jul 2016 21:53:00 -0400 Subject: [PATCH 0022/1035] notes on latest updates and todos --- core/todo.txt | 2 ++ todo.txt | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/core/todo.txt b/core/todo.txt index 8502e27129..e90cc93044 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -1,4 +1,6 @@ 0241 (3.1.2) +X rewrite csv handling +X fix parsing bugs, remove newlines option, improve performance gohai X Fix GLExceptions on Raspberry Pi when using offscreen PGraphics diff --git a/todo.txt b/todo.txt index 12827388de..bb73aaaf47 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,7 @@ 0251 (3.1.2) high +_ update to 8 102 14 _ Pasting code from editor to empty editor produces Exception _ https://github.com/processing/processing/issues/4522 _ did we lose settings.path because it was too buggy? @@ -11,6 +12,14 @@ _ https://github.com/processing/processing/issues/4476 _ possible infinite loop on modified externally _ https://github.com/processing/processing/issues/3965 +lower +_ make when opening new editor window, open on the same display as current +_ https://github.com/processing/processing/issues/4526 +_ hi-dpi support on Linux +_ https://github.com/processing/processing/issues/4183 +_ PDE and sketches are 2x smaller on high-res Windows machines +_ https://github.com/processing/processing/issues/2411 + gohai X IO: Fix drawing for SPIAnalogDigital examples X https://github.com/processing/processing/pull/4480 From 5cfd880582c0a425e38cd56aa7fe263136519b36 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Thu, 28 Jul 2016 19:05:04 -0400 Subject: [PATCH 0023/1035] incorporate patch from Jakub for network paths on Windows --- todo.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/todo.txt b/todo.txt index bb73aaaf47..08e167d7e5 100644 --- a/todo.txt +++ b/todo.txt @@ -6,9 +6,6 @@ _ Pasting code from editor to empty editor produces Exception _ https://github.com/processing/processing/issues/4522 _ did we lose settings.path because it was too buggy? _ https://github.com/processing/processing/issues/3948 -_ lots of reports about a failure to launch on Windows -_ seems related to running from network drives -_ https://github.com/processing/processing/issues/4476 _ possible infinite loop on modified externally _ https://github.com/processing/processing/issues/3965 @@ -32,6 +29,12 @@ jakub X Return of the error check toggle X https://github.com/processing/processing/pull/4491 X https://github.com/processing/processing/issues/4485 +X problems when running PDE from network locations +X lots of reports about a failure to launch on Windows +X seems related to running from network drives +X https://github.com/processing/processing/issues/4417 +X https://github.com/processing/processing/pull/4582 +_ https://github.com/processing/processing/issues/4476 fixed in 3.1.1 X debugger deadlocks when choosing "Step Into" on println() From 4f066f331f1f9a16cec67e58b3d0e73ecd0b0697 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Thu, 28 Jul 2016 19:13:12 -0400 Subject: [PATCH 0024/1035] fix to prevent random(lo, hi) from returning hi (fixes #4551) --- core/src/processing/core/PApplet.java | 8 +++++++- core/todo.txt | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index cb84f67c86..af28a975d8 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -4921,7 +4921,13 @@ public final float randomGaussian() { public final float random(float low, float high) { if (low >= high) return low; float diff = high - low; - return random(diff) + low; + float value = 0; + // because of rounding error, can't just add low, otherwise it may hit high + // https://github.com/processing/processing/issues/4551 + do { + value = random(diff) + low; + } while (value == high); + return value; } diff --git a/core/todo.txt b/core/todo.txt index e90cc93044..6fa4d2f77f 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -1,6 +1,8 @@ 0241 (3.1.2) X rewrite csv handling X fix parsing bugs, remove newlines option, improve performance +X prevent random(low, high) from returning 'high' +X https://github.com/processing/processing/issues/4551 gohai X Fix GLExceptions on Raspberry Pi when using offscreen PGraphics From 497a2c430734fc691b09616881e9523d2c35d9f0 Mon Sep 17 00:00:00 2001 From: tyfkda Date: Sun, 3 Jul 2016 21:26:41 +0900 Subject: [PATCH 0025/1035] Show warning message if mode cannot be changed If selected mode cannot handle current source, show warning dialog and reselect old (current) mode. --- app/src/processing/app/Base.java | 9 ++++-- app/src/processing/app/ui/Editor.java | 32 ++++++++++++-------- build/shared/lib/languages/PDE.properties | 2 ++ build/shared/lib/languages/PDE_ja.properties | 2 ++ 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 35ae217c3e..543be6b912 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -885,8 +885,9 @@ public Mode getNextMode() { /** * The call has already checked to make sure this sketch is not modified, * now change the mode. + * @return true if mode is changed. */ - public void changeMode(Mode mode) { + public boolean changeMode(Mode mode) { Mode oldMode = activeEditor.getMode(); if (oldMode != mode) { Sketch sketch = activeEditor.getSketch(); @@ -908,7 +909,9 @@ public void changeMode(Mode mode) { break; } } - if (newModeCanHandleCurrentSource) { + if (!newModeCanHandleCurrentSource) { + return false; + } else { final File props = new File(sketch.getCodeFolder(), "sketch.properties"); saveModeSettings(props, nextMode); handleClose(activeEditor, true); @@ -918,10 +921,12 @@ public void changeMode(Mode mode) { // re-open the sketch using the mode we were in before saveModeSettings(props, oldMode); handleOpen(sketch.getMainFilePath()); + return false; } } } } + return true; } diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 94adc2a7cf..76421eff34 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -505,22 +505,15 @@ public void rebuildModePopup() { item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (!sketch.isModified()) { - base.changeMode(m); + if (!base.changeMode(m)) { + reselectMode(); + Messages.showWarning(Language.text("warn.cannot_change_mode.title"), + Language.interpolate("warn.cannot_change_mode.body", m)); + } } else { + reselectMode(); Messages.showWarning("Save", "Please save the sketch before changing the mode."); - - // Re-select the old checkbox, because it was automatically - // updated by Java, even though the Mode could not be changed. - // https://github.com/processing/processing/issues/2615 - for (Component c : getModePopup().getComponents()) { - if (c instanceof JRadioButtonMenuItem) { - if (((JRadioButtonMenuItem)c).getText() == mode.getTitle()) { - ((JRadioButtonMenuItem)c).setSelected(true); - break; - } - } - } } } }); @@ -543,6 +536,19 @@ public void actionPerformed(ActionEvent e) { Toolkit.setMenuMnemsInside(modePopup); } + // Re-select the old checkbox, because it was automatically + // updated by Java, even though the Mode could not be changed. + // https://github.com/processing/processing/issues/2615 + private void reselectMode() { + for (Component c : getModePopup().getComponents()) { + if (c instanceof JRadioButtonMenuItem) { + if (((JRadioButtonMenuItem)c).getText() == mode.getTitle()) { + ((JRadioButtonMenuItem)c).setSelected(true); + break; + } + } + } + } public JPopupMenu getModePopup() { return modePopup.getPopupMenu(); diff --git a/build/shared/lib/languages/PDE.properties b/build/shared/lib/languages/PDE.properties index 7a5e086442..f1c3eedb14 100644 --- a/build/shared/lib/languages/PDE.properties +++ b/build/shared/lib/languages/PDE.properties @@ -529,6 +529,8 @@ contrib.import.errors.link = Error: The library %s has a strange looking downloa warn.delete = Delete warn.delete.sketch = Are you sure you want to delete this sketch? warn.delete.file = Are you sure you want to delete "%s"? +warn.cannot_change_mode.title = Cannot change mode +warn.cannot_change_mode.body = Cannot change mode,\nbecause "%s" mode is not compatible with current mode. # --------------------------------------- diff --git a/build/shared/lib/languages/PDE_ja.properties b/build/shared/lib/languages/PDE_ja.properties index 5f7231a5b9..bd59345f86 100644 --- a/build/shared/lib/languages/PDE_ja.properties +++ b/build/shared/lib/languages/PDE_ja.properties @@ -528,6 +528,8 @@ contrib.import.errors.link = Error: The library %s has a strange looking downloa warn.delete = Delete warn.delete.sketch = Are you sure you want to delete this sketch? warn.delete.file = Are you sure you want to delete "%s"? +warn.cannot_change_mode.title = モード変更失敗 +warn.cannot_change_mode.body = 互換性がないため、"%s"モードに切り替えられません。 # --------------------------------------- From 005189ddc7e5bf306f759920de7100a0aa6181c2 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Thu, 28 Jul 2016 20:26:25 -0400 Subject: [PATCH 0026/1035] notes about recent updates --- core/todo.txt | 8 ++++++++ todo.txt | 46 +++++++++++++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/core/todo.txt b/core/todo.txt index 6fa4d2f77f..495aa96f0f 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -18,6 +18,14 @@ jakub X Prevent NPE in loadImage() when called before setup() X https://github.com/processing/processing/pull/4505 +andres +X fix crash when calling getUniformLoc() called in PShader.set() +X https://github.com/processing/processing/issues/4542 + + +_ disable async saveFrame() by default? +_ https://github.com/processing/processing/issues/4578 + _ Can't render PGraphics object using image() within a PDF _ https://github.com/processing/processing/issues/4473 diff --git a/todo.txt b/todo.txt index 08e167d7e5..9ee017e6cb 100644 --- a/todo.txt +++ b/todo.txt @@ -1,21 +1,5 @@ 0251 (3.1.2) -high -_ update to 8 102 14 -_ Pasting code from editor to empty editor produces Exception -_ https://github.com/processing/processing/issues/4522 -_ did we lose settings.path because it was too buggy? -_ https://github.com/processing/processing/issues/3948 -_ possible infinite loop on modified externally -_ https://github.com/processing/processing/issues/3965 - -lower -_ make when opening new editor window, open on the same display as current -_ https://github.com/processing/processing/issues/4526 -_ hi-dpi support on Linux -_ https://github.com/processing/processing/issues/4183 -_ PDE and sketches are 2x smaller on high-res Windows machines -_ https://github.com/processing/processing/issues/2411 gohai X IO: Fix drawing for SPIAnalogDigital examples @@ -24,6 +8,10 @@ X Update JVM warning text on Linux X https://github.com/processing/processing/pull/4512 X undo the 8u91 workaround, add Mesa warning for ARM X https://github.com/processing/processing/pull/4508 +X IO: We want motors, they said (implements SoftwareServo) +X https://github.com/processing/processing/pull/4546 +X Add a temporary workaround for the CHIP to deal with cursor problems +X https://github.com/processing/processing/pull/4554 jakub X Return of the error check toggle @@ -34,7 +22,13 @@ X lots of reports about a failure to launch on Windows X seems related to running from network drives X https://github.com/processing/processing/issues/4417 X https://github.com/processing/processing/pull/4582 -_ https://github.com/processing/processing/issues/4476 +X https://github.com/processing/processing/issues/4476 + +contrib +X updates to Japanese translation +X https://github.com/processing/processing/pull/4564 +X Make preferences button wider for Japanese +X https://github.com/processing/processing/pull/4558 fixed in 3.1.1 X debugger deadlocks when choosing "Step Into" on println() @@ -42,6 +36,24 @@ X https://github.com/processing/processing/issues/3923 X Suggestions switch scope to first import X https://github.com/processing/processing/issues/4016 + +high +_ update to 8 102 14 +_ Pasting code from editor to empty editor produces Exception +_ https://github.com/processing/processing/issues/4522 +_ did we lose settings.path because it was too buggy? +_ https://github.com/processing/processing/issues/3948 +_ possible infinite loop on modified externally +_ https://github.com/processing/processing/issues/3965 + +lower +_ make when opening new editor window, open on the same display as current +_ https://github.com/processing/processing/issues/4526 +_ hi-dpi support on Linux +_ https://github.com/processing/processing/issues/4183 +_ PDE and sketches are 2x smaller on high-res Windows machines +_ https://github.com/processing/processing/issues/2411 + _ Library path mismatch between processing-java and export _ https://github.com/processing/processing/issues/4493 From 81d3f1f914522a37f378e16a3a03bf1528ddce69 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Fri, 29 Jul 2016 10:28:27 -0400 Subject: [PATCH 0027/1035] update to Java 8u102 --- build/build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build.xml b/build/build.xml index f608c4010e..97abc2e061 100644 --- a/build/build.xml +++ b/build/build.xml @@ -59,7 +59,7 @@ - + From 5eed2ffc51e220141d8763374b080eb0c1b4fe93 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Fri, 29 Jul 2016 10:28:35 -0400 Subject: [PATCH 0028/1035] disable async image saving by default --- core/src/processing/core/PGraphics.java | 4 +++- core/todo.txt | 12 +++++++----- todo.txt | 21 ++++++++++++--------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java index 4f62d3542d..96343b4d7e 100644 --- a/core/src/processing/core/PGraphics.java +++ b/core/src/processing/core/PGraphics.java @@ -718,7 +718,9 @@ public class PGraphics extends PImage implements PConstants { public PGraphics() { - // Allows subclasses to override + // In 3.1.2, giving up on the async image saving as the default + hints[DISABLE_ASYNC_SAVEFRAME] = true; + System.out.println("disabling async saveFrame"); } diff --git a/core/todo.txt b/core/todo.txt index 495aa96f0f..dc6557ffeb 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -3,6 +3,8 @@ X rewrite csv handling X fix parsing bugs, remove newlines option, improve performance X prevent random(low, high) from returning 'high' X https://github.com/processing/processing/issues/4551 +X disable async saveFrame() by default +X https://github.com/processing/processing/issues/4578 gohai X Fix GLExceptions on Raspberry Pi when using offscreen PGraphics @@ -23,9 +25,6 @@ X fix crash when calling getUniformLoc() called in PShader.set() X https://github.com/processing/processing/issues/4542 -_ disable async saveFrame() by default? -_ https://github.com/processing/processing/issues/4578 - _ Can't render PGraphics object using image() within a PDF _ https://github.com/processing/processing/issues/4473 @@ -33,8 +32,6 @@ _ JSONObject get() method is private _ https://github.com/processing/processing/issues/4334 _ https://github.com/processing/processing/pull/4336 -_ add push() and pop() methods to mirror js? - _ textAlign(CENTER) and pixelDensity(2) aligning incorrectly with Java2D _ https://github.com/processing/processing/issues/4020 _ add increment() that takes IntDict to merge things @@ -58,6 +55,11 @@ _ if not, need to add them to the block list _ the --hide-stop option not working (FX only? traces?) +discussion +_ add push() and pop() methods to mirror js? +_ how to handle the touch api + + known _ window must close when using file dialogs with OpenGL on Windows _ https://github.com/processing/processing/issues/3831 diff --git a/todo.txt b/todo.txt index 9ee017e6cb..c58e4ae5e0 100644 --- a/todo.txt +++ b/todo.txt @@ -1,5 +1,5 @@ 0251 (3.1.2) - +X update to Java 8u102 build 14 gohai X IO: Fix drawing for SPIAnalogDigital examples @@ -12,6 +12,9 @@ X IO: We want motors, they said (implements SoftwareServo) X https://github.com/processing/processing/pull/4546 X Add a temporary workaround for the CHIP to deal with cursor problems X https://github.com/processing/processing/pull/4554 +X Fix the Downloader so that builds work again +X https://github.com/processing/processing/issues/4496 +X https://github.com/processing/processing/pull/4511 jakub X Return of the error check toggle @@ -29,16 +32,23 @@ X updates to Japanese translation X https://github.com/processing/processing/pull/4564 X Make preferences button wider for Japanese X https://github.com/processing/processing/pull/4558 +X Show warning message if mode cannot be changed +X https://github.com/processing/processing/pull/4559 fixed in 3.1.1 X debugger deadlocks when choosing "Step Into" on println() X https://github.com/processing/processing/issues/3923 X Suggestions switch scope to first import X https://github.com/processing/processing/issues/4016 +X loadImage() immediately after saveFrame() foiled by async default +X https://github.com/processing/processing/issues/4218 +X the hint() mostly works, but gross to use a hint frequently high -_ update to 8 102 14 +_ NullPointerException in SketchCode.getDocumentText() +_ https://github.com/processing/processing/issues/4555 +_ https://github.com/processing/processing/pull/4547 _ Pasting code from editor to empty editor produces Exception _ https://github.com/processing/processing/issues/4522 _ did we lose settings.path because it was too buggy? @@ -113,13 +123,6 @@ _ Tweak Mode sometimes freezes while running, require a force quit _ https://github.com/processing/processing/issues/3928 -discussion -_ loadImage() immediately after saveFrame() foiled by async default -_ https://github.com/processing/processing/issues/4218 -_ the hint() mostly works, but gross to use a hint frequently -_ how to handle the touch api - - gui _ Tooltip over variable decl has wrong style and content _ make all tooltips run through our style From 911fff21d725e7061e70a8d75c55245732fcba70 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Fri, 29 Jul 2016 10:54:52 -0400 Subject: [PATCH 0029/1035] write release notes, finalizing 3.1.2 --- build/shared/revisions.txt | 105 +++++++++++++++++++++++++++++++++++++ core/todo.txt | 19 +++---- todo.txt | 2 +- 3 files changed, 116 insertions(+), 10 deletions(-) diff --git a/build/shared/revisions.txt b/build/shared/revisions.txt index ad4c6f1d89..bc79e2790c 100644 --- a/build/shared/revisions.txt +++ b/build/shared/revisions.txt @@ -1,3 +1,108 @@ +PROCESSING 3.1.2 (REV 0251) - 29 July 2016 + +Happy Fathom Fiesta Day! We'll be taking the afternoon off to enjoy +the summer, maybe you should take the afternoon off and dive into +a new Processing release? + + +[ pde fixes ] + ++ NullPointerException in LanguageBundle.read() on startup that prevented + Processing from starting up on Windows machines. Network drive issue. + https://github.com/processing/processing/issues/4417 + https://github.com/processing/processing/pull/4582 + https://github.com/processing/processing/issues/4476 + ++ Bring back preference to hide the error checking. Error checking will + continue in the background because it's needed for parsing/preprocessing, + but some were complaining about the error checker messages. + https://github.com/processing/processing/pull/4491 + https://github.com/processing/processing/issues/4485 + ++ Updates to Japanese translation + https://github.com/processing/processing/pull/4564 + ++ Make preferences button wider for Japanese + https://github.com/processing/processing/pull/4558 + ++ Fix logic for warning message when the Mode cannot be changed + https://github.com/processing/processing/pull/4559 + ++ Update to Java 8u102 build 14 + + +[ api fixes ] + ++ Rewrite CSV handling to take care of some parsing bugs and improve + performance. Note that the 'newlines' option is no longer necessary + when loading files that contain newline characters mid-field. + ++ Prevent random(low, high) from returning 'high' + https://github.com/processing/processing/issues/4551 + ++ Fixed iterator remove() methods so they don't skip container elements + https://github.com/processing/processing/pull/4519 + ++ Added a check for length 0 arrays in expand() + https://github.com/processing/processing/pull/4520 + + +[ graphics ] + ++ Disable asynchronous saveFrame() by default. This can really improve + performance, but can cause weird glitches. Bring it back by using + hint(ENABLE_ASYNC_SAVEFRAME) in your code to blissfully and speedily + create image sequences. + https://github.com/processing/processing/issues/4578 + ++ Prevent NPE in loadImage() when called before setup() + https://github.com/processing/processing/pull/4505 + ++ Fix crash when calling getUniformLoc() called in PShader.set() + https://github.com/processing/processing/issues/4542 + + +[ raspberry pi ] + ++ IO: Fix drawing for SPIAnalogDigital examples + https://github.com/processing/processing/pull/4480 + ++ Update JVM warning text on Linux + https://github.com/processing/processing/pull/4512 + ++ Undo the 8u91 workaround, add Mesa warning for ARM + https://github.com/processing/processing/pull/4508 + ++ IO: We want motors, they said (implements SoftwareServo) + https://github.com/processing/processing/pull/4546 + ++ Add a temporary workaround for the CHIP to deal with cursor problems + https://github.com/processing/processing/pull/4554 + ++ Fix GLExceptions on Raspberry Pi when using offscreen PGraphics + https://github.com/processing/processing/pull/4524 + + +[ fixed earlier ] + ++ Debugger deadlocks when choosing "Step Into" on println() + https://github.com/processing/processing/issues/3923 + ++ Suggestions switch scope to first import + https://github.com/processing/processing/issues/4016 + ++ loadImage() immediately after saveFrame() foiled by async default + https://github.com/processing/processing/issues/4218 + the hint() mostly works, but gross to use a hint frequently + ++ Fix the Downloader so that builds work again + https://github.com/processing/processing/issues/4496 + https://github.com/processing/processing/pull/4511 + + +. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + PROCESSING 3.1.1 (REV 0250) - 16 May 2016 Happy Day-after-my-Mother-in-Law's-birthday! (After the last two releases diff --git a/core/todo.txt b/core/todo.txt index dc6557ffeb..248e00fa02 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -24,6 +24,10 @@ andres X fix crash when calling getUniformLoc() called in PShader.set() X https://github.com/processing/processing/issues/4542 +earlier +X see if 8u65 or 8u66 fix the JavaFX problem +X if not, need to add them to the block list + _ Can't render PGraphics object using image() within a PDF _ https://github.com/processing/processing/issues/4473 @@ -45,19 +49,14 @@ _ or is that being set/unset used for any state info? _ when calling exit(), if sketch has halted w/ exception, force the quit _ otherwise have to double-quit with cmd-q on OS X _ simple test case: using size() inside setup() from Eclipse -_ figure our size(img.width, img.height) situation -_ just make loadImage() work in settings -_ update the wiki and reference -_ menu bar not hiding properly in exported applications with FX2D -_ hideMenuBar() called from setup() works fine -_ see if 8u65 or 8u66 fix the JavaFX problem -_ if not, need to add them to the block list -_ the --hide-stop option not working (FX only? traces?) discussion _ add push() and pop() methods to mirror js? _ how to handle the touch api +_ figure our size(img.width, img.height) situation +_ just make loadImage() work in settings +_ update the wiki and reference known @@ -86,8 +85,10 @@ _ https://github.com/processing/processing/issues/3753 javafx -_ fix menu bar hiding in exported JavaFX apps +_ menu bar not hiding properly in exported applications with FX2D +_ hideMenuBar() called from setup() works fine _ just call it around setup time? +_ the --hide-stop option not working (FX only? traces?) _ make wiki about quirks _ starving the thread makes things really slow down _ keyPressed() is always uppercase, keyTyped() will be correct diff --git a/todo.txt b/todo.txt index c58e4ae5e0..75771db01b 100644 --- a/todo.txt +++ b/todo.txt @@ -32,7 +32,7 @@ X updates to Japanese translation X https://github.com/processing/processing/pull/4564 X Make preferences button wider for Japanese X https://github.com/processing/processing/pull/4558 -X Show warning message if mode cannot be changed +X Show warning message if Mode cannot be changed X https://github.com/processing/processing/pull/4559 fixed in 3.1.1 From 138445dc2a6111957988b2aed0afceee5e542359 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Fri, 29 Jul 2016 11:02:42 -0400 Subject: [PATCH 0030/1035] @#($*&! --- core/src/processing/core/PGraphics.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java index 96343b4d7e..c7606c7ace 100644 --- a/core/src/processing/core/PGraphics.java +++ b/core/src/processing/core/PGraphics.java @@ -720,7 +720,6 @@ public class PGraphics extends PImage implements PConstants { public PGraphics() { // In 3.1.2, giving up on the async image saving as the default hints[DISABLE_ASYNC_SAVEFRAME] = true; - System.out.println("disabling async saveFrame"); } From 4674080d4cb9a0ac9696b5da770ab3551c24e81f Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Fri, 29 Jul 2016 11:16:50 -0400 Subject: [PATCH 0031/1035] starting the next release --- app/src/processing/app/Base.java | 4 +-- core/done.txt | 31 +++++++++++++++++++++ core/todo.txt | 30 +------------------- done.txt | 47 ++++++++++++++++++++++++++++++++ todo.txt | 46 +------------------------------ 5 files changed, 82 insertions(+), 76 deletions(-) diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 543be6b912..e1fe1df470 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -55,9 +55,9 @@ public class Base { // Added accessors for 0218 because the UpdateCheck class was not properly // updating the values, due to javac inlining the static final values. - static private final int REVISION = 251; + static private final int REVISION = 252; /** This might be replaced by main() if there's a lib/version.txt file. */ - static private String VERSION_NAME = "0251"; //$NON-NLS-1$ + static private String VERSION_NAME = "0252"; //$NON-NLS-1$ /** Set true if this a proper release rather than a numbered revision. */ /** True if heavy debugging error/log messages are enabled */ diff --git a/core/done.txt b/core/done.txt index 5137f4d175..49f9240e83 100644 --- a/core/done.txt +++ b/core/done.txt @@ -1,3 +1,34 @@ +0251 (3.1.2) +X rewrite csv handling +X fix parsing bugs, remove newlines option, improve performance +X prevent random(low, high) from returning 'high' +X https://github.com/processing/processing/issues/4551 +X disable async saveFrame() by default +X https://github.com/processing/processing/issues/4578 + +gohai +X Fix GLExceptions on Raspberry Pi when using offscreen PGraphics +X https://github.com/processing/processing/pull/4524 + +leslie +X fixed iterator remove() methods so they dont skip container elements +X https://github.com/processing/processing/pull/4519 +X added a check for length 0 arrays in expand() +X https://github.com/processing/processing/pull/4520 + +jakub +X Prevent NPE in loadImage() when called before setup() +X https://github.com/processing/processing/pull/4505 + +andres +X fix crash when calling getUniformLoc() called in PShader.set() +X https://github.com/processing/processing/issues/4542 + +earlier +X see if 8u65 or 8u66 fix the JavaFX problem +X if not, need to add them to the block list + + 0250 (3.1.1) X implement support for encoding= option in loadTable() diff --git a/core/todo.txt b/core/todo.txt index 248e00fa02..3ea5ab96c3 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -1,32 +1,4 @@ -0241 (3.1.2) -X rewrite csv handling -X fix parsing bugs, remove newlines option, improve performance -X prevent random(low, high) from returning 'high' -X https://github.com/processing/processing/issues/4551 -X disable async saveFrame() by default -X https://github.com/processing/processing/issues/4578 - -gohai -X Fix GLExceptions on Raspberry Pi when using offscreen PGraphics -X https://github.com/processing/processing/pull/4524 - -leslie -X fixed iterator remove() methods so they dont skip container elements -X https://github.com/processing/processing/pull/4519 -X added a check for length 0 arrays in expand() -X https://github.com/processing/processing/pull/4520 - -jakub -X Prevent NPE in loadImage() when called before setup() -X https://github.com/processing/processing/pull/4505 - -andres -X fix crash when calling getUniformLoc() called in PShader.set() -X https://github.com/processing/processing/issues/4542 - -earlier -X see if 8u65 or 8u66 fix the JavaFX problem -X if not, need to add them to the block list +0252 (3.1.3?) _ Can't render PGraphics object using image() within a PDF diff --git a/done.txt b/done.txt index d0bb16087a..5399f7949f 100644 --- a/done.txt +++ b/done.txt @@ -1,3 +1,50 @@ +0251 (3.1.2) +X update to Java 8u102 build 14 + +gohai +X IO: Fix drawing for SPIAnalogDigital examples +X https://github.com/processing/processing/pull/4480 +X Update JVM warning text on Linux +X https://github.com/processing/processing/pull/4512 +X undo the 8u91 workaround, add Mesa warning for ARM +X https://github.com/processing/processing/pull/4508 +X IO: We want motors, they said (implements SoftwareServo) +X https://github.com/processing/processing/pull/4546 +X Add a temporary workaround for the CHIP to deal with cursor problems +X https://github.com/processing/processing/pull/4554 +X Fix the Downloader so that builds work again +X https://github.com/processing/processing/issues/4496 +X https://github.com/processing/processing/pull/4511 + +jakub +X Return of the error check toggle +X https://github.com/processing/processing/pull/4491 +X https://github.com/processing/processing/issues/4485 +X problems when running PDE from network locations +X lots of reports about a failure to launch on Windows +X seems related to running from network drives +X https://github.com/processing/processing/issues/4417 +X https://github.com/processing/processing/pull/4582 +X https://github.com/processing/processing/issues/4476 + +contrib +X updates to Japanese translation +X https://github.com/processing/processing/pull/4564 +X Make preferences button wider for Japanese +X https://github.com/processing/processing/pull/4558 +X Show warning message if Mode cannot be changed +X https://github.com/processing/processing/pull/4559 + +fixed in 3.1.1 +X debugger deadlocks when choosing "Step Into" on println() +X https://github.com/processing/processing/issues/3923 +X Suggestions switch scope to first import +X https://github.com/processing/processing/issues/4016 +X loadImage() immediately after saveFrame() foiled by async default +X https://github.com/processing/processing/issues/4218 +X the hint() mostly works, but gross to use a hint frequently + + 0250 (3.1.1) X Out of date Modes will hang Processing 3 on startup or when changing Modes X Prevent bad Mode from taking down the environment diff --git a/todo.txt b/todo.txt index 75771db01b..2c628f12fa 100644 --- a/todo.txt +++ b/todo.txt @@ -1,48 +1,4 @@ -0251 (3.1.2) -X update to Java 8u102 build 14 - -gohai -X IO: Fix drawing for SPIAnalogDigital examples -X https://github.com/processing/processing/pull/4480 -X Update JVM warning text on Linux -X https://github.com/processing/processing/pull/4512 -X undo the 8u91 workaround, add Mesa warning for ARM -X https://github.com/processing/processing/pull/4508 -X IO: We want motors, they said (implements SoftwareServo) -X https://github.com/processing/processing/pull/4546 -X Add a temporary workaround for the CHIP to deal with cursor problems -X https://github.com/processing/processing/pull/4554 -X Fix the Downloader so that builds work again -X https://github.com/processing/processing/issues/4496 -X https://github.com/processing/processing/pull/4511 - -jakub -X Return of the error check toggle -X https://github.com/processing/processing/pull/4491 -X https://github.com/processing/processing/issues/4485 -X problems when running PDE from network locations -X lots of reports about a failure to launch on Windows -X seems related to running from network drives -X https://github.com/processing/processing/issues/4417 -X https://github.com/processing/processing/pull/4582 -X https://github.com/processing/processing/issues/4476 - -contrib -X updates to Japanese translation -X https://github.com/processing/processing/pull/4564 -X Make preferences button wider for Japanese -X https://github.com/processing/processing/pull/4558 -X Show warning message if Mode cannot be changed -X https://github.com/processing/processing/pull/4559 - -fixed in 3.1.1 -X debugger deadlocks when choosing "Step Into" on println() -X https://github.com/processing/processing/issues/3923 -X Suggestions switch scope to first import -X https://github.com/processing/processing/issues/4016 -X loadImage() immediately after saveFrame() foiled by async default -X https://github.com/processing/processing/issues/4218 -X the hint() mostly works, but gross to use a hint frequently +0252 (3.1.3?) high From 1e4390d1e301e15a6021a34f49e6fcd81f0aa27f Mon Sep 17 00:00:00 2001 From: tyfkda Date: Sat, 30 Jul 2016 07:47:25 +0900 Subject: [PATCH 0032/1035] Simplify conditional branch Checking `editor.tabs.expand` is done just before the block, so this condition is always met. --- java/src/processing/mode/java/JavaInputHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/processing/mode/java/JavaInputHandler.java b/java/src/processing/mode/java/JavaInputHandler.java index 3e823f16bb..99b7f18c4f 100644 --- a/java/src/processing/mode/java/JavaInputHandler.java +++ b/java/src/processing/mode/java/JavaInputHandler.java @@ -160,7 +160,7 @@ public boolean handlePressed(KeyEvent event) { textarea.setSelectedText(spaces(tabSize)); event.consume(); - } else if (!Preferences.getBoolean("editor.tabs.expand")) { + } else { // !Preferences.getBoolean("editor.tabs.expand") textarea.setSelectedText("\t"); event.consume(); } From c983f07a2a324be56f87392894103fdfb30c26a1 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 30 Jul 2016 11:47:10 -0400 Subject: [PATCH 0033/1035] looking into alternative for #4560 --- .../processing/mode/java/pdex/JavaTextAreaPainter.java | 10 +++++++++- todo.txt | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java index 27e08c2e45..d3204186cb 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -71,6 +71,7 @@ public class JavaTextAreaPainter extends TextAreaPainter protected Font gutterTextFont; protected Color gutterTextColor; + protected Color gutterPastColor; protected Color gutterLineHighlightColor; @@ -163,7 +164,10 @@ protected void paintLeftGutter(Graphics gfx, int line, int x) { text = getJavaTextArea().getGutterText(line); } - gfx.setColor(gutterTextColor); + gfx.setColor(line < textArea.getLineCount() ? gutterTextColor : gutterPastColor); +// if (line >= textArea.getLineCount()) { +// //gfx.setColor(new Color(gutterTextColor.getRGB(), ); +// } int textRight = Editor.LEFT_GUTTER - Editor.GUTTER_MARGIN; int textBaseline = textArea.lineToY(line) + fm.getHeight(); @@ -355,6 +359,10 @@ public void setMode(Mode mode) { gutterTextFont = mode.getFont("editor.gutter.text.font"); gutterTextColor = mode.getColor("editor.gutter.text.color"); + gutterPastColor = new Color(gutterTextColor.getRed(), + gutterTextColor.getGreen(), + gutterTextColor.getBlue(), + 96); gutterLineHighlightColor = mode.getColor("editor.gutter.linehighlight.color"); } diff --git a/todo.txt b/todo.txt index 2c628f12fa..1dcbfdc1cd 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,10 @@ 0252 (3.1.3?) +_ modify line number color when no lines extend that far? +_ https://github.com/processing/processing/pull/4560 + +contrib +X Simplify conditional branch +X https://github.com/processing/processing/pull/4589 high From f6d8e188f94583fd459124e1229d4288d5ac8fa0 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 30 Jul 2016 11:49:13 -0400 Subject: [PATCH 0034/1035] don't hide exception.. need to fix --- .../mode/java/pdex/JavaTextAreaPainter.java | 12 +++++------- todo.txt | 1 + 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java index d3204186cb..61f4b5db6c 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -111,13 +111,10 @@ public void mousePressed(MouseEvent event) { * Paint a line. Paints the gutter (with background color and text) then the * line (background color and text). * - * @param gfx - * the graphics context + * @param gfx the graphics context * @param tokenMarker - * @param line - * 0-based line number - * @param x - * horizontal position + * @param line 0-based line number + * @param x horizontal position */ @Override protected void paintLine(Graphics gfx, int line, int x, @@ -128,7 +125,8 @@ protected void paintLine(Graphics gfx, int line, int x, super.paintLine(gfx, line, x + Editor.LEFT_GUTTER, tokenMarker); } catch (Exception e) { - Messages.log(e.getMessage()); + //Messages.log(e.getMessage()); + e.printStackTrace(); } // formerly only when in debug mode diff --git a/todo.txt b/todo.txt index 1dcbfdc1cd..3b87d81e6c 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,7 @@ 0252 (3.1.3?) _ modify line number color when no lines extend that far? _ https://github.com/processing/processing/pull/4560 +_ swap out the fonts? contrib X Simplify conditional branch From 9b725a23d7d4abaab1c43b96538dc1c3ff304f10 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 30 Jul 2016 12:09:11 -0400 Subject: [PATCH 0035/1035] hrm... --- java/src/processing/mode/java/pdex/JavaTextAreaPainter.java | 3 ++- todo.txt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java index 61f4b5db6c..1f9e311dab 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -46,7 +46,6 @@ import javax.swing.text.Segment; import javax.swing.text.Utilities; -import processing.app.Messages; import processing.app.Mode; import processing.app.SketchCode; import processing.app.syntax.SyntaxDocument; @@ -183,6 +182,8 @@ protected void paintLeftGutter(Graphics gfx, int line, int x) { //text = makeOSF(String.valueOf(line + 1)); gfx.setFont(gutterTextFont); +// ((Graphics2D) gfx).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, +// RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); // Right-align the text char[] txt = text.toCharArray(); int tx = textRight - gfx.getFontMetrics().charsWidth(txt, 0, txt.length); diff --git a/todo.txt b/todo.txt index 3b87d81e6c..11ec9b54e4 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,8 @@ 0252 (3.1.3?) _ modify line number color when no lines extend that far? _ https://github.com/processing/processing/pull/4560 +_ text gutter doesn't seem to be hidpi +X or is it b/c screen not quite 2x? (nope) _ swap out the fonts? contrib From 5221faa3dae5620d49794dc500edbf85d8cdc5d8 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sun, 31 Jul 2016 16:42:52 -0400 Subject: [PATCH 0036/1035] minor edits pulled in from ftsv project --- core/src/processing/data/Table.java | 38 +++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/core/src/processing/data/Table.java b/core/src/processing/data/Table.java index 9f6191013f..d22fa931e2 100644 --- a/core/src/processing/data/Table.java +++ b/core/src/processing/data/Table.java @@ -548,7 +548,7 @@ protected void parseAwfulCSV(BufferedReader reader, */ - class CommaSeparatedLine { + static class CommaSeparatedLine { char[] c; String[] pieces; int pieceCount; @@ -797,6 +797,23 @@ static protected String[] splitLineCSV(String line, BufferedReader reader) throw i++; ii++; } + /* + if (autoTyping) { + if (autoTypes == null) { + autoTypes = new int[pieces.length]; + + } else { + if (autoTypes.length < pieces.length) { + + } + Double.parseDouble(""); + Float.parseFloat("blah"); +// if (autoTypes[pieceCount]) { +// +// } + } + } + */ String s = new String(c, start, ii - start); pieces[pieceCount++] = s; } @@ -2000,12 +2017,7 @@ public void setColumnType(String columnName, String columnType) { } - /** - * Set the data type for a column so that using it is more efficient. - * @param column the column to change - * @param columnType One of int, long, float, double, string, or category. - */ - public void setColumnType(int column, String columnType) { + static int parseColumnType(String columnType) { columnType = columnType.toLowerCase(); int type = -1; if (columnType.equals("string")) { @@ -2023,7 +2035,17 @@ public void setColumnType(int column, String columnType) { } else { throw new IllegalArgumentException("'" + columnType + "' is not a valid column type."); } - setColumnType(column, type); + return type; + } + + + /** + * Set the data type for a column so that using it is more efficient. + * @param column the column to change + * @param columnType One of int, long, float, double, string, or category. + */ + public void setColumnType(int column, String columnType) { + setColumnType(column, parseColumnType(columnType)); } From 49475c668220a3840594c4f15fd6976b29e8d62a Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sun, 31 Jul 2016 20:05:56 -0400 Subject: [PATCH 0037/1035] that ain't supposed to be in there --- core/src/processing/data/Table.java | 87 ----------------------------- 1 file changed, 87 deletions(-) diff --git a/core/src/processing/data/Table.java b/core/src/processing/data/Table.java index d22fa931e2..5d86e45bbe 100644 --- a/core/src/processing/data/Table.java +++ b/core/src/processing/data/Table.java @@ -739,93 +739,6 @@ protected String[] splitLineCSV(String line, BufferedReader reader) throws IOExc } return csl.handle(line, reader); } - /* - static protected String[] splitLineCSV(String line, BufferedReader reader) throws IOException { - char[] c = line.toCharArray(); - - // get tally of number of columns and allocate the array - int cols = 1; // the first comma indicates the second column - boolean quote = false; - for (int i = 0; i < c.length; i++) { - if (!quote && (c[i] == ',')) { - cols++; - } else if (c[i] == '\"') { - // double double quotes (escaped quotes like "") will simply toggle - // this back and forth, so it should remain accurate - quote = !quote; - } - } - String[] pieces = new String[cols]; - - // now do actual parsing - int pieceCount = 0; - int offset = 0; - while (offset < c.length) { - int start = offset; - int stop = nextComma(c, offset); - while (stop == -1) { - // found a newline inside the quote, grab another line - String nextLine = reader.readLine(); - System.out.println("extending to " + nextLine); - if (nextLine == null) { - System.err.println(line); - throw new IOException("Found a quoted line that wasn't terminated properly."); - } - char[] temp = new char[c.length + 1 + nextLine.length()]; - PApplet.arrayCopy(c, temp, c.length); - // NOTE: we're converting to \n here, which isn't perfect - temp[c.length] = '\n'; - line.getChars(0, nextLine.length(), temp, c.length + 1); - c = temp; - stop = nextComma(c, offset); - System.out.println("stop is now " + stop); - } - offset = stop + 1; // next time around, need to step over the comment - if (c[start] == '\"' && c[stop-1] == '\"') { - start++; - stop--; - } - int i = start; - int ii = start; - while (i < stop) { - if (c[i] == '\"') { - i++; // skip over pairs of double quotes become one - } - if (i != ii) { - c[ii] = c[i]; - } - i++; - ii++; - } - /* - if (autoTyping) { - if (autoTypes == null) { - autoTypes = new int[pieces.length]; - - } else { - if (autoTypes.length < pieces.length) { - - } - Double.parseDouble(""); - Float.parseFloat("blah"); -// if (autoTypes[pieceCount]) { -// -// } - } - } - */ - String s = new String(c, start, ii - start); - pieces[pieceCount++] = s; - } - // Make any remaining entries blanks instead of nulls. Empty columns from - // CSV are always "" not null, so this handles successive commas in a line - for (int i = pieceCount; i < pieces.length; i++) { - pieces[i] = ""; - - } - return pieces; - } - */ /** From 92eae7f906d40914c77add4ce68118d14a403c52 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sun, 31 Jul 2016 20:10:21 -0400 Subject: [PATCH 0038/1035] remove dumb UnsupportedEncodingException stuff that is no longer necessary --- core/src/processing/core/PApplet.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index af28a975d8..1598996185 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -57,6 +57,7 @@ import java.io.*; import java.lang.reflect.*; import java.net.*; +import java.nio.charset.StandardCharsets; import java.text.*; import java.util.*; import java.util.regex.*; @@ -6562,10 +6563,8 @@ static public BufferedReader createReader(File file) { * following lines any more I'm gonna send Sun my medical bills. */ static public BufferedReader createReader(InputStream input) { - InputStreamReader isr = null; - try { - isr = new InputStreamReader(input, "UTF-8"); - } catch (UnsupportedEncodingException e) { } // not gonna happen + InputStreamReader isr = + new InputStreamReader(input, StandardCharsets.UTF_8); return new BufferedReader(isr); } @@ -6625,12 +6624,10 @@ static public PrintWriter createWriter(File file) { * It's the JavaSoft API engineers who need to explain themselves. */ static public PrintWriter createWriter(OutputStream output) { - try { - BufferedOutputStream bos = new BufferedOutputStream(output, 8192); - OutputStreamWriter osw = new OutputStreamWriter(bos, "UTF-8"); - return new PrintWriter(osw); - } catch (UnsupportedEncodingException e) { } // not gonna happen - return null; + BufferedOutputStream bos = new BufferedOutputStream(output, 8192); + OutputStreamWriter osw = + new OutputStreamWriter(bos, StandardCharsets.UTF_8); + return new PrintWriter(osw); } From 7cd5743fe1148261a8046d6c142fe7f648258338 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sun, 31 Jul 2016 20:11:04 -0400 Subject: [PATCH 0039/1035] notes about encoding changes --- core/todo.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/todo.txt b/core/todo.txt index 3ea5ab96c3..830ced34f2 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -1,4 +1,6 @@ 0252 (3.1.3?) +X some Table cleanup based on other CSV parsing work +X use StandardCharsets.UTF_8 instead of getting encoding by name _ Can't render PGraphics object using image() within a PDF From a2e8954c59f83a848601fa04c2568b17e0f5d012 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sun, 31 Jul 2016 20:20:41 -0400 Subject: [PATCH 0040/1035] the stones said it best: syntactic sugar, why do you taste so good? --- core/src/processing/core/PApplet.java | 9 +++++++++ core/todo.txt | 1 + 2 files changed, 10 insertions(+) diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 1598996185..7b006e3136 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -10034,6 +10034,15 @@ static public void main(final String[] args) { } + /** + * Convenience method so that PApplet.main(YourSketch.class) + * launches a sketch, rather than having to call getName() on it. + */ + static public void main(final Class mainClass) { + main(mainClass.getName(), null); + } + + /** * Convenience method so that PApplet.main("YourSketch") launches a sketch, * rather than having to wrap it into a single element String array. diff --git a/core/todo.txt b/core/todo.txt index 830ced34f2..58d3253788 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -1,6 +1,7 @@ 0252 (3.1.3?) X some Table cleanup based on other CSV parsing work X use StandardCharsets.UTF_8 instead of getting encoding by name +X PApplet.main(Blah.class) now works _ Can't render PGraphics object using image() within a PDF From 66612e30ec8ea068d113acf8cd65b763135931b9 Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Mon, 1 Aug 2016 16:49:21 +0200 Subject: [PATCH 0041/1035] Prevent NPEs when tab document is null JavaMode visits all tabs on sketch load to extract breakpoints, so doc is always set (except when running Tweak mode; it sets docs to null to force reload). Other modes have null docs until the tabs are visited manually. Fixes #4555 --- app/src/processing/app/ui/Editor.java | 29 ++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 76421eff34..dd445f4fcf 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -1371,10 +1371,13 @@ public void actionPerformed(ActionEvent e) { redoAction.updateRedoState(); if (sketch != null) { sketch.setModified(!getText().equals(sketch.getCurrentCode().getSavedProgram())); + // Go through all tabs; Replace All, Rename or Undo could have changed them for (SketchCode sc : sketch.getCode()) { - try { - sc.setModified(!sc.getDocumentText().equals(sc.getSavedProgram())); - } catch (BadLocationException ignore) { } + if (sc.getDocument() != null) { + try { + sc.setModified(!sc.getDocumentText().equals(sc.getSavedProgram())); + } catch (BadLocationException ignore) { } + } } repaintHeader(); } @@ -1433,10 +1436,14 @@ public void actionPerformed(ActionEvent e) { undoAction.updateUndoState(); if (sketch != null) { sketch.setModified(!getText().equals(sketch.getCurrentCode().getSavedProgram())); + // Go through all tabs; Replace All, Rename or Undo could have changed them for (SketchCode sc : sketch.getCode()) { - try { - sc.setModified(!sc.getDocumentText().equals(sc.getSavedProgram())); - } catch (BadLocationException ignore) { } + if (sc.getDocument() != null) { + try { + sc.setModified(!sc.getDocumentText().equals(sc.getSavedProgram())); + } catch (BadLocationException ignore) { + } + } } repaintHeader(); } @@ -2717,10 +2724,14 @@ public void prepareRun() { // make sure any edits have been stored //current.setProgram(editor.getText()); + // Go through all tabs; Replace All, Rename or Undo could have changed them for (SketchCode sc : sketch.getCode()) { - try { - sc.setProgram(sc.getDocumentText()); - } catch (BadLocationException e) { } + if (sc.getDocument() != null) { + try { + sc.setProgram(sc.getDocumentText()); + } catch (BadLocationException e) { + } + } } // // if an external editor is being used, need to grab the From ef17e5c7e5991004da6d7103871d048a513d8dc9 Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Mon, 1 Aug 2016 17:28:50 +0200 Subject: [PATCH 0042/1035] Error checker now adds 'public' to all default access methods Fixes #4583 --- .../mode/java/pdex/PreprocessingService.java | 3 +- .../mode/java/pdex/SourceUtils.java | 43 +++++++++---------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/java/src/processing/mode/java/pdex/PreprocessingService.java b/java/src/processing/mode/java/pdex/PreprocessingService.java index a87cb052f7..8a3005256f 100644 --- a/java/src/processing/mode/java/pdex/PreprocessingService.java +++ b/java/src/processing/mode/java/pdex/PreprocessingService.java @@ -402,8 +402,7 @@ private PreprocessedSketch preprocessSketch(PreprocessedSketch prevResult) { // Prepare advanced transforms which operate on AST TextTransform toCompilable = new TextTransform(parsableStage); - toCompilable.addAll(SourceUtils.addPublicToTopLevelMethods(parsableCU)); - toCompilable.addAll(SourceUtils.replaceColorAndFixFloats(parsableCU)); + toCompilable.addAll(SourceUtils.preprocessAST(parsableCU)); // Transform code to compilable state String compilableStage = toCompilable.apply(); diff --git a/java/src/processing/mode/java/pdex/SourceUtils.java b/java/src/processing/mode/java/pdex/SourceUtils.java index 5853ead4e3..66703b246d 100644 --- a/java/src/processing/mode/java/pdex/SourceUtils.java +++ b/java/src/processing/mode/java/pdex/SourceUtils.java @@ -5,8 +5,8 @@ import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.NumberLiteral; import org.eclipse.jdt.core.dom.SimpleType; -import org.eclipse.jdt.core.dom.TypeDeclaration; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; @@ -141,25 +141,6 @@ public static List wrapSketch(PdePreprocessor.Mode mode, String className, } - public static List addPublicToTopLevelMethods(CompilationUnit cu) { - List edits = new ArrayList<>(); - - // Add public modifier to top level methods - for (Object node : cu.types()) { - if (node instanceof TypeDeclaration) { - TypeDeclaration type = (TypeDeclaration) node; - for (MethodDeclaration method : type.getMethods()) { - if (method.modifiers().isEmpty() && !method.isConstructor()) { - edits.add(Edit.insert(method.getStartPosition(), "public ")); - } - } - } - } - - return edits; - } - - // Verifies that whole input String is floating point literal. Can't be used for searching. // https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-DecimalFloatingPointLiteral public static final Pattern FLOATING_POINT_LITERAL_VERIFIER; @@ -173,13 +154,18 @@ public static List addPublicToTopLevelMethods(CompilationUnit cu) { "(?:^" + DIGITS + EXPONENT_PART + "?[fFdD]$)"); } - public static List replaceColorAndFixFloats(CompilationUnit cu) { + // Mask to quickly resolve whether there are any access modifiers present + private static final int ACCESS_MODIFIERS_MASK = + Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED; + + public static List preprocessAST(CompilationUnit cu) { final List edits = new ArrayList<>(); - // Walk the tree, replace "color" with "int" and add 'f' to floats + // Walk the tree cu.accept(new ASTVisitor() { @Override public boolean visit(SimpleType node) { + // replace "color" with "int" if ("color".equals(node.getName().toString())) { edits.add(Edit.replace(node.getStartPosition(), node.getLength(), "int")); } @@ -188,12 +174,23 @@ public boolean visit(SimpleType node) { @Override public boolean visit(NumberLiteral node) { + // add 'f' to floats String s = node.getToken().toLowerCase(); if (FLOATING_POINT_LITERAL_VERIFIER.matcher(s).matches() && !s.endsWith("f") && !s.endsWith("d")) { edits.add(Edit.insert(node.getStartPosition() + node.getLength(), "f")); } return super.visit(node); } + + @Override + public boolean visit(MethodDeclaration node) { + // add 'public' to methods with default visibility + int accessModifiers = node.getModifiers() & ACCESS_MODIFIERS_MASK; + if (accessModifiers == 0) { + edits.add(Edit.insert(node.getStartPosition(), "public ")); + } + return super.visit(node); + } }); return edits; @@ -322,4 +319,4 @@ static public void scrubCommentsAndStrings(StringBuilder p) { } -} \ No newline at end of file +} From 3129f5d44faa958024b55702efaea87aadf2a901 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Mon, 1 Aug 2016 11:41:17 -0400 Subject: [PATCH 0043/1035] add support for PApplet.main(Class c) --- core/src/processing/core/PApplet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 7b006e3136..3b19a09f9f 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -10038,8 +10038,8 @@ static public void main(final String[] args) { * Convenience method so that PApplet.main(YourSketch.class) * launches a sketch, rather than having to call getName() on it. */ - static public void main(final Class mainClass) { - main(mainClass.getName(), null); + static public void main(final Class mainClass, String... args) { + main(mainClass.getName(), args); } From 86b559f2e008491ccfb6904afcc728d78911de68 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Mon, 1 Aug 2016 11:41:28 -0400 Subject: [PATCH 0044/1035] cleanups to Input Method code --- .../app/syntax/im/InputMethodSupport.java | 85 ++++++++++--------- todo.txt | 12 ++- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/app/src/processing/app/syntax/im/InputMethodSupport.java b/app/src/processing/app/syntax/im/InputMethodSupport.java index c7a36da56d..f092e05c13 100644 --- a/app/src/processing/app/syntax/im/InputMethodSupport.java +++ b/app/src/processing/app/syntax/im/InputMethodSupport.java @@ -26,27 +26,25 @@ import processing.app.syntax.JEditTextArea; import processing.app.syntax.TextAreaPainter; + /** - * on-the-spot style input support for CJK.(Chinese, Japanese, Korean). - * This class is implemented to fix Bug #854 from 2010-02-16. + * On-the-spot style input support for CJK (Chinese, Japanese, Korean). * - * @see Bug 854 : implement input method support for Japanese (and other languages) - * @see Bug 1531 : Can't input full-width space when Japanese IME is on. - * @see Java Input Method Framework - * (IMF) Technology + * @see Bugzilla 854: implement input method support for Japanese (and other languages) + * @see Bugzilla 1531: Can't input full-width space when Japanese IME is on. + * @see Java Input Method Framework (IMF) Technology * @see The Java Tutorials * * @author Takashi Maekawa (takachin@generative.info) * @author Satoshi Okita */ -public class InputMethodSupport implements InputMethodRequests, - InputMethodListener { +public class InputMethodSupport implements InputMethodRequests, InputMethodListener { - private static final Attribute[] CUSTOM_IM_ATTRIBUTES = { + static private final Attribute[] CUSTOM_IM_ATTRIBUTES = { TextAttribute.INPUT_METHOD_HIGHLIGHT, }; - private int committed_count = 0; + private int committedCount = 0; private JEditTextArea textArea; private AttributedString composedTextString; @@ -56,9 +54,13 @@ public InputMethodSupport(JEditTextArea textArea) { this.textArea.addInputMethodListener(this); } + ///////////////////////////////////////////////////////////////////////////// + // InputMethodRequest + ///////////////////////////////////////////////////////////////////////////// + @Override public Rectangle getTextLocation(TextHitInfo offset) { if (Base.DEBUG) { @@ -82,11 +84,13 @@ public TextHitInfo getLocationOffset(int x, int y) { return null; } + @Override public int getInsertPositionOffset() { - return textArea.getCaretPosition() * -1; + return -textArea.getCaretPosition(); } + @Override public AttributedCharacterIterator getCommittedText(int beginIndex, int endIndex, AttributedCharacterIterator.Attribute[] attributes) { @@ -95,26 +99,33 @@ public AttributedCharacterIterator getCommittedText(int beginIndex, return new AttributedString(textAreaString).getIterator(); } + @Override public int getCommittedTextLength() { - return committed_count; + return committedCount; } + @Override public AttributedCharacterIterator cancelLatestCommittedText( AttributedCharacterIterator.Attribute[] attributes) { return null; } + @Override public AttributedCharacterIterator getSelectedText( AttributedCharacterIterator.Attribute[] attributes) { return null; } + ///////////////////////////////////////////////////////////////////////////// + // InputMethodListener + ///////////////////////////////////////////////////////////////////////////// + /** * Handles events from InputMethod. * @@ -132,22 +143,21 @@ public void inputMethodTextChanged(InputMethodEvent event) { } AttributedCharacterIterator text = event.getText(); // text = composedText + commitedText - committed_count = event.getCommittedCharacterCount(); - - - // The caret for Input Method. - // if you type a character by a input method, original caret become off. - // a JEditTextArea is not implemented by the AttributedStirng and TextLayout. - // so JEditTextArea Caret On-off logic. - // - // japanese : if the enter key pressed, event.getText is null. - // japanese : if first space key pressed, event.getText is null. - // chinese(pinin) : if a space key pressed, event.getText is null. - // taiwan(bopomofo): ? - // korean : ? + committedCount = event.getCommittedCharacterCount(); + + // The caret for Input Method. If you type a character by a input method, + // original caret position will be incorrect. JEditTextArea is not + // implemented using AttributedString and TextLayout. textArea.setCaretVisible(false); + + // Japanese : if the enter key pressed, event.getText is null. + // Japanese : if first space key pressed, event.getText is null. + // Chinese (pinin) : if a space key pressed, event.getText is null. + // Taiwan (bopomofo): ? + // Korean : ? + // Korean Input Method - if (text != null && text.getEndIndex() - (text.getBeginIndex() + committed_count) <= 0) { + if (text != null && text.getEndIndex() - (text.getBeginIndex() + committedCount) <= 0) { textArea.setCaretVisible(true); } // Japanese Input Method @@ -155,26 +165,22 @@ public void inputMethodTextChanged(InputMethodEvent event) { textArea.setCaretVisible(true); } - char c; if (text != null) { - int toCopy = committed_count; - c = text.first(); - while (toCopy-- > 0) { - if (Base.DEBUG) { - Messages.log("INSERT:'" + c + "'"); - } - this.insertCharacter(c); + int remaining = committedCount; + char c = text.first(); + while (remaining-- > 0) { + insertCharacter(c); c = text.next(); } CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter(); if (Base.DEBUG) { - Messages.log(" textArea.getCaretPosition() + committed_count: " + (textArea.getCaretPosition() + committed_count)); + Messages.log(" textArea.getCaretPosition() + committed_count: " + (textArea.getCaretPosition() + committedCount)); } - compositionPainter.setComposedTextLayout(getTextLayout(text, committed_count), textArea.getCaretPosition() + committed_count); + compositionPainter.setComposedTextLayout(getTextLayout(text, committedCount), textArea.getCaretPosition() + committedCount); compositionPainter.setCaret(event.getCaret()); - } else { - // hide input method. + + } else { // otherwise hide the input method CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter(); compositionPainter.setComposedTextLayout(null, 0); compositionPainter.setCaret(null); @@ -183,6 +189,7 @@ public void inputMethodTextChanged(InputMethodEvent event) { textArea.repaint(); } + private TextLayout getTextLayout(AttributedCharacterIterator text, int committedCount) { boolean antialias = Preferences.getBoolean("editor.smooth"); TextAreaPainter painter = textArea.getPainter(); @@ -213,11 +220,13 @@ private TextLayout getTextLayout(AttributedCharacterIterator text, int committed return new TextLayout(composedTextString.getIterator(), frc); } + @Override public void caretPositionChanged(InputMethodEvent event) { event.consume(); } + private void insertCharacter(char c) { if (Base.DEBUG) { Messages.log("debug: insertCharacter(char c) textArea.getCaretPosition()=" + textArea.getCaretPosition()); diff --git a/todo.txt b/todo.txt index 11ec9b54e4..dda292c22f 100644 --- a/todo.txt +++ b/todo.txt @@ -9,11 +9,17 @@ contrib X Simplify conditional branch X https://github.com/processing/processing/pull/4589 +jakub +X NullPointerException in SketchCode.getDocumentText() +X https://github.com/processing/processing/issues/4555 +o https://github.com/processing/processing/pull/4547 +X https://github.com/processing/processing/pull/4596 +X Error checker now adds 'public' to all default access methods +X https://github.com/processing/processing/pull/4597 +X https://github.com/processing/processing/issues/4583 + high -_ NullPointerException in SketchCode.getDocumentText() -_ https://github.com/processing/processing/issues/4555 -_ https://github.com/processing/processing/pull/4547 _ Pasting code from editor to empty editor produces Exception _ https://github.com/processing/processing/issues/4522 _ did we lose settings.path because it was too buggy? From 62f5089e7ed80fbe75d0f52c451a0b8b509ace3c Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Mon, 1 Aug 2016 16:14:37 -0400 Subject: [PATCH 0045/1035] store separate state information in attempt to fix #4522 --- .../processing/app/syntax/PdeKeywords.java | 90 +++++++++++-------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/app/src/processing/app/syntax/PdeKeywords.java b/app/src/processing/app/syntax/PdeKeywords.java index a8fddcb259..c9df098853 100644 --- a/app/src/processing/app/syntax/PdeKeywords.java +++ b/app/src/processing/app/syntax/PdeKeywords.java @@ -32,8 +32,8 @@ public class PdeKeywords extends TokenMarker { protected KeywordMap keywordColoring; - protected int lastOffset; - protected int lastKeyword; +// protected int lastOffset; +// protected int lastKeyword; /** @@ -71,11 +71,23 @@ public void addColoring(String keyword, String coloring) { } + class MarkerState { + int lastOffset; + int lastKeyword; + + MarkerState(int offset) { + lastOffset = offset; + lastKeyword = offset; + } + } + + public byte markTokensImpl(byte token, Segment line, int lineIndex) { char[] array = line.array; int offset = line.offset; - lastOffset = offset; - lastKeyword = offset; +// lastOffset = offset; +// lastKeyword = offset; + MarkerState ms = new MarkerState(offset); int mlength = offset + line.count; boolean backslash = false; @@ -96,52 +108,52 @@ public byte markTokensImpl(byte token, Segment line, int lineIndex) { backslash = false; break; case '"': - doKeyword(line, i, c); + doKeyword(ms, line, i, c); if (backslash) backslash = false; else { - addToken(i - lastOffset, token); + addToken(i - ms.lastOffset, token); token = Token.LITERAL1; - lastOffset = lastKeyword = i; + ms.lastOffset = ms.lastKeyword = i; } break; case '\'': - doKeyword(line, i, c); + doKeyword(ms, line, i, c); if (backslash) backslash = false; else { - addToken(i - lastOffset, token); + addToken(i - ms.lastOffset, token); token = Token.LITERAL2; - lastOffset = lastKeyword = i; + ms.lastOffset = ms.lastKeyword = i; } break; case ':': - if (lastKeyword == offset) { - if (doKeyword(line, i, c)) + if (ms.lastKeyword == offset) { + if (doKeyword(ms, line, i, c)) break; backslash = false; - addToken(i1 - lastOffset, Token.LABEL); - lastOffset = lastKeyword = i1; - } else if (doKeyword(line, i, c)) + addToken(i1 - ms.lastOffset, Token.LABEL); + ms.lastOffset = ms.lastKeyword = i1; + } else if (doKeyword(ms, line, i, c)) break; break; case '/': backslash = false; - doKeyword(line, i, c); + doKeyword(ms, line, i, c); if (mlength - i > 1) { switch (array[i1]) { case '*': - addToken(i - lastOffset, token); - lastOffset = lastKeyword = i; + addToken(i - ms.lastOffset, token); + ms.lastOffset = ms.lastKeyword = i; if (mlength - i > 2 && array[i + 2] == '*') token = Token.COMMENT2; else token = Token.COMMENT1; break; case '/': - addToken(i - lastOffset, token); + addToken(i - ms.lastOffset, token); addToken(mlength - i, Token.COMMENT1); - lastOffset = lastKeyword = mlength; + ms.lastOffset = ms.lastKeyword = mlength; break loop; } // https://github.com/processing/processing/issues/1681 @@ -153,7 +165,7 @@ public byte markTokensImpl(byte token, Segment line, int lineIndex) { default: backslash = false; if (!Character.isLetterOrDigit(c) && c != '_') { - doKeyword(line, i, c); + doKeyword(ms, line, i, c); } break; } @@ -164,9 +176,9 @@ public byte markTokensImpl(byte token, Segment line, int lineIndex) { if (c == '*' && mlength - i > 1) { if (array[i1] == '/') { i++; - addToken((i + 1) - lastOffset, token); + addToken((i + 1) - ms.lastOffset, token); token = Token.NULL; - lastOffset = lastKeyword = i + 1; + ms.lastOffset = ms.lastKeyword = i + 1; } } break; @@ -174,18 +186,18 @@ public byte markTokensImpl(byte token, Segment line, int lineIndex) { if (backslash) backslash = false; else if (c == '"') { - addToken(i1 - lastOffset, token); + addToken(i1 - ms.lastOffset, token); token = Token.NULL; - lastOffset = lastKeyword = i1; + ms.lastOffset = ms.lastKeyword = i1; } break; case Token.LITERAL2: if (backslash) backslash = false; else if (c == '\'') { - addToken(i1 - lastOffset, Token.LITERAL1); + addToken(i1 - ms.lastOffset, Token.LITERAL1); token = Token.NULL; - lastOffset = lastKeyword = i1; + ms.lastOffset = ms.lastKeyword = i1; } break; default: @@ -194,44 +206,44 @@ else if (c == '\'') { } if (token == Token.NULL) { - doKeyword(line, mlength, '\0'); + doKeyword(ms, line, mlength, '\0'); } switch (token) { case Token.LITERAL1: case Token.LITERAL2: - addToken(mlength - lastOffset, Token.INVALID); + addToken(mlength - ms.lastOffset, Token.INVALID); token = Token.NULL; break; case Token.KEYWORD2: - addToken(mlength - lastOffset, token); + addToken(mlength - ms.lastOffset, token); if (!backslash) token = Token.NULL; - addToken(mlength - lastOffset, token); + addToken(mlength - ms.lastOffset, token); break; default: - addToken(mlength - lastOffset, token); + addToken(mlength - ms.lastOffset, token); break; } return token; } - protected boolean doKeyword(Segment line, int i, char c) { + protected boolean doKeyword(MarkerState ms, Segment line, int i, char c) { int i1 = i + 1; - int len = i - lastKeyword; + int len = i - ms.lastKeyword; boolean paren = Editor.checkParen(line.array, i, line.array.length); - byte id = keywordColoring.lookup(line, lastKeyword, len, paren); + byte id = keywordColoring.lookup(line, ms.lastKeyword, len, paren); if (id != Token.NULL) { - if (lastKeyword != lastOffset) { - addToken(lastKeyword - lastOffset, Token.NULL); + if (ms.lastKeyword != ms.lastOffset) { + addToken(ms.lastKeyword - ms.lastOffset, Token.NULL); } addToken(len, id); - lastOffset = i; + ms.lastOffset = i; } - lastKeyword = i1; + ms.lastKeyword = i1; return false; } } From ca853f09c66e81c214f17bcaad1c8bba1b07a478 Mon Sep 17 00:00:00 2001 From: tyfkda Date: Tue, 2 Aug 2016 09:06:47 +0900 Subject: [PATCH 0046/1035] Set sketch as modified when any character committed using input method support --- app/src/processing/app/syntax/InputHandler.java | 8 ++++++++ app/src/processing/app/syntax/JEditTextArea.java | 6 ++++++ .../processing/app/syntax/im/InputMethodSupport.java | 12 ++++++++++++ java/src/processing/mode/java/JavaInputHandler.java | 7 +++++++ 4 files changed, 33 insertions(+) diff --git a/app/src/processing/app/syntax/InputHandler.java b/app/src/processing/app/syntax/InputHandler.java index debf3023bb..5009dc4070 100644 --- a/app/src/processing/app/syntax/InputHandler.java +++ b/app/src/processing/app/syntax/InputHandler.java @@ -1173,4 +1173,12 @@ public static int findWordEnd(String line, int pos, String noWordSep) { } return wordEnd; } + + + /** + * Called when input method support committed a character. + * @param c The input character + */ + public void onCommittedFromInputMethodSupport(char c) { + } } diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java index 0466a19553..94ce33c81e 100644 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ b/app/src/processing/app/syntax/JEditTextArea.java @@ -193,6 +193,12 @@ public InputMethodRequests getInputMethodRequests() { if (Preferences.getBoolean("editor.input_method_support")) { if (inputMethodSupport == null) { inputMethodSupport = new InputMethodSupport(this); + inputMethodSupport.setCallback(new InputMethodSupport.Callback() { + @Override + public void onCommitted(char c) { + inputHandler.onCommittedFromInputMethodSupport(c); + } + }); } return inputMethodSupport; } diff --git a/app/src/processing/app/syntax/im/InputMethodSupport.java b/app/src/processing/app/syntax/im/InputMethodSupport.java index f092e05c13..b44a686f06 100644 --- a/app/src/processing/app/syntax/im/InputMethodSupport.java +++ b/app/src/processing/app/syntax/im/InputMethodSupport.java @@ -40,6 +40,10 @@ */ public class InputMethodSupport implements InputMethodRequests, InputMethodListener { + public interface Callback { + public void onCommitted(char c); + } + static private final Attribute[] CUSTOM_IM_ATTRIBUTES = { TextAttribute.INPUT_METHOD_HIGHLIGHT, }; @@ -47,6 +51,7 @@ public class InputMethodSupport implements InputMethodRequests, InputMethodListe private int committedCount = 0; private JEditTextArea textArea; private AttributedString composedTextString; + private Callback callback; public InputMethodSupport(JEditTextArea textArea) { this.textArea = textArea; @@ -55,6 +60,11 @@ public InputMethodSupport(JEditTextArea textArea) { } + public void setCallback(Callback callback) { + this.callback = callback; + } + + ///////////////////////////////////////////////////////////////////////////// // InputMethodRequest @@ -170,6 +180,8 @@ public void inputMethodTextChanged(InputMethodEvent event) { char c = text.first(); while (remaining-- > 0) { insertCharacter(c); + if (callback != null) + callback.onCommitted(c); c = text.next(); } diff --git a/java/src/processing/mode/java/JavaInputHandler.java b/java/src/processing/mode/java/JavaInputHandler.java index 99b7f18c4f..b675c063aa 100644 --- a/java/src/processing/mode/java/JavaInputHandler.java +++ b/java/src/processing/mode/java/JavaInputHandler.java @@ -334,6 +334,13 @@ public boolean handleTyped(KeyEvent event) { } + @Override + public void onCommittedFromInputMethodSupport(char c) { + Sketch sketch = editor.getSketch(); + sketch.setModified(true); + } + + /** * Return the index for the first character on this line. */ From 030ff8ec5d1ab31615d516dc3dc12be94f502068 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Wed, 3 Aug 2016 18:30:25 -0400 Subject: [PATCH 0047/1035] trying out a toJSON() method --- core/src/processing/data/IntDict.java | 15 +++++++++++++++ core/todo.txt | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/core/src/processing/data/IntDict.java b/core/src/processing/data/IntDict.java index 9c9a2b943a..dd6337b7e1 100644 --- a/core/src/processing/data/IntDict.java +++ b/core/src/processing/data/IntDict.java @@ -660,8 +660,22 @@ public void write(PrintWriter writer) { } + /** + * Return this dictionary as a String in JSON format. + */ + public String toJSON() { + StringList items = new StringList(); + for (int i = 0; i < size(); i++) { + items.append("\"" + keys[i] + "\": " + values[i]); + } + return "{ " + items.join(", ") + " }"; + } + + @Override public String toString() { + return getClass().getSimpleName() + " size=" + size() + " " + toJSON(); + /* StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName() + " size=" + size() + " { "); for (int i = 0; i < size(); i++) { @@ -672,5 +686,6 @@ public String toString() { } sb.append(" }"); return sb.toString(); + */ } } diff --git a/core/todo.txt b/core/todo.txt index 58d3253788..59916d1711 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -2,6 +2,10 @@ X some Table cleanup based on other CSV parsing work X use StandardCharsets.UTF_8 instead of getting encoding by name X PApplet.main(Blah.class) now works +X add toJSON() method to IntDict +_ do the same for the other data classes +_ note the difference between this and toJSONObject() or toJSONArray() +_ or is that the better way to handle it? hm _ Can't render PGraphics object using image() within a PDF From 1898f5d1c9d5de4f2f752f7e55dcfb148a57cefc Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Thu, 4 Aug 2016 10:19:08 -0400 Subject: [PATCH 0048/1035] Enable input method support by default on Japanese/Korean/Chinese systems --- app/src/processing/app/Language.java | 12 +++++++++++ app/src/processing/app/Preferences.java | 28 +++++-------------------- todo.txt | 3 +++ 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/app/src/processing/app/Language.java b/app/src/processing/app/Language.java index 5b155477a5..93543d10d3 100644 --- a/app/src/processing/app/Language.java +++ b/app/src/processing/app/Language.java @@ -236,6 +236,18 @@ static public String getLanguage() { } + /** + * Is this a CJK language where Input Method support is suggested/required? + * @return true if the user is running in Japanese, Korean, or Chinese + */ + static public boolean useInputMethod() { + final String language = getLanguage(); + return (language.equals("ja") || + language.equals("ko") || + language.equals("zh")); + } + + // /** Set new language (called by Preferences) */ // static public void setLanguage(String language) { // this.language = language; diff --git a/app/src/processing/app/Preferences.java b/app/src/processing/app/Preferences.java index a963d22b60..b52d8cb247 100644 --- a/app/src/processing/app/Preferences.java +++ b/app/src/processing/app/Preferences.java @@ -71,29 +71,6 @@ static public void init() { "You'll need to reinstall Processing.", e); } - /* provisionally removed in 3.0a6, see changes in load() - - // check for platform-specific properties in the defaults - String platformExt = "." + PConstants.platformNames[PApplet.platform]; //$NON-NLS-1$ - int platformExtLength = platformExt.length(); - - // Get a list of keys that are specific to this platform - ArrayList platformKeys = new ArrayList(); - for (String key : table.keySet()) { - if (key.endsWith(platformExt)) { - platformKeys.add(key); - } - } - - // Use those platform-specific keys to override - for (String key : platformKeys) { - // this is a key specific to a particular platform - String actualKey = key.substring(0, key.length() - platformExtLength); - String value = get(key); - set(actualKey, value); - } - */ - // Clone the defaults, then override any them with the user's preferences. // This ensures that any new/added preference will be present. defaults = new HashMap(table); @@ -101,6 +78,11 @@ static public void init() { // other things that have to be set explicitly for the defaults setColor("run.window.bgcolor", SystemColor.control); //$NON-NLS-1$ + // For CJK users, enable IM support by default + if (Language.useInputMethod()) { + setBoolean("editor.input_method_support", true); + } + // next load user preferences file preferencesFile = Base.getSettingsFile(PREFS_FILE); boolean firstRun = !preferencesFile.exists(); diff --git a/todo.txt b/todo.txt index dda292c22f..a47ad15bf6 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,7 @@ 0252 (3.1.3?) +X Enable input method support by default on Japanese/Korean/Chinese systems +X https://github.com/processing/processing/pull/4598 + _ modify line number color when no lines extend that far? _ https://github.com/processing/processing/pull/4560 _ text gutter doesn't seem to be hidpi From 5e65ac85d4d2fb9d96c12e59be17234142810256 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Thu, 4 Aug 2016 10:53:53 -0400 Subject: [PATCH 0049/1035] switch from character to string-based commits in IM (#4594) --- .../processing/app/syntax/im/InputMethodSupport.java | 10 +++++++--- todo.txt | 9 ++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/processing/app/syntax/im/InputMethodSupport.java b/app/src/processing/app/syntax/im/InputMethodSupport.java index f092e05c13..00897476ec 100644 --- a/app/src/processing/app/syntax/im/InputMethodSupport.java +++ b/app/src/processing/app/syntax/im/InputMethodSupport.java @@ -166,12 +166,14 @@ public void inputMethodTextChanged(InputMethodEvent event) { } if (text != null) { - int remaining = committedCount; + char[] insertion = new char[committedCount]; char c = text.first(); - while (remaining-- > 0) { - insertCharacter(c); + for (int i = 0; i < committedCount; i++) { + insertion[i] = c; c = text.next(); } + // Insert this as a compound edit + textArea.setSelectedText(new String(insertion), true); CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter(); if (Base.DEBUG) { @@ -227,6 +229,7 @@ public void caretPositionChanged(InputMethodEvent event) { } + /* private void insertCharacter(char c) { if (Base.DEBUG) { Messages.log("debug: insertCharacter(char c) textArea.getCaretPosition()=" + textArea.getCaretPosition()); @@ -240,4 +243,5 @@ private void insertCharacter(char c) { e.printStackTrace(); } } + */ } diff --git a/todo.txt b/todo.txt index a47ad15bf6..205c76a443 100644 --- a/todo.txt +++ b/todo.txt @@ -1,16 +1,19 @@ 0252 (3.1.3?) -X Enable input method support by default on Japanese/Korean/Chinese systems -X https://github.com/processing/processing/pull/4598 - _ modify line number color when no lines extend that far? _ https://github.com/processing/processing/pull/4560 _ text gutter doesn't seem to be hidpi X or is it b/c screen not quite 2x? (nope) _ swap out the fonts? +_ Set text color for InputMethod +_ https://github.com/processing/processing/pull/4593 contrib X Simplify conditional branch X https://github.com/processing/processing/pull/4589 +X Enable input method support by default on Japanese/Korean/Chinese systems +X https://github.com/processing/processing/pull/4598 +X Set sketch as modified when any character committed using input method +X https://github.com/processing/processing/pull/4599 jakub X NullPointerException in SketchCode.getDocumentText() From f4ce14a98b3b22c17ad64462de9db53fccdc7c3b Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Thu, 4 Aug 2016 11:06:10 -0400 Subject: [PATCH 0050/1035] trying to simplify #4599 --- .../processing/app/syntax/InputHandler.java | 3 +-- .../processing/app/syntax/JEditTextArea.java | 12 ++++++---- .../app/syntax/im/InputMethodSupport.java | 24 ++++++++++++------- .../mode/java/JavaInputHandler.java | 5 ++-- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/app/src/processing/app/syntax/InputHandler.java b/app/src/processing/app/syntax/InputHandler.java index 5009dc4070..a695be1a09 100644 --- a/app/src/processing/app/syntax/InputHandler.java +++ b/app/src/processing/app/syntax/InputHandler.java @@ -1177,8 +1177,7 @@ public static int findWordEnd(String line, int pos, String noWordSep) { /** * Called when input method support committed a character. - * @param c The input character */ - public void onCommittedFromInputMethodSupport(char c) { + public void handleInputMethodCommit() { } } diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java index 94ce33c81e..2290a3701b 100644 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ b/app/src/processing/app/syntax/JEditTextArea.java @@ -192,13 +192,15 @@ protected TextAreaPainter createPainter(final TextAreaDefaults defaults) { public InputMethodRequests getInputMethodRequests() { if (Preferences.getBoolean("editor.input_method_support")) { if (inputMethodSupport == null) { - inputMethodSupport = new InputMethodSupport(this); + inputMethodSupport = new InputMethodSupport(this, inputHandler); + /* inputMethodSupport.setCallback(new InputMethodSupport.Callback() { @Override public void onCommitted(char c) { - inputHandler.onCommittedFromInputMethodSupport(c); + inputHandler.onInputMethodCommit(c); } }); + */ } return inputMethodSupport; } @@ -1379,14 +1381,14 @@ public void setSelectedText(String selectedText) { /** * Replaces the selection with the specified text. * @param selectedText The replacement text for the selection - * @param recordCompoundEdit Whether the replacement should be + * @param recordCompoundEdit Whether the replacement should be * recorded as a compound edit */ public void setSelectedText(String selectedText, boolean recordCompoundEdit) { if (!editable) { throw new InternalError("Text component read only"); } - + if (recordCompoundEdit) { document.beginCompoundEdit(); } @@ -1477,7 +1479,7 @@ public void overwriteSetSelectedText(String str) // Don't overstrike if there is a selection if(!overwrite || selectionStart != selectionEnd) { - // record the whole operation as a compound edit if + // record the whole operation as a compound edit if // selected text is being replaced boolean isSelectAndReplaceOp = (selectionStart != selectionEnd); setSelectedText(str, isSelectAndReplaceOp); diff --git a/app/src/processing/app/syntax/im/InputMethodSupport.java b/app/src/processing/app/syntax/im/InputMethodSupport.java index 2755576ba7..e148e5d73a 100644 --- a/app/src/processing/app/syntax/im/InputMethodSupport.java +++ b/app/src/processing/app/syntax/im/InputMethodSupport.java @@ -21,6 +21,7 @@ import processing.app.Base; import processing.app.Messages; import processing.app.Preferences; +import processing.app.syntax.InputHandler; import processing.app.syntax.JEditTextArea; import processing.app.syntax.TextAreaPainter; @@ -38,29 +39,38 @@ */ public class InputMethodSupport implements InputMethodRequests, InputMethodListener { + /* public interface Callback { public void onCommitted(char c); } + private Callback callback; + */ + static private final Attribute[] CUSTOM_IM_ATTRIBUTES = { TextAttribute.INPUT_METHOD_HIGHLIGHT, }; - private int committedCount = 0; private JEditTextArea textArea; + private InputHandler inputHandler; + + private int committedCount = 0; private AttributedString composedTextString; - private Callback callback; - public InputMethodSupport(JEditTextArea textArea) { + public InputMethodSupport(JEditTextArea textArea, InputHandler inputHandler) { this.textArea = textArea; - this.textArea.enableInputMethods(true); - this.textArea.addInputMethodListener(this); + this.inputHandler = inputHandler; + + textArea.enableInputMethods(true); + textArea.addInputMethodListener(this); } + /* public void setCallback(Callback callback) { this.callback = callback; } + */ ///////////////////////////////////////////////////////////////////////////// @@ -182,9 +192,7 @@ public void inputMethodTextChanged(InputMethodEvent event) { } // Insert this as a compound edit textArea.setSelectedText(new String(insertion), true); - if (callback != null) { - callback.onCommitted(c); - } + inputHandler.handleInputMethodCommit(); CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter(); if (Base.DEBUG) { diff --git a/java/src/processing/mode/java/JavaInputHandler.java b/java/src/processing/mode/java/JavaInputHandler.java index b675c063aa..596f35063a 100644 --- a/java/src/processing/mode/java/JavaInputHandler.java +++ b/java/src/processing/mode/java/JavaInputHandler.java @@ -335,9 +335,8 @@ public boolean handleTyped(KeyEvent event) { @Override - public void onCommittedFromInputMethodSupport(char c) { - Sketch sketch = editor.getSketch(); - sketch.setModified(true); + public void handleInputMethodCommit() { + editor.getSketch().setModified(true); } From 1a67e1d0207ec498177ef6afe2c8a79d39b58bc3 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Thu, 4 Aug 2016 11:48:12 -0400 Subject: [PATCH 0051/1035] notes about recent edits --- todo.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/todo.txt b/todo.txt index 205c76a443..45a38d0b43 100644 --- a/todo.txt +++ b/todo.txt @@ -6,6 +6,8 @@ X or is it b/c screen not quite 2x? (nope) _ swap out the fonts? _ Set text color for InputMethod _ https://github.com/processing/processing/pull/4593 +_ Processing .jar files in CLASSPATH can cause startup crash +_ https://github.com/processing/processing/issues/4128 contrib X Simplify conditional branch @@ -14,6 +16,8 @@ X Enable input method support by default on Japanese/Korean/Chinese systems X https://github.com/processing/processing/pull/4598 X Set sketch as modified when any character committed using input method X https://github.com/processing/processing/pull/4599 +X Insert characters by InputMethod at one time +X https://github.com/processing/processing/pull/4594 jakub X NullPointerException in SketchCode.getDocumentText() From 4d32adfb3f11c4deb8bd258a2f3aa549aa354e60 Mon Sep 17 00:00:00 2001 From: tyfkda Date: Fri, 5 Aug 2016 08:52:38 +0900 Subject: [PATCH 0052/1035] Insert string when it is committed `inputMethodTextChanged` is called every time when a user is modifying a text on input method support. In the time, code haven't modified yet. Code is modified when the user pressed enter key to decide the input, and only the time `committedCount` becomes non zero. --- .../app/syntax/im/InputMethodSupport.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/processing/app/syntax/im/InputMethodSupport.java b/app/src/processing/app/syntax/im/InputMethodSupport.java index e148e5d73a..ff62c6b597 100644 --- a/app/src/processing/app/syntax/im/InputMethodSupport.java +++ b/app/src/processing/app/syntax/im/InputMethodSupport.java @@ -184,15 +184,17 @@ public void inputMethodTextChanged(InputMethodEvent event) { } if (text != null) { - char[] insertion = new char[committedCount]; - char c = text.first(); - for (int i = 0; i < committedCount; i++) { - insertion[i] = c; - c = text.next(); + if (committedCount > 0) { + char[] insertion = new char[committedCount]; + char c = text.first(); + for (int i = 0; i < committedCount; i++) { + insertion[i] = c; + c = text.next(); + } + // Insert this as a compound edit + textArea.setSelectedText(new String(insertion), true); + inputHandler.handleInputMethodCommit(); } - // Insert this as a compound edit - textArea.setSelectedText(new String(insertion), true); - inputHandler.handleInputMethodCommit(); CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter(); if (Base.DEBUG) { From 52ff568d7da4363e7d9301629a0435f81dc4fd83 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Fri, 5 Aug 2016 10:22:09 -0400 Subject: [PATCH 0053/1035] proper coloring for InputMethod support (#4593) --- .../processing/app/syntax/JEditTextArea.java | 21 ++++++++++--------- .../app/syntax/im/InputMethodSupport.java | 18 +++++++++------- todo.txt | 6 ++++++ 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java index 2290a3701b..97217e541c 100644 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ b/app/src/processing/app/syntax/JEditTextArea.java @@ -77,7 +77,9 @@ public class JEditTextArea extends JComponent /** The size of the offset between the leftmost padding and the code */ public static final int leftHandGutter = 6; - private InputMethodSupport inputMethodSupport = null; + private InputMethodSupport inputMethodSupport; + + private TextAreaDefaults defaults; private Brackets bracketHelper = new Brackets(); @@ -87,6 +89,8 @@ public class JEditTextArea extends JComponent * @param defaults The default settings */ public JEditTextArea(TextAreaDefaults defaults, InputHandler inputHandler) { + this.defaults = defaults; + // Enable the necessary events enableEvents(AWTEvent.KEY_EVENT_MASK); @@ -192,15 +196,7 @@ protected TextAreaPainter createPainter(final TextAreaDefaults defaults) { public InputMethodRequests getInputMethodRequests() { if (Preferences.getBoolean("editor.input_method_support")) { if (inputMethodSupport == null) { - inputMethodSupport = new InputMethodSupport(this, inputHandler); - /* - inputMethodSupport.setCallback(new InputMethodSupport.Callback() { - @Override - public void onCommitted(char c) { - inputHandler.onInputMethodCommit(c); - } - }); - */ + inputMethodSupport = new InputMethodSupport(this); } return inputMethodSupport; } @@ -271,6 +267,11 @@ public final Printable getPrintable() { } + public TextAreaDefaults getDefaults() { + return defaults; + } + + /** * Returns the input handler. */ diff --git a/app/src/processing/app/syntax/im/InputMethodSupport.java b/app/src/processing/app/syntax/im/InputMethodSupport.java index ff62c6b597..2e83768d68 100644 --- a/app/src/processing/app/syntax/im/InputMethodSupport.java +++ b/app/src/processing/app/syntax/im/InputMethodSupport.java @@ -21,8 +21,8 @@ import processing.app.Base; import processing.app.Messages; import processing.app.Preferences; -import processing.app.syntax.InputHandler; import processing.app.syntax.JEditTextArea; +import processing.app.syntax.TextAreaDefaults; import processing.app.syntax.TextAreaPainter; @@ -52,14 +52,12 @@ public interface Callback { }; private JEditTextArea textArea; - private InputHandler inputHandler; private int committedCount = 0; private AttributedString composedTextString; - public InputMethodSupport(JEditTextArea textArea, InputHandler inputHandler) { + public InputMethodSupport(JEditTextArea textArea) { this.textArea = textArea; - this.inputHandler = inputHandler; textArea.enableInputMethods(true); textArea.addInputMethodListener(this); @@ -193,7 +191,7 @@ public void inputMethodTextChanged(InputMethodEvent event) { } // Insert this as a compound edit textArea.setSelectedText(new String(insertion), true); - inputHandler.handleInputMethodCommit(); + textArea.getInputHandler().handleInputMethodCommit(); } CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter(); @@ -218,12 +216,18 @@ private TextLayout getTextLayout(AttributedCharacterIterator text, int committed TextAreaPainter painter = textArea.getPainter(); // create attributed string with font info. - //if (text.getEndIndex() - (text.getBeginIndex() + committedCharacterCount) > 0) { if (text.getEndIndex() - (text.getBeginIndex() + committedCount) > 0) { composedTextString = new AttributedString(text, committedCount, text.getEndIndex(), CUSTOM_IM_ATTRIBUTES); Font font = painter.getFontMetrics().getFont(); + + TextAreaDefaults defaults = textArea.getDefaults(); + Color bgColor = defaults.lineHighlight ? + defaults.lineHighlightColor : defaults.bgcolor; + composedTextString.addAttribute(TextAttribute.FONT, font); - composedTextString.addAttribute(TextAttribute.BACKGROUND, Color.WHITE); + composedTextString.addAttribute(TextAttribute.FOREGROUND, defaults.fgcolor); + composedTextString.addAttribute(TextAttribute.BACKGROUND, bgColor); + } else { composedTextString = new AttributedString(""); return null; diff --git a/todo.txt b/todo.txt index 45a38d0b43..d3530d00eb 100644 --- a/todo.txt +++ b/todo.txt @@ -10,6 +10,8 @@ _ Processing .jar files in CLASSPATH can cause startup crash _ https://github.com/processing/processing/issues/4128 contrib + +input method work X Simplify conditional branch X https://github.com/processing/processing/pull/4589 X Enable input method support by default on Japanese/Korean/Chinese systems @@ -18,6 +20,10 @@ X Set sketch as modified when any character committed using input method X https://github.com/processing/processing/pull/4599 X Insert characters by InputMethod at one time X https://github.com/processing/processing/pull/4594 +X Insert string when it is committed +X https://github.com/processing/processing/pull/4602 +_ Set text color for InputMethod +_ https://github.com/processing/processing/pull/4593 jakub X NullPointerException in SketchCode.getDocumentText() From 1214fc7112366bbcf3bfa39de86fa68834b9f372 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Fri, 5 Aug 2016 10:22:20 -0400 Subject: [PATCH 0054/1035] proper coloring for InputMethod support (#4593) --- todo.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/todo.txt b/todo.txt index d3530d00eb..137bfdec70 100644 --- a/todo.txt +++ b/todo.txt @@ -22,8 +22,8 @@ X Insert characters by InputMethod at one time X https://github.com/processing/processing/pull/4594 X Insert string when it is committed X https://github.com/processing/processing/pull/4602 -_ Set text color for InputMethod -_ https://github.com/processing/processing/pull/4593 +X Set text color for InputMethod +X https://github.com/processing/processing/pull/4593 jakub X NullPointerException in SketchCode.getDocumentText() From 35151e318434c80aa39533f2714ad63879c271e0 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Fri, 5 Aug 2016 10:50:19 -0400 Subject: [PATCH 0055/1035] move input method callback since it's broadly applicable --- .../processing/app/syntax/PdeInputHandler.java | 18 +++++++++++++++++- app/src/processing/app/ui/Editor.java | 6 +++--- .../processing/mode/java/JavaInputHandler.java | 9 +-------- todo.txt | 2 ++ 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/app/src/processing/app/syntax/PdeInputHandler.java b/app/src/processing/app/syntax/PdeInputHandler.java index be918de07b..bf0a7cdf66 100755 --- a/app/src/processing/app/syntax/PdeInputHandler.java +++ b/app/src/processing/app/syntax/PdeInputHandler.java @@ -30,6 +30,7 @@ import processing.app.Platform; import processing.app.Preferences; +import processing.app.ui.Editor; /** @@ -39,7 +40,17 @@ */ public class PdeInputHandler extends DefaultInputHandler { - public PdeInputHandler() { + /** + * Need the Editor object for Input Method changes, plus most subclasses + * will want a local copy anyway. Changed after Processing 3.1.2, need to + * see if this breaks any other Modes before releasing. + */ + protected Editor editor; + + + public PdeInputHandler(Editor editor) { + this.editor = editor; + // Use option on mac for text edit controls that are ctrl on Windows/Linux. // (i.e. ctrl-left/right is option-left/right on OS X) String mod = Platform.isMacOS() ? "A" : "C"; @@ -253,4 +264,9 @@ public boolean handlePressed(KeyEvent event) { public boolean handleTyped(KeyEvent event) { return false; } + + + public void handleInputMethodCommit() { + editor.getSketch().setModified(true); + } } \ No newline at end of file diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index dd445f4fcf..0c713d181b 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -389,7 +389,7 @@ public void removeToolContrib(ToolContribution tc) { protected JEditTextArea createTextArea() { return new JEditTextArea(new PdeTextAreaDefaults(mode), - new PdeInputHandler()); + new PdeInputHandler(this)); } @@ -1770,7 +1770,7 @@ public void beginCompoundEdit() { startCompoundEdit(); super.beginCompoundEdit(); } - + @Override public void endCompoundEdit() { stopCompoundEdit(); @@ -1805,7 +1805,7 @@ public void insertUpdate(DocumentEvent e) { if (!isInserting && !textarea.isOverwriteEnabled() && isDirectEdit()) { endTextEditHistory(); } - + if (!textarea.isOverwriteEnabled()) { isInserting = true; } diff --git a/java/src/processing/mode/java/JavaInputHandler.java b/java/src/processing/mode/java/JavaInputHandler.java index 596f35063a..9e3ae59121 100644 --- a/java/src/processing/mode/java/JavaInputHandler.java +++ b/java/src/processing/mode/java/JavaInputHandler.java @@ -40,7 +40,6 @@ * continuing to hack this class. */ public class JavaInputHandler extends PdeInputHandler { - private Editor editor; /** ctrl-alt on windows and linux, cmd-alt on mac os x */ static final int CTRL_ALT = ActionEvent.ALT_MASK | @@ -48,7 +47,7 @@ public class JavaInputHandler extends PdeInputHandler { public JavaInputHandler(Editor editor) { - this.editor = editor; + super(editor); } @@ -334,12 +333,6 @@ public boolean handleTyped(KeyEvent event) { } - @Override - public void handleInputMethodCommit() { - editor.getSketch().setModified(true); - } - - /** * Return the index for the first character on this line. */ diff --git a/todo.txt b/todo.txt index 137bfdec70..ced8718534 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,6 @@ 0252 (3.1.3?) +X change PdeInputHandler constructor +_ check whether this breaks other Modes before releasing _ modify line number color when no lines extend that far? _ https://github.com/processing/processing/pull/4560 _ text gutter doesn't seem to be hidpi From 38e92a8d174da313d69346a96c03ba8e63c09fa6 Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Sat, 6 Aug 2016 15:23:08 +0200 Subject: [PATCH 0056/1035] Fix resizing targets for async save Got burned by not setting pixelWidth and pixelHeight properly. Fixes #4578 --- core/src/processing/core/PGraphics.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java index c7606c7ace..9b2e18cdbc 100644 --- a/core/src/processing/core/PGraphics.java +++ b/core/src/processing/core/PGraphics.java @@ -8364,12 +8364,10 @@ public PImage getAvailableTarget(int requestedWidth, int requestedHeight, // ign targetsCreated++; } else { target = targetPool.take(); - if (target.width != requestedWidth || - target.height != requestedHeight) { - target.width = requestedWidth; - target.height = requestedHeight; + if (target.pixelWidth != requestedWidth || + target.pixelHeight != requestedHeight) { // TODO: this kills performance when saving different sizes - target.pixels = new int[requestedWidth * requestedHeight]; + target = new PImage(requestedWidth, requestedHeight); } } target.format = format; From e06c7a49109931d392707b5715ea2a6ac49f591b Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 6 Aug 2016 10:22:24 -0400 Subject: [PATCH 0057/1035] these shouldn't be executable --- app/src/processing/app/syntax/DefaultInputHandler.java | 0 app/src/processing/app/syntax/PdeInputHandler.java | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 app/src/processing/app/syntax/DefaultInputHandler.java mode change 100755 => 100644 app/src/processing/app/syntax/PdeInputHandler.java diff --git a/app/src/processing/app/syntax/DefaultInputHandler.java b/app/src/processing/app/syntax/DefaultInputHandler.java old mode 100755 new mode 100644 diff --git a/app/src/processing/app/syntax/PdeInputHandler.java b/app/src/processing/app/syntax/PdeInputHandler.java old mode 100755 new mode 100644 From e9f2840d999c0f481481f39a3e7af5656a7d703c Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 6 Aug 2016 11:39:27 -0400 Subject: [PATCH 0058/1035] further refactoring --- .../processing/app/syntax/JEditTextArea.java | 9 +- .../app/syntax/TextAreaPainter.java | 6 + app/src/processing/app/ui/Editor.java | 20 +- core/todo.txt | 10 +- java/src/processing/mode/java/JavaEditor.java | 29 +- .../mode/java/pdex/JavaTextArea.java | 174 +---------- .../mode/java/pdex/JavaTextAreaPainter.java | 271 +++++++----------- todo.txt | 11 +- 8 files changed, 161 insertions(+), 369 deletions(-) diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java index 97217e541c..fd1355bf91 100644 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ b/app/src/processing/app/syntax/JEditTextArea.java @@ -794,26 +794,27 @@ public int xToOffset(int line, int x) { } } + /** * Converts a point to an offset, from the start of the text. * @param x The x co-ordinate of the point * @param y The y co-ordinate of the point */ - public int xyToOffset(int x, int y) - { + public int xyToOffset(int x, int y) { int line = yToLine(y); int start = getLineStartOffset(line); return start + xToOffset(line,x); } + /** * Returns the document this text area is editing. */ - public final SyntaxDocument getDocument() - { + public final SyntaxDocument getDocument() { return document; } + /** * Sets the document this text area is editing. * @param document The document diff --git a/app/src/processing/app/syntax/TextAreaPainter.java b/app/src/processing/app/syntax/TextAreaPainter.java index 59c99a5a88..2dd0f76916 100644 --- a/app/src/processing/app/syntax/TextAreaPainter.java +++ b/app/src/processing/app/syntax/TextAreaPainter.java @@ -438,6 +438,12 @@ public FontMetrics getFontMetrics(SyntaxStyle style) { } + // fry [160806 for 3.2] + public int getLineHeight() { + return fm.getHeight() + fm.getDescent(); + } + + // /** // * Sets the font for this component. This is overridden to update the // * cached font metrics and to recalculate which lines are visible. diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 0c713d181b..58f5025fd2 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -2358,6 +2358,14 @@ public Point getSketchLocation() { // } + public boolean isDebuggerEnabled() { + return false; + } + + + public void toggleBreakpoint(int lineIndex) { } + + /** * Check if the sketch is modified and ask user to save changes. * @return false if canceling the close/quit operation @@ -2930,18 +2938,6 @@ public boolean isHalted() { static Color bgColorError; - /* - public void toolTipError(JComponent comp, String message) { - setToolTip(comp, message, true); - } - - - public void toolTipWarning(JComponent comp, String message) { - setToolTip(comp, message, false); - } - */ - - public void statusToolTip(JComponent comp, String message, boolean error) { if (font == null) { font = Toolkit.getSansFont(9, Font.PLAIN); diff --git a/core/todo.txt b/core/todo.txt index 59916d1711..203a3111ce 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -1,4 +1,4 @@ -0252 (3.1.3?) +0252 (3.2) X some Table cleanup based on other CSV parsing work X use StandardCharsets.UTF_8 instead of getting encoding by name X PApplet.main(Blah.class) now works @@ -7,6 +7,14 @@ _ do the same for the other data classes _ note the difference between this and toJSONObject() or toJSONArray() _ or is that the better way to handle it? hm +jakub +X Fix resizing targets for async save +X https://github.com/processing/processing/pull/4607 +X https://github.com/processing/processing/issues/4578 + +_ disable OpenGL ES on Linux? +_ https://github.com/processing/processing/issues/4584 + _ Can't render PGraphics object using image() within a PDF _ https://github.com/processing/processing/issues/4473 diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 82a949ccc7..9272a08409 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -126,7 +126,8 @@ protected JavaEditor(Base base, String path, EditorState state, getJavaTextArea().setMode(jmode); - initPDEX(); + preprocessingService = new PreprocessingService(this); + pdex = new PDEX(this, preprocessingService); Toolkit.setMenuMnemonics(textarea.getRightClickPopup()); @@ -146,18 +147,12 @@ public void caretUpdate(CaretEvent e) { }); } - + public PdePreprocessor createPreprocessor(final String sketchName) { - return new PdePreprocessor(sketchName); + return new PdePreprocessor(sketchName); } - - protected void initPDEX() { - preprocessingService = new PreprocessingService(this); - pdex = new PDEX(this, preprocessingService); - } - - + protected JEditTextArea createTextArea() { return new JavaTextArea(new PdeTextAreaDefaults(mode), this); } @@ -1195,12 +1190,13 @@ public void handleContinue() { } - /** Toggle a breakpoint on the current line. */ - public void toggleBreakpoint() { - debugger.toggleBreakpoint(getCurrentLineID().lineIdx()); - } +// /** Toggle a breakpoint on the current line. */ +// public void toggleBreakpoint() { +// toggleBreakpoint(getCurrentLineID().lineIdx()); +// } + @Override public void toggleBreakpoint(int lineIndex) { debugger.toggleBreakpoint(lineIndex); } @@ -1430,7 +1426,8 @@ public void actionPerformed(ActionEvent e) { item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Logger.getLogger(JavaEditor.class.getName()).log(Level.INFO, "Invoked 'Toggle Breakpoint' menu item"); - toggleBreakpoint(); + // TODO wouldn't getCaretLine() do the same thing with less effort? + toggleBreakpoint(getCurrentLineID().lineIdx()); } }); debugMenu.add(item); @@ -1522,8 +1519,8 @@ public void actionPerformed(ActionEvent e) { } + @Override public boolean isDebuggerEnabled() { - //return enableDebug.isSelected(); return debugEnabled; } diff --git a/java/src/processing/mode/java/pdex/JavaTextArea.java b/java/src/processing/mode/java/pdex/JavaTextArea.java index eb08813666..9eb57fb99e 100644 --- a/java/src/processing/mode/java/pdex/JavaTextArea.java +++ b/java/src/processing/mode/java/pdex/JavaTextArea.java @@ -2,7 +2,7 @@ /* Part of the Processing project - http://processing.org -Copyright (c) 2012-15 The Processing Foundation +Copyright (c) 2012-16 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -41,48 +41,19 @@ import processing.app.Mode; import processing.app.Platform; import processing.app.syntax.JEditTextArea; +import processing.app.syntax.PdeTextArea; import processing.app.syntax.TextAreaDefaults; import processing.app.ui.Editor; -// TODO The way listeners are added/removed here is fragile and -// likely to cause bugs that are very difficult to find. -// We shouldn't be re-inventing the wheel with how listeners are handled. -// TODO We're overriding more things in JEditTextArea than we should, which -// makes it trickier for other Modes (Python, etc) to subclass because -// they'll need to re-implement what's in here, but first wade through it. -// To fix, we need to clean this up and put the appropriate cross-Mode -// changes into JEditTextArea (or a subclass in processing.app) - -public class JavaTextArea extends JEditTextArea { - protected final JavaEditor editor; - - protected Image gutterGradient; - - /// the text marker for highlighting breakpoints in the gutter - static public final String BREAK_MARKER = "<>"; - /// the text marker for highlighting the current line in the gutter - static public final String STEP_MARKER = "->"; - - /// maps line index to gutter text - protected final Map gutterText = new HashMap<>(); +public class JavaTextArea extends PdeTextArea { + //protected final JavaEditor editor; private CompletionPanel suggestion; public JavaTextArea(TextAreaDefaults defaults, JavaEditor editor) { - super(defaults, new JavaInputHandler(editor)); - this.editor = editor; - - // change cursor to pointer in the gutter area - painter.addMouseMotionListener(gutterCursorMouseAdapter); - - //addCompletionPopupListner(); - add(CENTER, painter); - - // load settings from theme.txt - Mode mode = editor.getMode(); - gutterGradient = mode.makeGradient("editor", Editor.LEFT_GUTTER, 500); + super(defaults, new JavaInputHandler(editor), editor); // TweakMode code prevCompListeners = painter.getComponentListeners(); @@ -102,16 +73,11 @@ protected JavaTextAreaPainter createPainter(final TextAreaDefaults defaults) { } - protected JavaTextAreaPainter getCustomPainter() { + protected JavaTextAreaPainter getJavaPainter() { return (JavaTextAreaPainter) painter; } - public void setMode(JavaMode mode) { - getCustomPainter().setMode(mode); - } - - /** * Handles KeyEvents for TextArea (code completion begins from here). */ @@ -120,7 +86,7 @@ public void processKeyEvent(KeyEvent evt) { if (evt.getKeyCode() == KeyEvent.VK_ESCAPE) { if (suggestion != null){ if (suggestion.isVisible()){ - Messages.log("esc key"); + Messages.log("ESC key"); hideSuggestion(); evt.consume(); return; @@ -544,114 +510,6 @@ protected static String parsePhrase(final String lineText) { } - public Image getGutterGradient() { - return gutterGradient; - } - - - /** - * Set the gutter text of a specific line. - * - * @param lineIdx - * the line index (0-based) - * @param text - * the text - */ - public void setGutterText(int lineIdx, String text) { - gutterText.put(lineIdx, text); - painter.invalidateLine(lineIdx); - } - - - /** - * Clear the gutter text of a specific line. - * - * @param lineIdx - * the line index (0-based) - */ - public void clearGutterText(int lineIdx) { - gutterText.remove(lineIdx); - painter.invalidateLine(lineIdx); - } - - - /** - * Clear all gutter text. - */ - public void clearGutterText() { - for (int lineIdx : gutterText.keySet()) { - painter.invalidateLine(lineIdx); - } - gutterText.clear(); - } - - - /** - * Retrieve the gutter text of a specific line. - * - * @param lineIdx - * the line index (0-based) - * @return the gutter text - */ - public String getGutterText(int lineIdx) { - return gutterText.get(lineIdx); - } - - - /** - * Convert a character offset to a horizontal pixel position inside the text - * area. Overridden to take gutter width into account. - * - * @param line - * the 0-based line number - * @param offset - * the character offset (0 is the first character on a line) - * @return the horizontal position - */ - @Override - public int _offsetToX(int line, int offset) { - return super._offsetToX(line, offset) + Editor.LEFT_GUTTER; - } - - - /** - * Convert a horizontal pixel position to a character offset. Overridden to - * take gutter width into account. - * - * @param line - * the 0-based line number - * @param x - * the horizontal pixel position - * @return he character offset (0 is the first character on a line) - */ - @Override - public int xToOffset(int line, int x) { - return super.xToOffset(line, x - Editor.LEFT_GUTTER); - } - - - /** - * Sets default cursor (instead of text cursor) in the gutter area. - */ - protected final MouseMotionAdapter gutterCursorMouseAdapter = new MouseMotionAdapter() { - private int lastX; // previous horizontal position of the mouse cursor - - @Override - public void mouseMoved(MouseEvent me) { - if (me.getX() < Editor.LEFT_GUTTER) { - if (lastX >= Editor.LEFT_GUTTER) { - painter.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - } - } else { - if (lastX < Editor.LEFT_GUTTER) { - painter.setCursor(new Cursor(Cursor.TEXT_CURSOR)); - } - } - lastX = me.getX(); - } - }; - - // appears unused, removed when looking to change completion trigger [fry 140801] /* public void showSuggestionLater(final DefaultListModel defListModel, final String word) { @@ -667,10 +525,7 @@ public void run() { /** - * Calculates location of caret and displays the suggestion popup at the location. - * - * @param listModel - * @param subWord + * Calculates location of caret and displays the suggestion popup. */ protected void showSuggestion(DefaultListModel listModel, String subWord) { hideSuggestion(); @@ -682,10 +537,9 @@ protected void showSuggestion(DefaultListModel listModel, S int position = getCaretPosition(); Point location = new Point(); try { - location.x = offsetToX(getCaretLine(), position - - getLineStartOffset(getCaretLine())); - location.y = lineToY(getCaretLine()) - + getPainter().getFontMetrics().getHeight() + getPainter().getFontMetrics().getDescent(); + location.x = offsetToX(getCaretLine(), + position - getLineStartOffset(getCaretLine())); + location.y = lineToY(getCaretLine()) + getPainter().getLineHeight(); //log("TA position: " + location); } catch (Exception e2) { e2.printStackTrace(); @@ -748,7 +602,7 @@ public void startTweakMode() { // ignore if we are already in interactiveMode if (!tweakMode) { removeAllListeners(); - getCustomPainter().startTweakMode(); + getJavaPainter().startTweakMode(); this.editable = false; this.caretBlinks = false; this.setCaretVisible(false); @@ -762,7 +616,7 @@ public void stopTweakMode() { if (tweakMode) { removeAllListeners(); addPrevListeners(); - getCustomPainter().stopTweakMode(); + getJavaPainter().stopTweakMode(); editable = true; caretBlinks = true; setCaretVisible(true); @@ -790,6 +644,6 @@ private void addPrevListeners() { public void updateInterface(List> handles, List> colorBoxes) { - getCustomPainter().updateInterface(handles, colorBoxes); + getJavaPainter().updateInterface(handles, colorBoxes); } } diff --git a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java index 1f9e311dab..d9724e3b78 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -2,7 +2,7 @@ /* Part of the Processing project - http://processing.org -Copyright (c) 2012-15 The Processing Foundation +Copyright (c) 2012-16 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -26,13 +26,11 @@ import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; -import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Toolkit; -import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; @@ -48,59 +46,22 @@ import processing.app.Mode; import processing.app.SketchCode; +import processing.app.syntax.PdeTextAreaPainter; import processing.app.syntax.SyntaxDocument; import processing.app.syntax.TextAreaDefaults; -import processing.app.syntax.TextAreaPainter; import processing.app.syntax.TokenMarker; import processing.app.ui.Editor; -// TODO Most of this needs to be merged into the main TextAreaPainter, -// since it's not specific to Java. [fry 150821] - /** * Customized line painter. Adds support for background colors, * left hand gutter area with background color and text. */ -public class JavaTextAreaPainter extends TextAreaPainter - implements MouseListener, MouseMotionListener { - - public Color errorUnderlineColor; - public Color warningUnderlineColor; - - protected Font gutterTextFont; - protected Color gutterTextColor; - protected Color gutterPastColor; - protected Color gutterLineHighlightColor; - +public class JavaTextAreaPainter extends PdeTextAreaPainter { public JavaTextAreaPainter(final JavaTextArea textArea, TextAreaDefaults defaults) { super(textArea, defaults); - // Handle mouse clicks to toggle breakpoints - addMouseListener(new MouseAdapter() { - long lastTime; // OS X seems to be firing multiple mouse events - - public void mousePressed(MouseEvent event) { - JavaEditor javaEditor = getJavaEditor(); - // Don't toggle breakpoints when the debugger isn't enabled - // https://github.com/processing/processing/issues/3306 - if (javaEditor.isDebuggerEnabled()) { - long thisTime = event.getWhen(); - if (thisTime - lastTime > 100) { - if (event.getX() < Editor.LEFT_GUTTER) { - int offset = getJavaTextArea().xyToOffset(event.getX(), event.getY()); - if (offset >= 0) { - int lineIndex = getJavaTextArea().getLineOfOffset(offset); - javaEditor.toggleBreakpoint(lineIndex); - } - } - lastTime = thisTime; - } - } - } - }); - // TweakMode code tweakMode = false; cursorType = Cursor.DEFAULT_CURSOR; @@ -349,23 +310,6 @@ static private void paintSquiggle(Graphics g, int y, int x1, int x2) { } - /** - * Loads theme for TextAreaPainter(XQMode) - */ - public void setMode(Mode mode) { - errorUnderlineColor = mode.getColor("editor.error.underline.color"); - warningUnderlineColor = mode.getColor("editor.warning.underline.color"); - - gutterTextFont = mode.getFont("editor.gutter.text.font"); - gutterTextColor = mode.getColor("editor.gutter.text.color"); - gutterPastColor = new Color(gutterTextColor.getRed(), - gutterTextColor.getGreen(), - gutterTextColor.getBlue(), - 96); - gutterLineHighlightColor = mode.getColor("editor.gutter.linehighlight.color"); - } - - @Override public String getToolTipText(MouseEvent evt) { int line = evt.getY() / getFontMetrics().getHeight() + textArea.getFirstLine(); @@ -385,9 +329,7 @@ public String getToolTipText(MouseEvent evt) { if (x >= getJavaTextArea().offsetToX(line, startOffset) && x <= getJavaTextArea().offsetToX(line, stopOffset)) { - getJavaEditor().statusToolTip(JavaTextAreaPainter.this, - problem.getMessage(), - problem.isError()); + getEditor().statusToolTip(this, problem.getMessage(), problem.isError()); return super.getToolTipText(evt); } } @@ -455,8 +397,93 @@ synchronized public void paint(Graphics gfx) { protected void startTweakMode() { - addMouseListener(this); - addMouseMotionListener(this); + addMouseListener(new MouseListener() { + + @Override + public void mouseReleased(MouseEvent e) { + if (mouseHandle != null) { + mouseHandle.resetProgress(); + mouseHandle = null; + + updateCursor(e.getX(), e.getY()); + repaint(); + } + } + + @Override + public void mousePressed(MouseEvent e) { + int currentTab = getCurrentCodeIndex(); + // check for clicks on number handles + for (Handle n : handles.get(currentTab)) { + if (n.pick(e.getX(), e.getY())) { + cursorType = -1; + JavaTextAreaPainter.this.setCursor(blankCursor); + mouseHandle = n; + mouseHandle.setCenterX(e.getX()); + repaint(); + return; + } + } + + // check for clicks on color boxes + for (ColorControlBox box : colorBoxes.get(currentTab)) { + if (box.pick(e.getX(), e.getY())) { + if (colorSelector != null) { + // we already show a color selector, close it + colorSelector.frame.dispatchEvent(new WindowEvent(colorSelector.frame, WindowEvent.WINDOW_CLOSING)); + } + + colorSelector = new ColorSelector(box); + colorSelector.frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + colorSelector.frame.setVisible(false); + colorSelector = null; + } + }); + colorSelector.show(getLocationOnScreen().x + e.getX() + 30, + getLocationOnScreen().y + e.getY() - 130); + } + } + } + + @Override + public void mouseExited(MouseEvent e) { } + + @Override + public void mouseEntered(MouseEvent e) { } + + @Override + public void mouseClicked(MouseEvent e) { } + }); + + addMouseMotionListener(new MouseMotionListener() { + + @Override + public void mouseMoved(MouseEvent e) { + updateCursor(e.getX(), e.getY()); + + if (!Settings.alwaysShowColorBoxes) { + showHideColorBoxes(e.getY()); + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if (mouseHandle != null) { + // set the current drag amount of the arrows + mouseHandle.setCurrentX(e.getX()); + + // update code text with the new value + updateCodeText(); + + if (colorSelector != null) { + colorSelector.refreshColor(); + } + + repaint(); + } + } + }); tweakMode = true; setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); repaint(); @@ -613,126 +640,20 @@ private void showHideColorBoxes(int y) { } - @Override - public void mouseDragged(MouseEvent e) { - if (mouseHandle != null) { - // set the current drag amount of the arrows - mouseHandle.setCurrentX(e.getX()); - - // update code text with the new value - updateCodeText(); - - if (colorSelector != null) { - colorSelector.refreshColor(); - } - - repaint(); - } - } - - - @Override - public void mouseExited(MouseEvent e) { - } - - - @Override - public void mousePressed(MouseEvent e) { - int currentTab = getCurrentCodeIndex(); - // check for clicks on number handles - for (Handle n : handles.get(currentTab)) { - if (n.pick(e.getX(), e.getY())) { - cursorType = -1; - this.setCursor(blankCursor); - mouseHandle = n; - mouseHandle.setCenterX(e.getX()); - repaint(); - return; - } - } - - // check for clicks on color boxes - for (ColorControlBox box : colorBoxes.get(currentTab)) { - if (box.pick(e.getX(), e.getY())) { - if (colorSelector != null) { - // we already show a color selector, close it - colorSelector.frame.dispatchEvent(new WindowEvent(colorSelector.frame, WindowEvent.WINDOW_CLOSING)); - } - - colorSelector = new ColorSelector(box); - colorSelector.frame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - colorSelector.frame.setVisible(false); - colorSelector = null; - } - }); - colorSelector.show(getLocationOnScreen().x + e.getX() + 30, - getLocationOnScreen().y + e.getY() - 130); - } - } - } - - - @Override - public void mouseReleased(MouseEvent e) { - if (mouseHandle != null) { - mouseHandle.resetProgress(); - mouseHandle = null; - - updateCursor(e.getX(), e.getY()); - repaint(); - } - } - - - @Override - public void mouseMoved(MouseEvent e) { - updateCursor(e.getX(), e.getY()); - - if (!Settings.alwaysShowColorBoxes) { - showHideColorBoxes(e.getY()); - } - } - - - @Override - public void mouseClicked(MouseEvent e) { - // TODO Auto-generated method stub - } - - - @Override - public void mouseEntered(MouseEvent e) { - // TODO Auto-generated method stub - } - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - @Override - public int getScrollWidth() { - // https://github.com/processing/processing/issues/3591 - return super.getWidth() - Editor.LEFT_GUTTER; + private JavaEditor getJavaEditor() { + return (JavaEditor) getEditor(); } - public Editor getEditor() { - return ((JavaTextArea) textArea).editor; - } - - - private JavaEditor getJavaEditor() { - return ((JavaTextArea) textArea).editor; + private JavaTextArea getJavaTextArea() { + return (JavaTextArea) textArea; } private int getCurrentCodeIndex() { - return getEditor().getSketch().getCurrentCodeIndex(); - } - - - private JavaTextArea getJavaTextArea() { - return (JavaTextArea) textArea; + return getEditor().getSketch().getCurrentCodeIndex(); } } diff --git a/todo.txt b/todo.txt index ced8718534..3aca8844e6 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,4 @@ -0252 (3.1.3?) +0252 (3.2) X change PdeInputHandler constructor _ check whether this breaks other Modes before releasing _ modify line number color when no lines extend that far? @@ -11,6 +11,9 @@ _ https://github.com/processing/processing/pull/4593 _ Processing .jar files in CLASSPATH can cause startup crash _ https://github.com/processing/processing/issues/4128 +_ Help Menu disabled on OS X +_ https://github.com/processing/processing/issues/43503#issuecomment-237715947 + contrib input method work @@ -45,6 +48,10 @@ _ https://github.com/processing/processing/issues/3948 _ possible infinite loop on modified externally _ https://github.com/processing/processing/issues/3965 +medium +_ Move general PDE code out of JavaMode and into general base classes +_ https://github.com/processing/processing/issues/4606 + lower _ make when opening new editor window, open on the same display as current _ https://github.com/processing/processing/issues/4526 @@ -110,6 +117,8 @@ _ mouse events (i.e. toggle breakpoint) seem to be firing twice run/debug _ Tweak Mode sometimes freezes while running, require a force quit _ https://github.com/processing/processing/issues/3928 +_ TweakMode listener mess in JavaTextArea +_ https://github.com/processing/processing/issues/4605 gui From 57060e1f8ef65332f8e1a3758cdf50b3eca49b27 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 6 Aug 2016 12:31:49 -0400 Subject: [PATCH 0059/1035] heavy refactoring to separate Java and non-Java code for Modes --- app/src/processing/app/Problem.java | 35 ++++ .../processing/app/syntax/PdeTextArea.java | 177 ++++++++++++++++++ .../app/syntax/PdeTextAreaPainter.java | 100 ++++++++++ app/src/processing/app/ui/Editor.java | 151 ++++++++++++++- app/src/processing/app/ui/ErrorTable.java | 12 +- java/src/processing/mode/java/JavaEditor.java | 170 +---------------- .../processing/mode/java/MarkerColumn.java | 21 +-- .../mode/java/pdex/CompletionCandidate.java | 2 +- .../java/pdex/ErrorMessageSimplifier.java | 2 +- .../pdex/{Problem.java => JavaProblem.java} | 6 +- .../mode/java/pdex/JavaTextArea.java | 111 ++++++----- .../mode/java/pdex/JavaTextAreaPainter.java | 14 +- java/src/processing/mode/java/pdex/PDEX.java | 9 +- 13 files changed, 552 insertions(+), 258 deletions(-) create mode 100644 app/src/processing/app/Problem.java create mode 100644 app/src/processing/app/syntax/PdeTextArea.java create mode 100644 app/src/processing/app/syntax/PdeTextAreaPainter.java rename java/src/processing/mode/java/pdex/{Problem.java => JavaProblem.java} (97%) diff --git a/app/src/processing/app/Problem.java b/app/src/processing/app/Problem.java new file mode 100644 index 0000000000..7cd93db511 --- /dev/null +++ b/app/src/processing/app/Problem.java @@ -0,0 +1,35 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + Copyright (c) 2012-16 The Processing Foundation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +package processing.app; + + +public interface Problem { + public boolean isError(); + public boolean isWarning(); + + public int getTabIndex(); + public int getLineNumber(); + public String getMessage(); + + public int getStartOffset(); + public int getStopOffset(); +} + diff --git a/app/src/processing/app/syntax/PdeTextArea.java b/app/src/processing/app/syntax/PdeTextArea.java new file mode 100644 index 0000000000..02ef7c3f05 --- /dev/null +++ b/app/src/processing/app/syntax/PdeTextArea.java @@ -0,0 +1,177 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* +Part of the Processing project - http://processing.org +Copyright (c) 2012-16 The Processing Foundation + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +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. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +package processing.app.syntax; + +import java.awt.Cursor; +import java.awt.Image; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.util.HashMap; +import java.util.Map; + +import processing.app.Mode; +import processing.app.ui.Editor; + + +/** + * Extensions to JEditTextArea to for the PDE. These were moved out of + * JavaTextArea because they were not Java-specific and would be helpful + * for other Mode implementations. + */ +public class PdeTextArea extends JEditTextArea { + protected final Editor editor; + + protected Image gutterGradient; + + /// the text marker for highlighting breakpoints in the gutter + static public final String BREAK_MARKER = "<>"; + /// the text marker for highlighting the current line in the gutter + static public final String STEP_MARKER = "->"; + + /// maps line index to gutter text + protected final Map gutterText = new HashMap<>(); + + + public PdeTextArea(TextAreaDefaults defaults, InputHandler inputHandler, + Editor editor) { + super(defaults, inputHandler); + this.editor = editor; + + // change cursor to pointer in the gutter area + painter.addMouseMotionListener(gutterCursorMouseAdapter); + + add(CENTER, painter); + + // load settings from theme.txt + Mode mode = editor.getMode(); + gutterGradient = mode.makeGradient("editor", Editor.LEFT_GUTTER, 500); + } + + + public Image getGutterGradient() { + return gutterGradient; + } + + + public void setMode(Mode mode) { + ((PdeTextAreaPainter) painter).setMode(mode); + } + + + /** + * Set the gutter text of a specific line. + * + * @param lineIdx + * the line index (0-based) + * @param text + * the text + */ + public void setGutterText(int lineIdx, String text) { + gutterText.put(lineIdx, text); + painter.invalidateLine(lineIdx); + } + + + /** + * Clear the gutter text of a specific line. + * + * @param lineIdx + * the line index (0-based) + */ + public void clearGutterText(int lineIdx) { + gutterText.remove(lineIdx); + painter.invalidateLine(lineIdx); + } + + + /** + * Clear all gutter text. + */ + public void clearGutterText() { + for (int lineIdx : gutterText.keySet()) { + painter.invalidateLine(lineIdx); + } + gutterText.clear(); + } + + + /** + * Retrieve the gutter text of a specific line. + * @param lineIdx the line index (0-based) + * @return the gutter text + */ + public String getGutterText(int lineIdx) { + return gutterText.get(lineIdx); + } + + + /** + * Convert a character offset to a horizontal pixel position inside + * the text area. Overridden to take gutter width into account. + * @param line the 0-based line number + * @param offset the character offset (0 is the first character on a line) + * @return the horizontal position + */ + @Override + public int _offsetToX(int line, int offset) { + return super._offsetToX(line, offset) + Editor.LEFT_GUTTER; + } + + + /** + * Convert a horizontal pixel position to a character offset. Overridden to + * take gutter width into account. + * @param line the 0-based line number + * @param x the horizontal pixel position + * @return he character offset (0 is the first character on a line) + */ + @Override + public int xToOffset(int line, int x) { + return super.xToOffset(line, x - Editor.LEFT_GUTTER); + } + + + /** + * Sets default cursor (instead of text cursor) in the gutter area. + */ + protected final MouseMotionAdapter gutterCursorMouseAdapter = new MouseMotionAdapter() { + private int lastX; // previous horizontal position of the mouse cursor + + @Override + public void mouseMoved(MouseEvent me) { + if (me.getX() < Editor.LEFT_GUTTER) { + if (lastX >= Editor.LEFT_GUTTER) { + painter.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + } + } else { + if (lastX < Editor.LEFT_GUTTER) { + painter.setCursor(new Cursor(Cursor.TEXT_CURSOR)); + } + } + lastX = me.getX(); + } + }; + + + public Editor getEditor() { + return editor; + } +} \ No newline at end of file diff --git a/app/src/processing/app/syntax/PdeTextAreaPainter.java b/app/src/processing/app/syntax/PdeTextAreaPainter.java new file mode 100644 index 0000000000..7547fb4504 --- /dev/null +++ b/app/src/processing/app/syntax/PdeTextAreaPainter.java @@ -0,0 +1,100 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* +Part of the Processing project - http://processing.org +Copyright (c) 2012-16 The Processing Foundation + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +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. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +package processing.app.syntax; + +import java.awt.Color; +import java.awt.Font; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import processing.app.Mode; +import processing.app.ui.Editor; + + +public class PdeTextAreaPainter extends TextAreaPainter { + public Color errorUnderlineColor; + public Color warningUnderlineColor; + + protected Font gutterTextFont; + protected Color gutterTextColor; + protected Color gutterPastColor; + protected Color gutterLineHighlightColor; + + + public PdeTextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults) { + super(textArea, defaults); + + // Handle mouse clicks to toggle breakpoints + addMouseListener(new MouseAdapter() { + long lastTime; // OS X seems to be firing multiple mouse events + + public void mousePressed(MouseEvent event) { + // Don't toggle breakpoints when the debugger isn't enabled + // https://github.com/processing/processing/issues/3306 + if (getEditor().isDebuggerEnabled()) { + long thisTime = event.getWhen(); + if (thisTime - lastTime > 100) { + if (event.getX() < Editor.LEFT_GUTTER) { + int offset = textArea.xyToOffset(event.getX(), event.getY()); + if (offset >= 0) { + int lineIndex = textArea.getLineOfOffset(offset); + getEditor().toggleBreakpoint(lineIndex); + } + } + lastTime = thisTime; + } + } + } + }); + } + + + /** + * Loads theme for TextAreaPainter + */ + public void setMode(Mode mode) { + errorUnderlineColor = mode.getColor("editor.error.underline.color"); + warningUnderlineColor = mode.getColor("editor.warning.underline.color"); + + gutterTextFont = mode.getFont("editor.gutter.text.font"); + gutterTextColor = mode.getColor("editor.gutter.text.color"); + gutterPastColor = new Color(gutterTextColor.getRed(), + gutterTextColor.getGreen(), + gutterTextColor.getBlue(), + 96); + gutterLineHighlightColor = mode.getColor("editor.gutter.linehighlight.color"); + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + @Override + public int getScrollWidth() { + // https://github.com/processing/processing/issues/3591 + return super.getWidth() - Editor.LEFT_GUTTER; + } + + + public Editor getEditor() { + return ((PdeTextArea) textArea).editor; + } +} \ No newline at end of file diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 58f5025fd2..1204690014 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -30,6 +30,7 @@ import processing.app.Mode; import processing.app.Platform; import processing.app.Preferences; +import processing.app.Problem; import processing.app.RunnerListener; import processing.app.Sketch; import processing.app.SketchCode; @@ -38,6 +39,10 @@ import processing.app.contrib.ContributionManager; import processing.app.syntax.*; import processing.core.*; +import processing.mode.java.JavaMode; +import processing.mode.java.MarkerColumn; +import processing.mode.java.pdex.JavaProblem; +import processing.mode.java.pdex.JavaTextArea; import java.awt.BorderLayout; import java.awt.Color; @@ -55,10 +60,12 @@ import java.io.*; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Stack; import java.util.Timer; import java.util.TimerTask; +import java.util.stream.Collectors; import javax.swing.*; import javax.swing.event.*; @@ -79,6 +86,7 @@ public abstract class Editor extends JFrame implements RunnerListener { static public final int RIGHT_GUTTER = 12; static public final int GUTTER_MARGIN = 3; + private MarkerColumn errorColumn; // Otherwise, if the window is resized with the message label // set to blank, its preferredSize() will be fuckered @@ -148,6 +156,8 @@ public abstract class Editor extends JFrame implements RunnerListener { Image backgroundGradient; + protected List problems = Collections.emptyList(); + protected Editor(final Base base, String path, final EditorState state, final Mode mode) throws EditorException { @@ -1572,6 +1582,11 @@ public JEditTextArea getTextArea() { } + public PdeTextArea getPdeTextArea() { + return (PdeTextArea) textarea; + } + + /** * Get the contents of the current buffer. Used by the Sketch class. */ @@ -2772,12 +2787,22 @@ public ErrorTable getErrorTable() { * Called by ErrorTable when a row is selected. Action taken is specific * to each Mode, based on the object passed in. */ - public void errorTableClick(Object item) { } + public void errorTableClick(Object item) { + highlight((Problem) item); + } public void errorTableDoubleClick(Object item) { } + /** + * Handle whether the tiny red error indicator is shown near + * the error button at the bottom of the PDE + */ + public void updateErrorToggle(boolean hasErrors) { + footer.setNotification(errorTable.getParent(), hasErrors); + } + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -2932,6 +2957,130 @@ public boolean isHalted() { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + public void setProblemList(List problems) { + this.problems = problems; + boolean hasErrors = problems.stream().anyMatch(Problem::isError); + updateErrorTable(problems); + errorColumn.updateErrorPoints(problems); + textarea.repaint(); + updateErrorToggle(hasErrors); + updateEditorStatus(); + } + + + /** + * Updates the error table in the Error Window. + */ + public void updateErrorTable(List problems) { + errorTable.clearRows(); + + for (Problem p : problems) { + String message = p.getMessage(); + errorTable.addRow(p, message, + sketch.getCode(p.getTabIndex()).getPrettyName(), + Integer.toString(p.getLineNumber() + 1)); + // Added +1 because lineNumbers internally are 0-indexed + } + } + + + public void highlight(Problem p) { + if (p != null) { + highlight(p.getTabIndex(), p.getStartOffset(), p.getStartOffset()); + } + } + + + public void highlight(int tabIndex, int startOffset, int stopOffset) { + // Switch to tab + toFront(); + sketch.setCurrentCode(tabIndex); + + // Make sure offsets are in bounds + int length = textarea.getDocumentLength(); + startOffset = PApplet.constrain(startOffset, 0, length); + stopOffset = PApplet.constrain(stopOffset, 0, length); + + // Highlight the code + textarea.select(startOffset, stopOffset); + + // Scroll to error line + textarea.scrollToCaret(); + repaint(); + } + + + public List getProblems() { + return problems; + } + + + /** + * Updates editor status bar, depending on whether the caret is on an error + * line or not + */ + public void updateEditorStatus() { + Problem problem = findProblem(textarea.getCaretLine()); + if (problem != null) { + int type = problem.isError() ? + EditorStatus.CURSOR_LINE_ERROR : EditorStatus.CURSOR_LINE_WARNING; + statusMessage(problem.getMessage(), type); + } else { + switch (getStatusMode()) { + case EditorStatus.CURSOR_LINE_ERROR: + case EditorStatus.CURSOR_LINE_WARNING: + statusEmpty(); + break; + } + } + } + + + /** + * @return the Problem for the first error or warning on 'line' + */ + Problem findProblem(int line) { + int currentTab = getSketch().getCurrentCodeIndex(); + return problems.stream() + .filter(p -> p.getTabIndex() == currentTab) + .filter(p -> { + int pStartLine = p.getLineNumber(); + int pEndOffset = p.getStopOffset(); + int pEndLine = textarea.getLineOfOffset(pEndOffset); + return line >= pStartLine && line <= pEndLine; + }) + .findFirst() + .orElse(null); + } + + + public List findProblems(int line) { + int currentTab = getSketch().getCurrentCodeIndex(); + return problems.stream() + .filter(p -> p.getTabIndex() == currentTab) + .filter(p -> { + int pStartLine = p.getLineNumber(); + int pEndOffset = p.getStopOffset(); + int pEndLine = textarea.getLineOfOffset(pEndOffset); + return line >= pStartLine && line <= pEndLine; + }) + .collect(Collectors.toList()); + } + + + public void repaintErrorBar() { + errorColumn.repaint(); + } + + + public void showConsole() { + footer.setPanel(console); + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + static Font font; static Color textColor; static Color bgColorWarning; diff --git a/app/src/processing/app/ui/ErrorTable.java b/app/src/processing/app/ui/ErrorTable.java index 6c4691de00..14d02e11cd 100644 --- a/app/src/processing/app/ui/ErrorTable.java +++ b/app/src/processing/app/ui/ErrorTable.java @@ -39,17 +39,13 @@ import processing.app.Language; import processing.app.Mode; +import processing.app.Problem; import processing.app.ui.Editor; public class ErrorTable extends JTable { Editor editor; - public interface Entry { - public boolean isError(); - public boolean isWarning(); - } - static final String[] columnNames = { "", // the blank column used for spacing Language.text("editor.footer.errors.problem"), @@ -131,9 +127,9 @@ public void clearRows() { } - public void addRow(Entry data, String message, String filename, String line) { + public void addRow(Problem data, String msg, String filename, String line) { DefaultTableModel dtm = (DefaultTableModel) getModel(); - dtm.addRow(new Object[] { data, message, filename, line }); + dtm.addRow(new Object[] { data, msg, filename, line }); } @@ -206,7 +202,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) { - Entry entry = (Entry) table.getValueAt(row, DATA_COLUMN); + Problem entry = (Problem) table.getValueAt(row, DATA_COLUMN); if (selected) { setForeground(textColorSelected); diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 9272a08409..74a08fb1bc 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -11,7 +11,6 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; import javax.swing.*; import javax.swing.border.*; @@ -34,7 +33,7 @@ import processing.mode.java.pdex.ImportStatement; import processing.mode.java.pdex.JavaTextArea; import processing.mode.java.pdex.PDEX; -import processing.mode.java.pdex.Problem; +import processing.mode.java.pdex.JavaProblem; import processing.mode.java.pdex.SourceUtils; import processing.mode.java.preproc.PdePreprocessor; import processing.mode.java.runner.Runner; @@ -74,8 +73,6 @@ public class JavaEditor extends Editor { protected PreprocessingService preprocessingService; protected PDEX pdex; - protected List problems = Collections.emptyList(); - protected JavaEditor(Base base, String path, EditorState state, Mode mode) throws EditorException { @@ -2368,161 +2365,34 @@ public void statusEmpty(){ */ - public void setProblemList(List problems) { - this.problems = problems; - boolean hasErrors = problems.stream().anyMatch(Problem::isError); - updateErrorTable(problems); - errorColumn.updateErrorPoints(problems); - textarea.repaint(); - updateErrorToggle(hasErrors); - updateEditorStatus(); - } - - /** * Updates the error table in the Error Window. + * Overridden to handle the fugly import suggestions text. */ + @Override public void updateErrorTable(List problems) { errorTable.clearRows(); for (Problem p : problems) { + JavaProblem jp = (JavaProblem) p; String message = p.getMessage(); if (JavaMode.importSuggestEnabled && - p.getImportSuggestions() != null && - p.getImportSuggestions().length > 0) { + jp.getImportSuggestions() != null && + jp.getImportSuggestions().length > 0) { message += " (double-click for suggestions)"; } errorTable.addRow(p, message, - sketch.getCode(p.getTabIndex()).getPrettyName(), + sketch.getCode(jp.getTabIndex()).getPrettyName(), Integer.toString(p.getLineNumber() + 1)); // Added +1 because lineNumbers internally are 0-indexed } } - public void highlight(Problem p) { - if (p != null) { - highlight(p.getTabIndex(), p.getStartOffset(), p.getStartOffset()); - } - } - - - public void highlight(int tabIndex, int startOffset, int stopOffset) { - // Switch to tab - toFront(); - sketch.setCurrentCode(tabIndex); - - // Make sure offsets are in bounds - int length = textarea.getDocumentLength(); - startOffset = PApplet.constrain(startOffset, 0, length); - stopOffset = PApplet.constrain(stopOffset, 0, length); - - // Highlight the code - textarea.select(startOffset, stopOffset); - - // Scroll to error line - textarea.scrollToCaret(); - repaint(); - } - - - public List getProblems() { - return problems; - } - - - /** - * Updates editor status bar, depending on whether the caret is on an error - * line or not - */ - public void updateEditorStatus() { - Problem problem = findProblem(textarea.getCaretLine()); - if (problem != null) { - int type = problem.isError() ? - EditorStatus.CURSOR_LINE_ERROR : EditorStatus.CURSOR_LINE_WARNING; - statusMessage(problem.getMessage(), type); - } else { - switch (getStatusMode()) { - case EditorStatus.CURSOR_LINE_ERROR: - case EditorStatus.CURSOR_LINE_WARNING: - statusEmpty(); - break; - } - } - } - - - /** - * @return the Problem for the first error or warning on 'line' - */ - public Problem findProblem(int line) { - JavaTextArea textArea = getJavaTextArea(); - int currentTab = getSketch().getCurrentCodeIndex(); - return problems.stream() - .filter(p -> p.getTabIndex() == currentTab) - .filter(p -> { - int pStartLine = p.getLineNumber(); - int pEndOffset = p.getStopOffset(); - int pEndLine = textArea.getLineOfOffset(pEndOffset); - return line >= pStartLine && line <= pEndLine; - }) - .findFirst() - .orElse(null); - } - - - public List findProblems(int line) { - JavaTextArea textArea = getJavaTextArea(); - int currentTab = getSketch().getCurrentCodeIndex(); - return problems.stream() - .filter(p -> p.getTabIndex() == currentTab) - .filter(p -> { - int pStartLine = p.getLineNumber(); - int pEndOffset = p.getStopOffset(); - int pEndLine = textArea.getLineOfOffset(pEndOffset); - return line >= pStartLine && line <= pEndLine; - }) - .collect(Collectors.toList()); - } - - - /* - public void clearErrorPoints() { - List errorPoints = getErrorPoints(); - synchronized (errorPoints) { // necessary? - errorPoints.clear(); - } - } - */ - - - public void repaintErrorBar() { - errorColumn.repaint(); - } - - - public void showConsole() { - footer.setPanel(console); - } - - -// /** Toggle between Console and Errors List */ -// public void showProblemListView(String buttonName) { -//// CardLayout cl = (CardLayout) consoleProblemsPane.getLayout(); -//// cl.show(consoleProblemsPane, buttonName); -//// ((JTabbedPane) consolePanel).setSelectedIndex(ERROR_TAB_INDEX); -// footer.setPanel(errorTableScrollPane); -// } - - - public void errorTableClick(Object item) { - highlight((Problem) item); - } - - + @Override public void errorTableDoubleClick(Object item) { - Problem p = (Problem) item; + JavaProblem p = (JavaProblem) item; // MouseEvent evt = null; String[] suggs = p.getImportSuggestions(); @@ -2620,28 +2490,6 @@ public void windowGainedFocus(WindowEvent e) { } -// /** Updates the error table */ -// synchronized public boolean updateTable(final TableModel tableModel) { -// return errorTable.updateTable(tableModel); -// } - - - /** - * Handle whether the tiny red error indicator is shown near - * the error button at the bottom of the PDE - */ - public void updateErrorToggle(boolean hasErrors) { - footer.setNotification(errorTable.getParent(), hasErrors); -// String title = Language.text("editor.footer.errors"); -// if (hasErrors) { -// title += "*"; -// } -// ((JTabbedPane) footer).setTitleAt(ERROR_TAB_INDEX, title); -//// btnShowErrors.updateMarker(hasErrors, -//// errorBar.errorColor); - } - - public boolean hasJavaTabs() { return hasJavaTabs; } diff --git a/java/src/processing/mode/java/MarkerColumn.java b/java/src/processing/mode/java/MarkerColumn.java index 1e29c75b6a..2de540b7e8 100644 --- a/java/src/processing/mode/java/MarkerColumn.java +++ b/java/src/processing/mode/java/MarkerColumn.java @@ -34,11 +34,12 @@ import javax.swing.JPanel; import processing.app.Mode; +import processing.app.Problem; import processing.app.Sketch; import processing.app.SketchCode; import processing.app.ui.Editor; import processing.core.PApplet; -import processing.mode.java.pdex.Problem; +import processing.mode.java.pdex.JavaProblem; /** @@ -51,7 +52,7 @@ * which displays the overall errors in a document */ public class MarkerColumn extends JPanel { - protected JavaEditor editor; + protected Editor editor; // static final int WIDE = 12; @@ -92,7 +93,7 @@ public void repaint() { @Override public void paintComponent(Graphics g) { - g.drawImage(editor.getJavaTextArea().getGutterGradient(), + g.drawImage(editor.getPdeTextArea().getGutterGradient(), 0, 0, getWidth(), getHeight(), this); int currentTabIndex = editor.getSketch().getCurrentCodeIndex(); @@ -110,7 +111,7 @@ public void paintComponent(Graphics g) { } - public void updateErrorPoints(final List problems) { + public void updateErrorPoints(final List problems) { errorPoints = problems.stream() .map(LineMarker::new) .collect(Collectors.toList()); @@ -131,24 +132,12 @@ private void scrollToMarkerAt(final int y) { } - /* - @Override - public JToolTip createToolTip() { - return new ErrorToolTip(editor.getMode(), this); - } - */ - - /** Show tooltip on hover. */ private void showMarkerHover(final int y) { try { LineMarker m = findClosestMarker(y); if (m != null) { Problem p = m.problem; -// String kind = p.isError() ? -// Language.text("editor.status.error") : -// Language.text("editor.status.warning"); -// setToolTipText(kind + ": " + p.getMessage()); editor.statusToolTip(MarkerColumn.this, p.getMessage(), p.isError()); setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } diff --git a/java/src/processing/mode/java/pdex/CompletionCandidate.java b/java/src/processing/mode/java/pdex/CompletionCandidate.java index a8e6387458..6b355db48c 100644 --- a/java/src/processing/mode/java/pdex/CompletionCandidate.java +++ b/java/src/processing/mode/java/pdex/CompletionCandidate.java @@ -32,7 +32,7 @@ import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -public class CompletionCandidate implements Comparable{ +public class CompletionCandidate implements Comparable { private final String elementName; private final String label; // the toString value private final String completionString; diff --git a/java/src/processing/mode/java/pdex/ErrorMessageSimplifier.java b/java/src/processing/mode/java/pdex/ErrorMessageSimplifier.java index 8061708986..df7909e8ee 100644 --- a/java/src/processing/mode/java/pdex/ErrorMessageSimplifier.java +++ b/java/src/processing/mode/java/pdex/ErrorMessageSimplifier.java @@ -85,7 +85,7 @@ public static String getIDName(int id) { /** * Tones down the jargon in the ecj reported errors. */ - public static String getSimplifiedErrorMessage(Problem problem) { + public static String getSimplifiedErrorMessage(JavaProblem problem) { if (problem == null) return null; IProblem iprob = problem.getIProblem(); diff --git a/java/src/processing/mode/java/pdex/Problem.java b/java/src/processing/mode/java/pdex/JavaProblem.java similarity index 97% rename from java/src/processing/mode/java/pdex/Problem.java rename to java/src/processing/mode/java/pdex/JavaProblem.java index 8d8fac2e00..bb4da88987 100644 --- a/java/src/processing/mode/java/pdex/Problem.java +++ b/java/src/processing/mode/java/pdex/JavaProblem.java @@ -25,14 +25,14 @@ import org.eclipse.jdt.core.compiler.IProblem; -import processing.app.ui.ErrorTable; +import processing.app.Problem; /** * Wrapper class for IProblem that stores the tabIndex and line number * according to its tab, including the original IProblem object */ -public class Problem implements ErrorTable.Entry { +public class JavaProblem implements Problem { /** * The IProblem which is being wrapped */ @@ -73,7 +73,7 @@ public class Problem implements ErrorTable.Entry { * @param tabIndex - The tab number to which the error belongs to * @param lineNumber - Line number(pde code) of the error */ - public Problem(IProblem iProblem, int tabIndex, int lineNumber) { + public JavaProblem(IProblem iProblem, int tabIndex, int lineNumber) { this.iProblem = iProblem; if(iProblem.isError()) { type = ERROR; diff --git a/java/src/processing/mode/java/pdex/JavaTextArea.java b/java/src/processing/mode/java/pdex/JavaTextArea.java index 9eb57fb99e..71bdd08acc 100644 --- a/java/src/processing/mode/java/pdex/JavaTextArea.java +++ b/java/src/processing/mode/java/pdex/JavaTextArea.java @@ -20,30 +20,29 @@ package processing.mode.java.pdex; -import processing.mode.java.JavaInputHandler; -import processing.mode.java.JavaMode; -import processing.mode.java.JavaEditor; -import processing.mode.java.tweak.ColorControlBox; -import processing.mode.java.tweak.Handle; - -import java.awt.*; -import java.awt.event.*; +import java.awt.EventQueue; +import java.awt.Point; +import java.awt.event.ComponentListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; import java.util.BitSet; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.swing.DefaultListModel; import javax.swing.SwingWorker; import processing.app.Messages; -import processing.app.Mode; import processing.app.Platform; -import processing.app.syntax.JEditTextArea; import processing.app.syntax.PdeTextArea; import processing.app.syntax.TextAreaDefaults; -import processing.app.ui.Editor; +import processing.mode.java.JavaEditor; +import processing.mode.java.JavaInputHandler; +import processing.mode.java.JavaMode; +import processing.mode.java.tweak.ColorControlBox; +import processing.mode.java.tweak.Handle; public class JavaTextArea extends PdeTextArea { @@ -55,24 +54,24 @@ public class JavaTextArea extends PdeTextArea { public JavaTextArea(TextAreaDefaults defaults, JavaEditor editor) { super(defaults, new JavaInputHandler(editor), editor); - // TweakMode code - prevCompListeners = painter.getComponentListeners(); - prevMouseListeners = painter.getMouseListeners(); - prevMMotionListeners = painter.getMouseMotionListeners(); - prevKeyListeners = editor.getKeyListeners(); - suggestionGenerator = new CompletionGenerator(); tweakMode = false; } + public JavaEditor getJavaEditor() { + return (JavaEditor) editor; + } + + @Override protected JavaTextAreaPainter createPainter(final TextAreaDefaults defaults) { return new JavaTextAreaPainter(this, defaults); } + // used by Tweak Mode protected JavaTextAreaPainter getJavaPainter() { return (JavaTextAreaPainter) painter; } @@ -142,7 +141,7 @@ public void processKeyEvent(KeyEvent evt) { super.processKeyEvent(evt); // code completion disabled if Java tabs present - if (!editor.hasJavaTabs()) { + if (!getJavaEditor().hasJavaTabs()) { if (evt.getID() == KeyEvent.KEY_TYPED) { processCompletionKeys(evt); } else if (!Platform.isMacOS() && evt.getID() == KeyEvent.KEY_RELEASED) { @@ -285,10 +284,11 @@ protected void fetchPhrase() { } // Adjust line number for tabbed sketches - int codeIndex = editor.getSketch().getCodeIndex(editor.getCurrentTab()); + //int codeIndex = editor.getSketch().getCodeIndex(getJavaEditor().getCurrentTab()); + int codeIndex = editor.getSketch().getCurrentCodeIndex(); int lineStartOffset = editor.getTextArea().getLineStartOffset(caretLineIndex); - editor.getPreprocessingService().whenDone(ps -> { + getJavaEditor().getPreprocessingService().whenDone(ps -> { int lineNumber = ps.tabOffsetToJavaLine(codeIndex, lineStartOffset); String phrase = null; @@ -525,30 +525,29 @@ public void run() { /** - * Calculates location of caret and displays the suggestion popup. + * Calculates location of caret and displays the suggestion pop-up. */ protected void showSuggestion(DefaultListModel listModel, String subWord) { + // TODO can this be ListModel instead? why is size() in DefaultListModel + // different from getSize() in ListModel (or are they, really?) hideSuggestion(); - if (listModel.size() == 0) { - Messages.log("TextArea: No suggestions to show."); - - } else { + if (listModel.size() != 0) { int position = getCaretPosition(); - Point location = new Point(); try { - location.x = offsetToX(getCaretLine(), - position - getLineStartOffset(getCaretLine())); - location.y = lineToY(getCaretLine()) + getPainter().getLineHeight(); - //log("TA position: " + location); - } catch (Exception e2) { - e2.printStackTrace(); - return; - } + Point location = + new Point(offsetToX(getCaretLine(), + position - getLineStartOffset(getCaretLine())), + lineToY(getCaretLine()) + getPainter().getLineHeight()); + suggestion = new CompletionPanel(this, position, subWord, + listModel, location, getJavaEditor()); + requestFocusInWindow(); - suggestion = new CompletionPanel(this, position, subWord, - listModel, location, editor); - requestFocusInWindow(); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + Messages.log("TextArea: No suggestions to show."); } } @@ -569,15 +568,23 @@ public void hideSuggestion() { // save input listeners to stop/start text edit - protected final ComponentListener[] prevCompListeners; - protected final MouseListener[] prevMouseListeners; - protected final MouseMotionListener[] prevMMotionListeners; - protected final KeyListener[] prevKeyListeners; + protected ComponentListener[] baseCompListeners; + protected MouseListener[] baseMouseListeners; + protected MouseMotionListener[] baseMotionListeners; + protected KeyListener[] baseKeyListeners; protected boolean tweakMode; /* remove all standard interaction listeners */ - public void removeAllListeners() { + public void tweakRemoveListeners() { + if (baseCompListeners == null) { + // First time in tweak mode, grab the default listeners. Moved from the + // constructor since not all listeners may have been added at that point. + baseCompListeners = painter.getComponentListeners(); + baseMouseListeners = painter.getMouseListeners(); + baseMotionListeners = painter.getMouseMotionListeners(); + baseKeyListeners = editor.getKeyListeners(); + } ComponentListener[] componentListeners = painter.getComponentListeners(); MouseListener[] mouseListeners = painter.getMouseListeners(); MouseMotionListener[] mouseMotionListeners = painter.getMouseMotionListeners(); @@ -601,7 +608,7 @@ public void removeAllListeners() { public void startTweakMode() { // ignore if we are already in interactiveMode if (!tweakMode) { - removeAllListeners(); + tweakRemoveListeners(); getJavaPainter().startTweakMode(); this.editable = false; this.caretBlinks = false; @@ -614,8 +621,8 @@ public void startTweakMode() { public void stopTweakMode() { // ignore if we are not in interactive mode if (tweakMode) { - removeAllListeners(); - addPrevListeners(); + tweakRemoveListeners(); + tweakRestoreBaseListeners(); getJavaPainter().stopTweakMode(); editable = true; caretBlinks = true; @@ -625,18 +632,18 @@ public void stopTweakMode() { } - private void addPrevListeners() { + private void tweakRestoreBaseListeners() { // add the original text-edit listeners - for (ComponentListener cl : prevCompListeners) { + for (ComponentListener cl : baseCompListeners) { painter.addComponentListener(cl); } - for (MouseListener ml : prevMouseListeners) { + for (MouseListener ml : baseMouseListeners) { painter.addMouseListener(ml); } - for (MouseMotionListener mml : prevMMotionListeners) { + for (MouseMotionListener mml : baseMotionListeners) { painter.addMouseMotionListener(mml); } - for (KeyListener kl : prevKeyListeners) { + for (KeyListener kl : baseKeyListeners) { editor.addKeyListener(kl); } } diff --git a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java index d9724e3b78..51164a8a3d 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -23,7 +23,6 @@ import processing.mode.java.JavaEditor; import processing.mode.java.tweak.*; -import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; @@ -44,7 +43,7 @@ import javax.swing.text.Segment; import javax.swing.text.Utilities; -import processing.app.Mode; +import processing.app.Problem; import processing.app.SketchCode; import processing.app.syntax.PdeTextAreaPainter; import processing.app.syntax.SyntaxDocument; @@ -228,16 +227,9 @@ static private String trimRight(String str) { /** * Paints the underline for an error/warning line - * - * @param gfx - * the graphics context - * @param tokenMarker - * @param line - * 0-based line number: NOTE - * @param x */ protected void paintErrorLine(Graphics gfx, int line, int x) { - List problems = getJavaEditor().findProblems(line); + List problems = getEditor().findProblems(line); for (Problem problem : problems) { int startOffset = problem.getStartOffset(); int stopOffset = problem.getStopOffset(); @@ -314,7 +306,7 @@ static private void paintSquiggle(Graphics g, int y, int x1, int x2) { public String getToolTipText(MouseEvent evt) { int line = evt.getY() / getFontMetrics().getHeight() + textArea.getFirstLine(); if (line >= 0 || line < textArea.getLineCount()) { - List problems = getJavaEditor().findProblems(line); + List problems = getEditor().findProblems(line); for (Problem problem : problems) { int lineStart = textArea.getLineStartOffset(line); int lineEnd = textArea.getLineStopOffset(line); diff --git a/java/src/processing/mode/java/pdex/PDEX.java b/java/src/processing/mode/java/pdex/PDEX.java index b2ebd32c82..6182faed36 100644 --- a/java/src/processing/mode/java/pdex/PDEX.java +++ b/java/src/processing/mode/java/pdex/PDEX.java @@ -72,6 +72,7 @@ import processing.app.Language; import processing.app.Messages; import processing.app.Platform; +import processing.app.Problem; import processing.app.Sketch; import processing.app.SketchCode; import processing.app.syntax.SyntaxDocument; @@ -1092,7 +1093,7 @@ private void handleSketchProblems(PreprocessedSketch ps) { SketchInterval in = ps.mapJavaToSketch(start, stop); if (in == SketchInterval.BEFORE_START) return null; int line = ps.tabOffsetToTabLine(in.tabIndex, in.startTabOffset); - Problem p = new Problem(iproblem, in.tabIndex, line); + JavaProblem p = new JavaProblem(iproblem, in.tabIndex, line); p.setPDEOffsets(in.startTabOffset, in.stopTabOffset); return p; }) @@ -1104,13 +1105,13 @@ private void handleSketchProblems(PreprocessedSketch ps) { Map> undefinedTypeProblems = problems.stream() // Get only problems with undefined types/names .filter(p -> { - int id = p.getIProblem().getID(); + int id = ((JavaProblem) p).getIProblem().getID(); return id == IProblem.UndefinedType || id == IProblem.UndefinedName || id == IProblem.UnresolvedVariable; }) // Group problems by the missing type/name - .collect(Collectors.groupingBy(p -> p.getIProblem().getArguments()[0])); + .collect(Collectors.groupingBy(p -> ((JavaProblem) p).getIProblem().getArguments()[0])); if (!undefinedTypeProblems.isEmpty()) { final ClassPath cp = ps.searchClassPath; @@ -1121,7 +1122,7 @@ private void handleSketchProblems(PreprocessedSketch ps) { String missingClass = entry.getKey(); List affectedProblems = entry.getValue(); String[] suggestions = getImportSuggestions(cp, missingClass); - affectedProblems.forEach(p -> p.setImportSuggestions(suggestions)); + affectedProblems.forEach(p -> ((JavaProblem) p).setImportSuggestions(suggestions)); }); } } From 5180fe7eb1900d7d115463668b9d5811e3664708 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 6 Aug 2016 12:36:09 -0400 Subject: [PATCH 0060/1035] move MarkerColumn into processing.app.ui --- app/src/processing/app/ui/Editor.java | 15 ++++++++++----- .../src/processing/app/ui}/MarkerColumn.java | 10 +++++----- java/src/processing/mode/java/JavaEditor.java | 2 -- 3 files changed, 15 insertions(+), 12 deletions(-) rename {java/src/processing/mode/java => app/src/processing/app/ui}/MarkerColumn.java (95%) diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 1204690014..73ce7d46dd 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -39,10 +39,6 @@ import processing.app.contrib.ContributionManager; import processing.app.syntax.*; import processing.core.*; -import processing.mode.java.JavaMode; -import processing.mode.java.MarkerColumn; -import processing.mode.java.pdex.JavaProblem; -import processing.mode.java.pdex.JavaTextArea; import java.awt.BorderLayout; import java.awt.Color; @@ -86,7 +82,7 @@ public abstract class Editor extends JFrame implements RunnerListener { static public final int RIGHT_GUTTER = 12; static public final int GUTTER_MARGIN = 3; - private MarkerColumn errorColumn; + protected MarkerColumn errorColumn; // Otherwise, if the window is resized with the message label // set to blank, its preferredSize() will be fuckered @@ -2934,6 +2930,15 @@ public void statusEmpty() { } + public void statusMessage(String message, int type) { + if (EventQueue.isDispatchThread()) { + status.message(message, type); + } else { + EventQueue.invokeLater(() -> statusMessage(message, type)); + } + } + + public void startIndeterminate() { status.startIndeterminate(); } diff --git a/java/src/processing/mode/java/MarkerColumn.java b/app/src/processing/app/ui/MarkerColumn.java similarity index 95% rename from java/src/processing/mode/java/MarkerColumn.java rename to app/src/processing/app/ui/MarkerColumn.java index 2de540b7e8..c93a379be0 100644 --- a/java/src/processing/mode/java/MarkerColumn.java +++ b/app/src/processing/app/ui/MarkerColumn.java @@ -2,7 +2,7 @@ /* Part of the Processing project - http://processing.org -Copyright (c) 2012-15 The Processing Foundation +Copyright (c) 2012-16 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -18,7 +18,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package processing.mode.java; +package processing.app.ui; import java.awt.Color; import java.awt.Cursor; @@ -39,7 +39,6 @@ import processing.app.SketchCode; import processing.app.ui.Editor; import processing.core.PApplet; -import processing.mode.java.pdex.JavaProblem; /** @@ -63,7 +62,7 @@ public class MarkerColumn extends JPanel { private List errorPoints = new ArrayList(); - public MarkerColumn(JavaEditor editor, int height) { + public MarkerColumn(Editor editor, int height) { this.editor = editor; Mode mode = editor.getMode(); @@ -111,7 +110,7 @@ public void paintComponent(Graphics g) { } - public void updateErrorPoints(final List problems) { + public void updateErrorPoints(final List problems) { errorPoints = problems.stream() .map(LineMarker::new) .collect(Collectors.toList()); @@ -197,6 +196,7 @@ public Dimension getMinimumSize() { return new Dimension(Editor.RIGHT_GUTTER, super.getMinimumSize().height); } + /** * Line markers displayed on the Error Column. */ diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 74a08fb1bc..12ab253ea4 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -63,8 +63,6 @@ public class JavaEditor extends Editor { protected VariableInspector inspector; protected JMenuItem inspectorItem; - private MarkerColumn errorColumn; - static final int ERROR_TAB_INDEX = 0; private boolean hasJavaTabs; From 3eb5ac17892464cb83dd1f247f9bfbe32badcafb Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 6 Aug 2016 12:40:55 -0400 Subject: [PATCH 0061/1035] removing some dead code, other cleaning --- java/src/processing/mode/java/JavaEditor.java | 76 +------------------ 1 file changed, 4 insertions(+), 72 deletions(-) diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 12ab253ea4..3504c24855 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -2210,7 +2210,6 @@ public boolean isInCurrentTab(LineID line) { */ @Override public void setCode(SketchCode code) { - Document oldDoc = code.getDocument(); //System.out.println("tab switch: " + code.getFileName()); @@ -2254,14 +2253,13 @@ public void setCode(SketchCode code) { /** * Get a tab by its file name. - * @param fileName the filename to search for. - * @return the {@link SketchCode} object representing the tab, or null if - * not found + * @param filename the filename to search for. + * @return the {@link SketchCode} object for the tab, or null if not found */ - public SketchCode getTab(String fileName) { + public SketchCode getTab(String filename) { Sketch s = getSketch(); for (SketchCode c : s.getCode()) { - if (c.getFileName().equals(fileName)) { + if (c.getFileName().equals(filename)) { return c; } } @@ -2297,72 +2295,6 @@ public void statusHalted() { } - public void statusMessage(String message, int type) { - if (EventQueue.isDispatchThread()) { - status.message(message, type); - } else { - EventQueue.invokeLater(() -> statusMessage(message, type)); - } - } - - - /* - static final int STATUS_EMPTY = 100; - static final int STATUS_COMPILER_ERR = 200; - static final int STATUS_WARNING = 300; - static final int STATUS_INFO = 400; - static final int STATUS_ERR = 500; - - int statusMessageType = STATUS_EMPTY; - String statusMessage; - - public void statusMessage(final String what, int type){ - // Don't re-display the old message again - if (type != STATUS_EMPTY) { - if (what.equals(statusMessage) && type == statusMessageType) { - return; - } - } - statusMessage = new String(what); - statusMessageType = type; - switch (type) { - case STATUS_COMPILER_ERR: - case STATUS_ERR: - super.statusError(what); - break; - case STATUS_INFO: - case STATUS_WARNING: - statusNotice(what); - break; - } - // Don't need to clear compiler error messages - if (type == STATUS_COMPILER_ERR) return; - - // Clear the message after a delay - SwingWorker s = new SwingWorker() { - @Override - protected Object doInBackground() throws Exception { - try { - Thread.sleep(2 * 1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - statusEmpty(); - return null; - } - }; - s.execute(); - } - - - public void statusEmpty(){ - statusMessage = null; - statusMessageType = STATUS_EMPTY; - super.statusEmpty(); - } - */ - - /** * Updates the error table in the Error Window. * Overridden to handle the fugly import suggestions text. From 191398790c0cba8e64a7f7f1aa5e5ab686f13389 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 6 Aug 2016 13:21:52 -0400 Subject: [PATCH 0062/1035] more function moving --- .../processing/app/syntax/PdeTextArea.java | 8 +- .../app/syntax/PdeTextAreaPainter.java | 250 ++++++++++++++- .../mode/java/pdex/JavaTextArea.java | 28 +- .../mode/java/pdex/JavaTextAreaPainter.java | 297 +----------------- 4 files changed, 275 insertions(+), 308 deletions(-) diff --git a/app/src/processing/app/syntax/PdeTextArea.java b/app/src/processing/app/syntax/PdeTextArea.java index 02ef7c3f05..43b403107e 100644 --- a/app/src/processing/app/syntax/PdeTextArea.java +++ b/app/src/processing/app/syntax/PdeTextArea.java @@ -66,7 +66,13 @@ public PdeTextArea(TextAreaDefaults defaults, InputHandler inputHandler, } - public Image getGutterGradient() { + @Override + protected TextAreaPainter createPainter(final TextAreaDefaults defaults) { + return new PdeTextAreaPainter(this, defaults); + } + + + public Image getGutterGradient() { return gutterGradient; } diff --git a/app/src/processing/app/syntax/PdeTextAreaPainter.java b/app/src/processing/app/syntax/PdeTextAreaPainter.java index 7547fb4504..757d6f977d 100644 --- a/app/src/processing/app/syntax/PdeTextAreaPainter.java +++ b/app/src/processing/app/syntax/PdeTextAreaPainter.java @@ -22,13 +22,26 @@ import java.awt.Color; import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.geom.GeneralPath; +import java.util.List; + +import javax.swing.text.BadLocationException; +import javax.swing.text.Segment; +import javax.swing.text.Utilities; import processing.app.Mode; +import processing.app.Problem; import processing.app.ui.Editor; +/** + * Adds support to TextAreaPainter for background colors, + * and the left hand gutter area with background color and text. + */ public class PdeTextAreaPainter extends TextAreaPainter { public Color errorUnderlineColor; public Color warningUnderlineColor; @@ -87,14 +100,247 @@ public void setMode(Mode mode) { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + /** + * Paint a line. Paints the gutter (with background color and text) then the + * line (background color and text). + * + * @param gfx the graphics context + * @param tokenMarker + * @param line 0-based line number + * @param x horizontal position + */ + @Override + protected void paintLine(Graphics gfx, int line, int x, TokenMarker marker) { + try { + // TODO This line is causing NPEs randomly ever since I added the + // toggle for Java Mode/Debugger toolbar. [Manindra] + super.paintLine(gfx, line, x + Editor.LEFT_GUTTER, marker); + + } catch (Exception e) { + e.printStackTrace(); + } + + paintLeftGutter(gfx, line, x); + paintErrorLine(gfx, line, x); + } + + + /** + * Paints the underline for an error/warning line + */ + protected void paintErrorLine(Graphics gfx, int line, int x) { + List problems = getEditor().findProblems(line); + for (Problem problem : problems) { + int startOffset = problem.getStartOffset(); + int stopOffset = problem.getStopOffset(); + + int lineOffset = textArea.getLineStartOffset(line); + + int wiggleStart = Math.max(startOffset, lineOffset); + int wiggleStop = Math.min(stopOffset, textArea.getLineStopOffset(line)); + + int y = textArea.lineToY(line) + fm.getLeading() + fm.getMaxDescent(); + + try { + String badCode = null; + String goodCode = null; + try { + SyntaxDocument doc = textArea.getDocument(); + badCode = doc.getText(wiggleStart, wiggleStop - wiggleStart); + goodCode = doc.getText(lineOffset, wiggleStart - lineOffset); + //log("paintErrorLine() LineText GC: " + goodCode); + //log("paintErrorLine() LineText BC: " + badCode); + } catch (BadLocationException bl) { + // Error in the import statements or end of code. + // System.out.print("BL caught. " + ta.getLineCount() + " ," + // + line + " ,"); + // log((ta.getLineStopOffset(line) - start - 1)); + return; + } + + int trimmedLength = badCode.trim().length(); + int rightTrimmedLength = trimRight(badCode).length(); + int leftTrimLength = rightTrimmedLength - trimmedLength; + + // Fix offsets when bad code is just whitespace + if (trimmedLength == 0) { + leftTrimLength = 0; + rightTrimmedLength = badCode.length(); + } + + int x1 = textArea.offsetToX(line, goodCode.length() + leftTrimLength); + int x2 = textArea.offsetToX(line, goodCode.length() + rightTrimmedLength); + if (x1 == x2) x2 += fm.stringWidth(" "); + int y1 = y + fm.getHeight() - 2; + + if (line != problem.getLineNumber()) { + x1 = Editor.LEFT_GUTTER; // on the following lines, wiggle extends to the left border + } + + gfx.setColor(errorUnderlineColor); + if (problem.isWarning()) { + gfx.setColor(warningUnderlineColor); + } + paintSquiggle(gfx, y1, x1, x2); + + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + + /** + * Paint the gutter: draw the background, draw line numbers, break points. + * @param gfx the graphics context + * @param line 0-based line number + * @param x horizontal position + */ + protected void paintLeftGutter(Graphics gfx, int line, int x) { + int y = textArea.lineToY(line) + fm.getLeading() + fm.getMaxDescent(); + if (line == textArea.getSelectionStopLine()) { + gfx.setColor(gutterLineHighlightColor); + gfx.fillRect(0, y, Editor.LEFT_GUTTER, fm.getHeight()); + } else { + //gfx.setColor(getJavaTextArea().gutterBgColor); + gfx.setClip(0, y, Editor.LEFT_GUTTER, fm.getHeight()); + gfx.drawImage(((PdeTextArea) textArea).getGutterGradient(), 0, 0, getWidth(), getHeight(), this); + gfx.setClip(null); // reset + } + + String text = null; + if (getEditor().isDebuggerEnabled()) { + text = getPdeTextArea().getGutterText(line); + } + + gfx.setColor(line < textArea.getLineCount() ? gutterTextColor : gutterPastColor); +// if (line >= textArea.getLineCount()) { +// //gfx.setColor(new Color(gutterTextColor.getRGB(), ); +// } + int textRight = Editor.LEFT_GUTTER - Editor.GUTTER_MARGIN; + int textBaseline = textArea.lineToY(line) + fm.getHeight(); + + if (text != null) { + if (text.equals(PdeTextArea.BREAK_MARKER)) { + drawDiamond(gfx, textRight - 8, textBaseline - 8, 8, 8); + + } else if (text.equals(PdeTextArea.STEP_MARKER)) { + //drawRightArrow(gfx, textRight - 7, textBaseline - 7, 7, 6); + drawRightArrow(gfx, textRight - 7, textBaseline - 7.5f, 7, 7); + } + } else { + // if no special text for a breakpoint, just show the line number + text = String.valueOf(line + 1); + //text = makeOSF(String.valueOf(line + 1)); + + gfx.setFont(gutterTextFont); +// ((Graphics2D) gfx).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, +// RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + // Right-align the text + char[] txt = text.toCharArray(); + int tx = textRight - gfx.getFontMetrics().charsWidth(txt, 0, txt.length); + // Using 'fm' here because it's relative to the editor text size, + // not the numbers in the gutter + Utilities.drawTabbedText(new Segment(txt, 0, text.length()), + tx, textBaseline, gfx, this, 0); + } + } + + + static private void drawDiamond(Graphics g, + float x, float y, float w, float h) { + Graphics2D g2 = (Graphics2D) g; + GeneralPath path = new GeneralPath(); + path.moveTo(x + w/2, y); + path.lineTo(x + w, y + h/2); + path.lineTo(x + w/2, y + h); + path.lineTo(x, y + h/2); + path.closePath(); + g2.fill(path); + } + + + static private void drawRightArrow(Graphics g, + float x, float y, float w, float h) { + Graphics2D g2 = (Graphics2D) g; + GeneralPath path = new GeneralPath(); + path.moveTo(x, y); + path.lineTo(x + w, y + h/2); + path.lineTo(x, y + h); + path.closePath(); + g2.fill(path); + } + + + /** + * Remove all trailing whitespace from a line + */ + static private String trimRight(String str) { + int i = str.length() - 1; + while (i >= 0 && Character.isWhitespace(str.charAt(i))) { + i--; + } + return str.substring(0, i+1); + } + + + static private void paintSquiggle(Graphics g, int y, int x1, int x2) { + int xx = x1; + + while (xx < x2) { + g.drawLine(xx, y, xx + 2, y + 1); + xx += 2; + g.drawLine(xx, y + 1, xx + 2, y); + xx += 2; + } + } + + + @Override + public String getToolTipText(MouseEvent event) { + int line = event.getY() / getFontMetrics().getHeight() + textArea.getFirstLine(); + if (line >= 0 || line < textArea.getLineCount()) { + List problems = getEditor().findProblems(line); + for (Problem problem : problems) { + int lineStart = textArea.getLineStartOffset(line); + int lineEnd = textArea.getLineStopOffset(line); + + int errorStart = problem.getStartOffset(); + int errorEnd = problem.getStopOffset() + 1; + + int startOffset = Math.max(errorStart, lineStart) - lineStart; + int stopOffset = Math.min(errorEnd, lineEnd) - lineStart; + + int x = event.getX(); + + if (x >= textArea.offsetToX(line, startOffset) && + x <= textArea.offsetToX(line, stopOffset)) { + getEditor().statusToolTip(this, problem.getMessage(), problem.isError()); + return super.getToolTipText(event); + } + } + } + setToolTipText(null); + return super.getToolTipText(event); + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + @Override public int getScrollWidth() { - // https://github.com/processing/processing/issues/3591 + // TODO https://github.com/processing/processing/issues/3591 return super.getWidth() - Editor.LEFT_GUTTER; } public Editor getEditor() { - return ((PdeTextArea) textArea).editor; + return getPdeTextArea().editor; + } + + + public PdeTextArea getPdeTextArea() { + return (PdeTextArea) textArea; } } \ No newline at end of file diff --git a/java/src/processing/mode/java/pdex/JavaTextArea.java b/java/src/processing/mode/java/pdex/JavaTextArea.java index 71bdd08acc..3a1bcd4152 100644 --- a/java/src/processing/mode/java/pdex/JavaTextArea.java +++ b/java/src/processing/mode/java/pdex/JavaTextArea.java @@ -45,9 +45,11 @@ import processing.mode.java.tweak.Handle; +/** + * TextArea implementation for Java Mode. Primary differences from PdeTextArea + * are completions, suggestions, and tweak handling. + */ public class JavaTextArea extends PdeTextArea { - //protected final JavaEditor editor; - private CompletionPanel suggestion; @@ -79,6 +81,8 @@ protected JavaTextAreaPainter getJavaPainter() { /** * Handles KeyEvents for TextArea (code completion begins from here). + * TODO Needs explanation of why this implemented with an override + * of processKeyEvent() instead of using listeners. */ @Override public void processKeyEvent(KeyEvent evt) { @@ -234,7 +238,6 @@ private void prepareSuggestions(final KeyEvent evt) { * @param evt - the KeyEvent which triggered this method */ protected void fetchPhrase() { - if (suggestionRunning) { suggestionRequested = true; return; @@ -345,8 +348,8 @@ protected void fetchPhrase() { }); } - protected static String parsePhrase(final String lineText) { + protected static String parsePhrase(final String lineText) { boolean overloading = false; { // Check if we can provide suggestions for this phrase ending @@ -505,25 +508,10 @@ protected static String parsePhrase(final String lineText) { if (phrase.length() == 0 || Character.isDigit(phrase.charAt(0))) { return null; // Can't suggest for numbers or empty phrases } - return phrase; } - // appears unused, removed when looking to change completion trigger [fry 140801] - /* - public void showSuggestionLater(final DefaultListModel defListModel, final String word) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - showSuggestion(defListModel,word); - } - - }); - } - */ - - /** * Calculates location of caret and displays the suggestion pop-up. */ @@ -651,6 +639,6 @@ private void tweakRestoreBaseListeners() { public void updateInterface(List> handles, List> colorBoxes) { - getJavaPainter().updateInterface(handles, colorBoxes); + getJavaPainter().updateTweakInterface(handles, colorBoxes); } } diff --git a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java index 51164a8a3d..7859c1e731 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -20,9 +20,6 @@ package processing.mode.java.pdex; -import processing.mode.java.JavaEditor; -import processing.mode.java.tweak.*; - import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; @@ -35,26 +32,21 @@ import java.awt.event.MouseMotionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.awt.geom.GeneralPath; import java.awt.image.BufferedImage; import java.util.List; -import javax.swing.text.BadLocationException; -import javax.swing.text.Segment; -import javax.swing.text.Utilities; - -import processing.app.Problem; import processing.app.SketchCode; import processing.app.syntax.PdeTextAreaPainter; -import processing.app.syntax.SyntaxDocument; import processing.app.syntax.TextAreaDefaults; -import processing.app.syntax.TokenMarker; -import processing.app.ui.Editor; +import processing.mode.java.JavaEditor; +import processing.mode.java.tweak.ColorControlBox; +import processing.mode.java.tweak.ColorSelector; +import processing.mode.java.tweak.Handle; +import processing.mode.java.tweak.Settings; /** - * Customized line painter. Adds support for background colors, - * left hand gutter area with background color and text. + * Customized line painter to handle tweak mode features. */ public class JavaTextAreaPainter extends PdeTextAreaPainter { @@ -66,272 +58,12 @@ public JavaTextAreaPainter(final JavaTextArea textArea, TextAreaDefaults default cursorType = Cursor.DEFAULT_CURSOR; } - /** - * Paint a line. Paints the gutter (with background color and text) then the - * line (background color and text). - * - * @param gfx the graphics context - * @param tokenMarker - * @param line 0-based line number - * @param x horizontal position - */ - @Override - protected void paintLine(Graphics gfx, int line, int x, - TokenMarker tokenMarker) { - try { - // TODO This line is causing NPEs randomly ever since I added the - // toggle for Java Mode/Debugger toolbar. [Manindra] - super.paintLine(gfx, line, x + Editor.LEFT_GUTTER, tokenMarker); - - } catch (Exception e) { - //Messages.log(e.getMessage()); - e.printStackTrace(); - } - - // formerly only when in debug mode - paintLeftGutter(gfx, line, x); -// paintGutterBg(gfx, line, x); -// paintGutterLine(gfx, line, x); -// paintGutterText(gfx, line, x); - - paintErrorLine(gfx, line, x); - } - - - /** - * Paint the gutter: draw the background, draw line numbers, break points. - * @param gfx the graphics context - * @param line 0-based line number - * @param x horizontal position - */ - protected void paintLeftGutter(Graphics gfx, int line, int x) { - int y = textArea.lineToY(line) + fm.getLeading() + fm.getMaxDescent(); - if (line == textArea.getSelectionStopLine()) { - gfx.setColor(gutterLineHighlightColor); - gfx.fillRect(0, y, Editor.LEFT_GUTTER, fm.getHeight()); - } else { - //gfx.setColor(getJavaTextArea().gutterBgColor); - gfx.setClip(0, y, Editor.LEFT_GUTTER, fm.getHeight()); - gfx.drawImage(getJavaTextArea().getGutterGradient(), 0, 0, getWidth(), getHeight(), this); - gfx.setClip(null); // reset - } - - String text = null; - if (getJavaEditor().isDebuggerEnabled()) { - text = getJavaTextArea().getGutterText(line); - } - - gfx.setColor(line < textArea.getLineCount() ? gutterTextColor : gutterPastColor); -// if (line >= textArea.getLineCount()) { -// //gfx.setColor(new Color(gutterTextColor.getRGB(), ); -// } - int textRight = Editor.LEFT_GUTTER - Editor.GUTTER_MARGIN; - int textBaseline = textArea.lineToY(line) + fm.getHeight(); - - if (text != null) { - if (text.equals(JavaTextArea.BREAK_MARKER)) { - drawDiamond(gfx, textRight - 8, textBaseline - 8, 8, 8); - - } else if (text.equals(JavaTextArea.STEP_MARKER)) { - //drawRightArrow(gfx, textRight - 7, textBaseline - 7, 7, 6); - drawRightArrow(gfx, textRight - 7, textBaseline - 7.5f, 7, 7); - } - } else { - // if no special text for a breakpoint, just show the line number - text = String.valueOf(line + 1); - //text = makeOSF(String.valueOf(line + 1)); - - gfx.setFont(gutterTextFont); -// ((Graphics2D) gfx).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, -// RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); - // Right-align the text - char[] txt = text.toCharArray(); - int tx = textRight - gfx.getFontMetrics().charsWidth(txt, 0, txt.length); - // Using 'fm' here because it's relative to the editor text size, - // not the numbers in the gutter - Utilities.drawTabbedText(new Segment(txt, 0, text.length()), - tx, textBaseline, gfx, this, 0); - } - } - - - private void drawDiamond(Graphics g, float x, float y, float w, float h) { - Graphics2D g2 = (Graphics2D) g; - GeneralPath path = new GeneralPath(); - path.moveTo(x + w/2, y); - path.lineTo(x + w, y + h/2); - path.lineTo(x + w/2, y + h); - path.lineTo(x, y + h/2); - path.closePath(); - g2.fill(path); - } - - - private void drawRightArrow(Graphics g, float x, float y, float w, float h) { - Graphics2D g2 = (Graphics2D) g; - GeneralPath path = new GeneralPath(); - path.moveTo(x, y); - path.lineTo(x + w, y + h/2); - path.lineTo(x, y + h); - path.closePath(); - g2.fill(path); - } - - - /* - // Failed attempt to switch line numbers to old-style figures - String makeOSF(String what) { - char[] c = what.toCharArray(); - for (int i = 0; i < c.length; i++) { - c[i] += (char) (c[i] - '0' + 0x362); - } - return new String(c); - } - */ - - - /** - * Paint the background color of a line. - * - * @param gfx - * the graphics context - * @param line - * 0-based line number - * @param x - private void paintLineBgColor(Graphics gfx, int line, int x) { - int y = textArea.lineToY(line); - y += fm.getLeading() + fm.getMaxDescent(); - int height = fm.getHeight(); - - Color col = getJavaTextArea().getLineBgColor(line); - if (col != null) { - // paint line background - gfx.setColor(col); - gfx.fillRect(0, y, getWidth(), height); - } - } - */ - - - /** - * Remove all trailing whitespace from a line - */ - static private String trimRight(String str) { - int i = str.length() - 1; - while (i >= 0 && Character.isWhitespace(str.charAt(i))) { - i--; - } - return str.substring(0, i+1); - } - - - /** - * Paints the underline for an error/warning line - */ - protected void paintErrorLine(Graphics gfx, int line, int x) { - List problems = getEditor().findProblems(line); - for (Problem problem : problems) { - int startOffset = problem.getStartOffset(); - int stopOffset = problem.getStopOffset(); - - int lineOffset = textArea.getLineStartOffset(line); - - int wiggleStart = Math.max(startOffset, lineOffset); - int wiggleStop = Math.min(stopOffset, textArea.getLineStopOffset(line)); - - int y = textArea.lineToY(line) + fm.getLeading() + fm.getMaxDescent(); - - try { - String badCode = null; - String goodCode = null; - try { - SyntaxDocument doc = textArea.getDocument(); - badCode = doc.getText(wiggleStart, wiggleStop - wiggleStart); - goodCode = doc.getText(lineOffset, wiggleStart - lineOffset); - //log("paintErrorLine() LineText GC: " + goodCode); - //log("paintErrorLine() LineText BC: " + badCode); - } catch (BadLocationException bl) { - // Error in the import statements or end of code. - // System.out.print("BL caught. " + ta.getLineCount() + " ," - // + line + " ,"); - // log((ta.getLineStopOffset(line) - start - 1)); - return; - } - - int trimmedLength = badCode.trim().length(); - int rightTrimmedLength = trimRight(badCode).length(); - int leftTrimLength = rightTrimmedLength - trimmedLength; - - // Fix offsets when bad code is just whitespace - if (trimmedLength == 0) { - leftTrimLength = 0; - rightTrimmedLength = badCode.length(); - } - - int x1 = textArea.offsetToX(line, goodCode.length() + leftTrimLength); - int x2 = textArea.offsetToX(line, goodCode.length() + rightTrimmedLength); - if (x1 == x2) x2 += fm.stringWidth(" "); - int y1 = y + fm.getHeight() - 2; - - if (line != problem.getLineNumber()) { - x1 = Editor.LEFT_GUTTER; // on the following lines, wiggle extends to the left border - } - - gfx.setColor(errorUnderlineColor); - if (problem.isWarning()) { - gfx.setColor(warningUnderlineColor); - } - paintSquiggle(gfx, y1, x1, x2); - - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - static private void paintSquiggle(Graphics g, int y, int x1, int x2) { - int xx = x1; + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - while (xx < x2) { - g.drawLine(xx, y, xx + 2, y + 1); - xx += 2; - g.drawLine(xx, y + 1, xx + 2, y); - xx += 2; - } - } - - - @Override - public String getToolTipText(MouseEvent evt) { - int line = evt.getY() / getFontMetrics().getHeight() + textArea.getFirstLine(); - if (line >= 0 || line < textArea.getLineCount()) { - List problems = getEditor().findProblems(line); - for (Problem problem : problems) { - int lineStart = textArea.getLineStartOffset(line); - int lineEnd = textArea.getLineStopOffset(line); + // TWEAK MODE - int errorStart = problem.getStartOffset(); - int errorEnd = problem.getStopOffset() + 1; - int startOffset = Math.max(errorStart, lineStart) - lineStart; - int stopOffset = Math.min(errorEnd, lineEnd) - lineStart; - - int x = evt.getX(); - - if (x >= getJavaTextArea().offsetToX(line, startOffset) && - x <= getJavaTextArea().offsetToX(line, stopOffset)) { - getEditor().statusToolTip(this, problem.getMessage(), problem.isError()); - return super.getToolTipText(evt); - } - } - } - setToolTipText(null); - return super.getToolTipText(evt); - } - - - // TweakMode code protected int horizontalAdjustment = 0; public boolean tweakMode = false; @@ -497,12 +229,12 @@ protected void stopTweakMode() { } - protected void updateInterface(List> handles, - List> colorBoxes) { + protected void updateTweakInterface(List> handles, + List> colorBoxes) { this.handles = handles; this.colorBoxes = colorBoxes; - initInterfacePositions(); + initTweakInterfacePositions(); repaint(); } @@ -512,7 +244,7 @@ protected void updateInterface(List> handles, * synchronize this method to prevent the execution of 'paint' in the middle. * (don't paint while we make changes to the text of the editor) */ - private synchronized void initInterfacePositions() { + private synchronized void initTweakInterfacePositions() { SketchCode[] code = getEditor().getSketch().getCode(); int prevScroll = textArea.getVerticalScrollPosition(); String prevText = textArea.getText(); @@ -640,11 +372,6 @@ private JavaEditor getJavaEditor() { } - private JavaTextArea getJavaTextArea() { - return (JavaTextArea) textArea; - } - - private int getCurrentCodeIndex() { return getEditor().getSketch().getCurrentCodeIndex(); } From 68bfec01c3373ec89684ab8ce26f8180f463fb0e Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 6 Aug 2016 14:52:42 -0400 Subject: [PATCH 0063/1035] moving more things into Editor --- app/src/processing/app/ui/Editor.java | 5 ++++ core/todo.txt | 7 +++++- java/src/processing/mode/java/JavaEditor.java | 23 +------------------ .../mode/java/pdex/JavaTextAreaPainter.java | 4 ++-- todo.txt | 2 ++ 5 files changed, 16 insertions(+), 25 deletions(-) diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 73ce7d46dd..2db1e9ebb6 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -278,6 +278,11 @@ public void actionPerformed(ActionEvent e) { } }); } + textarea.addCaretListener(new CaretListener() { + public void caretUpdate(CaretEvent e) { + updateEditorStatus(); + } + }); footer = createFooter(); diff --git a/core/todo.txt b/core/todo.txt index 203a3111ce..4be30e948a 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -12,10 +12,15 @@ X Fix resizing targets for async save X https://github.com/processing/processing/pull/4607 X https://github.com/processing/processing/issues/4578 +contrib +X Make loadStrings() and loadJSONObject/loadJSONArray() error msgs consistent +X https://github.com/processing/processing/issues/4265 +X https://github.com/processing/processing/pull/4268 + + _ disable OpenGL ES on Linux? _ https://github.com/processing/processing/issues/4584 - _ Can't render PGraphics object using image() within a PDF _ https://github.com/processing/processing/issues/4473 diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 3504c24855..14bf6d560f 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -80,21 +80,6 @@ protected JavaEditor(Base base, String path, EditorState state, debugger = new Debugger(this); inspector = new VariableInspector(this); - // set action on frame close - // addWindowListener(new WindowAdapter() { - // @Override - // public void windowClosing(WindowEvent e) { - // onWindowClosing(e); - // } - // }); - - -// // load settings from theme.txt -// breakpointColor = mode.getColor("breakpoint.bgcolor"); -// breakpointMarkerColor = mode.getColor("breakpoint.marker.color"); -// currentLineColor = mode.getColor("currentline.bgcolor"); -// currentLineMarkerColor = mode.getColor("currentline.marker.color"); - // set breakpoints from marker comments for (LineID lineID : stripBreakpointComments()) { //System.out.println("setting: " + lineID); @@ -119,7 +104,7 @@ protected JavaEditor(Base base, String path, EditorState state, // add our hacked version back to the editor box.add(textAndError); - getJavaTextArea().setMode(jmode); + getPdeTextArea().setMode(jmode); preprocessingService = new PreprocessingService(this); pdex = new PDEX(this, preprocessingService); @@ -134,12 +119,6 @@ public void windowLostFocus(WindowEvent e) { public void windowGainedFocus(WindowEvent e) { } }); - - textarea.addCaretListener(new CaretListener() { - public void caretUpdate(CaretEvent e) { - updateEditorStatus(); - } - }); } diff --git a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java index 7859c1e731..0bd682a155 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -234,7 +234,7 @@ protected void updateTweakInterface(List> handles, this.handles = handles; this.colorBoxes = colorBoxes; - initTweakInterfacePositions(); + updateTweakInterfacePositions(); repaint(); } @@ -244,7 +244,7 @@ protected void updateTweakInterface(List> handles, * synchronize this method to prevent the execution of 'paint' in the middle. * (don't paint while we make changes to the text of the editor) */ - private synchronized void initTweakInterfacePositions() { + private synchronized void updateTweakInterfacePositions() { SketchCode[] code = getEditor().getSketch().getCode(); int prevScroll = textArea.getVerticalScrollPosition(); String prevText = textArea.getText(); diff --git a/todo.txt b/todo.txt index 3aca8844e6..682d3aeec3 100644 --- a/todo.txt +++ b/todo.txt @@ -51,6 +51,8 @@ _ https://github.com/processing/processing/issues/3965 medium _ Move general PDE code out of JavaMode and into general base classes _ https://github.com/processing/processing/issues/4606 +_ detect changes in case with libraries +_ https://github.com/processing/processing/issues/4507 lower _ make when opening new editor window, open on the same display as current From d55eb3490ccdbf4baab88dbfa77644839d7d6c22 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 6 Aug 2016 15:03:06 -0400 Subject: [PATCH 0064/1035] remove ErrorMarker hack, make it standard in Editor --- app/src/processing/app/ui/Editor.java | 10 ++++++++-- java/src/processing/mode/java/JavaEditor.java | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 2db1e9ebb6..64060dd028 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -286,11 +286,17 @@ public void caretUpdate(CaretEvent e) { footer = createFooter(); - upper.add(textarea); + // build the central panel with the text area & error marker column + JPanel editorPanel = new JPanel(new BorderLayout()); + errorColumn = new MarkerColumn(this, textarea.getMinimumSize().height); + editorPanel.add(errorColumn, BorderLayout.EAST); + textarea.setBounds(0, 0, errorColumn.getX() - 1, textarea.getHeight()); + editorPanel.add(textarea); + upper.add(editorPanel); splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, upper, footer); - // disable this because it hides the message area, which is essential (issue #745) + // disable this because it hides the message area (Google Code issue #745) splitPane.setOneTouchExpandable(false); // repaint child panes while resizing splitPane.setContinuousLayout(true); diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 14bf6d560f..cf185af781 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -90,6 +90,7 @@ protected JavaEditor(Base base, String path, EditorState state, hasJavaTabs = checkForJavaTabs(); + /* // hack to add a JPanel to the right-hand side of the text area JPanel textAndError = new JPanel(); // parent is a vertical box with the toolbar, the header, and the text area @@ -103,6 +104,7 @@ protected JavaEditor(Base base, String path, EditorState state, textAndError.add(textarea); // add our hacked version back to the editor box.add(textAndError); + */ getPdeTextArea().setMode(jmode); From 1c8e14b93d1bd81801b2d0b1c088a8353a4857fb Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 6 Aug 2016 15:48:55 -0400 Subject: [PATCH 0065/1035] get styles to work properly, other cleanups --- app/src/processing/app/Mode.java | 2 -- app/src/processing/app/syntax/PdeTextAreaPainter.java | 9 ++++++--- app/src/processing/app/ui/Editor.java | 3 +++ java/src/processing/mode/java/JavaEditor.java | 2 -- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/src/processing/app/Mode.java b/app/src/processing/app/Mode.java index a5bf1b1d4c..faf1540e03 100644 --- a/app/src/processing/app/Mode.java +++ b/app/src/processing/app/Mode.java @@ -209,8 +209,6 @@ public void setupGUI() { // other things that have to be set explicitly for the defaults theme.setColor("run.window.bgcolor", SystemColor.control); -// loadBackground(); - } catch (IOException e) { Messages.showError("Problem loading theme.txt", "Could not load theme.txt, please re-install Processing", e); diff --git a/app/src/processing/app/syntax/PdeTextAreaPainter.java b/app/src/processing/app/syntax/PdeTextAreaPainter.java index 757d6f977d..290cdfc414 100644 --- a/app/src/processing/app/syntax/PdeTextAreaPainter.java +++ b/app/src/processing/app/syntax/PdeTextAreaPainter.java @@ -81,7 +81,10 @@ public void mousePressed(MouseEvent event) { /** - * Loads theme for TextAreaPainter + * Loads theme for TextAreaPainter. This is handled here because in the olden + * days, Modes had different visual design. Now, these are just pulling the + * defaults from the standard theme, though there may be minor additions or + * overrides added in a Mode's own theme.txt file. */ public void setMode(Mode mode) { errorUnderlineColor = mode.getColor("editor.error.underline.color"); @@ -101,8 +104,8 @@ public void setMode(Mode mode) { /** - * Paint a line. Paints the gutter (with background color and text) then the - * line (background color and text). + * Paint a line. Paints the gutter (with background color and text) + * then the line (background color and text). * * @param gfx the graphics context * @param tokenMarker diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 64060dd028..716d46b7a4 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -294,6 +294,9 @@ public void caretUpdate(CaretEvent e) { editorPanel.add(textarea); upper.add(editorPanel); + // set colors and fonts for the painter object + getPdeTextArea().setMode(mode); + splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, upper, footer); // disable this because it hides the message area (Google Code issue #745) diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index cf185af781..8d90163b42 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -106,8 +106,6 @@ protected JavaEditor(Base base, String path, EditorState state, box.add(textAndError); */ - getPdeTextArea().setMode(jmode); - preprocessingService = new PreprocessingService(this); pdex = new PDEX(this, preprocessingService); From 68a0188f498fdcacc4ebca0fba872658ed01fa13 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 6 Aug 2016 16:06:26 -0400 Subject: [PATCH 0066/1035] add blank constructor to prevent Python Mode breakage --- app/src/processing/app/syntax/PdeInputHandler.java | 4 ++++ todo.txt | 8 +++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/processing/app/syntax/PdeInputHandler.java b/app/src/processing/app/syntax/PdeInputHandler.java index bf0a7cdf66..2ae4c43a3b 100644 --- a/app/src/processing/app/syntax/PdeInputHandler.java +++ b/app/src/processing/app/syntax/PdeInputHandler.java @@ -48,6 +48,10 @@ public class PdeInputHandler extends DefaultInputHandler { protected Editor editor; + /** Not recommended, but included for compatibility. */ + public PdeInputHandler() { } + + public PdeInputHandler(Editor editor) { this.editor = editor; diff --git a/todo.txt b/todo.txt index 682d3aeec3..0ad76b34da 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,6 @@ 0252 (3.2) +_ Move general PDE code out of JavaMode and into general base classes +_ https://github.com/processing/processing/issues/4606 X change PdeInputHandler constructor _ check whether this breaks other Modes before releasing _ modify line number color when no lines extend that far? @@ -6,8 +8,6 @@ _ https://github.com/processing/processing/pull/4560 _ text gutter doesn't seem to be hidpi X or is it b/c screen not quite 2x? (nope) _ swap out the fonts? -_ Set text color for InputMethod -_ https://github.com/processing/processing/pull/4593 _ Processing .jar files in CLASSPATH can cause startup crash _ https://github.com/processing/processing/issues/4128 @@ -16,7 +16,7 @@ _ https://github.com/processing/processing/issues/43503#issuecomment-237715947 contrib -input method work +input method work from tyfkda X Simplify conditional branch X https://github.com/processing/processing/pull/4589 X Enable input method support by default on Japanese/Korean/Chinese systems @@ -49,8 +49,6 @@ _ possible infinite loop on modified externally _ https://github.com/processing/processing/issues/3965 medium -_ Move general PDE code out of JavaMode and into general base classes -_ https://github.com/processing/processing/issues/4606 _ detect changes in case with libraries _ https://github.com/processing/processing/issues/4507 From ccda52074e29235eb11ad4fe60b733ef6d69172c Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 6 Aug 2016 16:49:55 -0400 Subject: [PATCH 0067/1035] remove java.ext.dirs on startup (fixes #4608) --- build/build.xml | 3 +++ build/linux/processing | 4 ++-- build/windows/config-cmd.xml | 2 ++ build/windows/config.xml | 2 ++ todo.txt | 14 +++++++++----- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/build/build.xml b/build/build.xml index 97abc2e061..3ba471098b 100644 --- a/build/build.xml +++ b/build/build.xml @@ -559,6 +559,9 @@

#fJy9{c3{xy8ibx{%W`gaU@ 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 literal 0 HcmV?d00001 diff --git a/java/libraries/io/library/linux-armv6hf/libprocessing-io.so b/java/libraries/io/library/linux-armv6hf/libprocessing-io.so index 2f0960b223b4e7ddc98f37df720fc012ba8c3598..94ed162d63f93f494d191b56336e3b6af7fb3f4c 100755 GIT binary patch literal 35000 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