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.modules.form.layoutdesign;
0043:
0044: import java.util.ArrayList;
0045: import java.util.Collections;
0046: import java.util.Iterator;
0047: import java.util.LinkedList;
0048: import java.util.List;
0049:
0050: /**
0051: * @author Tomas Pavek
0052: */
0053:
0054: public final class LayoutInterval implements LayoutConstants {
0055: static final int ATTRIBUTE_FILL = 1;
0056: static final int ATTRIBUTE_FORMER_FILL = 2;
0057: static final int ATTR_CLOSED_GROUP = 32;
0058:
0059: // attributes denoting intervals with different size behavior in design time
0060: static final int ATTR_DESIGN_CONTAINER_GAP = 4;
0061: static final int ATTR_DESIGN_RESIZING = 8;
0062: static final int ATTR_DESIGN_SUPPRESSED_RESIZING = 16;
0063:
0064: // attributes used during aligning
0065: static final int ATTR_ALIGN_PRE = 64;
0066: static final int ATTR_ALIGN_POST = 128;
0067:
0068: static final int DESIGN_ATTRS = ATTR_DESIGN_CONTAINER_GAP
0069: | ATTR_DESIGN_RESIZING | ATTR_DESIGN_SUPPRESSED_RESIZING
0070: | ATTR_ALIGN_PRE | ATTR_ALIGN_POST;
0071:
0072: static final int ATTR_PERSISTENT_MASK = ATTRIBUTE_FILL
0073: | ATTRIBUTE_FORMER_FILL | ATTR_CLOSED_GROUP;
0074:
0075: // type of the interval - SINGLE, SEQUENTIAL, PARALLEL
0076: private int type;
0077:
0078: // additional attributes set on the interval as bit flags
0079: private int attributes;
0080:
0081: // alignment of the interval (if in a parallel group)
0082: private int alignment = DEFAULT;
0083:
0084: // parent interval (group )
0085: private LayoutInterval parentInterval;
0086:
0087: // internall alignment of a group (if this is a parallel group)
0088: private int groupAlignment = LEADING;
0089:
0090: // contained sub-intervals (if this is a group)
0091: private List<LayoutInterval> subIntervals;
0092:
0093: // associated LayoutComponent (if any)
0094: private LayoutComponent layoutComponent;
0095:
0096: // type of padding (default gap; if this is a preferred gap)
0097: private PaddingType paddingType;
0098: private String[] paddingDefComps; // 2 components, needed for INDENT gap
0099:
0100: // minimum, preferred, and maximum size definitions
0101: private int minSize;
0102: private int prefSize;
0103: private int maxSize;
0104:
0105: // current position and size of the interval in the visual representation
0106: private LayoutRegion currentSpace;
0107:
0108: // -----
0109: // setup methods - each setter should be called max. once after creation,
0110: // other changes should be done via LayoutModel to be fired and recorded
0111: // for undo/redo
0112:
0113: LayoutInterval(int type) {
0114: this .type = type;
0115: minSize = NOT_EXPLICITLY_DEFINED;
0116: prefSize = NOT_EXPLICITLY_DEFINED;
0117: if (type == SEQUENTIAL || type == PARALLEL) {
0118: subIntervals = new ArrayList<LayoutInterval>();
0119: maxSize = NOT_EXPLICITLY_DEFINED; // group can resize by default
0120: } else {
0121: assert type == SINGLE;
0122: maxSize = USE_PREFERRED_SIZE;
0123: }
0124: }
0125:
0126: void setAlignment(int alignment) {
0127: this .alignment = alignment;
0128: }
0129:
0130: void setGroupAlignment(int alignment) {
0131: assert alignment != DEFAULT && type == PARALLEL;
0132: groupAlignment = alignment;
0133: }
0134:
0135: void setComponent(LayoutComponent comp) {
0136: this .layoutComponent = comp;
0137: }
0138:
0139: void setMinimumSize(int size) {
0140: // for groups we expect only two states - shrinking suppressed/allowed
0141: assert isSingle()
0142: || (size == USE_PREFERRED_SIZE || size == NOT_EXPLICITLY_DEFINED);
0143: minSize = size;
0144: }
0145:
0146: void setPreferredSize(int size) {
0147: assert (size != USE_PREFERRED_SIZE && isSingle())
0148: || (size == NOT_EXPLICITLY_DEFINED); // groups should not have explicit size
0149: prefSize = size;
0150: }
0151:
0152: void setMaximumSize(int size) {
0153: // for single interval the max size must be defined
0154: // for groups only two states - growing suppressed or allowed
0155: assert (isSingle() && size != NOT_EXPLICITLY_DEFINED)
0156: || (isGroup() && (size == USE_PREFERRED_SIZE || size == NOT_EXPLICITLY_DEFINED));
0157: maxSize = size;
0158: }
0159:
0160: void setSize(int size) {
0161: setMinimumSize(size);
0162: setPreferredSize(size);
0163: setMaximumSize(size);
0164: }
0165:
0166: void setSizes(int min, int pref, int max) {
0167: setMinimumSize(min);
0168: setPreferredSize(pref);
0169: setMaximumSize(max);
0170: }
0171:
0172: int getMinimumSize() {
0173: return minSize;
0174: }
0175:
0176: int getPreferredSize() {
0177: return prefSize;
0178: }
0179:
0180: int getMaximumSize() {
0181: return maxSize;
0182: }
0183:
0184: void setPaddingType(PaddingType type) {
0185: paddingType = type;
0186: }
0187:
0188: String[] getPaddingDefComponents() {
0189: return paddingDefComps;
0190: }
0191:
0192: void setPaddingDefComponents(String compId1, String compId2) {
0193: if (compId1 == null) {
0194: paddingDefComps = null;
0195: } else {
0196: paddingDefComps = new String[] { compId1, compId2 };
0197: }
0198: }
0199:
0200: // ---------
0201: // public methods
0202:
0203: /**
0204: * Returns the type of the structure of the interval. It can be a single
0205: * interval or a group with sub-intervals arranged either sequentially
0206: * or parallelly.
0207: * @return type of the interval: SINGLE, SEQUENTIAL, or PARALLEL
0208: */
0209: public int getType() {
0210: return type;
0211: }
0212:
0213: /**
0214: * Returns alignment of the interval within a parallel group. If the
0215: * interval is not part of a parallel group, the alignment is meaningless.
0216: * @return alignment of the interval within a parallel group (LEADING,
0217: * TRAILING, CENTER, or BASELINE); DEFAULT if in a sequential group
0218: */
0219: public int getAlignment() {
0220: return alignment == DEFAULT && parentInterval != null
0221: && parentInterval.isParallel() ? parentInterval
0222: .getGroupAlignment() : alignment;
0223: }
0224:
0225: /**
0226: * Returns the common alignment of sub-intervals within a group (makes
0227: * sense only for a parallel group).
0228: * @return alignment of the group (LEADING, TRAILING, CENTER, or BASELINE)
0229: */
0230: public int getGroupAlignment() {
0231: return groupAlignment;
0232: }
0233:
0234: /**
0235: * Returns the minimum size of the interval. Instead of a specific size
0236: * it may return also one of the constants NOT_EXPLICITLY_DEFINED or
0237: * USE_PREFERRED_SIZE.
0238: * @param designTime if true, size for design time layout is returned
0239: * (design time behavior may be different in terms of resizing)
0240: * @return minimum interval size, or one of the constants:
0241: * NOT_EXPLICITLY_DEFINED or USE_PREFERRED_SIZE
0242: */
0243: public int getMinimumSize(boolean designTime) {
0244: if (!designTime) {
0245: return minSize;
0246: }
0247: if (hasAttribute(ATTR_DESIGN_SUPPRESSED_RESIZING)) {
0248: assert !hasAttribute(ATTR_DESIGN_RESIZING);
0249: return USE_PREFERRED_SIZE;
0250: }
0251: if (hasAttribute(ATTR_DESIGN_RESIZING)) {
0252: return isEmptySpace()
0253: && (getPreferredSize(designTime) != 0) ? NOT_EXPLICITLY_DEFINED
0254: : 0;
0255: }
0256: return minSize;
0257: }
0258:
0259: /**
0260: * Returns the preferred size of the interval. If no specific size was set,
0261: * it returns NOT_EXPLICITLY_DEFINED constant.
0262: * @param designTime if true, size for design time layout is returned
0263: * (design time behavior may be different in terms of resizing)
0264: * @return preferred size of the interval, or NOT_EXPLICITLY_DEFINED constant
0265: */
0266: public int getPreferredSize(boolean designTime) {
0267: return prefSize;
0268: }
0269:
0270: /**
0271: * Returns the maximum size of the interval. Instead of a specific size
0272: * it may return also one of the constants NOT_EXPLICITLY_DEFINED or
0273: * USE_PREFERRED_SIZE.
0274: * @param designTime if true, size for design time layout is returned
0275: * (design time behavior may be different in terms of resizing)
0276: * @return maximum interval size, or one of the constants:
0277: * NOT_EXPLICITLY_DEFINED or USE_PREFERRED_SIZE
0278: */
0279: public int getMaximumSize(boolean designTime) {
0280: if (!designTime) {
0281: return maxSize;
0282: }
0283: if (hasAttribute(ATTR_DESIGN_SUPPRESSED_RESIZING)) {
0284: assert !hasAttribute(ATTR_DESIGN_RESIZING);
0285: return USE_PREFERRED_SIZE;
0286: }
0287: if (hasAttribute(ATTR_DESIGN_RESIZING)) {
0288: return Short.MAX_VALUE;
0289: }
0290: return maxSize;
0291: }
0292:
0293: /**
0294: * Returns number of sub-intervals of this interval.
0295: * @return number of sub-intervals of this interval, 0 if it is not a group
0296: */
0297: public int getSubIntervalCount() {
0298: return subIntervals != null ? subIntervals.size() : 0;
0299: }
0300:
0301: /**
0302: * Returns an iterator of sub-intervals.
0303: * @return iterator of sub-intervals, empty if there are no sub-intervals
0304: */
0305: public Iterator<LayoutInterval> getSubIntervals() {
0306: return subIntervals != null ? subIntervals.iterator()
0307: : Collections.EMPTY_LIST.iterator();
0308: }
0309:
0310: /**
0311: * If this interval represents a component's width or height, this methods
0312: * returns the component.
0313: * @return LayoutComponent instance representing the associated component.
0314: * Null if this interval does not represent a component.
0315: */
0316: public LayoutComponent getComponent() {
0317: return layoutComponent;
0318: }
0319:
0320: // helper methods (redundant - based on derived information)
0321:
0322: public boolean isParallel() {
0323: return type == PARALLEL;
0324: }
0325:
0326: public boolean isSequential() {
0327: return type == SEQUENTIAL;
0328: }
0329:
0330: /**
0331: * Returns whether this interval defines a lyout component.
0332: * @return true if this interval represents a layout component,
0333: * false otherwise
0334: */
0335: public boolean isComponent() {
0336: return layoutComponent != null;
0337: }
0338:
0339: /**
0340: * Returns whether this interval defines an "empty" space (gap) in the
0341: * layout, not including nor being able to include any component.
0342: * @return true if this is a single interval not representing a component,
0343: * false otherwise
0344: */
0345: public boolean isEmptySpace() {
0346: return type == SINGLE && layoutComponent == null;
0347: }
0348:
0349: public boolean isDefaultPadding(boolean designTime) {
0350: return isEmptySpace()
0351: && (getMinimumSize(designTime) == NOT_EXPLICITLY_DEFINED || getPreferredSize(designTime) == NOT_EXPLICITLY_DEFINED);
0352: }
0353:
0354: public PaddingType getPaddingType() {
0355: return paddingType;
0356: }
0357:
0358: public boolean isSingle() {
0359: return type == SINGLE;
0360: }
0361:
0362: /**
0363: * Returns whether this interval represents a group structure that can have
0364: * have sub-intervals.
0365: * @return whether this interval is a group, either sequential or parallel
0366: */
0367: public boolean isGroup() {
0368: return type == SEQUENTIAL || type == PARALLEL;
0369: }
0370:
0371: /**
0372: * @return whether the interval is allowed to grow (according to its
0373: * definition); if allowed, the real growing possibility may still
0374: * depend on the associated component
0375: */
0376: // public boolean isAllowedToGrow() {
0377: // return maxSize != USE_PREFERRED_SIZE
0378: // && (prefSize == NOT_EXPLICITLY_DEFINED
0379: // || maxSize == NOT_EXPLICITLY_DEFINED
0380: // || maxSize > prefSize);
0381: // }
0382: /**
0383: * @return whether the interval is allowed to shrink (according to its
0384: * definition); if allowed, the real growing possibility may still
0385: * depend on the associated component
0386: */
0387: // public boolean isAllowedToShrink() {
0388: // return minSize != USE_PREFERRED_SIZE
0389: // && (prefSize == NOT_EXPLICITLY_DEFINED
0390: // || minSize == NOT_EXPLICITLY_DEFINED
0391: // || minSize < prefSize);
0392: // }
0393: // end of public methods
0394: // -----
0395: boolean hasAttribute(int attr) {
0396: return (attributes & attr) == attr;
0397: }
0398:
0399: void setAttribute(int attr) {
0400: attributes |= attr;
0401: }
0402:
0403: void unsetAttribute(int attr) {
0404: attributes &= ~attr;
0405: }
0406:
0407: /**
0408: * Sets attributes of the layout interval. Should be used by persistence manager only!
0409: *
0410: * @param attrs attributes.
0411: */
0412: void setAttributes(int attrs) {
0413: attributes = attrs;
0414: }
0415:
0416: /**
0417: * Returns attributes of this layout interval. You should use
0418: * <code>hasAttribute()</code> when you are interested in one
0419: * particular attribute.
0420: */
0421: int getAttributes() {
0422: return attributes;
0423: }
0424:
0425: /**
0426: * @return the value of the alignment field of the interval - unlike
0427: * getAlignment() it does not ask the parent if not set (DEFAULT)
0428: */
0429: int getRawAlignment() {
0430: return alignment;
0431: }
0432:
0433: // -----
0434:
0435: public LayoutInterval getParent() {
0436: return parentInterval;
0437: }
0438:
0439: int add(LayoutInterval interval, int index) {
0440: if (index < 0) {
0441: index = subIntervals.size();
0442: }
0443: subIntervals.add(index, interval);
0444: interval.parentInterval = this ;
0445: return index;
0446: }
0447:
0448: int remove(LayoutInterval interval) {
0449: int index = subIntervals.indexOf(interval);
0450: if (index >= 0) {
0451: subIntervals.remove(index);
0452: interval.parentInterval = null;
0453: }
0454: return index;
0455: }
0456:
0457: LayoutInterval remove(int index) {
0458: LayoutInterval interval = subIntervals.get(index);
0459: subIntervals.remove(index);
0460: interval.parentInterval = null;
0461: return interval;
0462: }
0463:
0464: LayoutInterval getSubInterval(int index) {
0465: return subIntervals != null ? subIntervals.get(index) : null;
0466: }
0467:
0468: int indexOf(LayoutInterval interval) {
0469: return subIntervals != null ? subIntervals.indexOf(interval)
0470: : -1;
0471: }
0472:
0473: boolean isParentOf(LayoutInterval interval) {
0474: if (isGroup()) {
0475: do {
0476: interval = interval.getParent();
0477: if (interval == this )
0478: return true;
0479: } while (interval != null);
0480: }
0481: return false;
0482: }
0483:
0484: // -----
0485: // current state of the layout - current position and size of layout
0486: // interval kept to be available quickly for the layout designer
0487:
0488: LayoutRegion getCurrentSpace() {
0489: assert !isEmptySpace(); // [temporary - nobody should be interested in gap positions]
0490: if (currentSpace == null) {
0491: currentSpace = new LayoutRegion();
0492: }
0493: return currentSpace;
0494: }
0495:
0496: void setCurrentSpace(LayoutRegion space) {
0497: currentSpace = space;
0498: }
0499:
0500: // -----
0501: // static helper methods
0502:
0503: /**
0504: * @return the closest parent interval that matches given type
0505: */
0506: static LayoutInterval getFirstParent(LayoutInterval interval,
0507: int type) {
0508: LayoutInterval parent = interval.getParent();
0509: while (parent != null && parent.getType() != type) {
0510: parent = parent.getParent();
0511: }
0512: return parent;
0513: }
0514:
0515: static LayoutInterval getRoot(LayoutInterval interval) {
0516: while (interval.getParent() != null) {
0517: interval = interval.getParent();
0518: }
0519: // assert interval.isParallel();
0520: return interval;
0521: }
0522:
0523: /**
0524: * Finds common parent of the given intervals.
0525: *
0526: * @param intervals intervals whose parent should be found.
0527: * @return common parent of the given intervals.
0528: */
0529: static LayoutInterval getCommonParent(LayoutInterval[] intervals) {
0530: assert (intervals != null) && (intervals.length > 0);
0531: LayoutInterval parent = intervals[0].getParent();
0532: for (int i = 1; i < intervals.length; i++) {
0533: parent = getCommonParent(parent, intervals[i]);
0534: }
0535: return parent;
0536: }
0537:
0538: /**
0539: * Finds common parent of two given intervals. In case one interval is
0540: * parent of the other then this interval is returned directly, not its
0541: * parent.
0542: *
0543: * @param interval1 interval whose parent should be found.
0544: * @param interval2 interval whose parent should be found.
0545: * @return common parent of two given intervals.
0546: */
0547: static LayoutInterval getCommonParent(LayoutInterval interval1,
0548: LayoutInterval interval2) {
0549: // Find all parents of given intervals
0550: Iterator parents1 = parentsOfInterval(interval1).iterator();
0551: Iterator parents2 = parentsOfInterval(interval2).iterator();
0552: LayoutInterval parent1 = (LayoutInterval) parents1.next();
0553: LayoutInterval parent2 = (LayoutInterval) parents2.next();
0554: assert (parent1 == parent2);
0555:
0556: // Candidate for the common parent
0557: LayoutInterval parent = null;
0558: while (parent1 == parent2) {
0559: parent = parent1;
0560: if (parents1.hasNext()) {
0561: parent1 = (LayoutInterval) parents1.next();
0562: } else {
0563: break;
0564: }
0565: if (parents2.hasNext()) {
0566: parent2 = (LayoutInterval) parents2.next();
0567: } else {
0568: break;
0569: }
0570: }
0571: return parent;
0572: }
0573:
0574: /**
0575: * Calculates all parents of the given interval.
0576: *
0577: * @param interval interval whose parents should be found.
0578: * @return <code>List</code> of <code>LayoutInterval</code> objects that
0579: * are parents of the given interval. The root is the first in the list;
0580: * the interval itelf is also included - at the end.
0581: */
0582: private static List<LayoutInterval> parentsOfInterval(
0583: LayoutInterval interval) {
0584: List<LayoutInterval> parents = new LinkedList<LayoutInterval>();
0585: while (interval != null) {
0586: parents.add(0, interval);
0587: interval = interval.getParent();
0588: }
0589: return parents;
0590: }
0591:
0592: static int getCount(LayoutInterval group, int alignment,
0593: boolean nonEmpty) {
0594: int n = 0;
0595: Iterator it = group.getSubIntervals();
0596: while (it.hasNext()) {
0597: LayoutInterval li = (LayoutInterval) it.next();
0598: if ((group.isSequential()
0599: || alignment == LayoutRegion.ALL_POINTS
0600: || li.getAlignment() == alignment || wantResize(li))
0601: && (!nonEmpty || !li.isEmptySpace())) { // count in
0602: n++;
0603: }
0604: }
0605: return n;
0606: }
0607:
0608: static LayoutInterval getDirectNeighbor(LayoutInterval interval,
0609: int alignment, boolean nonEmpty) {
0610: LayoutInterval parent = interval.getParent();
0611: if (parent == null || parent.isParallel())
0612: return null;
0613:
0614: LayoutInterval neighbor = null;
0615: int d = (alignment == LEADING ? -1 : 1);
0616: int n = parent.getSubIntervalCount();
0617: int index = parent.indexOf(interval) + d;
0618: while (index >= 0 && index < n && neighbor == null) {
0619: LayoutInterval li = parent.getSubInterval(index);
0620: index += d;
0621: if (!nonEmpty || !li.isEmptySpace()) {
0622: neighbor = li;
0623: }
0624: }
0625: return neighbor;
0626: }
0627:
0628: /**
0629: * @param alignment direction in which the neighbor is looked for (LEADING or TRAILING)
0630: * @param nonEmpty true if empty spaces (gaps) should be skipped
0631: * @param outOfParent true if can go up (out of the first sequential parent)
0632: * for an indirect neighbor
0633: * @param aligned true if the indirect neighbor must be in contact with the
0634: * given interval
0635: */
0636: static LayoutInterval getNeighbor(LayoutInterval interval,
0637: int alignment, boolean nonEmpty, boolean outOfParent,
0638: boolean aligned) {
0639: assert alignment == LEADING || alignment == TRAILING;
0640:
0641: LayoutInterval neighbor = null;
0642: LayoutInterval parent = interval;
0643: int d = (alignment == LEADING ? -1 : 1);
0644:
0645: do {
0646: do { // find sequential parent first
0647: interval = parent;
0648: parent = interval.getParent();
0649: if (aligned && parent != null && parent.isParallel()
0650: && !isAlignedAtBorder(interval, alignment)) { // interval not aligned in parent
0651: parent = null;
0652: }
0653: } while (parent != null && parent.isParallel());
0654:
0655: if (parent != null) { // look for the neighbor in the sequence
0656: neighbor = getDirectNeighbor(interval, alignment,
0657: nonEmpty);
0658: }
0659: } while (neighbor == null && parent != null && outOfParent);
0660:
0661: return neighbor;
0662: }
0663:
0664: static LayoutInterval getNeighbor(LayoutInterval interval,
0665: int parentType, int alignment) {
0666: assert alignment == LEADING || alignment == TRAILING;
0667: LayoutInterval sibling = null;
0668: LayoutInterval parent = interval;
0669: do {
0670: do {
0671: interval = parent;
0672: parent = parent.getParent();
0673: } while ((parent != null)
0674: && (parent.getType() != parentType));
0675: if (parent != null) {
0676: List subs = parent.subIntervals;
0677: int index = subs.indexOf(interval);
0678: if ((alignment == LEADING) && (index > 0)) {
0679: sibling = (LayoutInterval) subs.get(index - 1);
0680: } else if ((alignment == TRAILING)
0681: && (index + 1 < subs.size())) {
0682: sibling = (LayoutInterval) subs.get(index + 1);
0683: }
0684: }
0685: } while ((parent != null) && (sibling == null));
0686: return sibling;
0687: }
0688:
0689: static boolean startsWithEmptySpace(LayoutInterval interval,
0690: int alignment) {
0691: assert alignment == LEADING || alignment == TRAILING;
0692: if (interval.isSingle()) {
0693: return interval.isEmptySpace();
0694: }
0695: if (interval.isSequential()) {
0696: int index = alignment == LEADING ? 0 : interval
0697: .getSubIntervalCount() - 1;
0698: return startsWithEmptySpace(interval.getSubInterval(index),
0699: alignment);
0700: } else { // parallel group
0701: for (Iterator it = interval.getSubIntervals(); it.hasNext();) {
0702: LayoutInterval li = (LayoutInterval) it.next();
0703: if (startsWithEmptySpace(li, alignment)) {
0704: return true;
0705: }
0706: }
0707: }
0708: return false;
0709: }
0710:
0711: /**
0712: * Checks whether an interval is permanently aligned to its parent at given
0713: * border. (The asked relation is hard, always maintained by the layout.)
0714: * For a sequential parent the interval is aligned if it is first or last.
0715: * For parallel parent the interval must have the given alignment in the
0716: * group, or be resizing.
0717: */
0718: static boolean isAlignedAtBorder(LayoutInterval interval,
0719: int alignment) {
0720: if (alignment != LEADING && alignment != TRAILING) {
0721: return false;
0722: }
0723: LayoutInterval parent = interval.getParent();
0724: if (parent == null) {
0725: return false;
0726: }
0727: if (parent.isSequential()) {
0728: int index = alignment == LEADING ? 0 : parent
0729: .getSubIntervalCount() - 1;
0730: return interval == parent.getSubInterval(index);
0731: } else { // parallel parent
0732: return interval.getAlignment() == alignment
0733: || wantResize(interval);
0734: }
0735: }
0736:
0737: /**
0738: * Checks whether an interval is permanently aligned with a given parent
0739: * interval - need not be the direct parent. This is a multi-level version
0740: * of the other (simple) isAlignedAtBorder method.
0741: */
0742: static boolean isAlignedAtBorder(LayoutInterval interval,
0743: LayoutInterval parent, int alignment) {
0744: do {
0745: if (!isAlignedAtBorder(interval, alignment)) {
0746: return false;
0747: }
0748: interval = interval.getParent();
0749: } while (interval != parent);
0750: return true;
0751: }
0752:
0753: /**
0754: * Checks whether given interval is placed at border side of its parent.
0755: * Cares about the current visual situation only - the place may change if
0756: * the alignment is not backed by the layout structure.
0757: * Note this method requires the current visual state (positions) of the
0758: * relevant intervals to be up-to-date.
0759: */
0760: static boolean isPlacedAtBorder(LayoutInterval interval,
0761: int dimension, int alignment) {
0762: if (alignment != LEADING && alignment != TRAILING) {
0763: return false;
0764: }
0765: LayoutInterval parent = interval.getParent();
0766: if (parent == null) {
0767: return false;
0768: }
0769: if (interval.isEmptySpace()) {
0770: if (parent.isSequential()) {
0771: int index = alignment == LEADING ? 0 : parent
0772: .getSubIntervalCount() - 1;
0773: return interval == parent.getSubInterval(index);
0774: } else { // gap in parallel parent
0775: return true;
0776: }
0777: } else { // check visual position
0778: return LayoutRegion.distance(interval.getCurrentSpace(),
0779: parent.getCurrentSpace(), dimension, alignment,
0780: alignment) == 0;
0781: }
0782: }
0783:
0784: /**
0785: * Checks whether an interval is placed at border side of given parent
0786: * (need not be the direct parent). This is a multi-level version of the
0787: * simpler isPlacededAtBorder method.
0788: * Note this method requires the current visual state (positions) of the
0789: * relevant intervals to be up-to-date.
0790: */
0791: static boolean isPlacedAtBorder(LayoutInterval interval,
0792: LayoutInterval parent, int dimension, int alignment) {
0793: if (alignment != LEADING && alignment != TRAILING) {
0794: return false;
0795: }
0796: if (interval.isEmptySpace()) {
0797: LayoutInterval p = interval.getParent();
0798: if (p.isSequential()) {
0799: int index = alignment == LEADING ? 0 : p
0800: .getSubIntervalCount() - 1;
0801: if (interval != p.getSubInterval(index)) {
0802: return false;
0803: }
0804: }
0805: if (p == parent) {
0806: return true;
0807: }
0808: interval = p;
0809: }
0810: return LayoutRegion.distance(interval.getCurrentSpace(), parent
0811: .getCurrentSpace(), dimension, alignment, alignment) == 0
0812: && parent.isParentOf(interval);
0813: }
0814:
0815: // [to be replaced by separate methods like isAlignedAtBorder, isPlacedBorder, isLastInterval]
0816: static boolean isBorderInterval(LayoutInterval interval,
0817: int alignment, boolean attached) {
0818: LayoutInterval parent = interval.getParent();
0819: if (parent != null
0820: && (alignment == LEADING || alignment == TRAILING)) {
0821: if (parent.isSequential()) {
0822: int index = alignment == LEADING ? 0 : parent
0823: .getSubIntervalCount() - 1;
0824: while (index >= 0
0825: && index < parent.getSubIntervalCount()) {
0826: LayoutInterval li = parent.getSubInterval(index);
0827: if (li == interval) {
0828: return true;
0829: } else if (attached || !li.isEmptySpace()) {
0830: return false;
0831: }
0832: index += alignment == LEADING ? 1 : -1;
0833: }
0834: } else {
0835: return !attached
0836: || interval.getAlignment() == alignment
0837: || wantResize(interval);
0838: }
0839: // if (interval.getAlignment() == alignment) {
0840: // return interval.getCurrentSpace().positions[dimension][alignment]
0841: // == parent.getCurrentSpace().positions[dimension][alignment];
0842: }
0843: return false;
0844: }
0845:
0846: static boolean isClosedGroup(LayoutInterval group, int alignment) {
0847: assert group.isParallel();
0848:
0849: if (group.hasAttribute(ATTR_CLOSED_GROUP)
0850: || group.getGroupAlignment() == CENTER
0851: || group.getGroupAlignment() == BASELINE) {
0852: return true;
0853: }
0854:
0855: Iterator it = group.getSubIntervals();
0856: while (it.hasNext()) {
0857: LayoutInterval li = (LayoutInterval) it.next();
0858: if (li.getAlignment() == alignment || wantResize(li)) {
0859: return true;
0860: }
0861: }
0862: return false;
0863: }
0864:
0865: static boolean isExplicitlyClosedGroup(LayoutInterval group) {
0866: return group.hasAttribute(ATTR_CLOSED_GROUP);
0867: }
0868:
0869: static boolean isDefaultPadding(LayoutInterval interval) {
0870: return interval.isEmptySpace()
0871: && (interval.getMinimumSize() == NOT_EXPLICITLY_DEFINED || interval
0872: .getPreferredSize() == NOT_EXPLICITLY_DEFINED);
0873: }
0874:
0875: static boolean isFixedDefaultPadding(LayoutInterval interval) {
0876: return interval.isEmptySpace()
0877: && (interval.getMinimumSize() == NOT_EXPLICITLY_DEFINED || interval
0878: .getMinimumSize() == USE_PREFERRED_SIZE)
0879: && interval.getPreferredSize() == NOT_EXPLICITLY_DEFINED
0880: && (interval.getMaximumSize() == NOT_EXPLICITLY_DEFINED || interval
0881: .getMaximumSize() == USE_PREFERRED_SIZE);
0882: }
0883:
0884: /**
0885: * @return whether given interval is allowed to resize (not defined as fixed)
0886: */
0887: static boolean canResize(LayoutInterval interval) {
0888: // [don't care about shrinking, assuming min possibly not defined - is it ok?]
0889: int max = interval.getMaximumSize();
0890: int pref = interval.getPreferredSize();
0891: assert interval.isGroup() || max != NOT_EXPLICITLY_DEFINED;
0892: return (max != pref && max != USE_PREFERRED_SIZE)
0893: || max == NOT_EXPLICITLY_DEFINED;
0894: }
0895:
0896: /**
0897: * Finds out whether given interval would resize if allowed (given more
0898: * space by its parent).
0899: * @return whether given interval would resize if given opportunity
0900: */
0901: static boolean wantResize(LayoutInterval interval) {
0902: return canResize(interval)
0903: && (!interval.isGroup() || contentWantResize(interval));
0904: }
0905:
0906: /**
0907: * Finds out whether given interval would resize if allowed (given more
0908: * space by its parent). This method also considers resizing of the whole
0909: * layout (some parent of the interval could block the resizing).
0910: * @return whether given interval would resize if given opportunity
0911: */
0912: static boolean wantResizeInLayout(LayoutInterval interval) {
0913: if (!wantResize(interval))
0914: return false;
0915:
0916: while (interval.getParent() != null) {
0917: interval = interval.getParent();
0918: if (!canResize(interval))
0919: return false;
0920: }
0921: return true;
0922: }
0923:
0924: static boolean contentWantResize(LayoutInterval group) {
0925: boolean subres = false;
0926: Iterator it = group.getSubIntervals();
0927: while (it.hasNext()) {
0928: if (wantResize((LayoutInterval) it.next())) {
0929: subres = true;
0930: break;
0931: }
0932: }
0933: return subres;
0934: }
0935:
0936: static int getIntervalCurrentSize(LayoutInterval interval,
0937: int dimension) {
0938: if (!interval.isEmptySpace()) {
0939: return interval.getCurrentSpace().size(dimension);
0940: }
0941:
0942: int posL;
0943: int posT;
0944:
0945: LayoutInterval parent = interval.getParent();
0946: if (parent.isSequential()) {
0947: int index = parent.indexOf(interval);
0948: posL = index > 0 ? parent.getSubInterval(index - 1)
0949: .getCurrentSpace().positions[dimension][TRAILING]
0950: : parent.getCurrentSpace().positions[dimension][LEADING];
0951: posT = index + 1 < parent.getSubIntervalCount() ? parent
0952: .getSubInterval(index + 1).getCurrentSpace().positions[dimension][LEADING]
0953: : parent.getCurrentSpace().positions[dimension][TRAILING];
0954: } else {
0955: posL = parent.getCurrentSpace().positions[dimension][LEADING];
0956: posT = parent.getCurrentSpace().positions[dimension][TRAILING];
0957: }
0958:
0959: return posT - posL;
0960:
0961: }
0962:
0963: /**
0964: * Computes effective alignment of an interval in its parent. In case of
0965: * a sequential parent, the effective interval alignment depends on other
0966: * intervals and their resizability. E.g. if a preceding interval is
0967: * resizing then the interval is effectivelly "pushed" to the trailing end.
0968: * If there are no other intervals resizing then the parent alignment is
0969: * returned. If there are resizing intervals on both sides, or the interval
0970: * itself is resizing, then the there is no (positive) effective alignment.
0971: * @return LEADING, TRAILING, or DEFAULT
0972: */
0973: static int getEffectiveAlignment(LayoutInterval interval) {
0974: LayoutInterval parent = interval.getParent();
0975: if (parent.isParallel())
0976: return interval.getAlignment();
0977:
0978: if (LayoutInterval.wantResize(interval))
0979: return DEFAULT;
0980:
0981: boolean before = true;
0982: boolean leadingFixed = true;
0983: boolean trailingFixed = true;
0984: Iterator it = parent.getSubIntervals();
0985: do {
0986: LayoutInterval li = (LayoutInterval) it.next();
0987: if (li == interval) {
0988: before = false;
0989: } else if (LayoutInterval.wantResize(li)) {
0990: if (before)
0991: leadingFixed = false;
0992: else
0993: trailingFixed = false;
0994: }
0995: } while (it.hasNext());
0996:
0997: if (leadingFixed && !trailingFixed)
0998: return LEADING;
0999: if (!leadingFixed && trailingFixed)
1000: return TRAILING;
1001: if (leadingFixed && trailingFixed)
1002: return parent.getAlignment();
1003:
1004: return DEFAULT; // !leadingFixed && !trailingFixed
1005: }
1006:
1007: /**
1008: * Computes effective alignment of given interval's edge in its direct
1009: * parent. In case of a sequential parent, the effective interval alignment
1010: * depends on other intervals and their resizability.
1011: * @return effective alignment within parent, or DEFAULT in case of
1012: * ambiguous alignment in sequential parent
1013: */
1014: static int getEffectiveAlignment(LayoutInterval interval, int edge) {
1015: assert edge == LEADING || edge == TRAILING;
1016:
1017: boolean wantResize = LayoutInterval.wantResize(interval);
1018:
1019: LayoutInterval parent = interval.getParent();
1020: if (parent.isParallel())
1021: return wantResize ? edge : interval.getAlignment();
1022:
1023: int n = parent.getSubIntervalCount();
1024: int i = edge == LEADING ? 0 : n - 1;
1025: int d = edge == LEADING ? 1 : -1;
1026: boolean before = true;
1027: boolean beforeFixed = true;
1028: boolean afterFixed = true;
1029: while (i >= 0 && i < n) {
1030: LayoutInterval li = parent.getSubInterval(i);
1031: if (li == interval) {
1032: before = false;
1033: } else if (LayoutInterval.wantResize(li)) {
1034: if (before)
1035: beforeFixed = false;
1036: else
1037: afterFixed = false;
1038: }
1039: i += d;
1040: }
1041:
1042: if (beforeFixed && !afterFixed)
1043: return edge;
1044: if (!beforeFixed && afterFixed)
1045: return edge ^ 1;
1046: if (beforeFixed && afterFixed)
1047: return wantResize ? edge : parent.getAlignment();
1048:
1049: return DEFAULT; // !leadingFixed && !trailingFixed
1050: }
1051:
1052: /**
1053: * Computes effective alignment of an interval's edge relatively to given
1054: * parent.
1055: * @return effective alignment within parent, or DEFAULT in case of
1056: * ambiguous alignment in sequential parent
1057: */
1058: static int getEffectiveAlignmentInParent(LayoutInterval interval,
1059: LayoutInterval parent, int edge) {
1060: assert parent.isParentOf(interval);
1061: int alignment = edge;
1062: do {
1063: alignment = getEffectiveAlignment(interval, alignment);
1064: interval = interval.getParent();
1065: if (alignment != LEADING && alignment != TRAILING) {
1066: while (interval != parent) {
1067: if (getEffectiveAlignment(interval) != alignment)
1068: return DEFAULT;
1069: interval = interval.getParent();
1070: }
1071: }
1072: } while (interval != parent);
1073: return alignment;
1074: }
1075:
1076: /**
1077: * Creates clone of the given interval. Doesn't clone content of groups, nor
1078: * it sets LayoutComponent. Just the type, alignments and sizes are copied.
1079: *
1080: * @param interval interval to be cloned.
1081: * @param clone interval that should contain cloned data. Can be <code>null</code>.
1082: * @return shallow clone of the interval.
1083: */
1084: static LayoutInterval cloneInterval(LayoutInterval interval,
1085: LayoutInterval clone) {
1086: clone = (clone == null) ? new LayoutInterval(interval.getType())
1087: : clone;
1088: clone.setAlignment(interval.getAlignment());
1089: clone.setAttributes(interval.getAttributes()
1090: & ATTR_PERSISTENT_MASK);
1091: if (interval.getType() == PARALLEL) {
1092: clone.setGroupAlignment(interval.getGroupAlignment());
1093: }
1094: clone.setSizes(interval.getMinimumSize(), interval
1095: .getPreferredSize(), interval.getMaximumSize());
1096: if (isDefaultPadding(interval)) {
1097: clone.setPaddingType(interval.getPaddingType());
1098: }
1099: return clone;
1100: }
1101: }
|