1 package net.argius.stew.ui.window;
3 import static java.awt.event.InputEvent.ALT_DOWN_MASK;
4 import static java.awt.event.KeyEvent.*;
5 import static javax.swing.KeyStroke.getKeyStroke;
6 import static net.argius.stew.ui.window.AnyActionKey.breakCommand;
7 import static net.argius.stew.ui.window.AnyActionKey.execute;
8 import static net.argius.stew.ui.window.ConsoleTextArea.ActionKey.*;
10 import java.awt.event.*;
14 import javax.swing.text.*;
15 import javax.swing.text.Highlighter.Highlight;
16 import javax.swing.text.Highlighter.HighlightPainter;
17 import javax.swing.undo.*;
19 import net.argius.stew.*;
20 import net.argius.stew.text.*;
23 * The console style text area.
25 final class ConsoleTextArea extends JTextArea implements AnyActionListener, TextSearch {
28 submit, copyOrBreak, addNewLine, jumpToHomePosition, outputMessage, insertText, doNothing;
31 private static final Logger log = Logger.getLogger(ConsoleTextArea.class);
32 private static final String BREAK_PROMPT = "[BREAK] > ";
34 private final AnyActionListener anyActionListener;
35 private final UndoManager undoManager;
37 private int homePosition;
39 ConsoleTextArea(AnyActionListener anyActionListener) {
41 this.anyActionListener = anyActionListener;
42 this.undoManager = AnyAction.setUndoAction(this);
43 ((AbstractDocument)getDocument()).setDocumentFilter(new ConsoleTextAreaDocumentFilter());
45 final int shortcutKey = Utilities.getMenuShortcutKeyMask();
46 AnyAction aa = new AnyAction(this);
48 aa.bindSelf(submit, getKeyStroke(VK_ENTER, 0));
49 aa.bindSelf(copyOrBreak, getKeyStroke(VK_C, shortcutKey));
50 aa.bindSelf(breakCommand, getKeyStroke(VK_B, ALT_DOWN_MASK));
51 aa.bindSelf(addNewLine, getKeyStroke(VK_ENTER, shortcutKey));
52 aa.bindSelf(jumpToHomePosition, getKeyStroke(VK_HOME, 0));
55 private final class ConsoleTextAreaDocumentFilter extends DocumentFilter {
57 ConsoleTextAreaDocumentFilter() {
61 public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
62 if (isEditablePosition(offset)) {
63 super.insertString(fb, offset, string, attr);
68 public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
69 if (isEditablePosition(offset)) {
70 super.remove(fb, offset, length);
75 public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
76 if (isEditablePosition(offset)) {
77 super.replace(fb, offset, length, text, attrs);
84 public void anyActionPerformed(AnyActionEvent ev) {
85 log.atEnter("anyActionPerformed", ev);
86 if (ev.isAnyOf(submit)) {
87 final int ep = getEndPosition();
88 if (getCaretPosition() == ep) {
89 anyActionListener.anyActionPerformed(new AnyActionEvent(this, execute));
93 } else if (ev.isAnyOf(copyOrBreak)) {
94 if (getSelectedText() == null) {
97 Action copyAction = new DefaultEditorKit.CopyAction();
98 copyAction.actionPerformed(ev);
100 } else if (ev.isAnyOf(breakCommand)) {
102 } else if (ev.isAnyOf(addNewLine)) {
103 insert("\n", getCaretPosition());
104 } else if (ev.isAnyOf(jumpToHomePosition)) {
105 setCaretPosition(getHomePosition());
106 } else if (ev.isAnyOf(insertText)) {
107 if (!isEditablePosition(getCaretPosition())) {
108 setCaretPosition(getEndPosition());
110 replaceSelection(TextUtilities.join(" ", Arrays.asList(ev.getArgs())));
112 } else if (ev.isAnyOf(outputMessage)) {
113 for (Object o : ev.getArgs()) {
114 output(String.valueOf(o));
116 } else if (ev.isAnyOf(doNothing)) {
119 log.warn("not expected: Event=%s", ev);
121 log.atExit("anyActionPerformed");
125 return undoManager.canUndo();
129 return undoManager.canRedo();
135 * @param movesCaretToEnd true if it moves Caret to the end, otherwise false
137 void append(String s, boolean movesCaretToEnd) {
139 if (movesCaretToEnd) {
140 setCaretPosition(getEndPosition());
148 void output(String s) {
150 undoManager.discardAllEdits();
151 homePosition = getEndPosition();
152 setCaretPosition(homePosition);
156 * Replaces text from prompt to the end.
159 void replace(String s) {
160 replaceRange(s, homePosition, getEndPosition());
164 * Prepares submitting.
165 * Clears selection, moves cursor to end, and focuses this.
167 void prepareSubmitting() {
168 final int ep = getEndPosition();
169 setSelectionStart(ep);
170 moveCaretPosition(ep);
183 * Returns the text that is editable.
186 String getEditableText() {
188 return getText(homePosition, getEndPosition() - homePosition);
189 } catch (BadLocationException ex) {
190 throw new RuntimeException(ex);
195 * Tests whether the specified position is editable.
199 boolean isEditablePosition(int position) {
200 return (position >= homePosition);
203 int getHomePosition() {
207 int getEndPosition() {
208 Document document = getDocument();
209 Position position = document.getEndPosition();
210 return position.getOffset() - 1;
213 void resetHomePosition() {
214 undoManager.discardAllEdits();
215 homePosition = getEndPosition();
219 append(BREAK_PROMPT);
225 public void updateUI() {
226 if (getCaret() == null) {
229 final int p = getCaretPosition();
238 public boolean search(Matcher matcher) {
241 Highlighter highlighter = getHighlighter();
242 HighlightPainter painter = TextSearch.Matcher.getHighlightPainter();
243 final String text = getText();
245 boolean matched = false;
246 while (matcher.find(text, start)) {
248 int matchedIndex = matcher.getStart();
249 highlighter.addHighlight(matchedIndex, matcher.getEnd(), painter);
250 start = matchedIndex + 1;
252 addKeyListener(new TextSearchKeyListener());
254 } catch (BadLocationException ex) {
255 throw new RuntimeException(ex);
259 private final class TextSearchKeyListener extends KeyAdapter {
261 public void keyTyped(KeyEvent e) {
262 removeKeyListener(this);
268 public void reset() {
272 void removeHighlights() {
273 for (Highlight highlight : getHighlighter().getHighlights()) {
274 getHighlighter().removeHighlight(highlight);