mirror of
https://github.com/arduino/Arduino.git
synced 2025-01-18 07:52:14 +01:00
new editor based on RSyntaxTextArea
This commit is contained in:
parent
63f153c0c2
commit
1379505e13
36
app/src/ArduinoIDE.java
Normal file
36
app/src/ArduinoIDE.java
Normal file
@ -0,0 +1,36 @@
|
||||
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
Part of the Processing project - http://processing.org
|
||||
|
||||
Copyright (c) 2004-10 Ben Fry and Casey Reas
|
||||
Copyright (c) 2001-04 Massachusetts Institute of Technology
|
||||
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
import processing.app.Base;
|
||||
|
||||
/**
|
||||
* Arduino IDE Launcher class
|
||||
* @author Ricardo JL Rufino (ricardo@criativasoft.com.br)
|
||||
* @date 23/01/2015
|
||||
*/
|
||||
public class ArduinoIDE {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Base.main(args);
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
* This file is part of Arduino.
|
||||
*
|
||||
@ -29,16 +31,18 @@
|
||||
|
||||
package cc.arduino.packages.formatter;
|
||||
|
||||
import processing.app.Base;
|
||||
import processing.app.Editor;
|
||||
import processing.app.helpers.FileUtils;
|
||||
import processing.app.syntax.JEditTextArea;
|
||||
import processing.app.tools.Tool;
|
||||
import static processing.app.I18n._;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static processing.app.I18n._;
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import processing.app.Base;
|
||||
import processing.app.Editor;
|
||||
import processing.app.helpers.FileUtils;
|
||||
import processing.app.syntax.SketchTextArea;
|
||||
import processing.app.tools.Tool;
|
||||
|
||||
public class AStyle implements Tool {
|
||||
|
||||
@ -84,13 +88,17 @@ public class AStyle implements Tool {
|
||||
return;
|
||||
}
|
||||
|
||||
JEditTextArea textArea = editor.getTextArea();
|
||||
int line = textArea.getLineOfOffset(textArea.getCaretPosition());
|
||||
int lineOffset = textArea.getCaretPosition() - textArea.getLineStartOffset(line);
|
||||
|
||||
SketchTextArea textArea = editor.getTextArea();
|
||||
editor.setText(formattedText);
|
||||
editor.getSketch().setModified(true);
|
||||
textArea.setCaretPosition(Math.min(textArea.getLineStartOffset(line) + lineOffset, textArea.getSafeLineStopOffset(line) - 1));
|
||||
|
||||
try {
|
||||
int line = textArea.getLineOfOffset(textArea.getCaretPosition());
|
||||
int lineOffset = textArea.getCaretPosition() - textArea.getLineStartOffset(line);
|
||||
textArea.setCaretPosition(Math.min(textArea.getLineStartOffset(line) + lineOffset, textArea.getLineEndOffset(line) - 1));
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// mark as finished
|
||||
editor.statusNotice(_("Auto Format finished."));
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
package processing.app;
|
||||
|
||||
import processing.app.syntax.JEditTextArea;
|
||||
|
||||
import javax.swing.undo.CannotRedoException;
|
||||
import javax.swing.undo.CannotUndoException;
|
||||
import javax.swing.undo.UndoableEdit;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
|
||||
public class CaretAwareUndoableEdit implements UndoableEdit {
|
||||
|
||||
private final UndoableEdit undoableEdit;
|
||||
private final int caretPosition;
|
||||
|
||||
public CaretAwareUndoableEdit(UndoableEdit undoableEdit, JEditTextArea textArea) {
|
||||
public CaretAwareUndoableEdit(UndoableEdit undoableEdit, RSyntaxTextArea textArea) {
|
||||
this.undoableEdit = undoableEdit;
|
||||
this.caretPosition = textArea.getCaretPosition();
|
||||
}
|
||||
|
@ -51,6 +51,12 @@ import javax.swing.event.*;
|
||||
import javax.swing.text.*;
|
||||
import javax.swing.undo.*;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
|
||||
import org.fife.ui.rtextarea.Gutter;
|
||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
|
||||
import cc.arduino.packages.BoardPort;
|
||||
import cc.arduino.packages.Uploader;
|
||||
import cc.arduino.packages.uploaders.SerialUploader;
|
||||
@ -125,8 +131,8 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
//JEditorPane editorPane;
|
||||
|
||||
JEditTextArea textarea;
|
||||
EditorListener listener;
|
||||
SketchTextArea textarea;
|
||||
RTextScrollPane scrollPane;
|
||||
|
||||
// runtime information and window placement
|
||||
Point sketchWindowLocation;
|
||||
@ -144,9 +150,6 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
JMenuItem undoItem, redoItem;
|
||||
protected UndoAction undoAction;
|
||||
protected RedoAction redoAction;
|
||||
LastUndoableEditAwareUndoManager undo;
|
||||
// used internally, and only briefly
|
||||
CompoundEdit compoundEdit;
|
||||
|
||||
FindReplace find;
|
||||
|
||||
@ -231,10 +234,8 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
header = new EditorHeader(this);
|
||||
upper.add(header);
|
||||
|
||||
textarea = new JEditTextArea(new PdeTextAreaDefaults());
|
||||
textarea = createTextArea();
|
||||
textarea.setName("editor");
|
||||
textarea.setRightClickPopup(new TextAreaPopup());
|
||||
textarea.setHorizontalOffset(6);
|
||||
|
||||
// assemble console panel, consisting of status area and the console itself
|
||||
consolePanel = new JPanel();
|
||||
@ -252,9 +253,19 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
lineStatus = new EditorLineStatus(textarea);
|
||||
consolePanel.add(lineStatus, BorderLayout.SOUTH);
|
||||
|
||||
upper.add(textarea);
|
||||
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
|
||||
upper, consolePanel);
|
||||
//scrollPane = new RTextScrollPane(textarea);
|
||||
|
||||
scrollPane = new RTextScrollPane(textarea, true);
|
||||
scrollPane.setViewportBorder(BorderFactory.createEmptyBorder());
|
||||
scrollPane.setIconRowHeaderEnabled(true);
|
||||
|
||||
Gutter gutter = scrollPane.getGutter();
|
||||
gutter.setBookmarkingEnabled(false);
|
||||
//gutter.setBookmarkIcon(CompletionsRenderer.getIcon(CompletionType.TEMPLATE));
|
||||
gutter.setIconRowHeaderInheritsGutterBackground(true);
|
||||
|
||||
upper.add(scrollPane);
|
||||
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, upper, consolePanel);
|
||||
|
||||
splitPane.setOneTouchExpandable(true);
|
||||
// repaint child panes while resizing
|
||||
@ -280,7 +291,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
// hopefully these are no longer needed w/ swing
|
||||
// (har har har.. that was wishful thinking)
|
||||
listener = new EditorListener(this, textarea);
|
||||
// listener = new EditorListener(this, textarea);
|
||||
pain.add(box);
|
||||
|
||||
// get shift down/up events so we can show the alt version of toolbar buttons
|
||||
@ -439,32 +450,29 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
saveMenuItem.setEnabled(!external);
|
||||
saveAsMenuItem.setEnabled(!external);
|
||||
|
||||
textarea.setDisplayLineNumbers(PreferencesData.getBoolean("editor.linenumbers"));
|
||||
textarea.setMarginLineEnabled(PreferencesData.getBoolean("editor.linenumbers"));
|
||||
|
||||
TextAreaPainter painter = textarea.getPainter();
|
||||
if (external) {
|
||||
// disable line highlight and turn off the caret when disabling
|
||||
Color color = Theme.getColor("editor.external.bgcolor");
|
||||
painter.setBackground(color);
|
||||
painter.setLineHighlightEnabled(false);
|
||||
textarea.setCaretVisible(false);
|
||||
textarea.setBackground(color);
|
||||
textarea.setHighlightCurrentLine(false);
|
||||
textarea.setEditable(false);
|
||||
|
||||
} else {
|
||||
Color color = Theme.getColor("editor.bgcolor");
|
||||
painter.setBackground(color);
|
||||
boolean highlight = PreferencesData.getBoolean("editor.linehighlight");
|
||||
painter.setLineHighlightEnabled(highlight);
|
||||
textarea.setCaretVisible(true);
|
||||
textarea.setHighlightCurrentLine(highlight);
|
||||
textarea.setEditable(true);
|
||||
}
|
||||
|
||||
// apply changes to the font size for the editor
|
||||
//TextAreaPainter painter = textarea.getPainter();
|
||||
painter.setFont(PreferencesData.getFont("editor.font"));
|
||||
textarea.setFont(PreferencesData.getFont("editor.font"));
|
||||
//Font font = painter.getFont();
|
||||
//textarea.getPainter().setFont(new Font("Courier", Font.PLAIN, 36));
|
||||
|
||||
// in case tab expansion stuff has changed
|
||||
listener.applyPreferences();
|
||||
// listener.applyPreferences();
|
||||
|
||||
// in case moved to a new location
|
||||
// For 0125, changing to async version (to be implemented later)
|
||||
@ -943,6 +951,23 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected SketchTextArea createTextArea(){
|
||||
SketchTextArea textArea = new SketchTextArea();
|
||||
textArea.requestFocusInWindow();
|
||||
textArea.setMarkOccurrences(true);
|
||||
textArea.setCodeFoldingEnabled(PreferencesData.getBoolean("editor.codefolding"));
|
||||
textArea.setAntiAliasingEnabled(PreferencesData.getBoolean("editor.antialias"));
|
||||
// textArea.setClearWhitespaceLinesEnabled(false);
|
||||
textArea.setTabsEmulated(PreferencesData.getBoolean("editor.tabs.expand"));
|
||||
textArea.setTabSize(PreferencesData.getInteger("editor.tabs.size"));
|
||||
textArea.setEditorListener(new EditorListener(this));
|
||||
|
||||
ToolTipManager.sharedInstance().registerComponent(textArea);
|
||||
|
||||
configurePopupMenu(textArea);
|
||||
return textArea;
|
||||
}
|
||||
|
||||
protected JMenuItem createToolMenuItem(String className) {
|
||||
try {
|
||||
@ -1401,18 +1426,6 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
item = newJMenuItem(_("Use Selection For Find"), 'E');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (find == null) {
|
||||
find = new FindReplace(Editor.this);
|
||||
}
|
||||
find.setLocationRelativeTo(Editor.this);
|
||||
find.setFindText( getSelectedText() );
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
@ -1462,24 +1475,18 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
try {
|
||||
undo.undo();
|
||||
textarea.undoLastAction();
|
||||
sketch.setModified(true);
|
||||
} catch (CannotUndoException ex) {
|
||||
//System.out.println("Unable to undo: " + ex);
|
||||
//ex.printStackTrace();
|
||||
}
|
||||
if (undo.getLastUndoableEdit() != null && undo.getLastUndoableEdit() instanceof CaretAwareUndoableEdit) {
|
||||
CaretAwareUndoableEdit undoableEdit = (CaretAwareUndoableEdit) undo.getLastUndoableEdit();
|
||||
int nextCaretPosition = undoableEdit.getCaretPosition() - 1;
|
||||
if (nextCaretPosition >= 0 && textarea.getDocumentLength() > nextCaretPosition) {
|
||||
textarea.setCaretPosition(nextCaretPosition);
|
||||
}
|
||||
}
|
||||
updateUndoState();
|
||||
redoAction.updateRedoState();
|
||||
}
|
||||
|
||||
protected void updateUndoState() {
|
||||
|
||||
UndoManager undo = textarea.getUndoManager();
|
||||
|
||||
if (undo.canUndo()) {
|
||||
this.setEnabled(true);
|
||||
undoItem.setEnabled(true);
|
||||
@ -1503,21 +1510,17 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
try {
|
||||
undo.redo();
|
||||
textarea.redoLastAction();
|
||||
sketch.setModified(true);
|
||||
} catch (CannotRedoException ex) {
|
||||
//System.out.println("Unable to redo: " + ex);
|
||||
//ex.printStackTrace();
|
||||
}
|
||||
if (undo.getLastUndoableEdit() != null && undo.getLastUndoableEdit() instanceof CaretAwareUndoableEdit) {
|
||||
CaretAwareUndoableEdit undoableEdit = (CaretAwareUndoableEdit) undo.getLastUndoableEdit();
|
||||
textarea.setCaretPosition(undoableEdit.getCaretPosition());
|
||||
}
|
||||
updateRedoState();
|
||||
undoAction.updateUndoState();
|
||||
}
|
||||
|
||||
protected void updateRedoState() {
|
||||
UndoManager undo = textarea.getUndoManager();
|
||||
|
||||
if (undo.canRedo()) {
|
||||
redoItem.setEnabled(true);
|
||||
redoItem.setText(undo.getRedoPresentationName());
|
||||
@ -1581,13 +1584,13 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
|
||||
/**
|
||||
* Get the JEditTextArea object for use (not recommended). This should only
|
||||
* Get the TextArea object for use (not recommended). This should only
|
||||
* be used in obscure cases that really need to hack the internals of the
|
||||
* JEditTextArea. Most tools should only interface via the get/set functions
|
||||
* found in this class. This will maintain compatibility with future releases,
|
||||
* which will not use JEditTextArea.
|
||||
* which will not use TextArea.
|
||||
*/
|
||||
public JEditTextArea getTextArea() {
|
||||
public SketchTextArea getTextArea() {
|
||||
return textarea;
|
||||
}
|
||||
|
||||
@ -1604,7 +1607,11 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
* Get a range of text from the current buffer.
|
||||
*/
|
||||
public String getText(int start, int stop) {
|
||||
return textarea.getText(start, stop - start);
|
||||
try {
|
||||
return textarea.getText(start, stop - start);
|
||||
} catch (BadLocationException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1612,20 +1619,10 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
* Replace the entire contents of the front-most tab.
|
||||
*/
|
||||
public void setText(String what) {
|
||||
startCompoundEdit();
|
||||
textarea.setText(what);
|
||||
stopCompoundEdit();
|
||||
}
|
||||
|
||||
|
||||
public void insertText(String what) {
|
||||
startCompoundEdit();
|
||||
int caret = getCaretOffset();
|
||||
setSelection(caret, caret);
|
||||
textarea.setSelectedText(what);
|
||||
stopCompoundEdit();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called to update the text but not switch to a different set of code
|
||||
@ -1651,15 +1648,10 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
|
||||
public void setSelectedText(String what) {
|
||||
textarea.setSelectedText(what);
|
||||
textarea.replaceSelection(what);
|
||||
}
|
||||
|
||||
|
||||
public void setSelection(int start, int stop) {
|
||||
// make sure that a tool isn't asking for a bad location
|
||||
start = PApplet.constrain(start, 0, textarea.getDocumentLength());
|
||||
stop = PApplet.constrain(stop, 0, textarea.getDocumentLength());
|
||||
|
||||
textarea.select(start, stop);
|
||||
}
|
||||
|
||||
@ -1695,7 +1687,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
* Get the end point of the current selection.
|
||||
*/
|
||||
public int getSelectionStop() {
|
||||
return textarea.getSelectionStop();
|
||||
return textarea.getSelectionEnd();
|
||||
}
|
||||
|
||||
|
||||
@ -1703,18 +1695,11 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
* Get text for a specified line.
|
||||
*/
|
||||
public String getLineText(int line) {
|
||||
return textarea.getLineText(line);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace the text on a specified line.
|
||||
*/
|
||||
public void setLineText(int line, String what) {
|
||||
startCompoundEdit();
|
||||
textarea.select(getLineStartOffset(line), getLineStopOffset(line));
|
||||
textarea.setSelectedText(what);
|
||||
stopCompoundEdit();
|
||||
try {
|
||||
return textarea.getText(textarea.getLineStartOffset(line), textarea.getLineEndOffset(line));
|
||||
} catch (BadLocationException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1722,7 +1707,11 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
* Get character offset for the start of a given line of text.
|
||||
*/
|
||||
public int getLineStartOffset(int line) {
|
||||
return textarea.getLineStartOffset(line);
|
||||
try {
|
||||
return textarea.getLineStartOffset(line);
|
||||
} catch (BadLocationException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1730,7 +1719,11 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
* Get character offset for end of a given line of text.
|
||||
*/
|
||||
public int getLineStopOffset(int line) {
|
||||
return textarea.getLineStopOffset(line);
|
||||
try {
|
||||
return textarea.getLineEndOffset(line);
|
||||
} catch (BadLocationException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1740,31 +1733,10 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
public int getLineCount() {
|
||||
return textarea.getLineCount();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use before a manipulating text to group editing operations together as a
|
||||
* single undo. Use stopCompoundEdit() once finished.
|
||||
*/
|
||||
public void startCompoundEdit() {
|
||||
compoundEdit = new CompoundEdit();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use with startCompoundEdit() to group edit operations in a single undo.
|
||||
*/
|
||||
public void stopCompoundEdit() {
|
||||
compoundEdit.end();
|
||||
undo.addEdit(compoundEdit);
|
||||
undoAction.updateUndoState();
|
||||
redoAction.updateRedoState();
|
||||
compoundEdit = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getScrollPosition() {
|
||||
return textarea.getScrollPosition();
|
||||
return scrollPane.getVerticalScrollBar().getValue();
|
||||
}
|
||||
|
||||
|
||||
@ -1775,15 +1747,12 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
* Switch between tabs, this swaps out the Document object
|
||||
* that's currently being manipulated.
|
||||
*/
|
||||
protected void setCode(SketchCodeDocument codeDoc) {
|
||||
SyntaxDocument document = (SyntaxDocument) codeDoc.getDocument();
|
||||
protected void setCode(final SketchCodeDocument codeDoc) {
|
||||
RSyntaxDocument document = (RSyntaxDocument) codeDoc.getDocument();
|
||||
|
||||
if (document == null) { // this document not yet inited
|
||||
document = new SyntaxDocument();
|
||||
codeDoc.setDocument(document);
|
||||
|
||||
// turn on syntax highlighting
|
||||
document.setTokenMarker(new PdeKeywords());
|
||||
document = new RSyntaxDocument(RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS);
|
||||
document.putProperty(PlainDocument.tabSizeAttribute, Preferences.getInteger("editor.tabs.size"));
|
||||
|
||||
// insert the program text into the document object
|
||||
try {
|
||||
@ -1791,37 +1760,39 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
} catch (BadLocationException bl) {
|
||||
bl.printStackTrace();
|
||||
}
|
||||
|
||||
// set up this guy's own undo manager
|
||||
// code.undo = new UndoManager();
|
||||
|
||||
// connect the undo listener to the editor
|
||||
document.addUndoableEditListener(new UndoableEditListener() {
|
||||
public void undoableEditHappened(UndoableEditEvent e) {
|
||||
if (compoundEdit != null) {
|
||||
compoundEdit.addEdit(new CaretAwareUndoableEdit(e.getEdit(), textarea));
|
||||
} else if (undo != null) {
|
||||
undo.addEdit(new CaretAwareUndoableEdit(e.getEdit(), textarea));
|
||||
}
|
||||
if (compoundEdit != null || undo != null) {
|
||||
sketch.setModified(true);
|
||||
undoAction.updateUndoState();
|
||||
redoAction.updateRedoState();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
codeDoc.setDocument(document);
|
||||
}
|
||||
|
||||
// update the document object that's in use
|
||||
textarea.setDocument(document,
|
||||
codeDoc.getSelectionStart(), codeDoc.getSelectionStop(),
|
||||
codeDoc.getScrollPosition());
|
||||
|
||||
if(codeDoc.getUndo() == null){
|
||||
codeDoc.setUndo(new LastUndoableEditAwareUndoManager(textarea, this));
|
||||
document.addUndoableEditListener(codeDoc.getUndo());
|
||||
}
|
||||
|
||||
// Update the document object that's in use
|
||||
textarea.switchDocument(document, codeDoc.getUndo());
|
||||
|
||||
// HACK multiple tabs: for update Listeners of Gutter, forcin call: Gutter.setTextArea(RTextArea)
|
||||
// BUG: https://github.com/bobbylight/RSyntaxTextArea/issues/84
|
||||
scrollPane.setViewportView(textarea);
|
||||
|
||||
textarea.select(codeDoc.getSelectionStart(), codeDoc.getSelectionStop());
|
||||
textarea.requestFocus(); // get the caret blinking
|
||||
|
||||
this.undo = codeDoc.getUndo();
|
||||
undoAction.updateUndoState();
|
||||
redoAction.updateRedoState();
|
||||
|
||||
final int position = codeDoc.getScrollPosition();
|
||||
|
||||
// invokeLater: Expect the document to be rendered correctly to set the new position
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
scrollPane.getVerticalScrollBar().setValue(position);
|
||||
undoAction.updateUndoState();
|
||||
redoAction.updateRedoState();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1833,7 +1804,6 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
*/
|
||||
public void handleCut() {
|
||||
textarea.cut();
|
||||
sketch.setModified(true);
|
||||
}
|
||||
|
||||
|
||||
@ -1860,7 +1830,6 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
*/
|
||||
public void handlePaste() {
|
||||
textarea.paste();
|
||||
sketch.setModified(true);
|
||||
}
|
||||
|
||||
|
||||
@ -1871,104 +1840,68 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
textarea.selectAll();
|
||||
}
|
||||
|
||||
|
||||
protected void handleCommentUncomment() {
|
||||
startCompoundEdit();
|
||||
|
||||
int startLine = textarea.getSelectionStartLine();
|
||||
int stopLine = textarea.getSelectionStopLine();
|
||||
|
||||
int lastLineStart = textarea.getLineStartOffset(stopLine);
|
||||
int selectionStop = textarea.getSelectionStop();
|
||||
// If the selection ends at the beginning of the last line,
|
||||
// then don't (un)comment that line.
|
||||
if (selectionStop == lastLineStart) {
|
||||
// Though if there's no selection, don't do that
|
||||
if (textarea.isSelectionActive()) {
|
||||
stopLine--;
|
||||
}
|
||||
}
|
||||
|
||||
// If the text is empty, ignore the user.
|
||||
// Also ensure that all lines are commented (not just the first)
|
||||
// when determining whether to comment or uncomment.
|
||||
int length = textarea.getDocumentLength();
|
||||
boolean commented = true;
|
||||
for (int i = startLine; commented && (i <= stopLine); i++) {
|
||||
int pos = textarea.getLineStartOffset(i);
|
||||
if (pos + 2 > length) {
|
||||
commented = false;
|
||||
} else {
|
||||
// Check the first two characters to see if it's already a comment.
|
||||
String begin = textarea.getText(pos, 2);
|
||||
//System.out.println("begin is '" + begin + "'");
|
||||
commented = begin.equals("//");
|
||||
}
|
||||
}
|
||||
|
||||
for (int line = startLine; line <= stopLine; line++) {
|
||||
int location = textarea.getLineStartOffset(line);
|
||||
if (commented) {
|
||||
// remove a comment
|
||||
textarea.select(location, location+2);
|
||||
if (textarea.getSelectedText().equals("//")) {
|
||||
textarea.setSelectedText("");
|
||||
}
|
||||
} else {
|
||||
// add a comment
|
||||
textarea.select(location, location);
|
||||
textarea.setSelectedText("//");
|
||||
}
|
||||
}
|
||||
// Subtract one from the end, otherwise selects past the current line.
|
||||
// (Which causes subsequent calls to keep expanding the selection)
|
||||
textarea.select(textarea.getLineStartOffset(startLine),
|
||||
textarea.getLineStopOffset(stopLine) - 1);
|
||||
stopCompoundEdit();
|
||||
/**
|
||||
* Begins an "atomic" edit. This method is called when TextArea
|
||||
* KNOWS that some edits should be compound automatically, such as the playing back of a macro.
|
||||
*
|
||||
* @see #endInternalAtomicEdit()
|
||||
*/
|
||||
public void beginInternalAtomicEdit(){
|
||||
textarea.getUndoManager().beginInternalAtomicEdit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends an "atomic" edit.
|
||||
*
|
||||
* @see #beginInternalAtomicEdit()
|
||||
*/
|
||||
public void endInternalAtomicEdit(){
|
||||
textarea.getUndoManager().endInternalAtomicEdit();
|
||||
}
|
||||
|
||||
|
||||
void handleCommentUncomment() {
|
||||
|
||||
Action action = textarea.getActionMap().get(RSyntaxTextAreaEditorKit.rstaToggleCommentAction);
|
||||
action.actionPerformed(null);
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected void handleIndentOutdent(boolean indent) {
|
||||
int tabSize = PreferencesData.getInteger("editor.tabs.size");
|
||||
String tabString = Editor.EMPTY.substring(0, tabSize);
|
||||
if (indent) {
|
||||
|
||||
startCompoundEdit();
|
||||
int caretPosition = textarea.getCaretPosition();
|
||||
boolean noSelec = !textarea.isSelectionActive();
|
||||
|
||||
int startLine = textarea.getSelectionStartLine();
|
||||
int stopLine = textarea.getSelectionStopLine();
|
||||
|
||||
// If the selection ends at the beginning of the last line,
|
||||
// then don't (un)comment that line.
|
||||
int lastLineStart = textarea.getLineStartOffset(stopLine);
|
||||
int selectionStop = textarea.getSelectionStop();
|
||||
if (selectionStop == lastLineStart) {
|
||||
// Though if there's no selection, don't do that
|
||||
if (textarea.isSelectionActive()) {
|
||||
stopLine--;
|
||||
}
|
||||
}
|
||||
|
||||
for (int line = startLine; line <= stopLine; line++) {
|
||||
int location = textarea.getLineStartOffset(line);
|
||||
|
||||
if (indent) {
|
||||
textarea.select(location, location);
|
||||
textarea.setSelectedText(tabString);
|
||||
|
||||
} else { // outdent
|
||||
textarea.select(location, location + tabSize);
|
||||
// Don't eat code if it's not indented
|
||||
if (textarea.getSelectedText().equals(tabString)) {
|
||||
textarea.setSelectedText("");
|
||||
// if no selection, focus on first char.
|
||||
if (noSelec) {
|
||||
try {
|
||||
int line = textarea.getCaretLineNumber();
|
||||
int startOffset = textarea.getLineStartOffset(line);
|
||||
textarea.setCaretPosition(startOffset);
|
||||
} catch (BadLocationException e) {
|
||||
}
|
||||
}
|
||||
|
||||
// Insert Tab or Spaces..
|
||||
Action action = textarea.getActionMap().get(RSyntaxTextAreaEditorKit.insertTabAction);
|
||||
action.actionPerformed(null);
|
||||
|
||||
if (noSelec) {
|
||||
textarea.setCaretPosition(caretPosition);
|
||||
}
|
||||
|
||||
} else {
|
||||
Action action = textarea.getActionMap().get(RSyntaxTextAreaEditorKit.rstaDecreaseIndentAction);
|
||||
action.actionPerformed(null);
|
||||
}
|
||||
// Subtract one from the end, otherwise selects past the current line.
|
||||
// (Which causes subsequent calls to keep expanding the selection)
|
||||
textarea.select(textarea.getLineStartOffset(startLine),
|
||||
textarea.getLineStopOffset(stopLine) - 1);
|
||||
stopCompoundEdit();
|
||||
}
|
||||
|
||||
/** Checks the preferences you are in external editing mode */
|
||||
public static boolean isExternalMode(){
|
||||
return PreferencesData.getBoolean("editor.external");
|
||||
}
|
||||
|
||||
protected String getCurrentKeyword() {
|
||||
String text = "";
|
||||
@ -2077,6 +2010,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
textarea.removeAllLineHighlights();
|
||||
sketch.prepare();
|
||||
sketch.build(verbose, saveHex);
|
||||
statusNotice(_("Done compiling."));
|
||||
@ -2258,7 +2192,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
sketch.setCurrentCode(codeIndex);
|
||||
textarea.select(selStart, selStop);
|
||||
textarea.setScrollPosition(scrollPos);
|
||||
scrollPane.getVerticalScrollBar().setValue(scrollPos);
|
||||
}
|
||||
|
||||
|
||||
@ -2383,6 +2317,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
public boolean handleSave(boolean immediately) {
|
||||
//stopRunner();
|
||||
handleStop(); // 0136
|
||||
textarea.removeAllLineHighlights();
|
||||
|
||||
if (untitled) {
|
||||
return handleSaveAs();
|
||||
@ -2759,9 +2694,9 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
}
|
||||
if (pageFormat != null) {
|
||||
//System.out.println("setting page format " + pageFormat);
|
||||
printerJob.setPrintable(textarea.getPainter(), pageFormat);
|
||||
printerJob.setPrintable(textarea, pageFormat);
|
||||
} else {
|
||||
printerJob.setPrintable(textarea.getPainter());
|
||||
printerJob.setPrintable(textarea);
|
||||
}
|
||||
// set the name of the job to the code name
|
||||
printerJob.setJobName(sketch.getCurrentCode().getPrettyName());
|
||||
@ -2818,7 +2753,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
// The error is at the end of this current chunk of code,
|
||||
// so the last line needs to be selected.
|
||||
line = textarea.getLineCount() - 1;
|
||||
if (textarea.getLineText(line).length() == 0) {
|
||||
if (getLineText(line).length() == 0) {
|
||||
// The last line may be zero length, meaning nothing to select.
|
||||
// If so, back up one more line.
|
||||
line--;
|
||||
@ -2827,8 +2762,12 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
if (line < 0 || line >= textarea.getLineCount()) {
|
||||
System.err.println(I18n.format(_("Bad error line: {0}"), line));
|
||||
} else {
|
||||
textarea.select(textarea.getLineStartOffset(line),
|
||||
textarea.getLineStopOffset(line) - 1);
|
||||
try {
|
||||
textarea.addLineHighlight(line, new Color(1, 0, 0, 0.2f));
|
||||
textarea.setCaretPosition(textarea.getLineStartOffset(line));
|
||||
} catch (BadLocationException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2879,148 +2818,101 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
lineStatus.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the edit popup menu.
|
||||
*/
|
||||
class TextAreaPopup extends JPopupMenu {
|
||||
//private String currentDir = System.getProperty("user.dir");
|
||||
private String referenceFile = null;
|
||||
|
||||
private JMenuItem cutItem;
|
||||
private JMenuItem copyItem;
|
||||
private JMenuItem discourseItem;
|
||||
private JMenuItem referenceItem;
|
||||
private JMenuItem openURLItem;
|
||||
private JSeparator openURLItemSeparator;
|
||||
protected void configurePopupMenu(final SketchTextArea textarea){
|
||||
|
||||
private String clickedURL;
|
||||
JPopupMenu menu = textarea.getPopupMenu();
|
||||
|
||||
public TextAreaPopup() {
|
||||
openURLItem = new JMenuItem(_("Open URL"));
|
||||
openURLItem.addActionListener(new ActionListener() {
|
||||
menu.addSeparator();
|
||||
|
||||
JMenuItem item = createToolMenuItem("cc.arduino.packages.formatter.AStyle");
|
||||
item.setName("menuToolsAutoFormat");
|
||||
|
||||
menu.add(item);
|
||||
|
||||
item = newJMenuItem(_("Comment/Uncomment"), '/');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.openURL(clickedURL);
|
||||
handleCommentUncomment();
|
||||
}
|
||||
});
|
||||
add(openURLItem);
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
openURLItemSeparator = new JSeparator();
|
||||
add(openURLItemSeparator);
|
||||
|
||||
cutItem = new JMenuItem(_("Cut"));
|
||||
cutItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleCut();
|
||||
}
|
||||
});
|
||||
add(cutItem);
|
||||
|
||||
copyItem = new JMenuItem(_("Copy"));
|
||||
copyItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleCopy();
|
||||
}
|
||||
});
|
||||
add(copyItem);
|
||||
|
||||
discourseItem = new JMenuItem(_("Copy for Forum"));
|
||||
discourseItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleDiscourseCopy();
|
||||
}
|
||||
});
|
||||
add(discourseItem);
|
||||
|
||||
discourseItem = new JMenuItem(_("Copy as HTML"));
|
||||
discourseItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleHTMLCopy();
|
||||
}
|
||||
});
|
||||
add(discourseItem);
|
||||
|
||||
JMenuItem item = new JMenuItem(_("Paste"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handlePaste();
|
||||
}
|
||||
});
|
||||
add(item);
|
||||
|
||||
item = new JMenuItem(_("Select All"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
item = newJMenuItem(_("Increase Indent"), ']');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleSelectAll();
|
||||
handleIndentOutdent(true);
|
||||
}
|
||||
});
|
||||
add(item);
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
addSeparator();
|
||||
item = newJMenuItem(_("Decrease Indent"), '[');
|
||||
item.setName("menuDecreaseIndent");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleIndentOutdent(false);
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem(_("Comment/Uncomment"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleCommentUncomment();
|
||||
}
|
||||
});
|
||||
add(item);
|
||||
item = new JMenuItem(_("Copy for Forum"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleDiscourseCopy();
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem(_("Increase Indent"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleIndentOutdent(true);
|
||||
}
|
||||
});
|
||||
add(item);
|
||||
item = new JMenuItem(_("Copy as HTML"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleHTMLCopy();
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem(_("Decrease Indent"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleIndentOutdent(false);
|
||||
}
|
||||
});
|
||||
add(item);
|
||||
final JMenuItem referenceItem = new JMenuItem(_("Find in Reference"));
|
||||
referenceItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleFindReference();
|
||||
}
|
||||
});
|
||||
menu.add(referenceItem);
|
||||
|
||||
addSeparator();
|
||||
final JMenuItem openURLItem = new JMenuItem(_("Open URL"));
|
||||
openURLItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.openURL(e.getActionCommand());
|
||||
}
|
||||
});
|
||||
menu.add(openURLItem);
|
||||
|
||||
menu.addPopupMenuListener(new PopupMenuListener() {
|
||||
|
||||
referenceItem = new JMenuItem(_("Find in Reference"));
|
||||
referenceItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleFindReference();
|
||||
}
|
||||
});
|
||||
add(referenceItem);
|
||||
}
|
||||
|
||||
// if no text is selected, disable copy and cut menu items
|
||||
public void show(Component component, int x, int y) {
|
||||
int lineNo = textarea.getLineOfOffset(textarea.xyToOffset(x, y));
|
||||
int offset = textarea.xToOffset(lineNo, x);
|
||||
String line = textarea.getLineText(lineNo);
|
||||
clickedURL = textarea.checkClickedURL(line, offset);
|
||||
if (clickedURL != null) {
|
||||
openURLItem.setVisible(true);
|
||||
openURLItemSeparator.setVisible(true);
|
||||
} else {
|
||||
openURLItem.setVisible(false);
|
||||
openURLItemSeparator.setVisible(false);
|
||||
@Override
|
||||
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
|
||||
String referenceFile = PdeKeywords.getReference(getCurrentKeyword());
|
||||
referenceItem.setEnabled(referenceFile != null);
|
||||
|
||||
int offset = textarea.getCaretPosition();
|
||||
org.fife.ui.rsyntaxtextarea.Token token = RSyntaxUtilities.getTokenAtOffset(textarea, offset);
|
||||
if (token != null && token.isHyperlink()) {
|
||||
openURLItem.setEnabled(true);
|
||||
openURLItem.setActionCommand(token.getLexeme());
|
||||
} else {
|
||||
openURLItem.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (textarea.isSelectionActive()) {
|
||||
cutItem.setEnabled(true);
|
||||
copyItem.setEnabled(true);
|
||||
discourseItem.setEnabled(true);
|
||||
|
||||
} else {
|
||||
cutItem.setEnabled(false);
|
||||
copyItem.setEnabled(false);
|
||||
discourseItem.setEnabled(false);
|
||||
@Override
|
||||
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
|
||||
}
|
||||
|
||||
referenceFile = PdeKeywords.getReference(getCurrentKeyword());
|
||||
referenceItem.setEnabled(referenceFile != null);
|
||||
@Override
|
||||
public void popupMenuCanceled(PopupMenuEvent e) {
|
||||
}
|
||||
});
|
||||
|
||||
super.show(component, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
Part of the Processing project - http://processing.org
|
||||
|
||||
@ -20,26 +22,23 @@
|
||||
|
||||
package processing.app;
|
||||
|
||||
import processing.app.helpers.OSUtils;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Image;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import processing.app.helpers.OSUtils;
|
||||
import processing.app.helpers.PreferencesMap;
|
||||
import processing.app.syntax.JEditTextArea;
|
||||
import processing.app.syntax.SketchTextArea;
|
||||
|
||||
|
||||
/**
|
||||
* Li'l status bar fella that shows the line number.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class EditorLineStatus extends JComponent {
|
||||
JEditTextArea textarea;
|
||||
SketchTextArea textarea;
|
||||
|
||||
int start = -1, stop;
|
||||
|
||||
Image resize;
|
||||
@ -55,9 +54,11 @@ public class EditorLineStatus extends JComponent {
|
||||
String name = "";
|
||||
String serialport = "";
|
||||
|
||||
public EditorLineStatus(JEditTextArea textarea) {
|
||||
|
||||
public EditorLineStatus(SketchTextArea textarea) {
|
||||
|
||||
this.textarea = textarea;
|
||||
textarea.editorLineStatus = this;
|
||||
textarea.setEditorLineStatus(this);
|
||||
|
||||
background = Theme.getColor("linestatus.bgcolor");
|
||||
font = Theme.getFont("linestatus.font");
|
||||
|
@ -1,636 +1,83 @@
|
||||
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
Part of the Processing project - http://processing.org
|
||||
|
||||
Copyright (c) 2004-08 Ben Fry and Casey Reas
|
||||
Copyright (c) 2001-04 Massachusetts Institute of Technology
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
package processing.app;
|
||||
|
||||
import processing.app.syntax.*;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import processing.app.syntax.SketchTextArea;
|
||||
|
||||
|
||||
/**
|
||||
* Filters key events for tab expansion/indent/etc.
|
||||
* <p/>
|
||||
* For version 0099, some changes have been made to make the indents
|
||||
* smarter. There are still issues though:
|
||||
* + indent happens when it picks up a curly brace on the previous line,
|
||||
* but not if there's a blank line between them.
|
||||
* + It also doesn't handle single indent situations where a brace
|
||||
* isn't used (i.e. an if statement or for loop that's a single line).
|
||||
* It shouldn't actually be using braces.
|
||||
* Solving these issues, however, would probably best be done by a
|
||||
* smarter parser/formatter, rather than continuing to hack this class.
|
||||
*/
|
||||
public class EditorListener {
|
||||
public class EditorListener implements KeyListener {
|
||||
|
||||
private Editor editor;
|
||||
private JEditTextArea textarea;
|
||||
|
||||
private boolean externalEditor;
|
||||
private boolean tabsExpand;
|
||||
private boolean tabsIndent;
|
||||
private int tabSize;
|
||||
private String tabString;
|
||||
private boolean autoIndent;
|
||||
|
||||
// private int selectionStart, selectionEnd;
|
||||
// private int position;
|
||||
|
||||
/** ctrl-alt on windows and linux, cmd-alt on mac os x */
|
||||
static final int CTRL_ALT = ActionEvent.ALT_MASK |
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
|
||||
|
||||
|
||||
public EditorListener(Editor editor, JEditTextArea textarea) {
|
||||
|
||||
public EditorListener(Editor editor) {
|
||||
super();
|
||||
this.editor = editor;
|
||||
this.textarea = textarea;
|
||||
|
||||
// let him know that i'm leechin'
|
||||
textarea.editorListener = this;
|
||||
|
||||
applyPreferences();
|
||||
}
|
||||
|
||||
/** ctrl-alt on windows and linux, cmd-alt on mac os x */
|
||||
static final int CTRL_ALT = ActionEvent.ALT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
|
||||
|
||||
static final int CTRL_SHIFT = ActionEvent.SHIFT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
|
||||
|
||||
static final int CTRL = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
|
||||
|
||||
|
||||
public void applyPreferences() {
|
||||
tabsExpand = PreferencesData.getBoolean("editor.tabs.expand");
|
||||
//tabsIndent = Preferences.getBoolean("editor.tabs.indent");
|
||||
tabSize = PreferencesData.getInteger("editor.tabs.size");
|
||||
tabString = Editor.EMPTY.substring(0, tabSize);
|
||||
autoIndent = PreferencesData.getBoolean("editor.indent");
|
||||
externalEditor = PreferencesData.getBoolean("editor.external");
|
||||
}
|
||||
|
||||
|
||||
//public void setExternalEditor(boolean externalEditor) {
|
||||
//this.externalEditor = externalEditor;
|
||||
//}
|
||||
|
||||
|
||||
/**
|
||||
* Intercepts key pressed events for JEditTextArea.
|
||||
* <p/>
|
||||
* Called by JEditTextArea inside processKeyEvent(). Note that this
|
||||
* won't intercept actual characters, because those are fired on
|
||||
* keyTyped().
|
||||
* @return true if the event has been handled (to remove it from the queue)
|
||||
*/
|
||||
public boolean keyPressed(KeyEvent event) {
|
||||
// don't do things if the textarea isn't editable
|
||||
if (externalEditor) return false;
|
||||
|
||||
//deselect(); // this is for paren balancing
|
||||
public void keyTyped(KeyEvent event) {
|
||||
char c = event.getKeyChar();
|
||||
int code = event.getKeyCode();
|
||||
|
||||
// if (code == KeyEvent.VK_SHIFT) {
|
||||
// editor.toolbar.setShiftPressed(true);
|
||||
// }
|
||||
|
||||
//System.out.println((int)c + " " + code + " " + event);
|
||||
//System.out.println();
|
||||
if ((event.getModifiers() & KeyEvent.CTRL_MASK) != 0) {
|
||||
// The char is not control code when CTRL key pressed? It should be a shortcut.
|
||||
if (!Character.isISOControl(c)) {
|
||||
event.consume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent event) {
|
||||
|
||||
SketchTextArea textarea = editor.getTextArea();
|
||||
|
||||
if (!textarea.isEditable()) return;
|
||||
|
||||
Sketch sketch = editor.getSketch();
|
||||
|
||||
int code = event.getKeyCode();
|
||||
|
||||
// Navigation..
|
||||
if ((event.getModifiers() & CTRL) == CTRL && code == KeyEvent.VK_TAB) {
|
||||
sketch.handleNextCode();
|
||||
}
|
||||
|
||||
// Navigation..
|
||||
// FIXME: not working on LINUX !!!
|
||||
if (((event.getModifiers() & CTRL_SHIFT) == CTRL_SHIFT)) {
|
||||
if(code == KeyEvent.VK_TAB)
|
||||
sketch.handlePrevCode();
|
||||
}
|
||||
|
||||
// Navigation..
|
||||
if ((event.getModifiers() & CTRL_ALT) == CTRL_ALT) {
|
||||
if (code == KeyEvent.VK_LEFT) {
|
||||
sketch.handlePrevCode();
|
||||
return true;
|
||||
} else if (code == KeyEvent.VK_RIGHT) {
|
||||
sketch.handleNextCode();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((event.getModifiers() & KeyEvent.CTRL_MASK) != 0) {
|
||||
// Consume ctrl-m(carriage return) keypresses
|
||||
if (code == KeyEvent.VK_M) {
|
||||
event.consume(); // does nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
// The char is not control code when CTRL key pressed? It should be a shortcut.
|
||||
if (!Character.isISOControl(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((event.getModifiers() & KeyEvent.META_MASK) != 0) {
|
||||
//event.consume(); // does nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO i don't like these accessors. clean em up later.
|
||||
if (!editor.getSketch().isModified()) {
|
||||
if ((code == KeyEvent.VK_BACK_SPACE) || (code == KeyEvent.VK_TAB) ||
|
||||
(code == KeyEvent.VK_ENTER) || ((c >= 32) && (c < 128))) {
|
||||
sketch.setModified(true);
|
||||
}
|
||||
}
|
||||
|
||||
if ((code == KeyEvent.VK_UP) &&
|
||||
((event.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
|
||||
// back up to the last empty line
|
||||
char contents[] = textarea.getText().toCharArray();
|
||||
//int origIndex = textarea.getCaretPosition() - 1;
|
||||
int caretIndex = textarea.getCaretPosition();
|
||||
|
||||
int index = calcLineStart(caretIndex - 1, contents);
|
||||
//System.out.println("line start " + (int) contents[index]);
|
||||
index -= 2; // step over the newline
|
||||
//System.out.println((int) contents[index]);
|
||||
boolean onlySpaces = true;
|
||||
while (index > 0) {
|
||||
if (contents[index] == 10) {
|
||||
if (onlySpaces) {
|
||||
index++;
|
||||
break;
|
||||
} else {
|
||||
onlySpaces = true; // reset
|
||||
}
|
||||
} else if (contents[index] != ' ') {
|
||||
onlySpaces = false;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
// if the first char, index will be -2
|
||||
if (index < 0) index = 0;
|
||||
|
||||
if ((event.getModifiers() & KeyEvent.SHIFT_MASK) != 0) {
|
||||
textarea.setSelectionStart(caretIndex);
|
||||
textarea.setSelectionEnd(index);
|
||||
} else {
|
||||
textarea.setCaretPosition(index);
|
||||
}
|
||||
event.consume();
|
||||
return true;
|
||||
|
||||
} else if ((code == KeyEvent.VK_DOWN) &&
|
||||
((event.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
|
||||
char contents[] = textarea.getText().toCharArray();
|
||||
int caretIndex = textarea.getCaretPosition();
|
||||
|
||||
int index = caretIndex;
|
||||
int lineStart = 0;
|
||||
boolean onlySpaces = false; // don't count this line
|
||||
while (index < contents.length) {
|
||||
if (contents[index] == 10) {
|
||||
if (onlySpaces) {
|
||||
index = lineStart; // this is it
|
||||
break;
|
||||
} else {
|
||||
lineStart = index + 1;
|
||||
onlySpaces = true; // reset
|
||||
}
|
||||
} else if (contents[index] != ' ') {
|
||||
onlySpaces = false;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
// if the first char, index will be -2
|
||||
//if (index < 0) index = 0;
|
||||
|
||||
//textarea.setSelectionStart(index);
|
||||
//textarea.setSelectionEnd(index);
|
||||
if ((event.getModifiers() & KeyEvent.SHIFT_MASK) != 0) {
|
||||
textarea.setSelectionStart(caretIndex);
|
||||
textarea.setSelectionEnd(index);
|
||||
} else {
|
||||
textarea.setCaretPosition(index);
|
||||
}
|
||||
event.consume();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
switch ((int) c) {
|
||||
|
||||
case 9: // TAB
|
||||
if (textarea.isSelectionActive()) {
|
||||
boolean outdent = (event.getModifiers() & KeyEvent.SHIFT_MASK) != 0;
|
||||
editor.handleIndentOutdent(!outdent);
|
||||
|
||||
} else if (tabsExpand) { // expand tabs
|
||||
textarea.setSelectedText(tabString);
|
||||
event.consume();
|
||||
return true;
|
||||
|
||||
} else if (tabsIndent) {
|
||||
// this code is incomplete
|
||||
|
||||
// if this brace is the only thing on the line, outdent
|
||||
//char contents[] = getCleanedContents();
|
||||
char contents[] = textarea.getText().toCharArray();
|
||||
// index to the character to the left of the caret
|
||||
int prevCharIndex = textarea.getCaretPosition() - 1;
|
||||
|
||||
// now find the start of this line
|
||||
int lineStart = calcLineStart(prevCharIndex, contents);
|
||||
|
||||
int lineEnd = lineStart;
|
||||
while ((lineEnd < contents.length - 1) &&
|
||||
(contents[lineEnd] != 10)) {
|
||||
lineEnd++;
|
||||
}
|
||||
|
||||
// get the number of braces, to determine whether this is an indent
|
||||
int braceBalance = 0;
|
||||
int index = lineStart;
|
||||
while ((index < contents.length) &&
|
||||
(contents[index] != 10)) {
|
||||
if (contents[index] == '{') {
|
||||
braceBalance++;
|
||||
} else if (contents[index] == '}') {
|
||||
braceBalance--;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
// if it's a starting indent, need to ignore it, so lineStart
|
||||
// will be the counting point. but if there's a closing indent,
|
||||
// then the lineEnd should be used.
|
||||
int where = (braceBalance > 0) ? lineStart : lineEnd;
|
||||
int indent = calcBraceIndent(where, contents);
|
||||
if (indent == -1) {
|
||||
// no braces to speak of, do nothing
|
||||
indent = 0;
|
||||
} else {
|
||||
indent += tabSize;
|
||||
}
|
||||
|
||||
// and the number of spaces it has
|
||||
int spaceCount = calcSpaceCount(prevCharIndex, contents);
|
||||
|
||||
textarea.setSelectionStart(lineStart);
|
||||
textarea.setSelectionEnd(lineStart + spaceCount);
|
||||
textarea.setSelectedText(Editor.EMPTY.substring(0, indent));
|
||||
|
||||
event.consume();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 10: // auto-indent
|
||||
case 13:
|
||||
if (autoIndent) {
|
||||
char contents[] = textarea.getText().toCharArray();
|
||||
|
||||
// this is the previous character
|
||||
// (i.e. when you hit return, it'll be the last character
|
||||
// just before where the newline will be inserted)
|
||||
int origIndex = textarea.getCaretPosition() - 1;
|
||||
|
||||
// NOTE all this cursing about CRLF stuff is probably moot
|
||||
// NOTE since the switch to JEditTextArea, which seems to use
|
||||
// NOTE only LFs internally (thank god). disabling for 0099.
|
||||
// walk through the array to the current caret position,
|
||||
// and count how many weirdo windows line endings there are,
|
||||
// which would be throwing off the caret position number
|
||||
/*
|
||||
int offset = 0;
|
||||
int realIndex = origIndex;
|
||||
for (int i = 0; i < realIndex-1; i++) {
|
||||
if ((contents[i] == 13) && (contents[i+1] == 10)) {
|
||||
offset++;
|
||||
realIndex++;
|
||||
}
|
||||
}
|
||||
// back up until \r \r\n or \n.. @#($* cross platform
|
||||
//System.out.println(origIndex + " offset = " + offset);
|
||||
origIndex += offset; // ARGH!#(* WINDOWS#@($*
|
||||
*/
|
||||
|
||||
// if the previous thing is a brace (whether prev line or
|
||||
// up farther) then the correct indent is the number of spaces
|
||||
// on that line + 'indent'.
|
||||
// if the previous line is not a brace, then just use the
|
||||
// identical indentation to the previous line
|
||||
|
||||
// calculate the amount of indent on the previous line
|
||||
// this will be used *only if the prev line is not an indent*
|
||||
int spaceCount = calcSpaceCount(origIndex, contents);
|
||||
|
||||
// If the last character was a left curly brace, then indent.
|
||||
// For 0122, walk backwards a bit to make sure that the there
|
||||
// isn't a curly brace several spaces (or lines) back. Also
|
||||
// moved this before calculating extraCount, since it'll affect
|
||||
// that as well.
|
||||
int index2 = origIndex;
|
||||
while ((index2 >= 0) &&
|
||||
Character.isWhitespace(contents[index2])) {
|
||||
index2--;
|
||||
}
|
||||
if (index2 != -1) {
|
||||
// still won't catch a case where prev stuff is a comment
|
||||
if (contents[index2] == '{') {
|
||||
// intermediate lines be damned,
|
||||
// use the indent for this line instead
|
||||
spaceCount = calcSpaceCount(index2, contents);
|
||||
spaceCount += tabSize;
|
||||
}
|
||||
}
|
||||
//System.out.println("spaceCount should be " + spaceCount);
|
||||
|
||||
// now before inserting this many spaces, walk forward from
|
||||
// the caret position and count the number of spaces,
|
||||
// so that the number of spaces aren't duplicated again
|
||||
int index = origIndex + 1;
|
||||
int extraCount = 0;
|
||||
while ((index < contents.length) &&
|
||||
(contents[index] == ' ')) {
|
||||
//spaceCount--;
|
||||
extraCount++;
|
||||
index++;
|
||||
}
|
||||
int braceCount = 0;
|
||||
while ((index < contents.length) && (contents[index] != '\n')) {
|
||||
if (contents[index] == '}') {
|
||||
braceCount++;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
// hitting return on a line with spaces *after* the caret
|
||||
// can cause trouble. for 0099, was ignoring the case, but this is
|
||||
// annoying, so in 0122 we're trying to fix that.
|
||||
/*
|
||||
if (spaceCount - extraCount > 0) {
|
||||
spaceCount -= extraCount;
|
||||
}
|
||||
*/
|
||||
spaceCount -= extraCount;
|
||||
//if (spaceCount < 0) spaceCount = 0;
|
||||
//System.out.println("extraCount is " + extraCount);
|
||||
|
||||
// now, check to see if the current line contains a } and if so,
|
||||
// outdent again by indent
|
||||
//if (braceCount > 0) {
|
||||
//spaceCount -= 2;
|
||||
//}
|
||||
|
||||
if (spaceCount < 0) {
|
||||
// for rev 0122, actually delete extra space
|
||||
//textarea.setSelectionStart(origIndex + 1);
|
||||
textarea.setSelectionEnd(textarea.getSelectionStop() - spaceCount);
|
||||
textarea.setSelectedText("\n");
|
||||
} else {
|
||||
String insertion = "\n" + Editor.EMPTY.substring(0, spaceCount);
|
||||
textarea.setSelectedText(insertion);
|
||||
}
|
||||
|
||||
// not gonna bother handling more than one brace
|
||||
if (braceCount > 0) {
|
||||
int sel = textarea.getSelectionStart();
|
||||
// sel - tabSize will be -1 if start/end parens on the same line
|
||||
// http://dev.processing.org/bugs/show_bug.cgi?id=484
|
||||
if (sel - tabSize >= 0) {
|
||||
textarea.select(sel - tabSize, sel);
|
||||
String s = Editor.EMPTY.substring(0, tabSize);
|
||||
// if these are spaces that we can delete
|
||||
if (textarea.getSelectedText().equals(s)) {
|
||||
textarea.setSelectedText("");
|
||||
} else {
|
||||
textarea.select(sel, sel);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Enter/Return was being consumed by somehow even if false
|
||||
// was returned, so this is a band-aid to simply fire the event again.
|
||||
// http://dev.processing.org/bugs/show_bug.cgi?id=1073
|
||||
textarea.setSelectedText(String.valueOf(c));
|
||||
}
|
||||
// mark this event as already handled (all but ignored)
|
||||
event.consume();
|
||||
return true;
|
||||
|
||||
case '}':
|
||||
if (autoIndent) {
|
||||
// first remove anything that was there (in case this multiple
|
||||
// characters are selected, so that it's not in the way of the
|
||||
// spaces for the auto-indent
|
||||
if (textarea.getSelectionStart() != textarea.getSelectionStop()) {
|
||||
textarea.setSelectedText("");
|
||||
}
|
||||
|
||||
// if this brace is the only thing on the line, outdent
|
||||
char contents[] = textarea.getText().toCharArray();
|
||||
// index to the character to the left of the caret
|
||||
int prevCharIndex = textarea.getCaretPosition() - 1;
|
||||
|
||||
// backup from the current caret position to the last newline,
|
||||
// checking for anything besides whitespace along the way.
|
||||
// if there's something besides whitespace, exit without
|
||||
// messing any sort of indenting.
|
||||
int index = prevCharIndex;
|
||||
boolean finished = false;
|
||||
while ((index != -1) && (!finished)) {
|
||||
if (contents[index] == 10) {
|
||||
finished = true;
|
||||
index++;
|
||||
} else if (contents[index] != ' ') {
|
||||
// don't do anything, this line has other stuff on it
|
||||
return false;
|
||||
} else {
|
||||
index--;
|
||||
}
|
||||
}
|
||||
if (!finished) return false; // brace with no start
|
||||
int lineStartIndex = index;
|
||||
|
||||
int pairedSpaceCount = calcBraceIndent(prevCharIndex, contents); //, 1);
|
||||
if (pairedSpaceCount == -1) return false;
|
||||
|
||||
textarea.setSelectionStart(lineStartIndex);
|
||||
textarea.setSelectedText(Editor.EMPTY.substring(0, pairedSpaceCount));
|
||||
|
||||
// mark this event as already handled
|
||||
event.consume();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// public boolean keyReleased(KeyEvent event) {
|
||||
// if (code == KeyEvent.VK_SHIFT) {
|
||||
// editor.toolbar.setShiftPressed(false);
|
||||
|
||||
// if (event.isAltDown() && code == KeyEvent.VK_T) {
|
||||
// int line = textarea.getCaretLineNumber();
|
||||
// textarea.setActiveLineRange(line, line + 3);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
public boolean keyTyped(KeyEvent event) {
|
||||
char c = event.getKeyChar();
|
||||
|
||||
if ((event.getModifiers() & KeyEvent.CTRL_MASK) != 0) {
|
||||
// The char is not control code when CTRL key pressed? It should be a shortcut.
|
||||
if (!Character.isISOControl(c)) {
|
||||
event.consume();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the index for the first character on this line.
|
||||
*/
|
||||
protected int calcLineStart(int index, char contents[]) {
|
||||
// backup from the current caret position to the last newline,
|
||||
// so that we can figure out how far this line was indented
|
||||
/*int spaceCount = 0;*/
|
||||
boolean finished = false;
|
||||
while ((index != -1) && (!finished)) {
|
||||
if ((contents[index] == 10) ||
|
||||
(contents[index] == 13)) {
|
||||
finished = true;
|
||||
//index++; // maybe ?
|
||||
} else {
|
||||
index--; // new
|
||||
}
|
||||
}
|
||||
// add one because index is either -1 (the start of the document)
|
||||
// or it's the newline character for the previous line
|
||||
return index + 1;
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the number of spaces on this line.
|
||||
*/
|
||||
protected int calcSpaceCount(int index, char contents[]) {
|
||||
index = calcLineStart(index, contents);
|
||||
|
||||
int spaceCount = 0;
|
||||
// now walk forward and figure out how many spaces there are
|
||||
while ((index < contents.length) && (index >= 0) &&
|
||||
(contents[index++] == ' ')) {
|
||||
spaceCount++;
|
||||
}
|
||||
return spaceCount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Walk back from 'index' until the brace that seems to be
|
||||
* the beginning of the current block, and return the number of
|
||||
* spaces found on that line.
|
||||
*/
|
||||
protected int calcBraceIndent(int index, char contents[]) {
|
||||
// now that we know things are ok to be indented, walk
|
||||
// backwards to the last { to see how far its line is indented.
|
||||
// this isn't perfect cuz it'll pick up commented areas,
|
||||
// but that's not really a big deal and can be fixed when
|
||||
// this is all given a more complete (proper) solution.
|
||||
int braceDepth = 1;
|
||||
boolean finished = false;
|
||||
while ((index != -1) && (!finished)) {
|
||||
if (contents[index] == '}') {
|
||||
// aww crap, this means we're one deeper
|
||||
// and will have to find one more extra {
|
||||
braceDepth++;
|
||||
//if (braceDepth == 0) {
|
||||
//finished = true;
|
||||
//}
|
||||
index--;
|
||||
} else if (contents[index] == '{') {
|
||||
braceDepth--;
|
||||
if (braceDepth == 0) {
|
||||
finished = true;
|
||||
}
|
||||
index--;
|
||||
} else {
|
||||
index--;
|
||||
}
|
||||
}
|
||||
// never found a proper brace, be safe and don't do anything
|
||||
if (!finished) return -1;
|
||||
|
||||
// check how many spaces on the line with the matching open brace
|
||||
//int pairedSpaceCount = calcSpaceCount(index, contents);
|
||||
//System.out.println(pairedSpaceCount);
|
||||
return calcSpaceCount(index, contents);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the character array and blank out the commented areas.
|
||||
* This hasn't yet been tested, the plan was to make auto-indent
|
||||
* less gullible (it gets fooled by braces that are commented out).
|
||||
*/
|
||||
protected char[] getCleanedContents() {
|
||||
char c[] = textarea.getText().toCharArray();
|
||||
|
||||
int index = 0;
|
||||
while (index < c.length - 1) {
|
||||
if ((c[index] == '/') && (c[index+1] == '*')) {
|
||||
c[index++] = 0;
|
||||
c[index++] = 0;
|
||||
while ((index < c.length - 1) &&
|
||||
!((c[index] == '*') && (c[index+1] == '/'))) {
|
||||
c[index++] = 0;
|
||||
}
|
||||
|
||||
} else if ((c[index] == '/') && (c[index+1] == '/')) {
|
||||
// clear out until the end of the line
|
||||
while ((index < c.length) && (c[index] != 10)) {
|
||||
c[index++] = 0;
|
||||
}
|
||||
if (index != c.length) {
|
||||
index++; // skip over the newline
|
||||
}
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
protected char[] getCleanedContents() {
|
||||
char c[] = textarea.getText().toCharArray();
|
||||
boolean insideMulti; // multi-line comment
|
||||
boolean insideSingle; // single line double slash
|
||||
|
||||
//for (int i = 0; i < c.length - 1; i++) {
|
||||
int index = 0;
|
||||
while (index < c.length - 1) {
|
||||
if (insideMulti && (c[index] == '*') && (c[index+1] == '/')) {
|
||||
insideMulti = false;
|
||||
index += 2;
|
||||
} else if ((c[index] == '/') && (c[index+1] == '*')) {
|
||||
insideMulti = true;
|
||||
index += 2;
|
||||
} else if ((c[index] == '/') && (c[index+1] == '/')) {
|
||||
// clear out until the end of the line
|
||||
while (c[index] != 10) {
|
||||
c[index++] = 0;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
@ -2,31 +2,36 @@ package processing.app;
|
||||
|
||||
import javax.swing.undo.CannotRedoException;
|
||||
import javax.swing.undo.CannotUndoException;
|
||||
import javax.swing.undo.UndoManager;
|
||||
import javax.swing.undo.UndoableEdit;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class LastUndoableEditAwareUndoManager extends UndoManager {
|
||||
import org.fife.ui.rtextarea.RUndoManager;
|
||||
|
||||
private UndoableEdit lastUndoableEdit;
|
||||
import processing.app.syntax.SketchTextArea;
|
||||
|
||||
public LastUndoableEditAwareUndoManager() {
|
||||
this.lastUndoableEdit = null;
|
||||
public class LastUndoableEditAwareUndoManager extends RUndoManager {
|
||||
|
||||
private Editor editor;
|
||||
|
||||
public LastUndoableEditAwareUndoManager(SketchTextArea textarea, Editor editor) {
|
||||
super(textarea);
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void undo() throws CannotUndoException {
|
||||
lastUndoableEdit = super.editToBeUndone();
|
||||
super.undo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void redo() throws CannotRedoException {
|
||||
lastUndoableEdit = super.editToBeRedone();
|
||||
super.redo();
|
||||
}
|
||||
|
||||
public UndoableEdit getLastUndoableEdit() {
|
||||
return lastUndoableEdit;
|
||||
|
||||
@Override
|
||||
public void updateActions() {
|
||||
super.updateActions();
|
||||
editor.undoAction.updateUndoState();
|
||||
editor.redoAction.updateRedoState();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ public class Sketch {
|
||||
|
||||
for (SketchCode code : data.getCodes()) {
|
||||
if (code.getMetadata() == null)
|
||||
code.setMetadata(new SketchCodeDocument(code));
|
||||
code.setMetadata(new SketchCodeDocument(this, code));
|
||||
}
|
||||
|
||||
// set the main file to be the current tab
|
||||
@ -409,7 +409,7 @@ public class Sketch {
|
||||
return;
|
||||
}
|
||||
ensureExistence();
|
||||
data.addCode((new SketchCodeDocument(newFile)).getCode());
|
||||
data.addCode((new SketchCodeDocument(this, newFile)).getCode());
|
||||
}
|
||||
|
||||
// sort the entries
|
||||
@ -905,7 +905,7 @@ public class Sketch {
|
||||
}
|
||||
|
||||
if (codeExtension != null) {
|
||||
SketchCode newCode = (new SketchCodeDocument(destFile)).getCode();
|
||||
SketchCode newCode = (new SketchCodeDocument(this, destFile)).getCode();
|
||||
|
||||
if (replacement) {
|
||||
data.replaceCode(newCode);
|
||||
|
@ -2,37 +2,47 @@ package processing.app;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.undo.UndoManager;
|
||||
|
||||
public class SketchCodeDocument{
|
||||
import org.fife.ui.rtextarea.RTextArea;
|
||||
|
||||
public class SketchCodeDocument implements SketchDocumentProvider, DocumentListener{
|
||||
|
||||
private SketchCode code;
|
||||
private Sketch sketch;
|
||||
private Document document;
|
||||
|
||||
// Undo Manager for this tab, each tab keeps track of their own Editor.undo
|
||||
// will be set to this object when this code is the tab that's currently the
|
||||
// front.
|
||||
private LastUndoableEditAwareUndoManager undo = new LastUndoableEditAwareUndoManager();
|
||||
private UndoManager undo;
|
||||
|
||||
// saved positions from last time this tab was used
|
||||
private int selectionStart;
|
||||
private int selectionStop;
|
||||
private int scrollPosition;
|
||||
|
||||
public SketchCodeDocument(SketchCode code) {
|
||||
public SketchCodeDocument(Sketch sketch, SketchCode code) {
|
||||
this.code = code;
|
||||
this.sketch = sketch;
|
||||
this.code.setMetadata(this);
|
||||
}
|
||||
|
||||
public SketchCodeDocument(File file) {
|
||||
public SketchCodeDocument(Sketch sketch, File file) {
|
||||
this.code = new SketchCode(file, this);
|
||||
this.sketch = sketch;
|
||||
}
|
||||
|
||||
public LastUndoableEditAwareUndoManager getUndo() {
|
||||
public UndoManager getUndo() {
|
||||
return undo;
|
||||
}
|
||||
|
||||
public void setUndo(LastUndoableEditAwareUndoManager undo) {
|
||||
public void setUndo(UndoManager undo) {
|
||||
this.undo = undo;
|
||||
}
|
||||
|
||||
@ -74,6 +84,24 @@ public class SketchCodeDocument{
|
||||
|
||||
public void setDocument(Document document) {
|
||||
this.document = document;
|
||||
document.addDocumentListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
if(!code.isModified()) sketch.setModified(true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
if(!code.isModified()) sketch.setModified(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
// Callback for when styles in the current document change.
|
||||
// This method is never called.
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,9 +28,11 @@ import java.awt.Font;
|
||||
import java.awt.SystemColor;
|
||||
import java.io.File;
|
||||
|
||||
import javax.swing.text.StyleContext;
|
||||
|
||||
import processing.app.helpers.OSUtils;
|
||||
import processing.app.helpers.PreferencesHelper;
|
||||
import processing.app.helpers.PreferencesMap;
|
||||
import processing.app.syntax.SyntaxStyle;
|
||||
|
||||
/**
|
||||
* Storage class for theme settings. This was separated from the Preferences
|
||||
@ -104,17 +106,40 @@ public class Theme {
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default font for text areas.
|
||||
*
|
||||
* @return The default font.
|
||||
*/
|
||||
public static final Font getDefaultFont() {
|
||||
|
||||
static public SyntaxStyle getStyle(String what) {
|
||||
String split[] = get("editor." + what + ".style").split(",");
|
||||
// Use StyleContext to get a composite font for better Asian language
|
||||
// support; see Sun bug S282887.
|
||||
StyleContext sc = StyleContext.getDefaultStyleContext();
|
||||
Font font = null;
|
||||
|
||||
Color color = PreferencesHelper.parseColor(split[0]);
|
||||
if (OSUtils.isMacOS()) {
|
||||
// Snow Leopard (1.6) uses Menlo as default monospaced font,
|
||||
// pre-Snow Leopard used Monaco.
|
||||
font = sc.getFont("Menlo", Font.PLAIN, 12);
|
||||
if (!"Menlo".equals(font.getFamily())) {
|
||||
font = sc.getFont("Monaco", Font.PLAIN, 12);
|
||||
if (!"Monaco".equals(font.getFamily())) { // Shouldn't happen
|
||||
font = sc.getFont("Monospaced", Font.PLAIN, 13);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Consolas added in Vista, used by VS2010+.
|
||||
font = sc.getFont("Consolas", Font.PLAIN, 13);
|
||||
if (!"Consolas".equals(font.getFamily())) {
|
||||
font = sc.getFont("Monospaced", Font.PLAIN, 13);
|
||||
}
|
||||
}
|
||||
|
||||
String style = split[1];
|
||||
boolean bold = style.contains("bold");
|
||||
boolean italic = style.contains("italic");
|
||||
boolean underlined = style.contains("underlined");
|
||||
//System.out.println(font.getFamily() + ", " + font.getName());
|
||||
return font;
|
||||
|
||||
return new SyntaxStyle(color, italic, bold, underlined);
|
||||
}
|
||||
}
|
||||
|
@ -24,59 +24,55 @@
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import processing.app.*;
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenTypes;
|
||||
import processing.app.Base;
|
||||
import processing.app.BaseNoGui;
|
||||
import processing.app.legacy.PApplet;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
public class PdeKeywords extends CTokenMarker {
|
||||
public class PdeKeywords {
|
||||
|
||||
// lookup table for the TokenMarker subclass, handles coloring
|
||||
static KeywordMap keywordColoring;
|
||||
// Value is org.fife.ui.rsyntaxtextarea.TokenTypes
|
||||
private static HashMap<String, Integer> keywords = new HashMap<String, Integer>();
|
||||
|
||||
// lookup table that maps keywords to their html reference pages
|
||||
static Hashtable keywordToReference;
|
||||
private static HashMap<String, String> keywordToReference = new HashMap<String, String>();
|
||||
|
||||
|
||||
public PdeKeywords() {
|
||||
super(false, getKeywords());
|
||||
public static HashMap<String, Integer> reload() {
|
||||
keywords.clear();
|
||||
keywordToReference.clear();
|
||||
return get();
|
||||
}
|
||||
|
||||
public static HashMap<String, Integer> get() {
|
||||
|
||||
/**
|
||||
* Handles loading of keywords file.
|
||||
* <P>
|
||||
* Uses getKeywords() method because that's part of the
|
||||
* TokenMarker classes.
|
||||
* <P>
|
||||
* It is recommended that a # sign be used for comments
|
||||
* inside keywords.txt.
|
||||
*/
|
||||
static public KeywordMap getKeywords() {
|
||||
if (keywordColoring == null) {
|
||||
if (keywords.isEmpty()) {
|
||||
try {
|
||||
keywordColoring = new KeywordMap(false);
|
||||
keywordToReference = new Hashtable();
|
||||
getKeywords(new File(BaseNoGui.getContentFile("lib"), "keywords.txt"));
|
||||
for (ContributedLibrary lib : Base.getLibraries()) {
|
||||
File keywords = new File(lib.getInstalledFolder(), "keywords.txt");
|
||||
if (keywords.exists()) getKeywords(keywords);
|
||||
load(new File(BaseNoGui.getContentFile("lib"), "keywords.txt"));
|
||||
if (Base.getLibraries() != null) {
|
||||
for (ContributedLibrary lib : Base.getLibraries()) {
|
||||
File keywords = new File(lib.getInstalledFolder(), "keywords.txt");
|
||||
if (keywords.exists()) load(keywords);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Base.showError("Problem loading keywords",
|
||||
"Could not load keywords.txt,\n" +
|
||||
"please re-install Arduino.", e);
|
||||
"Could not load keywords.txt,\n" +
|
||||
"please re-install Arduino.", e);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
return keywordColoring;
|
||||
|
||||
return keywords;
|
||||
}
|
||||
|
||||
static private void getKeywords(File input) throws IOException {
|
||||
|
||||
static private void load(File input) throws Exception {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(new FileInputStream(input)));
|
||||
@ -111,7 +107,40 @@ public class PdeKeywords extends CTokenMarker {
|
||||
((isKey ? Token.KEYWORD1 : Token.LITERAL1) + num);
|
||||
//System.out.println("got " + (isKey ? "keyword" : "literal") +
|
||||
// (num+1) + " for " + keyword);
|
||||
keywordColoring.add(keyword, id);
|
||||
|
||||
int tokenType = TokenTypes.IDENTIFIER;
|
||||
|
||||
// KEYWORD1 Classes, datatypes, and C++ keywords
|
||||
// KEYWORD2 Methods and functions
|
||||
// KEYWORD3 setup and loop functions, as well as the Serial keywords
|
||||
// LITERAL1 Constants
|
||||
// LITERAL2 Built-in variables (INPUT,OUTPUT,CHANGE,FALLING)
|
||||
|
||||
switch (id) {
|
||||
case Token.KEYWORD1:
|
||||
tokenType = TokenTypes.VARIABLE;
|
||||
break;
|
||||
case Token.KEYWORD2:
|
||||
tokenType = TokenTypes.FUNCTION;
|
||||
break;
|
||||
case Token.KEYWORD3:
|
||||
tokenType = TokenTypes.RESERVED_WORD;
|
||||
break;
|
||||
case Token.LITERAL1:
|
||||
tokenType = TokenTypes.PREPROCESSOR;
|
||||
break;
|
||||
case Token.LITERAL2:
|
||||
tokenType = TokenTypes.RESERVED_WORD_2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ("true".equals(keyword) || "false".equals(keyword)) {
|
||||
tokenType = TokenTypes.LITERAL_BOOLEAN;
|
||||
}
|
||||
|
||||
keywords.put(keyword, tokenType);
|
||||
}
|
||||
if (pieces.length >= 3) {
|
||||
String htmlFilename = pieces[2].trim();
|
||||
@ -128,8 +157,8 @@ public class PdeKeywords extends CTokenMarker {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static public String getReference(String keyword) {
|
||||
return (String) keywordToReference.get(keyword);
|
||||
public static String getReference(String keyword) {
|
||||
if (keywordToReference == null) return null;
|
||||
return keywordToReference.get(keyword);
|
||||
}
|
||||
}
|
||||
|
243
app/src/processing/app/syntax/SketchTextArea.java
Normal file
243
app/src/processing/app/syntax/SketchTextArea.java
Normal file
@ -0,0 +1,243 @@
|
||||
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import java.awt.AWTKeyStroke;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.Segment;
|
||||
import javax.swing.undo.UndoManager;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.*;
|
||||
import org.fife.ui.rsyntaxtextarea.Token;
|
||||
import org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip;
|
||||
import org.fife.ui.rtextarea.RUndoManager;
|
||||
|
||||
import processing.app.Base;
|
||||
import processing.app.BaseNoGui;
|
||||
import processing.app.EditorLineStatus;
|
||||
import processing.app.EditorListener;
|
||||
|
||||
/**
|
||||
* Arduino Sketch code editor based on RSyntaxTextArea (http://fifesoft.com/rsyntaxtextarea)
|
||||
* @author Ricardo JL Rufino (ricardo@criativasoft.com.br)
|
||||
* @date 20/04/2015
|
||||
* @since 1.6.4
|
||||
*/
|
||||
public class SketchTextArea extends RSyntaxTextArea {
|
||||
|
||||
private final static Logger LOG = Logger.getLogger(SketchTextArea.class.getName());
|
||||
|
||||
/** The last docTooltip displayed. */
|
||||
private FocusableTip docTooltip;
|
||||
|
||||
/**
|
||||
* The component that tracks the current line number.
|
||||
*/
|
||||
protected EditorLineStatus editorLineStatus;
|
||||
|
||||
private EditorListener editorListener;
|
||||
|
||||
public SketchTextArea() {
|
||||
super();
|
||||
installFeatures();
|
||||
}
|
||||
|
||||
|
||||
protected void installFeatures(){
|
||||
FileInputStream defaultXmlInputStream = null;
|
||||
try {
|
||||
defaultXmlInputStream = new FileInputStream(new File(BaseNoGui.getContentFile("lib"), "theme/syntax/default.xml"));
|
||||
Theme theme = Theme.load(defaultXmlInputStream);
|
||||
theme.apply(this);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (defaultXmlInputStream != null) {
|
||||
try {
|
||||
defaultXmlInputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setLinkGenerator(new DocLinkGenerator());
|
||||
|
||||
fixControlTab();
|
||||
installTokenMaker();
|
||||
}
|
||||
|
||||
// Removing the default focus traversal keys
|
||||
// This is because the DefaultKeyboardFocusManager handles the keypress and consumes the event
|
||||
protected void fixControlTab(){
|
||||
KeyStroke ctrlTab = KeyStroke.getKeyStroke("ctrl TAB");
|
||||
KeyStroke ctrlShiftTab = KeyStroke.getKeyStroke("ctrl shift TAB");
|
||||
|
||||
// Remove ctrl-tab from normal focus traversal
|
||||
Set<AWTKeyStroke> forwardKeys = new HashSet<AWTKeyStroke>(this.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
|
||||
forwardKeys.remove(ctrlTab);
|
||||
this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardKeys);
|
||||
|
||||
// Remove ctrl-shift-tab from normal focus traversal
|
||||
Set<AWTKeyStroke> backwardKeys = new HashSet<AWTKeyStroke>(this.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
|
||||
backwardKeys.remove(ctrlShiftTab);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setEditorLineStatus(EditorLineStatus editorLineStatus) {
|
||||
this.editorLineStatus = editorLineStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void select(int selectionStart, int selectionEnd) {
|
||||
super.select(selectionStart, selectionEnd);
|
||||
if(editorLineStatus != null) editorLineStatus.set(selectionStart, selectionEnd);
|
||||
}
|
||||
|
||||
public boolean isSelectionActive() {
|
||||
return this.getSelectedText() != null;
|
||||
}
|
||||
|
||||
public void setSelectedText(String text){
|
||||
|
||||
int old = getTextMode();
|
||||
setTextMode(OVERWRITE_MODE);
|
||||
replaceSelection(text);
|
||||
setTextMode(old);
|
||||
|
||||
}
|
||||
|
||||
protected void installTokenMaker(){
|
||||
AbstractTokenMakerFactory atmf = (AbstractTokenMakerFactory)TokenMakerFactory.getDefaultInstance();
|
||||
atmf.putMapping(SYNTAX_STYLE_CPLUSPLUS, "processing.app.syntax.SketchTokenMaker");
|
||||
setSyntaxEditingStyle(SYNTAX_STYLE_CPLUSPLUS);
|
||||
}
|
||||
|
||||
public void processKeyEvent(KeyEvent evt) {
|
||||
|
||||
// this had to be added because the menu key events weren't making it up to the frame.
|
||||
|
||||
switch(evt.getID()) {
|
||||
case KeyEvent.KEY_TYPED:
|
||||
if (editorListener != null) editorListener.keyTyped(evt);
|
||||
break;
|
||||
case KeyEvent.KEY_PRESSED:
|
||||
if (editorListener != null) editorListener.keyPressed(evt);
|
||||
break;
|
||||
case KeyEvent.KEY_RELEASED:
|
||||
// inputHandler.keyReleased(evt);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!evt.isConsumed()){
|
||||
super.processKeyEvent(evt);
|
||||
}
|
||||
}
|
||||
|
||||
public void switchDocument(Document document, UndoManager newUndo) {
|
||||
|
||||
// HACK: Dont discard changes on curret UndoManager.
|
||||
// BUG: https://github.com/bobbylight/RSyntaxTextArea/issues/84
|
||||
setUndoManager(null); // bypass reset current undo manager...
|
||||
|
||||
super.setDocument(document);
|
||||
|
||||
setUndoManager((RUndoManager) newUndo);
|
||||
|
||||
// HACK: Complement previous hack (hide code folding on switch) | Drawback: Lose folding state
|
||||
// if(sketch.getCodeCount() > 1 && textarea.isCodeFoldingEnabled()){
|
||||
// textarea.setCodeFoldingEnabled(false);
|
||||
// textarea.setCodeFoldingEnabled(true);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JPopupMenu createPopupMenu() {
|
||||
JPopupMenu menu = super.createPopupMenu();
|
||||
return menu;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configurePopupMenu(JPopupMenu popupMenu) {
|
||||
super.configurePopupMenu(popupMenu);
|
||||
}
|
||||
|
||||
public void getTextLine(int line, Segment segment) {
|
||||
try {
|
||||
int offset = getLineStartOffset(line);
|
||||
int end = getLineEndOffset(line);
|
||||
getDocument().getText(offset, end - offset, segment);
|
||||
} catch (BadLocationException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public String getTextLine(int line) {
|
||||
try {
|
||||
int offset = getLineStartOffset(line);
|
||||
int end = getLineEndOffset(line);
|
||||
return getDocument().getText(offset, end - offset);
|
||||
} catch (BadLocationException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setEditorListener(EditorListener editorListener) {
|
||||
this.editorListener = editorListener;
|
||||
}
|
||||
|
||||
private static class DocLinkGenerator implements LinkGenerator{
|
||||
|
||||
@Override
|
||||
public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, final int offs) {
|
||||
|
||||
final Token token = textArea.modelToToken(offs);
|
||||
|
||||
final String reference = PdeKeywords.getReference(token.getLexeme());
|
||||
|
||||
// LOG.fine("reference: " + reference + ", match: " + (token.getType() == TokenTypes.DATA_TYPE || token.getType() == TokenTypes.VARIABLE || token.getType() == TokenTypes.FUNCTION));
|
||||
|
||||
if(token != null && (reference != null || (token.getType() == TokenTypes.DATA_TYPE || token.getType() == TokenTypes.VARIABLE || token.getType() == TokenTypes.FUNCTION) )){
|
||||
|
||||
LinkGeneratorResult generatorResult = new LinkGeneratorResult() {
|
||||
|
||||
@Override
|
||||
public int getSourceOffset() {
|
||||
return offs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HyperlinkEvent execute() {
|
||||
|
||||
LOG.fine("Open Reference: " + reference);
|
||||
|
||||
Base.showReference("Reference/" + reference);
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return generatorResult;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
121
app/src/processing/app/syntax/SketchTokenMaker.java
Normal file
121
app/src/processing/app/syntax/SketchTokenMaker.java
Normal file
@ -0,0 +1,121 @@
|
||||
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.TokenMap;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenTypes;
|
||||
import org.fife.ui.rsyntaxtextarea.modes.CPlusPlusTokenMaker;
|
||||
|
||||
import processing.app.Base;
|
||||
|
||||
/**
|
||||
* Controls the syntax highlighting of {@link SketchTextArea} based on the {@link PdeKeywords}
|
||||
* @author Ricardo JL Rufino (ricardo@criativasoft.com.br)
|
||||
* @date 20/04/2015
|
||||
* @since 1.6.4
|
||||
*/
|
||||
public class SketchTokenMaker extends CPlusPlusTokenMaker {
|
||||
|
||||
static TokenMap extraTokens;
|
||||
|
||||
public SketchTokenMaker() {
|
||||
extraTokens = getKeywords();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToken(char[] array, int start, int end, int tokenType, int startOffset, boolean hyperlink) {
|
||||
// This assumes all of your extra tokens would normally be scanned as IDENTIFIER.
|
||||
if (tokenType == TokenTypes.IDENTIFIER || tokenType == TokenTypes.DATA_TYPE) {
|
||||
int newType = extraTokens.get(array, start, end);
|
||||
if (newType>-1) {
|
||||
tokenType = newType;
|
||||
}
|
||||
}
|
||||
super.addToken(array, start, end, tokenType, startOffset, hyperlink);
|
||||
}
|
||||
|
||||
public static void addKeyword(String keyword, int type) {
|
||||
extraTokens.put(keyword, type);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
extraTokens = new TokenMap();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles loading of keywords file.
|
||||
* <P>
|
||||
* It is recommended that a # sign be used for comments
|
||||
* inside keywords.txt.
|
||||
*/
|
||||
static public TokenMap getKeywords() {
|
||||
if (extraTokens == null) {
|
||||
try {
|
||||
extraTokens = new TokenMap(false);
|
||||
|
||||
extraTokens.put("setup", TokenTypes.RESERVED_WORD);
|
||||
extraTokens.put("loop", TokenTypes.RESERVED_WORD);
|
||||
|
||||
extraTokens.put("HIGH", TokenTypes.RESERVED_WORD_2);
|
||||
extraTokens.put("LOW", TokenTypes.RESERVED_WORD_2);
|
||||
extraTokens.put("OUTPUT", TokenTypes.RESERVED_WORD_2);
|
||||
extraTokens.put("INPUT", TokenTypes.RESERVED_WORD_2);
|
||||
extraTokens.put("INPUT_PULLUP", TokenTypes.RESERVED_WORD_2);
|
||||
|
||||
extraTokens.put("CHANGE", TokenTypes.RESERVED_WORD_2);
|
||||
extraTokens.put("FALLING", TokenTypes.RESERVED_WORD_2);
|
||||
extraTokens.put("RISING", TokenTypes.RESERVED_WORD_2);
|
||||
|
||||
extraTokens.put("PI", TokenTypes.LITERAL_NUMBER_FLOAT);
|
||||
extraTokens.put("HALF_PI", TokenTypes.LITERAL_NUMBER_FLOAT);
|
||||
extraTokens.put("TWO_PI", TokenTypes.LITERAL_NUMBER_FLOAT);
|
||||
extraTokens.put("DEG_TO_RAD", TokenTypes.LITERAL_NUMBER_FLOAT);
|
||||
extraTokens.put("RAD_TO_DEG", TokenTypes.LITERAL_NUMBER_FLOAT);
|
||||
extraTokens.put("EULER", TokenTypes.LITERAL_NUMBER_FLOAT);
|
||||
|
||||
// Print.
|
||||
extraTokens.put("DEC", TokenTypes.RESERVED_WORD_2);
|
||||
extraTokens.put("HEX", TokenTypes.RESERVED_WORD_2);
|
||||
extraTokens.put("OCT", TokenTypes.RESERVED_WORD_2);
|
||||
extraTokens.put("BIN", TokenTypes.RESERVED_WORD_2);
|
||||
|
||||
extraTokens.put("true", TokenTypes.LITERAL_BOOLEAN);
|
||||
extraTokens.put("false", TokenTypes.LITERAL_BOOLEAN);
|
||||
|
||||
// Related IO
|
||||
extraTokens.put("pinMode", TokenTypes.FUNCTION);
|
||||
extraTokens.put("digitalWrite", TokenTypes.FUNCTION);
|
||||
extraTokens.put("digitalRead", TokenTypes.FUNCTION);
|
||||
extraTokens.put("analogRead", TokenTypes.FUNCTION);
|
||||
extraTokens.put("analogReference", TokenTypes.FUNCTION);
|
||||
extraTokens.put("analogWrite", TokenTypes.FUNCTION);
|
||||
|
||||
// Others.
|
||||
extraTokens.put("DIGITAL", TokenTypes.RESERVED_WORD_2);
|
||||
extraTokens.put("ANALOG", TokenTypes.RESERVED_WORD_2);
|
||||
|
||||
// force load references.
|
||||
PdeKeywords.reload();
|
||||
|
||||
|
||||
HashMap<String, Integer> keywords = PdeKeywords.get();
|
||||
Set<String> keys = keywords.keySet();
|
||||
for (String key : keys) {
|
||||
extraTokens.put(key, keywords.get(key));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Base.showError("Problem loading keywords",
|
||||
"Could not load keywords.txt,\n" +
|
||||
"please re-install Arduino.", e);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
return extraTokens;
|
||||
}
|
||||
|
||||
}
|
@ -25,11 +25,14 @@ package processing.app.tools;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.*;
|
||||
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Segment;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.Token;
|
||||
|
||||
import processing.app.*;
|
||||
import processing.app.syntax.*;
|
||||
import processing.app.legacy.PApplet;
|
||||
|
||||
/**
|
||||
* Format for Discourse Tool
|
||||
@ -44,6 +47,8 @@ import processing.app.legacy.PApplet;
|
||||
* <p/>
|
||||
* Updated for 0144 to only format the selected lines.
|
||||
* <p/>
|
||||
* Updated for 1.5.8 - Simplification, using RSyntaxTextArea TokenImpl formatter (08 dec 2014 - Ricardo JL Rufino)
|
||||
* <p/>
|
||||
* Notes from the original source:
|
||||
* Discourse.java This is a dirty-mix source.
|
||||
* NOTE that: No macs and no keyboard. Unreliable source.
|
||||
@ -54,7 +59,7 @@ public class DiscourseFormat {
|
||||
|
||||
private Editor editor;
|
||||
// JTextArea of the actual Editor
|
||||
private JEditTextArea textarea;
|
||||
private SketchTextArea textarea;
|
||||
private boolean html;
|
||||
|
||||
|
||||
@ -78,10 +83,16 @@ public class DiscourseFormat {
|
||||
StringBuilder cf = new StringBuilder(html ? "<pre>\n" : "[code]\n");
|
||||
|
||||
int selStart = textarea.getSelectionStart();
|
||||
int selStop = textarea.getSelectionStop();
|
||||
int selStop = textarea.getSelectionEnd();
|
||||
|
||||
int startLine = textarea.getSelectionStartLine();
|
||||
int stopLine = textarea.getSelectionStopLine();
|
||||
int startLine;
|
||||
int stopLine;
|
||||
try {
|
||||
startLine = textarea.getLineOfOffset(selStart);
|
||||
stopLine = textarea.getLineOfOffset(selStop);
|
||||
} catch (BadLocationException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If no selection, convert all the lines
|
||||
if (selStart == selStop) {
|
||||
@ -89,8 +100,11 @@ public class DiscourseFormat {
|
||||
stopLine = textarea.getLineCount() - 1;
|
||||
} else {
|
||||
// Make sure the selection doesn't end at the beginning of the last line
|
||||
if (textarea.getLineStartOffset(stopLine) == selStop) {
|
||||
stopLine--;
|
||||
try {
|
||||
if (textarea.getLineStartOffset(stopLine) == selStop) {
|
||||
stopLine--;
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,23 +153,15 @@ public class DiscourseFormat {
|
||||
public void appendFormattedLine(StringBuilder cf, int line) {
|
||||
Segment segment = new Segment();
|
||||
|
||||
TextAreaPainter painter = textarea.getPainter();
|
||||
TokenMarker tokenMarker = textarea.getTokenMarker();
|
||||
|
||||
// Use painter's cached info for speed
|
||||
// FontMetrics fm = painter.getFontMetrics();
|
||||
|
||||
// get line text from parent text area
|
||||
textarea.getLineText(line, segment);
|
||||
|
||||
textarea.getTextLine(line, segment);
|
||||
|
||||
char[] segmentArray = segment.array;
|
||||
int limit = segment.getEndIndex();
|
||||
int segmentOffset = segment.offset;
|
||||
int segmentCount = segment.count;
|
||||
// int width = 0;
|
||||
|
||||
// If syntax coloring is disabled, do simple translation
|
||||
if (tokenMarker == null) {
|
||||
if (!html) {
|
||||
for (int j = 0; j < segmentCount; j++) {
|
||||
char c = segmentArray[j + segmentOffset];
|
||||
appendToHTML(c, cf);
|
||||
@ -169,82 +175,19 @@ public class DiscourseFormat {
|
||||
}
|
||||
|
||||
} else {
|
||||
// If syntax coloring is enabled, we have to do this
|
||||
// because tokens can vary in width
|
||||
Token tokens;
|
||||
if ((painter.getCurrentLineIndex() == line) &&
|
||||
(painter.getCurrentLineTokens() != null)) {
|
||||
tokens = painter.getCurrentLineTokens();
|
||||
|
||||
} else {
|
||||
painter.setCurrentLineIndex(line);
|
||||
painter.setCurrentLineTokens(tokenMarker.markTokens(segment, line));
|
||||
tokens = painter.getCurrentLineTokens();
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
// Font defaultFont = painter.getFont();
|
||||
SyntaxStyle[] styles = painter.getStyles();
|
||||
|
||||
for (;;) {
|
||||
byte id = tokens.id;
|
||||
if (id == Token.END) {
|
||||
char c = segmentArray[segmentOffset + offset];
|
||||
if (segmentOffset + offset < limit) {
|
||||
appendToHTML(c, cf);
|
||||
} else {
|
||||
cf.append('\n');
|
||||
}
|
||||
return; // cf.toString();
|
||||
}
|
||||
if (id == Token.NULL) {
|
||||
// fm = painter.getFontMetrics();
|
||||
} else {
|
||||
// Place open tags []
|
||||
if (html) {
|
||||
cf.append("<span style=\"color: #");
|
||||
cf.append(PApplet.hex(styles[id].getColor().getRGB() & 0xFFFFFF, 6));
|
||||
cf.append(";\">");
|
||||
}
|
||||
|
||||
if (html && styles[id].isBold())
|
||||
cf.append("<b>");
|
||||
|
||||
// fm = styles[id].getFontMetrics(defaultFont);
|
||||
}
|
||||
int length = tokens.length;
|
||||
|
||||
for (int j = 0; j < length; j++) {
|
||||
char c = segmentArray[segmentOffset + offset + j];
|
||||
if (offset == 0 && c == ' ') {
|
||||
// Works on Safari but not Camino 1.6.3 or Firefox 2.x on OS X.
|
||||
cf.append(html ? " " : '\u00A0'); //
|
||||
// if ((j % 2) == 1) {
|
||||
// cf.append("[b]\u00A0[/b]");
|
||||
// } else {
|
||||
// cf.append(' ');
|
||||
// }
|
||||
} else {
|
||||
appendToHTML(c, cf);
|
||||
}
|
||||
// Place close tags [/]
|
||||
if (html && j == (length - 1) && id != Token.NULL && styles[id].isBold())
|
||||
cf.append("</b>");
|
||||
if (html && j == (length - 1) && id != Token.NULL)
|
||||
cf.append("</span>");
|
||||
// int charWidth;
|
||||
// if (c == '\t') {
|
||||
// charWidth = (int) painter
|
||||
// .nextTabStop(width, offset + j)
|
||||
// - width;
|
||||
// } else {
|
||||
// charWidth = fm.charWidth(c);
|
||||
// }
|
||||
// width += charWidth;
|
||||
}
|
||||
offset += length;
|
||||
tokens = tokens.next;
|
||||
|
||||
Token tokenList = textarea.getTokenListForLine(line);
|
||||
|
||||
while(tokenList != null){
|
||||
if(tokenList.getType() == Token.NULL){
|
||||
cf.append('\n');
|
||||
}else if(tokenList.isPaintable()){
|
||||
tokenList.appendHTMLRepresentation(cf, textarea, false);
|
||||
}
|
||||
|
||||
tokenList = tokenList.getNextToken();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user