1
0
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:
ricardo.jl.rufino 2015-04-22 14:13:37 -03:00 committed by Federico Fissore
parent 63f153c0c2
commit 1379505e13
14 changed files with 939 additions and 1161 deletions

36
app/src/ArduinoIDE.java Normal file
View 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);
}
}

View File

@ -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."));
}

View File

@ -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();
}

View File

@ -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)
@ -944,6 +952,23 @@ public class Editor extends JFrame implements RunnerListener {
}
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 {
Class<?> toolClass = Class.forName(className);
@ -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) {
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);
try {
return textarea.getText(textarea.getLineStartOffset(line), textarea.getLineEndOffset(line));
} catch (BadLocationException e) {
return "";
}
/**
* 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();
}
@ -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) {
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;
}
}
@ -1742,29 +1735,8 @@ public class Editor extends JFrame implements RunnerListener {
}
/**
* 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,38 +1760,40 @@ 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();
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,103 +1840,67 @@ 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--;
}
/**
* 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();
}
// 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("//");
}
/**
* Ends an "atomic" edit.
*
* @see #beginInternalAtomicEdit()
*/
public void endInternalAtomicEdit(){
textarea.getUndoManager().endInternalAtomicEdit();
}
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();
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);
startCompoundEdit();
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("");
int caretPosition = textarea.getCaretPosition();
boolean noSelec = !textarea.isSelectionActive();
// 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);
}
// 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();
} else {
Action action = textarea.getActionMap().get(RSyntaxTextAreaEditorKit.rstaDecreaseIndentAction);
action.actionPerformed(null);
}
}
/** Checks the preferences you are in external editing mode */
public static boolean isExternalMode(){
return PreferencesData.getBoolean("editor.external");
}
protected String getCurrentKeyword() {
@ -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() {
public void actionPerformed(ActionEvent e) {
Base.openURL(clickedURL);
}
});
add(openURLItem);
menu.addSeparator();
openURLItemSeparator = new JSeparator();
add(openURLItemSeparator);
JMenuItem item = createToolMenuItem("cc.arduino.packages.formatter.AStyle");
item.setName("menuToolsAutoFormat");
cutItem = new JMenuItem(_("Cut"));
cutItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleCut();
}
});
add(cutItem);
menu.add(item);
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() {
public void actionPerformed(ActionEvent e) {
handleSelectAll();
}
});
add(item);
addSeparator();
item = new JMenuItem(_("Comment/Uncomment"));
item = newJMenuItem(_("Comment/Uncomment"), '/');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleCommentUncomment();
}
});
add(item);
menu.add(item);
item = new JMenuItem(_("Increase Indent"));
item = newJMenuItem(_("Increase Indent"), ']');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleIndentOutdent(true);
}
});
add(item);
menu.add(item);
item = new JMenuItem(_("Decrease Indent"));
item = newJMenuItem(_("Decrease Indent"), '[');
item.setName("menuDecreaseIndent");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleIndentOutdent(false);
}
});
add(item);
menu.add(item);
addSeparator();
item = new JMenuItem(_("Copy for Forum"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleDiscourseCopy();
}
});
menu.add(item);
referenceItem = new JMenuItem(_("Find in Reference"));
item = new JMenuItem(_("Copy as HTML"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleHTMLCopy();
}
});
menu.add(item);
final JMenuItem referenceItem = new JMenuItem(_("Find in Reference"));
referenceItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleFindReference();
}
});
add(referenceItem);
menu.add(referenceItem);
final JMenuItem openURLItem = new JMenuItem(_("Open URL"));
openURLItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Base.openURL(e.getActionCommand());
}
});
menu.add(openURLItem);
// 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);
}
menu.addPopupMenuListener(new PopupMenuListener() {
if (textarea.isSelectionActive()) {
cutItem.setEnabled(true);
copyItem.setEnabled(true);
discourseItem.setEnabled(true);
} else {
cutItem.setEnabled(false);
copyItem.setEnabled(false);
discourseItem.setEnabled(false);
}
referenceFile = PdeKeywords.getReference(getCurrentKeyword());
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
String referenceFile = PdeKeywords.getReference(getCurrentKeyword());
referenceItem.setEnabled(referenceFile != null);
super.show(component, x, y);
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);
}
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
}
}

View File

@ -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");

View File

@ -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;
public class EditorListener implements KeyListener {
/**
* 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 {
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;
public EditorListener(Editor editor) {
super();
this.editor = editor;
}
/** 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_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 EditorListener(Editor editor, JEditTextArea textarea) {
this.editor = editor;
this.textarea = textarea;
// let him know that i'm leechin'
textarea.editorListener = this;
applyPreferences();
}
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);
// }
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();
}
}
}
//System.out.println((int)c + " " + code + " " + event);
//System.out.println();
@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 (event.isAltDown() && code == KeyEvent.VK_T) {
// int line = textarea.getCaretLineNumber();
// textarea.setActiveLineRange(line, line + 3);
// }
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;
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
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);
// }
// }
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;
}
/**
* 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++;
}
}
}
*/
}

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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.
}
}

View File

@ -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
@ -105,16 +107,39 @@ public class Theme {
return font;
}
static public SyntaxStyle getStyle(String what) {
String split[] = get("editor." + what + ".style").split(",");
/**
* Returns the default font for text areas.
*
* @return The default font.
*/
public static final Font getDefaultFont() {
Color color = PreferencesHelper.parseColor(split[0]);
// Use StyleContext to get a composite font for better Asian language
// support; see Sun bug S282887.
StyleContext sc = StyleContext.getDefaultStyleContext();
Font font = null;
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);
}
}
//System.out.println(font.getFamily() + ", " + font.getName());
return font;
String style = split[1];
boolean bold = style.contains("bold");
boolean italic = style.contains("italic");
boolean underlined = style.contains("underlined");
return new SyntaxStyle(color, italic, bold, underlined);
}
}

View File

@ -24,47 +24,42 @@
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"));
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()) getKeywords(keywords);
if (keywords.exists()) load(keywords);
}
}
} catch (Exception e) {
Base.showError("Problem loading keywords",
@ -73,10 +68,11 @@ public class PdeKeywords extends CTokenMarker {
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);
}
}

View 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;
}
};
}

View 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;
}
}

View File

@ -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,9 +100,12 @@ public class DiscourseFormat {
stopLine = textarea.getLineCount() - 1;
} else {
// Make sure the selection doesn't end at the beginning of the last line
try {
if (textarea.getLineStartOffset(stopLine) == selStop) {
stopLine--;
}
} catch (BadLocationException e) {
}
}
// Read the code line by line
@ -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();
}
Token tokenList = textarea.getTokenListForLine(line);
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 {
while(tokenList != null){
if(tokenList.getType() == Token.NULL){
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(";\">");
}else if(tokenList.isPaintable()){
tokenList.appendHTMLRepresentation(cf, textarea, false);
}
if (html && styles[id].isBold())
cf.append("<b>");
tokenList = tokenList.getNextToken();
}
// 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 ? "&nbsp;" : '\u00A0'); // &nbsp;
// 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;
}
}
}
}