private void mergeParallelInclusions()

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;
    }