diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 5ee01aaa6..82a949ccc 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -85,30 +85,6 @@ public class JavaEditor extends Editor { debugger = new Debugger(this); inspector = new VariableInspector(this); - // Add show usage option - JMenuItem showUsageItem = new JMenuItem(Language.text("editor.popup.show_usage")); - showUsageItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleShowUsage(); - } - }); - getTextArea().getRightClickPopup().add(showUsageItem); - - // add refactor option - JMenuItem renameItem = new JMenuItem(Language.text("editor.popup.rename")); - renameItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleRefactor(); - } - }); - - // TODO: Add support for word select on right click and rename. - // ta.customPainter.addMouseListener(new MouseAdapter() { - // public void mouseClicked(MouseEvent evt) { - // System.out.println(evt); - // } - // }); - textarea.getRightClickPopup().add(renameItem); // set action on frame close // addWindowListener(new WindowAdapter() { // @Override @@ -117,7 +93,6 @@ public class JavaEditor extends Editor { // } // }); - Toolkit.setMenuMnemonics(textarea.getRightClickPopup()); // // load settings from theme.txt // breakpointColor = mode.getColor("breakpoint.bgcolor"); @@ -153,6 +128,8 @@ public class JavaEditor extends Editor { initPDEX(); + Toolkit.setMenuMnemonics(textarea.getRightClickPopup()); + // ensure completion is hidden when editor loses focus addWindowFocusListener(new WindowFocusListener() { public void windowLostFocus(WindowEvent e) { @@ -173,60 +150,11 @@ public class JavaEditor extends Editor { public PdePreprocessor createPreprocessor(final String sketchName) { return new PdePreprocessor(sketchName); } - + protected void initPDEX() { preprocessingService = new PreprocessingService(this); pdex = new PDEX(this, preprocessingService); - - // Add ctrl+click listener - getJavaTextArea().getPainter().addMouseListener(new MouseAdapter() { - public void mouseReleased(MouseEvent evt) { - if (evt.getButton() == MouseEvent.BUTTON1) { - if ((evt.isControlDown() && !Platform.isMacOS()) || evt.isMetaDown()) { - handleCtrlClick(evt); - } - } else if (evt.getButton() == MouseEvent.BUTTON2) { - handleCtrlClick(evt); - } - } - }); - - sketchChanged(); - - for (SketchCode code : getSketch().getCode()) { - Document document = code.getDocument(); - addDocumentListener(document); - } - } - - - public void addDocumentListener(Document doc) { - if (doc != null) doc.addDocumentListener(sketchChangedListener); - } - - - protected final DocumentListener sketchChangedListener = new DocumentListener() { - @Override - public void insertUpdate(DocumentEvent e) { - sketchChanged(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - sketchChanged(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - sketchChanged(); - } - }; - - - protected void sketchChanged() { - pdex.notifySketchChanged(); - preprocessingService.notifySketchChanged(); } @@ -257,7 +185,7 @@ public class JavaEditor extends Editor { if (preprocessingService != null) { if (hasJavaTabsChanged) { preprocessingService.handleHasJavaTabsChange(hasJavaTabs); - pdex.handleHasJavaTabsChange(hasJavaTabs); + pdex.hasJavaTabsChanged(hasJavaTabs); if (hasJavaTabs) { setProblemList(Collections.emptyList()); } @@ -266,7 +194,7 @@ public class JavaEditor extends Editor { int currentTabCount = sketch.getCodeCount(); if (currentTabCount != previousTabCount) { previousTabCount = currentTabCount; - sketchChanged(); + pdex.sketchChanged(); } } } @@ -2298,8 +2226,8 @@ public class JavaEditor extends Editor { super.setCode(code); Document newDoc = code.getDocument(); - if (oldDoc != newDoc && preprocessingService != null) { - addDocumentListener(newDoc); + if (oldDoc != newDoc && pdex != null) { + pdex.documentChanged(newDoc); } // set line background colors for tab @@ -2717,36 +2645,6 @@ public class JavaEditor extends Editor { } - /** Handle refactor operation */ - private void handleRefactor() { - int startOffset = getSelectionStart(); - int stopOffset = getSelectionStop(); - int tabIndex = sketch.getCurrentCodeIndex(); - - pdex.handleRename(tabIndex, startOffset, stopOffset); - } - - - /** Handle show usage operation */ - private void handleShowUsage() { - int startOffset = getSelectionStart(); - int stopOffset = getSelectionStop(); - int tabIndex = sketch.getCurrentCodeIndex(); - - pdex.handleShowUsage(tabIndex, startOffset, stopOffset); - } - - - /** Handle ctrl+click */ - private void handleCtrlClick(MouseEvent evt) { - int off = getJavaTextArea().xyToOffset(evt.getX(), evt.getY()); - if (off < 0) return; - int tabIndex = sketch.getCurrentCodeIndex(); - - pdex.handleCtrlClick(tabIndex, off); - } - - public boolean hasJavaTabs() { return hasJavaTabs; } @@ -2781,7 +2679,7 @@ public class JavaEditor extends Editor { jmode.loadPreferences(); Messages.log("Applying prefs"); // trigger it once to refresh UI - sketchChanged(); + pdex.preferencesChanged(); } } diff --git a/java/src/processing/mode/java/pdex/PDEX.java b/java/src/processing/mode/java/pdex/PDEX.java index fa6e5efd3..8acfa5d6d 100644 --- a/java/src/processing/mode/java/pdex/PDEX.java +++ b/java/src/processing/mode/java/pdex/PDEX.java @@ -24,6 +24,10 @@ import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -47,18 +51,24 @@ import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; +import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JTree; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import javax.swing.text.BadLocationException; +import javax.swing.text.Document; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; +import processing.app.Language; import processing.app.Messages; +import processing.app.Platform; import processing.app.Sketch; import processing.app.SketchCode; import processing.app.syntax.SyntaxDocument; @@ -78,6 +88,7 @@ public class PDEX { private ErrorChecker errorChecker; + private InspectMode inspectMode; private ShowUsage showUsage; private Rename rename; private DebugTree debugTree; @@ -94,36 +105,57 @@ public class PDEX { errorChecker = new ErrorChecker(editor, pps); + inspectMode = new InspectMode(editor, pps); showUsage = new ShowUsage(editor, pps); - rename = new Rename(editor); + rename = new Rename(editor, pps); if (SHOW_DEBUG_TREE) { debugTree = new DebugTree(editor, pps); } + + for (SketchCode code : editor.getSketch().getCode()) { + Document document = code.getDocument(); + addDocumentListener(document); + } + + sketchChanged(); } - public void handleShowUsage(int tabIndex, int startTabOffset, int stopTabOffset) { - Messages.log("* handleShowUsage"); - if (!enabled) return; // show usage disabled if java tabs - pps.whenDoneBlocking(ps -> showUsage.findUsageAndUpdateTree(ps, tabIndex, startTabOffset, stopTabOffset)); + public void addDocumentListener(Document doc) { + if (doc != null) doc.addDocumentListener(sketchChangedListener); } - public void handleRename(int tabIndex, int startTabOffset, int stopTabOffset) { - Messages.log("* handleRename"); - if (!enabled) return; // refactoring disabled w/ java tabs - pps.whenDoneBlocking(ps -> rename.handleRename(ps, tabIndex, startTabOffset, stopTabOffset)); + protected final DocumentListener sketchChangedListener = new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + sketchChanged(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + sketchChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + sketchChanged(); + } + }; + + + public void sketchChanged() { + errorChecker.notifySketchChanged(); + pps.notifySketchChanged(); } - public void handleCtrlClick(int tabIndex, int offset) { - Messages.log("* handleCtrlClick"); - if (!enabled) return; // disabled w/ java tabs - pps.whenDoneBlocking(ps -> handleCtrlClick(ps, tabIndex, offset)); + public void preferencesChanged() { + sketchChanged(); } - public void handleHasJavaTabsChange(boolean hasJavaTabs) { + public void hasJavaTabsChanged(boolean hasJavaTabs) { enabled = !hasJavaTabs; if (!enabled) { showUsage.hide(); @@ -131,12 +163,8 @@ public class PDEX { } - public void notifySketchChanged() { - errorChecker.notifySketchChanged(); - } - - public void dispose() { + inspectMode.dispose(); errorChecker.dispose(); showUsage.dispose(); rename.dispose(); @@ -146,53 +174,133 @@ public class PDEX { } - // Thread: worker - private void handleCtrlClick(PreprocessedSketch ps, int tabIndex, int offset) { - ASTNode root = ps.compilationUnit; - int javaOffset = ps.tabOffsetToJavaOffset(tabIndex, offset); + public void documentChanged(Document newDoc) { + addDocumentListener(newDoc); + } - SimpleName simpleName = getSimpleNameAt(root, javaOffset, javaOffset); - if (simpleName == null) { - Messages.log("no simple name found at click location"); - return; - } + private class InspectMode { - IBinding binding = resolveBinding(simpleName); - if (binding == null) { - Messages.log("binding not resolved"); - return; - } + boolean isMouseDown; + boolean isCtrlDown; + boolean isMetaDown; + boolean inspectModeEnabled; - String key = binding.getKey(); - ASTNode decl = ps.compilationUnit.findDeclaringNode(key); - if (decl == null) { - Messages.log("decl not found, showing usage instead"); - showUsage.findUsageAndUpdateTree(ps, binding); - return; - } + JavaEditor editor; + PreprocessingService pps; - SimpleName declName = null; - switch (binding.getKind()) { - case IBinding.TYPE: declName = ((TypeDeclaration) decl).getName(); break; - case IBinding.METHOD: declName = ((MethodDeclaration) decl).getName(); break; - case IBinding.VARIABLE: declName = ((VariableDeclaration) decl).getName(); break; - } - if (declName == null) { - Messages.log("decl name not found " + decl); - return; - } + InspectMode(JavaEditor editor, PreprocessingService pps) { + this.editor = editor; + this.pps = pps; - if (declName.equals(simpleName)) { - showUsage.findUsageAndUpdateTree(ps, binding); - } else { - Messages.log("found declaration, offset " + decl.getStartPosition() + ", name: " + declName); - SketchInterval si = ps.mapJavaToSketch(declName); - if (!ps.inRange(si)) return; - EventQueue.invokeLater(() -> { - editor.highlight(si.tabIndex, si.startTabOffset, si.stopTabOffset); + // Add ctrl+click listener + editor.getJavaTextArea().getPainter().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + isMouseDown = true; + } + + @Override + public void mouseReleased(MouseEvent evt) { + isMouseDown = false; + if (inspectModeEnabled && evt.getButton() == MouseEvent.BUTTON1) { + handleInspect(evt); + } else if (!inspectModeEnabled && evt.getButton() == MouseEvent.BUTTON2) { + handleInspect(evt); + } + checkInspectMode(); + } }); + + editor.getJavaTextArea().addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + isMetaDown = isMetaDown || e.getKeyCode() == KeyEvent.VK_META; + isCtrlDown = isCtrlDown || e.getKeyCode() == KeyEvent.VK_CONTROL; + if (!inspectModeEnabled) checkInspectMode(); + } + + @Override + public void keyReleased(KeyEvent e) { + isMetaDown = isMetaDown && e.getKeyCode() != KeyEvent.VK_META; + isCtrlDown = isCtrlDown && e.getKeyCode() != KeyEvent.VK_CONTROL; + if (inspectModeEnabled) checkInspectMode(); + } + }); + } + + + void checkInspectMode() { + System.out.println(isMouseDown + " " + isCtrlDown + " " + isMetaDown); + inspectModeEnabled = !isMouseDown && (isCtrlDown && !Platform.isMacOS()) || isMetaDown; + } + + + // Thread: EDT + void handleInspect(MouseEvent evt) { + int off = editor.getJavaTextArea().xyToOffset(evt.getX(), evt.getY()); + if (off < 0) return; + int tabIndex = editor.getSketch().getCurrentCodeIndex(); + + pps.whenDoneBlocking(ps -> handleInspect(ps, tabIndex, off)); + } + + + // Thread: worker + private void handleInspect(PreprocessedSketch ps, int tabIndex, int offset) { + ASTNode root = ps.compilationUnit; + int javaOffset = ps.tabOffsetToJavaOffset(tabIndex, offset); + + SimpleName simpleName = getSimpleNameAt(root, javaOffset, javaOffset); + + if (simpleName == null) { + Messages.log("no simple name found at click location"); + return; + } + + IBinding binding = resolveBinding(simpleName); + if (binding == null) { + Messages.log("binding not resolved"); + return; + } + + String key = binding.getKey(); + ASTNode decl = ps.compilationUnit.findDeclaringNode(key); + if (decl == null) { + Messages.log("decl not found, showing usage instead"); + showUsage.findUsageAndUpdateTree(ps, binding); + return; + } + + SimpleName declName = null; + switch (binding.getKind()) { + case IBinding.TYPE: declName = ((TypeDeclaration) decl).getName(); break; + case IBinding.METHOD: declName = ((MethodDeclaration) decl).getName(); break; + case IBinding.VARIABLE: declName = ((VariableDeclaration) decl).getName(); break; + } + if (declName == null) { + Messages.log("decl name not found " + decl); + return; + } + + if (declName.equals(simpleName)) { + showUsage.findUsageAndUpdateTree(ps, binding); + } else { + Messages.log("found declaration, offset " + decl.getStartPosition() + ", name: " + declName); + SketchInterval si = ps.mapJavaToSketch(declName); + if (!ps.inRange(si)) return; + EventQueue.invokeLater(() -> { + editor.highlight(si.tabIndex, si.startTabOffset, si.stopTabOffset); + }); + } + } + + + void dispose() { + // Nothing to do + } + } @@ -214,6 +322,11 @@ public class PDEX { this.editor = editor; // this.pps = pps; + // Add show usage option + JMenuItem showUsageItem = new JMenuItem(Language.text("editor.popup.show_usage")); + showUsageItem.addActionListener(e -> handleShowUsage()); + editor.getTextArea().getRightClickPopup().add(showUsageItem); + reloadListener = this::reloadShowUsage; { // Show Usage window @@ -265,9 +378,19 @@ public class PDEX { } + // Thread: EDT + void handleShowUsage() { + int startOffset = editor.getSelectionStart(); + int stopOffset = editor.getSelectionStop(); + int tabIndex = editor.getSketch().getCurrentCodeIndex(); + + pps.whenDoneBlocking(ps -> handleShowUsage(ps, tabIndex, startOffset, stopOffset)); + } + + // Thread: worker - void findUsageAndUpdateTree(PreprocessedSketch ps, int tabIndex, - int startTabOffset, int stopTabOffset) { + void handleShowUsage(PreprocessedSketch ps, int tabIndex, + int startTabOffset, int stopTabOffset) { // Map offsets int startJavaOffset = ps.tabOffsetToJavaOffset(tabIndex, startTabOffset); int stopJavaOffset = ps.tabOffsetToJavaOffset(tabIndex, stopTabOffset); @@ -484,13 +607,21 @@ public class PDEX { final JLabel oldNameLabel; final JavaEditor editor; + final PreprocessingService pps; IBinding binding; PreprocessedSketch ps; - Rename(JavaEditor editor) { + Rename(JavaEditor editor, PreprocessingService pps) { this.editor = editor; + this.pps = pps; + + // Add rename option + JMenuItem renameItem = new JMenuItem(Language.text("editor.popup.rename")); + renameItem.addActionListener(e -> handleRename()); + editor.getTextArea().getRightClickPopup().add(renameItem); + window = new JDialog(editor); window.setTitle("Enter new name:"); @@ -567,6 +698,15 @@ public class PDEX { } + // Thread: EDT + void handleRename() { + int startOffset = editor.getSelectionStart(); + int stopOffset = editor.getSelectionStop(); + int tabIndex = editor.getSketch().getCurrentCodeIndex(); + + pps.whenDoneBlocking(ps -> handleRename(ps, tabIndex, startOffset, stopOffset)); + } + // Thread: worker void handleRename(PreprocessedSketch ps, int tabIndex, int startTabOffset, int stopTabOffset) {