| /* 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()); | |
| } | |
| } |