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