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