diff --git a/app/.classpath b/app/.classpath index 326de565678..1491af0b6cb 100644 --- a/app/.classpath +++ b/app/.classpath @@ -31,5 +31,6 @@ + diff --git a/app/lib/rsyntaxtextarea-2.6.0-SNAPSHOT.jar b/app/lib/rsyntaxtextarea-2.6.0-SNAPSHOT.jar new file mode 100644 index 00000000000..2d6b2d20be7 Binary files /dev/null and b/app/lib/rsyntaxtextarea-2.6.0-SNAPSHOT.jar differ diff --git a/app/lib/rsyntaxtextarea-BSD.txt b/app/lib/rsyntaxtextarea-BSD.txt new file mode 100644 index 00000000000..3a6e638904a --- /dev/null +++ b/app/lib/rsyntaxtextarea-BSD.txt @@ -0,0 +1,24 @@ +Copyright (c) 2012, Robert Futrell +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/app/src/ArduinoIDE.java b/app/src/ArduinoIDE.java new file mode 100644 index 00000000000..329a1ee4ad9 --- /dev/null +++ b/app/src/ArduinoIDE.java @@ -0,0 +1,44 @@ +/* + * This file is part of Arduino. + * + * Copyright 2015 Ricardo JL Rufino (ricardo@criativasoft.com.br) + * + * Arduino 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + */ + +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); + } + +} diff --git a/app/src/cc/arduino/packages/formatter/AStyle.java b/app/src/cc/arduino/packages/formatter/AStyle.java index 1747c622fd3..9c61ff2db48 100644 --- a/app/src/cc/arduino/packages/formatter/AStyle.java +++ b/app/src/cc/arduino/packages/formatter/AStyle.java @@ -1,3 +1,5 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + /* * This file is part of Arduino. * @@ -32,9 +34,10 @@ import processing.app.Base; import processing.app.Editor; import processing.app.helpers.FileUtils; -import processing.app.syntax.JEditTextArea; +import processing.app.syntax.SketchTextArea; import processing.app.tools.Tool; +import javax.swing.text.BadLocationException; import java.io.File; import java.io.IOException; @@ -84,17 +87,50 @@ public void run() { return; } - JEditTextArea textArea = editor.getTextArea(); - int line = textArea.getLineOfOffset(textArea.getCaretPosition()); - int lineOffset = textArea.getCaretPosition() - textArea.getLineStartOffset(line); + SketchTextArea textArea = editor.getTextArea(); + + int line = getLineOfOffset(textArea); + int lineOffset = getLineOffset(textArea, line); + editor.getTextArea().getUndoManager().beginInternalAtomicEdit(); editor.setText(formattedText); editor.getSketch().setModified(true); - textArea.setCaretPosition(Math.min(textArea.getLineStartOffset(line) + lineOffset, textArea.getSafeLineStopOffset(line) - 1)); + editor.getTextArea().getUndoManager().endInternalAtomicEdit(); + + if (line != -1 && lineOffset != -1) { + setCaretPosition(textArea, line, lineOffset); + } + // mark as finished editor.statusNotice(_("Auto Format finished.")); } + private void setCaretPosition(SketchTextArea textArea, int line, int lineOffset) { + try { + textArea.setCaretPosition(Math.min(textArea.getLineStartOffset(line) + lineOffset, textArea.getLineEndOffset(line) - 1)); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + + private int getLineOffset(SketchTextArea textArea, int line) { + try { + return textArea.getCaretPosition() - textArea.getLineStartOffset(line); + } catch (BadLocationException e) { + e.printStackTrace(); + } + return -1; + } + + private int getLineOfOffset(SketchTextArea textArea) { + try { + return textArea.getLineOfOffset(textArea.getCaretPosition()); + } catch (BadLocationException e) { + e.printStackTrace(); + } + return -1; + } + @Override public String getMenuTitle() { return _("Auto Format"); diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 2af1c508615..d5c4f69ac0d 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -53,15 +53,20 @@ import processing.app.macosx.ThinkDifferent; import processing.app.packages.LibraryList; import processing.app.packages.UserLibrary; +import processing.app.syntax.PdeKeywords; import processing.app.tools.MenuScroller; import processing.app.tools.ZipDeflater; import javax.swing.*; + import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import java.util.List; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; import static processing.app.I18n._; @@ -114,6 +119,8 @@ public boolean apply(UserLibrary library) { private List boardsCustomMenus; private volatile Action openBoardsManager; + private final PdeKeywords pdeKeywords; + static public void main(String args[]) throws Exception { System.setProperty("awt.useSystemAAFontSettings", "on"); System.setProperty("swing.aatext", "true"); @@ -137,6 +144,8 @@ static public void guardedMain(String args[]) throws Exception { BaseNoGui.initLogger(); + initLogger(); + BaseNoGui.notifier = new GUIUserNotifier(); initPlatform(); @@ -215,6 +224,34 @@ static public void guardedMain(String args[]) throws Exception { INSTANCE = new Base(args); } + + static public void initLogger() { + Handler consoleHandler = new ConsoleLogger(); + consoleHandler.setLevel(Level.ALL); + consoleHandler.setFormatter(new LogFormatter("%1$tl:%1$tM:%1$tS [%4$7s] %2$s: %5$s%n")); + + Logger globalLogger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + globalLogger.setLevel(consoleHandler.getLevel()); + + // Remove default + Handler[] handlers = globalLogger.getHandlers(); + for(Handler handler : handlers) { + globalLogger.removeHandler(handler); + } + Logger root = Logger.getLogger(""); + handlers = root.getHandlers(); + for(Handler handler : handlers) { + root.removeHandler(handler); + } + + globalLogger.addHandler(consoleHandler); + + Logger.getLogger("cc.arduino.packages.autocomplete").setParent(globalLogger); + Logger.getLogger("br.com.criativasoft.cpluslibparser").setParent(globalLogger); + Logger.getLogger(Base.class.getPackage().getName()).setParent(globalLogger); + + } + static protected void setCommandLine() { commandLine = true; @@ -275,6 +312,9 @@ public Base(String[] args) throws Exception { // Setup board-dependent variables. onBoardOrPortChange(); + this.pdeKeywords = new PdeKeywords(); + this.pdeKeywords.reload(); + CommandlineParser parser = CommandlineParser.newCommandlineParser(args); for (String path : parser.getFilenames()) { @@ -2098,6 +2138,14 @@ static public void setIcon(Frame frame) { // don't use the low-res icon on Mac OS X; the window should // already have the right icon from the .app file. if (OSUtils.isMacOS()) return; + + // don't use the low-res icon on Linux + if (OSUtils.isLinux()){ + String current = System.getProperty("user.dir"); + Image image = Toolkit.getDefaultToolkit().createImage(current + "/lib/arduino.png"); + frame.setIconImage(image); + return; + } Image image = Toolkit.getDefaultToolkit().createImage(PApplet.ICON_IMAGE); frame.setIconImage(image); @@ -2152,7 +2200,12 @@ static public void showReference(String prefix, String filename) { File referenceFile = new File(referenceFolder, filename); if (!referenceFile.exists()) referenceFile = new File(referenceFolder, filename + ".html"); - openURL(referenceFile.getAbsolutePath()); + + if(referenceFile.exists()){ + openURL(referenceFile.getAbsolutePath()); + }else{ + showWarning(_("Problem Opening URL"), I18n.format(_("Could not open the URL\n{0}"), referenceFile), null); + } } public static void showEdisonGettingStarted() { @@ -2724,4 +2777,8 @@ public List getEditors() { public Action getOpenBoardsManager() { return openBoardsManager; } + + public PdeKeywords getPdeKeywords() { + return pdeKeywords; + } } diff --git a/app/src/processing/app/CaretAwareUndoableEdit.java b/app/src/processing/app/CaretAwareUndoableEdit.java index ba8e67d8570..d9b1349a54d 100644 --- a/app/src/processing/app/CaretAwareUndoableEdit.java +++ b/app/src/processing/app/CaretAwareUndoableEdit.java @@ -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(); } diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index c211ef5f9b9..72cb0b53242 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -46,10 +46,17 @@ import java.util.zip.*; import javax.swing.*; +import javax.swing.border.EmptyBorder; 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 +132,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 +151,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; @@ -240,10 +244,8 @@ public void windowDeactivated(WindowEvent e) { 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(); @@ -261,9 +263,20 @@ public void windowDeactivated(WindowEvent e) { lineStatus = new EditorLineStatus(textarea); consolePanel.add(lineStatus, BorderLayout.SOUTH); - upper.add(textarea); - splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, - upper, consolePanel); + // RTextScrollPane + scrollPane = new RTextScrollPane(textarea, true); + scrollPane.setBorder(new EmptyBorder(0, 0, 0, 0)); + scrollPane.setViewportBorder(BorderFactory.createEmptyBorder()); + scrollPane.setLineNumbersEnabled(Preferences.getBoolean("editor.linenumbers")); + scrollPane.setIconRowHeaderEnabled(false); + + 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 @@ -289,7 +302,7 @@ public void windowDeactivated(WindowEvent e) { // 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 @@ -448,38 +461,37 @@ protected void applyPreferences() { saveMenuItem.setEnabled(!external); saveAsMenuItem.setEnabled(!external); - textarea.setDisplayLineNumbers(PreferencesData.getBoolean("editor.linenumbers")); + textarea.setCodeFoldingEnabled(PreferencesData.getBoolean("editor.code_folding")); + scrollPane.setLineNumbersEnabled(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) //sketchbook.rebuildMenus(); // For 0126, moved into Base, which will notify all editors. //base.rebuildMenusAsync(); + } @@ -911,6 +923,33 @@ protected String findClassInZipFile(String base, File file) { } + protected SketchTextArea createTextArea() throws IOException { + SketchTextArea textArea = new SketchTextArea(base.getPdeKeywords()); + textArea.requestFocusInWindow(); + textArea.setMarkOccurrences(true); + textArea.setMarginLineEnabled(false); + textArea.setCodeFoldingEnabled(PreferencesData.getBoolean("editor.codefolding")); + textArea.setAntiAliasingEnabled(PreferencesData.getBoolean("editor.antialias")); + textArea.setTabsEmulated(PreferencesData.getBoolean("editor.tabs.expand")); + textArea.setTabSize(PreferencesData.getInteger("editor.tabs.size")); + textArea.setEditorListener(new EditorListener(this)); + textArea.addHyperlinkListener(new HyperlinkListener() { + @Override + public void hyperlinkUpdate(HyperlinkEvent hyperlinkEvent) { + try { + base.getPlatform().openURL(hyperlinkEvent.getURL().toExternalForm()); + } catch (Exception e) { + Base.showWarning(e.getMessage(), e.getMessage(), e); + } + } + }); + + ToolTipManager.sharedInstance().registerComponent(textArea); + + configurePopupMenu(textArea); + return textArea; + } + protected JMenuItem createToolMenuItem(String className) { try { Class toolClass = Class.forName(className); @@ -1368,18 +1407,6 @@ public void actionPerformed(ActionEvent e) { }); 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; } @@ -1429,24 +1456,18 @@ public UndoAction() { 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); @@ -1470,21 +1491,17 @@ public RedoAction() { 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()); @@ -1540,13 +1557,13 @@ public Sketch getSketch() { /** - * 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; } @@ -1563,7 +1580,11 @@ public String getText() { * Get a range of text from the current buffer. */ public String getText(int start, int stop) { - return textarea.getText(start, stop - start); + try { + return textarea.getText(start, stop - start); + } catch (BadLocationException e) { + return null; + } } @@ -1571,20 +1592,10 @@ public String getText(int start, int stop) { * 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 @@ -1610,15 +1621,10 @@ public String getSelectedText() { 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); } @@ -1654,7 +1660,7 @@ public int getSelectionStart() { * Get the end point of the current selection. */ public int getSelectionStop() { - return textarea.getSelectionStop(); + return textarea.getSelectionEnd(); } @@ -1662,18 +1668,11 @@ public int getSelectionStop() { * Get text for a specified line. */ public String getLineText(int line) { - return textarea.getLineText(line); - } - - - /** - * Replace the text on a specified line. - */ - public void setLineText(int line, String what) { - startCompoundEdit(); - textarea.select(getLineStartOffset(line), getLineStopOffset(line)); - textarea.setSelectedText(what); - stopCompoundEdit(); + try { + return textarea.getText(textarea.getLineStartOffset(line), textarea.getLineEndOffset(line)); + } catch (BadLocationException e) { + return ""; + } } @@ -1681,7 +1680,11 @@ public void setLineText(int line, String what) { * Get character offset for the start of a given line of text. */ public int getLineStartOffset(int line) { - return textarea.getLineStartOffset(line); + try { + return textarea.getLineStartOffset(line); + } catch (BadLocationException e) { + return -1; + } } @@ -1689,7 +1692,11 @@ public int getLineStartOffset(int line) { * 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; + } } @@ -1699,31 +1706,10 @@ public int getLineStopOffset(int line) { public int getLineCount() { return textarea.getLineCount(); } - - - /** - * Use before a manipulating text to group editing operations together as a - * single undo. Use stopCompoundEdit() once finished. - */ - public void startCompoundEdit() { - compoundEdit = new CompoundEdit(); - } - - - /** - * Use with startCompoundEdit() to group edit operations in a single undo. - */ - public void stopCompoundEdit() { - compoundEdit.end(); - undo.addEdit(compoundEdit); - undoAction.updateUndoState(); - redoAction.updateRedoState(); - compoundEdit = null; - } - + public int getScrollPosition() { - return textarea.getScrollPosition(); + return scrollPane.getVerticalScrollBar().getValue(); } @@ -1734,15 +1720,12 @@ public int getScrollPosition() { * 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(new ArduinoTokenMakerFactory(base.getPdeKeywords()), RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS); + document.putProperty(PlainDocument.tabSizeAttribute, Preferences.getInteger("editor.tabs.size")); // insert the program text into the document object try { @@ -1750,37 +1733,39 @@ protected void setCode(SketchCodeDocument codeDoc) { } catch (BadLocationException bl) { bl.printStackTrace(); } - // set up this guy's own undo manager // code.undo = new UndoManager(); - - // connect the undo listener to the editor - document.addUndoableEditListener(new UndoableEditListener() { - public void undoableEditHappened(UndoableEditEvent e) { - if (compoundEdit != null) { - compoundEdit.addEdit(new CaretAwareUndoableEdit(e.getEdit(), textarea)); - } else if (undo != null) { - undo.addEdit(new CaretAwareUndoableEdit(e.getEdit(), textarea)); - } - if (compoundEdit != null || undo != null) { - sketch.setModified(true); - undoAction.updateUndoState(); - redoAction.updateRedoState(); - } - } - }); + + codeDoc.setDocument(document); } - // update the document object that's in use - textarea.setDocument(document, - codeDoc.getSelectionStart(), codeDoc.getSelectionStop(), - codeDoc.getScrollPosition()); - + if(codeDoc.getUndo() == null){ + codeDoc.setUndo(new LastUndoableEditAwareUndoManager(textarea, this)); + document.addUndoableEditListener(codeDoc.getUndo()); + } + + // Update the document object that's in use + textarea.switchDocument(document, codeDoc.getUndo()); + + // HACK multiple tabs: for update Listeners of Gutter, forcin call: Gutter.setTextArea(RTextArea) + // BUG: https://github.com/bobbylight/RSyntaxTextArea/issues/84 + scrollPane.setViewportView(textarea); + + textarea.select(codeDoc.getSelectionStart(), codeDoc.getSelectionStop()); textarea.requestFocus(); // get the caret blinking - - this.undo = codeDoc.getUndo(); - undoAction.updateUndoState(); - redoAction.updateRedoState(); + + final int position = codeDoc.getScrollPosition(); + + // invokeLater: Expect the document to be rendered correctly to set the new position + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + scrollPane.getVerticalScrollBar().setValue(position); + undoAction.updateUndoState(); + redoAction.updateRedoState(); + } + }); + } @@ -1792,7 +1777,6 @@ public void undoableEditHappened(UndoableEditEvent e) { */ public void handleCut() { textarea.cut(); - sketch.setModified(true); } @@ -1819,7 +1803,6 @@ protected void handleHTMLCopy() { */ public void handlePaste() { textarea.paste(); - sketch.setModified(true); } @@ -1830,104 +1813,68 @@ public void handleSelectAll() { textarea.selectAll(); } + /** + * 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(); + } - protected void handleCommentUncomment() { - startCompoundEdit(); + /** + * Ends an "atomic" edit. + * + * @see #beginInternalAtomicEdit() + */ + public void endInternalAtomicEdit(){ + textarea.getUndoManager().endInternalAtomicEdit(); + } - 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--; - } - } + void handleCommentUncomment() { - // 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("//"); - } - } + Action action = textarea.getActionMap().get(RSyntaxTextAreaEditorKit.rstaToggleCommentAction); + action.actionPerformed(null); - 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(); - } + } 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) { - if (indent) { - textarea.select(location, location); - textarea.setSelectedText(tabString); + int caretPosition = textarea.getCaretPosition(); + boolean noSelec = !textarea.isSelectionActive(); - } else { // outdent - textarea.select(location, location + tabSize); - // Don't eat code if it's not indented - if (textarea.getSelectedText().equals(tabString)) { - textarea.setSelectedText(""); + // if no selection, focus on first char. + if (noSelec) { + try { + int line = textarea.getCaretLineNumber(); + int startOffset = textarea.getLineStartOffset(line); + textarea.setCaretPosition(startOffset); + } catch (BadLocationException e) { } } + + // Insert Tab or Spaces.. + Action action = textarea.getActionMap().get(RSyntaxTextAreaEditorKit.insertTabAction); + action.actionPerformed(null); + + if (noSelec) { + textarea.setCaretPosition(caretPosition); + } + + } else { + Action action = textarea.getActionMap().get(RSyntaxTextAreaEditorKit.rstaDecreaseIndentAction); + action.actionPerformed(null); } - // Subtract one from the end, otherwise selects past the current line. - // (Which causes subsequent calls to keep expanding the selection) - textarea.select(textarea.getLineStartOffset(startLine), - textarea.getLineStopOffset(stopLine) - 1); - stopCompoundEdit(); } + + /** Checks the preferences you are in external editing mode */ + public static boolean isExternalMode(){ + return PreferencesData.getBoolean("editor.external"); + } protected String getCurrentKeyword() { String text = ""; @@ -1973,7 +1920,7 @@ protected String getCurrentKeyword() { protected void handleFindReference() { String text = getCurrentKeyword(); - String referenceFile = PdeKeywords.getReference(text); + String referenceFile = base.getPdeKeywords().getReference(text); if (referenceFile == null) { statusNotice(I18n.format(_("No reference available for \"{0}\""), text)); } else { @@ -2028,6 +1975,7 @@ public BuildHandler(boolean verbose) { @Override public void run() { try { + textarea.removeAllLineHighlights(); sketch.prepare(); sketch.build(verbose); statusNotice(_("Done compiling.")); @@ -2209,7 +2157,7 @@ protected void handleOpenUnchecked(File file, int codeIndex, sketch.setCurrentCode(codeIndex); textarea.select(selStart, selStop); - textarea.setScrollPosition(scrollPos); + scrollPane.getVerticalScrollBar().setValue(scrollPos); } @@ -2334,6 +2282,7 @@ protected boolean handleOpenInternal(File sketchFile) { public boolean handleSave(boolean immediately) { //stopRunner(); handleStop(); // 0136 + textarea.removeAllLineHighlights(); if (untitled) { return handleSaveAs(); @@ -2710,9 +2659,9 @@ public void handlePrint() { } 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()); @@ -2769,7 +2718,7 @@ public void statusError(Exception e) { // 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--; @@ -2778,8 +2727,12 @@ public void statusError(Exception e) { 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(); + } } } } @@ -2830,148 +2783,101 @@ protected void onBoardOrPortChange() { 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; - - private String clickedURL; - - public TextAreaPopup() { - openURLItem = new JMenuItem(_("Open URL")); - openURLItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Base.openURL(clickedURL); - } - }); - add(openURLItem); - - openURLItemSeparator = new JSeparator(); - add(openURLItemSeparator); - cutItem = new JMenuItem(_("Cut")); - cutItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleCut(); - } - }); - add(cutItem); - - copyItem = new JMenuItem(_("Copy")); - copyItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleCopy(); - } - }); - add(copyItem); + protected void configurePopupMenu(final SketchTextArea textarea){ - discourseItem = new JMenuItem(_("Copy for Forum")); - discourseItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleDiscourseCopy(); - } - }); - add(discourseItem); + JPopupMenu menu = textarea.getPopupMenu(); - discourseItem = new JMenuItem(_("Copy as HTML")); - discourseItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleHTMLCopy(); - } - }); - add(discourseItem); + menu.addSeparator(); - JMenuItem item = new JMenuItem(_("Paste")); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handlePaste(); - } - }); - add(item); + JMenuItem item = createToolMenuItem("cc.arduino.packages.formatter.AStyle"); + item.setName("menuToolsAutoFormat"); - item = new JMenuItem(_("Select All")); - item.addActionListener(new ActionListener() { + menu.add(item); + + item = newJMenuItem(_("Comment/Uncomment"), '/'); + item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - handleSelectAll(); + handleCommentUncomment(); } - }); - add(item); + }); + menu.add(item); - addSeparator(); + item = newJMenuItem(_("Increase Indent"), ']'); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleIndentOutdent(true); + } + }); + menu.add(item); - item = new JMenuItem(_("Comment/Uncomment")); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleCommentUncomment(); - } - }); - add(item); + item = newJMenuItem(_("Decrease Indent"), '['); + item.setName("menuDecreaseIndent"); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleIndentOutdent(false); + } + }); + menu.add(item); - item = new JMenuItem(_("Increase Indent")); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleIndentOutdent(true); - } - }); - add(item); + item = new JMenuItem(_("Copy for Forum")); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleDiscourseCopy(); + } + }); + menu.add(item); - item = new JMenuItem(_("Decrease Indent")); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleIndentOutdent(false); - } - }); - add(item); + item = new JMenuItem(_("Copy as HTML")); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleHTMLCopy(); + } + }); + menu.add(item); - addSeparator(); + final JMenuItem referenceItem = new JMenuItem(_("Find in Reference")); + referenceItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleFindReference(); + } + }); + menu.add(referenceItem); - referenceItem = new JMenuItem(_("Find in Reference")); - referenceItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleFindReference(); - } - }); - add(referenceItem); - } - - // if no text is selected, disable copy and cut menu items - public void show(Component component, int x, int y) { - int lineNo = textarea.getLineOfOffset(textarea.xyToOffset(x, y)); - int offset = textarea.xToOffset(lineNo, x); - String line = textarea.getLineText(lineNo); - clickedURL = textarea.checkClickedURL(line, offset); - if (clickedURL != null) { - openURLItem.setVisible(true); - openURLItemSeparator.setVisible(true); - } else { - openURLItem.setVisible(false); - openURLItemSeparator.setVisible(false); + final JMenuItem openURLItem = new JMenuItem(_("Open URL")); + openURLItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Base.openURL(e.getActionCommand()); } + }); + menu.add(openURLItem); + + menu.addPopupMenuListener(new PopupMenuListener() { - if (textarea.isSelectionActive()) { - cutItem.setEnabled(true); - copyItem.setEnabled(true); - discourseItem.setEnabled(true); + @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); + } + } - } else { - cutItem.setEnabled(false); - copyItem.setEnabled(false); - discourseItem.setEnabled(false); + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { } - referenceFile = PdeKeywords.getReference(getCurrentKeyword()); - referenceItem.setEnabled(referenceFile != null); + @Override + public void popupMenuCanceled(PopupMenuEvent e) { + } + }); - super.show(component, x, y); - } } + } diff --git a/app/src/processing/app/EditorLineStatus.java b/app/src/processing/app/EditorLineStatus.java index 408b6545b64..2acb1eb1074 100644 --- a/app/src/processing/app/EditorLineStatus.java +++ b/app/src/processing/app/EditorLineStatus.java @@ -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"); diff --git a/app/src/processing/app/EditorListener.java b/app/src/processing/app/EditorListener.java index 0acb7e4c817..19e90b1c3ac 100644 --- a/app/src/processing/app/EditorListener.java +++ b/app/src/processing/app/EditorListener.java @@ -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.*; -import java.awt.event.*; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import processing.app.syntax.SketchTextArea; -/** - * Filters key events for tab expansion/indent/etc. - *

- * For version 0099, some changes have been made to make the indents - * smarter. There are still issues though: - * + indent happens when it picks up a curly brace on the previous line, - * but not if there's a blank line between them. - * + It also doesn't handle single indent situations where a brace - * isn't used (i.e. an if statement or for loop that's a single line). - * It shouldn't actually be using braces. - * Solving these issues, however, would probably best be done by a - * smarter parser/formatter, rather than continuing to hack this class. - */ -public class EditorListener { +public class EditorListener implements KeyListener { + private Editor editor; - private JEditTextArea textarea; - - private boolean externalEditor; - private boolean tabsExpand; - private boolean tabsIndent; - private int tabSize; - private String tabString; - private boolean autoIndent; - -// private int selectionStart, selectionEnd; -// private int position; - - /** ctrl-alt on windows and linux, cmd-alt on mac os x */ - static final int CTRL_ALT = ActionEvent.ALT_MASK | - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); - - - public EditorListener(Editor editor, JEditTextArea textarea) { + + public EditorListener(Editor editor) { + super(); this.editor = editor; - this.textarea = textarea; - - // let him know that i'm leechin' - textarea.editorListener = this; - - applyPreferences(); } + + /** ctrl-alt on windows and linux, cmd-alt on mac os x */ + static final int CTRL_ALT = ActionEvent.ALT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + static final int CTRL_SHIFT = ActionEvent.SHIFT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + static final int CTRL = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + - public void applyPreferences() { - tabsExpand = PreferencesData.getBoolean("editor.tabs.expand"); - //tabsIndent = Preferences.getBoolean("editor.tabs.indent"); - tabSize = PreferencesData.getInteger("editor.tabs.size"); - tabString = Editor.EMPTY.substring(0, tabSize); - autoIndent = PreferencesData.getBoolean("editor.indent"); - externalEditor = PreferencesData.getBoolean("editor.external"); - } - - - //public void setExternalEditor(boolean externalEditor) { - //this.externalEditor = externalEditor; - //} - - - /** - * Intercepts key pressed events for JEditTextArea. - *

- * 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 - char c = event.getKeyChar(); - int code = event.getKeyCode(); - -// if (code == KeyEvent.VK_SHIFT) { -// editor.toolbar.setShiftPressed(true); -// } - - //System.out.println((int)c + " " + code + " " + event); - //System.out.println(); - - Sketch sketch = editor.getSketch(); - - if ((event.getModifiers() & CTRL_ALT) == CTRL_ALT) { - if (code == KeyEvent.VK_LEFT) { - sketch.handlePrevCode(); - return true; - } else if (code == KeyEvent.VK_RIGHT) { - sketch.handleNextCode(); - return true; - } - } - - if ((event.getModifiers() & KeyEvent.CTRL_MASK) != 0) { - // Consume ctrl-m(carriage return) keypresses - if (code == KeyEvent.VK_M) { - event.consume(); // does nothing - return false; - } - - // The char is not control code when CTRL key pressed? It should be a shortcut. - if (!Character.isISOControl(c)) { - return false; - } - } - - if ((event.getModifiers() & KeyEvent.META_MASK) != 0) { - //event.consume(); // does nothing - return false; - } - - // TODO i don't like these accessors. clean em up later. - if (!editor.getSketch().isModified()) { - if ((code == KeyEvent.VK_BACK_SPACE) || (code == KeyEvent.VK_TAB) || - (code == KeyEvent.VK_ENTER) || ((c >= 32) && (c < 128))) { - sketch.setModified(true); - } - } - - if ((code == KeyEvent.VK_UP) && - ((event.getModifiers() & KeyEvent.CTRL_MASK) != 0)) { - // back up to the last empty line - char contents[] = textarea.getText().toCharArray(); - //int origIndex = textarea.getCaretPosition() - 1; - int caretIndex = textarea.getCaretPosition(); - - int index = calcLineStart(caretIndex - 1, contents); - //System.out.println("line start " + (int) contents[index]); - index -= 2; // step over the newline - //System.out.println((int) contents[index]); - boolean onlySpaces = true; - while (index > 0) { - if (contents[index] == 10) { - if (onlySpaces) { - index++; - break; - } else { - onlySpaces = true; // reset - } - } else if (contents[index] != ' ') { - onlySpaces = false; - } - index--; - } - // if the first char, index will be -2 - if (index < 0) index = 0; - - if ((event.getModifiers() & KeyEvent.SHIFT_MASK) != 0) { - textarea.setSelectionStart(caretIndex); - textarea.setSelectionEnd(index); - } else { - textarea.setCaretPosition(index); - } - event.consume(); - return true; - - } else if ((code == KeyEvent.VK_DOWN) && - ((event.getModifiers() & KeyEvent.CTRL_MASK) != 0)) { - char contents[] = textarea.getText().toCharArray(); - int caretIndex = textarea.getCaretPosition(); - - int index = caretIndex; - int lineStart = 0; - boolean onlySpaces = false; // don't count this line - while (index < contents.length) { - if (contents[index] == 10) { - if (onlySpaces) { - index = lineStart; // this is it - break; - } else { - lineStart = index + 1; - onlySpaces = true; // reset - } - } else if (contents[index] != ' ') { - onlySpaces = false; - } - index++; - } - // if the first char, index will be -2 - //if (index < 0) index = 0; - - //textarea.setSelectionStart(index); - //textarea.setSelectionEnd(index); - if ((event.getModifiers() & KeyEvent.SHIFT_MASK) != 0) { - textarea.setSelectionStart(caretIndex); - textarea.setSelectionEnd(index); - } else { - textarea.setCaretPosition(index); - } - event.consume(); - return true; - } - - - switch ((int) c) { - - case 9: // TAB - if (textarea.isSelectionActive()) { - boolean outdent = (event.getModifiers() & KeyEvent.SHIFT_MASK) != 0; - editor.handleIndentOutdent(!outdent); - - } else if (tabsExpand) { // expand tabs - textarea.setSelectedText(tabString); - event.consume(); - return true; - - } else if (tabsIndent) { - // this code is incomplete - - // if this brace is the only thing on the line, outdent - //char contents[] = getCleanedContents(); - char contents[] = textarea.getText().toCharArray(); - // index to the character to the left of the caret - int prevCharIndex = textarea.getCaretPosition() - 1; - - // now find the start of this line - int lineStart = calcLineStart(prevCharIndex, contents); - - int lineEnd = lineStart; - while ((lineEnd < contents.length - 1) && - (contents[lineEnd] != 10)) { - lineEnd++; - } - - // get the number of braces, to determine whether this is an indent - int braceBalance = 0; - int index = lineStart; - while ((index < contents.length) && - (contents[index] != 10)) { - if (contents[index] == '{') { - braceBalance++; - } else if (contents[index] == '}') { - braceBalance--; - } - index++; - } - - // if it's a starting indent, need to ignore it, so lineStart - // will be the counting point. but if there's a closing indent, - // then the lineEnd should be used. - int where = (braceBalance > 0) ? lineStart : lineEnd; - int indent = calcBraceIndent(where, contents); - if (indent == -1) { - // no braces to speak of, do nothing - indent = 0; - } else { - indent += tabSize; - } - - // and the number of spaces it has - int spaceCount = calcSpaceCount(prevCharIndex, contents); - - textarea.setSelectionStart(lineStart); - textarea.setSelectionEnd(lineStart + spaceCount); - textarea.setSelectedText(Editor.EMPTY.substring(0, indent)); - - event.consume(); - return true; - } - break; - - case 10: // auto-indent - case 13: - if (autoIndent) { - char contents[] = textarea.getText().toCharArray(); - - // this is the previous character - // (i.e. when you hit return, it'll be the last character - // just before where the newline will be inserted) - int origIndex = textarea.getCaretPosition() - 1; - - // NOTE all this cursing about CRLF stuff is probably moot - // NOTE since the switch to JEditTextArea, which seems to use - // NOTE only LFs internally (thank god). disabling for 0099. - // walk through the array to the current caret position, - // and count how many weirdo windows line endings there are, - // which would be throwing off the caret position number - /* - int offset = 0; - int realIndex = origIndex; - for (int i = 0; i < realIndex-1; i++) { - if ((contents[i] == 13) && (contents[i+1] == 10)) { - offset++; - realIndex++; - } - } - // back up until \r \r\n or \n.. @#($* cross platform - //System.out.println(origIndex + " offset = " + offset); - origIndex += offset; // ARGH!#(* WINDOWS#@($* - */ - - // if the previous thing is a brace (whether prev line or - // up farther) then the correct indent is the number of spaces - // on that line + 'indent'. - // if the previous line is not a brace, then just use the - // identical indentation to the previous line - - // calculate the amount of indent on the previous line - // this will be used *only if the prev line is not an indent* - int spaceCount = calcSpaceCount(origIndex, contents); - - // If the last character was a left curly brace, then indent. - // For 0122, walk backwards a bit to make sure that the there - // isn't a curly brace several spaces (or lines) back. Also - // moved this before calculating extraCount, since it'll affect - // that as well. - int index2 = origIndex; - while ((index2 >= 0) && - Character.isWhitespace(contents[index2])) { - index2--; - } - if (index2 != -1) { - // still won't catch a case where prev stuff is a comment - if (contents[index2] == '{') { - // intermediate lines be damned, - // use the indent for this line instead - spaceCount = calcSpaceCount(index2, contents); - spaceCount += tabSize; - } - } - //System.out.println("spaceCount should be " + spaceCount); - - // now before inserting this many spaces, walk forward from - // the caret position and count the number of spaces, - // so that the number of spaces aren't duplicated again - int index = origIndex + 1; - int extraCount = 0; - while ((index < contents.length) && - (contents[index] == ' ')) { - //spaceCount--; - extraCount++; - index++; - } - int braceCount = 0; - while ((index < contents.length) && (contents[index] != '\n')) { - if (contents[index] == '}') { - braceCount++; - } - index++; - } - - // hitting return on a line with spaces *after* the caret - // can cause trouble. for 0099, was ignoring the case, but this is - // annoying, so in 0122 we're trying to fix that. - /* - if (spaceCount - extraCount > 0) { - spaceCount -= extraCount; - } - */ - spaceCount -= extraCount; - //if (spaceCount < 0) spaceCount = 0; - //System.out.println("extraCount is " + extraCount); - - // now, check to see if the current line contains a } and if so, - // outdent again by indent - //if (braceCount > 0) { - //spaceCount -= 2; - //} - - if (spaceCount < 0) { - // for rev 0122, actually delete extra space - //textarea.setSelectionStart(origIndex + 1); - textarea.setSelectionEnd(textarea.getSelectionStop() - spaceCount); - textarea.setSelectedText("\n"); - } else { - String insertion = "\n" + Editor.EMPTY.substring(0, spaceCount); - textarea.setSelectedText(insertion); - } - - // not gonna bother handling more than one brace - if (braceCount > 0) { - int sel = textarea.getSelectionStart(); - // sel - tabSize will be -1 if start/end parens on the same line - // http://dev.processing.org/bugs/show_bug.cgi?id=484 - if (sel - tabSize >= 0) { - textarea.select(sel - tabSize, sel); - String s = Editor.EMPTY.substring(0, tabSize); - // if these are spaces that we can delete - if (textarea.getSelectedText().equals(s)) { - textarea.setSelectedText(""); - } else { - textarea.select(sel, sel); - } - } - } - } else { - // Enter/Return was being consumed by somehow even if false - // was returned, so this is a band-aid to simply fire the event again. - // http://dev.processing.org/bugs/show_bug.cgi?id=1073 - textarea.setSelectedText(String.valueOf(c)); - } - // mark this event as already handled (all but ignored) - event.consume(); - return true; - - case '}': - if (autoIndent) { - // first remove anything that was there (in case this multiple - // characters are selected, so that it's not in the way of the - // spaces for the auto-indent - if (textarea.getSelectionStart() != textarea.getSelectionStop()) { - textarea.setSelectedText(""); - } - - // if this brace is the only thing on the line, outdent - char contents[] = textarea.getText().toCharArray(); - // index to the character to the left of the caret - int prevCharIndex = textarea.getCaretPosition() - 1; - - // backup from the current caret position to the last newline, - // checking for anything besides whitespace along the way. - // if there's something besides whitespace, exit without - // messing any sort of indenting. - int index = prevCharIndex; - boolean finished = false; - while ((index != -1) && (!finished)) { - if (contents[index] == 10) { - finished = true; - index++; - } else if (contents[index] != ' ') { - // don't do anything, this line has other stuff on it - return false; - } else { - index--; - } - } - if (!finished) return false; // brace with no start - int lineStartIndex = index; - - int pairedSpaceCount = calcBraceIndent(prevCharIndex, contents); //, 1); - if (pairedSpaceCount == -1) return false; - - textarea.setSelectionStart(lineStartIndex); - textarea.setSelectedText(Editor.EMPTY.substring(0, pairedSpaceCount)); - - // mark this event as already handled - event.consume(); - return true; - } - break; - } - return false; - } - - -// public boolean keyReleased(KeyEvent event) { -// if (code == KeyEvent.VK_SHIFT) { -// editor.toolbar.setShiftPressed(false); -// } -// } - - - public boolean keyTyped(KeyEvent event) { + public void 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; } + @Override + public void keyPressed(KeyEvent event) { + + SketchTextArea textarea = editor.getTextArea(); + + if (!textarea.isEditable()) return; + + Sketch sketch = editor.getSketch(); - - /** - * 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 - } + int code = event.getKeyCode(); + + // Navigation.. + if ((event.getModifiers() & CTRL) == CTRL && code == KeyEvent.VK_TAB) { + sketch.handleNextCode(); } - // 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++; + // Navigation.. + // FIXME: not working on LINUX !!! + if (((event.getModifiers() & CTRL_SHIFT) == CTRL_SHIFT)) { + if(code == KeyEvent.VK_TAB) + sketch.handlePrevCode(); } - 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--; + + // Navigation.. + if ((event.getModifiers() & CTRL_ALT) == CTRL_ALT) { + if (code == KeyEvent.VK_LEFT) { + sketch.handlePrevCode(); + } else if (code == KeyEvent.VK_RIGHT) { + sketch.handleNextCode(); } } - // 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); + +// if (event.isAltDown() && code == KeyEvent.VK_T) { +// int line = textarea.getCaretLineNumber(); +// textarea.setActiveLineRange(line, line + 3); +// } + } - - /** - * 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; + @Override + public void keyReleased(KeyEvent e) { + // TODO Auto-generated method stub + } - /* - 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++; - } - } - } - */ -} +} \ No newline at end of file diff --git a/app/src/processing/app/LastUndoableEditAwareUndoManager.java b/app/src/processing/app/LastUndoableEditAwareUndoManager.java index 0cd678a935b..736be42d39b 100644 --- a/app/src/processing/app/LastUndoableEditAwareUndoManager.java +++ b/app/src/processing/app/LastUndoableEditAwareUndoManager.java @@ -2,31 +2,36 @@ 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(); } + + } diff --git a/app/src/processing/app/Preferences.java b/app/src/processing/app/Preferences.java index e951f3e4623..7d361c41d49 100644 --- a/app/src/processing/app/Preferences.java +++ b/app/src/processing/app/Preferences.java @@ -222,6 +222,7 @@ public String toString() { JCheckBox verboseCompilationBox; JCheckBox verboseUploadBox; JCheckBox displayLineNumbersBox; + JCheckBox enableCodeFoldingBox; JCheckBox verifyUploadBox; JCheckBox externalEditorBox; JCheckBox checkUpdatesBox; @@ -357,7 +358,6 @@ public void actionPerformed(ActionEvent e) { fontSizeField.setText(String.valueOf(editorFont.getSize())); top += d.height + GUI_BETWEEN; - // Show verbose output during: [ ] compilation [ ] upload box = Box.createHorizontalBox(); @@ -402,6 +402,15 @@ public void actionPerformed(ActionEvent e) { right = Math.max(right, left + d.width); top += d.height + GUI_BETWEEN; + // [ ] Enable Code Folding + + enableCodeFoldingBox = new JCheckBox(_("Enable Code Folding")); + pane.add(enableCodeFoldingBox); + d = enableCodeFoldingBox.getPreferredSize(); + enableCodeFoldingBox.setBounds(left, top, d.width + 10, d.height); + right = Math.max(right, left + d.width); + top += d.height + GUI_BETWEEN; + // [ ] Verify code after upload verifyUploadBox = new JCheckBox(_("Verify code after upload")); @@ -724,6 +733,7 @@ protected void applyFrame() { PreferencesData.setBoolean("build.verbose", verboseCompilationBox.isSelected()); PreferencesData.setBoolean("upload.verbose", verboseUploadBox.isSelected()); PreferencesData.setBoolean("editor.linenumbers", displayLineNumbersBox.isSelected()); + PreferencesData.setBoolean("editor.code_folding", enableCodeFoldingBox.isSelected()); PreferencesData.setBoolean("upload.verify", verifyUploadBox.isSelected()); PreferencesData.setBoolean("editor.save_on_verify", saveVerifyUploadBox.isSelected()); @@ -814,6 +824,7 @@ protected void showFrame(Editor editor) { verboseCompilationBox.setSelected(PreferencesData.getBoolean("build.verbose")); verboseUploadBox.setSelected(PreferencesData.getBoolean("upload.verbose")); displayLineNumbersBox.setSelected(PreferencesData.getBoolean("editor.linenumbers")); + enableCodeFoldingBox.setSelected(PreferencesData.getBoolean("editor.code_folding")); verifyUploadBox.setSelected(PreferencesData.getBoolean("upload.verify")); //closingLastQuitsBox. diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index 111c1caf882..d0e01222642 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -112,7 +112,7 @@ protected void load() throws IOException { 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 @@ protected void nameCode(String newName) { return; } ensureExistence(); - data.addCode((new SketchCodeDocument(newFile)).getCode()); + data.addCode((new SketchCodeDocument(this, newFile)).getCode()); } // sort the entries @@ -905,7 +905,7 @@ public boolean addFile(File sourceFile) { } if (codeExtension != null) { - SketchCode newCode = (new SketchCodeDocument(destFile)).getCode(); + SketchCode newCode = (new SketchCodeDocument(this, destFile)).getCode(); if (replacement) { data.replaceCode(newCode); diff --git a/app/src/processing/app/SketchCodeDocument.java b/app/src/processing/app/SketchCodeDocument.java index 857a270abc2..681f0af9151 100644 --- a/app/src/processing/app/SketchCodeDocument.java +++ b/app/src/processing/app/SketchCodeDocument.java @@ -2,37 +2,43 @@ 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{ +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 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 +80,24 @@ public Document getDocument() { 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. + } + } diff --git a/app/src/processing/app/Theme.java b/app/src/processing/app/Theme.java index 4100bdfc5fe..841880d9b92 100644 --- a/app/src/processing/app/Theme.java +++ b/app/src/processing/app/Theme.java @@ -21,16 +21,19 @@ package processing.app; -import static processing.app.I18n._; +import processing.app.helpers.OSUtils; +import processing.app.helpers.PreferencesHelper; +import processing.app.helpers.PreferencesMap; -import java.awt.Color; -import java.awt.Font; -import java.awt.SystemColor; +import javax.swing.text.StyleContext; +import java.awt.*; +import java.awt.font.TextAttribute; import java.io.File; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; -import processing.app.helpers.PreferencesHelper; -import processing.app.helpers.PreferencesMap; -import processing.app.syntax.SyntaxStyle; +import static processing.app.I18n._; /** * Storage class for theme settings. This was separated from the Preferences @@ -39,9 +42,13 @@ */ public class Theme { - /** Copy of the defaults in case the user mangles a preference. */ + /** + * Copy of the defaults in case the user mangles a preference. + */ static PreferencesMap defaults; - /** Table of attributes/values for the theme. */ + /** + * Table of attributes/values for the theme. + */ static PreferencesMap table = new PreferencesMap(); static protected void init() { @@ -49,7 +56,7 @@ static protected void init() { table.load(new File(BaseNoGui.getContentFile("lib"), "theme/theme.txt")); } catch (Exception te) { Base.showError(null, _("Could not read color theme settings.\n" + - "You'll need to reinstall Arduino."), te); + "You'll need to reinstall Arduino."), te); } // other things that have to be set explicitly for the defaults @@ -105,7 +112,41 @@ static public Font getFont(String attr) { return font; } - static public SyntaxStyle getStyle(String what) { + /** + * Returns the default font for text areas. + * + * @return The default font. + */ + public static final Font getDefaultFont() { + + // 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; + } + + public static Map getStyledFont(String what, Font font) { String split[] = get("editor." + what + ".style").split(","); Color color = PreferencesHelper.parseColor(split[0]); @@ -115,6 +156,18 @@ static public SyntaxStyle getStyle(String what) { boolean italic = style.contains("italic"); boolean underlined = style.contains("underlined"); - return new SyntaxStyle(color, italic, bold, underlined); + Font styledFont = new Font(font.getFamily(), (bold ? Font.BOLD : 0) | (italic ? Font.ITALIC : 0), font.getSize()); + if (underlined) { + Map attr = new Hashtable(); + attr.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); + styledFont = styledFont.deriveFont(attr); + } + + Map result = new HashMap(); + result.put("color", color); + result.put("font", styledFont); + + return result; } + } diff --git a/app/src/processing/app/helpers/ConsoleLogger.java b/app/src/processing/app/helpers/ConsoleLogger.java new file mode 100644 index 00000000000..ab9b838ab51 --- /dev/null +++ b/app/src/processing/app/helpers/ConsoleLogger.java @@ -0,0 +1,59 @@ +/* + * This file is part of Arduino. + * + * Copyright 2015 Ricardo JL Rufino (ricardo@criativasoft.com.br) + * + * Arduino 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + */ + +package processing.app.helpers; + +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.util.logging.LogRecord; +import java.util.logging.StreamHandler; + +public class ConsoleLogger extends StreamHandler { + + public ConsoleLogger() { + setOutputStream(new PrintStream(new FileOutputStream(FileDescriptor.out))); + } + + + public void publish(LogRecord record) { + super.publish(record); + flush(); + } + + /** + * Override StreamHandler.close to do a flush but not + * to close the output stream. That is, we do not + * close FileDescriptor.out. + */ + public void close() { + flush(); + } + +} diff --git a/app/src/processing/app/helpers/LogFormatter.java b/app/src/processing/app/helpers/LogFormatter.java new file mode 100644 index 00000000000..a3d1f58f26c --- /dev/null +++ b/app/src/processing/app/helpers/LogFormatter.java @@ -0,0 +1,78 @@ +/* + * This file is part of Arduino. + * + * Copyright 2015 Ricardo JL Rufino (ricardo@criativasoft.com.br) + * + * Arduino 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + */ + +package processing.app.helpers; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Date; +import java.util.logging.Formatter; +import java.util.logging.LogRecord; + +public class LogFormatter extends Formatter { + + public String format; + private final Date dat = new Date(); + + public LogFormatter(String logformat) { + format = logformat; + } + + @Override + public String format(LogRecord record) { + dat.setTime(record.getMillis()); + String source; + if (record.getSourceClassName() != null) { + source = record.getSourceClassName().substring(record.getSourceClassName().lastIndexOf('.') + 1); + if (record.getSourceMethodName() != null) { + source += "." + record.getSourceMethodName(); + } + } else { + source = record.getLoggerName(); + } + String message = formatMessage(record); + String throwable = ""; + if (record.getThrown() != null) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + pw.println(); + record.getThrown().printStackTrace(pw); + pw.close(); + throwable = sw.toString(); + } + return String.format(format, + dat, + source, + record.getLoggerName(), + record.getLevel(), + message, + throwable); + } + +} diff --git a/app/src/processing/app/syntax/ArduinoTokenMakerFactory.java b/app/src/processing/app/syntax/ArduinoTokenMakerFactory.java new file mode 100644 index 00000000000..e1728cb62c0 --- /dev/null +++ b/app/src/processing/app/syntax/ArduinoTokenMakerFactory.java @@ -0,0 +1,25 @@ +package processing.app.syntax; + +import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory; +import org.fife.ui.rsyntaxtextarea.RSyntaxDocument; +import org.fife.ui.rsyntaxtextarea.TokenMaker; + +public class ArduinoTokenMakerFactory extends AbstractTokenMakerFactory { + + private final PdeKeywords pdeKeywords; + + public ArduinoTokenMakerFactory(PdeKeywords pdeKeywords) { + this.pdeKeywords = pdeKeywords; + } + + @Override + protected TokenMaker getTokenMakerImpl(String key) { + return new SketchTokenMaker(pdeKeywords); + } + + @Override + protected void initTokenMakerMap() { + putMapping(RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS, SketchTokenMaker.class.getName()); + } + +} diff --git a/app/src/processing/app/syntax/CTokenMarker.java b/app/src/processing/app/syntax/CTokenMarker.java deleted file mode 100644 index ccb9b0b4822..00000000000 --- a/app/src/processing/app/syntax/CTokenMarker.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * CTokenMarker.java - C token marker - * Copyright (C) 1998, 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import javax.swing.text.Segment; - -/** - * C token marker. - * - * @author Slava Pestov - */ -public class CTokenMarker extends TokenMarker -{ - public CTokenMarker() - { - this(true,getKeywords()); - } - - public CTokenMarker(boolean cpp, KeywordMap keywords) - { - this.cpp = cpp; - this.keywords = keywords; - } - - public byte markTokensImpl(byte token, Segment line, int lineIndex) - { - char[] array = line.array; - int offset = line.offset; - lastOffset = offset; - lastKeyword = offset; - int mlength = line.count + offset; - boolean backslash = false; - -loop: for(int i = offset; i < mlength; i++) - { - int i1 = (i+1); - - char c = array[i]; - if(c == '\\') - { - backslash = !backslash; - continue; - } - - switch(token) - { - case Token.NULL: - switch(c) - { - case '#': - if(backslash) - backslash = false; - else if(cpp) - { - if(doKeyword(line,i,c)) - break; - addToken(i - lastOffset,token); - addToken(mlength - i,Token.KEYWORD2); - lastOffset = lastKeyword = mlength; - break loop; - } - break; - case '"': - doKeyword(line,i,c); - if(backslash) - backslash = false; - else - { - addToken(i - lastOffset,token); - token = Token.LITERAL1; - lastOffset = lastKeyword = i; - } - break; - case '\'': - doKeyword(line,i,c); - if(backslash) - backslash = false; - else - { - addToken(i - lastOffset,token); - token = Token.LITERAL2; - lastOffset = lastKeyword = i; - } - break; - case ':': - if(lastKeyword == offset) - { - if(doKeyword(line,i,c)) - break; - backslash = false; - addToken(i1 - lastOffset,Token.LABEL); - lastOffset = lastKeyword = i1; - } - else if(doKeyword(line,i,c)) - break; - break; - case '/': - backslash = false; - doKeyword(line,i,c); - if(mlength - i > 1) - { - switch(array[i1]) - { - case '*': - addToken(i - lastOffset,token); - lastOffset = lastKeyword = i; - if(mlength - i > 2 && array[i+2] == '*') - token = Token.COMMENT2; - else - token = Token.COMMENT1; - break; - case '/': - addToken(i - lastOffset,token); - addToken(mlength - i,Token.COMMENT1); - lastOffset = lastKeyword = mlength; - break loop; - } - } - break; - default: - backslash = false; - if(!Character.isLetterOrDigit(c) - && c != '_') - doKeyword(line,i,c); - break; - } - break; - case Token.COMMENT1: - case Token.COMMENT2: - backslash = false; - if(c == '*' && mlength - i > 1) - { - if(array[i1] == '/') - { - i++; - addToken((i+1) - lastOffset,token); - token = Token.NULL; - lastOffset = lastKeyword = i+1; - } - } - break; - case Token.LITERAL1: - if(backslash) - backslash = false; - else if(c == '"') - { - addToken(i1 - lastOffset,token); - token = Token.NULL; - lastOffset = lastKeyword = i1; - } - break; - case Token.LITERAL2: - if(backslash) - backslash = false; - else if(c == '\'') - { - addToken(i1 - lastOffset,Token.LITERAL1); - token = Token.NULL; - lastOffset = lastKeyword = i1; - } - break; - default: - throw new InternalError("Invalid state: " - + token); - } - } - - if(token == Token.NULL) - doKeyword(line,mlength,'\0'); - - switch(token) - { - case Token.LITERAL1: - case Token.LITERAL2: - addToken(mlength - lastOffset,Token.INVALID); - token = Token.NULL; - break; - case Token.KEYWORD2: - addToken(mlength - lastOffset,token); - if (!backslash) token = Token.NULL; - addToken(mlength - lastOffset,token); - break; - default: - addToken(mlength - lastOffset,token); - break; - } - - return token; - } - - public static KeywordMap getKeywords() - { - if(cKeywords == null) - { - cKeywords = new KeywordMap(false); - cKeywords.add("char",Token.KEYWORD3); - cKeywords.add("double",Token.KEYWORD3); - cKeywords.add("enum",Token.KEYWORD3); - cKeywords.add("float",Token.KEYWORD3); - cKeywords.add("int",Token.KEYWORD3); - cKeywords.add("long",Token.KEYWORD3); - cKeywords.add("short",Token.KEYWORD3); - cKeywords.add("signed",Token.KEYWORD3); - cKeywords.add("struct",Token.KEYWORD3); - cKeywords.add("typedef",Token.KEYWORD3); - cKeywords.add("union",Token.KEYWORD3); - cKeywords.add("unsigned",Token.KEYWORD3); - cKeywords.add("void",Token.KEYWORD3); - cKeywords.add("auto",Token.KEYWORD1); - cKeywords.add("const",Token.KEYWORD1); - cKeywords.add("extern",Token.KEYWORD1); - cKeywords.add("register",Token.KEYWORD1); - cKeywords.add("static",Token.KEYWORD1); - cKeywords.add("volatile",Token.KEYWORD1); - cKeywords.add("break",Token.KEYWORD1); - cKeywords.add("case",Token.KEYWORD1); - cKeywords.add("continue",Token.KEYWORD1); - cKeywords.add("default",Token.KEYWORD1); - cKeywords.add("do",Token.KEYWORD1); - cKeywords.add("else",Token.KEYWORD1); - cKeywords.add("for",Token.KEYWORD1); - cKeywords.add("goto",Token.KEYWORD1); - cKeywords.add("if",Token.KEYWORD1); - cKeywords.add("return",Token.KEYWORD1); - cKeywords.add("sizeof",Token.KEYWORD1); - cKeywords.add("switch",Token.KEYWORD1); - cKeywords.add("while",Token.KEYWORD1); - cKeywords.add("asm",Token.KEYWORD2); - cKeywords.add("asmlinkage",Token.KEYWORD2); - cKeywords.add("far",Token.KEYWORD2); - cKeywords.add("huge",Token.KEYWORD2); - cKeywords.add("inline",Token.KEYWORD2); - cKeywords.add("near",Token.KEYWORD2); - cKeywords.add("pascal",Token.KEYWORD2); - cKeywords.add("true",Token.LITERAL2); - cKeywords.add("false",Token.LITERAL2); - cKeywords.add("NULL",Token.LITERAL2); - } - return cKeywords; - } - - // private members - private static KeywordMap cKeywords; - - private boolean cpp; - private KeywordMap keywords; - private int lastOffset; - private int lastKeyword; - - private boolean doKeyword(Segment line, int i, char c) - { - int i1 = i+1; - - int len = i - lastKeyword; - byte id = keywords.lookup(line,lastKeyword,len); - if(id != Token.NULL) - { - if(lastKeyword != lastOffset) - addToken(lastKeyword - lastOffset,Token.NULL); - addToken(len,id); - lastOffset = i; - } - lastKeyword = i1; - return false; - } -} diff --git a/app/src/processing/app/syntax/DefaultInputHandler.java b/app/src/processing/app/syntax/DefaultInputHandler.java deleted file mode 100644 index e9e23fea12d..00000000000 --- a/app/src/processing/app/syntax/DefaultInputHandler.java +++ /dev/null @@ -1,373 +0,0 @@ -/* - * DefaultInputHandler.java - Default implementation of an input handler - * Copyright (C) 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import javax.swing.KeyStroke; -import java.awt.event.*; -import java.awt.Toolkit; -import java.util.Hashtable; -import java.util.StringTokenizer; - -/** - * The default input handler. It maps sequences of keystrokes into actions - * and inserts key typed events into the text area. - * @author Slava Pestov - */ -public class DefaultInputHandler extends InputHandler -{ - /** - * Creates a new input handler with no key bindings defined. - */ - public DefaultInputHandler() - { - bindings = currentBindings = new Hashtable(); - } - - /** - * Sets up the default key bindings. - */ - public void addDefaultKeyBindings() - { - addKeyBinding("BACK_SPACE",BACKSPACE); - addKeyBinding("C+BACK_SPACE",BACKSPACE_WORD); - addKeyBinding("DELETE",DELETE); - addKeyBinding("C+DELETE",DELETE_WORD); - - addKeyBinding("ENTER",INSERT_BREAK); - addKeyBinding("TAB",INSERT_TAB); - - addKeyBinding("INSERT",OVERWRITE); - addKeyBinding("C+\\",TOGGLE_RECT); - - addKeyBinding("HOME",HOME); - addKeyBinding("END",END); - addKeyBinding("S+HOME",SELECT_HOME); - addKeyBinding("S+END",SELECT_END); - addKeyBinding("C+HOME",DOCUMENT_HOME); - addKeyBinding("C+END",DOCUMENT_END); - addKeyBinding("CS+HOME",SELECT_DOC_HOME); - addKeyBinding("CS+END",SELECT_DOC_END); - - addKeyBinding("PAGE_UP",PREV_PAGE); - addKeyBinding("PAGE_DOWN",NEXT_PAGE); - addKeyBinding("S+PAGE_UP",SELECT_PREV_PAGE); - addKeyBinding("S+PAGE_DOWN",SELECT_NEXT_PAGE); - - addKeyBinding("LEFT",PREV_CHAR); - addKeyBinding("S+LEFT",SELECT_PREV_CHAR); - addKeyBinding("C+LEFT",PREV_WORD); - addKeyBinding("CS+LEFT",SELECT_PREV_WORD); - addKeyBinding("RIGHT",NEXT_CHAR); - addKeyBinding("S+RIGHT",SELECT_NEXT_CHAR); - addKeyBinding("C+RIGHT",NEXT_WORD); - addKeyBinding("CS+RIGHT",SELECT_NEXT_WORD); - addKeyBinding("UP",PREV_LINE); - addKeyBinding("S+UP",SELECT_PREV_LINE); - addKeyBinding("DOWN",NEXT_LINE); - addKeyBinding("S+DOWN",SELECT_NEXT_LINE); - - addKeyBinding("C+ENTER",REPEAT); - } - - /** - * Adds a key binding to this input handler. The key binding is - * a list of white space separated key strokes of the form - * [modifiers+]key where modifier is C for Control, A for Alt, - * or S for Shift, and key is either a character (a-z) or a field - * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE) - * @param keyBinding The key binding - * @param action The action - */ - public void addKeyBinding(String keyBinding, ActionListener action) - { - Hashtable current = bindings; - - StringTokenizer st = new StringTokenizer(keyBinding); - while(st.hasMoreTokens()) - { - KeyStroke keyStroke = parseKeyStroke(st.nextToken()); - if(keyStroke == null) - return; - - if(st.hasMoreTokens()) - { - Object o = current.get(keyStroke); - if(o instanceof Hashtable) - current = (Hashtable)o; - else - { - o = new Hashtable(); - current.put(keyStroke,o); - current = (Hashtable)o; - } - } - else - current.put(keyStroke,action); - } - } - - /** - * Removes a key binding from this input handler. This is not yet - * implemented. - * @param keyBinding The key binding - */ - public void removeKeyBinding(String keyBinding) - { - throw new InternalError("Not yet implemented"); - } - - /** - * Removes all key bindings from this input handler. - */ - public void removeAllKeyBindings() - { - bindings.clear(); - } - - /** - * Returns a copy of this input handler that shares the same - * key bindings. Setting key bindings in the copy will also - * set them in the original. - */ - public InputHandler copy() - { - return new DefaultInputHandler(this); - } - - /** - * Handle a key pressed event. This will look up the binding for - * the key stroke and execute it. - */ - public void keyPressed(KeyEvent evt) - { - int keyCode = evt.getKeyCode(); - int modifiers = evt.getModifiers(); - - // moved this earlier so it doesn't get random meta clicks - if (keyCode == KeyEvent.VK_CONTROL || - keyCode == KeyEvent.VK_SHIFT || - keyCode == KeyEvent.VK_ALT || - keyCode == KeyEvent.VK_META) { - return; - } - - // don't get command-s or other menu key equivs on mac - // unless it's something that's specifically bound (cmd-left or right) - //if ((modifiers & KeyEvent.META_MASK) != 0) return; - if ((modifiers & KeyEvent.META_MASK) != 0) { - KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers); - if (currentBindings.get(keyStroke) == null) { - return; - } - } - - /* - char keyChar = evt.getKeyChar(); - System.out.println("code=" + keyCode + " char=" + keyChar + - " charint=" + ((int)keyChar)); - System.out.println("other codes " + KeyEvent.VK_ALT + " " + - KeyEvent.VK_META); - */ - - if((modifiers & ~KeyEvent.SHIFT_MASK) != 0 - || evt.isActionKey() - || keyCode == KeyEvent.VK_BACK_SPACE - || keyCode == KeyEvent.VK_DELETE - || keyCode == KeyEvent.VK_ENTER - || keyCode == KeyEvent.VK_TAB - || keyCode == KeyEvent.VK_ESCAPE) - { - if(grabAction != null) - { - handleGrabAction(evt); - return; - } - - KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, - modifiers); - Object o = currentBindings.get(keyStroke); - if(o == null) - { - // Don't beep if the user presses some - // key we don't know about unless a - // prefix is active. Otherwise it will - // beep when caps lock is pressed, etc. - if(currentBindings != bindings) - { - Toolkit.getDefaultToolkit().beep(); - // F10 should be passed on, but C+e F10 - // shouldn't - repeatCount = 0; - repeat = false; - evt.consume(); - } - currentBindings = bindings; - return; - } - else if(o instanceof ActionListener) - { - currentBindings = bindings; - - executeAction(((ActionListener)o), - evt.getSource(),null); - - evt.consume(); - return; - } - else if(o instanceof Hashtable) - { - currentBindings = (Hashtable)o; - evt.consume(); - return; - } - } - } - - /** - * Handle a key typed event. This inserts the key into the text area. - */ - public void keyTyped(KeyEvent evt) - { - int modifiers = evt.getModifiers(); - char c = evt.getKeyChar(); - - // this is the apple/cmd key on macosx.. so menu commands - // were being passed through as legit keys.. added this line - // in an attempt to prevent. - if ((modifiers & KeyEvent.META_MASK) != 0) return; - - if (c != KeyEvent.CHAR_UNDEFINED) // && - // (modifiers & KeyEvent.ALT_MASK) == 0) - { - if(c >= 0x20 && c != 0x7f) - { - KeyStroke keyStroke = KeyStroke.getKeyStroke( - Character.toUpperCase(c)); - Object o = currentBindings.get(keyStroke); - - if(o instanceof Hashtable) - { - currentBindings = (Hashtable)o; - return; - } - else if(o instanceof ActionListener) - { - currentBindings = bindings; - executeAction((ActionListener)o, - evt.getSource(), - String.valueOf(c)); - return; - } - - currentBindings = bindings; - - if(grabAction != null) - { - handleGrabAction(evt); - return; - } - - // 0-9 adds another 'digit' to the repeat number - if(repeat && Character.isDigit(c)) - { - repeatCount *= 10; - repeatCount += (c - '0'); - return; - } - - executeAction(INSERT_CHAR,evt.getSource(), - String.valueOf(evt.getKeyChar())); - - repeatCount = 0; - repeat = false; - } - } - } - - /** - * Converts a string to a keystroke. The string should be of the - * form modifiers+shortcut where modifiers - * is any combination of A for Alt, C for Control, S for Shift - * or M for Meta, and shortcut is either a single character, - * or a keycode name from the KeyEvent class, without - * the VK_ prefix. - * @param keyStroke A string description of the key stroke - */ - public static KeyStroke parseKeyStroke(String keyStroke) - { - if(keyStroke == null) - return null; - int modifiers = 0; - int index = keyStroke.indexOf('+'); - if(index != -1) - { - for(int i = 0; i < index; i++) - { - switch(Character.toUpperCase(keyStroke - .charAt(i))) - { - case 'A': - modifiers |= InputEvent.ALT_MASK; - break; - case 'C': - modifiers |= InputEvent.CTRL_MASK; - break; - case 'M': - modifiers |= InputEvent.META_MASK; - break; - case 'S': - modifiers |= InputEvent.SHIFT_MASK; - break; - } - } - } - String key = keyStroke.substring(index + 1); - if(key.length() == 1) - { - char ch = Character.toUpperCase(key.charAt(0)); - if(modifiers == 0) - return KeyStroke.getKeyStroke(ch); - else - return KeyStroke.getKeyStroke(ch,modifiers); - } - else if(key.length() == 0) - { - System.err.println("Invalid key stroke: " + keyStroke); - return null; - } - else - { - int ch; - - try - { - ch = KeyEvent.class.getField("VK_".concat(key)) - .getInt(null); - } - catch(Exception e) - { - System.err.println("Invalid key stroke: " - + keyStroke); - return null; - } - - return KeyStroke.getKeyStroke(ch,modifiers); - } - } - - // private members - private Hashtable bindings; - private Hashtable currentBindings; - - private DefaultInputHandler(DefaultInputHandler copy) - { - bindings = currentBindings = copy.bindings; - } -} diff --git a/app/src/processing/app/syntax/InputHandler.java b/app/src/processing/app/syntax/InputHandler.java deleted file mode 100644 index e146713484b..00000000000 --- a/app/src/processing/app/syntax/InputHandler.java +++ /dev/null @@ -1,1135 +0,0 @@ -/* - * InputHandler.java - Manages key bindings and executes actions - * Copyright (C) 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import javax.swing.text.*; -import javax.swing.JPopupMenu; -import java.awt.event.*; -import java.awt.Component; -import java.util.*; - -/** - * An input handler converts the user's key strokes into concrete actions. - * It also takes care of macro recording and action repetition.

- * - * This class provides all the necessary support code for an input - * handler, but doesn't actually do any key binding logic. It is up - * to the implementations of this class to do so. - * - * @author Slava Pestov - */ -public abstract class InputHandler extends KeyAdapter -{ - /** - * If this client property is set to Boolean.TRUE on the text area, - * the home/end keys will support 'smart' BRIEF-like behaviour - * (one press = start/end of line, two presses = start/end of - * viewscreen, three presses = start/end of document). By default, - * this property is not set. - */ - public static final String SMART_HOME_END_PROPERTY = "InputHandler.homeEnd"; - - public static final ActionListener BACKSPACE = new backspace(); - public static final ActionListener BACKSPACE_WORD = new backspace_word(); - public static final ActionListener DELETE = new delete(); - public static final ActionListener DELETE_WORD = new delete_word(); - public static final ActionListener END = new end(false); - public static final ActionListener DOCUMENT_END = new document_end(false); - public static final ActionListener SELECT_END = new end(true); - public static final ActionListener SELECT_DOC_END = new document_end(true); - public static final ActionListener INSERT_BREAK = new insert_break(); - public static final ActionListener INSERT_TAB = new insert_tab(); - public static final ActionListener HOME = new home(false); - public static final ActionListener DOCUMENT_HOME = new document_home(false); - public static final ActionListener SELECT_HOME = new home(true); - public static final ActionListener SELECT_DOC_HOME = new document_home(true); - public static final ActionListener NEXT_CHAR = new next_char(false); - public static final ActionListener NEXT_LINE = new next_line(false); - public static final ActionListener NEXT_PAGE = new next_page(false); - public static final ActionListener NEXT_WORD = new next_word(false); - public static final ActionListener SELECT_NEXT_CHAR = new next_char(true); - public static final ActionListener SELECT_NEXT_LINE = new next_line(true); - public static final ActionListener SELECT_NEXT_PAGE = new next_page(true); - public static final ActionListener SELECT_NEXT_WORD = new next_word(true); - public static final ActionListener OVERWRITE = new overwrite(); - public static final ActionListener PREV_CHAR = new prev_char(false); - public static final ActionListener PREV_LINE = new prev_line(false); - public static final ActionListener PREV_PAGE = new prev_page(false); - public static final ActionListener PREV_WORD = new prev_word(false); - public static final ActionListener SELECT_PREV_CHAR = new prev_char(true); - public static final ActionListener SELECT_PREV_LINE = new prev_line(true); - public static final ActionListener SELECT_PREV_PAGE = new prev_page(true); - public static final ActionListener SELECT_PREV_WORD = new prev_word(true); - public static final ActionListener REPEAT = new repeat(); - public static final ActionListener TOGGLE_RECT = new toggle_rect(); - public static final ActionListener CLIPBOARD_CUT = new clipboard_cut(); // [fry] - public static final ActionListener CLIPBOARD_COPY = new clipboard_copy(); - public static final ActionListener CLIPBOARD_PASTE = new clipboard_paste(); - - // Default action - public static final ActionListener INSERT_CHAR = new insert_char(); - - private static Hashtable actions; - - static - { - actions = new Hashtable(); - actions.put("backspace",BACKSPACE); - actions.put("backspace-word",BACKSPACE_WORD); - actions.put("delete",DELETE); - actions.put("delete-word",DELETE_WORD); - actions.put("end",END); - actions.put("select-end",SELECT_END); - actions.put("document-end",DOCUMENT_END); - actions.put("select-doc-end",SELECT_DOC_END); - actions.put("insert-break",INSERT_BREAK); - actions.put("insert-tab",INSERT_TAB); - actions.put("home",HOME); - actions.put("select-home",SELECT_HOME); - actions.put("document-home",DOCUMENT_HOME); - actions.put("select-doc-home",SELECT_DOC_HOME); - actions.put("next-char",NEXT_CHAR); - actions.put("next-line",NEXT_LINE); - actions.put("next-page",NEXT_PAGE); - actions.put("next-word",NEXT_WORD); - actions.put("select-next-char",SELECT_NEXT_CHAR); - actions.put("select-next-line",SELECT_NEXT_LINE); - actions.put("select-next-page",SELECT_NEXT_PAGE); - actions.put("select-next-word",SELECT_NEXT_WORD); - actions.put("overwrite",OVERWRITE); - actions.put("prev-char",PREV_CHAR); - actions.put("prev-line",PREV_LINE); - actions.put("prev-page",PREV_PAGE); - actions.put("prev-word",PREV_WORD); - actions.put("select-prev-char",SELECT_PREV_CHAR); - actions.put("select-prev-line",SELECT_PREV_LINE); - actions.put("select-prev-page",SELECT_PREV_PAGE); - actions.put("select-prev-word",SELECT_PREV_WORD); - actions.put("repeat",REPEAT); - actions.put("toggle-rect",TOGGLE_RECT); - actions.put("insert-char",INSERT_CHAR); - actions.put("clipboard-cut",CLIPBOARD_CUT); - actions.put("clipboard-copy",CLIPBOARD_COPY); - actions.put("clipboard-paste",CLIPBOARD_PASTE); - } - - /** - * Returns a named text area action. - * @param name The action name - */ - public static ActionListener getAction(String name) - { - return (ActionListener)actions.get(name); - } - - /** - * Returns the name of the specified text area action. - * @param listener The action - */ - public static String getActionName(ActionListener listener) - { - Enumeration en = getActions(); - while(en.hasMoreElements()) - { - String name = (String)en.nextElement(); - ActionListener _listener = getAction(name); - if(_listener == listener) { - return name; - } - } - return null; - } - - /** - * Returns an enumeration of all available actions. - */ - public static Enumeration getActions() - { - return actions.keys(); - } - - /** - * Adds the default key bindings to this input handler. - * This should not be called in the constructor of this - * input handler, because applications might load the - * key bindings from a file, etc. - */ - public abstract void addDefaultKeyBindings(); - - /** - * Adds a key binding to this input handler. - * @param keyBinding The key binding (the format of this is - * input-handler specific) - * @param action The action - */ - public abstract void addKeyBinding(String keyBinding, ActionListener action); - - /** - * Removes a key binding from this input handler. - * @param keyBinding The key binding - */ - public abstract void removeKeyBinding(String keyBinding); - - /** - * Removes all key bindings from this input handler. - */ - public abstract void removeAllKeyBindings(); - - /** - * Grabs the next key typed event and invokes the specified - * action with the key as a the action command. - */ - public void grabNextKeyStroke(ActionListener listener) - { - grabAction = listener; - } - - /** - * Returns if repeating is enabled. When repeating is enabled, - * actions will be executed multiple times. This is usually - * invoked with a special key stroke in the input handler. - */ - public boolean isRepeatEnabled() - { - return repeat; - } - - /** - * Enables repeating. When repeating is enabled, actions will be - * executed multiple times. Once repeating is enabled, the input - * handler should read a number from the keyboard. - */ - public void setRepeatEnabled(boolean repeat) - { - this.repeat = repeat; - } - - /** - * Returns the number of times the next action will be repeated. - */ - public int getRepeatCount() - { - return (repeat ? Math.max(1,repeatCount) : 1); - } - - /** - * Sets the number of times the next action will be repeated. - * @param repeatCount The repeat count - */ - public void setRepeatCount(int repeatCount) - { - this.repeatCount = repeatCount; - } - - /** - * Returns the macro recorder. If this is non-null, all executed - * actions should be forwarded to the recorder. - */ - public InputHandler.MacroRecorder getMacroRecorder() - { - return recorder; - } - - /** - * Sets the macro recorder. If this is non-null, all executed - * actions should be forwarded to the recorder. - * @param recorder The macro recorder - */ - public void setMacroRecorder(InputHandler.MacroRecorder recorder) - { - this.recorder = recorder; - } - - /** - * Returns a copy of this input handler that shares the same - * key bindings. Setting key bindings in the copy will also - * set them in the original. - */ - public abstract InputHandler copy(); - - /** - * Executes the specified action, repeating and recording it as - * necessary. - * @param listener The action listener - * @param source The event source - * @param actionCommand The action command - */ - public void executeAction(ActionListener listener, Object source, - String actionCommand) - { - // create event - ActionEvent evt = new ActionEvent(source, - ActionEvent.ACTION_PERFORMED, - actionCommand); - - // don't do anything if the action is a wrapper - // (like EditAction.Wrapper) - if(listener instanceof Wrapper) - { - listener.actionPerformed(evt); - return; - } - - // remember old values, in case action changes them - boolean _repeat = repeat; - int _repeatCount = getRepeatCount(); - - // execute the action - if(listener instanceof InputHandler.NonRepeatable) - listener.actionPerformed(evt); - else - { - for(int i = 0; i < Math.max(1,repeatCount); i++) - listener.actionPerformed(evt); - } - - // do recording. Notice that we do no recording whatsoever - // for actions that grab keys - if(grabAction == null) - { - if(recorder != null) - { - if(!(listener instanceof InputHandler.NonRecordable)) - { - if(_repeatCount != 1) - recorder.actionPerformed(REPEAT,String.valueOf(_repeatCount)); - - recorder.actionPerformed(listener,actionCommand); - } - } - - // If repeat was true originally, clear it - // Otherwise it might have been set by the action, etc - if(_repeat) - { - repeat = false; - repeatCount = 0; - } - } - } - - /** - * Returns the text area that fired the specified event. - * @param evt The event - */ - public static JEditTextArea getTextArea(EventObject evt) - { - if(evt != null) - { - Object o = evt.getSource(); - if(o instanceof Component) - { - // find the parent text area - Component c = (Component)o; - for(;;) - { - if(c instanceof JEditTextArea) - return (JEditTextArea)c; - else if(c == null) - break; - if(c instanceof JPopupMenu) - c = ((JPopupMenu)c) - .getInvoker(); - else - c = c.getParent(); - } - } - } - - // this shouldn't happen - System.err.println("BUG: getTextArea() returning null"); - System.err.println("Report this to Slava Pestov "); - return null; - } - - // protected members - - /** - * If a key is being grabbed, this method should be called with - * the appropriate key event. It executes the grab action with - * the typed character as the parameter. - */ - protected void handleGrabAction(KeyEvent evt) - { - // Clear it *before* it is executed so that executeAction() - // resets the repeat count - ActionListener _grabAction = grabAction; - grabAction = null; - executeAction(_grabAction,evt.getSource(), - String.valueOf(evt.getKeyChar())); - } - - // protected members - protected ActionListener grabAction; - protected boolean repeat; - protected int repeatCount; - protected InputHandler.MacroRecorder recorder; - - /** - * If an action implements this interface, it should not be repeated. - * Instead, it will handle the repetition itself. - */ - public interface NonRepeatable {} - - /** - * If an action implements this interface, it should not be recorded - * by the macro recorder. Instead, it will do its own recording. - */ - public interface NonRecordable {} - - /** - * For use by EditAction.Wrapper only. - * @since jEdit 2.2final - */ - public interface Wrapper {} - - /** - * Macro recorder. - */ - public interface MacroRecorder - { - void actionPerformed(ActionListener listener, - String actionCommand); - } - - public static class backspace implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - - if(!textArea.isEditable()) - { - textArea.getToolkit().beep(); - return; - } - - if(textArea.getSelectionStart() - != textArea.getSelectionStop()) - { - textArea.setSelectedText(""); - } - else - { - int caret = textArea.getCaretPosition(); - if(caret == 0) - { - textArea.getToolkit().beep(); - return; - } - try - { - textArea.getDocument().remove(caret - 1,1); - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - } - } - } - } - - public static class backspace_word implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - int start = textArea.getSelectionStart(); - if(start != textArea.getSelectionStop()) - { - textArea.setSelectedText(""); - } - - int line = textArea.getCaretLine(); - int lineStart = textArea.getLineStartOffset(line); - int caret = start - lineStart; - - String lineText = textArea.getLineText(textArea - .getCaretLine()); - - if(caret == 0) - { - if(lineStart == 0) - { - textArea.getToolkit().beep(); - return; - } - caret--; - } - else - { - String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); - caret = TextUtilities.findWordStart(lineText,caret,noWordSep); - } - - try - { - textArea.getDocument().remove( - caret + lineStart, - start - (caret + lineStart)); - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - } - } - } - - public static class delete implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - - if(!textArea.isEditable()) - { - textArea.getToolkit().beep(); - return; - } - - if(textArea.getSelectionStart() - != textArea.getSelectionStop()) - { - textArea.setSelectedText(""); - } - else - { - int caret = textArea.getCaretPosition(); - if(caret == textArea.getDocumentLength()) - { - textArea.getToolkit().beep(); - return; - } - try - { - textArea.getDocument().remove(caret,1); - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - } - } - } - } - - public static class delete_word implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - int start = textArea.getSelectionStart(); - if(start != textArea.getSelectionStop()) - { - textArea.setSelectedText(""); - } - - int line = textArea.getCaretLine(); - int lineStart = textArea.getLineStartOffset(line); - int caret = start - lineStart; - - String lineText = textArea.getLineText(textArea - .getCaretLine()); - - if(caret == lineText.length()) - { - if(lineStart + caret == textArea.getDocumentLength()) - { - textArea.getToolkit().beep(); - return; - } - caret++; - } - else - { - String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); - caret = TextUtilities.findWordEnd(lineText,caret,noWordSep); - } - - try - { - textArea.getDocument().remove(start, - (caret + lineStart) - start); - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - } - } - } - - public static class end implements ActionListener - { - private boolean select; - - public end(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - - int caret = textArea.getCaretPosition(); - - int lastOfLine = textArea.getLineStopOffset( - textArea.getCaretLine()) - 1; - int lastVisibleLine = textArea.getFirstLine() - + textArea.getVisibleLines(); - if(lastVisibleLine >= textArea.getLineCount()) - { - lastVisibleLine = Math.min(textArea.getLineCount() - 1, - lastVisibleLine); - } - else - lastVisibleLine -= (textArea.getElectricScroll() + 1); - - int lastVisible = textArea.getLineStopOffset(lastVisibleLine) - 1; - int lastDocument = textArea.getDocumentLength(); - - if(caret == lastDocument) - { - textArea.getToolkit().beep(); - return; - } - else if(!Boolean.TRUE.equals(textArea.getClientProperty( - SMART_HOME_END_PROPERTY))) - caret = lastOfLine; - else if(caret == lastVisible) - caret = lastDocument; - else if(caret == lastOfLine) - caret = lastVisible; - else - caret = lastOfLine; - - if(select) - textArea.select(textArea.getMarkPosition(),caret); - else - textArea.setCaretPosition(caret); - } - } - - public static class document_end implements ActionListener - { - private boolean select; - - public document_end(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - if(select) - textArea.select(textArea.getMarkPosition(), - textArea.getDocumentLength()); - else - textArea.setCaretPosition(textArea - .getDocumentLength()); - } - } - - public static class home implements ActionListener - { - private boolean select; - - public home(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - - int caret = textArea.getCaretPosition(); - - int firstLine = textArea.getFirstLine(); - - int firstOfLine = textArea.getLineStartOffset( - textArea.getCaretLine()); - int firstVisibleLine = (firstLine == 0 ? 0 : - firstLine + textArea.getElectricScroll()); - int firstVisible = textArea.getLineStartOffset( - firstVisibleLine); - - if(caret == 0) - { - textArea.getToolkit().beep(); - return; - } - else if(!Boolean.TRUE.equals(textArea.getClientProperty( - SMART_HOME_END_PROPERTY))) - caret = firstOfLine; - else if(caret == firstVisible) - caret = 0; - else if(caret == firstOfLine) - caret = firstVisible; - else - caret = firstOfLine; - - if(select) - textArea.select(textArea.getMarkPosition(),caret); - else - textArea.setCaretPosition(caret); - } - } - - public static class document_home implements ActionListener - { - private boolean select; - - public document_home(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - if(select) - textArea.select(textArea.getMarkPosition(),0); - else - textArea.setCaretPosition(0); - } - } - - public static class insert_break implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - - if(!textArea.isEditable()) - { - textArea.getToolkit().beep(); - return; - } - - textArea.setSelectedText("\n"); - } - } - - public static class insert_tab implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - - if(!textArea.isEditable()) - { - textArea.getToolkit().beep(); - return; - } - - textArea.overwriteSetSelectedText("\t"); - } - } - - public static class next_char implements ActionListener - { - private boolean select; - - public next_char(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - int caret = textArea.getCaretPosition(); - - if(caret == textArea.getDocumentLength()) - { - if (textArea.getSelectionStart() != - textArea.getSelectionStop()) { - // just move to the end of the selection - textArea.select(caret, caret); - } else { - // beep at the user for being annoying - textArea.getToolkit().beep(); - } - - } else if (select) { - textArea.select(textArea.getMarkPosition(), caret+1); - - } else { - int start = textArea.getSelectionStart(); - int end = textArea.getSelectionStop(); - if (start != end) { - textArea.select(end, end); - } else { - textArea.setCaretPosition(caret + 1); - } - } - } - } - - public static class next_line implements ActionListener - { - private boolean select; - - public next_line(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - int caret = textArea.getCaretPosition(); - int line = textArea.getCaretLine(); - - if(line == textArea.getLineCount() - 1) - { - //textArea.getToolkit().beep(); - int doc = textArea.getDocumentLength(); - if (select) { - textArea.select(textArea.getMarkPosition(), doc); - } else { - textArea.setCaretPosition(doc); - } - return; - } - - int magic = textArea.getMagicCaretPosition(); - if(magic == -1) - { - magic = textArea.offsetToX(line, - caret - textArea.getLineStartOffset(line)); - } - - caret = textArea.getLineStartOffset(line + 1) - + textArea.xToOffset(line + 1,magic); - if(select) - textArea.select(textArea.getMarkPosition(),caret); - else - textArea.setCaretPosition(caret); - textArea.setMagicCaretPosition(magic); - } - } - - public static class next_page implements ActionListener - { - private boolean select; - - public next_page(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - int lineCount = textArea.getLineCount(); - int firstLine = textArea.getFirstLine(); - int visibleLines = textArea.getVisibleLines(); - int line = textArea.getCaretLine(); - - firstLine += visibleLines; - - if(firstLine + visibleLines >= lineCount - 1) - firstLine = lineCount - visibleLines; - - textArea.setFirstLine(firstLine); - - int caret = textArea.getLineStartOffset( - Math.min(textArea.getLineCount() - 1, - line + visibleLines)); - if(select) - textArea.select(textArea.getMarkPosition(),caret); - else - textArea.setCaretPosition(caret); - } - } - - public static class next_word implements ActionListener - { - private boolean select; - - public next_word(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - int caret = textArea.getCaretPosition(); - int line = textArea.getCaretLine(); - int lineStart = textArea.getLineStartOffset(line); - caret -= lineStart; - - String lineText = textArea.getLineText(textArea - .getCaretLine()); - - if(caret == lineText.length()) - { - if(lineStart + caret == textArea.getDocumentLength()) - { - textArea.getToolkit().beep(); - return; - } - caret++; - } - else - { - String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); - caret = TextUtilities.findWordEnd(lineText,caret,noWordSep); - } - - if(select) - textArea.select(textArea.getMarkPosition(), - lineStart + caret); - else - textArea.setCaretPosition(lineStart + caret); - } - } - - public static class overwrite implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - textArea.setOverwriteEnabled( - !textArea.isOverwriteEnabled()); - } - } - - public static class prev_char implements ActionListener - { - private boolean select; - - public prev_char(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - int caret = textArea.getCaretPosition(); - if(caret == 0) - { - textArea.getToolkit().beep(); - return; - } - - if (select) { - textArea.select(textArea.getMarkPosition(), caret-1); - } else { - int start = textArea.getSelectionStart(); - int end = textArea.getSelectionStop(); - if (start != end) { - textArea.select(start, start); - } else { - textArea.setCaretPosition(caret - 1); - } - } - } - } - - public static class prev_line implements ActionListener - { - private boolean select; - - public prev_line(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - int caret = textArea.getCaretPosition(); - int line = textArea.getCaretLine(); - - if(line == 0) - { - if (select) { - if (textArea.getSelectionStart() != 0) { - textArea.select(textArea.getMarkPosition(), 0); - } - } else { - textArea.setCaretPosition(0); - } - //textArea.getToolkit().beep(); - return; - } - - int magic = textArea.getMagicCaretPosition(); - if(magic == -1) - { - magic = textArea.offsetToX(line, - caret - textArea.getLineStartOffset(line)); - } - - caret = textArea.getLineStartOffset(line - 1) - + textArea.xToOffset(line - 1,magic); - if(select) - textArea.select(textArea.getMarkPosition(),caret); - else - textArea.setCaretPosition(caret); - textArea.setMagicCaretPosition(magic); - } - } - - public static class prev_page implements ActionListener - { - private boolean select; - - public prev_page(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - int firstLine = textArea.getFirstLine(); - int visibleLines = textArea.getVisibleLines(); - int line = textArea.getCaretLine(); - - if(firstLine < visibleLines) - firstLine = visibleLines; - - textArea.setFirstLine(firstLine - visibleLines); - - int caret = textArea.getLineStartOffset( - Math.max(0,line - visibleLines)); - if(select) - textArea.select(textArea.getMarkPosition(),caret); - else - textArea.setCaretPosition(caret); - } - } - - public static class prev_word implements ActionListener - { - private boolean select; - - public prev_word(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - int caret = textArea.getCaretPosition(); - int line = textArea.getCaretLine(); - int lineStart = textArea.getLineStartOffset(line); - caret -= lineStart; - - String lineText = textArea.getLineText(textArea - .getCaretLine()); - - if(caret == 0) - { - if(lineStart == 0) - { - textArea.getToolkit().beep(); - return; - } - caret--; - } - else - { - String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); - caret = TextUtilities.findWordStart(lineText,caret,noWordSep); - } - - if(select) - textArea.select(textArea.getMarkPosition(), - lineStart + caret); - else - textArea.setCaretPosition(lineStart + caret); - } - } - - public static class repeat implements ActionListener, - InputHandler.NonRecordable - { - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - textArea.getInputHandler().setRepeatEnabled(true); - String actionCommand = evt.getActionCommand(); - if(actionCommand != null) - { - textArea.getInputHandler().setRepeatCount( - Integer.parseInt(actionCommand)); - } - } - } - - public static class toggle_rect implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - textArea.setSelectionRectangular( - !textArea.isSelectionRectangular()); - } - } - - - public static class clipboard_cut implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - getTextArea(evt).cut(); - } - } - - - public static class clipboard_copy implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - getTextArea(evt).copy(); - } - } - - - public static class clipboard_paste implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - getTextArea(evt).paste(); - } - } - - - public static class insert_char implements ActionListener, - InputHandler.NonRepeatable - { - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - String str = evt.getActionCommand(); - int repeatCount = textArea.getInputHandler().getRepeatCount(); - - if(textArea.isEditable()) - { - StringBuffer buf = new StringBuffer(); - for(int i = 0; i < repeatCount; i++) - buf.append(str); - textArea.overwriteSetSelectedText(buf.toString()); - } - else - { - textArea.getToolkit().beep(); - } - } - } -} diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java deleted file mode 100644 index c12102038c8..00000000000 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ /dev/null @@ -1,2448 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - * JEditTextArea.java - jEdit's text component - * Copyright (C) 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import processing.app.*; - -import javax.swing.event.*; -import javax.swing.text.*; -import javax.swing.undo.*; -import javax.swing.*; - -import java.awt.datatransfer.*; -import java.awt.event.*; -import java.awt.*; -import java.util.Enumeration; -import java.util.Vector; -import java.awt.im.InputMethodRequests; - -import processing.app.syntax.im.InputMethodSupport; - -/** - * jEdit's text area component. It is more suited for editing program - * source code than JEditorPane, because it drops the unnecessary features - * (images, variable-width lines, and so on) and adds a whole bunch of - * useful goodies such as: - *

    - *
  • More flexible key binding scheme - *
  • Supports macro recorders - *
  • Rectangular selection - *
  • Bracket highlighting - *
  • Syntax highlighting - *
  • Command repetition - *
  • Block caret can be enabled - *
- * It is also faster and doesn't have as many problems. It can be used - * in other applications; the only other part of jEdit it depends on is - * the syntax package.

- * - * To use it in your app, treat it like any other component, for example: - *

JEditTextArea ta = new JEditTextArea();
- * ta.setTokenMarker(new JavaTokenMarker());
- * ta.setText("public class Test {\n"
- *     + "    public static void main(String[] args) {\n"
- *     + "        System.out.println(\"Hello World\");\n"
- *     + "    }\n"
- *     + "}");
- * - * @author Slava Pestov - */ -public class JEditTextArea extends JComponent -{ - /** - * Adding components with this name to the text area will place - * them left of the horizontal scroll bar. In jEdit, the status - * bar is added this way. - */ - public static String LEFT_OF_SCROLLBAR = "los"; - - /** - * Creates a new JEditTextArea with the default settings. - */ - /* - public JEditTextArea() - { - this(TextAreaDefaults.getDefaults()); - } - */ - - /** - * Creates a new JEditTextArea with the specified settings. - * @param defaults The default settings - */ - public JEditTextArea(TextAreaDefaults defaults) - { - // Enable the necessary events - enableEvents(AWTEvent.KEY_EVENT_MASK); - - // Initialize some misc. stuff - painter = new TextAreaPainter(this,defaults); - editorLineNumbers = new TextAreaLineNumbers(this,defaults); - documentHandler = new DocumentHandler(); - eventListenerList = new EventListenerList(); - caretEvent = new MutableCaretEvent(); - lineSegment = new Segment(); - bracketLine = bracketPosition = -1; - blink = true; - - // Initialize the GUI - setLayout(new ScrollLayout()); - add(LEFT, editorLineNumbers); - add(CENTER, painter); - add(RIGHT, vertical = new JScrollBar(JScrollBar.VERTICAL)); - add(BOTTOM, horizontal = new JScrollBar(JScrollBar.HORIZONTAL)); - - // Add some event listeners - vertical.addAdjustmentListener(new AdjustHandler()); - horizontal.addAdjustmentListener(new AdjustHandler()); - painter.addComponentListener(new ComponentHandler()); - painter.addMouseListener(new MouseHandler()); - painter.addMouseMotionListener(new DragHandler()); - addFocusListener(new FocusHandler()); - // send tab keys through to the text area - // http://dev.processing.org/bugs/show_bug.cgi?id=1267 - setFocusTraversalKeysEnabled(false); - - // Load the defaults - setInputHandler(defaults.inputHandler); - setDocument(defaults.document); - editable = defaults.editable; - caretVisible = defaults.caretVisible; - caretBlinks = defaults.caretBlinks; - electricScroll = defaults.electricScroll; - - // We don't seem to get the initial focus event? - focusedComponent = this; - - addMouseWheelListener(new MouseWheelListener() { - public void mouseWheelMoved(MouseWheelEvent e) { - if (!scrollBarsInitialized) return; - int amt = e.getWheelRotation(); - vertical.setValue(vertical.getValue() + amt * 3); - } - }); - } - - /** - * Inline Input Method Support for Japanese. - */ - private InputMethodSupport inputMethodSupport = null; - public InputMethodRequests getInputMethodRequests() { - if (inputMethodSupport == null) { - inputMethodSupport = new InputMethodSupport(this); - } - return inputMethodSupport; - } - - /** - * Get current position of the vertical scroll bar. [fry] - */ - public int getScrollPosition() { - return vertical.getValue(); - } - - - /** - * Set position of the vertical scroll bar. [fry] - */ - public void setScrollPosition(int what) { - vertical.setValue(what); - } - - - /** - * Returns if this component can be traversed by pressing - * the Tab key. This returns false. - */ -// public final boolean isManagingFocus() { -// return true; -// } - - /** - * Returns the object responsible for painting this text area. - */ - public final TextAreaPainter getPainter() { - return painter; - } - - /** - * Returns the input handler. - */ - public final InputHandler getInputHandler() { - return inputHandler; - } - - /** - * Sets the input handler. - * @param inputHandler The new input handler - */ - public void setInputHandler(InputHandler inputHandler) { - this.inputHandler = inputHandler; - } - - /** - * Returns true if the caret is blinking, false otherwise. - */ - public final boolean isCaretBlinkEnabled() { - return caretBlinks; - } - - /** - * Toggles caret blinking. - * @param caretBlinks True if the caret should blink, false otherwise - */ - public void setCaretBlinkEnabled(boolean caretBlinks) { - this.caretBlinks = caretBlinks; - if(!caretBlinks) - blink = false; - - painter.invalidateSelectedLines(); - } - - /** - * Returns true if the caret is visible, false otherwise. - */ - public final boolean isCaretVisible() { - return (!caretBlinks || blink) && caretVisible; - } - - /** - * Sets if the caret should be visible. - * @param caretVisible True if the caret should be visible, false - * otherwise - */ - public void setCaretVisible(boolean caretVisible) { - this.caretVisible = caretVisible; - blink = true; - - painter.invalidateSelectedLines(); - } - - /** - * Blinks the caret. - */ - public final void blinkCaret() { - if (caretBlinks) { - blink = !blink; - painter.invalidateSelectedLines(); - } else { - blink = true; - } - } - - /** - * Returns the number of lines from the top and button of the - * text area that are always visible. - */ - public final int getElectricScroll() { - return electricScroll; - } - - /** - * Sets the number of lines from the top and bottom of the text - * area that are always visible - * @param electricScroll The number of lines always visible from - * the top or bottom - */ - public final void setElectricScroll(int electricScroll) { - this.electricScroll = electricScroll; - } - - - /** - * Updates the state of the scroll bars. This should be called - * if the number of lines in the document changes, or when the - * size of the text are changes. - */ - public void updateScrollBars() { - if (vertical != null && visibleLines != 0) { - vertical.setValues(firstLine,visibleLines,0,getLineCount()); - vertical.setUnitIncrement(2); - vertical.setBlockIncrement(visibleLines); - } - - //if (horizontal != null && width != 0) { - if ((horizontal != null) && (painter.getWidth() != 0)) { - //int value = horizontal.getValue(); - //System.out.println("updateScrollBars"); - //int width = painter.getWidth(); - int lineCount = getLineCount(); - int maxLineLength = 0; - for (int i = 0; i < lineCount; i++) { - int lineLength = getLineLength(i); - if (lineLength > maxLineLength) { - maxLineLength = lineLength; - } - } - int charWidth = painter.getFontMetrics().charWidth('w'); - int width = maxLineLength * charWidth; - int painterWidth = painter.getWidth(); - //System.out.println("max line len " + maxLineLength); - //System.out.println("width " + width); - //System.out.println("text area width " + painter.getWidth()); - - // this was the default, but it's enormous - //horizontal.setValues(-horizontalOffset,width,0,width * 5); - - // something more reasonable, though this is a bad solution - //horizontal.setValues(-horizontalOffset,width,0,width * 2); - - // in general.. time to start looking at that other syntax pkg - // since most code should fit the window horizontally, just use - // the default settings for the width, this is a nicer solution - // until a better update mechanism can be implemented [fry] - - //horizontal.setValues(0, width, 0, width); - //0, width - horizontalOffset); - // works, from pre-75 versions of p5 - //horizontal.setValues(-horizontalOffset, width, 0, width); - - // gets weird when writing to the end of lines - //horizontal.setValues(value, painterWidth, 0, width); - - // seems to work, implemented for 0075 - horizontal.setValues(-horizontalOffset, painterWidth, 0, width); - - //horizontal.setUnitIncrement(painter.getFontMetrics().charWidth('w')); - horizontal.setUnitIncrement(charWidth); - horizontal.setBlockIncrement(width / 2); - } - updateLineNumbers(); - } - - private void updateLineNumbers() { - if (editorLineNumbers != null) { - editorLineNumbers.updateLineNumbers(getFirstLine() + 1, Math.min(getFirstLine() + getVisibleLines() + 1, getLineCount())); - editorLineNumbers.updateWidthForNumDigits(String.valueOf(getLineCount()).length()); - } - } - - /** - * Returns the line displayed at the text area's origin. - */ - public final int getFirstLine() { - return firstLine; - } - - /** - * Sets the line displayed at the text area's origin without - * updating the scroll bars. - */ - public void setFirstLine(int firstLine) { - if (firstLine == this.firstLine) return; - - this.firstLine = firstLine; - if (firstLine != vertical.getValue()) { - updateScrollBars(); - } - repaintEditor(); - } - - /** - * Returns the number of lines visible in this text area. - */ - public final int getVisibleLines() { - return visibleLines; - } - - /** - * Recalculates the number of visible lines. This should not - * be called directly. - */ - public final void recalculateVisibleLines() { - if (painter == null) return; - - int height = painter.getHeight(); - int lineHeight = painter.getFontMetrics().getHeight(); - visibleLines = height / lineHeight; - updateScrollBars(); - } - - /** - * Returns the horizontal offset of drawn lines. - */ - public final int getHorizontalOffset() { - return horizontalOffset; - } - - /** - * Sets the horizontal offset of drawn lines. This can be used to - * implement horizontal scrolling. - * @param horizontalOffset offset The new horizontal offset - */ - public void setHorizontalOffset(int horizontalOffset) - { - if(horizontalOffset == this.horizontalOffset) - return; - this.horizontalOffset = horizontalOffset; - if(horizontalOffset != horizontal.getValue()) - updateScrollBars(); - repaintEditor(); - } - - /** - * A fast way of changing both the first line and horizontal - * offset. - * @param firstLine The new first line - * @param horizontalOffset The new horizontal offset - * @return True if any of the values were changed, false otherwise - */ - public boolean setOrigin(int firstLine, int horizontalOffset) - { - boolean changed = false; - //int oldFirstLine = this.firstLine; - - if(horizontalOffset != this.horizontalOffset) - { - this.horizontalOffset = horizontalOffset; - changed = true; - } - - if(firstLine != this.firstLine) - { - this.firstLine = firstLine; - changed = true; - } - - if(changed) - { - updateScrollBars(); - repaintEditor(); - } - - return changed; - } - - private void repaintEditor() { - painter.repaint(); - updateLineNumbers(); - } - - /** - * Ensures that the caret is visible by scrolling the text area if - * necessary. - * @return True if scrolling was actually performed, false if the - * caret was already visible - */ - public boolean scrollToCaret() - { - int line = getCaretLine(); - int lineStart = getLineStartOffset(line); - int offset = Math.max(0,Math.min(getLineLength(line) - 1, - getCaretPosition() - lineStart)); - - return scrollTo(line,offset); - } - - /** - * Ensures that the specified line and offset is visible by scrolling - * the text area if necessary. - * @param line The line to scroll to - * @param offset The offset in the line to scroll to - * @return True if scrolling was actually performed, false if the - * line and offset was already visible - */ - public boolean scrollTo(int line, int offset) - { - // visibleLines == 0 before the component is realized - // we can't do any proper scrolling then, so we have - // this hack... - if (visibleLines == 0) { - setFirstLine(Math.max(0,line - electricScroll)); - return true; - } - - int newFirstLine = firstLine; - int newHorizontalOffset = horizontalOffset; - - if(line < firstLine + electricScroll) { - newFirstLine = Math.max(0,line - electricScroll); - - } else if(line + electricScroll >= firstLine + visibleLines) { - newFirstLine = (line - visibleLines) + electricScroll + 1; - if(newFirstLine + visibleLines >= getLineCount()) - newFirstLine = getLineCount() - visibleLines; - if(newFirstLine < 0) - newFirstLine = 0; - } - - int x = _offsetToX(line,offset); - int width = painter.getFontMetrics().charWidth('w'); - - if(x < 0) { - newHorizontalOffset = Math.min(0,horizontalOffset - x + width + 5); - } else if(x + width >= painter.getWidth()) { - newHorizontalOffset = horizontalOffset + - (painter.getWidth() - x) - width - 5; - } - - return setOrigin(newFirstLine,newHorizontalOffset); - } - - /** - * Converts a line index to a y co-ordinate. - * @param line The line - */ - public int lineToY(int line) - { - FontMetrics fm = painter.getFontMetrics(); - return (line - firstLine) * fm.getHeight() - - (fm.getLeading() + fm.getMaxDescent()); - } - - /** - * Converts a y co-ordinate to a line index. - * @param y The y co-ordinate - */ - public int yToLine(int y) - { - FontMetrics fm = painter.getFontMetrics(); - int height = fm.getHeight(); - return Math.max(0,Math.min(getLineCount() - 1, - y / height + firstLine)); - } - - /** - * Converts an offset in a line into an x co-ordinate. This is a - * slow version that can be used any time. - * @param line The line - * @param offset The offset, from the start of the line - */ - public final int offsetToX(int line, int offset) - { - // don't use cached tokens - painter.currentLineTokens = null; - return _offsetToX(line,offset); - } - - /** - * Converts an offset in a line into an x co-ordinate. This is a - * fast version that should only be used if no changes were made - * to the text since the last repaint. - * @param line The line - * @param offset The offset, from the start of the line - */ - public int _offsetToX(int line, int offset) - { - TokenMarker tokenMarker = getTokenMarker(); - - /* Use painter's cached info for speed */ - FontMetrics fm = painter.getFontMetrics(); - - getLineText(line,lineSegment); - - int segmentOffset = lineSegment.offset; - int x = horizontalOffset; - - /* If syntax coloring is disabled, do simple translation */ - if(tokenMarker == null) - { - lineSegment.count = offset; - return x + Utilities.getTabbedTextWidth(lineSegment, - fm,x,painter,0); - } - /* If syntax coloring is enabled, we have to do this because - * tokens can vary in width */ - else - { - Token tokens; - if(painter.currentLineIndex == line - && painter.currentLineTokens != null) - tokens = painter.currentLineTokens; - else - { - painter.currentLineIndex = line; - tokens = painter.currentLineTokens - = tokenMarker.markTokens(lineSegment,line); - } - - //Toolkit toolkit = painter.getToolkit(); - Font defaultFont = painter.getFont(); - SyntaxStyle[] styles = painter.getStyles(); - - for(;;) - { - byte id = tokens.id; - if(id == Token.END) - { - return x; - } - - if(id == Token.NULL) - fm = painter.getFontMetrics(); - else - fm = styles[id].getFontMetrics(defaultFont, this); - - int length = tokens.length; - - if(offset + segmentOffset < lineSegment.offset + length) - { - lineSegment.count = offset - (lineSegment.offset - segmentOffset); - return x + Utilities.getTabbedTextWidth( - lineSegment,fm,x,painter,0); - } - else - { - lineSegment.count = length; - x += Utilities.getTabbedTextWidth( - lineSegment,fm,x,painter,0); - lineSegment.offset += length; - } - tokens = tokens.next; - } - } - } - - /** - * Converts an x co-ordinate to an offset within a line. - * @param line The line - * @param x The x co-ordinate - */ - public int xToOffset(int line, int x) - { - TokenMarker tokenMarker = getTokenMarker(); - - /* Use painter's cached info for speed */ - FontMetrics fm = painter.getFontMetrics(); - - getLineText(line,lineSegment); - - char[] segmentArray = lineSegment.array; - int segmentOffset = lineSegment.offset; - int segmentCount = lineSegment.count; - - int width = horizontalOffset; - - if(tokenMarker == null) - { - for(int i = 0; i < segmentCount; i++) - { - char c = segmentArray[i + segmentOffset]; - int charWidth; - if(c == '\t') - charWidth = (int)painter.nextTabStop(width,i) - - width; - else - charWidth = fm.charWidth(c); - - if(painter.isBlockCaretEnabled()) - { - if(x - charWidth <= width) - return i; - } - else - { - if(x - charWidth / 2 <= width) - return i; - } - - width += charWidth; - } - - return segmentCount; - } - else - { - Token tokens; - if(painter.currentLineIndex == line && painter - .currentLineTokens != null) - tokens = painter.currentLineTokens; - else - { - painter.currentLineIndex = line; - tokens = painter.currentLineTokens - = tokenMarker.markTokens(lineSegment,line); - } - - int offset = 0; - //Toolkit toolkit = painter.getToolkit(); - Font defaultFont = painter.getFont(); - SyntaxStyle[] styles = painter.getStyles(); - - for(;;) - { - byte id = tokens.id; - if(id == Token.END) - return offset; - - if(id == Token.NULL) - fm = painter.getFontMetrics(); - else - fm = styles[id].getFontMetrics(defaultFont, this); - - int length = tokens.length; - - for(int i = 0; i < length; i++) - { - char c = segmentArray[segmentOffset + offset + i]; - int charWidth; - if(c == '\t') - charWidth = (int)painter.nextTabStop(width,offset + i) - - width; - else - charWidth = fm.charWidth(c); - - if(painter.isBlockCaretEnabled()) - { - if(x - charWidth <= width) - return offset + i; - } - else - { - if(x - charWidth / 2 <= width) - return offset + i; - } - - width += charWidth; - } - - offset += length; - tokens = tokens.next; - } - } - } - - /** - * Converts a point to an offset, from the start of the text. - * @param x The x co-ordinate of the point - * @param y The y co-ordinate of the point - */ - public int xyToOffset(int x, int y) - { - int line = yToLine(y); - int start = getLineStartOffset(line); - return start + xToOffset(line,x); - } - - /** - * Returns the document this text area is editing. - */ - public final SyntaxDocument getDocument() - { - return document; - } - - /** - * Sets the document this text area is editing. - * @param document The document - */ - public void setDocument(SyntaxDocument document) { - if (this.document == document) - return; - if (this.document != null) - this.document.removeDocumentListener(documentHandler); - this.document = document; - - document.addDocumentListener(documentHandler); - - select(0, 0); - updateScrollBars(); - repaintEditor(); - } - - - /** - * Set document with a twist, includes the old caret - * and scroll positions, added for p5. [fry] - */ - public void setDocument(SyntaxDocument document, - int start, int stop, int scroll) { - if (this.document == document) - return; - if (this.document != null) - this.document.removeDocumentListener(documentHandler); - this.document = document; - - document.addDocumentListener(documentHandler); - - select(start, stop); - updateScrollBars(); - setScrollPosition(scroll); - repaintEditor(); - } - - - /** - * Returns the document's token marker. Equivalent to calling - * getDocument().getTokenMarker(). - */ - public final TokenMarker getTokenMarker() - { - return document.getTokenMarker(); - } - - /** - * Sets the document's token marker. Equivalent to caling - * getDocument().setTokenMarker(). - * @param tokenMarker The token marker - */ - public final void setTokenMarker(TokenMarker tokenMarker) - { - document.setTokenMarker(tokenMarker); - } - - /** - * Returns the length of the document. Equivalent to calling - * getDocument().getLength(). - */ - public final int getDocumentLength() - { - return document.getLength(); - } - - /** - * Returns the number of lines in the document. - */ - public final int getLineCount() - { - if (document != null) { - return document.getDefaultRootElement().getElementCount(); - } else { - return 0; - } - } - - /** - * Returns the line containing the specified offset. - * @param offset The offset - */ - public final int getLineOfOffset(int offset) - { - return document.getDefaultRootElement().getElementIndex(offset); - } - - /** - * Returns the start offset of the specified line. - * @param line The line - * @return The start offset of the specified line, or -1 if the line is - * invalid - */ - public int getLineStartOffset(int line) - { - Element lineElement = document.getDefaultRootElement() - .getElement(line); - if(lineElement == null) - return -1; - else - return lineElement.getStartOffset(); - } - - /** - * Returns the end offset of the specified line. - * @param line The line - * @return The end offset of the specified line, or -1 if the line is - * invalid. - */ - public int getLineStopOffset(int line) - { - Element lineElement = document.getDefaultRootElement() - .getElement(line); - if(lineElement == null) - return -1; - else - return lineElement.getEndOffset(); - } - - /** - * Returns the end offset of the specified line, but not past the end of the text - * @param line The line - * @return The end offset of the specified line, safe to use for a selection, or -1 if the line is - * invalid. - */ - public int getSafeLineStopOffset(int line) - { - return Math.min(getLineStopOffset(line),getDocumentLength()); - } - - /** - * Returns the length of the specified line. - * @param line The line - */ - public int getLineLength(int line) - { - Element lineElement = document.getDefaultRootElement() - .getElement(line); - if(lineElement == null) - return -1; - else - return lineElement.getEndOffset() - - lineElement.getStartOffset() - 1; - } - - /** - * Returns the entire text of this text area. - */ - public String getText() - { - try - { - return document.getText(0,document.getLength()); - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - return null; - } - } - - - /** - * Sets the entire text of this text area. - */ - public void setText(String text) - { - try { - document.beginCompoundEdit(); - document.remove(0,document.getLength()); - document.insertString(0,text,null); - - } catch (BadLocationException bl) { - bl.printStackTrace(); - - } finally { - document.endCompoundEdit(); - } - } - - - /** - * Returns the specified substring of the document. - * @param start The start offset - * @param len The length of the substring - * @return The substring, or null if the offsets are invalid - */ - public final String getText(int start, int len) - { - try - { - return document.getText(start,len); - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - return null; - } - } - - /** - * Copies the specified substring of the document into a segment. - * If the offsets are invalid, the segment will contain a null string. - * @param start The start offset - * @param len The length of the substring - * @param segment The segment - */ - public final void getText(int start, int len, Segment segment) - { - try - { - document.getText(start,len,segment); - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - segment.offset = segment.count = 0; - } - } - - /** - * Returns the text on the specified line. - * @param lineIndex The line - * @return The text, or null if the line is invalid - */ - public final String getLineText(int lineIndex) - { - int start = getLineStartOffset(lineIndex); - return getText(start,getLineStopOffset(lineIndex) - start - 1); - } - - /** - * Copies the text on the specified line into a segment. If the line - * is invalid, the segment will contain a null string. - * @param lineIndex The line - */ - public final void getLineText(int lineIndex, Segment segment) - { - int start = getLineStartOffset(lineIndex); - getText(start,getLineStopOffset(lineIndex) - start - 1,segment); - } - - /** - * Returns the selection start offset. - */ - public final int getSelectionStart() - { - return selectionStart; - } - - /** - * Returns the offset where the selection starts on the specified - * line. - */ - public int getSelectionStart(int line) - { - if(line == selectionStartLine) - return selectionStart; - else if(rectSelect) - { - Element map = document.getDefaultRootElement(); - int start = selectionStart - map.getElement(selectionStartLine) - .getStartOffset(); - - Element lineElement = map.getElement(line); - int lineStart = lineElement.getStartOffset(); - int lineEnd = lineElement.getEndOffset() - 1; - return Math.min(lineEnd,lineStart + start); - } - else - return getLineStartOffset(line); - } - - /** - * Returns the selection start line. - */ - public final int getSelectionStartLine() - { - return selectionStartLine; - } - - /** - * Sets the selection start. The new selection will be the new - * selection start and the old selection end. - * @param selectionStart The selection start - * @see #select(int,int) - */ - public final void setSelectionStart(int selectionStart) - { - select(selectionStart,selectionEnd); - } - - /** - * Returns the selection end offset. - */ - public final int getSelectionStop() - { - return selectionEnd; - } - - /** - * Returns the offset where the selection ends on the specified - * line. - */ - public int getSelectionStop(int line) - { - if(line == selectionEndLine) - return selectionEnd; - else if(rectSelect) - { - Element map = document.getDefaultRootElement(); - int end = selectionEnd - map.getElement(selectionEndLine) - .getStartOffset(); - - Element lineElement = map.getElement(line); - int lineStart = lineElement.getStartOffset(); - int lineEnd = lineElement.getEndOffset() - 1; - return Math.min(lineEnd,lineStart + end); - } - else - return getLineStopOffset(line) - 1; - } - - /** - * Returns the selection end line. - */ - public final int getSelectionStopLine() - { - return selectionEndLine; - } - - /** - * Sets the selection end. The new selection will be the old - * selection start and the bew selection end. - * @param selectionEnd The selection end - * @see #select(int,int) - */ - public final void setSelectionEnd(int selectionEnd) - { - select(selectionStart,selectionEnd); - } - - - public final boolean isSelectionActive() - { - return(selectionStart != selectionEnd); - } - - /** - * Returns the caret position. This will either be the selection - * start or the selection end, depending on which direction the - * selection was made in. - */ - public final int getCaretPosition() - { - return (biasLeft ? selectionStart : selectionEnd); - } - - /** - * Returns the caret line. - */ - public final int getCaretLine() - { - return (biasLeft ? selectionStartLine : selectionEndLine); - } - - /** - * Returns the mark position. This will be the opposite selection - * bound to the caret position. - * @see #getCaretPosition() - */ - public final int getMarkPosition() - { - return (biasLeft ? selectionEnd : selectionStart); - } - - /** - * Returns the mark line. - */ - public final int getMarkLine() - { - return (biasLeft ? selectionEndLine : selectionStartLine); - } - - /** - * Sets the caret position. The new selection will consist of the - * caret position only (hence no text will be selected) - * @param caret The caret position - * @see #select(int,int) - */ - public final void setCaretPosition(int caret) - { - select(caret,caret); - } - - /** - * Selects all text in the document. - */ - public final void selectAll() - { - select(0,getDocumentLength()); - } - - /** - * Moves the mark to the caret position. - */ - public final void selectNone() - { - select(getCaretPosition(),getCaretPosition()); - } - - /** - * Selects from the start offset to the end offset. This is the - * general selection method used by all other selecting methods. - * The caret position will be start if start < end, and end - * if end > start. - * @param start The start offset - * @param end The end offset - */ - public void select(int start, int end) - { - int newStart, newEnd; - boolean newBias; - if(start <= end) - { - newStart = start; - newEnd = end; - newBias = false; - } - else - { - newStart = end; - newEnd = start; - newBias = true; - } - - if (newEnd > getDocumentLength()) { - newEnd = getDocumentLength(); - } - - if(newStart < 0) - { - throw new IllegalArgumentException("Bounds out of" - + " range: " + newStart + "," + - newEnd + " [" + getDocumentLength() + "]"); - } - - // If the new position is the same as the old, we don't - // do all this crap, however we still do the stuff at - // the end (clearing magic position, scrolling) - if(newStart != selectionStart || newEnd != selectionEnd - || newBias != biasLeft) - { - int newStartLine = getLineOfOffset(newStart); - int newEndLine = getLineOfOffset(newEnd); - - if(painter.isBracketHighlightEnabled()) - { - if(bracketLine != -1) - painter.invalidateLine(bracketLine); - updateBracketHighlight(end); - if(bracketLine != -1) - painter.invalidateLine(bracketLine); - } - - painter.invalidateLineRange(selectionStartLine,selectionEndLine); - painter.invalidateLineRange(newStartLine,newEndLine); - - document.addUndoableEdit(new CaretUndo(selectionStart,selectionEnd)); - - selectionStart = newStart; - selectionEnd = newEnd; - selectionStartLine = newStartLine; - selectionEndLine = newEndLine; - biasLeft = newBias; - - if (newStart != newEnd) { - Clipboard unixclipboard = getToolkit().getSystemSelection(); - if (unixclipboard != null) { - String selection = getSelectedText(); - if (selection != null) { - unixclipboard.setContents(new StringSelection(selection), null); - } - } - } - - fireCaretEvent(); - } - - // When the user is typing, etc, we don't want the caret - // to blink - blink = true; - caretTimer.restart(); - - // Disable rectangle select if selection start = selection end - if(selectionStart == selectionEnd) - rectSelect = false; - - // Clear the `magic' caret position used by up/down - magicCaret = -1; - - scrollToCaret(); - - // notify the line number feller - if (editorLineStatus != null) { - editorLineStatus.set(selectionStartLine, selectionEndLine); - //System.out.println("why " + selectionStartLine + " " + selectionEndLine); - //System.out.println(getLineOfOffset(start) + " " + - // getLineOfOffset(end)); - } - } - - private boolean isWordCharacter( char ch, String noWordSep ) - { - return Character.isLetterOrDigit(ch) || ch=='_' || noWordSep.indexOf(ch) != -1; - } - - protected void setNewSelectionWord( int line, int offset ) - { - if (getLineLength(line) == 0) { - newSelectionStart = getLineStartOffset(line); - newSelectionEnd = newSelectionStart; - return; - } - - String noWordSep = (String)document.getProperty("noWordSep"); - if(noWordSep == null) - noWordSep = ""; - - String lineText = getLineText(line); - - int wordStart = 0; - int wordEnd = lineText.length(); - - char ch = lineText.charAt(Math.max(0,offset - 1)); - - // special case for whitespace (fry 0122, bug #348) - // this is really nasty.. turns out that double-clicking any non-letter - // or digit char gets lumped together.. sooo, this quickly gets messy, - // because really it needs to check whether the chars are of the same - // type.. so a double space or double - might be grouped together, - // but what about a +=1? do + and - get grouped but not the 1? blech, - // coming back to this later. it's not a difficult fix, just a - // time-consuming one to track down all the proper cases. - /* - if (ch == ' ') { - //System.out.println("yeehaa"); - - for(int i = offset - 1; i >= 0; i--) { - if (lineText.charAt(i) == ' ') { - wordStart = i; - } else { - break; - } - } - for(int i = offset; i < lineText.length(); i++) { - if (lineText.charAt(i) == ' ') { - wordEnd = i + 1; - } else { - break; - } - } - - } else { - */ - - // If the user clicked on a non-letter char, - // we select the surrounding non-letters - boolean selectNoLetter = !isWordCharacter(ch,noWordSep); - - for(int i = offset - 1; i >= 0; i--) { - ch = lineText.charAt(i); - if (selectNoLetter ^ !isWordCharacter(ch,noWordSep)) { - wordStart = i + 1; - break; - } - } - - for(int i = offset; i < lineText.length(); i++) { - ch = lineText.charAt(i); - if(selectNoLetter ^ !isWordCharacter(ch,noWordSep)) { - wordEnd = i; - break; - } - } - //} - int lineStart = getLineStartOffset(line); - - newSelectionStart = lineStart + wordStart; - newSelectionEnd = lineStart + wordEnd; - } - - - /** - * Returns the selected text, or null if no selection is active. - */ - public final String getSelectedText() - { - if(selectionStart == selectionEnd) - return null; - - if(rectSelect) - { - // Return each row of the selection on a new line - - Element map = document.getDefaultRootElement(); - - int start = selectionStart - map.getElement(selectionStartLine) - .getStartOffset(); - int end = selectionEnd - map.getElement(selectionEndLine) - .getStartOffset(); - - // Certain rectangles satisfy this condition... - if(end < start) - { - int tmp = end; - end = start; - start = tmp; - } - - StringBuffer buf = new StringBuffer(); - Segment seg = new Segment(); - - for(int i = selectionStartLine; i <= selectionEndLine; i++) - { - Element lineElement = map.getElement(i); - int lineStart = lineElement.getStartOffset(); - int lineEnd = lineElement.getEndOffset() - 1; - int lineLen = lineEnd - lineStart; - - lineStart = Math.min(lineStart + start,lineEnd); - lineLen = Math.min(end - start,lineEnd - lineStart); - - getText(lineStart,lineLen,seg); - buf.append(seg.array,seg.offset,seg.count); - - if(i != selectionEndLine) - buf.append('\n'); - } - - return buf.toString(); - } - else - { - return getText(selectionStart, - selectionEnd - selectionStart); - } - } - - /** - * Replaces the selection with the specified text. - * @param selectedText The replacement text for the selection - */ - public void setSelectedText(String selectedText) - { - if(!editable) - { - throw new InternalError("Text component" - + " read only"); - } - - document.beginCompoundEdit(); - - try - { - if(rectSelect) - { - Element map = document.getDefaultRootElement(); - - int start = selectionStart - map.getElement(selectionStartLine) - .getStartOffset(); - int end = selectionEnd - map.getElement(selectionEndLine) - .getStartOffset(); - - // Certain rectangles satisfy this condition... - if(end < start) - { - int tmp = end; - end = start; - start = tmp; - } - - int lastNewline = 0; - int currNewline = 0; - - for(int i = selectionStartLine; i <= selectionEndLine; i++) - { - Element lineElement = map.getElement(i); - int lineStart = lineElement.getStartOffset(); - int lineEnd = lineElement.getEndOffset() - 1; - int rectStart = Math.min(lineEnd,lineStart + start); - - document.remove(rectStart,Math.min(lineEnd - rectStart, - end - start)); - - if(selectedText == null) - continue; - - currNewline = selectedText.indexOf('\n',lastNewline); - if(currNewline == -1) - currNewline = selectedText.length(); - - document.insertString(rectStart,selectedText - .substring(lastNewline,currNewline),null); - - lastNewline = Math.min(selectedText.length(), - currNewline + 1); - } - - if(selectedText != null && - currNewline != selectedText.length()) - { - int offset = map.getElement(selectionEndLine) - .getEndOffset() - 1; - document.insertString(offset,"\n",null); - document.insertString(offset + 1,selectedText - .substring(currNewline + 1),null); - } - } - else - { - document.remove(selectionStart, - selectionEnd - selectionStart); - if(selectedText != null) - { - document.insertString(selectionStart, - selectedText,null); - } - } - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - throw new InternalError("Cannot replace" - + " selection"); - } - // No matter what happends... stops us from leaving document - // in a bad state - finally - { - document.endCompoundEdit(); - } - - setCaretPosition(selectionEnd); - } - - /** - * Returns true if this text area is editable, false otherwise. - */ - public final boolean isEditable() - { - return editable; - } - - /** - * Sets if this component is editable. - * @param editable True if this text area should be editable, - * false otherwise - */ - public final void setEditable(boolean editable) - { - this.editable = editable; - } - - /** - * Returns the right click popup menu. - */ - public final JPopupMenu getRightClickPopup() - { - return popup; - } - - /** - * Sets the right click popup menu. - * @param popup The popup - */ - //public final void setRightClickPopup(EditPopupMenu popup) - public final void setRightClickPopup(JPopupMenu popup) - { - this.popup = popup; - } - - - /** - * Returns the `magic' caret position. This can be used to preserve - * the column position when moving up and down lines. - */ - public final int getMagicCaretPosition() - { - return magicCaret; - } - - /** - * Sets the `magic' caret position. This can be used to preserve - * the column position when moving up and down lines. - * @param magicCaret The magic caret position - */ - public final void setMagicCaretPosition(int magicCaret) - { - this.magicCaret = magicCaret; - } - - /** - * Similar to setSelectedText(), but overstrikes the - * appropriate number of characters if overwrite mode is enabled. - * @param str The string - * @see #setSelectedText(String) - * @see #isOverwriteEnabled() - */ - public void overwriteSetSelectedText(String str) - { - // Don't overstrike if there is a selection - if(!overwrite || selectionStart != selectionEnd) - { - setSelectedText(str); - return; - } - - // Don't overstrike if we're on the end of - // the line - int caret = getCaretPosition(); - int caretLineEnd = getLineStopOffset(getCaretLine()); - if(caretLineEnd - caret <= str.length()) - { - setSelectedText(str); - return; - } - - document.beginCompoundEdit(); - - try - { - document.remove(caret,str.length()); - document.insertString(caret,str,null); - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - } - finally - { - document.endCompoundEdit(); - } - } - - /** - * Returns true if overwrite mode is enabled, false otherwise. - */ - public final boolean isOverwriteEnabled() - { - return overwrite; - } - - /** - * Sets if overwrite mode should be enabled. - * @param overwrite True if overwrite mode should be enabled, - * false otherwise. - */ - public final void setOverwriteEnabled(boolean overwrite) - { - this.overwrite = overwrite; - painter.invalidateSelectedLines(); - } - - /** - * Returns true if the selection is rectangular, false otherwise. - */ - public final boolean isSelectionRectangular() - { - return rectSelect; - } - - /** - * Sets if the selection should be rectangular. - * @param rectSelect True if the selection should be rectangular, - * false otherwise. - */ - public final void setSelectionRectangular(boolean rectSelect) - { - this.rectSelect = rectSelect; - painter.invalidateSelectedLines(); - } - - /** - * Returns the position of the highlighted bracket (the bracket - * matching the one before the caret) - */ - public final int getBracketPosition() - { - return bracketPosition; - } - - /** - * Returns the line of the highlighted bracket (the bracket - * matching the one before the caret) - */ - public final int getBracketLine() - { - return bracketLine; - } - - /** - * Adds a caret change listener to this text area. - * @param listener The listener - */ - public final void addCaretListener(CaretListener listener) - { - eventListenerList.add(CaretListener.class,listener); - } - - /** - * Removes a caret change listener from this text area. - * @param listener The listener - */ - public final void removeCaretListener(CaretListener listener) - { - eventListenerList.remove(CaretListener.class,listener); - } - - /** - * Deletes the selected text from the text area and places it - * into the clipboard. - */ - public void cut() - { - if(editable) - { - copy(); - setSelectedText(""); - } - } - - /** - * Places the selected text into the clipboard. - */ - public void copy() - { - if(selectionStart != selectionEnd) - { - Clipboard clipboard = getToolkit().getSystemClipboard(); - - String selection = getSelectedText(); - - int repeatCount = inputHandler.getRepeatCount(); - StringBuffer buf = new StringBuffer(); - for(int i = 0; i < repeatCount; i++) - buf.append(selection); - - Transferable t = new StringSelection(buf.toString()); - clipboard.setContents(t, null); - - Clipboard unixclipboard = getToolkit().getSystemSelection(); - if (unixclipboard != null) unixclipboard.setContents(t, null); - } - } - - /** - * Inserts the clipboard contents into the text. - */ - public void paste() { - if (editable) { - Clipboard clipboard = getToolkit().getSystemClipboard(); - try { - // The MacOS MRJ doesn't convert \r to \n, so do it here - String selection = ((String)clipboard.getContents(this).getTransferData(DataFlavor.stringFlavor)).replace('\r','\n'); - - // particularly on macosx when pasting from safari, - // replace unicode x00A0 (non-breaking space) - // with just a plain space. [fry 030929] - selection = selection.replace('\u00A0', ' '); - - int repeatCount = inputHandler.getRepeatCount(); - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < repeatCount; i++) - buf.append(selection); - selection = buf.toString(); - setSelectedText(selection); - - } catch(Exception e) { - getToolkit().beep(); - System.err.println("Clipboard does not contain a string"); - } - } - } - - /** - * Called by the AWT when this component is removed from it's parent. - * This stops clears the currently focused component. - */ - public void removeNotify() - { - super.removeNotify(); - if(focusedComponent == this) - focusedComponent = null; - } - - /** - * Forwards key events directly to the input handler. - * This is slightly faster than using a KeyListener - * because some Swing overhead is avoided. - */ - public EditorListener editorListener; - - /** - * The component that tracks the current line number. - */ - public EditorLineStatus editorLineStatus; - - - public void processKeyEvent(KeyEvent evt) { - // this had to be added in Processing 007X, because the menu key - // events weren't making it up to the frame. - super.processKeyEvent(evt); - - //System.out.println("jedittextarea: " + evt); - //System.out.println(); - if (inputHandler == null) return; - - switch(evt.getID()) { - case KeyEvent.KEY_TYPED: - if ((editorListener == null) || !editorListener.keyTyped(evt)) { - inputHandler.keyTyped(evt); - } - break; - case KeyEvent.KEY_PRESSED: - if ((editorListener == null) || !editorListener.keyPressed(evt)) { - inputHandler.keyPressed(evt); - } - break; - case KeyEvent.KEY_RELEASED: - inputHandler.keyReleased(evt); - break; - } - } - - // protected members - protected static String LEFT = "left"; - protected static String CENTER = "center"; - protected static String RIGHT = "right"; - protected static String BOTTOM = "bottom"; - - protected static JEditTextArea focusedComponent; - protected static Timer caretTimer; - - protected TextAreaPainter painter; - protected TextAreaLineNumbers editorLineNumbers; - - //protected EditPopupMenu popup; - protected JPopupMenu popup; - - protected EventListenerList eventListenerList; - protected MutableCaretEvent caretEvent; - - protected boolean caretBlinks; - protected boolean caretVisible; - protected boolean blink; - - protected boolean editable; - - protected int firstLine; - protected int visibleLines; - protected int electricScroll; - - protected int horizontalOffset; - - protected JScrollBar vertical; - protected JScrollBar horizontal; - protected boolean scrollBarsInitialized; - - protected InputHandler inputHandler; - protected SyntaxDocument document; - protected DocumentHandler documentHandler; - - protected Segment lineSegment; - - protected int selectionStart; - protected int selectionStartLine; - protected int selectionEnd; - protected int selectionEndLine; - protected boolean biasLeft; - - protected int newSelectionStart; // hack to get around lack of multiple returns in Java - protected int newSelectionEnd; - - protected boolean selectWord; - protected boolean selectLine; - protected int selectionAncorStart; - protected int selectionAncorEnd; - - protected int bracketPosition; - protected int bracketLine; - - protected int magicCaret; - protected boolean overwrite; - protected boolean rectSelect; - - - protected void fireCaretEvent() - { - Object[] listeners = eventListenerList.getListenerList(); - for(int i = listeners.length - 2; i >= 0; i--) - { - if(listeners[i] == CaretListener.class) - { - ((CaretListener)listeners[i+1]).caretUpdate(caretEvent); - } - } - } - - protected void updateBracketHighlight(int newCaretPosition) - { - if(newCaretPosition == 0) - { - bracketPosition = bracketLine = -1; - return; - } - - try - { - int offset = TextUtilities.findMatchingBracket( - document,newCaretPosition - 1); - if(offset != -1) - { - bracketLine = getLineOfOffset(offset); - bracketPosition = offset - getLineStartOffset(bracketLine); - return; - } - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - } - - bracketLine = bracketPosition = -1; - } - - protected void documentChanged(DocumentEvent evt) - { - DocumentEvent.ElementChange ch = - evt.getChange(document.getDefaultRootElement()); - - int count; - if(ch == null) - count = 0; - else - count = ch.getChildrenAdded().length - - ch.getChildrenRemoved().length; - - int line = getLineOfOffset(evt.getOffset()); - if(count == 0) - { - painter.invalidateLine(line); - } - // do magic stuff - else if(line < firstLine) - { - setFirstLine(firstLine + count); - } - // end of magic stuff - else - { - painter.invalidateLineRange(line,firstLine + visibleLines); - updateScrollBars(); - } - } - - class ScrollLayout implements LayoutManager - { - //final int LEFT_EXTRA = 5; - - public void addLayoutComponent(String name, Component comp) - { - if(name.equals(LEFT)) - left = comp; - else if(name.equals(CENTER)) - center = comp; - else if(name.equals(RIGHT)) - right = comp; - else if(name.equals(BOTTOM)) - bottom = comp; - else if(name.equals(LEFT_OF_SCROLLBAR)) - leftOfScrollBar.addElement(comp); - } - - public void removeLayoutComponent(Component comp) - { - if(left == comp) - left = null; - if(center == comp) - center = null; - if(right == comp) - right = null; - if(bottom == comp) - bottom = null; - else - leftOfScrollBar.removeElement(comp); - } - - public Dimension preferredLayoutSize(Container parent) - { - Dimension dim = new Dimension(); - Insets insets = getInsets(); - dim.width = insets.left + insets.right; - dim.height = insets.top + insets.bottom; - - Dimension centerPref = center.getPreferredSize(); - dim.width += centerPref.width; - dim.height += centerPref.height; - Dimension leftPref = left.getPreferredSize(); - dim.width += leftPref.width; - Dimension rightPref = right.getPreferredSize(); - dim.width += rightPref.width; - Dimension bottomPref = bottom.getPreferredSize(); - dim.height += bottomPref.height; - - return dim; - } - - public Dimension minimumLayoutSize(Container parent) - { - Dimension dim = new Dimension(); - Insets insets = getInsets(); - dim.width = insets.left + insets.right; - dim.height = insets.top + insets.bottom; - - Dimension centerPref = center.getMinimumSize(); - dim.width += centerPref.width; - dim.height += centerPref.height; - Dimension leftPref = left.getMinimumSize(); - dim.width += leftPref.width; - Dimension rightPref = right.getMinimumSize(); - dim.width += rightPref.width; - Dimension bottomPref = bottom.getMinimumSize(); - dim.height += bottomPref.height; - - dim.height += 5; - - return dim; - } - - public void layoutContainer(Container parent) - { - Dimension size = parent.getSize(); - Insets insets = parent.getInsets(); - int itop = insets.top; - int ileft = insets.left; - int ibottom = insets.bottom; - int iright = insets.right; - - int leftWidth = left.getSize().width; - int rightWidth = right.getPreferredSize().width; - int bottomHeight = bottom.getPreferredSize().height; - int centerWidth = size.width - leftWidth - rightWidth - ileft - iright; - int centerHeight = size.height - bottomHeight - itop - ibottom; - - left.setBounds(ileft, - itop, - leftWidth, - centerHeight); - - ileft += leftWidth; - - center.setBounds(ileft, // + LEFT_EXTRA, - itop, - centerWidth, // - LEFT_EXTRA, - centerHeight); - - right.setBounds(ileft + centerWidth, - itop, - rightWidth, - centerHeight); - - // Lay out all status components, in order - Enumeration status = leftOfScrollBar.elements(); - while (status.hasMoreElements()) { - Component comp = (Component)status.nextElement(); - Dimension dim = comp.getPreferredSize(); - comp.setBounds(ileft, - itop + centerHeight, - dim.width, - bottomHeight); - ileft += dim.width; - } - - bottom.setBounds(ileft, - itop + centerHeight, - size.width - rightWidth - ileft - iright, - bottomHeight); - } - - // private members - private Component left; - private Component center; - private Component right; - private Component bottom; - private Vector leftOfScrollBar = new Vector(); - } - - static class CaretBlinker implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if(focusedComponent != null - && focusedComponent.hasFocus()) - focusedComponent.blinkCaret(); - } - }); - } - } - - class MutableCaretEvent extends CaretEvent - { - MutableCaretEvent() - { - super(JEditTextArea.this); - } - - public int getDot() - { - return getCaretPosition(); - } - - public int getMark() - { - return getMarkPosition(); - } - } - -/* -#ifdef JDK14 - class WheelHandler implements MouseWheelListener { - - public void mouseWheelMoved(MouseWheelEvent e) { - if (!scrollBarsInitialized) return; - - int amt = e.getWheelRotation(); - //System.out.println(amt); - vertical.setValue(vertical.getValue() + amt * wheelMultiplier); - } - } -#endif -*/ - - class AdjustHandler implements AdjustmentListener - { - public void adjustmentValueChanged(final AdjustmentEvent evt) - { - if(!scrollBarsInitialized) - return; - - // If this is not done, mousePressed events accumilate - // and the result is that scrolling doesn't stop after - // the mouse is released - SwingUtilities.invokeLater(new Runnable() { - public void run() - { - if(evt.getAdjustable() == vertical) - setFirstLine(vertical.getValue()); - else - setHorizontalOffset(-horizontal.getValue()); - } - }); - } - } - - class ComponentHandler extends ComponentAdapter - { - public void componentResized(ComponentEvent evt) - { - recalculateVisibleLines(); - scrollBarsInitialized = true; - } - } - - class DocumentHandler implements DocumentListener - { - public void insertUpdate(DocumentEvent evt) - { - documentChanged(evt); - - int offset = evt.getOffset(); - int length = evt.getLength(); - - int newStart; - int newEnd; - - if (selectionStart > offset || - (selectionStart == selectionEnd && selectionStart == offset)) - newStart = selectionStart + length; - else - newStart = selectionStart; - - if(selectionEnd >= offset) - newEnd = selectionEnd + length; - else - newEnd = selectionEnd; - - select(newStart,newEnd); - } - - public void removeUpdate(DocumentEvent evt) - { - documentChanged(evt); - - int offset = evt.getOffset(); - int length = evt.getLength(); - - int newStart; - int newEnd; - - if(selectionStart > offset) - { - if(selectionStart > offset + length) - newStart = selectionStart - length; - else - newStart = offset; - } - else - newStart = selectionStart; - - if(selectionEnd > offset) - { - if(selectionEnd > offset + length) - newEnd = selectionEnd - length; - else - newEnd = offset; - } - else - newEnd = selectionEnd; - - select(newStart,newEnd); - } - - public void changedUpdate(DocumentEvent evt) - { - } - } - - class DragHandler implements MouseMotionListener - { - public void mouseDragged(MouseEvent evt) - { - if (popup != null && popup.isVisible()) return; - - if ( !selectWord && !selectLine ) { - setSelectionRectangular((evt.getModifiers() - & InputEvent.CTRL_MASK) != 0); - select(getMarkPosition(),xyToOffset(evt.getX(),evt.getY())); - } else { - int line = yToLine(evt.getY()); - if ( selectWord ) { - setNewSelectionWord( line, xToOffset(line,evt.getX()) ); - } else { - newSelectionStart = getLineStartOffset(line); - newSelectionEnd = getSafeLineStopOffset(line); - } - if ( newSelectionStart < selectionAncorStart ) { - select(newSelectionStart,selectionAncorEnd); - } else if ( newSelectionEnd > selectionAncorEnd ) { - select(selectionAncorStart,newSelectionEnd); - } else { - select(newSelectionStart,newSelectionEnd); - } - } - } - - final Cursor normalCursor = new Cursor(Cursor.DEFAULT_CURSOR); - final Cursor handCursor = new Cursor(Cursor.HAND_CURSOR); - - public void mouseMoved(MouseEvent evt) { - int line = yToLine(evt.getY()); - int offset = xToOffset(line, evt.getX()); - boolean wantHandCursor = checkClickedURL(getLineText(line), offset) != null; - JComponent src = (JComponent) evt.getSource(); - if (wantHandCursor) - src.setCursor(handCursor); - else - src.setCursor(normalCursor); - } - } - - class FocusHandler implements FocusListener - { - public void focusGained(FocusEvent evt) - { - //System.out.println("JEditTextArea: focusGained"); - setCaretVisible(true); - focusedComponent = JEditTextArea.this; - } - - public void focusLost(FocusEvent evt) - { - //System.out.println("JEditTextArea: focusLost"); - setCaretVisible(false); - focusedComponent = null; - } - } - - public String checkClickedURL(String line, int offset) { - String[] parse = SyntaxUtilities.parseCommentUrls(line); - if (parse==null) - return null; - int start = parse[0].length(); - int stop = start + parse[1].length(); - if (offsetstop) - return null; - return parse[1]; - } - - class MouseHandler extends MouseAdapter - { - public void mousePressed(MouseEvent evt) - { - requestFocus(); - - // Focus events not fired sometimes? - setCaretVisible(true); - focusedComponent = JEditTextArea.this; - - // isPopupTrigger wasn't working for danh on windows - boolean trigger = (evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0; - // but it's required for macosx, since control-click does - // the same thing as a right-mouse click - if (!trigger && evt.isPopupTrigger()) trigger = true; - - if (trigger && (popup != null)) { - popup.show(painter,evt.getX(),evt.getY()); - return; - } - - // on Linux, middle button pastes selected text - if ((evt.getModifiers() & InputEvent.BUTTON2_MASK) != 0) { - Clipboard unixclipboard = getToolkit().getSystemSelection(); - if (unixclipboard != null) { - Transferable t = unixclipboard.getContents(null); - if (t != null && t.isDataFlavorSupported(DataFlavor.stringFlavor)) { - try { - String s = (String)t.getTransferData(DataFlavor.stringFlavor); - s = s.replace('\u00A0', ' '); - if (editable) setSelectedText(s); - } catch (Exception e) { - System.err.println(e); - e.printStackTrace(); - } - } - return; - } - } - - int line = yToLine(evt.getY()); - int offset = xToOffset(line,evt.getX()); - int dot = getLineStartOffset(line) + offset; - - selectLine = false; - selectWord = false; - - switch(evt.getClickCount()) { - - case 1: - doSingleClick(evt,line,offset,dot); - break; - - case 2: - // It uses the bracket matching stuff, so - // it can throw a BLE - try { - doDoubleClick(evt,line,offset,dot); - } catch(BadLocationException bl) { - bl.printStackTrace(); - } - break; - - case 3: - doTripleClick(evt,line,offset,dot); - break; - } - } - - - private void doSingleClick(MouseEvent evt, int line, - int offset, int dot) { - // Check for click on urls - String clickedURL = checkClickedURL(getLineText(line), offset); - if (clickedURL != null) { - Base.openURL(clickedURL); - return; - } - - if ((evt.getModifiers() & InputEvent.SHIFT_MASK) != 0) { - rectSelect = (evt.getModifiers() & InputEvent.CTRL_MASK) != 0; - select(getMarkPosition(),dot); - } else { - setCaretPosition(dot); - } - } - - - private void doDoubleClick(MouseEvent evt, int line, - int offset, int dot) throws BadLocationException - { - // Ignore empty lines - if (getLineLength(line) == 0) - return; - - try { - int bracket = TextUtilities.findMatchingBracket(document, - Math.max(0,dot - 1)); - if (bracket != -1) { - int mark = getMarkPosition(); - // Hack - if (bracket > mark) { - bracket++; - mark--; - } - select(mark,bracket); - return; - } - } catch(BadLocationException bl) { - bl.printStackTrace(); - } - - setNewSelectionWord( line, offset ); - select(newSelectionStart,newSelectionEnd); - selectWord = true; - selectionAncorStart = selectionStart; - selectionAncorEnd = selectionEnd; - - /* - String lineText = getLineText(line); - String noWordSep = (String)document.getProperty("noWordSep"); - int wordStart = TextUtilities.findWordStart(lineText,offset,noWordSep); - int wordEnd = TextUtilities.findWordEnd(lineText,offset,noWordSep); - - int lineStart = getLineStartOffset(line); - select(lineStart + wordStart,lineStart + wordEnd); - */ - } - - private void doTripleClick(MouseEvent evt, int line, - int offset, int dot) - { - selectLine = true; - select(getLineStartOffset(line),getSafeLineStopOffset(line)); - selectionAncorStart = selectionStart; - selectionAncorEnd = selectionEnd; - } - } - - class CaretUndo extends AbstractUndoableEdit - { - private int start; - private int end; - - CaretUndo(int start, int end) - { - this.start = start; - this.end = end; - } - - public boolean isSignificant() - { - return false; - } - - public String getPresentationName() - { - return "caret move"; - } - - public void undo() throws CannotUndoException - { - super.undo(); - - select(start,end); - } - - public void redo() throws CannotRedoException - { - super.redo(); - - select(start,end); - } - - public boolean addEdit(UndoableEdit edit) - { - if(edit instanceof CaretUndo) - { - CaretUndo cedit = (CaretUndo)edit; - start = cedit.start; - end = cedit.end; - cedit.die(); - - return true; - } - else - return false; - } - } - - static - { - caretTimer = new Timer(500,new CaretBlinker()); - caretTimer.setInitialDelay(500); - caretTimer.start(); - } - - public void setDisplayLineNumbers(boolean displayLineNumbers) { - editorLineNumbers.setDisplayLineNumbers(displayLineNumbers); - } -} diff --git a/app/src/processing/app/syntax/KeywordMap.java b/app/src/processing/app/syntax/KeywordMap.java deleted file mode 100644 index 27d225f8c0d..00000000000 --- a/app/src/processing/app/syntax/KeywordMap.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * KeywordMap.java - Fast keyword->id map - * Copyright (C) 1998, 1999 Slava Pestov - * Copyright (C) 1999 Mike Dillon - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import javax.swing.text.Segment; - -/** - * A KeywordMap is similar to a hashtable in that it maps keys - * to values. However, the `keys' are Swing segments. This allows lookups of - * text substrings without the overhead of creating a new string object. - *

- * This class is used by CTokenMarker to map keywords to ids. - * - * @author Slava Pestov, Mike Dillon - */ -public class KeywordMap -{ - /** - * Creates a new KeywordMap. - * @param ignoreCase True if keys are case insensitive - */ - public KeywordMap(boolean ignoreCase) - { - this(ignoreCase, 52); - this.ignoreCase = ignoreCase; - } - - /** - * Creates a new KeywordMap. - * @param ignoreCase True if the keys are case insensitive - * @param mapLength The number of `buckets' to create. - * A value of 52 will give good performance for most maps. - */ - public KeywordMap(boolean ignoreCase, int mapLength) - { - this.mapLength = mapLength; - this.ignoreCase = ignoreCase; - map = new Keyword[mapLength]; - } - - /** - * Looks up a key. - * @param text The text segment - * @param offset The offset of the substring within the text segment - * @param length The length of the substring - */ - public byte lookup(Segment text, int offset, int length) - { - if(length == 0) - return Token.NULL; - Keyword k = map[getSegmentMapKey(text, offset, length)]; - while(k != null) - { - if(length != k.keyword.length) - { - k = k.next; - continue; - } - if(SyntaxUtilities.regionMatches(ignoreCase,text,offset, - k.keyword)) - return k.id; - k = k.next; - } - return Token.NULL; - } - - /** - * Adds a key-value mapping. - * @param keyword The key - * @param id The value - */ - public void add(String keyword, byte id) - { - int key = getStringMapKey(keyword); - map[key] = new Keyword(keyword.toCharArray(),id,map[key]); - } - - /** - * Returns true if the keyword map is set to be case insensitive, - * false otherwise. - */ - public boolean getIgnoreCase() - { - return ignoreCase; - } - - /** - * Sets if the keyword map should be case insensitive. - * @param ignoreCase True if the keyword map should be case - * insensitive, false otherwise - */ - public void setIgnoreCase(boolean ignoreCase) - { - this.ignoreCase = ignoreCase; - } - - // protected members - protected int mapLength; - - protected int getStringMapKey(String s) - { - return (Character.toUpperCase(s.charAt(0)) + - Character.toUpperCase(s.charAt(s.length()-1))) - % mapLength; - } - - protected int getSegmentMapKey(Segment s, int off, int len) - { - return (Character.toUpperCase(s.array[off]) + - Character.toUpperCase(s.array[off + len - 1])) - % mapLength; - } - - // private members - class Keyword - { - public Keyword(char[] keyword, byte id, Keyword next) - { - this.keyword = keyword; - this.id = id; - this.next = next; - } - - public char[] keyword; - public byte id; - public Keyword next; - } - - private Keyword[] map; - private boolean ignoreCase; -} diff --git a/app/src/processing/app/syntax/PdeKeywords.java b/app/src/processing/app/syntax/PdeKeywords.java index b221796a5f1..3b5a575ce83 100644 --- a/app/src/processing/app/syntax/PdeKeywords.java +++ b/app/src/processing/app/syntax/PdeKeywords.java @@ -24,112 +24,158 @@ package processing.app.syntax; -import processing.app.*; +import cc.arduino.contributions.libraries.ContributedLibrary; +import org.fife.ui.rsyntaxtextarea.TokenMap; +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 java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; -import cc.arduino.contributions.libraries.ContributedLibrary; +public class PdeKeywords { -public class PdeKeywords extends CTokenMarker { + private static final Map KNOWN_TOKEN_TYPES = new HashMap(); + private static final Pattern ALPHA = Pattern.compile("\\w"); + + static { + KNOWN_TOKEN_TYPES.put("RESERVED_WORD", TokenTypes.RESERVED_WORD); + KNOWN_TOKEN_TYPES.put("RESERVED_WORD_2", TokenTypes.RESERVED_WORD_2); + KNOWN_TOKEN_TYPES.put("VARIABLE", TokenTypes.VARIABLE); + KNOWN_TOKEN_TYPES.put("OPERATOR", TokenTypes.OPERATOR); + KNOWN_TOKEN_TYPES.put("DATA_TYPE", TokenTypes.DATA_TYPE); + KNOWN_TOKEN_TYPES.put("LITERAL_BOOLEAN", TokenTypes.LITERAL_BOOLEAN); + KNOWN_TOKEN_TYPES.put("LITERAL_CHAR", TokenTypes.LITERAL_CHAR); + } // lookup table for the TokenMarker subclass, handles coloring - static KeywordMap keywordColoring; + private final TokenMap keywordTokenType; + private final Map keywordOldToken; + private final Map keywordTokenTypeAsString; // lookup table that maps keywords to their html reference pages - static Hashtable keywordToReference; - + private final Map keywordToReference; public PdeKeywords() { - super(false, getKeywords()); + this.keywordTokenType = new TokenMap(); + this.keywordOldToken = new HashMap(); + this.keywordTokenTypeAsString = new HashMap(); + this.keywordToReference = new HashMap(); } - /** * Handles loading of keywords file. - *

+ *

* Uses getKeywords() method because that's part of the * TokenMarker classes. - *

+ *

* It is recommended that a # sign be used for comments * inside keywords.txt. */ - static public KeywordMap getKeywords() { - if (keywordColoring == null) { - try { - keywordColoring = new KeywordMap(false); - keywordToReference = new Hashtable(); - getKeywords(new File(BaseNoGui.getContentFile("lib"), "keywords.txt")); - for (ContributedLibrary lib : Base.getLibraries()) { - File keywords = new File(lib.getInstalledFolder(), "keywords.txt"); - if (keywords.exists()) getKeywords(keywords); + public void reload() { + try { + parseKeywordsTxt(new File(BaseNoGui.getContentFile("lib"), "keywords.txt")); + for (ContributedLibrary lib : Base.getLibraries()) { + File keywords = new File(lib.getInstalledFolder(), "keywords.txt"); + if (keywords.exists()) { + parseKeywordsTxt(keywords); } - } catch (Exception e) { - Base.showError("Problem loading keywords", - "Could not load keywords.txt,\n" + - "please re-install Arduino.", e); - System.exit(1); } + } catch (Exception e) { + Base.showError("Problem loading keywords", "Could not load keywords.txt,\nplease re-install Arduino.", e); + System.exit(1); } - return keywordColoring; } - - static private void getKeywords(File input) throws IOException { + + private void parseKeywordsTxt(File input) throws Exception { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(new FileInputStream(input))); - String line = null; + String line; while ((line = reader.readLine()) != null) { //System.out.println("line is " + line); // in case there's any garbage on the line - //if (line.trim().length() == 0) continue; + line = line.trim(); + if (line.length() == 0 || line.startsWith("#")) { + continue; + } String pieces[] = PApplet.split(line, '\t'); + + String keyword = pieces[0].trim(); + if (pieces.length >= 2) { - //int tab = line.indexOf('\t'); - // any line with no tab is ignored - // meaning that a comment is any line without a tab - //if (tab == -1) continue; - - String keyword = pieces[0].trim(); - //String keyword = line.substring(0, tab).trim(); - //String second = line.substring(tab + 1); - //tab = second.indexOf('\t'); - //String coloring = second.substring(0, tab).trim(); - //String htmlFilename = second.substring(tab + 1).trim(); - String coloring = pieces[1].trim(); - - if (coloring.length() > 0 && Character.isDigit(coloring.charAt(coloring.length() - 1))) { - // text will be KEYWORD or LITERAL - boolean isKey = (coloring.charAt(0) == 'K'); - // KEYWORD1 -> 0, KEYWORD2 -> 1, etc - int num = coloring.charAt(coloring.length() - 1) - '1'; - byte id = (byte) - ((isKey ? Token.KEYWORD1 : Token.LITERAL1) + num); - //System.out.println("got " + (isKey ? "keyword" : "literal") + - // (num+1) + " for " + keyword); - keywordColoring.add(keyword, id); - } - if (pieces.length >= 3) { - String htmlFilename = pieces[2].trim(); - if (htmlFilename.length() > 0) { - keywordToReference.put(keyword, htmlFilename); - } - } + keywordOldToken.put(keyword, pieces[1]); + } + + if (pieces.length >= 3) { + parseHTMLReferenceFileName(pieces[2], keyword); + } + if (pieces.length >= 4) { + parseRSyntaxTextAreaTokenType(pieces[3], keyword); } } + + fillMissingTokenType(); } finally { if (reader != null) { reader.close(); } } + + } + + private void fillMissingTokenType() { + for (Map.Entry oldTokenEntry : keywordOldToken.entrySet()) { + String keyword = oldTokenEntry.getKey(); + if (!keywordTokenTypeAsString.containsKey(keyword)) { + if ("KEYWORD1".equals(oldTokenEntry.getValue())) { + parseRSyntaxTextAreaTokenType("DATA_TYPE", keyword); + } else { + parseRSyntaxTextAreaTokenType("FUNCTION", keyword); + } + } + } + } + + private void parseRSyntaxTextAreaTokenType(String tokenTypeAsString, String keyword) { + if (!ALPHA.matcher(keyword).find()) { + return; + } + + if (KNOWN_TOKEN_TYPES.containsKey(tokenTypeAsString)) { + keywordTokenType.put(keyword, KNOWN_TOKEN_TYPES.get(tokenTypeAsString)); + keywordTokenTypeAsString.put(keyword, tokenTypeAsString); + } else { + keywordTokenType.put(keyword, TokenTypes.FUNCTION); + keywordTokenTypeAsString.put(keyword, "FUNCTION"); + } + } + + private void parseHTMLReferenceFileName(String piece, String keyword) { + String htmlFilename = piece.trim(); + if (htmlFilename.length() > 0) { + keywordToReference.put(keyword, htmlFilename); + } + } + + public String getReference(String keyword) { + return keywordToReference.get(keyword); } + public String getTokenTypeAsString(String keyword) { + return keywordTokenTypeAsString.get(keyword); + } - static public String getReference(String keyword) { - return (String) keywordToReference.get(keyword); + public int getTokenType(char[] array, int start, int end) { + return keywordTokenType.get(array, start, end); } } diff --git a/app/src/processing/app/syntax/PdeTextAreaDefaults.java b/app/src/processing/app/syntax/PdeTextAreaDefaults.java deleted file mode 100644 index 391416f5ca1..00000000000 --- a/app/src/processing/app/syntax/PdeTextAreaDefaults.java +++ /dev/null @@ -1,211 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - PdeTextAreaDefaults - grabs font/color settings for the editor - Part of the Processing project - http://processing.org - - Copyright (c) 2004-06 Ben Fry and Casey Reas - Copyright (c) 2001-03 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.syntax; - -import processing.app.*; -import processing.app.helpers.OSUtils; - - -public class PdeTextAreaDefaults extends TextAreaDefaults { - - public PdeTextAreaDefaults() { - - inputHandler = new DefaultInputHandler(); - //inputHandler.addDefaultKeyBindings(); // 0122 - - // use option on mac for text edit controls that are ctrl on windows/linux - String mod = OSUtils.isMacOS() ? "A" : "C"; - - // right now, ctrl-up/down is select up/down, but mod should be - // used instead, because the mac expects it to be option(alt) - - inputHandler.addKeyBinding("BACK_SPACE", InputHandler.BACKSPACE); - // for 0122, shift-backspace is delete, for 0176, it's now a preference, - // to prevent holy warriors from attacking me for it. - if (PreferencesData.getBoolean("editor.keys.shift_backspace_is_delete")) { - inputHandler.addKeyBinding("S+BACK_SPACE", InputHandler.DELETE); - } else { - inputHandler.addKeyBinding("S+BACK_SPACE", InputHandler.BACKSPACE); - } - - inputHandler.addKeyBinding("DELETE", InputHandler.DELETE); - inputHandler.addKeyBinding("S+DELETE", InputHandler.DELETE); - - // the following two were changing for 0122 for better mac/pc compatability - inputHandler.addKeyBinding(mod+"+BACK_SPACE", InputHandler.BACKSPACE_WORD); - inputHandler.addKeyBinding(mod+"+DELETE", InputHandler.DELETE_WORD); - - // handled by listener, don't bother here - //inputHandler.addKeyBinding("ENTER", InputHandler.INSERT_BREAK); - //inputHandler.addKeyBinding("TAB", InputHandler.INSERT_TAB); - - inputHandler.addKeyBinding("INSERT", InputHandler.OVERWRITE); - - // http://dev.processing.org/bugs/show_bug.cgi?id=162 - // added for 0176, though the bindings do not appear relevant for osx - if (PreferencesData.getBoolean("editor.keys.alternative_cut_copy_paste")) { - inputHandler.addKeyBinding("C+INSERT", InputHandler.CLIPBOARD_COPY); - inputHandler.addKeyBinding("S+INSERT", InputHandler.CLIPBOARD_PASTE); - inputHandler.addKeyBinding("S+DELETE", InputHandler.CLIPBOARD_CUT); - } - - // disabling for 0122, not sure what this does - //inputHandler.addKeyBinding("C+\\", InputHandler.TOGGLE_RECT); - - // for 0122, these have been changed for better compatibility - // HOME and END now mean the beginning/end of the document - // for 0176 changed this to a preference so that the Mac OS X people - // can get the "normal" behavior as well if they prefer. - if (PreferencesData.getBoolean("editor.keys.home_and_end_travel_far")) { - inputHandler.addKeyBinding("HOME", InputHandler.DOCUMENT_HOME); - inputHandler.addKeyBinding("END", InputHandler.DOCUMENT_END); - inputHandler.addKeyBinding("S+HOME", InputHandler.SELECT_DOC_HOME); - inputHandler.addKeyBinding("S+END", InputHandler.SELECT_DOC_END); - } else { - // for 0123 added the proper windows defaults - inputHandler.addKeyBinding("HOME", InputHandler.HOME); - inputHandler.addKeyBinding("END", InputHandler.END); - inputHandler.addKeyBinding("S+HOME", InputHandler.SELECT_HOME); - inputHandler.addKeyBinding("S+END", InputHandler.SELECT_END); - inputHandler.addKeyBinding("C+HOME", InputHandler.DOCUMENT_HOME); - inputHandler.addKeyBinding("C+END", InputHandler.DOCUMENT_END); - inputHandler.addKeyBinding("CS+HOME", InputHandler.SELECT_DOC_HOME); - inputHandler.addKeyBinding("CS+END", InputHandler.SELECT_DOC_END); - } - - if (OSUtils.isMacOS()) { - inputHandler.addKeyBinding("M+LEFT", InputHandler.HOME); - inputHandler.addKeyBinding("M+RIGHT", InputHandler.END); - inputHandler.addKeyBinding("MS+LEFT", InputHandler.SELECT_HOME); // 0122 - inputHandler.addKeyBinding("MS+RIGHT", InputHandler.SELECT_END); // 0122 - } else { - inputHandler.addKeyBinding("C+LEFT", InputHandler.HOME); // 0122 - inputHandler.addKeyBinding("C+RIGHT", InputHandler.END); // 0122 - inputHandler.addKeyBinding("CS+HOME", InputHandler.SELECT_HOME); // 0122 - inputHandler.addKeyBinding("CS+END", InputHandler.SELECT_END); // 0122 - } - - inputHandler.addKeyBinding("PAGE_UP", InputHandler.PREV_PAGE); - inputHandler.addKeyBinding("PAGE_DOWN", InputHandler.NEXT_PAGE); - inputHandler.addKeyBinding("S+PAGE_UP", InputHandler.SELECT_PREV_PAGE); - inputHandler.addKeyBinding("S+PAGE_DOWN", InputHandler.SELECT_NEXT_PAGE); - - inputHandler.addKeyBinding("LEFT", InputHandler.PREV_CHAR); - inputHandler.addKeyBinding("S+LEFT", InputHandler.SELECT_PREV_CHAR); - inputHandler.addKeyBinding(mod + "+LEFT", InputHandler.PREV_WORD); - inputHandler.addKeyBinding(mod + "S+LEFT", InputHandler.SELECT_PREV_WORD); - inputHandler.addKeyBinding("RIGHT", InputHandler.NEXT_CHAR); - inputHandler.addKeyBinding("S+RIGHT", InputHandler.SELECT_NEXT_CHAR); - inputHandler.addKeyBinding(mod + "+RIGHT", InputHandler.NEXT_WORD); - inputHandler.addKeyBinding(mod + "S+RIGHT", InputHandler.SELECT_NEXT_WORD); - - inputHandler.addKeyBinding("UP", InputHandler.PREV_LINE); - inputHandler.addKeyBinding(mod + "+UP", InputHandler.PREV_LINE); // p5 - inputHandler.addKeyBinding("S+UP", InputHandler.SELECT_PREV_LINE); - inputHandler.addKeyBinding("DOWN", InputHandler.NEXT_LINE); - inputHandler.addKeyBinding(mod + "+DOWN", InputHandler.NEXT_LINE); // p5 - inputHandler.addKeyBinding("S+DOWN", InputHandler.SELECT_NEXT_LINE); - - inputHandler.addKeyBinding("MS+UP", InputHandler.SELECT_DOC_HOME); - inputHandler.addKeyBinding("CS+UP", InputHandler.SELECT_DOC_HOME); - inputHandler.addKeyBinding("MS+DOWN", InputHandler.SELECT_DOC_END); - inputHandler.addKeyBinding("CS+DOWN", InputHandler.SELECT_DOC_END); - - inputHandler.addKeyBinding(mod + "+ENTER", InputHandler.REPEAT); - - document = new SyntaxDocument(); - editable = true; - electricScroll = 3; - - cols = 80; - rows = 15; - - - // moved from SyntaxUtilities - //DEFAULTS.styles = SyntaxUtilities.getDefaultSyntaxStyles(); - - styles = new SyntaxStyle[Token.ID_COUNT]; - - // comments - styles[Token.COMMENT1] = Theme.getStyle("comment1"); - styles[Token.COMMENT2] = Theme.getStyle("comment2"); - - // abstract, final, private - styles[Token.KEYWORD1] = Theme.getStyle("keyword1"); - - // beginShape, point, line - styles[Token.KEYWORD2] = Theme.getStyle("keyword2"); - - // byte, char, short, color - styles[Token.KEYWORD3] = Theme.getStyle("keyword3"); - - // constants: null, true, this, RGB, TWO_PI - styles[Token.LITERAL1] = Theme.getStyle("literal1"); - - // p5 built in variables: mouseX, width, pixels - styles[Token.LITERAL2] = Theme.getStyle("literal2"); - - // ?? - styles[Token.LABEL] = Theme.getStyle("label"); - - // http://arduino.cc/ - styles[Token.URL] = Theme.getStyle("url"); - - // + - = / - styles[Token.OPERATOR] = Theme.getStyle("operator"); - - // area that's not in use by the text (replaced with tildes) - styles[Token.INVALID] = Theme.getStyle("invalid"); - - - // moved from TextAreaPainter - - font = PreferencesData.getFont("editor.font"); - - fgcolor = Theme.getColor("editor.fgcolor"); - bgcolor = Theme.getColor("editor.bgcolor"); - - caretVisible = true; - caretBlinks = PreferencesData.getBoolean("editor.caret.blink"); - caretColor = Theme.getColor("editor.caret.color"); - - selectionColor = Theme.getColor("editor.selection.color"); - - lineHighlight = - Theme.getBoolean("editor.linehighlight"); - lineHighlightColor = - Theme.getColor("editor.linehighlight.color"); - - bracketHighlight = - Theme.getBoolean("editor.brackethighlight"); - bracketHighlightColor = - Theme.getColor("editor.brackethighlight.color"); - - eolMarkers = Theme.getBoolean("editor.eolmarkers"); - eolMarkerColor = Theme.getColor("editor.eolmarkers.color"); - - paintInvalid = Theme.getBoolean("editor.invalid"); - } -} diff --git a/app/src/processing/app/syntax/SketchTextArea.java b/app/src/processing/app/syntax/SketchTextArea.java new file mode 100644 index 00000000000..68d929c675d --- /dev/null +++ b/app/src/processing/app/syntax/SketchTextArea.java @@ -0,0 +1,475 @@ +/* + * This file is part of Arduino. + * + * Copyright 2015 Ricardo JL Rufino (ricardo@criativasoft.com.br) + * + * Arduino 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + */ + +package processing.app.syntax; + +import org.fife.ui.rsyntaxtextarea.*; +import org.fife.ui.rsyntaxtextarea.Theme; +import org.fife.ui.rsyntaxtextarea.Token; +import org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip; +import org.fife.ui.rtextarea.RTextArea; +import org.fife.ui.rtextarea.RUndoManager; + +import processing.app.*; + +import javax.swing.*; +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.KeyEvent; +import java.awt.event.MouseEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +/** + * 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; + + private final PdeKeywords pdeKeywords; + + public SketchTextArea(PdeKeywords pdeKeywords) throws IOException { + this.pdeKeywords = pdeKeywords; + installFeatures(); + } + + protected void installFeatures() throws IOException { + setTheme(PreferencesData.get("editor.syntax_theme", "default")); + + setLinkGenerator(new DocLinkGenerator(pdeKeywords)); + + fixControlTab(); + + setSyntaxEditingStyle(SYNTAX_STYLE_CPLUSPLUS); + } + + public void setTheme(String name) throws IOException { + FileInputStream defaultXmlInputStream = null; + try { + defaultXmlInputStream = new FileInputStream(new File(BaseNoGui.getContentFile("lib"), "theme/syntax/" + name + ".xml")); + Theme theme = Theme.load(defaultXmlInputStream); + theme.apply(this); + } finally { + if (defaultXmlInputStream != null) { + defaultXmlInputStream.close(); + } + } + + setForeground(processing.app.Theme.getColor("editor.fgcolor")); + setBackground(processing.app.Theme.getColor("editor.bgcolor")); + setCurrentLineHighlightColor(processing.app.Theme.getColor("editor.linehighlight.color")); + setCaretColor(processing.app.Theme.getColor("editor.caret.color")); + setSelectedTextColor(null); + setUseSelectedTextColor(false); + setSelectionColor(processing.app.Theme.getColor("editor.selection.color")); + setMatchedBracketBorderColor(processing.app.Theme.getColor("editor.brackethighlight.color")); + setHyperlinkForeground((Color) processing.app.Theme.getStyledFont("url", getFont()).get("color")); + + setSyntaxTheme(TokenTypes.DATA_TYPE, "data_type"); + setSyntaxTheme(TokenTypes.FUNCTION, "function"); + setSyntaxTheme(TokenTypes.RESERVED_WORD, "reserved_word"); + setSyntaxTheme(TokenTypes.RESERVED_WORD_2, "reserved_word_2"); + setSyntaxTheme(TokenTypes.VARIABLE, "variable"); + setSyntaxTheme(TokenTypes.OPERATOR, "operator"); + setSyntaxTheme(TokenTypes.COMMENT_DOCUMENTATION, "comment1"); + setSyntaxTheme(TokenTypes.COMMENT_EOL, "comment1"); + setSyntaxTheme(TokenTypes.COMMENT_KEYWORD, "comment1"); + setSyntaxTheme(TokenTypes.COMMENT_MARKUP, "comment1"); + setSyntaxTheme(TokenTypes.LITERAL_CHAR, "literal_char"); + setSyntaxTheme(TokenTypes.LITERAL_STRING_DOUBLE_QUOTE, "literal_string_double_quote"); + } + + private void setSyntaxTheme(int tokenType, String id) { + Style style = getSyntaxScheme().getStyle(tokenType); + + Map styledFont = processing.app.Theme.getStyledFont(id, style.font); + style.foreground = (Color) styledFont.get("color"); + style.font = (Font) styledFont.get("font"); + + getSyntaxScheme().setStyle(tokenType, style); + } + + // 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 forwardKeys = new HashSet(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 backwardKeys = new HashSet(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); + + } + + 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); + } + + @Override + protected RTAMouseListener createMouseListener() { + return new SketchTextAreaMouseListener(this); + } + + 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 { + + private final PdeKeywords pdeKeywords; + + public DocLinkGenerator(PdeKeywords pdeKeywords) { + this.pdeKeywords = pdeKeywords; + } + + @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; + } + } + + + /** + * Handles http hyperlinks. + * NOTE (@Ricardo JL Rufino): Workaround to enable hyperlinks by default: https://github.com/bobbylight/RSyntaxTextArea/issues/119 + */ + private class SketchTextAreaMouseListener extends RTextAreaMutableCaretEvent { + + private Insets insets; + private boolean isScanningForLinks; + private int hoveredOverLinkOffset = -1; + + protected SketchTextAreaMouseListener(RTextArea textArea) { + super(textArea); + insets = new Insets(0, 0, 0, 0); + } + + /** + * Notifies all listeners that have registered interest for notification + * on this event type. The listener list is processed last to first. + * + * @param e The event to fire. + * @see EventListenerList + */ + private void fireHyperlinkUpdate(HyperlinkEvent e) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==HyperlinkListener.class) { + ((HyperlinkListener)listeners[i+1]).hyperlinkUpdate(e); + } + } + } + + private HyperlinkEvent createHyperlinkEvent(MouseEvent e) { + HyperlinkEvent he = null; + + Token t = viewToToken(e.getPoint()); + if (t!=null) { + // Copy token, viewToModel() unfortunately modifies Token + t = new TokenImpl(t); + } + + if (t != null && t.isHyperlink()) { + URL url = null; + String desc = null; + try { + String temp = t.getLexeme(); + // URI's need "http://" prefix for web URL's to work. + if (temp.startsWith("www.")) { + temp = "http://" + temp; + } + url = new URL(temp); + } catch (MalformedURLException mue) { + desc = mue.getMessage(); + } + he = new HyperlinkEvent(SketchTextArea.this, HyperlinkEvent.EventType.ACTIVATED, url, desc); + } + + return he; + } + + @Override + public void mouseClicked(MouseEvent e) { + if (getHyperlinksEnabled()) { + HyperlinkEvent he = createHyperlinkEvent(e); + if (he!=null) { + fireHyperlinkUpdate(he); + } + } + } + + @Override + public void mouseMoved(MouseEvent e) { + + super.mouseMoved(e); + + if (!getHyperlinksEnabled()) { + return; + } + +// LinkGenerator linkGenerator = getLinkGenerator(); + + // GitHub issue RSyntaxTextArea/#25 - links identified at "edges" of editor + // should not be activated if mouse is in margin insets. + insets = getInsets(insets); + if (insets!=null) { + int x = e.getX(); + int y = e.getY(); + if (x<=insets.left || y -1) { + tokenType = newType; + } + super.addToken(array, start, end, tokenType, startOffset, hyperlink); + } + +} diff --git a/app/src/processing/app/syntax/SyntaxDocument.java b/app/src/processing/app/syntax/SyntaxDocument.java deleted file mode 100644 index 2a1c3103ce9..00000000000 --- a/app/src/processing/app/syntax/SyntaxDocument.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * SyntaxDocument.java - Document that can be tokenized - * Copyright (C) 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import javax.swing.event.*; -import javax.swing.text.*; -import javax.swing.undo.UndoableEdit; - -/** - * A document implementation that can be tokenized by the syntax highlighting - * system. - * - * @author Slava Pestov - */ -public class SyntaxDocument extends PlainDocument -{ - /** - * Returns the token marker that is to be used to split lines - * of this document up into tokens. May return null if this - * document is not to be colorized. - */ - public TokenMarker getTokenMarker() - { - return tokenMarker; - } - - /** - * Sets the token marker that is to be used to split lines of - * this document up into tokens. May throw an exception if - * this is not supported for this type of document. - * @param tm The new token marker - */ - public void setTokenMarker(TokenMarker tm) - { - tokenMarker = tm; - if(tm == null) - return; - tokenMarker.insertLines(0,getDefaultRootElement() - .getElementCount()); - tokenizeLines(); - } - - /** - * Reparses the document, by passing all lines to the token - * marker. This should be called after the document is first - * loaded. - */ - public void tokenizeLines() - { - tokenizeLines(0,getDefaultRootElement().getElementCount()); - } - - /** - * Reparses the document, by passing the specified lines to the - * token marker. This should be called after a large quantity of - * text is first inserted. - * @param start The first line to parse - * @param len The number of lines, after the first one to parse - */ - public void tokenizeLines(int start, int len) - { - if(tokenMarker == null || !tokenMarker.supportsMultilineTokens()) - return; - - Segment lineSegment = new Segment(); - Element map = getDefaultRootElement(); - - len += start; - - try - { - for(int i = start; i < len; i++) - { - Element lineElement = map.getElement(i); - int lineStart = lineElement.getStartOffset(); - getText(lineStart,lineElement.getEndOffset() - - lineStart - 1,lineSegment); - tokenMarker.markTokens(lineSegment,i); - } - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - } - } - - /** - * Starts a compound edit that can be undone in one operation. - * Subclasses that implement undo should override this method; - * this class has no undo functionality so this method is - * empty. - */ - public void beginCompoundEdit() {} - - /** - * Ends a compound edit that can be undone in one operation. - * Subclasses that implement undo should override this method; - * this class has no undo functionality so this method is - * empty. - */ - public void endCompoundEdit() {} - - /** - * Adds an undoable edit to this document's undo list. The edit - * should be ignored if something is currently being undone. - * @param edit The undoable edit - * - * @since jEdit 2.2pre1 - */ - public void addUndoableEdit(UndoableEdit edit) {} - - // protected members - protected TokenMarker tokenMarker; - - /** - * We overwrite this method to update the token marker - * state immediately so that any event listeners get a - * consistent token marker. - */ - protected void fireInsertUpdate(DocumentEvent evt) - { - if(tokenMarker != null) - { - DocumentEvent.ElementChange ch = evt.getChange( - getDefaultRootElement()); - if(ch != null) - { - tokenMarker.insertLines(ch.getIndex() + 1, - ch.getChildrenAdded().length - - ch.getChildrenRemoved().length); - } - } - - super.fireInsertUpdate(evt); - } - - /** - * We overwrite this method to update the token marker - * state immediately so that any event listeners get a - * consistent token marker. - */ - protected void fireRemoveUpdate(DocumentEvent evt) - { - if(tokenMarker != null) - { - DocumentEvent.ElementChange ch = evt.getChange( - getDefaultRootElement()); - if(ch != null) - { - tokenMarker.deleteLines(ch.getIndex() + 1, - ch.getChildrenRemoved().length - - ch.getChildrenAdded().length); - } - } - - super.fireRemoveUpdate(evt); - } -} diff --git a/app/src/processing/app/syntax/SyntaxStyle.java b/app/src/processing/app/syntax/SyntaxStyle.java deleted file mode 100644 index 23a7c71b0a0..00000000000 --- a/app/src/processing/app/syntax/SyntaxStyle.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * SyntaxStyle.java - A simple text style class - * Copyright (C) 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import java.awt.*; -import java.awt.font.TextAttribute; -import java.util.Hashtable; -import java.util.Map; - -import javax.swing.JComponent; - - -/** - * A simple text style class. It can specify the color, italic flag, - * and bold flag of a run of text. - * @author Slava Pestov - */ -public class SyntaxStyle -{ - /** - * Creates a new SyntaxStyle. - * @param color The text color - * @param italic True if the text should be italics - * @param bold True if the text should be bold - */ - public SyntaxStyle(Color color, boolean italic, boolean bold, boolean underlined) - { - this.color = color; - this.italic = italic; - this.bold = bold; - this.underlined = underlined; - } - - /** - * Returns the color specified in this style. - */ - public Color getColor() - { - return color; - } - - /** - * Returns true if no font styles are enabled. - */ - public boolean isPlain() - { - return !(bold || italic || underlined); - } - - /** - * Returns true if italics is enabled for this style. - */ - public boolean isItalic() - { - return italic; - } - - /** - * Returns true if boldface is enabled for this style. - */ - public boolean isBold() - { - return bold; - } - - /** - * @return true if underline is enabled for this style. - */ - public boolean isUnderlined() { - return underlined; - } - - /** - * Returns the specified font, but with the style's bold, underline and - * italic flags applied. - */ - public Font getStyledFont(Font font) - { - if(font == null) - throw new NullPointerException("font param must not" - + " be null"); - if(font.equals(lastFont)) - return lastStyledFont; - lastFont = font; - - lastStyledFont = new Font(font.getFamily(), - (bold ? Font.BOLD : 0) - | (italic ? Font.ITALIC : 0), - font.getSize()); - if (underlined) { - Map attr = new Hashtable(); - attr.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); - lastStyledFont = lastStyledFont.deriveFont(attr); - } - return lastStyledFont; - } - - /** - * Returns the font metrics for the styled font. - */ - public FontMetrics getFontMetrics(Font font, JComponent comp) - { - if(font == null) - throw new NullPointerException("font param must not" - + " be null"); - if(font.equals(lastFont) && fontMetrics != null) - return fontMetrics; - lastFont = font; - lastStyledFont = new Font(font.getFamily(), - (bold ? Font.BOLD : 0) - | (italic ? Font.ITALIC : 0), - font.getSize()); - if (underlined) { - Map attr = new Hashtable(); - attr.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); - lastStyledFont = lastStyledFont.deriveFont(attr); - } - //fontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(lastStyledFont); - fontMetrics = comp.getFontMetrics(lastStyledFont); - return fontMetrics; - } - - /** - * Sets the foreground color and font of the specified graphics - * context to that specified in this style. - * @param gfx The graphics context - * @param font The font to add the styles to - */ - public void setGraphicsFlags(Graphics gfx, Font font) - { - Font _font = getStyledFont(font); - gfx.setFont(_font); - gfx.setColor(color); - } - - /** - * Returns a string representation of this object. - */ - public String toString() - { - return getClass().getName() + "[color=" + color + - (italic ? ",italic" : "") + - (bold ? ",bold" : "") + - (underlined ? ",underlined" : "") + - "]"; - } - - // private members - private Color color; - private boolean italic; - private boolean bold; - private boolean underlined; - private Font lastFont; - private Font lastStyledFont; - private FontMetrics fontMetrics; -} diff --git a/app/src/processing/app/syntax/SyntaxUtilities.java b/app/src/processing/app/syntax/SyntaxUtilities.java deleted file mode 100644 index 6eef977a08b..00000000000 --- a/app/src/processing/app/syntax/SyntaxUtilities.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * SyntaxUtilities.java - Utility functions used by syntax colorizing - * Copyright (C) 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import javax.swing.text.*; -import java.awt.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - - -/** - * Class with several utility functions used by jEdit's syntax colorizing - * subsystem. - * - * @author Slava Pestov - */ -public class SyntaxUtilities -{ - /** - * Checks if a subregion of a Segment is equal to a - * string. - * @param ignoreCase True if case should be ignored, false otherwise - * @param text The segment - * @param offset The offset into the segment - * @param match The string to match - */ - public static boolean regionMatches(boolean ignoreCase, Segment text, - int offset, String match) - { - int length = offset + match.length(); - char[] textArray = text.array; - if(length > text.offset + text.count) - return false; - for(int i = offset, j = 0; i < length; i++, j++) - { - char c1 = textArray[i]; - char c2 = match.charAt(j); - if(ignoreCase) - { - c1 = Character.toUpperCase(c1); - c2 = Character.toUpperCase(c2); - } - if(c1 != c2) - return false; - } - return true; - } - - - /** - * Checks if a subregion of a Segment is equal to a - * character array. - * @param ignoreCase True if case should be ignored, false otherwise - * @param text The segment - * @param offset The offset into the segment - * @param match The character array to match - */ - public static boolean regionMatches(boolean ignoreCase, Segment text, - int offset, char[] match) - { - int length = offset + match.length; - char[] textArray = text.array; - if(length > text.offset + text.count) - return false; - for(int i = offset, j = 0; i < length; i++, j++) - { - char c1 = textArray[i]; - char c2 = match[j]; - if(ignoreCase) - { - c1 = Character.toUpperCase(c1); - c2 = Character.toUpperCase(c2); - } - if(c1 != c2) - return false; - } - return true; - } - - - /** - * Returns the default style table. This can be passed to the - * setStyles() method of SyntaxDocument - * to use the default syntax styles. - */ - public static SyntaxStyle[] getDefaultSyntaxStyles() - { - SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT]; - - styles[Token.COMMENT1] = new SyntaxStyle(Color.black,true,false,false); - styles[Token.COMMENT2] = new SyntaxStyle(new Color(0x990033),true,false,false); - styles[Token.KEYWORD1] = new SyntaxStyle(Color.black,false,true,false); - styles[Token.KEYWORD2] = new SyntaxStyle(Color.magenta,false,false,false); - styles[Token.KEYWORD3] = new SyntaxStyle(new Color(0x009600),false,false,false); - styles[Token.LITERAL1] = new SyntaxStyle(new Color(0x650099),false,false,false); - styles[Token.LITERAL2] = new SyntaxStyle(new Color(0x650099),false,true,false); - styles[Token.LABEL] = new SyntaxStyle(new Color(0x990033),false,true,false); - styles[Token.OPERATOR] = new SyntaxStyle(Color.black,false,true,false); - styles[Token.URL] = new SyntaxStyle(Color.blue,true,false,false); - styles[Token.INVALID] = new SyntaxStyle(Color.red,false,true,false); - - return styles; - } - - - /** - * Paints the specified line onto the graphics context. Note that this - * method munges the offset and count values of the segment. - * @param line The line segment - * @param tokens The token list for the line - * @param styles The syntax style list - * @param expander The tab expander used to determine tab stops. May - * be null - * @param gfx The graphics context - * @param x The x co-ordinate - * @param y The y co-ordinate - * @return The x co-ordinate, plus the width of the painted string - */ - public static int paintSyntaxLine(Segment line, Token tokens, - SyntaxStyle[] styles, - TabExpander expander, Graphics gfx, - int x, int y) - { - Font defaultFont = gfx.getFont(); - Color defaultColor = gfx.getColor(); - - for(;;) - { - byte id = tokens.id; - if(id == Token.END) - break; - - int length = tokens.length; - if(id == Token.NULL) - { - if(!defaultColor.equals(gfx.getColor())) - gfx.setColor(defaultColor); - if(!defaultFont.equals(gfx.getFont())) - gfx.setFont(defaultFont); - } - else - styles[id].setGraphicsFlags(gfx,defaultFont); - - line.count = length; - if (id == Token.COMMENT1 || id == Token.COMMENT2) - x = drawTabbedCommentsText(line, x, y, gfx, expander, styles, styles[id]); - else - x = Utilities.drawTabbedText(line, x, y, gfx, expander, 0); - line.offset += length; - - tokens = tokens.next; - } - - return x; - } - - /** - * Parse comments and identify "@schematics <something>" pattern. - * - * @param line - * A string to parse - * @return null if the pattern is not found, otherwise an array of - * String is returned: the elements with index 0, 1 and 2 are - * respectively the preamble, the <something> stuff, and - * the remaining part of the string. - */ - public static String[] parseCommentUrls(String line) { - Matcher m = urlPattern.matcher(line.toString()); - if (!m.find()) - return null; - - String res[] = new String[3]; - res[0] = line.substring(0, m.start(1)); - res[1] = line.substring(m.start(1), m.end(1)); - res[2] = line.substring(m.end(1)); - // System.out.println("0 =>"+res[0]+"<\n1 =>"+res[1]+"< \n2 =>"+res[2]+"<"); - return res; - } - - static private Pattern urlPattern = Pattern.compile( - "((?:https?|ftp)://" + // ( Protocol - "(?:(?:[\\w_\\-]+:)?[\\w_\\-]+@)?" + // Username and password - "(?:[\\w_\\-]+\\.)+[\\w_\\-]+" + // Domain name - "(?::[0-9]{1,5})?" + // Port - "(?:/[\\w_\\-./?%&=+]*)?)" + // Path ) - "(?:\\s|$)"); // whitespace or EOL - - public static Segment stringToSegment(String v) { - return new Segment(v.toCharArray(), 0, v.length()); - } - - private static int drawTabbedCommentsText(Segment line, int x, int y, - Graphics gfx, TabExpander expander, SyntaxStyle[] styles, - SyntaxStyle commentStyle) { - - String parse[] = parseCommentUrls(line.toString()); - if (parse == null) - // Revert to plain writing. - return Utilities.drawTabbedText(line, x, y, gfx, expander, 0); - Segment pre = stringToSegment(parse[0]); - Segment tag = stringToSegment(parse[1]); - Segment post = stringToSegment(parse[2]); - - if (pre.count>0) - x = Utilities.drawTabbedText(pre, x, y, gfx, expander, 0); - - Font f = gfx.getFont(); - styles[Token.URL].setGraphicsFlags(gfx, f); - x = Utilities.drawTabbedText(tag, x, y, gfx, expander, 0); - - commentStyle.setGraphicsFlags(gfx, f); - if (post.count>0) - x = Utilities.drawTabbedText(post, x, y, gfx, expander, 0); - return x; - } - - // private members - private SyntaxUtilities() {} -} diff --git a/app/src/processing/app/syntax/TextAreaDefaults.java b/app/src/processing/app/syntax/TextAreaDefaults.java deleted file mode 100644 index c2e878578db..00000000000 --- a/app/src/processing/app/syntax/TextAreaDefaults.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * TextAreaDefaults.java - Encapsulates default values for various settings - * Copyright (C) 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import java.awt.*; -//import javax.swing.JPopupMenu; - -/** - * Encapsulates default settings for a text area. This can be passed - * to the constructor once the necessary fields have been filled out. - * The advantage of doing this over calling lots of set() methods after - * creating the text area is that this method is faster. - */ -public class TextAreaDefaults -{ - private static TextAreaDefaults DEFAULTS; - - public InputHandler inputHandler; - public SyntaxDocument document; - public boolean editable; - - public boolean caretVisible; - public boolean caretBlinks; - public boolean blockCaret; - public int electricScroll; - - public int cols; - public int rows; - public SyntaxStyle[] styles; - public Color caretColor; - public Color selectionColor; - public Color lineHighlightColor; - public boolean lineHighlight; - public Color bracketHighlightColor; - public boolean bracketHighlight; - public Color eolMarkerColor; - public boolean eolMarkers; - public boolean paintInvalid; - - - // moved from TextAreaPainter [fry] - public Font font; - public Color fgcolor; - public Color bgcolor; - - //public JPopupMenu popup; - - - /** - * Returns a new TextAreaDefaults object with the default values filled - * in. - */ - public static TextAreaDefaults getDefaults() - { - if (DEFAULTS == null) { - DEFAULTS = new TextAreaDefaults(); - - DEFAULTS.inputHandler = new DefaultInputHandler(); - DEFAULTS.inputHandler.addDefaultKeyBindings(); - DEFAULTS.document = new SyntaxDocument(); - DEFAULTS.editable = true; - - DEFAULTS.caretVisible = true; - DEFAULTS.caretBlinks = true; - DEFAULTS.electricScroll = 3; - - DEFAULTS.cols = 80; - DEFAULTS.rows = 25; - DEFAULTS.styles = SyntaxUtilities.getDefaultSyntaxStyles(); - DEFAULTS.caretColor = Color.red; - DEFAULTS.selectionColor = new Color(0xccccff); - DEFAULTS.lineHighlightColor = new Color(0xe0e0e0); - DEFAULTS.lineHighlight = true; - DEFAULTS.bracketHighlightColor = Color.black; - DEFAULTS.bracketHighlight = true; - DEFAULTS.eolMarkerColor = new Color(0x009999); - DEFAULTS.eolMarkers = true; - DEFAULTS.paintInvalid = true; - } - - return DEFAULTS; - } -} diff --git a/app/src/processing/app/syntax/TextAreaLineNumbers.java b/app/src/processing/app/syntax/TextAreaLineNumbers.java deleted file mode 100644 index 39f7438f281..00000000000 --- a/app/src/processing/app/syntax/TextAreaLineNumbers.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * TextAreaLineNumbers.java - Show line numbers for the open file in the editor - * Copyright (C) 2013 Cayci Gorlitsky - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Rectangle; - -import javax.swing.border.MatteBorder; - -public class TextAreaLineNumbers extends TextAreaPainter { - - private final int LEFT_INDENT = 6; - private final int RIGHT_INDENT = 6; - private final int RIGHT_BORDER_WIDTH = 1; - private final int PADDING_WIDTH = LEFT_INDENT + RIGHT_INDENT + RIGHT_BORDER_WIDTH; - - private final int MIN_WIDTH; - private final int DIGIT_WIDTH; - private final int MIN_NUM_DIGITS = 2; - - private int currStartNum = 0; - private int currEndNum = 0; - private int currNumDigits = MIN_NUM_DIGITS; - - - - public TextAreaLineNumbers(JEditTextArea textArea, TextAreaDefaults defaults) { - super(textArea, defaults); - DIGIT_WIDTH = getFontMetrics(getFont()).stringWidth("0"); - MIN_WIDTH = DIGIT_WIDTH * MIN_NUM_DIGITS + PADDING_WIDTH; - setEnabled(false); - setBorder(new MatteBorder(0, 0, 0, RIGHT_BORDER_WIDTH, new Color(240, 240, 240))); - } - - public void updateLineNumbers(int startNum, int endNum) { - if (currStartNum == startNum && currEndNum == endNum) { - return; - } - currStartNum = startNum; - currEndNum = endNum; - - invalidate(); - repaint(); - } - - @Override - public void paint(Graphics gfx) { - super.paint(gfx); - getBorder().paintBorder(this, gfx, 0, 0, getSize().width, getSize().height); - } - - @Override - protected void paintLine(Graphics gfx, TokenMarker tokenMarker, - int line, int x) - { - currentLineIndex = line; - gfx.setFont(getFont()); - gfx.setColor(Color.GRAY); - int y = textArea.lineToY(line); - int startX = getBounds().x + getBounds().width; - if (line >= 0 && line < textArea.getLineCount()) { - String lineNumberString = String.valueOf(line+1); - int lineStartX = startX - RIGHT_BORDER_WIDTH - RIGHT_INDENT - fm.stringWidth(lineNumberString); - gfx.drawString(lineNumberString,lineStartX,y + fm.getHeight()); - } - } - - public void updateWidthForNumDigits(int numDigits) { - if (currNumDigits == numDigits) { - return; - } - currNumDigits = numDigits; - - if (isVisible()) { - updateBounds(); - invalidate(); - repaint(); - } - } - - public void setDisplayLineNumbers(boolean displayLineNumbers) { - setVisible(displayLineNumbers); - if (displayLineNumbers) { - updateBounds(); - } else { - setBounds(new Rectangle(0, getHeight())); - } - invalidate(); - repaint(); - } - - private void updateBounds() { - if (isVisible()) { - setBounds(new Rectangle(Math.max(MIN_WIDTH, DIGIT_WIDTH * currNumDigits + PADDING_WIDTH), getHeight())); - textArea.validate(); - } - } -} diff --git a/app/src/processing/app/syntax/TextAreaPainter.java b/app/src/processing/app/syntax/TextAreaPainter.java deleted file mode 100644 index 3607c46fa72..00000000000 --- a/app/src/processing/app/syntax/TextAreaPainter.java +++ /dev/null @@ -1,787 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - * TextAreaPainter.java - Paints the text area - * Copyright (C) 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import processing.app.*; -import processing.app.syntax.im.CompositionTextPainter; - -import javax.swing.ToolTipManager; -import javax.swing.text.*; -import javax.swing.JComponent; -import java.awt.event.MouseEvent; -import java.awt.*; -import java.awt.print.*; - -/** - * The text area repaint manager. It performs double buffering and paints - * lines of text. - * @author Slava Pestov - */ -public class TextAreaPainter extends JComponent -implements TabExpander, Printable -{ - /** True if inside printing, will handle disabling the highlight */ - boolean printing; - /** Current setting for editor.antialias preference */ - boolean antialias; - - /** A specific painter composed by the InputMethod.*/ - protected CompositionTextPainter compositionTextPainter; - - /** - * Creates a new repaint manager. This should be not be called - * directly. - */ - public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults) - { - this.textArea = textArea; - - setAutoscrolls(true); - setDoubleBuffered(true); - setOpaque(true); - - ToolTipManager.sharedInstance().registerComponent(this); - - currentLine = new Segment(); - currentLineIndex = -1; - - setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); - - setFont(defaults.font); - setForeground(defaults.fgcolor); - setBackground(defaults.bgcolor); - - antialias = PreferencesData.getBoolean("editor.antialias"); - - blockCaret = defaults.blockCaret; - styles = defaults.styles; - cols = defaults.cols; - rows = defaults.rows; - caretColor = defaults.caretColor; - selectionColor = defaults.selectionColor; - lineHighlightColor = defaults.lineHighlightColor; - lineHighlight = defaults.lineHighlight; - bracketHighlightColor = defaults.bracketHighlightColor; - bracketHighlight = defaults.bracketHighlight; - paintInvalid = defaults.paintInvalid; - eolMarkerColor = defaults.eolMarkerColor; - eolMarkers = defaults.eolMarkers; - } - - /** - * Get CompositionTextPainter. if CompositionTextPainter is not created, create it. - */ - public CompositionTextPainter getCompositionTextpainter(){ - if(compositionTextPainter == null){ - compositionTextPainter = new CompositionTextPainter(textArea); - } - return compositionTextPainter; - } - - /** - * Returns if this component can be traversed by pressing the - * Tab key. This returns false. - */ -// public final boolean isManagingFocus() -// { -// return false; -// } - - /** - * Returns the syntax styles used to paint colorized text. Entry n - * will be used to paint tokens with id = n. - * @see processing.app.syntax.Token - */ - public final SyntaxStyle[] getStyles() - { - return styles; - } - - /** - * Sets the syntax styles used to paint colorized text. Entry n - * will be used to paint tokens with id = n. - * @param styles The syntax styles - * @see processing.app.syntax.Token - */ - public final void setStyles(SyntaxStyle[] styles) - { - this.styles = styles; - repaint(); - } - - /** - * Returns the caret color. - */ - public final Color getCaretColor() - { - return caretColor; - } - - /** - * Sets the caret color. - * @param caretColor The caret color - */ - public final void setCaretColor(Color caretColor) - { - this.caretColor = caretColor; - invalidateSelectedLines(); - } - - /** - * Returns the selection color. - */ - public final Color getSelectionColor() - { - return selectionColor; - } - - /** - * Sets the selection color. - * @param selectionColor The selection color - */ - public final void setSelectionColor(Color selectionColor) - { - this.selectionColor = selectionColor; - invalidateSelectedLines(); - } - - /** - * Returns the line highlight color. - */ - public final Color getLineHighlightColor() - { - return lineHighlightColor; - } - - /** - * Sets the line highlight color. - * @param lineHighlightColor The line highlight color - */ - public final void setLineHighlightColor(Color lineHighlightColor) - { - this.lineHighlightColor = lineHighlightColor; - invalidateSelectedLines(); - } - - /** - * Returns true if line highlight is enabled, false otherwise. - */ - public final boolean isLineHighlightEnabled() - { - return lineHighlight; - } - - /** - * Enables or disables current line highlighting. - * @param lineHighlight True if current line highlight - * should be enabled, false otherwise - */ - public final void setLineHighlightEnabled(boolean lineHighlight) - { - this.lineHighlight = lineHighlight; - invalidateSelectedLines(); - } - - /** - * Returns the bracket highlight color. - */ - public final Color getBracketHighlightColor() - { - return bracketHighlightColor; - } - - /** - * Sets the bracket highlight color. - * @param bracketHighlightColor The bracket highlight color - */ - public final void setBracketHighlightColor(Color bracketHighlightColor) - { - this.bracketHighlightColor = bracketHighlightColor; - invalidateLine(textArea.getBracketLine()); - } - - /** - * Returns true if bracket highlighting is enabled, false otherwise. - * When bracket highlighting is enabled, the bracket matching the - * one before the caret (if any) is highlighted. - */ - public final boolean isBracketHighlightEnabled() - { - return bracketHighlight; - } - - /** - * Enables or disables bracket highlighting. - * When bracket highlighting is enabled, the bracket matching the - * one before the caret (if any) is highlighted. - * @param bracketHighlight True if bracket highlighting should be - * enabled, false otherwise - */ - public final void setBracketHighlightEnabled(boolean bracketHighlight) - { - this.bracketHighlight = bracketHighlight; - invalidateLine(textArea.getBracketLine()); - } - - /** - * Returns true if the caret should be drawn as a block, false otherwise. - */ - public final boolean isBlockCaretEnabled() - { - return blockCaret; - } - - /** - * Sets if the caret should be drawn as a block, false otherwise. - * @param blockCaret True if the caret should be drawn as a block, - * false otherwise. - */ - public final void setBlockCaretEnabled(boolean blockCaret) - { - this.blockCaret = blockCaret; - invalidateSelectedLines(); - } - - /** - * Returns the EOL marker color. - */ - public final Color getEOLMarkerColor() - { - return eolMarkerColor; - } - - /** - * Sets the EOL marker color. - * @param eolMarkerColor The EOL marker color - */ - public final void setEOLMarkerColor(Color eolMarkerColor) - { - this.eolMarkerColor = eolMarkerColor; - repaint(); - } - - /** - * Returns true if EOL markers are drawn, false otherwise. - */ - public final boolean getEOLMarkersPainted() - { - return eolMarkers; - } - - /** - * Sets if EOL markers are to be drawn. - * @param eolMarkers True if EOL markers should be drawn, false otherwise - */ - public final void setEOLMarkersPainted(boolean eolMarkers) - { - this.eolMarkers = eolMarkers; - repaint(); - } - - /** - * Returns true if invalid lines are painted as red tildes (~), - * false otherwise. - */ - public boolean getInvalidLinesPainted() - { - return paintInvalid; - } - - /** - * Sets if invalid lines are to be painted as red tildes. - * @param paintInvalid True if invalid lines should be drawn, false otherwise - */ - public void setInvalidLinesPainted(boolean paintInvalid) - { - this.paintInvalid = paintInvalid; - } - - /** - * Adds a custom highlight painter. - * @param highlight The highlight - */ - public void addCustomHighlight(Highlight highlight) - { - highlight.init(textArea,highlights); - highlights = highlight; - } - - /** - * Highlight interface. - */ - public interface Highlight - { - /** - * Called after the highlight painter has been added. - * @param textArea The text area - * @param next The painter this one should delegate to - */ - void init(JEditTextArea textArea, Highlight next); - - /** - * This should paint the highlight and delgate to the - * next highlight painter. - * @param gfx The graphics context - * @param line The line number - * @param y The y co-ordinate of the line - */ - void paintHighlight(Graphics gfx, int line, int y); - - /** - * Returns the tool tip to display at the specified - * location. If this highlighter doesn't know what to - * display, it should delegate to the next highlight - * painter. - * @param evt The mouse event - */ - String getToolTipText(MouseEvent evt); - } - - /** - * Returns the tool tip to display at the specified location. - * @param evt The mouse event - */ - public String getToolTipText(MouseEvent evt) - { - if(highlights != null) - return highlights.getToolTipText(evt); - else - return null; - } - - /** - * Returns the font metrics used by this component. - */ - public FontMetrics getFontMetrics() - { - return fm; - } - - /** - * Sets the font for this component. This is overridden to update the - * cached font metrics and to recalculate which lines are visible. - * @param font The font - */ - public void setFont(Font font) - { - super.setFont(font); - fm = super.getFontMetrics(font); - textArea.recalculateVisibleLines(); - } - - /** - * Repaints the text. - * @param gfx The graphics context - */ - public void paint(Graphics gfx) - { - Graphics2D g2 = (Graphics2D) gfx; - g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - antialias ? - RenderingHints.VALUE_TEXT_ANTIALIAS_ON : - RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); - - tabSize = fm.charWidth(' ') * ((Integer)textArea.getDocument().getProperty(PlainDocument.tabSizeAttribute)).intValue(); - - Rectangle clipRect = gfx.getClipBounds(); - - gfx.setColor(getBackground()); - gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height); - - // We don't use yToLine() here because that method doesn't - // return lines past the end of the document - int height = fm.getHeight(); - int firstLine = textArea.getFirstLine(); - int firstInvalid = firstLine + clipRect.y / height; - // Because the clipRect's height is usually an even multiple - // of the font height, we subtract 1 from it, otherwise one - // too many lines will always be painted. - int lastInvalid = firstLine + (clipRect.y + clipRect.height - 1) / height; - - try { - TokenMarker tokenMarker = textArea.getDocument().getTokenMarker(); - int x = textArea.getHorizontalOffset(); - - for (int line = firstInvalid; line <= lastInvalid; line++) { - paintLine(gfx,tokenMarker,line,x); - } - - if (tokenMarker != null && tokenMarker.isNextLineRequested()) { - int h = clipRect.y + clipRect.height; - repaint(0,h,getWidth(),getHeight() - h); - } - } catch (Exception e) { - System.err.println("Error repainting line" - + " range {" + firstInvalid + "," - + lastInvalid + "}:"); - e.printStackTrace(); - } - } - - - public int print(Graphics g, PageFormat pageFormat, int pageIndex) { - int lineHeight = fm.getHeight(); - int linesPerPage = (int) (pageFormat.getImageableHeight() / lineHeight); - int lineCount = textArea.getLineCount(); - int lastPage = lineCount / linesPerPage; - - if (pageIndex > lastPage) { - return NO_SUCH_PAGE; - - } else { - Graphics2D g2d = (Graphics2D)g; - TokenMarker tokenMarker = textArea.getDocument().getTokenMarker(); - int firstLine = pageIndex*linesPerPage; - g2d.translate(Math.max(54, pageFormat.getImageableX()), - pageFormat.getImageableY() - firstLine*lineHeight); - printing = true; - for (int line = firstLine; line < firstLine + linesPerPage; line++) { - paintLine(g2d, tokenMarker, line, 0); - } - printing = false; - return PAGE_EXISTS; - } - } - - - /** - * Marks a line as needing a repaint. - * @param line The line to invalidate - */ - public final void invalidateLine(int line) - { - repaint(0,textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(), - getWidth(),fm.getHeight()); - } - - /** - * Marks a range of lines as needing a repaint. - * @param firstLine The first line to invalidate - * @param lastLine The last line to invalidate - */ - public final void invalidateLineRange(int firstLine, int lastLine) - { - repaint(0,textArea.lineToY(firstLine) + - fm.getMaxDescent() + fm.getLeading(), - getWidth(),(lastLine - firstLine + 1) * fm.getHeight()); - } - - /** - * Repaints the lines containing the selection. - */ - public final void invalidateSelectedLines() - { - invalidateLineRange(textArea.getSelectionStartLine(), - textArea.getSelectionStopLine()); - } - - /** - * Implementation of TabExpander interface. Returns next tab stop after - * a specified point. - * @param x The x co-ordinate - * @param tabOffset Ignored - * @return The next tab stop after x - */ - public float nextTabStop(float x, int tabOffset) - { - int offset = textArea.getHorizontalOffset(); - int ntabs = ((int)x - offset) / tabSize; - return (ntabs + 1) * tabSize + offset; - } - - /** - * Returns the painter's preferred size. - */ - public Dimension getPreferredSize() - { - Dimension dim = new Dimension(); - dim.width = fm.charWidth('w') * cols; - dim.height = fm.getHeight() * rows; - return dim; - } - - - /** - * Returns the painter's minimum size. - */ - public Dimension getMinimumSize() - { - Dimension dim = new Dimension(); - dim.width = fm.charWidth('w') * 10; - dim.height = fm.getHeight() * 4; - return dim; - } - - // package-private members - int currentLineIndex; - Token currentLineTokens; - Segment currentLine; - - /** - * Accessor used by tools that want to hook in and grab the formatting. - */ - public int getCurrentLineIndex() { - return currentLineIndex; - } - - /** - * Accessor used by tools that want to hook in and grab the formatting. - */ - public void setCurrentLineIndex(int what) { - currentLineIndex = what; - } - - /** - * Accessor used by tools that want to hook in and grab the formatting. - */ - public Token getCurrentLineTokens() { - return currentLineTokens; - } - - /** - * Accessor used by tools that want to hook in and grab the formatting. - */ - public void setCurrentLineTokens(Token tokens) { - currentLineTokens = tokens; - } - - /** - * Accessor used by tools that want to hook in and grab the formatting. - */ - public Segment getCurrentLine() { - return currentLine; - } - - - // protected members - protected JEditTextArea textArea; - - protected SyntaxStyle[] styles; - protected Color caretColor; - protected Color selectionColor; - protected Color lineHighlightColor; - protected Color bracketHighlightColor; - protected Color eolMarkerColor; - - protected boolean blockCaret; - protected boolean lineHighlight; - protected boolean bracketHighlight; - protected boolean paintInvalid; - protected boolean eolMarkers; - protected int cols; - protected int rows; - - protected int tabSize; - protected FontMetrics fm; - - protected Highlight highlights; - - protected void paintLine(Graphics gfx, TokenMarker tokenMarker, - int line, int x) - { - Font defaultFont = getFont(); - Color defaultColor = getForeground(); - - currentLineIndex = line; - int y = textArea.lineToY(line); - - if (line < 0 || line >= textArea.getLineCount()) { - if (paintInvalid) { - paintHighlight(gfx,line,y); - styles[Token.INVALID].setGraphicsFlags(gfx,defaultFont); - gfx.drawString("~",0,y + fm.getHeight()); - } - } else if(tokenMarker == null) { - paintPlainLine(gfx,line,defaultFont,defaultColor,x,y); - } else { - paintSyntaxLine(gfx,tokenMarker,line,defaultFont, - defaultColor,x,y); - } - } - - protected void paintPlainLine(Graphics gfx, int line, Font defaultFont, - Color defaultColor, int x, int y) - { - paintHighlight(gfx,line,y); - textArea.getLineText(line,currentLine); - - gfx.setFont(defaultFont); - gfx.setColor(defaultColor); - - y += fm.getHeight(); - x = Utilities.drawTabbedText(currentLine,x,y,gfx,this,0); - /* - * Draw characters via input method. - */ - if (compositionTextPainter != null && compositionTextPainter.hasComposedTextLayout()) { - compositionTextPainter.draw(gfx, lineHighlightColor); - } - if (eolMarkers) { - gfx.setColor(eolMarkerColor); - gfx.drawString(".",x,y); - } - } - - protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker, - int line, Font defaultFont, - Color defaultColor, int x, int y) - { - textArea.getLineText(currentLineIndex,currentLine); - currentLineTokens = tokenMarker.markTokens(currentLine, - currentLineIndex); - - paintHighlight(gfx,line,y); - - gfx.setFont(defaultFont); - gfx.setColor(defaultColor); - y += fm.getHeight(); - x = SyntaxUtilities.paintSyntaxLine(currentLine, - currentLineTokens, - styles, this, gfx, x, y); - /* - * Draw characters via input method. - */ - if (compositionTextPainter != null && compositionTextPainter.hasComposedTextLayout()) { - compositionTextPainter.draw(gfx, lineHighlightColor); - } - if (eolMarkers) { - gfx.setColor(eolMarkerColor); - gfx.drawString(".",x,y); - } - } - - protected void paintHighlight(Graphics gfx, int line, int y) - { - if (!printing) { - if (line >= textArea.getSelectionStartLine() - && line <= textArea.getSelectionStopLine()) - paintLineHighlight(gfx,line,y); - - if (highlights != null) - highlights.paintHighlight(gfx,line,y); - - if (bracketHighlight && line == textArea.getBracketLine()) - paintBracketHighlight(gfx,line,y); - - if (line == textArea.getCaretLine()) - paintCaret(gfx,line,y); - } - } - - protected void paintLineHighlight(Graphics gfx, int line, int y) - { - int height = fm.getHeight(); - y += fm.getLeading() + fm.getMaxDescent(); - - int selectionStart = textArea.getSelectionStart(); - int selectionEnd = textArea.getSelectionStop(); - - if (selectionStart == selectionEnd) { - if (lineHighlight) { - gfx.setColor(lineHighlightColor); - gfx.fillRect(0,y,getWidth(),height); - } - } else { - gfx.setColor(selectionColor); - - int selectionStartLine = textArea.getSelectionStartLine(); - int selectionEndLine = textArea.getSelectionStopLine(); - int lineStart = textArea.getLineStartOffset(line); - - int x1, x2; - if (textArea.isSelectionRectangular()) { - int lineLen = textArea.getLineLength(line); - x1 = textArea._offsetToX(line,Math.min(lineLen, selectionStart - textArea.getLineStartOffset(selectionStartLine))); - x2 = textArea._offsetToX(line,Math.min(lineLen, selectionEnd - textArea.getLineStartOffset(selectionEndLine))); - if (x1 == x2) - x2++; - } else if(selectionStartLine == selectionEndLine) { - x1 = textArea._offsetToX(line, selectionStart - lineStart); - x2 = textArea._offsetToX(line, selectionEnd - lineStart); - } else if(line == selectionStartLine) { - x1 = textArea._offsetToX(line, selectionStart - lineStart); - x2 = getWidth(); - } else if(line == selectionEndLine) { - //x1 = 0; - // hack from stendahl to avoid doing weird side selection thing - x1 = textArea._offsetToX(line, 0); - // attempt at getting the gutter too, but doesn't seem to work - //x1 = textArea._offsetToX(line, -textArea.getHorizontalOffset()); - x2 = textArea._offsetToX(line, selectionEnd - lineStart); - } else { - //x1 = 0; - // hack from stendahl to avoid doing weird side selection thing - x1 = textArea._offsetToX(line, 0); - // attempt at getting the gutter too, but doesn't seem to work - //x1 = textArea._offsetToX(line, -textArea.getHorizontalOffset()); - x2 = getWidth(); - } - - // "inlined" min/max() - gfx.fillRect(x1 > x2 ? x2 : x1,y,x1 > x2 ? - (x1 - x2) : (x2 - x1),height); - } - - } - - protected void paintBracketHighlight(Graphics gfx, int line, int y) - { - int position = textArea.getBracketPosition(); - if(position == -1) - return; - y += fm.getLeading() + fm.getMaxDescent(); - int x = textArea._offsetToX(line,position); - gfx.setColor(bracketHighlightColor); - // Hack!!! Since there is no fast way to get the character - // from the bracket matching routine, we use ( since all - // brackets probably have the same width anyway - gfx.drawRect(x,y,fm.charWidth('(') - 1, - fm.getHeight() - 1); - } - - protected void paintCaret(Graphics gfx, int line, int y) - { - //System.out.println("painting caret " + line + " " + y); - if (textArea.isCaretVisible()) { - //System.out.println("caret is visible"); - int offset = - textArea.getCaretPosition() - textArea.getLineStartOffset(line); - int caretX = textArea._offsetToX(line, offset); - int caretWidth = ((blockCaret || - textArea.isOverwriteEnabled()) ? - fm.charWidth('w') : 1); - y += fm.getLeading() + fm.getMaxDescent(); - int height = fm.getHeight(); - - //System.out.println("caretX, width = " + caretX + " " + caretWidth); - - gfx.setColor(caretColor); - - if (textArea.isOverwriteEnabled()) { - gfx.fillRect(caretX,y + height - 1, caretWidth,1); - - } else { - // some machines don't like the drawRect for the single - // pixel caret.. this caused a lot of hell because on that - // minority of machines, the caret wouldn't show up past - // the first column. the fix is to use drawLine() in - // those cases, as a workaround. - if (caretWidth == 1) { - gfx.drawLine(caretX, y, caretX, y + height - 1); - } else { - gfx.drawRect(caretX, y, caretWidth - 1, height - 1); - } - //gfx.drawRect(caretX, y, caretWidth, height - 1); - } - } - } -} diff --git a/app/src/processing/app/syntax/TextUtilities.java b/app/src/processing/app/syntax/TextUtilities.java deleted file mode 100644 index f009cd0513d..00000000000 --- a/app/src/processing/app/syntax/TextUtilities.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * TextUtilities.java - Utility functions used by the text area classes - * Copyright (C) 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import javax.swing.text.*; - -/** - * Class with several utility functions used by the text area component. - * @author Slava Pestov - */ -public class TextUtilities -{ - /** - * Returns the offset of the bracket matching the one at the - * specified offset of the document, or -1 if the bracket is - * unmatched (or if the character is not a bracket). - * @param doc The document - * @param offset The offset - * @exception BadLocationException If an out-of-bounds access - * was attempted on the document text - */ - public static int findMatchingBracket(Document doc, int offset) - throws BadLocationException - { - if(doc.getLength() == 0) - return -1; - char c = doc.getText(offset,1).charAt(0); - char cprime; // c` - corresponding character - boolean direction; // true = back, false = forward - - switch(c) - { - case '(': cprime = ')'; direction = false; break; - case ')': cprime = '('; direction = true; break; - case '[': cprime = ']'; direction = false; break; - case ']': cprime = '['; direction = true; break; - case '{': cprime = '}'; direction = false; break; - case '}': cprime = '{'; direction = true; break; - default: return -1; - } - - int count; - - // How to merge these two cases is left as an exercise - // for the reader. - - // Go back or forward - if(direction) - { - // Count is 1 initially because we have already - // `found' one closing bracket - count = 1; - - // Get text[0,offset-1]; - String text = doc.getText(0,offset); - - // Scan backwards - for(int i = offset - 1; i >= 0; i--) - { - // If text[i] == c, we have found another - // closing bracket, therefore we will need - // two opening brackets to complete the - // match. - char x = text.charAt(i); - if(x == c) - count++; - - // If text[i] == cprime, we have found a - // opening bracket, so we return i if - // --count == 0 - else if(x == cprime) - { - if(--count == 0) - return i; - } - } - } - else - { - // Count is 1 initially because we have already - // `found' one opening bracket - count = 1; - - // So we don't have to + 1 in every loop - offset++; - - // Number of characters to check - int len = doc.getLength() - offset; - - // Get text[offset+1,len]; - String text = doc.getText(offset,len); - - // Scan forwards - for(int i = 0; i < len; i++) - { - // If text[i] == c, we have found another - // opening bracket, therefore we will need - // two closing brackets to complete the - // match. - char x = text.charAt(i); - - if(x == c) - count++; - - // If text[i] == cprime, we have found an - // closing bracket, so we return i if - // --count == 0 - else if(x == cprime) - { - if(--count == 0) - return i + offset; - } - } - } - - // Nothing found - return -1; - } - - /** - * Locates the start of the word at the specified position. - * @param line The text - * @param pos The position - */ - public static int findWordStart(String line, int pos, String noWordSep) - { - char ch = line.charAt(pos - 1); - - if(noWordSep == null) - noWordSep = ""; - boolean selectNoLetter = (!Character.isLetterOrDigit(ch) - && noWordSep.indexOf(ch) == -1); - - int wordStart = 0; - for(int i = pos - 1; i >= 0; i--) - { - ch = line.charAt(i); - if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) && - noWordSep.indexOf(ch) == -1)) - { - wordStart = i + 1; - break; - } - } - - return wordStart; - } - - /** - * Locates the end of the word at the specified position. - * @param line The text - * @param pos The position - */ - public static int findWordEnd(String line, int pos, String noWordSep) - { - char ch = line.charAt(pos); - - if(noWordSep == null) - noWordSep = ""; - boolean selectNoLetter = (!Character.isLetterOrDigit(ch) - && noWordSep.indexOf(ch) == -1); - - int wordEnd = line.length(); - for(int i = pos; i < line.length(); i++) - { - ch = line.charAt(i); - if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) && - noWordSep.indexOf(ch) == -1)) - { - wordEnd = i; - break; - } - } - return wordEnd; - } -} diff --git a/app/src/processing/app/syntax/TokenMarker.java b/app/src/processing/app/syntax/TokenMarker.java deleted file mode 100644 index 9244556d3c7..00000000000 --- a/app/src/processing/app/syntax/TokenMarker.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * TokenMarker.java - Generic token marker - * Copyright (C) 1998, 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import javax.swing.text.Segment; - -/** - * A token marker that splits lines of text into tokens. Each token carries - * a length field and an indentification tag that can be mapped to a color - * for painting that token.

- * - * For performance reasons, the linked list of tokens is reused after each - * line is tokenized. Therefore, the return value of markTokens - * should only be used for immediate painting. Notably, it cannot be - * cached. - * - * @author Slava Pestov - */ -public abstract class TokenMarker -{ - /** - * A wrapper for the lower-level markTokensImpl method - * that is called to split a line up into tokens. - * @param line The line - * @param lineIndex The line number - */ - public Token markTokens(Segment line, int lineIndex) - { - if(lineIndex >= length) - { - throw new IllegalArgumentException("Tokenizing invalid line: " - + lineIndex); - } - - lastToken = null; - - LineInfo info = lineInfo[lineIndex]; - LineInfo prev; - if(lineIndex == 0) - prev = null; - else - prev = lineInfo[lineIndex - 1]; - - byte oldToken = info.token; - byte token = markTokensImpl(prev == null ? - Token.NULL : prev.token,line,lineIndex); - - info.token = token; - - /* - * This is a foul hack. It stops nextLineRequested - * from being cleared if the same line is marked twice. - * - * Why is this necessary? It's all JEditTextArea's fault. - * When something is inserted into the text, firing a - * document event, the insertUpdate() method shifts the - * caret (if necessary) by the amount inserted. - * - * All caret movement is handled by the select() method, - * which eventually pipes the new position to scrollTo() - * and calls repaint(). - * - * Note that at this point in time, the new line hasn't - * yet been painted; the caret is moved first. - * - * scrollTo() calls offsetToX(), which tokenizes the line - * unless it is being called on the last line painted - * (in which case it uses the text area's painter cached - * token list). What scrollTo() does next is irrelevant. - * - * After scrollTo() has done it's job, repaint() is - * called, and eventually we end up in paintLine(), whose - * job is to paint the changed line. It, too, calls - * markTokens(). - * - * The problem was that if the line started a multiline - * token, the first markTokens() (done in offsetToX()) - * would set nextLineRequested (because the line end - * token had changed) but the second would clear it - * (because the line was the same that time) and therefore - * paintLine() would never know that it needed to repaint - * subsequent lines. - * - * This bug took me ages to track down, that's why I wrote - * all the relevant info down so that others wouldn't - * duplicate it. - */ - if(!(lastLine == lineIndex && nextLineRequested)) - nextLineRequested = (oldToken != token); - - lastLine = lineIndex; - - addToken(0,Token.END); - - return firstToken; - } - - /** - * An abstract method that splits a line up into tokens. It - * should parse the line, and call addToken() to - * add syntax tokens to the token list. Then, it should return - * the initial token type for the next line.

- * - * For example if the current line contains the start of a - * multiline comment that doesn't end on that line, this method - * should return the comment token type so that it continues on - * the next line. - * - * @param token The initial token type for this line - * @param line The line to be tokenized - * @param lineIndex The index of the line in the document, - * starting at 0 - * @return The initial token type for the next line - */ - protected abstract byte markTokensImpl(byte token, Segment line, - int lineIndex); - - /** - * Returns if the token marker supports tokens that span multiple - * lines. If this is true, the object using this token marker is - * required to pass all lines in the document to the - * markTokens() method (in turn).

- * - * The default implementation returns true; it should be overridden - * to return false on simpler token markers for increased speed. - */ - public boolean supportsMultilineTokens() - { - return true; - } - - /** - * Informs the token marker that lines have been inserted into - * the document. This inserts a gap in the lineInfo - * array. - * @param index The first line number - * @param lines The number of lines - */ - public void insertLines(int index, int lines) - { - if(lines <= 0) - return; - length += lines; - ensureCapacity(length); - int len = index + lines; - System.arraycopy(lineInfo,index,lineInfo,len, - lineInfo.length - len); - - for(int i = index + lines - 1; i >= index; i--) - { - lineInfo[i] = new LineInfo(); - } - } - - /** - * Informs the token marker that line have been deleted from - * the document. This removes the lines in question from the - * lineInfo array. - * @param index The first line number - * @param lines The number of lines - */ - public void deleteLines(int index, int lines) - { - if (lines <= 0) - return; - int len = index + lines; - length -= lines; - System.arraycopy(lineInfo,len,lineInfo, - index,lineInfo.length - len); - } - - /** - * Returns the number of lines in this token marker. - */ - public int getLineCount() - { - return length; - } - - /** - * Returns true if the next line should be repainted. This - * will return true after a line has been tokenized that starts - * a multiline token that continues onto the next line. - */ - public boolean isNextLineRequested() - { - return nextLineRequested; - } - - // protected members - - /** - * The first token in the list. This should be used as the return - * value from markTokens(). - */ - protected Token firstToken; - - /** - * The last token in the list. New tokens are added here. - * This should be set to null before a new line is to be tokenized. - */ - protected Token lastToken; - - /** - * An array for storing information about lines. It is enlarged and - * shrunk automatically by the insertLines() and - * deleteLines() methods. - */ - protected LineInfo[] lineInfo; - - /** - * The number of lines in the model being tokenized. This can be - * less than the length of the lineInfo array. - */ - protected int length; - - /** - * The last tokenized line. - */ - protected int lastLine; - - /** - * True if the next line should be painted. - */ - protected boolean nextLineRequested; - - /** - * Creates a new TokenMarker. This DOES NOT create - * a lineInfo array; an initial call to insertLines() - * does that. - */ - protected TokenMarker() - { - lastLine = -1; - } - - /** - * Ensures that the lineInfo array can contain the - * specified index. This enlarges it if necessary. No action is - * taken if the array is large enough already.

- * - * It should be unnecessary to call this under normal - * circumstances; insertLine() should take care of - * enlarging the line info array automatically. - * - * @param index The array index - */ - protected void ensureCapacity(int index) - { - if(lineInfo == null) - lineInfo = new LineInfo[index + 1]; - else if(lineInfo.length <= index) - { - LineInfo[] lineInfoN = new LineInfo[(index + 1) * 2]; - System.arraycopy(lineInfo,0,lineInfoN,0, - lineInfo.length); - lineInfo = lineInfoN; - } - } - - /** - * Adds a token to the token list. - * @param length The length of the token - * @param id The id of the token - */ - protected void addToken(int length, byte id) - { - if(id >= Token.INTERNAL_FIRST && id <= Token.INTERNAL_LAST) - throw new InternalError("Invalid id: " + id); - - if(length == 0 && id != Token.END) - return; - - if(firstToken == null) - { - firstToken = new Token(length,id); - lastToken = firstToken; - } - else if(lastToken == null) - { - lastToken = firstToken; - firstToken.length = length; - firstToken.id = id; - } - else if(lastToken.next == null) - { - lastToken.next = new Token(length,id); - lastToken = lastToken.next; - } - else - { - lastToken = lastToken.next; - lastToken.length = length; - lastToken.id = id; - } - } - - /** - * Inner class for storing information about tokenized lines. - */ - public class LineInfo - { - /** - * Creates a new LineInfo object with token = Token.NULL - * and obj = null. - */ - public LineInfo() - { - } - - /** - * Creates a new LineInfo object with the specified - * parameters. - */ - public LineInfo(byte token, Object obj) - { - this.token = token; - this.obj = obj; - } - - /** - * The id of the last token of the line. - */ - public byte token; - - /** - * This is for use by the token marker implementations - * themselves. It can be used to store anything that - * is an object and that needs to exist on a per-line - * basis. - */ - public Object obj; - } -} diff --git a/app/src/processing/app/syntax/im/CompositionTextManager.java b/app/src/processing/app/syntax/im/CompositionTextManager.java deleted file mode 100644 index ba9ee155f7f..00000000000 --- a/app/src/processing/app/syntax/im/CompositionTextManager.java +++ /dev/null @@ -1,198 +0,0 @@ -package processing.app.syntax.im; - -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.font.FontRenderContext; -import java.awt.font.TextAttribute; -import java.awt.font.TextLayout; -import java.text.AttributedCharacterIterator; -import java.text.AttributedString; - -import javax.swing.text.BadLocationException; - -import processing.app.syntax.JEditTextArea; -import processing.app.syntax.TextAreaPainter; - -/** - * This class Manage texts from input method - * by begin-process-end steps. - * - * First, if a user start inputing via input method, - * beginCompositionText is called from InputMethodSupport. - * Second, the user continues from input method, processCompositionText is called - * and reflect user inputs to text area. - * Finally the user try to commit text, endCompositionText is called. - * - * @author Takashi Maekawa (takachin@generative.info) - */ - -public class CompositionTextManager { - private JEditTextArea textArea; - private String prevComposeString; - private int prevCommittedCount; - private boolean isInputProcess; - private int initialCaretPosition; - public static final int COMPOSING_UNDERBAR_HEIGHT = 5; - - /** - * Create text manager class with a textarea. - * @param textArea texarea component for PDE. - */ - public CompositionTextManager(JEditTextArea textArea) { - this.textArea = textArea; - prevComposeString = ""; - isInputProcess = false; - prevCommittedCount = 0; - } - - /** - * Get this text manager is whether in input process or not. - */ - public boolean getIsInputProcess() { - return isInputProcess; - } - /** - * Insert full width space - */ - public void insertFullWidthSpace() { - initialCaretPosition = textArea.getCaretPosition(); - int layoutCaretPosition = initialCaretPosition; - try { - textArea.getDocument().insertString(layoutCaretPosition, "\u3000", null); - } catch (BadLocationException e) { - e.printStackTrace(); - } - } - - /** - * Called when a user begins input from input method. - * This method initializes text manager. - * - * @param text Text from InputMethodEvent. - * @param commited_count Numbers of committed characters in text. - */ - public void beginCompositionText(AttributedCharacterIterator text, int committed_count) { - isInputProcess = true; - prevComposeString = ""; - initialCaretPosition = textArea.getCaretPosition(); - processCompositionText(text, committed_count); - } - - /** - * Called when a user processing input characters and - * select candidates from input method. - * - * @param text Text from InputMethodEvent. - * @param commited_count Numbers of committed characters in text. - */ - public void processCompositionText(AttributedCharacterIterator text, int committed_count) { - int layoutCaretPosition = initialCaretPosition + committed_count; - CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter(); - compositionPainter.setComposedTextLayout(getTextLayout(text, committed_count), layoutCaretPosition); - int textLength = text.getEndIndex() - text.getBeginIndex() - committed_count; - StringBuffer unCommitedStringBuf = new StringBuffer(textLength); - char c; - for (c = text.setIndex(committed_count); c != AttributedCharacterIterator.DONE - && textLength > 0; c = text.next(), --textLength) { - unCommitedStringBuf.append(c); - } - String unCommittedString = unCommitedStringBuf.toString(); - try { - if(canRemovePreviousInput(committed_count)){ - textArea.getDocument().remove(layoutCaretPosition, prevComposeString.length()); - } - textArea.getDocument().insertString(layoutCaretPosition, unCommittedString, null); - if(committed_count > 0){ - initialCaretPosition = initialCaretPosition + committed_count; - } - prevComposeString = unCommittedString; - prevCommittedCount = committed_count; - } catch (BadLocationException e) { - e.printStackTrace(); - } - } - - private boolean canRemovePreviousInput(int committed_count){ - return (prevCommittedCount == committed_count || prevCommittedCount > committed_count); - } - - /** - * Called when a user fixed text from input method or delete all - * composition text. This method resets CompositionTextPainter. - * - * @param text Text from InputMethodEvent. - * @param commited_count Numbers of committed characters in text. - */ - public void endCompositionText(AttributedCharacterIterator text, int committed_count) { - /* - * If there are no committed characters, remove it all from textarea. - * This case will happen if a user delete all composing characters by backspace or delete key. - * If it does, these previous characters are needed to be deleted. - */ - if(committed_count == 0){ - removeNotCommittedText(text); - } - CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter(); - compositionPainter.invalidateComposedTextLayout(initialCaretPosition + committed_count); - prevComposeString = ""; - isInputProcess = false; - } - - private void removeNotCommittedText(AttributedCharacterIterator text){ - if (prevComposeString.length() == 0) { - return; - } - try { - textArea.getDocument().remove(initialCaretPosition, prevComposeString.length()); - } catch (BadLocationException e) { - e.printStackTrace(); - } - } - - private TextLayout getTextLayout(AttributedCharacterIterator text, int committed_count) { - AttributedString composed = new AttributedString(text, committed_count, text.getEndIndex()); - Font font = textArea.getPainter().getFont(); - FontRenderContext context = ((Graphics2D) (textArea.getPainter().getGraphics())).getFontRenderContext(); - composed.addAttribute(TextAttribute.FONT, font); - TextLayout layout = new TextLayout(composed.getIterator(), context); - return layout; - } - - private Point getCaretLocation() { - Point loc = new Point(); - TextAreaPainter painter = textArea.getPainter(); - FontMetrics fm = painter.getFontMetrics(); - int offsetY = fm.getHeight() - COMPOSING_UNDERBAR_HEIGHT; - int lineIndex = textArea.getCaretLine(); - loc.y = lineIndex * fm.getHeight() + offsetY; - int offsetX = textArea.getCaretPosition() - - textArea.getLineStartOffset(lineIndex); - loc.x = textArea.offsetToX(lineIndex, offsetX); - return loc; - } - - public Rectangle getTextLocation() { - Point caret = getCaretLocation(); - return getCaretRectangle(caret.x, caret.y); - } - - private Rectangle getCaretRectangle(int x, int y) { - TextAreaPainter painter = textArea.getPainter(); - Point origin = painter.getLocationOnScreen(); - int height = painter.getFontMetrics().getHeight(); - return new Rectangle(origin.x + x, origin.y + y, 0, height); - } - - public AttributedCharacterIterator getCommittedText(int beginIndex, int endIndex) { - int length = endIndex - beginIndex; - String textAreaString = textArea.getText(beginIndex, length); - return new AttributedString(textAreaString).getIterator(); - } - - public int getInsertPositionOffset() { - return textArea.getCaretPosition() * -1; - } -} diff --git a/app/src/processing/app/syntax/im/CompositionTextPainter.java b/app/src/processing/app/syntax/im/CompositionTextPainter.java deleted file mode 100644 index 0084f491f92..00000000000 --- a/app/src/processing/app/syntax/im/CompositionTextPainter.java +++ /dev/null @@ -1,124 +0,0 @@ -package processing.app.syntax.im; - -import java.awt.Color; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.font.TextLayout; - -import processing.app.syntax.JEditTextArea; -import processing.app.syntax.TextAreaPainter; - -/** - * Paint texts from input method. Text via input method are transmitted by - * AttributedCaharacterIterator. This class helps the PDE's TextAreaPainter - * to handle AttributedCaharacterIterator. - * - * For practical purposes, paint to textarea is done by TextLayout class. - * Because TextLayout class is easy to draw composing texts. (For example, - * draw underline composing texts, focus when select from candidates text.) - * - * @author Takashi Maekawa (takachin@generative.info) - */ -public class CompositionTextPainter { - private TextLayout composedTextLayout; - private int composedBeginCaretPosition = 0; - private JEditTextArea textArea; - - /** - * Constructor for painter. - * @param textarea textarea used by PDE. - */ - public CompositionTextPainter(JEditTextArea textArea) { - this.textArea = textArea; - composedTextLayout = null; - } - - /** - * Check the painter has TextLayout. - * If a user input via InputMethod, this result will return true. - * @param textarea textarea used by PDE. - */ - public boolean hasComposedTextLayout() { - return (composedTextLayout != null); - } - - /** - * Set TextLayout to the painter. - * TextLayout will be created and set by CompositionTextManager. - * - * @see CompositionTextManager - * @param textarea textarea used by PDE. - */ - public void setComposedTextLayout(TextLayout composedTextLayout, int composedStartCaretPosition) { - this.composedTextLayout = composedTextLayout; - this.composedBeginCaretPosition = composedStartCaretPosition; - } - - /** - * Invalidate this TextLayout to set null. - * If a user end input via InputMethod, this method will called from CompositionTextManager.endCompositionText - */ - public void invalidateComposedTextLayout(int composedEndCaretPosition) { - this.composedTextLayout = null; - this.composedBeginCaretPosition = composedEndCaretPosition; - //this.composedBeginCaretPosition = textArea.getCaretPosition(); - } - - /** - * Draw text via input method with composed text information. - * This method can draw texts with some underlines to illustrate converting characters. - * - * This method is workaround for TextAreaPainter. - * Because, TextAreaPainter can't treat AttributedCharacterIterator directly. - * AttributedCharacterIterator has very important information when composing text. - * It has a map where are converted characters and committed characters. - * Ideally, changing TextAreaPainter method can treat AttributedCharacterIterator is better. But it's very tough!! - * So I choose to write some code as a workaround. - * - * This draw method is proceeded with the following steps. - * 1. Original TextAreaPainter draws characters. - * 2. This refillComposedArea method erase previous paint characters by textarea's background color. - * The refill area is only square that width and height defined by characters with input method. - * 3. CompositionTextPainter.draw method paints composed text. It was actually drawn by TextLayout. - * - * @param gfx set TextAreaPainter's Graphics object. - * @param fillBackGroundColor set textarea's background. - */ - public void draw(Graphics gfx, Color fillBackGroundColor) { - assert(composedTextLayout != null); - Point composedLoc = getCaretLocation(); - refillComposedArea(fillBackGroundColor, composedLoc.x, composedLoc.y); - composedTextLayout.draw((Graphics2D) gfx, composedLoc.x, composedLoc.y); - } - - /** - * Fill color to erase characters drawn by original TextAreaPainter. - * - * @param fillColor fill color to erase characters drawn by original TextAreaPainter method. - * @param x x-coordinate where to fill. - * @param y y-coordinate where to fill. - */ - private void refillComposedArea(Color fillColor, int x, int y) { - Graphics gfx = textArea.getPainter().getGraphics(); - gfx.setColor(fillColor); - FontMetrics fm = textArea.getPainter().getFontMetrics(); - int newY = y - (fm.getHeight() - CompositionTextManager.COMPOSING_UNDERBAR_HEIGHT); - int paintHeight = fm.getHeight(); - int paintWidth = (int) composedTextLayout.getBounds().getWidth(); - gfx.fillRect(x, newY, paintWidth, paintHeight); - } - - private Point getCaretLocation() { - Point loc = new Point(); - TextAreaPainter painter = textArea.getPainter(); - FontMetrics fm = painter.getFontMetrics(); - int offsetY = fm.getHeight() - CompositionTextManager.COMPOSING_UNDERBAR_HEIGHT; - int lineIndex = textArea.getCaretLine(); - loc.y = lineIndex * fm.getHeight() + offsetY; - int offsetX = composedBeginCaretPosition - textArea.getLineStartOffset(lineIndex); - loc.x = textArea.offsetToX(lineIndex, offsetX); - return loc; - } -} diff --git a/app/src/processing/app/syntax/im/InputMethodSupport.java b/app/src/processing/app/syntax/im/InputMethodSupport.java deleted file mode 100644 index 461be3d1677..00000000000 --- a/app/src/processing/app/syntax/im/InputMethodSupport.java +++ /dev/null @@ -1,120 +0,0 @@ -package processing.app.syntax.im; - -import java.awt.Rectangle; -import java.awt.event.InputMethodEvent; -import java.awt.event.InputMethodListener; -import java.awt.font.TextHitInfo; -import java.awt.im.InputMethodRequests; -import java.text.AttributedCharacterIterator; - -import processing.app.syntax.JEditTextArea; - -/** - * Support in-line Japanese input for PDE. (Maybe Chinese, Korean and more) - * This class is implemented by Java Input Method Framework and handles - * If you would like to know more about Java Input Method Framework, - * Please see http://java.sun.com/j2se/1.5.0/docs/guide/imf/ - * - * This class is implemented to fix Bug #854. - * http://dev.processing.org/bugs/show_bug.cgi?id=854 - * - * @author Takashi Maekawa (takachin@generative.info) - */ -public class InputMethodSupport implements InputMethodRequests, - InputMethodListener { - - private int committed_count = 0; - private CompositionTextManager textManager; - - public InputMethodSupport(JEditTextArea textArea) { - textManager = new CompositionTextManager(textArea); - textArea.enableInputMethods(true); - textArea.addInputMethodListener(this); - } - - public Rectangle getTextLocation(TextHitInfo offset) { - return textManager.getTextLocation(); - } - - public TextHitInfo getLocationOffset(int x, int y) { - return null; - } - - public int getInsertPositionOffset() { - return textManager.getInsertPositionOffset(); - } - - public AttributedCharacterIterator getCommittedText(int beginIndex, - int endIndex, AttributedCharacterIterator.Attribute[] attributes) { - return textManager.getCommittedText(beginIndex, endIndex); - } - - public int getCommittedTextLength() { - return committed_count; - } - - public AttributedCharacterIterator cancelLatestCommittedText( - AttributedCharacterIterator.Attribute[] attributes) { - return null; - } - - public AttributedCharacterIterator getSelectedText( - AttributedCharacterIterator.Attribute[] attributes) { - return null; - } - - /** - * Handles events from InputMethod. - * This method judges whether beginning of input or - * progress of input or end and call related method. - * - * @param event event from Input Method. - */ - public void inputMethodTextChanged(InputMethodEvent event) { - AttributedCharacterIterator text = event.getText(); - committed_count = event.getCommittedCharacterCount(); - if(isFullWidthSpaceInput(text)){ - textManager.insertFullWidthSpace(); - caretPositionChanged(event); - return; - } - if(isBeginInputProcess(text, textManager)){ - textManager.beginCompositionText(text, committed_count); - caretPositionChanged(event); - return; - } - if (isInputProcess(text)){ - textManager.processCompositionText(text, committed_count); - caretPositionChanged(event); - return; - } - textManager.endCompositionText(text, committed_count); - caretPositionChanged(event); - } - - private boolean isFullWidthSpaceInput(AttributedCharacterIterator text){ - if(text == null) - return false; - if(textManager.getIsInputProcess()) - return false; - return (String.valueOf(text.first()).equals("\u3000")); - } - - private boolean isBeginInputProcess(AttributedCharacterIterator text, CompositionTextManager textManager){ - if(text == null) - return false; - if(textManager.getIsInputProcess()) - return false; - return (isInputProcess(text)); - } - - private boolean isInputProcess(AttributedCharacterIterator text){ - if(text == null) - return false; - return (text.getEndIndex() - (text.getBeginIndex() + committed_count) > 0); - } - - public void caretPositionChanged(InputMethodEvent event) { - event.consume(); - } -} diff --git a/app/src/processing/app/syntax/readme.txt b/app/src/processing/app/syntax/readme.txt deleted file mode 100644 index 07a825cd7bb..00000000000 --- a/app/src/processing/app/syntax/readme.txt +++ /dev/null @@ -1,46 +0,0 @@ -OLDSYNTAX PACKAGE README - -I am placing the jEdit 2.2.1 syntax highlighting package in the public -domain. This means it can be integrated into commercial programs, etc. - -This package requires at least Java 1.1 and Swing 1.1. Syntax -highlighting for the following file types is supported: - -- C++, C -- CORBA IDL -- Eiffel -- HTML -- Java -- Java properties -- JavaScript -- MS-DOS INI -- MS-DOS batch files -- Makefile -- PHP -- Perl -- Python -- TeX -- Transact-SQL -- Unix patch/diff -- Unix shell script -- XML - -This package is undocumented; read the source (start by taking a look at -JEditTextArea.java) to find out how to use it; it's really simple. Feel -free to e-mail questions, queries, etc. to me, but keep in mind that -this code is very old and I no longer maintain it. So if you find a bug, -don't bother me about it; fix it yourself. - -* Copyright - -The jEdit 2.2.1 syntax highlighting package contains code that is -Copyright 1998-1999 Slava Pestov, Artur Biesiadowski, Clancy Malcolm, -Jonathan Revusky, Juha Lindfors and Mike Dillon. - -You may use and modify this package for any purpose. Redistribution is -permitted, in both source and binary form, provided that this notice -remains intact in all source distributions of this package. - --- Slava Pestov -25 September 2000 - diff --git a/app/src/processing/app/tools/DiscourseFormat.java b/app/src/processing/app/tools/DiscourseFormat.java index a4a381c5a22..75240cb6947 100644 --- a/app/src/processing/app/tools/DiscourseFormat.java +++ b/app/src/processing/app/tools/DiscourseFormat.java @@ -25,11 +25,14 @@ 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 @@ *

* Updated for 0144 to only format the selected lines. *

+ * Updated for 1.5.8 - Simplification, using RSyntaxTextArea TokenImpl formatter (08 dec 2014 - Ricardo JL Rufino) + *

* 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 void show() { StringBuilder cf = new StringBuilder(html ? "

\n" : "[code]\n");
 
     int selStart = textarea.getSelectionStart();
-    int selStop = textarea.getSelectionStop();
-
-    int startLine = textarea.getSelectionStartLine();
-    int stopLine = textarea.getSelectionStopLine();
+    int selStop = textarea.getSelectionEnd();
+
+    int startLine;
+    int stopLine;
+    try {
+      startLine = textarea.getLineOfOffset(selStart);
+      stopLine = textarea.getLineOfOffset(selStop);
+    } catch (BadLocationException e) {
+      return;
+    }
 
     // If no selection, convert all the lines
     if (selStart == selStop) {
@@ -89,8 +100,11 @@ public void show() {
       stopLine = textarea.getLineCount() - 1;
     } else {
       // Make sure the selection doesn't end at the beginning of the last line
-      if (textarea.getLineStartOffset(stopLine) == selStop) {
-        stopLine--;
+      try {
+        if (textarea.getLineStartOffset(stopLine) == selStop) {
+          stopLine--;
+        }
+      } catch (BadLocationException e) {
       }
     }
 
@@ -139,23 +153,15 @@ private void appendToHTML(char c, StringBuilder buffer) {
   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 void appendFormattedLine(StringBuilder cf, int line) {
       }
 
     } else {
-      // If syntax coloring is enabled, we have to do this
-      // because tokens can vary in width
-      Token tokens;
-      if ((painter.getCurrentLineIndex() == line) &&
-          (painter.getCurrentLineTokens() != null)) {
-        tokens = painter.getCurrentLineTokens();
-
-      } else {
-        painter.setCurrentLineIndex(line);
-        painter.setCurrentLineTokens(tokenMarker.markTokens(segment, line));
-        tokens = painter.getCurrentLineTokens();
-      }
-
-      int offset = 0;
-//      Font defaultFont = painter.getFont();
-      SyntaxStyle[] styles = painter.getStyles();
-
-      for (;;) {
-        byte id = tokens.id;
-        if (id == Token.END) {
-          char c = segmentArray[segmentOffset + offset];
-          if (segmentOffset + offset < limit) {
-            appendToHTML(c, cf);
-          } else {
-            cf.append('\n');
-          }
-          return; // cf.toString();
-        }
-        if (id == Token.NULL) {
-//          fm = painter.getFontMetrics();
-        } else {
-          // Place open tags []
-          if (html) {
-            cf.append("");
-          }
-
-          if (html && styles[id].isBold())
-            cf.append("");
-
-//          fm = styles[id].getFontMetrics(defaultFont);
-        }
-        int length = tokens.length;
-
-        for (int j = 0; j < length; j++) {
-          char c = segmentArray[segmentOffset + offset + j];
-          if (offset == 0 && c == ' ') {
-            // Works on Safari but not Camino 1.6.3 or Firefox 2.x on OS X.
-            cf.append(html ? " " : '\u00A0');  //  
-//            if ((j % 2) == 1) {
-//              cf.append("[b]\u00A0[/b]");
-//            } else {
-//              cf.append(' ');
-//            }
-          } else {
-            appendToHTML(c, cf);
-          }
-          // Place close tags [/]
-          if (html && j == (length - 1) && id != Token.NULL && styles[id].isBold())
-            cf.append("");
-          if (html && j == (length - 1) && id != Token.NULL)
-            cf.append("");
-//          int charWidth;
-//          if (c == '\t') {
-//            charWidth = (int) painter
-//              .nextTabStop(width, offset + j)
-//              - width;
-//          } else {
-//            charWidth = fm.charWidth(c);
-//          }
-//          width += charWidth;
+      
+      Token tokenList = textarea.getTokenListForLine(line);
+      
+      while(tokenList != null){
+        if(tokenList.getType() == Token.NULL){
+          cf.append('\n');
+        }else if(tokenList.isPaintable()){
+          tokenList.appendHTMLRepresentation(cf, textarea, false);
         }
-        offset += length;
-        tokens = tokens.next;
+        
+        tokenList = tokenList.getNextToken();
       }
+  
     }
   }
 }
diff --git a/app/test/processing/app/AbstractWithPreferencesTest.java b/app/test/processing/app/AbstractWithPreferencesTest.java
index b3124929c79..4f176471dfb 100644
--- a/app/test/processing/app/AbstractWithPreferencesTest.java
+++ b/app/test/processing/app/AbstractWithPreferencesTest.java
@@ -41,6 +41,8 @@ public void init() throws Exception {
     Preferences.init(null);
     Theme.init();
 
+    BaseNoGui.initPackages();
+
     Base.untitledFolder = Base.createTempFolder("untitled");
     DeleteFilesOnShutdown.add(Base.untitledFolder);
   }
diff --git a/app/test/processing/app/AutoformatProducesOneUndoActionTest.java b/app/test/processing/app/AutoformatProducesOneUndoActionTest.java
new file mode 100644
index 00000000000..9db88512b1d
--- /dev/null
+++ b/app/test/processing/app/AutoformatProducesOneUndoActionTest.java
@@ -0,0 +1,56 @@
+package processing.app;
+
+import org.fest.swing.fixture.JMenuItemFixture;
+import org.junit.Test;
+import processing.app.helpers.RSyntaxTextAreaFixture;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+public class AutoformatProducesOneUndoActionTest extends AbstractGUITest {
+
+  public static final String SOURCE_BEFORE = "void setup() {\n" +
+          "              // put your setup code here, to run once:\n" +
+          "\n" +
+          "}\n" +
+          "\n" +
+          "void loop() {\n" +
+          "  // put your main code here, to run repeatedly:\n" +
+          "\n" +
+          "}";
+  public static final String SOURCE_AFTER = "void setup() {\n" +
+          "  // put your setup code here, to run once:\n" +
+          "\n" +
+          "}\n" +
+          "\n" +
+          "void loop() {\n" +
+          "  // put your main code here, to run repeatedly:\n" +
+          "\n" +
+          "}";
+
+  @Test
+  public void shouldSaveCaretPositionAfterAutoformat() {
+    JMenuItemFixture menuEditUndo = window.menuItem("menuEditUndo");
+    menuEditUndo.requireDisabled();
+
+    JMenuItemFixture menuToolsAutoFormat = window.menuItem("menuToolsAutoFormat");
+    menuToolsAutoFormat.requireEnabled();
+
+    RSyntaxTextAreaFixture editor = window.RSyntaxTextArea("editor");
+    editor.setText(SOURCE_BEFORE);
+
+    editor.setCaretPosition(29); // right before the first // (double slash)
+
+    menuToolsAutoFormat.click();
+
+    String formattedText = editor.getText();
+    assertEquals(SOURCE_AFTER, formattedText);
+
+    assertEquals(29, editor.getCaretPosition());
+
+    menuEditUndo.requireEnabled();
+    menuEditUndo.click();
+    assertEquals(SOURCE_BEFORE, editor.getText());
+  }
+
+}
diff --git a/app/test/processing/app/AutoformatSavesCaretPositionTest.java b/app/test/processing/app/AutoformatSavesCaretPositionTest.java
index 52112c46f2b..afc80a856fb 100644
--- a/app/test/processing/app/AutoformatSavesCaretPositionTest.java
+++ b/app/test/processing/app/AutoformatSavesCaretPositionTest.java
@@ -31,7 +31,7 @@
 
 import org.fest.swing.fixture.JMenuItemFixture;
 import org.junit.Test;
-import processing.app.helpers.JEditTextAreaFixture;
+import processing.app.helpers.RSyntaxTextAreaFixture;
 
 import static org.junit.Assert.assertEquals;
 
@@ -42,7 +42,7 @@ public void shouldSaveCaretPositionAfterAutoformat() {
     JMenuItemFixture menuToolsAutoFormat = window.menuItem("menuToolsAutoFormat");
     menuToolsAutoFormat.requireEnabled();
 
-    JEditTextAreaFixture editor = window.jEditTextArea("editor");
+    RSyntaxTextAreaFixture editor = window.RSyntaxTextArea("editor");
     editor.setText("void setup() {\n" +
             "              // put your setup code here, to run once:\n" +
             "\n" +
diff --git a/app/test/processing/app/AutoformatTest.java b/app/test/processing/app/AutoformatTest.java
index b5c766bec0d..b84d054fb28 100644
--- a/app/test/processing/app/AutoformatTest.java
+++ b/app/test/processing/app/AutoformatTest.java
@@ -31,7 +31,7 @@
 
 import org.fest.swing.fixture.JMenuItemFixture;
 import org.junit.Test;
-import processing.app.helpers.JEditTextAreaFixture;
+import processing.app.helpers.RSyntaxTextAreaFixture;
 
 import static org.junit.Assert.assertEquals;
 
@@ -42,7 +42,7 @@ public void shouldProduceNicelyFormattedCode() throws Exception {
     JMenuItemFixture menuToolsAutoFormat = window.menuItem("menuToolsAutoFormat");
     menuToolsAutoFormat.requireEnabled();
 
-    JEditTextAreaFixture editor = window.jEditTextArea("editor");
+    RSyntaxTextAreaFixture editor = window.RSyntaxTextArea("editor");
     editor.setText("void setup() {\n" +
             "// put your setup code here, to run once:\n" +
             "int foo[] = { 1, 2, 3, 4, 5};\n" +
diff --git a/app/test/processing/app/BlockCommentGeneratesOneUndoActionTest.java b/app/test/processing/app/BlockCommentGeneratesOneUndoActionTest.java
index e0a2965d00e..1e496b2890d 100644
--- a/app/test/processing/app/BlockCommentGeneratesOneUndoActionTest.java
+++ b/app/test/processing/app/BlockCommentGeneratesOneUndoActionTest.java
@@ -29,15 +29,16 @@
 
 package processing.app;
 
+import static org.junit.Assert.assertEquals;
+
+import java.awt.Frame;
+
 import org.fest.swing.edt.GuiActionRunner;
 import org.fest.swing.edt.GuiQuery;
 import org.fest.swing.fixture.JMenuItemFixture;
 import org.junit.Test;
-import processing.app.helpers.JEditTextAreaFixture;
 
-import java.awt.*;
-
-import static org.junit.Assert.assertEquals;
+import processing.app.helpers.RSyntaxTextAreaFixture;
 
 public class BlockCommentGeneratesOneUndoActionTest extends AbstractGUITest {
 
@@ -46,7 +47,7 @@ public void shouldUndoAndRedo() throws Exception {
     JMenuItemFixture menuEditUndo = window.menuItem("menuEditUndo");
     menuEditUndo.requireDisabled();
 
-    JEditTextAreaFixture jEditTextArea = window.jEditTextArea("editor");
+    RSyntaxTextAreaFixture jEditTextArea = window.RSyntaxTextArea("editor");
     String previousText = jEditTextArea.getText();
 
     jEditTextArea.selectAll();
diff --git a/app/test/processing/app/HittingEscapeOnCloseConfirmationDialogTest.java b/app/test/processing/app/HittingEscapeOnCloseConfirmationDialogTest.java
index 447ffd8f3dd..2bf3d3d7c0a 100644
--- a/app/test/processing/app/HittingEscapeOnCloseConfirmationDialogTest.java
+++ b/app/test/processing/app/HittingEscapeOnCloseConfirmationDialogTest.java
@@ -33,7 +33,7 @@
 import org.fest.swing.finder.WindowFinder;
 import org.fest.swing.fixture.DialogFixture;
 import org.junit.Test;
-import processing.app.helpers.JEditTextAreaFixture;
+import processing.app.helpers.RSyntaxTextAreaFixture;
 
 import javax.swing.*;
 import java.awt.event.KeyEvent;
@@ -44,7 +44,7 @@ public class HittingEscapeOnCloseConfirmationDialogTest extends AbstractGUITest
 
   @Test
   public void shouldJustCloseTheDialog() throws Exception {
-    JEditTextAreaFixture editor = window.jEditTextArea("editor");
+    RSyntaxTextAreaFixture editor = window.RSyntaxTextArea("editor");
     editor.setText("test");
 
     window.close();
diff --git a/app/test/processing/app/ReduceIndentWith1CharOnLastLineTest.java b/app/test/processing/app/ReduceIndentWith1CharOnLastLineTest.java
index c8623cc877a..894089bcbf2 100644
--- a/app/test/processing/app/ReduceIndentWith1CharOnLastLineTest.java
+++ b/app/test/processing/app/ReduceIndentWith1CharOnLastLineTest.java
@@ -34,7 +34,7 @@
 import org.fest.swing.fixture.JMenuItemFixture;
 import org.junit.Test;
 
-import processing.app.helpers.JEditTextAreaFixture;
+import processing.app.helpers.RSyntaxTextAreaFixture;
 
 public class ReduceIndentWith1CharOnLastLineTest extends AbstractGUITest {
 
@@ -42,7 +42,7 @@ public class ReduceIndentWith1CharOnLastLineTest extends AbstractGUITest {
   public void shouldJustCloseTheDialog() throws Exception {
     JMenuItemFixture menuDecreaseIndent = window.menuItem("menuDecreaseIndent");
 
-    JEditTextAreaFixture editor = window.jEditTextArea("editor");
+    RSyntaxTextAreaFixture editor = window.RSyntaxTextArea("editor");
     editor.setText("void loop()\n{\n  Serial.begin(9600)\n}");
 
     editor.selectAll();
diff --git a/app/test/processing/app/ReplacingTextGeneratesTwoUndoActionsTest.java b/app/test/processing/app/ReplacingTextGeneratesTwoUndoActionsTest.java
index f7291b51f28..565c1b0274a 100644
--- a/app/test/processing/app/ReplacingTextGeneratesTwoUndoActionsTest.java
+++ b/app/test/processing/app/ReplacingTextGeneratesTwoUndoActionsTest.java
@@ -31,7 +31,7 @@
 
 import org.fest.swing.fixture.JMenuItemFixture;
 import org.junit.Test;
-import processing.app.helpers.JEditTextAreaFixture;
+import processing.app.helpers.RSyntaxTextAreaFixture;
 
 import static org.junit.Assert.assertEquals;
 
@@ -44,19 +44,19 @@ public void shouldUndoAndRedo() throws Exception {
     JMenuItemFixture menuEditRedo = window.menuItem("menuEditRedo");
     menuEditRedo.requireDisabled();
 
-    JEditTextAreaFixture jEditTextArea = window.jEditTextArea("editor");
+    RSyntaxTextAreaFixture RSyntaxTextArea = window.RSyntaxTextArea("editor");
 
-    jEditTextArea.setText("fake text");
+    RSyntaxTextArea.setText("fake text");
 
     menuEditUndo.requireEnabled();
     menuEditUndo.click();
 
-    assertEquals("", jEditTextArea.getText());
+    assertEquals("", RSyntaxTextArea.getText());
 
     menuEditRedo.requireEnabled();
     menuEditRedo.click();
 
-    //assertEquals("fake text", jEditTextArea.getText());
+    //assertEquals("fake text", RSyntaxTextArea.getText());
 
     menuEditUndo.requireEnabled();
     menuEditUndo.click();
diff --git a/app/test/processing/app/helpers/ArduinoFrameFixture.java b/app/test/processing/app/helpers/ArduinoFrameFixture.java
index 0d425289b6a..2d3528064c3 100644
--- a/app/test/processing/app/helpers/ArduinoFrameFixture.java
+++ b/app/test/processing/app/helpers/ArduinoFrameFixture.java
@@ -30,8 +30,9 @@
 package processing.app.helpers;
 
 import org.fest.swing.fixture.FrameFixture;
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
+
 import processing.app.Editor;
-import processing.app.syntax.JEditTextArea;
 
 public class ArduinoFrameFixture extends FrameFixture {
 
@@ -42,8 +43,8 @@ public ArduinoFrameFixture(Editor editor) {
     this.editor = editor;
   }
 
-  public JEditTextAreaFixture jEditTextArea(String name) {
-    return new JEditTextAreaFixture(robot, (JEditTextArea) this.robot.finder().find(new JEditTextAreaComponentMatcher(name)));
+  public RSyntaxTextAreaFixture RSyntaxTextArea(String name) {
+    return new RSyntaxTextAreaFixture(robot, (RSyntaxTextArea) this.robot.finder().find(new RSyntaxTextAreaComponentMatcher(name)));
   }
 
   public Editor getEditor() {
diff --git a/app/test/processing/app/helpers/JEditTextAreaComponentDriver.java b/app/test/processing/app/helpers/JEditTextAreaComponentDriver.java
deleted file mode 100644
index 22ef7848c5d..00000000000
--- a/app/test/processing/app/helpers/JEditTextAreaComponentDriver.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * This file is part of Arduino.
- *
- * Copyright 2015 Arduino LLC (http://www.arduino.cc/)
- *
- * Arduino 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * As a special exception, you may use this file as part of a free software
- * library without restriction.  Specifically, if other files instantiate
- * templates or use macros or inline functions from this file, or you compile
- * this file and link it with other files to produce an executable, this
- * file does not by itself cause the resulting executable to be covered by
- * the GNU General Public License.  This exception does not however
- * invalidate any other reasons why the executable file might be covered by
- * the GNU General Public License.
- */
-
-package processing.app.helpers;
-
-import org.fest.swing.core.Robot;
-import org.fest.swing.driver.JComponentDriver;
-import org.fest.swing.edt.GuiActionRunner;
-import org.fest.swing.edt.GuiQuery;
-import processing.app.syntax.JEditTextArea;
-
-public class JEditTextAreaComponentDriver extends JComponentDriver {
-
-  public JEditTextAreaComponentDriver(Robot robot) {
-    super(robot);
-  }
-
-  public void enterText(JEditTextArea target, String text) {
-    focusAndWaitForFocusGain(target);
-    robot.enterText(text);
-  }
-
-  public void setText(final JEditTextArea target, final String text) {
-    focusAndWaitForFocusGain(target);
-    GuiActionRunner.execute(new GuiQuery() {
-
-      protected JEditTextArea executeInEDT() {
-        target.setText(text);
-        return target;
-      }
-
-    });
-    robot.waitForIdle();
-  }
-
-  public String getText(final JEditTextArea target) {
-    focusAndWaitForFocusGain(target);
-    return GuiActionRunner.execute(new GuiQuery() {
-
-      protected String executeInEDT() {
-        return target.getText();
-      }
-
-    });
-  }
-
-  public JEditTextArea selectAll(final JEditTextArea target) {
-    return GuiActionRunner.execute(new GuiQuery() {
-
-      protected JEditTextArea executeInEDT() {
-        target.selectAll();
-        return target;
-      }
-
-    });
-  }
-
-  public Integer getCaretPosition(final JEditTextArea target) {
-    focusAndWaitForFocusGain(target);
-    return GuiActionRunner.execute(new GuiQuery() {
-
-      protected Integer executeInEDT() {
-        return target.getCaretPosition();
-      }
-
-    });
-  }
-
-  public void setCaretPosition(final JEditTextArea target, final int caretPosition) {
-    focusAndWaitForFocusGain(target);
-    GuiActionRunner.execute(new GuiQuery() {
-
-      protected JEditTextArea executeInEDT() {
-        target.setCaretPosition(caretPosition);
-        return target;
-      }
-
-    });
-    robot.waitForIdle();
-  }
-
-}
diff --git a/app/test/processing/app/helpers/RSyntaxTextAreaComponentDriver.java b/app/test/processing/app/helpers/RSyntaxTextAreaComponentDriver.java
new file mode 100644
index 00000000000..73d2694de8c
--- /dev/null
+++ b/app/test/processing/app/helpers/RSyntaxTextAreaComponentDriver.java
@@ -0,0 +1,79 @@
+package processing.app.helpers;
+
+import org.fest.swing.core.Robot;
+import org.fest.swing.driver.JComponentDriver;
+import org.fest.swing.edt.GuiActionRunner;
+import org.fest.swing.edt.GuiQuery;
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
+
+public class RSyntaxTextAreaComponentDriver extends JComponentDriver {
+
+  public RSyntaxTextAreaComponentDriver(Robot robot) {
+    super(robot);
+  }
+
+  public void enterText(RSyntaxTextArea target, String text) {
+    focusAndWaitForFocusGain(target);
+    robot.enterText(text);
+  }
+
+  public void setText(final RSyntaxTextArea target, final String text) {
+    focusAndWaitForFocusGain(target);
+    GuiActionRunner.execute(new GuiQuery() {
+
+      protected RSyntaxTextArea executeInEDT() {
+        target.setText(text);
+        return target;
+      }
+
+    });
+    robot.waitForIdle();
+  }
+
+  public String getText(final RSyntaxTextArea target) {
+    focusAndWaitForFocusGain(target);
+    return GuiActionRunner.execute(new GuiQuery() {
+
+      protected String executeInEDT() {
+        return target.getText();
+      }
+
+    });
+  }
+
+  public RSyntaxTextArea selectAll(final RSyntaxTextArea target) {
+    return GuiActionRunner.execute(new GuiQuery() {
+
+      protected RSyntaxTextArea executeInEDT() {
+        target.selectAll();
+        return target;
+      }
+
+    });
+  }
+
+  public Integer getCaretPosition(final RSyntaxTextArea target) {
+    focusAndWaitForFocusGain(target);
+    return GuiActionRunner.execute(new GuiQuery() {
+
+      protected Integer executeInEDT() {
+        return target.getCaretPosition();
+      }
+
+    });
+  }
+
+  public void setCaretPosition(final RSyntaxTextArea target, final int caretPosition) {
+    focusAndWaitForFocusGain(target);
+    GuiActionRunner.execute(new GuiQuery() {
+
+      protected RSyntaxTextArea executeInEDT() {
+        target.setCaretPosition(caretPosition);
+        return target;
+      }
+
+    });
+    robot.waitForIdle();
+  }
+
+}
diff --git a/app/test/processing/app/helpers/JEditTextAreaComponentMatcher.java b/app/test/processing/app/helpers/RSyntaxTextAreaComponentMatcher.java
similarity index 80%
rename from app/test/processing/app/helpers/JEditTextAreaComponentMatcher.java
rename to app/test/processing/app/helpers/RSyntaxTextAreaComponentMatcher.java
index ad6f0ce77f4..43f08710508 100644
--- a/app/test/processing/app/helpers/JEditTextAreaComponentMatcher.java
+++ b/app/test/processing/app/helpers/RSyntaxTextAreaComponentMatcher.java
@@ -1,7 +1,7 @@
 /*
  * This file is part of Arduino.
  *
- * Copyright 2015 Arduino LLC (http://www.arduino.cc/)
+ * Copyright 2015 Ricardo JL Rufino (ricardo@criativasoft.com.br)
  *
  * Arduino is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,21 +29,21 @@
 
 package processing.app.helpers;
 
-import org.fest.swing.core.ComponentMatcher;
-import processing.app.syntax.JEditTextArea;
+import java.awt.Component;
 
-import java.awt.*;
+import org.fest.swing.core.ComponentMatcher;
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
 
-public class JEditTextAreaComponentMatcher implements ComponentMatcher {
+public class RSyntaxTextAreaComponentMatcher implements ComponentMatcher {
 
   private final String name;
 
-  public JEditTextAreaComponentMatcher(String name) {
+  public RSyntaxTextAreaComponentMatcher(String name) {
     this.name = name;
   }
 
   @Override
   public boolean matches(Component component) {
-    return component instanceof JEditTextArea && name.equals(component.getName());
+    return component instanceof RSyntaxTextArea && name.equals(component.getName());
   }
 }
diff --git a/app/test/processing/app/helpers/JEditTextAreaFixture.java b/app/test/processing/app/helpers/RSyntaxTextAreaFixture.java
similarity index 60%
rename from app/test/processing/app/helpers/JEditTextAreaFixture.java
rename to app/test/processing/app/helpers/RSyntaxTextAreaFixture.java
index 291910abe7e..fc8bbf28856 100644
--- a/app/test/processing/app/helpers/JEditTextAreaFixture.java
+++ b/app/test/processing/app/helpers/RSyntaxTextAreaFixture.java
@@ -1,7 +1,7 @@
 /*
  * This file is part of Arduino.
  *
- * Copyright 2015 Arduino LLC (http://www.arduino.cc/)
+ * Copyright 2015 Ricardo JL Rufino (ricardo@criativasoft.com.br)
  *
  * Arduino is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -31,51 +31,51 @@
 
 import org.fest.swing.core.Robot;
 import org.fest.swing.fixture.ComponentFixture;
-import processing.app.syntax.JEditTextArea;
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
 
-public class JEditTextAreaFixture extends ComponentFixture {
+public class RSyntaxTextAreaFixture extends ComponentFixture {
 
-  private final JEditTextAreaComponentDriver driver;
+  private final RSyntaxTextAreaComponentDriver driver;
 
-  public JEditTextAreaFixture(Robot robot, Class type) {
+  public RSyntaxTextAreaFixture(Robot robot, Class type) {
     super(robot, type);
-    this.driver = new JEditTextAreaComponentDriver(robot);
+    this.driver = new RSyntaxTextAreaComponentDriver(robot);
   }
 
-  public JEditTextAreaFixture(Robot robot, String name, Class type) {
+  public RSyntaxTextAreaFixture(Robot robot, String name, Class type) {
     super(robot, name, type);
-    this.driver = new JEditTextAreaComponentDriver(robot);
+    this.driver = new RSyntaxTextAreaComponentDriver(robot);
   }
 
-  public JEditTextAreaFixture(Robot robot, JEditTextArea target) {
+  public RSyntaxTextAreaFixture(Robot robot, RSyntaxTextArea target) {
     super(robot, target);
-    this.driver = new JEditTextAreaComponentDriver(robot);
+    this.driver = new RSyntaxTextAreaComponentDriver(robot);
   }
 
-  public JEditTextAreaFixture enterText(String text) {
-    driver.enterText((JEditTextArea) target, text);
+  public RSyntaxTextAreaFixture enterText(String text) {
+    driver.enterText((RSyntaxTextArea) target, text);
     return this;
   }
 
-  public JEditTextAreaFixture setText(String text) {
-    driver.setText((JEditTextArea) target, text);
+  public RSyntaxTextAreaFixture setText(String text) {
+    driver.setText((RSyntaxTextArea) target, text);
     return this;
   }
 
   public String getText() {
-    return driver.getText((JEditTextArea) target);
+    return driver.getText((RSyntaxTextArea) target);
   }
 
-  public JEditTextAreaFixture selectAll() {
-    driver.selectAll((JEditTextArea) target);
+  public RSyntaxTextAreaFixture selectAll() {
+    driver.selectAll((RSyntaxTextArea) target);
     return this;
   }
 
   public int getCaretPosition() {
-    return driver.getCaretPosition((JEditTextArea) target);
+    return driver.getCaretPosition((RSyntaxTextArea) target);
   }
 
   public void setCaretPosition(int caretPosition) {
-    driver.setCaretPosition((JEditTextArea) target, caretPosition);
+    driver.setCaretPosition((RSyntaxTextArea) target, caretPosition);
   }
 }
diff --git a/app/test/processing/app/syntax/PdeKeywordsTest.java b/app/test/processing/app/syntax/PdeKeywordsTest.java
new file mode 100644
index 00000000000..1ae7f333522
--- /dev/null
+++ b/app/test/processing/app/syntax/PdeKeywordsTest.java
@@ -0,0 +1,30 @@
+package processing.app.syntax;
+
+import org.fife.ui.rsyntaxtextarea.TokenTypes;
+import org.junit.Test;
+import processing.app.AbstractWithPreferencesTest;
+import processing.app.BaseNoGui;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class PdeKeywordsTest extends AbstractWithPreferencesTest {
+
+  @Test
+  public void testKeywordsTxtParsing() throws Exception {
+    PdeKeywords pdeKeywords = new PdeKeywords();
+    pdeKeywords.reload();
+
+    assertEquals("Constants", pdeKeywords.getReference("HIGH"));
+    assertEquals("RESERVED_WORD_2", pdeKeywords.getTokenTypeAsString("HIGH"));
+    assertEquals(TokenTypes.RESERVED_WORD_2, pdeKeywords.getTokenType("HIGH".toCharArray(), 0, 3));
+
+    assertEquals("IncrementCompound", pdeKeywords.getReference("+="));
+    assertNull(pdeKeywords.getTokenTypeAsString("+="));
+
+    assertNull(pdeKeywords.getReference("Mouse"));
+    assertEquals("DATA_TYPE", pdeKeywords.getTokenTypeAsString("Mouse"));
+    assertEquals(TokenTypes.DATA_TYPE, pdeKeywords.getTokenType("Mouse".toCharArray(), 0, 4));
+  }
+
+}
diff --git a/arduino-core/src/processing/app/preproc/PdePreprocessor.java b/arduino-core/src/processing/app/preproc/PdePreprocessor.java
index 576f7468b9d..dc30922a92e 100644
--- a/arduino-core/src/processing/app/preproc/PdePreprocessor.java
+++ b/arduino-core/src/processing/app/preproc/PdePreprocessor.java
@@ -42,6 +42,9 @@ Processing version Copyright (c) 2004-05 Ben Fry and Casey Reas
  * Class that orchestrates preprocessing p5 syntax into straight Java.
  */
 public class PdePreprocessor {
+  
+  private static final String IMPORT_REGEX = "^\\s*#include\\s*[<\"](\\S+)[\">]";
+  
   // stores number of built user-defined function prototypes
   public int prototypeCount = 0;
 
@@ -94,10 +97,9 @@ public int writePrefix(String program)
     }
 
     //String importRegexp = "(?:^|\\s|;)(import\\s+)(\\S+)(\\s*;)";
-    String importRegexp = "^\\s*#include\\s*[<\"](\\S+)[\">]";
     programImports = new ArrayList();
 
-    String[][] pieces = PApplet.matchAll(program, importRegexp);
+    String[][] pieces = PApplet.matchAll(program, IMPORT_REGEX);
 
     if (pieces != null)
       for (int i = 0; i < pieces.length; i++)
@@ -121,6 +123,19 @@ public int writePrefix(String program)
     return headerCount + prototypeCount;
   }
 
+  public static List findIncludes(String code){
+   
+    String[][] pieces = PApplet.matchAll(code, IMPORT_REGEX);
+
+    ArrayList programImports = new ArrayList();
+    
+    if (pieces != null)
+      for (int i = 0; i < pieces.length; i++)
+        programImports.add(pieces[i][1]);  // the package name
+
+    return programImports;
+  }
+    
 
   static String substituteUnicode(String program) {
     // check for non-ascii chars (these will be/must be in unicode format)
diff --git a/build/build.xml b/build/build.xml
index b26cbfb8063..be58b13ca86 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -596,6 +596,8 @@
     
 
     
+    
+    
     
 
     
diff --git a/build/linux/dist/arduino b/build/linux/dist/arduino
index d942d69472b..e6518315721 100755
--- a/build/linux/dist/arduino
+++ b/build/linux/dist/arduino
@@ -26,5 +26,5 @@ else
 	SPLASH="-splash:./lib/splash.png"
 fi
 
-java -Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel $SPLASH processing.app.Base --curdir $CURDIR "$@"
+java -Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel $SPLASH ArduinoIDE --curdir $CURDIR "$@"
 
diff --git a/build/linux/dist/arduino.desktop b/build/linux/dist/arduino.desktop
index dac3ebd9d68..8b271e49393 100644
--- a/build/linux/dist/arduino.desktop
+++ b/build/linux/dist/arduino.desktop
@@ -3,8 +3,8 @@ Type=Application
 Name=Arduino IDE
 GenericName=Integrated Development Environment
 Comment=An IDE for Arduino-compatible electronics prototyping platforms
-Exec=arduino
-Icon=arduino
+Exec=FULL_PATH/arduino
+Icon=FULL_PATH/lib/arduino.png
 Terminal=false
 Categories=Development;IDE;Electronics;
 MimeType=text/x-arduino
diff --git a/build/linux/dist/install.sh b/build/linux/dist/install.sh
new file mode 100644
index 00000000000..d34c8aac85c
--- /dev/null
+++ b/build/linux/dist/install.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+CURDIR=`pwd`
+APPDIR="$(dirname -- "$(readlink -f -- "${0}")" )"
+
+cd "$APPDIR"
+
+# Set arduino.desktop absolute path workaround
+mv arduino.desktop arduino.desktop-bak
+sed -e "s,FULL_PATH,$PWD,g" arduino.desktop-bak > arduino.desktop
+rm arduino.desktop-bak
+
+cp arduino.desktop ~/.local/share/applications/arduino.desktop
+cp arduino.desktop ~/Desktop/arduino.desktop
+
+echo "Instaled Arduino IDE icons on menu and desktop !"
+
diff --git a/build/macosx/template.app/Contents/Info.plist b/build/macosx/template.app/Contents/Info.plist
index 9c573c988ba..95bf9cca42f 100755
--- a/build/macosx/template.app/Contents/Info.plist
+++ b/build/macosx/template.app/Contents/Info.plist
@@ -97,7 +97,7 @@
       
-      $JAVAROOT/antlr.jar:$JAVAROOT/apple.jar:$JAVAROOT/arduino-core.jar:$JAVAROOT/bcpg-jdk15on-152.jar:$JAVAROOT/bcprov-jdk15on-152.jar:$JAVAROOT/commons-codec-1.7.jar:$JAVAROOT/commons-compress-1.8.jar:$JAVAROOT/commons-exec-1.1.jar:$JAVAROOT/commons-httpclient-3.1.jar:$JAVAROOT/commons-lang3-3.3.2.jar:$JAVAROOT/commons-logging-1.0.4.jar:$JAVAROOT/ecj.jar:$JAVAROOT/guava-18.0.jar:$JAVAROOT/jackson-annotations-2.2.3.jar:$JAVAROOT/jackson-core-2.2.3.jar:$JAVAROOT/jackson-databind-2.2.3.jar:$JAVAROOT/jackson-module-mrbean-2.2.3.jar:$JAVAROOT/java-semver-0.8.0.jar:$JAVAROOT/jmdns-3.4.1.jar:$JAVAROOT/jna.jar:$JAVAROOT/jsch-0.1.50.jar:$JAVAROOT/jssc-2.8.0.jar:$JAVAROOT/pde.jar:$JAVAROOT/quaqua.jar
+      $JAVAROOT/antlr.jar:$JAVAROOT/apple.jar:$JAVAROOT/arduino-core.jar:$JAVAROOT/bcpg-jdk15on-152.jar:$JAVAROOT/bcprov-jdk15on-152.jar:$JAVAROOT/commons-codec-1.7.jar:$JAVAROOT/commons-compress-1.8.jar:$JAVAROOT/commons-exec-1.1.jar:$JAVAROOT/commons-httpclient-3.1.jar:$JAVAROOT/commons-lang3-3.3.2.jar:$JAVAROOT/commons-logging-1.0.4.jar:$JAVAROOT/ecj.jar:$JAVAROOT/guava-18.0.jar:$JAVAROOT/jackson-annotations-2.2.3.jar:$JAVAROOT/jackson-core-2.2.3.jar:$JAVAROOT/jackson-databind-2.2.3.jar:$JAVAROOT/jackson-module-mrbean-2.2.3.jar:$JAVAROOT/java-semver-0.8.0.jar:$JAVAROOT/jmdns-3.4.1.jar:$JAVAROOT/jna.jar:$JAVAROOT/jsch-0.1.50.jar:$JAVAROOT/jssc-2.8.0.jar:$JAVAROOT/pde.jar:$JAVAROOT/quaqua.jar:$JAVAROOT/rsyntaxtextarea-2.6.0-SNAPSHOT.jar
 
       JVMArchs  
         
diff --git a/build/shared/lib/arduino.png b/build/shared/lib/arduino.png
new file mode 100644
index 00000000000..28fa03ab7f7
Binary files /dev/null and b/build/shared/lib/arduino.png differ
diff --git a/build/shared/lib/keywords.txt b/build/shared/lib/keywords.txt
index 5fe83687824..9ac844ede70 100644
--- a/build/shared/lib/keywords.txt
+++ b/build/shared/lib/keywords.txt
@@ -5,57 +5,57 @@
 
 # LITERAL2 specifies constants
 
-HIGH	LITERAL2	Constants
-LOW	LITERAL2	Constants
-INPUT	LITERAL2	Constants
-INPUT_PULLUP	LITERAL2	Constants
-OUTPUT	LITERAL2	Constants
-DEC	LITERAL2	Serial_Print
-BIN	LITERAL2	Serial_Print
-HEX	LITERAL2	Serial_Print
-OCT	LITERAL2	Serial_Print
-PI	LITERAL2
-HALF_PI	LITERAL2
-TWO_PI	LITERAL2
-LSBFIRST	LITERAL2	ShiftOut
-MSBFIRST	LITERAL2	ShiftOut
-CHANGE	LITERAL2	AttachInterrupt
-FALLING	LITERAL2	AttachInterrupt
-RISING	LITERAL2	AttachInterrupt
-DEFAULT	LITERAL2	AnalogReference
-EXTERNAL	LITERAL2	AnalogReference
-INTERNAL	LITERAL2	AnalogReference
-INTERNAL1V1	LITERAL2	AnalogReference
-INTERNAL2V56	LITERAL2	AnalogReference
+HIGH	LITERAL2	Constants	RESERVED_WORD_2
+LOW	LITERAL2	Constants	RESERVED_WORD_2
+INPUT	LITERAL2	Constants	RESERVED_WORD_2
+INPUT_PULLUP	LITERAL2	Constants	RESERVED_WORD_2
+OUTPUT	LITERAL2	Constants	RESERVED_WORD_2
+DEC	LITERAL2	Serial_Print	RESERVED_WORD_2
+BIN	LITERAL2	Serial_Print	RESERVED_WORD_2
+HEX	LITERAL2	Serial_Print	RESERVED_WORD_2
+OCT	LITERAL2	Serial_Print	RESERVED_WORD_2
+PI	LITERAL2		RESERVED_WORD_2
+HALF_PI	LITERAL2		RESERVED_WORD_2
+TWO_PI	LITERAL2		RESERVED_WORD_2
+LSBFIRST	LITERAL2	ShiftOut	RESERVED_WORD_2
+MSBFIRST	LITERAL2	ShiftOut	RESERVED_WORD_2
+CHANGE	LITERAL2	AttachInterrupt	RESERVED_WORD_2
+FALLING	LITERAL2	AttachInterrupt	RESERVED_WORD_2
+RISING	LITERAL2	AttachInterrupt	RESERVED_WORD_2
+DEFAULT	LITERAL2	AnalogReference	RESERVED_WORD_2
+EXTERNAL	LITERAL2	AnalogReference	RESERVED_WORD_2
+INTERNAL	LITERAL2	AnalogReference	RESERVED_WORD_2
+INTERNAL1V1	LITERAL2	AnalogReference	RESERVED_WORD_2
+INTERNAL2V56	LITERAL2	AnalogReference	RESERVED_WORD_2
 
-boolean	LITERAL2	BooleanVariables
-byte	LITERAL2	Byte
-char	LITERAL2	Char
-const	LITERAL2	Const
-false	LITERAL2	Constants
-float	LITERAL2	Float
-null	LITERAL2	
-int	LITERAL2	Int
-long	LITERAL2	Long
-new	LITERAL2	
-private	LITERAL2
-protected	LITERAL2	
-public	LITERAL2	
-short	LITERAL2
-signed	LITERAL2
-static	LITERAL2	Static
-String	LITERAL2	String
-void	LITERAL2	Void
-true	LITERAL2	
-unsigned	LITERAL2
+boolean	LITERAL2	BooleanVariables	RESERVED_WORD_2
+byte	LITERAL2	Byte	RESERVED_WORD_2
+char	LITERAL2	Char	RESERVED_WORD_2
+const	LITERAL2	Const	RESERVED_WORD_2
+false	LITERAL2	Constants	LITERAL_BOOLEAN
+float	LITERAL2	Float	RESERVED_WORD_2
+null	LITERAL2		RESERVED_WORD_2
+int	LITERAL2	Int	RESERVED_WORD_2
+long	LITERAL2	Long	RESERVED_WORD_2
+new	LITERAL2		RESERVED_WORD_2
+private	LITERAL2		RESERVED_WORD_2
+protected	LITERAL2		RESERVED_WORD_2
+public	LITERAL2		RESERVED_WORD_2
+short	LITERAL2		RESERVED_WORD_2
+signed	LITERAL2		RESERVED_WORD_2
+static	LITERAL2	Static	RESERVED_WORD_2
+String	LITERAL2	String	RESERVED_WORD_2
+void	LITERAL2	Void	RESERVED_WORD_2
+true	LITERAL2		LITERAL_BOOLEAN
+unsigned	LITERAL2		RESERVED_WORD_2
 
-boolean	LITERAL2	boolean_
-byte	LITERAL2	byte_
-char	LITERAL2	char_
-float	LITERAL2	float_
-int	LITERAL2	int_
-long	LITERAL2	long_
-word	LITERAL2	word_
+boolean	LITERAL2	boolean_	RESERVED_WORD_2
+byte	LITERAL2	byte_	RESERVED_WORD_2
+char	LITERAL2	char_	RESERVED_WORD_2
+float	LITERAL2	float_	RESERVED_WORD_2
+int	LITERAL2	int_	RESERVED_WORD_2
+long	LITERAL2	long_	RESERVED_WORD_2
+word	LITERAL2	word_	RESERVED_WORD_2
 
 # KEYWORD2 specifies methods and functions
 
@@ -113,11 +113,11 @@ shiftOut	KEYWORD2	ShiftOut
 tone	KEYWORD2	Tone
 yield	KEYWORD2	Yield
 
-Serial	KEYWORD1	Serial
-Serial1	KEYWORD1	Serial
-Serial2	KEYWORD1	Serial
-Serial3	KEYWORD1	Serial
-SerialUSB	KEYWORD1	Serial
+Serial	KEYWORD1	Serial	DATA_TYPE
+Serial1	KEYWORD1	Serial	DATA_TYPE
+Serial2	KEYWORD1	Serial	DATA_TYPE
+Serial3	KEYWORD1	Serial	DATA_TYPE
+SerialUSB	KEYWORD1	Serial	DATA_TYPE
 begin	KEYWORD2	Serial_Begin
 end	KEYWORD2	Serial_End
 peek	KEYWORD2	Serial_Peek
@@ -156,8 +156,8 @@ substring	KEYWORD2
 toCharArray	KEYWORD2
 toInt	KEYWORD2
 
-Keyboard	KEYWORD1
-Mouse	KEYWORD1
+Keyboard	KEYWORD1		DATA_TYPE
+Mouse	KEYWORD1		DATA_TYPE
 press	KEYWORD2
 release	KEYWORD2
 releaseAll	KEYWORD2
@@ -168,28 +168,28 @@ isPressed	KEYWORD2
 
 # KEYWORD3 specifies structures
 
-break	KEYWORD3	Break
-case	KEYWORD3	SwitchCase
-class	KEYWORD3
-continue	KEYWORD3	Continue
-default	KEYWORD3	SwitchCase	
-do	KEYWORD3	DoWhile
-double	KEYWORD3	Double
-else	KEYWORD3	Else
-for	KEYWORD3	For
-if	KEYWORD3	If
-register	KEYWORD3	
-return	KEYWORD3	Return	
+break	KEYWORD3	Break	RESERVED_WORD
+case	KEYWORD3	SwitchCase	RESERVED_WORD
+class	KEYWORD3		RESERVED_WORD
+continue	KEYWORD3	Continue	RESERVED_WORD
+default	KEYWORD3	SwitchCase	RESERVED_WORD
+do	KEYWORD3	DoWhile	RESERVED_WORD
+double	KEYWORD3	Double	RESERVED_WORD
+else	KEYWORD3	Else	RESERVED_WORD
+for	KEYWORD3	For	RESERVED_WORD
+if	KEYWORD3	If	RESERVED_WORD
+register	KEYWORD3		RESERVED_WORD
+return	KEYWORD3	Return	RESERVED_WORD
 	
-switch	KEYWORD3	SwitchCase
-this	KEYWORD3	
-throw	KEYWORD3	
-try	KEYWORD3
-while	KEYWORD3	While
-word	KEYWORD3	Word
+switch	KEYWORD3	SwitchCase	RESERVED_WORD
+this	KEYWORD3		RESERVED_WORD
+throw	KEYWORD3		RESERVED_WORD
+try	KEYWORD3		RESERVED_WORD
+while	KEYWORD3	While	RESERVED_WORD
+word	KEYWORD3	Word	RESERVED_WORD
 
-setup	KEYWORD3	Setup
-loop	KEYWORD3	Loop
+setup	KEYWORD3	Setup	RESERVED_WORD
+loop	KEYWORD3	Loop	RESERVED_WORD
 
 # operators aren't highlighted, but may have documentation
 
@@ -199,15 +199,15 @@ loop	KEYWORD3	Loop
 =		assign
 &		BitwiseAnd
 |		BitwiseAnd
-,		
+,
 //		Comments
-?:		
+?:
 {}		Braces
 --		Increment
 /		Arithmetic
 /*		Comments
 .		dot
-==		
+==
 <		greaterthan
 <=		greaterthanorequalto
 ++		Increment
diff --git a/build/shared/lib/preferences.txt b/build/shared/lib/preferences.txt
index 5dfde1ce28d..2f3e105d930 100644
--- a/build/shared/lib/preferences.txt
+++ b/build/shared/lib/preferences.txt
@@ -86,13 +86,15 @@ editor.window.height.min = 290
 # tested to be 515 on Windows XP, this leaves some room
 #editor.window.height.min.windows = 530
 
+# Enable code folding 
+editor.code_folding=true
 
 # font size for editor
 editor.font=Monospaced,plain,12
 # Monaco is nicer on Mac OS X, so use that explicitly
-editor.font.macosx = Monaco,plain,10
+editor.font.macosx = Monaco,plain,12
 
-# anti-aliased text, turned off by default
+# anti-aliased text, turned on by default
 editor.antialias=true
 
 # color to be used for background when 'external editor' enabled
diff --git a/build/shared/lib/theme/syntax/dark.xml b/build/shared/lib/theme/syntax/dark.xml
new file mode 100644
index 00000000000..c9283f38d3d
--- /dev/null
+++ b/build/shared/lib/theme/syntax/dark.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+   
+   
+   
+   
+   
+   
+   
+   
+   
+    
+   
+   
+   
+   
+      
+      
+      
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+