1 /* JTextComponent.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.AWTEvent;
44 import java.awt.Color;
45 import java.awt.Container;
46 import java.awt.Dimension;
47 import java.awt.Insets;
48 import java.awt.Point;
49 import java.awt.Rectangle;
50 import java.awt.Shape;
51 import java.awt.datatransfer.Clipboard;
52 import java.awt.datatransfer.DataFlavor;
53 import java.awt.datatransfer.StringSelection;
54 import java.awt.datatransfer.Transferable;
55 import java.awt.datatransfer.UnsupportedFlavorException;
56 import java.awt.event.ActionEvent;
57 import java.awt.event.InputMethodListener;
58 import java.awt.event.KeyEvent;
59 import java.awt.event.MouseEvent;
60 import java.io.IOException;
61 import java.io.Reader;
62 import java.io.Writer;
63 import java.text.BreakIterator;
64 import java.util.Enumeration;
65 import java.util.Hashtable;
66 
67 import javax.accessibility.Accessible;
68 import javax.accessibility.AccessibleAction;
69 import javax.accessibility.AccessibleContext;
70 import javax.accessibility.AccessibleEditableText;
71 import javax.accessibility.AccessibleRole;
72 import javax.accessibility.AccessibleState;
73 import javax.accessibility.AccessibleStateSet;
74 import javax.accessibility.AccessibleText;
75 import javax.swing.Action;
76 import javax.swing.ActionMap;
77 import javax.swing.InputMap;
78 import javax.swing.JComponent;
79 import javax.swing.JViewport;
80 import javax.swing.KeyStroke;
81 import javax.swing.Scrollable;
82 import javax.swing.SwingConstants;
83 import javax.swing.TransferHandler;
84 import javax.swing.UIManager;
85 import javax.swing.event.CaretEvent;
86 import javax.swing.event.CaretListener;
87 import javax.swing.event.DocumentEvent;
88 import javax.swing.event.DocumentListener;
89 import javax.swing.plaf.ActionMapUIResource;
90 import javax.swing.plaf.InputMapUIResource;
91 import javax.swing.plaf.TextUI;
92 
93 public abstract class JTextComponent extends JComponent
94   implements Scrollable, Accessible
95 {
96   /**
97    * AccessibleJTextComponent implements accessibility hooks for
98    * JTextComponent.  It allows an accessibility driver to read and
99    * manipulate the text component's contents as well as update UI
100    * elements such as the caret.
101    */
102   public class AccessibleJTextComponent extends AccessibleJComponent implements
103       AccessibleText, CaretListener, DocumentListener, AccessibleAction,
104       AccessibleEditableText
105   {
106     private static final long serialVersionUID = 7664188944091413696L;
107 
108     /**
109      * The caret's offset.
110      */
111     private int caretDot;
112 
113     /**
114      * Construct an AccessibleJTextComponent.
115      */
AccessibleJTextComponent()116     public AccessibleJTextComponent()
117     {
118       super();
119       JTextComponent.this.addCaretListener(this);
120       caretDot = getCaretPosition();
121     }
122 
123     /**
124      * Retrieve the current caret position.  The index of the first
125      * caret position is 0.
126      *
127      * @return caret position
128      */
getCaretPosition()129     public int getCaretPosition()
130     {
131       return JTextComponent.this.getCaretPosition();
132     }
133 
134     /**
135      * Retrieve the current text selection.  If no text is selected
136      * this method returns null.
137      *
138      * @return the currently selected text or null
139      */
getSelectedText()140     public String getSelectedText()
141     {
142       return JTextComponent.this.getSelectedText();
143     }
144 
145     /**
146      * Retrieve the index of the first character in the current text
147      * selection.  If there is no text in the text component, this
148      * method returns 0.  If there is text in the text component, but
149      * there is no selection, this method returns the current caret
150      * position.
151      *
152      * @return the index of the first character in the selection, the
153      * current caret position or 0
154      */
getSelectionStart()155     public int getSelectionStart()
156     {
157       if (getSelectedText() == null
158           || (JTextComponent.this.getText().equals("")))
159         return 0;
160       return JTextComponent.this.getSelectionStart();
161     }
162 
163     /**
164      * Retrieve the index of the last character in the current text
165      * selection.  If there is no text in the text component, this
166      * method returns 0.  If there is text in the text component, but
167      * there is no selection, this method returns the current caret
168      * position.
169      *
170      * @return the index of the last character in the selection, the
171      * current caret position or 0
172      */
getSelectionEnd()173     public int getSelectionEnd()
174     {
175       return JTextComponent.this.getSelectionEnd();
176     }
177 
178     /**
179      * Handle a change in the caret position and fire any applicable
180      * property change events.
181      *
182      * @param e - the caret update event
183      */
caretUpdate(CaretEvent e)184     public void caretUpdate(CaretEvent e)
185     {
186       int dot = e.getDot();
187       int mark = e.getMark();
188       if (caretDot != dot)
189         {
190           firePropertyChange(ACCESSIBLE_CARET_PROPERTY, new Integer(caretDot),
191                              new Integer(dot));
192           caretDot = dot;
193         }
194       if (mark != dot)
195         {
196           firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
197                              getSelectedText());
198         }
199     }
200 
201     /**
202      * Retreive the accessible state set of this component.
203      *
204      * @return the accessible state set of this component
205      */
getAccessibleStateSet()206     public AccessibleStateSet getAccessibleStateSet()
207     {
208       AccessibleStateSet state = super.getAccessibleStateSet();
209       if (isEditable())
210         state.add(AccessibleState.EDITABLE);
211       return state;
212     }
213 
214     /**
215      * Retrieve the accessible role of this component.
216      *
217      * @return the accessible role of this component
218      *
219      * @see AccessibleRole
220      */
getAccessibleRole()221     public AccessibleRole getAccessibleRole()
222     {
223       return AccessibleRole.TEXT;
224     }
225 
226     /**
227      * Retrieve an AccessibleEditableText object that controls this
228      * text component.
229      *
230      * @return this
231      */
getAccessibleEditableText()232     public AccessibleEditableText getAccessibleEditableText()
233     {
234       return this;
235     }
236 
237     /**
238      * Retrieve an AccessibleText object that controls this text
239      * component.
240      *
241      * @return this
242      *
243      * @see AccessibleText
244      */
getAccessibleText()245     public AccessibleText getAccessibleText()
246     {
247       return this;
248     }
249 
250     /**
251      * Handle a text insertion event and fire an
252      * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
253      * event.
254      *
255      * @param e - the insertion event
256      */
insertUpdate(DocumentEvent e)257     public void insertUpdate(DocumentEvent e)
258     {
259       firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
260                          new Integer(e.getOffset()));
261     }
262 
263     /**
264      * Handle a text removal event and fire an
265      * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
266      * event.
267      *
268      * @param e - the removal event
269      */
removeUpdate(DocumentEvent e)270     public void removeUpdate(DocumentEvent e)
271     {
272       firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
273                          new Integer(e.getOffset()));
274     }
275 
276     /**
277      * Handle a text change event and fire an
278      * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
279      * event.
280      *
281      * @param e - text change event
282      */
changedUpdate(DocumentEvent e)283     public void changedUpdate(DocumentEvent e)
284     {
285       firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
286                          new Integer(e.getOffset()));
287     }
288 
289     /**
290      * Get the index of the character at the given point, in component
291      * pixel co-ordinates.  If the point argument is invalid this
292      * method returns -1.
293      *
294      * @param p - a point in component pixel co-ordinates
295      *
296      * @return a character index, or -1
297      */
getIndexAtPoint(Point p)298     public int getIndexAtPoint(Point p)
299     {
300       return viewToModel(p);
301     }
302 
303     /**
304      * Calculate the bounding box of the character at the given index.
305      * The returned x and y co-ordinates are relative to this text
306      * component's top-left corner.  If the index is invalid this
307      * method returns null.
308      *
309      * @param index - the character index
310      *
311      * @return a character's bounding box, or null
312      */
getCharacterBounds(int index)313     public Rectangle getCharacterBounds(int index)
314     {
315       // This is basically the same as BasicTextUI.modelToView().
316 
317       Rectangle bounds = null;
318       if (index >= 0 && index < doc.getLength() - 1)
319         {
320           if (doc instanceof AbstractDocument)
321             ((AbstractDocument) doc).readLock();
322           try
323             {
324               TextUI ui = getUI();
325               if (ui != null)
326                 {
327                   // Get editor rectangle.
328                   Rectangle rect = new Rectangle();
329                   Insets insets = getInsets();
330                   rect.x = insets.left;
331                   rect.y = insets.top;
332                   rect.width = getWidth() - insets.left - insets.right;
333                   rect.height = getHeight() - insets.top - insets.bottom;
334                   View rootView = ui.getRootView(JTextComponent.this);
335                   if (rootView != null)
336                     {
337                       rootView.setSize(rect.width, rect.height);
338                       Shape s = rootView.modelToView(index,
339                                                      Position.Bias.Forward,
340                                                      index + 1,
341                                                      Position.Bias.Backward,
342                                                      rect);
343                       if (s != null)
344                         bounds = s.getBounds();
345                     }
346                 }
347             }
348           catch (BadLocationException ex)
349             {
350               // Ignore (return null).
351             }
352           finally
353             {
354               if (doc instanceof AbstractDocument)
355                 ((AbstractDocument) doc).readUnlock();
356             }
357         }
358       return bounds;
359     }
360 
361     /**
362      * Return the length of the text in this text component.
363      *
364      * @return a character length
365      */
getCharCount()366     public int getCharCount()
367     {
368       return JTextComponent.this.getText().length();
369     }
370 
371    /**
372     * Gets the character attributes of the character at index. If
373     * the index is out of bounds, null is returned.
374     *
375     * @param index - index of the character
376     *
377     * @return the character's attributes
378     */
getCharacterAttribute(int index)379     public AttributeSet getCharacterAttribute(int index)
380     {
381       AttributeSet atts;
382       if (doc instanceof AbstractDocument)
383         ((AbstractDocument) doc).readLock();
384       try
385         {
386           Element el = doc.getDefaultRootElement();
387           while (! el.isLeaf())
388             {
389               int i = el.getElementIndex(index);
390               el = el.getElement(i);
391             }
392           atts = el.getAttributes();
393         }
394       finally
395         {
396           if (doc instanceof AbstractDocument)
397             ((AbstractDocument) doc).readUnlock();
398         }
399       return atts;
400     }
401 
402     /**
403      * Gets the text located at index. null is returned if the index
404      * or part is invalid.
405      *
406      * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
407      * @param index - index of the part
408      *
409      * @return the part of text at that index, or null
410      */
getAtIndex(int part, int index)411     public String getAtIndex(int part, int index)
412     {
413       return getAtIndexImpl(part, index, 0);
414     }
415 
416     /**
417      * Gets the text located after index. null is returned if the index
418      * or part is invalid.
419      *
420      * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
421      * @param index - index after the part
422      *
423      * @return the part of text after that index, or null
424      */
getAfterIndex(int part, int index)425     public String getAfterIndex(int part, int index)
426     {
427       return getAtIndexImpl(part, index, 1);
428     }
429 
430     /**
431      * Gets the text located before index. null is returned if the index
432      * or part is invalid.
433      *
434      * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
435      * @param index - index before the part
436      *
437      * @return the part of text before that index, or null
438      */
getBeforeIndex(int part, int index)439     public String getBeforeIndex(int part, int index)
440     {
441       return getAtIndexImpl(part, index, -1);
442     }
443 
444     /**
445      * Implements getAtIndex(), getBeforeIndex() and getAfterIndex().
446      *
447      * @param part the part to return, either CHARACTER, WORD or SENTENCE
448      * @param index the index
449      * @param dir the direction, -1 for backwards, 0 for here, +1 for forwards
450      *
451      * @return the resulting string
452      */
getAtIndexImpl(int part, int index, int dir)453     private String getAtIndexImpl(int part, int index, int dir)
454     {
455       String ret = null;
456       if (doc instanceof AbstractDocument)
457         ((AbstractDocument) doc).readLock();
458       try
459         {
460           BreakIterator iter = null;
461           switch (part)
462           {
463             case CHARACTER:
464               iter = BreakIterator.getCharacterInstance(getLocale());
465               break;
466             case WORD:
467               iter = BreakIterator.getWordInstance(getLocale());
468               break;
469             case SENTENCE:
470               iter = BreakIterator.getSentenceInstance(getLocale());
471               break;
472             default:
473               break;
474           }
475           String text = doc.getText(0, doc.getLength() - 1);
476           iter.setText(text);
477           int start = index;
478           int end = index;
479           switch (dir)
480           {
481           case 0:
482             if (iter.isBoundary(index))
483               {
484                 start = index;
485                 end = iter.following(index);
486               }
487             else
488               {
489                 start = iter.preceding(index);
490                 end = iter.next();
491               }
492             break;
493           case 1:
494             start = iter.following(index);
495             end = iter.next();
496             break;
497           case -1:
498             end = iter.preceding(index);
499             start = iter.previous();
500             break;
501           default:
502             assert false;
503           }
504           ret = text.substring(start, end);
505         }
506       catch (BadLocationException ex)
507         {
508           // Ignore (return null).
509         }
510       finally
511         {
512           if (doc instanceof AbstractDocument)
513             ((AbstractDocument) doc).readUnlock();
514         }
515       return ret;
516     }
517 
518     /**
519      * Returns the number of actions for this object. The zero-th
520      * object represents the default action.
521      *
522      * @return the number of actions (0-based).
523      */
getAccessibleActionCount()524     public int getAccessibleActionCount()
525     {
526       return getActions().length;
527     }
528 
529     /**
530      * Returns the description of the i-th action. Null is returned if
531      * i is out of bounds.
532      *
533      * @param i - the action to get the description for
534      *
535      * @return description of the i-th action
536      */
getAccessibleActionDescription(int i)537     public String getAccessibleActionDescription(int i)
538     {
539       String desc = null;
540       Action[] actions = getActions();
541       if (i >= 0 && i < actions.length)
542         desc = (String) actions[i].getValue(Action.NAME);
543       return desc;
544     }
545 
546     /**
547      * Performs the i-th action. Nothing happens if i is
548      * out of bounds.
549      *
550      * @param i - the action to perform
551      *
552      * @return true if the action was performed successfully
553      */
doAccessibleAction(int i)554     public boolean doAccessibleAction(int i)
555     {
556       boolean ret = false;
557       Action[] actions = getActions();
558       if (i >= 0 && i < actions.length)
559         {
560           ActionEvent ev = new ActionEvent(JTextComponent.this,
561                                            ActionEvent.ACTION_PERFORMED, null);
562           actions[i].actionPerformed(ev);
563           ret = true;
564         }
565       return ret;
566     }
567 
568     /**
569      * Sets the text contents.
570      *
571      * @param s - the new text contents.
572      */
setTextContents(String s)573     public void setTextContents(String s)
574     {
575       setText(s);
576     }
577 
578     /**
579      * Inserts the text at the given index.
580      *
581      * @param index - the index to insert the new text at.
582      * @param s - the new text
583      */
insertTextAtIndex(int index, String s)584     public void insertTextAtIndex(int index, String s)
585     {
586       try
587         {
588           doc.insertString(index, s, null);
589         }
590       catch (BadLocationException ex)
591         {
592           // What should we do with this?
593           ex.printStackTrace();
594         }
595     }
596 
597     /**
598      * Gets the text between two indexes.
599      *
600      * @param start - the starting index (inclusive)
601      * @param end - the ending index (exclusive)
602      */
getTextRange(int start, int end)603     public String getTextRange(int start, int end)
604     {
605       try
606       {
607         return JTextComponent.this.getText(start, end - start);
608       }
609       catch (BadLocationException ble)
610       {
611         return "";
612       }
613     }
614 
615     /**
616      * Deletes the text between two indexes.
617      *
618      * @param start - the starting index (inclusive)
619      * @param end - the ending index (exclusive)
620      */
delete(int start, int end)621     public void delete(int start, int end)
622     {
623       replaceText(start, end, "");
624     }
625 
626     /**
627      * Cuts the text between two indexes. The text is put
628      * into the system clipboard.
629      *
630      * @param start - the starting index (inclusive)
631      * @param end - the ending index (exclusive)
632      */
cut(int start, int end)633     public void cut(int start, int end)
634     {
635       JTextComponent.this.select(start, end);
636       JTextComponent.this.cut();
637     }
638 
639     /**
640      * Pastes the text from the system clipboard to the given index.
641      *
642      * @param start - the starting index
643      */
paste(int start)644     public void paste(int start)
645     {
646       JTextComponent.this.setCaretPosition(start);
647       JTextComponent.this.paste();
648     }
649 
650     /**
651      * Replaces the text between two indexes with the given text.
652      *
653      *
654      * @param start - the starting index (inclusive)
655      * @param end - the ending index (exclusive)
656      * @param s - the text to paste
657      */
replaceText(int start, int end, String s)658     public void replaceText(int start, int end, String s)
659     {
660       JTextComponent.this.select(start, end);
661       JTextComponent.this.replaceSelection(s);
662     }
663 
664     /**
665      * Selects the text between two indexes.
666      *
667      * @param start - the starting index (inclusive)
668      * @param end - the ending index (exclusive)
669      */
selectText(int start, int end)670     public void selectText(int start, int end)
671     {
672       JTextComponent.this.select(start, end);
673     }
674 
675     /**
676      * Sets the attributes of all the text between two indexes.
677      *
678      * @param start - the starting index (inclusive)
679      * @param end - the ending index (exclusive)
680      * @param s - the new attribute set for the text in the range
681      */
setAttributes(int start, int end, AttributeSet s)682     public void setAttributes(int start, int end, AttributeSet s)
683     {
684       if (doc instanceof StyledDocument)
685         {
686           StyledDocument sdoc = (StyledDocument) doc;
687           sdoc.setCharacterAttributes(start, end - start, s, true);
688         }
689     }
690   }
691 
692   public static class KeyBinding
693   {
694     public KeyStroke key;
695     public String actionName;
696 
697     /**
698      * Creates a new <code>KeyBinding</code> instance.
699      *
700      * @param key a <code>KeyStroke</code> value
701      * @param actionName a <code>String</code> value
702      */
KeyBinding(KeyStroke key, String actionName)703     public KeyBinding(KeyStroke key, String actionName)
704     {
705       this.key = key;
706       this.actionName = actionName;
707     }
708   }
709 
710   /**
711    * According to <a
712    * href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">this
713    * report</a>, a pair of private classes wraps a {@link
714    * javax.swing.text.Keymap} in the new {@link InputMap} / {@link
715    * ActionMap} interfaces, such that old Keymap-using code can make use of
716    * the new framework.
717    *
718    * <p>A little bit of experimentation with these classes reveals the following
719    * structure:
720    *
721    * <ul>
722    *
723    * <li>KeymapWrapper extends {@link InputMap} and holds a reference to
724    * the underlying {@link Keymap}.</li>
725    *
726    * <li>KeymapWrapper maps {@link KeyStroke} objects to {@link Action}
727    * objects, by delegation to the underlying {@link Keymap}.</li>
728    *
729    * <li>KeymapActionMap extends {@link ActionMap} also holds a reference to
730    * the underlying {@link Keymap} but only appears to use it for listing
731    * its keys. </li>
732    *
733    * <li>KeymapActionMap maps all {@link Action} objects to
734    * <em>themselves</em>, whether they exist in the underlying {@link
735    * Keymap} or not, and passes other objects to the parent {@link
736    * ActionMap} for resolving.
737    *
738    * </ul>
739    */
740 
741   private class KeymapWrapper extends InputMap
742   {
743     Keymap map;
744 
KeymapWrapper(Keymap k)745     public KeymapWrapper(Keymap k)
746     {
747       map = k;
748     }
749 
size()750     public int size()
751     {
752       return map.getBoundKeyStrokes().length + super.size();
753     }
754 
get(KeyStroke ks)755     public Object get(KeyStroke ks)
756     {
757       Action mapped = null;
758       Keymap m = map;
759       while(mapped == null && m != null)
760         {
761           mapped = m.getAction(ks);
762           if (mapped == null && ks.getKeyEventType() == KeyEvent.KEY_TYPED)
763             mapped = m.getDefaultAction();
764           if (mapped == null)
765             m = m.getResolveParent();
766         }
767 
768       if (mapped == null)
769         return super.get(ks);
770       else
771         return mapped;
772     }
773 
keys()774     public KeyStroke[] keys()
775     {
776       KeyStroke[] superKeys = super.keys();
777       KeyStroke[] mapKeys = map.getBoundKeyStrokes();
778       KeyStroke[] bothKeys = new KeyStroke[superKeys.length + mapKeys.length];
779       for (int i = 0; i < superKeys.length; ++i)
780         bothKeys[i] = superKeys[i];
781       for (int i = 0; i < mapKeys.length; ++i)
782         bothKeys[i + superKeys.length] = mapKeys[i];
783       return bothKeys;
784     }
785 
allKeys()786     public KeyStroke[] allKeys()
787     {
788       KeyStroke[] superKeys = super.allKeys();
789       KeyStroke[] mapKeys = map.getBoundKeyStrokes();
790       int skl = 0;
791       int mkl = 0;
792       if (superKeys != null)
793         skl = superKeys.length;
794       if (mapKeys != null)
795         mkl = mapKeys.length;
796       KeyStroke[] bothKeys = new KeyStroke[skl + mkl];
797       for (int i = 0; i < skl; ++i)
798         bothKeys[i] = superKeys[i];
799       for (int i = 0; i < mkl; ++i)
800         bothKeys[i + skl] = mapKeys[i];
801       return bothKeys;
802     }
803   }
804 
805   private class KeymapActionMap extends ActionMap
806   {
807     Keymap map;
808 
KeymapActionMap(Keymap k)809     public KeymapActionMap(Keymap k)
810     {
811       map = k;
812     }
813 
get(Object cmd)814     public Action get(Object cmd)
815     {
816       if (cmd instanceof Action)
817         return (Action) cmd;
818       else
819         return super.get(cmd);
820     }
821 
size()822     public int size()
823     {
824       return map.getBoundKeyStrokes().length + super.size();
825     }
826 
keys()827     public Object[] keys()
828     {
829       Object[] superKeys = super.keys();
830       Object[] mapKeys = map.getBoundKeyStrokes();
831       Object[] bothKeys = new Object[superKeys.length + mapKeys.length];
832       for (int i = 0; i < superKeys.length; ++i)
833         bothKeys[i] = superKeys[i];
834       for (int i = 0; i < mapKeys.length; ++i)
835         bothKeys[i + superKeys.length] = mapKeys[i];
836       return bothKeys;
837     }
838 
allKeys()839     public Object[] allKeys()
840     {
841       Object[] superKeys = super.allKeys();
842       Object[] mapKeys = map.getBoundKeyStrokes();
843       Object[] bothKeys = new Object[superKeys.length + mapKeys.length];
844       for (int i = 0; i < superKeys.length; ++i)
845         bothKeys[i] = superKeys[i];
846       for (int i = 0; i < mapKeys.length; ++i)
847         bothKeys[i + superKeys.length] = mapKeys[i];
848       return bothKeys;
849     }
850 
851   }
852 
853   static class DefaultKeymap implements Keymap
854   {
855     String name;
856     Keymap parent;
857     Hashtable map;
858     Action defaultAction;
859 
DefaultKeymap(String name)860     public DefaultKeymap(String name)
861     {
862       this.name = name;
863       this.map = new Hashtable();
864     }
865 
addActionForKeyStroke(KeyStroke key, Action a)866     public void addActionForKeyStroke(KeyStroke key, Action a)
867     {
868       map.put(key, a);
869     }
870 
871     /**
872      * Looks up a KeyStroke either in the current map or the parent Keymap;
873      * does <em>not</em> return the default action if lookup fails.
874      *
875      * @param key The KeyStroke to look up an Action for.
876      *
877      * @return The mapping for <code>key</code>, or <code>null</code>
878      * if no mapping exists in this Keymap or any of its parents.
879      */
getAction(KeyStroke key)880     public Action getAction(KeyStroke key)
881     {
882       if (map.containsKey(key))
883         return (Action) map.get(key);
884       else if (parent != null)
885         return parent.getAction(key);
886       else
887         return null;
888     }
889 
getBoundActions()890     public Action[] getBoundActions()
891     {
892       Action [] ret = new Action[map.size()];
893       Enumeration e = map.elements();
894       int i = 0;
895       while (e.hasMoreElements())
896         {
897           ret[i++] = (Action) e.nextElement();
898         }
899       return ret;
900     }
901 
getBoundKeyStrokes()902     public KeyStroke[] getBoundKeyStrokes()
903     {
904       KeyStroke [] ret = new KeyStroke[map.size()];
905       Enumeration e = map.keys();
906       int i = 0;
907       while (e.hasMoreElements())
908         {
909           ret[i++] = (KeyStroke) e.nextElement();
910         }
911       return ret;
912     }
913 
getDefaultAction()914     public Action getDefaultAction()
915     {
916       return defaultAction;
917     }
918 
getKeyStrokesForAction(Action a)919     public KeyStroke[] getKeyStrokesForAction(Action a)
920     {
921       int i = 0;
922       Enumeration e = map.keys();
923       while (e.hasMoreElements())
924         {
925           if (map.get(e.nextElement()).equals(a))
926             ++i;
927         }
928       KeyStroke [] ret = new KeyStroke[i];
929       i = 0;
930       e = map.keys();
931       while (e.hasMoreElements())
932         {
933           KeyStroke k = (KeyStroke) e.nextElement();
934           if (map.get(k).equals(a))
935             ret[i++] = k;
936         }
937       return ret;
938     }
939 
getName()940     public String getName()
941     {
942       return name;
943     }
944 
getResolveParent()945     public Keymap getResolveParent()
946     {
947       return parent;
948     }
949 
isLocallyDefined(KeyStroke key)950     public boolean isLocallyDefined(KeyStroke key)
951     {
952       return map.containsKey(key);
953     }
954 
removeBindings()955     public void removeBindings()
956     {
957       map.clear();
958     }
959 
removeKeyStrokeBinding(KeyStroke key)960     public void removeKeyStrokeBinding(KeyStroke key)
961     {
962       map.remove(key);
963     }
964 
setDefaultAction(Action a)965     public void setDefaultAction(Action a)
966     {
967       defaultAction = a;
968     }
969 
setResolveParent(Keymap p)970     public void setResolveParent(Keymap p)
971     {
972       parent = p;
973     }
974   }
975 
976   class DefaultTransferHandler extends TransferHandler
977   {
canImport(JComponent component, DataFlavor[] flavors)978     public boolean canImport(JComponent component, DataFlavor[] flavors)
979     {
980       JTextComponent textComponent = (JTextComponent) component;
981 
982       if (! (textComponent.isEnabled()
983              && textComponent.isEditable()
984              && flavors != null))
985         return false;
986 
987       for (int i = 0; i < flavors.length; ++i)
988         if (flavors[i].equals(DataFlavor.stringFlavor))
989            return true;
990 
991       return false;
992     }
993 
exportToClipboard(JComponent component, Clipboard clipboard, int action)994     public void exportToClipboard(JComponent component, Clipboard clipboard,
995                                   int action)
996     {
997       JTextComponent textComponent = (JTextComponent) component;
998       int start = textComponent.getSelectionStart();
999       int end = textComponent.getSelectionEnd();
1000 
1001       if (start == end)
1002         return;
1003 
1004       try
1005         {
1006           // Copy text to clipboard.
1007           String data = textComponent.getDocument().getText(start, end);
1008           StringSelection selection = new StringSelection(data);
1009           clipboard.setContents(selection, null);
1010 
1011           // Delete selected text on cut action.
1012           if (action == MOVE)
1013             doc.remove(start, end - start);
1014         }
1015       catch (BadLocationException e)
1016         {
1017           // Ignore this and do nothing.
1018         }
1019     }
1020 
getSourceActions()1021     public int getSourceActions()
1022     {
1023       return NONE;
1024     }
1025 
importData(JComponent component, Transferable transferable)1026     public boolean importData(JComponent component, Transferable transferable)
1027     {
1028       DataFlavor flavor = null;
1029       DataFlavor[] flavors = transferable.getTransferDataFlavors();
1030 
1031       if (flavors == null)
1032         return false;
1033 
1034       for (int i = 0; i < flavors.length; ++i)
1035         if (flavors[i].equals(DataFlavor.stringFlavor))
1036           flavor = flavors[i];
1037 
1038       if (flavor == null)
1039         return false;
1040 
1041       try
1042         {
1043           JTextComponent textComponent = (JTextComponent) component;
1044           String data = (String) transferable.getTransferData(flavor);
1045           textComponent.replaceSelection(data);
1046           return true;
1047         }
1048       catch (IOException e)
1049         {
1050           // Ignored.
1051         }
1052       catch (UnsupportedFlavorException e)
1053         {
1054           // Ignored.
1055         }
1056 
1057       return false;
1058     }
1059   }
1060 
1061   private static final long serialVersionUID = -8796518220218978795L;
1062 
1063   public static final String DEFAULT_KEYMAP = "default";
1064   public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
1065 
1066   private static DefaultTransferHandler defaultTransferHandler;
1067   private static Hashtable keymaps = new Hashtable();
1068   private Keymap keymap;
1069   private char focusAccelerator = '\0';
1070   private NavigationFilter navigationFilter;
1071 
1072   /**
1073    * Get a Keymap from the global keymap table, by name.
1074    *
1075    * @param n The name of the Keymap to look up
1076    *
1077    * @return A Keymap associated with the provided name, or
1078    * <code>null</code> if no such Keymap exists
1079    *
1080    * @see #addKeymap
1081    * @see #removeKeymap
1082    * @see #keymaps
1083    */
getKeymap(String n)1084   public static Keymap getKeymap(String n)
1085   {
1086     return (Keymap) keymaps.get(n);
1087   }
1088 
1089   /**
1090    * Remove a Keymap from the global Keymap table, by name.
1091    *
1092    * @param n The name of the Keymap to remove
1093    *
1094    * @return The keymap removed from the global table
1095    *
1096    * @see #addKeymap
1097    * @see #getKeymap()
1098    * @see #keymaps
1099    */
removeKeymap(String n)1100   public static Keymap removeKeymap(String n)
1101   {
1102     Keymap km = (Keymap) keymaps.get(n);
1103     keymaps.remove(n);
1104     return km;
1105   }
1106 
1107   /**
1108    * Create a new Keymap with a specific name and parent, and add the new
1109    * Keymap to the global keymap table. The name may be <code>null</code>,
1110    * in which case the new Keymap will <em>not</em> be added to the global
1111    * Keymap table. The parent may also be <code>null</code>, which is
1112    * harmless.
1113    *
1114    * @param n The name of the new Keymap, or <code>null</code>
1115    * @param parent The parent of the new Keymap, or <code>null</code>
1116    *
1117    * @return The newly created Keymap
1118    *
1119    * @see #removeKeymap
1120    * @see #getKeymap()
1121    * @see #keymaps
1122    */
addKeymap(String n, Keymap parent)1123   public static Keymap addKeymap(String n, Keymap parent)
1124   {
1125     Keymap k = new DefaultKeymap(n);
1126     k.setResolveParent(parent);
1127     if (n != null)
1128       keymaps.put(n, k);
1129     return k;
1130   }
1131 
1132   /**
1133    * Get the current Keymap of this component.
1134    *
1135    * @return The component's current Keymap
1136    *
1137    * @see #setKeymap
1138    * @see #keymap
1139    */
getKeymap()1140   public Keymap getKeymap()
1141   {
1142     return keymap;
1143   }
1144 
1145   /**
1146    * Set the current Keymap of this component, installing appropriate
1147    * {@link KeymapWrapper} and {@link KeymapActionMap} objects in the
1148    * {@link InputMap} and {@link ActionMap} parent chains, respectively,
1149    * and fire a property change event with name <code>"keymap"</code>.
1150    *
1151    * @see #getKeymap()
1152    * @see #keymap
1153    */
setKeymap(Keymap k)1154   public void setKeymap(Keymap k)
1155   {
1156 
1157     // phase 1: replace the KeymapWrapper entry in the InputMap chain.
1158     // the goal here is to always maintain the following ordering:
1159     //
1160     //   [InputMap]? -> [KeymapWrapper]? -> [InputMapUIResource]*
1161     //
1162     // that is to say, component-specific InputMaps need to remain children
1163     // of Keymaps, and Keymaps need to remain children of UI-installed
1164     // InputMaps (and the order of each group needs to be preserved, of
1165     // course).
1166 
1167     KeymapWrapper kw = (k == null ? null : new KeymapWrapper(k));
1168     InputMap childInputMap = getInputMap(JComponent.WHEN_FOCUSED);
1169     if (childInputMap == null)
1170       setInputMap(JComponent.WHEN_FOCUSED, kw);
1171     else
1172       {
1173         while (childInputMap.getParent() != null
1174                && !(childInputMap.getParent() instanceof KeymapWrapper)
1175                && !(childInputMap.getParent() instanceof InputMapUIResource))
1176           childInputMap = childInputMap.getParent();
1177 
1178         // option 1: there is nobody to replace at the end of the chain
1179         if (childInputMap.getParent() == null)
1180           childInputMap.setParent(kw);
1181 
1182         // option 2: there is already a KeymapWrapper in the chain which
1183         // needs replacing (possibly with its own parents, possibly without)
1184         else if (childInputMap.getParent() instanceof KeymapWrapper)
1185           {
1186             if (kw == null)
1187               childInputMap.setParent(childInputMap.getParent().getParent());
1188             else
1189               {
1190                 kw.setParent(childInputMap.getParent().getParent());
1191                 childInputMap.setParent(kw);
1192               }
1193           }
1194 
1195         // option 3: there is an InputMapUIResource in the chain, which marks
1196         // the place where we need to stop and insert ourselves
1197         else if (childInputMap.getParent() instanceof InputMapUIResource)
1198           {
1199             if (kw != null)
1200               {
1201                 kw.setParent(childInputMap.getParent());
1202                 childInputMap.setParent(kw);
1203               }
1204           }
1205       }
1206 
1207     // phase 2: replace the KeymapActionMap entry in the ActionMap chain
1208 
1209     KeymapActionMap kam = (k == null ? null : new KeymapActionMap(k));
1210     ActionMap childActionMap = getActionMap();
1211     if (childActionMap == null)
1212       setActionMap(kam);
1213     else
1214       {
1215         while (childActionMap.getParent() != null
1216                && !(childActionMap.getParent() instanceof KeymapActionMap)
1217                && !(childActionMap.getParent() instanceof ActionMapUIResource))
1218           childActionMap = childActionMap.getParent();
1219 
1220         // option 1: there is nobody to replace at the end of the chain
1221         if (childActionMap.getParent() == null)
1222           childActionMap.setParent(kam);
1223 
1224         // option 2: there is already a KeymapActionMap in the chain which
1225         // needs replacing (possibly with its own parents, possibly without)
1226         else if (childActionMap.getParent() instanceof KeymapActionMap)
1227           {
1228             if (kam == null)
1229               childActionMap.setParent(childActionMap.getParent().getParent());
1230             else
1231               {
1232                 kam.setParent(childActionMap.getParent().getParent());
1233                 childActionMap.setParent(kam);
1234               }
1235           }
1236 
1237         // option 3: there is an ActionMapUIResource in the chain, which marks
1238         // the place where we need to stop and insert ourselves
1239         else if (childActionMap.getParent() instanceof ActionMapUIResource)
1240           {
1241             if (kam != null)
1242               {
1243                 kam.setParent(childActionMap.getParent());
1244                 childActionMap.setParent(kam);
1245               }
1246           }
1247       }
1248 
1249     // phase 3: update the explicit keymap field
1250 
1251     Keymap old = keymap;
1252     keymap = k;
1253     firePropertyChange("keymap", old, k);
1254   }
1255 
1256   /**
1257    * Resolves a set of bindings against a set of actions and inserts the
1258    * results into a {@link Keymap}. Specifically, for each provided binding
1259    * <code>b</code>, if there exists a provided action <code>a</code> such
1260    * that <code>a.getValue(Action.NAME) == b.ActionName</code> then an
1261    * entry is added to the Keymap mapping <code>b</code> to
1262    * <code>a</code>.
1263    *
1264    * @param map The Keymap to add new mappings to
1265    * @param bindings The set of bindings to add to the Keymap
1266    * @param actions The set of actions to resolve binding names against
1267    *
1268    * @see Action#NAME
1269    * @see Action#getValue
1270    * @see KeyBinding#actionName
1271    */
loadKeymap(Keymap map, JTextComponent.KeyBinding[] bindings, Action[] actions)1272   public static void loadKeymap(Keymap map,
1273                                 JTextComponent.KeyBinding[] bindings,
1274                                 Action[] actions)
1275   {
1276     Hashtable acts = new Hashtable(actions.length);
1277     for (int i = 0; i < actions.length; ++i)
1278       acts.put(actions[i].getValue(Action.NAME), actions[i]);
1279       for (int i = 0; i < bindings.length; ++i)
1280       if (acts.containsKey(bindings[i].actionName))
1281         map.addActionForKeyStroke(bindings[i].key, (Action) acts.get(bindings[i].actionName));
1282   }
1283 
1284   /**
1285    * Returns the set of available Actions this component's associated
1286    * editor can run.  Equivalent to calling
1287    * <code>getUI().getEditorKit().getActions()</code>. This set of Actions
1288    * is a reasonable value to provide as a parameter to {@link
1289    * #loadKeymap}, when resolving a set of {@link KeyBinding} objects
1290    * against this component.
1291    *
1292    * @return The set of available Actions on this component's {@link EditorKit}
1293    *
1294    * @see TextUI#getEditorKit
1295    * @see EditorKit#getActions()
1296    */
getActions()1297   public Action[] getActions()
1298   {
1299     return getUI().getEditorKit(this).getActions();
1300   }
1301 
1302   // These are package-private to avoid an accessor method.
1303   Document doc;
1304   Caret caret;
1305   boolean editable;
1306 
1307   private Highlighter highlighter;
1308   private Color caretColor;
1309   private Color disabledTextColor;
1310   private Color selectedTextColor;
1311   private Color selectionColor;
1312   private Insets margin;
1313   private boolean dragEnabled;
1314 
1315   /**
1316    * Creates a new <code>JTextComponent</code> instance.
1317    */
JTextComponent()1318   public JTextComponent()
1319   {
1320     Keymap defkeymap = getKeymap(DEFAULT_KEYMAP);
1321     if (defkeymap == null)
1322       {
1323         defkeymap = addKeymap(DEFAULT_KEYMAP, null);
1324         defkeymap.setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction());
1325       }
1326 
1327     setFocusable(true);
1328     setEditable(true);
1329     enableEvents(AWTEvent.KEY_EVENT_MASK);
1330     setOpaque(true);
1331     updateUI();
1332   }
1333 
setDocument(Document newDoc)1334   public void setDocument(Document newDoc)
1335   {
1336     Document oldDoc = doc;
1337     try
1338       {
1339         if (oldDoc instanceof AbstractDocument)
1340           ((AbstractDocument) oldDoc).readLock();
1341 
1342         doc = newDoc;
1343         firePropertyChange("document", oldDoc, newDoc);
1344       }
1345     finally
1346       {
1347         if (oldDoc instanceof AbstractDocument)
1348           ((AbstractDocument) oldDoc).readUnlock();
1349       }
1350     revalidate();
1351     repaint();
1352   }
1353 
getDocument()1354   public Document getDocument()
1355   {
1356     return doc;
1357   }
1358 
1359   /**
1360    * Get the <code>AccessibleContext</code> of this object.
1361    *
1362    * @return an <code>AccessibleContext</code> object
1363    */
getAccessibleContext()1364   public AccessibleContext getAccessibleContext()
1365   {
1366     return new AccessibleJTextComponent();
1367   }
1368 
setMargin(Insets m)1369   public void setMargin(Insets m)
1370   {
1371     margin = m;
1372   }
1373 
getMargin()1374   public Insets getMargin()
1375   {
1376     return margin;
1377   }
1378 
setText(String text)1379   public void setText(String text)
1380   {
1381     try
1382       {
1383         if (doc instanceof AbstractDocument)
1384           ((AbstractDocument) doc).replace(0, doc.getLength(), text, null);
1385         else
1386           {
1387             doc.remove(0, doc.getLength());
1388             doc.insertString(0, text, null);
1389           }
1390       }
1391     catch (BadLocationException e)
1392       {
1393         // This can never happen.
1394         throw (InternalError) new InternalError().initCause(e);
1395       }
1396   }
1397 
1398   /**
1399    * Retrieves the current text in this text document.
1400    *
1401    * @return the text
1402    *
1403    * @exception NullPointerException if the underlaying document is null
1404    */
getText()1405   public String getText()
1406   {
1407     if (doc == null)
1408       return null;
1409 
1410     try
1411       {
1412         return doc.getText(0, doc.getLength());
1413       }
1414     catch (BadLocationException e)
1415       {
1416         // This should never happen.
1417         return "";
1418       }
1419   }
1420 
1421   /**
1422    * Retrieves a part of the current text in this document.
1423    *
1424    * @param offset the postion of the first character
1425    * @param length the length of the text to retrieve
1426    *
1427    * @return the text
1428    *
1429    * @exception BadLocationException if arguments do not hold pre-conditions
1430    */
getText(int offset, int length)1431   public String getText(int offset, int length)
1432     throws BadLocationException
1433   {
1434     return getDocument().getText(offset, length);
1435   }
1436 
1437   /**
1438    * Retrieves the currently selected text in this text document.
1439    *
1440    * @return the selected text
1441    *
1442    * @exception NullPointerException if the underlaying document is null
1443    */
getSelectedText()1444   public String getSelectedText()
1445   {
1446     int start = getSelectionStart();
1447     int offset = getSelectionEnd() - start;
1448 
1449     if (offset <= 0)
1450       return null;
1451 
1452     try
1453       {
1454         return doc.getText(start, offset);
1455       }
1456     catch (BadLocationException e)
1457       {
1458         // This should never happen.
1459         return null;
1460       }
1461   }
1462 
1463   /**
1464    * Returns a string that specifies the name of the Look and Feel class
1465    * that renders this component.
1466    *
1467    * @return the string "TextComponentUI"
1468    */
getUIClassID()1469   public String getUIClassID()
1470   {
1471     return "TextComponentUI";
1472   }
1473 
1474   /**
1475    * Returns a string representation of this JTextComponent.
1476    */
paramString()1477   protected String paramString()
1478   {
1479     // TODO: Do something useful here.
1480     return super.paramString();
1481   }
1482 
1483   /**
1484    * This method returns the label's UI delegate.
1485    *
1486    * @return The label's UI delegate.
1487    */
getUI()1488   public TextUI getUI()
1489   {
1490     return (TextUI) ui;
1491   }
1492 
1493   /**
1494    * This method sets the label's UI delegate.
1495    *
1496    * @param newUI The label's UI delegate.
1497    */
setUI(TextUI newUI)1498   public void setUI(TextUI newUI)
1499   {
1500     super.setUI(newUI);
1501   }
1502 
1503   /**
1504    * This method resets the label's UI delegate to the default UI for the
1505    * current look and feel.
1506    */
updateUI()1507   public void updateUI()
1508   {
1509     setUI((TextUI) UIManager.getUI(this));
1510   }
1511 
getPreferredScrollableViewportSize()1512   public Dimension getPreferredScrollableViewportSize()
1513   {
1514     return getPreferredSize();
1515   }
1516 
getScrollableUnitIncrement(Rectangle visible, int orientation, int direction)1517   public int getScrollableUnitIncrement(Rectangle visible, int orientation,
1518                                         int direction)
1519   {
1520     // We return 1/10 of the visible area as documented in Sun's API docs.
1521     if (orientation == SwingConstants.HORIZONTAL)
1522       return visible.width / 10;
1523     else if (orientation == SwingConstants.VERTICAL)
1524       return visible.height / 10;
1525     else
1526       throw new IllegalArgumentException("orientation must be either "
1527                                       + "javax.swing.SwingConstants.VERTICAL "
1528                                       + "or "
1529                                       + "javax.swing.SwingConstants.HORIZONTAL"
1530                                          );
1531   }
1532 
getScrollableBlockIncrement(Rectangle visible, int orientation, int direction)1533   public int getScrollableBlockIncrement(Rectangle visible, int orientation,
1534                                          int direction)
1535   {
1536     // We return the whole visible area as documented in Sun's API docs.
1537     if (orientation == SwingConstants.HORIZONTAL)
1538       return visible.width;
1539     else if (orientation == SwingConstants.VERTICAL)
1540       return visible.height;
1541     else
1542       throw new IllegalArgumentException("orientation must be either "
1543                                       + "javax.swing.SwingConstants.VERTICAL "
1544                                       + "or "
1545                                       + "javax.swing.SwingConstants.HORIZONTAL"
1546                                          );
1547   }
1548 
1549   /**
1550    * Checks whether this text component it editable.
1551    *
1552    * @return true if editable, false otherwise
1553    */
isEditable()1554   public boolean isEditable()
1555   {
1556     return editable;
1557   }
1558 
1559   /**
1560    * Enables/disabled this text component's editability.
1561    *
1562    * @param newValue true to make it editable, false otherwise.
1563    */
setEditable(boolean newValue)1564   public void setEditable(boolean newValue)
1565   {
1566     if (editable == newValue)
1567       return;
1568 
1569     boolean oldValue = editable;
1570     editable = newValue;
1571     firePropertyChange("editable", oldValue, newValue);
1572   }
1573 
1574   /**
1575    * The <code>Caret</code> object used in this text component.
1576    *
1577    * @return the caret object
1578    */
getCaret()1579   public Caret getCaret()
1580   {
1581     return caret;
1582   }
1583 
1584   /**
1585    * Sets a new <code>Caret</code> for this text component.
1586    *
1587    * @param newCaret the new <code>Caret</code> to set
1588    */
setCaret(Caret newCaret)1589   public void setCaret(Caret newCaret)
1590   {
1591     if (caret != null)
1592       caret.deinstall(this);
1593 
1594     Caret oldCaret = caret;
1595     caret = newCaret;
1596 
1597     if (caret != null)
1598       caret.install(this);
1599 
1600     firePropertyChange("caret", oldCaret, newCaret);
1601   }
1602 
getCaretColor()1603   public Color getCaretColor()
1604   {
1605     return caretColor;
1606   }
1607 
setCaretColor(Color newColor)1608   public void setCaretColor(Color newColor)
1609   {
1610     Color oldCaretColor = caretColor;
1611     caretColor = newColor;
1612     firePropertyChange("caretColor", oldCaretColor, newColor);
1613   }
1614 
getDisabledTextColor()1615   public Color getDisabledTextColor()
1616   {
1617     return disabledTextColor;
1618   }
1619 
setDisabledTextColor(Color newColor)1620   public void setDisabledTextColor(Color newColor)
1621   {
1622     Color oldColor = disabledTextColor;
1623     disabledTextColor = newColor;
1624     firePropertyChange("disabledTextColor", oldColor, newColor);
1625   }
1626 
getSelectedTextColor()1627   public Color getSelectedTextColor()
1628   {
1629     return selectedTextColor;
1630   }
1631 
setSelectedTextColor(Color newColor)1632   public void setSelectedTextColor(Color newColor)
1633   {
1634     Color oldColor = selectedTextColor;
1635     selectedTextColor = newColor;
1636     firePropertyChange("selectedTextColor", oldColor, newColor);
1637   }
1638 
getSelectionColor()1639   public Color getSelectionColor()
1640   {
1641     return selectionColor;
1642   }
1643 
setSelectionColor(Color newColor)1644   public void setSelectionColor(Color newColor)
1645   {
1646     Color oldColor = selectionColor;
1647     selectionColor = newColor;
1648     firePropertyChange("selectionColor", oldColor, newColor);
1649   }
1650 
1651   /**
1652    * Retrisves the current caret position.
1653    *
1654    * @return the current position
1655    */
getCaretPosition()1656   public int getCaretPosition()
1657   {
1658     return caret.getDot();
1659   }
1660 
1661   /**
1662    * Sets the caret to a new position.
1663    *
1664    * @param position the new position
1665    */
setCaretPosition(int position)1666   public void setCaretPosition(int position)
1667   {
1668     if (doc == null)
1669       return;
1670 
1671     if (position < 0 || position > doc.getLength())
1672       throw new IllegalArgumentException();
1673 
1674     caret.setDot(position);
1675   }
1676 
1677   /**
1678    * Moves the caret to a given position. This selects the text between
1679    * the old and the new position of the caret.
1680    */
moveCaretPosition(int position)1681   public void moveCaretPosition(int position)
1682   {
1683     if (doc == null)
1684       return;
1685 
1686     if (position < 0 || position > doc.getLength())
1687       throw new IllegalArgumentException();
1688 
1689     caret.moveDot(position);
1690   }
1691 
getHighlighter()1692   public Highlighter getHighlighter()
1693   {
1694     return highlighter;
1695   }
1696 
setHighlighter(Highlighter newHighlighter)1697   public void setHighlighter(Highlighter newHighlighter)
1698   {
1699     if (highlighter != null)
1700       highlighter.deinstall(this);
1701 
1702     Highlighter oldHighlighter = highlighter;
1703     highlighter = newHighlighter;
1704 
1705     if (highlighter != null)
1706       highlighter.install(this);
1707 
1708     firePropertyChange("highlighter", oldHighlighter, newHighlighter);
1709   }
1710 
1711   /**
1712    * Returns the start postion of the currently selected text.
1713    *
1714    * @return the start postion
1715    */
getSelectionStart()1716   public int getSelectionStart()
1717   {
1718     return Math.min(caret.getDot(), caret.getMark());
1719   }
1720 
1721   /**
1722    * Selects the text from the given postion to the selection end position.
1723    *
1724    * @param start the start positon of the selected text.
1725    */
setSelectionStart(int start)1726   public void setSelectionStart(int start)
1727   {
1728     select(start, getSelectionEnd());
1729   }
1730 
1731   /**
1732    * Returns the end postion of the currently selected text.
1733    *
1734    * @return the end postion
1735    */
getSelectionEnd()1736   public int getSelectionEnd()
1737   {
1738     return Math.max(caret.getDot(), caret.getMark());
1739   }
1740 
1741   /**
1742    * Selects the text from the selection start postion to the given position.
1743    *
1744    * @param end the end positon of the selected text.
1745    */
setSelectionEnd(int end)1746   public void setSelectionEnd(int end)
1747   {
1748     select(getSelectionStart(), end);
1749   }
1750 
1751   /**
1752    * Selects a part of the content of the text component.
1753    *
1754    * @param start the start position of the selected text
1755    * @param end the end position of the selected text
1756    */
select(int start, int end)1757   public void select(int start, int end)
1758   {
1759     int length = doc.getLength();
1760 
1761     start = Math.max(start, 0);
1762     start = Math.min(start, length);
1763 
1764     end = Math.max(end, start);
1765     end = Math.min(end, length);
1766 
1767     setCaretPosition(start);
1768     moveCaretPosition(end);
1769   }
1770 
1771   /**
1772    * Selects the whole content of the text component.
1773    */
selectAll()1774   public void selectAll()
1775   {
1776     select(0, doc.getLength());
1777   }
1778 
replaceSelection(String content)1779   public synchronized void replaceSelection(String content)
1780   {
1781     int dot = caret.getDot();
1782     int mark = caret.getMark();
1783 
1784     // If content is empty delete selection.
1785     if (content == null)
1786       {
1787         caret.setDot(dot);
1788         return;
1789       }
1790 
1791     try
1792       {
1793         int start = getSelectionStart();
1794         int end = getSelectionEnd();
1795 
1796         // Remove selected text.
1797         if (dot != mark)
1798           doc.remove(start, end - start);
1799 
1800         // Insert new text.
1801         doc.insertString(start, content, null);
1802 
1803         // Set dot to new position,
1804         dot = start + content.length();
1805         setCaretPosition(dot);
1806 
1807         // and update it's magic position.
1808         caret.setMagicCaretPosition(modelToView(dot).getLocation());
1809       }
1810     catch (BadLocationException e)
1811       {
1812         // This should never happen.
1813       }
1814   }
1815 
getScrollableTracksViewportHeight()1816   public boolean getScrollableTracksViewportHeight()
1817   {
1818     if (getParent() instanceof JViewport)
1819       return getParent().getHeight() > getPreferredSize().height;
1820 
1821     return false;
1822   }
1823 
getScrollableTracksViewportWidth()1824   public boolean getScrollableTracksViewportWidth()
1825   {
1826     boolean res = false;
1827     Container c = getParent();
1828     if (c instanceof JViewport)
1829       res = ((JViewport) c).getExtentSize().width > getPreferredSize().width;
1830 
1831     return res;
1832   }
1833 
1834   /**
1835    * Adds a <code>CaretListener</code> object to this text component.
1836    *
1837    * @param listener the listener to add
1838    */
addCaretListener(CaretListener listener)1839   public void addCaretListener(CaretListener listener)
1840   {
1841     listenerList.add(CaretListener.class, listener);
1842   }
1843 
1844   /**
1845    * Removed a <code>CaretListener</code> object from this text component.
1846    *
1847    * @param listener the listener to remove
1848    */
removeCaretListener(CaretListener listener)1849   public void removeCaretListener(CaretListener listener)
1850   {
1851     listenerList.remove(CaretListener.class, listener);
1852   }
1853 
1854   /**
1855    * Returns all added <code>CaretListener</code> objects.
1856    *
1857    * @return an array of listeners
1858    */
getCaretListeners()1859   public CaretListener[] getCaretListeners()
1860   {
1861     return (CaretListener[]) getListeners(CaretListener.class);
1862   }
1863 
1864   /**
1865    * Notifies all registered <code>CaretListener</code> objects that the caret
1866    * was updated.
1867    *
1868    * @param event the event to send
1869    */
fireCaretUpdate(CaretEvent event)1870   protected void fireCaretUpdate(CaretEvent event)
1871   {
1872     CaretListener[] listeners = getCaretListeners();
1873 
1874     for (int index = 0; index < listeners.length; ++index)
1875       listeners[index].caretUpdate(event);
1876   }
1877 
1878   /**
1879    * Adds an <code>InputListener</code> object to this text component.
1880    *
1881    * @param listener the listener to add
1882    */
addInputMethodListener(InputMethodListener listener)1883   public void addInputMethodListener(InputMethodListener listener)
1884   {
1885     listenerList.add(InputMethodListener.class, listener);
1886   }
1887 
1888   /**
1889    * Removes an <code>InputListener</code> object from this text component.
1890    *
1891    * @param listener the listener to remove
1892    */
removeInputMethodListener(InputMethodListener listener)1893   public void removeInputMethodListener(InputMethodListener listener)
1894   {
1895     listenerList.remove(InputMethodListener.class, listener);
1896   }
1897 
1898   /**
1899    * Returns all added <code>InputMethodListener</code> objects.
1900    *
1901    * @return an array of listeners
1902    */
getInputMethodListeners()1903   public InputMethodListener[] getInputMethodListeners()
1904   {
1905     return (InputMethodListener[]) getListeners(InputMethodListener.class);
1906   }
1907 
modelToView(int position)1908   public Rectangle modelToView(int position) throws BadLocationException
1909   {
1910     return getUI().modelToView(this, position);
1911   }
1912 
getDragEnabled()1913   public boolean getDragEnabled()
1914   {
1915     return dragEnabled;
1916   }
1917 
setDragEnabled(boolean enabled)1918   public void setDragEnabled(boolean enabled)
1919   {
1920     dragEnabled = enabled;
1921   }
1922 
viewToModel(Point pt)1923   public int viewToModel(Point pt)
1924   {
1925     return getUI().viewToModel(this, pt);
1926   }
1927 
copy()1928   public void copy()
1929   {
1930     if (isEnabled())
1931     doTransferAction("copy", TransferHandler.getCopyAction());
1932   }
1933 
cut()1934   public void cut()
1935   {
1936     if (editable && isEnabled())
1937       doTransferAction("cut", TransferHandler.getCutAction());
1938   }
1939 
paste()1940   public void paste()
1941   {
1942     if (editable && isEnabled())
1943       doTransferAction("paste", TransferHandler.getPasteAction());
1944   }
1945 
doTransferAction(String name, Action action)1946   private void doTransferAction(String name, Action action)
1947   {
1948     // Install default TransferHandler if none set.
1949     if (getTransferHandler() == null)
1950       {
1951         if (defaultTransferHandler == null)
1952           defaultTransferHandler = new DefaultTransferHandler();
1953 
1954         setTransferHandler(defaultTransferHandler);
1955       }
1956 
1957     // Perform action.
1958     ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
1959                                         action.getValue(Action.NAME).toString());
1960     action.actionPerformed(event);
1961   }
1962 
setFocusAccelerator(char newKey)1963   public void setFocusAccelerator(char newKey)
1964   {
1965     if (focusAccelerator == newKey)
1966       return;
1967 
1968     char oldKey = focusAccelerator;
1969     focusAccelerator = newKey;
1970     firePropertyChange(FOCUS_ACCELERATOR_KEY, oldKey, newKey);
1971   }
1972 
getFocusAccelerator()1973   public char getFocusAccelerator()
1974   {
1975     return focusAccelerator;
1976   }
1977 
1978   /**
1979    * @since 1.4
1980    */
getNavigationFilter()1981   public NavigationFilter getNavigationFilter()
1982   {
1983     return navigationFilter;
1984   }
1985 
1986   /**
1987    * @since 1.4
1988    */
setNavigationFilter(NavigationFilter filter)1989   public void setNavigationFilter(NavigationFilter filter)
1990   {
1991     navigationFilter = filter;
1992   }
1993 
1994   /**
1995    * Read and set the content this component. If not overridden, the
1996    * method reads the component content as a plain text.
1997    *
1998    * The second parameter of this method describes the input stream. It can
1999    * be String, URL, File and so on. If not null, this object is added to
2000    * the properties of the associated document under the key
2001    * {@link Document#StreamDescriptionProperty}.
2002    *
2003    * @param input an input stream to read from.
2004    * @param streamDescription an object, describing the stream.
2005    *
2006    * @throws IOException if the reader throws it.
2007    *
2008    * @see #getDocument()
2009    * @see Document#getProperty(Object)
2010    */
read(Reader input, Object streamDescription)2011   public void read(Reader input, Object streamDescription)
2012             throws IOException
2013   {
2014     if (streamDescription != null)
2015       {
2016         Document d = getDocument();
2017         if (d != null)
2018           d.putProperty(Document.StreamDescriptionProperty, streamDescription);
2019       }
2020 
2021     CPStringBuilder b = new CPStringBuilder();
2022     int c;
2023 
2024     // Read till -1 (EOF).
2025     while ((c = input.read()) >= 0)
2026       b.append((char) c);
2027 
2028     setText(b.toString());
2029   }
2030 
2031   /**
2032    * Write the content of this component to the given stream. If not
2033    * overridden, the method writes the component content as a plain text.
2034    *
2035    * @param output the writer to write into.
2036    *
2037    * @throws IOException if the writer throws it.
2038    */
write(Writer output)2039   public void write(Writer output)
2040              throws IOException
2041   {
2042     output.write(getText());
2043   }
2044 
2045   /**
2046    * Returns the tooltip text for this text component for the given mouse
2047    * event. This forwards the call to
2048    * {@link TextUI#getToolTipText(JTextComponent, Point)}.
2049    *
2050    * @param ev the mouse event
2051    *
2052    * @return the tooltip text for this text component for the given mouse
2053    *         event
2054    */
getToolTipText(MouseEvent ev)2055   public String getToolTipText(MouseEvent ev)
2056   {
2057     return getUI().getToolTipText(this, ev.getPoint());
2058   }
2059 }
2060