KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > gjt > sp > jedit > gui > InputHandler


1 /*
2  * InputHandler.java - Manages key bindings and executes actions
3  * :tabSize=8:indentSize=8:noTabs=false:
4  * :folding=explicit:collapseFolds=1:
5  *
6  * Copyright (C) 1999, 2003 Slava Pestov
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */

22
23 package org.gjt.sp.jedit.gui;
24
25 //{{{ Imports
26
import javax.swing.*;
27 import javax.swing.text.JTextComponent JavaDoc;
28
29 import org.gjt.sp.jedit.textarea.JEditTextArea;
30 import org.gjt.sp.jedit.*;
31 import org.gjt.sp.jedit.buffer.JEditBuffer;
32 import org.gjt.sp.jedit.input.AbstractInputHandler;
33 import org.gjt.sp.util.Log;
34
35 import java.awt.event.KeyEvent JavaDoc;
36 import java.awt.*;
37 //}}}
38

39 /**
40  * An input handler converts the user's key strokes into concrete actions.
41  * It also takes care of macro recording and action repetition.<p>
42  *
43  * This class provides all the necessary support code for an input
44  * handler, but doesn't actually do any key binding logic. It is up
45  * to the implementations of this class to do so.
46  *
47  * @author Slava Pestov
48  * @version $Id: InputHandler.java 8244 2006-12-19 08:06:03Z kpouer $
49  * @see org.gjt.sp.jedit.gui.DefaultInputHandler
50  */

