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