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.*;
9 import java.awt.datatransfer.*;
10 import java.awt.dnd.*;
11 import java.awt.event.*;
15 import javax.swing.text.*;
16 import javax.swing.text.Highlighter.Highlight;
17 import javax.swing.text.Highlighter.HighlightPainter;
18 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));
54 class DropTargetAdapterImpl extends DropTargetAdapter {
56 public void drop(DropTargetDropEvent dtde) {
57 Transferable t = dtde.getTransferable();
58 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
59 dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
61 StringBuilder buffer = new StringBuilder();
62 @SuppressWarnings("unchecked")
63 List<File> fileList = (List<File>)t.getTransferData(DataFlavor.javaFileListFlavor);
64 for (File file : fileList) {
65 buffer.append(file.getAbsolutePath()).append(" ");
67 append(buffer.toString());
68 } catch (UnsupportedFlavorException ex) {
69 throw new RuntimeException(ex);
70 } catch (IOException ex) {
71 throw new RuntimeException(ex);
76 setDropTarget(new DropTarget(this, new DropTargetAdapterImpl()));
79 private final class ConsoleTextAreaDocumentFilter extends DocumentFilter {
81 ConsoleTextAreaDocumentFilter() {
85 public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
86 if (isEditablePosition(offset)) {
87 super.insertString(fb, offset, string, attr);
92 public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
93 if (isEditablePosition(offset)) {
94 super.remove(fb, offset, length);
99 public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
100 if (isEditablePosition(offset)) {
101 super.replace(fb, offset, length, text, attrs);
108 public void anyActionPerformed(AnyActionEvent ev) {
109 log.atEnter("anyActionPerformed", ev);
110 if (ev.isAnyOf(submit)) {
111 final int ep = getEndPosition();
112 if (getCaretPosition() == ep) {
113 anyActionListener.anyActionPerformed(new AnyActionEvent(this, execute));
115 setCaretPosition(ep);
117 } else if (ev.isAnyOf(copyOrBreak)) {
118 if (getSelectedText() == null) {
121 Action copyAction = new DefaultEditorKit.CopyAction();
122 copyAction.actionPerformed(ev);
124 } else if (ev.isAnyOf(breakCommand)) {
126 } else if (ev.isAnyOf(addNewLine)) {
127 insert("\n", getCaretPosition());
128 } else if (ev.isAnyOf(jumpToHomePosition)) {
129 setCaretPosition(getHomePosition());
130 } else if (ev.isAnyOf(insertText)) {
131 if (!isEditablePosition(getCaretPosition())) {
132 setCaretPosition(getEndPosition());
134 replaceSelection(TextUtilities.join(" ", Arrays.asList(ev.getArgs())));
136 } else if (ev.isAnyOf(outputMessage)) {
137 for (Object o : ev.getArgs()) {
138 output(String.valueOf(o));
140 } else if (ev.isAnyOf(doNothing)) {
143 log.warn("not expected: Event=%s", ev);
145 log.atExit("anyActionPerformed");
149 return undoManager.canUndo();
153 return undoManager.canRedo();
159 * @param movesCaretToEnd true if it moves Caret to the end, otherwise false
161 void append(String s, boolean movesCaretToEnd) {
163 if (movesCaretToEnd) {
164 setCaretPosition(getEndPosition());
172 void output(String s) {
174 undoManager.discardAllEdits();
175 homePosition = getEndPosition();
176 setCaretPosition(homePosition);
180 * Replaces text from prompt to the end.
183 void replace(String s) {
184 replaceRange(s, homePosition, getEndPosition());
196 * Returns the text that is editable.
199 String getEditableText() {
201 return getText(homePosition, getEndPosition() - homePosition);
202 } catch (BadLocationException ex) {
203 throw new RuntimeException(ex);
208 * Tests whether the specified position is editable.
212 boolean isEditablePosition(int position) {
213 return (position >= homePosition);
216 int getHomePosition() {
220 int getEndPosition() {
221 Document document = getDocument();
222 Position position = document.getEndPosition();
223 return position.getOffset() - 1;
226 void resetHomePosition() {
227 undoManager.discardAllEdits();
228 homePosition = getEndPosition();
232 append(BREAK_PROMPT);
238 public void updateUI() {
239 if (getCaret() == null) {
242 final int p = getCaretPosition();
251 public boolean search(Matcher matcher) {
254 Highlighter highlighter = getHighlighter();
255 HighlightPainter painter = TextSearch.Matcher.getHighlightPainter();
256 final String text = getText();
258 boolean matched = false;
259 while (matcher.find(text, start)) {
261 int matchedIndex = matcher.getStart();
262 highlighter.addHighlight(matchedIndex, matcher.getEnd(), painter);
263 start = matchedIndex + 1;
265 addKeyListener(new TextSearchKeyListener());
267 } catch (BadLocationException ex) {
268 throw new RuntimeException(ex);
272 private final class TextSearchKeyListener extends KeyAdapter {
274 public void keyTyped(KeyEvent e) {
275 removeKeyListener(this);
281 public void reset() {
285 void removeHighlights() {
286 for (Highlight highlight : getHighlighter().getHighlights()) {
287 getHighlighter().removeHighlight(highlight);