51 public abstract class InputHandler extends AbstractInputHandler
52 {
53     //{{{ InputHandler constructor
54
/**
55      * Creates a new input handler.
56      * @param view The view
57      */

58     public InputHandler(View view)
59     {
60         super();
61         this.view = view;
62     } //}}}
63

64     //{{{ addKeyBinding() method
65
/**
66      * Adds a key binding to this input handler.
67      * @param keyBinding The key binding (the format of this is
68      * input-handler specific)
69      * @param action The action
70      */

71     public abstract void addKeyBinding(String JavaDoc keyBinding, String JavaDoc action);
72     //}}}
73

74     //{{{ addKeyBinding() method
75
/**
76      * Adds a key binding to this input handler.
77      * @param keyBinding The key binding (the format of this is
78      * input-handler specific)
79      * @param action The action
80      */

81     public abstract void addKeyBinding(String JavaDoc keyBinding, EditAction action);
82     //}}}
83

84     //{{{ removeKeyBinding() method
85
/**
86      * Removes a key binding from this input handler.
87      * @param keyBinding The key binding
88      */

89     public abstract void removeKeyBinding(String JavaDoc keyBinding);
90     //}}}
91

92     //{{{ removeAllKeyBindings() method
93
/**
94      * Removes all key bindings from this input handler.
95      */

96     public abstract void removeAllKeyBindings();
97     //}}}
98

99     /**
100      * Handles a keystroke.
101      * @param keyStroke The key stroke.
102      * @return true if the input could be handled.
103      * @since jEdit 4.2pre5
104      */

105     public final boolean handleKey(KeyEventTranslator.Key keyStroke)
106     {
107         return handleKey(keyStroke, false);
108     }
109
110     /**
111      * Forwards key events directly to the input handler.
112      * This is slightly faster than using a KeyListener
113      * because some Swing overhead is avoided.
114      * @since 4.3pre7
115      */

116     public void processKeyEvent(KeyEvent JavaDoc evt, int from, boolean global)
117     {
118         if(Debug.DUMP_KEY_EVENTS)
119         {
120             Log.log(Log.DEBUG,this,"Key event : "
121                 + GrabKeyDialog.toString(evt) + " from " + from);
122             Log.log(Log.DEBUG,this,view+".isFocused()="+view.isFocused()+'.',new Exception JavaDoc());
123         }
124
125         if(view.getTextArea().hasFocus() && from == View.VIEW)
126             return;
127
128         evt = _preprocessKeyEvent(evt);
129         if(evt == null)
130             return;
131
132         if(Debug.DUMP_KEY_EVENTS)
133         {
134             Log.log(Log.DEBUG,this,"Key event after workaround: "
135                 + GrabKeyDialog.toString(evt) + " from " + from);
136         }
137
138         Component JavaDoc prefixFocusOwner = view.getPrefixFocusOwner();
139         boolean focusOnTextArea = false;
140         switch(evt.getID())
141         {
142         case KeyEvent.KEY_TYPED:
143             // if the user pressed eg C+e n n in the
144
// search bar we want focus to go back there
145
// after the prefix is done
146
if(prefixFocusOwner != null)
147             {
148                 if(prefixFocusOwner.isShowing())
149                 {
150                     prefixFocusOwner.requestFocus();
151                     focusOnTextArea = true;
152                 }
153             }
154
155             if(keyEventInterceptor != null)
156                 keyEventInterceptor.keyTyped(evt);
157             else if(from == View.ACTION_BAR
158                 || (Debug.GLOBAL_SHORTCUTS_FOR_DOCKED_DOCKABLES &&
159                     Options.SIMPLIFIED_KEY_HANDLING)
160                 || isPrefixActive()
161                 || view.getTextArea().hasFocus())
162             {
163                 processKeyEventKeyStrokeHandling(evt,from,"type ",global);
164             }
165
166
167             processKeyEventSub(focusOnTextArea);
168
169             break;
170         case KeyEvent.KEY_PRESSED:
171             if(keyEventInterceptor != null)
172                 keyEventInterceptor.keyPressed(evt);
173             else if(KeyEventWorkaround.isBindable(evt.getKeyCode()))
174             {
175                 if(prefixFocusOwner != null)
176                 {
177                     if(prefixFocusOwner.isShowing())
178                     {
179                         prefixFocusOwner.requestFocus();
180                         focusOnTextArea = true;
181                     }
182                     view.setPrefixFocusOwner(null);
183                 }
184
185                 processKeyEventKeyStrokeHandling(evt,from,"press",global);
186
187                 processKeyEventSub(focusOnTextArea);
188
189             }
190             break;
191         case KeyEvent.KEY_RELEASED:
192             if(keyEventInterceptor != null)
193                 keyEventInterceptor.keyReleased(evt);
194             break;
195         }
196     } //}}}
197

198     //{{{ _preprocessKeyEvent() method
199
private KeyEvent JavaDoc _preprocessKeyEvent(KeyEvent JavaDoc evt)
200     {
201         if(view.isClosed())
202             return null;
203         Component JavaDoc focusOwner = view.getFocusOwner();
204         if (Options.SIMPLIFIED_KEY_HANDLING)
205         {
206             /*
207                 It seems that the "else" path below does
208                 not work. Apparently, is is there to prevent
209                 some keyboard events to be "swallowed" by
210                 jEdit when the keyboard event in fact should
211                 be scheduled to swing for further handling.
212
213                 On some "key typed" events, the "return null;"
214                 is triggered. However, these key events
215                 actually do not seem to be handled elseewhere,
216                 so they are not handled at all.
217
218                 This behaviour exists with old keyboard handling
219                 as well as with new keyboard handling. However,
220                 the new keyboard handling is more sensitive
221                 about what kinds of key events it receives. It
222                 expects to see all "key typed" events,
223                 which is incompatible with the "return null;"
224                 below.
225
226                 This bug triggers jEdit bug 1493185 ( https://sourceforge.net/tracker/?func=detail&aid=1493185&group_id=588&atid=100588 ).
227
228                 Thus, we disable the possibility of
229                 key event swallowing for the new key event
230                 handling.
231
232             */

233         }
234         else
235         {
236             if(focusOwner instanceof JComponent)
237             {
238                 JComponent comp = (JComponent)focusOwner;
239                 InputMap map = comp.getInputMap();
240                 ActionMap am = comp.getActionMap();
241
242                 if(map != null && am != null && comp.isEnabled())
243                 {
244                     KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(evt);
245                     Object JavaDoc binding = map.get(keyStroke);
246                     if(binding != null && am.get(binding) != null)
247                     {
248                         return null;
249                     }
250                 }
251             }
252         }
253
254         if(focusOwner instanceof JTextComponent JavaDoc)
255         {
256             // fix for the bug where key events in JTextComponents
257
// inside views are also handled by the input handler
258
if(evt.getID() == KeyEvent.KEY_PRESSED)
259             {
260                 switch(evt.getKeyCode())
261                 {
262                 case KeyEvent.VK_ENTER:
263                 case KeyEvent.VK_TAB:
264                 case KeyEvent.VK_BACK_SPACE:
265                 case KeyEvent.VK_SPACE:
266                     return null;
267                 }
268             }
269         }
270
271         if(evt.isConsumed())
272             return null;
273
274         if(Debug.DUMP_KEY_EVENTS)
275         {
276             Log.log(Log.DEBUG,this,"Key event (preprocessing) : "
277                     + GrabKeyDialog.toString(evt));
278         }
279
280         return KeyEventWorkaround.processKeyEvent(evt);
281     } //}}}
282

283     //{{{ processKeyEventSub() method
284
private void processKeyEventSub(boolean focusOnTextArea)
285     {
286         // we might have been closed as a result of
287
// the above
288
if(view.isClosed())
289             return;
290
291         // this is a weird hack.
292
// we don't want C+e a to insert 'a' in the
293
// search bar if the search bar has focus...
294
if(isPrefixActive())
295         {
296             Component JavaDoc focusOwner = view.getFocusOwner();
297             if(focusOwner instanceof JTextComponent JavaDoc)
298             {
299                 view.setPrefixFocusOwner(focusOwner);
300                 view.getTextArea().requestFocus();
301             }
302             else if(focusOnTextArea)
303             {
304                 view.getTextArea().requestFocus();
305             }
306             else
307             {
308                 view.setPrefixFocusOwner(null);
309             }
310         }
311         else
312         {
313             view.setPrefixFocusOwner(null);
314         }
315     }
316     //}}}
317

318     //{{{ getRepeatCount() method
319
/**
320      * Returns the number of times the next action will be repeated.
321      */

322     public int getRepeatCount()
323     {
324         return repeatCount;
325     } //}}}
326

327     //{{{ setRepeatCount() method
328
/**
329      * Sets the number of times the next action will be repeated.
330      * @param repeatCount The repeat count
331      */

332     public void setRepeatCount(int repeatCount)
333     {
334         int oldRepeatCount = this.repeatCount;
335         this.repeatCount = repeatCount;
336         if(oldRepeatCount != repeatCount)
337             view.getStatus().setMessage(null);
338     } //}}}
339

340     //{{{ getLastAction() method
341
/**
342      * Returns the last executed action.
343      * @since jEdit 2.5pre5
344      */

345     public EditAction getLastAction()
346     {
347         return lastAction;
348     } //}}}
349

350     //{{{ readNextChar() method
351
/**
352      * Invokes the specified BeanShell code, replacing __char__ in the
353      * code with the next input character.
354      * @param msg The prompt to display in the status bar
355      * @param code The code
356      * @since jEdit 3.2pre2
357      */

358     public void readNextChar(String JavaDoc msg, String JavaDoc code)
359     {
360         view.getStatus().setMessage(msg);
361         readNextChar = code;
362     } //}}}
363

364     //{{{ readNextChar() method
365
/**
366      * @deprecated Use the other form of this method instead
367      */

368     public void readNextChar(String JavaDoc code)
369     {
370         readNextChar = code;
371     } //}}}
372

373     //{{{ invokeAction() method
374
/**
375      * Invokes the specified action, repeating and recording it as
376      * necessary.
377      * @param action The action
378      * @since jEdit 4.2pre1
379      */

380     public void invokeAction(String JavaDoc action)
381     {
382         invokeAction(jEdit.getAction(action));
383     } //}}}
384

385     //{{{ invokeAction() method
386
/**
387      * Invokes the specified action, repeating and recording it as
388      * necessary.
389      * @param action The action
390      */

391     public void invokeAction(EditAction action)
392     {
393         JEditBuffer buffer = view.getBuffer();
394
395         /* if(buffer.insideCompoundEdit())
396             buffer.endCompoundEdit(); */

397
398         // remember the last executed action
399
if(!action.noRememberLast())
400         {
401             HistoryModel.getModel("action").addItem(action.getName());
402             if(lastAction == action)
403                 lastActionCount++;
404             else
405             {
406                 lastAction = action;
407                 lastActionCount = 1;
408             }
409         }
410
411         // remember old values, in case action changes them
412
int _repeatCount = repeatCount;
413
414         // execute the action
415
if(action.noRepeat() || _repeatCount == 1)
416             action.invoke(view);
417         else
418         {
419             // stop people doing dumb stuff like C+ENTER 100 C+n
420
if(_repeatCount > REPEAT_COUNT_THRESHOLD)
421             {
422                 String JavaDoc label = action.getLabel();
423                 if(label == null)
424                     label = action.getName();
425                 else
426                     label = GUIUtilities.prettifyMenuLabel(label);
427
428                 Object JavaDoc[] pp = { label, _repeatCount };
429
430                 if(GUIUtilities.confirm(view,"large-repeat-count",pp,
431                     JOptionPane.WARNING_MESSAGE,
432                     JOptionPane.YES_NO_OPTION)
433                     != JOptionPane.YES_OPTION)
434                 {
435                     repeatCount = 1;
436                     view.getStatus().setMessage(null);
437                     return;
438                 }
439             }
440
441             try
442             {
443                 buffer.beginCompoundEdit();
444
445                 for(int i = 0; i < _repeatCount; i++)
446                     action.invoke(view);
447             }
448             finally
449             {
450                 buffer.endCompoundEdit();
451             }
452         }
453
454         Macros.Recorder recorder = view.getMacroRecorder();
455
456         if(recorder != null && !action.noRecord())
457             recorder.record(_repeatCount,action.getCode());
458
459         // If repeat was true originally, clear it
460
// Otherwise it might have been set by the action, etc
461
if(_repeatCount != 1)
462         {
463             // first of all, if this action set a
464
// readNextChar, do not clear the repeat
465
if(readNextChar != null)
466                 return;
467
468             repeatCount = 1;
469             view.getStatus().setMessage(null);
470         }
471     } //}}}
472

473     //{{{ invokeLastAction() method
474
public void invokeLastAction()
475     {
476         if(lastAction == null)
477             view.getToolkit().beep();
478         else
479             invokeAction(lastAction);
480     } //}}}
481

482
483     //{{{ Instance variables
484
protected View view;
485
486     protected EditAction lastAction;
487
488     //}}}
489

490     //{{{ userInput() method
491
protected void userInput(char ch)
492     {
493         lastActionCount = 0;
494
495         JEditTextArea textArea = view.getTextArea();
496
497         /* Buffer buffer = view.getBuffer();
498         if(!buffer.insideCompoundEdit())
499             buffer.beginCompoundEdit(); */

500
501         if(repeatCount == 1)
502             textArea.userInput(ch);
503         else
504         {
505             // stop people doing dumb stuff like C+ENTER 100 C+n
506
if(repeatCount > REPEAT_COUNT_THRESHOLD)
507             {
508                 Object JavaDoc[] pp = { String.valueOf(ch),
509                     repeatCount };
510
511                 if(GUIUtilities.confirm(view,
512                     "large-repeat-count.user-input",pp,
513                     JOptionPane.WARNING_MESSAGE,
514                     JOptionPane.YES_NO_OPTION)
515                     != JOptionPane.YES_OPTION)
516                 {
517                     repeatCount = 1;
518                     view.getStatus().setMessage(null);
519                     return;
520                 }
521             }
522
523             JEditBuffer buffer = view.getBuffer();
524             try
525             {
526                 if(repeatCount != 1)
527                     buffer.beginCompoundEdit();
528                 for(int i = 0; i < repeatCount; i++)
529                     textArea.userInput(ch);
530             }
531             finally
532             {
533                 if(repeatCount != 1)
534                     buffer.endCompoundEdit();
535             }
536         }
537
538         Macros.Recorder recorder = view.getMacroRecorder();
539
540         if(recorder != null)
541         {
542             recorder.recordInput(repeatCount,ch,
543                 textArea.isOverwriteEnabled());
544         }
545
546         repeatCount = 1;
547     } //}}}
548

549     //{{{ invokeReadNextChar() method
550
protected void invokeReadNextChar(char ch)
551     {
552         JEditBuffer buffer = view.getBuffer();
553
554         /* if(buffer.insideCompoundEdit())
555             buffer.endCompoundEdit(); */

556
557         String JavaDoc charStr = MiscUtilities.charsToEscapes(String.valueOf(ch));
558
559         // this might be a bit slow if __char__ occurs a lot
560
int index;
561         while((index = readNextChar.indexOf("__char__")) != -1)
562         {
563             readNextChar = readNextChar.substring(0,index)
564                 + '\'' + charStr + '\''
565                 + readNextChar.substring(index + 8);
566         }
567
568         Macros.Recorder recorder = view.getMacroRecorder();
569         if(recorder != null)
570             recorder.record(getRepeatCount(),readNextChar);
571
572         view.getStatus().setMessage(null);
573
574         if(getRepeatCount() != 1)
575         {
576             try
577             {
578                 buffer.beginCompoundEdit();
579
580                 BeanShell.eval(view,BeanShell.getNameSpace(),
581                     "for(int i = 1; i < "
582                     + getRepeatCount() + "; i++)\n{\n"
583                     + readNextChar + "\n}");
584             }
585             finally
586             {
587                 buffer.endCompoundEdit();
588             }
589         }
590         else
591             BeanShell.eval(view,BeanShell.getNameSpace(),readNextChar);
592
593         readNextChar = null;
594     } //}}}
595

596     //}}}
597
}
598
Popular Tags