/* | 
|
 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. | 
|
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|
 * | 
|
 * This code is free software; you can redistribute it and/or modify it | 
|
 * under the terms of the GNU General Public License version 2 only, as | 
|
 * published by the Free Software Foundation.  Oracle designates this | 
|
 * particular file as subject to the "Classpath" exception as provided | 
|
 * by Oracle in the LICENSE file that accompanied this code. | 
|
 * | 
|
 * This code 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 | 
|
 * version 2 for more details (a copy is included in the LICENSE file that | 
|
 * accompanied this code). | 
|
 * | 
|
 * You should have received a copy of the GNU General Public License version | 
|
 * 2 along with this work; if not, write to the Free Software Foundation, | 
|
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|
 * | 
|
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|
 * or visit www.oracle.com if you need additional information or have any | 
|
 * questions. | 
|
*/  | 
|
package javax.swing.plaf.basic;  | 
|
import java.util.*;  | 
|
import java.awt.*;  | 
|
import java.awt.event.*;  | 
|
import java.awt.datatransfer.*;  | 
|
import java.awt.im.InputContext;  | 
|
import java.beans.*;  | 
|
import java.io.*;  | 
|
import javax.swing.*;  | 
|
import javax.swing.plaf.*;  | 
|
import javax.swing.text.*;  | 
|
import javax.swing.event.*;  | 
|
import javax.swing.border.Border;  | 
|
import javax.swing.plaf.UIResource;  | 
|
import javax.swing.plaf.synth.SynthUI;  | 
|
import sun.swing.DefaultLookup;  | 
|
import sun.awt.AppContext;  | 
|
import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag;  | 
|
/** | 
|
 * <p> | 
|
 * Basis of a text components look-and-feel.  This provides the | 
|
 * basic editor view and controller services that may be useful | 
|
 * when creating a look-and-feel for an extension of | 
|
 * <code>JTextComponent</code>. | 
|
 * <p> | 
|
 * Most state is held in the associated <code>JTextComponent</code> | 
|
 * as bound properties, and the UI installs default values for the | 
|
 * various properties.  This default will install something for | 
|
 * all of the properties.  Typically, a LAF implementation will | 
|
 * do more however.  At a minimum, a LAF would generally install | 
|
 * key bindings. | 
|
 * <p> | 
|
 * This class also provides some concurrency support if the | 
|
 * <code>Document</code> associated with the JTextComponent is a subclass of | 
|
 * <code>AbstractDocument</code>.  Access to the View (or View hierarchy) is | 
|
 * serialized between any thread mutating the model and the Swing | 
|
 * event thread (which is expected to render, do model/view coordinate | 
|
 * translation, etc).  <em>Any access to the root view should first | 
|
 * acquire a read-lock on the AbstractDocument and release that lock | 
|
 * in a finally block.</em> | 
|
 * <p> | 
|
 * An important method to define is the {@link #getPropertyPrefix} method | 
|
 * which is used as the basis of the keys used to fetch defaults | 
|
 * from the UIManager.  The string should reflect the type of | 
|
 * TextUI (eg. TextField, TextArea, etc) without the particular | 
|
 * LAF part of the name (eg Metal, Motif, etc). | 
|
 * <p> | 
|
 * To build a view of the model, one of the following strategies | 
|
 * can be employed. | 
|
 * <ol> | 
|
 * <li> | 
|
 * One strategy is to simply redefine the | 
|
 * ViewFactory interface in the UI.  By default, this UI itself acts | 
|
 * as the factory for View implementations.  This is useful | 
|
 * for simple factories.  To do this reimplement the | 
|
 * {@link #create} method. | 
|
 * <li> | 
|
 * A common strategy for creating more complex types of documents | 
|
 * is to have the EditorKit implementation return a factory.  Since | 
|
 * the EditorKit ties all of the pieces necessary to maintain a type | 
|
 * of document, the factory is typically an important part of that | 
|
 * and should be produced by the EditorKit implementation. | 
|
 * </ol> | 
|
 * <p> | 
|
 * <strong>Warning:</strong> | 
|
 * Serialized objects of this class will not be compatible with | 
|
 * future Swing releases. The current serialization support is | 
|
 * appropriate for short term storage or RMI between applications running | 
|
 * the same version of Swing.  As of 1.4, support for long term storage | 
|
 * of all JavaBeans™ | 
|
 * has been added to the <code>java.beans</code> package. | 
|
 * Please see {@link java.beans.XMLEncoder}. | 
|
 * | 
|
 * @author Timothy Prinzing | 
|
 * @author Shannon Hickey (drag and drop) | 
|
*/  | 
|
public abstract class BasicTextUI extends TextUI implements ViewFactory {  | 
|
    /** | 
|
     * Creates a new UI. | 
|
*/  | 
|
    public BasicTextUI() { | 
|
painted = false;  | 
|
}  | 
|
    /** | 
|
     * Creates the object to use for a caret.  By default an | 
|
     * instance of BasicCaret is created.  This method | 
|
     * can be redefined to provide something else that implements | 
|
     * the InputPosition interface or a subclass of JCaret. | 
|
     * | 
|
     * @return the caret object | 
|
*/  | 
|
protected Caret createCaret() {  | 
|
return new BasicCaret();  | 
|
}  | 
|
    /** | 
|
     * Creates the object to use for adding highlights.  By default | 
|
     * an instance of BasicHighlighter is created.  This method | 
|
     * can be redefined to provide something else that implements | 
|
     * the Highlighter interface or a subclass of DefaultHighlighter. | 
|
     * | 
|
     * @return the highlighter | 
|
*/  | 
|
protected Highlighter createHighlighter() {  | 
|
return new BasicHighlighter();  | 
|
}  | 
|
    /** | 
|
     * Fetches the name of the keymap that will be installed/used | 
|
     * by default for this UI. This is implemented to create a | 
|
     * name based upon the classname.  The name is the the name | 
|
     * of the class with the package prefix removed. | 
|
     * | 
|
     * @return the name | 
|
*/  | 
|
protected String getKeymapName() {  | 
|
String nm = getClass().getName();  | 
|
int index = nm.lastIndexOf('.');  | 
|
if (index >= 0) {  | 
|
nm = nm.substring(index+1, nm.length());  | 
|
}  | 
|
return nm;  | 
|
}  | 
|
    /** | 
|
     * Creates the keymap to use for the text component, and installs | 
|
     * any necessary bindings into it.  By default, the keymap is | 
|
     * shared between all instances of this type of TextUI. The | 
|
     * keymap has the name defined by the getKeymapName method.  If the | 
|
     * keymap is not found, then DEFAULT_KEYMAP from JTextComponent is used. | 
|
     * <p> | 
|
     * The set of bindings used to create the keymap is fetched | 
|
     * from the UIManager using a key formed by combining the | 
|
     * {@link #getPropertyPrefix} method | 
|
     * and the string <code>.keyBindings</code>.  The type is expected | 
|
     * to be <code>JTextComponent.KeyBinding[]</code>. | 
|
     * | 
|
     * @return the keymap | 
|
     * @see #getKeymapName | 
|
     * @see javax.swing.text.JTextComponent | 
|
*/  | 
|
protected Keymap createKeymap() {  | 
|
String nm = getKeymapName();  | 
|
Keymap map = JTextComponent.getKeymap(nm);  | 
|
if (map == null) {  | 
|
Keymap parent = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);  | 
|
map = JTextComponent.addKeymap(nm, parent);  | 
|
String prefix = getPropertyPrefix();  | 
|
Object o = DefaultLookup.get(editor, this,  | 
|
prefix + ".keyBindings");  | 
|
if ((o != null) && (o instanceof JTextComponent.KeyBinding[])) {  | 
|
JTextComponent.KeyBinding[] bindings = (JTextComponent.KeyBinding[]) o;  | 
|
JTextComponent.loadKeymap(map, bindings, getComponent().getActions());  | 
|
}  | 
|
}  | 
|
return map;  | 
|
}  | 
|
    /** | 
|
     * This method gets called when a bound property is changed | 
|
     * on the associated JTextComponent.  This is a hook | 
|
     * which UI implementations may change to reflect how the | 
|
     * UI displays bound properties of JTextComponent subclasses. | 
|
     * This is implemented to do nothing (i.e. the response to | 
|
     * properties in JTextComponent itself are handled prior | 
|
     * to calling this method). | 
|
     * | 
|
     * This implementation updates the background of the text | 
|
     * component if the editable and/or enabled state changes. | 
|
     * | 
|
     * @param evt the property change event | 
|
*/  | 
|
protected void propertyChange(PropertyChangeEvent evt) {  | 
|
if (evt.getPropertyName().equals("editable") ||  | 
|
evt.getPropertyName().equals("enabled")) {  | 
|
updateBackground((JTextComponent)evt.getSource());  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Updates the background of the text component based on whether the | 
|
     * text component is editable and/or enabled. | 
|
     * | 
|
     * @param c the JTextComponent that needs its background color updated | 
|
*/  | 
|
private void updateBackground(JTextComponent c) {  | 
|
// This is a temporary workaround.  | 
|
// This code does not correctly deal with Synth (Synth doesn't use  | 
|
// properties like this), nor does it deal with the situation where  | 
|
// the developer grabs the color from a JLabel and sets it as  | 
|
// the background for a JTextArea in all look and feels. The problem  | 
|
// scenario results if the Color obtained for the Label and TextArea  | 
|
// is ==, which is the case for the windows look and feel.  | 
|
// Until an appropriate solution is found, the code is being  | 
|
        // reverted to what it was before the original fix. | 
|
if (this instanceof SynthUI || (c instanceof JTextArea)) {  | 
|
return;  | 
|
}  | 
|
Color background = c.getBackground();  | 
|
if (background instanceof UIResource) {  | 
|
String prefix = getPropertyPrefix();  | 
|
Color disabledBG =  | 
|
DefaultLookup.getColor(c, this, prefix + ".disabledBackground", null);  | 
|
Color inactiveBG =  | 
|
DefaultLookup.getColor(c, this, prefix + ".inactiveBackground", null);  | 
|
Color bg =  | 
|
DefaultLookup.getColor(c, this, prefix + ".background", null);  | 
|
            /* In an ideal situation, the following check would not be necessary | 
|
             * and we would replace the color any time the previous color was a | 
|
             * UIResouce. However, it turns out that there is existing code that | 
|
             * uses the following inadvisable pattern to turn a text area into | 
|
             * what appears to be a multi-line label: | 
|
             * | 
|
             * JLabel label = new JLabel(); | 
|
             * JTextArea area = new JTextArea(); | 
|
             * area.setBackground(label.getBackground()); | 
|
             * area.setEditable(false); | 
|
             * | 
|
             * JLabel's default background is a UIResource. As such, just | 
|
             * checking for UIResource would have us always changing the | 
|
             * background away from what the developer wanted. | 
|
             * | 
|
             * Therefore, for JTextArea/JEditorPane, we'll additionally check | 
|
             * that the color we're about to replace matches one that was | 
|
             * installed by us from the UIDefaults. | 
|
*/  | 
|
if ((c instanceof JTextArea || c instanceof JEditorPane)  | 
|
&& background != disabledBG  | 
|
&& background != inactiveBG  | 
|
&& background != bg) {  | 
|
return;  | 
|
}  | 
|
Color newColor = null;  | 
|
if (!c.isEnabled()) {  | 
|
newColor = disabledBG;  | 
|
}  | 
|
if (newColor == null && !c.isEditable()) {  | 
|
newColor = inactiveBG;  | 
|
}  | 
|
if (newColor == null) {  | 
|
newColor = bg;  | 
|
}  | 
|
if (newColor != null && newColor != background) {  | 
|
c.setBackground(newColor);  | 
|
}  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Gets the name used as a key to look up properties through the | 
|
     * UIManager.  This is used as a prefix to all the standard | 
|
     * text properties. | 
|
     * | 
|
     * @return the name | 
|
*/  | 
|
protected abstract String getPropertyPrefix();  | 
|
    /** | 
|
     * Initializes component properties, such as font, foreground, | 
|
     * background, caret color, selection color, selected text color, | 
|
     * disabled text color, and border color.  The font, foreground, and | 
|
     * background properties are only set if their current value is either null | 
|
     * or a UIResource, other properties are set if the current | 
|
     * value is null. | 
|
     * | 
|
     * @see #uninstallDefaults | 
|
     * @see #installUI | 
|
*/  | 
|
protected void installDefaults()  | 
|
    { | 
|
String prefix = getPropertyPrefix();  | 
|
Font f = editor.getFont();  | 
|
if ((f == null) || (f instanceof UIResource)) {  | 
|
editor.setFont(UIManager.getFont(prefix + ".font"));  | 
|
}  | 
|
Color bg = editor.getBackground();  | 
|
if ((bg == null) || (bg instanceof UIResource)) {  | 
|
editor.setBackground(UIManager.getColor(prefix + ".background"));  | 
|
}  | 
|
Color fg = editor.getForeground();  | 
|
if ((fg == null) || (fg instanceof UIResource)) {  | 
|
editor.setForeground(UIManager.getColor(prefix + ".foreground"));  | 
|
}  | 
|
Color color = editor.getCaretColor();  | 
|
if ((color == null) || (color instanceof UIResource)) {  | 
|
editor.setCaretColor(UIManager.getColor(prefix + ".caretForeground"));  | 
|
}  | 
|
Color s = editor.getSelectionColor();  | 
|
if ((s == null) || (s instanceof UIResource)) {  | 
|
editor.setSelectionColor(UIManager.getColor(prefix + ".selectionBackground"));  | 
|
}  | 
|
Color sfg = editor.getSelectedTextColor();  | 
|
if ((sfg == null) || (sfg instanceof UIResource)) {  | 
|
editor.setSelectedTextColor(UIManager.getColor(prefix + ".selectionForeground"));  | 
|
}  | 
|
Color dfg = editor.getDisabledTextColor();  | 
|
if ((dfg == null) || (dfg instanceof UIResource)) {  | 
|
editor.setDisabledTextColor(UIManager.getColor(prefix + ".inactiveForeground"));  | 
|
}  | 
|
Border b = editor.getBorder();  | 
|
if ((b == null) || (b instanceof UIResource)) {  | 
|
editor.setBorder(UIManager.getBorder(prefix + ".border"));  | 
|
}  | 
|
Insets margin = editor.getMargin();  | 
|
if (margin == null || margin instanceof UIResource) {  | 
|
editor.setMargin(UIManager.getInsets(prefix + ".margin"));  | 
|
}  | 
|
updateCursor();  | 
|
}  | 
|
    private void installDefaults2() { | 
|
editor.addMouseListener(dragListener);  | 
|
editor.addMouseMotionListener(dragListener);  | 
|
String prefix = getPropertyPrefix();  | 
|
Caret caret = editor.getCaret();  | 
|
if (caret == null || caret instanceof UIResource) {  | 
|
caret = createCaret();  | 
|
editor.setCaret(caret);  | 
|
int rate = DefaultLookup.getInt(getComponent(), this, prefix + ".caretBlinkRate", 500);  | 
|
caret.setBlinkRate(rate);  | 
|
}  | 
|
Highlighter highlighter = editor.getHighlighter();  | 
|
if (highlighter == null || highlighter instanceof UIResource) {  | 
|
editor.setHighlighter(createHighlighter());  | 
|
}  | 
|
TransferHandler th = editor.getTransferHandler();  | 
|
if (th == null || th instanceof UIResource) {  | 
|
editor.setTransferHandler(getTransferHandler());  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Sets the component properties that have not been explicitly overridden | 
|
     * to {@code null}.  A property is considered overridden if its current | 
|
     * value is not a {@code UIResource}. | 
|
     * | 
|
     * @see #installDefaults | 
|
     * @see #uninstallUI | 
|
*/  | 
|
protected void uninstallDefaults()  | 
|
    { | 
|
editor.removeMouseListener(dragListener);  | 
|
editor.removeMouseMotionListener(dragListener);  | 
|
if (editor.getCaretColor() instanceof UIResource) {  | 
|
editor.setCaretColor(null);  | 
|
}  | 
|
if (editor.getSelectionColor() instanceof UIResource) {  | 
|
editor.setSelectionColor(null);  | 
|
}  | 
|
if (editor.getDisabledTextColor() instanceof UIResource) {  | 
|
editor.setDisabledTextColor(null);  | 
|
}  | 
|
if (editor.getSelectedTextColor() instanceof UIResource) {  | 
|
editor.setSelectedTextColor(null);  | 
|
}  | 
|
if (editor.getBorder() instanceof UIResource) {  | 
|
editor.setBorder(null);  | 
|
}  | 
|
if (editor.getMargin() instanceof UIResource) {  | 
|
editor.setMargin(null);  | 
|
}  | 
|
if (editor.getCaret() instanceof UIResource) {  | 
|
editor.setCaret(null);  | 
|
}  | 
|
if (editor.getHighlighter() instanceof UIResource) {  | 
|
editor.setHighlighter(null);  | 
|
}  | 
|
if (editor.getTransferHandler() instanceof UIResource) {  | 
|
editor.setTransferHandler(null);  | 
|
}  | 
|
if (editor.getCursor() instanceof UIResource) {  | 
|
editor.setCursor(null);  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Installs listeners for the UI. | 
|
*/  | 
|
    protected void installListeners() { | 
|
}  | 
|
    /** | 
|
     * Uninstalls listeners for the UI. | 
|
*/  | 
|
    protected void uninstallListeners() { | 
|
}  | 
|
    protected void installKeyboardActions() { | 
|
// backward compatibility support... keymaps for the UI  | 
|
        // are now installed in the more friendly input map. | 
|
editor.setKeymap(createKeymap());  | 
|
InputMap km = getInputMap();  | 
|
if (km != null) {  | 
|
SwingUtilities.replaceUIInputMap(editor, JComponent.WHEN_FOCUSED,  | 
|
km);  | 
|
}  | 
|
ActionMap map = getActionMap();  | 
|
if (map != null) {  | 
|
SwingUtilities.replaceUIActionMap(editor, map);  | 
|
}  | 
|
updateFocusAcceleratorBinding(false);  | 
|
}  | 
|
    /** | 
|
     * Get the InputMap to use for the UI. | 
|
*/  | 
|
InputMap getInputMap() {  | 
|
InputMap map = new InputMapUIResource();  | 
|
InputMap shared =  | 
|
(InputMap)DefaultLookup.get(editor, this,  | 
|
getPropertyPrefix() + ".focusInputMap");  | 
|
if (shared != null) {  | 
|
map.setParent(shared);  | 
|
}  | 
|
return map;  | 
|
}  | 
|
    /** | 
|
     * Invoked when the focus accelerator changes, this will update the | 
|
     * key bindings as necessary. | 
|
*/  | 
|
    void updateFocusAcceleratorBinding(boolean changed) { | 
|
char accelerator = editor.getFocusAccelerator();  | 
|
if (changed || accelerator != '\0') {  | 
|
InputMap km = SwingUtilities.getUIInputMap  | 
|
(editor, JComponent.WHEN_IN_FOCUSED_WINDOW);  | 
|
if (km == null && accelerator != '\0') {  | 
|
km = new ComponentInputMapUIResource(editor);  | 
|
SwingUtilities.replaceUIInputMap(editor, JComponent.  | 
|
WHEN_IN_FOCUSED_WINDOW, km);  | 
|
ActionMap am = getActionMap();  | 
|
SwingUtilities.replaceUIActionMap(editor, am);  | 
|
}  | 
|
if (km != null) {  | 
|
km.clear();  | 
|
if (accelerator != '\0') {  | 
|
km.put(KeyStroke.getKeyStroke(accelerator, BasicLookAndFeel.getFocusAcceleratorKeyMask()), "requestFocus");  | 
|
}  | 
|
}  | 
|
}  | 
|
}  | 
|
/**  | 
|
* Invoked when editable property is changed.  | 
|
*  | 
|
* removing 'TAB' and 'SHIFT-TAB' from traversalKeysSet in case  | 
|
* editor is editable  | 
|
* adding 'TAB' and 'SHIFT-TAB' to traversalKeysSet in case  | 
|
* editor is non editable  | 
|
*/  | 
|
    void updateFocusTraversalKeys() { | 
|
        /* | 
|
         * Fix for 4514331 Non-editable JTextArea and similar | 
|
         * should allow Tab to keyboard - accessibility | 
|
*/  | 
|
EditorKit editorKit = getEditorKit(editor);  | 
|
if ( editorKit != null  | 
|
&& editorKit instanceof DefaultEditorKit) {  | 
|
Set<AWTKeyStroke> storedForwardTraversalKeys = editor.  | 
|
getFocusTraversalKeys(KeyboardFocusManager.  | 
|
FORWARD_TRAVERSAL_KEYS);  | 
|
Set<AWTKeyStroke> storedBackwardTraversalKeys = editor.  | 
|
getFocusTraversalKeys(KeyboardFocusManager.  | 
|
BACKWARD_TRAVERSAL_KEYS);  | 
|
Set<AWTKeyStroke> forwardTraversalKeys =  | 
|
new HashSet<AWTKeyStroke>(storedForwardTraversalKeys);  | 
|
Set<AWTKeyStroke> backwardTraversalKeys =  | 
|
new HashSet<AWTKeyStroke>(storedBackwardTraversalKeys);  | 
|
            if (editor.isEditable()) { | 
|
forwardTraversalKeys.  | 
|
remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));  | 
|
backwardTraversalKeys.  | 
|
remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB,  | 
|
InputEvent.SHIFT_MASK));  | 
|
            } else { | 
|
forwardTraversalKeys.add(KeyStroke.  | 
|
getKeyStroke(KeyEvent.VK_TAB, 0));  | 
|
backwardTraversalKeys.  | 
|
add(KeyStroke.  | 
|
getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK));  | 
|
}  | 
|
LookAndFeel.installProperty(editor,  | 
|
                                        "focusTraversalKeysForward", | 
|
forwardTraversalKeys);  | 
|
LookAndFeel.installProperty(editor,  | 
|
                                        "focusTraversalKeysBackward", | 
|
backwardTraversalKeys);  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * As needed updates cursor for the target editor. | 
|
*/  | 
|
    private void updateCursor() { | 
|
if ((! editor.isCursorSet())  | 
|
|| editor.getCursor() instanceof UIResource) {  | 
|
Cursor cursor = (editor.isEditable()) ? textCursor : null;  | 
|
editor.setCursor(cursor);  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Returns the <code>TransferHandler</code> that will be installed if | 
|
     * their isn't one installed on the <code>JTextComponent</code>. | 
|
*/  | 
|
TransferHandler getTransferHandler() {  | 
|
return defaultTransferHandler;  | 
|
}  | 
|
    /** | 
|
     * Fetch an action map to use. | 
|
*/  | 
|
ActionMap getActionMap() {  | 
|
String mapName = getPropertyPrefix() + ".actionMap";  | 
|
ActionMap map = (ActionMap)UIManager.get(mapName);  | 
|
if (map == null) {  | 
|
map = createActionMap();  | 
|
if (map != null) {  | 
|
UIManager.getLookAndFeelDefaults().put(mapName, map);  | 
|
}  | 
|
}  | 
|
ActionMap componentMap = new ActionMapUIResource();  | 
|
componentMap.put("requestFocus", new FocusAction());  | 
|
        /* | 
|
         * fix for bug 4515750 | 
|
         * JTextField & non-editable JTextArea bind return key - default btn not accessible | 
|
         * | 
|
         * Wrap the return action so that it is only enabled when the | 
|
         * component is editable. This allows the default button to be | 
|
         * processed when the text component has focus and isn't editable. | 
|
         * | 
|
*/  | 
|
if (getEditorKit(editor) instanceof DefaultEditorKit) {  | 
|
if (map != null) {  | 
|
Object obj = map.get(DefaultEditorKit.insertBreakAction);  | 
|
if (obj != null  | 
|
&& obj instanceof DefaultEditorKit.InsertBreakAction) {  | 
|
Action action = new TextActionWrapper((TextAction)obj);  | 
|
componentMap.put(action.getValue(Action.NAME),action);  | 
|
}  | 
|
}  | 
|
}  | 
|
if (map != null) {  | 
|
componentMap.setParent(map);  | 
|
}  | 
|
return componentMap;  | 
|
}  | 
|
    /** | 
|
     * Create a default action map.  This is basically the | 
|
     * set of actions found exported by the component. | 
|
*/  | 
|
ActionMap createActionMap() {  | 
|
ActionMap map = new ActionMapUIResource();  | 
|
Action[] actions = editor.getActions();  | 
|
        //System.out.println("building map for UI: " + getPropertyPrefix()); | 
|
int n = actions.length;  | 
|
for (int i = 0; i < n; i++) {  | 
|
Action a = actions[i];  | 
|
map.put(a.getValue(Action.NAME), a);  | 
|
            //System.out.println("  " + a.getValue(Action.NAME)); | 
|
}  | 
|
map.put(TransferHandler.getCutAction().getValue(Action.NAME),  | 
|
TransferHandler.getCutAction());  | 
|
map.put(TransferHandler.getCopyAction().getValue(Action.NAME),  | 
|
TransferHandler.getCopyAction());  | 
|
map.put(TransferHandler.getPasteAction().getValue(Action.NAME),  | 
|
TransferHandler.getPasteAction());  | 
|
return map;  | 
|
}  | 
|
    protected void uninstallKeyboardActions() { | 
|
editor.setKeymap(null);  | 
|
SwingUtilities.replaceUIInputMap(editor, JComponent.  | 
|
WHEN_IN_FOCUSED_WINDOW, null);  | 
|
SwingUtilities.replaceUIActionMap(editor, null);  | 
|
}  | 
|
    /** | 
|
     * Paints a background for the view.  This will only be | 
|
     * called if isOpaque() on the associated component is | 
|
     * true.  The default is to paint the background color | 
|
     * of the component. | 
|
     * | 
|
     * @param g the graphics context | 
|
*/  | 
|
protected void paintBackground(Graphics g) {  | 
|
g.setColor(editor.getBackground());  | 
|
g.fillRect(0, 0, editor.getWidth(), editor.getHeight());  | 
|
}  | 
|
    /** | 
|
     * Fetches the text component associated with this | 
|
     * UI implementation.  This will be null until | 
|
     * the ui has been installed. | 
|
     * | 
|
     * @return the editor component | 
|
*/  | 
|
protected final JTextComponent getComponent() {  | 
|
return editor;  | 
|
}  | 
|
    /** | 
|
     * Flags model changes. | 
|
     * This is called whenever the model has changed. | 
|
     * It is implemented to rebuild the view hierarchy | 
|
     * to represent the default root element of the | 
|
     * associated model. | 
|
*/  | 
|
    protected void modelChanged() { | 
|
        // create a view hierarchy | 
|
ViewFactory f = rootView.getViewFactory();  | 
|
Document doc = editor.getDocument();  | 
|
Element elem = doc.getDefaultRootElement();  | 
|
setView(f.create(elem));  | 
|
}  | 
|
    /** | 
|
     * Sets the current root of the view hierarchy and calls invalidate(). | 
|
     * If there were any child components, they will be removed (i.e. | 
|
     * there are assumed to have come from components embedded in views). | 
|
     * | 
|
     * @param v the root view | 
|
*/  | 
|
protected final void setView(View v) {  | 
|
rootView.setView(v);  | 
|
painted = false;  | 
|
editor.revalidate();  | 
|
editor.repaint();  | 
|
}  | 
|
    /** | 
|
     * Paints the interface safely with a guarantee that | 
|
     * the model won't change from the view of this thread. | 
|
     * This does the following things, rendering from | 
|
     * back to front. | 
|
     * <ol> | 
|
     * <li> | 
|
     * If the component is marked as opaque, the background | 
|
     * is painted in the current background color of the | 
|
     * component. | 
|
     * <li> | 
|
     * The highlights (if any) are painted. | 
|
     * <li> | 
|
     * The view hierarchy is painted. | 
|
     * <li> | 
|
     * The caret is painted. | 
|
     * </ol> | 
|
     * | 
|
     * @param g the graphics context | 
|
*/  | 
|
protected void paintSafely(Graphics g) {  | 
|
painted = true;  | 
|
Highlighter highlighter = editor.getHighlighter();  | 
|
Caret caret = editor.getCaret();  | 
|
        // paint the background | 
|
        if (editor.isOpaque()) { | 
|
paintBackground(g);  | 
|
}  | 
|
        // paint the highlights | 
|
if (highlighter != null) {  | 
|
highlighter.paint(g);  | 
|
}  | 
|
        // paint the view hierarchy | 
|
Rectangle alloc = getVisibleEditorRect();  | 
|
if (alloc != null) {  | 
|
rootView.paint(g, alloc);  | 
|
}  | 
|
        // paint the caret | 
|
if (caret != null) {  | 
|
caret.paint(g);  | 
|
}  | 
|
        if (dropCaret != null) { | 
|
dropCaret.paint(g);  | 
|
}  | 
|
}  | 
|
// --- ComponentUI methods --------------------------------------------  | 
|
    /** | 
|
     * Installs the UI for a component.  This does the following | 
|
     * things. | 
|
     * <ol> | 
|
     * <li> | 
|
     * Sets the associated component to opaque if the opaque property | 
|
     * has not already been set by the client program. This will cause the | 
|
     * component's background color to be painted. | 
|
     * <li> | 
|
     * Installs the default caret and highlighter into the | 
|
     * associated component. These properties are only set if their | 
|
     * current value is either {@code null} or an instance of | 
|
     * {@link UIResource}. | 
|
     * <li> | 
|
     * Attaches to the editor and model.  If there is no | 
|
     * model, a default one is created. | 
|
     * <li> | 
|
     * Creates the view factory and the view hierarchy used | 
|
     * to represent the model. | 
|
     * </ol> | 
|
     * | 
|
     * @param c the editor component | 
|
     * @see ComponentUI#installUI | 
|
*/  | 
|
public void installUI(JComponent c) {  | 
|
if (c instanceof JTextComponent) {  | 
|
editor = (JTextComponent) c;  | 
|
// common case is background painted... this can  | 
|
// easily be changed by subclasses or from outside  | 
|
            // of the component. | 
|
LookAndFeel.installProperty(editor, "opaque", Boolean.TRUE);  | 
|
LookAndFeel.installProperty(editor, "autoscrolls", Boolean.TRUE);  | 
|
            // install defaults | 
|
installDefaults();  | 
|
installDefaults2();  | 
|
            // attach to the model and editor | 
|
editor.addPropertyChangeListener(updateHandler);  | 
|
Document doc = editor.getDocument();  | 
|
if (doc == null) {  | 
|
// no model, create a default one. This will  | 
|
// fire a notification to the updateHandler  | 
|
                // which takes care of the rest. | 
|
editor.setDocument(getEditorKit(editor).createDefaultDocument());  | 
|
            } else { | 
|
doc.addDocumentListener(updateHandler);  | 
|
modelChanged();  | 
|
}  | 
|
            // install keymap | 
|
installListeners();  | 
|
installKeyboardActions();  | 
|
LayoutManager oldLayout = editor.getLayout();  | 
|
if ((oldLayout == null) || (oldLayout instanceof UIResource)) {  | 
|
// by default, use default LayoutManger implementation that  | 
|
                // will position the components associated with a View object. | 
|
editor.setLayout(updateHandler);  | 
|
}  | 
|
updateBackground(editor);  | 
|
        } else { | 
|
throw new Error("TextUI needs JTextComponent");  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Deinstalls the UI for a component.  This removes the listeners, | 
|
     * uninstalls the highlighter, removes views, and nulls out the keymap. | 
|
     * | 
|
     * @param c the editor component | 
|
     * @see ComponentUI#uninstallUI | 
|
*/  | 
|
public void uninstallUI(JComponent c) {  | 
|
        // detach from the model | 
|
editor.removePropertyChangeListener(updateHandler);  | 
|
editor.getDocument().removeDocumentListener(updateHandler);  | 
|
        // view part | 
|
painted = false;  | 
|
uninstallDefaults();  | 
|
rootView.setView(null);  | 
|
c.removeAll();  | 
|
LayoutManager lm = c.getLayout();  | 
|
if (lm instanceof UIResource) {  | 
|
c.setLayout(null);  | 
|
}  | 
|
        // controller part | 
|
uninstallKeyboardActions();  | 
|
uninstallListeners();  | 
|
editor = null;  | 
|
}  | 
|
    /** | 
|
     * Superclass paints background in an uncontrollable way | 
|
     * (i.e. one might want an image tiled into the background). | 
|
     * To prevent this from happening twice, this method is | 
|
     * reimplemented to simply paint. | 
|
     * <p> | 
|
     * <em>NOTE:</em> NOTE: Superclass is also not thread-safe in its | 
|
     * rendering of the background, although that is not an issue with the | 
|
     * default rendering. | 
|
*/  | 
|
public void update(Graphics g, JComponent c) {  | 
|
paint(g, c);  | 
|
}  | 
|
    /** | 
|
     * Paints the interface.  This is routed to the | 
|
     * paintSafely method under the guarantee that | 
|
     * the model won't change from the view of this thread | 
|
     * while it's rendering (if the associated model is | 
|
     * derived from AbstractDocument).  This enables the | 
|
     * model to potentially be updated asynchronously. | 
|
     * | 
|
     * @param g the graphics context | 
|
     * @param c the editor component | 
|
*/  | 
|
public final void paint(Graphics g, JComponent c) {  | 
|
        if ((rootView.getViewCount() > 0) && (rootView.getView(0) != null)) { | 
|
Document doc = editor.getDocument();  | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readLock();  | 
|
}  | 
|
            try { | 
|
paintSafely(g);  | 
|
            } finally { | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readUnlock();  | 
|
}  | 
|
}  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Gets the preferred size for the editor component.  If the component | 
|
     * has been given a size prior to receiving this request, it will | 
|
     * set the size of the view hierarchy to reflect the size of the component | 
|
     * before requesting the preferred size of the view hierarchy.  This | 
|
     * allows formatted views to format to the current component size before | 
|
     * answering the request.  Other views don't care about currently formatted | 
|
     * size and give the same answer either way. | 
|
     * | 
|
     * @param c the editor component | 
|
     * @return the size | 
|
*/  | 
|
public Dimension getPreferredSize(JComponent c) {  | 
|
Document doc = editor.getDocument();  | 
|
Insets i = c.getInsets();  | 
|
Dimension d = c.getSize();  | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readLock();  | 
|
}  | 
|
        try { | 
|
if ((d.width > (i.left + i.right)) && (d.height > (i.top + i.bottom))) {  | 
|
rootView.setSize(d.width - i.left - i.right, d.height - i.top - i.bottom);  | 
|
}  | 
|
else if (d.width == 0 && d.height == 0) {  | 
|
// Probably haven't been layed out yet, force some sort of  | 
|
                // initial sizing. | 
|
rootView.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);  | 
|
}  | 
|
d.width = (int) Math.min((long) rootView.getPreferredSpan(View.X_AXIS) +  | 
|
(long) i.left + (long) i.right, Integer.MAX_VALUE);  | 
|
d.height = (int) Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) +  | 
|
(long) i.top + (long) i.bottom, Integer.MAX_VALUE);  | 
|
        } finally { | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readUnlock();  | 
|
}  | 
|
}  | 
|
return d;  | 
|
}  | 
|
    /** | 
|
     * Gets the minimum size for the editor component. | 
|
     * | 
|
     * @param c the editor component | 
|
     * @return the size | 
|
*/  | 
|
public Dimension getMinimumSize(JComponent c) {  | 
|
Document doc = editor.getDocument();  | 
|
Insets i = c.getInsets();  | 
|
Dimension d = new Dimension();  | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readLock();  | 
|
}  | 
|
        try { | 
|
d.width = (int) rootView.getMinimumSpan(View.X_AXIS) + i.left + i.right;  | 
|
d.height = (int) rootView.getMinimumSpan(View.Y_AXIS) + i.top + i.bottom;  | 
|
        } finally { | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readUnlock();  | 
|
}  | 
|
}  | 
|
return d;  | 
|
}  | 
|
    /** | 
|
     * Gets the maximum size for the editor component. | 
|
     * | 
|
     * @param c the editor component | 
|
     * @return the size | 
|
*/  | 
|
public Dimension getMaximumSize(JComponent c) {  | 
|
Document doc = editor.getDocument();  | 
|
Insets i = c.getInsets();  | 
|
Dimension d = new Dimension();  | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readLock();  | 
|
}  | 
|
        try { | 
|
d.width = (int) Math.min((long) rootView.getMaximumSpan(View.X_AXIS) +  | 
|
(long) i.left + (long) i.right, Integer.MAX_VALUE);  | 
|
d.height = (int) Math.min((long) rootView.getMaximumSpan(View.Y_AXIS) +  | 
|
(long) i.top + (long) i.bottom, Integer.MAX_VALUE);  | 
|
        } finally { | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readUnlock();  | 
|
}  | 
|
}  | 
|
return d;  | 
|
}  | 
|
// ---- TextUI methods -------------------------------------------  | 
|
    /** | 
|
     * Gets the allocation to give the root View.  Due | 
|
     * to an unfortunate set of historical events this | 
|
     * method is inappropriately named.  The Rectangle | 
|
     * returned has nothing to do with visibility. | 
|
     * The component must have a non-zero positive size for | 
|
     * this translation to be computed. | 
|
     * | 
|
     * @return the bounding box for the root view | 
|
*/  | 
|
protected Rectangle getVisibleEditorRect() {  | 
|
Rectangle alloc = editor.getBounds();  | 
|
if ((alloc.width > 0) && (alloc.height > 0)) {  | 
|
alloc.x = alloc.y = 0;  | 
|
Insets insets = editor.getInsets();  | 
|
alloc.x += insets.left;  | 
|
alloc.y += insets.top;  | 
|
alloc.width -= insets.left + insets.right;  | 
|
alloc.height -= insets.top + insets.bottom;  | 
|
return alloc;  | 
|
}  | 
|
return null;  | 
|
}  | 
|
    /** | 
|
     * Converts the given location in the model to a place in | 
|
     * the view coordinate system. | 
|
     * The component must have a non-zero positive size for | 
|
     * this translation to be computed. | 
|
     * | 
|
     * @param tc the text component for which this UI is installed | 
|
     * @param pos the local location in the model to translate >= 0 | 
|
     * @return the coordinates as a rectangle, null if the model is not painted | 
|
     * @exception BadLocationException  if the given position does not | 
|
     *   represent a valid location in the associated document | 
|
     * @see TextUI#modelToView | 
|
*/  | 
|
public Rectangle modelToView(JTextComponent tc, int pos) throws BadLocationException {  | 
|
return modelToView(tc, pos, Position.Bias.Forward);  | 
|
}  | 
|
    /** | 
|
     * Converts the given location in the model to a place in | 
|
     * the view coordinate system. | 
|
     * The component must have a non-zero positive size for | 
|
     * this translation to be computed. | 
|
     * | 
|
     * @param tc the text component for which this UI is installed | 
|
     * @param pos the local location in the model to translate >= 0 | 
|
     * @return the coordinates as a rectangle, null if the model is not painted | 
|
     * @exception BadLocationException  if the given position does not | 
|
     *   represent a valid location in the associated document | 
|
     * @see TextUI#modelToView | 
|
*/  | 
|
public Rectangle modelToView(JTextComponent tc, int pos, Position.Bias bias) throws BadLocationException {  | 
|
Document doc = editor.getDocument();  | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readLock();  | 
|
}  | 
|
        try { | 
|
Rectangle alloc = getVisibleEditorRect();  | 
|
if (alloc != null) {  | 
|
rootView.setSize(alloc.width, alloc.height);  | 
|
Shape s = rootView.modelToView(pos, alloc, bias);  | 
|
if (s != null) {  | 
|
return s.getBounds();  | 
|
}  | 
|
}  | 
|
        } finally { | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readUnlock();  | 
|
}  | 
|
}  | 
|
return null;  | 
|
}  | 
|
    /** | 
|
     * Converts the given place in the view coordinate system | 
|
     * to the nearest representative location in the model. | 
|
     * The component must have a non-zero positive size for | 
|
     * this translation to be computed. | 
|
     * | 
|
     * @param tc the text component for which this UI is installed | 
|
     * @param pt the location in the view to translate.  This | 
|
     *  should be in the same coordinate system as the mouse events. | 
|
     * @return the offset from the start of the document >= 0, | 
|
     *   -1 if not painted | 
|
     * @see TextUI#viewToModel | 
|
*/  | 
|
public int viewToModel(JTextComponent tc, Point pt) {  | 
|
return viewToModel(tc, pt, discardBias);  | 
|
}  | 
|
    /** | 
|
     * Converts the given place in the view coordinate system | 
|
     * to the nearest representative location in the model. | 
|
     * The component must have a non-zero positive size for | 
|
     * this translation to be computed. | 
|
     * | 
|
     * @param tc the text component for which this UI is installed | 
|
     * @param pt the location in the view to translate.  This | 
|
     *  should be in the same coordinate system as the mouse events. | 
|
     * @return the offset from the start of the document >= 0, | 
|
     *   -1 if the component doesn't yet have a positive size. | 
|
     * @see TextUI#viewToModel | 
|
*/  | 
|
public int viewToModel(JTextComponent tc, Point pt,  | 
|
Position.Bias[] biasReturn) {  | 
|
int offs = -1;  | 
|
Document doc = editor.getDocument();  | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readLock();  | 
|
}  | 
|
        try { | 
|
Rectangle alloc = getVisibleEditorRect();  | 
|
if (alloc != null) {  | 
|
rootView.setSize(alloc.width, alloc.height);  | 
|
offs = rootView.viewToModel(pt.x, pt.y, alloc, biasReturn);  | 
|
}  | 
|
        } finally { | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readUnlock();  | 
|
}  | 
|
}  | 
|
return offs;  | 
|
}  | 
|
    /** | 
|
     * {@inheritDoc} | 
|
*/  | 
|
public int getNextVisualPositionFrom(JTextComponent t, int pos,  | 
|
Position.Bias b, int direction, Position.Bias[] biasRet)  | 
|
throws BadLocationException{  | 
|
Document doc = editor.getDocument();  | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readLock();  | 
|
}  | 
|
        try { | 
|
            if (painted) { | 
|
Rectangle alloc = getVisibleEditorRect();  | 
|
if (alloc != null) {  | 
|
rootView.setSize(alloc.width, alloc.height);  | 
|
}  | 
|
return rootView.getNextVisualPositionFrom(pos, b, alloc, direction,  | 
|
biasRet);  | 
|
}  | 
|
        } finally { | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readUnlock();  | 
|
}  | 
|
}  | 
|
return -1;  | 
|
}  | 
|
    /** | 
|
     * Causes the portion of the view responsible for the | 
|
     * given part of the model to be repainted.  Does nothing if | 
|
     * the view is not currently painted. | 
|
     * | 
|
     * @param tc the text component for which this UI is installed | 
|
     * @param p0 the beginning of the range >= 0 | 
|
     * @param p1 the end of the range >= p0 | 
|
     * @see TextUI#damageRange | 
|
*/  | 
|
public void damageRange(JTextComponent tc, int p0, int p1) {  | 
|
damageRange(tc, p0, p1, Position.Bias.Forward, Position.Bias.Backward);  | 
|
}  | 
|
    /** | 
|
     * Causes the portion of the view responsible for the | 
|
     * given part of the model to be repainted. | 
|
     * | 
|
     * @param p0 the beginning of the range >= 0 | 
|
     * @param p1 the end of the range >= p0 | 
|
*/  | 
|
public void damageRange(JTextComponent t, int p0, int p1,  | 
|
Position.Bias p0Bias, Position.Bias p1Bias) {  | 
|
        if (painted) { | 
|
Rectangle alloc = getVisibleEditorRect();  | 
|
if (alloc != null) {  | 
|
Document doc = t.getDocument();  | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readLock();  | 
|
}  | 
|
                try { | 
|
rootView.setSize(alloc.width, alloc.height);  | 
|
Shape toDamage = rootView.modelToView(p0, p0Bias,  | 
|
p1, p1Bias, alloc);  | 
|
Rectangle rect = (toDamage instanceof Rectangle) ?  | 
|
(Rectangle)toDamage : toDamage.getBounds();  | 
|
editor.repaint(rect.x, rect.y, rect.width, rect.height);  | 
|
} catch (BadLocationException e) {  | 
|
                } finally { | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readUnlock();  | 
|
}  | 
|
}  | 
|
}  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Fetches the EditorKit for the UI. | 
|
     * | 
