1
0
mirror of https://github.com/arduino/Arduino.git synced 2025-03-13 10:29:35 +01:00

Use a separate RSyntaxTextArea for each editor tab

RSyntaxTextArea appears to support using a single instance and replacing
the underlying text and document when switching between tabs, but in
practice this support is not complete and even though the
RSyntaxTextArea developers did some work to improve the situation, they
recommend to just use a seperate instance for each tab.

This commit implements exactly that. A new class EditorTab is introduce
to wrap the RSyntaxTextArea and containing scroll pane, and to
encapsulate the code related to handling the text area itself. Doing so
removes some quirks and prepares for some later additions. In
particular, error highlights are now no longer shared between all tabs,
which was previously the case.

This commit mostly moves code from Editor into EditorTab, and updates
the callers to use getCurrentTab() and call methods on the result
instead of calling them on Editor. Some code is added to take care of
creating multiple EditorTab objects and switching between them. Some
small changes have been made to make the flow of opening files work,
though these are mostly a bit hacky.

While moving code, changes to the rest of the code were kept minimal,
retaining existing interfaces as much as possible. This sometimes result
in less than ideal code, which should be cleaned up in subsequent
commits.

The SketchCodeDocument class has been pretty much emptied out, since
it was mostly used to store things for tabs in the background, which are
now just stored in each RSyntaxTextArea separately. The last remaining
bits of this class can probably be moved or implemented differently
later, so it can be removed.

The entire flow of working with sketches and files needs to be cleaned
up next, so no thorough attempt at testing this commit was done. It is
likely that there are plenty of corner cases and race conditions, which
will be fixed once the reset of the code is cleaned up.

Fixes #3441
This commit is contained in:
Matthijs Kooijman 2015-12-03 23:36:55 +01:00 committed by Martino Facchin
parent c945b6c30a
commit 982d4f3fbf
13 changed files with 637 additions and 601 deletions

View File

