diff --git a/app/.classpath b/app/.classpath index f32ff129bf7..b3104a0644e 100644 --- a/app/.classpath +++ b/app/.classpath @@ -41,7 +41,7 @@ - + diff --git a/app/lib/rsyntaxtextarea-2.5.8.1+arduino.jar b/app/lib/rsyntaxtextarea-2.5.8.1+arduino.jar deleted file mode 100644 index 66e48d20aa4..00000000000 Binary files a/app/lib/rsyntaxtextarea-2.5.8.1+arduino.jar and /dev/null differ diff --git a/app/lib/rsyntaxtextarea-2.6.1.jar b/app/lib/rsyntaxtextarea-2.6.1.jar new file mode 100644 index 00000000000..b834e2d412b Binary files /dev/null and b/app/lib/rsyntaxtextarea-2.6.1.jar differ diff --git a/app/src/processing/app/CaretAwareUndoableEdit.java b/app/src/processing/app/CaretAwareUndoableEdit.java deleted file mode 100644 index b0983ad2f04..00000000000 --- a/app/src/processing/app/CaretAwareUndoableEdit.java +++ /dev/null @@ -1,77 +0,0 @@ -package processing.app; - -import processing.app.syntax.SketchTextArea; - -import javax.swing.undo.CannotRedoException; -import javax.swing.undo.CannotUndoException; -import javax.swing.undo.UndoableEdit; - -public class CaretAwareUndoableEdit implements UndoableEdit { - - private final UndoableEdit undoableEdit; - private final int caretPosition; - - public CaretAwareUndoableEdit(UndoableEdit undoableEdit, SketchTextArea textArea) { - this.undoableEdit = undoableEdit; - this.caretPosition = textArea.getCaretPosition(); - } - - @Override - public void undo() throws CannotUndoException { - undoableEdit.undo(); - } - - @Override - public boolean canUndo() { - return undoableEdit.canUndo(); - } - - @Override - public void redo() throws CannotRedoException { - undoableEdit.redo(); - } - - @Override - public boolean canRedo() { - return undoableEdit.canRedo(); - } - - @Override - public void die() { - undoableEdit.die(); - } - - @Override - public boolean addEdit(UndoableEdit undoableEdit) { - return this.undoableEdit.addEdit(undoableEdit); - } - - @Override - public boolean replaceEdit(UndoableEdit undoableEdit) { - return this.undoableEdit.replaceEdit(undoableEdit); - } - - @Override - public boolean isSignificant() { - return undoableEdit.isSignificant(); - } - - @Override - public String getPresentationName() { - return undoableEdit.getPresentationName(); - } - - @Override - public String getUndoPresentationName() { - return undoableEdit.getUndoPresentationName(); - } - - @Override - public String getRedoPresentationName() { - return undoableEdit.getRedoPresentationName(); - } - - public int getCaretPosition() { - return caretPosition; - } -} diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index c7e60c35f2f..9ee54d8024c 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -33,6 +33,7 @@ import jssc.SerialPortException; import processing.app.debug.RunnerException; import processing.app.forms.PasswordAuthorizationDialog; +import processing.app.helpers.ActionWrapper; import processing.app.helpers.Keys; import processing.app.helpers.OSUtils; import processing.app.helpers.PreferencesMapException; @@ -44,9 +45,9 @@ import javax.swing.*; import javax.swing.event.*; import javax.swing.text.BadLocationException; -import javax.swing.undo.CannotRedoException; -import javax.swing.undo.CannotUndoException; -import javax.swing.undo.UndoManager; +import org.fife.ui.rtextarea.RTextArea; +import org.fife.ui.rtextarea.RecordableTextAction; + import java.awt.*; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; @@ -182,12 +183,6 @@ public boolean test(SketchController sketch) { //boolean presenting; private boolean uploading; - // undo fellers - private JMenuItem undoItem; - private JMenuItem redoItem; - protected UndoAction undoAction; - protected RedoAction redoAction; - private FindReplace find; Runnable runHandler; @@ -1265,43 +1260,58 @@ public void actionPerformed(ActionEvent e) { return menu; } + /** + * Wrapper around RSyntaxTextArea's RecordableTextActions. Normally, + * these actions use the event source, or the most recently focused + * component to find the text area to work on, but this does not + * always work. This wrapper makes them always work on the currently + * selected tab instead. + */ + class RstaActionWrapper extends ActionWrapper { + RstaActionWrapper(RecordableTextAction action) { + super(action); + } + + @Override + public void actionPerformed(ActionEvent e) { + ((RecordableTextAction) getWrappedAction()).actionPerformedImpl(e, getCurrentTab().getTextArea()); + } + } private JMenu buildEditMenu() { JMenu menu = new JMenu(tr("Edit")); menu.setName("menuEdit"); menu.setMnemonic(KeyEvent.VK_E); - undoItem = newJMenuItem(tr("Undo"), 'Z'); + // Create a dummy RTextArea, to force it to create the static list + // of actions accessible through RTextArea.getAction(). + new RTextArea(); + + RecordableTextAction undoAction = RTextArea.getAction(RTextArea.UNDO_ACTION); + JMenuItem undoItem = new JMenuItem(new RstaActionWrapper(undoAction)); undoItem.setName("menuEditUndo"); - undoItem.addActionListener(undoAction = new UndoAction()); menu.add(undoItem); - if (!OSUtils.isMacOS()) { - redoItem = newJMenuItem(tr("Redo"), 'Y'); - } else { - redoItem = newJMenuItemShift(tr("Redo"), 'Z'); - } + RecordableTextAction redoAction = RTextArea.getAction(RTextArea.REDO_ACTION); + JMenuItem redoItem = new JMenuItem(new RstaActionWrapper(redoAction)); redoItem.setName("menuEditRedo"); - redoItem.addActionListener(redoAction = new RedoAction()); menu.add(redoItem); + // RSTA defaults to Ctrl-Y for redo, but convention on OSX is Ctrl-Shift-Z + // This might not be the best place, but it works. + if (OSUtils.isMacOS()) { + int mods = SHORTCUT_KEY_MASK | ActionEvent.SHIFT_MASK; + redoAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, mods)); + } + menu.addSeparator(); - JMenuItem cutItem = newJMenuItem(tr("Cut"), 'X'); - cutItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - getCurrentTab().handleCut(); - } - }); - menu.add(cutItem); + RecordableTextAction cutAction = RTextArea.getAction(RTextArea.CUT_ACTION); + menu.add(new JMenuItem(new RstaActionWrapper(cutAction))); + + RecordableTextAction copyAction = RTextArea.getAction(RTextArea.COPY_ACTION); + menu.add(new JMenuItem(new RstaActionWrapper(copyAction))); - JMenuItem copyItem = newJMenuItem(tr("Copy"), 'C'); - copyItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - getCurrentTab().getTextArea().copy(); - } - }); - menu.add(copyItem); JMenuItem copyForumItem = newJMenuItemShift(tr("Copy for Forum"), 'C'); copyForumItem.addActionListener(new ActionListener() { @@ -1319,21 +1329,11 @@ public void actionPerformed(ActionEvent e) { }); menu.add(copyHTMLItem); - JMenuItem pasteItem = newJMenuItem(tr("Paste"), 'V'); - pasteItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - getCurrentTab().handlePaste(); - } - }); - menu.add(pasteItem); + RecordableTextAction pasteAction = RTextArea.getAction(RTextArea.PASTE_ACTION); + menu.add(new JMenuItem(new RstaActionWrapper(pasteAction))); - JMenuItem selectAllItem = newJMenuItem(tr("Select All"), 'A'); - selectAllItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - getCurrentTab().handleSelectAll(); - } - }); - menu.add(selectAllItem); + RecordableTextAction selectAllAction = RTextArea.getAction(RTextArea.SELECT_ALL_ACTION); + menu.add(new JMenuItem(new RstaActionWrapper(selectAllAction))); JMenuItem gotoLine = newJMenuItem(tr("Go to line..."), 'L'); gotoLine.addActionListener(e -> { @@ -1422,21 +1422,6 @@ public void actionPerformed(ActionEvent e) { menu.add(useSelectionForFindItem); } - menu.addMenuListener(new MenuListener() { - @Override - public void menuSelected(MenuEvent e) { - boolean enabled = getCurrentTab().getSelectedText() != null; - cutItem.setEnabled(enabled); - copyItem.setEnabled(enabled); - } - - @Override - public void menuDeselected(MenuEvent e) {} - - @Override - public void menuCanceled(MenuEvent e) {} - }); - return menu; } @@ -1474,75 +1459,6 @@ private static JMenuItem newJMenuItemAlt(String title, int what) { return menuItem; } - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - class UndoAction extends AbstractAction { - public UndoAction() { - super("Undo"); - this.setEnabled(false); - } - - public void actionPerformed(ActionEvent e) { - try { - getCurrentTab().handleUndo(); - } catch (CannotUndoException ex) { - //System.out.println("Unable to undo: " + ex); - //ex.printStackTrace(); - } - } - - protected void updateUndoState() { - UndoManager undo = getCurrentTab().getUndoManager(); - - if (undo.canUndo()) { - this.setEnabled(true); - undoItem.setEnabled(true); - undoItem.setText(undo.getUndoPresentationName()); - putValue(Action.NAME, undo.getUndoPresentationName()); - } else { - this.setEnabled(false); - undoItem.setEnabled(false); - undoItem.setText(tr("Undo")); - putValue(Action.NAME, "Undo"); - } - } - } - - - class RedoAction extends AbstractAction { - public RedoAction() { - super("Redo"); - this.setEnabled(false); - } - - public void actionPerformed(ActionEvent e) { - try { - getCurrentTab().handleRedo(); - } catch (CannotRedoException ex) { - //System.out.println("Unable to redo: " + ex); - //ex.printStackTrace(); - } - } - - protected void updateRedoState() { - UndoManager undo = getCurrentTab().getUndoManager(); - - if (undo.canRedo()) { - redoItem.setEnabled(true); - redoItem.setText(undo.getRedoPresentationName()); - putValue(Action.NAME, undo.getRedoPresentationName()); - } else { - this.setEnabled(false); - redoItem.setEnabled(false); - redoItem.setText(tr("Redo")); - putValue(Action.NAME, "Redo"); - } - } - } - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -1610,8 +1526,6 @@ public List getTabs() { */ public void selectTab(final int index) { currentTabIndex = index; - undoAction.updateUndoState(); - redoAction.updateRedoState(); updateTitle(); header.rebuild(); getCurrentTab().activated(); diff --git a/app/src/processing/app/EditorTab.java b/app/src/processing/app/EditorTab.java index 988b7813657..c44d2d51627 100644 --- a/app/src/processing/app/EditorTab.java +++ b/app/src/processing/app/EditorTab.java @@ -44,7 +44,6 @@ import javax.swing.text.BadLocationException; import javax.swing.text.Element; import javax.swing.text.PlainDocument; -import javax.swing.undo.UndoManager; import javax.swing.text.DefaultCaret; import org.fife.ui.rsyntaxtextarea.RSyntaxDocument; @@ -52,7 +51,6 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities; import org.fife.ui.rtextarea.Gutter; import org.fife.ui.rtextarea.RTextScrollPane; -import org.fife.ui.rtextarea.RUndoManager; import cc.arduino.UpdatableBoardsLibsFakeURLsHandler; import processing.app.helpers.DocumentTextChangeListener; @@ -107,10 +105,6 @@ public EditorTab(Editor editor, SketchFile file, String contents) file.setStorage(this); applyPreferences(); add(this.scrollPane, BorderLayout.CENTER); - - RUndoManager undo = new LastUndoableEditAwareUndoManager(this.textarea, this.editor); - document.addUndoableEditListener(undo); - textarea.setUndoManager(undo); } private RSyntaxDocument createDocument(String contents) { @@ -406,14 +400,14 @@ public void setText(String what) { int oldLength = doc.getLength(); // The undo manager already seems to group the insert and remove together // automatically, but better be explicit about it. - textarea.getUndoManager().beginInternalAtomicEdit(); + textarea.beginAtomicEdit(); try { doc.insertString(oldLength, what, null); doc.remove(0, oldLength); } catch (BadLocationException e) { System.err.println("Unexpected failure replacing text"); } finally { - textarea.getUndoManager().endInternalAtomicEdit(); + textarea.endAtomicEdit(); } } finally { caret.setUpdatePolicy(policy); @@ -553,10 +547,6 @@ void handleRedo() { textarea.redoLastAction(); } - public UndoManager getUndoManager() { - return textarea.getUndoManager(); - } - public String getCurrentKeyword() { String text = ""; if (textarea.getSelectedText() != null) diff --git a/app/src/processing/app/LastUndoableEditAwareUndoManager.java b/app/src/processing/app/LastUndoableEditAwareUndoManager.java deleted file mode 100644 index 736be42d39b..00000000000 --- a/app/src/processing/app/LastUndoableEditAwareUndoManager.java +++ /dev/null @@ -1,37 +0,0 @@ -package processing.app; - -import javax.swing.undo.CannotRedoException; -import javax.swing.undo.CannotUndoException; - -import org.fife.ui.rtextarea.RUndoManager; - -import processing.app.syntax.SketchTextArea; - -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 { - super.undo(); - } - - @Override - public synchronized void redo() throws CannotRedoException { - super.redo(); - } - - @Override - public void updateActions() { - super.updateActions(); - editor.undoAction.updateUndoState(); - editor.redoAction.updateRedoState(); - } - - -} diff --git a/app/src/processing/app/helpers/ActionWrapper.java b/app/src/processing/app/helpers/ActionWrapper.java new file mode 100644 index 00000000000..fdd34b7013e --- /dev/null +++ b/app/src/processing/app/helpers/ActionWrapper.java @@ -0,0 +1,54 @@ +package processing.app.helpers; + +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.Action; + +public class ActionWrapper implements Action { + protected Action action; + + public ActionWrapper(Action wrapped) { + this.action = wrapped; + } + + public Action getWrappedAction() { + return this.action; + } + + @Override + public void actionPerformed(ActionEvent e) { + this.action.actionPerformed(e); + } + + @Override + public Object getValue(String key) { + return this.action.getValue(key); + } + + @Override + public void putValue(String key, Object value) { + this.action.putValue(key, value); + } + + @Override + public void setEnabled(boolean b) { + this.action.setEnabled(b); + } + + @Override + public boolean isEnabled() { + return this.action.isEnabled(); + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + this.action.addPropertyChangeListener(listener); + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + this.action.removePropertyChangeListener(listener); + } + +} diff --git a/build/windows/launcher/config.xml b/build/windows/launcher/config.xml index 7168db2e01b..0e511358505 100644 --- a/build/windows/launcher/config.xml +++ b/build/windows/launcher/config.xml @@ -47,7 +47,7 @@ %EXEDIR%/lib/jsch-0.1.50.jar %EXEDIR%/lib/jssc-2.8.0.jar %EXEDIR%/lib/pde.jar - %EXEDIR%/lib/rsyntaxtextarea-2.5.8.1+arduino.jar + %EXEDIR%/lib/rsyntaxtextarea-2.6.1.jar %EXEDIR%/lib/xml-apis-1.3.04.jar %EXEDIR%/lib/xml-apis-ext-1.3.04.jar %EXEDIR%/lib/xmlgraphics-commons-2.0.jar diff --git a/build/windows/launcher/config_debug.xml b/build/windows/launcher/config_debug.xml index 1710133024e..ac863160792 100644 --- a/build/windows/launcher/config_debug.xml +++ b/build/windows/launcher/config_debug.xml @@ -47,7 +47,7 @@ %EXEDIR%/lib/jsch-0.1.50.jar %EXEDIR%/lib/jssc-2.8.0.jar %EXEDIR%/lib/pde.jar - %EXEDIR%/lib/rsyntaxtextarea-2.5.8.1+arduino.jar + %EXEDIR%/lib/rsyntaxtextarea-2.6.1.jar %EXEDIR%/lib/xml-apis-1.3.04.jar %EXEDIR%/lib/xml-apis-ext-1.3.04.jar %EXEDIR%/lib/xmlgraphics-commons-2.0.jar