1 package net.miginfocom.layout;
4 import java.lang.ref.WeakReference;
10 * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
11 * All rights reserved.
13 * Redistribution and use in source and binary forms, with or without modification,
14 * are permitted provided that the following conditions are met:
15 * Redistributions of source code must retain the above copyright notice, this list
16 * of conditions and the following disclaimer.
17 * Redistributions in binary form must reproduce the above copyright notice, this
18 * list of conditions and the following disclaimer in the documentation and/or other
19 * materials provided with the distribution.
20 * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without specific
22 * prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
30 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
36 * @author Mikael Grev, MiG InfoCom AB
40 /** Holds components in a grid. Does most of the logic behind the layout manager.
42 public final class Grid
44 public static final boolean TEST_GAPS = true;
46 private static final Float[] GROW_100 = new Float[] {ResizeConstraint.WEIGHT_100};
48 private static final DimConstraint DOCK_DIM_CONSTRAINT = new DimConstraint();
50 DOCK_DIM_CONSTRAINT.setGrowPriority(0);
53 /** This is the maximum grid position for "normal" components. Docking components use the space out to
54 * <code>MAX_DOCK_GRID</code> and below 0.
56 private static final int MAX_GRID = 30000;
58 /** Docking components will use the grid coordinates <code>-MAX_DOCK_GRID -> 0</code> and <code>MAX_GRID -> MAX_DOCK_GRID</code>.
60 private static final int MAX_DOCK_GRID = 32767;
62 /** A constraint used for gaps.
64 private static final ResizeConstraint GAP_RC_CONST = new ResizeConstraint(200, ResizeConstraint.WEIGHT_100, 50, null);
65 private static final ResizeConstraint GAP_RC_CONST_PUSH = new ResizeConstraint(200, ResizeConstraint.WEIGHT_100, 50, ResizeConstraint.WEIGHT_100);
67 /** Used for components that doesn't have a CC set. Not that it's really really important that the CC is never changed in this Grid class.
69 private static final CC DEF_CC = new CC();
71 /** The constraints. Never <code>null</code>.
75 /** The parent that is layout out and this grid is done for. Never <code>null</code>.
77 private final ContainerWrapper container;
79 /** An x, y array implemented as a sparse array to accommodate for any grid size without wasting memory (or rather 15 bit (0-MAX_GRID * 0-MAX_GRID).
81 private final LinkedHashMap<Integer, Cell> grid = new LinkedHashMap<Integer, Cell>(); // [(y << 16) + x] -> Cell. null key for absolute positioned compwraps
83 private HashMap<Integer, BoundSize> wrapGapMap = null; // Row or Column index depending in the dimension that "wraps". Normally row indexes but may be column indexes if "flowy". 0 means before first row/col.
85 /** The size of the grid. Row count and column count.
87 private final TreeSet<Integer> rowIndexes = new TreeSet<Integer>(), colIndexes = new TreeSet<Integer>();
89 /** The row and column specifications.
91 private final AC rowConstr, colConstr;
93 /** The in the constructor calculated min/pref/max sizes of the rows and columns.
95 private FlowSizeSpec colFlowSpecs = null, rowFlowSpecs = null;
97 /** Components that are connections in one dimension (such as baseline alignment for instance) are grouped together and stored here.
98 * One for each row/column.
100 private final ArrayList<LinkedDimGroup>[] colGroupLists, rowGroupLists; //[(start)row/col number]
102 /** The in the constructor calculated min/pref/max size of the whole grid.
104 private int[] width = null, height = null;
106 /** If debug is on contains the bounds for things to paint when calling {@link ContainerWrapper#paintDebugCell(int, int, int, int)}
108 private ArrayList<int[]> debugRects = null; // [x, y, width, height]
110 /** If any of the absolute coordinates for component bounds has links the name of the target is in this Set.
111 * Since it requires some memory and computations this is checked at the creation so that
112 * the link information is only created if needed later.
114 * The boolean is true for groups id:s and null for normal id:s.
116 private HashMap<String, Boolean> linkTargetIDs = null;
118 private final int dockOffY, dockOffX;
120 private final Float[] pushXs, pushYs;
122 private final ArrayList<LayoutCallback> callbackList;
125 * @param container The container that will be laid out.
126 * @param lc The form flow constraints.
127 * @param rowConstr The rows specifications. If more cell rows are required, the last element will be used for when there is no corresponding element in this array.
128 * @param colConstr The columns specifications. If more cell rows are required, the last element will be used for when there is no corresponding element in this array.
129 * @param ccMap The map containing the parsed constraints for each child component of <code>parent</code>. Will not be altered. Can have null CC which will use a common
131 * @param callbackList A list of callbacks or <code>null</code> if none. Will not be altered.
133 public Grid(ContainerWrapper container, LC lc, AC rowConstr, AC colConstr, Map<? extends ComponentWrapper, CC> ccMap, ArrayList<LayoutCallback> callbackList)
136 this.rowConstr = rowConstr;
137 this.colConstr = colConstr;
138 this.container = container;
139 this.callbackList = callbackList;
141 int wrap = lc.getWrapAfter() != 0 ? lc.getWrapAfter() : (lc.isFlowX() ? colConstr : rowConstr).getConstaints().length;
142 boolean useVisualPadding = lc.isVisualPadding();
144 final ComponentWrapper[] comps = container.getComponents();
146 boolean hasTagged = false; // So we do not have to sort if it will not do any good
147 boolean hasPushX = false, hasPushY = false;
148 boolean hitEndOfRow = false;
149 final int[] cellXY = new int[2];
150 final ArrayList<int[]> spannedRects = new ArrayList<int[]>(2);
152 final DimConstraint[] specs = (lc.isFlowX() ? rowConstr : colConstr).getConstaints();
154 int sizeGroupsX = 0, sizeGroupsY = 0;
155 int[] dockInsets = null; // top, left, bottom, right insets for docks.
157 LinkHandler.clearTemporaryBounds(container.getLayout());
159 for (int i = 0; i < comps.length;) {
160 ComponentWrapper comp = comps[i];
161 CC rootCc = getCC(comp, ccMap);
165 int hideMode = comp.isVisible() ? -1 : rootCc.getHideMode() != -1 ? rootCc.getHideMode() : lc.getHideMode();
167 if (hideMode == 3) { // To work with situations where there are components that does not have a layout manager, or not this one.
168 setLinkedBounds(comp, rootCc, comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight(), rootCc.isExternal());
170 continue; // The "external" component should not be handled further.
173 if (rootCc.getHorizontal().getSizeGroup() != null)
175 if (rootCc.getVertical().getSizeGroup() != null)
178 // Special treatment of absolute positioned components.
179 if (getPos(comp, rootCc) != null || rootCc.isExternal()) {
181 CompWrap cw = new CompWrap(comp, rootCc, hideMode, useVisualPadding);
182 Cell cell = grid.get(null);
184 grid.put(null, new Cell(cw));
186 cell.compWraps.add(cw);
189 if (!rootCc.isBoundsInGrid() || rootCc.isExternal()) {
190 setLinkedBounds(comp, rootCc, comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight(), rootCc.isExternal());
196 if (rootCc.getDockSide() != -1) {
197 if (dockInsets == null)
198 dockInsets = new int[] {-MAX_DOCK_GRID, -MAX_DOCK_GRID, MAX_DOCK_GRID, MAX_DOCK_GRID};
200 addDockingCell(dockInsets, rootCc.getDockSide(), new CompWrap(comp, rootCc, hideMode, useVisualPadding));
205 Boolean cellFlowX = rootCc.getFlowX();
208 if (rootCc.isNewline()) {
209 wrap(cellXY, rootCc.getNewlineGapSize());
210 } else if (hitEndOfRow) {
215 final boolean isRowInGridMode = !lc.isNoGrid() && !((DimConstraint) LayoutUtil.getIndexSafe(specs, lc.isFlowX() ? cellXY[1] : cellXY[0])).isNoGrid();
217 // Move to a free y, x if no absolute grid specified
218 int cx = rootCc.getCellX();
219 int cy = rootCc.getCellY();
220 if ((cx < 0 || cy < 0) && isRowInGridMode && rootCc.getSkip() == 0) { // 3.7.2: If skip, don't find an empty cell first.
221 while (!isCellFree(cellXY[1], cellXY[0], spannedRects)) {
222 if (Math.abs(increase(cellXY, 1)) >= wrap)
226 if (cx >= 0 && cy >= 0) {
230 } else { // Only one coordinate is specified. Use the current row (flowx) or column (flowy) to fill in.
237 ensureIndexSizes(cx, cy);
239 cell = getCell(cellXY[1], cellXY[0]); // Might be null
242 // Skip a number of cells. Changed for 3.6.1 to take wrap into account and thus "skip" to the next and possibly more rows.
243 for (int s = 0, skipCount = rootCc.getSkip(); s < skipCount; s++) {
245 if (Math.abs(increase(cellXY, 1)) >= wrap)
247 } while (!isCellFree(cellXY[1], cellXY[0], spannedRects));
250 // If cell is not created yet, create it and set it.
252 int spanx = Math.min(!isRowInGridMode && lc.isFlowX() ? LayoutUtil.INF : rootCc.getSpanX(), MAX_GRID - cellXY[0]);
253 int spany = Math.min(!isRowInGridMode && !lc.isFlowX() ? LayoutUtil.INF : rootCc.getSpanY(), MAX_GRID - cellXY[1]);
255 cell = new Cell(spanx, spany, cellFlowX != null ? cellFlowX : lc.isFlowX());
257 setCell(cellXY[1], cellXY[0], cell);
259 // Add a rectangle so we can know that spanned cells occupy more space.
260 if (spanx > 1 || spany > 1)
261 spannedRects.add(new int[] {cellXY[0], cellXY[1], spanx, spany});
264 // Add the one, or all, components that split the grid position to the same Cell.
265 boolean wrapHandled = false;
266 int splitLeft = isRowInGridMode ? rootCc.getSplit() - 1 : LayoutUtil.INF;
267 boolean splitExit = false;
268 final boolean spanRestOfRow = (lc.isFlowX() ? rootCc.getSpanX() : rootCc.getSpanY()) == LayoutUtil.INF;
270 for (; splitLeft >= 0 && i < comps.length; splitLeft--) {
271 ComponentWrapper compAdd = comps[i];
272 CC cc = getCC(compAdd, ccMap);
276 boolean visible = compAdd.isVisible();
277 hideMode = visible ? -1 : cc.getHideMode() != -1 ? cc.getHideMode() : lc.getHideMode();
279 if (cc.isExternal() || hideMode == 3) {
281 splitLeft++; // Added for 3.5.5 so that these components does not "take" a split slot.
282 continue; // To work with situations where there are components that does not have a layout manager, or not this one.
285 hasPushX |= (visible || hideMode > 1) && (cc.getPushX() != null);
286 hasPushY |= (visible || hideMode > 1) && (cc.getPushY() != null);
288 if (cc != rootCc) { // If not first in a cell
289 if (cc.isNewline() || !cc.isBoundsInGrid() || cc.getDockSide() != -1)
292 if (splitLeft > 0 && cc.getSkip() > 0) {
298 CompWrap cw = new CompWrap(compAdd, cc, hideMode, useVisualPadding);
299 cell.compWraps.add(cw);
300 cell.hasTagged |= cc.getTag() != null;
301 hasTagged |= cell.hasTagged;
304 if (cc.getHorizontal().getSizeGroup() != null)
306 if (cc.getVertical().getSizeGroup() != null)
312 if ((cc.isWrap() || (spanRestOfRow && splitLeft == 0))) {
314 wrap(cellXY, cc.getWrapGapSize());
323 if (!wrapHandled && isRowInGridMode) {
324 int span = lc.isFlowX() ? cell.spanx : cell.spany;
325 if (Math.abs((lc.isFlowX() ? cellXY[0] : cellXY[1])) + span >= wrap) {
328 increase(cellXY, splitExit ? span - 1 : span);
333 // If there were size groups, calculate the largest values in the groups (for min/pref/max) and enforce them on the rest in the group.
334 if (sizeGroupsX > 0 || sizeGroupsY > 0) {
335 HashMap<String, int[]> sizeGroupMapX = sizeGroupsX > 0 ? new HashMap<String, int[]>(sizeGroupsX) : null;
336 HashMap<String, int[]> sizeGroupMapY = sizeGroupsY > 0 ? new HashMap<String, int[]>(sizeGroupsY) : null;
337 ArrayList<CompWrap> sizeGroupCWs = new ArrayList<CompWrap>(Math.max(sizeGroupsX, sizeGroupsY));
339 for (Cell cell : grid.values()) {
340 for (int i = 0; i < cell.compWraps.size(); i++) {
341 CompWrap cw = cell.compWraps.get(i);
342 String sgx = cw.cc.getHorizontal().getSizeGroup();
343 String sgy = cw.cc.getVertical().getSizeGroup();
345 if (sgx != null || sgy != null) {
346 if (sgx != null && sizeGroupMapX != null)
347 addToSizeGroup(sizeGroupMapX, sgx, cw.getSizes(true));
348 if (sgy != null && sizeGroupMapY != null)
349 addToSizeGroup(sizeGroupMapY, sgy, cw.getSizes(false));
350 sizeGroupCWs.add(cw);
355 // Set/equalize the sizeGroups to same the values.
356 for (CompWrap cw : sizeGroupCWs) {
357 if (sizeGroupMapX != null)
358 cw.setForcedSizes(sizeGroupMapX.get(cw.cc.getHorizontal().getSizeGroup()), true); // Target method handles null sizes
359 if (sizeGroupMapY != null)
360 cw.setForcedSizes(sizeGroupMapY.get(cw.cc.getVertical().getSizeGroup()), false); // Target method handles null sizes
365 sortCellsByPlatform(grid.values(), container);
367 // Calculate gaps now that the cells are filled and we know all adjacent components.
368 boolean ltr = LayoutUtil.isLeftToRight(lc, container);
369 for (Cell cell : grid.values()) {
370 ArrayList<CompWrap> cws = cell.compWraps;
372 for (int i = 0, lastI = cws.size() - 1; i <= lastI; i++) {
373 CompWrap cw = cws.get(i);
374 ComponentWrapper cwBef = i > 0 ? cws.get(i - 1).comp : null;
375 ComponentWrapper cwAft = i < lastI ? cws.get(i + 1).comp : null;
377 String tag = getCC(cw.comp, ccMap).getTag();
378 CC ccBef = cwBef != null ? getCC(cwBef, ccMap) : null;
379 CC ccAft = cwAft != null ? getCC(cwAft, ccMap) : null;
381 cw.calcGaps(cwBef, ccBef, cwAft, ccAft, tag, cell.flowx, ltr);
385 dockOffX = getDockInsets(colIndexes);
386 dockOffY = getDockInsets(rowIndexes);
388 // Add synthetic indexes for empty rows and columns so they can get a size
389 ensureIndexSizes(colConstr.getCount(), rowConstr.getCount());
391 colGroupLists = divideIntoLinkedGroups(false);
392 rowGroupLists = divideIntoLinkedGroups(true);
394 pushXs = hasPushX || lc.isFillX() ? getDefaultPushWeights(false) : null;
395 pushYs = hasPushY || lc.isFillY() ? getDefaultPushWeights(true) : null;
397 if (LayoutUtil.isDesignTime(container))
398 saveGrid(container, grid);
401 private void ensureIndexSizes(int colCount, int rowCount)
403 for (int i = 0; i < colCount; i++)
405 for (int i = 0; i < rowCount; i++)
409 private static CC getCC(ComponentWrapper comp, Map<? extends ComponentWrapper, CC> ccMap)
411 CC cc = ccMap.get(comp);
412 return cc != null ? cc : DEF_CC;
415 private void addLinkIDs(CC cc)
417 String[] linkIDs = cc.getLinkTargets();
418 for (String linkID : linkIDs) {
419 if (linkTargetIDs == null)
420 linkTargetIDs = new HashMap<String, Boolean>();
421 linkTargetIDs.put(linkID, null);
425 /** If the container (parent) that this grid is laying out has changed its bounds, call this method to
426 * clear any cached values min/pref/max sizes of the components and rows/columns.
428 * If any component can have changed cell the grid needs to be recreated.
430 public void invalidateContainerSize()
433 invalidateComponentSizes();
436 private void invalidateComponentSizes()
438 for (Cell cell : grid.values()) {
439 for (CompWrap compWrap : cell.compWraps)
440 compWrap.invalidateSizes();
445 * @deprecated since 5.0 Last boolean is not needed and is gotten from the new {@link net.miginfocom.layout.ComponentWrapper#getContentBias()} instead;
447 public boolean layout(int[] bounds, UnitValue alignX, UnitValue alignY, boolean debug, boolean notUsed)
449 return layoutImpl(bounds, alignX, alignY, debug, false);
452 /** Does the actual layout. Uses many values calculated in the constructor.
453 * @param bounds The bounds to layout against. Normally that of the parent. [x, y, width, height].
454 * @param alignX The alignment for the x-axis. Can be null.
455 * @param alignY The alignment for the y-axis. Can be null.
456 * @param debug If debug information should be saved in {@link #debugRects}.
457 * @return If the layout has changed the preferred size and there is need for a new layout. This can happen if one or more components
458 * in the grid has a content bias according to {@link net.miginfocom.layout.ComponentWrapper#getContentBias()}.
461 public boolean layout(int[] bounds, UnitValue alignX, UnitValue alignY, boolean debug)
463 return layoutImpl(bounds, alignX, alignY, debug, false);
466 /** Does the actual layout. Uses many values calculated in the constructor.
467 * @param bounds The bounds to layout against. Normally that of the parent. [x, y, width, height].
468 * @param alignX The alignment for the x-axis. Can be null.
469 * @param alignY The alignment for the y-axis. Can be null.
470 * @param debug If debug information should be saved in {@link #debugRects}.
471 * @param trialRun If true the bounds calculated will not be transferred to the components. Only the internal size
472 * of the components will be calculated.
473 * @return If the layout has changed the preferred size and there is need for a new layout. This can happen if one or more components
474 * in the grid has a content bias according to {@link net.miginfocom.layout.ComponentWrapper#getContentBias()}.
477 private boolean layoutImpl(int[] bounds, UnitValue alignX, UnitValue alignY, boolean debug, boolean trialRun)
480 debugRects = new ArrayList<int[]>();
482 if (colFlowSpecs == null)
483 checkSizeCalcs(bounds[2], bounds[3]);
485 resetLinkValues(true, true);
487 layoutInOneDim(bounds[2], alignX, false, pushXs);
488 layoutInOneDim(bounds[3], alignY, true, pushYs);
490 HashMap<String, Integer> endGrpXMap = null, endGrpYMap = null;
491 int compCount = container.getComponentCount();
493 // Transfer the calculated bound from the ComponentWrappers to the actual Components.
494 boolean addVisualPadding = lc.isVisualPadding();
495 boolean layoutAgain = false;
497 for (int j = 0; j < (linkTargetIDs != null ? 2 : 1); j++) { // First do the calculations (maybe more than once) then set the bounds when done
502 for (Cell cell : grid.values()) {
503 for (CompWrap cw : cell.compWraps) {
505 doAgain |= doAbsoluteCorrections(cw, bounds);
506 if (!doAgain) { // If we are going to do this again, do not bother this time around
507 if (cw.cc.getHorizontal().getEndGroup() != null)
508 endGrpXMap = addToEndGroup(endGrpXMap, cw.cc.getHorizontal().getEndGroup(), cw.x + cw.w);
510 if (cw.cc.getVertical().getEndGroup() != null)
511 endGrpYMap = addToEndGroup(endGrpYMap, cw.cc.getVertical().getEndGroup(), cw.y + cw.h);
514 // @since 3.7.2 Needed or absolute "pos" pointing to "visual" or "container" didn't work if
515 // their bounds changed during the layout cycle. At least not in SWT.
516 if (linkTargetIDs != null && (linkTargetIDs.containsKey("visual") || linkTargetIDs.containsKey("container"))) {
521 if (linkTargetIDs == null || j == 1) {
522 if (cw.cc.getHorizontal().getEndGroup() != null)
523 cw.w = endGrpXMap.get(cw.cc.getHorizontal().getEndGroup()) - cw.x;
525 if (cw.cc.getVertical().getEndGroup() != null)
526 cw.h = endGrpYMap.get(cw.cc.getVertical().getEndGroup()) - cw.y;
532 cw.transferBounds(addVisualPadding);
534 if (callbackList != null) {
535 for (LayoutCallback callback : callbackList)
536 callback.correctBounds(cw.comp);
541 clearGroupLinkBounds();
542 if (++count > ((compCount << 3) + 10)) {
543 System.err.println("Unstable cyclic dependency in absolute linked values.");
551 // Add debug shapes for the "cells". Use the CompWraps as base for inding the cells.
553 for (Cell cell : grid.values()) {
554 ArrayList<CompWrap> compWraps = cell.compWraps;
555 for (CompWrap cw : compWraps) {
556 LinkedDimGroup hGrp = getGroupContaining(colGroupLists, cw);
557 LinkedDimGroup vGrp = getGroupContaining(rowGroupLists, cw);
559 if (hGrp != null && vGrp != null)
560 debugRects.add(new int[]{hGrp.lStart + bounds[0] - (hGrp.fromEnd ? hGrp.lSize : 0), vGrp.lStart + bounds[1] - (vGrp.fromEnd ? vGrp.lSize : 0), hGrp.lSize, vGrp.lSize});
567 public void paintDebug()
569 if (debugRects != null) {
570 container.paintDebugOutline(lc.isVisualPadding());
572 ArrayList<int[]> painted = new ArrayList<int[]>();
573 for (int[] r : debugRects) {
574 if (!painted.contains(r)) {
575 container.paintDebugCell(r[0], r[1], r[2], r[3]);
580 for (Cell cell : grid.values()) {
581 ArrayList<CompWrap> compWraps = cell.compWraps;
582 for (CompWrap compWrap : compWraps)
583 compWrap.comp.paintDebugOutline(lc.isVisualPadding());
588 public ContainerWrapper getContainer()
593 public final int[] getWidth()
595 return getWidth(lastRefHeight);
598 public final int[] getWidth(int refHeight)
600 checkSizeCalcs(lastRefWidth, refHeight);
601 return width.clone();
604 public final int[] getHeight()
606 return getHeight(lastRefWidth);
609 public final int[] getHeight(int refWidth)
611 checkSizeCalcs(refWidth, lastRefHeight);
612 return height.clone();
615 private int lastRefWidth = 0, lastRefHeight = 0;
617 private void checkSizeCalcs(int refWidth, int refHeight)
619 if (colFlowSpecs == null)
620 calcGridSizes(refWidth, refHeight);
622 if ((refWidth > 0 && refWidth != lastRefWidth) || (refHeight > 0 && refHeight != lastRefHeight)) {
623 int[] refBounds = new int[] {0, 0, (refWidth > 0 ? refWidth : width[LayoutUtil.PREF]), (refHeight > 0 ? refHeight : height[LayoutUtil.PREF])};
624 layoutImpl(refBounds, null, null, false, true);
625 calcGridSizes(refWidth, refHeight);
628 lastRefWidth = refWidth;
629 lastRefHeight = refHeight;
632 private void calcGridSizes(int refWidth, int refHeight)
634 // Note, in these calls the grid can be invalidated and specs set to null. Therefore use local versions.
635 FlowSizeSpec colSpecs = calcRowsOrColsSizes(true, refWidth);
636 FlowSizeSpec rowSpecs = calcRowsOrColsSizes(false, refHeight);
638 colFlowSpecs = colSpecs;
639 rowFlowSpecs = rowSpecs;
641 width = getMinPrefMaxSumSize(true, colSpecs.sizes);
642 height = getMinPrefMaxSumSize(false, rowSpecs.sizes);
644 if (linkTargetIDs == null) {
645 resetLinkValues(false, true);
647 // This call makes some components flicker on SWT. They get their bounds changed twice since
648 // the change might affect the absolute size adjustment below. There's no way around this that
650 layout(new int[]{0, 0, refWidth, refHeight}, null, null, false);
651 resetLinkValues(false, false);
654 adjustSizeForAbsolute(true);
655 adjustSizeForAbsolute(false);
658 private UnitValue[] getPos(ComponentWrapper cw, CC cc)
660 UnitValue[] callbackPos = null;
661 if (callbackList != null) {
662 for (int i = 0; i < callbackList.size() && callbackPos == null; i++)
663 callbackPos = callbackList.get(i).getPosition(cw); // NOT a copy!
666 // If one is null, return the other (which many also be null)
667 UnitValue[] ccPos = cc.getPos(); // A copy!!
668 if (callbackPos == null || ccPos == null)
669 return callbackPos != null ? callbackPos : ccPos;
672 for (int i = 0; i < 4; i++) {
673 UnitValue cbUv = callbackPos[i];
681 private BoundSize[] getCallbackSize(ComponentWrapper cw)
683 if (callbackList != null) {
684 for (LayoutCallback callback : callbackList) {
685 BoundSize[] bs = callback.getSize(cw); // NOT a copy!
693 private static int getDockInsets(TreeSet<Integer> set)
696 for (Integer i : set) {
700 break; // Since they are sorted we can break
707 * @param cw Never <code>null</code>.
708 * @param cc Never <code>null</code>.
709 * @param external The bounds should be stored even if they are not in {@link #linkTargetIDs}.
710 * @return If a change has been made.
712 private boolean setLinkedBounds(ComponentWrapper cw, CC cc, int x, int y, int w, int h, boolean external)
714 String id = cc.getId() != null ? cc.getId() : cw.getLinkId();
719 int grIx = id.indexOf('.');
721 gid = id.substring(0, grIx);
722 id = id.substring(grIx + 1);
725 Object lay = container.getLayout();
726 boolean changed = false;
727 if (external || (linkTargetIDs != null && linkTargetIDs.containsKey(id)))
728 changed = LinkHandler.setBounds(lay, id, x, y, w, h, !external, false);
730 if (gid != null && (external || (linkTargetIDs != null && linkTargetIDs.containsKey(gid)))) {
731 if (linkTargetIDs == null)
732 linkTargetIDs = new HashMap<String, Boolean>(4);
734 linkTargetIDs.put(gid, Boolean.TRUE);
735 changed |= LinkHandler.setBounds(lay, gid, x, y, w, h, !external, true);
742 * @param p The point to increase
743 * @param cnt How many cells to advance.
744 * @return The new value in the "increasing" dimension.
746 private int increase(int[] p, int cnt)
748 return lc.isFlowX() ? (p[0] += cnt) : (p[1] += cnt);
751 /** Wraps to the next row or column depending on if horizontal flow or vertical flow is used.
752 * @param cellXY The point to wrap and thus set either x or y to 0 and increase the other one.
753 * @param gapSize The gaps size specified in a "wrap XXX" or "newline XXX" or <code>null</code> if none.
755 private void wrap(int[] cellXY, BoundSize gapSize)
757 boolean flowx = lc.isFlowX();
758 cellXY[0] = flowx ? 0 : cellXY[0] + 1;
759 cellXY[1] = flowx ? cellXY[1] + 1 : 0;
761 if (gapSize != null) {
762 if (wrapGapMap == null)
763 wrapGapMap = new HashMap<Integer, BoundSize>(8);
765 wrapGapMap.put(cellXY[flowx ? 1 : 0], gapSize);
768 // add the row/column so that the gap in the last row/col will not be removed.
770 rowIndexes.add(cellXY[1]);
772 colIndexes.add(cellXY[0]);
776 /** Sort components (normally buttons in a button bar) so they appear in the correct order.
777 * @param cells The cells to sort.
778 * @param parent The parent.
780 private static void sortCellsByPlatform(Collection<Cell> cells, ContainerWrapper parent)
782 String order = PlatformDefaults.getButtonOrder();
783 String orderLo = order.toLowerCase();
785 int unrelSize = PlatformDefaults.convertToPixels(1, "u", true, 0, parent, null);
787 if (unrelSize == UnitConverter.UNABLE)
788 throw new IllegalArgumentException("'unrelated' not recognized by PlatformDefaults!");
790 int[] gapUnrel = new int[] {unrelSize, unrelSize, LayoutUtil.NOT_SET};
791 int[] flGap = new int[] {0, 0, LayoutUtil.NOT_SET};
793 for (Cell cell : cells) {
797 CompWrap prevCW = null;
798 boolean nextUnrel = false;
799 boolean nextPush = false;
800 ArrayList<CompWrap> sortedList = new ArrayList<CompWrap>(cell.compWraps.size());
802 for (int i = 0, iSz = orderLo.length(); i < iSz; i++) {
803 char c = orderLo.charAt(i);
804 if (c == '+' || c == '_') {
809 String tag = PlatformDefaults.getTagForChar(c);
811 for (int j = 0, jSz = cell.compWraps.size(); j < jSz; j++) {
812 CompWrap cw = cell.compWraps.get(j);
813 if (tag.equals(cw.cc.getTag())) {
814 if (Character.isUpperCase(order.charAt(i)))
815 cw.adjustMinHorSizeUp((int) PlatformDefaults.getMinimumButtonWidthIncludingPadding(0, parent, cw.comp));
820 (prevCW != null ? prevCW : cw).mergeGapSizes(gapUnrel, cell.flowx, prevCW == null);
822 cw.forcedPushGaps = 1;
828 // "unknown" components will always get an Unrelated gap.
838 // If we have a gap that was supposed to push but no more components was found to but the "gap before" then compensate.
839 if (sortedList.size() > 0) {
840 CompWrap cw = sortedList.get(sortedList.size() - 1);
842 cw.mergeGapSizes(gapUnrel, cell.flowx, false);
844 cw.forcedPushGaps |= 2;
847 // Remove first and last gap if not set explicitly.
848 if (cw.cc.getHorizontal().getGapAfter() == null)
849 cw.setGaps(flGap, 3);
851 cw = sortedList.get(0);
852 if (cw.cc.getHorizontal().getGapBefore() == null)
853 cw.setGaps(flGap, 1);
856 // Exchange the unsorted CompWraps for the sorted one.
857 if (cell.compWraps.size() == sortedList.size()) {
858 cell.compWraps.clear();
860 cell.compWraps.removeAll(sortedList);
862 cell.compWraps.addAll(sortedList);
866 private Float[] getDefaultPushWeights(boolean isRows)
868 ArrayList<LinkedDimGroup>[] groupLists = isRows ? rowGroupLists : colGroupLists;
870 Float[] pushWeightArr = GROW_100; // Only create specific if any of the components have grow.
871 for (int i = 0, ix = 1; i < groupLists.length; i++, ix += 2) {
872 ArrayList<LinkedDimGroup> grps = groupLists[i];
873 Float rowPushWeight = null;
874 for (LinkedDimGroup grp : grps) {
875 for (int c = 0; c < grp._compWraps.size(); c++) {
876 CompWrap cw = grp._compWraps.get(c);
877 int hideMode = cw.comp.isVisible() ? -1 : cw.cc.getHideMode() != -1 ? cw.cc.getHideMode() : lc.getHideMode();
879 Float pushWeight = hideMode < 2 ? (isRows ? cw.cc.getPushY() : cw.cc.getPushX()) : null;
880 if (rowPushWeight == null || (pushWeight != null && pushWeight > rowPushWeight))
881 rowPushWeight = pushWeight;
885 if (rowPushWeight != null) {
886 if (pushWeightArr == GROW_100)
887 pushWeightArr = new Float[(groupLists.length << 1) + 1];
888 pushWeightArr[ix] = rowPushWeight;
892 return pushWeightArr;
895 private void clearGroupLinkBounds()
897 if (linkTargetIDs == null)
900 for (Map.Entry<String, Boolean> o : linkTargetIDs.entrySet()) {
901 if (o.getValue() == Boolean.TRUE)
902 LinkHandler.clearBounds(container.getLayout(), o.getKey());
906 private void resetLinkValues(boolean parentSize, boolean compLinks)
908 Object lay = container.getLayout();
910 LinkHandler.clearTemporaryBounds(lay);
912 boolean defIns = !hasDocks();
914 int parW = parentSize ? lc.getWidth().constrain(container.getWidth(), getParentSize(container, true), container) : 0;
915 int parH = parentSize ? lc.getHeight().constrain(container.getHeight(), getParentSize(container, false), container) : 0;
917 int insX = LayoutUtil.getInsets(lc, 0, defIns).getPixels(0, container, null);
918 int insY = LayoutUtil.getInsets(lc, 1, defIns).getPixels(0, container, null);
919 int visW = parW - insX - LayoutUtil.getInsets(lc, 2, defIns).getPixels(0, container, null);
920 int visH = parH - insY - LayoutUtil.getInsets(lc, 3, defIns).getPixels(0, container, null);
922 LinkHandler.setBounds(lay, "visual", insX, insY, visW, visH, true, false);
923 LinkHandler.setBounds(lay, "container", 0, 0, parW, parH, true, false);
926 /** Returns the {@link net.miginfocom.layout.Grid.LinkedDimGroup} that has the {@link net.miginfocom.layout.Grid.CompWrap}
928 * @param groupLists The lists to search in.
929 * @param cw The component wrap to find.
930 * @return The linked group or <code>null</code> if none had the component wrap.
932 private static LinkedDimGroup getGroupContaining(ArrayList<LinkedDimGroup>[] groupLists, CompWrap cw)
934 for (ArrayList<LinkedDimGroup> groups : groupLists) {
935 for (LinkedDimGroup group : groups) {
936 ArrayList<CompWrap> cwList = group._compWraps;
937 for (CompWrap aCwList : cwList) {
946 private boolean doAbsoluteCorrections(CompWrap cw, int[] bounds)
948 boolean changed = false;
950 int[] stSz = getAbsoluteDimBounds(cw, bounds[2], true);
952 cw.setDimBounds(stSz[0], stSz[1], true);
954 stSz = getAbsoluteDimBounds(cw, bounds[3], false);
956 cw.setDimBounds(stSz[0], stSz[1], false);
958 // If there is a link id, store the new bounds.
959 if (linkTargetIDs != null)
960 changed = setLinkedBounds(cw.comp, cw.cc, cw.x, cw.y, cw.w, cw.h, false);
965 /** Adjust grid's width or height for the absolute components' positions.
967 private void adjustSizeForAbsolute(boolean isHor)
969 int[] curSizes = isHor ? width : height;
971 Cell absCell = grid.get(null);
972 if (absCell == null || absCell.compWraps.size() == 0)
975 ArrayList<CompWrap> cws = absCell.compWraps;
978 for (int j = 0, cwSz = absCell.compWraps.size(); j < cwSz + 3; j++) { // "Do Again" max absCell.compWraps.size() + 3 times.
979 boolean doAgain = false;
980 for (int i = 0; i < cwSz; i++) {
981 CompWrap cw = cws.get(i);
982 int[] stSz = getAbsoluteDimBounds(cw, 0, isHor);
983 int end = stSz[0] + stSz[1];
987 // If there is a link id, store the new bounds.
988 if (linkTargetIDs != null)
989 doAgain |= setLinkedBounds(cw.comp, cw.cc, stSz[0], stSz[0], stSz[1], stSz[1], false);
994 // We need to check this again since the coords may be smaller this round.
996 clearGroupLinkBounds();
999 maxEnd += LayoutUtil.getInsets(lc, isHor ? 3 : 2, !hasDocks()).getPixels(0, container, null);
1001 if (curSizes[LayoutUtil.MIN] < maxEnd)
1002 curSizes[LayoutUtil.MIN] = maxEnd;
1003 if (curSizes[LayoutUtil.PREF] < maxEnd)
1004 curSizes[LayoutUtil.PREF] = maxEnd;
1007 private int[] getAbsoluteDimBounds(CompWrap cw, int refSize, boolean isHor)
1009 if (cw.cc.isExternal()) {
1011 return new int[] {cw.comp.getX(), cw.comp.getWidth()};
1013 return new int[] {cw.comp.getY(), cw.comp.getHeight()};
1017 UnitValue[] pad = cw.cc.getPadding();
1019 // If no changes do not create a lot of objects
1020 UnitValue[] pos = getPos(cw.comp, cw.cc);
1021 if (pos == null && pad == null)
1025 int st = isHor ? cw.x : cw.y;
1026 int sz = isHor ? cw.w : cw.h;
1028 // If absolute, use those coordinates instead.
1030 UnitValue stUV = pos[isHor ? 0 : 1];
1031 UnitValue endUV = pos[isHor ? 2 : 3];
1033 int minSz = cw.getSize(LayoutUtil.MIN, isHor);
1034 int maxSz = cw.getSize(LayoutUtil.MAX, isHor);
1035 sz = Math.min(Math.max(cw.getSize(LayoutUtil.PREF, isHor), minSz), maxSz);
1038 st = stUV.getPixels(stUV.getUnit() == UnitValue.ALIGN ? sz : refSize, container, cw.comp);
1040 if (endUV != null) // if (endUV == null && cw.cc.isBoundsIsGrid() == true)
1041 sz = Math.min(Math.max((isHor ? (cw.x + cw.w) : (cw.y + cw.h)) - st, minSz), maxSz);
1044 if (endUV != null) {
1045 if (stUV != null) { // if (stUV != null || cw.cc.isBoundsIsGrid()) {
1046 sz = Math.min(Math.max(endUV.getPixels(refSize, container, cw.comp) - st, minSz), maxSz);
1048 st = endUV.getPixels(refSize, container, cw.comp) - sz;
1053 // If constraint has padding -> correct the start/size
1055 UnitValue uv = pad[isHor ? 1 : 0];
1056 int p = uv != null ? uv.getPixels(refSize, container, cw.comp) : 0;
1058 uv = pad[isHor ? 3 : 2];
1059 sz += -p + (uv != null ? uv.getPixels(refSize, container, cw.comp) : 0);
1062 return new int[] {st, sz};
1065 private void layoutInOneDim(int refSize, UnitValue align, boolean isRows, Float[] defaultPushWeights)
1067 boolean fromEnd = !(isRows ? lc.isTopToBottom() : LayoutUtil.isLeftToRight(lc, container));
1068 DimConstraint[] primDCs = (isRows ? rowConstr : colConstr).getConstaints();
1069 FlowSizeSpec fss = isRows ? rowFlowSpecs : colFlowSpecs;
1070 ArrayList<LinkedDimGroup>[] rowCols = isRows ? rowGroupLists : colGroupLists;
1072 int[] rowColSizes = LayoutUtil.calculateSerial(fss.sizes, fss.resConstsInclGaps, defaultPushWeights, LayoutUtil.PREF, refSize);
1074 if (LayoutUtil.isDesignTime(container)) {
1075 TreeSet<Integer> indexes = isRows ? rowIndexes : colIndexes;
1076 int[] ixArr = new int[indexes.size()];
1078 for (Integer i : indexes)
1081 putSizesAndIndexes(container.getComponent(), rowColSizes, ixArr, isRows);
1084 int curPos = align != null ? align.getPixels(refSize - LayoutUtil.sum(rowColSizes), container, null) : 0;
1087 curPos = refSize - curPos;
1089 for (int i = 0 ; i < rowCols.length; i++) {
1090 ArrayList<LinkedDimGroup> linkedGroups = rowCols[i];
1091 int scIx = i - (isRows ? dockOffY : dockOffX);
1096 curPos += (fromEnd ? -rowColSizes[bIx] : rowColSizes[bIx]);
1098 DimConstraint primDC = scIx >= 0 ? primDCs[scIx >= primDCs.length ? primDCs.length - 1 : scIx] : DOCK_DIM_CONSTRAINT;
1100 int rowSize = rowColSizes[bIx2];
1102 for (LinkedDimGroup group : linkedGroups) {
1103 int groupSize = rowSize;
1105 groupSize = LayoutUtil.sum(rowColSizes, bIx2, Math.min((group.span << 1) - 1, rowColSizes.length - bIx2 - 1));
1107 group.layout(primDC, curPos, groupSize, group.span);
1110 curPos += (fromEnd ? -rowSize : rowSize);
1114 private static void addToSizeGroup(HashMap<String, int[]> sizeGroups, String sizeGroup, int[] size)
1116 int[] sgSize = sizeGroups.get(sizeGroup);
1117 if (sgSize == null) {
1118 sizeGroups.put(sizeGroup, new int[] {size[LayoutUtil.MIN], size[LayoutUtil.PREF], size[LayoutUtil.MAX]});
1120 sgSize[LayoutUtil.MIN] = Math.max(size[LayoutUtil.MIN], sgSize[LayoutUtil.MIN]);
1121 sgSize[LayoutUtil.PREF] = Math.max(size[LayoutUtil.PREF], sgSize[LayoutUtil.PREF]);
1122 sgSize[LayoutUtil.MAX] = Math.min(size[LayoutUtil.MAX], sgSize[LayoutUtil.MAX]);
1126 private static HashMap<String, Integer> addToEndGroup(HashMap<String, Integer> endGroups, String endGroup, int end)
1128 if (endGroup != null) {
1129 if (endGroups == null)
1130 endGroups = new HashMap<String, Integer>(4);
1132 Integer oldEnd = endGroups.get(endGroup);
1133 if (oldEnd == null || end > oldEnd)
1134 endGroups.put(endGroup, end);
1139 /** Calculates Min, Preferred and Max size for the columns OR rows.
1140 * @param isHor If it is the horizontal dimension to calculate.
1141 * @param containerSize The reference container size in the dimension. If <= 0 it will be replaced by the actual container's size.
1142 * @return The sizes in a {@link net.miginfocom.layout.Grid.FlowSizeSpec}.
1144 private FlowSizeSpec calcRowsOrColsSizes(boolean isHor, int containerSize)
1146 ArrayList<LinkedDimGroup>[] groupsLists = isHor ? colGroupLists : rowGroupLists;
1147 Float[] defPush = isHor ? pushXs : pushYs;
1149 if (containerSize <= 0)
1150 containerSize = isHor ? container.getWidth() : container.getHeight();
1152 BoundSize cSz = isHor ? lc.getWidth() : lc.getHeight();
1154 containerSize = cSz.constrain(containerSize, getParentSize(container, isHor), container);
1156 DimConstraint[] primDCs = (isHor? colConstr : rowConstr).getConstaints();
1157 TreeSet<Integer> primIndexes = isHor ? colIndexes : rowIndexes;
1159 int[][] rowColBoundSizes = new int[primIndexes.size()][];
1160 HashMap<String, int[]> sizeGroupMap = new HashMap<String, int[]>(4);
1161 DimConstraint[] allDCs = new DimConstraint[primIndexes.size()];
1163 Iterator<Integer> primIt = primIndexes.iterator();
1164 for (int r = 0; r < rowColBoundSizes.length; r++) {
1165 int cellIx = primIt.next();
1166 int[] rowColSizes = new int[3];
1168 if (cellIx >= -MAX_GRID && cellIx <= MAX_GRID) { // If not dock cell
1169 allDCs[r] = primDCs[cellIx >= primDCs.length ? primDCs.length - 1 : cellIx];
1171 allDCs[r] = DOCK_DIM_CONSTRAINT;
1174 ArrayList<LinkedDimGroup> groups = groupsLists[r];
1176 int[] groupSizes = new int[] {
1177 getTotalGroupsSizeParallel(groups, LayoutUtil.MIN, false),
1178 getTotalGroupsSizeParallel(groups, LayoutUtil.PREF, false),
1181 correctMinMax(groupSizes);
1182 BoundSize dimSize = allDCs[r].getSize();
1184 for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.MAX; sType++) {
1186 int rowColSize = groupSizes[sType];
1188 UnitValue uv = dimSize.getSize(sType);
1190 // If the size of the column is a link to some other size, use that instead
1191 int unit = uv.getUnit();
1192 if (unit == UnitValue.PREF_SIZE) {
1193 rowColSize = groupSizes[LayoutUtil.PREF];
1194 } else if (unit == UnitValue.MIN_SIZE) {
1195 rowColSize = groupSizes[LayoutUtil.MIN];
1196 } else if (unit == UnitValue.MAX_SIZE) {
1197 rowColSize = groupSizes[LayoutUtil.MAX];
1199 rowColSize = uv.getPixels(containerSize, container, null);
1201 } else if (cellIx >= -MAX_GRID && cellIx <= MAX_GRID && rowColSize == 0) {
1202 rowColSize = LayoutUtil.isDesignTime(container) ? LayoutUtil.getDesignTimeEmptySize() : 0; // Empty rows with no size set gets XX pixels if design time
1205 rowColSizes[sType] = rowColSize;
1208 correctMinMax(rowColSizes);
1209 addToSizeGroup(sizeGroupMap, allDCs[r].getSizeGroup(), rowColSizes);
1211 rowColBoundSizes[r] = rowColSizes;
1214 // Set/equalize the size groups to same the values.
1215 if (sizeGroupMap.size() > 0) {
1216 for (int r = 0; r < rowColBoundSizes.length; r++) {
1217 if (allDCs[r].getSizeGroup() != null)
1218 rowColBoundSizes[r] = sizeGroupMap.get(allDCs[r].getSizeGroup());
1223 ResizeConstraint[] resConstrs = getRowResizeConstraints(allDCs);
1225 boolean[] fillInPushGaps = new boolean[allDCs.length + 1];
1226 int[][] gapSizes = getRowGaps(allDCs, containerSize, isHor, fillInPushGaps);
1228 FlowSizeSpec fss = mergeSizesGapsAndResConstrs(resConstrs, fillInPushGaps, rowColBoundSizes, gapSizes);
1230 // Spanning components are not handled yet. Check and adjust the multi-row min/pref they enforce.
1231 adjustMinPrefForSpanningComps(allDCs, defPush, fss, groupsLists);
1236 private static int getParentSize(ComponentWrapper cw, boolean isHor)
1238 ContainerWrapper p = cw.getParent();
1239 return p != null ? (isHor ? cw.getWidth() : cw.getHeight()) : 0;
1242 private int[] getMinPrefMaxSumSize(boolean isHor, int[][] sizes)
1244 int[] retSizes = new int[3];
1246 BoundSize sz = isHor ? lc.getWidth() : lc.getHeight();
1248 for (int i = 0; i < sizes.length; i++) {
1249 if (sizes[i] != null) {
1250 int[] size = sizes[i];
1251 for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.MAX; sType++) {
1252 if (sz.getSize(sType) != null) {
1254 retSizes[sType] = sz.getSize(sType).getPixels(getParentSize(container, isHor), container, null);
1256 int s = size[sType];
1258 if (s != LayoutUtil.NOT_SET) {
1259 if (sType == LayoutUtil.PREF) {
1260 int bnd = size[LayoutUtil.MAX];
1261 if (bnd != LayoutUtil.NOT_SET && bnd < s)
1264 bnd = size[LayoutUtil.MIN];
1265 if (bnd > s) // Includes s == LayoutUtil.NOT_SET since < 0.
1269 retSizes[sType] += s; // MAX compensated below.
1272 // So that MAX is always correct.
1273 if (size[LayoutUtil.MAX] == LayoutUtil.NOT_SET || retSizes[LayoutUtil.MAX] > LayoutUtil.INF)
1274 retSizes[LayoutUtil.MAX] = LayoutUtil.INF;
1280 correctMinMax(retSizes);
1285 private static ResizeConstraint[] getRowResizeConstraints(DimConstraint[] specs)
1287 ResizeConstraint[] resConsts = new ResizeConstraint[specs.length];
1288 for (int i = 0; i < resConsts.length; i++)
1289 resConsts[i] = specs[i].resize;
1293 private static ResizeConstraint[] getComponentResizeConstraints(ArrayList<CompWrap> compWraps, boolean isHor)
1295 ResizeConstraint[] resConsts = new ResizeConstraint[compWraps.size()];
1296 for (int i = 0; i < resConsts.length; i++) {
1297 CC fc = compWraps.get(i).cc;
1298 resConsts[i] = fc.getDimConstraint(isHor).resize;
1300 // Always grow docking components in the correct dimension.
1301 int dock = fc.getDockSide();
1302 if (isHor ? (dock == 0 || dock == 2) : (dock == 1 || dock == 3)) {
1303 ResizeConstraint dc = resConsts[i];
1304 resConsts[i] = new ResizeConstraint(dc.shrinkPrio, dc.shrink, dc.growPrio, ResizeConstraint.WEIGHT_100);
1310 private static boolean[] getComponentGapPush(ArrayList<CompWrap> compWraps, boolean isHor)
1312 // Make one element bigger and or the after gap with the next before gap.
1313 boolean[] barr = new boolean[compWraps.size() + 1];
1314 for (int i = 0; i < barr.length; i++) {
1316 boolean push = i > 0 && compWraps.get(i - 1).isPushGap(isHor, false);
1318 if (!push && i < (barr.length - 1))
1319 push = compWraps.get(i).isPushGap(isHor, true);
1326 /** Returns the row gaps in pixel sizes. One more than there are <code>specs</code> sent in.
1330 * @param fillInPushGaps If the gaps are pushing. <b>NOTE!</b> this argument will be filled in and thus changed!
1331 * @return The row gaps in pixel sizes. One more than there are <code>specs</code> sent in.
1333 private int[][] getRowGaps(DimConstraint[] specs, int refSize, boolean isHor, boolean[] fillInPushGaps)
1335 BoundSize defGap = isHor ? lc.getGridGapX() : lc.getGridGapY();
1337 defGap = isHor ? PlatformDefaults.getGridGapX() : PlatformDefaults.getGridGapY();
1338 int[] defGapArr = defGap.getPixelSizes(refSize, container, null);
1340 boolean defIns = !hasDocks();
1342 UnitValue firstGap = LayoutUtil.getInsets(lc, isHor ? 1 : 0, defIns);
1343 UnitValue lastGap = LayoutUtil.getInsets(lc, isHor ? 3 : 2, defIns);
1345 int[][] retValues = new int[specs.length + 1][];
1347 for (int i = 0, wgIx = 0; i < retValues.length; i++) {
1348 DimConstraint specBefore = i > 0 ? specs[i - 1] : null;
1349 DimConstraint specAfter = i < specs.length ? specs[i] : null;
1351 // No gap if between docking components.
1352 boolean edgeBefore = (specBefore == DOCK_DIM_CONSTRAINT || specBefore == null);
1353 boolean edgeAfter = (specAfter == DOCK_DIM_CONSTRAINT || specAfter == null);
1354 if (edgeBefore && edgeAfter)
1357 BoundSize wrapGapSize = (wrapGapMap == null || isHor == lc.isFlowX() ? null : wrapGapMap.get(wgIx++));
1359 if (wrapGapSize == null) {
1361 int[] gapBefore = specBefore != null ? specBefore.getRowGaps(container, null, refSize, false) : null;
1362 int[] gapAfter = specAfter != null ? specAfter.getRowGaps(container, null, refSize, true) : null;
1364 if (edgeBefore && gapAfter == null && firstGap != null) {
1366 int bef = firstGap.getPixels(refSize, container, null);
1367 retValues[i] = new int[] {bef, bef, bef};
1369 } else if (edgeAfter && gapBefore == null && firstGap != null) {
1371 int aft = lastGap.getPixels(refSize, container, null);
1372 retValues[i] = new int[] {aft, aft, aft};
1375 retValues[i] = gapAfter != gapBefore ? mergeSizes(gapAfter, gapBefore) : new int[] {defGapArr[0], defGapArr[1], defGapArr[2]};
1378 if (specBefore != null && specBefore.isGapAfterPush() || specAfter != null && specAfter.isGapBeforePush())
1379 fillInPushGaps[i] = true;
1382 if (wrapGapSize.isUnset()) {
1383 retValues[i] = new int[] {defGapArr[0], defGapArr[1], defGapArr[2]};
1385 retValues[i] = wrapGapSize.getPixelSizes(refSize, container, null);
1387 fillInPushGaps[i] = wrapGapSize.getGapPush();
1393 private static int[][] getGaps(ArrayList<CompWrap> compWraps, boolean isHor)
1395 int compCount = compWraps.size();
1396 int[][] retValues = new int[compCount + 1][];
1398 retValues[0] = compWraps.get(0).getGaps(isHor, true);
1399 for (int i = 0; i < compCount; i++) {
1400 int[] gap1 = compWraps.get(i).getGaps(isHor, false);
1401 int[] gap2 = i < compCount - 1 ? compWraps.get(i + 1).getGaps(isHor, true) : null;
1403 retValues[i + 1] = mergeSizes(gap1, gap2);
1409 private boolean hasDocks()
1411 return (dockOffX > 0 || dockOffY > 0 || rowIndexes.last() > MAX_GRID || colIndexes.last() > MAX_GRID);
1414 /** Adjust min/pref size for columns(or rows) that has components that spans multiple columns (or rows).
1415 * @param specs The specs for the columns or rows. Last index will be used if <code>count</code> is greater than this array's length.
1416 * @param defPush The default grow weight if the specs does not have anyone that will grow. Comes from "push" in the CC.
1418 * @param groupsLists
1420 private void adjustMinPrefForSpanningComps(DimConstraint[] specs, Float[] defPush, FlowSizeSpec fss, ArrayList<LinkedDimGroup>[] groupsLists)
1422 for (int r = groupsLists.length - 1; r >= 0; r--) { // Since 3.7.3 Iterate from end to start. Will solve some multiple spanning components hard to solve problems.
1423 ArrayList<LinkedDimGroup> groups = groupsLists[r];
1425 for (LinkedDimGroup group : groups) {
1426 if (group.span == 1)
1429 int[] sizes = group.getMinPrefMax();
1430 for (int s = LayoutUtil.MIN; s <= LayoutUtil.PREF; s++) {
1431 int cSize = sizes[s];
1432 if (cSize == LayoutUtil.NOT_SET)
1436 int sIx = (r << 1) + 1;
1437 int len = Math.min((group.span << 1), fss.sizes.length - sIx) - 1;
1438 for (int j = sIx; j < sIx + len; j++) {
1439 int sz = fss.sizes[j][s];
1440 if (sz != LayoutUtil.NOT_SET)
1444 if (rowSize < cSize && len > 0) {
1445 for (int eagerness = 0, newRowSize = 0; eagerness < 4 && newRowSize < cSize; eagerness++)
1446 newRowSize = fss.expandSizes(specs, defPush, cSize, sIx, len, s, eagerness);
1453 /** For one dimension divide the component wraps into logical groups. One group for component wraps that share a common something,
1454 * line the property to layout by base line.
1455 * @param isRows If rows, and not columns, are to be divided.
1456 * @return One <code>ArrayList<LinkedDimGroup></code> for every row/column.
1458 private ArrayList<LinkedDimGroup>[] divideIntoLinkedGroups(boolean isRows)
1460 boolean fromEnd = !(isRows ? lc.isTopToBottom() : LayoutUtil.isLeftToRight(lc, container));
1461 TreeSet<Integer> primIndexes = isRows ? rowIndexes : colIndexes;
1462 TreeSet<Integer> secIndexes = isRows ? colIndexes : rowIndexes;
1463 DimConstraint[] primDCs = (isRows ? rowConstr : colConstr).getConstaints();
1465 @SuppressWarnings("unchecked")
1466 ArrayList<LinkedDimGroup>[] groupLists = new ArrayList[primIndexes.size()];
1469 for (int i : primIndexes) {
1472 if (i >= -MAX_GRID && i <= MAX_GRID) { // If not dock cell
1473 dc = primDCs[i >= primDCs.length ? primDCs.length - 1 : i];
1475 dc = DOCK_DIM_CONSTRAINT;
1478 ArrayList<LinkedDimGroup> groupList = new ArrayList<LinkedDimGroup>(4);
1479 groupLists[gIx++] = groupList;
1481 for (Integer ix : secIndexes) {
1482 Cell cell = isRows ? getCell(i, ix) : getCell(ix, i);
1483 if (cell == null || cell.compWraps.size() == 0)
1486 int span = (isRows ? cell.spany : cell.spanx);
1488 span = convertSpanToSparseGrid(i, span, primIndexes);
1490 boolean isPar = (cell.flowx == isRows);
1492 if ((!isPar && cell.compWraps.size() > 1) || span > 1) {
1494 int linkType = isPar ? LinkedDimGroup.TYPE_PARALLEL : LinkedDimGroup.TYPE_SERIAL;
1495 LinkedDimGroup lg = new LinkedDimGroup("p," + ix, span, linkType, !isRows, fromEnd);
1496 lg.setCompWraps(cell.compWraps);
1499 for (int cwIx = 0; cwIx < cell.compWraps.size(); cwIx++) {
1500 CompWrap cw = cell.compWraps.get(cwIx);
1501 boolean rowBaselineAlign = (isRows && lc.isTopToBottom() && dc.getAlignOrDefault(!isRows) == UnitValue.BASELINE_IDENTITY); // Disable baseline for bottomToTop since I can not verify it working.
1502 boolean isBaseline = isRows && cw.isBaselineAlign(rowBaselineAlign);
1504 String linkCtx = isBaseline ? "baseline" : null;
1506 // Find a group with same link context and put it in that group.
1507 boolean foundList = false;
1508 for (int glIx = 0, lastGl = groupList.size() - 1; glIx <= lastGl; glIx++) {
1509 LinkedDimGroup group = groupList.get(glIx);
1510 if (group.linkCtx == linkCtx || linkCtx != null && linkCtx.equals(group.linkCtx)) {
1511 group.addCompWrap(cw);
1517 // If none found and at last add a new group.
1519 int linkType = isBaseline ? LinkedDimGroup.TYPE_BASELINE : LinkedDimGroup.TYPE_PARALLEL;
1520 LinkedDimGroup lg = new LinkedDimGroup(linkCtx, 1, linkType, !isRows, fromEnd);
1531 /** Spanning is specified in the uncompressed grid number. They can for instance be more than 60000 for the outer
1532 * edge dock grid cells. When the grid is compressed and indexed after only the cells that area occupied the span
1533 * is erratic. This method use the row/col indexes and corrects the span to be correct for the compressed grid.
1534 * @param span The span in the uncompressed grid. <code>LayoutUtil.INF</code> will be interpreted to span the rest
1535 * of the column/row excluding the surrounding docking components.
1536 * @param indexes The indexes in the correct dimension.
1537 * @return The converted span.
1539 private static int convertSpanToSparseGrid(int curIx, int span, TreeSet<Integer> indexes)
1541 int lastIx = curIx + span;
1544 for (Integer ix : indexes) {
1546 continue; // We have not arrived to the correct index yet
1556 private boolean isCellFree(int r, int c, ArrayList<int[]> occupiedRects)
1558 if (getCell(r, c) != null)
1561 for (int[] rect : occupiedRects) {
1562 if (rect[0] <= c && rect[1] <= r && rect[0] + rect[2] > c && rect[1] + rect[3] > r)
1568 private Cell getCell(int r, int c)
1570 return grid.get((r << 16) + (c & 0xffff));
1573 private void setCell(int r, int c, Cell cell)
1576 throw new IllegalArgumentException("Cell position cannot be negative. row: " + r + ", col: " + c);
1578 if (c > MAX_GRID || r > MAX_GRID)
1579 throw new IllegalArgumentException("Cell position out of bounds. Out of cells. row: " + r + ", col: " + c);
1584 grid.put((r << 16) + (c & 0xffff), cell);
1587 /** Adds a docking cell. That cell is outside the normal cell indexes.
1588 * @param dockInsets The current dock insets. Will be updated!
1589 * @param side top == 0, left == 1, bottom = 2, right = 3.
1590 * @param cw The compwrap to put in a cell and add.
1592 private void addDockingCell(int[] dockInsets, int side, CompWrap cw)
1594 int r, c, spanx = 1, spany = 1;
1598 r = side == 0 ? dockInsets[0]++ : dockInsets[2]--;
1600 spanx = dockInsets[3] - dockInsets[1] + 1; // The +1 is for cell 0.
1601 colIndexes.add(dockInsets[3]); // Make sure there is a receiving cell
1606 c = side == 1 ? dockInsets[1]++ : dockInsets[3]--;
1608 spany = dockInsets[2] - dockInsets[0] + 1; // The +1 is for cell 0.
1609 rowIndexes.add(dockInsets[2]); // Make sure there is a receiving cell
1613 throw new IllegalArgumentException("Internal error 123.");
1619 grid.put((r << 16) + (c & 0xffff), new Cell(cw, spanx, spany, spanx > 1));
1622 /** A simple representation of a cell in the grid. Contains a number of component wraps, if they span more than one cell.
1624 private static class Cell
1626 private final int spanx, spany;
1627 private final boolean flowx;
1628 private final ArrayList<CompWrap> compWraps = new ArrayList<CompWrap>(2);
1630 private boolean hasTagged = false; // If one or more components have styles and need to be checked by the component sorter
1632 private Cell(CompWrap cw)
1634 this(cw, 1, 1, true);
1637 private Cell(int spanx, int spany, boolean flowx)
1639 this(null, spanx, spany, flowx);
1642 private Cell(CompWrap cw, int spanx, int spany, boolean flowx)
1652 /** A number of component wraps that share a layout "something" <b>in one dimension</b>
1654 private static class LinkedDimGroup
1656 private static final int TYPE_SERIAL = 0;
1657 private static final int TYPE_PARALLEL = 1;
1658 private static final int TYPE_BASELINE = 2;
1660 private final String linkCtx;
1661 private final int span;
1662 private final int linkType;
1663 private final boolean isHor, fromEnd;
1665 private final ArrayList<CompWrap> _compWraps = new ArrayList<CompWrap>(4);
1667 private int lStart = 0, lSize = 0; // Currently mostly for debug painting
1669 private LinkedDimGroup(String linkCtx, int span, int linkType, boolean isHor, boolean fromEnd)
1671 this.linkCtx = linkCtx;
1673 this.linkType = linkType;
1675 this.fromEnd = fromEnd;
1678 private void addCompWrap(CompWrap cw)
1683 private void setCompWraps(ArrayList<CompWrap> cws)
1685 if (_compWraps != cws) {
1687 _compWraps.addAll(cws);
1691 private void layout(DimConstraint dc, int start, int size, int spanCount)
1696 if (_compWraps.isEmpty())
1699 ContainerWrapper parent = _compWraps.get(0).comp.getParent();
1700 if (linkType == TYPE_PARALLEL) {
1701 layoutParallel(parent, _compWraps, dc, start, size, isHor, fromEnd);
1702 } else if (linkType == TYPE_BASELINE) {
1703 layoutBaseline(parent, _compWraps, dc, start, size, LayoutUtil.PREF, spanCount);
1705 layoutSerial(parent, _compWraps, dc, start, size, isHor, spanCount, fromEnd);
1709 /** Returns the min/pref/max sizes for this cell. Returned array <b>must not be altered</b>
1710 * @return A shared min/pref/max array of sizes. Always of length 3 and never <code>null</code>. Will always be of type STATIC and PIXEL.
1712 private int[] getMinPrefMax()
1714 int[] sizes = new int[3];
1715 if (!_compWraps.isEmpty()) {
1716 for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.PREF; sType++) {
1717 if (linkType == TYPE_PARALLEL) {
1718 sizes[sType] = getTotalSizeParallel(_compWraps, sType, isHor);
1719 } else if (linkType == TYPE_BASELINE) {
1720 AboveBelow aboveBelow = getBaselineAboveBelow(_compWraps, sType, false);
1721 sizes[sType] = aboveBelow.sum();
1723 sizes[sType] = getTotalSizeSerial(_compWraps, sType, isHor);
1726 sizes[LayoutUtil.MAX] = LayoutUtil.INF;
1732 /** Wraps a {@link java.awt.Component} together with its constraint. Caches a lot of information about the component so
1733 * for instance not the preferred size has to be calculated more than once.
1735 * Note! Does not ask the min/pref/max sizes again after the constructor. This means that
1737 private final class CompWrap
1739 private final ComponentWrapper comp;
1740 private final CC cc;
1741 private final int eHideMode;
1742 private final boolean useVisualPadding;
1743 private boolean sizesOk = false;
1744 private boolean isAbsolute;
1746 private int[][] gaps; // [top,left(actually before),bottom,right(actually after)][min,pref,max]
1748 private final int[] horSizes = new int[3];
1749 private final int[] verSizes = new int[3];
1751 private int x = LayoutUtil.NOT_SET, y = LayoutUtil.NOT_SET, w = LayoutUtil.NOT_SET, h = LayoutUtil.NOT_SET;
1753 private int forcedPushGaps = 0; // 1 == before, 2 = after. Bitwise.
1758 * @param eHideMode Effective hide mode. <= 0 means visible.
1759 * @param useVisualPadding
1761 private CompWrap(ComponentWrapper c, CC cc, int eHideMode, boolean useVisualPadding)
1765 this.eHideMode = eHideMode;
1766 this.useVisualPadding = useVisualPadding;
1767 this.isAbsolute = cc.getHorizontal().getSize().isAbsolute() && cc.getVertical().getSize().isAbsolute();
1769 if (eHideMode > 1) {
1770 gaps = new int[4][];
1771 for (int i = 0; i < gaps.length; i++)
1772 gaps[i] = new int[3];
1776 private int[] getSizes(boolean isHor)
1779 return isHor ? horSizes : verSizes;
1782 private void validateSize()
1784 BoundSize[] callbackSz = getCallbackSize(comp);
1786 if (isAbsolute && sizesOk && callbackSz == null)
1789 if (eHideMode <= 0) {
1790 int contentBias = comp.getContentBias();
1792 int sizeHint = contentBias == -1 ? -1 : (contentBias == 0 ? (w != LayoutUtil.NOT_SET ? w : comp.getWidth()) : (h != LayoutUtil.NOT_SET ? h : comp.getHeight()));
1794 BoundSize hBS = (callbackSz != null && callbackSz[0] != null) ? callbackSz[0] : cc.getHorizontal().getSize();
1795 BoundSize vBS = (callbackSz != null && callbackSz[1] != null) ? callbackSz[1] : cc.getVertical().getSize();
1797 for (int i = LayoutUtil.MIN; i <= LayoutUtil.MAX; i++) {
1798 switch (contentBias) {
1801 horSizes[i] = getSize(hBS, i, true, useVisualPadding, -1);
1802 verSizes[i] = getSize(vBS, i, false, useVisualPadding, -1);
1805 horSizes[i] = getSize(hBS, i, true, useVisualPadding, -1);
1806 verSizes[i] = getSize(vBS, i, false, useVisualPadding, sizeHint > 0 ? sizeHint : horSizes[i]);
1809 verSizes[i] = getSize(vBS, i, false, useVisualPadding, -1);
1810 horSizes[i] = getSize(hBS, i, true, useVisualPadding, sizeHint > 0 ? sizeHint : verSizes[i]);
1815 correctMinMax(horSizes);
1816 correctMinMax(verSizes);
1818 Arrays.fill(horSizes, 0); // Needed if component goes from visible -> invisible without recreating the grid.
1819 Arrays.fill(verSizes, 0);
1824 private int getSize(BoundSize uvs, int sizeType, boolean isHor, boolean useVP, int sizeHint)
1827 if (uvs == null || uvs.getSize(sizeType) == null) {
1829 case LayoutUtil.MIN:
1830 size = isHor ? comp.getMinimumWidth(sizeHint) : comp.getMinimumHeight(sizeHint);
1832 case LayoutUtil.PREF:
1833 size = isHor ? comp.getPreferredWidth(sizeHint) : comp.getPreferredHeight(sizeHint);
1836 size = isHor ? comp.getMaximumWidth(sizeHint) : comp.getMaximumHeight(sizeHint);
1840 //Do not include visual padding when calculating layout
1841 int[] visualPadding = comp.getVisualPadding();
1843 // Assume visualPadding is of length 4: top, left, bottom, right
1844 if (visualPadding != null && visualPadding.length > 0)
1845 size -= isHor ? (visualPadding[1] + visualPadding[3]) : (visualPadding[0] + visualPadding[2]);
1848 ContainerWrapper par = comp.getParent();
1849 float refValue = isHor ? par.getWidth() : par.getHeight();
1850 size = uvs.getSize(sizeType).getPixels(refValue, par, comp);
1856 private void calcGaps(ComponentWrapper before, CC befCC, ComponentWrapper after, CC aftCC, String tag, boolean flowX, boolean isLTR)
1858 ContainerWrapper par = comp.getParent();
1859 int parW = par.getWidth();
1860 int parH = par.getHeight();
1862 BoundSize befGap = before != null ? (flowX ? befCC.getHorizontal() : befCC.getVertical()).getGapAfter() : null;
1863 BoundSize aftGap = after != null ? (flowX ? aftCC.getHorizontal() : aftCC.getVertical()).getGapBefore() : null;
1865 mergeGapSizes(cc.getVertical().getComponentGaps(par, comp, befGap, (flowX ? null : before), tag, parH, 0, isLTR), false, true);
1866 mergeGapSizes(cc.getHorizontal().getComponentGaps(par, comp, befGap, (flowX ? before : null), tag, parW, 1, isLTR), true, true);
1867 mergeGapSizes(cc.getVertical().getComponentGaps(par, comp, aftGap, (flowX ? null : after), tag, parH, 2, isLTR), false, false);
1868 mergeGapSizes(cc.getHorizontal().getComponentGaps(par, comp, aftGap, (flowX ? after : null), tag, parW, 3, isLTR), true, false);
1871 private void setDimBounds(int start, int size, boolean isHor)
1874 if (start != x || w != size) {
1877 if (comp.getContentBias() == LayoutUtil.HORIZONTAL)
1878 invalidateSizes(); // Only for components that have a bias the sizes will have changed.
1881 if (start != y || h != size) {
1884 if (comp.getContentBias() == LayoutUtil.VERTICAL)
1885 invalidateSizes(); // Only for components that have a bias the sizes will have changed.
1890 void invalidateSizes()
1895 private boolean isPushGap(boolean isHor, boolean isBefore)
1897 if (isHor && ((isBefore ? 1 : 2) & forcedPushGaps) != 0)
1898 return true; // Forced
1900 DimConstraint dc = cc.getDimConstraint(isHor);
1901 BoundSize s = isBefore ? dc.getGapBefore() : dc.getGapAfter();
1902 return s != null && s.getGapPush();
1905 /** Transfers the bounds to the component
1907 private void transferBounds(boolean addVisualPadding)
1909 if (cc.isExternal())
1917 if (addVisualPadding) {
1918 //Add the visual padding back to the component when changing its size
1919 int[] visualPadding = comp.getVisualPadding();
1920 if (visualPadding != null) {
1921 //assume visualPadding is of length 4: top, left, bottom, right
1922 compX -= visualPadding[1];
1923 compY -= visualPadding[0];
1924 compW += (visualPadding[1] + visualPadding[3]);
1925 compH += (visualPadding[0] + visualPadding[2]);
1929 comp.setBounds(compX, compY, compW, compH);
1932 private void setForcedSizes(int[] sizes, boolean isHor)
1937 System.arraycopy(sizes, 0, getSizes(isHor), 0, 3);
1941 private void setGaps(int[] minPrefMax, int ix)
1944 gaps = new int[][] {null, null, null, null};
1946 gaps[ix] = minPrefMax;
1949 private void mergeGapSizes(int[] sizes, boolean isHor, boolean isTL)
1952 gaps = new int[][] {null, null, null, null};
1957 int gapIX = getGapIx(isHor, isTL);
1958 int[] oldGaps = gaps[gapIX];
1959 if (oldGaps == null) {
1960 oldGaps = new int[] {0, 0, LayoutUtil.INF};
1961 gaps[gapIX] = oldGaps;
1964 oldGaps[LayoutUtil.MIN] = Math.max(sizes[LayoutUtil.MIN], oldGaps[LayoutUtil.MIN]);
1965 oldGaps[LayoutUtil.PREF] = Math.max(sizes[LayoutUtil.PREF], oldGaps[LayoutUtil.PREF]);
1966 oldGaps[LayoutUtil.MAX] = Math.min(sizes[LayoutUtil.MAX], oldGaps[LayoutUtil.MAX]);
1969 private int getGapIx(boolean isHor, boolean isTL)
1971 return isHor ? (isTL ? 1 : 3) : (isTL ? 0 : 2);
1974 private int getSizeInclGaps(int sizeType, boolean isHor)
1976 return filter(sizeType, getGapBefore(sizeType, isHor) + getSize(sizeType, isHor) + getGapAfter(sizeType, isHor));
1979 private int getSize(int sizeType, boolean isHor)
1981 return filter(sizeType, getSizes(isHor)[sizeType]);
1984 private int getGapBefore(int sizeType, boolean isHor)
1986 int[] gaps = getGaps(isHor, true);
1987 return gaps != null ? filter(sizeType, gaps[sizeType]) : 0;
1990 private int getGapAfter(int sizeType, boolean isHor)
1992 int[] gaps = getGaps(isHor, false);
1993 return gaps != null ? filter(sizeType, gaps[sizeType]) : 0;
1996 private int[] getGaps(boolean isHor, boolean isTL)
1998 return gaps[getGapIx(isHor, isTL)];
2001 private int filter(int sizeType, int size)
2003 if (size == LayoutUtil.NOT_SET)
2004 return sizeType != LayoutUtil.MAX ? 0 : LayoutUtil.INF;
2005 return constrainSize(size);
2008 private boolean isBaselineAlign(boolean defValue)
2010 Float g = cc.getVertical().getGrow();
2011 if (g != null && g.intValue() != 0)
2014 UnitValue al = cc.getVertical().getAlign();
2015 return (al != null ? al == UnitValue.BASELINE_IDENTITY : defValue) && comp.hasBaseline();
2018 private int getBaseline(int sizeType)
2020 return comp.getBaseline(getSize(sizeType, true), getSize(sizeType, false));
2023 void adjustMinHorSizeUp(int minSize)
2025 int[] sz = getSizes(true);
2026 if (sz[LayoutUtil.MIN] < minSize)
2027 sz[LayoutUtil.MIN] = minSize;
2032 //***************************************************************************************
2034 //***************************************************************************************
2036 private static void layoutBaseline(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, int sizeType, int spanCount)
2038 AboveBelow aboveBelow = getBaselineAboveBelow(compWraps, sizeType, true);
2039 int blRowSize = aboveBelow.sum();
2041 CC cc = compWraps.get(0).cc;
2043 // Align for the whole baseline component array
2044 UnitValue align = cc.getVertical().getAlign();
2045 if (spanCount == 1 && align == null)
2046 align = dc.getAlignOrDefault(false);
2047 if (align == UnitValue.BASELINE_IDENTITY)
2048 align = UnitValue.CENTER;
2050 int offset = start + aboveBelow.maxAbove + (align != null ? Math.max(0, align.getPixels(size - blRowSize, parent, null)) : 0);
2051 for (CompWrap cw : compWraps) {
2053 if (cw.y + cw.h > start + size)
2054 cw.h = start + size - cw.y;
2058 private static void layoutSerial(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, boolean isHor, int spanCount, boolean fromEnd)
2060 FlowSizeSpec fss = mergeSizesGapsAndResConstrs(
2061 getComponentResizeConstraints(compWraps, isHor),
2062 getComponentGapPush(compWraps, isHor),
2063 getComponentSizes(compWraps, isHor),
2064 getGaps(compWraps, isHor));
2066 Float[] pushW = dc.isFill() ? GROW_100 : null;
2067 int[] sizes = LayoutUtil.calculateSerial(fss.sizes, fss.resConstsInclGaps, pushW, LayoutUtil.PREF, size);
2068 setCompWrapBounds(parent, sizes, compWraps, dc.getAlignOrDefault(isHor), start, size, isHor, fromEnd);
2071 private static void setCompWrapBounds(ContainerWrapper parent, int[] allSizes, ArrayList<CompWrap> compWraps, UnitValue rowAlign, int start, int size, boolean isHor, boolean fromEnd)
2073 int totSize = LayoutUtil.sum(allSizes);
2074 CC cc = compWraps.get(0).cc;
2075 UnitValue align = correctAlign(cc, rowAlign, isHor, fromEnd);
2078 int slack = size - totSize;
2079 if (slack > 0 && align != null) {
2080 int al = Math.min(slack, Math.max(0, align.getPixels(slack, parent, null)));
2081 cSt += (fromEnd ? -al : al);
2084 for (int i = 0, bIx = 0, iSz = compWraps.size(); i < iSz; i++) {
2085 CompWrap cw = compWraps.get(i);
2087 cSt -= allSizes[bIx++];
2088 cw.setDimBounds(cSt - allSizes[bIx], allSizes[bIx], isHor);
2089 cSt -= allSizes[bIx++];
2091 cSt += allSizes[bIx++];
2092 cw.setDimBounds(cSt, allSizes[bIx], isHor);
2093 cSt += allSizes[bIx++];
2098 private static void layoutParallel(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, boolean isHor, boolean fromEnd)
2100 int[][] sizes = new int[compWraps.size()][]; // [compIx][gapBef,compSize,gapAft]
2102 for (int i = 0; i < sizes.length; i++) {
2103 CompWrap cw = compWraps.get(i);
2105 DimConstraint cDc = cw.cc.getDimConstraint(isHor);
2107 ResizeConstraint[] resConstr = new ResizeConstraint[] {
2108 cw.isPushGap(isHor, true) ? GAP_RC_CONST_PUSH : GAP_RC_CONST,
2110 cw.isPushGap(isHor, false) ? GAP_RC_CONST_PUSH : GAP_RC_CONST,
2113 int[][] sz = new int[][] {
2114 cw.getGaps(isHor, true), cw.getSizes(isHor), cw.getGaps(isHor, false)
2117 Float[] pushW = dc.isFill() ? GROW_100 : null;
2119 sizes[i] = LayoutUtil.calculateSerial(sz, resConstr, pushW, LayoutUtil.PREF, size);
2122 UnitValue rowAlign = dc.getAlignOrDefault(isHor);
2123 setCompWrapBounds(parent, sizes, compWraps, rowAlign, start, size, isHor, fromEnd);
2126 private static void setCompWrapBounds(ContainerWrapper parent, int[][] sizes, ArrayList<CompWrap> compWraps, UnitValue rowAlign, int start, int size, boolean isHor, boolean fromEnd)
2128 for (int i = 0; i < sizes.length; i++) {
2129 CompWrap cw = compWraps.get(i);
2131 UnitValue align = correctAlign(cw.cc, rowAlign, isHor, fromEnd);
2133 int[] cSizes = sizes[i];
2134 int gapBef = cSizes[0];
2135 int cSize = cSizes[1]; // No Math.min(size, cSizes[1]) here!
2136 int gapAft = cSizes[2];
2138 int cSt = fromEnd ? start - gapBef : start + gapBef;
2139 int slack = size - cSize - gapBef - gapAft;
2140 if (slack > 0 && align != null) {
2141 int al = Math.min(slack, Math.max(0, align.getPixels(slack, parent, null)));
2142 cSt += (fromEnd ? -al : al);
2145 cw.setDimBounds(fromEnd ? cSt - cSize : cSt, cSize, isHor);
2149 private static UnitValue correctAlign(CC cc, UnitValue rowAlign, boolean isHor, boolean fromEnd)
2151 UnitValue align = (isHor ? cc.getHorizontal() : cc.getVertical()).getAlign();
2154 if (align == UnitValue.BASELINE_IDENTITY)
2155 align = UnitValue.CENTER;
2158 if (align == UnitValue.LEFT)
2159 align = UnitValue.RIGHT;
2160 else if (align == UnitValue.RIGHT)
2161 align = UnitValue.LEFT;
2166 private static class AboveBelow {
2170 AboveBelow(int maxAbove, int maxBelow) {
2171 this.maxAbove = maxAbove;
2172 this.maxBelow = maxBelow;
2176 return maxAbove + maxBelow;
2180 private static AboveBelow getBaselineAboveBelow(ArrayList<CompWrap> compWraps, int sType, boolean centerBaseline)
2182 int maxAbove = Integer.MIN_VALUE;
2183 int maxBelow = Integer.MIN_VALUE;
2184 for (CompWrap cw : compWraps) {
2185 int height = cw.getSize(sType, false);
2186 if (height >= LayoutUtil.INF)
2187 return new AboveBelow(LayoutUtil.INF / 2, LayoutUtil.INF / 2);
2189 int baseline = cw.getBaseline(sType);
2190 int above = baseline + cw.getGapBefore(sType, false);
2191 maxAbove = Math.max(above, maxAbove);
2192 maxBelow = Math.max(height - baseline + cw.getGapAfter(sType, false), maxBelow);
2195 cw.setDimBounds(-baseline, height, false);
2197 return new AboveBelow(maxAbove, maxBelow);
2200 private static int getTotalSizeParallel(ArrayList<CompWrap> compWraps, int sType, boolean isHor)
2202 int size = sType == LayoutUtil.MAX ? LayoutUtil.INF : 0;
2204 for (CompWrap cw : compWraps) {
2205 int cwSize = cw.getSizeInclGaps(sType, isHor);
2206 if (cwSize >= LayoutUtil.INF)
2207 return LayoutUtil.INF;
2209 if (sType == LayoutUtil.MAX ? cwSize < size : cwSize > size)
2212 return constrainSize(size);
2215 private static int getTotalSizeSerial(ArrayList<CompWrap> compWraps, int sType, boolean isHor)
2218 for (int i = 0, iSz = compWraps.size(), lastGapAfter = 0; i < iSz; i++) {
2219 CompWrap wrap = compWraps.get(i);
2220 int gapBef = wrap.getGapBefore(sType, isHor);
2221 if (gapBef > lastGapAfter)
2222 totSize += gapBef - lastGapAfter;
2224 totSize += wrap.getSize(sType, isHor);
2225 totSize += (lastGapAfter = wrap.getGapAfter(sType, isHor));
2227 if (totSize >= LayoutUtil.INF)
2228 return LayoutUtil.INF;
2230 return constrainSize(totSize);
2233 private static int getTotalGroupsSizeParallel(ArrayList<LinkedDimGroup> groups, int sType, boolean countSpanning)
2235 int size = sType == LayoutUtil.MAX ? LayoutUtil.INF : 0;
2236 for (LinkedDimGroup group : groups) {
2237 if (countSpanning || group.span == 1) {
2238 int grpSize = group.getMinPrefMax()[sType];
2239 if (grpSize >= LayoutUtil.INF)
2240 return LayoutUtil.INF;
2242 if (sType == LayoutUtil.MAX ? grpSize < size : grpSize > size)
2246 return constrainSize(size);
2252 * @return Might contain LayoutUtil.NOT_SET
2254 private static int[][] getComponentSizes(ArrayList<CompWrap> compWraps, boolean isHor)
2256 int[][] compSizes = new int[compWraps.size()][];
2257 for (int i = 0; i < compSizes.length; i++)
2258 compSizes[i] = compWraps.get(i).getSizes(isHor);
2262 /** Merges sizes and gaps together with Resize Constraints. For gaps {@link #GAP_RC_CONST} is used.
2263 * @param resConstr One resize constraint for every row/component. Can be lesser in length and the last element should be used for missing elements.
2264 * @param gapPush If the corresponding gap should be considered pushing and thus want to take free space if left over. Should be one more than resConstrs!
2265 * @param minPrefMaxSizes The sizes (min/pref/max) for every row/component.
2266 * @param gapSizes The gaps before and after each row/component packed in one double sized array.
2267 * @return A holder for the merged values.
2269 private static FlowSizeSpec mergeSizesGapsAndResConstrs(ResizeConstraint[] resConstr, boolean[] gapPush, int[][] minPrefMaxSizes, int[][] gapSizes)
2271 int[][] sizes = new int[(minPrefMaxSizes.length << 1) + 1][]; // Make room for gaps around.
2272 ResizeConstraint[] resConstsInclGaps = new ResizeConstraint[sizes.length];
2274 sizes[0] = gapSizes[0];
2275 for (int i = 0, crIx = 1; i < minPrefMaxSizes.length; i++, crIx += 2) {
2277 // Component bounds and constraints
2278 resConstsInclGaps[crIx] = resConstr[i];
2279 sizes[crIx] = minPrefMaxSizes[i];
2281 sizes[crIx + 1] = gapSizes[i + 1];
2283 if (sizes[crIx - 1] != null)
2284 resConstsInclGaps[crIx - 1] = gapPush[i < gapPush.length ? i : gapPush.length - 1] ? GAP_RC_CONST_PUSH : GAP_RC_CONST;
2286 if (i == (minPrefMaxSizes.length - 1) && sizes[crIx + 1] != null)
2287 resConstsInclGaps[crIx + 1] = gapPush[(i + 1) < gapPush.length ? (i + 1) : gapPush.length - 1] ? GAP_RC_CONST_PUSH : GAP_RC_CONST;
2290 // Check for null and set it to 0, 0, 0.
2291 for (int i = 0; i < sizes.length; i++) {
2292 if (sizes[i] == null)
2293 sizes[i] = new int[3];
2296 return new FlowSizeSpec(sizes, resConstsInclGaps);
2299 private static int[] mergeSizes(int[] oldValues, int[] newValues)
2301 if (oldValues == null)
2304 if (newValues == null)
2307 int[] ret = new int[oldValues.length];
2308 for (int i = 0; i < ret.length; i++)
2309 ret[i] = mergeSizes(oldValues[i], newValues[i], true);
2314 private static int mergeSizes(int oldValue, int newValue, boolean toMax)
2316 if (oldValue == LayoutUtil.NOT_SET || oldValue == newValue)
2319 if (newValue == LayoutUtil.NOT_SET)
2322 return toMax != oldValue > newValue ? newValue : oldValue;
2325 private static int constrainSize(int s)
2327 return s > 0 ? (s < LayoutUtil.INF ? s : LayoutUtil.INF) : 0;
2330 private static void correctMinMax(int s[])
2332 if (s[LayoutUtil.MIN] > s[LayoutUtil.MAX])
2333 s[LayoutUtil.MIN] = s[LayoutUtil.MAX]; // Since MAX is almost always explicitly set use that
2335 if (s[LayoutUtil.PREF] < s[LayoutUtil.MIN])
2336 s[LayoutUtil.PREF] = s[LayoutUtil.MIN];
2338 if (s[LayoutUtil.PREF] > s[LayoutUtil.MAX])
2339 s[LayoutUtil.PREF] = s[LayoutUtil.MAX];
2342 private static final class FlowSizeSpec
2344 private final int[][] sizes; // [row/col index][min, pref, max]
2345 private final ResizeConstraint[] resConstsInclGaps; // [row/col index]
2347 private FlowSizeSpec(int[][] sizes, ResizeConstraint[] resConstsInclGaps)
2350 this.resConstsInclGaps = resConstsInclGaps;
2354 * @param specs The specs for the columns or rows. Last index will be used of <code>fromIx + len</code> is greater than this array's length.
2355 * @param targetSize The size to try to meet.
2356 * @param defGrow The default grow weight if the specs does not have anyone that will grow. Comes from "push" in the CC.
2360 * @param eagerness How eager the algorithm should be to try to expand the sizes.
2362 * <li>0 - Grow only rows/columns which have the <code>sizeType</code> set to be the containing components AND which has a grow weight > 0.
2363 * <li>1 - Grow only rows/columns which have the <code>sizeType</code> set to be the containing components AND which has a grow weight > 0 OR unspecified.
2364 * <li>2 - Grow all rows/columns that have a grow weight > 0.
2365 * <li>3 - Grow all rows/columns that have a grow weight > 0 OR unspecified.
2367 * @return The new size.
2369 private int expandSizes(DimConstraint[] specs, Float[] defGrow, int targetSize, int fromIx, int len, int sizeType, int eagerness)
2371 ResizeConstraint[] resConstr = new ResizeConstraint[len];
2372 int[][] sizesToExpand = new int[len][];
2373 for (int i = 0; i < len; i++) {
2374 int[] minPrefMax = sizes[i + fromIx];
2375 sizesToExpand[i] = new int[] {minPrefMax[sizeType], minPrefMax[LayoutUtil.PREF], minPrefMax[LayoutUtil.MAX]};
2377 if (eagerness <= 1 && i % 2 == 0) { // (i % 2 == 0) means only odd indexes, which is only rows/col indexes and not gaps.
2378 int cIx = (i + fromIx - 1) >> 1;
2379 DimConstraint spec = (DimConstraint) LayoutUtil.getIndexSafe(specs, cIx);
2381 BoundSize sz = spec.getSize();
2382 if ( (sizeType == LayoutUtil.MIN && sz.getMin() != null && sz.getMin().getUnit() != UnitValue.MIN_SIZE) ||
2383 (sizeType == LayoutUtil.PREF && sz.getPreferred() != null && sz.getPreferred().getUnit() != UnitValue.PREF_SIZE)) {
2387 resConstr[i] = (ResizeConstraint) LayoutUtil.getIndexSafe(resConstsInclGaps, i + fromIx);
2390 Float[] growW = (eagerness == 1 || eagerness == 3) ? extractSubArray(specs, defGrow, fromIx, len): null;
2391 int[] newSizes = LayoutUtil.calculateSerial(sizesToExpand, resConstr, growW, LayoutUtil.PREF, targetSize);
2394 for (int i = 0; i < len; i++) {
2395 int s = newSizes[i];
2396 sizes[i + fromIx][sizeType] = s;
2403 private static Float[] extractSubArray(DimConstraint[] specs, Float[] arr, int ix, int len)
2405 if (arr == null || arr.length < ix + len) {
2406 Float[] growLastArr = new Float[len];
2408 // Handle a group where some rows (first one/few and/or last one/few) are docks.
2409 for (int i = ix + len - 1; i >= 0; i -= 2) {
2410 int specIx = (i >> 1);
2411 if (specs[specIx] != DOCK_DIM_CONSTRAINT) {
2412 growLastArr[i - ix] = ResizeConstraint.WEIGHT_100;
2419 Float[] newArr = new Float[len];
2420 System.arraycopy(arr, ix, newArr, 0, len);
2424 private static WeakHashMap<Object, int[][]>[] PARENT_ROWCOL_SIZES_MAP = null;
2425 @SuppressWarnings( "unchecked" )
2426 private static synchronized void putSizesAndIndexes(Object parComp, int[] sizes, int[] ixArr, boolean isRows)
2428 if (PARENT_ROWCOL_SIZES_MAP == null) // Lazy since only if designing in IDEs
2429 PARENT_ROWCOL_SIZES_MAP = new WeakHashMap[] {new WeakHashMap<Object,int[][]>(4), new WeakHashMap<Object,int[][]>(4)};
2431 PARENT_ROWCOL_SIZES_MAP[isRows ? 0 : 1].put(parComp, new int[][]{ixArr, sizes});
2434 static synchronized int[][] getSizesAndIndexes(Object parComp, boolean isRows)
2436 if (PARENT_ROWCOL_SIZES_MAP == null)
2439 return PARENT_ROWCOL_SIZES_MAP[isRows ? 0 : 1].get(parComp);
2442 private static WeakHashMap<Object, ArrayList<WeakCell>> PARENT_GRIDPOS_MAP = null;
2443 private static synchronized void saveGrid(ComponentWrapper parComp, LinkedHashMap<Integer, Cell> grid)
2445 if (PARENT_GRIDPOS_MAP == null) // Lazy since only if designing in IDEs
2446 PARENT_GRIDPOS_MAP = new WeakHashMap<Object, ArrayList<WeakCell>>(4);
2448 ArrayList<WeakCell> weakCells = new ArrayList<WeakCell>(grid.size());
2450 for (Map.Entry<Integer, Cell> e : grid.entrySet()) {
2451 Cell cell = e.getValue();
2452 Integer xyInt = e.getKey();
2453 if (xyInt != null) {
2454 int x = (xyInt << 16) >> 16;
2455 int y = xyInt >> 16;
2457 for (CompWrap cw : cell.compWraps)
2458 weakCells.add(new WeakCell(cw.comp.getComponent(), x, y, cell.spanx, cell.spany));
2462 PARENT_GRIDPOS_MAP.put(parComp.getComponent(), weakCells);
2465 static synchronized HashMap<Object, int[]> getGridPositions(Object parComp)
2467 ArrayList<WeakCell> weakCells = PARENT_GRIDPOS_MAP != null ? PARENT_GRIDPOS_MAP.get(parComp) : null;
2468 if (weakCells == null)
2471 HashMap<Object, int[]> retMap = new HashMap<Object, int[]>();
2473 for (WeakCell wc : weakCells) {
2474 Object component = wc.componentRef.get();
2475 if (component != null)
2476 retMap.put(component, new int[] {wc.x, wc.y, wc.spanX, wc.spanY});
2482 private static class WeakCell
2484 private final WeakReference<Object> componentRef;
2485 private final int x, y, spanX, spanY;
2487 private WeakCell(Object component, int x, int y, int spanX, int spanY)
2489 this.componentRef = new WeakReference<Object>(component);