1 /* DefaultEditorKit.java --
2    Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath 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; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package javax.swing.text;
40 
41 import gnu.java.lang.CPStringBuilder;
42 
43 import java.awt.Toolkit;
44 import java.awt.event.ActionEvent;
45 
46 import java.io.BufferedReader;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.io.InputStreamReader;
50 import java.io.OutputStream;
51 import java.io.OutputStreamWriter;
52 import java.io.Reader;
53 import java.io.Writer;
54 
55 import javax.swing.Action;
56 import javax.swing.SwingConstants;
57 
58 /**
59  * The default implementation of {@link EditorKit}. This <code>EditorKit</code>
60  * a plain text <code>Document</code> and several commands that together
61  * make up a basic editor, like cut / copy + paste.
62  *
63  * @author original author unknown
64  * @author Roman Kennke (roman@kennke.org)
65  * @author Robert Schuster (robertschuster@fsfe.org)
66  */
67 public class DefaultEditorKit extends EditorKit
68 {
69   static class SelectionPreviousWordAction
70       extends TextAction
71   {
SelectionPreviousWordAction()72     SelectionPreviousWordAction()
73     {
74       super(selectionPreviousWordAction);
75     }
76 
actionPerformed(ActionEvent event)77     public void actionPerformed(ActionEvent event)
78     {
79       try
80         {
81           JTextComponent t = getTextComponent(event);
82 
83           if (t != null)
84             {
85               int offs = Utilities.getPreviousWord(t, t.getCaretPosition());
86 
87               Caret c = t.getCaret();
88               c.moveDot(offs);
89               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
90             }
91         }
92       catch(BadLocationException ble)
93         {
94           // Can't happen.
95         }
96     }
97   }
98 
99   static class SelectionNextWordAction
100       extends TextAction
101   {
SelectionNextWordAction()102     SelectionNextWordAction()
103     {
104       super(selectionNextWordAction);
105     }
106 
actionPerformed(ActionEvent event)107     public void actionPerformed(ActionEvent event)
108     {
109       try
110         {
111           JTextComponent t = getTextComponent(event);
112 
113           if (t != null)
114             {
115               int offs = Utilities.getNextWord(t, t.getCaretPosition());
116 
117               Caret c = t.getCaret();
118               c.moveDot(offs);
119               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
120             }
121         }
122       catch(BadLocationException ble)
123         {
124           // Can't happen.
125         }
126     }
127   }
128 
129   static class SelectionBeginWordAction extends TextAction
130   {
SelectionBeginWordAction()131     SelectionBeginWordAction()
132     {
133       super(selectionBeginWordAction);
134     }
135 
actionPerformed(ActionEvent event)136     public void actionPerformed(ActionEvent event)
137     {
138       try
139         {
140           JTextComponent t = getTextComponent(event);
141 
142           if (t != null)
143             {
144               int offs = Utilities.getWordStart(t, t.getCaretPosition());
145 
146               Caret c = t.getCaret();
147               c.moveDot(offs);
148               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
149             }
150         }
151       catch(BadLocationException ble)
152         {
153           // Can't happen.
154         }
155     }
156   }
157 
158   static class SelectionEndWordAction extends TextAction
159   {
SelectionEndWordAction()160     SelectionEndWordAction()
161     {
162       super(selectionEndWordAction);
163     }
164 
actionPerformed(ActionEvent event)165     public void actionPerformed(ActionEvent event)
166     {
167       try
168         {
169           JTextComponent t = getTextComponent(event);
170 
171           if (t != null)
172             {
173               int offs = Utilities.getWordEnd(t, t.getCaretPosition());
174 
175               Caret c = t.getCaret();
176               c.moveDot(offs);
177               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
178             }
179         }
180       catch(BadLocationException ble)
181         {
182           // Can't happen.
183         }
184     }
185   }
186 
187   static class BeginWordAction extends TextAction
188   {
BeginWordAction()189     BeginWordAction()
190     {
191       super(beginWordAction);
192     }
193 
actionPerformed(ActionEvent event)194     public void actionPerformed(ActionEvent event)
195     {
196       try
197         {
198           JTextComponent t = getTextComponent(event);
199 
200           if (t != null)
201             {
202               int offs = Utilities.getWordStart(t, t.getCaretPosition());
203 
204               Caret c = t.getCaret();
205               c.setDot(offs);
206               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
207             }
208         }
209       catch(BadLocationException ble)
210         {
211           // Can't happen.
212         }
213     }
214   }
215 
216   static class EndWordAction extends TextAction
217   {
EndWordAction()218     EndWordAction()
219     {
220       super(endWordAction);
221     }
222 
actionPerformed(ActionEvent event)223     public void actionPerformed(ActionEvent event)
224     {
225       try
226         {
227           JTextComponent t = getTextComponent(event);
228 
229           if (t != null)
230             {
231               int offs = Utilities.getWordEnd(t, t.getCaretPosition());
232 
233               Caret c = t.getCaret();
234               c.setDot(offs);
235               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
236             }
237         }
238       catch(BadLocationException ble)
239         {
240           // Can't happen.
241         }
242     }
243   }
244 
245   static class PreviousWordAction
246       extends TextAction
247   {
PreviousWordAction()248     PreviousWordAction()
249     {
250       super(previousWordAction);
251     }
252 
actionPerformed(ActionEvent event)253     public void actionPerformed(ActionEvent event)
254     {
255       try
256         {
257           JTextComponent t = getTextComponent(event);
258 
259           if (t != null)
260             {
261               int offs = Utilities.getPreviousWord(t, t.getCaretPosition());
262 
263               Caret c = t.getCaret();
264               c.setDot(offs);
265               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
266             }
267         }
268       catch(BadLocationException ble)
269         {
270           // Can't happen.
271         }
272     }
273   }
274 
275   static class NextWordAction
276       extends TextAction
277   {
NextWordAction()278     NextWordAction()
279     {
280       super(nextWordAction);
281     }
282 
actionPerformed(ActionEvent event)283     public void actionPerformed(ActionEvent event)
284     {
285       try
286         {
287           JTextComponent t = getTextComponent(event);
288 
289           if (t != null)
290             {
291               int offs = Utilities.getNextWord(t, t.getCaretPosition());
292 
293               Caret c = t.getCaret();
294               c.setDot(offs);
295               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
296             }
297         }
298       catch(BadLocationException ble)
299         {
300           // Can't happen.
301         }
302     }
303   }
304 
305   static class SelectAllAction
306       extends TextAction
307   {
SelectAllAction()308     SelectAllAction()
309     {
310       super(selectAllAction);
311     }
312 
actionPerformed(ActionEvent event)313     public void actionPerformed(ActionEvent event)
314     {
315       JTextComponent t = getTextComponent(event);
316       if (t != null)
317         {
318           int offs = t.getDocument().getLength();
319           Caret c = t.getCaret();
320           c.setDot(0);
321           c.moveDot(offs);
322           try
323             {
324               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
325             }
326           catch(BadLocationException ble)
327             {
328               // Can't happen.
329             }
330         }
331     }
332   }
333 
334   static class SelectionBeginAction
335       extends TextAction
336   {
SelectionBeginAction()337     SelectionBeginAction()
338     {
339       super(selectionBeginAction);
340     }
341 
actionPerformed(ActionEvent event)342     public void actionPerformed(ActionEvent event)
343     {
344       JTextComponent t = getTextComponent(event);
345       if (t != null)
346         {
347           Caret c = t.getCaret();
348           c.moveDot(0);
349           try
350             {
351               c.setMagicCaretPosition(t.modelToView(0).getLocation());
352             }
353           catch(BadLocationException ble)
354             {
355               // Can't happen.
356             }
357         }
358     }
359   }
360 
361   static class SelectionEndAction
362       extends TextAction
363   {
SelectionEndAction()364     SelectionEndAction()
365     {
366       super(selectionEndAction);
367     }
368 
actionPerformed(ActionEvent event)369     public void actionPerformed(ActionEvent event)
370     {
371       JTextComponent t = getTextComponent(event);
372       if (t != null)
373         {
374           int offs = t.getDocument().getLength();
375           Caret c = t.getCaret();
376           c.moveDot(offs);
377           try
378             {
379               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
380             }
381           catch(BadLocationException ble)
382             {
383               // Can't happen.
384             }
385         }
386     }
387   }
388 
389   static class SelectionBeginLineAction
390     extends TextAction
391   {
392 
SelectionBeginLineAction()393     SelectionBeginLineAction()
394     {
395       super(selectionBeginLineAction);
396     }
397 
actionPerformed(ActionEvent event)398     public void actionPerformed(ActionEvent event)
399     {
400       JTextComponent t = getTextComponent(event);
401       if (t != null)
402         {
403           Caret c = t.getCaret();
404           try
405             {
406               int offs = Utilities.getRowStart(t, c.getDot());
407               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
408             }
409           catch(BadLocationException ble)
410             {
411               // Can't happen.
412             }
413         }
414     }
415   }
416 
417   static class SelectionEndLineAction
418       extends TextAction
419   {
SelectionEndLineAction()420     SelectionEndLineAction()
421     {
422       super(selectionEndLineAction);
423     }
424 
actionPerformed(ActionEvent event)425     public void actionPerformed(ActionEvent event)
426     {
427       JTextComponent t = getTextComponent(event);
428       if (t != null)
429         {
430           Caret c = t.getCaret();
431           try
432             {
433               int offs = Utilities.getRowEnd(t, c.getDot());
434               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
435             }
436           catch(BadLocationException ble)
437             {
438               // Can't happen.
439             }
440         }
441     }
442   }
443 
444   static class SelectLineAction extends TextAction
445   {
SelectLineAction()446     SelectLineAction()
447     {
448       super(selectLineAction);
449     }
450 
actionPerformed(ActionEvent event)451     public void actionPerformed(ActionEvent event)
452     {
453       JTextComponent t = getTextComponent(event);
454       if (t != null)
455         {
456           Caret c = t.getCaret();
457           try
458             {
459               int offs1 = Utilities.getRowStart(t, c.getDot());
460               int offs2 = Utilities.getRowEnd(t, c.getDot());
461               c.setDot(offs2);
462               c.moveDot(offs1);
463               c.setMagicCaretPosition(t.modelToView(offs2).getLocation());
464             }
465           catch(BadLocationException ble)
466             {
467               // Can't happen.
468             }
469         }
470     }
471   }
472 
473   static class SelectWordAction extends TextAction
474   {
SelectWordAction()475     SelectWordAction()
476     {
477       super(selectWordAction);
478     }
479 
actionPerformed(ActionEvent event)480     public void actionPerformed(ActionEvent event)
481     {
482       JTextComponent t = getTextComponent(event);
483       if (t != null)
484         {
485           Caret c = t.getCaret();
486           int dot = c.getDot();
487           try
488             {
489               int wordStart = Utilities.getWordStart(t, dot);
490 
491               if (dot == wordStart)
492                 {
493                   // Current cursor position is on the first character in a word.
494                   c.setDot(wordStart);
495                   c.moveDot(Utilities.getWordEnd(t, wordStart));
496                 }
497               else
498                 {
499                   // Current cursor position is not on the first character
500                   // in a word.
501                   int nextWord = Utilities.getNextWord(t, dot);
502                   int previousWord = Utilities.getPreviousWord(t, dot);
503                   int previousWordEnd = Utilities.getWordEnd(t, previousWord);
504 
505                   // Cursor position is in the space between two words. In such a
506                   // situation just select the space.
507                   if (dot >= previousWordEnd && dot <= nextWord)
508                     {
509                       c.setDot(previousWordEnd);
510                       c.moveDot(nextWord);
511                     }
512                   else
513                     {
514                       // Cursor position is inside a word. Just select it then.
515                       c.setDot(previousWord);
516                       c.moveDot(previousWordEnd);
517                     }
518                 }
519 
520               // If the position was updated change the magic caret position
521               // as well.
522               if (c.getDot() != dot)
523                 c.setMagicCaretPosition(t.modelToView(c.getDot()).getLocation());
524             }
525           catch(BadLocationException ble)
526             {
527               // Can't happen.
528             }
529         }
530     }
531   }
532 
533   static class SelectionDownAction
534       extends TextAction.VerticalMovementAction
535   {
SelectionDownAction()536     SelectionDownAction()
537     {
538       super(selectionDownAction, SwingConstants.SOUTH);
539     }
540 
actionPerformedImpl(Caret c, int offs)541     protected void actionPerformedImpl(Caret c, int offs)
542     {
543       c.moveDot(offs);
544     }
545 
546   }
547 
548   static class SelectionUpAction
549   extends TextAction.VerticalMovementAction
550   {
SelectionUpAction()551     SelectionUpAction()
552     {
553       super(selectionUpAction, SwingConstants.NORTH);
554     }
555 
actionPerformedImpl(Caret c, int offs)556     protected void actionPerformedImpl(Caret c, int offs)
557     {
558       c.moveDot(offs);
559     }
560 
561   }
562 
563   static class SelectionForwardAction
564       extends TextAction.HorizontalMovementAction
565   {
SelectionForwardAction()566     SelectionForwardAction()
567     {
568       super(selectionForwardAction, SwingConstants.EAST);
569     }
570 
actionPerformedImpl(Caret c, int offs)571     protected void actionPerformedImpl(Caret c, int offs)
572     {
573       c.moveDot(offs);
574     }
575   }
576 
577   static class SelectionBackwardAction
578       extends TextAction.HorizontalMovementAction
579   {
SelectionBackwardAction()580     SelectionBackwardAction()
581     {
582       super(selectionBackwardAction, SwingConstants.WEST);
583     }
584 
actionPerformedImpl(Caret c, int offs)585     protected void actionPerformedImpl(Caret c, int offs)
586     {
587       c.moveDot(offs);
588     }
589   }
590 
591   static class DownAction
592       extends TextAction.VerticalMovementAction
593   {
DownAction()594     DownAction()
595     {
596       super(downAction, SwingConstants.SOUTH);
597     }
598 
actionPerformedImpl(Caret c, int offs)599     protected void actionPerformedImpl(Caret c, int offs)
600     {
601       c.setDot(offs);
602     }
603   }
604 
605   static class UpAction
606       extends TextAction.VerticalMovementAction
607   {
UpAction()608     UpAction()
609     {
610       super(upAction, SwingConstants.NORTH);
611     }
612 
actionPerformedImpl(Caret c, int offs)613     protected void actionPerformedImpl(Caret c, int offs)
614     {
615       c.setDot(offs);
616     }
617 
618   }
619 
620   static class ForwardAction
621       extends TextAction.HorizontalMovementAction
622   {
ForwardAction()623     ForwardAction()
624     {
625       super(forwardAction, SwingConstants.EAST);
626     }
627 
actionPerformedImpl(Caret c, int offs)628     protected void actionPerformedImpl(Caret c, int offs)
629     {
630       c.setDot(offs);
631     }
632 
633   }
634 
635   static class BackwardAction
636       extends TextAction.HorizontalMovementAction
637   {
BackwardAction()638     BackwardAction()
639     {
640       super(backwardAction, SwingConstants.WEST);
641     }
642 
actionPerformedImpl(Caret c, int offs)643     protected void actionPerformedImpl(Caret c, int offs)
644     {
645       c.setDot(offs);
646     }
647 
648   }
649 
650   static class DeletePrevCharAction
651       extends TextAction
652   {
DeletePrevCharAction()653     DeletePrevCharAction()
654     {
655       super(deletePrevCharAction);
656     }
657 
actionPerformed(ActionEvent event)658     public void actionPerformed(ActionEvent event)
659     {
660       JTextComponent t = getTextComponent(event);
661       if (t != null)
662         {
663           try
664             {
665               int pos = t.getSelectionStart();
666               int len = t.getSelectionEnd() - pos;
667 
668               if (len > 0)
669                   t.getDocument().remove(pos, len);
670               else if (pos > 0)
671                 {
672                   pos--;
673                   t.getDocument().remove(pos, 1);
674                   Caret c = t.getCaret();
675                   c.setDot(pos);
676                   c.setMagicCaretPosition(t.modelToView(pos).getLocation());
677                 }
678             }
679           catch (BadLocationException e)
680             {
681               // FIXME: we're not authorized to throw this.. swallow it?
682             }
683         }
684     }
685   }
686 
687   static class DeleteNextCharAction
688       extends TextAction
689   {
DeleteNextCharAction()690     DeleteNextCharAction()
691     {
692       super(deleteNextCharAction);
693     }
694 
actionPerformed(ActionEvent event)695     public void actionPerformed(ActionEvent event)
696     {
697       JTextComponent t = getTextComponent(event);
698       if (t != null)
699         {
700           try
701             {
702               int pos = t.getSelectionStart();
703               int len = t.getSelectionEnd() - pos;
704 
705               if (len > 0)
706                   t.getDocument().remove(pos, len);
707               else if (pos < t.getDocument().getLength())
708                   t.getDocument().remove(pos, 1);
709 
710               Caret c = t.getCaret();
711               c.setDot(pos);
712               c.setMagicCaretPosition(t.modelToView(pos).getLocation());
713             }
714           catch (BadLocationException e)
715             {
716               // FIXME: we're not authorized to throw this.. swallow it?
717             }
718         }
719     }
720   }
721 
722   static class EndLineAction
723       extends TextAction
724   {
EndLineAction()725     EndLineAction()
726     {
727       super(endLineAction);
728     }
729 
actionPerformed(ActionEvent event)730     public void actionPerformed(ActionEvent event)
731     {
732       JTextComponent t = getTextComponent(event);
733       if (t != null)
734         {
735           try
736             {
737               int offs = Utilities.getRowEnd(t, t.getCaretPosition());
738               if (offs > -1)
739                 {
740                   Caret c = t.getCaret();
741                   c.setDot(offs);
742                   c.setMagicCaretPosition(t.modelToView(offs).getLocation());
743                 }
744             }
745           catch (BadLocationException ble)
746             {
747               // Nothing to do here
748             }
749         }
750     }
751   }
752 
753   static class BeginLineAction
754       extends TextAction
755   {
BeginLineAction()756     BeginLineAction()
757     {
758       super(beginLineAction);
759     }
760 
actionPerformed(ActionEvent event)761     public void actionPerformed(ActionEvent event)
762     {
763       JTextComponent t = getTextComponent(event);
764       if (t != null)
765         {
766           try
767             {
768               int offs = Utilities.getRowStart(t, t.getCaretPosition());
769               if (offs > -1)
770                 {
771                   Caret c = t.getCaret();
772                   c.setDot(offs);
773                   c.setMagicCaretPosition(t.modelToView(offs).getLocation());
774                 }
775             }
776           catch (BadLocationException ble)
777             {
778               // Do nothing here.
779             }
780         }
781     }
782   }
783 
784   static class BeginAction extends TextAction
785   {
786 
BeginAction()787     BeginAction()
788     {
789       super(beginAction);
790     }
791 
actionPerformed(ActionEvent event)792     public void actionPerformed(ActionEvent event)
793     {
794       JTextComponent t = getTextComponent(event);
795       if (t != null)
796         {
797           Caret c = t.getCaret();
798           c.setDot(0);
799           try
800             {
801               c.setMagicCaretPosition(t.modelToView(0).getLocation());
802             }
803           catch(BadLocationException ble)
804             {
805               // Can't happen.
806             }
807         }
808     }
809   }
810 
811   static class EndAction extends TextAction
812   {
813 
EndAction()814     EndAction()
815     {
816       super(endAction);
817     }
818 
actionPerformed(ActionEvent event)819     public void actionPerformed(ActionEvent event)
820     {
821       JTextComponent t = getTextComponent(event);
822       if (t != null)
823         {
824           int offs = t.getDocument().getLength();
825           Caret c = t.getCaret();
826           c.setDot(offs);
827           try
828             {
829               c.setMagicCaretPosition(t.modelToView(offs).getLocation());
830             }
831           catch(BadLocationException ble)
832             {
833               // Can't happen.
834             }
835         }
836     }
837   }
838 
839   /**
840    * Creates a beep on the PC speaker.
841    *
842    * @see Toolkit#beep()
843    */
844   public static class BeepAction extends TextAction
845   {
846     /**
847      * Creates a new <code>BeepAction</code>.
848      */
BeepAction()849     public BeepAction()
850     {
851       super(beepAction);
852     }
853 
854     /**
855      * Performs the <code>Action</code>.
856      *
857      * @param event the action event describing the user action
858      */
actionPerformed(ActionEvent event)859     public void actionPerformed(ActionEvent event)
860     {
861       Toolkit.getDefaultToolkit().beep();
862     }
863   }
864 
865   /**
866    * Copies the selected content into the system clipboard.
867    *
868    * @see Toolkit#getSystemClipboard()
869    * @see CutAction
870    * @see PasteAction
871    */
872   public static class CopyAction extends TextAction
873   {
874 
875     /**
876      * Create a new <code>CopyAction</code>.
877      */
CopyAction()878     public CopyAction()
879     {
880       super(copyAction);
881     }
882 
883     /**
884      * Performs the <code>Action</code>.
885      *
886      * @param event the action event describing the user action
887      */
actionPerformed(ActionEvent event)888     public void actionPerformed(ActionEvent event)
889     {
890       JTextComponent target = getTextComponent(event);
891       if (target != null)
892         target.copy();
893     }
894   }
895 
896 
897   /**
898    * Copies the selected content into the system clipboard and deletes the
899    * selection.
900    *
901    * @see Toolkit#getSystemClipboard()
902    * @see CopyAction
903    * @see PasteAction
904    */
905   public static class CutAction extends TextAction
906   {
907 
908     /**
909      * Create a new <code>CutAction</code>.
910      */
CutAction()911     public CutAction()
912     {
913       super(cutAction);
914     }
915 
916     /**
917      * Performs the <code>Action</code>.
918      *
919      * @param event the action event describing the user action
920      */
actionPerformed(ActionEvent event)921     public void actionPerformed(ActionEvent event)
922     {
923       JTextComponent target = getTextComponent(event);
924       if (target != null)
925         target.cut();
926     }
927   }
928 
929   /**
930    * Copies content from the system clipboard into the editor.
931    *
932    * @see Toolkit#getSystemClipboard()
933    * @see CopyAction
934    * @see CutAction
935    */
936   public static class PasteAction extends TextAction
937   {
938 
939     /**
940      * Create a new <code>PasteAction</code>.
941      */
PasteAction()942     public PasteAction()
943     {
944       super(pasteAction);
945     }
946 
947     /**
948      * Performs the <code>Action</code>.
949      *
950      * @param event the action event describing the user action
951      */
actionPerformed(ActionEvent event)952     public void actionPerformed(ActionEvent event)
953     {
954       JTextComponent target = getTextComponent(event);
955       if (target != null)
956         target.paste();
957     }
958   }
959 
960   /**
961    * This action is executed as default action when a KEY_TYPED
962    * event is received and no keymap entry exists for that. The purpose
963    * of this action is to filter out a couple of characters. This includes
964    * the control characters and characters with the ALT-modifier.
965    *
966    * If an event does not get filtered, it is inserted into the document
967    * of the text component. If there is some text selected in the text
968    * component, this text will be replaced.
969    */
970   public static class DefaultKeyTypedAction
971     extends TextAction
972   {
973 
974     /**
975      * Creates a new <code>DefaultKeyTypedAction</code>.
976      */
DefaultKeyTypedAction()977     public DefaultKeyTypedAction()
978     {
979       super(defaultKeyTypedAction);
980     }
981 
982     /**
983      * Performs the <code>Action</code>.
984      *
985      * @param event the action event describing the user action
986      */
actionPerformed(ActionEvent event)987     public void actionPerformed(ActionEvent event)
988     {
989       // first we filter the following events:
990       // - control characters
991       // - key events with the ALT modifier
992       JTextComponent target = getTextComponent(event);
993       if ((target != null) && (event != null))
994         {
995           if ((target.isEditable()) && (target.isEnabled()))
996             {
997               String content = event.getActionCommand();
998               int mod = event.getModifiers();
999               if ((content != null) && (content.length() > 0)
1000                   && (mod & ActionEvent.ALT_MASK) == 0
1001                   && (mod & ActionEvent.CTRL_MASK) == 0)
1002                 {
1003                   char c = content.charAt(0);
1004                   if ((c >= 0x20) && (c != 0x7F))
1005                     {
1006                       target.replaceSelection(content);
1007                     }
1008                 }
1009             }
1010         }
1011     }
1012   }
1013 
1014   /**
1015    * This action inserts a newline character into the document
1016    * of the text component. This is typically triggered by hitting
1017    * ENTER on the keyboard.
1018    */
1019   public static class InsertBreakAction extends TextAction
1020   {
1021 
1022     /**
1023      * Creates a new <code>InsertBreakAction</code>.
1024      */
InsertBreakAction()1025     public InsertBreakAction()
1026     {
1027       super(insertBreakAction);
1028     }
1029 
1030     /**
1031      * Performs the <code>Action</code>.
1032      *
1033      * @param event the action event describing the user action
1034      */
actionPerformed(ActionEvent event)1035     public void actionPerformed(ActionEvent event)
1036     {
1037       JTextComponent t = getTextComponent(event);
1038       if (t != null)
1039         t.replaceSelection("\n");
1040     }
1041   }
1042 
1043   /**
1044    * Places content into the associated editor. If there currently is a
1045    * selection, this selection is replaced.
1046    */
1047   // FIXME: Figure out what this Action is supposed to do. Obviously text
1048   // that is entered by the user is inserted through DefaultKeyTypedAction.
1049   public static class InsertContentAction extends TextAction
1050   {
1051 
1052     /**
1053      * Creates a new <code>InsertContentAction</code>.
1054      */
InsertContentAction()1055     public InsertContentAction()
1056     {
1057       super(insertContentAction);
1058     }
1059 
1060     /**
1061      * Performs the <code>Action</code>.
1062      *
1063      * @param event the action event describing the user action
1064      */
actionPerformed(ActionEvent event)1065     public void actionPerformed(ActionEvent event)
1066     {
1067       // FIXME: Figure out what this Action is supposed to do. Obviously text
1068       // that is entered by the user is inserted through DefaultKeyTypedAction.
1069     }
1070   }
1071 
1072   /**
1073    * Inserts a TAB character into the text editor.
1074    */
1075   public static class InsertTabAction extends TextAction
1076   {
1077 
1078     /**
1079      * Creates a new <code>TabAction</code>.
1080      */
InsertTabAction()1081     public InsertTabAction()
1082     {
1083       super(insertTabAction);
1084     }
1085 
1086     /**
1087      * Performs the <code>Action</code>.
1088      *
1089      * @param event the action event describing the user action
1090      */
actionPerformed(ActionEvent event)1091     public void actionPerformed(ActionEvent event)
1092     {
1093       JTextComponent t = getTextComponent(event);
1094       if (t != null)
1095         t.replaceSelection("\t");
1096     }
1097   }
1098 
1099   /**
1100    * The serial version of DefaultEditorKit.
1101    */
1102   private static final long serialVersionUID = 9017245433028523428L;
1103 
1104   /**
1105    * The name of the <code>Action</code> that moves the caret one character
1106    * backwards.
1107    *
1108    * @see #getActions()
1109    */
1110   public static final String backwardAction = "caret-backward";
1111 
1112   /**
1113    * The name of the <code>Action</code> that creates a beep in the speaker.
1114    *
1115    * @see #getActions()
1116    */
1117   public static final String beepAction = "beep";
1118 
1119   /**
1120    * The name of the <code>Action</code> that moves the caret to the beginning
1121    * of the <code>Document</code>.
1122    *
1123    * @see #getActions()
1124    */
1125   public static final String beginAction = "caret-begin";
1126 
1127   /**
1128    * The name of the <code>Action</code> that moves the caret to the beginning
1129    * of the current line.
1130    *
1131    * @see #getActions()
1132    */
1133   public static final String beginLineAction = "caret-begin-line";
1134 
1135   /**
1136    * The name of the <code>Action</code> that moves the caret to the beginning
1137    * of the current paragraph.
1138    *
1139    * @see #getActions()
1140    */
1141   public static final String beginParagraphAction = "caret-begin-paragraph";
1142 
1143   /**
1144    * The name of the <code>Action</code> that moves the caret to the beginning
1145    * of the current word.
1146    *
1147    * @see #getActions()
1148    */
1149   public static final String beginWordAction = "caret-begin-word";
1150 
1151   /**
1152    * The name of the <code>Action</code> that copies the selected content
1153    * into the system clipboard.
1154    *
1155    * @see #getActions()
1156    */
1157   public static final String copyAction = "copy-to-clipboard";
1158 
1159   /**
1160    * The name of the <code>Action</code> that copies the selected content
1161    * into the system clipboard and removes the selection.
1162    *
1163    * @see #getActions()
1164    */
1165   public static final String cutAction = "cut-to-clipboard";
1166 
1167   /**
1168    * The name of the <code>Action</code> that is performed by default if
1169    * a key is typed and there is no keymap entry.
1170    *
1171    * @see #getActions()
1172    */
1173   public static final String defaultKeyTypedAction = "default-typed";
1174 
1175   /**
1176    * The name of the <code>Action</code> that deletes the character that
1177    * follows the current caret position.
1178    *
1179    * @see #getActions()
1180    */
1181   public static final String deleteNextCharAction = "delete-next";
1182 
1183   /**
1184    * The name of the <code>Action</code> that deletes the character that
1185    * precedes the current caret position.
1186    *
1187    * @see #getActions()
1188    */
1189   public static final String deletePrevCharAction = "delete-previous";
1190 
1191   /**
1192    * The name of the <code>Action</code> that moves the caret one line down.
1193    *
1194    * @see #getActions()
1195    */
1196   public static final String downAction = "caret-down";
1197 
1198   /**
1199    * The name of the <code>Action</code> that moves the caret to the end
1200    * of the <code>Document</code>.
1201    *
1202    * @see #getActions()
1203    */
1204   public static final String endAction = "caret-end";
1205 
1206   /**
1207    * The name of the <code>Action</code> that moves the caret to the end
1208    * of the current line.
1209    *
1210    * @see #getActions()
1211    */
1212   public static final String endLineAction = "caret-end-line";
1213 
1214   /**
1215    * When a document is read and an CRLF is encountered, then we add a property
1216    * with this name and a value of &quot;\r\n&quot;.
1217    */
1218   public static final String EndOfLineStringProperty = "__EndOfLine__";
1219 
1220   /**
1221    * The name of the <code>Action</code> that moves the caret to the end
1222    * of the current paragraph.
1223    *
1224    * @see #getActions()
1225    */
1226   public static final String endParagraphAction = "caret-end-paragraph";
1227 
1228   /**
1229    * The name of the <code>Action</code> that moves the caret to the end
1230    * of the current word.
1231    *
1232    * @see #getActions()
1233    */
1234   public static final String endWordAction = "caret-end-word";
1235 
1236   /**
1237    * The name of the <code>Action</code> that moves the caret one character
1238    * forward.
1239    *
1240    * @see #getActions()
1241    */
1242   public static final String forwardAction = "caret-forward";
1243 
1244   /**
1245    * The name of the <code>Action</code> that inserts a line break.
1246    *
1247    * @see #getActions()
1248    */
1249   public static final String insertBreakAction = "insert-break";
1250 
1251   /**
1252    * The name of the <code>Action</code> that inserts some content.
1253    *
1254    * @see #getActions()
1255    */
1256   public static final String insertContentAction = "insert-content";
1257 
1258   /**
1259    * The name of the <code>Action</code> that inserts a TAB.
1260    *
1261    * @see #getActions()
1262    */
1263   public static final String insertTabAction = "insert-tab";
1264 
1265   /**
1266    * The name of the <code>Action</code> that moves the caret to the beginning
1267    * of the next word.
1268    *
1269    * @see #getActions()
1270    */
1271   public static final String nextWordAction = "caret-next-word";
1272 
1273   /**
1274    * The name of the <code>Action</code> that moves the caret one page down.
1275    *
1276    * @see #getActions()
1277    */
1278   public static final String pageDownAction = "page-down";
1279 
1280   /**
1281    * The name of the <code>Action</code> that moves the caret one page up.
1282    *
1283    * @see #getActions()
1284    */
1285   public static final String pageUpAction = "page-up";
1286 
1287   /**
1288    * The name of the <code>Action</code> that copies content from the system
1289    * clipboard into the document.
1290    *
1291    * @see #getActions()
1292    */
1293   public static final String pasteAction = "paste-from-clipboard";
1294 
1295   /**
1296    * The name of the <code>Action</code> that moves the caret to the beginning
1297    * of the previous word.
1298    *
1299    * @see #getActions()
1300    */
1301   public static final String previousWordAction = "caret-previous-word";
1302 
1303   /**
1304    * The name of the <code>Action</code> that sets the editor in read only
1305    * mode.
1306    *
1307    * @see #getActions()
1308    */
1309   public static final String readOnlyAction = "set-read-only";
1310 
1311   /**
1312    * The name of the <code>Action</code> that selects the whole document.
1313    *
1314    * @see #getActions()
1315    */
1316   public static final String selectAllAction = "select-all";
1317 
1318   /**
1319    * The name of the <code>Action</code> that moves the caret one character
1320    * backwards, possibly extending the current selection.
1321    *
1322    * @see #getActions()
1323    */
1324   public static final String selectionBackwardAction = "selection-backward";
1325 
1326   /**
1327    * The name of the <code>Action</code> that moves the caret to the beginning
1328    * of the document, possibly extending the current selection.
1329    *
1330    * @see #getActions()
1331    */
1332   public static final String selectionBeginAction = "selection-begin";
1333 
1334   /**
1335    * The name of the <code>Action</code> that moves the caret to the beginning
1336    * of the current line, possibly extending the current selection.
1337    *
1338    * @see #getActions()
1339    */
1340   public static final String selectionBeginLineAction = "selection-begin-line";
1341 
1342   /**
1343    * The name of the <code>Action</code> that moves the caret to the beginning
1344    * of the current paragraph, possibly extending the current selection.
1345    *
1346    * @see #getActions()
1347    */
1348   public static final String selectionBeginParagraphAction =
1349     "selection-begin-paragraph";
1350 
1351   /**
1352    * The name of the <code>Action</code> that moves the caret to the beginning
1353    * of the current word, possibly extending the current selection.
1354    *
1355    * @see #getActions()
1356    */
1357   public static final String selectionBeginWordAction = "selection-begin-word";
1358 
1359   /**
1360    * The name of the <code>Action</code> that moves the caret one line down,
1361    * possibly extending the current selection.
1362    *
1363    * @see #getActions()
1364    */
1365   public static final String selectionDownAction = "selection-down";
1366 
1367   /**
1368    * The name of the <code>Action</code> that moves the caret to the end
1369    * of the document, possibly extending the current selection.
1370    *
1371    * @see #getActions()
1372    */
1373   public static final String selectionEndAction = "selection-end";
1374 
1375   /**
1376    * The name of the <code>Action</code> that moves the caret to the end
1377    * of the current line, possibly extending the current selection.
1378    *
1379    * @see #getActions()
1380    */
1381   public static final String selectionEndLineAction = "selection-end-line";
1382 
1383   /**
1384    * The name of the <code>Action</code> that moves the caret to the end
1385    * of the current paragraph, possibly extending the current selection.
1386    *
1387    * @see #getActions()
1388    */
1389   public static final String selectionEndParagraphAction =
1390     "selection-end-paragraph";
1391 
1392   /**
1393    * The name of the <code>Action</code> that moves the caret to the end
1394    * of the current word, possibly extending the current selection.
1395    *
1396    * @see #getActions()
1397    */
1398   public static final String selectionEndWordAction = "selection-end-word";
1399 
1400   /**
1401    * The name of the <code>Action</code> that moves the caret one character
1402    * forwards, possibly extending the current selection.
1403    *
1404    * @see #getActions()
1405    */
1406   public static final String selectionForwardAction = "selection-forward";
1407 
1408   /**
1409    * The name of the <code>Action</code> that moves the caret to the beginning
1410    * of the next word, possibly extending the current selection.
1411    *
1412    * @see #getActions()
1413    */
1414   public static final String selectionNextWordAction = "selection-next-word";
1415 
1416   /**
1417    * The name of the <code>Action</code> that moves the caret to the beginning
1418    * of the previous word, possibly extending the current selection.
1419    *
1420    * @see #getActions()
1421    */
1422   public static final String selectionPreviousWordAction =
1423     "selection-previous-word";
1424 
1425   /**
1426    * The name of the <code>Action</code> that moves the caret one line up,
1427    * possibly extending the current selection.
1428    *
1429    * @see #getActions()
1430    */
1431   public static final String selectionUpAction = "selection-up";
1432 
1433   /**
1434    * The name of the <code>Action</code> that selects the line around the
1435    * caret.
1436    *
1437    * @see #getActions()
1438    */
1439   public static final String selectLineAction = "select-line";
1440 
1441   /**
1442    * The name of the <code>Action</code> that selects the paragraph around the
1443    * caret.
1444    *
1445    * @see #getActions()
1446    */
1447   public static final String selectParagraphAction = "select-paragraph";
1448 
1449   /**
1450    * The name of the <code>Action</code> that selects the word around the
1451    * caret.
1452    *
1453    * @see #getActions()
1454    */
1455   public static final String selectWordAction = "select-word";
1456 
1457   /**
1458    * The name of the <code>Action</code> that moves the caret one line up.
1459    *
1460    * @see #getActions()
1461    */
1462   public static final String upAction = "caret-up";
1463 
1464   /**
1465    * The name of the <code>Action</code> that sets the editor in read-write
1466    * mode.
1467    *
1468    * @see #getActions()
1469    */
1470   public static final String writableAction = "set-writable";
1471 
1472   /**
1473    * Creates a new <code>DefaultEditorKit</code>.
1474    */
DefaultEditorKit()1475   public DefaultEditorKit()
1476   {
1477     // Nothing to do here.
1478   }
1479 
1480   /**
1481    * The <code>Action</code>s that are supported by the
1482    * <code>DefaultEditorKit</code>.
1483    */
1484   private static Action[] defaultActions =
1485   new Action[] {
1486     // These classes are public because they are so in the RI.
1487     new BeepAction(),
1488     new CopyAction(),
1489     new CutAction(),
1490     new DefaultKeyTypedAction(),
1491     new InsertBreakAction(),
1492     new InsertContentAction(),
1493     new InsertTabAction(),
1494     new PasteAction(),
1495 
1496     // These are (package-)private inner classes.
1497     new DeleteNextCharAction(),
1498     new DeletePrevCharAction(),
1499 
1500     new BeginLineAction(),
1501     new SelectionBeginLineAction(),
1502 
1503     new EndLineAction(),
1504     new SelectionEndLineAction(),
1505 
1506     new BackwardAction(),
1507     new SelectionBackwardAction(),
1508 
1509     new ForwardAction(),
1510     new SelectionForwardAction(),
1511 
1512     new UpAction(),
1513     new SelectionUpAction(),
1514 
1515     new DownAction(),
1516     new SelectionDownAction(),
1517 
1518     new NextWordAction(),
1519     new SelectionNextWordAction(),
1520 
1521     new PreviousWordAction(),
1522     new SelectionPreviousWordAction(),
1523 
1524     new BeginAction(),
1525     new SelectionBeginAction(),
1526 
1527     new EndAction(),
1528     new SelectionEndAction(),
1529 
1530     new BeginWordAction(),
1531     new SelectionBeginWordAction(),
1532 
1533     new EndWordAction(),
1534     new SelectionEndWordAction(),
1535 
1536     new SelectAllAction(),
1537     new SelectLineAction(),
1538     new SelectWordAction()
1539   };
1540 
1541   /**
1542    * Creates the <code>Caret</code> for this <code>EditorKit</code>. This
1543    * returns a {@link DefaultCaret} in this case.
1544    *
1545    * @return the <code>Caret</code> for this <code>EditorKit</code>
1546    */
createCaret()1547   public Caret createCaret()
1548   {
1549     return new DefaultCaret();
1550   }
1551 
1552   /**
1553    * Creates the default {@link Document} that this <code>EditorKit</code>
1554    * supports. This is a {@link PlainDocument} in this case.
1555    *
1556    * @return the default {@link Document} that this <code>EditorKit</code>
1557    *         supports
1558    */
createDefaultDocument()1559   public Document createDefaultDocument()
1560   {
1561     return new PlainDocument();
1562   }
1563 
1564   /**
1565    * Returns the <code>Action</code>s supported by this <code>EditorKit</code>.
1566    *
1567    * @return the <code>Action</code>s supported by this <code>EditorKit</code>
1568    */
getActions()1569   public Action[] getActions()
1570   {
1571     return defaultActions;
1572   }
1573 
1574   /**
1575    * Returns the content type that this <code>EditorKit</code> supports.
1576    * The <code>DefaultEditorKit</code> supports the content type
1577    * <code>text/plain</code>.
1578    *
1579    * @return the content type that this <code>EditorKit</code> supports
1580    */
getContentType()1581   public String getContentType()
1582   {
1583     return "text/plain";
1584   }
1585 
1586   /**
1587    * Returns a {@link ViewFactory} that is able to create {@link View}s for
1588    * the <code>Element</code>s that are used in this <code>EditorKit</code>'s
1589    * model. This returns null which lets the UI of the text component supply
1590    * <code>View</code>s.
1591    *
1592    * @return a {@link ViewFactory} that is able to create {@link View}s for
1593    *         the <code>Element</code>s that are used in this
1594    *         <code>EditorKit</code>'s model
1595    */
getViewFactory()1596   public ViewFactory getViewFactory()
1597   {
1598     return null;
1599   }
1600 
1601   /**
1602    * Reads a document of the supported content type from an {@link InputStream}
1603    * into the actual {@link Document} object.
1604    *
1605    * @param in the stream from which to read the document
1606    * @param document the document model into which the content is read
1607    * @param offset the offset inside to document where the content is inserted
1608    *
1609    * @throws BadLocationException if <code>offset</code> is an invalid location
1610    *         inside <code>document</code>
1611    * @throws IOException if something goes wrong while reading from
1612    *        <code>in</code>
1613    */
read(InputStream in, Document document, int offset)1614   public void read(InputStream in, Document document, int offset)
1615     throws BadLocationException, IOException
1616   {
1617     read(new InputStreamReader(in), document, offset);
1618   }
1619 
1620   /**
1621    * Reads a document of the supported content type from a {@link Reader}
1622    * into the actual {@link Document} object.
1623    *
1624    * @param in the reader from which to read the document
1625    * @param document the document model into which the content is read
1626    * @param offset the offset inside to document where the content is inserted
1627    *
1628    * @throws BadLocationException if <code>offset</code> is an invalid location
1629    *         inside <code>document</code>
1630    * @throws IOException if something goes wrong while reading from
1631    *        <code>in</code>
1632    */
read(Reader in, Document document, int offset)1633   public void read(Reader in, Document document, int offset)
1634     throws BadLocationException, IOException
1635   {
1636     BufferedReader reader = new BufferedReader(in);
1637 
1638     String line;
1639     CPStringBuilder content = new CPStringBuilder();
1640 
1641     while ((line = reader.readLine()) != null)
1642       {
1643         content.append(line);
1644         content.append("\n");
1645       }
1646 
1647     document.insertString(offset, content.substring(0, content.length() - 1),
1648                           SimpleAttributeSet.EMPTY);
1649   }
1650 
1651   /**
1652    * Writes the <code>Document</code> (or a fragment of the
1653    * <code>Document</code>) to an {@link OutputStream} in the
1654    * supported content type format.
1655    *
1656    * @param out the stream to write to
1657    * @param document the document that should be written out
1658    * @param offset the beginning offset from where to write
1659    * @param len the length of the fragment to write
1660    *
1661    * @throws BadLocationException if <code>offset</code> or
1662    *         <code>offset + len</code>is an invalid location inside
1663    *         <code>document</code>
1664    * @throws IOException if something goes wrong while writing to
1665    *        <code>out</code>
1666    */
write(OutputStream out, Document document, int offset, int len)1667   public void write(OutputStream out, Document document, int offset, int len)
1668     throws BadLocationException, IOException
1669   {
1670     write(new OutputStreamWriter(out), document, offset, len);
1671   }
1672 
1673   /**
1674    * Writes the <code>Document</code> (or a fragment of the
1675    * <code>Document</code>) to a {@link Writer} in the
1676    * supported content type format.
1677    *
1678    * @param out the writer to write to
1679    * @param document the document that should be written out
1680    * @param offset the beginning offset from where to write
1681    * @param len the length of the fragment to write
1682    *
1683    * @throws BadLocationException if <code>offset</code> is an
1684    * invalid location inside <code>document</code>.
1685    * @throws IOException if something goes wrong while writing to
1686    *        <code>out</code>
1687    */
write(Writer out, Document document, int offset, int len)1688   public void write(Writer out, Document document, int offset, int len)
1689       throws BadLocationException, IOException
1690   {
1691     // Throw a BLE if offset is invalid
1692     if (offset < 0 || offset > document.getLength())
1693       throw new BadLocationException("Tried to write to invalid location",
1694                                      offset);
1695 
1696     // If they gave an overly large len, just adjust it
1697     if (offset + len > document.getLength())
1698       len = document.getLength() - offset;
1699 
1700     out.write(document.getText(offset, len));
1701   }
1702 }
1703