View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  
20  package org.xnap.gui.util;
21  
22  import java.awt.event.ActionEvent;
23  import java.awt.event.InputEvent;
24  import java.awt.event.KeyEvent;
25  import java.util.Iterator;
26  import java.util.LinkedList;
27  
28  import javax.swing.Action;
29  import javax.swing.JEditorPane;
30  import javax.swing.JTextArea;
31  import javax.swing.JTextField;
32  import javax.swing.JTextPane;
33  import javax.swing.KeyStroke;
34  import javax.swing.text.BadLocationException;
35  import javax.swing.text.DefaultEditorKit;
36  import javax.swing.text.Document;
37  import javax.swing.text.JTextComponent;
38  import javax.swing.text.Keymap;
39  import javax.swing.text.TextAction;
40  import javax.swing.text.Utilities;
41  
42  import org.apache.log4j.Logger;
43  
44  /***
45   * Generic class which activates Emacs keybindings for java input {@link
46   * JTextComponent}s.
47   *
48   * TODO: Some actions don't work at the end of line, that's because they
49   * depend on built-in actions.
50   */
51  public class EmacsKeyBindings
52  {
53      //--- Constant(s) ---
54  
55      public static final String killLineAction = "emacs-kill-line";
56  
57      public static final String killRingSaveAction = "emacs-kill-ring-save";
58  
59      public static final String killRegionAction = "emacs-kill-region";
60  
61      public static final String backwardKillWordAction 
62  		= "emacs-backward-kill-word";
63  
64      public static final String capitalizeWordAction = "emacs-capitalize-word";
65  
66      public static final String downcaseWordAction = "emacs-downcase-word";
67      
68      public static final String killWordAction = "emacs-kill-word";
69  
70      public static final String setMarkCommandAction 
71  		= "emacs-set-mark-command";
72  
73      public static final String yankAction = "emacs-yank";
74  
75      public static final String yankPopAction = "emacs-yank-pop";
76  
77      public static final String upcaseWordAction = "emacs-upcase-word";
78  
79      public static final JTextComponent.KeyBinding[] EMACS_KEY_BINDINGS = {
80  		new JTextComponent.
81  			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
82  											  InputEvent.CTRL_MASK),
83  					   DefaultEditorKit.pasteAction),
84  		new JTextComponent.
85  			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W,
86  											  InputEvent.ALT_MASK),
87  					   DefaultEditorKit.copyAction),
88  		new JTextComponent.
89  			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W,
90  											  InputEvent.CTRL_MASK),
91  					   DefaultEditorKit.cutAction),
92  		new JTextComponent.
93  			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_E,
94  											  InputEvent.CTRL_MASK),
95  					   DefaultEditorKit.endLineAction),
96  		new JTextComponent.
97  			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_A,
98  											  InputEvent.CTRL_MASK),
99  					   DefaultEditorKit.beginLineAction),
100 		new JTextComponent.
101 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_D,
102 											  InputEvent.CTRL_MASK),
103 					   DefaultEditorKit.deleteNextCharAction),
104 		new JTextComponent.
105 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_N,
106 											  InputEvent.CTRL_MASK),
107 					   DefaultEditorKit.downAction),
108 		new JTextComponent.
109 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_P,
110 											  InputEvent.CTRL_MASK),
111 					   DefaultEditorKit.upAction),
112 		new JTextComponent.
113 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_B,
114 											  InputEvent.ALT_MASK),
115 					   DefaultEditorKit.previousWordAction),
116 		new JTextComponent.
117 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LESS,
118 											  InputEvent.ALT_MASK),
119 					   DefaultEditorKit.beginAction),
120 		new JTextComponent.
121 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LESS,
122 											  InputEvent.ALT_MASK 
123 											  + InputEvent.SHIFT_MASK),
124 					   DefaultEditorKit.endAction),
125 		new JTextComponent.
126 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_F,
127 											  InputEvent.ALT_MASK),
128 					   DefaultEditorKit.nextWordAction),
129 		new JTextComponent.
130 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_F,
131 											  InputEvent.CTRL_MASK),
132 					   DefaultEditorKit.forwardAction),
133 		new JTextComponent.
134 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_B,
135 											  InputEvent.CTRL_MASK),
136 					   DefaultEditorKit.backwardAction),
137 		new JTextComponent.
138 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V,
139 											  InputEvent.CTRL_MASK),
140 					   DefaultEditorKit.pageDownAction),
141 		new JTextComponent.
142 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V,
143 											  InputEvent.ALT_MASK),
144 					   DefaultEditorKit.pageUpAction),
145 		new JTextComponent.
146 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_D,
147 											  InputEvent.ALT_MASK),
148 					   EmacsKeyBindings.killWordAction),
149 		new JTextComponent.
150 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE,
151 											  InputEvent.ALT_MASK),
152 					   EmacsKeyBindings.backwardKillWordAction),
153 		new JTextComponent.
154 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
155 											  InputEvent.CTRL_MASK),
156 					   EmacsKeyBindings.setMarkCommandAction),
157 		new JTextComponent.
158 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W,
159 											  InputEvent.ALT_MASK),
160 					   EmacsKeyBindings.killRingSaveAction),
161 		new JTextComponent.
162 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W,
163 											  InputEvent.CTRL_MASK),
164 					   EmacsKeyBindings.killRegionAction),
165 	
166 		new JTextComponent.
167 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_K,
168 											  InputEvent.CTRL_MASK),
169 					   EmacsKeyBindings.killLineAction),
170 	
171 		new JTextComponent.
172 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
173 											  InputEvent.CTRL_MASK),
174 					   EmacsKeyBindings.yankAction),
175 
176 		new JTextComponent.
177 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
178 											  InputEvent.ALT_MASK),
179 					   EmacsKeyBindings.yankPopAction),
180 	
181 		new JTextComponent.
182 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_C,
183 											  InputEvent.ALT_MASK),
184 					   EmacsKeyBindings.capitalizeWordAction),
185 	    
186 		new JTextComponent.
187 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_L,
188 											  InputEvent.ALT_MASK),
189 					   EmacsKeyBindings.downcaseWordAction),
190 	    
191 		new JTextComponent.
192 			KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_U,
193 											  InputEvent.ALT_MASK),
194 					   EmacsKeyBindings.upcaseWordAction),
195     };
196 
197     //--- Data field(s) ---
198 	
199 	private static Logger logger = Logger.getLogger(EmacsKeyBindings.class);
200 
201     //--- Constructor(s) ---
202     
203     //--- Method(s) ---
204 
205 	public static void load()
206 	{
207 		JTextComponent[] jtcs = new JTextComponent[] {
208 			new JTextArea(),
209 			new JTextPane(),
210 			new JTextField(),
211 			new JEditorPane(),
212 		};
213 
214 		for (int i = 0; i < jtcs.length; i++) {
215 			Keymap orig = jtcs[i].getKeymap();
216 			Keymap backup = JTextComponent.addKeymap
217 				(jtcs[i].getClass().getName(), null);
218 			
219 			Action[] bound = orig.getBoundActions();
220 			for (int j = 0; j < bound.length; j++) {
221 				KeyStroke[] strokes = orig.getKeyStrokesForAction(bound[j]);
222 				for (int k = 0; k < strokes.length; k++) {
223 					backup.addActionForKeyStroke(strokes[k], bound[j]);
224 				}
225 			}
226 
227 			backup.setDefaultAction(orig.getDefaultAction());
228 		}
229 
230 		loadEmacsKeyBindings();
231 	}
232 
233 	public static void unload()
234 	{
235 		JTextComponent[] jtcs = new JTextComponent[] {
236 			new JTextArea(),
237 			new JTextPane(),
238 			new JTextField(),
239 			new JEditorPane(),
240 		};
241 
242 		for (int i = 0; i < jtcs.length; i++) {
243 			Keymap backup = JTextComponent.getKeymap
244 				(jtcs[i].getClass().getName());
245 
246 			if (backup != null) {
247 				Keymap current = jtcs[i].getKeymap();
248 				current.removeBindings();
249 
250 				Action[] bound = backup.getBoundActions();
251 				for (int j = 0; j < bound.length; j++) {
252 					KeyStroke[] strokes = 
253 						backup.getKeyStrokesForAction(bound[i]);
254 					for (int k = 0; k < strokes.length; k++) {
255 						current.addActionForKeyStroke(strokes[k], bound[j]);
256 					}
257 				}
258 				current.setDefaultAction(backup.getDefaultAction());
259 			}
260 		}
261 	}
262 
263     /***
264      * Activates Emacs keybindings for all text components extending {@link
265      * JTextComponent}.
266      */
267     private static void loadEmacsKeyBindings()
268     {
269 		logger.debug("Loading emacs keybindings");
270 
271 		JTextComponent[] jtcs = new JTextComponent[] {
272 			new JTextArea(),
273 			new JTextPane(),
274 			new JTextField(),
275 			new JEditorPane(),
276 		};
277 
278 		for (int i = 0; i < jtcs.length; i++) {
279 	
280 			Keymap k = jtcs[i].getKeymap();
281 
282 			JTextComponent.loadKeymap(k, EMACS_KEY_BINDINGS,
283 									  jtcs[i].getActions());
284 	    
285 			k.addActionForKeyStroke(KeyStroke.getKeyStroke
286 									(KeyEvent.VK_D, InputEvent.ALT_MASK),
287 									new KillWordAction(killWordAction));
288 			k.addActionForKeyStroke(KeyStroke.getKeyStroke
289 									(KeyEvent.VK_BACK_SPACE, 
290 									 InputEvent.ALT_MASK),
291 									new BackwardKillWordAction(backwardKillWordAction));
292 			k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
293 														   InputEvent.CTRL_MASK),
294 									new SetMarkCommandAction(setMarkCommandAction));
295 			k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_W,
296 														   InputEvent.ALT_MASK),
297 									new KillRingSaveAction(killRingSaveAction));
298 			k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_W,
299 														   InputEvent.CTRL_MASK),
300 									new KillRegionAction(killRegionAction));
301 			k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_K,
302 														   InputEvent.CTRL_MASK),
303 									new KillLineAction(killLineAction));
304 			k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
305 														   InputEvent.CTRL_MASK),
306 									new YankAction("emacs-yank"));
307 			k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
308 														   InputEvent.ALT_MASK),
309 									new YankPopAction("emacs-yank-pop"));
310 			k.addActionForKeyStroke
311 				(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.ALT_MASK),
312 				 new CapitalizeWordAction(capitalizeWordAction));
313 	    
314 			k.addActionForKeyStroke
315 				(KeyStroke.getKeyStroke(KeyEvent.VK_L, InputEvent.ALT_MASK),
316 				 new DowncaseWordAction(downcaseWordAction));
317 
318 			k.addActionForKeyStroke
319 				(KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.ALT_MASK),
320 				 new UpcaseWordAction(upcaseWordAction));
321 		}
322     }
323 
324     //--- Inner Class(es) ---
325 
326     /***
327      * Kills the next word.
328      */
329     public static class KillWordAction extends TextAction
330     {
331 		public KillWordAction(String nm)
332 		{
333 			super(nm);
334 		}
335 
336 		public void actionPerformed(ActionEvent e)
337 		{
338 			JTextComponent jtc = getTextComponent(e);
339 			if (jtc != null) {
340 				try {
341 					int offs = jtc.getCaretPosition();
342 					jtc.setSelectionStart(offs);
343 					offs = Utilities.getNextWord(jtc, offs);
344 					jtc.setSelectionEnd(offs);
345 					jtc.cut();
346 				}
347 				catch (BadLocationException ble) {
348 					jtc.getToolkit().beep();
349 				}
350 			}
351 		}
352     }
353 
354     /***
355      * Kills the previous word.
356      */
357     public static class BackwardKillWordAction extends TextAction
358     {
359 		public BackwardKillWordAction(String nm)
360 		{
361 			super(nm);
362 		}
363 
364 		public void actionPerformed(ActionEvent e)
365 		{
366 			JTextComponent jtc = getTextComponent(e);
367 			if (jtc != null) {
368 				try {
369 					int offs = jtc.getCaretPosition();
370 					jtc.setSelectionEnd(offs);
371 					offs = Utilities.getPreviousWord(jtc, offs);
372 					jtc.setSelectionStart(offs);
373 					jtc.cut();
374 				}
375 				catch (BadLocationException ble) {
376 					jtc.getToolkit().beep();
377 				}
378 			}
379 		}
380     }
381 
382     /***
383      * Copies the marked region and stores it in the killring.
384      */
385     public static class KillRingSaveAction extends TextAction
386     {
387 		public KillRingSaveAction(String nm)
388 		{
389 			super(nm);
390 		}
391 
392 		public void actionPerformed(ActionEvent e)
393 		{
394 			JTextComponent jtc = getTextComponent(e);
395 			if (jtc != null && SetMarkCommandAction.isMarked(jtc)) {
396 				jtc.setSelectionStart(SetMarkCommandAction.getCaretPosition());
397 				jtc.moveCaretPosition(jtc.getCaretPosition());
398 				jtc.copy();
399 				YankAction.add(jtc.getSelectedText());
400 				SetMarkCommandAction.reset();
401 				// todo reset selection
402 			}
403 		}
404     }
405 
406     /***
407      * Kills the marked region and stores it in the killring.
408      */
409     public static class KillRegionAction extends TextAction
410     {
411 		public KillRegionAction(String nm)
412 		{
413 			super(nm);
414 		}
415 
416 		public void actionPerformed(ActionEvent e)
417 		{
418 			JTextComponent jtc = getTextComponent(e);
419 			if (jtc != null && SetMarkCommandAction.isMarked(jtc)) {
420 				jtc.setSelectionStart(SetMarkCommandAction.getCaretPosition());
421 				jtc.moveCaretPosition(jtc.getCaretPosition());
422 				SetMarkCommandAction.reset();
423 				YankAction.add(jtc.getSelectedText());
424 				jtc.cut();
425 			}
426 		}
427     }
428 
429     /***
430      * Kills text up to the end of the current line and stores it in the
431      * killring.
432      */
433     public static class KillLineAction extends TextAction
434     {
435 		public KillLineAction(String nm)
436 		{
437 			super(nm);
438 		}
439 
440 		public void actionPerformed(ActionEvent e)
441 		{
442 			JTextComponent jtc = getTextComponent(e);
443 			if (jtc != null) {
444 				try {
445 					int start = jtc.getCaretPosition();
446 					int end = Utilities.getRowEnd(jtc, start);
447 					if (start == end && jtc.isEditable()) {
448 						Document doc = jtc.getDocument();
449 						doc.remove(end, 1);
450 					}
451 					else {
452 						jtc.setSelectionStart(start);
453 						jtc.setSelectionEnd(end);
454 						YankAction.add(jtc.getSelectedText());
455 						jtc.cut();
456 						// jtc.replaceSelection("");
457 					}
458 				}
459 				catch (BadLocationException ble) {
460 					jtc.getToolkit().beep();
461 				}
462 			}
463 		}
464     }
465 
466     /***
467      * Sets a beginning mark.
468      */
469     public static class SetMarkCommandAction extends TextAction
470     {
471 		//--- Constant(s) ---
472 
473 		//--- Data field(s) ---
474 
475 		private static int position = -1;
476 		private static JTextComponent jtc;
477 	
478 	//--- Constructor(s) ---
479 
480 		public SetMarkCommandAction(String nm)
481 		{
482 			super(nm);
483 		}
484 	
485 		//--- Method(s) ---
486     
487 		public void actionPerformed(ActionEvent e)
488 		{
489 			jtc = getTextComponent(e);
490 			if (jtc != null) {
491 				position = jtc.getCaretPosition();
492 			}
493 		}
494 
495 		public static boolean isMarked(JTextComponent jt)
496 		{
497 			return (jtc == jt && position != -1);
498 		}
499 
500 		public static void reset()
501 		{
502 			jtc = null;
503 			position = -1;
504 		}
505 
506 		public static int getCaretPosition()
507 		{
508 			return position;
509 		}
510     }
511 
512     /***
513      * Pastes text from killring.
514      */
515     public static class YankAction extends TextAction
516     {
517 		public static LinkedList killring = new LinkedList();
518 		public static int start = -1;
519 		public static int end = -1;
520 	
521 		public YankAction(String nm)
522 		{
523 			super(nm);
524 		}
525 	
526 		public void actionPerformed(ActionEvent event)
527 		{
528 			JTextComponent jtc = getTextComponent(event);
529 	    
530 			if (jtc != null) {
531 				try {
532 					start = jtc.getCaretPosition();
533 					jtc.paste();
534 					end = jtc.getCaretPosition();
535 					add(jtc.getText(start, end));
536 				}
537 				catch (Exception e) {
538 				}
539 			}
540 		}
541 	
542 	
543 		/***
544 		 * Uniquely adds <code>item</code> to killring, i.e. if
545 		 * <code>item</code> is already in killring, it's moved to the front,
546 		 * otherwise it's added as first element.
547 		 */
548 		public static void add(String item)
549 		{
550 			for (Iterator i = killring.iterator(); i.hasNext();) {
551 				if (item.equals((String)i.next())) {
552 					i.remove();
553 					break;
554 				}
555 			}
556 			killring.addFirst(item);
557 		}
558 
559 		/***
560 		 * Returns killring successor of <code>item</code> and adds
561 		 * <code>item</code> to killring.
562 		 *
563 		 * @param predecessor 
564 		 * @return Returns first item if <code>item == null</code>.
565 		 */
566 		public static String getNext(String predecessor)
567 		{
568 			if (killring.size() == 0) {
569 				return null;
570 			}
571 
572 			if (predecessor == null) {
573 				return (String)killring.getFirst();
574 			}
575 	   
576 			for (Iterator i = killring.iterator(); i.hasNext();) {
577 				if (predecessor.equals((String)i.next())) {
578 					i.remove();
579 					if (i.hasNext()) {
580 						String result = (String)i.next();
581 						killring.addFirst(predecessor);
582 						return result;
583 					}
584 					else {
585 						break;
586 					}
587 				}
588 			}
589 	    
590 			if (killring.size() == 0) {
591 				return null;
592 			}
593 			String result = (String)killring.getFirst();
594 			killring.addFirst(predecessor);
595 			return result;
596 		}
597     }
598 
599     /***
600      * Pastes an element from the killring cycling through it.
601      */
602     public static class YankPopAction extends TextAction
603     {
604 
605 		public YankPopAction(String nm)
606 		{
607 			super(nm);
608 		}
609 
610 		public void actionPerformed(ActionEvent event)
611 		{
612 			JTextComponent jtc = getTextComponent(event);
613 	    
614 			if (jtc != null && YankAction.killring.size() > 0) {
615 				jtc.setSelectionStart(YankAction.start);
616 				jtc.setSelectionEnd(YankAction.end);
617 				String toYank = YankAction.getNext(jtc.getSelectedText());
618 				if (toYank != null) {
619 					jtc.replaceSelection(toYank);
620 					YankAction.end = jtc.getCaretPosition();
621 				}
622 				else {
623 					jtc.getToolkit().beep();
624 				}
625 			}
626 		}
627     }
628 
629     /***
630      * Capitalizes the next word.
631      */
632     public static class CapitalizeWordAction extends TextAction
633     {
634 		public CapitalizeWordAction(String nm)
635 		{
636 			super(nm);
637 		}
638 
639 		/***
640 		 * At first the same code as in {@link
641 		 * EmacsKeyBindings.DowncaseWordAction} is performed, to ensure the
642 		 * word is in lower case, then the first letter is capialized.  */
643 		public void actionPerformed(ActionEvent event)
644 		{
645 			JTextComponent jtc = getTextComponent(event);
646 
647 			if (jtc != null) {
648 				try {
649 					/* downcase code */
650 					int start = jtc.getCaretPosition();
651 					int end = Utilities.getNextWord(jtc, start);
652 					jtc.setSelectionStart(start);
653 					jtc.setSelectionEnd(end);
654 					String word = jtc.getText(start, end - start);
655 					jtc.replaceSelection(word.toLowerCase());
656 
657 					/* actual capitalize code */
658 					int offs = Utilities.getWordStart(jtc, start);
659 					// get first letter
660 					String c = jtc.getText(offs, 1);
661 					// we're at the end of the previous word
662 					if (c.equals(" ")) {
663 						/* ugly java workaround to get the beginning of the
664                            word.  */
665 						offs = Utilities.getWordStart(jtc, ++offs);
666 						c = jtc.getText(offs, 1);
667 					}
668 					if (Character.isLetter(c.charAt(0))) {
669 						jtc.setSelectionStart(offs);
670 						jtc.setSelectionEnd(offs + 1);
671 						jtc.replaceSelection(c.toUpperCase());
672 					}
673 					end = Utilities.getWordEnd(jtc, offs);
674 					jtc.setCaretPosition(end);
675 				}
676 				catch (BadLocationException ble) {
677 					jtc.getToolkit().beep();
678 				}
679 			}
680 		}
681     }
682 
683     /***
684      * Renders all characters of the next word to lowercase.
685      */
686     public static class DowncaseWordAction extends TextAction
687     {
688 		public DowncaseWordAction(String nm)
689 		{
690 			super(nm);
691 		}
692 
693 		public void actionPerformed(ActionEvent event)
694 		{
695 			JTextComponent jtc = getTextComponent(event);
696 
697 			if (jtc != null) {
698 				try {
699 					int start = jtc.getCaretPosition();
700 					int end = Utilities.getNextWord(jtc, start);
701 					jtc.setSelectionStart(start);
702 					jtc.setSelectionEnd(end);
703 					String word = jtc.getText(start, end - start);
704 					jtc.replaceSelection(word.toLowerCase());
705 				}
706 				catch (BadLocationException ble) {
707 					jtc.getToolkit().beep();
708 				}
709 			}
710 		}
711     }
712 
713     /***
714      * Renders all characters of the next word to upppercase.
715      */
716     public static class UpcaseWordAction extends TextAction
717     {
718 		public UpcaseWordAction(String nm)
719 		{
720 			super(nm);
721 		}
722 
723 		public void actionPerformed(ActionEvent event)
724 		{
725 			JTextComponent jtc = getTextComponent(event);
726 
727 			if (jtc != null) {
728 				try {
729 					int start = jtc.getCaretPosition();
730 					int end = Utilities.getNextWord(jtc, start);
731 					jtc.setSelectionStart(start);
732 					jtc.setSelectionEnd(end);
733 					String word = jtc.getText(start, end - start);
734 					jtc.replaceSelection(word.toUpperCase());
735 				}
736 				catch (BadLocationException ble) {
737 					jtc.getToolkit().beep();
738 				}
739 			}
740 		}
741     }
742 }