0N/A/*
2362N/A * Copyright (c) 1997, 2006, 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.io.PrintStream;
0N/Aimport java.util.Vector;
0N/Aimport java.awt.*;
0N/Aimport javax.swing.event.DocumentEvent;
0N/Aimport javax.swing.SizeRequirements;
0N/A
0N/A/**
0N/A * A view that arranges its children into a box shape by tiling
0N/A * its children along an axis. The box is somewhat like that
0N/A * found in TeX where there is alignment of the
0N/A * children, flexibility of the children is considered, etc.
0N/A * This is a building block that might be useful to represent
0N/A * things like a collection of lines, paragraphs,
0N/A * lists, columns, pages, etc. The axis along which the children are tiled is
0N/A * considered the major axis. The orthoginal axis is the minor axis.
0N/A * <p>
0N/A * Layout for each axis is handled separately by the methods
0N/A * <code>layoutMajorAxis</code> and <code>layoutMinorAxis</code>.
0N/A * Subclasses can change the layout algorithm by
0N/A * reimplementing these methods. These methods will be called
0N/A * as necessary depending upon whether or not there is cached
0N/A * layout information and the cache is considered
0N/A * valid. These methods are typically called if the given size
0N/A * along the axis changes, or if <code>layoutChanged</code> is
0N/A * called to force an updated layout. The <code>layoutChanged</code>
0N/A * method invalidates cached layout information, if there is any.
0N/A * The requirements published to the parent view are calculated by
0N/A * the methods <code>calculateMajorAxisRequirements</code>
0N/A * and <code>calculateMinorAxisRequirements</code>.
0N/A * If the layout algorithm is changed, these methods will
0N/A * likely need to be reimplemented.
0N/A *
0N/A * @author Timothy Prinzing
0N/A */
0N/Apublic class BoxView extends CompositeView {
0N/A
0N/A /**
0N/A * Constructs a <code>BoxView</code>.
0N/A *
0N/A * @param elem the element this view is responsible for
0N/A * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
0N/A */
0N/A public BoxView(Element elem, int axis) {
0N/A super(elem);
0N/A tempRect = new Rectangle();
0N/A this.majorAxis = axis;
0N/A
0N/A majorOffsets = new int[0];
0N/A majorSpans = new int[0];
0N/A majorReqValid = false;
0N/A majorAllocValid = false;
0N/A minorOffsets = new int[0];
0N/A minorSpans = new int[0];
0N/A minorReqValid = false;
0N/A minorAllocValid = false;
0N/A }
0N/A
0N/A /**
0N/A * Fetches the tile axis property. This is the axis along which
0N/A * the child views are tiled.
0N/A *
0N/A * @return the major axis of the box, either
0N/A * <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
0N/A *
0N/A * @since 1.3
0N/A */
0N/A public int getAxis() {
0N/A return majorAxis;
0N/A }
0N/A
0N/A /**
0N/A * Sets the tile axis property. This is the axis along which
0N/A * the child views are tiled.
0N/A *
0N/A * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
0N/A *
0N/A * @since 1.3
0N/A */
0N/A public void setAxis(int axis) {
0N/A boolean axisChanged = (axis != majorAxis);
0N/A majorAxis = axis;
0N/A if (axisChanged) {
0N/A preferenceChanged(null, true, true);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Invalidates the layout along an axis. This happens
0N/A * automatically if the preferences have changed for
0N/A * any of the child views. In some cases the layout
0N/A * may need to be recalculated when the preferences
0N/A * have not changed. The layout can be marked as
0N/A * invalid by calling this method. The layout will
0N/A * be updated the next time the <code>setSize</code> method
0N/A * is called on this view (typically in paint).
0N/A *
0N/A * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
0N/A *
0N/A * @since 1.3
0N/A */
0N/A public void layoutChanged(int axis) {
0N/A if (axis == majorAxis) {
0N/A majorAllocValid = false;
0N/A } else {
0N/A minorAllocValid = false;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Determines if the layout is valid along the given axis.
0N/A *
0N/A * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
0N/A *
0N/A * @since 1.4
0N/A */
0N/A protected boolean isLayoutValid(int axis) {
0N/A if (axis == majorAxis) {
0N/A return majorAllocValid;
0N/A } else {
0N/A return minorAllocValid;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Paints a child. By default
0N/A * that is all it does, but a subclass can use this to paint
0N/A * things relative to the child.
0N/A *
0N/A * @param g the graphics context
0N/A * @param alloc the allocated region to paint into
0N/A * @param index the child index, >= 0 && < getViewCount()
0N/A */
0N/A protected void paintChild(Graphics g, Rectangle alloc, int index) {
0N/A View child = getView(index);
0N/A child.paint(g, alloc);
0N/A }
0N/A
0N/A // --- View methods ---------------------------------------------
0N/A
0N/A /**
0N/A * Invalidates the layout and resizes the cache of
0N/A * requests/allocations. The child allocations can still
0N/A * be accessed for the old layout, but the new children
0N/A * will have an offset and span of 0.
0N/A *
0N/A * @param index the starting index into the child views to insert
0N/A * the new views; this should be a value >= 0 and <= getViewCount
0N/A * @param length the number of existing child views to remove;
0N/A * This should be a value >= 0 and <= (getViewCount() - offset)
0N/A * @param elems the child views to add; this value can be
0N/A * <code>null</code>to indicate no children are being added
0N/A * (useful to remove)
0N/A */
0N/A public void replace(int index, int length, View[] elems) {
0N/A super.replace(index, length, elems);
0N/A
0N/A // invalidate cache
0N/A int nInserted = (elems != null) ? elems.length : 0;
0N/A majorOffsets = updateLayoutArray(majorOffsets, index, nInserted);
0N/A majorSpans = updateLayoutArray(majorSpans, index, nInserted);
0N/A majorReqValid = false;
0N/A majorAllocValid = false;
0N/A minorOffsets = updateLayoutArray(minorOffsets, index, nInserted);
0N/A minorSpans = updateLayoutArray(minorSpans, index, nInserted);
0N/A minorReqValid = false;
0N/A minorAllocValid = false;
0N/A }
0N/A
0N/A /**
0N/A * Resizes the given layout array to match the new number of
0N/A * child views. The current number of child views are used to
0N/A * produce the new array. The contents of the old array are
0N/A * inserted into the new array at the appropriate places so that
0N/A * the old layout information is transferred to the new array.
0N/A *
0N/A * @param oldArray the original layout array
0N/A * @param offset location where new views will be inserted
0N/A * @param nInserted the number of child views being inserted;
0N/A * therefore the number of blank spaces to leave in the
0N/A * new array at location <code>offset</code>
0N/A * @return the new layout array
0N/A */
0N/A int[] updateLayoutArray(int[] oldArray, int offset, int nInserted) {
0N/A int n = getViewCount();
0N/A int[] newArray = new int[n];
0N/A
0N/A System.arraycopy(oldArray, 0, newArray, 0, offset);
0N/A System.arraycopy(oldArray, offset,
0N/A newArray, offset + nInserted, n - nInserted - offset);
0N/A return newArray;
0N/A }
0N/A
0N/A /**
0N/A * Forwards the given <code>DocumentEvent</code> to the child views
0N/A * that need to be notified of the change to the model.
0N/A * If a child changed its requirements and the allocation
0N/A * was valid prior to forwarding the portion of the box
0N/A * from the starting child to the end of the box will
0N/A * be repainted.
0N/A *
0N/A * @param ec changes to the element this view is responsible
0N/A * for (may be <code>null</code> if there were no changes)
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 * @see #insertUpdate
0N/A * @see #removeUpdate
0N/A * @see #changedUpdate
0N/A * @since 1.3
0N/A */
0N/A protected void forwardUpdate(DocumentEvent.ElementChange ec,
0N/A DocumentEvent e, Shape a, ViewFactory f) {
0N/A boolean wasValid = isLayoutValid(majorAxis);
0N/A super.forwardUpdate(ec, e, a, f);
0N/A
0N/A // determine if a repaint is needed
0N/A if (wasValid && (! isLayoutValid(majorAxis))) {
0N/A // Repaint is needed because one of the tiled children
0N/A // have changed their span along the major axis. If there
0N/A // is a hosting component and an allocated shape we repaint.
0N/A Component c = getContainer();
0N/A if ((a != null) && (c != null)) {
0N/A int pos = e.getOffset();
0N/A int index = getViewIndexAtPosition(pos);
0N/A Rectangle alloc = getInsideAllocation(a);
0N/A if (majorAxis == X_AXIS) {
0N/A alloc.x += majorOffsets[index];
0N/A alloc.width -= majorOffsets[index];
0N/A } else {
0N/A alloc.y += minorOffsets[index];
0N/A alloc.height -= minorOffsets[index];
0N/A }
0N/A c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * This is called by a child to indicate its
0N/A * preferred span has changed. This is implemented to
0N/A * throw away cached layout information so that new
0N/A * calculations will be done the next time the children
0N/A * need an allocation.
0N/A *
0N/A * @param child the child view
0N/A * @param width true if the width preference should change
0N/A * @param height true if the height preference should change
0N/A */
0N/A public void preferenceChanged(View child, boolean width, boolean height) {
0N/A boolean majorChanged = (majorAxis == X_AXIS) ? width : height;
0N/A boolean minorChanged = (majorAxis == X_AXIS) ? height : width;
0N/A if (majorChanged) {
0N/A majorReqValid = false;
0N/A majorAllocValid = false;
0N/A }
0N/A if (minorChanged) {
0N/A minorReqValid = false;
0N/A minorAllocValid = false;
0N/A }
0N/A super.preferenceChanged(child, width, height);
0N/A }
0N/A
0N/A /**
0N/A * Gets the resize weight. A value of 0 or less is not resizable.
0N/A *
0N/A * @param axis may be either <code>View.X_AXIS</code> or
0N/A * <code>View.Y_AXIS</code>
0N/A * @return the weight
0N/A * @exception IllegalArgumentException for an invalid axis
0N/A */
0N/A public int getResizeWeight(int axis) {
0N/A checkRequests(axis);
0N/A if (axis == majorAxis) {
0N/A if ((majorRequest.preferred != majorRequest.minimum) ||
0N/A (majorRequest.preferred != majorRequest.maximum)) {
0N/A return 1;
0N/A }
0N/A } else {
0N/A if ((minorRequest.preferred != minorRequest.minimum) ||
0N/A (minorRequest.preferred != minorRequest.maximum)) {
0N/A return 1;
0N/A }
0N/A }
0N/A return 0;
0N/A }
0N/A
0N/A /**
0N/A * Sets the size of the view along an axis. This should cause
0N/A * layout of the view along the given axis.
0N/A *
0N/A * @param axis may be either <code>View.X_AXIS</code> or
0N/A * <code>View.Y_AXIS</code>
0N/A * @param span the span to layout to >= 0
0N/A */
0N/A void setSpanOnAxis(int axis, float span) {
0N/A if (axis == majorAxis) {
0N/A if (majorSpan != (int) span) {
0N/A majorAllocValid = false;
0N/A }
0N/A if (! majorAllocValid) {
0N/A // layout the major axis
0N/A majorSpan = (int) span;
0N/A checkRequests(majorAxis);
0N/A layoutMajorAxis(majorSpan, axis, majorOffsets, majorSpans);
0N/A majorAllocValid = true;
0N/A
0N/A // flush changes to the children
0N/A updateChildSizes();
0N/A }
0N/A } else {
0N/A if (((int) span) != minorSpan) {
0N/A minorAllocValid = false;
0N/A }
0N/A if (! minorAllocValid) {
0N/A // layout the minor axis
0N/A minorSpan = (int) span;
0N/A checkRequests(axis);
0N/A layoutMinorAxis(minorSpan, axis, minorOffsets, minorSpans);
0N/A minorAllocValid = true;
0N/A
0N/A // flush changes to the children
0N/A updateChildSizes();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Propagates the current allocations to the child views.
0N/A */
0N/A void updateChildSizes() {
0N/A int n = getViewCount();
0N/A if (majorAxis == X_AXIS) {
0N/A for (int i = 0; i < n; i++) {
0N/A View v = getView(i);
0N/A v.setSize((float) majorSpans[i], (float) minorSpans[i]);
0N/A }
0N/A } else {
0N/A for (int i = 0; i < n; i++) {
0N/A View v = getView(i);
0N/A v.setSize((float) minorSpans[i], (float) majorSpans[i]);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the size of the view along an axis. This is implemented
0N/A * to return zero.
0N/A *
0N/A * @param axis may be either <code>View.X_AXIS</code> or
0N/A * <code>View.Y_AXIS</code>
0N/A * @return the current span of the view along the given axis, >= 0
0N/A */
0N/A float getSpanOnAxis(int axis) {
0N/A if (axis == majorAxis) {
0N/A return majorSpan;
0N/A } else {
0N/A return minorSpan;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets the size of the view. This should cause
0N/A * layout of the view if the view caches any layout
0N/A * information. This is implemented to call the
0N/A * layout method with the sizes inside of the insets.
0N/A *
0N/A * @param width the width >= 0
0N/A * @param height the height >= 0
0N/A */
0N/A public void setSize(float width, float height) {
0N/A layout(Math.max(0, (int)(width - getLeftInset() - getRightInset())),
0N/A Math.max(0, (int)(height - getTopInset() - getBottomInset())));
0N/A }
0N/A
0N/A /**
0N/A * Renders the <code>BoxView</code> using the given
0N/A * rendering surface and area
0N/A * on that surface. Only the children that intersect
0N/A * the clip bounds of the given <code>Graphics</code>
0N/A * will be rendered.
0N/A *
0N/A * @param g the rendering surface to use
0N/A * @param allocation the allocated region to render into
0N/A * @see View#paint
0N/A */
0N/A public void paint(Graphics g, Shape allocation) {
0N/A Rectangle alloc = (allocation instanceof Rectangle) ?
0N/A (Rectangle)allocation : allocation.getBounds();
0N/A int n = getViewCount();
0N/A int x = alloc.x + getLeftInset();
0N/A int y = alloc.y + getTopInset();
0N/A Rectangle clip = g.getClipBounds();
0N/A for (int i = 0; i < n; i++) {
0N/A tempRect.x = x + getOffset(X_AXIS, i);
0N/A tempRect.y = y + getOffset(Y_AXIS, i);
0N/A tempRect.width = getSpan(X_AXIS, i);
0N/A tempRect.height = getSpan(Y_AXIS, i);
0N/A int trx0 = tempRect.x, trx1 = trx0 + tempRect.width;
0N/A int try0 = tempRect.y, try1 = try0 + tempRect.height;
0N/A int crx0 = clip.x, crx1 = crx0 + clip.width;
0N/A int cry0 = clip.y, cry1 = cry0 + clip.height;
0N/A // We should paint views that intersect with clipping region
0N/A // even if the intersection has no inside points (is a line).
0N/A // This is needed for supporting views that have zero width, like
0N/A // views that contain only combining marks.
0N/A if ((trx1 >= crx0) && (try1 >= cry0) && (crx1 >= trx0) && (cry1 >= try0)) {
0N/A paintChild(g, tempRect, i);
0N/A }
0N/A }
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. This is implemented to return
0N/A * <code>null</code> if the layout is invalid,
0N/A * otherwise the superclass behavior is executed.
0N/A *
0N/A * @param index the index of the child, >= 0 && < getViewCount()
0N/A * @param a the allocation to this view
0N/A * @return the allocation to the child; or <code>null</code>
0N/A * if <code>a</code> is <code>null</code>;
0N/A * or <code>null</code> if the layout is invalid
0N/A */
0N/A public Shape getChildAllocation(int index, Shape a) {
0N/A if (a != null) {
0N/A Shape ca = super.getChildAllocation(index, a);
0N/A if ((ca != null) && (! isAllocationValid())) {
0N/A // The child allocation may not have been set yet.
0N/A Rectangle r = (ca instanceof Rectangle) ?
0N/A (Rectangle) ca : ca.getBounds();
0N/A if ((r.width == 0) && (r.height == 0)) {
0N/A return null;
0N/A }
0N/A }
0N/A return ca;
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. This makes
0N/A * sure the allocation is valid before calling the superclass.
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
0N/A * @exception BadLocationException if the given position does
0N/A * not 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 if (! isAllocationValid()) {
0N/A Rectangle alloc = a.getBounds();
0N/A setSize(alloc.width, alloc.height);
0N/A }
0N/A return super.modelToView(pos, a, b);
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 >= 0
0N/A * @param y y coordinate of the view location to convert >= 0
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 >= 0
0N/A * @see View#viewToModel
0N/A */
0N/A public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
0N/A if (! isAllocationValid()) {
0N/A Rectangle alloc = a.getBounds();
0N/A setSize(alloc.width, alloc.height);
0N/A }
0N/A return super.viewToModel(x, y, a, bias);
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 total alignment
0N/A * needed to position the children with the alignment points
0N/A * lined up along the axis orthoginal to the axis that is
0N/A * being tiled. The axis being tiled will request to be
0N/A * centered (i.e. 0.5f).
0N/A *
0N/A * @param axis may be either <code>View.X_AXIS</code>
0N/A * or <code>View.Y_AXIS</code>
0N/A * @return the desired alignment >= 0.0f && <= 1.0f; this should
0N/A * be a value 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 * @exception IllegalArgumentException for an invalid axis
0N/A */
0N/A public float getAlignment(int axis) {
0N/A checkRequests(axis);
0N/A if (axis == majorAxis) {
0N/A return majorRequest.alignment;
0N/A } else {
0N/A return minorRequest.alignment;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Determines the preferred span for this view along an
0N/A * axis.
0N/A *
0N/A * @param axis may be either <code>View.X_AXIS</code>
0N/A * or <code>View.Y_AXIS</code>
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 type
0N/A */
0N/A public float getPreferredSpan(int axis) {
0N/A checkRequests(axis);
0N/A float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
0N/A getTopInset() + getBottomInset();
0N/A if (axis == majorAxis) {
0N/A return ((float)majorRequest.preferred) + marginSpan;
0N/A } else {
0N/A return ((float)minorRequest.preferred) + marginSpan;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Determines the minimum span for this view along an
0N/A * axis.
0N/A *
0N/A * @param axis may be either <code>View.X_AXIS</code>
0N/A * or <code>View.Y_AXIS</code>
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 type
0N/A */
0N/A public float getMinimumSpan(int axis) {
0N/A checkRequests(axis);
0N/A float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
0N/A getTopInset() + getBottomInset();
0N/A if (axis == majorAxis) {
0N/A return ((float)majorRequest.minimum) + marginSpan;
0N/A } else {
0N/A return ((float)minorRequest.minimum) + marginSpan;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Determines the maximum span for this view along an
0N/A * axis.
0N/A *
0N/A * @param axis may be either <code>View.X_AXIS</code>
0N/A * or <code>View.Y_AXIS</code>
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 type
0N/A */
0N/A public float getMaximumSpan(int axis) {
0N/A checkRequests(axis);
0N/A float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
0N/A getTopInset() + getBottomInset();
0N/A if (axis == majorAxis) {
0N/A return ((float)majorRequest.maximum) + marginSpan;
0N/A } else {
0N/A return ((float)minorRequest.maximum) + marginSpan;
0N/A }
0N/A }
0N/A
0N/A // --- local methods ----------------------------------------------------
0N/A
0N/A /**
0N/A * Are the allocations for the children still
0N/A * valid?
0N/A *
0N/A * @return true if allocations still valid
0N/A */
0N/A protected boolean isAllocationValid() {
0N/A return (majorAllocValid && minorAllocValid);
0N/A }
0N/A
0N/A /**
0N/A * Determines if a point falls before an allocated region.
0N/A *
0N/A * @param x the X coordinate >= 0
0N/A * @param y the Y coordinate >= 0
0N/A * @param innerAlloc the allocated region; this is the area
0N/A * inside of the insets
0N/A * @return true if the point lies before the region else false
0N/A */
0N/A protected boolean isBefore(int x, int y, Rectangle innerAlloc) {
0N/A if (majorAxis == View.X_AXIS) {
0N/A return (x < innerAlloc.x);
0N/A } else {
0N/A return (y < innerAlloc.y);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Determines if a point falls after an allocated region.
0N/A *
0N/A * @param x the X coordinate >= 0
0N/A * @param y the Y coordinate >= 0
0N/A * @param innerAlloc the allocated region; this is the area
0N/A * inside of the insets
0N/A * @return true if the point lies after the region else false
0N/A */
0N/A protected boolean isAfter(int x, int y, Rectangle innerAlloc) {
0N/A if (majorAxis == View.X_AXIS) {
0N/A return (x > (innerAlloc.width + innerAlloc.x));
0N/A } else {
0N/A return (y > (innerAlloc.height + innerAlloc.y));
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Fetches the child view at the given coordinates.
0N/A *
0N/A * @param x the X coordinate >= 0
0N/A * @param y the Y coordinate >= 0
0N/A * @param alloc the parents inner allocation on entry, which should
0N/A * be changed to the childs allocation on exit
0N/A * @return the view
0N/A */
0N/A protected View getViewAtPoint(int x, int y, Rectangle alloc) {
0N/A int n = getViewCount();
0N/A if (majorAxis == View.X_AXIS) {
0N/A if (x < (alloc.x + majorOffsets[0])) {
0N/A childAllocation(0, alloc);
0N/A return getView(0);
0N/A }
0N/A for (int i = 0; i < n; i++) {
0N/A if (x < (alloc.x + majorOffsets[i])) {
0N/A childAllocation(i - 1, alloc);
0N/A return getView(i - 1);
0N/A }
0N/A }
0N/A childAllocation(n - 1, alloc);
0N/A return getView(n - 1);
0N/A } else {
0N/A if (y < (alloc.y + majorOffsets[0])) {
0N/A childAllocation(0, alloc);
0N/A return getView(0);
0N/A }
0N/A for (int i = 0; i < n; i++) {
0N/A if (y < (alloc.y + majorOffsets[i])) {
0N/A childAllocation(i - 1, alloc);
0N/A return getView(i - 1);
0N/A }
0N/A }
0N/A childAllocation(n - 1, alloc);
0N/A return getView(n - 1);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Allocates a region for a child view.
0N/A *
0N/A * @param index the index of the child view to
0N/A * allocate, >= 0 && < getViewCount()
0N/A * @param alloc the allocated region
0N/A */
0N/A protected void childAllocation(int index, Rectangle alloc) {
0N/A alloc.x += getOffset(X_AXIS, index);
0N/A alloc.y += getOffset(Y_AXIS, index);
0N/A alloc.width = getSpan(X_AXIS, index);
0N/A alloc.height = getSpan(Y_AXIS, index);
0N/A }
0N/A
0N/A /**
0N/A * Perform layout on the box
0N/A *
0N/A * @param width the width (inside of the insets) >= 0
0N/A * @param height the height (inside of the insets) >= 0
0N/A */
0N/A protected void layout(int width, int height) {
0N/A setSpanOnAxis(X_AXIS, width);
0N/A setSpanOnAxis(Y_AXIS, height);
0N/A }
0N/A
0N/A /**
0N/A * Returns the current width of the box. This is the width that
0N/A * it was last allocated.
0N/A * @return the current width of the box
0N/A */
0N/A public int getWidth() {
0N/A int span;
0N/A if (majorAxis == X_AXIS) {
0N/A span = majorSpan;
0N/A } else {
0N/A span = minorSpan;
0N/A }
0N/A span += getLeftInset() - getRightInset();
0N/A return span;
0N/A }
0N/A
0N/A /**
0N/A * Returns the current height of the box. This is the height that
0N/A * it was last allocated.
0N/A * @return the current height of the box
0N/A */
0N/A public int getHeight() {
0N/A int span;
0N/A if (majorAxis == Y_AXIS) {
0N/A span = majorSpan;
0N/A } else {
0N/A span = minorSpan;
0N/A }
0N/A span += getTopInset() - getBottomInset();
0N/A return span;
0N/A }
0N/A
0N/A /**
0N/A * Performs layout for the major axis of the box (i.e. the
0N/A * axis that it represents). The results of the layout (the
0N/A * offset and span for each children) are placed in the given
0N/A * arrays which represent the allocations to the children
0N/A * along the major axis.
0N/A *
0N/A * @param targetSpan the total span given to the view, which
0N/A * would be used to layout the children
0N/A * @param axis the axis being layed out
0N/A * @param offsets the offsets from the origin of the view for
0N/A * each of the child views; this is a return value and is
0N/A * filled in by the implementation of this method
0N/A * @param spans the span of each child view; this is a return
0N/A * value and is filled in by the implementation of this method
0N/A */
0N/A protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
0N/A /*
0N/A * first pass, calculate the preferred sizes
0N/A * and the flexibility to adjust the sizes.
0N/A */
0N/A long preferred = 0;
0N/A int n = getViewCount();
0N/A for (int i = 0; i < n; i++) {
0N/A View v = getView(i);
0N/A spans[i] = (int) v.getPreferredSpan(axis);
0N/A preferred += spans[i];
0N/A }
0N/A
0N/A /*
0N/A * Second pass, expand or contract by as much as possible to reach
0N/A * the target span.
0N/A */
0N/A
0N/A // determine the adjustment to be made
0N/A long desiredAdjustment = targetSpan - preferred;
0N/A float adjustmentFactor = 0.0f;
0N/A int[] diffs = null;
0N/A
0N/A if (desiredAdjustment != 0) {
0N/A long totalSpan = 0;
0N/A diffs = new int[n];
0N/A for (int i = 0; i < n; i++) {
0N/A View v = getView(i);
0N/A int tmp;
0N/A if (desiredAdjustment < 0) {
0N/A tmp = (int)v.getMinimumSpan(axis);
0N/A diffs[i] = spans[i] - tmp;
0N/A } else {
0N/A tmp = (int)v.getMaximumSpan(axis);
0N/A diffs[i] = tmp - spans[i];
0N/A }
0N/A totalSpan += tmp;
0N/A }
0N/A
0N/A float maximumAdjustment = Math.abs(totalSpan - preferred);
0N/A adjustmentFactor = desiredAdjustment / maximumAdjustment;
0N/A adjustmentFactor = Math.min(adjustmentFactor, 1.0f);
0N/A adjustmentFactor = Math.max(adjustmentFactor, -1.0f);
0N/A }
0N/A
0N/A // make the adjustments
0N/A int totalOffset = 0;
0N/A for (int i = 0; i < n; i++) {
0N/A offsets[i] = totalOffset;
0N/A if (desiredAdjustment != 0) {
0N/A float adjF = adjustmentFactor * diffs[i];
0N/A spans[i] += Math.round(adjF);
0N/A }
0N/A totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Performs layout for the minor axis of the box (i.e. the
0N/A * axis orthoginal to the axis that it represents). The results
0N/A * of the layout (the offset and span for each children) are
0N/A * placed in the given arrays which represent the allocations to
0N/A * the children along the minor axis.
0N/A *
0N/A * @param targetSpan the total span given to the view, which
0N/A * would be used to layout the children
0N/A * @param axis the axis being layed out
0N/A * @param offsets the offsets from the origin of the view for
0N/A * each of the child views; this is a return value and is
0N/A * filled in by the implementation of this method
0N/A * @param spans the span of each child view; this is a return
0N/A * value and is filled in by the implementation of this method
0N/A */
0N/A protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
0N/A int n = getViewCount();
0N/A for (int i = 0; i < n; i++) {
0N/A View v = getView(i);
0N/A int max = (int) v.getMaximumSpan(axis);
0N/A if (max < targetSpan) {
0N/A // can't make the child this wide, align it
0N/A float align = v.getAlignment(axis);
0N/A offsets[i] = (int) ((targetSpan - max) * align);
0N/A spans[i] = max;
0N/A } else {
0N/A // make it the target width, or as small as it can get.
0N/A int min = (int)v.getMinimumSpan(axis);
0N/A offsets[i] = 0;
0N/A spans[i] = Math.max(min, targetSpan);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Calculates the size requirements for the major axis
0N/A * <code>axis</code>.
0N/A *
0N/A * @param axis the axis being studied
0N/A * @param r the <code>SizeRequirements</code> object;
0N/A * if <code>null</code> one will be created
0N/A * @return the newly initialized <code>SizeRequirements</code> object
0N/A * @see javax.swing.SizeRequirements
0N/A */
0N/A protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
0N/A // calculate tiled request
0N/A float min = 0;
0N/A float pref = 0;
0N/A float max = 0;
0N/A
0N/A int n = getViewCount();
0N/A for (int i = 0; i < n; i++) {
0N/A View v = getView(i);
0N/A min += v.getMinimumSpan(axis);
0N/A pref += v.getPreferredSpan(axis);
0N/A max += v.getMaximumSpan(axis);
0N/A }
0N/A
0N/A if (r == null) {
0N/A r = new SizeRequirements();
0N/A }
0N/A r.alignment = 0.5f;
0N/A r.minimum = (int) min;
0N/A r.preferred = (int) pref;
0N/A r.maximum = (int) max;
0N/A return r;
0N/A }
0N/A
0N/A /**
0N/A * Calculates the size requirements for the minor axis
0N/A * <code>axis</code>.
0N/A *
0N/A * @param axis the axis being studied
0N/A * @param r the <code>SizeRequirements</code> object;
0N/A * if <code>null</code> one will be created
0N/A * @return the newly initialized <code>SizeRequirements</code> object
0N/A * @see javax.swing.SizeRequirements
0N/A */
0N/A protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
0N/A int min = 0;
0N/A long pref = 0;
0N/A int max = Integer.MAX_VALUE;
0N/A int n = getViewCount();
0N/A for (int i = 0; i < n; i++) {
0N/A View v = getView(i);
0N/A min = Math.max((int) v.getMinimumSpan(axis), min);
0N/A pref = Math.max((int) v.getPreferredSpan(axis), pref);
0N/A max = Math.max((int) v.getMaximumSpan(axis), max);
0N/A }
0N/A
0N/A if (r == null) {
0N/A r = new SizeRequirements();
0N/A r.alignment = 0.5f;
0N/A }
0N/A r.preferred = (int) pref;
0N/A r.minimum = min;
0N/A r.maximum = max;
0N/A return r;
0N/A }
0N/A
0N/A /**
0N/A * Checks the request cache and update if needed.
0N/A * @param axis the axis being studied
0N/A * @exception IllegalArgumentException if <code>axis</code> is
0N/A * neither <code>View.X_AXIS</code> nor <code>View.Y_AXIS</code>
0N/A */
0N/A void checkRequests(int axis) {
0N/A if ((axis != X_AXIS) && (axis != Y_AXIS)) {
0N/A throw new IllegalArgumentException("Invalid axis: " + axis);
0N/A }
0N/A if (axis == majorAxis) {
0N/A if (!majorReqValid) {
0N/A majorRequest = calculateMajorAxisRequirements(axis,
0N/A majorRequest);
0N/A majorReqValid = true;
0N/A }
0N/A } else if (! minorReqValid) {
0N/A minorRequest = calculateMinorAxisRequirements(axis, minorRequest);
0N/A minorReqValid = true;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Computes the location and extent of each child view
0N/A * in this <code>BoxView</code> given the <code>targetSpan</code>,
0N/A * which is the width (or height) of the region we have to
0N/A * work with.
0N/A *
0N/A * @param targetSpan the total span given to the view, which
0N/A * would be used to layout the children
0N/A * @param axis the axis being studied, either
0N/A * <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
0N/A * @param offsets an empty array filled by this method with
0N/A * values specifying the location of each child view
0N/A * @param spans an empty array filled by this method with
0N/A * values specifying the extent of each child view
0N/A */
0N/A protected void baselineLayout(int targetSpan, int axis, int[] offsets, int[] spans) {
0N/A int totalAscent = (int)(targetSpan * getAlignment(axis));
0N/A int totalDescent = targetSpan - totalAscent;
0N/A
0N/A int n = getViewCount();
0N/A
0N/A for (int i = 0; i < n; i++) {
0N/A View v = getView(i);
0N/A float align = v.getAlignment(axis);
0N/A float viewSpan;
0N/A
0N/A if (v.getResizeWeight(axis) > 0) {
0N/A // if resizable then resize to the best fit
0N/A
0N/A // the smallest span possible
0N/A float minSpan = v.getMinimumSpan(axis);
0N/A // the largest span possible
0N/A float maxSpan = v.getMaximumSpan(axis);
0N/A
0N/A if (align == 0.0f) {
0N/A // if the alignment is 0 then we need to fit into the descent
0N/A viewSpan = Math.max(Math.min(maxSpan, totalDescent), minSpan);
0N/A } else if (align == 1.0f) {
0N/A // if the alignment is 1 then we need to fit into the ascent
0N/A viewSpan = Math.max(Math.min(maxSpan, totalAscent), minSpan);
0N/A } else {
0N/A // figure out the span that we must fit into
0N/A float fitSpan = Math.min(totalAscent / align,
0N/A totalDescent / (1.0f - align));
0N/A // fit into the calculated span
0N/A viewSpan = Math.max(Math.min(maxSpan, fitSpan), minSpan);
0N/A }
0N/A } else {
0N/A // otherwise use the preferred spans
0N/A viewSpan = v.getPreferredSpan(axis);
0N/A }
0N/A
0N/A offsets[i] = totalAscent - (int)(viewSpan * align);
0N/A spans[i] = (int)viewSpan;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Calculates the size requirements for this <code>BoxView</code>
0N/A * by examining the size of each child view.
0N/A *
0N/A * @param axis the axis being studied
0N/A * @param r the <code>SizeRequirements</code> object;
0N/A * if <code>null</code> one will be created
0N/A * @return the newly initialized <code>SizeRequirements</code> object
0N/A */
0N/A protected SizeRequirements baselineRequirements(int axis, SizeRequirements r) {
0N/A SizeRequirements totalAscent = new SizeRequirements();
0N/A SizeRequirements totalDescent = new SizeRequirements();
0N/A
0N/A if (r == null) {
0N/A r = new SizeRequirements();
0N/A }
0N/A
0N/A r.alignment = 0.5f;
0N/A
0N/A int n = getViewCount();
0N/A
0N/A // loop through all children calculating the max of all their ascents and
0N/A // descents at minimum, preferred, and maximum sizes
0N/A for (int i = 0; i < n; i++) {
0N/A View v = getView(i);
0N/A float align = v.getAlignment(axis);
0N/A float span;
0N/A int ascent;
0N/A int descent;
0N/A
0N/A // find the maximum of the preferred ascents and descents
0N/A span = v.getPreferredSpan(axis);
0N/A ascent = (int)(align * span);
0N/A descent = (int)(span - ascent);
0N/A totalAscent.preferred = Math.max(ascent, totalAscent.preferred);
0N/A totalDescent.preferred = Math.max(descent, totalDescent.preferred);
0N/A
0N/A if (v.getResizeWeight(axis) > 0) {
0N/A // if the view is resizable then do the same for the minimum and
0N/A // maximum ascents and descents
0N/A span = v.getMinimumSpan(axis);
0N/A ascent = (int)(align * span);
0N/A descent = (int)(span - ascent);
0N/A totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
0N/A totalDescent.minimum = Math.max(descent, totalDescent.minimum);
0N/A
0N/A span = v.getMaximumSpan(axis);
0N/A ascent = (int)(align * span);
0N/A descent = (int)(span - ascent);
0N/A totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
0N/A totalDescent.maximum = Math.max(descent, totalDescent.maximum);
0N/A } else {
0N/A // otherwise use the preferred
0N/A totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
0N/A totalDescent.minimum = Math.max(descent, totalDescent.minimum);
0N/A totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
0N/A totalDescent.maximum = Math.max(descent, totalDescent.maximum);
0N/A }
0N/A }
0N/A
0N/A // we now have an overall preferred, minimum, and maximum ascent and descent
0N/A
0N/A // calculate the preferred span as the sum of the preferred ascent and preferred descent
0N/A r.preferred = (int)Math.min((long)totalAscent.preferred + (long)totalDescent.preferred,
0N/A Integer.MAX_VALUE);
0N/A
0N/A // calculate the preferred alignment as the preferred ascent divided by the preferred span
0N/A if (r.preferred > 0) {
0N/A r.alignment = (float)totalAscent.preferred / r.preferred;
0N/A }
0N/A
0N/A
0N/A if (r.alignment == 0.0f) {
0N/A // if the preferred alignment is 0 then the minimum and maximum spans are simply
0N/A // the minimum and maximum descents since there's nothing above the baseline
0N/A r.minimum = totalDescent.minimum;
0N/A r.maximum = totalDescent.maximum;
0N/A } else if (r.alignment == 1.0f) {
0N/A // if the preferred alignment is 1 then the minimum and maximum spans are simply
0N/A // the minimum and maximum ascents since there's nothing below the baseline
0N/A r.minimum = totalAscent.minimum;
0N/A r.maximum = totalAscent.maximum;
0N/A } else {
0N/A // we want to honor the preferred alignment so we calculate two possible minimum
0N/A // span values using 1) the minimum ascent and the alignment, and 2) the minimum
0N/A // descent and the alignment. We'll choose the larger of these two numbers.
0N/A r.minimum = Math.round(Math.max(totalAscent.minimum / r.alignment,
0N/A totalDescent.minimum / (1.0f - r.alignment)));
0N/A // a similar calculation is made for the maximum but we choose the smaller number.
0N/A r.maximum = Math.round(Math.min(totalAscent.maximum / r.alignment,
0N/A totalDescent.maximum / (1.0f - r.alignment)));
0N/A }
0N/A
0N/A return r;
0N/A }
0N/A
0N/A /**
0N/A * Fetches the offset of a particular child's current layout.
0N/A * @param axis the axis being studied
0N/A * @param childIndex the index of the requested child
0N/A * @return the offset (location) for the specified child
0N/A */
0N/A protected int getOffset(int axis, int childIndex) {
0N/A int[] offsets = (axis == majorAxis) ? majorOffsets : minorOffsets;
0N/A return offsets[childIndex];
0N/A }
0N/A
0N/A /**
0N/A * Fetches the span of a particular childs current layout.
0N/A * @param axis the axis being studied
0N/A * @param childIndex the index of the requested child
0N/A * @return the span (width or height) of the specified child
0N/A */
0N/A protected int getSpan(int axis, int childIndex) {
0N/A int[] spans = (axis == majorAxis) ? majorSpans : minorSpans;
0N/A return spans[childIndex];
0N/A }
0N/A
0N/A /**
0N/A * Determines in which direction the next view lays.
0N/A * Consider the View at index n. Typically the <code>View</code>s
0N/A * are layed out from left to right, so that the <code>View</code>
0N/A * to the EAST will be at index n + 1, and the <code>View</code>
0N/A * to the WEST will be at index n - 1. In certain situations,
0N/A * such as with bidirectional text, it is possible
0N/A * that the <code>View</code> to EAST is not at index n + 1,
0N/A * but rather at index n - 1, or that the <code>View</code>
0N/A * to the WEST is not at index n - 1, but index n + 1.
0N/A * In this case this method would return true,
0N/A * indicating the <code>View</code>s are layed out in
0N/A * descending order. Otherwise the method would return false
0N/A * indicating the <code>View</code>s are layed out in ascending order.
0N/A * <p>
0N/A * If the receiver is laying its <code>View</code>s along the
0N/A * <code>Y_AXIS</code>, this will will return the value from
0N/A * invoking the same method on the <code>View</code>
0N/A * responsible for rendering <code>position</code> and
0N/A * <code>bias</code>. Otherwise this will return false.
0N/A *
0N/A * @param position position into the model
0N/A * @param bias either <code>Position.Bias.Forward</code> or
0N/A * <code>Position.Bias.Backward</code>
0N/A * @return true if the <code>View</code>s surrounding the
0N/A * <code>View</code> responding for rendering
0N/A * <code>position</code> and <code>bias</code>
0N/A * are layed out in descending order; otherwise false
0N/A */
0N/A protected boolean flipEastAndWestAtEnds(int position,
0N/A Position.Bias bias) {
0N/A if(majorAxis == Y_AXIS) {
0N/A int testPos = (bias == Position.Bias.Backward) ?
0N/A Math.max(0, position - 1) : position;
0N/A int index = getViewIndexAtPosition(testPos);
0N/A if(index != -1) {
0N/A View v = getView(index);
0N/A if(v != null && v instanceof CompositeView) {
0N/A return ((CompositeView)v).flipEastAndWestAtEnds(position,
0N/A bias);
0N/A }
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A // --- variables ------------------------------------------------
0N/A
0N/A int majorAxis;
0N/A
0N/A int majorSpan;
0N/A int minorSpan;
0N/A
0N/A /*
0N/A * Request cache
0N/A */
0N/A boolean majorReqValid;
0N/A boolean minorReqValid;
0N/A SizeRequirements majorRequest;
0N/A SizeRequirements minorRequest;
0N/A
0N/A /*
0N/A * Allocation cache
0N/A */
0N/A boolean majorAllocValid;
0N/A int[] majorOffsets;
0N/A int[] majorSpans;
0N/A boolean minorAllocValid;
0N/A int[] minorOffsets;
0N/A int[] minorSpans;
0N/A
0N/A /** used in paint. */
0N/A Rectangle tempRect;
0N/A}