/projects/netbeans-7.3/form/src/org/netbeans/modules/form/layoutdesign/LayoutOperations.java
Java | 1124 lines | 909 code | 78 blank | 137 comment | 444 complexity | a28e4b92b6e2ac1316df9cd0bdce6bea MD5 | raw file
- /*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
- *
- * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
- * Other names may be trademarks of their respective owners.
- *
- * The contents of this file are subject to the terms of either the GNU
- * General Public License Version 2 only ("GPL") or the Common
- * Development and Distribution License("CDDL") (collectively, the
- * "License"). You may not use this file except in compliance with the
- * License. You can obtain a copy of the License at
- * http://www.netbeans.org/cddl-gplv2.html
- * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
- * specific language governing permissions and limitations under the
- * License. When distributing the software, include this License Header
- * Notice in each file and include the License file at
- * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the GPL Version 2 section of the License file that
- * accompanied this code. If applicable, add the following below the
- * License Header, with the fields enclosed by brackets [] replaced by
- * your own identifying information:
- * "Portions Copyrighted [year] [name of copyright owner]"
- *
- * Contributor(s):
- *
- * The Original Software is NetBeans. The Initial Developer of the Original
- * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
- * Microsystems, Inc. All Rights Reserved.
- *
- * If you wish your version of this file to be governed by only the CDDL
- * or only the GPL Version 2, indicate your decision by adding
- * "[Contributor] elects to include this software in this distribution
- * under the [CDDL or GPL Version 2] license." If you do not indicate a
- * single choice of license, a recipient has the option to distribute
- * your version of this file under either the CDDL, the GPL Version 2 or
- * to extend the choice of license to its licensees as provided above.
- * However, if you add GPL Version 2 code and therefore, elected the GPL
- * Version 2 license, then the option applies only if the new code is
- * made subject to such option by the copyright holder.
- */
- package org.netbeans.modules.form.layoutdesign;
- import java.awt.Dimension;
- import java.util.*;
- /**
- * This class serves as a library of various useful and well-defined operations
- * on the layout model.
- *
- * @author Tomas Pavek
- */
- class LayoutOperations implements LayoutConstants {
- private LayoutModel layoutModel;
- private VisualMapper visualMapper;
- private static final boolean PREFER_ZERO_GAPS = true;
- private static final boolean SYMETRIC_ZERO_GAPS = true;
- LayoutOperations(LayoutModel model, VisualMapper mapper) {
- layoutModel = model;
- visualMapper = mapper;
- }
- LayoutModel getModel() {
- return layoutModel;
- }
- VisualMapper getMapper() {
- return visualMapper;
- }
- // -----
- /**
- * Extracts surroundings of given interval (placed in a sequential group).
- * Extracted intervals are removed and go to the 'restLeading' and
- * 'restTrailing' lists. Does not extract/remove the interval itself.
- */
- int extract(LayoutInterval interval, int alignment, boolean closed,
- List<List> restLeading, List<List> restTrailing) {
- return extract(interval, interval, alignment, closed, restLeading, restTrailing);
- }
-
- int extract(LayoutInterval leading, LayoutInterval trailing, int alignment, boolean closed,
- List<List> restLeading, List<List> restTrailing)
- {
- LayoutInterval seq = leading.getParent();
- assert seq.isSequential();
- int leadingIndex = seq.indexOf(leading);
- int trailingIndex = seq.indexOf(trailing);
- int count = seq.getSubIntervalCount();
- int extractCount;
- if (closed) {
- extractCount = trailingIndex - leadingIndex + 1;
- } else if (alignment != LEADING && alignment != TRAILING) {
- extractCount = 1;
- }
- else {
- extractCount = alignment == LEADING ? count - leadingIndex : leadingIndex + 1;
- }
- if (extractCount < seq.getSubIntervalCount()) {
- List<Object/*Integer or LayoutInterval*/> toRemainL = null;
- List<Object/*Integer or LayoutInterval*/> toRemainT = null;
- int startIndex = alignment == LEADING ? leadingIndex : leadingIndex - extractCount + 1;
- int endIndex = alignment == LEADING ? trailingIndex + extractCount - 1 : trailingIndex;
- Iterator it = seq.getSubIntervals();
- for (int idx=0; it.hasNext(); idx++) {
- LayoutInterval li = (LayoutInterval) it.next();
- if (idx < startIndex) {
- if (toRemainL == null) {
- toRemainL = new LinkedList<Object>();
- toRemainL.add(new Integer(LayoutInterval.getEffectiveAlignment(li)));
- }
- toRemainL.add(li);
- }
- else if (idx > endIndex) {
- if (toRemainT == null) {
- toRemainT = new LinkedList<Object>();
- toRemainT.add(new Integer(LayoutInterval.getEffectiveAlignment(li)));
- }
- toRemainT.add(li);
- }
- }
- if (toRemainL != null) {
- it = toRemainL.iterator();
- it.next();
- do {
- layoutModel.removeInterval((LayoutInterval)it.next());
- }
- while (it.hasNext());
- restLeading.add(toRemainL);
- }
- if (toRemainT != null) {
- it = toRemainT.iterator();
- it.next();
- do {
- layoutModel.removeInterval((LayoutInterval)it.next());
- }
- while (it.hasNext());
- restTrailing.add(toRemainT);
- }
- }
- return extractCount;
- }
- /**
- * Adds parallel content of a group specified in List to given sequence.
- * Used to create a remainder parallel group to a group of aligned intervals.
- * @param list the content of the group, output from 'extract' method
- * @param seq a sequential group where to add to
- * @param index the index in the sequence where to add
- * @param dimension
- * @param position the position of the remainder group relative to the main
- * group (LEADING or TRAILING)
- // * @param mainAlignment effective alignment of the main group (LEADING or
- // * TRAILING or something else meaning not aligned)
- * @return parallel group if it has been created, or null
- */
- LayoutInterval addGroupContent(List<List> list, LayoutInterval seq,
- int index, int dimension, int position/*, int mainAlignment*/)
- {
- assert seq.isSequential() && (position == LEADING || position == TRAILING);
- boolean resizingFillGap = false;
- LayoutInterval commonGap = null;
- boolean onlyGaps = true;
- // Remove sequences just with one gap
- for (int i=list.size()-1; i >= 0; i--) {
- List subList = list.get(i);
- assert subList.size() >= 2;
- if (subList.size() == 2) { // there is just one interval
- LayoutInterval li = (LayoutInterval) subList.get(1);
- if (li.isEmptySpace()) {
- if (commonGap == null || li.getPreferredSize() > commonGap.getPreferredSize())
- commonGap = li;
- if (LayoutInterval.canResize(li))
- resizingFillGap = true;
- list.remove(i);
- }
- else onlyGaps = false;
- }
- else onlyGaps = false;
- }
- if (onlyGaps) { // just one gap
- if (resizingFillGap && !LayoutInterval.canResize(commonGap))
- layoutModel.setIntervalSize(commonGap, NOT_EXPLICITLY_DEFINED,
- commonGap.getPreferredSize(),
- Short.MAX_VALUE);
- insertGapIntoSequence(commonGap, seq, index, dimension);
- return null;
- }
- if (list.size() == 1) { // just one sequence
- List subList = list.get(0);
- for (int n=subList.size(),i=n-1; i > 0; i--) { // skip alignment at 0
- LayoutInterval li = (LayoutInterval) subList.get(i);
- if (resizingFillGap && li.isEmptySpace() && !LayoutInterval.canResize(li)
- && ((i == 1 && position == TRAILING) || (i == n-1 && position == LEADING)))
- { // make the end gap resizing
- layoutModel.setIntervalSize(
- li, NOT_EXPLICITLY_DEFINED, li.getPreferredSize(), Short.MAX_VALUE);
- }
- if (li.isEmptySpace()
- && ((i == 1 && position == LEADING) || (i == n-1 && position == TRAILING))) {
- insertGapIntoSequence(li, seq, index, dimension);
- } else {
- layoutModel.addInterval(li, seq, index);
- }
- }
- return null;
- }
- // create parallel group for multiple intervals/sequences
- LayoutInterval group = new LayoutInterval(PARALLEL);
- // if (position == mainAlignment) {
- // // [but this should eliminate resizability only for gaps...]
- // group.setMinimumSize(USE_PREFERRED_SIZE);
- // group.setMaximumSize(USE_PREFERRED_SIZE);
- // }
- //// group.setGroupAlignment(alignment);
- // fill the group
- for (Iterator it=list.iterator(); it.hasNext(); ) {
- List subList = (List) it.next();
- LayoutInterval interval;
- if (subList.size() == 2) { // there is just one interval - use it directly
- int alignment = ((Integer)subList.get(0)).intValue();
- interval = (LayoutInterval) subList.get(1);
- if (alignment == LEADING || alignment == TRAILING)
- layoutModel.setIntervalAlignment(interval, alignment);
- }
- else { // there are more intervals - create sequence
- interval = new LayoutInterval(SEQUENTIAL);
- int alignment = ((Integer)subList.get(0)).intValue();
- if (alignment == LEADING || alignment == TRAILING)
- interval.setAlignment(alignment);
- for (int i=1,n=subList.size(); i < n; i++) {
- LayoutInterval li = (LayoutInterval) subList.get(i);
- if (resizingFillGap && li.isEmptySpace() && !LayoutInterval.canResize(li)
- && ((i == 1 && position == TRAILING) || (i == n-1 && position == LEADING)))
- { // make the end gap resizing
- layoutModel.setIntervalSize(
- li, NOT_EXPLICITLY_DEFINED, li.getPreferredSize(), Short.MAX_VALUE);
- }
- layoutModel.addInterval(li, interval, -1);
- }
- }
- layoutModel.addInterval(interval, group, -1);
- }
- layoutModel.addInterval(group, seq, index);
- return group;
- }
- /**
- * Adds 'interval' to 'target'. If needed, 'interval' is dismounted and
- * instead its sub-intervals are added. This is done e.g. when adding a
- * sequence to another sequence, or if adding a parallel group to another
- * parallel group with same alignment. Also redundant groups are canceled
- * (containing just one interval).
- * @return the increase of sub-intervals in target
- */
- int addContent(LayoutInterval interval, LayoutInterval target, int index) {
- return addContent(interval, target, index, -1);
- }
- /**
- * Adds 'interval' (or its content) to 'target'. In addition to the other
- * addContent method this one takes care of merging consecutive gaps that
- * may occur when adding content of a sequence to another sequence.
- */
- int addContent(LayoutInterval interval, LayoutInterval target, int index, int dimension) {
- int count = target.getSubIntervalCount();
- while (interval.isGroup() && interval.getSubIntervalCount() == 1) {
- interval = layoutModel.removeInterval(interval, 0);
- }
- if (interval.isSequential() && target.isSequential()) {
- if (index < 0) {
- index = target.getSubIntervalCount();
- }
- int startIndex = index;
- while (interval.getSubIntervalCount() > 0) {
- LayoutInterval li = layoutModel.removeInterval(interval, 0);
- layoutModel.addInterval(li, target, index++);
- }
- if (dimension == HORIZONTAL || dimension == VERTICAL) {
- // consecutive gaps may happen where the sequence was inserted
- startIndex--; // before first added
- index--; // last added
- if (startIndex >= 0 && mergeConsecutiveGaps(target, startIndex, dimension)) {
- index--; // one less
- }
- mergeConsecutiveGaps(target, index, dimension);
- }
- } else if (interval.isParallel() && target.isParallel()) {
- layoutModel.addInterval(interval, target, index);
- dissolveRedundantGroup(interval);
- } else {
- if (target.isSequential() && interval.getRawAlignment() != DEFAULT) {
- layoutModel.setIntervalAlignment(interval, DEFAULT);
- }
- layoutModel.addInterval(interval, target, index);
- }
- return target.getSubIntervalCount() - count;
- }
- void resizeInterval(LayoutInterval interval, int size) {
- assert size >= 0 || size == NOT_EXPLICITLY_DEFINED;
- int min = (interval.getMinimumSize() == interval.getPreferredSize()
- && interval.getMaximumSize() < Short.MAX_VALUE) ?
- size : interval.getMinimumSize();
- int max = interval.getMaximumSize() == interval.getPreferredSize() ?
- ((size == NOT_EXPLICITLY_DEFINED) ? USE_PREFERRED_SIZE : size) : interval.getMaximumSize();
- layoutModel.setIntervalSize(interval, min, size, max);
- }
- void setIntervalResizing(LayoutInterval interval, boolean resizing) {
- layoutModel.setIntervalSize(interval,
- resizing ? NOT_EXPLICITLY_DEFINED : USE_PREFERRED_SIZE,
- interval.getPreferredSize(),
- resizing ? Short.MAX_VALUE : USE_PREFERRED_SIZE);
- }
- void suppressGroupResizing(LayoutInterval group) {
- // don't for root group
- if (group.getParent() != null) {
- layoutModel.setIntervalSize(group, group.getMinimumSize(),
- group.getPreferredSize(),
- USE_PREFERRED_SIZE);
- }
- }
- void enableGroupResizing(LayoutInterval group) {
- layoutModel.setIntervalSize(group, group.getMinimumSize(),
- group.getPreferredSize(),
- NOT_EXPLICITLY_DEFINED);
- }
- void eliminateResizing(LayoutInterval interval, int dimension, Collection<LayoutInterval> eliminated) {
- if (interval.isSingle()) {
- if (LayoutInterval.wantResize(interval)) {
- int pref = LayoutRegion.UNKNOWN;
- if (interval.hasAttribute(LayoutInterval.ATTR_SIZE_DIFF)) {
- pref = LayoutInterval.getCurrentSize(interval, dimension);
- }
- if (pref == LayoutRegion.UNKNOWN) {
- pref = interval.getPreferredSize();
- }
- layoutModel.setIntervalSize(interval, USE_PREFERRED_SIZE, pref, USE_PREFERRED_SIZE);
- if (eliminated != null) {
- eliminated.add(interval);
- }
- }
- } else {
- for (Iterator<LayoutInterval> it=interval.getSubIntervals(); it.hasNext(); ) {
- eliminateResizing(it.next(), dimension, eliminated);
- }
- }
- }
- Collection<LayoutInterval> eliminateRedundantSuppressedResizing(LayoutInterval group, int dimension) {
- if (!group.isParallel()) {
- group = group.getParent();
- }
- if (group.getParent() != null && !LayoutInterval.canResize(group)) {
- int groupSize = group.getCurrentSpace().size(dimension);
- if (groupSize <= 0) {
- return Collections.EMPTY_LIST; // unknown current size
- }
- LayoutInterval oneResizing = null;
- boolean span = false;
- for (Iterator<LayoutInterval> it=group.getSubIntervals(); it.hasNext(); ) {
- LayoutInterval li = it.next();
- if (LayoutInterval.wantResize(li)) {
- if (oneResizing == null) {
- oneResizing = li;
- } else {
- return Collections.EMPTY_LIST; // more than one is not redundant
- }
- } else if (!span && !li.isEmptySpace()
- && li.getCurrentSpace().size(dimension) == groupSize) {
- span = true;
- }
- }
- if (oneResizing != null && !span) {
- List<LayoutInterval> l = new LinkedList<LayoutInterval>();
- if (!oneResizing.isGroup() || !suppressResizingInSubgroup(oneResizing, l)) {
- eliminateResizing(oneResizing, dimension, l);
- }
- enableGroupResizing(group);
- return l;
- }
- }
- return Collections.EMPTY_LIST;
- }
- /**
- * @return true if group either does not want to resize, or was set to
- * suppressed resizing when parallel with more than one resizing sub interval
- */
- private boolean suppressResizingInSubgroup(LayoutInterval group, Collection<LayoutInterval> eliminated) {
- LayoutInterval oneResizing = null;
- for (Iterator<LayoutInterval> it=group.getSubIntervals(); it.hasNext(); ) {
- LayoutInterval li = it.next();
- boolean res;
- if (LayoutInterval.canResize(li)) {
- if (li.isGroup()) {
- res = !suppressResizingInSubgroup(li, eliminated);
- } else {
- res = true;
- }
- } else {
- res = false;
- }
- if (res) {
- if (oneResizing == null) {
- oneResizing = li;
- } else if (group.isParallel()) { // more than 1 resizing
- suppressGroupResizing(group);
- eliminated.add(group);
- return true;
- } else {
- return false; // can't suppress a sequence
- }
- }
- }
- return oneResizing == null;
- }
- boolean completeGroupResizing(LayoutInterval group, int dimension) {
- if (!PREFER_ZERO_GAPS || !group.isParallel() || !LayoutInterval.canResize(group)
- || !LayoutInterval.contentWantResize(group)) {
- return false;
- }
- boolean gapAdded = false;
- List<LayoutInterval> list = null;
- for (Iterator<LayoutInterval> it=group.getSubIntervals(); it.hasNext(); ) {
- LayoutInterval li = it.next();
- if (!li.isEmptySpace() && !LayoutInterval.wantResize(li)
- && (li.getAlignment() == LEADING || li.getAlignment() == TRAILING)) {
- if (list == null) {
- list = new LinkedList<LayoutInterval>();
- }
- list.add(li);
- }
- }
- if (list != null) {
- for (LayoutInterval li : list) {
- LayoutInterval gap = new LayoutInterval(SINGLE);
- gap.setMinimumSize(0);
- gap.setPreferredSize(0);
- gap.setMaximumSize(Short.MAX_VALUE);
- gap.setAttribute(LayoutInterval.ATTR_FLEX_SIZEDEF);
- insertGap(gap, li,
- li.getCurrentSpace().positions[dimension][li.getAlignment()^1],
- dimension, li.getAlignment()^1);
- if (gap.getParent() != null) {
- gapAdded = true;
- }
- }
- }
- return gapAdded;
- }
- /**
- * Sets all components in a parallel group that have the same size with
- * 'aligned' to resizing so they all accommodate to same size.
- */
- void setParallelSameSize(LayoutInterval group, LayoutInterval aligned, int dimension) {
- assert group.isParallel();
- LayoutInterval alignedComp = getOneNonEmpty(aligned);
- for (Iterator it=group.getSubIntervals(); it.hasNext(); ) {
- LayoutInterval li = (LayoutInterval) it.next();
- if (li == aligned) {
- continue;
- }
- if (li.isParallel()) {
- setParallelSameSize(li, alignedComp, dimension);
- } else {
- LayoutInterval sub = getOneNonEmpty(li);
- if (sub != null
- && LayoutRegion.sameSpace(alignedComp.getCurrentSpace(), sub.getCurrentSpace(), dimension)
- && !LayoutInterval.wantResize(li)) {
- // viusally aligned subinterval
- if (sub.isParallel()) {
- setParallelSameSize(sub, alignedComp, dimension);
- } else if (!isPressurizedComponent(sub)) {
- // make this component filling the group - effectively keeping same size
- if (!LayoutInterval.isAlignedAtBorder(li, aligned.getAlignment())) {
- layoutModel.setIntervalAlignment(li, aligned.getAlignment());
- }
- int min = sub.getMinimumSize();
- layoutModel.setIntervalSize(sub,
- min != USE_PREFERRED_SIZE ? min : NOT_EXPLICITLY_DEFINED,
- sub.getPreferredSize(),
- Short.MAX_VALUE);
- }
- }
- }
- }
- }
- private static LayoutInterval getOneNonEmpty(LayoutInterval interval) {
- if (!interval.isSequential()) {
- return interval;
- }
- LayoutInterval nonEmpty = null;
- for (Iterator it=interval.getSubIntervals(); it.hasNext(); ) {
- LayoutInterval li = (LayoutInterval) it.next();
- if (!li.isEmptySpace()) {
- if (nonEmpty == null) {
- nonEmpty = li;
- } else {
- return null;
- }
- }
- }
- return nonEmpty;
- }
- /**
- * Is the component set to explicit fixed size smaller than its natural
- * minimum size?
- */
- private boolean isPressurizedComponent(LayoutInterval interval) {
- int pref = interval.getPreferredSize();
- if (interval.isComponent() && !LayoutInterval.canResize(interval)
- && pref != NOT_EXPLICITLY_DEFINED) {
- LayoutComponent comp = interval.getComponent();
- Dimension minSize = visualMapper.getComponentMinimumSize(comp.getId());
- if (minSize != null) {
- int min = comp.getLayoutInterval(HORIZONTAL) == interval ? minSize.width : minSize.height;
- if (pref < min) {
- return true;
- }
- }
- }
- return false;
- }
- void mergeParallelGroups(LayoutInterval group) {
- for (int i=group.getSubIntervalCount()-1; i >= 0; i--) {
- LayoutInterval sub = group.getSubInterval(i);
- if (sub.isGroup()) {
- mergeParallelGroups(sub);
- dissolveRedundantGroup(sub);
- }
- }
- }
- /**
- * Dissolves given group to parent group in case it is redundant.
- * @return true if the group was dissolved
- */
- boolean dissolveRedundantGroup(LayoutInterval group) {
- LayoutInterval parent = group.getParent();
- if (parent == null)
- return false;
- boolean justOne = (group.getSubIntervalCount() == 1);
- boolean dissolve = false;
- if (justOne) {
- dissolve = true;
- } else if (group.isSequential() && parent.isSequential()) {
- dissolve = true;
- } else if (group.isParallel() && parent.isParallel()) {
- int gA = group.getGroupAlignment();
- int pA = parent.getGroupAlignment();
- if (parent.getSubIntervalCount() == 1
- && (gA == pA || (gA != BASELINE && gA != CENTER))) {
- dissolve = true;
- } else { // check for compatible alignment and resizability
- int align = group.getAlignment();
- boolean sameAlign = true;
- boolean subResizing = false;
- Iterator it = group.getSubIntervals();
- while (it.hasNext()) {
- LayoutInterval li = (LayoutInterval) it.next();
- if (!subResizing && LayoutInterval.wantResize(li)) {
- subResizing = true;
- }
- if (li.getAlignment() != align) {
- sameAlign = false;
- }
- }
- boolean compatible;
- if (subResizing && (sameAlign || gA != BASELINE)) {
- compatible = false;
- boolean resizingAlreadyContained;
- if (LayoutInterval.canResize(group)) {
- resizingAlreadyContained = true;
- } else if (!LayoutInterval.canResize(parent)) {
- int dim = LayoutUtils.determineDimension(parent);
- resizingAlreadyContained = dim < 0
- || LayoutInterval.getCurrentSize(parent, dim) == LayoutInterval.getCurrentSize(group, dim);
- } else {
- resizingAlreadyContained = false;
- }
- if (resizingAlreadyContained) {
- it = parent.getSubIntervals();
- while (it.hasNext()) {
- LayoutInterval li = (LayoutInterval) it.next();
- if (li != group && LayoutInterval.wantResize(li)) {
- compatible = true;
- break;
- }
- }
- if (!compatible && (align == LEADING || align == TRAILING)) {
- LayoutInterval neighbor = LayoutInterval.getNeighbor(
- parent, group.getAlignment()^1, false, true, true);
- if (neighbor != null && neighbor.isEmptySpace()
- && neighbor.getPreferredSize() == NOT_EXPLICITLY_DEFINED)
- { // default fixed padding means there is no space for
- // independent size change, so the subgroup can be merged
- compatible = true;
- }
- }
- }
- }
- else compatible = sameAlign;
- dissolve = compatible;
- }
- }
- if (dissolve) { // the sub-group can be dissolved into parent group
- int alignmentInParent = group.getAlignment();
- int index = layoutModel.removeInterval(group);
- while (group.getSubIntervalCount() > 0) {
- LayoutInterval li = group.getSubInterval(0);
- int align = li.getAlignment();
- layoutModel.removeInterval(li);
- if (justOne && !LayoutInterval.canResize(group)) {
-
- }
- if (parent.isParallel()) { // moving to parallel group
- if (justOne) {
- if ((align != DEFAULT && align != alignmentInParent)
- || (align == DEFAULT && alignmentInParent != parent.getGroupAlignment())) {
- layoutModel.setIntervalAlignment(li, group.getRawAlignment());
- }
- if ((!LayoutInterval.canResize(group) || align == BASELINE)
- && LayoutInterval.wantResize(li)) {
- // resizing interval in fixed group - make it fixed
- if (li.isGroup()) {
- suppressGroupResizing(li);
- } else {
- layoutModel.setIntervalSize(li, USE_PREFERRED_SIZE, li.getPreferredSize(), USE_PREFERRED_SIZE);
- }
- }
- } else if (group.isParallel()) { // from parallel group
- if (li.getRawAlignment() == DEFAULT
- && group.getGroupAlignment() != parent.getGroupAlignment())
- { // force alignment explicitly
- layoutModel.setIntervalAlignment(li, align);
- }
- }
- layoutModel.addInterval(li, parent, index++);
- } else { // moving to sequential group
- index += addContent(li, parent, index, LayoutUtils.determineDimension(li));
- }
- }
- if (parent.getSubIntervalCount() == 1) {
- dissolveRedundantGroup(parent.getSubInterval(0));
- }
- return true;
- }
- return false;
- }
- /** NOT USED
- * This method goes through a sequential group and moves each interval next
- * to an open edge of a parallel group into the group.
- * @param parent sequential group to process
- * @param dimension
- */
- void moveInsideSequential(LayoutInterval parent, int dimension) {
- assert parent.isSequential();
- if (!parent.isSequential())
- return;
- int alignment = LEADING;
- do {
- LayoutInterval extend = findIntervalToExtend(parent, dimension, alignment);
- if (extend == null) {
- if (alignment == LEADING) {
- alignment = TRAILING;
- extend = findIntervalToExtend(parent, dimension, alignment);
- }
- if (extend == null)
- break;
- }
- LayoutInterval inGroup = extend.getParent(); // group to infiltrate
- LayoutInterval outGroup = inGroup;
- while (outGroup.getParent() != parent) {
- outGroup = outGroup.getParent();
- }
- int index = parent.indexOf(outGroup);
- int d = alignment == LEADING ? -1 : 1;
- // will the group remain open at the opposite edge?
- boolean commonEndingGap = true;
- for (int i=index-d, n=parent.getSubIntervalCount(); i >= 0 && i < n; i-=d) {
- LayoutInterval li = parent.getSubInterval(i);
- if ((!li.isEmptySpace() || (i-d >= 0 && i-d < n)) // ignore last gap
- && LayoutInterval.wantResize(li))
- { // resizing interval will close the group
- // possibly need to separate the rest of the group not to be influenced
- LayoutInterval endGap = parent.getSubInterval(alignment == LEADING ? n-1 : 0);
- if (endGap == null || endGap.getPreferredSize() != NOT_EXPLICITLY_DEFINED) {
- commonEndingGap = false;
- LayoutInterval closing = extend;
- int borderPos = parent.getCurrentSpace().positions[dimension][alignment^1];
- do {
- LayoutInterval par = closing.getParent();
- if (par.isParallel()) {
- separateGroupContent(closing, borderPos, dimension, alignment^1);
- }
- closing = par;
- }
- while (closing != outGroup);
- }
- break;
- }
- }
- int extendPos = extend.getCurrentSpace().positions[dimension][alignment^1];
- if (!extend.isSequential()) {
- LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
- seq.setAlignment(extend.getAlignment());
- layoutModel.addInterval(seq, inGroup, layoutModel.removeInterval(extend));
- layoutModel.setIntervalAlignment(extend, DEFAULT);
- layoutModel.addInterval(extend, seq, 0);
- extend = seq;
- }
- // move the intervals from outside inside the group, next to found interval (extend)
- LayoutInterval connectingGap = null;
- int idx, addIdx;
- if (alignment == LEADING) {
- idx = index + 1; // start behind the group
- addIdx = extend.getSubIntervalCount(); // add behind the interval
- }
- else {
- idx = index - 1; // start before the group
- addIdx = 0; // add before the interval
- }
- while (idx >= 0 && idx < parent.getSubIntervalCount()) {
- LayoutInterval li = parent.getSubInterval(idx);
- if (li.isEmptySpace()) {
- if (connectingGap == null) { // first gap
- if (extendPos != outGroup.getCurrentSpace().positions[dimension][alignment^1]) {
- // need to extend the first gap (extended interval inside group is smaller than the group)
- int neighborPos = parent.getSubInterval(idx-d).getCurrentSpace().positions[dimension][alignment];
- int distance = d * (extendPos - neighborPos);
- if (distance > 0)
- resizeInterval(li, distance);
- }
- connectingGap = li;
- }
- else if ((idx == 0 || idx == parent.getSubIntervalCount()-1)
- && commonEndingGap)
- { // keep the last gap out
- break;
- }
- }
- layoutModel.removeInterval(li);
- layoutModel.addInterval(li, extend, addIdx);
- if (alignment == LEADING)
- addIdx++;
- else
- idx--;
- }
- // check if the sequence was not whole moved into the group
- if (parent.getSubIntervalCount() == 1) { // only neighborGroup remained, eliminate the parent group
- assert outGroup == parent.getSubInterval(0);
- layoutModel.removeInterval(outGroup);
- LayoutInterval superParent = parent.getParent();
- addContent(outGroup, superParent, layoutModel.removeInterval(parent));
- break;
- }
- }
- while (true);
- }
- private LayoutInterval findIntervalToExtend(LayoutInterval parent, int dimension, int alignment) {
- int d = alignment == LEADING ? -1 : 1;
- int count = parent.getSubIntervalCount();
- int idx = alignment == LEADING ? count-1 : 0;
- boolean atBorder = true;
- boolean gap = false;
- while (idx >= 0 && idx < parent.getSubIntervalCount()) {
- LayoutInterval sub = parent.getSubInterval(idx);
- if (sub.isEmptySpace()) {
- gap = true;
- }
- else {
- if (!atBorder && gap && sub.isParallel()
- && !LayoutInterval.isClosedGroup(sub, alignment^1))
- { // this open parallel sub-group might be a candidate to move inside to
- int startIndex, endIndex;
- if (alignment == LEADING) {
- startIndex = idx + 1;
- endIndex = parent.getSubIntervalCount() - 1;
- }
- else {
- startIndex = 0;
- endIndex = idx - 1;
- }
- LayoutInterval extend = prepareGroupExtension(
- sub, parent, startIndex, endIndex, dimension, alignment^1);
- if (extend != null)
- return extend;
- }
- gap = false;
- atBorder = false;
- }
- idx += d;
- }
- return null;
- }
- private LayoutInterval prepareGroupExtension(LayoutInterval group,
- LayoutInterval parent, int startIndex, int endIndex,
- int dimension, int alignment)
- {
- boolean allOverlapping = true;
- LayoutInterval singleOverlap = null;
- List<LayoutInterval> overlapList = null;
- // looking for all intervals the given space is located next to
- Iterator it = group.getSubIntervals();
- while (it.hasNext()) {
- LayoutInterval li = (LayoutInterval) it.next();
- if (!li.isEmptySpace()) {
- if (LayoutUtils.contentOverlap(li, parent, startIndex, endIndex, dimension^1)) {
- // interval overlaps orthogonally
- if (singleOverlap == null) {
- singleOverlap = li;
- }
- else {
- if (overlapList == null) {
- overlapList = new LinkedList<LayoutInterval>();
- overlapList.add(singleOverlap);
- }
- overlapList.add(li);
- }
- }
- else allOverlapping = false;
- }
- }
- if (allOverlapping || singleOverlap == null)
- return null; // spans whole group or nothing
- if (overlapList != null) { // overlaps multiple intervals
- LayoutInterval subGroup = new LayoutInterval(PARALLEL);
- subGroup.setGroupAlignment(alignment^1);
- subGroup.setAlignment(alignment^1);
- int index = -1;
- do {
- LayoutInterval li = overlapList.remove(0);
- int idx = layoutModel.removeInterval(li);
- if (index < 0) {
- index = idx;
- }
- layoutModel.addInterval(li, subGroup, -1);
- subGroup.getCurrentSpace().expand(li.getCurrentSpace());
- }
- while (overlapList.size() > 0);
- layoutModel.addInterval(subGroup, group, index);
- singleOverlap = subGroup;
- }
- else {
- LayoutInterval subParallel;
- if (singleOverlap.isSequential()) {
- subParallel = singleOverlap.getSubInterval(
- alignment == LEADING ? 0 : singleOverlap.getSubIntervalCount()-1);
- if (!subParallel.isParallel())
- subParallel = null;
- }
- else if (singleOverlap.isParallel()) {
- subParallel = singleOverlap;
- }
- else subParallel = null;
- if (subParallel != null && !LayoutInterval.isClosedGroup(subParallel, alignment)) {
- LayoutInterval subOverlap = prepareGroupExtension(
- subParallel, parent, startIndex, endIndex, dimension, alignment);
- if (subOverlap != null)
- singleOverlap = subOverlap;
- }
- }
- return singleOverlap;
- }
- // [couldn't parallelizeWithParentSequence be used instead? or LayoutFeeder.separateSequence?]
- private void separateGroupContent(LayoutInterval separate, int outPos, int dimension, int alignment) {
- LayoutInterval group = separate.getParent();
- assert group.isParallel();
- LayoutInterval remainder = null;
- LayoutInterval remainderGroup = null;
- LayoutRegion remainderSpace = null;
- for (int i=0; i < group.getSubIntervalCount(); ) {
- LayoutInterval li = group.getSubInterval(i);
- if (li != separate) {
- assert li.getAlignment() == (alignment^1);
- layoutModel.removeInterval(li);
- if (remainder == null) {
- remainder = li;
- }
- else {
- if (remainderGroup == null) {
- remainderGroup = new LayoutInterval(PARALLEL);
- remainderGroup.setAlignment(alignment^1);
- remainderGroup.setGroupAlignment(alignment^1);
- layoutModel.addInterval(remainder, remainderGroup, 0);
- remainder = remainderGroup;
- }
- layoutModel.addInterval(li, remainderGroup, -1);
- }
- if (!li.isEmptySpace()) {
- if (remainderSpace == null) {
- remainderSpace = new LayoutRegion();
- }
- remainderSpace.expand(li.getCurrentSpace());
- }
- }
- else i++;
- }
- remainder.setCurrentSpace(remainderSpace);
- LayoutInterval remainderGap;
- int remainderPos = remainderSpace.positions[dimension][alignment];
- if (LayoutRegion.isValidCoordinate(outPos)) {
- int gapSize = alignment == LEADING ? remainderPos - outPos : outPos - remainderPos;
- remainderGap = new LayoutInterval(SINGLE);
- remainderGap.setSizes(NOT_EXPLICITLY_DEFINED, gapSize, Short.MAX_VALUE);
- }
- else { // take the existing gap next to group [this case is not used currently]
- remainderGap = LayoutInterval.getDirectNeighbor(group, alignment, false);
- if (remainderGap != null && remainderGap.isEmptySpace()) {
- layoutModel.removeInterval(remainderGap);
- // [should check for last interval in parent]
- LayoutInterval neighbor = LayoutInterval.getDirectNeighbor(group, alignment, true);
- outPos = neighbor != null ?
- neighbor.getCurrentSpace().positions[dimension][alignment^1] :
- group.getParent().getCurrentSpace().positions[dimension][alignment];
- int gapSize = alignment == LEADING ? remainderPos - outPos : outPos - remainderPos;
- resizeInterval(remainderGap, gapSize);
- }
- else remainderGap = null;
- }
- if (remainderGap != null) {
- LayoutInterval seq;
- if (remainder.isSequential()) {
- seq = remainder;
- }
- else {
- seq = new LayoutInterval(SEQUENTIAL);
- layoutModel.setIntervalAlignment(remainder, DEFAULT);
- layoutModel.addInterval(remainder, seq, 0);
- }
- layoutModel.addInterval(remainderGap, seq, alignment == LEADING ? 0 : -1);
- layoutModel.addInterval(seq, group, -1);
- group.getCurrentSpace().positions[dimension][alignment] = outPos;
- }
- else {
- layoutModel.addInterval(remainder, group, -1);
- }
- }
- /**
- * Makes given interval parallel with part of its parent sequence.
- */
- void parallelizeWithParentSequence(LayoutInterval interval, int endIndex, int dimension) {
- LayoutInterval parent = interval.getParent();
- assert parent.isParallel();
- LayoutInterval parParent = parent;
- while (!parParent.getParent().isSequential()) {
- parParent = parParent.getParent();
- }
- LayoutInterval parentSeq = parParent.getParent();
- int startIndex = parentSeq.indexOf(parParent);
- if (endIndex < 0)
- endIndex = parentSeq.getSubIntervalCount() - 1;
- else if (startIndex > endIndex) {
- int temp = startIndex;
- startIndex = endIndex;
- endIndex = temp;
- }
- layoutModel.removeInterval(interval);
- // TODO compensate possible group shrinking when removing the biggest interval
- if (interval.getAlignment() == DEFAULT) {
- layoutModel.setIntervalAlignment(interval, parent.getGroupAlignment());
- }
- addParallelWithSequence(interval, parentSeq, startIndex, endIndex, dimension);
- if (parent.getSubIntervalCount() == 1) {
- addContent(layoutModel.removeInterval(parent, 0),
- parent.getParent(),
- layoutModel.removeInterval(parent),
- dimension);
- }
- else if (parent.getSubIntervalCount() == 0) {
- layoutModel.removeInterval(parent);
- }
- }
- void addParallelWithSequence(LayoutInterval interval, LayoutInterval seq, int startIndex, int endIndex, int dimension) {
- LayoutInterval group;
- if (startIndex > 0 || endIndex < seq.getSubIntervalCount()-1) {
- group = new LayoutInterval(PARALLEL);
- if (interval.getAlignment() != DEFAULT) {
- group.setGroupAlignment(interval.getAlignment());
- }
- int startPos = LayoutUtils.getVisualPosition(seq.getSubInterval(startIndex), dimension, LEADING);
- int endPos = LayoutUtils.getVisualPosition(seq.getSubInterval(endIndex), dimension, TRAILING);
- group.getCurrentSpace().set(dimension, startPos, endPos);
- if (startIndex != endIndex) {
- LayoutInterval subSeq = new LayoutInterval(SEQUENTIAL);
- subSeq.setAlignment(interval.getAlignment()); // [was: seq.getAlignment()]
- for (int n=endIndex-startIndex+1; n > 0; n--) {
- layoutModel.addInterval(layoutModel.removeInterval(seq, startIndex), subSeq, -1);
- }
- layoutModel.addInterval(subSeq, group, 0);
- }
- else {
- layoutModel.addInterval(layoutModel.removeInterval(seq, startIndex), group, 0);
- }
- layoutModel.addInterval(group, seq, startIndex);
- group.getCurrentSpace().expand(interval.getCurrentSpace(), dimension);
- }
- else {
- group = seq.getParent();
- }
- layoutModel.addInterval(interval, group, -1);
- }
- int optimizeGaps(LayoutInterval group, int dimension) {
- for (int i=0; i < group.getSubIntervalCount(); i++) {
- LayoutInterval li = group.getSubInterval(i);
- if (li.isEmptySpace() && group.getSubIntervalCount() > 1) {
- // remove container supporting gap
- layoutModel.removeInterval(group, i);
- i--;
- }
- }
- if (group.getSubIntervalCount() <= 1) {
- return -1;
- }
- // 1) Determine which intervals have ending gaps for optimization, what's
- // their alignment and resizability, and whether whole group should be
- // processed or just a part (subgroup).
- boolean anyAlignedLeading = false; // if false the group is open at leading edge
- boolean anyAlignedTrailing = false; // if false the group is open at trailing edge
- boolean contentResizing = false;
- IntervalSet processLeading = null;
- IntervalSet processTrailing = null;
- boolean subGroupLeading = false;
- boolean subGroupTrailing = false;
- {
- // 1a) Analyze where the ending gaps are and how aligned.
- IntervalSet[] alignedGaps = new IntervalSet[] { new IntervalSet(), new IntervalSet() };
- IntervalSet[] alignedNoGaps = new IntervalSet[] { new IntervalSet(), new IntervalSet() };
- IntervalSet[] unalignedFixedGaps = new IntervalSet[] { new IntervalSet(), new IntervalSet() };
- IntervalSet[] unalignedResGaps = new IntervalSet[] { new IntervalSet(), new IntervalSet() };
- IntervalSet[] unalignedNoGaps = new IntervalSet[] { new IntervalSet(), new IntervalSet() };
- for (int i=0; i < group.getSubIntervalCount(); i++) {
- LayoutInterval li = group.getSubInterval(i);
- boolean leadAlign = false;
- boolean trailAlign = false;
- LayoutInterval leadingGap = null;
- LayoutInterval trailingGap = null;
- boolean leadGapRes = false;
- boolean trailGapRes = false;
- boolean contentRes = false;
- boolean noResizing = false;
- if (li.isSequential()) {
- // find out effective alignment of the sequence content without border gaps
- for (int j=0; j < li.getSubIntervalCount(); j++) {
- LayoutInterval sub = li.getSubInterval(j);
- if (j == 0 && sub.isEmptySpace()) {
- leadingGap = sub;
- leadGapRes = LayoutInterval.wantResize(sub);
- } else if (j+1 == li.getSubIntervalCount() && sub.isEmptySpace()) {
- trailingGap = sub;
- trailGapRes = LayoutInterval.wantResize(sub);
- } else if (!contentRes && LayoutInterval.wantResize(sub)) {
- contentRes = true;
- }
- }
- if (!contentRes) {
- if (lead