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