in java/form/src/org/netbeans/modules/form/layoutdesign/LayoutOperations.java [1059:1626]
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 (leadGapRes || trailGapRes) {
leadAlign = trailGapRes && !leadGapRes;
trailAlign = leadGapRes && !trailGapRes;
} else {
noResizing = true;
}
}
} else if (LayoutInterval.wantResize(li)) {
contentRes = true;
} else {
noResizing = true;
}
if (contentRes) {
leadAlign = trailAlign = true;
} else if (noResizing) {
int alignment = li.getAlignment();
leadAlign = (alignment == LEADING);
trailAlign = (alignment == TRAILING);
}
if (leadingGap != null) {
if (leadAlign) {
alignedGaps[LEADING].add(li, !noResizing);
} else if (leadGapRes) {
unalignedResGaps[LEADING].add(li, true);
} else {
unalignedFixedGaps[LEADING].add(li, false);
}
} else if (!LayoutUtils.hasSideGaps(li, LEADING, true)) {
if (leadAlign) {
alignedNoGaps[LEADING].add(li, !noResizing);
} else {
unalignedNoGaps[LEADING].add(li, false);
}
}
if (trailingGap != null) {
if (trailAlign) {
alignedGaps[TRAILING].add(li, !noResizing);
} else if (trailGapRes) {
unalignedResGaps[TRAILING].add(li, true);
} else {
unalignedFixedGaps[TRAILING].add(li, false);
}
} else if (!LayoutUtils.hasSideGaps(li, TRAILING, true)) {
if (trailAlign) {
alignedNoGaps[TRAILING].add(li, !noResizing);
} else {
unalignedNoGaps[TRAILING].add(li, false);
}
}
}
// 1b) Find out what gaps to optimize on each side of the group.
IntervalSet[] alignedVariants = countAlignedVariants(alignedGaps, unalignedFixedGaps, unalignedResGaps);
IntervalSet[] unalignedVariants = countUnalignedVariants(unalignedFixedGaps, unalignedResGaps, unalignedNoGaps, dimension);
IntervalSet[] leadingVariants = new IntervalSet[] { alignedVariants[LEADING], unalignedVariants[LEADING] };
IntervalSet[] trailingVariants = new IntervalSet[] { alignedVariants[TRAILING], unalignedVariants[TRAILING] };
IntervalSet bestLeading = null;
IntervalSet bestTrailing = null;
for (int i=0; i < leadingVariants.length; i++) {
IntervalSet iSet = leadingVariants[i];
if (bestLeading == null || iSet.count() > bestLeading.count()) {
bestLeading = iSet;
}
}
for (int i=0; i < trailingVariants.length; i++) {
IntervalSet iSet = trailingVariants[i];
if (bestTrailing == null || iSet.count() > bestTrailing.count()) {
bestTrailing = iSet;
}
}
if (bestLeading.count() < group.getSubIntervalCount()
&& bestTrailing.count() < group.getSubIntervalCount()) {
// can't optimize everything on both sides, so check combinations and
// look for a suitable subgroup
IntervalSet bestCombine = null;
for (int i=0; i < leadingVariants.length; i++) {
IntervalSet lSet = leadingVariants[i];
for (int j=0; j < trailingVariants.length; j++) {
IntervalSet tSet = trailingVariants[j];
IntervalSet comSet = new IntervalSet();
for (int ii=0; ii < group.getSubIntervalCount(); ii++) {
LayoutInterval li = group.getSubInterval(ii);
if (lSet.contains(li) && tSet.contains(li)) {
comSet.add(li, LayoutInterval.wantResize(li));
}
}
if (bestCombine == null || comSet.count() > bestCombine.count()) {
bestCombine = comSet;
}
}
}
if (bestCombine != null) {
IntervalSet bestSingle = bestLeading.count() > bestTrailing.count()
? bestLeading : bestTrailing;
if (bestSingle.count() - bestCombine.count() >= bestCombine.count()) {
// subgroup for one side
if (bestSingle == bestLeading) {
bestTrailing.clear();
} else {
bestLeading.clear();
}
} else { // subgroup for both sides
bestLeading = bestCombine;
bestTrailing = bestCombine;
}
}
}
processLeading = bestLeading;
if (processLeading.count() < 2) {
processLeading.clear();
}
processTrailing = bestTrailing;
if (processTrailing.count() < 2) {
processTrailing.clear();
}
subGroupLeading = processLeading.count() < group.getSubIntervalCount();
subGroupTrailing = processTrailing.count() < group.getSubIntervalCount();
if (processLeading.count() > 0 || processTrailing.count() > 0) {
// now when knowing which intervals are relevant for optimization,
// determine their alignment and resizability
for (int i=0; i < group.getSubIntervalCount(); i++) {
LayoutInterval li = group.getSubInterval(i);
boolean isL = processLeading.contains(li);
boolean isT = processTrailing.contains(li);
if (!isL && !isT) {
continue;
}
boolean leadAlign = false;
boolean trailAlign = false;
boolean contentRes = false;
boolean noResizing = false;
if (li.isSequential()) {
boolean leadGapRes = false;
boolean trailGapRes = false;
for (int j=0; j < li.getSubIntervalCount(); j++) {
LayoutInterval sub = li.getSubInterval(j);
if (j == 0 && isL && sub.isEmptySpace()) {
leadGapRes = LayoutInterval.wantResize(sub);
} else if (j+1 == li.getSubIntervalCount() && isT && sub.isEmptySpace()) {
trailGapRes = LayoutInterval.wantResize(sub);
} else if (!contentRes && LayoutInterval.wantResize(sub)) {
contentRes = true;
}
}
if (!contentRes) {
if (leadGapRes || trailGapRes) {
leadAlign = trailGapRes && !leadGapRes;
trailAlign = leadGapRes && !trailGapRes;
} else {
noResizing = true;
}
}
} else if (LayoutInterval.wantResize(li)) {
contentRes = true;
} else {
noResizing = true;
}
if (contentRes) {
leadAlign = trailAlign = true;
} else if (noResizing) {
int alignment = li.getAlignment();
leadAlign = (alignment == LEADING);
trailAlign = (alignment == TRAILING);
}
if (leadAlign && isL) {
anyAlignedLeading = true;
}
if (trailAlign && isT) {
anyAlignedTrailing = true;
}
if (contentRes) {
contentResizing = true;
}
}
} else {
contentResizing = LayoutInterval.wantResize(group);
}
}
// 2) Remove gaps where needed (to be substituted, or if just invalid).
boolean defaultLeadingPadding = false;
boolean defaultTrailingPadding = false;
PaddingType leadingPadding = null;
PaddingType trailingPadding = null;
boolean effectiveExplicitGapLeading = false;
boolean effectiveExplicitGapTrailing = false;
boolean resizingGapLeading = false;
boolean resizingGapTrailing = false;
LayoutInterval zeroGapLeading = null;
LayoutInterval zeroGapTrailing = null;
boolean validLeadingGapRemoved = false;
boolean validTrailingGapRemoved = false;
int commonGapLeadingSize = Integer.MIN_VALUE;
int commonGapTrailingSize = Integer.MIN_VALUE;
boolean mayNeedSecondPass = false;
List<LayoutInterval> reduceToZeroGapsLeading = new LinkedList<>();
List<LayoutInterval> reduceToZeroGapsTrailing = new LinkedList<>();
for (int i=0; i < group.getSubIntervalCount(); i++) {
LayoutInterval li = group.getSubInterval(i);
if (!li.isSequential() || li.getSubIntervalCount() == 0) {
continue;
}
LayoutInterval gap = li.getSubInterval(0);
if (gap.isEmptySpace()) {
boolean process = processLeading.contains(li);
if (!isEndingGapUsable(li, dimension, LEADING, process, contentResizing)) {
// default gap that would not work
layoutModel.removeInterval(gap);
gap = null;
}
if (gap != null && process) {
if (isEndingGapEffective(li, dimension, LEADING)) {
if (gap.getPreferredSize() == NOT_EXPLICITLY_DEFINED) {
// default padding to be used as common gap
defaultLeadingPadding = true;
leadingPadding = gap.getPaddingType();
} else {
effectiveExplicitGapLeading = true;
}
if (commonGapLeadingSize == Integer.MIN_VALUE) {
commonGapLeadingSize = (gap.getMinimumSize() != USE_PREFERRED_SIZE)
? gap.getMinimumSize() : gap.getPreferredSize();
}
}
if (gap.getMaximumSize() >= Short.MAX_VALUE) {
if (anyAlignedLeading) {
reduceToZeroGapsLeading.add(gap); // will change to zero gap instead
gap = null;
} else {
if (li.getAlignment() == LEADING) { // need to change alignment as we removed resizing gap
layoutModel.setIntervalAlignment(li, TRAILING);
}
resizingGapLeading = true;
if (gap.getPreferredSize() == 0) {
zeroGapLeading = gap;
}
}
}
if (gap != null) {
layoutModel.removeInterval(gap);
validLeadingGapRemoved = true;
}
}
}
gap = li.getSubInterval(li.getSubIntervalCount() - 1);
if (gap.isEmptySpace()) {
boolean process = processTrailing.contains(li);
if (!isEndingGapUsable(li, dimension, TRAILING, process, contentResizing)) {
// default gap that would not work
layoutModel.removeInterval(gap);
gap = null;
}
if (gap != null && process) {
if (isEndingGapEffective(li, dimension, TRAILING)) {
if (gap.getPreferredSize() == NOT_EXPLICITLY_DEFINED) {
// default padding to be used as common gap
defaultTrailingPadding = true;
trailingPadding = gap.getPaddingType();
} else {
effectiveExplicitGapTrailing = true;
}
if (commonGapTrailingSize == Integer.MIN_VALUE) {
commonGapTrailingSize = (gap.getMinimumSize() != USE_PREFERRED_SIZE)
? gap.getMinimumSize() : gap.getPreferredSize();
}
}
if (gap.getMaximumSize() >= Short.MAX_VALUE) {
if (anyAlignedTrailing) {
reduceToZeroGapsTrailing.add(gap); // will change to zero gap instead
gap = null;
} else {
if (li.getAlignment() == TRAILING) { // need to change alignment as we removed resizing gap
layoutModel.setIntervalAlignment(li, LEADING);
}
resizingGapTrailing = true;
if (gap.getPreferredSize() == 0) {
zeroGapTrailing = gap;
}
}
}
if (gap != null) {
layoutModel.removeInterval(gap);
validTrailingGapRemoved = true;
}
}
}
if (li.getSubIntervalCount() == 1) {
// only one interval remained in sequence - cancel the sequence
layoutModel.removeInterval(group, i); // removes li from group
LayoutInterval sub = layoutModel.removeInterval(li, 0); // removes last interval from li
layoutModel.setIntervalAlignment(sub, li.getRawAlignment());
layoutModel.addInterval(sub, group, i);
if (processLeading.contains(li)) {
processLeading.intervals.remove(li);
processLeading.intervals.add(sub);
}
if (processTrailing.contains(li)) {
processTrailing.intervals.remove(li);
processTrailing.intervals.add(sub);
}
if (sub.isParallel()) {
mayNeedSecondPass = true;
}
}
}
if (!validLeadingGapRemoved && !validTrailingGapRemoved) {
return -1;
}
if ((resizingGapLeading || resizingGapTrailing)
&& (!LayoutInterval.canResize(group) || contentResizing)) {
// removed a resizing gap, but it should be fixed when out of the group
if (!subGroupLeading) {
resizingGapLeading = false;
}
if (!subGroupTrailing) {
resizingGapTrailing = false;
}
if (!contentResizing) { // after removing resizing gaps the group with suppressed resizing has only fixed content
enableGroupResizing(group);
}
}
// 3) Create new L and T gaps to substitute removed gaps.
int[] groupOuterPos = group.getCurrentSpace().positions[dimension];
assert groupOuterPos[LEADING] > Short.MIN_VALUE && groupOuterPos[TRAILING] > Short.MIN_VALUE;
int groupInnerPosLeading = processLeading.count() > 0 ?
LayoutUtils.getPositionWithoutGap(processLeading.intervals, dimension, LEADING) :
groupOuterPos[LEADING];
int groupInnerPosTrailing = processTrailing.count() > 0 ?
LayoutUtils.getPositionWithoutGap(processTrailing.intervals, dimension, TRAILING) :
groupOuterPos[TRAILING];
LayoutInterval leadingGap = null;
LayoutInterval trailingGap = null;
if (validLeadingGapRemoved) {
if (!anyAlignedLeading) { // group is open at leading edge
int size = groupInnerPosLeading - groupOuterPos[LEADING];
if (size > 0 || defaultLeadingPadding) {
leadingGap = new LayoutInterval(SINGLE);
if (defaultLeadingPadding) {
leadingGap.setPaddingType(leadingPadding);
} else if (effectiveExplicitGapLeading) {
leadingGap.setPreferredSize(size);
if (resizingGapLeading) {
if (size < 0 || commonGapLeadingSize < 0 || commonGapLeadingSize <= size) {
leadingGap.setMinimumSize(commonGapLeadingSize);
}
} else if (size != NOT_EXPLICITLY_DEFINED) {
leadingGap.setMinimumSize(USE_PREFERRED_SIZE);
leadingGap.setMaximumSize(USE_PREFERRED_SIZE);
}
}
if (resizingGapLeading) {
leadingGap.setMaximumSize(Short.MAX_VALUE);
}
leadingGap.setAttribute(LayoutInterval.ATTR_FLEX_SIZEDEF);
} else if (size == 0) {
leadingGap = zeroGapLeading;
}
} else {
leadingGap = new LayoutInterval(SINGLE);
leadingGap.setSize(commonGapLeadingSize);
if (commonGapLeadingSize == DEFAULT) {
leadingGap.setPaddingType(leadingPadding);
}
if (!reduceToZeroGapsLeading.isEmpty()) {
int sizeDiff = groupInnerPosLeading - groupOuterPos[LEADING];
for (LayoutInterval gap : reduceToZeroGapsLeading) {
int gapSize = gap.getPreferredSize() - sizeDiff;
if (gapSize < 0) {
gapSize = 0;
}
layoutModel.setIntervalSize(gap, 0, gapSize, Short.MAX_VALUE);
}
}
}
}
if (validTrailingGapRemoved) {
if (!anyAlignedTrailing) { // group is open at trailing edge
int size = groupOuterPos[TRAILING] - groupInnerPosTrailing;
if (size > 0 || defaultTrailingPadding) {
trailingGap = new LayoutInterval(SINGLE);
if (defaultTrailingPadding) {
trailingGap.setPaddingType(trailingPadding);
} else if (effectiveExplicitGapTrailing) {
trailingGap.setPreferredSize(size);
if (resizingGapTrailing) {
if (size < 0 || commonGapTrailingSize < 0 || commonGapTrailingSize <= size) {
trailingGap.setMinimumSize(commonGapTrailingSize);
}
} else if (size != NOT_EXPLICITLY_DEFINED) {
trailingGap.setMinimumSize(USE_PREFERRED_SIZE);
trailingGap.setMaximumSize(USE_PREFERRED_SIZE);
}
}
if (resizingGapTrailing) {
trailingGap.setMaximumSize(Short.MAX_VALUE);
}
trailingGap.setAttribute(LayoutInterval.ATTR_FLEX_SIZEDEF);
} else if (size == 0) {
trailingGap = zeroGapTrailing;
}
} else {
trailingGap = new LayoutInterval(SINGLE);
trailingGap.setSize(commonGapTrailingSize);
if (commonGapTrailingSize == DEFAULT) {
trailingGap.setPaddingType(trailingPadding);
}
if (!reduceToZeroGapsTrailing.isEmpty()) {
int sizeDiff = groupOuterPos[TRAILING] - groupInnerPosTrailing;
for (LayoutInterval gap : reduceToZeroGapsTrailing) {
int gapSize = gap.getPreferredSize() - sizeDiff;
if (gapSize < 0) {
gapSize = 0;
}
layoutModel.setIntervalSize(gap, 0, gapSize, Short.MAX_VALUE);
}
}
}
}
// 4) Place the L/T subst. gaps outside the group, or create sub-group.
if ((leadingGap != null && subGroupLeading)
|| (trailingGap != null && subGroupTrailing)) {
// have a gap to put next to a subgroup (stays inside 'group')
LayoutInterval subGroup = new LayoutInterval(PARALLEL);
subGroup.setGroupAlignment(group.getGroupAlignment());
int commonAlignment = -1;
for (int i=0; i < group.getSubIntervalCount();) {
LayoutInterval li = group.getSubInterval(i);
if ((subGroupLeading && processLeading.contains(li))
|| (subGroupTrailing && processTrailing.contains(li))) {
int align = li.getAlignment();
if (commonAlignment == -1) {
commonAlignment = align;
} else if (align != commonAlignment) {
commonAlignment = LayoutRegion.ALL_POINTS;
}
layoutModel.removeInterval(group, i);
layoutModel.addInterval(li, subGroup, -1);
} else {
i++;
}
}
LayoutInterval seq = new LayoutInterval(SEQUENTIAL);
if (subGroupLeading && leadingGap != null) {
layoutModel.addInterval(leadingGap, seq, -1);
leadingGap = null;
}
seq.add(subGroup, -1);
if (subGroupTrailing && trailingGap != null) {
layoutModel.addInterval(trailingGap, seq, -1);
trailingGap = null;
}
layoutModel.addInterval(seq, group, -1);
if (commonAlignment != LayoutRegion.ALL_POINTS
&& seq.getAlignment() != commonAlignment) {
seq.setAlignment(commonAlignment);
}
int pos[] = subGroup.getCurrentSpace().positions[dimension];
pos[LEADING] = groupInnerPosLeading;
pos[TRAILING] = groupInnerPosTrailing;
pos[CENTER] = (groupInnerPosLeading + groupInnerPosTrailing) / 2;
}
boolean someGapOutsideGroup = (leadingGap != null || trailingGap != null);
boolean originalRootGroup = group.getParent() == null;
if (someGapOutsideGroup && !originalRootGroup) {
if (leadingGap != null) {
groupOuterPos[LEADING] = groupInnerPosLeading;
}
if (trailingGap != null) {
groupOuterPos[TRAILING] = groupInnerPosTrailing;
}
groupOuterPos[CENTER] = (groupOuterPos[LEADING] + groupOuterPos[TRAILING]) / 2;
}
if (leadingGap != null) {
group = insertGap(leadingGap, group, groupInnerPosLeading, dimension, LEADING);
}
if (trailingGap != null) {
group = insertGap(trailingGap, group, groupInnerPosTrailing, dimension, TRAILING);
}
if (someGapOutsideGroup && originalRootGroup && group.getParent() != null) {
group.getCurrentSpace().set(dimension, groupInnerPosLeading, groupInnerPosTrailing);
}
int idx = (someGapOutsideGroup && group.getParent() != null)
? group.getParent().indexOf(group) : -1;
if (mayNeedSecondPass) {
int count = group.getSubIntervalCount();
mergeParallelGroups(group);
if (group.getSubIntervalCount() > count) {
idx = optimizeGaps(group, dimension);
}
}
return idx;
}