summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/text/JTextComponent.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/text/JTextComponent.java')
-rw-r--r--libjava/classpath/javax/swing/text/JTextComponent.java2059
1 files changed, 2059 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/text/JTextComponent.java b/libjava/classpath/javax/swing/text/JTextComponent.java
new file mode 100644
index 000000000..a118cf86d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/JTextComponent.java
@@ -0,0 +1,2059 @@
+/* JTextComponent.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.AWTEvent;
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputMethodListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.text.BreakIterator;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleAction;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleEditableText;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleText;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JViewport;
+import javax.swing.KeyStroke;
+import javax.swing.Scrollable;
+import javax.swing.SwingConstants;
+import javax.swing.TransferHandler;
+import javax.swing.UIManager;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.InputMapUIResource;
+import javax.swing.plaf.TextUI;
+
+public abstract class JTextComponent extends JComponent
+ implements Scrollable, Accessible
+{
+ /**
+ * AccessibleJTextComponent implements accessibility hooks for
+ * JTextComponent. It allows an accessibility driver to read and
+ * manipulate the text component's contents as well as update UI
+ * elements such as the caret.
+ */
+ public class AccessibleJTextComponent extends AccessibleJComponent implements
+ AccessibleText, CaretListener, DocumentListener, AccessibleAction,
+ AccessibleEditableText
+ {
+ private static final long serialVersionUID = 7664188944091413696L;
+
+ /**
+ * The caret's offset.
+ */
+ private int caretDot;
+
+ /**
+ * Construct an AccessibleJTextComponent.
+ */
+ public AccessibleJTextComponent()
+ {
+ super();
+ JTextComponent.this.addCaretListener(this);
+ caretDot = getCaretPosition();
+ }
+
+ /**
+ * Retrieve the current caret position. The index of the first
+ * caret position is 0.
+ *
+ * @return caret position
+ */
+ public int getCaretPosition()
+ {
+ return JTextComponent.this.getCaretPosition();
+ }
+
+ /**
+ * Retrieve the current text selection. If no text is selected
+ * this method returns null.
+ *
+ * @return the currently selected text or null
+ */
+ public String getSelectedText()
+ {
+ return JTextComponent.this.getSelectedText();
+ }
+
+ /**
+ * Retrieve the index of the first character in the current text
+ * selection. If there is no text in the text component, this
+ * method returns 0. If there is text in the text component, but
+ * there is no selection, this method returns the current caret
+ * position.
+ *
+ * @return the index of the first character in the selection, the
+ * current caret position or 0
+ */
+ public int getSelectionStart()
+ {
+ if (getSelectedText() == null
+ || (JTextComponent.this.getText().equals("")))
+ return 0;
+ return JTextComponent.this.getSelectionStart();
+ }
+
+ /**
+ * Retrieve the index of the last character in the current text
+ * selection. If there is no text in the text component, this
+ * method returns 0. If there is text in the text component, but
+ * there is no selection, this method returns the current caret
+ * position.
+ *
+ * @return the index of the last character in the selection, the
+ * current caret position or 0
+ */
+ public int getSelectionEnd()
+ {
+ return JTextComponent.this.getSelectionEnd();
+ }
+
+ /**
+ * Handle a change in the caret position and fire any applicable
+ * property change events.
+ *
+ * @param e - the caret update event
+ */
+ public void caretUpdate(CaretEvent e)
+ {
+ int dot = e.getDot();
+ int mark = e.getMark();
+ if (caretDot != dot)
+ {
+ firePropertyChange(ACCESSIBLE_CARET_PROPERTY, new Integer(caretDot),
+ new Integer(dot));
+ caretDot = dot;
+ }
+ if (mark != dot)
+ {
+ firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
+ getSelectedText());
+ }
+ }
+
+ /**
+ * Retreive the accessible state set of this component.
+ *
+ * @return the accessible state set of this component
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = super.getAccessibleStateSet();
+ if (isEditable())
+ state.add(AccessibleState.EDITABLE);
+ return state;
+ }
+
+ /**
+ * Retrieve the accessible role of this component.
+ *
+ * @return the accessible role of this component
+ *
+ * @see AccessibleRole
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TEXT;
+ }
+
+ /**
+ * Retrieve an AccessibleEditableText object that controls this
+ * text component.
+ *
+ * @return this
+ */
+ public AccessibleEditableText getAccessibleEditableText()
+ {
+ return this;
+ }
+
+ /**
+ * Retrieve an AccessibleText object that controls this text
+ * component.
+ *
+ * @return this
+ *
+ * @see AccessibleText
+ */
+ public AccessibleText getAccessibleText()
+ {
+ return this;
+ }
+
+ /**
+ * Handle a text insertion event and fire an
+ * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
+ * event.
+ *
+ * @param e - the insertion event
+ */
+ public void insertUpdate(DocumentEvent e)
+ {
+ firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+ new Integer(e.getOffset()));
+ }
+
+ /**
+ * Handle a text removal event and fire an
+ * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
+ * event.
+ *
+ * @param e - the removal event
+ */
+ public void removeUpdate(DocumentEvent e)
+ {
+ firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+ new Integer(e.getOffset()));
+ }
+
+ /**
+ * Handle a text change event and fire an
+ * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
+ * event.
+ *
+ * @param e - text change event
+ */
+ public void changedUpdate(DocumentEvent e)
+ {
+ firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+ new Integer(e.getOffset()));
+ }
+
+ /**
+ * Get the index of the character at the given point, in component
+ * pixel co-ordinates. If the point argument is invalid this
+ * method returns -1.
+ *
+ * @param p - a point in component pixel co-ordinates
+ *
+ * @return a character index, or -1
+ */
+ public int getIndexAtPoint(Point p)
+ {
+ return viewToModel(p);
+ }
+
+ /**
+ * Calculate the bounding box of the character at the given index.
+ * The returned x and y co-ordinates are relative to this text
+ * component's top-left corner. If the index is invalid this
+ * method returns null.
+ *
+ * @param index - the character index
+ *
+ * @return a character's bounding box, or null
+ */
+ public Rectangle getCharacterBounds(int index)
+ {
+ // This is basically the same as BasicTextUI.modelToView().
+
+ Rectangle bounds = null;
+ if (index >= 0 && index < doc.getLength() - 1)
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ TextUI ui = getUI();
+ if (ui != null)
+ {
+ // Get editor rectangle.
+ Rectangle rect = new Rectangle();
+ Insets insets = getInsets();
+ rect.x = insets.left;
+ rect.y = insets.top;
+ rect.width = getWidth() - insets.left - insets.right;
+ rect.height = getHeight() - insets.top - insets.bottom;
+ View rootView = ui.getRootView(JTextComponent.this);
+ if (rootView != null)
+ {
+ rootView.setSize(rect.width, rect.height);
+ Shape s = rootView.modelToView(index,
+ Position.Bias.Forward,
+ index + 1,
+ Position.Bias.Backward,
+ rect);
+ if (s != null)
+ bounds = s.getBounds();
+ }
+ }
+ }
+ catch (BadLocationException ex)
+ {
+ // Ignore (return null).
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ }
+ return bounds;
+ }
+
+ /**
+ * Return the length of the text in this text component.
+ *
+ * @return a character length
+ */
+ public int getCharCount()
+ {
+ return JTextComponent.this.getText().length();
+ }
+
+ /**
+ * Gets the character attributes of the character at index. If
+ * the index is out of bounds, null is returned.
+ *
+ * @param index - index of the character
+ *
+ * @return the character's attributes
+ */
+ public AttributeSet getCharacterAttribute(int index)
+ {
+ AttributeSet atts;
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ Element el = doc.getDefaultRootElement();
+ while (! el.isLeaf())
+ {
+ int i = el.getElementIndex(index);
+ el = el.getElement(i);
+ }
+ atts = el.getAttributes();
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return atts;
+ }
+
+ /**
+ * Gets the text located at index. null is returned if the index
+ * or part is invalid.
+ *
+ * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
+ * @param index - index of the part
+ *
+ * @return the part of text at that index, or null
+ */
+ public String getAtIndex(int part, int index)
+ {
+ return getAtIndexImpl(part, index, 0);
+ }
+
+ /**
+ * Gets the text located after index. null is returned if the index
+ * or part is invalid.
+ *
+ * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
+ * @param index - index after the part
+ *
+ * @return the part of text after that index, or null
+ */
+ public String getAfterIndex(int part, int index)
+ {
+ return getAtIndexImpl(part, index, 1);
+ }
+
+ /**
+ * Gets the text located before index. null is returned if the index
+ * or part is invalid.
+ *
+ * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
+ * @param index - index before the part
+ *
+ * @return the part of text before that index, or null
+ */
+ public String getBeforeIndex(int part, int index)
+ {
+ return getAtIndexImpl(part, index, -1);
+ }
+
+ /**
+ * Implements getAtIndex(), getBeforeIndex() and getAfterIndex().
+ *
+ * @param part the part to return, either CHARACTER, WORD or SENTENCE
+ * @param index the index
+ * @param dir the direction, -1 for backwards, 0 for here, +1 for forwards
+ *
+ * @return the resulting string
+ */
+ private String getAtIndexImpl(int part, int index, int dir)
+ {
+ String ret = null;
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ BreakIterator iter = null;
+ switch (part)
+ {
+ case CHARACTER:
+ iter = BreakIterator.getCharacterInstance(getLocale());
+ break;
+ case WORD:
+ iter = BreakIterator.getWordInstance(getLocale());
+ break;
+ case SENTENCE:
+ iter = BreakIterator.getSentenceInstance(getLocale());
+ break;
+ default:
+ break;
+ }
+ String text = doc.getText(0, doc.getLength() - 1);
+ iter.setText(text);
+ int start = index;
+ int end = index;
+ switch (dir)
+ {
+ case 0:
+ if (iter.isBoundary(index))
+ {
+ start = index;
+ end = iter.following(index);
+ }
+ else
+ {
+ start = iter.preceding(index);
+ end = iter.next();
+ }
+ break;
+ case 1:
+ start = iter.following(index);
+ end = iter.next();
+ break;
+ case -1:
+ end = iter.preceding(index);
+ start = iter.previous();
+ break;
+ default:
+ assert false;
+ }
+ ret = text.substring(start, end);
+ }
+ catch (BadLocationException ex)
+ {
+ // Ignore (return null).
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the number of actions for this object. The zero-th
+ * object represents the default action.
+ *
+ * @return the number of actions (0-based).
+ */
+ public int getAccessibleActionCount()
+ {
+ return getActions().length;
+ }
+
+ /**
+ * Returns the description of the i-th action. Null is returned if
+ * i is out of bounds.
+ *
+ * @param i - the action to get the description for
+ *
+ * @return description of the i-th action
+ */
+ public String getAccessibleActionDescription(int i)
+ {
+ String desc = null;
+ Action[] actions = getActions();
+ if (i >= 0 && i < actions.length)
+ desc = (String) actions[i].getValue(Action.NAME);
+ return desc;
+ }
+
+ /**
+ * Performs the i-th action. Nothing happens if i is
+ * out of bounds.
+ *
+ * @param i - the action to perform
+ *
+ * @return true if the action was performed successfully
+ */
+ public boolean doAccessibleAction(int i)
+ {
+ boolean ret = false;
+ Action[] actions = getActions();
+ if (i >= 0 && i < actions.length)
+ {
+ ActionEvent ev = new ActionEvent(JTextComponent.this,
+ ActionEvent.ACTION_PERFORMED, null);
+ actions[i].actionPerformed(ev);
+ ret = true;
+ }
+ return ret;
+ }
+
+ /**
+ * Sets the text contents.
+ *
+ * @param s - the new text contents.
+ */
+ public void setTextContents(String s)
+ {
+ setText(s);
+ }
+
+ /**
+ * Inserts the text at the given index.
+ *
+ * @param index - the index to insert the new text at.
+ * @param s - the new text
+ */
+ public void insertTextAtIndex(int index, String s)
+ {
+ try
+ {
+ doc.insertString(index, s, null);
+ }
+ catch (BadLocationException ex)
+ {
+ // What should we do with this?
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Gets the text between two indexes.
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ */
+ public String getTextRange(int start, int end)
+ {
+ try
+ {
+ return JTextComponent.this.getText(start, end - start);
+ }
+ catch (BadLocationException ble)
+ {
+ return "";
+ }
+ }
+
+ /**
+ * Deletes the text between two indexes.
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ */
+ public void delete(int start, int end)
+ {
+ replaceText(start, end, "");
+ }
+
+ /**
+ * Cuts the text between two indexes. The text is put
+ * into the system clipboard.
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ */
+ public void cut(int start, int end)
+ {
+ JTextComponent.this.select(start, end);
+ JTextComponent.this.cut();
+ }
+
+ /**
+ * Pastes the text from the system clipboard to the given index.
+ *
+ * @param start - the starting index
+ */
+ public void paste(int start)
+ {
+ JTextComponent.this.setCaretPosition(start);
+ JTextComponent.this.paste();
+ }
+
+ /**
+ * Replaces the text between two indexes with the given text.
+ *
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ * @param s - the text to paste
+ */
+ public void replaceText(int start, int end, String s)
+ {
+ JTextComponent.this.select(start, end);
+ JTextComponent.this.replaceSelection(s);
+ }
+
+ /**
+ * Selects the text between two indexes.
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ */
+ public void selectText(int start, int end)
+ {
+ JTextComponent.this.select(start, end);
+ }
+
+ /**
+ * Sets the attributes of all the text between two indexes.
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ * @param s - the new attribute set for the text in the range
+ */
+ public void setAttributes(int start, int end, AttributeSet s)
+ {
+ if (doc instanceof StyledDocument)
+ {
+ StyledDocument sdoc = (StyledDocument) doc;
+ sdoc.setCharacterAttributes(start, end - start, s, true);
+ }
+ }
+ }
+
+ public static class KeyBinding
+ {
+ public KeyStroke key;
+ public String actionName;
+
+ /**
+ * Creates a new <code>KeyBinding</code> instance.
+ *
+ * @param key a <code>KeyStroke</code> value
+ * @param actionName a <code>String</code> value
+ */
+ public KeyBinding(KeyStroke key, String actionName)
+ {
+ this.key = key;
+ this.actionName = actionName;
+ }
+ }
+
+ /**
+ * According to <a
+ * href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">this
+ * report</a>, a pair of private classes wraps a {@link
+ * javax.swing.text.Keymap} in the new {@link InputMap} / {@link
+ * ActionMap} interfaces, such that old Keymap-using code can make use of
+ * the new framework.
+ *
+ * <p>A little bit of experimentation with these classes reveals the following
+ * structure:
+ *
+ * <ul>
+ *
+ * <li>KeymapWrapper extends {@link InputMap} and holds a reference to
+ * the underlying {@link Keymap}.</li>
+ *
+ * <li>KeymapWrapper maps {@link KeyStroke} objects to {@link Action}
+ * objects, by delegation to the underlying {@link Keymap}.</li>
+ *
+ * <li>KeymapActionMap extends {@link ActionMap} also holds a reference to
+ * the underlying {@link Keymap} but only appears to use it for listing
+ * its keys. </li>
+ *
+ * <li>KeymapActionMap maps all {@link Action} objects to
+ * <em>themselves</em>, whether they exist in the underlying {@link
+ * Keymap} or not, and passes other objects to the parent {@link
+ * ActionMap} for resolving.
+ *
+ * </ul>
+ */
+
+ private class KeymapWrapper extends InputMap
+ {
+ Keymap map;
+
+ public KeymapWrapper(Keymap k)
+ {
+ map = k;
+ }
+
+ public int size()
+ {
+ return map.getBoundKeyStrokes().length + super.size();
+ }
+
+ public Object get(KeyStroke ks)
+ {
+ Action mapped = null;
+ Keymap m = map;
+ while(mapped == null && m != null)
+ {
+ mapped = m.getAction(ks);
+ if (mapped == null && ks.getKeyEventType() == KeyEvent.KEY_TYPED)
+ mapped = m.getDefaultAction();
+ if (mapped == null)
+ m = m.getResolveParent();
+ }
+
+ if (mapped == null)
+ return super.get(ks);
+ else
+ return mapped;
+ }
+
+ public KeyStroke[] keys()
+ {
+ KeyStroke[] superKeys = super.keys();
+ KeyStroke[] mapKeys = map.getBoundKeyStrokes();
+ KeyStroke[] bothKeys = new KeyStroke[superKeys.length + mapKeys.length];
+ for (int i = 0; i < superKeys.length; ++i)
+ bothKeys[i] = superKeys[i];
+ for (int i = 0; i < mapKeys.length; ++i)
+ bothKeys[i + superKeys.length] = mapKeys[i];
+ return bothKeys;
+ }
+
+ public KeyStroke[] allKeys()
+ {
+ KeyStroke[] superKeys = super.allKeys();
+ KeyStroke[] mapKeys = map.getBoundKeyStrokes();
+ int skl = 0;
+ int mkl = 0;
+ if (superKeys != null)
+ skl = superKeys.length;
+ if (mapKeys != null)
+ mkl = mapKeys.length;
+ KeyStroke[] bothKeys = new KeyStroke[skl + mkl];
+ for (int i = 0; i < skl; ++i)
+ bothKeys[i] = superKeys[i];
+ for (int i = 0; i < mkl; ++i)
+ bothKeys[i + skl] = mapKeys[i];
+ return bothKeys;
+ }
+ }
+
+ private class KeymapActionMap extends ActionMap
+ {
+ Keymap map;
+
+ public KeymapActionMap(Keymap k)
+ {
+ map = k;
+ }
+
+ public Action get(Object cmd)
+ {
+ if (cmd instanceof Action)
+ return (Action) cmd;
+ else
+ return super.get(cmd);
+ }
+
+ public int size()
+ {
+ return map.getBoundKeyStrokes().length + super.size();
+ }
+
+ public Object[] keys()
+ {
+ Object[] superKeys = super.keys();
+ Object[] mapKeys = map.getBoundKeyStrokes();
+ Object[] bothKeys = new Object[superKeys.length + mapKeys.length];
+ for (int i = 0; i < superKeys.length; ++i)
+ bothKeys[i] = superKeys[i];
+ for (int i = 0; i < mapKeys.length; ++i)
+ bothKeys[i + superKeys.length] = mapKeys[i];
+ return bothKeys;
+ }
+
+ public Object[] allKeys()
+ {
+ Object[] superKeys = super.allKeys();
+ Object[] mapKeys = map.getBoundKeyStrokes();
+ Object[] bothKeys = new Object[superKeys.length + mapKeys.length];
+ for (int i = 0; i < superKeys.length; ++i)
+ bothKeys[i] = superKeys[i];
+ for (int i = 0; i < mapKeys.length; ++i)
+ bothKeys[i + superKeys.length] = mapKeys[i];
+ return bothKeys;
+ }
+
+ }
+
+ static class DefaultKeymap implements Keymap
+ {
+ String name;
+ Keymap parent;
+ Hashtable map;
+ Action defaultAction;
+
+ public DefaultKeymap(String name)
+ {
+ this.name = name;
+ this.map = new Hashtable();
+ }
+
+ public void addActionForKeyStroke(KeyStroke key, Action a)
+ {
+ map.put(key, a);
+ }
+
+ /**
+ * Looks up a KeyStroke either in the current map or the parent Keymap;
+ * does <em>not</em> return the default action if lookup fails.
+ *
+ * @param key The KeyStroke to look up an Action for.
+ *
+ * @return The mapping for <code>key</code>, or <code>null</code>
+ * if no mapping exists in this Keymap or any of its parents.
+ */
+ public Action getAction(KeyStroke key)
+ {
+ if (map.containsKey(key))
+ return (Action) map.get(key);
+ else if (parent != null)
+ return parent.getAction(key);
+ else
+ return null;
+ }
+
+ public Action[] getBoundActions()
+ {
+ Action [] ret = new Action[map.size()];
+ Enumeration e = map.elements();
+ int i = 0;
+ while (e.hasMoreElements())
+ {
+ ret[i++] = (Action) e.nextElement();
+ }
+ return ret;
+ }
+
+ public KeyStroke[] getBoundKeyStrokes()
+ {
+ KeyStroke [] ret = new KeyStroke[map.size()];
+ Enumeration e = map.keys();
+ int i = 0;
+ while (e.hasMoreElements())
+ {
+ ret[i++] = (KeyStroke) e.nextElement();
+ }
+ return ret;
+ }
+
+ public Action getDefaultAction()
+ {
+ return defaultAction;
+ }
+
+ public KeyStroke[] getKeyStrokesForAction(Action a)
+ {
+ int i = 0;
+ Enumeration e = map.keys();
+ while (e.hasMoreElements())
+ {
+ if (map.get(e.nextElement()).equals(a))
+ ++i;
+ }
+ KeyStroke [] ret = new KeyStroke[i];
+ i = 0;
+ e = map.keys();
+ while (e.hasMoreElements())
+ {
+ KeyStroke k = (KeyStroke) e.nextElement();
+ if (map.get(k).equals(a))
+ ret[i++] = k;
+ }
+ return ret;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public Keymap getResolveParent()
+ {
+ return parent;
+ }
+
+ public boolean isLocallyDefined(KeyStroke key)
+ {
+ return map.containsKey(key);
+ }
+
+ public void removeBindings()
+ {
+ map.clear();
+ }
+
+ public void removeKeyStrokeBinding(KeyStroke key)
+ {
+ map.remove(key);
+ }
+
+ public void setDefaultAction(Action a)
+ {
+ defaultAction = a;
+ }
+
+ public void setResolveParent(Keymap p)
+ {
+ parent = p;
+ }
+ }
+
+ class DefaultTransferHandler extends TransferHandler
+ {
+ public boolean canImport(JComponent component, DataFlavor[] flavors)
+ {
+ JTextComponent textComponent = (JTextComponent) component;
+
+ if (! (textComponent.isEnabled()
+ && textComponent.isEditable()
+ && flavors != null))
+ return false;
+
+ for (int i = 0; i < flavors.length; ++i)
+ if (flavors[i].equals(DataFlavor.stringFlavor))
+ return true;
+
+ return false;
+ }
+
+ public void exportToClipboard(JComponent component, Clipboard clipboard,
+ int action)
+ {
+ JTextComponent textComponent = (JTextComponent) component;
+ int start = textComponent.getSelectionStart();
+ int end = textComponent.getSelectionEnd();
+
+ if (start == end)
+ return;
+
+ try
+ {
+ // Copy text to clipboard.
+ String data = textComponent.getDocument().getText(start, end);
+ StringSelection selection = new StringSelection(data);
+ clipboard.setContents(selection, null);
+
+ // Delete selected text on cut action.
+ if (action == MOVE)
+ doc.remove(start, end - start);
+ }
+ catch (BadLocationException e)
+ {
+ // Ignore this and do nothing.
+ }
+ }
+
+ public int getSourceActions()
+ {
+ return NONE;
+ }
+
+ public boolean importData(JComponent component, Transferable transferable)
+ {
+ DataFlavor flavor = null;
+ DataFlavor[] flavors = transferable.getTransferDataFlavors();
+
+ if (flavors == null)
+ return false;
+
+ for (int i = 0; i < flavors.length; ++i)
+ if (flavors[i].equals(DataFlavor.stringFlavor))
+ flavor = flavors[i];
+
+ if (flavor == null)
+ return false;
+
+ try
+ {
+ JTextComponent textComponent = (JTextComponent) component;
+ String data = (String) transferable.getTransferData(flavor);
+ textComponent.replaceSelection(data);
+ return true;
+ }
+ catch (IOException e)
+ {
+ // Ignored.
+ }
+ catch (UnsupportedFlavorException e)
+ {
+ // Ignored.
+ }
+
+ return false;
+ }
+ }
+
+ private static final long serialVersionUID = -8796518220218978795L;
+
+ public static final String DEFAULT_KEYMAP = "default";
+ public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
+
+ private static DefaultTransferHandler defaultTransferHandler;
+ private static Hashtable keymaps = new Hashtable();
+ private Keymap keymap;
+ private char focusAccelerator = '\0';
+ private NavigationFilter navigationFilter;
+
+ /**
+ * Get a Keymap from the global keymap table, by name.
+ *
+ * @param n The name of the Keymap to look up
+ *
+ * @return A Keymap associated with the provided name, or
+ * <code>null</code> if no such Keymap exists
+ *
+ * @see #addKeymap
+ * @see #removeKeymap
+ * @see #keymaps
+ */
+ public static Keymap getKeymap(String n)
+ {
+ return (Keymap) keymaps.get(n);
+ }
+
+ /**
+ * Remove a Keymap from the global Keymap table, by name.
+ *
+ * @param n The name of the Keymap to remove
+ *
+ * @return The keymap removed from the global table
+ *
+ * @see #addKeymap
+ * @see #getKeymap()
+ * @see #keymaps
+ */
+ public static Keymap removeKeymap(String n)
+ {
+ Keymap km = (Keymap) keymaps.get(n);
+ keymaps.remove(n);
+ return km;
+ }
+
+ /**
+ * Create a new Keymap with a specific name and parent, and add the new
+ * Keymap to the global keymap table. The name may be <code>null</code>,
+ * in which case the new Keymap will <em>not</em> be added to the global
+ * Keymap table. The parent may also be <code>null</code>, which is
+ * harmless.
+ *
+ * @param n The name of the new Keymap, or <code>null</code>
+ * @param parent The parent of the new Keymap, or <code>null</code>
+ *
+ * @return The newly created Keymap
+ *
+ * @see #removeKeymap
+ * @see #getKeymap()
+ * @see #keymaps
+ */
+ public static Keymap addKeymap(String n, Keymap parent)
+ {
+ Keymap k = new DefaultKeymap(n);
+ k.setResolveParent(parent);
+ if (n != null)
+ keymaps.put(n, k);
+ return k;
+ }
+
+ /**
+ * Get the current Keymap of this component.
+ *
+ * @return The component's current Keymap
+ *
+ * @see #setKeymap
+ * @see #keymap
+ */
+ public Keymap getKeymap()
+ {
+ return keymap;
+ }
+
+ /**
+ * Set the current Keymap of this component, installing appropriate
+ * {@link KeymapWrapper} and {@link KeymapActionMap} objects in the
+ * {@link InputMap} and {@link ActionMap} parent chains, respectively,
+ * and fire a property change event with name <code>"keymap"</code>.
+ *
+ * @see #getKeymap()
+ * @see #keymap
+ */
+ public void setKeymap(Keymap k)
+ {
+
+ // phase 1: replace the KeymapWrapper entry in the InputMap chain.
+ // the goal here is to always maintain the following ordering:
+ //
+ // [InputMap]? -> [KeymapWrapper]? -> [InputMapUIResource]*
+ //
+ // that is to say, component-specific InputMaps need to remain children
+ // of Keymaps, and Keymaps need to remain children of UI-installed
+ // InputMaps (and the order of each group needs to be preserved, of
+ // course).
+
+ KeymapWrapper kw = (k == null ? null : new KeymapWrapper(k));
+ InputMap childInputMap = getInputMap(JComponent.WHEN_FOCUSED);
+ if (childInputMap == null)
+ setInputMap(JComponent.WHEN_FOCUSED, kw);
+ else
+ {
+ while (childInputMap.getParent() != null
+ && !(childInputMap.getParent() instanceof KeymapWrapper)
+ && !(childInputMap.getParent() instanceof InputMapUIResource))
+ childInputMap = childInputMap.getParent();
+
+ // option 1: there is nobody to replace at the end of the chain
+ if (childInputMap.getParent() == null)
+ childInputMap.setParent(kw);
+
+ // option 2: there is already a KeymapWrapper in the chain which
+ // needs replacing (possibly with its own parents, possibly without)
+ else if (childInputMap.getParent() instanceof KeymapWrapper)
+ {
+ if (kw == null)
+ childInputMap.setParent(childInputMap.getParent().getParent());
+ else
+ {
+ kw.setParent(childInputMap.getParent().getParent());
+ childInputMap.setParent(kw);
+ }
+ }
+
+ // option 3: there is an InputMapUIResource in the chain, which marks
+ // the place where we need to stop and insert ourselves
+ else if (childInputMap.getParent() instanceof InputMapUIResource)
+ {
+ if (kw != null)
+ {
+ kw.setParent(childInputMap.getParent());
+ childInputMap.setParent(kw);
+ }
+ }
+ }
+
+ // phase 2: replace the KeymapActionMap entry in the ActionMap chain
+
+ KeymapActionMap kam = (k == null ? null : new KeymapActionMap(k));
+ ActionMap childActionMap = getActionMap();
+ if (childActionMap == null)
+ setActionMap(kam);
+ else
+ {
+ while (childActionMap.getParent() != null
+ && !(childActionMap.getParent() instanceof KeymapActionMap)
+ && !(childActionMap.getParent() instanceof ActionMapUIResource))
+ childActionMap = childActionMap.getParent();
+
+ // option 1: there is nobody to replace at the end of the chain
+ if (childActionMap.getParent() == null)
+ childActionMap.setParent(kam);
+
+ // option 2: there is already a KeymapActionMap in the chain which
+ // needs replacing (possibly with its own parents, possibly without)
+ else if (childActionMap.getParent() instanceof KeymapActionMap)
+ {
+ if (kam == null)
+ childActionMap.setParent(childActionMap.getParent().getParent());
+ else
+ {
+ kam.setParent(childActionMap.getParent().getParent());
+ childActionMap.setParent(kam);
+ }
+ }
+
+ // option 3: there is an ActionMapUIResource in the chain, which marks
+ // the place where we need to stop and insert ourselves
+ else if (childActionMap.getParent() instanceof ActionMapUIResource)
+ {
+ if (kam != null)
+ {
+ kam.setParent(childActionMap.getParent());
+ childActionMap.setParent(kam);
+ }
+ }
+ }
+
+ // phase 3: update the explicit keymap field
+
+ Keymap old = keymap;
+ keymap = k;
+ firePropertyChange("keymap", old, k);
+ }
+
+ /**
+ * Resolves a set of bindings against a set of actions and inserts the
+ * results into a {@link Keymap}. Specifically, for each provided binding
+ * <code>b</code>, if there exists a provided action <code>a</code> such
+ * that <code>a.getValue(Action.NAME) == b.ActionName</code> then an
+ * entry is added to the Keymap mapping <code>b</code> to
+ * <code>a</code>.
+ *
+ * @param map The Keymap to add new mappings to
+ * @param bindings The set of bindings to add to the Keymap
+ * @param actions The set of actions to resolve binding names against
+ *
+ * @see Action#NAME
+ * @see Action#getValue
+ * @see KeyBinding#actionName
+ */
+ public static void loadKeymap(Keymap map,
+ JTextComponent.KeyBinding[] bindings,
+ Action[] actions)
+ {
+ Hashtable acts = new Hashtable(actions.length);
+ for (int i = 0; i < actions.length; ++i)
+ acts.put(actions[i].getValue(Action.NAME), actions[i]);
+ for (int i = 0; i < bindings.length; ++i)
+ if (acts.containsKey(bindings[i].actionName))
+ map.addActionForKeyStroke(bindings[i].key, (Action) acts.get(bindings[i].actionName));
+ }
+
+ /**
+ * Returns the set of available Actions this component's associated
+ * editor can run. Equivalent to calling
+ * <code>getUI().getEditorKit().getActions()</code>. This set of Actions
+ * is a reasonable value to provide as a parameter to {@link
+ * #loadKeymap}, when resolving a set of {@link KeyBinding} objects
+ * against this component.
+ *
+ * @return The set of available Actions on this component's {@link EditorKit}
+ *
+ * @see TextUI#getEditorKit
+ * @see EditorKit#getActions()
+ */
+ public Action[] getActions()
+ {
+ return getUI().getEditorKit(this).getActions();
+ }
+
+ // These are package-private to avoid an accessor method.
+ Document doc;
+ Caret caret;
+ boolean editable;
+
+ private Highlighter highlighter;
+ private Color caretColor;
+ private Color disabledTextColor;
+ private Color selectedTextColor;
+ private Color selectionColor;
+ private Insets margin;
+ private boolean dragEnabled;
+
+ /**
+ * Creates a new <code>JTextComponent</code> instance.
+ */
+ public JTextComponent()
+ {
+ Keymap defkeymap = getKeymap(DEFAULT_KEYMAP);
+ if (defkeymap == null)
+ {
+ defkeymap = addKeymap(DEFAULT_KEYMAP, null);
+ defkeymap.setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction());
+ }
+
+ setFocusable(true);
+ setEditable(true);
+ enableEvents(AWTEvent.KEY_EVENT_MASK);
+ setOpaque(true);
+ updateUI();
+ }
+
+ public void setDocument(Document newDoc)
+ {
+ Document oldDoc = doc;
+ try
+ {
+ if (oldDoc instanceof AbstractDocument)
+ ((AbstractDocument) oldDoc).readLock();
+
+ doc = newDoc;
+ firePropertyChange("document", oldDoc, newDoc);
+ }
+ finally
+ {
+ if (oldDoc instanceof AbstractDocument)
+ ((AbstractDocument) oldDoc).readUnlock();
+ }
+ revalidate();
+ repaint();
+ }
+
+ public Document getDocument()
+ {
+ return doc;
+ }
+
+ /**
+ * Get the <code>AccessibleContext</code> of this object.
+ *
+ * @return an <code>AccessibleContext</code> object
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return new AccessibleJTextComponent();
+ }
+
+ public void setMargin(Insets m)
+ {
+ margin = m;
+ }
+
+ public Insets getMargin()
+ {
+ return margin;
+ }
+
+ public void setText(String text)
+ {
+ try
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).replace(0, doc.getLength(), text, null);
+ else
+ {
+ doc.remove(0, doc.getLength());
+ doc.insertString(0, text, null);
+ }
+ }
+ catch (BadLocationException e)
+ {
+ // This can never happen.
+ throw (InternalError) new InternalError().initCause(e);
+ }
+ }
+
+ /**
+ * Retrieves the current text in this text document.
+ *
+ * @return the text
+ *
+ * @exception NullPointerException if the underlaying document is null
+ */
+ public String getText()
+ {
+ if (doc == null)
+ return null;
+
+ try
+ {
+ return doc.getText(0, doc.getLength());
+ }
+ catch (BadLocationException e)
+ {
+ // This should never happen.
+ return "";
+ }
+ }
+
+ /**
+ * Retrieves a part of the current text in this document.
+ *
+ * @param offset the postion of the first character
+ * @param length the length of the text to retrieve
+ *
+ * @return the text
+ *
+ * @exception BadLocationException if arguments do not hold pre-conditions
+ */
+ public String getText(int offset, int length)
+ throws BadLocationException
+ {
+ return getDocument().getText(offset, length);
+ }
+
+ /**
+ * Retrieves the currently selected text in this text document.
+ *
+ * @return the selected text
+ *
+ * @exception NullPointerException if the underlaying document is null
+ */
+ public String getSelectedText()
+ {
+ int start = getSelectionStart();
+ int offset = getSelectionEnd() - start;
+
+ if (offset <= 0)
+ return null;
+
+ try
+ {
+ return doc.getText(start, offset);
+ }
+ catch (BadLocationException e)
+ {
+ // This should never happen.
+ return null;
+ }
+ }
+
+ /**
+ * Returns a string that specifies the name of the Look and Feel class
+ * that renders this component.
+ *
+ * @return the string "TextComponentUI"
+ */
+ public String getUIClassID()
+ {
+ return "TextComponentUI";
+ }
+
+ /**
+ * Returns a string representation of this JTextComponent.
+ */
+ protected String paramString()
+ {
+ // TODO: Do something useful here.
+ return super.paramString();
+ }
+
+ /**
+ * This method returns the label's UI delegate.
+ *
+ * @return The label's UI delegate.
+ */
+ public TextUI getUI()
+ {
+ return (TextUI) ui;
+ }
+
+ /**
+ * This method sets the label's UI delegate.
+ *
+ * @param newUI The label's UI delegate.
+ */
+ public void setUI(TextUI newUI)
+ {
+ super.setUI(newUI);
+ }
+
+ /**
+ * This method resets the label's UI delegate to the default UI for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((TextUI) UIManager.getUI(this));
+ }
+
+ public Dimension getPreferredScrollableViewportSize()
+ {
+ return getPreferredSize();
+ }
+
+ public int getScrollableUnitIncrement(Rectangle visible, int orientation,
+ int direction)
+ {
+ // We return 1/10 of the visible area as documented in Sun's API docs.
+ if (orientation == SwingConstants.HORIZONTAL)
+ return visible.width / 10;
+ else if (orientation == SwingConstants.VERTICAL)
+ return visible.height / 10;
+ else
+ throw new IllegalArgumentException("orientation must be either "
+ + "javax.swing.SwingConstants.VERTICAL "
+ + "or "
+ + "javax.swing.SwingConstants.HORIZONTAL"
+ );
+ }
+
+ public int getScrollableBlockIncrement(Rectangle visible, int orientation,
+ int direction)
+ {
+ // We return the whole visible area as documented in Sun's API docs.
+ if (orientation == SwingConstants.HORIZONTAL)
+ return visible.width;
+ else if (orientation == SwingConstants.VERTICAL)
+ return visible.height;
+ else
+ throw new IllegalArgumentException("orientation must be either "
+ + "javax.swing.SwingConstants.VERTICAL "
+ + "or "
+ + "javax.swing.SwingConstants.HORIZONTAL"
+ );
+ }
+
+ /**
+ * Checks whether this text component it editable.
+ *
+ * @return true if editable, false otherwise
+ */
+ public boolean isEditable()
+ {
+ return editable;
+ }
+
+ /**
+ * Enables/disabled this text component's editability.
+ *
+ * @param newValue true to make it editable, false otherwise.
+ */
+ public void setEditable(boolean newValue)
+ {
+ if (editable == newValue)
+ return;
+
+ boolean oldValue = editable;
+ editable = newValue;
+ firePropertyChange("editable", oldValue, newValue);
+ }
+
+ /**
+ * The <code>Caret</code> object used in this text component.
+ *
+ * @return the caret object
+ */
+ public Caret getCaret()
+ {
+ return caret;
+ }
+
+ /**
+ * Sets a new <code>Caret</code> for this text component.
+ *
+ * @param newCaret the new <code>Caret</code> to set
+ */
+ public void setCaret(Caret newCaret)
+ {
+ if (caret != null)
+ caret.deinstall(this);
+
+ Caret oldCaret = caret;
+ caret = newCaret;
+
+ if (caret != null)
+ caret.install(this);
+
+ firePropertyChange("caret", oldCaret, newCaret);
+ }
+
+ public Color getCaretColor()
+ {
+ return caretColor;
+ }
+
+ public void setCaretColor(Color newColor)
+ {
+ Color oldCaretColor = caretColor;
+ caretColor = newColor;
+ firePropertyChange("caretColor", oldCaretColor, newColor);
+ }
+
+ public Color getDisabledTextColor()
+ {
+ return disabledTextColor;
+ }
+
+ public void setDisabledTextColor(Color newColor)
+ {
+ Color oldColor = disabledTextColor;
+ disabledTextColor = newColor;
+ firePropertyChange("disabledTextColor", oldColor, newColor);
+ }
+
+ public Color getSelectedTextColor()
+ {
+ return selectedTextColor;
+ }
+
+ public void setSelectedTextColor(Color newColor)
+ {
+ Color oldColor = selectedTextColor;
+ selectedTextColor = newColor;
+ firePropertyChange("selectedTextColor", oldColor, newColor);
+ }
+
+ public Color getSelectionColor()
+ {
+ return selectionColor;
+ }
+
+ public void setSelectionColor(Color newColor)
+ {
+ Color oldColor = selectionColor;
+ selectionColor = newColor;
+ firePropertyChange("selectionColor", oldColor, newColor);
+ }
+
+ /**
+ * Retrisves the current caret position.
+ *
+ * @return the current position
+ */
+ public int getCaretPosition()
+ {
+ return caret.getDot();
+ }
+
+ /**
+ * Sets the caret to a new position.
+ *
+ * @param position the new position
+ */
+ public void setCaretPosition(int position)
+ {
+ if (doc == null)
+ return;
+
+ if (position < 0 || position > doc.getLength())
+ throw new IllegalArgumentException();
+
+ caret.setDot(position);
+ }
+
+ /**
+ * Moves the caret to a given position. This selects the text between
+ * the old and the new position of the caret.
+ */
+ public void moveCaretPosition(int position)
+ {
+ if (doc == null)
+ return;
+
+ if (position < 0 || position > doc.getLength())
+ throw new IllegalArgumentException();
+
+ caret.moveDot(position);
+ }
+
+ public Highlighter getHighlighter()
+ {
+ return highlighter;
+ }
+
+ public void setHighlighter(Highlighter newHighlighter)
+ {
+ if (highlighter != null)
+ highlighter.deinstall(this);
+
+ Highlighter oldHighlighter = highlighter;
+ highlighter = newHighlighter;
+
+ if (highlighter != null)
+ highlighter.install(this);
+
+ firePropertyChange("highlighter", oldHighlighter, newHighlighter);
+ }
+
+ /**
+ * Returns the start postion of the currently selected text.
+ *
+ * @return the start postion
+ */
+ public int getSelectionStart()
+ {
+ return Math.min(caret.getDot(), caret.getMark());
+ }
+
+ /**
+ * Selects the text from the given postion to the selection end position.
+ *
+ * @param start the start positon of the selected text.
+ */
+ public void setSelectionStart(int start)
+ {
+ select(start, getSelectionEnd());
+ }
+
+ /**
+ * Returns the end postion of the currently selected text.
+ *
+ * @return the end postion
+ */
+ public int getSelectionEnd()
+ {
+ return Math.max(caret.getDot(), caret.getMark());
+ }
+
+ /**
+ * Selects the text from the selection start postion to the given position.
+ *
+ * @param end the end positon of the selected text.
+ */
+ public void setSelectionEnd(int end)
+ {
+ select(getSelectionStart(), end);
+ }
+
+ /**
+ * Selects a part of the content of the text component.
+ *
+ * @param start the start position of the selected text
+ * @param end the end position of the selected text
+ */
+ public void select(int start, int end)
+ {
+ int length = doc.getLength();
+
+ start = Math.max(start, 0);
+ start = Math.min(start, length);
+
+ end = Math.max(end, start);
+ end = Math.min(end, length);
+
+ setCaretPosition(start);
+ moveCaretPosition(end);
+ }
+
+ /**
+ * Selects the whole content of the text component.
+ */
+ public void selectAll()
+ {
+ select(0, doc.getLength());
+ }
+
+ public synchronized void replaceSelection(String content)
+ {
+ int dot = caret.getDot();
+ int mark = caret.getMark();
+
+ // If content is empty delete selection.
+ if (content == null)
+ {
+ caret.setDot(dot);
+ return;
+ }
+
+ try
+ {
+ int start = getSelectionStart();
+ int end = getSelectionEnd();
+
+ // Remove selected text.
+ if (dot != mark)
+ doc.remove(start, end - start);
+
+ // Insert new text.
+ doc.insertString(start, content, null);
+
+ // Set dot to new position,
+ dot = start + content.length();
+ setCaretPosition(dot);
+
+ // and update it's magic position.
+ caret.setMagicCaretPosition(modelToView(dot).getLocation());
+ }
+ catch (BadLocationException e)
+ {
+ // This should never happen.
+ }
+ }
+
+ public boolean getScrollableTracksViewportHeight()
+ {
+ if (getParent() instanceof JViewport)
+ return getParent().getHeight() > getPreferredSize().height;
+
+ return false;
+ }
+
+ public boolean getScrollableTracksViewportWidth()
+ {
+ boolean res = false;
+ Container c = getParent();
+ if (c instanceof JViewport)
+ res = ((JViewport) c).getExtentSize().width > getPreferredSize().width;
+
+ return res;
+ }
+
+ /**
+ * Adds a <code>CaretListener</code> object to this text component.
+ *
+ * @param listener the listener to add
+ */
+ public void addCaretListener(CaretListener listener)
+ {
+ listenerList.add(CaretListener.class, listener);
+ }
+
+ /**
+ * Removed a <code>CaretListener</code> object from this text component.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeCaretListener(CaretListener listener)
+ {
+ listenerList.remove(CaretListener.class, listener);
+ }
+
+ /**
+ * Returns all added <code>CaretListener</code> objects.
+ *
+ * @return an array of listeners
+ */
+ public CaretListener[] getCaretListeners()
+ {
+ return (CaretListener[]) getListeners(CaretListener.class);
+ }
+
+ /**
+ * Notifies all registered <code>CaretListener</code> objects that the caret
+ * was updated.
+ *
+ * @param event the event to send
+ */
+ protected void fireCaretUpdate(CaretEvent event)
+ {
+ CaretListener[] listeners = getCaretListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].caretUpdate(event);
+ }
+
+ /**
+ * Adds an <code>InputListener</code> object to this text component.
+ *
+ * @param listener the listener to add
+ */
+ public void addInputMethodListener(InputMethodListener listener)
+ {
+ listenerList.add(InputMethodListener.class, listener);
+ }
+
+ /**
+ * Removes an <code>InputListener</code> object from this text component.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeInputMethodListener(InputMethodListener listener)
+ {
+ listenerList.remove(InputMethodListener.class, listener);
+ }
+
+ /**
+ * Returns all added <code>InputMethodListener</code> objects.
+ *
+ * @return an array of listeners
+ */
+ public InputMethodListener[] getInputMethodListeners()
+ {
+ return (InputMethodListener[]) getListeners(InputMethodListener.class);
+ }
+
+ public Rectangle modelToView(int position) throws BadLocationException
+ {
+ return getUI().modelToView(this, position);
+ }
+
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ public void setDragEnabled(boolean enabled)
+ {
+ dragEnabled = enabled;
+ }
+
+ public int viewToModel(Point pt)
+ {
+ return getUI().viewToModel(this, pt);
+ }
+
+ public void copy()
+ {
+ if (isEnabled())
+ doTransferAction("copy", TransferHandler.getCopyAction());
+ }
+
+ public void cut()
+ {
+ if (editable && isEnabled())
+ doTransferAction("cut", TransferHandler.getCutAction());
+ }
+
+ public void paste()
+ {
+ if (editable && isEnabled())
+ doTransferAction("paste", TransferHandler.getPasteAction());
+ }
+
+ private void doTransferAction(String name, Action action)
+ {
+ // Install default TransferHandler if none set.
+ if (getTransferHandler() == null)
+ {
+ if (defaultTransferHandler == null)
+ defaultTransferHandler = new DefaultTransferHandler();
+
+ setTransferHandler(defaultTransferHandler);
+ }
+
+ // Perform action.
+ ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
+ action.getValue(Action.NAME).toString());
+ action.actionPerformed(event);
+ }
+
+ public void setFocusAccelerator(char newKey)
+ {
+ if (focusAccelerator == newKey)
+ return;
+
+ char oldKey = focusAccelerator;
+ focusAccelerator = newKey;
+ firePropertyChange(FOCUS_ACCELERATOR_KEY, oldKey, newKey);
+ }
+
+ public char getFocusAccelerator()
+ {
+ return focusAccelerator;
+ }
+
+ /**
+ * @since 1.4
+ */
+ public NavigationFilter getNavigationFilter()
+ {
+ return navigationFilter;
+ }
+
+ /**
+ * @since 1.4
+ */
+ public void setNavigationFilter(NavigationFilter filter)
+ {
+ navigationFilter = filter;
+ }
+
+ /**
+ * Read and set the content this component. If not overridden, the
+ * method reads the component content as a plain text.
+ *
+ * The second parameter of this method describes the input stream. It can
+ * be String, URL, File and so on. If not null, this object is added to
+ * the properties of the associated document under the key
+ * {@link Document#StreamDescriptionProperty}.
+ *
+ * @param input an input stream to read from.
+ * @param streamDescription an object, describing the stream.
+ *
+ * @throws IOException if the reader throws it.
+ *
+ * @see #getDocument()
+ * @see Document#getProperty(Object)
+ */
+ public void read(Reader input, Object streamDescription)
+ throws IOException
+ {
+ if (streamDescription != null)
+ {
+ Document d = getDocument();
+ if (d != null)
+ d.putProperty(Document.StreamDescriptionProperty, streamDescription);
+ }
+
+ CPStringBuilder b = new CPStringBuilder();
+ int c;
+
+ // Read till -1 (EOF).
+ while ((c = input.read()) >= 0)
+ b.append((char) c);
+
+ setText(b.toString());
+ }
+
+ /**
+ * Write the content of this component to the given stream. If not
+ * overridden, the method writes the component content as a plain text.
+ *
+ * @param output the writer to write into.
+ *
+ * @throws IOException if the writer throws it.
+ */
+ public void write(Writer output)
+ throws IOException
+ {
+ output.write(getText());
+ }
+
+ /**
+ * Returns the tooltip text for this text component for the given mouse
+ * event. This forwards the call to
+ * {@link TextUI#getToolTipText(JTextComponent, Point)}.
+ *
+ * @param ev the mouse event
+ *
+ * @return the tooltip text for this text component for the given mouse
+ * event
+ */
+ public String getToolTipText(MouseEvent ev)
+ {
+ return getUI().getToolTipText(this, ev.getPoint());
+ }
+}