001: /*
002: * TextAreaInputHandler.java - Manages key bindings and executes actions
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2006 Matthieu Casanova
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022: package org.gjt.sp.jedit.input;
023:
024: import org.gjt.sp.jedit.Debug;
025: import org.gjt.sp.jedit.MiscUtilities;
026: import org.gjt.sp.jedit.Registers;
027: import org.gjt.sp.jedit.gui.GrabKeyDialog;
028: import org.gjt.sp.jedit.gui.KeyEventTranslator;
029: import org.gjt.sp.jedit.gui.KeyEventWorkaround;
030: import org.gjt.sp.jedit.textarea.TextArea;
031: import org.gjt.sp.util.Log;
032:
033: import javax.swing.*;
034: import java.awt.*;
035: import java.awt.event.KeyEvent;
036:
037: /**
038: * This class manage the key bindings and execute the actions binded on the
039: * keyboard events for the standalone textarea.
040: *
041: * @author Matthieu Casanova
042: * @version $Id: FoldHandler.java 5568 2006-07-10 20:52:23Z kpouer $
043: */
044: public class TextAreaInputHandler extends AbstractInputHandler {
045: private final TextArea textArea;
046:
047: //{{{ TextAreaInputHandler constructor
048: public TextAreaInputHandler(TextArea textArea) {
049: this .textArea = textArea;
050: } //}}}
051:
052: //{{{ processKeyEvent() method
053: /**
054: * Forwards key events directly to the input handler.
055: * This is slightly faster than using a KeyListener
056: * because some Swing overhead is avoided.
057: * @param evt the keyboard event
058: * @param from the source of the event. Since this is the input handler of the textarea, it should always be 1
059: * @param global it is only true if the event comes from the DefaultKeyboardFocusManager
060: * @since 4.3pre7
061: */
062: public void processKeyEvent(KeyEvent evt, int from, boolean global) {
063: if (Debug.DUMP_KEY_EVENTS) {
064: Log.log(Log.DEBUG, this , "Key event : "
065: + GrabKeyDialog.toString(evt) + " from " + from);
066: // Log.log(Log.DEBUG,this,view+".isFocused()="+view.isFocused()+'.',new Exception());
067: }
068:
069: evt = _preprocessKeyEvent(evt);
070: if (evt == null)
071: return;
072:
073: if (Debug.DUMP_KEY_EVENTS) {
074: Log.log(Log.DEBUG, this , "Key event after workaround: "
075: + GrabKeyDialog.toString(evt) + " from " + from);
076: }
077:
078: boolean focusOnTextArea = false;
079: switch (evt.getID()) {
080: case KeyEvent.KEY_TYPED:
081: // if the user pressed eg C+e n n in the
082: // search bar we want focus to go back there
083: // after the prefix is done
084:
085: if (keyEventInterceptor != null)
086: keyEventInterceptor.keyTyped(evt);
087: else if (isPrefixActive() || textArea.hasFocus()) {
088: processKeyEventKeyStrokeHandling(evt, from, "type ",
089: global);
090: }
091:
092: processKeyEventSub(focusOnTextArea);
093:
094: break;
095: case KeyEvent.KEY_PRESSED:
096: if (keyEventInterceptor != null)
097: keyEventInterceptor.keyPressed(evt);
098: else if (KeyEventWorkaround.isBindable(evt.getKeyCode())) {
099: processKeyEventKeyStrokeHandling(evt, from, "press",
100: global);
101:
102: processKeyEventSub(focusOnTextArea);
103:
104: }
105: break;
106: case KeyEvent.KEY_RELEASED:
107: if (keyEventInterceptor != null)
108: keyEventInterceptor.keyReleased(evt);
109: break;
110: }
111: } //}}}
112:
113: //{{{ _preprocessKeyEvent() method
114: /**
115: * This method returns if the keyboard event can be handled or not.
116: *
117: * @param evt the keyboard event
118: * @return null if the keyboard event cannot be handled, or the keyboard event itself
119: * otherwise
120: */
121: private KeyEvent _preprocessKeyEvent(KeyEvent evt) {
122: Component focusOwner = textArea;
123: if (true /*Options.SIMPLIFIED_KEY_HANDLING*/) {
124: /*
125: It seems that the "else" path below does
126: not work. Apparently, is is there to prevent
127: some keyboard events to be "swallowed" by
128: jEdit when the keyboard event in fact should
129: be scheduled to swing for further handling.
130:
131: On some "key typed" events, the "return null;"
132: is triggered. However, these key events
133: actually do not seem to be handled elseewhere,
134: so they are not handled at all.
135:
136: This behaviour exists with old keyboard handling
137: as well as with new keyboard handling. However,
138: the new keyboard handling is more sensitive
139: about what kinds of key events it receives. It
140: expects to see all "key typed" events,
141: which is incompatible with the "return null;"
142: below.
143:
144: This bug triggers jEdit bug 1493185 ( https://sourceforge.net/tracker/?func=detail&aid=1493185&group_id=588&atid=100588 ).
145:
146: Thus, we disable the possibility of
147: key event swallowing for the new key event
148: handling.
149:
150: */
151: } else {
152: JComponent comp = (JComponent) focusOwner;
153: InputMap map = comp.getInputMap();
154: ActionMap am = comp.getActionMap();
155:
156: if (map != null && am != null && comp.isEnabled()) {
157: KeyStroke keyStroke = KeyStroke
158: .getKeyStrokeForEvent(evt);
159: Object binding = map.get(keyStroke);
160: if (binding != null && am.get(binding) != null) {
161: return null;
162: }
163: }
164: }
165:
166: if (evt.isConsumed())
167: return null;
168:
169: if (Debug.DUMP_KEY_EVENTS) {
170: Log.log(Log.DEBUG, this , "Key event (preprocessing) : "
171: + GrabKeyDialog.toString(evt));
172: }
173:
174: return KeyEventWorkaround.processKeyEvent(evt);
175: } //}}}
176:
177: //{{{ processKeyEventSub() method
178: private void processKeyEventSub(boolean focusOnTextArea) {
179: // this is a weird hack.
180: // we don't want C+e a to insert 'a' in the
181: // search bar if the search bar has focus...
182: if (isPrefixActive() && focusOnTextArea) {
183: textArea.requestFocus();
184: }
185: } //}}}
186:
187: //{{{ handleKey() method
188: /**
189: * Handles the given keystroke.
190: * @param keyStroke The key stroke
191: * @param dryRun only calculate the return value, do not have any other effect
192: * @since jEdit 4.2pre5
193: */
194: public boolean handleKey(KeyEventTranslator.Key keyStroke,
195: boolean dryRun) {
196: char input = '\0';
197: if (keyStroke.modifiers == null
198: || keyStroke.modifiers.equals("S")) {
199: switch (keyStroke.key) {
200: case '\n':
201: case '\t':
202: input = (char) keyStroke.key;
203: break;
204: default:
205: input = keyStroke.input;
206: break;
207: }
208: }
209:
210: if (readNextChar != null) {
211: if (input != '\0') {
212: if (!dryRun) {
213: invokeReadNextChar(input);
214: repeatCount = 1;
215: }
216: return true;
217: } else {
218: if (!dryRun) {
219: readNextChar = null;
220: }
221: }
222: }
223: if (!dryRun) {
224: if (input != '\0') {
225: if (!keyStroke.isFromGlobalContext()) { // let user input be only local
226: userInput(input);
227: }
228: } else {
229: // this is retarded. excuse me while I drool
230: // and make stupid noises
231: if (KeyEventWorkaround.isNumericKeypad(keyStroke.key))
232: KeyEventWorkaround.numericKeypadKey();
233: else if (keyStroke.key == 0) {
234: if ("C".equals(keyStroke.modifiers)) {
235: switch (keyStroke.input) {
236: case 'c':
237: Registers.copy(textArea, '$');
238: break;
239: case 'x':
240: Registers.cut(textArea, '$');
241: break;
242: case 'v':
243: Registers.paste(textArea, '$');
244: break;
245: }
246: }
247: } else {
248: switch (keyStroke.key) {
249: case KeyEvent.VK_HOME:
250: if ("C".equals(keyStroke.modifiers))
251: textArea.goToBufferStart("S"
252: .equals(keyStroke.modifiers));
253: else
254: textArea.goToStartOfLine("S"
255: .equals(keyStroke.modifiers));
256: break;
257: case KeyEvent.VK_END:
258: if ("C".equals(keyStroke.modifiers))
259: textArea.goToBufferEnd("S"
260: .equals(keyStroke.modifiers));
261: else
262: textArea.goToBufferEnd("S"
263: .equals(keyStroke.modifiers));
264: break;
265: case KeyEvent.VK_LEFT:
266: if ("C".equals(keyStroke.modifiers))
267: textArea.goToPrevWord("S"
268: .equals(keyStroke.modifiers));
269: else
270: textArea.goToPrevCharacter("S"
271: .equals(keyStroke.modifiers));
272: break;
273: case KeyEvent.VK_RIGHT:
274: if ("C".equals(keyStroke.modifiers))
275: textArea.goToNextWord("S"
276: .equals(keyStroke.modifiers));
277: else
278: textArea.goToNextCharacter("S"
279: .equals(keyStroke.modifiers));
280: break;
281: case KeyEvent.VK_UP:
282: textArea.goToPrevLine("S"
283: .equals(keyStroke.modifiers));
284: break;
285: case KeyEvent.VK_DOWN:
286: textArea.goToNextLine("S"
287: .equals(keyStroke.modifiers));
288: break;
289: case KeyEvent.VK_BACK_SPACE:
290: textArea.backspace();
291: break;
292: case KeyEvent.VK_DELETE:
293: textArea.delete();
294: break;
295: }
296: }
297:
298: }
299: }
300: return false;
301: } //}}}
302:
303: //{{{ userInput() method
304: protected void userInput(char ch) {
305: lastActionCount = 0;
306:
307: if (repeatCount == 1)
308: textArea.userInput(ch);
309: else {
310: // stop people doing dumb stuff like C+ENTER 100 C+n
311: /*if(repeatCount > REPEAT_COUNT_THRESHOLD)
312: {
313: Object[] pp = { String.valueOf(ch),
314: repeatCount };
315:
316: if(GUIUtilities.confirm(view,
317: "large-repeat-count.user-input",pp,
318: JOptionPane.WARNING_MESSAGE,
319: JOptionPane.YES_NO_OPTION)
320: != JOptionPane.YES_OPTION)
321: {
322: repeatCount = 1;
323: view.getStatus().setMessage(null);
324: return;
325: }
326: }
327:
328: JEditBuffer buffer = textArea.getBuffer();
329: try
330: {
331: if(repeatCount != 1)
332: buffer.beginCompoundEdit();
333: for(int i = 0; i < repeatCount; i++)
334: textArea.userInput(ch);
335: }
336: finally
337: {
338: if(repeatCount != 1)
339: buffer.endCompoundEdit();
340: } */
341: }
342:
343: repeatCount = 1;
344: } //}}}
345:
346: //{{{ invokeReadNextChar() method
347: protected void invokeReadNextChar(char ch) {
348: String charStr = MiscUtilities.charsToEscapes(String
349: .valueOf(ch));
350:
351: // this might be a bit slow if __char__ occurs a lot
352: int index;
353: while ((index = readNextChar.indexOf("__char__")) != -1) {
354: readNextChar = readNextChar.substring(0, index) + '\''
355: + charStr + '\''
356: + readNextChar.substring(index + 8);
357: }
358: readNextChar = null;
359: } //}}}
360: }
|