0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.lib.editor.view;
0043:
0044: import java.awt.Component;
0045: import java.awt.Graphics;
0046: import java.awt.Rectangle;
0047: import java.awt.Shape;
0048: import java.util.ArrayList;
0049: import java.util.List;
0050: import javax.swing.event.DocumentEvent;
0051: import javax.swing.text.BadLocationException;
0052: import javax.swing.text.Document;
0053: import javax.swing.text.Element;
0054: import javax.swing.text.Position;
0055: import javax.swing.text.View;
0056: import javax.swing.text.ViewFactory;
0057: import org.netbeans.editor.view.spi.EstimatedSpanView;
0058: import org.netbeans.editor.view.spi.ViewInsets;
0059: import org.netbeans.editor.view.spi.ViewLayoutState;
0060: import org.netbeans.lib.editor.util.swing.ElementUtilities;
0061:
0062: /**
0063: * Composite view implementation inspired
0064: * by the {@link javax.swing.text.AsyncBoxView}
0065: * holding its children in <code>GapObjectArray</code>
0066: * and capable of posting complex layout changes
0067: * into a separate layout thread.
0068: * <br>
0069: * This implementation is synchronous by default
0070: * but it contains hooks where the asynchronous behavior
0071: * can be installed.
0072: *
0073: * <p>
0074: * The operation of this view relies on the presence
0075: * of {@link LockView} under the root view in the view hierarchy.
0076: * <br>
0077: * All the view operation is expected to be single-threaded.
0078: * <br>
0079: * The view can only work with document instances
0080: * extending {@link javax.swing.text.AbstractDocument}
0081: * <br>
0082: * The view effectively only operates with preferred spans
0083: * of its children. However it can be extended
0084: * to consider minimum and maximum spans as well
0085: * if the particular layout algorithm would require it.
0086: *
0087: * <p>
0088: * This view implementation separates information
0089: * related to its children from the information required
0090: * for its own operation. The children are kept in a separate object.
0091: * <br>
0092: * The view allows to release its children after the layout information
0093: * for the whole view was determined but the view is not actively
0094: * being rendered. Releasing of the children saves memory
0095: * without loosing valuable information
0096: * about view's own layout.
0097: * <br>
0098: * The view does not initialize its children
0099: * upon call to {@link #setParent(javax.swing.text.View)}
0100: * thus saving memory and cpu time.
0101: * Only if it was previously asked for some information
0102: * related to children (e.g. <code>getViewCount()</code>)
0103: * or for preferred, minimum or maximum span of this view
0104: * it will initialize them during <code>setParent()</code>.
0105: * Once the parent view was set then the children
0106: * are populated immediately once anyone asks
0107: * for children related information or for spans.
0108: *
0109: * <p>
0110: * The view can be constructed and work with element parameter being null.
0111: * <br>
0112: * The following things need to be ensured when using the view in such setup:
0113: * <ul>
0114: * <li> <code>getDocument()</code>,
0115: * <code>getStartOffset()</code>,
0116: * <code>getEndOffset()</code>,
0117: * <code>getAttributes()</code>
0118: * must be overriden to not delegate to element.
0119: *
0120: * <li> <code>insertUpdate()</code> and <code>removeUpdate()</code>
0121: * methods will not find any element changes.
0122: *
0123: * <li> <code>reloadChildren()</code> should be revisited
0124: * whether it will create possible children in a proper way.
0125: *
0126: * </ul>
0127: *
0128: * <p>
0129: * Various constraints for using of this view implementation:
0130: * <ul>
0131: * <li> setParent() implementations of the views being added
0132: * to this view are allowed to trigger
0133: * <code>ViewLayoutState.Parent.majorAxisPreferenceChanged()</code>
0134: * synchronously.
0135: * <li> setParent() implementations of the views being added
0136: * to this view are not allowed to add or remove another children
0137: * synchronously.
0138: * </ul>
0139: *
0140: * @author Miloslav Metelka
0141: * @version 1.00
0142: */
0143:
0144: public class GapBoxView extends View implements ViewLayoutState.Parent,
0145: ViewLayoutState, EstimatedSpanView {
0146:
0147: private static final boolean debugRebuild = Boolean
0148: .getBoolean("netbeans.debug.editor.view.rebuild"); // NOI18N
0149:
0150: /**
0151: * Bit value in <code>statusBits</code> determining
0152: * whether X_AXIS is the major axis.
0153: * <br>
0154: * If this flag is not set then Y_AXIS
0155: * is the major axis.
0156: */
0157: private static final int X_MAJOR_AXIS_BIT = 1;
0158:
0159: /**
0160: * Bit value in <code>statusBits</code> determining
0161: * whether the major axis of this view
0162: * is orthogonal to the major axis of the layout state
0163: * that this view acts like.
0164: */
0165: private static final int MAJOR_AXES_ORTHOGONAL_BIT = 2;
0166:
0167: /**
0168: * Bit value in <code>statusBits</code> determining
0169: * whether preference for this view changed along the major axis.
0170: */
0171: private static final int MAJOR_AXIS_PREFERENCE_CHANGED_BIT = 4;
0172:
0173: /**
0174: * Bit value in <code>statusBits</code> determining
0175: * whether preference for this view changed along the minor axis.
0176: */
0177: private static final int MINOR_AXIS_PREFERENCE_CHANGED_BIT = 8;
0178:
0179: /**
0180: * Bit value in <code>statusBits</code> determining
0181: * whether complete layout of the children has to be done
0182: * during the next layout update of the children.
0183: */
0184: private static final int CHILDREN_LAYOUT_NECESSARY_BIT = 16;
0185:
0186: /**
0187: * Bit value in <code>statusBits</code> determining
0188: * whether there are any pending repaint requests from children
0189: * to be processed.
0190: */
0191: private static final int REPAINT_PENDING_BIT = 32;
0192:
0193: /**
0194: * Bit value in <code>statusBits</code> determining
0195: * whether more than one view along the major axis
0196: * needs to be repainted. The first view to repaint
0197: * is determined by <code>firstRepaintChildIndex</code>.
0198: * The whole area starting with the first view
0199: * to repaint till the end of the view will be repainted.
0200: */
0201: private static final int REPAINT_TILL_END_BIT = 64;
0202:
0203: /**
0204: * Bit value in <code>statusBits</code> determining
0205: * whether the preferred, minimum and maximum
0206: * spans along the axes are estimated instead
0207: * of measured exactly.
0208: */
0209: private static final int ESTIMATED_SPAN_BIT = 128;
0210:
0211: /**
0212: * Bit value in <code>statusBits</code> determining
0213: * whether the updateLayout() is currently being executed.
0214: */
0215: private static final int UPDATE_LAYOUT_IN_PROGRESS = 256;
0216:
0217: /**
0218: * Bit value in <code>statusBits</code> determining
0219: * whether this view is actively acting as layout state
0220: * i.e. if it has parent view being instance
0221: * of <code>ViewLayoutState.Parent</code>.
0222: */
0223: private static final int ACTIVE_LAYOUT_STATE = 512;
0224:
0225: // Bits related to this view acting as layout state follow
0226:
0227: /**
0228: * Bit value in <code>statusBits</code> determining
0229: * whether the x is the major axis of this view
0230: * participating as implementation of ViewLayoutState.
0231: */
0232: private static final int LAYOUT_STATE_X_MAJOR_AXIS_BIT = (ACTIVE_LAYOUT_STATE << 1);
0233:
0234: /**
0235: * Bit value in <code>statusBits</code> determining
0236: * that size of the view needs to be set again
0237: * by <code>View.setSize()</code>
0238: */
0239: private static final int LAYOUT_STATE_VIEW_SIZE_INVALID_BIT = (LAYOUT_STATE_X_MAJOR_AXIS_BIT << 1);
0240:
0241: /**
0242: * Last bit in <code>statusBits</code> used for operation
0243: * of this view. Subclasses may use higher bits if they want.
0244: */
0245: protected static final int GAP_BOX_VIEW_LAST_USED_STATUS_BIT = LAYOUT_STATE_VIEW_SIZE_INVALID_BIT;
0246:
0247: /**
0248: * Bit composition in <code>statusBits</code> determining
0249: * whether any of the parameters affecting layout have
0250: * changed and need to be updated.
0251: */
0252: private static final int LAYOUT_STATE_ANY_INVALID = MAJOR_AXIS_PREFERENCE_CHANGED_BIT
0253: | MINOR_AXIS_PREFERENCE_CHANGED_BIT
0254: | LAYOUT_STATE_VIEW_SIZE_INVALID_BIT | REPAINT_PENDING_BIT;
0255:
0256: /**
0257: * Composition of the bits forming the status of this view.
0258: */
0259: private int statusBits; // 16 bytes View + 4 = 20 bytes
0260:
0261: /**
0262: * Maintainer of </code>ViewLayoutState</code> children.
0263: */
0264: private GapBoxViewChildren children; // 20 + 4 = 24 bytes
0265:
0266: /**
0267: * Raw offset along the major axis of this view
0268: * when acting as layout state.
0269: */
0270: private double layoutStateMajorAxisRawOffset; // 24 + 8 = 32 bytes
0271:
0272: /**
0273: * Raw index of this view in its parent
0274: * when acting as layout state.
0275: */
0276: private int viewRawIndex; // 32 + 4 = 36 bytes
0277:
0278: /**
0279: * Cached preferred span along the major axis useful when this view acts
0280: * as layout state. Its value gets updated during layout update.
0281: * <br>
0282: * This is also the value returned by getMinimumSize(),
0283: * getPreferredSize(), and getMaximumSize() along
0284: * the major axis if the children are not present.
0285: * <br>
0286: * The value does include insets.
0287: * <br>
0288: * <code>float</code> is chosen as the view most typically
0289: * has major axes orthogonal (see isMajorAxesOrthogonal()).
0290: */
0291: private float lastMajorAxisPreferredSpan; // 36 + 4 = 40 bytes
0292:
0293: /**
0294: * Cached preferred span along the minor axis.
0295: * <br>
0296: * This is also the value returned by getMinimumSize(),
0297: * getPreferredSize(), and getMaximumSize() along
0298: * the major axis if the children are not present.
0299: * <br>
0300: * The value does include insets.
0301: */
0302: private float lastMinorAxisPreferredSpan; // 40 + 4 = 44 bytes
0303:
0304: /**
0305: * Current span along the minor axis as set by <code>setSize()</code>.
0306: * <br>
0307: * The value does include insets.
0308: */
0309: private float minorAxisAssignedSpan; // 44 + 4 = 48 bytes
0310:
0311: /**
0312: * Construct a composite box view over the given element.
0313: *
0314: * @param elem the element of the model to represent.
0315: * @param majorAxis the axis to tile along. This can be
0316: * either X_AXIS or Y_AXIS.
0317: */
0318: public GapBoxView(Element elem, int majorAxis) {
0319: super (elem);
0320:
0321: if (majorAxis == View.X_AXIS) {
0322: setStatusBits(X_MAJOR_AXIS_BIT);
0323: } // by default there should be no bits set
0324: }
0325:
0326: /**
0327: * Determines the preferred span for this view along an
0328: * axis.
0329: *
0330: * @param axis may be either View.X_AXIS or View.Y_AXIS
0331: * @return the span the view would like to be rendered into >= 0.
0332: * Typically the view is told to render into the span
0333: * that is returned, although there is no guarantee.
0334: * The parent may choose to resize or break the view.
0335: * @exception IllegalArgumentException for an invalid axis type
0336: */
0337: public float getPreferredSpan(int axis) {
0338: // assert ViewUtilities.isAxisValid(axis);
0339:
0340: return (axis == getMajorAxis()) ? (float) getMajorAxisPreferredSpan()
0341: : getMinorAxisPreferredSpan();
0342: }
0343:
0344: /**
0345: * Determines the minimum span for this view along an
0346: * axis.
0347: *
0348: * @param axis may be either <code>View.X_AXIS</code> or
0349: * <code>View.Y_AXIS</code>
0350: * @return the minimum span the view can be rendered into
0351: */
0352: public @Override
0353: float getMinimumSpan(int axis) {
0354: // If the following implementation gets overriden the view should
0355: // consider additional caching variables because it's acting
0356: // as a layout state.
0357: return getPreferredSpan(axis); // getResizeWeight() not reflected
0358: }
0359:
0360: /**
0361: * Determines the maximum span for this view along an
0362: * axis.
0363: *
0364: * @param axis may be either <code>View.X_AXIS</code> or
0365: * <code>View.Y_AXIS</code>
0366: * @return the maximum span the view can be rendered into
0367: */
0368: public @Override
0369: float getMaximumSpan(int axis) {
0370: // If the following implementation gets overriden the view should
0371: // consider additional caching variables because it's acting
0372: // as a layout state.
0373: return getPreferredSpan(axis); // getResizeWeight() not reflected
0374: }
0375:
0376: public @Override
0377: float getAlignment(int axis) {
0378: return 0.0f;
0379: }
0380:
0381: /**
0382: * Get the insets.
0383: * The default implementation here only returns null
0384: * but it can be redefined by subclasses.
0385: * @return insets of this view or null for no insets.
0386: */
0387: public ViewInsets getInsets() {
0388: return null;
0389: }
0390:
0391: /**
0392: * Get current preferred span along the major axis.
0393: */
0394: final double getMajorAxisPreferredSpan() {
0395: return (children != null) ? children
0396: .getMajorAxisPreferredSpan()
0397: + getMajorAxisInsetSpan() : lastMajorAxisPreferredSpan; // if no children then use cached value
0398: }
0399:
0400: final float getMinorAxisPreferredSpan() {
0401: return (children != null) ? children
0402: .getMinorAxisPreferredSpan()
0403: + getMinorAxisInsetSpan() : lastMinorAxisPreferredSpan;
0404: }
0405:
0406: /**
0407: * Get span along minor axis assigned to this view
0408: * by calling <code>View.setSize()</code> on it.
0409: */
0410: final float getMinorAxisAssignedSpan() {
0411: return minorAxisAssignedSpan;
0412: }
0413:
0414: /**
0415: * Fetch the major axis (the axis the children
0416: * are tiled along). This will have a value of
0417: * either X_AXIS or Y_AXIS.
0418: */
0419: public final int getMajorAxis() {
0420: return isXMajorAxis() ? View.X_AXIS : View.Y_AXIS;
0421: }
0422:
0423: /**
0424: * Fetch the minor axis (the axis orthoginal
0425: * to the tiled axis). This will have a value of
0426: * either X_AXIS or Y_AXIS.
0427: */
0428: public final int getMinorAxis() {
0429: return isXMajorAxis() ? View.Y_AXIS : View.X_AXIS;
0430: }
0431:
0432: /**
0433: * Return true if X is major axis or false if Y is major axis.
0434: */
0435: final boolean isXMajorAxis() {
0436: return isStatusBitsNonZero(X_MAJOR_AXIS_BIT);
0437: }
0438:
0439: /**
0440: * Test whether the major axis of this view
0441: * is orthogonal to the major axis of the layout state
0442: * that this view acts like.
0443: *
0444: * @return true if the axes are orthogonal (which is more common case)
0445: * or false if the axes are equal.
0446: * <br>
0447: * For example if a line view extends GapBoxView
0448: * then it has X as the major axis but it has an Y layout state
0449: * major axis (when acting as layout state for a document view)
0450: * so this method would return true in this case.
0451: */
0452: final boolean isMajorAxesOrthogonal() {
0453: return isStatusBitsNonZero(MAJOR_AXES_ORTHOGONAL_BIT);
0454: }
0455:
0456: /**
0457: * Returns the number of child views of this view.
0458: *
0459: * @return the number of views >= 0
0460: * @see #getView(int)
0461: */
0462: public @Override
0463: int getViewCount() {
0464: return getChildren().getChildCount();
0465: }
0466:
0467: /**
0468: * Returns the view in this container with the particular index.
0469: *
0470: * @param index index of the desired view, >= 0 and < getViewCount()
0471: * @return the view at index <code>index</code>
0472: */
0473: public @Override
0474: View getView(int index) {
0475: return getChild(index).getView();
0476: }
0477:
0478: /*
0479: * Replaces child views. If there are no views to remove
0480: * this acts as an insert. If there are no views to
0481: * add this acts as a remove. Views being removed will
0482: * have the parent set to <code>null</code>,
0483: * and the internal reference to them removed so that they
0484: * may be garbage collected.
0485: *
0486: * <p>
0487: * It is necessary to call <code>updateLayout()</code>
0488: * on this view at some point later so that the possible
0489: * layout changes are done.
0490: *
0491: * @param index the starting index into the child views >= 0
0492: * @param length the number of existing views to replace >= 0
0493: * @param views the child views to insert
0494: */
0495: public @Override
0496: void replace(int index, int length, View[] views) {
0497: if (length < 0) {
0498: throw new IllegalArgumentException("length=" + length
0499: + " < 0"); // NOI18N
0500: }
0501:
0502: if (length == 0 && (views == null || views.length == 0)) { // nothing to do
0503: return;
0504: }
0505:
0506: // make sure that the children are populated
0507: GapBoxViewChildren ch = getChildren();
0508:
0509: // Handle raplace in children only if either length > 0
0510: // or insertLength > 0 or both are > 0
0511: ch.replace(index, length, views);
0512: }
0513:
0514: /**
0515: * Get minimum number of children that must be added at once
0516: * by replace() so that the addition is treated as a lengthy
0517: * operation which means that all the added children will
0518: * have the estimated span flag turned on and
0519: * {@link #scheduleResetChildrenEstimatedSpan(int)}
0520: * will be called to walk through the added children
0521: * and reset their estimated span to false.
0522: */
0523: protected int getReplaceEstimatedThreshold() {
0524: return Integer.MAX_VALUE;
0525: }
0526:
0527: public final boolean isEstimatedSpan() {
0528: return isStatusBitsNonZero(ESTIMATED_SPAN_BIT);
0529: }
0530:
0531: public void setEstimatedSpan(boolean estimatedSpan) {
0532: if (isEstimatedSpan() != estimatedSpan) { // really changed
0533: if (estimatedSpan) {
0534: setStatusBits(ESTIMATED_SPAN_BIT);
0535: } else {
0536: clearStatusBits(ESTIMATED_SPAN_BIT);
0537:
0538: // If children exist make sure the task is scheduled to update them
0539: if (children != null) {
0540: int viewCount = getViewCount();
0541: if (viewCount > 0) {
0542: resetEstimatedSpan(0, viewCount); // reset all children
0543: }
0544: }
0545: }
0546:
0547: }
0548: }
0549:
0550: /**
0551: * Set estimated span flag to false on the given children views.
0552: * <br>
0553: * This method is called from both <code>setEstimatedSpan()</code>
0554: * and from <code>children.replace()</code> if the number of added
0555: * children exceeds threshold count.
0556: * <br>
0557: * Subclasses may want to do this on the background.
0558: */
0559: protected void resetEstimatedSpan(int childIndex, int count) {
0560: while (--count >= 0) {
0561: ViewLayoutState child = getChild(childIndex);
0562: View childView = child.getView();
0563: if (childView instanceof EstimatedSpanView) {
0564: ((EstimatedSpanView) childView).setEstimatedSpan(false);
0565: }
0566: childIndex++;
0567: }
0568: }
0569:
0570: /**
0571: * Remove the child views in the given index range
0572: * and let the default building mechanism to build the child views.
0573: *
0574: * <p>
0575: * It is necessary to call <code>updateLayout()</code>
0576: * on this view at some point later so that the possible
0577: * layout changes are done.
0578: *
0579: * @param index index of the first child view to be rebuilt
0580: * @param count number of chilren in the children array to be rebuilt.
0581: * If <code>index + count<code> is past the end of the children available
0582: * the value of count will be decreased accordingly.
0583: */
0584: public void rebuild(int index, int count) {
0585: if (count != 0) {
0586: int startOffset = (index == 0) ? -1 : getView(index - 1)
0587: .getEndOffset();
0588: int viewCount = getViewCount();
0589: int endIndex = Math.min(index + count, viewCount);
0590: int endOffset = (endIndex == viewCount) ? -1 : getView(
0591: endIndex).getStartOffset();
0592:
0593: if (debugRebuild) {
0594: /*DEBUG*/System.err
0595: .println("GapBoxView.rebuild(): index="
0596: + index // NOI18N
0597: + ", count="
0598: + count // NOI18N
0599: + ", so=" + startOffset + ", eo="
0600: + endOffset // NOI18N
0601: );
0602: }
0603:
0604: reloadChildren(index, count, startOffset, endOffset);
0605: }
0606: }
0607:
0608: /**
0609: * Rebuild based on specification of the offset range.
0610: *
0611: * @param startOffset starting offset of the area in which the views
0612: * should be rebuilt.
0613: * @param endOffset ending offset of the area in which the views
0614: * should be rebuilt.
0615: */
0616: public void offsetRebuild(int startOffset, int endOffset) {
0617: int index = ViewUtilitiesImpl.findLowerViewIndex(this ,
0618: startOffset, false);
0619: int count;
0620: if (index == -1) { // no child views
0621: index = 0;
0622: count = 0;
0623:
0624: } else { // child views exist
0625: count = ViewUtilitiesImpl.findUpperViewIndex(this ,
0626: endOffset, true)
0627: - index + 1;
0628: }
0629:
0630: rebuild(index, count);
0631: }
0632:
0633: /**
0634: * Sets the parent of the view.
0635: * The children are only initialized if someone
0636: * has previously asked for information
0637: * related to children (e.g. <code>getViewCount()</code>)
0638: * or for preferred, minimum or maximum span of this view.
0639: *
0640: * @param parent the parent of the view, <code>null</code> if none
0641: */
0642: public @Override
0643: void setParent(View parent) {
0644: super .setParent(parent);
0645:
0646: /* Make sure that the children get loaded.
0647: * It is necessary to do because children preferences will
0648: * define the preferences of the parent.
0649: */
0650: if (parent != null) {
0651: if (parent instanceof ViewLayoutState.Parent) {
0652: setStatusBits(ACTIVE_LAYOUT_STATE);
0653: } else {
0654: clearStatusBits(ACTIVE_LAYOUT_STATE);
0655: }
0656:
0657: // Resolving whether active layout state must be resolved prior getChildren()
0658: getChildren();
0659:
0660: } else { // parent is being set to null
0661: releaseChildren();
0662: clearStatusBits(ACTIVE_LAYOUT_STATE);
0663: }
0664: }
0665:
0666: public final boolean isActiveLayoutState() {
0667: return isStatusBitsNonZero(ACTIVE_LAYOUT_STATE);
0668: }
0669:
0670: protected GapBoxViewChildren getChildren() {
0671: if (children == null) {
0672: children = createChildren();
0673:
0674: // Possibly load the children
0675: View parent = getParent();
0676: if (parent != null) { // initialize with valid view factory only
0677: reloadChildren(0, 0, -1, -1);
0678: }
0679: }
0680:
0681: return children;
0682: }
0683:
0684: /**
0685: * Get children or null if the children were not yet initialized.
0686: */
0687: protected final GapBoxViewChildren getChildrenNull() {
0688: return children;
0689: }
0690:
0691: /**
0692: * Ask for releasing of the children.
0693: * The view will still remember the last allocated size
0694: * and preferred, minimum and maximum spans.
0695: * However various operations like painting or translations
0696: * between model and visual positions will
0697: * make the children to be loaded again.
0698: */
0699: public void releaseChildren() {
0700: if (children != null) {
0701: unloadChildren();
0702:
0703: children.unload();
0704: children = null;
0705: }
0706: }
0707:
0708: // Implements ViewLayoutState
0709: public final View getView() {
0710: return this ;
0711: }
0712:
0713: // Implements ViewLayoutState
0714: public ViewLayoutState selectLayoutMajorAxis(int axis) {
0715: // assert ViewUtilities.isAxisValid(axis);
0716:
0717: if (axis == View.X_AXIS) {
0718: setStatusBits(LAYOUT_STATE_X_MAJOR_AXIS_BIT);
0719: } else { // y as layout major axis
0720: clearStatusBits(LAYOUT_STATE_X_MAJOR_AXIS_BIT);
0721: }
0722:
0723: // Determine whether major axis of this view
0724: // is orthogonal to major axis for acting as layout state
0725: if (axis == getMajorAxis()) { // major axes equal
0726: clearStatusBits(MAJOR_AXES_ORTHOGONAL_BIT);
0727: } else { // major axes orthogonal
0728: setStatusBits(MAJOR_AXES_ORTHOGONAL_BIT);
0729: }
0730:
0731: return this ;
0732: }
0733:
0734: // Implements ViewLayoutState
0735: public boolean isFlyweight() {
0736: return false;
0737: }
0738:
0739: // Implements ViewLayoutState
0740: public void updateLayout() {
0741: if (isLayoutValid()) { // Nothing to do
0742: return;
0743: }
0744:
0745: if (isStatusBitsNonZero(UPDATE_LAYOUT_IN_PROGRESS)) {
0746: return;
0747: }
0748: setStatusBits(UPDATE_LAYOUT_IN_PROGRESS);
0749:
0750: View parent = getParent();
0751: if (parent == null) { // disconnected from hierarchy
0752: return;
0753: }
0754: ViewLayoutState.Parent lsParent = (parent instanceof ViewLayoutState.Parent) ? (ViewLayoutState.Parent) parent
0755: : null;
0756:
0757: // Make sure all individual pending children layout updates are addressed
0758: children.childrenUpdateLayout();
0759:
0760: // Layout the children if necessary
0761: if (isChildrenLayoutNecessary()) {
0762: resetChildrenLayoutNecessary();
0763:
0764: children.childrenLayout(); // re-compute layout info for children
0765: }
0766:
0767: // Update cached variable corresponding to layout state major axis
0768: boolean parentWillRepaint = false;
0769:
0770: // Check whether preference did not change along a particular axis
0771: // and if so message preferenceChanged to parent.
0772: // Cache the following two vars before they get cleared in next section:
0773: boolean majorAxisPreferenceChanged = isMajorAxisPreferenceChanged();
0774: boolean minorAxisPreferenceChanged = isMinorAxisPreferenceChanged();
0775: resetAxesPreferenceChanged();
0776:
0777: if (majorAxisPreferenceChanged) {
0778: // Update the cached value for the major axis
0779: if (children != null) { // only if children exist
0780: double delta = updateLastMajorAxisPreferredSpan();
0781: if (delta != 0.0d && lsParent != null) {
0782: if (isMajorAxesOrthogonal()) {
0783: lsParent.minorAxisPreferenceChanged(this );
0784: } else {
0785: lsParent
0786: .majorAxisPreferenceChanged(this , delta);
0787: parentWillRepaint = true;
0788: }
0789: }
0790: }
0791: }
0792:
0793: if (minorAxisPreferenceChanged) {
0794: // Update the cached value for the minor axis
0795: if (children != null) { // only if children exist
0796: double delta = updateLastMinorAxisPreferredSpan();
0797: if (delta != 0.0d && lsParent != null) {
0798: if (isMajorAxesOrthogonal()) {
0799: lsParent
0800: .majorAxisPreferenceChanged(this , delta);
0801: parentWillRepaint = true;
0802: } else {
0803: lsParent.minorAxisPreferenceChanged(this );
0804: }
0805: }
0806: }
0807: }
0808:
0809: // If not active layout state propagate preference change upwards
0810: // If this is active layout state then this was already propagated
0811: // by marking itself as needing layout update which is now being
0812: // updated.
0813: if (majorAxisPreferenceChanged || minorAxisPreferenceChanged
0814: || !isActiveLayoutState()) {
0815: // Either of major or minor axis (or both) has changed
0816: boolean horizontalChange = false;
0817: boolean verticalChange = false;
0818:
0819: if (isXMajorAxis()) {
0820: horizontalChange = majorAxisPreferenceChanged;
0821: verticalChange = minorAxisPreferenceChanged;
0822: } else {
0823: horizontalChange = minorAxisPreferenceChanged;
0824: verticalChange = majorAxisPreferenceChanged;
0825: }
0826:
0827: parent.preferenceChanged(this , horizontalChange,
0828: verticalChange);
0829: }
0830:
0831: // Check whether size must be set on this view
0832: if (isStatusBitsNonZero(LAYOUT_STATE_VIEW_SIZE_INVALID_BIT)) {
0833: clearStatusBits(LAYOUT_STATE_VIEW_SIZE_INVALID_BIT);
0834:
0835: if (lsParent != null) { // should only be done when having layout state parent
0836: float width;
0837: float height;
0838: float layoutStateMajorAxisSpan = getPreferredSpan(getLayoutStateMajorAxis());
0839: float layoutStateMinorAxisSpan = lsParent
0840: .getMinorAxisSpan(this );
0841: if (isXLayoutStateMajorAxis()) {
0842: width = layoutStateMajorAxisSpan;
0843: height = layoutStateMinorAxisSpan;
0844: } else {
0845: width = layoutStateMinorAxisSpan;
0846: height = layoutStateMajorAxisSpan;
0847: }
0848:
0849: setSize(width, height);
0850: }
0851: }
0852:
0853: if (children != null && isRepaintPending()) {
0854: if (!parentWillRepaint) {
0855: processRepaint(lsParent);
0856: }
0857: // After painting is finished reset the variables
0858: resetRepaintPending();
0859: }
0860:
0861: clearStatusBits(UPDATE_LAYOUT_IN_PROGRESS);
0862:
0863: // Call recursively to make sure that there is no more work.
0864: updateLayout();
0865: }
0866:
0867: /**
0868: * Update the layout in response to receiving notification of
0869: * change from the model.
0870: *
0871: * @param ec changes to the element this view is responsible
0872: * for (may be null if there were no changes).
0873: * @param e the change information from the associated document
0874: * @param a the current allocation of the view
0875: * @see #insertUpdate
0876: * @see #removeUpdate
0877: * @see #changedUpdate
0878: */
0879: protected @Override
0880: void updateLayout(DocumentEvent.ElementChange ec, DocumentEvent e,
0881: Shape a) {
0882:
0883: //super.updateLayout(ec, e, a);
0884: }
0885:
0886: /**
0887: * Called by children to mark layout of this view invalid.
0888: * This only has effect if this view is active layout state.
0889: */
0890: public void layoutInvalid(ViewLayoutState child) {
0891: int childIndex = children.getChildIndexNoCheck(child);
0892: children.markLayoutInvalid(childIndex, 1);
0893: }
0894:
0895: protected void markLayoutInvalid() {
0896: if (isActiveLayoutState()) {
0897: ((ViewLayoutState.Parent) getParent()).layoutInvalid(this );
0898: } else { // not active layout state
0899: // Update layout immediately - subclasses can override
0900: directUpdateLayout();
0901: }
0902: }
0903:
0904: /**
0905: * This method is called when this view is not acting as active
0906: * layout state and its layout becomes invalid.
0907: * <br>
0908: * By default the layout is updated immediately
0909: * but subclasses may change that but they must ensure
0910: * that the layout will be updated later.
0911: */
0912: protected void directUpdateLayout() {
0913: updateLayout();
0914: }
0915:
0916: /**
0917: * Process pending repaint requests from children.
0918: * <br>
0919: * Children are guaranteed to be non-null once this method gets called.
0920: */
0921: protected void processRepaint(ViewLayoutState.Parent lsParent) {
0922: if (lsParent != null) { // parent view is ViewLayoutState.Parent
0923: int firstRepaintChildIndex = children
0924: .getFirstRepaintChildIndex();
0925: double majorAxisOffset = children
0926: .getMajorAxisOffset(firstRepaintChildIndex);
0927: double repaintMajorOffset;
0928: double repaintMajorSpan;
0929: float repaintMinorOffset;
0930: float repaintMinorSpan;
0931: if (isRepaintTillEnd()
0932: || firstRepaintChildIndex >= getViewCount() // bit strange but possible after last child remove
0933: ) {
0934: if (isMajorAxesOrthogonal()) {
0935: repaintMajorOffset = 0;
0936: repaintMajorSpan = 0; // till end of view's span in parent
0937: repaintMinorOffset = (float) majorAxisOffset;
0938: repaintMinorSpan = 0; // till parent view minor span end
0939:
0940: } else { // major axes equal
0941: repaintMajorOffset = majorAxisOffset;
0942: repaintMajorSpan = 0; // till end of view's span in parent
0943: repaintMinorOffset = 0;
0944: repaintMinorSpan = 0; // till parent view minor span end
0945: }
0946:
0947: } else { // repainting just single child that did not change major axis span
0948: double majorAxisSpan = getChild(firstRepaintChildIndex)
0949: .getLayoutMajorAxisPreferredSpan();
0950: if (isMajorAxesOrthogonal()) {
0951: repaintMajorOffset = 0;
0952: repaintMajorSpan = 0; // till end of view's span in parent
0953: repaintMinorOffset = (float) majorAxisOffset;
0954: repaintMinorSpan = (float) majorAxisSpan;
0955:
0956: } else { // major axes equal
0957: repaintMajorOffset = majorAxisOffset;
0958: repaintMajorSpan = majorAxisSpan;
0959: repaintMinorOffset = 0;
0960: repaintMinorSpan = 0; // till parent view minor span end
0961: }
0962: }
0963:
0964: lsParent.repaint(this , repaintMajorOffset,
0965: repaintMajorSpan, repaintMinorOffset,
0966: repaintMinorSpan);
0967:
0968: } else { // do not know allocation here => repaint whole component
0969: Component c = getContainer();
0970: if (c != null) {
0971: c.repaint();
0972: }
0973: }
0974: }
0975:
0976: /**
0977: * Mark that the child with the given index should be repainted.
0978: *
0979: * @param childIndex index of child that should be marked for repaint.
0980: * @param repaintTillEnd if set to true then all children following
0981: * the child should be repainted as well.
0982: * @return true if lower child index was marked for repaint by this method
0983: * than there was before.
0984: */
0985: protected boolean markRepaint(int childIndex, boolean repaintTillEnd) {
0986: boolean lowerIndexMarked = false;
0987: if (children != null) {
0988: int firstRepaintChildIndex = children
0989: .getFirstRepaintChildIndex();
0990: if (!isRepaintTillEnd()) { // not repainting more yet
0991: if (firstRepaintChildIndex == -1) { // no repainting yet
0992: lowerIndexMarked = true;
0993: markRepaintPending();
0994: children.setFirstRepaintChildIndex(childIndex);
0995: if (repaintTillEnd) {
0996: setStatusBits(REPAINT_TILL_END_BIT);
0997: }
0998:
0999: } else if (firstRepaintChildIndex != childIndex) { // other child than first
1000: if (childIndex < firstRepaintChildIndex) {
1001: lowerIndexMarked = true;
1002: children.setFirstRepaintChildIndex(childIndex);
1003: }
1004: setStatusBits(REPAINT_TILL_END_BIT); // surely will repaint to end
1005:
1006: } else { // same child already scheduled for repaint
1007: if (repaintTillEnd) {
1008: setStatusBits(REPAINT_TILL_END_BIT);
1009: }
1010: }
1011:
1012: } else { // repaint more children already - firstRepaintChildIndex must be valid
1013: if (childIndex < firstRepaintChildIndex) {
1014: lowerIndexMarked = true;
1015: children.setFirstRepaintChildIndex(childIndex);
1016: }
1017: }
1018: }
1019:
1020: return lowerIndexMarked;
1021: }
1022:
1023: public final boolean isRepaintPending() {
1024: return isStatusBitsNonZero(REPAINT_PENDING_BIT);
1025: }
1026:
1027: protected final void markRepaintPending() {
1028: setStatusBits(REPAINT_PENDING_BIT);
1029: }
1030:
1031: protected void resetRepaintPending() {
1032: if (children != null) {
1033: children.setFirstRepaintChildIndex(-1);
1034: }
1035: clearStatusBits(REPAINT_PENDING_BIT | REPAINT_TILL_END_BIT);
1036: }
1037:
1038: public final boolean isRepaintTillEnd() {
1039: return isStatusBitsNonZero(REPAINT_TILL_END_BIT);
1040: }
1041:
1042: /**
1043: * Test whether the preference along the layout state minor axis
1044: * has really changed.
1045: * <br>
1046: * The default implementation only checks preferred span
1047: * but the implementation reflecting minimum and maximum spans
1048: * can extend this method.
1049: *
1050: * @return true if it has really changed or false if not.
1051: */
1052: protected boolean isLayoutMinorAxisPreferenceChanged(
1053: boolean majorAxesOrthogonal) {
1054: double delta;
1055: if (majorAxesOrthogonal) {
1056: // processing minor layout state axis but it's in fact major view axis
1057: delta = updateLastMajorAxisPreferredSpan();
1058: } else { // major axes equal
1059: // processing minor layout state axis which is also minor view axis
1060: delta = updateLastMinorAxisPreferredSpan();
1061: }
1062:
1063: return (delta != 0.0d);
1064: }
1065:
1066: private double updateLastMinorAxisPreferredSpan() {
1067: float currentMinorAxisPreferredSpan = children
1068: .getMinorAxisPreferredSpan();
1069: double delta = currentMinorAxisPreferredSpan
1070: - lastMinorAxisPreferredSpan;
1071: lastMinorAxisPreferredSpan = currentMinorAxisPreferredSpan;
1072: return delta;
1073: }
1074:
1075: private double updateLastMajorAxisPreferredSpan() {
1076: double currentMajorAxisPreferredSpan = children
1077: .getMajorAxisPreferredSpan();
1078: double delta = currentMajorAxisPreferredSpan
1079: - lastMajorAxisPreferredSpan;
1080: // Here the truncation occurs but if the major axes are orthogonal
1081: // or if the spans are not big enough to exhaust float precision
1082: // this should not hurt.
1083: lastMajorAxisPreferredSpan = (float) currentMajorAxisPreferredSpan;
1084: return delta;
1085: }
1086:
1087: // Implements ViewLayoutState
1088: public boolean isLayoutValid() {
1089: return !isStatusBitsNonZero(LAYOUT_STATE_ANY_INVALID)
1090: && (children == null || children
1091: .getUpdateLayoutChildCount() == 0);
1092: }
1093:
1094: // Implements ViewLayoutState
1095: public double getLayoutMajorAxisPreferredSpan() {
1096: return (isMajorAxesOrthogonal()) ? lastMinorAxisPreferredSpan
1097: : lastMajorAxisPreferredSpan;
1098: }
1099:
1100: // Implements ViewLayoutState
1101: public float getLayoutMinorAxisPreferredSpan() {
1102: return isMajorAxesOrthogonal() ? lastMajorAxisPreferredSpan
1103: : lastMinorAxisPreferredSpan;
1104: }
1105:
1106: // Implements ViewLayoutState
1107: public float getLayoutMinorAxisMinimumSpan() {
1108: // It has to be overriden if the layout state minimum span is maintained
1109: return getLayoutMinorAxisPreferredSpan();
1110: }
1111:
1112: // Implements ViewLayoutState
1113: public float getLayoutMinorAxisMaximumSpan() {
1114: // It has to be overriden if the layout state maximum span is maintained
1115: return getLayoutMinorAxisPreferredSpan();
1116: }
1117:
1118: // Implements ViewLayoutState
1119: public float getLayoutMinorAxisAlignment() {
1120: // Alignment is assumed not to change over time
1121: // It needs to be cached if that's not true
1122: return getAlignment(getLayoutStateMinorAxis());
1123: }
1124:
1125: // Implements ViewLayoutState
1126: public double getLayoutMajorAxisRawOffset() {
1127: return layoutStateMajorAxisRawOffset;
1128: }
1129:
1130: // Implements ViewLayoutState
1131: public void setLayoutMajorAxisRawOffset(double majorAxisRawOffset) {
1132: this .layoutStateMajorAxisRawOffset = majorAxisRawOffset;
1133: }
1134:
1135: protected final ViewLayoutState.Parent getLayoutStateParent() {
1136: View parent = getParent();
1137: return (parent instanceof ViewLayoutState.Parent) ? ((ViewLayoutState.Parent) parent)
1138: : null;
1139: }
1140:
1141: protected final boolean isXLayoutStateMajorAxis() {
1142: return (isStatusBitsNonZero(LAYOUT_STATE_X_MAJOR_AXIS_BIT));
1143: }
1144:
1145: protected final int getLayoutStateMajorAxis() {
1146: return (isStatusBitsNonZero(LAYOUT_STATE_X_MAJOR_AXIS_BIT)) ? View.X_AXIS
1147: : View.Y_AXIS;
1148: }
1149:
1150: protected final int getLayoutStateMinorAxis() {
1151: return (isStatusBitsNonZero(LAYOUT_STATE_X_MAJOR_AXIS_BIT)) ? View.Y_AXIS
1152: : View.X_AXIS;
1153: }
1154:
1155: // Implements ViewLayoutState
1156: public int getViewRawIndex() {
1157: return viewRawIndex;
1158: }
1159:
1160: // Implements ViewLayoutState
1161: public void setViewRawIndex(int viewRawIndex) {
1162: this .viewRawIndex = viewRawIndex;
1163: }
1164:
1165: // Implements ViewLayoutState
1166: public void viewPreferenceChanged(boolean width, boolean height) {
1167: markViewSizeInvalid();
1168: }
1169:
1170: // Implements ViewLayoutState
1171: public void markViewSizeInvalid() {
1172: setStatusBits(LAYOUT_STATE_VIEW_SIZE_INVALID_BIT);
1173: }
1174:
1175: // Implements ViewLayoutState.Parent
1176: /**
1177: * Preference of one of the children has changed along the major axis.
1178: */
1179: public void majorAxisPreferenceChanged(ViewLayoutState child,
1180: double majorAxisSpanDelta) {
1181: int childIndex = getChildIndexNoCheck(child);
1182: if (majorAxisSpanDelta != 0.0d) {
1183: // repaint till end as the children above the index get shifted
1184: markRepaint(childIndex, true);
1185: children.majorAxisPreferenceChanged(child, childIndex,
1186: majorAxisSpanDelta);
1187:
1188: } else { // make sure that the child gets repainted
1189: markRepaint(childIndex, false);
1190: }
1191: }
1192:
1193: // Implements ViewLayoutState.Parent
1194: /**
1195: * Preference of one of the children has changed along the minor axis.
1196: */
1197: public void minorAxisPreferenceChanged(ViewLayoutState child) {
1198: int childIndex = getChildIndexNoCheck(child);
1199: markRepaint(childIndex, false);
1200: children.minorAxisPreferenceChanged(child, childIndex);
1201: }
1202:
1203: // Implements ViewLayoutState.Parent
1204: /**
1205: * Get span of the given child along the minor axis of this view.
1206: */
1207: public float getMinorAxisSpan(ViewLayoutState child) {
1208: // Delegate to children
1209: return getChildren().getMinorAxisSpan(child);
1210: }
1211:
1212: // Implements ViewLayoutState.Parent
1213: public void repaint(ViewLayoutState child, double majorAxisOffset,
1214: double majorAxisSpan, float minorAxisOffset,
1215: float minorAxisSpan) {
1216:
1217: int childIndex = getChildIndexNoCheck(child);
1218: markRepaint(childIndex, false);
1219: }
1220:
1221: /**
1222: * Test whether complete layout of the children necessary.
1223: */
1224: public final boolean isChildrenLayoutNecessary() {
1225: return isStatusBitsNonZero(CHILDREN_LAYOUT_NECESSARY_BIT);
1226: }
1227:
1228: /**
1229: * Mark that a complete layout of children is necessary.
1230: * <br>
1231: * This method does no scheduling of the children layout update.
1232: */
1233: public final void markChildrenLayoutNecessary() {
1234: setStatusBits(CHILDREN_LAYOUT_NECESSARY_BIT);
1235: }
1236:
1237: final void resetChildrenLayoutNecessary() {
1238: clearStatusBits(CHILDREN_LAYOUT_NECESSARY_BIT);
1239: }
1240:
1241: /**
1242: * Child views can call this on the parent to indicate that
1243: * the preference has changed and should be reconsidered
1244: * for layout. This is reimplemented to queue new work
1245: * on the layout thread. This method gets messaged from
1246: * multiple threads via the children.
1247: *
1248: * @param childView the child view of this view or null to signal
1249: * change in this view.
1250: * @param width true if the width preference has changed
1251: * @param height true if the height preference has changed
1252: * @see javax.swing.JComponent#revalidate
1253: */
1254: public @Override
1255: void preferenceChanged(View childView, boolean width, boolean height) {
1256: if (childView == null) { // notify parent about this view change
1257: getParent().preferenceChanged(this , width, height);
1258:
1259: } else { // Child of this view has changed
1260: // First find the index of the child view
1261: int index;
1262: // Try to cast the view to ViewLayoutState and find index that way
1263: if (childView instanceof ViewLayoutState) {
1264: // Trust the view to be really child of this view - check is done later
1265: index = getChildIndexNoCheck((ViewLayoutState) childView);
1266: } else { // child view not instance of ViewLayoutState
1267: // Use binary search to find the view
1268: index = getViewIndex(childView.getStartOffset());
1269: }
1270:
1271: ViewLayoutState child = getChild(index);
1272: if (child.getView() != childView) {
1273: int ind;
1274: for (ind = getViewCount() - 1; ind >= 0; ind--) {
1275: if (getView(ind) == childView) {
1276: break;
1277: }
1278: }
1279: if (ind == -1) {
1280: throw new IllegalArgumentException("childView=" // NOI18N
1281: + childView + " not child of view " + this ); // NOI18N
1282:
1283: } else { // is child but at different index
1284: throw new IllegalStateException(
1285: "Internal error. Child expected at index="
1286: + index // NOI18N
1287: + " but found at index=" + ind); // NOI18N
1288: }
1289: }
1290:
1291: // Mark the child as invalid
1292: child.viewPreferenceChanged(width, height);
1293:
1294: // Mark the layout of the child as invalid - this must be done
1295: // _after_ the real changes affecting child's layout were performed
1296: // because the layout may be directly updated
1297: // by the parent during the call of the following method.
1298: children.markLayoutInvalid(index, 1);
1299: }
1300: }
1301:
1302: /**
1303: * Sets the size of the view. This should cause
1304: * layout of the view if the view caches any layout
1305: * information.
1306: *
1307: * <p>
1308: * The propagation of this operation to child views
1309: * can be done asynchronously if appropriate.
1310: *
1311: * @param width the width >= 0
1312: * @param height the height >= 0
1313: */
1314: public @Override
1315: void setSize(float width, float height) {
1316: float targetMajorAxisSpan;
1317: float targetMinorAxisSpan;
1318: if (isXMajorAxis()) {
1319: targetMajorAxisSpan = width;
1320: targetMinorAxisSpan = height;
1321: } else { // Y is major axis
1322: targetMajorAxisSpan = height;
1323: targetMinorAxisSpan = width;
1324: }
1325:
1326: // Span along major axis is ignored by default
1327: setSpanOnMajorAxis(targetMajorAxisSpan);
1328: setSpanOnMinorAxis(targetMinorAxisSpan);
1329: }
1330:
1331: protected void setSpanOnMajorAxis(float targetMajorAxisSpan) {
1332: // along the major axis the value is ignored by default
1333: // but subclasses doing e.g. line wrapping can override that
1334: }
1335:
1336: protected void setSpanOnMinorAxis(float targetMinorAxisSpan) {
1337: if (targetMinorAxisSpan != minorAxisAssignedSpan) {
1338: minorAxisAssignedSpan = targetMinorAxisSpan;
1339: //float targetSpanNoInsets = targetMinorAxisSpan - getMinorAxisInsetSpan();
1340:
1341: // do not recompute children if estimated span or estimated change task running
1342: if (!isEstimatedSpan() && !isChildrenResizeDisabled()) {
1343: // mark all of the ViewLayoutState instances as needing to
1344: // resize the child.
1345: int viewCount = getViewCount();
1346: if (viewCount != 0) {
1347: markSizeInvalid(0, viewCount);
1348: }
1349: }
1350: }
1351: }
1352:
1353: /**
1354: * This method marks sizes of all the children as invalid
1355: * so the next layout update will resize each children.
1356: * <br>
1357: * This is made as protected method since large complex views
1358: * may consider this operation lengthy with certain amount
1359: * of children so they may need to do this operation in background
1360: * and delegate to this implementation for small amount
1361: * of children only.
1362: *
1363: * @param >0 total number of child views of this view. It's given
1364: * as parameter because subclasses will typically decide their
1365: * behavior based on the total view count.
1366: */
1367: protected void markSizeInvalid(int childIndex, int count) {
1368: while (--count >= 0) {
1369: ViewLayoutState child = getChild(childIndex);
1370: if (!child.isFlyweight()) {
1371: child.markViewSizeInvalid();
1372: }
1373: childIndex++;
1374: }
1375:
1376: // Mark the layout of the child as invalid - this must be done
1377: // _after_ the real changes affecting child's layout were performed
1378: // because the layout may be directly updated
1379: // by the parent during the call of the following method.
1380: children.markLayoutInvalid(childIndex, count);
1381: }
1382:
1383: /**
1384: * Return true if the children should not be attempted to resize
1385: * once <code>setSize()</code> is called on this view.
1386: * <br>
1387: * Turning this on may save considerable time but it should be only
1388: * used if the views truly do not react on <code>setSize()</code>
1389: * e.g. this should *not* be used if line-wrapping is turned on.
1390: */
1391: protected boolean isChildrenResizeDisabled() {
1392: return false; // by default must resize children upon setSize() on view
1393: }
1394:
1395: /**
1396: * Fetches the allocation for the given child view.
1397: * This enables finding out where various views
1398: * are located, without assuming the views store
1399: * their location. This returns null since the
1400: * default is to not have any child views.
1401: *
1402: * @param index the index of the child, >= 0 and < getViewCount()
1403: * @param a the allocation to this view.
1404: * @return the allocation to the child
1405: */
1406: public @Override
1407: Shape getChildAllocation(int index, Shape a) {
1408: if (a == null) {
1409: return null;
1410: }
1411:
1412: Rectangle alloc = reallocate(a); // returned rect can be modified
1413: int this ViewAllocX = alloc.x;
1414: int this ViewAllocY = alloc.y;
1415:
1416: getChildren().getChildCoreAllocation(index, alloc); // alloc overwritten
1417: alloc.x += this ViewAllocX;
1418: alloc.y += this ViewAllocY;
1419:
1420: // Add insets if necessary
1421: ViewInsets insets = getInsets();
1422: if (insets != null) {
1423: alloc.x += insets.getLeft();
1424: alloc.y += insets.getRight();
1425: }
1426:
1427: return alloc;
1428: }
1429:
1430: /**
1431: * Fetches the child view index at the given point.
1432: * This is called by the various View methods that
1433: * need to calculate which child to forward a message
1434: * to.
1435: *
1436: * @param x the X coordinate >= 0
1437: * @param y the Y coordinate >= 0
1438: * @param a the allocation to thid view
1439: * @return index of the view that best represents the given visual
1440: * location or -1 if there are no children.
1441: * <br>
1442: * If the point is below the area of the first child view
1443: * then the index of the first child view is returned.
1444: * <br>
1445: * If the point is above the area of the last child view
1446: * then the index of the last child view is returned.
1447: */
1448: public int getViewIndexAtPoint(float x, float y, Shape a) {
1449: Rectangle alloc = reallocate(a); // returned rect can be modified
1450: x -= alloc.x;
1451: y -= alloc.y;
1452:
1453: // Subtract insets if necessary
1454: ViewInsets insets = getInsets();
1455: if (insets != null) {
1456: x -= insets.getLeft();
1457: y -= insets.getRight();
1458: }
1459:
1460: return getChildren().getChildIndexAtCorePoint(x, y);
1461: }
1462:
1463: /**
1464: * Returns the child view index representing the given position in
1465: * the model.
1466: *
1467: * @param offset the position >= 0.
1468: * @param b either forward or backward bias.
1469: * @return index of the view representing the given position, or
1470: * -1 if no view represents that position
1471: */
1472: public @Override
1473: int getViewIndex(int offset, Position.Bias b) {
1474: if (b == Position.Bias.Backward) {
1475: offset -= 1;
1476: }
1477:
1478: return getViewIndex(offset);
1479: }
1480:
1481: /**
1482: * Returns the child view index representing the given position in
1483: * the model.
1484: *
1485: * @param offset the position >= 0.
1486: * @return index of the view representing the given position, or
1487: * -1 if no view represents that position
1488: */
1489: public int getViewIndex(int offset) {
1490: return ViewUtilitiesImpl.findViewIndexBounded(this , offset);
1491: }
1492:
1493: /**
1494: * Render the view using the given allocation and
1495: * rendering surface.
1496: *
1497: * @param g the rendering surface to use
1498: * @param a the allocated region to render into
1499: * @see View#paint
1500: */
1501: public void paint(Graphics g, Shape a) {
1502: Rectangle alloc = reallocate(a); // returned rect can be modified
1503: getChildren().paintChildren(g, alloc);
1504: }
1505:
1506: /**
1507: * Provides a mapping from the document model coordinate space
1508: * to the coordinate space of the view mapped to it.
1509: *
1510: * @param pos the position to convert >= 0
1511: * @param a the allocated region to render into
1512: * @param b the bias toward the previous character or the
1513: * next character represented by the offset, in case the
1514: * position is a boundary of two views.
1515: * @return the bounding box of the given position is returned
1516: * @exception BadLocationException if the given position does
1517: * not represent a valid location in the associated document
1518: * @exception IllegalArgumentException for an invalid bias argument
1519: * @see View#viewToModel
1520: */
1521: public Shape modelToView(int pos, Shape a, Position.Bias b)
1522: throws BadLocationException {
1523: int index = getViewIndex(pos, b);
1524: if (index >= 0) {
1525: Shape ca = getChildAllocation(index, a);
1526:
1527: // forward to the child view
1528: ViewLayoutState child = getChild(index);
1529: View cv = child.getView();
1530: return cv.modelToView(pos, ca, b);
1531: } else {
1532: throw new BadLocationException("Offset "
1533: + pos
1534: + " with bias "
1535: + b
1536: + " is outside of the view" //NOI18N
1537: + ", children = "
1538: + getViewCount() //NOI18N
1539: + (getViewCount() > 0 ? " covering offsets <"
1540: + //NOI18N
1541: getView(0).getStartOffset() + ", "
1542: + //NOI18N
1543: getView(getViewCount() - 1).getEndOffset()
1544: + ">" : "") //NOI18N
1545: , pos);
1546: }
1547: }
1548:
1549: /**
1550: * Provides a mapping from the view coordinate space to the logical
1551: * coordinate space of the model. The biasReturn argument will be
1552: * filled in to indicate that the point given is closer to the next
1553: * character in the model or the previous character in the model.
1554: * <p>
1555: * This is expected to be called by the GUI thread, holding a
1556: * read-lock on the associated model. It is implemented to
1557: * locate the child view and determine it's allocation with a
1558: * lock on the ChildLocator object, and to call viewToModel
1559: * on the child view with a lock on the ViewLayoutState object
1560: * to avoid interaction with the layout thread.
1561: *
1562: * @param x the X coordinate >= 0
1563: * @param y the Y coordinate >= 0
1564: * @param a the allocated region to render into
1565: * @return the location within the model that best represents the
1566: * given point in the view >= 0. The biasReturn argument will be
1567: * filled in to indicate that the point given is closer to the next
1568: * character in the model or the previous character in the model.
1569: */
1570: public int viewToModel(float x, float y, Shape a,
1571: Position.Bias[] biasReturn) {
1572: int pos; // return position
1573: int index; // child index to forward to
1574: Shape ca; // child allocation
1575:
1576: index = getViewIndexAtPoint(x, y, a);
1577: index = Math.max(index, 0);
1578: if (index < getViewCount()) {
1579: ca = getChildAllocation(index, a);
1580:
1581: // forward to the child view
1582: ViewLayoutState child = getChild(index);
1583: View v = child.getView();
1584: pos = v.viewToModel(x, y, ca, biasReturn);
1585:
1586: } else { // at the end
1587: int endOff = getEndOffset();
1588: Document doc = getDocument();
1589: pos = (doc != null && doc.getLength() < endOff) ? doc
1590: .getLength() : endOff;
1591: }
1592:
1593: return pos;
1594: }
1595:
1596: /**
1597: * Provides a way to determine the next visually represented model
1598: * location that one might place a caret. Some views may not be visible,
1599: * they might not be in the same order found in the model, or they just
1600: * might not allow access to some of the locations in the model.
1601: *
1602: * @param pos the position to convert >= 0
1603: * @param a the allocated region to render into
1604: * @param direction the direction from the current position that can
1605: * be thought of as the arrow keys typically found on a keyboard;
1606: * this may be one of the following:
1607: * <ul>
1608: * <code>SwingConstants.WEST</code>
1609: * <code>SwingConstants.EAST</code>
1610: * <code>SwingConstants.NORTH</code>
1611: * <code>SwingConstants.SOUTH</code>
1612: * </ul>
1613: * @param biasRet an array contain the bias that was checked
1614: * @return the location within the model that best represents the next
1615: * location visual position
1616: * @exception BadLocationException
1617: * @exception IllegalArgumentException if <code>direction</code> is invalid
1618: */
1619: public @Override
1620: int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
1621: int direction, Position.Bias[] biasRet)
1622: throws BadLocationException {
1623:
1624: return ViewUtilitiesImpl.getNextVisualPositionFrom(this , pos,
1625: b, a, direction, biasRet);
1626: }
1627:
1628: /**
1629: * Fetch the object representing the layout state of
1630: * of the child at the given index.
1631: *
1632: * @param index the child index.
1633: * This must be a value >= 0 and < getViewCount().
1634: * @throws IndexOutOfBoundsException in case the index was invalid.
1635: */
1636: protected final ViewLayoutState getChild(int index) {
1637: return getChildren().getChild(index);
1638: }
1639:
1640: /**
1641: * Get the index of the given child layout state in this view.
1642: *
1643: * @param child layout state which index in this view should be found.
1644: * @return >=0 integer index of the given child in this view.
1645: * Returns -1 if the given child is not present at the given index
1646: * in this view.
1647: */
1648: protected final int getChildIndex(ViewLayoutState child) {
1649: return getChildren().getChildIndex(child);
1650: }
1651:
1652: /**
1653: * Get the index of the given child layout state in this view.
1654: *
1655: * @param child layout state which index in this view should be found.
1656: * @return >=0 integer index of the given child in this view.
1657: * <b>Note:</b> This method does no checking whether the child
1658: * is really the child of this view.
1659: */
1660: protected final int getChildIndexNoCheck(ViewLayoutState child) {
1661: return getChildren().getChildIndexNoCheck(child);
1662: }
1663:
1664: /**
1665: * Can be overriden by subclasses to return
1666: * a different children implementation.
1667: */
1668: protected GapBoxViewChildren createChildren() {
1669: return new GapBoxViewChildren(this );
1670: }
1671:
1672: protected boolean useCustomReloadChildren() {
1673: return (getElement() == null);
1674: }
1675:
1676: public @Override
1677: void insertUpdate(DocumentEvent evt, Shape a, ViewFactory f) {
1678: // #38993 - until parent is set - do not do anything
1679: if (children == null && getParent() == null) {
1680: return;
1681: }
1682:
1683: if (useCustomReloadChildren()) {
1684: customInsertUpdate(evt, a, f);
1685: } else { // custom insert update
1686: super .insertUpdate(evt, a, f); // default element-based update
1687: }
1688: }
1689:
1690: protected void customInsertUpdate(DocumentEvent evt, Shape a,
1691: ViewFactory f) {
1692: int[] offsetRange = getInsertUpdateRebuildOffsetRange(evt);
1693: if (offsetRange != null) {
1694: offsetRebuild(offsetRange[0], offsetRange[1]);
1695: } else {
1696: forwardUpdate(null, evt, a, f);
1697: }
1698: }
1699:
1700: /**
1701: * Get the offset area in which the views should be rebuilt
1702: * in reaction to insert update in the underlying document.
1703: *
1704: * @param evt document event for the document modification.
1705: * @return two-item integer array containing starting and ending offset
1706: * of the area to be rebuilt or <code>null</code> in case
1707: * no views should be rebuilt.
1708: */
1709: protected int[] getInsertUpdateRebuildOffsetRange(DocumentEvent evt) {
1710: DocumentEvent.ElementChange lineChange = evt.getChange(evt
1711: .getDocument().getDefaultRootElement());
1712: if (lineChange == null) {
1713: return null;
1714: }
1715:
1716: int startOffset = evt.getOffset();
1717: int endOffset = startOffset + evt.getLength();
1718: int[] offsetRange = new int[] { startOffset, endOffset };
1719: Element[] addedLines = lineChange.getChildrenAdded();
1720: ElementUtilities.updateOffsetRange(addedLines, offsetRange);
1721: Element[] removedLines = lineChange.getChildrenRemoved();
1722: ElementUtilities.updateOffsetRange(removedLines, offsetRange);
1723: return offsetRange;
1724: }
1725:
1726: public @Override
1727: void removeUpdate(DocumentEvent evt, Shape a, ViewFactory f) {
1728: // #38993 - until parent is set - do not do anything
1729: if (children == null && getParent() == null) {
1730: return;
1731: }
1732:
1733: if (useCustomReloadChildren()) {
1734: customRemoveUpdate(evt, a, f);
1735: } else {
1736: super .removeUpdate(evt, a, f); // default element-based update
1737: }
1738: }
1739:
1740: protected void customRemoveUpdate(DocumentEvent evt, Shape a,
1741: ViewFactory f) {
1742: int[] offsetRange = getRemoveUpdateRebuildOffsetRange(evt);
1743: if (offsetRange != null) {
1744: offsetRebuild(offsetRange[0], offsetRange[1]);
1745: } else {
1746: forwardUpdate(null, evt, a, f);
1747: }
1748: }
1749:
1750: /**
1751: * Get the offset area in which the views should be rebuilt
1752: * in reaction to insert update in the underlying document.
1753: *
1754: * @param evt document event for the document modification.
1755: * @return two-item integer array containing starting and ending offset
1756: * of the area to be rebuilt or <code>null</code> in case
1757: * no views should be rebuilt.
1758: */
1759: protected int[] getRemoveUpdateRebuildOffsetRange(DocumentEvent evt) {
1760: DocumentEvent.ElementChange lineChange = evt.getChange(evt
1761: .getDocument().getDefaultRootElement());
1762: if (lineChange == null) {
1763: return null;
1764: }
1765:
1766: int startOffset = evt.getOffset();
1767: int endOffset = startOffset;
1768: int[] offsetRange = new int[] { startOffset, endOffset };
1769: Element[] addedLines = lineChange.getChildrenAdded();
1770: ElementUtilities.updateOffsetRange(addedLines, offsetRange);
1771: Element[] removedLines = lineChange.getChildrenRemoved();
1772: ElementUtilities.updateOffsetRange(removedLines, offsetRange);
1773: return offsetRange;
1774: }
1775:
1776: public @Override
1777: void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1778: // #38993 - until parent is set - do not do anything
1779: if (children == null && getParent() == null) {
1780: return;
1781: }
1782:
1783: super .changedUpdate(e, a, f);
1784: }
1785:
1786: /**
1787: * Load the children in the selected range of offsets.
1788: * <br>
1789: * Some implementations may reload all the present children if necessary.
1790: *
1791: * @param index index at which the views should be added/replaced.
1792: * @param removeLength number of removed children views. It is useful
1793: * when rebuilding children for a portion of the view.
1794: * @param startOffset starting offset of the loading. It can be -1
1795: * to indicate the loading from <code>View.getStartOffset()</code>.
1796: * @param endOffset ending offset of the loading. It can be -1
1797: * to indicate the loading till <code>View.getEndOffset()</code>.
1798: */
1799: protected void reloadChildren(int index, int removeLength,
1800: int startOffset, int endOffset) {
1801: if (useCustomReloadChildren()) {
1802: if (startOffset == -1) {
1803: startOffset = getStartOffset();
1804: }
1805: if (endOffset == -1) {
1806: endOffset = getEndOffset();
1807: }
1808:
1809: customReloadChildren(index, removeLength, startOffset,
1810: endOffset);
1811:
1812: } else { // element load of children
1813: Element elem = getElement();
1814: int startIndex;
1815: if (startOffset == -1) {
1816: startIndex = 0;
1817: } else {
1818: if (index == 0) {
1819: if (startOffset != getStartOffset()) {
1820: throw new IllegalArgumentException(
1821: "Invalid startOffset=" + startOffset); // NOI18N
1822: }
1823: } else {
1824: if (startOffset != getView(index - 1)
1825: .getEndOffset()) {
1826: throw new IllegalArgumentException(
1827: "Invalid startOffset=" + startOffset); // NOI18N
1828: }
1829: }
1830: startIndex = index;
1831: }
1832:
1833: int endIndex = (endOffset == -1) ? elem.getElementCount()
1834: : elem.getElementIndex(endOffset - 1) + 1;
1835:
1836: // TODO uncomment assert (startIndex == index);
1837:
1838: elementReloadChildren(index, removeLength, endIndex
1839: - startIndex);
1840: }
1841: }
1842:
1843: /**
1844: * Loads child views by tracking child elements of the element
1845: * this view was created for.
1846: * @param index index at which the views should be added/replaced.
1847: * @param removeLength number of removed children views. It is useful
1848: * when rebuilding children for a portion of the view.
1849: * @param elementIndex index of the first child element for which
1850: * the view should be created
1851: * @param elementCount number of elements for which the views should be created.
1852: */
1853: protected void elementReloadChildren(int index, int removeLength,
1854: int elementCount) {
1855:
1856: Element e = getElement();
1857: View[] added = null;
1858:
1859: ViewFactory f = getViewFactory();
1860: // Null view factory can mean that one of the grand parents is already disconnected
1861: // from the view hierarchy. No added children for null factory.
1862:
1863: if (f != null) {
1864: added = new View[elementCount];
1865: for (int i = 0; i < elementCount; i++) {
1866: added[i] = f.create(e.getElement(index + i));
1867: }
1868:
1869: }
1870:
1871: replace(index, removeLength, added);
1872: }
1873:
1874: /**
1875: * Loads child views in a custom way.
1876: *
1877: * @param index index at which the views should be added/replaced.
1878: * @param removeLength number of removed children views. It is useful
1879: * when rebuilding children for a portion of the view.
1880: * @param startOffset starting offset from which the loading starts.
1881: * @param endOffset ending offset where the loading ends.
1882: */
1883: protected void customReloadChildren(int index, int removeLength,
1884: int startOffset, int endOffset) {
1885:
1886: View[] added = null;
1887: ViewFactory f = getViewFactory();
1888: // Null view factory can mean that one of the grand parents is already disconnected
1889: // from the view hierarchy. No added children for null factory.
1890:
1891: if (f != null) {
1892: Element elem = getElement();
1893:
1894: int elementCount = elem.getElementCount();
1895: int elementIndex = (elem != null) ? elem
1896: .getElementIndex(startOffset) : -1;
1897: if (elementIndex >= elementCount) {
1898: return; // Create no after last element
1899: }
1900: List childViews = new ArrayList();
1901: int viewCount = getViewCount();
1902:
1903: loop: while (startOffset < endOffset) {
1904: // Create custom child
1905: View childView = createCustomView(f, startOffset,
1906: endOffset, elementIndex);
1907: if (childView == null) {
1908: throw new IllegalStateException(
1909: "No view created for area (" // NOI18N
1910: + startOffset + ", "
1911: + endOffset
1912: + ")"); // NOI18N
1913: }
1914:
1915: // Assuming childView.getStartOffset() is at startOffset
1916: childViews.add(childView);
1917:
1918: // Update elementIndex
1919: int childViewEndOffset = childView.getEndOffset();
1920: while (childViewEndOffset > endOffset) {
1921: /* throw new IllegalStateException(
1922: "childViewEndOffset=" + childViewEndOffset // NOI18N
1923: + " > endOffset=" + endOffset // NOI18N
1924: );
1925: */
1926: /* The created child view interferes with a view
1927: * that is still present and which is not planned
1928: * to be removed.
1929: * This can happen e.g. when a fold hierarchy change
1930: * (caused by a document change) is fired
1931: * prior to the document change gets fired
1932: * to the view hierarchy.
1933: * The fix for that situation is to continue to remove
1934: * the present views until the end of the created view will match
1935: * a beginning of a present view.
1936: */
1937: if (index + removeLength >= viewCount) {
1938: // Should not happen but can't remove past the last view
1939: break;
1940: }
1941: endOffset = getView(index + removeLength)
1942: .getEndOffset();
1943: removeLength++;
1944: if (debugRebuild) {
1945: /*DEBUG*/System.err
1946: .println("GapBoxView.customReloadChildren(): Increased removeLength to " // NOI18N
1947: + removeLength
1948: + ", eo="
1949: + endOffset // NOI18N
1950: );
1951: }
1952: }
1953:
1954: Element childElem = elem.getElement(elementIndex);
1955: while (childElem.getEndOffset() <= childViewEndOffset) {
1956: elementIndex++;
1957: if (elementIndex == elementCount) {
1958: // #115034
1959: break loop;
1960: }
1961: childElem = elem.getElement(elementIndex);
1962: }
1963:
1964: startOffset = childViewEndOffset;
1965: }
1966:
1967: added = new View[childViews.size()];
1968: childViews.toArray(added);
1969: }
1970:
1971: replace(index, removeLength, added);
1972: }
1973:
1974: /**
1975: * Create custom child view starting at <code>startOffset</code>.
1976: *
1977: * @param f view factory to be used.
1978: * @param startOffset offset at which the created view must start.
1979: * @param maxEndOffset maximum ending offset to which the created view
1980: * may span.
1981: * @param elementIndex index of the child element that best represents
1982: * the startOffset. The element is child of the element that this view
1983: * is responsible for. If this view is not based by element then this
1984: * parameter will be -1.
1985: */
1986: protected View createCustomView(ViewFactory f, int startOffset,
1987: int maxEndOffset, int elementIndex) {
1988:
1989: /*
1990: // Default implementation delegating to view factory
1991: // is here just to show the possible functionality
1992: // and clarify the variables
1993:
1994: View v;
1995: if (parentElement != null) {
1996: Element elem = parentElement.getElement(elementIndex);
1997: if (elem.getStartOffset() != startOffset) {
1998: throw new IllegalStateException("Not element boundary");
1999: }
2000:
2001: if (elem.getEndOffset() > maxEndOffset) {
2002: throw new IllegalStateException("Beyond maximum ending offset");
2003: }
2004:
2005: v = f.create(elem);
2006:
2007: } else { // no element - need more information
2008: return null;
2009: }
2010: */
2011:
2012: return null;
2013: }
2014:
2015: /**
2016: * Subclasses may override this method and deallocate resources
2017: * bound to presence of children.
2018: * <br>
2019: * It's called by {@link #releaseChildren()} to unallocate
2020: * the resources for children.
2021: *
2022: * <p>
2023: * Once this method finishes all the children will
2024: * be set null as a parent and the reference
2025: * to children will be cleared.
2026: */
2027: protected void unloadChildren() {
2028: }
2029:
2030: /**
2031: * New ViewLayoutState records are created through
2032: * this method to allow subclasses the extend
2033: * the ViewLayoutState records to do/hold more
2034: */
2035: protected ViewLayoutState createChild(View v) {
2036: ViewLayoutState child;
2037: if (v instanceof ViewLayoutState) {
2038: child = (ViewLayoutState) v;
2039: } else { // view does not implement ViewLayoutState
2040: child = createDefaultChild(v);
2041: }
2042: return child;
2043: }
2044:
2045: /**
2046: * Return default implementation of the view layout state wrapper.
2047: */
2048: protected ViewLayoutState createDefaultChild(View v) {
2049: return new SimpleViewLayoutState(v); // only handle preferred spans
2050: }
2051:
2052: protected final boolean isMajorAxisPreferenceChanged() {
2053: return (isStatusBitsNonZero(MAJOR_AXIS_PREFERENCE_CHANGED_BIT));
2054: }
2055:
2056: protected void markMajorAxisPreferenceChanged() {
2057: setStatusBits(MAJOR_AXIS_PREFERENCE_CHANGED_BIT);
2058: }
2059:
2060: protected final boolean isMinorAxisPreferenceChanged() {
2061: return (isStatusBitsNonZero(MINOR_AXIS_PREFERENCE_CHANGED_BIT));
2062: }
2063:
2064: protected void markMinorAxisPreferenceChanged() {
2065: setStatusBits(MINOR_AXIS_PREFERENCE_CHANGED_BIT);
2066: }
2067:
2068: protected final void resetAxesPreferenceChanged() {
2069: clearStatusBits(MAJOR_AXIS_PREFERENCE_CHANGED_BIT
2070: | MINOR_AXIS_PREFERENCE_CHANGED_BIT);
2071: }
2072:
2073: /**
2074: * Get the span along an axis that is taken up by the view insets.
2075: *
2076: * @param axis the axis to determine the total insets along,
2077: * either X_AXIS or Y_AXIS.
2078: * @return span along the given axis taken up by view insets.
2079: */
2080: protected final float getInsetSpan(int axis) {
2081: // assert ViewUtilities.isAxisValid(axis);
2082:
2083: ViewInsets insets = getInsets();
2084: return (insets != null) ? ((axis == X_AXIS) ? insets
2085: .getLeftRight() : insets.getTopBottom()) : 0;
2086: }
2087:
2088: /**
2089: * Get the span along major axis that is taken up by the view insets.
2090: *
2091: * @return span along major axis taken up by view insets.
2092: */
2093: protected final float getMajorAxisInsetSpan() {
2094: ViewInsets insets = getInsets();
2095: return (insets != null) ? (isXMajorAxis() ? insets
2096: .getLeftRight() : insets.getTopBottom()) : 0;
2097: }
2098:
2099: /**
2100: * Get the span along minor axis that is taken up by the view insets.
2101: *
2102: * @return span along minor axis taken up by view insets.
2103: */
2104: protected final float getMinorAxisInsetSpan() {
2105: ViewInsets insets = getInsets();
2106: return (insets != null) ? (isXMajorAxis() ? insets
2107: .getTopBottom() : insets.getLeftRight()) : 0;
2108: }
2109:
2110: protected final int getStatusBits(int bits) {
2111: return (statusBits & bits);
2112: }
2113:
2114: protected final boolean isStatusBitsNonZero(int bits) {
2115: return (getStatusBits(bits) != 0);
2116: }
2117:
2118: protected final void setStatusBits(int bits) {
2119: statusBits |= bits;
2120: }
2121:
2122: protected final void clearStatusBits(int bits) {
2123: statusBits &= ~bits;
2124: }
2125:
2126: /**
2127: * Reallocate the view to the new size given by the passed shape.
2128: *
2129: * @param a shape to which to reallocate the view.
2130: * @return rectangle bounding the shape. The returned rectangle
2131: * can be mutated.
2132: */
2133: protected Rectangle reallocate(Shape a) {
2134: Rectangle alloc = a.getBounds(); // makes a fresh rectangle instance
2135:
2136: setSize(alloc.width, alloc.height); // set new size
2137:
2138: return alloc;
2139: }
2140:
2141: // Implements FlyView.Parent
2142: public int getStartOffset(int childViewIndex) {
2143: return getChildren().getChildStartOffset(childViewIndex);
2144: }
2145:
2146: // Implements FlyView.Parent
2147: public int getEndOffset(int childViewIndex) {
2148: return getChildren().getChildEndOffset(childViewIndex);
2149: }
2150:
2151: public String childToString(int childIndex) {
2152: StringBuffer sb = new StringBuffer();
2153: appendChildToStringBuffer(sb, childIndex, 0);
2154: return sb.toString();
2155: }
2156:
2157: public void appendChildToStringBuffer(StringBuffer sb,
2158: int childIndex, int indent) {
2159: ViewLayoutState child = getChild(childIndex);
2160: View childView = child.getView();
2161: Document doc = getDocument();
2162: boolean isFly = child.isFlyweight();
2163: boolean isEstimated = (childView instanceof EstimatedSpanView)
2164: && ((EstimatedSpanView) childView).isEstimatedSpan();
2165: boolean layoutValid = child.isLayoutValid();
2166: double offset = children.getMajorAxisOffset(childIndex);
2167: boolean indexesDiffer = !isFly
2168: && (getChildIndexNoCheck(child) != childIndex);
2169: boolean showRaw = false; // change for debugging purposes
2170:
2171: sb.append((isFly ? 'F' : 'R')); // flyweight / regular NOI18N
2172: sb.append(':');
2173: if (indexesDiffer) {
2174: sb.append(" WRONG-INDEX=" + getChildIndexNoCheck(child)); // NOI18N
2175: }
2176: if (showRaw) {
2177: sb.append("rI=" + child.getViewRawIndex()); // NOI18N
2178: }
2179: sb.append('<');
2180: appendOffsetInfo(sb, doc, childView.getStartOffset());
2181: sb.append(',');
2182: appendOffsetInfo(sb, doc, childView.getEndOffset());
2183: sb.append('>');
2184:
2185: sb.append(", major=").append(
2186: child.getLayoutMajorAxisPreferredSpan()); // NOI18N
2187: sb.append("(off=").append(offset); // NOI18N
2188:
2189: if (showRaw) {
2190: sb.append('(').append(child.getLayoutMajorAxisRawOffset())
2191: .append(')'); // NOI18N
2192: }
2193:
2194: sb.append("), minor[pref=").append(
2195: child.getLayoutMinorAxisPreferredSpan()); // NOI18N
2196: sb.append(", min=").append(
2197: child.getLayoutMinorAxisMinimumSpan()); // NOI18N
2198: sb.append(", max=").append(
2199: child.getLayoutMinorAxisMaximumSpan()); // NOI18N
2200: sb.append("] "); // NOI18N
2201: sb.append(isEstimated ? "E" : ""); // NOI18N
2202: sb.append(layoutValid ? "" : "I"); // NOI18N
2203:
2204: // Possibly add view description if GapBoxView
2205: if (childView instanceof GapBoxView) {
2206: sb.append("\n"); // NOI18N
2207: appendSpaces(sb, indent + 4);
2208: sb.append("VIEW: "); // NOI18N
2209: sb.append(childView.toString());
2210: sb.append(((GapBoxView) childView)
2211: .childrenToString(indent + 4));
2212: }
2213: }
2214:
2215: private static void appendOffsetInfo(StringBuffer sb, Document doc,
2216: int offset) {
2217: sb.append(offset);
2218: sb.append('[');
2219: // TODO - removed dependency on o.n.e.Utilities
2220: sb.append(org.netbeans.editor.Utilities.debugPosition(
2221: (org.netbeans.editor.BaseDocument) doc, offset));
2222: sb.append(']');
2223: }
2224:
2225: private static void appendSpaces(StringBuffer sb, int spaceCount) {
2226: while (--spaceCount >= 0) {
2227: sb.append(' ');
2228: }
2229: }
2230:
2231: public String childrenToString() {
2232: return childrenToString(0);
2233: }
2234:
2235: public String childrenToString(int indent) {
2236: StringBuffer sb = new StringBuffer();
2237:
2238: int viewCount = getViewCount();
2239: int totalDigitCount = Integer.toString(viewCount).length();
2240: for (int i = 0; i < viewCount; i++) {
2241: sb.append('\n');
2242: String iToString = Integer.toString(i);
2243: appendSpaces(sb, indent
2244: + (totalDigitCount - iToString.length()));
2245:
2246: sb.append('[');
2247: sb.append(iToString);
2248: sb.append("]: "); // NOI18N
2249: appendChildToStringBuffer(sb, i, indent);
2250: }
2251:
2252: return sb.toString();
2253: }
2254:
2255: public @Override
2256: String toString() {
2257: // Must not return anything about children because
2258: // that could cause them to be initialized (e.g. by getViewCount())
2259: return "lastMajorAxisPreferredSpan="
2260: + lastMajorAxisPreferredSpan // NOI18N
2261: + ", lastMinorAxisPreferredSpan="
2262: + lastMinorAxisPreferredSpan // NOI18N
2263: + ", minorAxisAssignedSpan="
2264: + getMinorAxisAssignedSpan(); // NOI18N
2265: }
2266:
2267: }
|