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