in java/form/src/org/netbeans/modules/form/layoutdesign/LayoutFeeder.java [3610:4057]
private void mergeParallelInclusions(List<IncludeDesc> inclusions, IncludeDesc original, boolean preserveOriginal) {
// 1st step - find representative (best) inclusion
IncludeDesc best = null;
boolean bestOriginal = false;
for (Iterator it=inclusions.iterator(); it.hasNext(); ) {
IncludeDesc iDesc = (IncludeDesc) it.next();
if (original == null || !preserveOriginal || canCombine(iDesc, original)) {
if (best != null) {
boolean originalCompatible = original != null && !preserveOriginal
&& iDesc.parent == original.parent;
if (!bestOriginal && originalCompatible) {
best = iDesc;
bestOriginal = true;
}
else if (bestOriginal == originalCompatible) {
LayoutInterval group1 = best.parent.isSequential() ?
best.parent.getParent() : best.parent;
LayoutInterval group2 = iDesc.parent.isSequential() ?
iDesc.parent.getParent() : iDesc.parent;
if (group1.isParentOf(group2)) {
best = iDesc; // deeper is better
}
else if (!group2.isParentOf(group1) && iDesc.distance < best.distance) {
best = iDesc;
}
}
}
else {
best = iDesc;
bestOriginal = original != null && !preserveOriginal && iDesc.parent == original.parent;
}
}
}
if (best == null) { // nothing compatible with original position
assert preserveOriginal;
inclusions.clear();
inclusions.add(original);
return;
}
LayoutInterval commonGroup = best.parent.isSequential() ? best.parent.getParent() : best.parent;
// 2nd remove incompatible inclusions, move compatible ones to same level
for (Iterator it=inclusions.iterator(); it.hasNext(); ) {
IncludeDesc iDesc = (IncludeDesc) it.next();
if (iDesc != best) {
if (!compatibleInclusions(iDesc, best, dimension)) {
it.remove();
} else if (iDesc.parent == best.parent && iDesc.neighbor == best.neighbor
&& (iDesc.neighbor != null || iDesc.index == iDesc.index)) {
it.remove(); // same inclusion twice (detect for better robustness)
} else if (iDesc.newSubGroup && LayoutUtils.contentOverlap(iDesc.parent, best.parent, dimension^1)) {
it.remove(); // don't try to solve what is already overlapping
} else {
LayoutInterval group = iDesc.parent.isSequential() ?
iDesc.parent.getParent() : iDesc.parent;
if (group.isParentOf(commonGroup)) {
LayoutInterval neighbor = iDesc.parent.isSequential() ?
iDesc.parent : iDesc.neighbor;
layoutModel.removeInterval(neighbor);
// [what about the alignment?]
layoutModel.addInterval(neighbor, commonGroup, -1);
// possibly adjust fixed gaps due to a position shift
for (int e=LEADING; e <= TRAILING; e++) {
int d = (e==LEADING) ? 1 : -1;
int posDiff = LayoutRegion.distance(group.getCurrentSpace(), commonGroup.getCurrentSpace(), dimension, e, e);
if (posDiff != LayoutRegion.UNKNOWN) {
posDiff *= d;
}
if (posDiff > 0) {
for (LayoutInterval gap : LayoutUtils.getSideGaps(neighbor, e, true)) {
int currentSize = LayoutInterval.canResize(gap) ? NOT_EXPLICITLY_DEFINED : gap.getPreferredSize();
if (currentSize > posDiff) {
operations.resizeInterval(gap, currentSize - posDiff);
}
}
}
}
if (iDesc.parent == group) {
iDesc.parent = commonGroup;
}
if (group.getSubIntervalCount() == 1 && group.getParent() != null) {
LayoutInterval parent = group.getParent();
LayoutInterval last = layoutModel.removeInterval(group, 0);
int index = layoutModel.removeInterval(group);
operations.addContent(last, parent, index, dimension);
updateInclusionsForEliminatedGroup(inclusions, group, parent, last, index);
if (last.getParent() == null) { // dissolved into parent
updateInclusionsForEliminatedGroup(inclusions, last, parent, null, index);
if (commonGroup == last) {
commonGroup = parent; // parent is parallel in this case
}
}
}
}
}
}
}
if (original != null && original.parent.isParallel() && original.snappedParallel != null
&& original.ortDistance != 0 && inclusions.size() > 1) {
// inclusion not overlapping orthogonally is not meaningful (either
// forced by addAlignedInclusion, or it's an original position of
// resizing in orthogonal dimension that started not overlapping)
inclusions.remove(original);
}
if (inclusions.size() == 1)
return;
// 3rd analyze inclusions requiring a subgroup (parallel with part of sequence)
LayoutInterval subGroup = null;
int subEffAlign = -1;
LayoutInterval nextTo = null;
List<List> separatedLeading = new LinkedList<List>();
List<List> separatedTrailing = new LinkedList<List>();
for (Iterator it=inclusions.iterator(); it.hasNext(); ) {
IncludeDesc iDesc = (IncludeDesc) it.next();
if (iDesc.parent.isSequential() && iDesc.newSubGroup) {
int[] parallelBoundaries = collectNeighborPositions(inclusions, iDesc, addingSpace, dimension);
LayoutInterval parSeq = extractParallelSequence(iDesc.parent, addingSpace, -1, -1, iDesc.alignment, parallelBoundaries);
if (parSeq != null) {
assert parSeq.isParallel(); // parallel group with part of the original sequence
if (subGroup == null) {
subGroup = parSeq;
subEffAlign = LayoutInterval.getEffectiveAlignment(parSeq);
} else {
do {
LayoutInterval sub = layoutModel.removeInterval(parSeq, 0);
layoutModel.addInterval(sub, subGroup, -1);
} while (parSeq.getSubIntervalCount() > 0);
// correct (shift) current positions of the common subgroup
if (subEffAlign == LEADING || subEffAlign == TRAILING) {
LayoutRegion commSpace = subGroup.getCurrentSpace();
LayoutRegion space = parSeq.getCurrentSpace();
int e1 = subEffAlign;
int e2 = (subEffAlign^1);
int d = (e1==LEADING) ? 1 : -1;
if (LayoutRegion.distance(commSpace, space, dimension, e1, e1)*d > 0) {
commSpace.setPos(dimension, LEADING, space.positions[dimension][LEADING]);
}
if (LayoutRegion.distance(commSpace, space, dimension, e2, e2)*d > 0) {
commSpace.setPos(dimension, e2, space.positions[dimension][e2]);
}
}
}
// extract surroundings of the group in the sequence
operations.extract(parSeq, DEFAULT, true, separatedLeading, separatedTrailing);
layoutModel.removeInterval(parSeq);
layoutModel.removeInterval(iDesc.parent);
} else { // nothing left to extract, the whole sequence is likely covered by the parallel neighbors
LayoutRegion seqSpace = iDesc.parent.getCurrentSpace();
if (seqSpace.isSet(dimension)) {
for (int e=LEADING; e <= TRAILING; e++) {
if (parallelBoundaries[e] != LayoutRegion.UNKNOWN) {
int sPos = seqSpace.positions[dimension][e^1];
int bPos = parallelBoundaries[e];
if (e == TRAILING) {
sPos = -sPos; bPos = -bPos;
}
if (bPos >= sPos) { // parallel neigbors span the whole sequence
iDesc.newSubGroup = false;
iDesc.index = (e == TRAILING ? 0 : iDesc.parent.getSubIntervalCount());
break;
}
}
}
}
if (iDesc.newSubGroup) {
it.remove();
if (inclusions.size() == 1) {
if (separatedLeading.isEmpty() && separatedTrailing.isEmpty()) {
return;
} else {
break;
}
}
}
}
}
}
int extractAlign = DEFAULT;
if (subGroup != null) {
if (separatedLeading.isEmpty())
extractAlign = TRAILING;
if (separatedTrailing.isEmpty())
extractAlign = LEADING;
}
// Surroundings of adding interval determined in step 4 are created separately
// one by one, but we need to unify the resizability of all the gaps next to
// the adding interval together.
boolean[] anyResizingNeighbor = new boolean[2];
int[] fixedSideGaps = new int[2];
List<LayoutInterval[]> unifyGaps = null;
// Placing the adding interval into individual inclusions in step 4
// could destroy it if it's a sequential group of multiple components.
LayoutInterval addingMultiWrapper;
if (addingInterval.isSequential()) {
addingMultiWrapper = new LayoutInterval(PARALLEL);
addingMultiWrapper.add(addingInterval, 0);
addingMultiWrapper.getCurrentSpace().set(addingSpace);
addingInterval = addingMultiWrapper;
} else {
addingMultiWrapper = null;
}
// 4th collect surroundings of adding interval
// (the intervals will go into a side group in step 5, or into subgroup
// of 'subGroup' next to the adding interval if in previous step some
// content was separated into a parallel subgroup of a sequence)
LayoutInterval subsubGroup = null;
for (Iterator it=inclusions.iterator(); it.hasNext(); ) {
IncludeDesc iDesc = (IncludeDesc) it.next();
if (iDesc.parent.isParallel() || !iDesc.newSubGroup) {
// add to this inclusion (temporarily) - then can get surroundings
LayoutInterval snp = null;
if (iDesc.snappedParallel != null && !iDesc.parent.isParentOf(iDesc.snappedParallel)) {
snp = iDesc.snappedParallel; // it may be removed from layout at this moment,
iDesc.snappedParallel = null; // and we won't do aligning in parallel anyway
}
addToGroup(iDesc, null, false);
if (snp != null) {
iDesc.snappedParallel = snp;
}
if (subGroup == null && !LayoutInterval.wantResize(addingInterval)) {
// now we may have L and T gaps next to the added interval
LayoutInterval lGap = LayoutInterval.getDirectNeighbor(addingInterval, LEADING, false);
LayoutInterval tGap = LayoutInterval.getDirectNeighbor(addingInterval, TRAILING, false);
if (lGap != null && lGap.isEmptySpace() && tGap != null && tGap.isEmptySpace()) {
LayoutInterval[] gaps = new LayoutInterval[] { lGap, tGap };
for (int i=LEADING; i <= TRAILING; i++) {
if (!LayoutInterval.canResize(gaps[i])) {
if (LayoutInterval.hasAnyResizingNeighbor(gaps[i], i)) {
anyResizingNeighbor[i] = true;
gaps[i] = null;
}
fixedSideGaps[i]++;
}
}
if (gaps[LEADING] != null && gaps[TRAILING] != null) {
if (unifyGaps == null) {
unifyGaps = new ArrayList<>();
}
unifyGaps.add(gaps);
}
}
}
// extract the surroundings
operations.extract(addingInterval, extractAlign, extractAlign == DEFAULT,
separatedLeading, separatedTrailing);
LayoutInterval parent = addingInterval.getParent();
layoutModel.removeInterval(addingInterval);
layoutModel.removeInterval(parent);
if (extractAlign != DEFAULT && LayoutInterval.getCount(parent, LayoutRegion.ALL_POINTS, true) >= 1) {
if (subsubGroup == null) {
subsubGroup = new LayoutInterval(PARALLEL);
subsubGroup.setGroupAlignment(extractAlign);
}
operations.addContent(parent, subsubGroup, -1, dimension);
}
}
if (iDesc.snappedNextTo != null)
nextTo = iDesc.snappedNextTo;
if (iDesc != best)
it.remove();
}
if (!inclusions.contains(best)) {
inclusions.add(best);
}
if (addingMultiWrapper != null) {
addingInterval = addingMultiWrapper.remove(0);
}
if (unifyGaps != null) {
// unify resizability of the border gaps collected for individual inclusions
for (LayoutInterval[] gaps : unifyGaps) {
int preferredFixedSide = fixedSideGaps[LEADING] >= fixedSideGaps[TRAILING] ? LEADING : TRAILING;
for (int i=LEADING; i <= TRAILING; i++) {
if (LayoutInterval.canResize(gaps[i]) && !anyResizingNeighbor[i]
&& (anyResizingNeighbor[i^1] || preferredFixedSide == i)) {
operations.setIntervalResizing(gaps[i], false);
if (!LayoutInterval.canResize(gaps[i^1])) {
operations.setIntervalResizing(gaps[i^i], true);
}
break;
}
}
}
} else if (subGroup != null && (subEffAlign == LEADING || subEffAlign == TRAILING)) {
// adjust size of the border gaps in the extracted sub-group (some may have shifted)
int d = (subEffAlign==LEADING) ? 1 : -1;
int groupPos = subGroup.getCurrentSpace().positions[dimension][subEffAlign];
for (LayoutInterval gap : LayoutUtils.getSideGaps(subGroup, subEffAlign, true)) {
int currentSize = LayoutInterval.canResize(gap) ? NOT_EXPLICITLY_DEFINED : gap.getPreferredSize();
if (currentSize > 0) {
int pos = LayoutUtils.getVisualPosition(gap, dimension, subEffAlign^1);
int expectedSize = (pos - groupPos) * d;
if (expectedSize > 0 && expectedSize < currentSize) {
operations.resizeInterval(gap, expectedSize);
}
}
}
}
// prepare the common group for merged content
int[] borderPos = commonGroup.getCurrentSpace().positions[dimension];
int[] neighborPos = (subGroup != null ? subGroup : addingInterval).getCurrentSpace().positions[dimension];
LayoutInterval commonSeq;
int index;
if (commonGroup.getSubIntervalCount() == 0 && commonGroup.getParent() != null) {
// the common group got empty - eliminate it to avoid unncessary nesting
LayoutInterval parent = commonGroup.getParent();
index = layoutModel.removeInterval(commonGroup);
updateInclusionsForEliminatedGroup_ParallelSnap(inclusions, commonGroup, parent, null);
if (parent.isSequential()) {
commonSeq = parent;
commonGroup = parent.getParent();
}
else { // parallel parent
commonSeq = new LayoutInterval(SEQUENTIAL);
commonSeq.setAlignment(commonGroup.getAlignment());
layoutModel.addInterval(commonSeq, parent, index);
commonGroup = parent;
index = 0;
}
}
else {
commonSeq = new LayoutInterval(SEQUENTIAL);
layoutModel.addInterval(commonSeq, commonGroup, -1);
index = 0;
}
if (commonSeq.getSubIntervalCount() == 0) {
commonSeq.getCurrentSpace().set(dimension, commonGroup.getCurrentSpace());
}
// 5th create groups of merged content around the adding component
LayoutInterval sideGroupLeading = null;
LayoutInterval sideGroupTrailing = null;
if (!separatedLeading.isEmpty()) {
int checkCount = commonSeq.getSubIntervalCount(); // remember ...
sideGroupLeading = operations.addGroupContent(
separatedLeading, commonSeq, index, dimension, LEADING); //, mainEffectiveAlign
index += commonSeq.getSubIntervalCount() - checkCount;
}
if (!separatedTrailing.isEmpty()) {
sideGroupTrailing = operations.addGroupContent(
separatedTrailing, commonSeq, index, dimension, TRAILING); //, mainEffectiveAlign
}
if (sideGroupLeading != null) {
int checkCount = commonSeq.getSubIntervalCount(); // remember ...
sideGroupLeading.getCurrentSpace().set(dimension, borderPos[LEADING], neighborPos[LEADING]);
operations.optimizeGaps(sideGroupLeading, dimension);
index += commonSeq.getSubIntervalCount() - checkCount;
}
if (sideGroupTrailing != null) {
sideGroupTrailing.getCurrentSpace().set(dimension, neighborPos[TRAILING], borderPos[TRAILING]);
operations.optimizeGaps(sideGroupTrailing, dimension);
}
// 6th adjust the final inclusion
best.parent = commonSeq;
best.newSubGroup = false;
best.neighbor = null;
LayoutInterval separatingGap;
int gapIdx = index;
if (gapIdx == commonSeq.getSubIntervalCount()) {
gapIdx--;
separatingGap = commonSeq.getSubInterval(gapIdx);
}
else {
separatingGap = commonSeq.getSubInterval(gapIdx);
if (!separatingGap.isEmptySpace()) {
gapIdx--;
if (gapIdx > 0)
separatingGap = commonSeq.getSubInterval(gapIdx);
}
}
if (!separatingGap.isEmptySpace())
separatingGap = null;
else if (subGroup == null) {
index = gapIdx;
// eliminate the gap if caused by addToGroup called to separate adding
// interval's surroundings to side groups; the gap will be created
// again when addToGroup is called definitively (for merged inclusions)
if (index == 0 && !LayoutInterval.isAlignedAtBorder(commonSeq, LEADING)) {
layoutModel.removeInterval(separatingGap);
separatingGap = null;
}
else if (index == commonSeq.getSubIntervalCount()-1 && !LayoutInterval.isAlignedAtBorder(commonSeq, TRAILING)) {
layoutModel.removeInterval(separatingGap);
separatingGap = null;
}
}
best.snappedNextTo = nextTo;
if (nextTo != null)
best.fixedPosition = true;
// 7th resolve subgroup
if (subGroup != null) {
if (separatingGap != null
&& (extractAlign == DEFAULT
|| (extractAlign == LEADING && index > gapIdx)
|| (extractAlign == TRAILING && index <= gapIdx)))
{ // subGroup goes next to a separating gap - which is likely superflous
// (the extracted parallel sequence in subGroup has its own gap)
layoutModel.removeInterval(separatingGap);
if (index >= gapIdx && index > 0)
index--;
}
int subIdx = index;
if (subsubGroup != null && subsubGroup.getSubIntervalCount() > 0) {
LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
seq.setAlignment(best.alignment);
operations.addContent(subsubGroup, seq, 0, dimension);
layoutModel.addInterval(seq, subGroup, -1);
// [should run optimizeGaps on subsubGroup?]
best.parent = seq;
index = extractAlign == LEADING ? 0 : seq.getSubIntervalCount();
}
else {
best.newSubGroup = true;
}
operations.addContent(subGroup, commonSeq, subIdx, dimension);
}
operations.mergeConsecutiveGaps(commonSeq, index-1, dimension);
best.index = index;
updateReplacedOriginalGroup(commonGroup, commonSeq, index);
if (subGroup != null && best.newSubGroup && best.snappedParallel != null) {
// after reconfiguring into subgroup it may require to re-check the aligned inclusion (bug 203742)
IncludeDesc alignedDesc = addAligningInclusion(inclusions);
if (alignedDesc != null) {
inclusions.remove(best.parent.isParentOf(alignedDesc.parent) ? best : alignedDesc);
}
}
optimizeStructure = true;
}