504 lines · 17.7 KB · Java
RawDownload
/* * Copyright (c) 1997, 2008, 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.text; import java.awt.*;import java.beans.PropertyChangeEvent;import java.beans.PropertyChangeListener;import java.util.Set;import javax.swing.SwingUtilities;import javax.swing.event.*; /** * Component decorator that implements the view interface.  The * entire element is used to represent the component.  This acts * as a gateway from the display-only View implementations to * interactive lightweight components (ie it allows components * to be embedded into the View hierarchy). * <p> * The component is placed relative to the text baseline * according to the value returned by * <code>Component.getAlignmentY</code>.  For Swing components * this value can be conveniently set using the method * <code>JComponent.setAlignmentY</code>.  For example, setting * a value of <code>0.75</code> will cause 75 percent of the * component to be above the baseline, and 25 percent of the * component to be below the baseline. * <p> * This class is implemented to do the extra work necessary to * work properly in the presence of multiple threads (i.e. from * asynchronous notification of model changes for example) by * ensuring that all component access is done on the event thread. * <p> * The component used is determined by the return value of the * createComponent method.  The default implementation of this * method is to return the component held as an attribute of * the element (by calling StyleConstants.getComponent).  A * limitation of this behavior is that the component cannot * be used by more than one text component (i.e. with a shared * model).  Subclasses can remove this constraint by implementing * the createComponent to actually create a component based upon * some kind of specification contained in the attributes.  The * ObjectView class in the html package is an example of a * ComponentView implementation that supports multiple component * views of a shared model. * * @author Timothy Prinzing */public class ComponentView extends View  {     /**     * Creates a new ComponentView object.     *     * @param elem the element to decorate     */    public ComponentView(Element elem) {        super(elem);    }     /**     * Create the component that is associated with     * this view.  This will be called when it has     * been determined that a new component is needed.     * This would result from a call to setParent or     * as a result of being notified that attributes     * have changed.     */    protected Component createComponent() {        AttributeSet attr = getElement().getAttributes();        Component comp = StyleConstants.getComponent(attr);        return comp;    }     /**     * Fetch the component associated with the view.     */    public final Component getComponent() {        return createdC;    }     // --- View methods ---------------------------------------------     /**     * The real paint behavior occurs naturally from the association     * that the component has with its parent container (the same     * container hosting this view).  This is implemented to do nothing.     *     * @param g the graphics context     * @param a the shape     * @see View#paint     */    public void paint(Graphics g, Shape a) {        if (c != null) {            Rectangle alloc = (a instanceof Rectangle) ?                (Rectangle) a : a.getBounds();            c.setBounds(alloc.x, alloc.y, alloc.width, alloc.height);        }    }     /**     * Determines the preferred span for this view along an     * axis.  This is implemented to return the value     * returned by Component.getPreferredSize along the     * axis of interest.     *     * @param axis may be either View.X_AXIS or View.Y_AXIS     * @return   the span the view would like to be rendered into >= 0.     *           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.     * @exception IllegalArgumentException for an invalid axis     */    public float getPreferredSpan(int axis) {        if ((axis != X_AXIS) && (axis != Y_AXIS)) {            throw new IllegalArgumentException("Invalid axis: " + axis);        }        if (c != null) {            Dimension size = c.getPreferredSize();            if (axis == View.X_AXIS) {                return size.width;            } else {                return size.height;            }        }        return 0;    }     /**     * Determines the minimum span for this view along an     * axis.  This is implemented to return the value     * returned by Component.getMinimumSize along the     * axis of interest.     *     * @param axis may be either View.X_AXIS or View.Y_AXIS     * @return   the span the view would like to be rendered into >= 0.     *           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.     * @exception IllegalArgumentException for an invalid axis     */    public float getMinimumSpan(int axis) {        if ((axis != X_AXIS) && (axis != Y_AXIS)) {            throw new IllegalArgumentException("Invalid axis: " + axis);        }        if (c != null) {            Dimension size = c.getMinimumSize();            if (axis == View.X_AXIS) {                return size.width;            } else {                return size.height;            }        }        return 0;    }     /**     * Determines the maximum span for this view along an     * axis.  This is implemented to return the value     * returned by Component.getMaximumSize along the     * axis of interest.     *     * @param axis may be either View.X_AXIS or View.Y_AXIS     * @return   the span the view would like to be rendered into >= 0.     *           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.     * @exception IllegalArgumentException for an invalid axis     */    public float getMaximumSpan(int axis) {        if ((axis != X_AXIS) && (axis != Y_AXIS)) {            throw new IllegalArgumentException("Invalid axis: " + axis);        }        if (c != null) {            Dimension size = c.getMaximumSize();            if (axis == View.X_AXIS) {                return size.width;            } else {                return size.height;            }        }        return 0;    }     /**     * Determines the desired alignment for this view along an     * axis.  This is implemented to give the alignment of the     * embedded component.     *     * @param axis may be either View.X_AXIS or View.Y_AXIS     * @return the desired alignment.  This should be a value     *   between 0.0 and 1.0 where 0 indicates alignment at the     *   origin and 1.0 indicates alignment to the full span     *   away from the origin.  An alignment of 0.5 would be the     *   center of the view.     */    public float getAlignment(int axis) {        if (c != null) {            switch (axis) {            case View.X_AXIS:                return c.getAlignmentX();            case View.Y_AXIS:                return c.getAlignmentY();            }        }        return super.getAlignment(axis);    }     /**     * Sets the parent for a child view.     * The parent calls this on the child to tell it who its     * parent is, giving the view access to things like     * the hosting Container.  The superclass behavior is     * executed, followed by a call to createComponent if     * the parent view parameter is non-null and a component     * has not yet been created. The embedded components parent     * is then set to the value returned by <code>getContainer</code>.     * If the parent view parameter is null, this view is being     * cleaned up, thus the component is removed from its parent.     * <p>     * The changing of the component hierarchy will     * touch the component lock, which is the one thing     * that is not safe from the View hierarchy.  Therefore,     * this functionality is executed immediately if on the     * event thread, or is queued on the event queue if     * called from another thread (notification of change     * from an asynchronous update).     *     * @param p the parent     */    public void setParent(View p) {        super.setParent(p);        if (SwingUtilities.isEventDispatchThread()) {            setComponentParent();        } else {            Runnable callSetComponentParent = new Runnable() {                public void run() {                    Document doc = getDocument();                    try {                        if (doc instanceof AbstractDocument) {                            ((AbstractDocument)doc).readLock();                        }                        setComponentParent();                        Container host = getContainer();                        if (host != null) {                            preferenceChanged(null, true, true);                            host.repaint();                        }                    } finally {                        if (doc instanceof AbstractDocument) {                            ((AbstractDocument)doc).readUnlock();                        }                    }                }            };            SwingUtilities.invokeLater(callSetComponentParent);        }    }     /**     * Set the parent of the embedded component     * with assurance that it is thread-safe.     */    void setComponentParent() {        View p = getParent();        if (p != null) {            Container parent = getContainer();            if (parent != null) {                if (c == null) {                    // try to build a component                    Component comp = createComponent();                    if (comp != null) {                        createdC = comp;                        c = new Invalidator(comp);                    }                }                if (c != null) {                    if (c.getParent() == null) {                        // components associated with the View tree are added                        // to the hosting container with the View as a constraint.                        parent.add(c, this);                        parent.addPropertyChangeListener("enabled", c);                    }                }            }        } else {            if (c != null) {                Container parent = c.getParent();                if (parent != null) {                    // remove the component from its hosting container                    parent.remove(c);                    parent.removePropertyChangeListener("enabled", c);                }            }        }    }     /**     * Provides a mapping from the coordinate space of the model to     * that of the view.     *     * @param pos the position to convert >= 0     * @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     * @see View#modelToView     */    public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {        int p0 = getStartOffset();        int p1 = getEndOffset();        if ((pos >= p0) && (pos <= p1)) {            Rectangle r = a.getBounds();            if (pos == p1) {                r.x += r.width;            }            r.width = 0;            return r;        }        throw new BadLocationException(pos + " not in range " + p0 + "," + p1, pos);    }     /**     * Provides a mapping from the view coordinate space to the logical     * coordinate space of the model.     *     * @param x the X coordinate >= 0     * @param y the Y coordinate >= 0     * @param a the allocated region to render into     * @return the location within the model that best represents     *    the given point in the view     * @see View#viewToModel     */    public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {        Rectangle alloc = (Rectangle) a;        if (x < alloc.x + (alloc.width / 2)) {            bias[0] = Position.Bias.Forward;            return getStartOffset();        }        bias[0] = Position.Bias.Backward;        return getEndOffset();    }     // --- member variables ------------------------------------------------     private Component createdC;    private Invalidator c;     /**     * This class feeds the invalidate back to the     * hosting View.  This is needed to get the View     * hierarchy to consider giving the component     * a different size (i.e. layout may have been     * cached between the associated view and the     * container hosting this component).     */    class Invalidator extends Container implements PropertyChangeListener {         // NOTE: When we remove this class we are going to have to some        // how enforce setting of the focus traversal keys on the children        // so that they don't inherit them from the JEditorPane. We need        // to do this as JEditorPane has abnormal bindings (it is a focus cycle        // root) and the children typically don't want these bindings as well.         Invalidator(Component child) {            setLayout(null);            add(child);            cacheChildSizes();        }         /**         * The components invalid layout needs         * to be propagated through the view hierarchy         * so the views (which position the component)         * can have their layout recomputed.         */        public void invalidate() {            super.invalidate();            if (getParent() != null) {                preferenceChanged(null, true, true);            }        }         public void doLayout() {            cacheChildSizes();        }         public void setBounds(int x, int y, int w, int h) {            super.setBounds(x, y, w, h);            if (getComponentCount() > 0) {                getComponent(0).setSize(w, h);            }            cacheChildSizes();        }         public void validateIfNecessary() {            if (!isValid()) {                validate();             }        }         private void cacheChildSizes() {            if (getComponentCount() > 0) {                Component child = getComponent(0);                min = child.getMinimumSize();                pref = child.getPreferredSize();                max = child.getMaximumSize();                yalign = child.getAlignmentY();                xalign = child.getAlignmentX();            } else {                min = pref = max = new Dimension(0, 0);            }        }         /**         * Shows or hides this component depending on the value of parameter         * <code>b</code>.         * @param b If <code>true</code>, shows this component;         * otherwise, hides this component.         * @see #isVisible         * @since JDK1.1         */        public void setVisible(boolean b) {            super.setVisible(b);            if (getComponentCount() > 0) {                getComponent(0).setVisible(b);            }        }         /**         * Overridden to fix 4759054. Must return true so that content         * is painted when inside a CellRendererPane which is normally         * invisible.         */        public boolean isShowing() {            return true;        }         public Dimension getMinimumSize() {            validateIfNecessary();            return min;        }         public Dimension getPreferredSize() {            validateIfNecessary();            return pref;        }         public Dimension getMaximumSize() {            validateIfNecessary();            return max;        }         public float getAlignmentX() {            validateIfNecessary();            return xalign;        }         public float getAlignmentY() {            validateIfNecessary();            return yalign;        }         public Set<AWTKeyStroke> getFocusTraversalKeys(int id) {            return KeyboardFocusManager.getCurrentKeyboardFocusManager().                    getDefaultFocusTraversalKeys(id);        }         public void propertyChange(PropertyChangeEvent ev) {            Boolean enable = (Boolean) ev.getNewValue();            if (getComponentCount() > 0) {                getComponent(0).setEnabled(enable);            }        }         Dimension min;        Dimension pref;        Dimension max;        float yalign;        float xalign;

Lines 1–500 of 504.