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