1 /* JTextComponent.java --
2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
39 package javax
.swing
.text
;
41 import gnu
.java
.lang
.CPStringBuilder
;
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
;
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
;
93 public abstract class JTextComponent
extends JComponent
94 implements Scrollable
, Accessible
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.
102 public class AccessibleJTextComponent
extends AccessibleJComponent
implements
103 AccessibleText
, CaretListener
, DocumentListener
, AccessibleAction
,
104 AccessibleEditableText
106 private static final long serialVersionUID
= 7664188944091413696L;
109 * The caret's offset.
111 private int caretDot
;
114 * Construct an AccessibleJTextComponent.
116 public AccessibleJTextComponent()
119 JTextComponent
.this.addCaretListener(this);
120 caretDot
= getCaretPosition();
124 * Retrieve the current caret position. The index of the first
125 * caret position is 0.
127 * @return caret position
129 public int getCaretPosition()
131 return JTextComponent
.this.getCaretPosition();
135 * Retrieve the current text selection. If no text is selected
136 * this method returns null.
138 * @return the currently selected text or null
140 public String
getSelectedText()
142 return JTextComponent
.this.getSelectedText();
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
152 * @return the index of the first character in the selection, the
153 * current caret position or 0
155 public int getSelectionStart()
157 if (getSelectedText() == null
158 || (JTextComponent
.this.getText().equals("")))
160 return JTextComponent
.this.getSelectionStart();
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
170 * @return the index of the last character in the selection, the
171 * current caret position or 0
173 public int getSelectionEnd()
175 return JTextComponent
.this.getSelectionEnd();
179 * Handle a change in the caret position and fire any applicable
180 * property change events.
182 * @param e - the caret update event
184 public void caretUpdate(CaretEvent e
)
186 int dot
= e
.getDot();
187 int mark
= e
.getMark();
190 firePropertyChange(ACCESSIBLE_CARET_PROPERTY
, new Integer(caretDot
),
196 firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY
, null,
202 * Retreive the accessible state set of this component.
204 * @return the accessible state set of this component
206 public AccessibleStateSet
getAccessibleStateSet()
208 AccessibleStateSet state
= super.getAccessibleStateSet();
210 state
.add(AccessibleState
.EDITABLE
);
215 * Retrieve the accessible role of this component.
217 * @return the accessible role of this component
219 * @see AccessibleRole
221 public AccessibleRole
getAccessibleRole()
223 return AccessibleRole
.TEXT
;
227 * Retrieve an AccessibleEditableText object that controls this
232 public AccessibleEditableText
getAccessibleEditableText()
238 * Retrieve an AccessibleText object that controls this text
243 * @see AccessibleText
245 public AccessibleText
getAccessibleText()
251 * Handle a text insertion event and fire an
252 * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
255 * @param e - the insertion event
257 public void insertUpdate(DocumentEvent e
)
259 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY
, null,
260 new Integer(e
.getOffset()));
264 * Handle a text removal event and fire an
265 * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
268 * @param e - the removal event
270 public void removeUpdate(DocumentEvent e
)
272 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY
, null,
273 new Integer(e
.getOffset()));
277 * Handle a text change event and fire an
278 * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
281 * @param e - text change event
283 public void changedUpdate(DocumentEvent e
)
285 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY
, null,
286 new Integer(e
.getOffset()));
290 * Get the index of the character at the given point, in component
291 * pixel co-ordinates. If the point argument is invalid this
294 * @param p - a point in component pixel co-ordinates
296 * @return a character index, or -1
298 public int getIndexAtPoint(Point p
)
300 return viewToModel(p
);
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.
309 * @param index - the character index
311 * @return a character's bounding box, or null
313 public Rectangle
getCharacterBounds(int index
)
315 // This is basically the same as BasicTextUI.modelToView().
317 Rectangle bounds
= null;
318 if (index
>= 0 && index
< doc
.getLength() - 1)
320 if (doc
instanceof AbstractDocument
)
321 ((AbstractDocument
) doc
).readLock();
327 // Get editor rectangle.
328 Rectangle rect
= new Rectangle();
329 Insets insets
= getInsets();
330 rect
.x
= insets
.left
;
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)
337 rootView
.setSize(rect
.width
, rect
.height
);
338 Shape s
= rootView
.modelToView(index
,
339 Position
.Bias
.Forward
,
341 Position
.Bias
.Backward
,
344 bounds
= s
.getBounds();
348 catch (BadLocationException ex
)
350 // Ignore (return null).
354 if (doc
instanceof AbstractDocument
)
355 ((AbstractDocument
) doc
).readUnlock();
362 * Return the length of the text in this text component.
364 * @return a character length
366 public int getCharCount()
368 return JTextComponent
.this.getText().length();
372 * Gets the character attributes of the character at index. If
373 * the index is out of bounds, null is returned.
375 * @param index - index of the character
377 * @return the character's attributes
379 public AttributeSet
getCharacterAttribute(int index
)
382 if (doc
instanceof AbstractDocument
)
383 ((AbstractDocument
) doc
).readLock();
386 Element el
= doc
.getDefaultRootElement();
387 while (! el
.isLeaf())
389 int i
= el
.getElementIndex(index
);
390 el
= el
.getElement(i
);
392 atts
= el
.getAttributes();
396 if (doc
instanceof AbstractDocument
)
397 ((AbstractDocument
) doc
).readUnlock();
403 * Gets the text located at index. null is returned if the index
404 * or part is invalid.
406 * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
407 * @param index - index of the part
409 * @return the part of text at that index, or null
411 public String
getAtIndex(int part
, int index
)
413 return getAtIndexImpl(part
, index
, 0);
417 * Gets the text located after index. null is returned if the index
418 * or part is invalid.
420 * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
421 * @param index - index after the part
423 * @return the part of text after that index, or null
425 public String
getAfterIndex(int part
, int index
)
427 return getAtIndexImpl(part
, index
, 1);
431 * Gets the text located before index. null is returned if the index
432 * or part is invalid.
434 * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
435 * @param index - index before the part
437 * @return the part of text before that index, or null
439 public String
getBeforeIndex(int part
, int index
)
441 return getAtIndexImpl(part
, index
, -1);
445 * Implements getAtIndex(), getBeforeIndex() and getAfterIndex().
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
451 * @return the resulting string
453 private String
getAtIndexImpl(int part
, int index
, int dir
)
456 if (doc
instanceof AbstractDocument
)
457 ((AbstractDocument
) doc
).readLock();
460 BreakIterator iter
= null;
464 iter
= BreakIterator
.getCharacterInstance(getLocale());
467 iter
= BreakIterator
.getWordInstance(getLocale());
470 iter
= BreakIterator
.getSentenceInstance(getLocale());
475 String text
= doc
.getText(0, doc
.getLength() - 1);
482 if (iter
.isBoundary(index
))
485 end
= iter
.following(index
);
489 start
= iter
.preceding(index
);
494 start
= iter
.following(index
);
498 end
= iter
.preceding(index
);
499 start
= iter
.previous();
504 ret
= text
.substring(start
, end
);
506 catch (BadLocationException ex
)
508 // Ignore (return null).
512 if (doc
instanceof AbstractDocument
)
513 ((AbstractDocument
) doc
).readUnlock();
519 * Returns the number of actions for this object. The zero-th
520 * object represents the default action.
522 * @return the number of actions (0-based).
524 public int getAccessibleActionCount()
526 return getActions().length
;
530 * Returns the description of the i-th action. Null is returned if
531 * i is out of bounds.
533 * @param i - the action to get the description for
535 * @return description of the i-th action
537 public String
getAccessibleActionDescription(int i
)
540 Action
[] actions
= getActions();
541 if (i
>= 0 && i
< actions
.length
)
542 desc
= (String
) actions
[i
].getValue(Action
.NAME
);
547 * Performs the i-th action. Nothing happens if i is
550 * @param i - the action to perform
552 * @return true if the action was performed successfully
554 public boolean doAccessibleAction(int i
)
557 Action
[] actions
= getActions();
558 if (i
>= 0 && i
< actions
.length
)
560 ActionEvent ev
= new ActionEvent(JTextComponent
.this,
561 ActionEvent
.ACTION_PERFORMED
, null);
562 actions
[i
].actionPerformed(ev
);
569 * Sets the text contents.
571 * @param s - the new text contents.
573 public void setTextContents(String s
)
579 * Inserts the text at the given index.
581 * @param index - the index to insert the new text at.
582 * @param s - the new text
584 public void insertTextAtIndex(int index
, String s
)
588 doc
.insertString(index
, s
, null);
590 catch (BadLocationException ex
)
592 // What should we do with this?
593 ex
.printStackTrace();
598 * Gets the text between two indexes.
600 * @param start - the starting index (inclusive)
601 * @param end - the ending index (exclusive)
603 public String
getTextRange(int start
, int end
)
607 return JTextComponent
.this.getText(start
, end
- start
);
609 catch (BadLocationException ble
)
616 * Deletes the text between two indexes.
618 * @param start - the starting index (inclusive)
619 * @param end - the ending index (exclusive)
621 public void delete(int start
, int end
)
623 replaceText(start
, end
, "");
627 * Cuts the text between two indexes. The text is put
628 * into the system clipboard.
630 * @param start - the starting index (inclusive)
631 * @param end - the ending index (exclusive)
633 public void cut(int start
, int end
)
635 JTextComponent
.this.select(start
, end
);
636 JTextComponent
.this.cut();
640 * Pastes the text from the system clipboard to the given index.
642 * @param start - the starting index
644 public void paste(int start
)
646 JTextComponent
.this.setCaretPosition(start
);
647 JTextComponent
.this.paste();
651 * Replaces the text between two indexes with the given text.
654 * @param start - the starting index (inclusive)
655 * @param end - the ending index (exclusive)
656 * @param s - the text to paste
658 public void replaceText(int start
, int end
, String s
)
660 JTextComponent
.this.select(start
, end
);
661 JTextComponent
.this.replaceSelection(s
);
665 * Selects the text between two indexes.
667 * @param start - the starting index (inclusive)
668 * @param end - the ending index (exclusive)
670 public void selectText(int start
, int end
)
672 JTextComponent
.this.select(start
, end
);
676 * Sets the attributes of all the text between two indexes.
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
682 public void setAttributes(int start
, int end
, AttributeSet s
)
684 if (doc
instanceof StyledDocument
)
686 StyledDocument sdoc
= (StyledDocument
) doc
;
687 sdoc
.setCharacterAttributes(start
, end
- start
, s
, true);
692 public static class KeyBinding
694 public KeyStroke key
;
695 public String actionName
;
698 * Creates a new <code>KeyBinding</code> instance.
700 * @param key a <code>KeyStroke</code> value
701 * @param actionName a <code>String</code> value
703 public KeyBinding(KeyStroke key
, String actionName
)
706 this.actionName
= actionName
;
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
718 * <p>A little bit of experimentation with these classes reveals the following
723 * <li>KeymapWrapper extends {@link InputMap} and holds a reference to
724 * the underlying {@link Keymap}.</li>
726 * <li>KeymapWrapper maps {@link KeyStroke} objects to {@link Action}
727 * objects, by delegation to the underlying {@link Keymap}.</li>
729 * <li>KeymapActionMap extends {@link ActionMap} also holds a reference to
730 * the underlying {@link Keymap} but only appears to use it for listing
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.
741 private class KeymapWrapper
extends InputMap
745 public KeymapWrapper(Keymap k
)
752 return map
.getBoundKeyStrokes().length
+ super.size();
755 public Object
get(KeyStroke ks
)
757 Action mapped
= null;
759 while(mapped
== null && m
!= null)
761 mapped
= m
.getAction(ks
);
762 if (mapped
== null && ks
.getKeyEventType() == KeyEvent
.KEY_TYPED
)
763 mapped
= m
.getDefaultAction();
765 m
= m
.getResolveParent();
769 return super.get(ks
);
774 public KeyStroke
[] keys()
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
];
786 public KeyStroke
[] allKeys()
788 KeyStroke
[] superKeys
= super.allKeys();
789 KeyStroke
[] mapKeys
= map
.getBoundKeyStrokes();
792 if (superKeys
!= null)
793 skl
= superKeys
.length
;
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
];
805 private class KeymapActionMap
extends ActionMap
809 public KeymapActionMap(Keymap k
)
814 public Action
get(Object cmd
)
816 if (cmd
instanceof Action
)
819 return super.get(cmd
);
824 return map
.getBoundKeyStrokes().length
+ super.size();
827 public Object
[] keys()
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
];
839 public Object
[] allKeys()
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
];
853 static class DefaultKeymap
implements Keymap
858 Action defaultAction
;
860 public DefaultKeymap(String name
)
863 this.map
= new Hashtable();
866 public void addActionForKeyStroke(KeyStroke key
, Action a
)
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.
875 * @param key The KeyStroke to look up an Action for.
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.
880 public Action
getAction(KeyStroke key
)
882 if (map
.containsKey(key
))
883 return (Action
) map
.get(key
);
884 else if (parent
!= null)
885 return parent
.getAction(key
);
890 public Action
[] getBoundActions()
892 Action
[] ret
= new Action
[map
.size()];
893 Enumeration e
= map
.elements();
895 while (e
.hasMoreElements())
897 ret
[i
++] = (Action
) e
.nextElement();
902 public KeyStroke
[] getBoundKeyStrokes()
904 KeyStroke
[] ret
= new KeyStroke
[map
.size()];
905 Enumeration e
= map
.keys();
907 while (e
.hasMoreElements())
909 ret
[i
++] = (KeyStroke
) e
.nextElement();
914 public Action
getDefaultAction()
916 return defaultAction
;
919 public KeyStroke
[] getKeyStrokesForAction(Action a
)
922 Enumeration e
= map
.keys();
923 while (e
.hasMoreElements())
925 if (map
.get(e
.nextElement()).equals(a
))
928 KeyStroke
[] ret
= new KeyStroke
[i
];
931 while (e
.hasMoreElements())
933 KeyStroke k
= (KeyStroke
) e
.nextElement();
934 if (map
.get(k
).equals(a
))
940 public String
getName()
945 public Keymap
getResolveParent()
950 public boolean isLocallyDefined(KeyStroke key
)
952 return map
.containsKey(key
);
955 public void removeBindings()
960 public void removeKeyStrokeBinding(KeyStroke key
)
965 public void setDefaultAction(Action a
)
970 public void setResolveParent(Keymap p
)
976 class DefaultTransferHandler
extends TransferHandler
978 public boolean canImport(JComponent component
, DataFlavor
[] flavors
)
980 JTextComponent textComponent
= (JTextComponent
) component
;
982 if (! (textComponent
.isEnabled()
983 && textComponent
.isEditable()
987 for (int i
= 0; i
< flavors
.length
; ++i
)
988 if (flavors
[i
].equals(DataFlavor
.stringFlavor
))
994 public void exportToClipboard(JComponent component
, Clipboard clipboard
,
997 JTextComponent textComponent
= (JTextComponent
) component
;
998 int start
= textComponent
.getSelectionStart();
999 int end
= textComponent
.getSelectionEnd();
1006 // Copy text to clipboard.
1007 String data
= textComponent
.getDocument().getText(start
, end
);
1008 StringSelection selection
= new StringSelection(data
);
1009 clipboard
.setContents(selection
, null);
1011 // Delete selected text on cut action.
1013 doc
.remove(start
, end
- start
);
1015 catch (BadLocationException e
)
1017 // Ignore this and do nothing.
1021 public int getSourceActions()
1026 public boolean importData(JComponent component
, Transferable transferable
)
1028 DataFlavor flavor
= null;
1029 DataFlavor
[] flavors
= transferable
.getTransferDataFlavors();
1031 if (flavors
== null)
1034 for (int i
= 0; i
< flavors
.length
; ++i
)
1035 if (flavors
[i
].equals(DataFlavor
.stringFlavor
))
1036 flavor
= flavors
[i
];
1043 JTextComponent textComponent
= (JTextComponent
) component
;
1044 String data
= (String
) transferable
.getTransferData(flavor
);
1045 textComponent
.replaceSelection(data
);
1048 catch (IOException e
)
1052 catch (UnsupportedFlavorException e
)
1061 private static final long serialVersionUID
= -8796518220218978795L;
1063 public static final String DEFAULT_KEYMAP
= "default";
1064 public static final String FOCUS_ACCELERATOR_KEY
= "focusAcceleratorKey";
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
;
1073 * Get a Keymap from the global keymap table, by name.
1075 * @param n The name of the Keymap to look up
1077 * @return A Keymap associated with the provided name, or
1078 * <code>null</code> if no such Keymap exists
1081 * @see #removeKeymap
1084 public static Keymap
getKeymap(String n
)
1086 return (Keymap
) keymaps
.get(n
);
1090 * Remove a Keymap from the global Keymap table, by name.
1092 * @param n The name of the Keymap to remove
1094 * @return The keymap removed from the global table
1100 public static Keymap
removeKeymap(String n
)
1102 Keymap km
= (Keymap
) keymaps
.get(n
);
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
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>
1117 * @return The newly created Keymap
1119 * @see #removeKeymap
1123 public static Keymap
addKeymap(String n
, Keymap parent
)
1125 Keymap k
= new DefaultKeymap(n
);
1126 k
.setResolveParent(parent
);
1133 * Get the current Keymap of this component.
1135 * @return The component's current Keymap
1140 public Keymap
getKeymap()
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>.
1154 public void setKeymap(Keymap k
)
1157 // phase 1: replace the KeymapWrapper entry in the InputMap chain.
1158 // the goal here is to always maintain the following ordering:
1160 // [InputMap]? -> [KeymapWrapper]? -> [InputMapUIResource]*
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
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
);
1173 while (childInputMap
.getParent() != null
1174 && !(childInputMap
.getParent() instanceof KeymapWrapper
)
1175 && !(childInputMap
.getParent() instanceof InputMapUIResource
))
1176 childInputMap
= childInputMap
.getParent();
1178 // option 1: there is nobody to replace at the end of the chain
1179 if (childInputMap
.getParent() == null)
1180 childInputMap
.setParent(kw
);
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
)
1187 childInputMap
.setParent(childInputMap
.getParent().getParent());
1190 kw
.setParent(childInputMap
.getParent().getParent());
1191 childInputMap
.setParent(kw
);
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
)
1201 kw
.setParent(childInputMap
.getParent());
1202 childInputMap
.setParent(kw
);
1207 // phase 2: replace the KeymapActionMap entry in the ActionMap chain
1209 KeymapActionMap kam
= (k
== null ?
null : new KeymapActionMap(k
));
1210 ActionMap childActionMap
= getActionMap();
1211 if (childActionMap
== null)
1215 while (childActionMap
.getParent() != null
1216 && !(childActionMap
.getParent() instanceof KeymapActionMap
)
1217 && !(childActionMap
.getParent() instanceof ActionMapUIResource
))
1218 childActionMap
= childActionMap
.getParent();
1220 // option 1: there is nobody to replace at the end of the chain
1221 if (childActionMap
.getParent() == null)
1222 childActionMap
.setParent(kam
);
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
)
1229 childActionMap
.setParent(childActionMap
.getParent().getParent());
1232 kam
.setParent(childActionMap
.getParent().getParent());
1233 childActionMap
.setParent(kam
);
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
)
1243 kam
.setParent(childActionMap
.getParent());
1244 childActionMap
.setParent(kam
);
1249 // phase 3: update the explicit keymap field
1251 Keymap old
= keymap
;
1253 firePropertyChange("keymap", old
, k
);
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
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
1269 * @see Action#getValue
1270 * @see KeyBinding#actionName
1272 public static void loadKeymap(Keymap map
,
1273 JTextComponent
.KeyBinding
[] bindings
,
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
));
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.
1292 * @return The set of available Actions on this component's {@link EditorKit}
1294 * @see TextUI#getEditorKit
1295 * @see EditorKit#getActions()
1297 public Action
[] getActions()
1299 return getUI().getEditorKit(this).getActions();
1302 // These are package-private to avoid an accessor method.
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
;
1316 * Creates a new <code>JTextComponent</code> instance.
1318 public JTextComponent()
1320 Keymap defkeymap
= getKeymap(DEFAULT_KEYMAP
);
1321 if (defkeymap
== null)
1323 defkeymap
= addKeymap(DEFAULT_KEYMAP
, null);
1324 defkeymap
.setDefaultAction(new DefaultEditorKit
.DefaultKeyTypedAction());
1329 enableEvents(AWTEvent
.KEY_EVENT_MASK
);
1334 public void setDocument(Document newDoc
)
1336 Document oldDoc
= doc
;
1339 if (oldDoc
instanceof AbstractDocument
)
1340 ((AbstractDocument
) oldDoc
).readLock();
1343 firePropertyChange("document", oldDoc
, newDoc
);
1347 if (oldDoc
instanceof AbstractDocument
)
1348 ((AbstractDocument
) oldDoc
).readUnlock();
1354 public Document
getDocument()
1360 * Get the <code>AccessibleContext</code> of this object.
1362 * @return an <code>AccessibleContext</code> object
1364 public AccessibleContext
getAccessibleContext()
1366 return new AccessibleJTextComponent();
1369 public void setMargin(Insets m
)
1374 public Insets
getMargin()
1379 public void setText(String text
)
1383 if (doc
instanceof AbstractDocument
)
1384 ((AbstractDocument
) doc
).replace(0, doc
.getLength(), text
, null);
1387 doc
.remove(0, doc
.getLength());
1388 doc
.insertString(0, text
, null);
1391 catch (BadLocationException e
)
1393 // This can never happen.
1394 throw (InternalError
) new InternalError().initCause(e
);
1399 * Retrieves the current text in this text document.
1403 * @exception NullPointerException if the underlaying document is null
1405 public String
getText()
1412 return doc
.getText(0, doc
.getLength());
1414 catch (BadLocationException e
)
1416 // This should never happen.
1422 * Retrieves a part of the current text in this document.
1424 * @param offset the postion of the first character
1425 * @param length the length of the text to retrieve
1429 * @exception BadLocationException if arguments do not hold pre-conditions
1431 public String
getText(int offset
, int length
)
1432 throws BadLocationException
1434 return getDocument().getText(offset
, length
);
1438 * Retrieves the currently selected text in this text document.
1440 * @return the selected text
1442 * @exception NullPointerException if the underlaying document is null
1444 public String
getSelectedText()
1446 int start
= getSelectionStart();
1447 int offset
= getSelectionEnd() - start
;
1454 return doc
.getText(start
, offset
);
1456 catch (BadLocationException e
)
1458 // This should never happen.
1464 * Returns a string that specifies the name of the Look and Feel class
1465 * that renders this component.
1467 * @return the string "TextComponentUI"
1469 public String
getUIClassID()
1471 return "TextComponentUI";
1475 * Returns a string representation of this JTextComponent.
1477 protected String
paramString()
1479 // TODO: Do something useful here.
1480 return super.paramString();
1484 * This method returns the label's UI delegate.
1486 * @return The label's UI delegate.
1488 public TextUI
getUI()
1494 * This method sets the label's UI delegate.
1496 * @param newUI The label's UI delegate.
1498 public void setUI(TextUI newUI
)
1504 * This method resets the label's UI delegate to the default UI for the
1505 * current look and feel.
1507 public void updateUI()
1509 setUI((TextUI
) UIManager
.getUI(this));
1512 public Dimension
getPreferredScrollableViewportSize()
1514 return getPreferredSize();
1517 public int getScrollableUnitIncrement(Rectangle visible
, int orientation
,
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;
1526 throw new IllegalArgumentException("orientation must be either "
1527 + "javax.swing.SwingConstants.VERTICAL "
1529 + "javax.swing.SwingConstants.HORIZONTAL"
1533 public int getScrollableBlockIncrement(Rectangle visible
, int orientation
,
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
;
1542 throw new IllegalArgumentException("orientation must be either "
1543 + "javax.swing.SwingConstants.VERTICAL "
1545 + "javax.swing.SwingConstants.HORIZONTAL"
1550 * Checks whether this text component it editable.
1552 * @return true if editable, false otherwise
1554 public boolean isEditable()
1560 * Enables/disabled this text component's editability.
1562 * @param newValue true to make it editable, false otherwise.
1564 public void setEditable(boolean newValue
)
1566 if (editable
== newValue
)
1569 boolean oldValue
= editable
;
1570 editable
= newValue
;
1571 firePropertyChange("editable", oldValue
, newValue
);
1575 * The <code>Caret</code> object used in this text component.
1577 * @return the caret object
1579 public Caret
getCaret()
1585 * Sets a new <code>Caret</code> for this text component.
1587 * @param newCaret the new <code>Caret</code> to set
1589 public void setCaret(Caret newCaret
)
1592 caret
.deinstall(this);
1594 Caret oldCaret
= caret
;
1598 caret
.install(this);
1600 firePropertyChange("caret", oldCaret
, newCaret
);
1603 public Color
getCaretColor()
1608 public void setCaretColor(Color newColor
)
1610 Color oldCaretColor
= caretColor
;
1611 caretColor
= newColor
;
1612 firePropertyChange("caretColor", oldCaretColor
, newColor
);
1615 public Color
getDisabledTextColor()
1617 return disabledTextColor
;
1620 public void setDisabledTextColor(Color newColor
)
1622 Color oldColor
= disabledTextColor
;
1623 disabledTextColor
= newColor
;
1624 firePropertyChange("disabledTextColor", oldColor
, newColor
);
1627 public Color
getSelectedTextColor()
1629 return selectedTextColor
;
1632 public void setSelectedTextColor(Color newColor
)
1634 Color oldColor
= selectedTextColor
;
1635 selectedTextColor
= newColor
;
1636 firePropertyChange("selectedTextColor", oldColor
, newColor
);
1639 public Color
getSelectionColor()
1641 return selectionColor
;
1644 public void setSelectionColor(Color newColor
)
1646 Color oldColor
= selectionColor
;
1647 selectionColor
= newColor
;
1648 firePropertyChange("selectionColor", oldColor
, newColor
);
1652 * Retrisves the current caret position.
1654 * @return the current position
1656 public int getCaretPosition()
1658 return caret
.getDot();
1662 * Sets the caret to a new position.
1664 * @param position the new position
1666 public void setCaretPosition(int position
)
1671 if (position
< 0 || position
> doc
.getLength())
1672 throw new IllegalArgumentException();
1674 caret
.setDot(position
);
1678 * Moves the caret to a given position. This selects the text between
1679 * the old and the new position of the caret.
1681 public void moveCaretPosition(int position
)
1686 if (position
< 0 || position
> doc
.getLength())
1687 throw new IllegalArgumentException();
1689 caret
.moveDot(position
);
1692 public Highlighter
getHighlighter()
1697 public void setHighlighter(Highlighter newHighlighter
)
1699 if (highlighter
!= null)
1700 highlighter
.deinstall(this);
1702 Highlighter oldHighlighter
= highlighter
;
1703 highlighter
= newHighlighter
;
1705 if (highlighter
!= null)
1706 highlighter
.install(this);
1708 firePropertyChange("highlighter", oldHighlighter
, newHighlighter
);
1712 * Returns the start postion of the currently selected text.
1714 * @return the start postion
1716 public int getSelectionStart()
1718 return Math
.min(caret
.getDot(), caret
.getMark());
1722 * Selects the text from the given postion to the selection end position.
1724 * @param start the start positon of the selected text.
1726 public void setSelectionStart(int start
)
1728 select(start
, getSelectionEnd());
1732 * Returns the end postion of the currently selected text.
1734 * @return the end postion
1736 public int getSelectionEnd()
1738 return Math
.max(caret
.getDot(), caret
.getMark());
1742 * Selects the text from the selection start postion to the given position.
1744 * @param end the end positon of the selected text.
1746 public void setSelectionEnd(int end
)
1748 select(getSelectionStart(), end
);
1752 * Selects a part of the content of the text component.
1754 * @param start the start position of the selected text
1755 * @param end the end position of the selected text
1757 public void select(int start
, int end
)
1759 int length
= doc
.getLength();
1761 start
= Math
.max(start
, 0);
1762 start
= Math
.min(start
, length
);
1764 end
= Math
.max(end
, start
);
1765 end
= Math
.min(end
, length
);
1767 setCaretPosition(start
);
1768 moveCaretPosition(end
);
1772 * Selects the whole content of the text component.
1774 public void selectAll()
1776 select(0, doc
.getLength());
1779 public synchronized void replaceSelection(String content
)
1781 int dot
= caret
.getDot();
1782 int mark
= caret
.getMark();
1784 // If content is empty delete selection.
1785 if (content
== null)
1793 int start
= getSelectionStart();
1794 int end
= getSelectionEnd();
1796 // Remove selected text.
1798 doc
.remove(start
, end
- start
);
1801 doc
.insertString(start
, content
, null);
1803 // Set dot to new position,
1804 dot
= start
+ content
.length();
1805 setCaretPosition(dot
);
1807 // and update it's magic position.
1808 caret
.setMagicCaretPosition(modelToView(dot
).getLocation());
1810 catch (BadLocationException e
)
1812 // This should never happen.
1816 public boolean getScrollableTracksViewportHeight()
1818 if (getParent() instanceof JViewport
)
1819 return getParent().getHeight() > getPreferredSize().height
;
1824 public boolean getScrollableTracksViewportWidth()
1826 boolean res
= false;
1827 Container c
= getParent();
1828 if (c
instanceof JViewport
)
1829 res
= ((JViewport
) c
).getExtentSize().width
> getPreferredSize().width
;
1835 * Adds a <code>CaretListener</code> object to this text component.
1837 * @param listener the listener to add
1839 public void addCaretListener(CaretListener listener
)
1841 listenerList
.add(CaretListener
.class, listener
);
1845 * Removed a <code>CaretListener</code> object from this text component.
1847 * @param listener the listener to remove
1849 public void removeCaretListener(CaretListener listener
)
1851 listenerList
.remove(CaretListener
.class, listener
);
1855 * Returns all added <code>CaretListener</code> objects.
1857 * @return an array of listeners
1859 public CaretListener
[] getCaretListeners()
1861 return (CaretListener
[]) getListeners(CaretListener
.class);
1865 * Notifies all registered <code>CaretListener</code> objects that the caret
1868 * @param event the event to send
1870 protected void fireCaretUpdate(CaretEvent event
)
1872 CaretListener
[] listeners
= getCaretListeners();
1874 for (int index
= 0; index
< listeners
.length
; ++index
)
1875 listeners
[index
].caretUpdate(event
);
1879 * Adds an <code>InputListener</code> object to this text component.
1881 * @param listener the listener to add
1883 public void addInputMethodListener(InputMethodListener listener
)
1885 listenerList
.add(InputMethodListener
.class, listener
);
1889 * Removes an <code>InputListener</code> object from this text component.
1891 * @param listener the listener to remove
1893 public void removeInputMethodListener(InputMethodListener listener
)
1895 listenerList
.remove(InputMethodListener
.class, listener
);
1899 * Returns all added <code>InputMethodListener</code> objects.
1901 * @return an array of listeners
1903 public InputMethodListener
[] getInputMethodListeners()
1905 return (InputMethodListener
[]) getListeners(InputMethodListener
.class);
1908 public Rectangle
modelToView(int position
) throws BadLocationException
1910 return getUI().modelToView(this, position
);
1913 public boolean getDragEnabled()
1918 public void setDragEnabled(boolean enabled
)
1920 dragEnabled
= enabled
;
1923 public int viewToModel(Point pt
)
1925 return getUI().viewToModel(this, pt
);
1931 doTransferAction("copy", TransferHandler
.getCopyAction());
1936 if (editable
&& isEnabled())
1937 doTransferAction("cut", TransferHandler
.getCutAction());
1942 if (editable
&& isEnabled())
1943 doTransferAction("paste", TransferHandler
.getPasteAction());
1946 private void doTransferAction(String name
, Action action
)
1948 // Install default TransferHandler if none set.
1949 if (getTransferHandler() == null)
1951 if (defaultTransferHandler
== null)
1952 defaultTransferHandler
= new DefaultTransferHandler();
1954 setTransferHandler(defaultTransferHandler
);
1958 ActionEvent event
= new ActionEvent(this, ActionEvent
.ACTION_PERFORMED
,
1959 action
.getValue(Action
.NAME
).toString());
1960 action
.actionPerformed(event
);
1963 public void setFocusAccelerator(char newKey
)
1965 if (focusAccelerator
== newKey
)
1968 char oldKey
= focusAccelerator
;
1969 focusAccelerator
= newKey
;
1970 firePropertyChange(FOCUS_ACCELERATOR_KEY
, oldKey
, newKey
);
1973 public char getFocusAccelerator()
1975 return focusAccelerator
;
1981 public NavigationFilter
getNavigationFilter()
1983 return navigationFilter
;
1989 public void setNavigationFilter(NavigationFilter filter
)
1991 navigationFilter
= filter
;
1995 * Read and set the content this component. If not overridden, the
1996 * method reads the component content as a plain text.
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}.
2003 * @param input an input stream to read from.
2004 * @param streamDescription an object, describing the stream.
2006 * @throws IOException if the reader throws it.
2008 * @see #getDocument()
2009 * @see Document#getProperty(Object)
2011 public void read(Reader input
, Object streamDescription
)
2014 if (streamDescription
!= null)
2016 Document d
= getDocument();
2018 d
.putProperty(Document
.StreamDescriptionProperty
, streamDescription
);
2021 CPStringBuilder b
= new CPStringBuilder();
2024 // Read till -1 (EOF).
2025 while ((c
= input
.read()) >= 0)
2028 setText(b
.toString());
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.
2035 * @param output the writer to write into.
2037 * @throws IOException if the writer throws it.
2039 public void write(Writer output
)
2042 output
.write(getText());
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)}.
2050 * @param ev the mouse event
2052 * @return the tooltip text for this text component for the given mouse
2055 public String
getToolTipText(MouseEvent ev
)
2057 return getUI().getToolTipText(this, ev
.getPoint());