diff --git a/app/src/processing/app/Problem.java b/app/src/processing/app/Problem.java new file mode 100644 index 000000000..7cd93db51 --- /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 000000000..02ef7c3f0 --- /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 000000000..7547fb450 --- /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 58f5025fd..120469001 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -30,6 +30,7 @@ import processing.app.Messages; 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.Util; 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.awt.print.*; 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 abstract class Editor extends JFrame implements RunnerListener { } + public PdeTextArea getPdeTextArea() { + return (PdeTextArea) textarea; + } + + /** * Get the contents of the current buffer. Used by the Sketch class. */ @@ -2772,12 +2787,22 @@ public abstract class Editor extends JFrame implements RunnerListener { * 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 abstract class Editor extends JFrame implements RunnerListener { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + 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 6c4691de0..14d02e11c 100644 --- a/app/src/processing/app/ui/ErrorTable.java +++ b/app/src/processing/app/ui/ErrorTable.java @@ -39,17 +39,13 @@ import javax.swing.table.TableColumn; 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 class ErrorTable extends JTable { } - 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 class ErrorTable extends JTable { 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 9272a0840..74a08fb1b 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -11,7 +11,6 @@ import java.util.List; 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.PreprocessingService; 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 class JavaEditor extends Editor { */ - 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 class JavaEditor extends Editor { } -// /** 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 1e29c75b6..2de540b7e 100644 --- a/java/src/processing/mode/java/MarkerColumn.java +++ b/java/src/processing/mode/java/MarkerColumn.java @@ -34,11 +34,12 @@ import java.util.stream.Collectors; 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 @@ import processing.mode.java.pdex.Problem; * 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 class MarkerColumn extends JPanel { @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 class MarkerColumn extends JPanel { } - 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 @@ public class MarkerColumn extends JPanel { } - /* - @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 a8e638745..6b355db48 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.TypeDeclaration; 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 806170898..df7909e8e 100644 --- a/java/src/processing/mode/java/pdex/ErrorMessageSimplifier.java +++ b/java/src/processing/mode/java/pdex/ErrorMessageSimplifier.java @@ -85,7 +85,7 @@ public class ErrorMessageSimplifier { /** * 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 8d8fac2e0..bb4da8898 100644 --- a/java/src/processing/mode/java/pdex/Problem.java +++ b/java/src/processing/mode/java/pdex/JavaProblem.java @@ -25,14 +25,14 @@ import java.util.regex.Pattern; 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 9eb57fb99..71bdd08ac 100644 --- a/java/src/processing/mode/java/pdex/JavaTextArea.java +++ b/java/src/processing/mode/java/pdex/JavaTextArea.java @@ -20,30 +20,29 @@ along with this program; if not, write to the Free Software Foundation, Inc. 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 class JavaTextArea extends PdeTextArea { 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 @@ public class JavaTextArea extends PdeTextArea { } // 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 class JavaTextArea extends PdeTextArea { /** - * 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 class JavaTextArea extends PdeTextArea { // 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 class JavaTextArea extends PdeTextArea { 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 class JavaTextArea extends PdeTextArea { 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 class JavaTextArea extends PdeTextArea { } - 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 d9724e3b7..51164a8a3 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -23,7 +23,6 @@ package processing.mode.java.pdex; 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.BadLocationException; 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 @@ public class JavaTextAreaPainter extends PdeTextAreaPainter { /** * 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 @@ public class JavaTextAreaPainter extends PdeTextAreaPainter { 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 b2ebd32c8..6182faed3 100644 --- a/java/src/processing/mode/java/pdex/PDEX.java +++ b/java/src/processing/mode/java/pdex/PDEX.java @@ -72,6 +72,7 @@ import javax.swing.tree.TreeModel; 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 @@ public class PDEX { 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 @@ public class PDEX { 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 @@ public class PDEX { 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)); }); } }