/* * 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 processing.app.Preferences; import processing.data.StringDict; 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.<p> * * 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 * @version $Id$ */ 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"; /** * If this PDE property is set to Boolean.TRUE, the home/end keys will * go to the first/last non-whitespace character of the line. If already at * the that character, the keypress will move the cursor to the actual * start/end of the line. * * SMART_HOME_END_PROPERTY takes precedence over this property. */ public static final String CONTEXT_AWARE_HOME_END = "editor.keys.home_and_end_travel_smart"; 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 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 Map<String, ActionListener> actions; private static final StringDict bracketsAndQuotesMap = new StringDict(new String[][] { { "(", ")" }, { "{", "}" }, { "[", "]" }, { "\"", "\"" }, { "\'", "\'" } }); static { actions = new HashMap<String, ActionListener>(); 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("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 actions.get(name); } /** * Returns the name of the specified text area action. * @param listener The action */ public static String getActionName(ActionListener listener) { Set<String> set = getActions(); for (String name : set) { ActionListener _listener = getAction(name); if (_listener == listener) { return name; } } return null; } /** * Returns an enumeration of all available actions. */ public static Set<String> getActions() { return actions.keySet(); } /** * 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 <sp@gjt.org>"); 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 { } public interface MacroRecorder { void actionPerformed(ActionListener listener, String actionCommand); } static public 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 = 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 = 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 caretLine = textArea.getCaretLine(); int lastOfLine = textArea.getLineStopOffset(caretLine) - 1; int lastNonWhiteSpaceOfLine = textArea.getLineStopNonWhiteSpaceOffset(caretLine) - 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 && !Preferences.getBoolean(CONTEXT_AWARE_HOME_END)) { textArea.getToolkit().beep(); return; } else if (!Boolean.TRUE.equals(textArea.getClientProperty(SMART_HOME_END_PROPERTY))) { if (!Preferences.getBoolean(CONTEXT_AWARE_HOME_END) || caret == lastNonWhiteSpaceOfLine) { caret = lastOfLine; } else { caret = lastNonWhiteSpaceOfLine; } } 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 caretLine = textArea.getCaretLine(); int firstOfLine = textArea.getLineStartOffset(caretLine); int firstNonWhiteSpaceOfLine = textArea.getLineStartNonWhiteSpaceOffset(caretLine); int firstVisibleLine = (firstLine == 0 ? 0 : firstLine + textArea.getElectricScroll()); int firstVisible = textArea.getLineStartOffset(firstVisibleLine); if (caret == 0 && !Preferences.getBoolean(CONTEXT_AWARE_HOME_END)) { textArea.getToolkit().beep(); return; } else if (!Boolean.TRUE.equals(textArea.getClientProperty(SMART_HOME_END_PROPERTY))) { if (!Preferences.getBoolean(CONTEXT_AWARE_HOME_END) || caret == firstNonWhiteSpaceOfLine) { caret = firstOfLine; } else { caret = firstNonWhiteSpaceOfLine; } } 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(); // Can't page down if there's nothing to visit // https://github.com/processing/processing/issues/2990 if (lineCount > visibleLines) { firstLine += visibleLines; // page down if (firstLine + visibleLines >= lineCount - 1) { // back up to the latest line we can go to 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 = 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 = 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 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()) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < repeatCount; i++) sb.append(str); if (Preferences.getBoolean("editor.completion.auto_close") && hasBracketsAndQuotes(str)) { matchBracketsAndQuotes(str, evt, textArea, sb); } else { textArea.overwriteSetSelectedText(sb.toString()); } } else { textArea.getToolkit().beep(); } } private void matchBracketsAndQuotes(String str, ActionEvent evt, JEditTextArea ta, StringBuilder sb) { sb.append(bracketsAndQuotesMap.get(str)); ta.overwriteSetSelectedText(sb.toString()); InputHandler.PREV_CHAR.actionPerformed(evt); } private boolean hasBracketsAndQuotes(String str) { for (String item : bracketsAndQuotesMap.keys()) { if (str.equals(item)) { return true; } } return false; } } /** * Locates the start of the word at the specified position. * Moved from TextUtilities.java [fry 121210]. * @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. * Moved from TextUtilities.java [fry 121210]. * @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; } /** * Called when input method support committed a character. */ public void handleInputMethodCommit() { } }