diff --git a/app/src/processing/app/syntax/PdeTextArea.java b/app/src/processing/app/syntax/PdeTextArea.java index 02ef7c3f0..43b403107 100644 --- a/app/src/processing/app/syntax/PdeTextArea.java +++ b/app/src/processing/app/syntax/PdeTextArea.java @@ -66,7 +66,13 @@ public class PdeTextArea extends JEditTextArea { } - 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 7547fb450..757d6f977 100644 --- a/app/src/processing/app/syntax/PdeTextAreaPainter.java +++ b/app/src/processing/app/syntax/PdeTextAreaPainter.java @@ -22,13 +22,26 @@ package processing.app.syntax; 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 class PdeTextAreaPainter extends TextAreaPainter { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + /** + * 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 71bdd08ac..3a1bcd415 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.ColorControlBox; 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 @@ public class JavaTextArea extends PdeTextArea { /** * 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 @@ public class JavaTextArea extends PdeTextArea { * @param evt - the KeyEvent which triggered this method */ protected void fetchPhrase() { - if (suggestionRunning) { suggestionRequested = true; return; @@ -345,8 +348,8 @@ public class JavaTextArea extends PdeTextArea { }); } - 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 @@ public class JavaTextArea extends PdeTextArea { 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 @@ public class JavaTextArea extends PdeTextArea { 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 51164a8a3..7859c1e73 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -20,9 +20,6 @@ along with this program; if not, write to the Free Software Foundation, Inc. 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.MouseListener; 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 class JavaTextAreaPainter extends PdeTextAreaPainter { 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); - } + // TWEAK MODE - /** - * 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); - - 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 @@ public class JavaTextAreaPainter extends PdeTextAreaPainter { } - 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 @@ public class JavaTextAreaPainter extends PdeTextAreaPainter { * 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 @@ public class JavaTextAreaPainter extends PdeTextAreaPainter { } - private JavaTextArea getJavaTextArea() { - return (JavaTextArea) textArea; - } - - private int getCurrentCodeIndex() { return getEditor().getSketch().getCurrentCodeIndex(); }