@ -78,7 +78,7 @@ public class AStyle implements Tool {
@Override
public void run() {
String originalText = editor.getText();
String originalText = editor.getCurrentTab().getText();
String formattedText = aStyleInterface.AStyleMain(originalText, formatterConfiguration);
if (formattedText.equals(originalText)) {
@ -86,14 +86,14 @@ public class AStyle implements Tool {
return;
}
SketchTextArea textArea = editor.getTextArea();
SketchTextArea textArea = editor.getCurrentTab().getTextArea();
int line = getLineOfOffset(textArea);
int lineOffset = getLineOffset(textArea, line);
textArea.getUndoManager().beginInternalAtomicEdit();
editor.removeAllLineHighlights();
editor.setText(formattedText);
editor.getCurrentTab().setText(formattedText);
editor.getSketch().setModified(true);
textArea.getUndoManager().endInternalAtomicEdit();

View File

@ -127,7 +127,7 @@ public class GoToLineNumber extends javax.swing.JDialog {
private void okActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okActionPerformed
try {
int line = Integer.parseInt(lineNumber.getText());
editor.goToLine(line);
editor.getCurrentTab().goToLine(line);
cancelActionPerformed(evt);
} catch (Exception e) {
// ignore

View File

@ -292,7 +292,7 @@ public class FindReplace extends javax.swing.JFrame {
return false;
}
String text = editor.getText();
String text = editor.getCurrentTab().getText();
if (ignoreCaseBox.isSelected()) {
search = search.toLowerCase();
@ -302,7 +302,7 @@ public class FindReplace extends javax.swing.JFrame {
int nextIndex;
if (!backwards) {
// int selectionStart = editor.textarea.getSelectionStart();
int selectionEnd = editor.getSelectionStop();
int selectionEnd = editor.getCurrentTab().getSelectionStop();
nextIndex = text.indexOf(search, selectionEnd);
if (wrap && nextIndex == -1) {
@ -311,7 +311,7 @@ public class FindReplace extends javax.swing.JFrame {
}
} else {
// int selectionStart = editor.textarea.getSelectionStart();
int selectionStart = editor.getSelectionStart() - 1;
int selectionStart = editor.getCurrentTab().getSelectionStart() - 1;
if (selectionStart >= 0) {
nextIndex = text.lastIndexOf(search, selectionStart);
@ -346,12 +346,12 @@ public class FindReplace extends javax.swing.JFrame {
if (backwards) {
sketch.handlePrevCode();
this.setVisible(true);
int l = editor.getText().length() - 1;
editor.setSelection(l, l);
int l = editor.getCurrentTab().getText().length() - 1;
editor.getCurrentTab().setSelection(l, l);
} else {
sketch.handleNextCode();
this.setVisible(true);
editor.setSelection(0, 0);
editor.getCurrentTab().setSelection(0, 0);
}
return find(wrap, backwards, true, originTab);
@ -365,7 +365,7 @@ public class FindReplace extends javax.swing.JFrame {
}
if (nextIndex != -1) {
editor.setSelection(nextIndex, nextIndex + search.length());
editor.getCurrentTab().setSelection(nextIndex, nextIndex + search.length());
return true;
}
@ -381,17 +381,17 @@ public class FindReplace extends javax.swing.JFrame {
return;
}
int newpos = editor.getSelectionStart() - findField.getText().length();
int newpos = editor.getCurrentTab().getSelectionStart() - findField.getText().length();
if (newpos < 0) {
newpos = 0;
}
editor.setSelection(newpos, newpos);
editor.getCurrentTab().setSelection(newpos, newpos);
boolean foundAtLeastOne = false;
if (find(false, false, searchAllFilesBox.isSelected(), -1)) {
foundAtLeastOne = true;
editor.setSelectedText(replaceField.getText());
editor.getCurrentTab().setSelectedText(replaceField.getText());
editor.getSketch().setModified(true); // TODO is this necessary?
}
@ -423,13 +423,13 @@ public class FindReplace extends javax.swing.JFrame {
editor.getSketch().setCurrentCode(0); // select the first tab
}
editor.setSelection(0, 0); // move to the beginning
editor.getCurrentTab().setSelection(0, 0); // move to the beginning
boolean foundAtLeastOne = false;
while (true) {
if (find(false, false, searchAllFilesBox.isSelected(), -1)) {
foundAtLeastOne = true;
editor.setSelectedText(replaceField.getText());
editor.getCurrentTab().setSelectedText(replaceField.getText());
editor.getSketch().setModified(true); // TODO is this necessary?
} else {
break;

View File

@ -608,10 +608,10 @@ public class Base {
activeEditor.rebuildRecentSketchesMenu();
if (PreferencesData.getBoolean("editor.external")) {
try {
int previousCaretPosition = activeEditor.getTextArea().getCaretPosition();
int previousCaretPosition = activeEditor.getCurrentTab().getTextArea().getCaretPosition();
activeEditor.getSketch().load(true);
if (previousCaretPosition < activeEditor.getText().length()) {
activeEditor.getTextArea().setCaretPosition(previousCaretPosition);
if (previousCaretPosition < activeEditor.getCurrentTab().getText().length()) {
activeEditor.getCurrentTab().getTextArea().setCaretPosition(previousCaretPosition);
}
} catch (IOException e) {
// noop

View File

@ -29,34 +29,21 @@ import cc.arduino.packages.uploaders.SerialUploader;
import cc.arduino.view.GoToLineNumber;
import cc.arduino.view.StubMenuListener;
import cc.arduino.view.findreplace.FindReplace;
import cc.arduino.UpdatableBoardsLibsFakeURLsHandler;
import com.jcraft.jsch.JSchException;
import jssc.SerialPortException;
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 processing.app.debug.RunnerException;
import processing.app.forms.PasswordAuthorizationDialog;
import processing.app.helpers.Keys;
import processing.app.helpers.OSUtils;
import processing.app.helpers.PreferencesMapException;
import processing.app.legacy.PApplet;
import processing.app.syntax.ArduinoTokenMakerFactory;
import processing.app.syntax.PdeKeywords;
import processing.app.syntax.SketchTextArea;
import processing.app.syntax.SketchTextAreaEditorKit;
import processing.app.tools.DiscourseFormat;
import processing.app.tools.MenuScroller;
import processing.app.tools.Tool;
import javax.swing.*;
import javax.swing.border.MatteBorder;
import javax.swing.event.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.PlainDocument;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
@ -91,9 +78,12 @@ public class Editor extends JFrame implements RunnerListener {
public static final int MAX_TIME_AWAITING_FOR_RESUMING_SERIAL_MONITOR = 10000;
private final Platform platform;
final Platform platform;
private JMenu recentSketchesMenu;
private JMenu programmersMenu;
private final Box upper;
private ArrayList<EditorTab> tabs = new ArrayList<>();
private int currentTabIndex = -1;
private static class ShouldSaveIfModified implements Predicate<Sketch> {
@ -170,12 +160,12 @@ public class Editor extends JFrame implements RunnerListener {
// currently opened program
Sketch sketch;
private EditorLineStatus lineStatus;
EditorLineStatus lineStatus;
//JEditorPane editorPane;
private SketchTextArea textarea;
private RTextScrollPane scrollPane;
/** Contains all EditorTabs, of which only one will be visible */
private JPanel codePanel;
//Runner runtime;
@ -263,7 +253,7 @@ public class Editor extends JFrame implements RunnerListener {
contentPain.add(pane, BorderLayout.CENTER);
Box box = Box.createVerticalBox();
Box upper = Box.createVerticalBox();
upper = Box.createVerticalBox();
if (toolbarMenu == null) {
toolbarMenu = new JMenu();
@ -275,9 +265,6 @@ public class Editor extends JFrame implements RunnerListener {
header = new EditorHeader(this);
upper.add(header);
textarea = createTextArea();
textarea.setName("editor");
// assemble console panel, consisting of status area and the console itself
JPanel consolePanel = new JPanel();
consolePanel.setLayout(new BorderLayout());
@ -294,19 +281,9 @@ public class Editor extends JFrame implements RunnerListener {
lineStatus = new EditorLineStatus();
consolePanel.add(lineStatus, BorderLayout.SOUTH);
// RTextScrollPane
scrollPane = new RTextScrollPane(textarea, true);
scrollPane.setBorder(new MatteBorder(0, 6, 0, 0, Theme.getColor("editor.bgcolor")));
scrollPane.setViewportBorder(BorderFactory.createEmptyBorder());
scrollPane.setLineNumbersEnabled(PreferencesData.getBoolean("editor.linenumbers"));
scrollPane.setIconRowHeaderEnabled(false);
Gutter gutter = scrollPane.getGutter();
gutter.setBookmarkingEnabled(false);
//gutter.setBookmarkIcon(CompletionsRenderer.getIcon(CompletionType.TEMPLATE));
gutter.setIconRowHeaderInheritsGutterBackground(true);
codePanel = new JPanel(new BorderLayout());
upper.add(codePanel);
upper.add(scrollPane);
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, upper, consolePanel);
// repaint child panes while resizing
@ -466,44 +443,11 @@ public class Editor extends JFrame implements RunnerListener {
* with things in the Preferences window.
*/
public void applyPreferences() {
// apply the setting for 'use external editor'
boolean external = PreferencesData.getBoolean("editor.external");
textarea.setEditable(!external);
saveMenuItem.setEnabled(!external);
saveAsMenuItem.setEnabled(!external);
textarea.setCodeFoldingEnabled(PreferencesData.getBoolean("editor.code_folding"));
scrollPane.setFoldIndicatorEnabled(PreferencesData.getBoolean("editor.code_folding"));
scrollPane.setLineNumbersEnabled(PreferencesData.getBoolean("editor.linenumbers"));
if (external) {
// disable line highlight and turn off the caret when disabling
textarea.setBackground(Theme.getColor("editor.external.bgcolor"));
textarea.setHighlightCurrentLine(false);
textarea.setEditable(false);
} else {
textarea.setBackground(Theme.getColor("editor.bgcolor"));
textarea.setHighlightCurrentLine(Theme.getBoolean("editor.linehighlight"));
textarea.setEditable(true);
}
// apply changes to the font size for the editor
Font editorFont = scale(PreferencesData.getFont("editor.font"));
textarea.setFont(editorFont);
scrollPane.getGutter().setLineNumberFont(editorFont);
// in case tab expansion stuff has changed
// listener.applyPreferences();
// in case moved to a new location
// For 0125, changing to async version (to be implemented later)
//sketchbook.rebuildMenus();
// For 0126, moved into Base, which will notify all editors.
//base.rebuildMenusAsync();
for (EditorTab tab: tabs)
tab.applyPreferences();
}
@ -1003,55 +947,14 @@ public class Editor extends JFrame implements RunnerListener {
}
}
return null;
}
private SketchTextArea createTextArea() throws IOException {
final SketchTextArea textArea = new SketchTextArea(base.getPdeKeywords());
textArea.setFocusTraversalKeysEnabled(false);
textArea.requestFocusInWindow();
textArea.setMarkOccurrences(PreferencesData.getBoolean("editor.advanced"));
textArea.setMarginLineEnabled(false);
textArea.setCodeFoldingEnabled(PreferencesData.getBoolean("editor.code_folding"));
textArea.setAntiAliasingEnabled(PreferencesData.getBoolean("editor.antialias"));
textArea.setTabsEmulated(PreferencesData.getBoolean("editor.tabs.expand"));
textArea.setTabSize(PreferencesData.getInteger("editor.tabs.size"));
textArea.addHyperlinkListener(evt -> {
try {
UpdatableBoardsLibsFakeURLsHandler boardLibHandler = new UpdatableBoardsLibsFakeURLsHandler(base);
boardLibHandler.openBoardLibManager(evt.getURL());
}
catch (Exception e) {
try {
platform.openURL(sketch.getFolder(), evt.getURL().toExternalForm());
} catch (Exception f) {
Base.showWarning(f.getMessage(), f.getMessage(), f);
}
}
});
textArea.addCaretListener(e -> {
Element root = textArea.getDocument().getDefaultRootElement();
int lineStart = root.getElementIndex(e.getMark());
int lineEnd = root.getElementIndex(e.getDot());
lineStatus.set(lineStart, lineEnd);
});
ToolTipManager.sharedInstance().registerComponent(textArea);
configurePopupMenu(textArea);
return textArea;
}
}
public void updateKeywords(PdeKeywords keywords) {
// update GUI for "Find In Reference"
textarea.setKeywords(keywords);
// update document for syntax highlighting
RSyntaxDocument document = (RSyntaxDocument) textarea.getDocument();
document.setTokenMakerFactory(new ArduinoTokenMakerFactory(keywords));
document.setSyntaxStyle(RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS);
for (EditorTab tab : tabs)
tab.updateKeywords(keywords);
}
private JMenuItem createToolMenuItem(String className) {
JMenuItem createToolMenuItem(String className) {
try {
Class<?> toolClass = Class.forName(className);
final Tool tool = (Tool) toolClass.newInstance();
@ -1381,7 +1284,7 @@ public class Editor extends JFrame implements RunnerListener {
JMenuItem cutItem = newJMenuItem(tr("Cut"), 'X');
cutItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleCut();
getCurrentTab().handleCut();
}
});
menu.add(cutItem);
@ -1389,7 +1292,7 @@ public class Editor extends JFrame implements RunnerListener {
JMenuItem copyItem = newJMenuItem(tr("Copy"), 'C');
copyItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textarea.copy();
getCurrentTab().getTextArea().copy();
}
});
menu.add(copyItem);
@ -1397,11 +1300,7 @@ public class Editor extends JFrame implements RunnerListener {
JMenuItem copyForumItem = newJMenuItemShift(tr("Copy for Forum"), 'C');
copyForumItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// SwingUtilities.invokeLater(new Runnable() {
// public void run() {
new DiscourseFormat(Editor.this, false).show();
// }
// });
getCurrentTab().handleHTMLCopy();
}
});
menu.add(copyForumItem);
@ -1409,11 +1308,7 @@ public class Editor extends JFrame implements RunnerListener {
JMenuItem copyHTMLItem = newJMenuItemAlt(tr("Copy as HTML"), 'C');
copyHTMLItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// SwingUtilities.invokeLater(new Runnable() {
// public void run() {
new DiscourseFormat(Editor.this, true).show();
// }
// });
getCurrentTab().handleDiscourseCopy();
}
});
menu.add(copyHTMLItem);
@ -1421,7 +1316,7 @@ public class Editor extends JFrame implements RunnerListener {
JMenuItem pasteItem = newJMenuItem(tr("Paste"), 'V');
pasteItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textarea.paste();
getCurrentTab().handlePaste();
sketch.setModified(true);
}
});
@ -1430,7 +1325,7 @@ public class Editor extends JFrame implements RunnerListener {
JMenuItem selectAllItem = newJMenuItem(tr("Select All"), 'A');
selectAllItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textarea.selectAll();
getCurrentTab().handleSelectAll();
}
});
menu.add(selectAllItem);
@ -1448,7 +1343,7 @@ public class Editor extends JFrame implements RunnerListener {
JMenuItem commentItem = newJMenuItem(tr("Comment/Uncomment"), '/');
commentItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleCommentUncomment();
getCurrentTab().handleCommentUncomment();
}
});
menu.add(commentItem);
@ -1457,7 +1352,7 @@ public class Editor extends JFrame implements RunnerListener {
increaseIndentItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
increaseIndentItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleIndentOutdent(true);
getCurrentTab().handleIndentOutdent(true);
}
});
menu.add(increaseIndentItem);
@ -1467,7 +1362,7 @@ public class Editor extends JFrame implements RunnerListener {
decreseIndentItem.setName("menuDecreaseIndent");
decreseIndentItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleIndentOutdent(false);
getCurrentTab().handleIndentOutdent(false);
}
});
menu.add(decreseIndentItem);
@ -1481,7 +1376,7 @@ public class Editor extends JFrame implements RunnerListener {
find = new FindReplace(Editor.this, Base.FIND_DIALOG_STATE);
}
if (!OSUtils.isMacOS()) {
find.setFindText(getSelectedText());
find.setFindText(getCurrentTab().getSelectedText());
}
find.setLocationRelativeTo(Editor.this);
find.setVisible(true);
@ -1516,7 +1411,7 @@ public class Editor extends JFrame implements RunnerListener {
if (find == null) {
find = new FindReplace(Editor.this, Base.FIND_DIALOG_STATE);
}
find.setFindText(getSelectedText());
find.setFindText(getCurrentTab().getSelectedText());
}
});
menu.add(useSelectionForFindItem);
@ -1586,7 +1481,7 @@ public class Editor extends JFrame implements RunnerListener {
public void actionPerformed(ActionEvent e) {
try {
textarea.undoLastAction();
getCurrentTab().handleUndo();
sketch.setModified(true);
} catch (CannotUndoException ex) {
//System.out.println("Unable to undo: " + ex);
@ -1595,8 +1490,7 @@ public class Editor extends JFrame implements RunnerListener {
}
protected void updateUndoState() {
UndoManager undo = textarea.getUndoManager();
UndoManager undo = getCurrentTab().getUndoManager();
if (undo.canUndo()) {
this.setEnabled(true);
@ -1621,7 +1515,7 @@ public class Editor extends JFrame implements RunnerListener {
public void actionPerformed(ActionEvent e) {
try {
textarea.redoLastAction();
getCurrentTab().handleRedo();
sketch.setModified(true);
} catch (CannotRedoException ex) {
//System.out.println("Unable to redo: " + ex);
@ -1630,8 +1524,8 @@ public class Editor extends JFrame implements RunnerListener {
}
protected void updateRedoState() {
UndoManager undo = textarea.getUndoManager();
UndoManager undo = getCurrentTab().getUndoManager();
if (undo.canRedo()) {
redoItem.setEnabled(true);
redoItem.setText(undo.getRedoPresentationName());
@ -1677,240 +1571,82 @@ public class Editor extends JFrame implements RunnerListener {
return sketch;
}
/**
* 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 TextArea.
* Gets the currently displaying tab.
*/
public SketchTextArea getTextArea() {
return textarea;
public EditorTab getCurrentTab() {
return tabs.get(currentTabIndex);
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/**
* Get the contents of the current buffer. Used by the Sketch class.
* Change the currently displayed tab.
* Note that the GUI might not update immediately, since this needs
* to run in the Event dispatch thread.
* @param index The index of the tab to select
*/
public String getText() {
return textarea.getText();
public void selectTab(final int index) {
currentTabIndex = index;
undoAction.updateUndoState();
redoAction.updateRedoState();
updateTitle();
// This must be run in the GUI thread
SwingUtilities.invokeLater(() -> {
codePanel.removeAll();
codePanel.add(tabs.get(index), BorderLayout.CENTER);
tabs.get(index).requestFocus(); // get the caret blinking
// For some reason, these are needed. Revalidate says it should be
// automatically called when components are added or removed, but without
// it, the component switched to is not displayed. repaint() is needed to
// clear the entire text area of any previous text.
codePanel.revalidate();
codePanel.repaint();
});
}
/**
* Replace the entire contents of the front-most tab.
*/
public void setText(String what) {
textarea.setText(what);
public EditorTab findTab(final SketchCode doc) {
return tabs.get(findTabIndex(doc));
}
/**
* Called to update the text but not switch to a different set of code
* (which would affect the undo manager).
*/
// public void setText2(String what, int start, int stop) {
// beginCompoundEdit();
// textarea.setText(what);
// endCompoundEdit();
//
// // make sure that a tool isn't asking for a bad location
// start = Math.max(0, Math.min(start, textarea.getDocumentLength()));
// stop = Math.max(0, Math.min(start, textarea.getDocumentLength()));
// textarea.select(start, stop);
//
// textarea.requestFocus(); // get the caret blinking
// }
public String getSelectedText() {
return textarea.getSelectedText();
public int findTabIndex(final SketchCode doc) {
for (int i = 0; i < tabs.size(); ++i) {
if (tabs.get(i).getSketchCode() == doc)
return i;
}
return -1;
}
public void setSelectedText(String what) {
textarea.replaceSelection(what);
}
public void setSelection(int start, int stop) {
textarea.select(start, stop);
}
/**
* Get the beginning point of the current selection.
*/
public int getSelectionStart() {
return textarea.getSelectionStart();
}
/**
* Get the end point of the current selection.
*/
public int getSelectionStop() {
return textarea.getSelectionEnd();
}
/**
* Get text for a specified line.
*/
private String getLineText(int line) {
try {
return textarea.getText(textarea.getLineStartOffset(line), textarea.getLineEndOffset(line));
} catch (BadLocationException e) {
return "";
public void sketchLoaded(Sketch sketch) {
tabs.clear();
currentTabIndex = -1;
tabs.ensureCapacity(sketch.getCodeCount());
for (SketchCode code : sketch.getCodes()) {
try {
addTab(code);
} catch(IOException e) {
// TODO: Improve / move error handling
System.err.println(e);
}
}
}
public int getScrollPosition() {
return scrollPane.getVerticalScrollBar().getValue();
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/**
* Switch between tabs, this swaps out the Document object
* that's currently being manipulated.
*/
protected void setCode(final SketchCodeDocument codeDoc) {
RSyntaxDocument document = (RSyntaxDocument) codeDoc.getDocument();
if (document == null) { // this document not yet inited
document = new RSyntaxDocument(new ArduinoTokenMakerFactory(base.getPdeKeywords()), RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS);
document.putProperty(PlainDocument.tabSizeAttribute, PreferencesData.getInteger("editor.tabs.size"));
// insert the program text into the document object
try {
document.insertString(0, codeDoc.getCode().getProgram(), null);
} catch (BadLocationException bl) {
bl.printStackTrace();
}
// set up this guy's own undo manager
// code.undo = new UndoManager();
codeDoc.setDocument(document);
}
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
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();
}
});
updateTitle();
selectTab(findTabIndex(codeDoc.getCode()));
}
protected void addTab(SketchCode code) throws IOException {
EditorTab tab = new EditorTab(this, code);
tabs.add(tab);
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/**
* Implements Edit &rarr; Cut.
*/
private void handleCut() {
textarea.cut();
}
private void handleDiscourseCopy() {
new DiscourseFormat(Editor.this, false).show();
}
private void handleHTMLCopy() {
new DiscourseFormat(Editor.this, true).show();
}
void handleCommentUncomment() {
Action action = textarea.getActionMap().get(RSyntaxTextAreaEditorKit.rstaToggleCommentAction);
action.actionPerformed(null);
}
private void handleIndentOutdent(boolean indent) {
if (indent) {
Action action = textarea.getActionMap().get(SketchTextAreaEditorKit.rtaIncreaseIndentAction);
action.actionPerformed(null);
} else {
Action action = textarea.getActionMap().get(RSyntaxTextAreaEditorKit.rstaDecreaseIndentAction);
action.actionPerformed(null);
}
}
private String getCurrentKeyword() {
String text = "";
if (textarea.getSelectedText() != null)
text = textarea.getSelectedText().trim();
try {
int current = textarea.getCaretPosition();
int startOffset = 0;
int endIndex = current;
String tmp = textarea.getDocument().getText(current, 1);
// TODO probably a regexp that matches Arduino lang special chars
// already exists.
String regexp = "[\\s\\n();\\\\.!='\\[\\]{}]";
while (!tmp.matches(regexp)) {
endIndex++;
tmp = textarea.getDocument().getText(endIndex, 1);
}
// For some reason document index start at 2.
// if( current - start < 2 ) return;
tmp = "";
while (!tmp.matches(regexp)) {
startOffset++;
if (current - startOffset < 0) {
tmp = textarea.getDocument().getText(0, 1);
break;
} else
tmp = textarea.getDocument().getText(current - startOffset, 1);
}
startOffset--;
int length = endIndex - current + startOffset;
text = textarea.getDocument().getText(current - startOffset, length);
} catch (BadLocationException bl) {
bl.printStackTrace();
}
return text;
}
private void handleFindReference(ActionEvent e) {
String text = getCurrentKeyword();
void handleFindReference(ActionEvent e) {
String text = getCurrentTab().getCurrentKeyword();
String referenceFile = base.getPdeKeywords().getReference(text);
if (referenceFile == null) {
@ -2000,12 +1736,13 @@ public class Editor extends JFrame implements RunnerListener {
}
public void removeAllLineHighlights() {
textarea.removeAllLineHighlights();
for (EditorTab tab : tabs)
tab.getTextArea().removeAllLineHighlights();
}
public void addLineHighlight(int line) throws BadLocationException {
textarea.addLineHighlight(line, new Color(1, 0, 0, 0.2f));
textarea.setCaretPosition(textarea.getLineStartOffset(line));
getCurrentTab().getTextArea().addLineHighlight(line, new Color(1, 0, 0, 0.2f));
getCurrentTab().getTextArea().setCaretPosition(getCurrentTab().getTextArea().getLineStartOffset(line));
}
private class DefaultStopHandler implements Runnable {
@ -2130,11 +1867,10 @@ public class Editor extends JFrame implements RunnerListener {
untitled = false;
sketch.setCurrentCode(codeIndex);
textarea.select(selStart, selStop);
scrollPane.getVerticalScrollBar().setValue(scrollPos);
getCurrentTab().setSelection(selStart, selStop);
getCurrentTab().setScrollPosition(scrollPos);
}
/**
* Second stage of open, occurs after having checked to see if the
* modifications (if any) to the previous sketch need to be saved.
@ -2205,6 +1941,7 @@ public class Editor extends JFrame implements RunnerListener {
Base.showWarning(tr("Error"), tr("Could not create the sketch."), e);
return false;
}
header.rebuild();
updateTitle();
// Disable untitled setting from previous document, if any
@ -2842,9 +2579,9 @@ public class Editor extends JFrame implements RunnerListener {
PrinterJob printerJob = PrinterJob.getPrinterJob();
if (pageFormat != null) {
//System.out.println("setting page format " + pageFormat);
printerJob.setPrintable(textarea, pageFormat);
printerJob.setPrintable(getCurrentTab().getTextArea(), pageFormat);
} else {
printerJob.setPrintable(textarea);
printerJob.setPrintable(getCurrentTab().getTextArea());
}
// set the name of the job to the code name
printerJob.setJobName(sketch.getCurrentCode().getPrettyName());
@ -2897,17 +2634,17 @@ public class Editor extends JFrame implements RunnerListener {
if (re.hasCodeLine()) {
int line = re.getCodeLine();
// subtract one from the end so that the \n ain't included
if (line >= textarea.getLineCount()) {
if (line >= getCurrentTab().getTextArea().getLineCount()) {
// 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 (getLineText(line).length() == 0) {
line = getCurrentTab().getTextArea().getLineCount() - 1;
if (getCurrentTab().getLineText(line).length() == 0) {
// The last line may be zero length, meaning nothing to select.
// If so, back up one more line.
line--;
}
}
if (line < 0 || line >= textarea.getLineCount()) {
if (line < 0 || line >= getCurrentTab().getTextArea().getLineCount()) {
System.err.println(I18n.format(tr("Bad error line: {0}"), line));
} else {
try {
@ -2952,7 +2689,6 @@ public class Editor extends JFrame implements RunnerListener {
statusNotice(EMPTY);
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
protected void onBoardOrPortChange() {
@ -2966,110 +2702,4 @@ public class Editor extends JFrame implements RunnerListener {
}
private void configurePopupMenu(final SketchTextArea textarea){
JPopupMenu menu = textarea.getPopupMenu();
menu.addSeparator();
JMenuItem item = createToolMenuItem("cc.arduino.packages.formatter.AStyle");
if (item == null) {
throw new NullPointerException("Tool cc.arduino.packages.formatter.AStyle unavailable");
}
item.setName("menuToolsAutoFormat");
menu.add(item);
item = newJMenuItem(tr("Comment/Uncomment"), '/');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleCommentUncomment();
}
});
menu.add(item);
item = newJMenuItem(tr("Increase Indent"), ']');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleIndentOutdent(true);
}
});
menu.add(item);
item = newJMenuItem(tr("Decrease Indent"), '[');
item.setName("menuDecreaseIndent");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleIndentOutdent(false);
}
});
menu.add(item);
item = new JMenuItem(tr("Copy for Forum"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleDiscourseCopy();
}
});
menu.add(item);
item = new JMenuItem(tr("Copy as HTML"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleHTMLCopy();
}
});
menu.add(item);
final JMenuItem referenceItem = new JMenuItem(tr("Find in Reference"));
referenceItem.addActionListener(this::handleFindReference);
menu.add(referenceItem);
final JMenuItem openURLItem = new JMenuItem(tr("Open URL"));
openURLItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Base.openURL(e.getActionCommand());
}
});
menu.add(openURLItem);
menu.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
String referenceFile = base.getPdeKeywords().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);
}
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
}
public void goToLine(int line) {
if (line <= 0) {
return;
}
try {
textarea.setCaretPosition(textarea.getLineStartOffset(line - 1));
} catch (BadLocationException e) {
//ignore
}
}
}

View File

@ -0,0 +1,481 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Arduino project - http://www.arduino.cc
Copyright (c) 2015 Matthijs Kooijman
Copyright (c) 2004-09 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
*/
package processing.app;
import static processing.app.I18n.tr;
import static processing.app.Theme.scale;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.ToolTipManager;
import javax.swing.border.MatteBorder;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.PlainDocument;
import javax.swing.undo.UndoManager;
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.UpdatableBoardsLibsFakeURLsHandler;
import processing.app.syntax.ArduinoTokenMakerFactory;
import processing.app.syntax.PdeKeywords;
import processing.app.syntax.SketchTextArea;
import processing.app.syntax.SketchTextAreaEditorKit;
import processing.app.tools.DiscourseFormat;
/**
* Single tab, editing a single file, in the main window.
*/
public class EditorTab extends JPanel {
protected Editor editor;
protected SketchTextArea textarea;
protected RTextScrollPane scrollPane;
protected SketchCode code;
public EditorTab(Editor editor, SketchCode code) throws IOException {
super(new BorderLayout());
this.editor = editor;
this.code = code;
this.textarea = createTextArea();
this.scrollPane = createScrollPane(this.textarea);
applyPreferences();
add(this.scrollPane, BorderLayout.CENTER);
UndoManager undo = new LastUndoableEditAwareUndoManager(this.textarea, this.editor);
((RSyntaxDocument)textarea.getDocument()).addUndoableEditListener(undo);
}
private RSyntaxDocument createDocument() {
RSyntaxDocument document = new RSyntaxDocument(new ArduinoTokenMakerFactory(editor.base.getPdeKeywords()), RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS);
document.putProperty(PlainDocument.tabSizeAttribute, PreferencesData.getInteger("editor.tabs.size"));
// insert the program text into the document object
try {
document.insertString(0, code.getProgram(), null);
} catch (BadLocationException bl) {
bl.printStackTrace();
}
((SketchCodeDocument) code.getMetadata()).setDocument(document);
return document;
}
private RTextScrollPane createScrollPane(SketchTextArea textArea) throws IOException {
RTextScrollPane scrollPane = new RTextScrollPane(textArea, true);
scrollPane.setBorder(new MatteBorder(0, 6, 0, 0, Theme.getColor("editor.bgcolor")));
scrollPane.setViewportBorder(BorderFactory.createEmptyBorder());
scrollPane.setLineNumbersEnabled(PreferencesData.getBoolean("editor.linenumbers"));
scrollPane.setIconRowHeaderEnabled(false);
Gutter gutter = scrollPane.getGutter();
gutter.setBookmarkingEnabled(false);
//gutter.setBookmarkIcon(CompletionsRenderer.getIcon(CompletionType.TEMPLATE));
gutter.setIconRowHeaderInheritsGutterBackground(true);
return scrollPane;
}
private SketchTextArea createTextArea() throws IOException {
RSyntaxDocument document = createDocument();
final SketchTextArea textArea = new SketchTextArea(document, editor.base.getPdeKeywords());
textArea.setName("editor");
textArea.setFocusTraversalKeysEnabled(false);
//textArea.requestFocusInWindow();
textArea.setMarkOccurrences(PreferencesData.getBoolean("editor.advanced"));
textArea.setMarginLineEnabled(false);
textArea.setCodeFoldingEnabled(PreferencesData.getBoolean("editor.code_folding"));
textArea.setAntiAliasingEnabled(PreferencesData.getBoolean("editor.antialias"));
textArea.setTabsEmulated(PreferencesData.getBoolean("editor.tabs.expand"));
textArea.setTabSize(PreferencesData.getInteger("editor.tabs.size"));
textArea.addHyperlinkListener(evt -> {
try {
UpdatableBoardsLibsFakeURLsHandler boardLibHandler = new UpdatableBoardsLibsFakeURLsHandler(editor.base);
boardLibHandler.openBoardLibManager(evt.getURL());
}
catch (Exception e) {
try {
editor.platform.openURL(editor.getSketch().getFolder(), evt.getURL().toExternalForm());
} catch (Exception f) {
Base.showWarning(f.getMessage(), f.getMessage(), f);
}
}
});
textArea.addCaretListener(e -> {
Element root = textArea.getDocument().getDefaultRootElement();
int lineStart = root.getElementIndex(e.getMark());
int lineEnd = root.getElementIndex(e.getDot());
editor.lineStatus.set(lineStart, lineEnd);
});
ToolTipManager.sharedInstance().registerComponent(textArea);
configurePopupMenu(textArea);
return textArea;
}
private void configurePopupMenu(final SketchTextArea textarea){
JPopupMenu menu = textarea.getPopupMenu();
menu.addSeparator();
JMenuItem item = editor.createToolMenuItem("cc.arduino.packages.formatter.AStyle");
if (item == null) {
throw new NullPointerException("Tool cc.arduino.packages.formatter.AStyle unavailable");
}
item.setName("menuToolsAutoFormat");
menu.add(item);
item = new JMenuItem(tr("Comment/Uncomment"), '/');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleCommentUncomment();
}
});
menu.add(item);
item = new JMenuItem(tr("Increase Indent"), ']');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleIndentOutdent(true);
}
});
menu.add(item);
item = new JMenuItem(tr("Decrease Indent"), '[');
item.setName("menuDecreaseIndent");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleIndentOutdent(false);
}
});
menu.add(item);
item = new JMenuItem(tr("Copy for Forum"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleDiscourseCopy();
}
});
menu.add(item);
item = new JMenuItem(tr("Copy as HTML"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleHTMLCopy();
}
});
menu.add(item);
final JMenuItem referenceItem = new JMenuItem(tr("Find in Reference"));
referenceItem.addActionListener(editor::handleFindReference);
menu.add(referenceItem);
final JMenuItem openURLItem = new JMenuItem(tr("Open URL"));
openURLItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Base.openURL(e.getActionCommand());
}
});
menu.add(openURLItem);
menu.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
String referenceFile = editor.base.getPdeKeywords().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);
}
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
}
public void applyPreferences() {
textarea.setCodeFoldingEnabled(PreferencesData.getBoolean("editor.code_folding"));
scrollPane.setFoldIndicatorEnabled(PreferencesData.getBoolean("editor.code_folding"));
scrollPane.setLineNumbersEnabled(PreferencesData.getBoolean("editor.linenumbers"));
// apply the setting for 'use external editor'
if (PreferencesData.getBoolean("editor.external")) {
// disable line highlight and turn off the caret when disabling
textarea.setBackground(Theme.getColor("editor.external.bgcolor"));
textarea.setHighlightCurrentLine(false);
textarea.setEditable(false);
} else {
textarea.setBackground(Theme.getColor("editor.bgcolor"));
textarea.setHighlightCurrentLine(Theme.getBoolean("editor.linehighlight"));
textarea.setEditable(true);
}
// apply changes to the font size for the editor
Font editorFont = scale(PreferencesData.getFont("editor.font"));
textarea.setFont(editorFont);
scrollPane.getGutter().setLineNumberFont(editorFont);
}
public void updateKeywords(PdeKeywords keywords) {
// update GUI for "Find In Reference"
textarea.setKeywords(keywords);
// update document for syntax highlighting
RSyntaxDocument document = (RSyntaxDocument) textarea.getDocument();
document.setTokenMakerFactory(new ArduinoTokenMakerFactory(keywords));
document.setSyntaxStyle(RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS);
}
/**
* 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 TextArea.
*/
public SketchTextArea getTextArea() {
return textarea;
}
/**
* Get the sketch this tab is editing a file from.
*/
public Sketch getSketch() {
return editor.getSketch();
}
/**
* Get the SketchCodeDocument that is being edited in this tab.
*/
public SketchCode getSketchCode() {
return this.code;
}
/**
* Get the contents of the text area.
*/
public String getText() {
return textarea.getText();
}
/**
* Replace the entire contents of this tab.
*/
public void setText(String what) {
textarea.setText(what);
}
public String getSelectedText() {
return textarea.getSelectedText();
}
public void setSelectedText(String what) {
textarea.replaceSelection(what);
}
public void setSelection(int start, int stop) {
textarea.select(start, stop);
}
public int getScrollPosition() {
return scrollPane.getVerticalScrollBar().getValue();
}
public void setScrollPosition(int pos) {
scrollPane.getVerticalScrollBar().setValue(pos);
}
/**
* Get the beginning point of the current selection.
*/
public int getSelectionStart() {
return textarea.getSelectionStart();
}
/**
* Get the end point of the current selection.
*/
public int getSelectionStop() {
return textarea.getSelectionEnd();
}
/**
* Get text for a specified line.
*/
public String getLineText(int line) {
try {
return textarea.getText(textarea.getLineStartOffset(line), textarea.getLineEndOffset(line));
} catch (BadLocationException e) {
return "";
}
}
/**
* Jump to the given line
* @param line The line number to jump to, 1-based.
*/
public void goToLine(int line) {
if (line <= 0) {
return;
}
try {
textarea.setCaretPosition(textarea.getLineStartOffset(line - 1));
} catch (BadLocationException e) {
//ignore
}
}
void handleCut() {
textarea.cut();
}
void handleCopy() {
textarea.copy();
}
void handlePaste() {
textarea.paste();
}
void handleSelectAll() {
textarea.selectAll();
}
void handleCommentUncomment() {
Action action = textarea.getActionMap().get(RSyntaxTextAreaEditorKit.rstaToggleCommentAction);
action.actionPerformed(null);
}
void handleDiscourseCopy() {
new DiscourseFormat(editor, this, false).show();
}
void handleHTMLCopy() {
new DiscourseFormat(editor, this, true).show();
}
void handleIndentOutdent(boolean indent) {
if (indent) {
Action action = textarea.getActionMap().get(SketchTextAreaEditorKit.rtaIncreaseIndentAction);
action.actionPerformed(null);
} else {
Action action = textarea.getActionMap().get(RSyntaxTextAreaEditorKit.rstaDecreaseIndentAction);
action.actionPerformed(null);
}
}
void handleUndo() {
textarea.undoLastAction();
}
void handleRedo() {
textarea.redoLastAction();
}
public UndoManager getUndoManager() {
return textarea.getUndoManager();
}
public String getCurrentKeyword() {
String text = "";
if (textarea.getSelectedText() != null)
text = textarea.getSelectedText().trim();
try {
int current = textarea.getCaretPosition();
int startOffset = 0;
int endIndex = current;
String tmp = textarea.getDocument().getText(current, 1);
// TODO probably a regexp that matches Arduino lang special chars
// already exists.
String regexp = "[\\s\\n();\\\\.!='\\[\\]{}]";
while (!tmp.matches(regexp)) {
endIndex++;
tmp = textarea.getDocument().getText(endIndex, 1);
}
// For some reason document index start at 2.
// if( current - start < 2 ) return;
tmp = "";
while (!tmp.matches(regexp)) {
startOffset++;
if (current - startOffset < 0) {
tmp = textarea.getDocument().getText(0, 1);
break;
} else
tmp = textarea.getDocument().getText(current - startOffset, 1);
}
startOffset--;
int length = endIndex - current + startOffset;
text = textarea.getDocument().getText(current - startOffset, length);
} catch (BadLocationException bl) {
bl.printStackTrace();
}
return text;
}
@Override
public void requestFocus() {
/** If focus is requested, focus the textarea instead. */
textarea.requestFocus();
}
}

View File

@ -456,7 +456,7 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key
shiftPressed = !shiftPressed;
repaint();
}
// Return false to continue processing this keyEvent
// Return false to continue processing this keyEvent
return false;
}
}

View File

@ -95,6 +95,7 @@ public class Sketch {
}
protected void load(boolean forceUpdate) throws IOException {
current = null;
data.load();
for (SketchCode code : data.getCodes()) {
@ -104,6 +105,7 @@ public class Sketch {
// set the main file to be the current tab
if (editor != null) {
editor.sketchLoaded(this);
setCurrentCode(currentIndex, forceUpdate);
}
}
@ -317,7 +319,7 @@ public class Sketch {
// first get the contents of the editor text area
if (current.getCode().isModified()) {
current.getCode().setProgram(editor.getText());
current.getCode().setProgram(editor.getCurrentTab().getText());
try {
// save this new SketchCode
current.getCode().save();
@ -362,9 +364,9 @@ public class Sketch {
// (unfortunately this will kill positions for carets etc)
editor.handleOpenUnchecked(newMainFile,
currentIndex,
editor.getSelectionStart(),
editor.getSelectionStop(),
editor.getScrollPosition());
editor.getCurrentTab().getSelectionStart(),
editor.getCurrentTab().getSelectionStop(),
editor.getCurrentTab().getScrollPosition());
// get the changes into the sketchbook menu
// (re-enabled in 0115 to fix bug #332)
@ -398,7 +400,17 @@ public class Sketch {
return;
}
ensureExistence();
data.addCode((new SketchCodeDocument(this, newFile)).getCode());
SketchCode code = (new SketchCodeDocument(this, newFile)).getCode();
try {
editor.addTab(code);
} catch (IOException e) {
Base.showWarning(tr("Error"),
I18n.format(
"Failed to open tab for new file"
), e);
return;
}
data.addCode(code);
}
// sort the entries
@ -542,7 +554,7 @@ public class Sketch {
// first get the contents of the editor text area
if (current.getCode().isModified()) {
current.getCode().setProgram(editor.getText());
current.getCode().setProgram(editor.getCurrentTab().getText());
}
// don't do anything if not actually modified
@ -698,7 +710,7 @@ public class Sketch {
// grab the contents of the current tab before saving
// first get the contents of the editor text area
if (current.getCode().isModified()) {
current.getCode().setProgram(editor.getText());
current.getCode().setProgram(editor.getCurrentTab().getText());
}
// save the other tabs to their new location
@ -734,9 +746,9 @@ public class Sketch {
editor.handleOpenUnchecked(newFile,
currentIndex,
editor.getSelectionStart(),
editor.getSelectionStop(),
editor.getScrollPosition());
editor.getCurrentTab().getSelectionStart(),
editor.getCurrentTab().getSelectionStop(),
editor.getCurrentTab().getScrollPosition());
// Name changed, rebuild the sketch menus
//editor.sketchbook.rebuildMenusAsync();
@ -950,9 +962,9 @@ public class Sketch {
buffer.append(">\n");
}
buffer.append('\n');
buffer.append(editor.getText());
editor.setText(buffer.toString());
editor.setSelection(0, 0); // scroll to start
buffer.append(editor.getCurrentTab().getText());
editor.getCurrentTab().setText(buffer.toString());
editor.getCurrentTab().setSelection(0, 0); // scroll to start
setModified(true);
}
@ -976,26 +988,12 @@ public class Sketch {
}
// get the text currently being edited
if (current != null) {
current.getCode().setProgram(editor.getText());
current.setSelectionStart(editor.getSelectionStart());
current.setSelectionStop(editor.getSelectionStop());
current.setScrollPosition(editor.getScrollPosition());
}
if (current != null)
current.getCode().setProgram(editor.getCurrentTab().getText());
current = (SketchCodeDocument) data.getCode(which).getMetadata();
currentIndex = which;
if (SwingUtilities.isEventDispatchThread()) {
editor.setCode(current);
} else {
try {
SwingUtilities.invokeAndWait(() -> editor.setCode(current));
} catch (Exception e) {
e.printStackTrace();
}
}
editor.setCode(current);
editor.header.rebuild();
}
@ -1051,7 +1049,7 @@ public class Sketch {
// make sure the user didn't hide the sketch folder
ensureExistence();
current.getCode().setProgram(editor.getText());
current.getCode().setProgram(editor.getCurrentTab().getText());
// TODO record history here
//current.history.record(program, SketchHistory.RUN);

View File

@ -5,23 +5,11 @@ import java.io.File;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;
import javax.swing.undo.UndoManager;
public class SketchCodeDocument implements DocumentListener{
public class SketchCodeDocument implements 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 UndoManager undo;
// saved positions from last time this tab was used
private int selectionStart;
private int selectionStop;
private int scrollPosition;
public SketchCodeDocument(Sketch sketch, SketchCode code) {
this.code = code;
@ -30,40 +18,7 @@ public class SketchCodeDocument implements DocumentListener{
}
public SketchCodeDocument(Sketch sketch, File file) {
this.code = new SketchCode(file, this);
this.sketch = sketch;
}
public UndoManager getUndo() {
return undo;
}
public void setUndo(UndoManager undo) {
this.undo = undo;
}
public int getSelectionStart() {
return selectionStart;
}
public void setSelectionStart(int selectionStart) {
this.selectionStart = selectionStart;
}
public int getSelectionStop() {
return selectionStop;
}
public void setSelectionStop(int selectionStop) {
this.selectionStop = selectionStop;
}
public int getScrollPosition() {
return scrollPosition;
}
public void setScrollPosition(int scrollPosition) {
this.scrollPosition = scrollPosition;
this(sketch, new SketchCode(file));
}
public SketchCode getCode() {
@ -74,12 +29,7 @@ public class SketchCodeDocument implements DocumentListener{
this.code = code;
}
public Document getDocument() {
return document;
}
public void setDocument(Document document) {
this.document = document;
document.addDocumentListener(this);
}
@ -96,8 +46,7 @@ public class SketchCodeDocument implements DocumentListener{
@Override
public void changedUpdate(DocumentEvent e) {
// Callback for when styles in the current document change.
// This method is never called.
// ignore
}
}

View File

@ -38,7 +38,6 @@ import org.fife.ui.rsyntaxtextarea.*;
import org.fife.ui.rsyntaxtextarea.Token;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextAreaUI;
import org.fife.ui.rtextarea.RUndoManager;
import processing.app.Base;
import processing.app.BaseNoGui;
import processing.app.PreferencesData;
@ -47,9 +46,7 @@ import javax.swing.event.EventListenerList;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Segment;
import javax.swing.undo.UndoManager;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.io.File;
@ -73,7 +70,8 @@ public class SketchTextArea extends RSyntaxTextArea {
private PdeKeywords pdeKeywords;
public SketchTextArea(PdeKeywords pdeKeywords) throws IOException {
public SketchTextArea(RSyntaxDocument document, PdeKeywords pdeKeywords) throws IOException {
super(document);
this.pdeKeywords = pdeKeywords;
installFeatures();
fixCtrlDeleteBehavior();
@ -153,25 +151,6 @@ public class SketchTextArea extends RSyntaxTextArea {
return this.getSelectedText() != null;
}
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 RTAMouseListener createMouseListener() {
return new SketchTextAreaMouseListener(this);

View File

@ -25,6 +25,7 @@ package processing.app.tools;
import org.fife.ui.rsyntaxtextarea.Token;
import processing.app.Editor;
import processing.app.EditorTab;
import processing.app.syntax.SketchTextArea;
import javax.swing.text.BadLocationException;
@ -65,9 +66,9 @@ public class DiscourseFormat {
* from the actual Processing Tab ready to send to the processing discourse
* web (copy & paste)
*/
public DiscourseFormat(Editor editor, boolean html) {
public DiscourseFormat(Editor editor, EditorTab tab, boolean html) {
this.editor = editor;
this.textarea = editor.getTextArea();
this.textarea = tab.getTextArea();
this.html = html;
}

View File

@ -69,10 +69,8 @@ public class FixEncoding implements Tool {
SketchCode code = sketch.getCode(i);
code.setProgram(loadWithLocalEncoding(code.getFile()));
code.setModified(true); // yes, because we want them to save this
editor.findTab(code).setText(code.getProgram());
}
// Update the currently visible program with its code
editor.setText(sketch.getCurrentCode().getProgram());
} catch (IOException e) {
String msg =
tr("An error occurred while trying to fix the file encoding.\nDo not attempt to save this sketch as it may overwrite\nthe old version. Use Open to re-open the sketch and try again.\n") +

View File

@ -55,7 +55,7 @@ public class BlockCommentGeneratesOneUndoActionTest extends AbstractGUITest {
GuiActionRunner.execute(new GuiQuery<Frame>() {
protected Frame executeInEDT() {
window.getEditor().handleCommentUncomment();
window.getEditor().getCurrentTab().handleCommentUncomment();
return window.getEditor();
}