libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / javax / swing / text / JTextComponent.java
blob24035e35ac2f9e3d42842e9ecf7d732d638a0079
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)
9 any later version.
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
19 02110-1301 USA.
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.
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
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.
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()
118 super();
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
150 * position.
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("")))
159 return 0;
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
168 * position.
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();
188 if (caretDot != dot)
190 firePropertyChange(ACCESSIBLE_CARET_PROPERTY, new Integer(caretDot),
191 new Integer(dot));
192 caretDot = dot;
194 if (mark != dot)
196 firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
197 getSelectedText());
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();
209 if (isEditable())
210 state.add(AccessibleState.EDITABLE);
211 return state;
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
228 * text component.
230 * @return this
232 public AccessibleEditableText getAccessibleEditableText()
234 return this;
238 * Retrieve an AccessibleText object that controls this text
239 * component.
241 * @return this
243 * @see AccessibleText
245 public AccessibleText getAccessibleText()
247 return this;
251 * Handle a text insertion event and fire an
252 * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
253 * event.
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
266 * event.
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
279 * event.
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
292 * method returns -1.
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();
324 TextUI ui = getUI();
325 if (ui != null)
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)
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();
348 catch (BadLocationException ex)
350 // Ignore (return null).
352 finally
354 if (doc instanceof AbstractDocument)
355 ((AbstractDocument) doc).readUnlock();
358 return bounds;
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();
371 /**
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)
381 AttributeSet atts;
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();
394 finally
396 if (doc instanceof AbstractDocument)
397 ((AbstractDocument) doc).readUnlock();
399 return atts;
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)
455 String ret = null;
456 if (doc instanceof AbstractDocument)
457 ((AbstractDocument) doc).readLock();
460 BreakIterator iter = null;
461 switch (part)
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;
475 String text = doc.getText(0, doc.getLength() - 1);
476 iter.setText(text);
477 int start = index;
478 int end = index;
479 switch (dir)
481 case 0:
482 if (iter.isBoundary(index))
484 start = index;
485 end = iter.following(index);
487 else
489 start = iter.preceding(index);
490 end = iter.next();
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;
504 ret = text.substring(start, end);
506 catch (BadLocationException ex)
508 // Ignore (return null).
510 finally
512 if (doc instanceof AbstractDocument)
513 ((AbstractDocument) doc).readUnlock();
515 return ret;
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)
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;
547 * Performs the i-th action. Nothing happens if i is
548 * out of bounds.
550 * @param i - the action to perform
552 * @return true if the action was performed successfully
554 public boolean doAccessibleAction(int i)
556 boolean ret = false;
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);
563 ret = true;
565 return ret;
569 * Sets the text contents.
571 * @param s - the new text contents.
573 public void setTextContents(String s)
575 setText(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)
611 return "";
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)
705 this.key = key;
706 this.actionName = actionName;
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.
718 * <p>A little bit of experimentation with these classes reveals the following
719 * structure:
721 * <ul>
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
731 * its keys. </li>
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.
738 * </ul>
741 private class KeymapWrapper extends InputMap
743 Keymap map;
745 public KeymapWrapper(Keymap k)
747 map = k;
750 public int size()
752 return map.getBoundKeyStrokes().length + super.size();
755 public Object get(KeyStroke ks)
757 Action mapped = null;
758 Keymap m = map;
759 while(mapped == null && m != null)
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();
768 if (mapped == null)
769 return super.get(ks);
770 else
771 return mapped;
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];
783 return bothKeys;
786 public KeyStroke[] allKeys()
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;
805 private class KeymapActionMap extends ActionMap
807 Keymap map;
809 public KeymapActionMap(Keymap k)
811 map = k;
814 public Action get(Object cmd)
816 if (cmd instanceof Action)
817 return (Action) cmd;
818 else
819 return super.get(cmd);
822 public int size()
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];
836 return bothKeys;
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];
848 return bothKeys;
853 static class DefaultKeymap implements Keymap
855 String name;
856 Keymap parent;
857 Hashtable map;
858 Action defaultAction;
860 public DefaultKeymap(String name)
862 this.name = name;
863 this.map = new Hashtable();
866 public void addActionForKeyStroke(KeyStroke key, Action a)
868 map.put(key, 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);
886 else
887 return null;
890 public Action[] getBoundActions()
892 Action [] ret = new Action[map.size()];
893 Enumeration e = map.elements();
894 int i = 0;
895 while (e.hasMoreElements())
897 ret[i++] = (Action) e.nextElement();
899 return ret;
902 public KeyStroke[] getBoundKeyStrokes()
904 KeyStroke [] ret = new KeyStroke[map.size()];
905 Enumeration e = map.keys();
906 int i = 0;
907 while (e.hasMoreElements())
909 ret[i++] = (KeyStroke) e.nextElement();
911 return ret;
914 public Action getDefaultAction()
916 return defaultAction;
919 public KeyStroke[] getKeyStrokesForAction(Action a)
921 int i = 0;
922 Enumeration e = map.keys();
923 while (e.hasMoreElements())
925 if (map.get(e.nextElement()).equals(a))
926 ++i;
928 KeyStroke [] ret = new KeyStroke[i];
929 i = 0;
930 e = map.keys();
931 while (e.hasMoreElements())
933 KeyStroke k = (KeyStroke) e.nextElement();
934 if (map.get(k).equals(a))
935 ret[i++] = k;
937 return ret;
940 public String getName()
942 return name;
945 public Keymap getResolveParent()
947 return parent;
950 public boolean isLocallyDefined(KeyStroke key)
952 return map.containsKey(key);
955 public void removeBindings()
957 map.clear();
960 public void removeKeyStrokeBinding(KeyStroke key)
962 map.remove(key);
965 public void setDefaultAction(Action a)
967 defaultAction = a;
970 public void setResolveParent(Keymap p)
972 parent = 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()
984 && flavors != null))
985 return false;
987 for (int i = 0; i < flavors.length; ++i)
988 if (flavors[i].equals(DataFlavor.stringFlavor))
989 return true;
991 return false;
994 public void exportToClipboard(JComponent component, Clipboard clipboard,
995 int action)
997 JTextComponent textComponent = (JTextComponent) component;
998 int start = textComponent.getSelectionStart();
999 int end = textComponent.getSelectionEnd();
1001 if (start == end)
1002 return;
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.
1012 if (action == MOVE)
1013 doc.remove(start, end - start);
1015 catch (BadLocationException e)
1017 // Ignore this and do nothing.
1021 public int getSourceActions()
1023 return NONE;
1026 public boolean importData(JComponent component, Transferable transferable)
1028 DataFlavor flavor = null;
1029 DataFlavor[] flavors = transferable.getTransferDataFlavors();
1031 if (flavors == null)
1032 return false;
1034 for (int i = 0; i < flavors.length; ++i)
1035 if (flavors[i].equals(DataFlavor.stringFlavor))
1036 flavor = flavors[i];
1038 if (flavor == null)
1039 return false;
1043 JTextComponent textComponent = (JTextComponent) component;
1044 String data = (String) transferable.getTransferData(flavor);
1045 textComponent.replaceSelection(data);
1046 return true;
1048 catch (IOException e)
1050 // Ignored.
1052 catch (UnsupportedFlavorException e)
1054 // Ignored.
1057 return false;
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
1080 * @see #addKeymap
1081 * @see #removeKeymap
1082 * @see #keymaps
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
1096 * @see #addKeymap
1097 * @see #getKeymap()
1098 * @see #keymaps
1100 public static Keymap removeKeymap(String n)
1102 Keymap km = (Keymap) keymaps.get(n);
1103 keymaps.remove(n);
1104 return km;
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.
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
1120 * @see #getKeymap()
1121 * @see #keymaps
1123 public static Keymap addKeymap(String n, Keymap parent)
1125 Keymap k = new DefaultKeymap(n);
1126 k.setResolveParent(parent);
1127 if (n != null)
1128 keymaps.put(n, k);
1129 return k;
1133 * Get the current Keymap of this component.
1135 * @return The component's current Keymap
1137 * @see #setKeymap
1138 * @see #keymap
1140 public Keymap getKeymap()
1142 return keymap;
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>.
1151 * @see #getKeymap()
1152 * @see #keymap
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
1165 // course).
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
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)
1186 if (kw == null)
1187 childInputMap.setParent(childInputMap.getParent().getParent());
1188 else
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)
1199 if (kw != null)
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)
1212 setActionMap(kam);
1213 else
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)
1228 if (kam == null)
1229 childActionMap.setParent(childActionMap.getParent().getParent());
1230 else
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)
1241 if (kam != null)
1243 kam.setParent(childActionMap.getParent());
1244 childActionMap.setParent(kam);
1249 // phase 3: update the explicit keymap field
1251 Keymap old = keymap;
1252 keymap = k;
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
1262 * <code>a</code>.
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
1268 * @see Action#NAME
1269 * @see Action#getValue
1270 * @see KeyBinding#actionName
1272 public static void loadKeymap(Keymap map,
1273 JTextComponent.KeyBinding[] bindings,
1274 Action[] actions)
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.
1303 Document doc;
1304 Caret caret;
1305 boolean editable;
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());
1327 setFocusable(true);
1328 setEditable(true);
1329 enableEvents(AWTEvent.KEY_EVENT_MASK);
1330 setOpaque(true);
1331 updateUI();
1334 public void setDocument(Document newDoc)
1336 Document oldDoc = doc;
1339 if (oldDoc instanceof AbstractDocument)
1340 ((AbstractDocument) oldDoc).readLock();
1342 doc = newDoc;
1343 firePropertyChange("document", oldDoc, newDoc);
1345 finally
1347 if (oldDoc instanceof AbstractDocument)
1348 ((AbstractDocument) oldDoc).readUnlock();
1350 revalidate();
1351 repaint();
1354 public Document getDocument()
1356 return doc;
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)
1371 margin = m;
1374 public Insets getMargin()
1376 return margin;
1379 public void setText(String text)
1383 if (doc instanceof AbstractDocument)
1384 ((AbstractDocument) doc).replace(0, doc.getLength(), text, null);
1385 else
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.
1401 * @return the text
1403 * @exception NullPointerException if the underlaying document is null
1405 public String getText()
1407 if (doc == null)
1408 return null;
1412 return doc.getText(0, doc.getLength());
1414 catch (BadLocationException e)
1416 // This should never happen.
1417 return "";
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
1427 * @return the text
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;
1449 if (offset <= 0)
1450 return null;
1454 return doc.getText(start, offset);
1456 catch (BadLocationException e)
1458 // This should never happen.
1459 return null;
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()
1490 return (TextUI) ui;
1494 * This method sets the label's UI delegate.
1496 * @param newUI The label's UI delegate.
1498 public void setUI(TextUI newUI)
1500 super.setUI(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,
1518 int direction)
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"
1533 public int getScrollableBlockIncrement(Rectangle visible, int orientation,
1534 int direction)
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"
1550 * Checks whether this text component it editable.
1552 * @return true if editable, false otherwise
1554 public boolean isEditable()
1556 return editable;
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)
1567 return;
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()
1581 return caret;
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)
1591 if (caret != null)
1592 caret.deinstall(this);
1594 Caret oldCaret = caret;
1595 caret = newCaret;
1597 if (caret != null)
1598 caret.install(this);
1600 firePropertyChange("caret", oldCaret, newCaret);
1603 public Color getCaretColor()
1605 return caretColor;
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)
1668 if (doc == null)
1669 return;
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)
1683 if (doc == null)
1684 return;
1686 if (position < 0 || position > doc.getLength())
1687 throw new IllegalArgumentException();
1689 caret.moveDot(position);
1692 public Highlighter getHighlighter()
1694 return highlighter;
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)
1787 caret.setDot(dot);
1788 return;
1793 int start = getSelectionStart();
1794 int end = getSelectionEnd();
1796 // Remove selected text.
1797 if (dot != mark)
1798 doc.remove(start, end - start);
1800 // Insert new text.
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;
1821 return false;
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;
1831 return res;
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
1866 * was updated.
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()
1915 return dragEnabled;
1918 public void setDragEnabled(boolean enabled)
1920 dragEnabled = enabled;
1923 public int viewToModel(Point pt)
1925 return getUI().viewToModel(this, pt);
1928 public void copy()
1930 if (isEnabled())
1931 doTransferAction("copy", TransferHandler.getCopyAction());
1934 public void cut()
1936 if (editable && isEnabled())
1937 doTransferAction("cut", TransferHandler.getCutAction());
1940 public void paste()
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);
1957 // Perform action.
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)
1966 return;
1968 char oldKey = focusAccelerator;
1969 focusAccelerator = newKey;
1970 firePropertyChange(FOCUS_ACCELERATOR_KEY, oldKey, newKey);
1973 public char getFocusAccelerator()
1975 return focusAccelerator;
1979 * @since 1.4
1981 public NavigationFilter getNavigationFilter()
1983 return navigationFilter;
1987 * @since 1.4
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)
2012 throws IOException
2014 if (streamDescription != null)
2016 Document d = getDocument();
2017 if (d != null)
2018 d.putProperty(Document.StreamDescriptionProperty, streamDescription);
2021 CPStringBuilder b = new CPStringBuilder();
2022 int c;
2024 // Read till -1 (EOF).
2025 while ((c = input.read()) >= 0)
2026 b.append((char) c);
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)
2040 throws IOException
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
2053 * event
2055 public String getToolTipText(MouseEvent ev)
2057 return getUI().getToolTipText(this, ev.getPoint());