|
     * @param tc the text component for which this UI is installed | 
|
     * @return the editor capabilities | 
|
     * @see TextUI#getEditorKit | 
|
*/  | 
|
public EditorKit getEditorKit(JTextComponent tc) {  | 
|
return defaultKit;  | 
|
}  | 
|
    /** | 
|
     * Fetches a View with the allocation of the associated | 
|
     * text component (i.e. the root of the hierarchy) that | 
|
     * can be traversed to determine how the model is being | 
|
     * represented spatially. | 
|
     * <p> | 
|
     * <font color=red><b>NOTE:</b>The View hierarchy can | 
|
     * be traversed from the root view, and other things | 
|
     * can be done as well.  Things done in this way cannot | 
|
     * be protected like simple method calls through the TextUI. | 
|
     * Therefore, proper operation in the presence of concurrency | 
|
     * must be arranged by any logic that calls this method! | 
|
     * </font> | 
|
     * | 
|
     * @param tc the text component for which this UI is installed | 
|
     * @return the view | 
|
     * @see TextUI#getRootView | 
|
*/  | 
|
public View getRootView(JTextComponent tc) {  | 
|
return rootView;  | 
|
}  | 
|
    /** | 
|
     * Returns the string to be used as the tooltip at the passed in location. | 
|
     * This forwards the method onto the root View. | 
|
     * | 
|
     * @see javax.swing.text.JTextComponent#getToolTipText | 
|
     * @see javax.swing.text.View#getToolTipText | 
|
     * @since 1.4 | 
|
*/  | 
|
public String getToolTipText(JTextComponent t, Point pt) {  | 
|
        if (!painted) { | 
|
return null;  | 
|
}  | 
|
Document doc = editor.getDocument();  | 
|
String tt = null;  | 
|
Rectangle alloc = getVisibleEditorRect();  | 
|
if (alloc != null) {  | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readLock();  | 
|
}  | 
|
            try { | 
|
tt = rootView.getToolTipText(pt.x, pt.y, alloc);  | 
|
            } finally { | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readUnlock();  | 
|
}  | 
|
}  | 
|
}  | 
|
return tt;  | 
|
}  | 
|
// --- ViewFactory methods ------------------------------  | 
|
    /** | 
|
     * Creates a view for an element. | 
|
     * If a subclass wishes to directly implement the factory | 
|
     * producing the view(s), it should reimplement this | 
|
     * method.  By default it simply returns null indicating | 
|
     * it is unable to represent the element. | 
|
     * | 
|
     * @param elem the element | 
|
     * @return the view | 
|
*/  | 
|
public View create(Element elem) {  | 
|
return null;  | 
|
}  | 
|
    /** | 
|
     * Creates a view for an element. | 
|
     * If a subclass wishes to directly implement the factory | 
|
     * producing the view(s), it should reimplement this | 
|
     * method.  By default it simply returns null indicating | 
|
     * it is unable to represent the part of the element. | 
|
     * | 
|
     * @param elem the element | 
|
     * @param p0 the starting offset >= 0 | 
|
     * @param p1 the ending offset >= p0 | 
|
     * @return the view | 
|
*/  | 
|
public View create(Element elem, int p0, int p1) {  | 
|
return null;  | 
|
}  | 
|
public static class BasicCaret extends DefaultCaret implements UIResource {}  | 
|
public static class BasicHighlighter extends DefaultHighlighter implements UIResource {}  | 
|
static class BasicCursor extends Cursor implements UIResource {  | 
|
        BasicCursor(int type) { | 
|
super(type);  | 
|
}  | 
|
BasicCursor(String name) {  | 
|
super(name);  | 
|
}  | 
|
}  | 
|
private static BasicCursor textCursor = new BasicCursor(Cursor.TEXT_CURSOR);  | 
|
// ----- member variables ---------------------------------------  | 
|
private static final EditorKit defaultKit = new DefaultEditorKit();  | 
|
transient JTextComponent editor;  | 
|
transient boolean painted;  | 
|
transient RootView rootView = new RootView();  | 
|
transient UpdateHandler updateHandler = new UpdateHandler();  | 
|
private static final TransferHandler defaultTransferHandler = new TextTransferHandler();  | 
|
private final DragListener dragListener = getDragListener();  | 
|
private static final Position.Bias[] discardBias = new Position.Bias[1];  | 
|
private DefaultCaret dropCaret;  | 
|
    /** | 
|
     * Root view that acts as a gateway between the component | 
|
     * and the View hierarchy. | 
|
*/  | 
|
class RootView extends View {  | 
|
        RootView() { | 
|
super(null);  | 
|
}  | 
|
void setView(View v) {  | 
|
View oldView = view;  | 
|
view = null;  | 
|
if (oldView != null) {  | 
|
// get rid of back reference so that the old  | 
|
                // hierarchy can be garbage collected. | 
|
oldView.setParent(null);  | 
|
}  | 
|
if (v != null) {  | 
|
v.setParent(this);  | 
|
}  | 
|
view = v;  | 
|
}  | 
|
        /** | 
|
         * Fetches the attributes to use when rendering.  At the root | 
|
         * level there are no attributes.  If an attribute is resolved | 
|
         * up the view hierarchy this is the end of the line. | 
|
*/  | 
|
public AttributeSet getAttributes() {  | 
|
return null;  | 
|
}  | 
|
        /** | 
|
         * Determines the preferred span for this view along an axis. | 
|
         * | 
|
         * @param axis may be either X_AXIS or Y_AXIS | 
|
         * @return the span the view would like to be rendered into. | 
|
         *         Typically the view is told to render into the span | 
|
         *         that is returned, although there is no guarantee. | 
|
         *         The parent may choose to resize or break the view. | 
|
*/  | 
|
        public float getPreferredSpan(int axis) { | 
|
            if (view != null) { | 
|
return view.getPreferredSpan(axis);  | 
|
}  | 
|
return 10;  | 
|
}  | 
|
        /** | 
|
         * Determines the minimum span for this view along an axis. | 
|
         * | 
|
         * @param axis may be either X_AXIS or Y_AXIS | 
|
         * @return the span the view would like to be rendered into. | 
|
         *         Typically the view is told to render into the span | 
|
         *         that is returned, although there is no guarantee. | 
|
         *         The parent may choose to resize or break the view. | 
|
*/  | 
|
        public float getMinimumSpan(int axis) { | 
|
            if (view != null) { | 
|
return view.getMinimumSpan(axis);  | 
|
}  | 
|
return 10;  | 
|
}  | 
|
        /** | 
|
         * Determines the maximum span for this view along an axis. | 
|
         * | 
|
         * @param axis may be either X_AXIS or Y_AXIS | 
|
         * @return the span the view would like to be rendered into. | 
|
         *         Typically the view is told to render into the span | 
|
         *         that is returned, although there is no guarantee. | 
|
         *         The parent may choose to resize or break the view. | 
|
*/  | 
|
        public float getMaximumSpan(int axis) { | 
|
return Integer.MAX_VALUE;  | 
|
}  | 
|
        /** | 
|
         * Specifies that a preference has changed. | 
|
         * Child views can call this on the parent to indicate that | 
|
         * the preference has changed.  The root view routes this to | 
|
         * invalidate on the hosting component. | 
|
         * <p> | 
|
         * This can be called on a different thread from the | 
|
         * event dispatching thread and is basically unsafe to | 
|
         * propagate into the component.  To make this safe, | 
|
         * the operation is transferred over to the event dispatching | 
|
         * thread for completion.  It is a design goal that all view | 
|
         * methods be safe to call without concern for concurrency, | 
|
         * and this behavior helps make that true. | 
|
         * | 
|
         * @param child the child view | 
|
         * @param width true if the width preference has changed | 
|
         * @param height true if the height preference has changed | 
|
*/  | 
|
public void preferenceChanged(View child, boolean width, boolean height) {  | 
|
editor.revalidate();  | 
|
}  | 
|
        /** | 
|
         * Determines the desired alignment for this view along an axis. | 
|
         * | 
|
         * @param axis may be either X_AXIS or Y_AXIS | 
|
         * @return the desired alignment, where 0.0 indicates the origin | 
|
         *     and 1.0 the full span away from the origin | 
|
*/  | 
|
        public float getAlignment(int axis) { | 
|
            if (view != null) { | 
|
return view.getAlignment(axis);  | 
|
}  | 
|
return 0;  | 
|
}  | 
|
        /** | 
|
         * Renders the view. | 
|
         * | 
|
         * @param g the graphics context | 
|
         * @param allocation the region to render into | 
|
*/  | 
|
public void paint(Graphics g, Shape allocation) {  | 
|
            if (view != null) { | 
|
Rectangle alloc = (allocation instanceof Rectangle) ?  | 
|
(Rectangle)allocation : allocation.getBounds();  | 
|
setSize(alloc.width, alloc.height);  | 
|
view.paint(g, allocation);  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * Sets the view parent. | 
|
         * | 
|
         * @param parent the parent view | 
|
*/  | 
|
public void setParent(View parent) {  | 
|
throw new Error("Can't set parent on root view");  | 
|
}  | 
|
        /** | 
|
         * Returns the number of views in this view.  Since | 
|
         * this view simply wraps the root of the view hierarchy | 
|
         * it has exactly one child. | 
|
         * | 
|
         * @return the number of views | 
|
         * @see #getView | 
|
*/  | 
|
        public int getViewCount() { | 
|
return 1;  | 
|
}  | 
|
        /** | 
|
         * Gets the n-th view in this container. | 
|
         * | 
|
         * @param n the number of the view to get | 
|
         * @return the view | 
|
*/  | 
|
public View getView(int n) {  | 
|
return view;  | 
|
}  | 
|
        /** | 
|
         * Returns the child view index representing the given position in | 
|
         * the model.  This is implemented to return the index of the only | 
|
         * child. | 
|
         * | 
|
         * @param pos the position >= 0 | 
|
         * @return  index of the view representing the given position, or | 
|
         *   -1 if no view represents that position | 
|
         * @since 1.3 | 
|
*/  | 
|
public int getViewIndex(int pos, Position.Bias b) {  | 
|
return 0;  | 
|
}  | 
|
        /** | 
|
         * Fetches the allocation for the given child view. | 
|
         * This enables finding out where various views | 
|
         * are located, without assuming the views store | 
|
         * their location.  This returns the given allocation | 
|
         * since this view simply acts as a gateway between | 
|
         * the view hierarchy and the associated component. | 
|
         * | 
|
         * @param index the index of the child | 
|
         * @param a  the allocation to this view. | 
|
         * @return the allocation to the child | 
|
*/  | 
|
public Shape getChildAllocation(int index, Shape a) {  | 
|
return a;  | 
|
}  | 
|
        /** | 
|
         * Provides a mapping from the document model coordinate space | 
|
         * to the coordinate space of the view mapped to it. | 
|
         * | 
|
         * @param pos the position to convert | 
|
         * @param a the allocated region to render into | 
|
         * @return the bounding box of the given position | 
|
*/  | 
|
public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {  | 
|
            if (view != null) { | 
|
return view.modelToView(pos, a, b);  | 
|
}  | 
|
return null;  | 
|
}  | 
|
        /** | 
|
         * Provides a mapping from the document model coordinate space | 
|
         * to the coordinate space of the view mapped to it. | 
|
         * | 
|
         * @param p0 the position to convert >= 0 | 
|
         * @param b0 the bias toward the previous character or the | 
|
         *  next character represented by p0, in case the | 
|
         *  position is a boundary of two views. | 
|
         * @param p1 the position to convert >= 0 | 
|
         * @param b1 the bias toward the previous character or the | 
|
         *  next character represented by p1, in case the | 
|
         *  position is a boundary of two views. | 
|
         * @param a the allocated region to render into | 
|
         * @return the bounding box of the given position is returned | 
|
         * @exception BadLocationException  if the given position does | 
|
         *   not represent a valid location in the associated document | 
|
         * @exception IllegalArgumentException for an invalid bias argument | 
|
         * @see View#viewToModel | 
|
*/  | 
|
public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a) throws BadLocationException {  | 
|
            if (view != null) { | 
|
return view.modelToView(p0, b0, p1, b1, a);  | 
|
}  | 
|
return null;  | 
|
}  | 
|
        /** | 
|
         * Provides a mapping from the view coordinate space to the logical | 
|
         * coordinate space of the model. | 
|
         * | 
|
         * @param x x coordinate of the view location to convert | 
|
         * @param y y coordinate of the view location to convert | 
|
         * @param a the allocated region to render into | 
|
         * @return the location within the model that best represents the | 
|
         *    given point in the view | 
|
*/  | 
|
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {  | 
|
            if (view != null) { | 
|
int retValue = view.viewToModel(x, y, a, bias);  | 
|
return retValue;  | 
|
}  | 
|
return -1;  | 
|
}  | 
|
        /** | 
|
         * Provides a way to determine the next visually represented model | 
|
         * location that one might place a caret.  Some views may not be visible, | 
|
         * they might not be in the same order found in the model, or they just | 
|
         * might not allow access to some of the locations in the model. | 
|
         * This method enables specifying a position to convert | 
|
         * within the range of >=0.  If the value is -1, a position | 
|
         * will be calculated automatically.  If the value < -1, | 
|
         * the {@code BadLocationException} will be thrown. | 
|
         * | 
|
         * @param pos the position to convert >= 0 | 
|
         * @param a the allocated region to render into | 
|
         * @param direction the direction from the current position that can | 
|
         *  be thought of as the arrow keys typically found on a keyboard. | 
|
         *  This may be SwingConstants.WEST, SwingConstants.EAST, | 
|
         *  SwingConstants.NORTH, or SwingConstants.SOUTH. | 
|
         * @return the location within the model that best represents the next | 
|
         *  location visual position. | 
|
         * @exception BadLocationException the given position is not a valid | 
|
         *                                 position within the document | 
|
         * @exception IllegalArgumentException for an invalid direction | 
|
*/  | 
|
public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,  | 
|
int direction,  | 
|
Position.Bias[] biasRet)  | 
|
throws BadLocationException {  | 
|
if (pos < -1) {  | 
|
throw new BadLocationException("invalid position", pos);  | 
|
}  | 
|
            if( view != null ) { | 
|
int nextPos = view.getNextVisualPositionFrom(pos, b, a,  | 
|
direction, biasRet);  | 
|
if(nextPos != -1) {  | 
|
pos = nextPos;  | 
|
}  | 
|
                else { | 
|
biasRet[0] = b;  | 
|
}  | 
|
}  | 
|
return pos;  | 
|
}  | 
|
        /** | 
|
         * Gives notification that something was inserted into the document | 
|
         * in a location that this view is responsible for. | 
|
         * | 
|
         * @param e the change information from the associated document | 
|
         * @param a the current allocation of the view | 
|
         * @param f the factory to use to rebuild if the view has children | 
|
*/  | 
|
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {  | 
|
            if (view != null) { | 
|
view.insertUpdate(e, a, f);  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * Gives notification that something was removed from the document | 
|
         * in a location that this view is responsible for. | 
|
         * | 
|
         * @param e the change information from the associated document | 
|
         * @param a the current allocation of the view | 
|
         * @param f the factory to use to rebuild if the view has children | 
|
*/  | 
|
public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {  | 
|
            if (view != null) { | 
|
view.removeUpdate(e, a, f);  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * Gives notification from the document that attributes were changed | 
|
         * in a location that this view is responsible for. | 
|
         * | 
|
         * @param e the change information from the associated document | 
|
         * @param a the current allocation of the view | 
|
         * @param f the factory to use to rebuild if the view has children | 
|
*/  | 
|
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {  | 
|
            if (view != null) { | 
|
view.changedUpdate(e, a, f);  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * Returns the document model underlying the view. | 
|
         * | 
|
         * @return the model | 
|
*/  | 
|
public Document getDocument() {  | 
|
return editor.getDocument();  | 
|
}  | 
|
        /** | 
|
         * Returns the starting offset into the model for this view. | 
|
         * | 
|
         * @return the starting offset | 
|
*/  | 
|
        public int getStartOffset() { | 
|
            if (view != null) { | 
|
return view.getStartOffset();  | 
|
}  | 
|
return getElement().getStartOffset();  | 
|
}  | 
|
        /** | 
|
         * Returns the ending offset into the model for this view. | 
|
         * | 
|
         * @return the ending offset | 
|
*/  | 
|
        public int getEndOffset() { | 
|
            if (view != null) { | 
|
return view.getEndOffset();  | 
|
}  | 
|
return getElement().getEndOffset();  | 
|
}  | 
|
        /** | 
|
         * Gets the element that this view is mapped to. | 
|
         * | 
|
         * @return the view | 
|
*/  | 
|
public Element getElement() {  | 
|
            if (view != null) { | 
|
return view.getElement();  | 
|
}  | 
|
return editor.getDocument().getDefaultRootElement();  | 
|
}  | 
|
        /** | 
|
         * Breaks this view on the given axis at the given length. | 
|
         * | 
|
         * @param axis may be either X_AXIS or Y_AXIS | 
|
         * @param len specifies where a break is desired in the span | 
|
         * @param the current allocation of the view | 
|
         * @return the fragment of the view that represents the given span | 
|
         *   if the view can be broken, otherwise null | 
|
*/  | 
|
public View breakView(int axis, float len, Shape a) {  | 
|
throw new Error("Can't break root view");  | 
|
}  | 
|
        /** | 
|
         * Determines the resizability of the view along the | 
|
         * given axis.  A value of 0 or less is not resizable. | 
|
         * | 
|
         * @param axis may be either X_AXIS or Y_AXIS | 
|
         * @return the weight | 
|
*/  | 
|
        public int getResizeWeight(int axis) { | 
|
            if (view != null) { | 
|
return view.getResizeWeight(axis);  | 
|
}  | 
|
return 0;  | 
|
}  | 
|
        /** | 
|
         * Sets the view size. | 
|
         * | 
|
         * @param width the width | 
|
         * @param height the height | 
|
*/  | 
|
        public void setSize(float width, float height) { | 
|
            if (view != null) { | 
|
view.setSize(width, height);  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * Fetches the container hosting the view.  This is useful for | 
|
         * things like scheduling a repaint, finding out the host | 
|
         * components font, etc.  The default implementation | 
|
         * of this is to forward the query to the parent view. | 
|
         * | 
|
         * @return the container | 
|
*/  | 
|
public Container getContainer() {  | 
|
return editor;  | 
|
}  | 
|
        /** | 
|
         * Fetches the factory to be used for building the | 
|
         * various view fragments that make up the view that | 
|
         * represents the model.  This is what determines | 
|
         * how the model will be represented.  This is implemented | 
|
         * to fetch the factory provided by the associated | 
|
         * EditorKit unless that is null, in which case this | 
|
         * simply returns the BasicTextUI itself which allows | 
|
         * subclasses to implement a simple factory directly without | 
|
         * creating extra objects. | 
|
         * | 
|
         * @return the factory | 
|
*/  | 
|
public ViewFactory getViewFactory() {  | 
|
EditorKit kit = getEditorKit(editor);  | 
|
ViewFactory f = kit.getViewFactory();  | 
|
if (f != null) {  | 
|
return f;  | 
|
}  | 
|
return BasicTextUI.this;  | 
|
}  | 
|
private View view;  | 
|
}  | 
|
    /** | 
|
     * Handles updates from various places.  If the model is changed, | 
|
     * this class unregisters as a listener to the old model and | 
|
     * registers with the new model.  If the document model changes, | 
|
     * the change is forwarded to the root view.  If the focus | 
|
     * accelerator changes, a new keystroke is registered to request | 
|
     * focus. | 
|
*/  | 
|
class UpdateHandler implements PropertyChangeListener, DocumentListener, LayoutManager2, UIResource {  | 
|
// --- PropertyChangeListener methods -----------------------  | 
|
        /** | 
|
         * This method gets called when a bound property is changed. | 
|
         * We are looking for document changes on the editor. | 
|
*/  | 
|
public final void propertyChange(PropertyChangeEvent evt) {  | 
|
Object oldValue = evt.getOldValue();  | 
|
Object newValue = evt.getNewValue();  | 
|
String propertyName = evt.getPropertyName();  | 
|
if ((oldValue instanceof Document) || (newValue instanceof Document)) {  | 
|
if (oldValue != null) {  | 
|
((Document)oldValue).removeDocumentListener(this);  | 
|
i18nView = false;  | 
|
}  | 
|
if (newValue != null) {  | 
|
((Document)newValue).addDocumentListener(this);  | 
|
if ("document" == propertyName) {  | 
|
setView(null);  | 
|
BasicTextUI.this.propertyChange(evt);  | 
|
modelChanged();  | 
|
return;  | 
|
}  | 
|
}  | 
|
modelChanged();  | 
|
}  | 
|
if ("focusAccelerator" == propertyName) {  | 
|
updateFocusAcceleratorBinding(true);  | 
|
} else if ("componentOrientation" == propertyName) {  | 
|
// Changes in ComponentOrientation require the views to be  | 
|
                // rebuilt. | 
|
modelChanged();  | 
|
} else if ("font" == propertyName) {  | 
|
modelChanged();  | 
|
} else if ("dropLocation" == propertyName) {  | 
|
dropIndexChanged();  | 
|
} else if ("editable" == propertyName) {  | 
|
updateCursor();  | 
|
modelChanged();  | 
|
}  | 
|
BasicTextUI.this.propertyChange(evt);  | 
|
}  | 
|
        private void dropIndexChanged() { | 
|
if (editor.getDropMode() == DropMode.USE_SELECTION) {  | 
|
return;  | 
|
}  | 
|
JTextComponent.DropLocation dropLocation = editor.getDropLocation();  | 
|
if (dropLocation == null) {  | 
|
if (dropCaret != null) {  | 
|
dropCaret.deinstall(editor);  | 
|
editor.repaint(dropCaret);  | 
|
dropCaret = null;  | 
|
}  | 
|
            } else { | 
|
if (dropCaret == null) {  | 
|
dropCaret = new BasicCaret();  | 
|
dropCaret.install(editor);  | 
|
dropCaret.setVisible(true);  | 
|
}  | 
|
dropCaret.setDot(dropLocation.getIndex(),  | 
|
dropLocation.getBias());  | 
|
}  | 
|
}  | 
|
// --- DocumentListener methods -----------------------  | 
|
        /** | 
|
         * The insert notification.  Gets sent to the root of the view structure | 
|
         * that represents the portion of the model being represented by the | 
|
         * editor.  The factory is added as an argument to the update so that | 
|
         * the views can update themselves in a dynamic (not hardcoded) way. | 
|
         * | 
|
         * @param e  The change notification from the currently associated | 
|
         *  document. | 
|
         * @see DocumentListener#insertUpdate | 
|
*/  | 
|
public final void insertUpdate(DocumentEvent e) {  | 
|
Document doc = e.getDocument();  | 
|
Object o = doc.getProperty("i18n");  | 
|
if (o instanceof Boolean) {  | 
|
Boolean i18nFlag = (Boolean) o;  | 
|
if (i18nFlag.booleanValue() != i18nView) {  | 
|
                    // i18n flag changed, rebuild the view | 
|
i18nView = i18nFlag.booleanValue();  | 
|
modelChanged();  | 
|
return;  | 
|
}  | 
|
}  | 
|
            // normal insert update | 
|
Rectangle alloc = (painted) ? getVisibleEditorRect() : null;  | 
|
rootView.insertUpdate(e, alloc, rootView.getViewFactory());  | 
|
}  | 
|
        /** | 
|
         * The remove notification.  Gets sent to the root of the view structure | 
|
         * that represents the portion of the model being represented by the | 
|
         * editor.  The factory is added as an argument to the update so that | 
|
         * the views can update themselves in a dynamic (not hardcoded) way. | 
|
         * | 
|
         * @param e  The change notification from the currently associated | 
|
         *  document. | 
|
         * @see DocumentListener#removeUpdate | 
|
*/  | 
|
public final void removeUpdate(DocumentEvent e) {  | 
|
Rectangle alloc = (painted) ? getVisibleEditorRect() : null;  | 
|
rootView.removeUpdate(e, alloc, rootView.getViewFactory());  | 
|
}  | 
|
        /** | 
|
         * The change notification.  Gets sent to the root of the view structure | 
|
         * that represents the portion of the model being represented by the | 
|
         * editor.  The factory is added as an argument to the update so that | 
|
         * the views can update themselves in a dynamic (not hardcoded) way. | 
|
         * | 
|
         * @param e  The change notification from the currently associated | 
|
         *  document. | 
|
         * @see DocumentListener#changedUpdate(DocumentEvent) | 
|
*/  | 
|
public final void changedUpdate(DocumentEvent e) {  | 
|
Rectangle alloc = (painted) ? getVisibleEditorRect() : null;  | 
|
rootView.changedUpdate(e, alloc, rootView.getViewFactory());  | 
|
}  | 
|
// --- LayoutManager2 methods --------------------------------  | 
|
        /** | 
|
         * Adds the specified component with the specified name to | 
|
         * the layout. | 
|
         * @param name the component name | 
|
         * @param comp the component to be added | 
|
*/  | 
|
public void addLayoutComponent(String name, Component comp) {  | 
|
// not supported  | 
|
}  | 
|
        /** | 
|
         * Removes the specified component from the layout. | 
|
         * @param comp the component to be removed | 
|
*/  | 
|
public void removeLayoutComponent(Component comp) {  | 
|
            if (constraints != null) { | 
|
                // remove the constraint record | 
|
constraints.remove(comp);  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * Calculates the preferred size dimensions for the specified | 
|
         * panel given the components in the specified parent container. | 
|
         * @param parent the component to be laid out | 
|
         * | 
|
         * @see #minimumLayoutSize | 
|
*/  | 
|
public Dimension preferredLayoutSize(Container parent) {  | 
|
            // should not be called (JComponent uses UI instead) | 
|
return null;  | 
|
}  | 
|
        /** | 
|
         * Calculates the minimum size dimensions for the specified | 
|
         * panel given the components in the specified parent container. | 
|
         * @param parent the component to be laid out | 
|
         * @see #preferredLayoutSize | 
|
*/  | 
|
public Dimension minimumLayoutSize(Container parent) {  | 
|
            // should not be called (JComponent uses UI instead) | 
|
return null;  | 
|
}  | 
|
        /** | 
|
         * Lays out the container in the specified panel.  This is | 
|
         * implemented to position all components that were added | 
|
         * with a View object as a constraint.  The current allocation | 
|
         * of the associated View is used as the location of the | 
|
         * component. | 
|
         * <p> | 
|
         * A read-lock is acquired on the document to prevent the | 
|
         * view tree from being modified while the layout process | 
|
         * is active. | 
|
         * | 
|
         * @param parent the component which needs to be laid out | 
|
*/  | 
|
public void layoutContainer(Container parent) {  | 
|
            if ((constraints != null) && (! constraints.isEmpty())) { | 
|
Rectangle alloc = getVisibleEditorRect();  | 
|
if (alloc != null) {  | 
|
Document doc = editor.getDocument();  | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readLock();  | 
|
}  | 
|
                    try { | 
|
rootView.setSize(alloc.width, alloc.height);  | 
|
Enumeration<Component> components = constraints.keys();  | 
|
while (components.hasMoreElements()) {  | 
|
Component comp = components.nextElement();  | 
|
View v = (View) constraints.get(comp);  | 
|
Shape ca = calculateViewPosition(alloc, v);  | 
|
if (ca != null) {  | 
|
Rectangle compAlloc = (ca instanceof Rectangle) ?  | 
|
(Rectangle) ca : ca.getBounds();  | 
|
comp.setBounds(compAlloc);  | 
|
}  | 
|
}  | 
|
                    } finally { | 
|
if (doc instanceof AbstractDocument) {  | 
|
((AbstractDocument)doc).readUnlock();  | 
|
}  | 
|
}  | 
|
}  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * Find the Shape representing the given view. | 
|
*/  | 
|
Shape calculateViewPosition(Shape alloc, View v) {  | 
|
int pos = v.getStartOffset();  | 
|
View child = null;  | 
|
for (View parent = rootView; (parent != null) && (parent != v); parent = child) {  | 
|
int index = parent.getViewIndex(pos, Position.Bias.Forward);  | 
|
alloc = parent.getChildAllocation(index, alloc);  | 
|
child = parent.getView(index);  | 
|
}  | 
|
return (child != null) ? alloc : null;  | 
|
}  | 
|
        /** | 
|
         * Adds the specified component to the layout, using the specified | 
|
         * constraint object.  We only store those components that were added | 
|
         * with a constraint that is of type View. | 
|
         * | 
|
         * @param comp the component to be added | 
|
         * @param constraint  where/how the component is added to the layout. | 
|
*/  | 
|
public void addLayoutComponent(Component comp, Object constraint) {  | 
|
if (constraint instanceof View) {  | 
|
                if (constraints == null) { | 
|
constraints = new Hashtable<Component, Object>(7);  | 
|
}  | 
|
constraints.put(comp, constraint);  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * Returns the maximum size of this component. | 
|
         * @see java.awt.Component#getMinimumSize() | 
|
         * @see java.awt.Component#getPreferredSize() | 
|
         * @see LayoutManager | 
|
*/  | 
|
public Dimension maximumLayoutSize(Container target) {  | 
|
            // should not be called (JComponent uses UI instead) | 
|
return null;  | 
|
}  | 
|
        /** | 
|
         * Returns the alignment along the x axis.  This specifies how | 
|
         * the component would like to be aligned relative to other | 
|
         * components.  The value should be a number between 0 and 1 | 
|
         * where 0 represents alignment along the origin, 1 is aligned | 
|
         * the furthest away from the origin, 0.5 is centered, etc. | 
|
*/  | 
|
public float getLayoutAlignmentX(Container target) {  | 
|
return 0.5f;  | 
|
}  | 
|
        /** | 
|
         * Returns the alignment along the y axis.  This specifies how | 
|
         * the component would like to be aligned relative to other | 
|
         * components.  The value should be a number between 0 and 1 | 
|
         * where 0 represents alignment along the origin, 1 is aligned | 
|
         * the furthest away from the origin, 0.5 is centered, etc. | 
|
*/  | 
|
public float getLayoutAlignmentY(Container target) {  | 
|
return 0.5f;  | 
|
}  | 
|
        /** | 
|
         * Invalidates the layout, indicating that if the layout manager | 
|
         * has cached information it should be discarded. | 
|
*/  | 
|
public void invalidateLayout(Container target) {  | 
|
}  | 
|
        /** | 
|
         * The "layout constraints" for the LayoutManager2 implementation. | 
|
         * These are View objects for those components that are represented | 
|
         * by a View in the View tree. | 
|
*/  | 
|
private Hashtable<Component, Object> constraints;  | 
|
private boolean i18nView = false;  | 
|
}  | 
|
    /** | 
|
     * Wrapper for text actions to return isEnabled false in case editor is non editable | 
|
*/  | 
|
class TextActionWrapper extends TextAction {  | 
|
public TextActionWrapper(TextAction action) {  | 
|
super((String)action.getValue(Action.NAME));  | 
|
this.action = action;  | 
|
}  | 
|
        /** | 
|
         * The operation to perform when this action is triggered. | 
|
         * | 
|
         * @param e the action event | 
|
*/  | 
|
public void actionPerformed(ActionEvent e) {  | 
|
action.actionPerformed(e);  | 
|
}  | 
|
        public boolean isEnabled() { | 
|
return (editor == null || editor.isEditable()) ? action.isEnabled() : false;  | 
|
}  | 
|
TextAction action = null;  | 
|
}  | 
|
    /** | 
|
     * Registered in the ActionMap. | 
|
*/  | 
|
class FocusAction extends AbstractAction {  | 
|
public void actionPerformed(ActionEvent e) {  | 
|
editor.requestFocus();  | 
|
}  | 
|
        public boolean isEnabled() { | 
|
return editor.isEditable();  | 
|
}  | 
|
}  | 
|
    private static DragListener getDragListener() { | 
|
        synchronized(DragListener.class) { | 
|
DragListener listener =  | 
|
(DragListener)AppContext.getAppContext().  | 
|
get(DragListener.class);  | 
|
if (listener == null) {  | 
|
listener = new DragListener();  | 
|
AppContext.getAppContext().put(DragListener.class, listener);  | 
|
}  | 
|
return listener;  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Listens for mouse events for the purposes of detecting drag gestures. | 
|
     * BasicTextUI will maintain one of these per AppContext. | 
|
*/  | 
|
static class DragListener extends MouseInputAdapter  | 
|
implements BeforeDrag {  | 
|
private boolean dragStarted;  | 
|
public void dragStarting(MouseEvent me) {  | 
|
dragStarted = true;  | 
|
}  | 
|
public void mousePressed(MouseEvent e) {  | 
|
JTextComponent c = (JTextComponent)e.getSource();  | 
|
if (c.getDragEnabled()) {  | 
|
dragStarted = false;  | 
|
if (isDragPossible(e) && DragRecognitionSupport.mousePressed(e)) {  | 
|
e.consume();  | 
|
}  | 
|
}  | 
|
}  | 
|
public void mouseReleased(MouseEvent e) {  | 
|
JTextComponent c = (JTextComponent)e.getSource();  | 
|
if (c.getDragEnabled()) {  | 
|
if (dragStarted) {  | 
|
e.consume();  | 
|
}  | 
|
DragRecognitionSupport.mouseReleased(e);  | 
|
}  | 
|
}  | 
|
public void mouseDragged(MouseEvent e) {  | 
|
JTextComponent c = (JTextComponent)e.getSource();  | 
|
if (c.getDragEnabled()) {  | 
|
if (dragStarted || DragRecognitionSupport.mouseDragged(e, this)) {  | 
|
e.consume();  | 
|
}  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * Determines if the following are true: | 
|
         * <ul> | 
|
         * <li>the component is enabled | 
|
         * <li>the press event is located over a selection | 
|
         * </ul> | 
|
*/  | 
|
protected boolean isDragPossible(MouseEvent e) {  | 
|
JTextComponent c = (JTextComponent)e.getSource();  | 
|
if (c.isEnabled()) {  | 
|
Caret caret = c.getCaret();  | 
|
int dot = caret.getDot();  | 
|
int mark = caret.getMark();  | 
|
if (dot != mark) {  | 
|
Point p = new Point(e.getX(), e.getY());  | 
|
int pos = c.viewToModel(p);  | 
|
int p0 = Math.min(dot, mark);  | 
|
int p1 = Math.max(dot, mark);  | 
|
if ((pos >= p0) && (pos < p1)) {  | 
|
return true;  | 
|
}  | 
|
}  | 
|
}  | 
|
return false;  | 
|
}  | 
|
}  | 
|
static class TextTransferHandler extends TransferHandler implements UIResource {  | 
|
private JTextComponent exportComp;  | 
|
private boolean shouldRemove;  | 
|
private int p0;  | 
|
private int p1;  | 
|
        /** | 
|
         * Whether or not this is a drop using | 
|
         * <code>DropMode.INSERT</code>. | 
|
*/  | 
|
private boolean modeBetween = false;  | 
|
        /** | 
|
         * Whether or not this is a drop. | 
|
*/  | 
|
private boolean isDrop = false;  | 
|
        /** | 
|
         * The drop action. | 
|
*/  | 
|
private int dropAction = MOVE;  | 
|
        /** | 
|
         * The drop bias. | 
|
*/  | 
|
private Position.Bias dropBias;  | 
|
        /** | 
|
         * Try to find a flavor that can be used to import a Transferable. | 
|
         * The set of usable flavors are tried in the following order: | 
|
         * <ol> | 
|
         *     <li>First, an attempt is made to find a flavor matching the content type | 
|
         *         of the EditorKit for the component. | 
|
         *     <li>Second, an attempt to find a text/plain flavor is made. | 
|
         *     <li>Third, an attempt to find a flavor representing a String reference | 
|
         *         in the same VM is made. | 
|
         *     <li>Lastly, DataFlavor.stringFlavor is searched for. | 
|
         * </ol> | 
|
*/  | 
|
protected DataFlavor getImportFlavor(DataFlavor[] flavors, JTextComponent c) {  | 
|
DataFlavor plainFlavor = null;  | 
|
DataFlavor refFlavor = null;  | 
|
DataFlavor stringFlavor = null;  | 
|
if (c instanceof JEditorPane) {  | 
|
for (int i = 0; i < flavors.length; i++) {  | 
|
String mime = flavors[i].getMimeType();  | 
|
if (mime.startsWith(((JEditorPane)c).getEditorKit().getContentType())) {  | 
|
return flavors[i];  | 
|
} else if (plainFlavor == null && mime.startsWith("text/plain")) {  | 
|
plainFlavor = flavors[i];  | 
|
} else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref")  | 
|
&& flavors[i].getRepresentationClass() == java.lang.String.class) {  | 
|
refFlavor = flavors[i];  | 
|
} else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {  | 
|
stringFlavor = flavors[i];  | 
|
}  | 
|
}  | 
|
if (plainFlavor != null) {  | 
|
return plainFlavor;  | 
|
} else if (refFlavor != null) {  | 
|
return refFlavor;  | 
|
} else if (stringFlavor != null) {  | 
|
return stringFlavor;  | 
|
}  | 
|
return null;  | 
|
}  | 
|
for (int i = 0; i < flavors.length; i++) {  | 
|
String mime = flavors[i].getMimeType();  | 
|
if (mime.startsWith("text/plain")) {  | 
|
return flavors[i];  | 
|
} else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref")  | 
|
&& flavors[i].getRepresentationClass() == java.lang.String.class) {  | 
|
refFlavor = flavors[i];  | 
|
} else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {  | 
|
stringFlavor = flavors[i];  | 
|
}  | 
|
}  | 
|
if (refFlavor != null) {  | 
|
return refFlavor;  | 
|
} else if (stringFlavor != null) {  | 
|
return stringFlavor;  | 
|
}  | 
|
return null;  | 
|
}  | 
|
        /** | 
|
         * Import the given stream data into the text component. | 
|
*/  | 
|
protected void handleReaderImport(Reader in, JTextComponent c, boolean useRead)  | 
|
throws BadLocationException, IOException {  | 
|
if (useRead) {  | 
|
int startPosition = c.getSelectionStart();  | 
|
int endPosition = c.getSelectionEnd();  | 
|
int length = endPosition - startPosition;  | 
|
EditorKit kit = c.getUI().getEditorKit(c);  | 
|
Document doc = c.getDocument();  | 
|
if (length > 0) {  | 
|
doc.remove(startPosition, length);  | 
|
}  | 
|
kit.read(in, doc, startPosition);  | 
|
            } else { | 
|
char[] buff = new char[1024];  | 
|
int nch;  | 
|
boolean lastWasCR = false;  | 
|
int last;  | 
|
StringBuffer sbuff = null;  | 
|
// Read in a block at a time, mapping \r\n to \n, as well as single  | 
|
                // \r to \n. | 
|
while ((nch = in.read(buff, 0, buff.length)) != -1) {  | 
|
if (sbuff == null) {  | 
|
sbuff = new StringBuffer(nch);  | 
|
}  | 
|
last = 0;  | 
|
for(int counter = 0; counter < nch; counter++) {  | 
|
switch(buff[counter]) {  | 
|
case '\r':  | 
|
if (lastWasCR) {  | 
|
if (counter == 0) {  | 
|
sbuff.append('\n');  | 
|
                                } else { | 
|
buff[counter - 1] = '\n';  | 
|
}  | 
|
                            } else { | 
|
lastWasCR = true;  | 
|
}  | 
|
break;  | 
|
case '\n':  | 
|
if (lastWasCR) {  | 
|
if (counter > (last + 1)) {  | 
|
sbuff.append(buff, last, counter - last - 1);  | 
|
}  | 
|
// else nothing to do, can skip \r, next write will  | 
|
                                // write \n | 
|
lastWasCR = false;  | 
|
last = counter;  | 
|
}  | 
|
break;  | 
|
default:  | 
|
if (lastWasCR) {  | 
|
if (counter == 0) {  | 
|
sbuff.append('\n');  | 
|
                                } else { | 
|
buff[counter - 1] = '\n';  | 
|
}  | 
|
lastWasCR = false;  | 
|
}  | 
|
break;  | 
|
}  | 
|
}  | 
|
if (last < nch) {  | 
|
if (lastWasCR) {  | 
|
if (last < (nch - 1)) {  | 
|
sbuff.append(buff, last, nch - last - 1);  | 
|
}  | 
|
                        } else { | 
|
sbuff.append(buff, last, nch - last);  | 
|
}  | 
|
}  | 
|
}  | 
|
if (lastWasCR) {  | 
|
sbuff.append('\n');  | 
|
}  | 
|
c.replaceSelection(sbuff != null ? sbuff.toString() : "");  | 
|
}  | 
|
}  | 
|
// --- TransferHandler methods ------------------------------------  | 
|
        /** | 
|
         * This is the type of transfer actions supported by the source.  Some models are | 
|
         * not mutable, so a transfer operation of COPY only should | 
|
         * be advertised in that case. | 
|
         * | 
|
         * @param c  The component holding the data to be transfered.  This | 
|
         *  argument is provided to enable sharing of TransferHandlers by | 
|
         *  multiple components. | 
|
         * @return  This is implemented to return NONE if the component is a JPasswordField | 
|
         *  since exporting data via user gestures is not allowed.  If the text component is | 
|
         *  editable, COPY_OR_MOVE is returned, otherwise just COPY is allowed. | 
|
*/  | 
|
public int getSourceActions(JComponent c) {  | 
|
if (c instanceof JPasswordField &&  | 
|
c.getClientProperty("JPasswordField.cutCopyAllowed") !=  | 
|
                Boolean.TRUE) { | 
|
return NONE;  | 
|
}  | 
|
return ((JTextComponent)c).isEditable() ? COPY_OR_MOVE : COPY;  | 
|
}  | 
|
        /** | 
|
         * Create a Transferable to use as the source for a data transfer. | 
|
         * | 
|
         * @param comp  The component holding the data to be transfered.  This | 
|
         *  argument is provided to enable sharing of TransferHandlers by | 
|
         *  multiple components. | 
|
         * @return  The representation of the data to be transfered. | 
|
         * | 
|
*/  | 
|
protected Transferable createTransferable(JComponent comp) {  | 
|
exportComp = (JTextComponent)comp;  | 
|
shouldRemove = true;  | 
|
p0 = exportComp.getSelectionStart();  | 
|
p1 = exportComp.getSelectionEnd();  | 
|
return (p0 != p1) ? (new TextTransferable(exportComp, p0, p1)) : null;  | 
|
}  | 
|
        /** | 
|
         * This method is called after data has been exported.  This method should remove | 
|
         * the data that was transfered if the action was MOVE. | 
|
         * | 
|
         * @param source The component that was the source of the data. | 
|
         * @param data   The data that was transferred or possibly null | 
|
         *               if the action is <code>NONE</code>. | 
|
         * @param action The actual action that was performed. | 
|
*/  | 
|
protected void exportDone(JComponent source, Transferable data, int action) {  | 
|
// only remove the text if shouldRemove has not been set to  | 
|
            // false by importData and only if the action is a move | 
|
if (shouldRemove && action == MOVE) {  | 
|
TextTransferable t = (TextTransferable)data;  | 
|
t.removeText();  | 
|
}  | 
|
exportComp = null;  | 
|
}  | 
|
        public boolean importData(TransferSupport support) { | 
|
isDrop = support.isDrop();  | 
|
if (isDrop) {  | 
|
modeBetween =  | 
|
((JTextComponent)support.getComponent()).getDropMode() == DropMode.INSERT;  | 
|
dropBias = ((JTextComponent.DropLocation)support.getDropLocation()).getBias();  | 
|
dropAction = support.getDropAction();  | 
|
}  | 
|
            try { | 
|
return super.importData(support);  | 
|
            } finally { | 
|
isDrop = false;  | 
|
modeBetween = false;  | 
|
dropBias = null;  | 
|
dropAction = MOVE;  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * This method causes a transfer to a component from a clipboard or a | 
|
         * DND drop operation.  The Transferable represents the data to be | 
|
         * imported into the component. | 
|
         * | 
|
         * @param comp  The component to receive the transfer.  This | 
|
         *  argument is provided to enable sharing of TransferHandlers by | 
|
         *  multiple components. | 
|
         * @param t     The data to import | 
|
         * @return  true if the data was inserted into the component, false otherwise. | 
|
*/  | 
|
public boolean importData(JComponent comp, Transferable t) {  | 
|
JTextComponent c = (JTextComponent)comp;  | 
|
int pos = modeBetween  | 
|
? c.getDropLocation().getIndex() : c.getCaretPosition();  | 
|
// if we are importing to the same component that we exported from  | 
|
// then don't actually do anything if the drop location is inside  | 
|
// the drag location and set shouldRemove to false so that exportDone  | 
|
            // knows not to remove any data | 
|
if (dropAction == MOVE && c == exportComp && pos >= p0 && pos <= p1) {  | 
|
shouldRemove = false;  | 
|
return true;  | 
|
}  | 
|
boolean imported = false;  | 
|
DataFlavor importFlavor = getImportFlavor(t.getTransferDataFlavors(), c);  | 
|
if (importFlavor != null) {  | 
|
                try { | 
|
boolean useRead = false;  | 
|
if (comp instanceof JEditorPane) {  | 
|
JEditorPane ep = (JEditorPane)comp;  | 
|
if (!ep.getContentType().startsWith("text/plain") &&  | 
|
importFlavor.getMimeType().startsWith(ep.getContentType())) {  | 
|
useRead = true;  | 
|
}  | 
|
}  | 
|
InputContext ic = c.getInputContext();  | 
|
if (ic != null) {  | 
|
ic.endComposition();  | 
|
}  | 
|
Reader r = importFlavor.getReaderForText(t);  | 
|
if (modeBetween) {  | 
|
Caret caret = c.getCaret();  | 
|
if (caret instanceof DefaultCaret) {  | 
|
((DefaultCaret)caret).setDot(pos, dropBias);  | 
|
                        } else { | 
|
c.setCaretPosition(pos);  | 
|
}  | 
|
}  | 
|
handleReaderImport(r, c, useRead);  | 
|
if (isDrop) {  | 
|
c.requestFocus();  | 
|
Caret caret = c.getCaret();  | 
|
if (caret instanceof DefaultCaret) {  | 
|
int newPos = caret.getDot();  | 
|
Position.Bias newBias = ((DefaultCaret)caret).getDotBias();  | 
|
((DefaultCaret)caret).setDot(pos, dropBias);  | 
|
((DefaultCaret)caret).moveDot(newPos, newBias);  | 
|
                        } else { | 
|
c.select(pos, c.getCaretPosition());  | 
|
}  | 
|
}  | 
|
imported = true;  | 
|
} catch (UnsupportedFlavorException ufe) {  | 
|
} catch (BadLocationException ble) {  | 
|
} catch (IOException ioe) {  | 
|
}  | 
|
}  | 
|
return imported;  | 
|
}  | 
|
        /** | 
|
         * This method indicates if a component would accept an import of the given | 
|
         * set of data flavors prior to actually attempting to import it. | 
|
         * | 
|
         * @param comp  The component to receive the transfer.  This | 
|
         *  argument is provided to enable sharing of TransferHandlers by | 
|
         *  multiple components. | 
|
         * @param flavors  The data formats available | 
|
         * @return  true if the data can be inserted into the component, false otherwise. | 
|
*/  | 
|
public boolean canImport(JComponent comp, DataFlavor[] flavors) {  | 
|
JTextComponent c = (JTextComponent)comp;  | 
|
if (!(c.isEditable() && c.isEnabled())) {  | 
|
return false;  | 
|
}  | 
|
return (getImportFlavor(flavors, c) != null);  | 
|
}  | 
|
        /** | 
|
         * A possible implementation of the Transferable interface | 
|
         * for text components.  For a JEditorPane with a rich set | 
|
         * of EditorKit implementations, conversions could be made | 
|
         * giving a wider set of formats.  This is implemented to | 
|
         * offer up only the active content type and text/plain | 
|
         * (if that is not the active format) since that can be | 
|
         * extracted from other formats. | 
|
*/  | 
|
static class TextTransferable extends BasicTransferable {  | 
|
TextTransferable(JTextComponent c, int start, int end) {  | 
|
super(null, null);  | 
|
this.c = c;  | 
|
Document doc = c.getDocument();  | 
|
                try { | 
|
p0 = doc.createPosition(start);  | 
|
p1 = doc.createPosition(end);  | 
|
plainData = c.getSelectedText();  | 
|
if (c instanceof JEditorPane) {  | 
|
JEditorPane ep = (JEditorPane)c;  | 
|
mimeType = ep.getContentType();  | 
|
                        if (mimeType.startsWith("text/plain")) { | 
|
return;  | 
|
}  | 
|
StringWriter sw = new StringWriter(p1.getOffset() - p0.getOffset());  | 
|
ep.getEditorKit().write(sw, doc, p0.getOffset(), p1.getOffset() - p0.getOffset());  | 
|
                        if (mimeType.startsWith("text/html")) { | 
|
htmlData = sw.toString();  | 
|
                        } else { | 
|
richText = sw.toString();  | 
|
}  | 
|
}  | 
|
} catch (BadLocationException ble) {  | 
|
} catch (IOException ioe) {  | 
|
}  | 
|
}  | 
|
            void removeText() { | 
|
if ((p0 != null) && (p1 != null) && (p0.getOffset() != p1.getOffset())) {  | 
|
                    try { | 
|
Document doc = c.getDocument();  | 
|
doc.remove(p0.getOffset(), p1.getOffset() - p0.getOffset());  | 
|
} catch (BadLocationException e) {  | 
|
}  | 
|
}  | 
|
}  | 
|
// ---- EditorKit other than plain or HTML text -----------------------  | 
|
            /** | 
|
             * If the EditorKit is not for text/plain or text/html, that format | 
|
             * is supported through the "richer flavors" part of BasicTransferable. | 
|
*/  | 
|
protected DataFlavor[] getRicherFlavors() {  | 
|
                if (richText == null) { | 
|
return null;  | 
|
}  | 
|
                try { | 
|
DataFlavor[] flavors = new DataFlavor[3];  | 
|
flavors[0] = new DataFlavor(mimeType + ";class=java.lang.String");  | 
|
flavors[1] = new DataFlavor(mimeType + ";class=java.io.Reader");  | 
|
flavors[2] = new DataFlavor(mimeType + ";class=java.io.InputStream;charset=unicode");  | 
|
return flavors;  | 
|
} catch (ClassNotFoundException cle) {  | 
|
// fall through to unsupported (should not happen)  | 
|
}  | 
|
return null;  | 
|
}  | 
|
            /** | 
|
             * The only richer format supported is the file list flavor | 
|
*/  | 
|
protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException {  | 
|
                if (richText == null) { | 
|
return null;  | 
|
}  | 
|
if (String.class.equals(flavor.getRepresentationClass())) {  | 
|
return richText;  | 
|
} else if (Reader.class.equals(flavor.getRepresentationClass())) {  | 
|
return new StringReader(richText);  | 
|
} else if (InputStream.class.equals(flavor.getRepresentationClass())) {  | 
|
return new StringBufferInputStream(richText);  | 
|
}  | 
|
throw new UnsupportedFlavorException(flavor);  | 
|
}  | 
|
Position p0;  | 
|
Position p1;  | 
|
String mimeType;  | 
|
String richText;  | 
|
JTextComponent c;  | 
|
}  | 
|
}  | 
|
}  |