1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.xnap.commons.gui.shortcut;
22
23 import java.awt.event.ActionEvent;
24 import java.awt.event.InputEvent;
25 import java.awt.event.KeyEvent;
26 import java.util.Collections;
27 import java.util.Iterator;
28 import java.util.LinkedList;
29 import java.util.List;
30 import javax.swing.Action;
31 import javax.swing.JEditorPane;
32 import javax.swing.JTextArea;
33 import javax.swing.JTextField;
34 import javax.swing.JTextPane;
35 import javax.swing.KeyStroke;
36 import javax.swing.text.BadLocationException;
37 import javax.swing.text.DefaultEditorKit;
38 import javax.swing.text.Document;
39 import javax.swing.text.JTextComponent;
40 import javax.swing.text.Keymap;
41 import javax.swing.text.TextAction;
42 import javax.swing.text.Utilities;
43 import org.apache.commons.logging.Log;
44 import org.apache.commons.logging.LogFactory;
45
46 /***
47 * Generic class which activates Emacs keybindings for java input {@link
48 * JTextComponent}s.
49 *
50 * The inner class actions can also be used independently.
51 *
52 * @author Felix Berger
53 */
54 public class EmacsKeyBindings
55 {
56
57 public static final String killLineAction = "emacs-kill-line";
58
59 public static final String killRingSaveAction = "emacs-kill-ring-save";
60
61 public static final String killRegionAction = "emacs-kill-region";
62
63 public static final String backwardKillWordAction
64 = "emacs-backward-kill-word";
65
66 public static final String capitalizeWordAction = "emacs-capitalize-word";
67
68 public static final String downcaseWordAction = "emacs-downcase-word";
69
70 public static final String killWordAction = "emacs-kill-word";
71
72 public static final String setMarkCommandAction
73 = "emacs-set-mark-command";
74
75 public static final String yankAction = "emacs-yank";
76
77 public static final String yankPopAction = "emacs-yank-pop";
78
79 public static final String upcaseWordAction = "emacs-upcase-word";
80
81 public static final JTextComponent.KeyBinding[] EMACS_KEY_BINDINGS = {
82 new JTextComponent.
83 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
84 InputEvent.CTRL_MASK),
85 DefaultEditorKit.pasteAction),
86 new JTextComponent.
87 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W,
88 InputEvent.ALT_MASK),
89 DefaultEditorKit.copyAction),
90 new JTextComponent.
91 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W,
92 InputEvent.CTRL_MASK),
93 DefaultEditorKit.cutAction),
94 new JTextComponent.
95 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_E,
96 InputEvent.CTRL_MASK),
97 DefaultEditorKit.endLineAction),
98 new JTextComponent.
99 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_A,
100 InputEvent.CTRL_MASK),
101 DefaultEditorKit.beginLineAction),
102 new JTextComponent.
103 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_D,
104 InputEvent.CTRL_MASK),
105 DefaultEditorKit.deleteNextCharAction),
106 new JTextComponent.
107 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_N,
108 InputEvent.CTRL_MASK),
109 DefaultEditorKit.downAction),
110 new JTextComponent.
111 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_P,
112 InputEvent.CTRL_MASK),
113 DefaultEditorKit.upAction),
114 new JTextComponent.
115 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_B,
116 InputEvent.ALT_MASK),
117 DefaultEditorKit.previousWordAction),
118 new JTextComponent.
119 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LESS,
120 InputEvent.ALT_MASK),
121 DefaultEditorKit.beginAction),
122 new JTextComponent.
123 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LESS,
124 InputEvent.ALT_MASK
125 + InputEvent.SHIFT_MASK),
126 DefaultEditorKit.endAction),
127 new JTextComponent.
128 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_F,
129 InputEvent.ALT_MASK),
130 DefaultEditorKit.nextWordAction),
131 new JTextComponent.
132 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_F,
133 InputEvent.CTRL_MASK),
134 DefaultEditorKit.forwardAction),
135 new JTextComponent.
136 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_B,
137 InputEvent.CTRL_MASK),
138 DefaultEditorKit.backwardAction),
139 new JTextComponent.
140 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V,
141 InputEvent.CTRL_MASK),
142 DefaultEditorKit.pageDownAction),
143 new JTextComponent.
144 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V,
145 InputEvent.ALT_MASK),
146 DefaultEditorKit.pageUpAction),
147 new JTextComponent.
148 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_D,
149 InputEvent.ALT_MASK),
150 EmacsKeyBindings.killWordAction),
151 new JTextComponent.
152 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE,
153 InputEvent.ALT_MASK),
154 EmacsKeyBindings.backwardKillWordAction),
155 new JTextComponent.
156 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
157 InputEvent.CTRL_MASK),
158 EmacsKeyBindings.setMarkCommandAction),
159 new JTextComponent.
160 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W,
161 InputEvent.ALT_MASK),
162 EmacsKeyBindings.killRingSaveAction),
163 new JTextComponent.
164 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W,
165 InputEvent.CTRL_MASK),
166 EmacsKeyBindings.killRegionAction),
167
168 new JTextComponent.
169 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_K,
170 InputEvent.CTRL_MASK),
171 EmacsKeyBindings.killLineAction),
172
173 new JTextComponent.
174 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
175 InputEvent.CTRL_MASK),
176 EmacsKeyBindings.yankAction),
177
178 new JTextComponent.
179 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
180 InputEvent.ALT_MASK),
181 EmacsKeyBindings.yankPopAction),
182
183 new JTextComponent.
184 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_C,
185 InputEvent.ALT_MASK),
186 EmacsKeyBindings.capitalizeWordAction),
187
188 new JTextComponent.
189 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_L,
190 InputEvent.ALT_MASK),
191 EmacsKeyBindings.downcaseWordAction),
192
193 new JTextComponent.
194 KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_U,
195 InputEvent.ALT_MASK),
196 EmacsKeyBindings.upcaseWordAction),
197 };
198
199
200 private static Log logger = LogFactory.getLog(EmacsKeyBindings.class);
201
202 /***
203 * Loads the emacs keybindings for all common <code>JTextComponent</code>s.
204 *
205 * The shared keymap instances of the concrete subclasses of
206 * {@link JTextComponent} are fed with the keybindings.
207 *
208 * The original keybindings are stored in a backup array.
209 */
210 public static void load()
211 {
212 JTextComponent[] jtcs = new JTextComponent[] {
213 new JTextArea(),
214 new JTextPane(),
215 new JTextField(),
216 new JEditorPane(),
217 };
218
219 for (int i = 0; i < jtcs.length; i++) {
220 Keymap orig = jtcs[i].getKeymap();
221 Keymap backup = JTextComponent.addKeymap
222 (jtcs[i].getClass().getName(), null);
223
224 Action[] bound = orig.getBoundActions();
225 for (int j = 0; j < bound.length; j++) {
226 KeyStroke[] strokes = orig.getKeyStrokesForAction(bound[j]);
227 for (int k = 0; k < strokes.length; k++) {
228 backup.addActionForKeyStroke(strokes[k], bound[j]);
229 }
230 }
231
232 backup.setDefaultAction(orig.getDefaultAction());
233 }
234
235 loadEmacsKeyBindings();
236 }
237
238 /***
239 * Restores the original keybindings for the concrete subclasses of
240 * {@link JTextComponent}.
241 *
242 */
243 public static void unload()
244 {
245 JTextComponent[] jtcs = new JTextComponent[] {
246 new JTextArea(),
247 new JTextPane(),
248 new JTextField(),
249 new JEditorPane(),
250 };
251
252 for (int i = 0; i < jtcs.length; i++) {
253 Keymap backup = JTextComponent.getKeymap
254 (jtcs[i].getClass().getName());
255
256 if (backup != null) {
257 Keymap current = jtcs[i].getKeymap();
258 current.removeBindings();
259
260 Action[] bound = backup.getBoundActions();
261 for (int j = 0; j < bound.length; j++) {
262 KeyStroke[] strokes =
263 backup.getKeyStrokesForAction(bound[i]);
264 for (int k = 0; k < strokes.length; k++) {
265 current.addActionForKeyStroke(strokes[k], bound[j]);
266 }
267 }
268 current.setDefaultAction(backup.getDefaultAction());
269 }
270 }
271 }
272
273 /***
274 * Activates Emacs keybindings for all text components extending {@link
275 * JTextComponent}.
276 */
277 private static void loadEmacsKeyBindings()
278 {
279 logger.debug("Loading emacs keybindings");
280
281 JTextComponent[] jtcs = new JTextComponent[] {
282 new JTextArea(),
283 new JTextPane(),
284 new JTextField(),
285 new JEditorPane(),
286 };
287
288 for (int i = 0; i < jtcs.length; i++) {
289
290 Keymap k = jtcs[i].getKeymap();
291
292 JTextComponent.loadKeymap(k, EMACS_KEY_BINDINGS,
293 jtcs[i].getActions());
294
295 k.addActionForKeyStroke(KeyStroke.getKeyStroke
296 (KeyEvent.VK_D, InputEvent.ALT_MASK),
297 new KillWordAction(killWordAction));
298 k.addActionForKeyStroke(KeyStroke.getKeyStroke
299 (KeyEvent.VK_BACK_SPACE,
300 InputEvent.ALT_MASK),
301 new BackwardKillWordAction(backwardKillWordAction));
302 k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
303 InputEvent.CTRL_MASK),
304 new SetMarkCommandAction(setMarkCommandAction));
305 k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_W,
306 InputEvent.ALT_MASK),
307 new KillRingSaveAction(killRingSaveAction));
308 k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_W,
309 InputEvent.CTRL_MASK),
310 new KillRegionAction(killRegionAction));
311 k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_K,
312 InputEvent.CTRL_MASK),
313 new KillLineAction(killLineAction));
314 k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
315 InputEvent.CTRL_MASK),
316 new YankAction("emacs-yank"));
317 k.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_Y,
318 InputEvent.ALT_MASK),
319 new YankPopAction("emacs-yank-pop"));
320 k.addActionForKeyStroke
321 (KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.ALT_MASK),
322 new CapitalizeWordAction(capitalizeWordAction));
323
324 k.addActionForKeyStroke
325 (KeyStroke.getKeyStroke(KeyEvent.VK_L, InputEvent.ALT_MASK),
326 new DowncaseWordAction(downcaseWordAction));
327
328 k.addActionForKeyStroke
329 (KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.ALT_MASK),
330 new UpcaseWordAction(upcaseWordAction));
331 }
332 }
333
334 /***
335 * This action kills the next word.
336 *
337 * It removes the next word on the right side of the cursor from the active
338 * text component and adds it to the clipboard.
339 */
340 public static class KillWordAction extends TextAction
341 {
342 public KillWordAction(String nm)
343 {
344 super(nm);
345 }
346
347 public void actionPerformed(ActionEvent e)
348 {
349 JTextComponent jtc = getTextComponent(e);
350 if (jtc != null) {
351 try {
352 int offs = jtc.getCaretPosition();
353 jtc.setSelectionStart(offs);
354 offs = getWordEnd(jtc, offs);
355 jtc.setSelectionEnd(offs);
356 KillRing.getInstance().add(jtc.getSelectedText());
357 jtc.cut();
358 }
359 catch (BadLocationException ble) {
360 jtc.getToolkit().beep();
361 }
362 }
363 }
364 }
365
366 /***
367 * This action kills the previous word.
368 *
369 * It removes the previous word on the left side of the cursor from the
370 * active text component and adds it to the clipboard.
371 */
372 public static class BackwardKillWordAction extends TextAction
373 {
374 public BackwardKillWordAction(String nm)
375 {
376 super(nm);
377 }
378
379 public void actionPerformed(ActionEvent e)
380 {
381 JTextComponent jtc = getTextComponent(e);
382 if (jtc != null) {
383 try {
384 int offs = jtc.getCaretPosition();
385 jtc.setSelectionEnd(offs);
386 offs = Utilities.getPreviousWord(jtc, offs);
387 jtc.setSelectionStart(offs);
388 KillRing.getInstance().add(jtc.getSelectedText());
389 jtc.cut();
390 }
391 catch (BadLocationException ble) {
392 jtc.getToolkit().beep();
393 }
394 }
395 }
396 }
397
398 /***
399 * This action copies the marked region and stores it in the killring.
400 */
401 public static class KillRingSaveAction extends TextAction
402 {
403 public KillRingSaveAction(String nm)
404 {
405 super(nm);
406 }
407
408 public void actionPerformed(ActionEvent e)
409 {
410 JTextComponent jtc = getTextComponent(e);
411 if (jtc != null && SetMarkCommandAction.isMarked(jtc)) {
412 jtc.setSelectionStart(SetMarkCommandAction.getCaretPosition());
413 jtc.moveCaretPosition(jtc.getCaretPosition());
414 jtc.copy();
415
416 KillRing.getInstance().add(jtc.getSelectedText());
417 SetMarkCommandAction.reset();
418
419 }
420 }
421 }
422
423 /***
424 * This action Kills the marked region and stores it in the killring.
425 */
426 public static class KillRegionAction extends TextAction
427 {
428 public KillRegionAction(String nm)
429 {
430 super(nm);
431 }
432
433 public void actionPerformed(ActionEvent e)
434 {
435 JTextComponent jtc = getTextComponent(e);
436 if (jtc != null && SetMarkCommandAction.isMarked(jtc)) {
437 int start, end;
438 if (SetMarkCommandAction.getCaretPosition() < jtc.getCaretPosition()) {
439 start = SetMarkCommandAction.getCaretPosition();
440 end = jtc.getCaretPosition();
441 }
442 else {
443 start = jtc.getCaretPosition();
444 end = SetMarkCommandAction.getCaretPosition();
445 }
446 if (start != end) {
447 jtc.select(start, end);
448 SetMarkCommandAction.reset();
449 KillRing.getInstance().add(jtc.getSelectedText());
450 jtc.cut();
451 }
452 else {
453 jtc.getToolkit().beep();
454 }
455 }
456 }
457 }
458
459 /***
460 * This actin kills text up to the end of the current line and stores it in
461 * the killring.
462 */
463 public static class KillLineAction extends TextAction
464 {
465 public KillLineAction(String nm)
466 {
467 super(nm);
468 }
469
470 public void actionPerformed(ActionEvent e)
471 {
472 JTextComponent jtc = getTextComponent(e);
473 if (jtc != null) {
474 try {
475 int start = jtc.getCaretPosition();
476 int end = Utilities.getRowEnd(jtc, start);
477 if (start == end && jtc.isEditable()) {
478 Document doc = jtc.getDocument();
479 doc.remove(end, 1);
480 }
481 else {
482 jtc.setSelectionStart(start);
483 jtc.setSelectionEnd(end);
484 KillRing.getInstance().add(jtc.getSelectedText());
485 jtc.cut();
486
487 }
488 }
489 catch (BadLocationException ble) {
490 jtc.getToolkit().beep();
491 }
492 }
493 }
494 }
495
496 /***
497 * This action sets a beginning mark for a selection.
498 */
499 public static class SetMarkCommandAction extends TextAction
500 {
501 private static int position = -1;
502 private static JTextComponent jtc;
503
504 public SetMarkCommandAction(String nm)
505 {
506 super(nm);
507 }
508
509 public void actionPerformed(ActionEvent e)
510 {
511 jtc = getTextComponent(e);
512 if (jtc != null) {
513 position = jtc.getCaretPosition();
514 }
515 }
516
517 public static boolean isMarked(JTextComponent jt)
518 {
519 return (jtc == jt && position != -1);
520 }
521
522 public static void reset()
523 {
524 jtc = null;
525 position = -1;
526 }
527
528 public static int getCaretPosition()
529 {
530 return position;
531 }
532 }
533
534 /***
535 * This action pastes text from the killring.
536 */
537 public static class YankAction extends TextAction
538 {
539 public static int start = -1;
540 public static int end = -1;
541
542 public YankAction(String nm)
543 {
544 super(nm);
545 }
546
547 public void actionPerformed(ActionEvent event)
548 {
549 JTextComponent jtc = getTextComponent(event);
550
551 if (jtc != null) {
552 try {
553 start = jtc.getCaretPosition();
554 jtc.paste();
555 end = jtc.getCaretPosition();
556 KillRing.getInstance().add(jtc.getText(start, end));
557 KillRing.getInstance().setCurrentTextComponent(jtc);
558 }
559 catch (Exception e) {
560 }
561 }
562 }
563 }
564
565 /***
566 * This action pastes an element from the killring cycling through it.
567 */
568 public static class YankPopAction extends TextAction
569 {
570
571 public YankPopAction(String nm)
572 {
573 super(nm);
574 }
575
576 public void actionPerformed(ActionEvent event)
577 {
578 JTextComponent jtc = getTextComponent(event);
579
580 if (jtc != null
581 && KillRing.getInstance().getCurrentTextComponent() == jtc
582 && jtc.getCaretPosition() == YankAction.end
583 && !KillRing.getInstance().isEmpty()) {
584 jtc.setSelectionStart(YankAction.start);
585 jtc.setSelectionEnd(YankAction.end);
586 String toYank = KillRing.getInstance().next();
587 if (toYank != null) {
588 jtc.replaceSelection(toYank);
589 YankAction.end = jtc.getCaretPosition();
590 }
591 else {
592 jtc.getToolkit().beep();
593 }
594 }
595 }
596 }
597
598 /***
599 * Manages all killed (cut) text pieces in a ring which is accessible
600 * through {@link YankPopAction}.
601 * <p>
602 * Also provides an unmodifiable copy of all cut pieces.
603 */
604 public static class KillRing
605 {
606 private JTextComponent jtc;
607 private LinkedList<String> ring = new LinkedList<String>();
608 Iterator<String> iter = ring.iterator();
609
610 private static final KillRing instance = new KillRing();
611
612 public static KillRing getInstance()
613 {
614 return instance;
615 }
616
617 void setCurrentTextComponent(JTextComponent jtc)
618 {
619 this.jtc = jtc;
620 }
621
622 JTextComponent getCurrentTextComponent()
623 {
624 return jtc;
625 }
626
627 /***
628 * Adds text to the front of the kill ring.
629 * <p>
630 * Deviating from the Emacs implementation we make sure the
631 * exact same text is not somewhere else in the ring.
632 */
633 void add(String text)
634 {
635 if (text.length() == 0) {
636 return;
637 }
638
639 ring.remove(text);
640 ring.addFirst(text);
641 while (ring.size() > 60) {
642 ring.removeLast();
643 }
644 iter = ring.iterator();
645
646 iter.next();
647 }
648
649 /***
650 * Returns an unmodifiable version of the ring list which contains
651 * the killed texts.
652 * @return the content of the kill ring
653 */
654 public List<String> getRing()
655 {
656 return Collections.unmodifiableList(ring);
657 }
658
659 public boolean isEmpty()
660 {
661 return ring.isEmpty();
662 }
663
664 /***
665 * Returns the next text element which is to be yank-popped.
666 * @return <code>null</code> if the ring is empty
667 */
668 String next()
669 {
670 if (ring.isEmpty()) {
671 return null;
672 }
673 else if (iter.hasNext()) {
674 return iter.next();
675 }
676 else {
677 iter = ring.iterator();
678
679 return iter.next();
680 }
681 }
682 }
683
684 /***
685 * This action capitalizes the next word on the right side of the caret.
686 */
687 public static class CapitalizeWordAction extends TextAction
688 {
689 public CapitalizeWordAction(String nm)
690 {
691 super(nm);
692 }
693
694 /***
695 * At first the same code as in {@link
696 * EmacsKeyBindings.DowncaseWordAction} is performed, to ensure the
697 * word is in lower case, then the first letter is capialized.
698 */
699 public void actionPerformed(ActionEvent event)
700 {
701 JTextComponent jtc = getTextComponent(event);
702
703 if (jtc != null) {
704 try {
705
706 int start = jtc.getCaretPosition();
707 int end = getWordEnd(jtc, start);
708 jtc.setSelectionStart(start);
709 jtc.setSelectionEnd(end);
710 String word = jtc.getText(start, end - start);
711 jtc.replaceSelection(word.toLowerCase());
712
713
714 int offs = Utilities.getWordStart(jtc, start);
715
716 String c = jtc.getText(offs, 1);
717
718 if (c.equals(" ")) {
719
720
721 offs = Utilities.getWordStart(jtc, ++offs);
722 c = jtc.getText(offs, 1);
723 }
724 if (Character.isLetter(c.charAt(0))) {
725 jtc.setSelectionStart(offs);
726 jtc.setSelectionEnd(offs + 1);
727 jtc.replaceSelection(c.toUpperCase());
728 }
729 end = Utilities.getWordEnd(jtc, offs);
730 jtc.setCaretPosition(end);
731 }
732 catch (BadLocationException ble) {
733 jtc.getToolkit().beep();
734 }
735 }
736 }
737 }
738
739 /***
740 * This action renders all characters of the next word to lowercase.
741 */
742 public static class DowncaseWordAction extends TextAction
743 {
744 public DowncaseWordAction(String nm)
745 {
746 super(nm);
747 }
748
749 public void actionPerformed(ActionEvent event)
750 {
751 JTextComponent jtc = getTextComponent(event);
752
753 if (jtc != null) {
754 try {
755 int start = jtc.getCaretPosition();
756 int end = getWordEnd(jtc, start);
757 jtc.setSelectionStart(start);
758 jtc.setSelectionEnd(end);
759 String word = jtc.getText(start, end - start);
760 jtc.replaceSelection(word.toLowerCase());
761 jtc.setCaretPosition(end);
762 }
763 catch (BadLocationException ble) {
764 jtc.getToolkit().beep();
765 }
766 }
767 }
768 }
769
770 /***
771 * This action renders all characters of the next word to upppercase.
772 */
773 public static class UpcaseWordAction extends TextAction
774 {
775 public UpcaseWordAction(String nm)
776 {
777 super(nm);
778 }
779
780 public void actionPerformed(ActionEvent event)
781 {
782 JTextComponent jtc = getTextComponent(event);
783
784 if (jtc != null) {
785 try {
786 int start = jtc.getCaretPosition();
787 int end = getWordEnd(jtc, start);
788 jtc.setSelectionStart(start);
789 jtc.setSelectionEnd(end);
790 String word = jtc.getText(start, end - start);
791 jtc.replaceSelection(word.toUpperCase());
792 jtc.setCaretPosition(end);
793 }
794 catch (BadLocationException ble) {
795 jtc.getToolkit().beep();
796 }
797 }
798 }
799 }
800
801 private static int getWordEnd(JTextComponent jtc, int start)
802 throws BadLocationException
803 {
804 try {
805 return Utilities.getNextWord(jtc, start);
806 }
807 catch (BadLocationException ble) {
808 int end = jtc.getText().length();
809 if (start < end) {
810 return end;
811 }
812 else {
813 throw ble;
814 }
815 }
816 }
817 }