diff --git a/app/src/processing/app/Platform.java b/app/src/processing/app/Platform.java
index b08f62bd0..3578e0a7d 100644
--- a/app/src/processing/app/Platform.java
+++ b/app/src/processing/app/Platform.java
@@ -26,6 +26,8 @@ package processing.app;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
import java.util.HashMap;
import java.util.Map;
@@ -281,9 +283,16 @@ public class Platform {
static public File getContentFile(String name) {
if (processingRoot == null) {
// Get the path to the .jar file that contains Base.class
- String path = Base.class.getProtectionDomain().getCodeSource().getLocation().getPath();
- // Path may have URL encoding, so remove it
- String decodedPath = PApplet.urlDecode(path);
+ URL pathURL =
+ Base.class.getProtectionDomain().getCodeSource().getLocation();
+ // Decode URL
+ String decodedPath;
+ try {
+ decodedPath = pathURL.toURI().getPath();
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ return null;
+ }
if (decodedPath.contains("/app/bin")) { // This means we're in Eclipse
final File build = new File(decodedPath, "../../build").getAbsoluteFile();
@@ -311,8 +320,7 @@ public class Platform {
System.err.println("Could not find lib folder via " +
jarFolder.getAbsolutePath() +
", switching to user.dir");
- final String userDir = System.getProperty("user.dir");
- processingRoot = new File(PApplet.urlDecode(userDir));
+ processingRoot = new File(""); // resolves to "user.dir"
}
}
}
diff --git a/app/src/processing/app/syntax/TextAreaPainter.java b/app/src/processing/app/syntax/TextAreaPainter.java
index bdfded94d..34da0c35e 100644
--- a/app/src/processing/app/syntax/TextAreaPainter.java
+++ b/app/src/processing/app/syntax/TextAreaPainter.java
@@ -133,6 +133,7 @@ public class TextAreaPainter extends JComponent implements TabExpander {
// moved from setFont() override (never quite comfortable w/ that override)
fm = super.getFontMetrics(plainFont);
+ tabSize = fm.charWidth(' ') * Preferences.getInteger("editor.tabs.size");
textArea.recalculateVisibleLines();
// fgcolor = mode.getColor("editor.fgcolor");
@@ -465,8 +466,6 @@ public class TextAreaPainter extends JComponent implements TabExpander {
// g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
// RenderingHints.VALUE_FRACTIONALMETRICS_ON);
- tabSize = fm.charWidth(' ') * ((Integer)textArea.getDocument().getProperty(PlainDocument.tabSizeAttribute)).intValue();
-
Rectangle clipRect = gfx.getClipBounds();
gfx.setColor(getBackground());
@@ -670,14 +669,18 @@ public class TextAreaPainter extends JComponent implements TabExpander {
// gfx.setFont(defaultFont);
// gfx.setColor(defaultColor);
+ int x0 = x - textArea.getHorizontalOffset();
+
y += fm.getHeight();
// doesn't respect fixed width like it should
// x = Utilities.drawTabbedText(currentLine, x, y, gfx, this, 0);
// int w = fm.charWidth(' ');
for (int i = 0; i < currentLine.count; i++) {
gfx.drawChars(currentLine.array, currentLine.offset+i, 1, x, y);
- x = currentLine.array[currentLine.offset + i] == '\t' ? (int)nextTabStop(x, i) :
+ x = currentLine.array[currentLine.offset + i] == '\t' ?
+ x0 + (int)nextTabStop(x - x0, i) :
x + fm.charWidth(currentLine.array[currentLine.offset+i]);
+ textArea.offsetToX(line, currentLine.offset + i);
}
// Draw characters via input method.
@@ -745,6 +748,8 @@ public class TextAreaPainter extends JComponent implements TabExpander {
// Font defaultFont = gfx.getFont();
// Color defaultColor = gfx.getColor();
+ int x0 = x - textArea.getHorizontalOffset();
+
// for (byte id = tokens.id; id != Token.END; tokens = tokens.next) {
for (;;) {
byte id = tokens.id;
@@ -772,8 +777,9 @@ public class TextAreaPainter extends JComponent implements TabExpander {
// int w = fm.charWidth(' ');
for (int i = 0; i < line.count; i++) {
gfx.drawChars(line.array, line.offset+i, 1, x, y);
- x = line.array[line.offset + i] == '\t' ? (int)nextTabStop(x, i) :
- x + fm.charWidth(line.array[line.offset+i]);
+ x = line.array[line.offset + i] == '\t' ?
+ x0 + (int)nextTabStop(x - x0, i) :
+ x + fm.charWidth(line.array[line.offset+i]);
}
//x += fm.charsWidth(line.array, line.offset, line.count);
//x += fm.charWidth(' ') * line.count;
diff --git a/app/src/processing/app/syntax/im/InputMethodSupport.java b/app/src/processing/app/syntax/im/InputMethodSupport.java
index 921cfc83b..9df319b99 100644
--- a/app/src/processing/app/syntax/im/InputMethodSupport.java
+++ b/app/src/processing/app/syntax/im/InputMethodSupport.java
@@ -34,7 +34,7 @@ import processing.app.syntax.TextAreaPainter;
* @see Bug 1531 : Can't input full-width space when Japanese IME is on.
* @see http://docs.oracle.com/javase/8/docs/technotes/guides/imf/index.html
* @see http://docs.oracle.com/javase/tutorial/2d/text/index.html
- *
+ *
* @author Takashi Maekawa (takachin@generative.info)
* @author Satoshi Okita
*/
@@ -44,11 +44,9 @@ public class InputMethodSupport implements InputMethodRequests,
private static final Attribute[] CUSTOM_IM_ATTRIBUTES = {
TextAttribute.INPUT_METHOD_HIGHLIGHT,
};
-
+
private int committed_count = 0;
- private TextHitInfo caret;
private JEditTextArea textArea;
- private AttributedCharacterIterator composedText;
private AttributedString composedTextString;
public InputMethodSupport(JEditTextArea textArea) {
@@ -70,10 +68,10 @@ public class InputMethodSupport implements InputMethodRequests,
// '+1' mean textArea.lineToY(line) + textArea.getPainter().getFontMetrics().getHeight().
// TextLayout#draw method need at least one height of font.
Rectangle rectangle = new Rectangle(textArea.offsetToX(line, offsetX), textArea.lineToY(line + 1), 0, 0);
-
+
Point location = textArea.getPainter().getLocationOnScreen();
rectangle.translate(location.x, location.y);
-
+
return rectangle;
}
@@ -87,7 +85,7 @@ public class InputMethodSupport implements InputMethodRequests,
public int getInsertPositionOffset() {
return textArea.getCaretPosition() * -1;
}
-
+
@Override
public AttributedCharacterIterator getCommittedText(int beginIndex,
int endIndex, AttributedCharacterIterator.Attribute[] attributes) {
@@ -118,12 +116,11 @@ public class InputMethodSupport implements InputMethodRequests,
/////////////////////////////////////////////////////////////////////////////
/**
* Handles events from InputMethod.
- *
+ *
* @param event event from Input Method.
*/
@Override
- public void inputMethodTextChanged(InputMethodEvent event) {
- composedText = null;
+ public void inputMethodTextChanged(InputMethodEvent event) {
if (Base.DEBUG) {
StringBuilder sb = new StringBuilder();
sb.append("#Called inputMethodTextChanged");
@@ -132,16 +129,16 @@ public class InputMethodSupport implements InputMethodRequests,
sb.append("\t parmString: " + event.paramString());
Messages.log(sb.toString());
}
-
+
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.
@@ -168,7 +165,7 @@ public class InputMethodSupport implements InputMethodRequests,
this.insertCharacter(c);
c = text.next();
}
-
+
CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter();
if (Base.DEBUG) {
Messages.log(" textArea.getCaretPosition() + committed_count: " + (textArea.getCaretPosition() + committed_count));
@@ -181,15 +178,14 @@ public class InputMethodSupport implements InputMethodRequests,
compositionPainter.setComposedTextLayout(null, 0);
compositionPainter.setCaret(null);
}
- caret = event.getCaret();
event.consume();
- textArea.repaint();
+ textArea.repaint();
}
-
+
private TextLayout getTextLayout(AttributedCharacterIterator text, int committedCount) {
boolean antialias = Preferences.getBoolean("editor.smooth");
TextAreaPainter painter = textArea.getPainter();
-
+
// create attributed string with font info.
//if (text.getEndIndex() - (text.getBeginIndex() + committedCharacterCount) > 0) {
if (text.getEndIndex() - (text.getBeginIndex() + committedCount) > 0) {
@@ -197,12 +193,11 @@ public class InputMethodSupport implements InputMethodRequests,
Font font = painter.getFontMetrics().getFont();
composedTextString.addAttribute(TextAttribute.FONT, font);
composedTextString.addAttribute(TextAttribute.BACKGROUND, Color.WHITE);
- composedText = composedTextString.getIterator();
} else {
composedTextString = new AttributedString("");
return null;
}
-
+
// set hint of antialiasing to render target.
Graphics2D g2d = (Graphics2D)painter.getGraphics();
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
@@ -219,10 +214,9 @@ public class InputMethodSupport implements InputMethodRequests,
@Override
public void caretPositionChanged(InputMethodEvent event) {
- caret = event.getCaret();
event.consume();
}
-
+
private void insertCharacter(char c) {
if (Base.DEBUG) {
Messages.log("debug: insertCharacter(char c) textArea.getCaretPosition()=" + textArea.getCaretPosition());
diff --git a/build/build.xml b/build/build.xml
index af8df134a..c0cf4734d 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -59,8 +59,8 @@
@@ -90,21 +106,6 @@ public class FloatDict {
}
- /**
- * @nowebref
- */
- public FloatDict(String[] keys, float[] values) {
- if (keys.length != values.length) {
- throw new IllegalArgumentException("key and value arrays must be the same length");
- }
- this.keys = keys;
- this.values = values;
- count = keys.length;
- for (int i = 0; i < count; i++) {
- indices.put(keys[i], i);
- }
- }
-
/**
* @webref floatdict:method
* @brief Returns the number of key/value pairs
@@ -139,71 +140,35 @@ public class FloatDict {
}
-// /**
-// * Return the internal array being used to store the keys. Allocated but
-// * unused entries will be removed. This array should not be modified.
-// */
-// public String[] keys() {
-// crop();
-// return keys;
-// }
-
- /**
- * @webref floatdict:method
- * @brief Return the internal array being used to store the keys
- */
public Iterable keys() {
return new Iterable() {
@Override
public Iterator iterator() {
- return new Iterator() {
- int index = -1;
-
- public void remove() {
- removeIndex(index);
- }
-
- public String next() {
- return key(++index);
- }
-
- public boolean hasNext() {
- return index+1 < size();
- }
- };
+ return keyIterator();
}
};
}
+
+
+ // Use this to iterate when you want to be able to remove elements along the way
+ public Iterator keyIterator() {
+ return new Iterator() {
+ int index = -1;
+ public void remove() {
+ removeIndex(index);
+ }
- /*
- static class KeyIterator implements Iterator {
- FloatHash parent;
- int index;
+ public String next() {
+ return key(++index);
+ }
- public KeyIterator(FloatHash parent) {
- this.parent = parent;
- index = -1;
- }
-
- public void remove() {
- parent.removeIndex(index);
- }
-
- public String next() {
- return parent.key(++index);
- }
-
- public boolean hasNext() {
- return index+1 < parent.size();
- }
-
- public void reset() {
- index = -1;
- }
+ public boolean hasNext() {
+ return index+1 < size();
+ }
+ };
}
- */
/**
@@ -213,6 +178,7 @@ public class FloatDict {
* @brief Return a copy of the internal keys array
*/
public String[] keyArray() {
+ crop();
return keyArray(null);
}
@@ -231,11 +197,6 @@ public class FloatDict {
}
-// public float[] values() {
-// crop();
-// return values;
-// }
-
/**
* @webref floatdict:method
* @brief Return the internal array being used to store the values
@@ -245,21 +206,26 @@ public class FloatDict {
@Override
public Iterator iterator() {
- return new Iterator() {
- int index = -1;
+ return valueIterator();
+ }
+ };
+ }
+
+
+ public Iterator valueIterator() {
+ return new Iterator() {
+ int index = -1;
- public void remove() {
- removeIndex(index);
- }
+ public void remove() {
+ removeIndex(index);
+ }
- public Float next() {
- return value(++index);
- }
+ public Float next() {
+ return value(++index);
+ }
- public boolean hasNext() {
- return index+1 < size();
- }
- };
+ public boolean hasNext() {
+ return index+1 < size();
}
};
}
@@ -272,6 +238,7 @@ public class FloatDict {
* @brief Create a new array and copy each of the values into it
*/
public float[] valueArray() {
+ crop();
return valueArray(null);
}
@@ -337,18 +304,6 @@ public class FloatDict {
}
-// /** Increase the value of a specific key by 1. */
-// public void inc(String key) {
-// inc(key, 1);
-//// int index = index(key);
-//// if (index == -1) {
-//// create(key, 1);
-//// } else {
-//// values[index]++;
-//// }
-// }
-
-
/**
* @webref floatdict:method
* @brief Add to a value
@@ -363,12 +318,6 @@ public class FloatDict {
}
-// /** Decrease the value of a key by 1. */
-// public void dec(String key) {
-// inc(key, -1);
-// }
-
-
/**
* @webref floatdict:method
* @brief Subtract from a value
@@ -417,8 +366,10 @@ public class FloatDict {
* @brief Return the smallest value
*/
public int minIndex() {
- checkMinMax("minIndex");
- // Will still return NaN if there is 1 or more entries, and they're all NaN
+ //checkMinMax("minIndex");
+ if (count == 0) return -1;
+
+ // Will still return NaN if there are 1 or more entries, and they're all NaN
float m = Float.NaN;
int mi = -1;
for (int i = 0; i < count; i++) {
@@ -430,7 +381,7 @@ public class FloatDict {
// calculate the rest
for (int j = i+1; j < count; j++) {
float d = values[j];
- if (!Float.isNaN(d) && (d < m)) {
+ if ((d == d) && (d < m)) {
m = values[j];
mi = j;
}
@@ -442,6 +393,7 @@ public class FloatDict {
}
+ // return the key for the minimum value
public String minKey() {
checkMinMax("minKey");
int index = minIndex();
@@ -452,6 +404,7 @@ public class FloatDict {
}
+ // return the minimum value, or throw an error if there are no values
public float minValue() {
checkMinMax("minValue");
int index = minIndex();
@@ -468,7 +421,10 @@ public class FloatDict {
*/
// The index of the entry that has the max value. Reference above is incorrect.
public int maxIndex() {
- checkMinMax("maxIndex");
+ //checkMinMax("maxIndex");
+ if (count == 0) {
+ return -1;
+ }
// Will still return NaN if there is 1 or more entries, and they're all NaN
float m = Float.NaN;
int mi = -1;
@@ -493,9 +449,9 @@ public class FloatDict {
}
- /** The key for a max value, or null if everything is NaN (no max). */
+ /** The key for a max value; null if empty or everything is NaN (no max). */
public String maxKey() {
- checkMinMax("maxKey");
+ //checkMinMax("maxKey");
int index = maxIndex();
if (index == -1) {
return null;
@@ -504,9 +460,9 @@ public class FloatDict {
}
- /** The max value. (Or NaN if they're all NaN.) */
+ /** The max value. (Or NaN if no entries or they're all NaN.) */
public float maxValue() {
- checkMinMax("maxValue");
+ //checkMinMax("maxValue");
int index = maxIndex();
if (index == -1) {
return Float.NaN;
@@ -573,24 +529,11 @@ public class FloatDict {
keys[b] = tkey;
values[b] = tvalue;
- indices.put(keys[a], Integer.valueOf(a));
- indices.put(keys[b], Integer.valueOf(b));
+// indices.put(keys[a], Integer.valueOf(a));
+// indices.put(keys[b], Integer.valueOf(b));
}
-// abstract class InternalSort extends Sort {
-// @Override
-// public int size() {
-// return count;
-// }
-//
-// @Override
-// public void swap(int a, int b) {
-// FloatHash.this.swap(a, b);
-// }
-// }
-
-
/**
* Sort the keys alphabetically (ignoring case). Uses the value as a
* tie-breaker (only really possible with a key that has a case change).
@@ -599,36 +542,16 @@ public class FloatDict {
* @brief Sort the keys alphabetically
*/
public void sortKeys() {
- sortImpl(true, false);
-// new InternalSort() {
-// @Override
-// public float compare(int a, int b) {
-// int result = keys[a].compareToIgnoreCase(keys[b]);
-// if (result != 0) {
-// return result;
-// }
-// return values[b] - values[a];
-// }
-// }.run();
+ sortImpl(true, false, true);
}
/**
* @webref floatdict:method
- * @brief Sort the keys alphabetially in reverse
+ * @brief Sort the keys alphabetically in reverse
*/
public void sortKeysReverse() {
- sortImpl(true, true);
-// new InternalSort() {
-// @Override
-// public float compare(int a, int b) {
-// int result = keys[b].compareToIgnoreCase(keys[a]);
-// if (result != 0) {
-// return result;
-// }
-// return values[a] - values[b];
-// }
-// }.run();
+ sortImpl(true, true, true);
}
@@ -639,13 +562,17 @@ public class FloatDict {
* @brief Sort by values in ascending order
*/
public void sortValues() {
- sortImpl(false, false);
-// new InternalSort() {
-// @Override
-// public float compare(int a, int b) {
-//
-// }
-// }.run();
+ sortValues(true);
+ }
+
+
+ /**
+ * Set true to ensure that the order returned is identical. Slightly
+ * slower because the tie-breaker for identical values compares the keys.
+ * @param stable
+ */
+ public void sortValues(boolean stable) {
+ sortImpl(false, false, stable);
}
@@ -654,50 +581,17 @@ public class FloatDict {
* @brief Sort by values in descending order
*/
public void sortValuesReverse() {
- sortImpl(false, true);
-// new InternalSort() {
-// @Override
-// public float compare(int a, int b) {
-// float diff = values[b] - values[a];
-// if (diff == 0 && keys[a] != null && keys[b] != null) {
-// diff = keys[a].compareToIgnoreCase(keys[b]);
-// }
-// return descending ? diff : -diff;
-// }
-// }.run();
+ sortValuesReverse(true);
}
-// // ascending puts the largest value at the end
-// // descending puts the largest value at 0
-// public void sortValues(final boolean descending, final boolean tiebreaker) {
-// Sort s = new Sort() {
-// @Override
-// public int size() {
-// return count;
-// }
-//
-// @Override
-// public float compare(int a, int b) {
-// float diff = values[b] - values[a];
-// if (tiebreaker) {
-// if (diff == 0) {
-// diff = keys[a].compareToIgnoreCase(keys[b]);
-// }
-// }
-// return descending ? diff : -diff;
-// }
-//
-// @Override
-// public void swap(int a, int b) {
-// FloatHash.this.swap(a, b);
-// }
-// };
-// s.run();
-// }
+ public void sortValuesReverse(boolean stable) {
+ sortImpl(false, true, stable);
+ }
- protected void sortImpl(final boolean useKeys, final boolean reverse) {
+ protected void sortImpl(final boolean useKeys, final boolean reverse,
+ final boolean stable) {
Sort s = new Sort() {
@Override
public int size() {
@@ -731,11 +625,11 @@ public class FloatDict {
if (useKeys) {
diff = keys[a].compareToIgnoreCase(keys[b]);
if (diff == 0) {
- return values[a] - values[b];
+ diff = values[a] - values[b];
}
} else { // sort values
diff = values[a] - values[b];
- if (diff == 0) {
+ if (diff == 0 && stable) {
diff = keys[a].compareToIgnoreCase(keys[b]);
}
}
@@ -748,18 +642,24 @@ public class FloatDict {
}
};
s.run();
+
+ // Set the indices after sort/swaps (performance fix 160411)
+ indices = new HashMap();
+ for (int i = 0; i < count; i++) {
+ indices.put(keys[i], i);
+ }
}
/**
* Sum all of the values in this dictionary, then return a new FloatDict of
* each key, divided by the total sum. The total for all values will be ~1.0.
- * @return a Dict with the original keys, mapped to their pct of the total
+ * @return a FloatDict with the original keys, mapped to their pct of the total
*/
public FloatDict getPercent() {
double sum = 0;
- for (float value : valueArray()) {
- sum += value;
+ for (int i = 0; i < count; i++) {
+ sum += values[i];
}
FloatDict outgoing = new FloatDict();
for (int i = 0; i < size(); i++) {
diff --git a/core/src/processing/data/IntDict.java b/core/src/processing/data/IntDict.java
index d33f22130..316f4a73d 100644
--- a/core/src/processing/data/IntDict.java
+++ b/core/src/processing/data/IntDict.java
@@ -133,74 +133,20 @@ public class IntDict {
}
-// private void crop() {
-// if (count != keys.length) {
-// keys = PApplet.subset(keys, 0, count);
-// values = PApplet.subset(values, 0, count);
-// }
-// }
+ protected void crop() {
+ if (count != keys.length) {
+ keys = PApplet.subset(keys, 0, count);
+ values = PApplet.subset(values, 0, count);
+ }
+ }
- /**
- * Return the internal array being used to store the keys. Allocated but
- * unused entries will be removed. This array should not be modified.
- *
- * @webref intdict:method
- * @brief Return the internal array being used to store the keys
- */
-// public String[] keys() {
-// crop();
-// return keys;
-// }
-
-
-// public Iterable keys() {
-// return new Iterable() {
-//
-// @Override
-// public Iterator iterator() {
-// return new Iterator() {
-// int index = -1;
-//
-// public void remove() {
-// removeIndex(index);
-// }
-//
-// public String next() {
-// return key(++index);
-// }
-//
-// public boolean hasNext() {
-// return index+1 < size();
-// }
-// };
-// }
-// };
-// }
-
-
- // Use this with 'for' loops
public Iterable keys() {
return new Iterable() {
@Override
public Iterator iterator() {
return keyIterator();
-// return new Iterator() {
-// int index = -1;
-//
-// public void remove() {
-// removeIndex(index);
-// }
-//
-// public String next() {
-// return key(++index);
-// }
-//
-// public boolean hasNext() {
-// return index+1 < size();
-// }
-// };
}
};
}
@@ -233,6 +179,7 @@ public class IntDict {
* @brief Return a copy of the internal keys array
*/
public String[] keyArray() {
+ crop();
return keyArray(null);
}
@@ -292,6 +239,7 @@ public class IntDict {
* @brief Create a new array and copy each of the values into it
*/
public int[] valueArray() {
+ crop();
return valueArray(null);
}
@@ -428,7 +376,9 @@ public class IntDict {
// return the index of the minimum value
public int minIndex() {
- checkMinMax("minIndex");
+ //checkMinMax("minIndex");
+ if (count == 0) return -1;
+
int index = 0;
int value = values[0];
for (int i = 1; i < count; i++) {
@@ -441,23 +391,30 @@ public class IntDict {
}
- // return the minimum value
+ // return the key for the minimum value
+ public String minKey() {
+ checkMinMax("minKey");
+ int index = minIndex();
+ if (index == -1) {
+ return null;
+ }
+ return keys[index];
+ }
+
+
+ // return the minimum value, or throw an error if there are no values
public int minValue() {
checkMinMax("minValue");
return values[minIndex()];
}
- // return the key for the minimum value
- public String minKey() {
- checkMinMax("minKey");
- return keys[minIndex()];
- }
-
-
// return the index of the max value
public int maxIndex() {
- checkMinMax("maxIndex");
+ //checkMinMax("maxIndex");
+ if (count == 0) {
+ return -1;
+ }
int index = 0;
int value = values[0];
for (int i = 1; i < count; i++) {
@@ -470,17 +427,21 @@ public class IntDict {
}
- // return the maximum value
- public int maxValue() {
- checkMinMax("maxValue");
- return values[maxIndex()];
+ /** return the key corresponding to the maximum value or null if no entries */
+ public String maxKey() {
+ //checkMinMax("maxKey");
+ int index = maxIndex();
+ if (index == -1) {
+ return null;
+ }
+ return keys[index];
}
- // return the key corresponding to the maximum value
- public String maxKey() {
- checkMinMax("maxKey");
- return keys[maxIndex()];
+ // return the maximum value or throw an error if zero length
+ public int maxValue() {
+ checkMinMax("maxIndex");
+ return values[maxIndex()];
}
@@ -541,8 +502,8 @@ public class IntDict {
keys[b] = tkey;
values[b] = tvalue;
- indices.put(keys[a], Integer.valueOf(a));
- indices.put(keys[b], Integer.valueOf(b));
+// indices.put(keys[a], Integer.valueOf(a));
+// indices.put(keys[b], Integer.valueOf(b));
}
@@ -554,7 +515,7 @@ public class IntDict {
* @brief Sort the keys alphabetically
*/
public void sortKeys() {
- sortImpl(true, false);
+ sortImpl(true, false, true);
}
/**
@@ -562,10 +523,10 @@ public class IntDict {
* tie-breaker (only really possible with a key that has a case change).
*
* @webref intdict:method
- * @brief Sort the keys alphabetially in reverse
+ * @brief Sort the keys alphabetically in reverse
*/
public void sortKeysReverse() {
- sortImpl(true, true);
+ sortImpl(true, true, true);
}
@@ -576,9 +537,20 @@ public class IntDict {
* @brief Sort by values in ascending order
*/
public void sortValues() {
- sortImpl(false, false);
+ sortValues(true);
}
+
+ /**
+ * Set true to ensure that the order returned is identical. Slightly
+ * slower because the tie-breaker for identical values compares the keys.
+ * @param stable
+ */
+ public void sortValues(boolean stable) {
+ sortImpl(false, false, stable);
+ }
+
+
/**
* Sort by values in descending order. The largest value will be at [0].
*
@@ -586,11 +558,17 @@ public class IntDict {
* @brief Sort by values in descending order
*/
public void sortValuesReverse() {
- sortImpl(false, true);
+ sortValuesReverse(true);
}
- protected void sortImpl(final boolean useKeys, final boolean reverse) {
+ public void sortValuesReverse(boolean stable) {
+ sortImpl(false, true, stable);
+ }
+
+
+ protected void sortImpl(final boolean useKeys, final boolean reverse,
+ final boolean stable) {
Sort s = new Sort() {
@Override
public int size() {
@@ -603,11 +581,11 @@ public class IntDict {
if (useKeys) {
diff = keys[a].compareToIgnoreCase(keys[b]);
if (diff == 0) {
- return values[a] - values[b];
+ diff = values[a] - values[b];
}
} else { // sort values
diff = values[a] - values[b];
- if (diff == 0) {
+ if (diff == 0 && stable) {
diff = keys[a].compareToIgnoreCase(keys[b]);
}
}
@@ -620,18 +598,24 @@ public class IntDict {
}
};
s.run();
+
+ // Set the indices after sort/swaps (performance fix 160411)
+ indices = new HashMap();
+ for (int i = 0; i < count; i++) {
+ indices.put(keys[i], i);
+ }
}
/**
* Sum all of the values in this dictionary, then return a new FloatDict of
* each key, divided by the total sum. The total for all values will be ~1.0.
- * @return a Dict with the original keys, mapped to their pct of the total
+ * @return an IntDict with the original keys, mapped to their pct of the total
*/
public FloatDict getPercent() {
double sum = 0;
- for (int value : valueArray()) {
- sum += value;
+ for (int i = 0; i < count; i++) {
+ sum += values[i];
}
FloatDict outgoing = new FloatDict();
for (int i = 0; i < size(); i++) {
@@ -655,6 +639,13 @@ public class IntDict {
}
+ public void print() {
+ for (int i = 0; i < size(); i++) {
+ System.out.println(keys[i] + " = " + values[i]);
+ }
+ }
+
+
/**
* Write tab-delimited entries out to
* @param writer
@@ -667,13 +658,6 @@ public class IntDict {
}
- public void print() {
- for (int i = 0; i < size(); i++) {
- System.out.println(keys[i] + " = " + values[i]);
- }
- }
-
-
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/core/src/processing/data/StringDict.java b/core/src/processing/data/StringDict.java
index 69fb4b716..330d362c9 100644
--- a/core/src/processing/data/StringDict.java
+++ b/core/src/processing/data/StringDict.java
@@ -142,39 +142,32 @@ public class StringDict {
}
-// /**
-// * Return the internal array being used to store the keys. Allocated but
-// * unused entries will be removed. This array should not be modified.
-// */
-// public String[] keys() {
-// crop();
-// return keys;
-// }
-
- /**
- * @webref stringdict:method
- * @brief Return the internal array being used to store the keys
- */
public Iterable keys() {
return new Iterable() {
@Override
public Iterator iterator() {
- return new Iterator() {
- int index = -1;
+ return keyIterator();
+ }
+ };
+ }
- public void remove() {
- removeIndex(index);
- }
- public String next() {
- return key(++index);
- }
+ // Use this to iterate when you want to be able to remove elements along the way
+ public Iterator keyIterator() {
+ return new Iterator() {
+ int index = -1;
- public boolean hasNext() {
- return index+1 < size();
- }
- };
+ public void remove() {
+ removeIndex(index);
+ }
+
+ public String next() {
+ return key(++index);
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
}
};
}
@@ -187,6 +180,7 @@ public class StringDict {
* @brief Return a copy of the internal keys array
*/
public String[] keyArray() {
+ crop();
return keyArray(null);
}
@@ -213,21 +207,26 @@ public class StringDict {
@Override
public Iterator iterator() {
- return new Iterator() {
- int index = -1;
+ return valueIterator();
+ }
+ };
+ }
- public void remove() {
- removeIndex(index);
- }
- public String next() {
- return value(++index);
- }
+ public Iterator valueIterator() {
+ return new Iterator() {
+ int index = -1;
- public boolean hasNext() {
- return index+1 < size();
- }
- };
+ public void remove() {
+ removeIndex(index);
+ }
+
+ public String next() {
+ return value(++index);
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
}
};
}
@@ -240,6 +239,7 @@ public class StringDict {
* @brief Create a new array and copy each of the values into it
*/
public String[] valueArray() {
+ crop();
return valueArray(null);
}
@@ -357,8 +357,8 @@ public class StringDict {
keys[b] = tkey;
values[b] = tvalue;
- indices.put(keys[a], Integer.valueOf(a));
- indices.put(keys[b], Integer.valueOf(b));
+// indices.put(keys[a], Integer.valueOf(a));
+// indices.put(keys[b], Integer.valueOf(b));
}
@@ -375,7 +375,7 @@ public class StringDict {
/**
* @webref stringdict:method
- * @brief Sort the keys alphabetially in reverse
+ * @brief Sort the keys alphabetically in reverse
*/
public void sortKeysReverse() {
sortImpl(true, true);
@@ -432,6 +432,12 @@ public class StringDict {
}
};
s.run();
+
+ // Set the indices after sort/swaps (performance fix 160411)
+ indices = new HashMap();
+ for (int i = 0; i < count; i++) {
+ indices.put(keys[i], i);
+ }
}
@@ -448,6 +454,13 @@ public class StringDict {
}
+ public void print() {
+ for (int i = 0; i < size(); i++) {
+ System.out.println(keys[i] + " = " + values[i]);
+ }
+ }
+
+
/**
* Write tab-delimited entries out to
* @param writer
@@ -460,13 +473,6 @@ public class StringDict {
}
- public void print() {
- for (int i = 0; i < size(); i++) {
- System.out.println(keys[i] + " = " + values[i]);
- }
- }
-
-
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/core/src/processing/javafx/PSurfaceFX.java b/core/src/processing/javafx/PSurfaceFX.java
index 29fa59861..9cd5baa11 100644
--- a/core/src/processing/javafx/PSurfaceFX.java
+++ b/core/src/processing/javafx/PSurfaceFX.java
@@ -25,7 +25,10 @@ package processing.javafx;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
+import java.net.URL;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import javafx.animation.Animation;
@@ -38,9 +41,14 @@ import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.event.EventType;
+import javafx.scene.Cursor;
+import javafx.scene.ImageCursor;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.canvas.Canvas;
+import javafx.scene.image.Image;
+import javafx.scene.image.PixelFormat;
+import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
@@ -329,6 +337,11 @@ public class PSurfaceFX implements PSurface {
// the stage, assign it only when it is all set up
surface.stage = stage;
}
+
+ @Override
+ public void stop() throws Exception {
+ surface.sketch.dispose();
+ }
}
@@ -352,6 +365,8 @@ public class PSurfaceFX implements PSurface {
Thread.sleep(5);
} catch (InterruptedException e) { }
}
+
+ setProcessingIcon(stage);
}
@@ -390,7 +405,42 @@ public class PSurfaceFX implements PSurface {
public void setIcon(PImage icon) {
- // TODO implement this in JavaFX
+ int w = icon.pixelWidth;
+ int h = icon.pixelHeight;
+ WritableImage im = new WritableImage(w, h);
+ im.getPixelWriter().setPixels(0, 0, w, h,
+ PixelFormat.getIntArgbInstance(),
+ icon.pixels,
+ 0, w);
+
+ Stage stage = (Stage) canvas.getScene().getWindow();
+ stage.getIcons().clear();
+ stage.getIcons().add(im);
+ }
+
+
+ List iconImages;
+
+ protected void setProcessingIcon(Stage stage) {
+ // Adapted from PSurfaceAWT
+ // Note: FX chooses wrong icon size, should be fixed in Java 9, see:
+ // https://bugs.openjdk.java.net/browse/JDK-8091186
+ // Removing smaller sizes helps a bit, but big ones are downsized
+ try {
+ if (iconImages == null) {
+ iconImages = new ArrayList<>();
+ final int[] sizes = { 48, 64, 128, 256, 512 };
+
+ for (int sz : sizes) {
+ URL url = PApplet.class.getResource("/icon/icon-" + sz + ".png");
+ Image image = new Image(url.toString());
+ iconImages.add(image);
+ }
+ }
+ List icons = stage.getIcons();
+ icons.clear();
+ icons.addAll(iconImages);
+ } catch (Exception e) { } // harmless; keep this to ourselves
}
@@ -590,28 +640,45 @@ public class PSurfaceFX implements PSurface {
// canvas.requestFocus();
// }
+ Cursor lastCursor = Cursor.DEFAULT;
public void setCursor(int kind) {
- // TODO Auto-generated method stub
-
+ Cursor c;
+ switch (kind) {
+ case PConstants.ARROW: c = Cursor.DEFAULT; break;
+ case PConstants.CROSS: c = Cursor.CROSSHAIR; break;
+ case PConstants.HAND: c = Cursor.HAND; break;
+ case PConstants.MOVE: c = Cursor.MOVE; break;
+ case PConstants.TEXT: c = Cursor.TEXT; break;
+ case PConstants.WAIT: c = Cursor.WAIT; break;
+ default: c = Cursor.DEFAULT; break;
+ }
+ lastCursor = c;
+ canvas.getScene().setCursor(c);
}
public void setCursor(PImage image, int hotspotX, int hotspotY) {
- // TODO Auto-generated method stub
-
+ int w = image.pixelWidth;
+ int h = image.pixelHeight;
+ WritableImage im = new WritableImage(w, h);
+ im.getPixelWriter().setPixels(0, 0, w, h,
+ PixelFormat.getIntArgbInstance(),
+ image.pixels,
+ 0, w);
+ ImageCursor c = new ImageCursor(im, hotspotX, hotspotY);
+ lastCursor = c;
+ canvas.getScene().setCursor(c);
}
public void showCursor() {
- // TODO Auto-generated method stub
-
+ canvas.getScene().setCursor(lastCursor);
}
public void hideCursor() {
- // TODO Auto-generated method stub
-
+ canvas.getScene().setCursor(Cursor.NONE);
}
diff --git a/core/src/processing/opengl/FontTexture.java b/core/src/processing/opengl/FontTexture.java
index c4c207b73..af28ad9f3 100644
--- a/core/src/processing/opengl/FontTexture.java
+++ b/core/src/processing/opengl/FontTexture.java
@@ -149,15 +149,9 @@ class FontTexture implements PConstants {
} else if (resize) {
// Replacing old smaller texture with larger one.
// But first we must copy the contents of the older
- // texture into the new one. Setting blend mode to
- // REPLACE to preserve color of transparent pixels.
+ // texture into the new one.
Texture tex0 = textures[lastTex];
-
- tex.pg.pushStyle();
- tex.pg.blendMode(REPLACE);
tex.put(tex0);
- tex.pg.popStyle();
-
textures[lastTex] = tex;
pg.setCache(images[lastTex], tex);
diff --git a/core/src/processing/opengl/PGL.java b/core/src/processing/opengl/PGL.java
index 8b471751c..96d7929b7 100644
--- a/core/src/processing/opengl/PGL.java
+++ b/core/src/processing/opengl/PGL.java
@@ -942,6 +942,8 @@ public abstract class PGL {
protected void restoreFirstFrame() {
+ if (firstFrame == null) return;
+
IntBuffer tex = allocateIntBuffer(1);
genTextures(1, tex);
diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java
index 2e11c9b03..2903fa561 100644
--- a/core/src/processing/opengl/PGraphicsOpenGL.java
+++ b/core/src/processing/opengl/PGraphicsOpenGL.java
@@ -12481,8 +12481,8 @@ public class PGraphicsOpenGL extends PGraphics {
} else {
texCache.setLastIndex(lastIndex, lastCache);
}
+ prevTexImage = newTexImage;
}
- prevTexImage = newTexImage;
}
// -----------------------------------------------------------------
diff --git a/core/src/processing/opengl/Texture.java b/core/src/processing/opengl/Texture.java
index 5d8a2a8d3..c2f75f3f2 100644
--- a/core/src/processing/opengl/Texture.java
+++ b/core/src/processing/opengl/Texture.java
@@ -1224,10 +1224,10 @@ public class Texture implements PConstants {
// FBO copy:
pg.pushFramebuffer();
pg.setFramebuffer(tempFbo);
- // Clear the color buffer to make sure that the alpha channel is set to
- // full transparency
- pgl.clearColor(0, 0, 0, 0);
- pgl.clear(PGL.COLOR_BUFFER_BIT);
+ // Replaces anything that this texture might contain in the area being
+ // replaced by the new one.
+ pg.pushStyle();
+ pg.blendMode(REPLACE);
if (scale) {
// Rendering tex into "this", and scaling the source rectangle
// to cover the entire destination region.
@@ -1243,8 +1243,10 @@ public class Texture implements PConstants {
0, 0, tempFbo.width, tempFbo.height, 1,
x, y, x + w, y + h, x, y, x + w, y + h);
}
+ pgl.flush(); // Needed to make sure that the change in this texture is
+ // available immediately.
+ pg.popStyle();
pg.popFramebuffer();
-
updateTexels(x, y, w, h);
}
@@ -1264,6 +1266,10 @@ public class Texture implements PConstants {
// FBO copy:
pg.pushFramebuffer();
pg.setFramebuffer(tempFbo);
+ // Replaces anything that this texture might contain in the area being
+ // replaced by the new one.
+ pg.pushStyle();
+ pg.blendMode(REPLACE);
if (scale) {
// Rendering tex into "this", and scaling the source rectangle
// to cover the entire destination region.
@@ -1279,6 +1285,9 @@ public class Texture implements PConstants {
0, 0, tempFbo.width, tempFbo.height,
x, y, w, h, x, y, w, h);
}
+ pgl.flush(); // Needed to make sure that the change in this texture is
+ // available immediately.
+ pg.popStyle();
pg.popFramebuffer();
updateTexels(x, y, w, h);
}
diff --git a/core/todo.txt b/core/todo.txt
index 87ac0fbba..c93f6f7b2 100644
--- a/core/todo.txt
+++ b/core/todo.txt
@@ -1,10 +1,40 @@
0249 (3.0.3)
+X Float/IntDict changes
+X minIndex() and maxIndex() return -1 when count is zero (rather than ex)
+X bug fix to reverse sorts
+X 2x performance increase for sorting
+X added optional "stable" parameter for sorting by values
+X set to false for higher performance
+X call crop() in keyArray() and valueArray() functions
+X they're duplicates, their length is an implementation detail
+X normalize features and error handling between all of them
_ add push() and pop() methods to mirror js?
+jakub
+X several JavaFX fixes
+X https://github.com/processing/processing/pull/4411
+X cursor() and noCursor() not working on FX2D
+X https://github.com/processing/processing/issues/4405
+X Make sure PImage.parent is set in loadImage()
+X https://github.com/processing/processing/pull/4412
+
andres
X Change convention for directional lights in OpenGL-Binding for GLSL
X https://github.com/processing/processing/issues/4275
+X internal texture copy does not update immediately
+X https://github.com/processing/processing/issues/4404
+X Font corruption issue in OpenGL
+X https://github.com/processing/processing/issues/4392
+X setStroke() does not work with imported OBJ Pshapes
+X https://github.com/processing/processing/issues/4377
+
+contribs
+X blendMode() resetting with getGraphics()
+X https://github.com/processing/processing/issues/4019
+X https://github.com/processing/processing/pull/4341
+X https://github.com/processing/processing/issues/4376
+
_ textAlign(CENTER) and pixelDensity(2) aligning incorrectly with Java2D
_ https://github.com/processing/processing/issues/4020
diff --git a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java
index 4d891893b..24a69f857 100644
--- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java
+++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java
@@ -301,20 +301,17 @@ public class JavaTextAreaPainter extends TextAreaPainter
return;
}
- // Take care of offsets
- int aw = fm.stringWidth(trimRight(badCode)) + textArea.getHorizontalOffset();
- // to the left of line + text
- // width
- int rw = fm.stringWidth(badCode.trim()); // real width
- int x1 = fm.stringWidth(goodCode) + (aw - rw);
- int y1 = y + fm.getHeight() - 2, x2 = x1 + rw;
+ int trimmedLength = badCode.trim().length();
+ int rightTrimmedLength = trimRight(badCode).length();
+ int leftTrimLength = rightTrimmedLength - trimmedLength;
+
+ int x1 = textArea.offsetToX(line, goodCode.length() + leftTrimLength);
+ int x2 = textArea.offsetToX(line, goodCode.length() + rightTrimmedLength);
+ int y1 = y + fm.getHeight() - 2;
if (line != problem.getLineNumber()) {
- x1 = 0; // on the following lines, wiggle extends to the left border
+ x1 = Editor.LEFT_GUTTER; // on the following lines, wiggle extends to the left border
}
- // Adding offsets for the gutter
- x1 += Editor.LEFT_GUTTER;
- x2 += Editor.LEFT_GUTTER;
gfx.setColor(errorUnderlineColor);
if (problem.isWarning()) {
diff --git a/todo.txt b/todo.txt
index e7a03b40d..34c716156 100644
--- a/todo.txt
+++ b/todo.txt
@@ -10,6 +10,9 @@ X https://github.com/processing/processing/pull/4325
X Fix non-ARM Linux deb build process
X https://github.com/processing/processing/issues/4308
X https://github.com/processing/processing/pull/4309
+_ processing-java output as UTF-8 makes Windows unhappy
+_ https://github.com/processing/processing/issues/1633
+_ includes possible fix for Windows
jakub
X Update app to Java 8
@@ -22,14 +25,20 @@ X update JDT to 4.5.2
X https://github.com/processing/processing/pull/4387
X JavaMode cleanup
X https://github.com/processing/processing/pull/4390
+X tabs aren't working properly (several bugs?)
+X https://github.com/processing/processing/issues/3975
+X https://github.com/processing/processing/pull/4410
+X file paths not decoding properly
+X https://github.com/processing/processing/issues/4417
+X https://github.com/processing/processing/pull/4426
+_ double check that this is working on OS X
+_ arrow keys aren't working in the examples window
+
_ createPreprocessor() added to JavaEditor
https://github.com/processing/processing/commit/2ecdc36ac7c680eb36e271d17ad80b657b3ae6a0
-_ tabs aren't working properly (several bugs?)
-_ https://github.com/processing/processing/issues/3975
-
_ possible infinite loop on modified externally
_ https://github.com/processing/processing/issues/3965
@@ -871,8 +880,6 @@ DIST / Windows
_ PDE and sketches are 2x smaller on high-res Windows 8 machines
_ https://github.com/processing/processing/issues/2411
-_ processing-java output as UTF-8 makes Windows unhappy
-_ https://github.com/processing/processing/issues/1633
_ does launching p5 from inside the .zip folder cause it to quit immediately?
_ how can we provide an error message here?
_ how to handle double-clicked files on windows?