OSDN Git Service

[#34600] add a new function "show all histories" into GUI menu
[stew/Stew4.git] / src / net / argius / stew / ui / window / ConsoleTextArea.java
1 package net.argius.stew.ui.window;
2
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
10 import java.awt.event.*;
11 import java.util.*;
12
13 import javax.swing.*;
14 import javax.swing.text.*;
15 import javax.swing.text.Highlighter.Highlight;
16 import javax.swing.text.Highlighter.HighlightPainter;
17 import javax.swing.undo.*;
18
19 import net.argius.stew.*;
20 import net.argius.stew.text.*;
21
22 /**
23  * The console style text area.
24  */
25 final class ConsoleTextArea extends JTextArea implements AnyActionListener, TextSearch {
26
27     enum ActionKey {
28         submit, copyOrBreak, addNewLine, jumpToHomePosition, outputMessage, insertText, doNothing;
29     }
30
31     private static final Logger log = Logger.getLogger(ConsoleTextArea.class);
32     private static final String BREAK_PROMPT = "[BREAK] > ";
33
34     private final AnyActionListener anyActionListener;
35     private final UndoManager undoManager;
36
37     private int homePosition;
38
39     ConsoleTextArea(AnyActionListener anyActionListener) {
40         // [Instances]
41         this.anyActionListener = anyActionListener;
42         this.undoManager = AnyAction.setUndoAction(this);
43         ((AbstractDocument)getDocument()).setDocumentFilter(new ConsoleTextAreaDocumentFilter());
44         // [Actions]
45         final int shortcutKey = Utilities.getMenuShortcutKeyMask();
46         AnyAction aa = new AnyAction(this);
47         aa.setUndoAction();
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));
53     }
54
55     private final class ConsoleTextAreaDocumentFilter extends DocumentFilter {
56
57         ConsoleTextAreaDocumentFilter() {
58         } // empty
59
60         @Override
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);
64             }
65         }
66
67         @Override
68         public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
69             if (isEditablePosition(offset)) {
70                 super.remove(fb, offset, length);
71             }
72         }
73
74         @Override
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);
78             }
79         }
80
81     }
82
83     @Override
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));
90             } else {
91                 setCaretPosition(ep);
92             }
93         } else if (ev.isAnyOf(copyOrBreak)) {
94             if (getSelectedText() == null) {
95                 sendBreak();
96             } else {
97                 Action copyAction = new DefaultEditorKit.CopyAction();
98                 copyAction.actionPerformed(ev);
99             }
100         } else if (ev.isAnyOf(breakCommand)) {
101             sendBreak();
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());
109             }
110             replaceSelection(TextUtilities.join(" ", Arrays.asList(ev.getArgs())));
111             requestFocus();
112         } else if (ev.isAnyOf(outputMessage)) {
113             for (Object o : ev.getArgs()) {
114                 output(String.valueOf(o));
115             }
116         } else if (ev.isAnyOf(doNothing)) {
117             // do nothing
118         } else {
119             log.warn("not expected: Event=%s", ev);
120         }
121         log.atExit("anyActionPerformed");
122     }
123
124     boolean canUndo() {
125         return undoManager.canUndo();
126     }
127
128     boolean canRedo() {
129         return undoManager.canRedo();
130     }
131
132     /**
133      * Appends text.
134      * @param s
135      * @param movesCaretToEnd true if it moves Caret to the end, otherwise false
136      */
137     void append(String s, boolean movesCaretToEnd) {
138         super.append(s);
139         if (movesCaretToEnd) {
140             setCaretPosition(getEndPosition());
141         }
142     }
143
144     /**
145      * Outputs text.
146      * @param s
147      */
148     void output(String s) {
149         super.append(s);
150         undoManager.discardAllEdits();
151         homePosition = getEndPosition();
152         setCaretPosition(homePosition);
153     }
154
155     /**
156      * Replaces text from prompt to the end.
157      * @param s
158      */
159     void replace(String s) {
160         replaceRange(s, homePosition, getEndPosition());
161     }
162
163     /**
164      * Prepares submitting.
165      * Clears selection, moves cursor to end, and focuses this.
166      */
167     void prepareSubmitting() {
168         final int ep = getEndPosition();
169         setSelectionStart(ep);
170         moveCaretPosition(ep);
171         requestFocus();
172     }
173
174     /**
175      * Clears text.
176      */
177     void clear() {
178         homePosition = 0;
179         setText("");
180     }
181
182     /**
183      * Returns the text that is editable.
184      * @return
185      */
186     String getEditableText() {
187         try {
188             return getText(homePosition, getEndPosition() - homePosition);
189         } catch (BadLocationException ex) {
190             throw new RuntimeException(ex);
191         }
192     }
193
194     /**
195      * Tests whether the specified position is editable.
196      * @param position
197      * @return
198      */
199     boolean isEditablePosition(int position) {
200         return (position >= homePosition);
201     }
202
203     int getHomePosition() {
204         return homePosition;
205     }
206
207     int getEndPosition() {
208         Document document = getDocument();
209         Position position = document.getEndPosition();
210         return position.getOffset() - 1;
211     }
212
213     void resetHomePosition() {
214         undoManager.discardAllEdits();
215         homePosition = getEndPosition();
216     }
217
218     void sendBreak() {
219         append(BREAK_PROMPT);
220         resetHomePosition();
221         validate();
222     }
223
224     @Override
225     public void updateUI() {
226         if (getCaret() == null) {
227             super.updateUI();
228         } else {
229             final int p = getCaretPosition();
230             super.updateUI();
231             setCaretPosition(p);
232         }
233     }
234
235     // text search
236
237     @Override
238     public boolean search(Matcher matcher) {
239         removeHighlights();
240         try {
241             Highlighter highlighter = getHighlighter();
242             HighlightPainter painter = TextSearch.Matcher.getHighlightPainter();
243             final String text = getText();
244             int start = 0;
245             boolean matched = false;
246             while (matcher.find(text, start)) {
247                 matched = true;
248                 int matchedIndex = matcher.getStart();
249                 highlighter.addHighlight(matchedIndex, matcher.getEnd(), painter);
250                 start = matchedIndex + 1;
251             }
252             addKeyListener(new TextSearchKeyListener());
253             return matched;
254         } catch (BadLocationException ex) {
255             throw new RuntimeException(ex);
256         }
257     }
258
259     private final class TextSearchKeyListener extends KeyAdapter {
260         @Override
261         public void keyTyped(KeyEvent e) {
262             removeKeyListener(this);
263             removeHighlights();
264         }
265     }
266     
267     @Override
268     public void reset() {
269         removeHighlights();
270     }
271
272     void removeHighlights() {
273         for (Highlight highlight : getHighlighter().getHighlights()) {
274             getHighlighter().removeHighlight(highlight);
275         }
276     }
277
278 }