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