View Javadoc

1   /*
2    * InputHandler.java - Manages key bindings and executes actions
3    * Copyright (C) 1999 Slava Pestov
4    *
5    * You may use and modify this package for any purpose. Redistribution is
6    * permitted, in both source and binary form, provided that this notice
7    * remains intact in all source distributions of this package.
8    */
9   
10  package org.syntax.jedit;
11  
12  import java.awt.Component;
13  import java.awt.Toolkit;
14  import java.awt.event.ActionEvent;
15  import java.awt.event.ActionListener;
16  import java.awt.event.InputEvent;
17  import java.awt.event.KeyAdapter;
18  import java.awt.event.KeyEvent;
19  import java.util.Enumeration;
20  import java.util.EventObject;
21  import java.util.Hashtable;
22  
23  import javax.swing.AbstractAction;
24  import javax.swing.Action;
25  import javax.swing.JPopupMenu;
26  import javax.swing.text.BadLocationException;
27  
28  import com.eviware.soapui.SoapUI;
29  import com.eviware.soapui.support.UISupport;
30  
31  /***
32   * An input handler converts the user's key strokes into concrete actions. It
33   * also takes care of macro recording and action repetition.
34   * <p>
35   * 
36   * This class provides all the necessary support code for an input handler, but
37   * doesn't actually do any key binding logic. It is up to the implementations of
38   * this class to do so.
39   * 
40   * @author Slava Pestov
41   * @version $Id$
42   * @see org.syntax.jedit.DefaultInputHandler 08/12/2002 Clipboard actions
43   *      (Oliver Henning)
44   */
45  public abstract class InputHandler extends KeyAdapter
46  {
47     private static boolean useCtrlKeyInsteadOfMenuKey = false;
48     
49     public static void useCtrlKeyInsteadOfMenuKey(boolean b)
50     {
51        useCtrlKeyInsteadOfMenuKey = b;
52     }
53     
54     public static int getMenuShortcutKeyMask()
55     {
56        if(useCtrlKeyInsteadOfMenuKey)
57           return InputEvent.CTRL_MASK;
58        else
59           return Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
60     }
61     
62  	/***
63  	 * If this client property is set to Boolean.TRUE on the text area, the
64  	 * home/end keys will support 'smart' BRIEF-like behaviour (one press =
65  	 * start/end of line, two presses = start/end of viewscreen, three presses =
66  	 * start/end of document). By default, this property is not set.
67  	 */
68  	public static final String SMART_HOME_END_PROPERTY = "InputHandler.homeEnd";
69  
70  	public static final ActionListener BACKSPACE = new backspace();
71  	public static final ActionListener BACKSPACE_WORD = new backspace_word();
72  	public static final ActionListener DELETE = new delete();
73  	public static final ActionListener DELETE_WORD = new delete_word();
74  	public static final ActionListener END = new end( false );
75  	public static final ActionListener DOCUMENT_END = new document_end( false );
76  	public static final ActionListener SELECT_ALL = new select_all();
77  	public static final ActionListener SELECT_END = new end( true );
78  	public static final ActionListener SELECT_DOC_END = new document_end( true );
79  	public static final ActionListener INSERT_BREAK = new insert_break();
80  	public static final ActionListener INSERT_TAB = new insert_tab();
81  	public static final ActionListener HOME = new home( false );
82  	public static final ActionListener DOCUMENT_HOME = new document_home( false );
83  	public static final ActionListener SELECT_HOME = new home( true );
84  	public static final ActionListener SELECT_DOC_HOME = new document_home( true );
85  	public static final ActionListener NEXT_CHAR = new next_char( false );
86  	public static final ActionListener NEXT_LINE = new next_line( false );
87  	public static final ActionListener NEXT_PAGE = new next_page( false );
88  	public static final ActionListener NEXT_WORD = new next_word( false );
89  	public static final ActionListener SELECT_NEXT_CHAR = new next_char( true );
90  	public static final ActionListener SELECT_NEXT_LINE = new next_line( true );
91  	public static final ActionListener SELECT_NEXT_PAGE = new next_page( true );
92  	public static final ActionListener SELECT_NEXT_WORD = new next_word( true );
93  	public static final ActionListener OVERWRITE = new overwrite();
94  	public static final ActionListener PREV_CHAR = new prev_char( false );
95  	public static final ActionListener PREV_LINE = new prev_line( false );
96  	public static final ActionListener PREV_PAGE = new prev_page( false );
97  	public static final ActionListener PREV_WORD = new prev_word( false );
98  	public static final ActionListener SELECT_PREV_CHAR = new prev_char( true );
99  	public static final ActionListener SELECT_PREV_LINE = new prev_line( true );
100 	public static final ActionListener SELECT_PREV_PAGE = new prev_page( true );
101 	public static final ActionListener SELECT_PREV_WORD = new prev_word( true );
102 	public static final ActionListener REPEAT = new repeat();
103 	public static final ActionListener TOGGLE_RECT = new toggle_rect();
104 	// Clipboard
105 	public static final Action CLIP_COPY = new clip_copy();
106 	public static final Action CLIP_PASTE = new clip_paste();
107 	public static final Action CLIP_CUT = new clip_cut();
108 
109 	// Default action
110 	public static final ActionListener INSERT_CHAR = new insert_char();
111 
112 	private static Hashtable<String, ActionListener> actions;
113 
114 	static
115 	{
116 		actions = new Hashtable<String, ActionListener>();
117 		actions.put( "backspace", BACKSPACE );
118 		actions.put( "backspace-word", BACKSPACE_WORD );
119 		actions.put( "delete", DELETE );
120 		actions.put( "delete-word", DELETE_WORD );
121 		actions.put( "end", END );
122 		actions.put( "select-all", SELECT_ALL );
123 		actions.put( "select-end", SELECT_END );
124 		actions.put( "document-end", DOCUMENT_END );
125 		actions.put( "select-doc-end", SELECT_DOC_END );
126 		actions.put( "insert-break", INSERT_BREAK );
127 		actions.put( "insert-tab", INSERT_TAB );
128 		actions.put( "home", HOME );
129 		actions.put( "select-home", SELECT_HOME );
130 		actions.put( "document-home", DOCUMENT_HOME );
131 		actions.put( "select-doc-home", SELECT_DOC_HOME );
132 		actions.put( "next-char", NEXT_CHAR );
133 		actions.put( "next-line", NEXT_LINE );
134 		actions.put( "next-page", NEXT_PAGE );
135 		actions.put( "next-word", NEXT_WORD );
136 		actions.put( "select-next-char", SELECT_NEXT_CHAR );
137 		actions.put( "select-next-line", SELECT_NEXT_LINE );
138 		actions.put( "select-next-page", SELECT_NEXT_PAGE );
139 		actions.put( "select-next-word", SELECT_NEXT_WORD );
140 		actions.put( "overwrite", OVERWRITE );
141 		actions.put( "prev-char", PREV_CHAR );
142 		actions.put( "prev-line", PREV_LINE );
143 		actions.put( "prev-page", PREV_PAGE );
144 		actions.put( "prev-word", PREV_WORD );
145 		actions.put( "select-prev-char", SELECT_PREV_CHAR );
146 		actions.put( "select-prev-line", SELECT_PREV_LINE );
147 		actions.put( "select-prev-page", SELECT_PREV_PAGE );
148 		actions.put( "select-prev-word", SELECT_PREV_WORD );
149 		actions.put( "repeat", REPEAT );
150 		actions.put( "toggle-rect", TOGGLE_RECT );
151 		actions.put( "insert-char", INSERT_CHAR );
152 		actions.put( "clipboard-copy", CLIP_COPY );
153 		actions.put( "clipboard-paste", CLIP_PASTE );
154 		actions.put( "clipboard-cut", CLIP_CUT );
155 	}
156 
157 	/***
158 	 * Returns a named text area action.
159 	 * 
160 	 * @param name
161 	 *           The action name
162 	 */
163 	public static ActionListener getAction( String name )
164 	{
165 		return actions.get( name );
166 	}
167 
168 	/***
169 	 * Returns the name of the specified text area action.
170 	 * 
171 	 * @param listener
172 	 *           The action
173 	 */
174 	public static String getActionName( ActionListener listener )
175 	{
176 		Enumeration _enum = getActions();
177 		while( _enum.hasMoreElements() )
178 		{
179 			String name = ( String )_enum.nextElement();
180 			ActionListener _listener = getAction( name );
181 			if( _listener == listener )
182 				return name;
183 		}
184 		return null;
185 	}
186 
187 	/***
188 	 * Returns an enumeration of all available actions.
189 	 */
190 	public static Enumeration getActions()
191 	{
192 		return actions.keys();
193 	}
194 
195 	/***
196 	 * Adds the default key bindings to this input handler. This should not be
197 	 * called in the constructor of this input handler, because applications
198 	 * might load the key bindings from a file, etc.
199 	 */
200 	public abstract void addDefaultKeyBindings();
201 
202 	/***
203 	 * Adds a key binding to this input handler.
204 	 * 
205 	 * @param keyBinding
206 	 *           The key binding (the format of this is input-handler specific)
207 	 * @param action
208 	 *           The action
209 	 */
210 	public abstract void addKeyBinding( String keyBinding, ActionListener action );
211 
212 	/***
213 	 * Removes a key binding from this input handler.
214 	 * 
215 	 * @param keyBinding
216 	 *           The key binding
217 	 */
218 	public abstract void removeKeyBinding( String keyBinding );
219 
220 	/***
221 	 * Removes all key bindings from this input handler.
222 	 */
223 	public abstract void removeAllKeyBindings();
224 
225 	/***
226 	 * Grabs the next key typed event and invokes the specified action with the
227 	 * key as a the action command.
228 	 * 
229 	 * @param action
230 	 *           The action
231 	 */
232 	public void grabNextKeyStroke( ActionListener listener )
233 	{
234 		grabAction = listener;
235 	}
236 
237 	/***
238 	 * Returns if repeating is enabled. When repeating is enabled, actions will
239 	 * be executed multiple times. This is usually invoked with a special key
240 	 * stroke in the input handler.
241 	 */
242 	public boolean isRepeatEnabled()
243 	{
244 		return repeat;
245 	}
246 
247 	/***
248 	 * Enables repeating. When repeating is enabled, actions will be executed
249 	 * multiple times. Once repeating is enabled, the input handler should read a
250 	 * number from the keyboard.
251 	 */
252 	public void setRepeatEnabled( boolean repeat )
253 	{
254 		this.repeat = repeat;
255 	}
256 
257 	/***
258 	 * Returns the number of times the next action will be repeated.
259 	 */
260 	public int getRepeatCount()
261 	{
262 		return( repeat ? Math.max( 1, repeatCount ) : 1 );
263 	}
264 
265 	/***
266 	 * Sets the number of times the next action will be repeated.
267 	 * 
268 	 * @param repeatCount
269 	 *           The repeat count
270 	 */
271 	public void setRepeatCount( int repeatCount )
272 	{
273 		this.repeatCount = repeatCount;
274 	}
275 
276 	/***
277 	 * Returns the macro recorder. If this is non-null, all executed actions
278 	 * should be forwarded to the recorder.
279 	 */
280 	public InputHandler.MacroRecorder getMacroRecorder()
281 	{
282 		return recorder;
283 	}
284 
285 	/***
286 	 * Sets the macro recorder. If this is non-null, all executed actions should
287 	 * be forwarded to the recorder.
288 	 * 
289 	 * @param recorder
290 	 *           The macro recorder
291 	 */
292 	public void setMacroRecorder( InputHandler.MacroRecorder recorder )
293 	{
294 		this.recorder = recorder;
295 	}
296 
297 	/***
298 	 * Returns a copy of this input handler that shares the same key bindings.
299 	 * Setting key bindings in the copy will also set them in the original.
300 	 */
301 	public abstract InputHandler copy();
302 
303 	/***
304 	 * Executes the specified action, repeating and recording it as necessary.
305 	 * 
306 	 * @param listener
307 	 *           The action listener
308 	 * @param source
309 	 *           The event source
310 	 * @param actionCommand
311 	 *           The action command
312 	 */
313 	public void executeAction( ActionListener listener, Object source, String actionCommand )
314 	{
315 		// create event
316 		ActionEvent evt = new ActionEvent( source, ActionEvent.ACTION_PERFORMED, actionCommand );
317 
318 		// don't do anything if the action is a wrapper
319 		// (like EditAction.Wrapper)
320 		if( listener instanceof Wrapper )
321 		{
322 			listener.actionPerformed( evt );
323 			return;
324 		}
325 
326 		// remember old values, in case action changes them
327 		boolean _repeat = repeat;
328 		int _repeatCount = getRepeatCount();
329 
330 		// execute the action
331 		if( listener instanceof InputHandler.NonRepeatable )
332 			listener.actionPerformed( evt );
333 		else
334 		{
335 			for( int i = 0; i < Math.max( 1, repeatCount ); i++ )
336 				listener.actionPerformed( evt );
337 		}
338 
339 		// do recording. Notice that we do no recording whatsoever
340 		// for actions that grab keys
341 		if( grabAction == null )
342 		{
343 			if( recorder != null )
344 			{
345 				if( !( listener instanceof InputHandler.NonRecordable ) )
346 				{
347 					if( _repeatCount != 1 )
348 						recorder.actionPerformed( REPEAT, String.valueOf( _repeatCount ) );
349 
350 					recorder.actionPerformed( listener, actionCommand );
351 				}
352 			}
353 
354 			// If repeat was true originally, clear it
355 			// Otherwise it might have been set by the action, etc
356 			if( _repeat )
357 			{
358 				repeat = false;
359 				repeatCount = 0;
360 			}
361 		}
362 	}
363 
364 	/***
365 	 * Returns the text area that fired the specified event.
366 	 * 
367 	 * @param evt
368 	 *           The event
369 	 */
370 	public static JEditTextArea getTextArea( EventObject evt )
371 	{
372 		if( evt != null )
373 		{
374 			Object o = evt.getSource();
375 			if( o instanceof Component )
376 			{
377 				// find the parent text area
378 				Component c = ( Component )o;
379 				for( ;; )
380 				{
381 					if( c instanceof JEditTextArea )
382 						return ( JEditTextArea )c;
383 					else if( c == null )
384 						break;
385 					if( c instanceof JPopupMenu )
386 						c = ( ( JPopupMenu )c ).getInvoker();
387 					else
388 						c = c.getParent();
389 				}
390 			}
391 		}
392 
393 		// this shouldn't happen
394 		System.err.println( "BUG: getTextArea() returning null" );
395 		System.err.println( "Report this to Slava Pestov <sp@gjt.org>" );
396 		return null;
397 	}
398 
399 	// protected members
400 
401 	/***
402 	 * If a key is being grabbed, this method should be called with the
403 	 * appropriate key event. It executes the grab action with the typed
404 	 * character as the parameter.
405 	 */
406 	protected void handleGrabAction( KeyEvent evt )
407 	{
408 		// Clear it *before* it is executed so that executeAction()
409 		// resets the repeat count
410 		ActionListener _grabAction = grabAction;
411 		grabAction = null;
412 		executeAction( _grabAction, evt.getSource(), String.valueOf( evt.getKeyChar() ) );
413 	}
414 
415 	// protected members
416 	protected ActionListener grabAction;
417 	protected boolean repeat;
418 	protected int repeatCount;
419 	protected InputHandler.MacroRecorder recorder;
420 
421 	/***
422 	 * If an action implements this interface, it should not be repeated.
423 	 * Instead, it will handle the repetition itself.
424 	 */
425 	public interface NonRepeatable
426 	{
427 	}
428 
429 	/***
430 	 * If an action implements this interface, it should not be recorded by the
431 	 * macro recorder. Instead, it will do its own recording.
432 	 */
433 	public interface NonRecordable
434 	{
435 	}
436 
437 	/***
438 	 * For use by EditAction.Wrapper only.
439 	 * 
440 	 * @since jEdit 2.2final
441 	 */
442 	public interface Wrapper
443 	{
444 	}
445 
446 	/***
447 	 * Macro recorder.
448 	 */
449 	public interface MacroRecorder
450 	{
451 		void actionPerformed( ActionListener listener, String actionCommand );
452 	}
453 
454 	public static class backspace implements ActionListener
455 	{
456 		public void actionPerformed( ActionEvent evt )
457 		{
458 			JEditTextArea textArea = getTextArea( evt );
459 
460 			if( !textArea.isEditable() )
461 			{
462 				textArea.getToolkit().beep();
463 				return;
464 			}
465 
466 			if( textArea.getSelectionStart() != textArea.getSelectionEnd() )
467 			{
468 				textArea.setSelectedText( "" );
469 			}
470 			else
471 			{
472 				int caret = textArea.getCaretPosition();
473 				if( caret == 0 )
474 				{
475 					textArea.getToolkit().beep();
476 					return;
477 				}
478 				try
479 				{
480 					textArea.getDocument().remove( caret - 1, 1 );
481 				}
482 				catch( BadLocationException bl )
483 				{
484 					SoapUI.logError( bl );
485 				}
486 			}
487 		}
488 	}
489 
490 	public static class backspace_word implements ActionListener
491 	{
492 		public void actionPerformed( ActionEvent evt )
493 		{
494 			JEditTextArea textArea = getTextArea( evt );
495 			int start = textArea.getSelectionStart();
496 			if( start != textArea.getSelectionEnd() )
497 			{
498 				textArea.setSelectedText( "" );
499 			}
500 
501 			int line = textArea.getCaretLine();
502 			int lineStart = textArea.getLineStartOffset( line );
503 			int caret = start - lineStart;
504 
505 			String lineText = textArea.getLineText( textArea.getCaretLine() );
506 
507 			if( caret == 0 )
508 			{
509 				if( lineStart == 0 )
510 				{
511 					textArea.getToolkit().beep();
512 					return;
513 				}
514 				caret-- ;
515 			}
516 			else
517 			{
518 				String noWordSep = ( String )textArea.getDocument().getProperty( "noWordSep" );
519 				caret = TextUtilities.findWordStart( lineText, caret, noWordSep );
520 			}
521 
522 			try
523 			{
524 				textArea.getDocument().remove( caret + lineStart, start - ( caret + lineStart ) );
525 			}
526 			catch( BadLocationException bl )
527 			{
528 				SoapUI.logError( bl );
529 			}
530 		}
531 	}
532 
533 	public static class delete implements ActionListener
534 	{
535 		public void actionPerformed( ActionEvent evt )
536 		{
537 			JEditTextArea textArea = getTextArea( evt );
538 
539 			if( !textArea.isEditable() )
540 			{
541 				textArea.getToolkit().beep();
542 				return;
543 			}
544 
545 			if( textArea.getSelectionStart() != textArea.getSelectionEnd() )
546 			{
547 				textArea.setSelectedText( "" );
548 			}
549 			else
550 			{
551 				int caret = textArea.getCaretPosition();
552 				if( caret == textArea.getDocumentLength() )
553 				{
554 					textArea.getToolkit().beep();
555 					return;
556 				}
557 				try
558 				{
559 					textArea.getDocument().remove( caret, 1 );
560 				}
561 				catch( BadLocationException bl )
562 				{
563 					SoapUI.logError( bl );
564 				}
565 			}
566 		}
567 	}
568 
569 	public static class delete_word implements ActionListener
570 	{
571 		public void actionPerformed( ActionEvent evt )
572 		{
573 			JEditTextArea textArea = getTextArea( evt );
574 			int start = textArea.getSelectionStart();
575 			if( start != textArea.getSelectionEnd() )
576 			{
577 				textArea.setSelectedText( "" );
578 			}
579 
580 			int line = textArea.getCaretLine();
581 			int lineStart = textArea.getLineStartOffset( line );
582 			int caret = start - lineStart;
583 
584 			String lineText = textArea.getLineText( textArea.getCaretLine() );
585 
586 			if( caret == lineText.length() )
587 			{
588 				if( lineStart + caret == textArea.getDocumentLength() )
589 				{
590 					textArea.getToolkit().beep();
591 					return;
592 				}
593 				caret++ ;
594 			}
595 			else
596 			{
597 				String noWordSep = ( String )textArea.getDocument().getProperty( "noWordSep" );
598 				caret = TextUtilities.findWordEnd( lineText, caret, noWordSep );
599 			}
600 
601 			try
602 			{
603 				textArea.getDocument().remove( start, ( caret + lineStart ) - start );
604 			}
605 			catch( BadLocationException bl )
606 			{
607 				SoapUI.logError( bl );
608 			}
609 		}
610 	}
611 
612 	public static class end implements ActionListener
613 	{
614 		private boolean select;
615 
616 		public end( boolean select )
617 		{
618 			this.select = select;
619 		}
620 
621 		public void actionPerformed( ActionEvent evt )
622 		{
623 			JEditTextArea textArea = getTextArea( evt );
624 
625 			int caret = textArea.getCaretPosition();
626 
627 			int lastOfLine = textArea.getLineEndOffset( textArea.getCaretLine() ) - 1;
628 			int lastVisibleLine = textArea.getFirstLine() + textArea.getVisibleLines();
629 			if( lastVisibleLine >= textArea.getLineCount() )
630 			{
631 				lastVisibleLine = Math.min( textArea.getLineCount() - 1, lastVisibleLine );
632 			}
633 			else
634 				lastVisibleLine -= 1; // (textArea.getElectricScroll() + 1);
635 
636 			int lastVisible = textArea.getLineEndOffset( lastVisibleLine ) - 1;
637 			int lastDocument = textArea.getDocumentLength();
638 
639 			if( caret == lastDocument )
640 			{
641 				textArea.getToolkit().beep();
642 				return;
643 			}
644 			else if( !Boolean.TRUE.equals( textArea.getClientProperty( SMART_HOME_END_PROPERTY ) ) )
645 				caret = lastOfLine;
646 			else if( caret == lastVisible )
647 				caret = lastDocument;
648 			else if( caret == lastOfLine )
649 				caret = lastVisible;
650 			else
651 				caret = lastOfLine;
652 
653 			if( select )
654 				textArea.select( textArea.getMarkPosition(), caret );
655 			else
656 				textArea.setCaretPosition( caret );
657 		}
658 	}
659 
660 	public static class select_all implements ActionListener
661 	{
662 		public void actionPerformed( ActionEvent evt )
663 		{
664 			JEditTextArea textArea = getTextArea( evt );
665 			textArea.selectAll();
666 		}
667 	}
668 
669 	public static class document_end implements ActionListener
670 	{
671 		private boolean select;
672 
673 		public document_end( boolean select )
674 		{
675 			this.select = select;
676 		}
677 
678 		public void actionPerformed( ActionEvent evt )
679 		{
680 			JEditTextArea textArea = getTextArea( evt );
681 			if( select )
682 				textArea.select( textArea.getMarkPosition(), textArea.getDocumentLength() );
683 			else
684 				textArea.setCaretPosition( textArea.getDocumentLength() );
685 		}
686 	}
687 
688 	public static class home implements ActionListener
689 	{
690 		private boolean select;
691 
692 		public home( boolean select )
693 		{
694 			this.select = select;
695 		}
696 
697 		public void actionPerformed( ActionEvent evt )
698 		{
699 			JEditTextArea textArea = getTextArea( evt );
700 
701 			int caret = textArea.getCaretPosition();
702 
703 			int firstLine = textArea.getFirstLine();
704 
705 			int firstOfLine = textArea.getLineStartOffset( textArea.getCaretLine() );
706 			int firstVisibleLine = ( firstLine == 0 ? 0 : firstLine + 1 );
707 			int firstVisible = textArea.getLineStartOffset( firstVisibleLine );
708 
709 			if( caret == 0 )
710 			{
711 				textArea.getToolkit().beep();
712 				return;
713 			}
714 			else if( !Boolean.TRUE.equals( textArea.getClientProperty( SMART_HOME_END_PROPERTY ) ) )
715 				caret = firstOfLine;
716 			else if( caret == firstVisible )
717 				caret = 0;
718 			else if( caret == firstOfLine )
719 				caret = firstVisible;
720 			else
721 				caret = firstOfLine;
722 
723 			if( select )
724 				textArea.select( textArea.getMarkPosition(), caret );
725 			else
726 				textArea.setCaretPosition( caret );
727 		}
728 	}
729 
730 	public static class document_home implements ActionListener
731 	{
732 		private boolean select;
733 
734 		public document_home( boolean select )
735 		{
736 			this.select = select;
737 		}
738 
739 		public void actionPerformed( ActionEvent evt )
740 		{
741 			JEditTextArea textArea = getTextArea( evt );
742 			if( select )
743 				textArea.select( textArea.getMarkPosition(), 0 );
744 			else
745 				textArea.setCaretPosition( 0 );
746 		}
747 	}
748 
749 	public static class insert_break implements ActionListener
750 	{
751 		public void actionPerformed( ActionEvent evt )
752 		{
753 			JEditTextArea textArea = getTextArea( evt );
754 
755 			if( !textArea.isEditable() )
756 			{
757 				textArea.getToolkit().beep();
758 				return;
759 			}
760 
761 			textArea.setSelectedText( "\n" );
762 		}
763 	}
764 
765 	public static class insert_tab implements ActionListener
766 	{
767 		public void actionPerformed( ActionEvent evt )
768 		{
769 			JEditTextArea textArea = getTextArea( evt );
770 
771 			if( !textArea.isEditable() )
772 			{
773 				textArea.getToolkit().beep();
774 				return;
775 			}
776 
777 			textArea.overwriteSetSelectedText( "\t" );
778 		}
779 	}
780 
781 	public static class next_char implements ActionListener
782 	{
783 		private boolean select;
784 
785 		public next_char( boolean select )
786 		{
787 			this.select = select;
788 		}
789 
790 		public void actionPerformed( ActionEvent evt )
791 		{
792 			JEditTextArea textArea = getTextArea( evt );
793 			int caret = textArea.getCaretPosition();
794 			if( caret == textArea.getDocumentLength() )
795 			{
796 				textArea.getToolkit().beep();
797 				return;
798 			}
799 
800 			if( select )
801 				textArea.select( textArea.getMarkPosition(), caret + 1 );
802 			else
803 				textArea.setCaretPosition( caret + 1 );
804 		}
805 	}
806 
807 	public static class next_line implements ActionListener
808 	{
809 		private boolean select;
810 
811 		public next_line( boolean select )
812 		{
813 			this.select = select;
814 		}
815 
816 		public void actionPerformed( ActionEvent evt )
817 		{
818 			JEditTextArea textArea = getTextArea( evt );
819 			int caret = textArea.getCaretPosition();
820 			int line = textArea.getCaretLine();
821 
822 			if( line == textArea.getLineCount() - 1 )
823 			{
824 				textArea.getToolkit().beep();
825 				return;
826 			}
827 
828 			int magic = textArea.getMagicCaretPosition();
829 			if( magic == -1 )
830 			{
831 				magic = textArea.offsetToX( line, caret - textArea.getLineStartOffset( line ) );
832 			}
833 
834 			caret = textArea.getLineStartOffset( line + 1 ) + textArea.xToOffset( line + 1, magic );
835 			if( select )
836 				textArea.select( textArea.getMarkPosition(), caret );
837 			else
838 				textArea.setCaretPosition( caret );
839 			textArea.setMagicCaretPosition( magic );
840 		}
841 	}
842 
843 	public static class next_page implements ActionListener
844 	{
845 		private boolean select;
846 
847 		public next_page( boolean select )
848 		{
849 			this.select = select;
850 		}
851 
852 		public void actionPerformed( ActionEvent evt )
853 		{
854 			JEditTextArea textArea = getTextArea( evt );
855 			int lineCount = textArea.getLineCount();
856 			int firstLine = textArea.getFirstLine();
857 			int visibleLines = textArea.getVisibleLines();
858 			int line = textArea.getCaretLine();
859 
860 			firstLine += visibleLines;
861 
862 			if( firstLine + visibleLines >= lineCount - 1 )
863 				firstLine = lineCount - visibleLines;
864 
865 			textArea.setFirstLine( firstLine );
866 
867 			int caret = textArea.getLineStartOffset( Math.min( textArea.getLineCount() - 1, line + visibleLines ) );
868 			if( select )
869 				textArea.select( textArea.getMarkPosition(), caret );
870 			else
871 				textArea.setCaretPosition( caret );
872 		}
873 	}
874 
875 	public static class next_word implements ActionListener
876 	{
877 		private boolean select;
878 
879 		public next_word( boolean select )
880 		{
881 			this.select = select;
882 		}
883 
884 		public void actionPerformed( ActionEvent evt )
885 		{
886 			JEditTextArea textArea = getTextArea( evt );
887 			int caret = textArea.getCaretPosition();
888 			int line = textArea.getCaretLine();
889 			int lineStart = textArea.getLineStartOffset( line );
890 			caret -= lineStart;
891 
892 			String lineText = textArea.getLineText( textArea.getCaretLine() );
893 
894 			if( caret == lineText.length() )
895 			{
896 				if( lineStart + caret == textArea.getDocumentLength() )
897 				{
898 					textArea.getToolkit().beep();
899 					return;
900 				}
901 				caret++ ;
902 			}
903 			else
904 			{
905 				String noWordSep = ( String )textArea.getDocument().getProperty( "noWordSep" );
906 				caret = TextUtilities.findWordEnd( lineText, caret, noWordSep );
907 			}
908 
909 			if( select )
910 				textArea.select( textArea.getMarkPosition(), lineStart + caret );
911 			else
912 				textArea.setCaretPosition( lineStart + caret );
913 		}
914 	}
915 
916 	public static class overwrite implements ActionListener
917 	{
918 		public void actionPerformed( ActionEvent evt )
919 		{
920 			JEditTextArea textArea = getTextArea( evt );
921 			textArea.setOverwriteEnabled( !textArea.isOverwriteEnabled() );
922 		}
923 	}
924 
925 	public static class prev_char implements ActionListener
926 	{
927 		private boolean select;
928 
929 		public prev_char( boolean select )
930 		{
931 			this.select = select;
932 		}
933 
934 		public void actionPerformed( ActionEvent evt )
935 		{
936 			JEditTextArea textArea = getTextArea( evt );
937 			int caret = textArea.getCaretPosition();
938 			if( caret == 0 )
939 			{
940 				textArea.getToolkit().beep();
941 				return;
942 			}
943 
944 			if( select )
945 				textArea.select( textArea.getMarkPosition(), caret - 1 );
946 			else
947 				textArea.setCaretPosition( caret - 1 );
948 		}
949 	}
950 
951 	public static class prev_line implements ActionListener
952 	{
953 		private boolean select;
954 
955 		public prev_line( boolean select )
956 		{
957 			this.select = select;
958 		}
959 
960 		public void actionPerformed( ActionEvent evt )
961 		{
962 			JEditTextArea textArea = getTextArea( evt );
963 			int caret = textArea.getCaretPosition();
964 			int line = textArea.getCaretLine();
965 
966 			if( line == 0 )
967 			{
968 				textArea.getToolkit().beep();
969 				return;
970 			}
971 
972 			int magic = textArea.getMagicCaretPosition();
973 			if( magic == -1 )
974 			{
975 				magic = textArea.offsetToX( line, caret - textArea.getLineStartOffset( line ) );
976 			}
977 
978 			caret = textArea.getLineStartOffset( line - 1 ) + textArea.xToOffset( line - 1, magic );
979 			if( select )
980 				textArea.select( textArea.getMarkPosition(), caret );
981 			else
982 				textArea.setCaretPosition( caret );
983 			textArea.setMagicCaretPosition( magic );
984 		}
985 	}
986 
987 	public static class prev_page implements ActionListener
988 	{
989 		private boolean select;
990 
991 		public prev_page( boolean select )
992 		{
993 			this.select = select;
994 		}
995 
996 		public void actionPerformed( ActionEvent evt )
997 		{
998 			JEditTextArea textArea = getTextArea( evt );
999 			int firstLine = textArea.getFirstLine();
1000 			int visibleLines = textArea.getVisibleLines();
1001 			int line = textArea.getCaretLine();
1002 
1003 			if( firstLine < visibleLines )
1004 				firstLine = visibleLines;
1005 
1006 			textArea.setFirstLine( firstLine - visibleLines );
1007 
1008 			int caret = textArea.getLineStartOffset( Math.max( 0, line - visibleLines ) );
1009 			if( select )
1010 				textArea.select( textArea.getMarkPosition(), caret );
1011 			else
1012 				textArea.setCaretPosition( caret );
1013 		}
1014 	}
1015 
1016 	public static class prev_word implements ActionListener
1017 	{
1018 		private boolean select;
1019 
1020 		public prev_word( boolean select )
1021 		{
1022 			this.select = select;
1023 		}
1024 
1025 		public void actionPerformed( ActionEvent evt )
1026 		{
1027 			JEditTextArea textArea = getTextArea( evt );
1028 			int caret = textArea.getCaretPosition();
1029 			int line = textArea.getCaretLine();
1030 			int lineStart = textArea.getLineStartOffset( line );
1031 			caret -= lineStart;
1032 
1033 			String lineText = textArea.getLineText( textArea.getCaretLine() );
1034 
1035 			if( caret == 0 )
1036 			{
1037 				if( lineStart == 0 )
1038 				{
1039 					textArea.getToolkit().beep();
1040 					return;
1041 				}
1042 				caret-- ;
1043 			}
1044 			else
1045 			{
1046 				String noWordSep = ( String )textArea.getDocument().getProperty( "noWordSep" );
1047 				caret = TextUtilities.findWordStart( lineText, caret, noWordSep );
1048 			}
1049 
1050 			if( select )
1051 				textArea.select( textArea.getMarkPosition(), lineStart + caret );
1052 			else
1053 				textArea.setCaretPosition( lineStart + caret );
1054 		}
1055 	}
1056 
1057 	public static class repeat implements ActionListener, InputHandler.NonRecordable
1058 	{
1059 		public void actionPerformed( ActionEvent evt )
1060 		{
1061 			JEditTextArea textArea = getTextArea( evt );
1062 			textArea.getInputHandler().setRepeatEnabled( true );
1063 			String actionCommand = evt.getActionCommand();
1064 			if( actionCommand != null )
1065 			{
1066 				textArea.getInputHandler().setRepeatCount( Integer.parseInt( actionCommand ) );
1067 			}
1068 		}
1069 	}
1070 
1071 	public static class toggle_rect implements ActionListener
1072 	{
1073 		public void actionPerformed( ActionEvent evt )
1074 		{
1075 			JEditTextArea textArea = getTextArea( evt );
1076 			textArea.setSelectionRectangular( !textArea.isSelectionRectangular() );
1077 		}
1078 	}
1079 
1080 	public static class insert_char implements ActionListener, InputHandler.NonRepeatable
1081 	{
1082 		public void actionPerformed( ActionEvent evt )
1083 		{
1084 			JEditTextArea textArea = getTextArea( evt );
1085 			String str = evt.getActionCommand();
1086 			int repeatCount = textArea.getInputHandler().getRepeatCount();
1087 
1088 			if( textArea.isEditable() )
1089 			{
1090 				StringBuffer buf = new StringBuffer();
1091 				for( int i = 0; i < repeatCount; i++ )
1092 					buf.append( str );
1093 				textArea.overwriteSetSelectedText( buf.toString() );
1094 			}
1095 			else
1096 			{
1097 				textArea.getToolkit().beep();
1098 			}
1099 		}
1100 	}
1101 
1102 	public static class clip_copy extends AbstractAction
1103 	{
1104 
1105 		public clip_copy()
1106 		{
1107 			super( "Copy" );
1108 			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "menu C" ) );
1109 		}
1110 
1111 		public void actionPerformed( ActionEvent evt )
1112 		{
1113 			JEditTextArea textArea = getTextArea( evt );
1114 			textArea.copy();
1115 		}
1116 	}
1117 
1118 	public static class clip_paste extends AbstractAction
1119 	{
1120 
1121 		public clip_paste()
1122 		{
1123 			super( "Paste" );
1124 			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "menu V" ) );
1125 		}
1126 
1127 		public void actionPerformed( ActionEvent evt )
1128 		{
1129 			JEditTextArea textArea = getTextArea( evt );
1130 			textArea.paste();
1131 		}
1132 	}
1133 
1134 	public static class clip_cut extends AbstractAction
1135 	{
1136 
1137 		public clip_cut()
1138 		{
1139 			super( "Cut" );
1140 			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "menu X" ) );
1141 		}
1142 
1143 		public void actionPerformed( ActionEvent evt )
1144 		{
1145 			JEditTextArea textArea = getTextArea( evt );
1146 			textArea.cut();
1147 		}
1148 	}
1149 }