diff options
Diffstat (limited to 'prototypes/miglayout/net/miginfocom/layout/Grid.java')
-rw-r--r-- | prototypes/miglayout/net/miginfocom/layout/Grid.java | 2245 |
1 files changed, 2245 insertions, 0 deletions
diff --git a/prototypes/miglayout/net/miginfocom/layout/Grid.java b/prototypes/miglayout/net/miginfocom/layout/Grid.java new file mode 100644 index 00000000..c45ce398 --- /dev/null +++ b/prototypes/miglayout/net/miginfocom/layout/Grid.java @@ -0,0 +1,2245 @@ +package net.miginfocom.layout;
+
+
+import java.util.*;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ * Date: 2006-sep-08
+ */
+
+/** Holds components in a grid. Does most of the logic behind the layout manager.
+ */
+public final class Grid
+{
+ public static final boolean TEST_GAPS = true;
+
+ private static final Float[] GROW_100 = new Float[] {ResizeConstraint.WEIGHT_100};
+
+ private static final DimConstraint DOCK_DIM_CONSTRAINT = new DimConstraint();
+ static {
+ DOCK_DIM_CONSTRAINT.setGrowPriority(0);
+ }
+
+ /** This is the maximum grid position for "normal" components. Docking components use the space out to
+ * <code>MAX_DOCK_GRID</code> and below 0.
+ */
+ private static final int MAX_GRID = 30000;
+
+ /** Docking components will use the grid coordinates <code>-MAX_DOCK_GRID -> 0</code> and <code>MAX_GRID -> MAX_DOCK_GRID</code>.
+ */
+ private static final int MAX_DOCK_GRID = 32767;
+
+ /** A constraint used for gaps.
+ */
+ private static final ResizeConstraint GAP_RC_CONST = new ResizeConstraint(200, ResizeConstraint.WEIGHT_100, 50, null);
+ private static final ResizeConstraint GAP_RC_CONST_PUSH = new ResizeConstraint(200, ResizeConstraint.WEIGHT_100, 50, ResizeConstraint.WEIGHT_100);
+
+ /** The constraints. Never <code>null</code>.
+ */
+ private final LC lc;
+
+ /** The parent that is layout out and this grid is done for. Never <code>null</code>.
+ */
+ private final ContainerWrapper container;
+
+ /** An x, y array implemented as a sparse array to accomodate for any grid size without wasting memory (or rather 15 bit (0-MAX_GRID * 0-MAX_GRID).
+ */
+ private final LinkedHashMap<Integer, Cell> grid = new LinkedHashMap<Integer, Cell>(); // [(y << 16) + x] -> Cell. null key for absolute positioned compwraps
+
+ private HashMap<Integer, BoundSize> wrapGapMap = null; // Row or Column index depending in the dimension that "raps". Normally row indexes but may be column indexes if "flowy". 0 means before first row/col.
+
+ /** The size of the grid. Row count and column count.
+ */
+ private final TreeSet<Integer> rowIndexes = new TreeSet<Integer>(), colIndexes = new TreeSet<Integer>();
+
+ /** The row and column specifications.
+ */
+ private final AC rowConstr, colConstr;
+
+ /** The in the constructor calculated min/pref/max sizes of the rows and columns.
+ */
+ private FlowSizeSpec colFlowSpecs = null, rowFlowSpecs = null;
+
+ /** Components that are connectione in one dimension (such as baseline alignment for instance) are grouped together and stored here.
+ * One for each row/column.
+ */
+ private ArrayList<LinkedDimGroup>[] colGroupLists, rowGroupLists; //[(start)row/col number]
+
+ /** The in the constructor calculated min/pref/max size of the whole grid.
+ */
+ private int[] width = null, height = null;
+
+ /** If debug is on contains the bounds for things to paint when calling {@link ContainerWrapper#paintDebugCell(int, int, int, int)}
+ */
+ private ArrayList<int[]> debugRects = null; // [x, y, width, height]
+
+ /** If any of the absolute coordinates for component bounds has links the name of the target is in this Set.
+ * Since it requires some memory and computations this is checked at the creation so that
+ * the link information is only created if needed later.
+ * <p>
+ * The boolean is true for groups id:s and null for normal id:s.
+ */
+ private HashMap<String, Boolean> linkTargetIDs = null;
+
+ private final int dockOffY, dockOffX;
+
+ private final Float[] growXs, growYs;
+
+ private final ArrayList<LayoutCallback> callbackList;
+
+ /** Constructor.
+ * @param container The container that will be laid out.
+ * @param lc The form flow constraints.
+ * @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.
+ * @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.
+ * @param fcMap The map containing the parsed constraints for each child component of <code>parent</code>. Will not be alterted.
+ * @param callbackList A list of callbacks or <code>null</code> if none. Will not be alterted.
+ */
+ public Grid(ContainerWrapper container, LC lc, AC rowConstr, AC colConstr, Map<ComponentWrapper, CC> fcMap, ArrayList<LayoutCallback> callbackList)
+ {
+ this.lc = lc;
+ this.rowConstr = rowConstr;
+ this.colConstr = colConstr;
+ this.container = container;
+ this.callbackList = callbackList;
+
+ int wrap = lc.getWrapAfter() != 0 ? lc.getWrapAfter() : (lc.isFlowX() ? colConstr : rowConstr).getConstaints().length;
+
+ final ComponentWrapper[] comps = container.getComponents();
+
+ boolean hasTagged = false; // So we do not have to sort if it will not do any good
+ boolean hasPushX = false, hasPushY = false;
+ final int[] p = new int[2];
+ final ArrayList<int[]> spannedRects = new ArrayList<int[]>(2);
+
+ final DimConstraint[] specs = (lc.isFlowX() ? rowConstr : colConstr).getConstaints();
+
+ int sizeGroupsX = 0, sizeGroupsY = 0;
+ int[] dockInsets = null; // top, left, bottom, right insets for docks.
+
+ LinkHandler.clearTemporaryBounds(container.getLayout());
+
+ for (int i = 0; i < comps.length;) {
+ ComponentWrapper comp = comps[i];
+ CC rootCc = fcMap.get(comp);
+
+ int hideMode = comp.isVisible() ? -1 : rootCc.getHideMode() != -1 ? rootCc.getHideMode() : lc.getHideMode();
+
+ if (rootCc == null || hideMode == 3) { // To work with situations where there are components that does not have a layout manager, or not this one.
+ if (rootCc != null)
+ setLinkedBounds(comp, rootCc, comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight(), rootCc.isExternal());
+ i++;
+ continue; // The "external" component should not be handled further.
+ }
+
+ if (rootCc.getHorizontal().getSizeGroup() != null)
+ sizeGroupsX++;
+ if (rootCc.getVertical().getSizeGroup() != null)
+ sizeGroupsY++;
+
+ // Special treatment of absolute positioned components.
+ UnitValue[] pos = getPos(comp, rootCc);
+ BoundSize[] cbSz = getCallbackSize(comp, rootCc);
+ if (pos != null || rootCc.isExternal()) {
+
+ // Check if link information should be saved later.
+ if (pos != null) {
+ for (int l = 0; l < pos.length; l++) {
+ UnitValue u = pos[l];
+ if (u != null && u.isLinkedDeep())
+ addLinkTargetIDs(u);
+ }
+ }
+
+ CompWrap cw = new CompWrap(comp, rootCc, hideMode, pos, cbSz);
+ Cell cell = grid.get(null);
+ if (cell == null) {
+ grid.put(null, new Cell(cw));
+ } else {
+ cell.compWraps.add(cw);
+ }
+
+ if (rootCc.isBoundsInGrid() == false || rootCc.isExternal()) {
+ setLinkedBounds(comp, rootCc, comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight(), rootCc.isExternal());
+ i++;
+ continue;
+ }
+ }
+
+ if (rootCc.getDockSide() != -1) {
+ if (dockInsets == null)
+ dockInsets = new int[] {-MAX_DOCK_GRID, -MAX_DOCK_GRID, MAX_DOCK_GRID, MAX_DOCK_GRID};
+
+ addDockingCell(dockInsets, rootCc.getDockSide(), new CompWrap(comp, rootCc, hideMode, pos, cbSz));
+ i++;
+ continue;
+ }
+
+ Boolean cellFlowX = rootCc.getFlowX();
+ Cell cell = null;
+
+ if (rootCc.isNewline())
+ wrap(p, rootCc.getNewlineGapSize(), true);
+
+ increase(p, rootCc.getSkip()); // Probably 0 advance...
+
+ boolean rowNoGrid = lc.isNoGrid() || ((DimConstraint) LayoutUtil.getIndexSafe(specs, lc.isFlowX() ? p[1] : p[0])).isNoGrid();
+
+ // Move to a free y, x if no absolute grid specified
+ int cx = rootCc.getCellX();
+ int cy = rootCc.getCellY();
+ if ((cx < 0 || cy < 0) && rowNoGrid == false) {
+ while (isCellFree(p[1], p[0], spannedRects) == false) {
+ if (Math.abs(increase(p, 1)) >= wrap)
+ wrap(p, null, true);
+ }
+ } else {
+ if (cx >= 0 && cy >= 0) {
+ if (cy >= 0) {
+ p[0] = cx;
+ p[1] = cy;
+ } else { // Only one coordinate is specified. Use the current row (flowx) or column (flowy) to fill in.
+ if (lc.isFlowX()) {
+ p[0] = cx;
+ } else {
+ p[1] = cx;
+ }
+ }
+ }
+ cell = getCell(p[1], p[0]); // Might be null
+ }
+
+ // If cell is not created yet, create it and set it.
+ if (cell == null) {
+ int spanx = Math.min(rowNoGrid && lc.isFlowX() ? LayoutUtil.INF : rootCc.getSpanX(), MAX_GRID - p[0]);
+ int spany = Math.min(rowNoGrid && !lc.isFlowX() ? LayoutUtil.INF : rootCc.getSpanY(), MAX_GRID - p[1]);
+
+ cell = new Cell(spanx, spany, cellFlowX != null ? cellFlowX.booleanValue() : lc.isFlowX());
+
+ setCell(p[1], p[0], cell);
+
+ // Add a rectangle so we can know that spanned cells occupy more space.
+ if (spanx > 1 || spany > 1)
+ spannedRects.add(new int[] {p[0], p[1], spanx, spany});
+ }
+
+ // Add the one, or all, components that split the grid position to the same Cell.
+ boolean forceWrap = false;
+ BoundSize wrapGap = null;
+ int splitLeft = rowNoGrid ? LayoutUtil.INF : rootCc.getSplit() - 1;
+ boolean splitExit = false;
+ for (; splitLeft >= 0 && forceWrap == false && i < comps.length; splitLeft--) {
+ ComponentWrapper compAdd = comps[i];
+ CC cc = fcMap.get(compAdd); // Can not be null.
+
+ hideMode = compAdd.isVisible() ? -1 : cc.getHideMode() != -1 ? cc.getHideMode() : lc.getHideMode();
+
+ if (cc == null || cc.isExternal() || hideMode == 3) {
+ i++;
+ continue; // To work with situations where there are components that does not have a layout manager, or not this one.
+ }
+
+ hasPushX |= (cc.getPushX() != null);
+ hasPushY |= (cc.getPushY() != null);
+
+ if (cc != rootCc) { // If not first in a cell
+ if (cc.isNewline() || cc.isBoundsInGrid() == false)
+ break;
+
+ if (splitLeft > 0 && cc.getSkip() > 0) {
+ splitExit = true;
+ break;
+ }
+
+ pos = getPos(compAdd, cc);
+ cbSz = getCallbackSize(compAdd, cc);
+ }
+
+ CompWrap cw = new CompWrap(compAdd, cc, hideMode, pos, cbSz);
+ cell.compWraps.add(cw);
+ cell.hasTagged |= cc.getTag() != null;
+ hasTagged |= cell.hasTagged;
+
+ if (cc != rootCc) {
+ if (cc.getHorizontal().getSizeGroup() != null)
+ sizeGroupsX++;
+ if (cc.getVertical().getSizeGroup() != null)
+ sizeGroupsY++;
+ }
+
+ int flowSpan = lc.isFlowX() ? rootCc.getSpanX() : rootCc.getSpanY();
+ forceWrap = (cc.isWrap() || (flowSpan == LayoutUtil.INF && splitLeft == 0));
+ wrapGap = cc.getWrapGapSize();
+ i++;
+ }
+
+ if (forceWrap) {
+ wrap(p, wrapGap, true);
+ } else if (rowNoGrid == false) {
+ int span = lc.isFlowX() ? cell.spanx : cell.spany;
+ if (Math.abs((lc.isFlowX() ? p[0] : p[1])) + span >= wrap) {
+ wrap(p, null, true);
+ } else {
+ int adv = lc.isFlowX() ? cell.spanx : cell.spany;
+ if (splitExit)
+ adv--;
+ increase(p, adv);
+ }
+ }
+ }
+
+ // 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.
+ if (sizeGroupsX > 0 || sizeGroupsY > 0) {
+ HashMap<String, int[]> sizeGroupMapX = sizeGroupsX > 0 ? new HashMap<String, int[]>(sizeGroupsX) : null;
+ HashMap<String, int[]> sizeGroupMapY = sizeGroupsY > 0 ? new HashMap<String, int[]>(sizeGroupsY) : null;
+ ArrayList<CompWrap> sizeGroupCWs = new ArrayList<CompWrap>(Math.max(sizeGroupsX, sizeGroupsY));
+
+ for (Iterator<Cell> it = grid.values().iterator(); it.hasNext();) {
+ Cell cell = it.next();
+ for (int i = 0; i < cell.compWraps.size(); i++) {
+ CompWrap cw = cell.compWraps.get(i);
+
+ if (cw.cc.getHorizontal().getSizeGroup() != null || cw.cc.getVertical().getSizeGroup() != null) {
+ addToSizeGroup(sizeGroupMapX, cw.cc.getHorizontal().getSizeGroup(), cw.horSizes);
+ addToSizeGroup(sizeGroupMapY, cw.cc.getVertical().getSizeGroup(), cw.verSizes);
+ sizeGroupCWs.add(cw);
+ }
+ }
+ }
+
+ // Set/equalize the sizeGroups to same the values.
+ if (sizeGroupCWs != null) {
+ for (int i = 0; i < sizeGroupCWs.size(); i++) {
+ CompWrap cw = sizeGroupCWs.get(i);
+ if (sizeGroupMapX != null)
+ cw.setSizes(sizeGroupMapX.get(cw.cc.getHorizontal().getSizeGroup()), true); // Target method handles null sizes
+ if (sizeGroupMapY != null)
+ cw.setSizes(sizeGroupMapY.get(cw.cc.getVertical().getSizeGroup()), false); // Target method handles null sizes
+ }
+ }
+ }
+
+ if (hasTagged)
+ sortCellsByPlatform(grid.values(), container);
+
+ // Calculate gaps now that the cells are filled and we know all adjacent components.
+ boolean ltr = LayoutUtil.isLeftToRight(lc, container);
+ for (Iterator<Cell> it = grid.values().iterator(); it.hasNext();) {
+ Cell cell = it.next();
+ ArrayList<CompWrap> cws = cell.compWraps;
+
+ for (int i = 0, lastI = cws.size() - 1; i <= lastI; i++) {
+ CompWrap cw = cws.get(i);
+ ComponentWrapper cwBef = i > 0 ? cws.get(i - 1).comp : null;
+ ComponentWrapper cwAft = i < lastI ? cws.get(i + 1).comp : null;
+
+ String tag = fcMap.get(cw.comp).getTag();
+ CC ccBef = cwBef != null ? fcMap.get(cwBef) : null;
+ CC ccAft = cwAft != null ? fcMap.get(cwAft) : null;
+
+ cw.calcGaps(cwBef, ccBef, cwAft, ccAft, tag, cell.flowx, ltr);
+ }
+ }
+
+ dockOffX = getDockInsets(colIndexes);
+ dockOffY = getDockInsets(rowIndexes);
+
+ // Add synthetic indexes for empty rows and columns so they can get a size
+ for (int i = 0, iSz = rowConstr.getCount(); i < iSz; i++)
+ rowIndexes.add(new Integer(i));
+ for (int i = 0, iSz = colConstr.getCount(); i < iSz; i++)
+ colIndexes.add(new Integer(i));
+
+ colGroupLists = divideIntoLinkedGroups(false);
+ rowGroupLists = divideIntoLinkedGroups(true);
+
+ growXs = getDefaultGrowWeights(hasPushX, false);
+ growYs = getDefaultGrowWeights(hasPushY, true);
+
+ }
+
+ /** If the container (parent) that this grid is laying out has changed its bounds, call this method to
+ * clear any cached values.
+ */
+ public void invalidateContainerSize()
+ {
+ colFlowSpecs = null;
+ }
+
+ /** Does the actual layout. Uses many values calculated in the constructor.
+ * @param bounds The bounds to layout against. Normally that of the parent. [x, y, width, height].
+ * @param alignX The alignment for the x-axis.
+ * @param alignY The alignment for the y-axis.
+ * @param debug If debug information should be saved in {@link #debugRects}.
+ * @param checkPrefChange If a check should be done to see if the setting of any new bounds changes the preferred size
+ * of a component. This is normally only turned on for SWT as SWT has a notion of width vs height calculation.
+ * @return If the layout has probably change the preferred size and there is need for a new layout (normally only SWT).
+ */
+ public boolean layout(int[] bounds, UnitValue alignX, UnitValue alignY, boolean debug, boolean checkPrefChange)
+ {
+ if (debug)
+ debugRects = new ArrayList<int[]>();
+
+ checkSizeCalcs();
+
+ resetLinkValues(true);
+
+ layoutInOneDim(bounds[2], alignX, false, growXs);
+ layoutInOneDim(bounds[3], alignY, true, growYs);
+
+ HashMap<String, Integer> endGrpXMap = null, endGrpYMap = null;
+ int compCount = container.getComponentCount();
+
+ // Transfer the calculated bound from the ComponentWrappers to the actual Components.
+ boolean layoutAgain = false;
+ if (compCount > 0) {
+ for (int j = 0; j < (linkTargetIDs != null ? 2 : 1); j++) { // First do the calculations (maybe more than once) then set the bounds when done
+ boolean doAgain;
+ int count = 0;
+ do {
+ doAgain = false;
+ for (Iterator<Cell> it = grid.values().iterator(); it.hasNext();) {
+ ArrayList<CompWrap> compWraps = it.next().compWraps;
+ for (int i = 0, iSz = compWraps.size(); i < iSz; i++) {
+ CompWrap cw = compWraps.get(i);
+
+ if (j == 0) {
+ doAgain |= doAbsoluteCorrections(cw, bounds);
+ if (doAgain == false) { // If we are going to do this again, do not bother this time around
+ if (cw.cc.getHorizontal().getEndGroup() != null)
+ endGrpXMap = addToEndGroup(endGrpXMap, cw.cc.getHorizontal().getEndGroup(), cw.x + cw.w);
+ if (cw.cc.getVertical().getEndGroup() != null)
+ endGrpYMap = addToEndGroup(endGrpYMap, cw.cc.getVertical().getEndGroup(), cw.y + cw.h);
+ }
+ }
+
+ if (linkTargetIDs == null || j == 1) {
+ if (cw.cc.getHorizontal().getEndGroup() != null)
+ cw.w = endGrpXMap.get(cw.cc.getHorizontal().getEndGroup()).intValue() - cw.x;
+ if (cw.cc.getVertical().getEndGroup() != null)
+ cw.h = endGrpYMap.get(cw.cc.getVertical().getEndGroup()).intValue() - cw.y;
+
+ cw.x += bounds[0];
+ cw.y += bounds[1];
+ layoutAgain = cw.transferBounds(checkPrefChange && !layoutAgain);
+
+ if (callbackList != null) {
+ for (int cb = 0; cb < callbackList.size(); cb++)
+ callbackList.get(cb).correctBounds(cw.comp);
+ }
+ }
+ }
+ }
+ clearGroupLinkBounds();
+ if (++count > ((compCount << 3) + 10)) {
+ System.err.println("Unstable Cyclic Dependency in absolute-linked values!");
+ break;
+ }
+
+ } while (doAgain);
+ }
+ }
+
+ // Add debug shapes for the "cells". Use the CompWraps as base for inding the cells.
+ if (debug) {
+ Collection<Cell> cwColl = grid.values();
+ for (Iterator<Cell> it = cwColl.iterator(); it.hasNext();) {
+ ArrayList<CompWrap> compWraps = it.next().compWraps;
+ for (int i = 0, iSz = compWraps.size(); i < iSz; i++) {
+ CompWrap cw = compWraps.get(i);
+ LinkedDimGroup hGrp = getGroupContaining(colGroupLists, cw);
+ LinkedDimGroup vGrp = getGroupContaining(rowGroupLists, cw);
+
+ if (hGrp != null && vGrp != null)
+ 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});
+ }
+ }
+ }
+ return layoutAgain;
+ }
+
+ public void paintDebug()
+ {
+ if (debugRects != null) {
+ container.paintDebugOutline();
+
+ ArrayList<int[]> painted = new ArrayList<int[]>();
+ for (int i = 0, iSz = debugRects.size(); i < iSz; i++) {
+ int[] r = debugRects.get(i);
+ if (painted.contains(r) == false) {
+ container.paintDebugCell(r[0], r[1], r[2], r[3]);
+ painted.add(r);
+ }
+ }
+
+ for (Iterator<Cell> it = grid.values().iterator(); it.hasNext();) {
+ ArrayList<CompWrap> compWraps = it.next().compWraps;
+ for (int i = 0, iSz = compWraps.size(); i < iSz; i++)
+ compWraps.get(i).comp.paintDebugOutline();
+ }
+ }
+ }
+
+ public final int[] getWidth()
+ {
+ checkSizeCalcs();
+ return width;
+ }
+
+ public final int[] getHeight()
+ {
+ checkSizeCalcs();
+ return height;
+ }
+
+ private void checkSizeCalcs()
+ {
+ if (colFlowSpecs == null) {
+ colFlowSpecs = calcRowsOrColsSizes(colGroupLists, growXs, container.getWidth(), true);
+ rowFlowSpecs = calcRowsOrColsSizes(rowGroupLists, growYs, container.getHeight(), false);
+
+ width = getMinPrefMaxSumSize(colFlowSpecs.sizes);
+ height = getMinPrefMaxSumSize(rowFlowSpecs.sizes);
+
+ resetLinkValues(false);
+
+ adjustSizeForAbsolute(width, true);
+ adjustSizeForAbsolute(height, false);
+ }
+ }
+
+ private final UnitValue[] getPos(ComponentWrapper cw, CC cc)
+ {
+ UnitValue[] cbPos = null;
+ if (callbackList != null) {
+ for (int i = 0; i < callbackList.size() && cbPos == null; i++)
+ cbPos = callbackList.get(i).getPosition(cw); // NOT a copy!
+ }
+
+ // If one is null, return the other (which many also be null)
+ UnitValue[] ccPos = cc.getPos(); // A copy!!
+ if (cbPos == null || ccPos == null)
+ return cbPos != null ? cbPos : ccPos;
+
+ // Merge
+ for (int i = 0; i < 4; i++) {
+ UnitValue cbUv = cbPos[i];
+ if (cbUv != null)
+ ccPos[i] = cbUv;
+ }
+
+ return ccPos;
+ }
+ private final BoundSize[] getCallbackSize(ComponentWrapper cw, CC cc)
+ {
+ if (callbackList != null) {
+ for (int i = 0; i < callbackList.size(); i++) {
+ BoundSize[] bs = callbackList.get(i).getSize(cw); // NOT a copy!
+ if (bs != null)
+ return bs;
+ }
+ }
+ return null;
+ }
+
+ private final int getDockInsets(TreeSet<Integer> set)
+ {
+ int c = 0;
+ for (Iterator<Integer> it = set.iterator(); it.hasNext();) {
+ if (it.next().intValue() < -MAX_GRID) {
+ c++;
+ } else {
+ break; // Since they are sorted we can break
+ }
+ }
+ return c;
+ }
+
+ /**
+ * @param cw Never <code>null</code>.
+ * @param fc Never <code>null</code>.
+ * @param external The bounds should be stored even if they are not in {@link #linkTargetIDs}.
+ * @return IF a change has been done.
+ */
+ private boolean setLinkedBounds(ComponentWrapper cw, CC fc, int x, int y, int w, int h, boolean external)
+ {
+ String id = fc.getId() != null ? fc.getId() : cw.getLinkId();
+ if (id == null)
+ return false;
+
+ String gid = null;
+ int grIx = id.indexOf('.');
+ if (grIx != -1 ) {
+ gid = id.substring(0, grIx);
+ id = id.substring(grIx + 1);
+ }
+
+ Object lay = container.getLayout();
+ boolean changed = false;
+ if (external || (linkTargetIDs != null && linkTargetIDs.containsKey(id)))
+ changed = LinkHandler.setBounds(lay, id, x, y, w, h, !external, false);
+
+ if (gid != null && (external || (linkTargetIDs != null && linkTargetIDs.containsKey(gid)))) {
+ if (linkTargetIDs == null)
+ linkTargetIDs = new HashMap<String, Boolean>(4);
+
+ linkTargetIDs.put(gid, Boolean.TRUE);
+ changed |= LinkHandler.setBounds(lay, gid, x, y, w, h, !external, true);
+ }
+
+ return changed;
+ }
+
+ private void addLinkTargetIDs(UnitValue uv)
+ {
+ if (uv.isLinked()) {
+ if (linkTargetIDs == null)
+ linkTargetIDs = new HashMap<String, Boolean>(4);
+
+ linkTargetIDs.put(uv.getLinkTargetId(), null);
+ } else {
+ for (int i = uv.getSubUnitCount() - 1; i >= 0; i--) {
+ UnitValue subUv = uv.getSubUnitValue(i);
+ if (subUv.isLinkedDeep())
+ addLinkTargetIDs(subUv);
+ }
+ }
+ }
+
+ /** Go to next cell.
+ * @param p The point to increase
+ * @param cnt How many cells to advance.
+ * @return The new value in the "incresing" dimension.
+ */
+ private final int increase(int[] p, int cnt)
+ {
+ return lc.isFlowX() ? (p[0] += cnt) : (p[1] += cnt);
+ }
+
+ /** Wraps to the next row or column depending on if horizontal flow or vertical flow is used.
+ * @param p The point to wrap and thus set either x or y to 0 and increase the other one.
+ * @param gapSize The gaps size specified in a "wrap XXX" or "newline XXX" or <code>null</code> if none.
+ * @param isWrap If gapSize is from a "wrap" (true) or "newline" (false).
+ */
+ private final void wrap(int[] p, BoundSize gapSize, boolean isWrap)
+ {
+ boolean flowx = lc.isFlowX();
+ p[0] = flowx ? 0 : p[0] + 1;
+ p[1] = flowx ? p[1] + 1 : 0;
+
+ if (gapSize != null) {
+ if (wrapGapMap == null)
+ wrapGapMap = new HashMap<Integer, BoundSize>(8);
+ int ix = p[flowx ? 1 : 0];
+ if (isWrap == false)
+ ix--; // newline. Use index before increment.
+ wrapGapMap.put(new Integer(ix), gapSize);
+
+ // add the row/column so that the gap in the last row/col will not be removed.
+ if (flowx) {
+ rowIndexes.add(new Integer(p[1]));
+ } else {
+ colIndexes.add(new Integer(p[0]));
+ }
+ }
+ }
+
+ /** Sort components (normally buttons in a button bar) so they appear in the correct order.
+ * @param cells The cells to sort.
+ * @param parent The parent.
+ */
+ private static void sortCellsByPlatform(Collection<Cell> cells, ContainerWrapper parent)
+ {
+ String order = PlatformDefaults.getButtonOrder();
+ String orderLo = order.toLowerCase();
+
+ int unrelSize = PlatformDefaults.convertToPixels(1, "u", true, 0, parent, null);
+
+ if (unrelSize == UnitConverter.UNABLE)
+ throw new IllegalArgumentException("'unrelated' not recognized by PlatformDefaults!");
+
+ int[] gapUnrel = new int[] {unrelSize, unrelSize, LayoutUtil.NOT_SET};
+ int[] flGap = new int[] {0, 0, LayoutUtil.NOT_SET};
+
+ for (Iterator<Cell> it = cells.iterator(); it.hasNext();) {
+ Cell cell = it.next();
+ if (cell.hasTagged == false)
+ continue;
+
+ CompWrap prevCW = null;
+ boolean nextUnrel = false;
+ boolean nextPush = false;
+ ArrayList<CompWrap> sortedList = new ArrayList<CompWrap>(cell.compWraps.size());
+
+ for (int i = 0, iSz = orderLo.length(); i < iSz; i++) {
+ char c = orderLo.charAt(i);
+ if (c == '+' || c == '_') {
+ nextUnrel = true;
+ if (c == '+')
+ nextPush = true;
+ } else {
+ String tag = PlatformDefaults.getTagForChar(c);
+ if (tag != null) {
+ for (int j = 0, jSz = cell.compWraps.size(); j < jSz; j++) {
+ CompWrap cw = cell.compWraps.get(j);
+ if (tag.equals(cw.cc.getTag())) {
+ if (Character.isUpperCase(order.charAt(i))) {
+ int min = PlatformDefaults.getMinimumButtonWidth().getPixels(0, parent, cw.comp);
+ if (min > cw.horSizes[LayoutUtil.MIN])
+ cw.horSizes[LayoutUtil.MIN] = min;
+
+ correctMinMax(cw.horSizes);
+ }
+
+ sortedList.add(cw);
+
+ if (nextUnrel) {
+ (prevCW != null ? prevCW : cw).mergeGapSizes(gapUnrel, cell.flowx, prevCW == null);
+ if (nextPush) {
+ cw.forcedPushGaps = 1;
+ nextUnrel = false;
+ nextPush = false;
+ }
+ }
+
+ // "unknown" components will always get an Unrelated gap.
+ if (c == 'u')
+ nextUnrel = true;
+ prevCW = cw;
+ }
+ }
+ }
+ }
+ }
+
+ // If we have a gap that was supposed to push but no more components was found to but the "gap before" then compensate.
+ if (sortedList.size() > 0) {
+ CompWrap cw = sortedList.get(sortedList.size() - 1);
+ if (nextUnrel) {
+ cw.mergeGapSizes(gapUnrel, cell.flowx, false);
+ if (nextPush)
+ cw.forcedPushGaps |= 2;
+ }
+
+ // Remove first and last gap if not set explicitly.
+ if (cw.cc.getHorizontal().getGapAfter() == null)
+ cw.setGaps(flGap, 3);
+
+ cw = sortedList.get(0);
+ if (cw.cc.getHorizontal().getGapBefore() == null)
+ cw.setGaps(flGap, 1);
+ }
+
+ // Exchange the unsorted CompWraps for the sorted one.
+ if (cell.compWraps.size() == sortedList.size()) {
+ cell.compWraps.clear();
+ } else {
+ cell.compWraps.removeAll(sortedList);
+ }
+ cell.compWraps.addAll(sortedList);
+ }
+ }
+
+ private Float[] getDefaultGrowWeights(boolean hasPush, boolean isRows)
+ {
+ if (hasPush == false && (isRows ? lc.isFillY() : lc.isFillX()) == false)
+ return null;
+
+ ArrayList<LinkedDimGroup>[] groupLists = isRows ? rowGroupLists : colGroupLists;
+
+ Float[] gwArr = GROW_100; // Only create specific if any of the components have grow.
+ for (int i = 0, ix = 1; i < groupLists.length; i++, ix += 2) {
+ ArrayList<LinkedDimGroup> grps = groupLists[i];
+ Float rowGw = null;
+ for (int j = 0; j < grps.size(); j++) {
+ LinkedDimGroup grp = grps.get(j);
+
+ for (int c = 0; c < grp._compWraps.size(); c++) {
+ CompWrap cw = grp._compWraps.get(c);
+ Float gw = hasPush ? (isRows ? cw.cc.getPushY() : cw.cc.getPushX()) : (isRows ? cw.cc.getVertical() : cw.cc.getHorizontal()).getGrow();
+ if (rowGw == null || (gw != null && gw.floatValue() > rowGw.floatValue()))
+ rowGw = gw;
+ }
+ }
+
+ if (rowGw != null) {
+ if (gwArr == GROW_100)
+ gwArr = new Float[(groupLists.length << 1) + 1];
+ gwArr[ix] = rowGw;
+ }
+ }
+
+ return gwArr;
+ }
+
+ private void clearGroupLinkBounds()
+ {
+ if (linkTargetIDs == null)
+ return;
+
+ for (Iterator<Map.Entry<String,Boolean>> it = linkTargetIDs.entrySet().iterator(); it.hasNext();) {
+ Map.Entry<String, Boolean> o = it.next();
+ if (o.getValue() == Boolean.TRUE)
+ LinkHandler.clearBounds(container.getLayout(), o.getKey());
+ }
+ }
+
+ private void resetLinkValues(boolean parentSize)
+ {
+ Object lay = container.getLayout();
+ LinkHandler.clearTemporaryBounds(lay);
+
+ boolean defIns = !hasDocks();
+
+ int parW = parentSize ? container.getWidth() : 0;
+ int parH = parentSize ? container.getHeight() : 0;
+ int insX = LayoutUtil.getInsets(lc, 0, defIns).getPixels(0, container, null);
+ int insY = LayoutUtil.getInsets(lc, 1, defIns).getPixels(0, container, null);
+ int insW = parW - insX - LayoutUtil.getInsets(lc, 2, defIns).getPixels(0, container, null);
+ int insH = parH - insY - LayoutUtil.getInsets(lc, 3, defIns).getPixels(0, container, null);
+
+ LinkHandler.setBounds(lay, "visual", insX, insY, insW, insH, true, false);
+ LinkHandler.setBounds(lay, "container", 0, 0, parW, parH, true, false);
+ }
+
+ /** Returns the {@link net.miginfocom.layout.Grid.LinkedDimGroup} that has the {@link net.miginfocom.layout.Grid.CompWrap}
+ * <code>cw</code>.
+ * @param groupLists The lists to search in.
+ * @param cw The component wrap to find.
+ * @return The linked group or <code>null</code> if none had the component wrap.
+ */
+ private static LinkedDimGroup getGroupContaining(ArrayList<LinkedDimGroup>[] groupLists, CompWrap cw)
+ {
+ for (int i = 0; i < groupLists.length; i++) {
+ ArrayList<LinkedDimGroup> groups = groupLists[i];
+ for (int j = 0, jSz = groups.size(); j < jSz; j++) {
+ ArrayList<CompWrap> cwList = groups.get(j)._compWraps;
+ for (int k = 0, kSz = cwList.size(); k < kSz; k++) {
+ if (cwList.get(k) == cw)
+ return groups.get(j);
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean doAbsoluteCorrections(CompWrap cw, int[] bounds)
+ {
+ boolean changed = false;
+
+ int[] stSz = getAbsoluteDimBounds(cw, bounds[2], true);
+ if (stSz != null)
+ cw.setDimBounds(stSz[0], stSz[1], true);
+
+ stSz = getAbsoluteDimBounds(cw, bounds[3], false);
+ if (stSz != null)
+ cw.setDimBounds(stSz[0], stSz[1], false);
+
+ // If there is a link id, store the new bounds.
+ if (linkTargetIDs != null)
+ changed = setLinkedBounds(cw.comp, cw.cc, cw.x, cw.y, cw.w, cw.h, false);
+
+ return changed;
+ }
+
+ private void adjustSizeForAbsolute(int[] curSizes, boolean isHor)
+ {
+ Cell absCell = grid.get(null);
+ if (absCell == null || absCell.compWraps.size() == 0)
+ return;
+
+ ArrayList<CompWrap> cws = absCell.compWraps;
+
+ int maxEnd = 0;
+ for (int j = 0, cwSz = absCell.compWraps.size(); j < cwSz + 3; j++) { // "Do Again" max absCell.compWraps.size() + 3 times.
+ boolean doAgain = false;
+ for (int i = 0; i < cwSz; i++) {
+ CompWrap cw = cws.get(i);
+ int[] stSz = getAbsoluteDimBounds(cw, 0, isHor);
+ int end = stSz[0] + stSz[1];
+ if (maxEnd < end)
+ maxEnd = end;
+
+ // If there is a link id, store the new bounds.
+ if (linkTargetIDs != null)
+ doAgain |= setLinkedBounds(cw.comp, cw.cc, stSz[0], stSz[0], stSz[1], stSz[1], false);
+ }
+ if (doAgain == false)
+ break;
+
+ // We need to check this again since the coords may be smaller this round.
+ maxEnd = 0;
+ clearGroupLinkBounds();
+ }
+
+ maxEnd += LayoutUtil.getInsets(lc, isHor ? 3 : 2, !hasDocks()).getPixels(0, container, null);
+
+ if (curSizes[LayoutUtil.MIN] < maxEnd)
+ curSizes[LayoutUtil.MIN] = maxEnd;
+ if (curSizes[LayoutUtil.PREF] < maxEnd)
+ curSizes[LayoutUtil.PREF] = maxEnd;
+ }
+
+ private int[] getAbsoluteDimBounds(CompWrap cw, int refSize, boolean isHor)
+ {
+ if (cw.cc.isExternal()) {
+ if (isHor) {
+ return new int[] {cw.comp.getX(), cw.comp.getWidth()};
+ } else {
+ return new int[] {cw.comp.getY(), cw.comp.getHeight()};
+ }
+ }
+
+ int[] plafPad = lc.isVisualPadding() ? cw.comp.getVisualPadding() : null;
+ UnitValue[] pad = cw.cc.getPadding();
+
+ // If no changes do not create a lot of objects
+ if (cw.pos == null && plafPad == null && pad == null)
+ return null;
+
+ // Set start
+ int st = isHor ? cw.x : cw.y;
+ int sz = isHor ? cw.w : cw.h;
+
+ // If absolute, use those coordinates instead.
+ if (cw.pos != null) {
+ UnitValue stUV = cw.pos != null ? cw.pos[isHor ? 0 : 1] : null;
+ UnitValue endUV = cw.pos != null ? cw.pos[isHor ? 2 : 3] : null;
+
+ int minSz = cw.getSize(LayoutUtil.MIN, isHor);
+ int maxSz = cw.getSize(LayoutUtil.MAX, isHor);
+ sz = Math.min(Math.max(cw.getSize(LayoutUtil.PREF, isHor), minSz), maxSz);
+
+ if (stUV != null) {
+ st = stUV.getPixels(stUV.getUnit() == UnitValue.ALIGN ? sz : refSize, container, cw.comp);
+
+ if (endUV != null) // if (endUV == null && cw.cc.isBoundsIsGrid() == true)
+ sz = Math.min(Math.max((isHor ? (cw.x + cw.w) : (cw.y + cw.h)) - st, minSz), maxSz);
+ }
+
+ if (endUV != null) {
+ if (stUV != null) { // if (stUV != null || cw.cc.isBoundsIsGrid()) {
+ sz = Math.min(Math.max(endUV.getPixels(refSize, container, cw.comp) - st, minSz), maxSz);
+ } else {
+ st = endUV.getPixels(refSize, container, cw.comp) - sz;
+ }
+ }
+ }
+
+ // If constraint has padding -> correct the start/size
+ if (pad != null) {
+ UnitValue uv = pad[isHor ? 1 : 0];
+ int p = uv != null ? uv.getPixels(refSize, container, cw.comp) : 0;
+ st += p;
+ uv = pad[isHor ? 3 : 2];
+ sz += -p + (uv != null ? uv.getPixels(refSize, container, cw.comp) : 0);
+ }
+
+ // If the plaf converter has padding -> correct the start/size
+ if (plafPad != null) {
+ int p = plafPad[isHor ? 1 : 0];
+ st += p;
+ sz += -p + (plafPad[isHor ? 3 : 2]);
+ }
+
+ return new int[] {st, sz};
+ }
+
+ private void layoutInOneDim(int refSize, UnitValue align, boolean isRows, Float[] defaultGrowW)
+ {
+ boolean fromEnd = !(isRows ? lc.isTopToBottom() : LayoutUtil.isLeftToRight(lc, container));
+ DimConstraint[] primDCs = (isRows ? rowConstr : colConstr).getConstaints();
+ FlowSizeSpec fss = isRows ? rowFlowSpecs : colFlowSpecs;
+ ArrayList<LinkedDimGroup>[] rowCols = isRows ? rowGroupLists : colGroupLists;
+
+ int[] rowColSizes = LayoutUtil.calculateSerial(fss.sizes, fss.resConstsInclGaps, defaultGrowW, LayoutUtil.PREF, refSize);
+
+ if (LayoutUtil.isDesignTime(container)) {
+ TreeSet<Integer> indexes = isRows ? rowIndexes : colIndexes;
+ int[] ixArr = new int[indexes.size()];
+ int ix = 0;
+ for (Integer i : indexes)
+ ixArr[ix++] = i.intValue();
+
+ putSizesAndIndexes(container.getComponent(), rowColSizes, ixArr, isRows);
+ }
+
+ int curPos = align != null ? Math.round(align.getPixels(refSize - LayoutUtil.sum(rowColSizes), container, null)) : 0;
+
+ if (fromEnd)
+ curPos = refSize - curPos;
+
+ for (int i = 0 ; i < rowCols.length; i++) {
+ ArrayList<LinkedDimGroup> linkedGroups = rowCols[i];
+ int scIx = i - (isRows ? dockOffY : dockOffX);
+
+ int bIx = i << 1;
+ int bIx2 = bIx + 1;
+
+ curPos += (fromEnd ? -rowColSizes[bIx] : rowColSizes[bIx]);
+
+ DimConstraint primDC = scIx >= 0 ? primDCs[scIx >= primDCs.length ? primDCs.length - 1 : scIx] : DOCK_DIM_CONSTRAINT;
+
+ int rowSize = rowColSizes[bIx2];
+
+ for (int j = 0; j < linkedGroups.size(); j++) {
+ LinkedDimGroup group = linkedGroups.get(j);
+ int groupSize = rowSize;
+ if (group.span > 1)
+ groupSize = LayoutUtil.sum(rowColSizes, bIx2, Math.min((group.span << 1) - 1, rowColSizes.length - bIx2 - 1));
+
+ group.layout(primDC, curPos, groupSize, group.span);
+ }
+
+ curPos += (fromEnd ? -rowSize : rowSize);
+ }
+ }
+
+ private void addToSizeGroup(HashMap<String, int[]> sizeGroups, String sizeGroup, int[] size)
+ {
+ if (sizeGroups == null)
+ return;
+
+ int[] sgSize = sizeGroups.get(sizeGroup);
+ if (sgSize == null) {
+ sizeGroups.put(sizeGroup, new int[] {size[LayoutUtil.MIN], size[LayoutUtil.PREF], size[LayoutUtil.MAX]});
+ } else {
+ sgSize[LayoutUtil.MIN] = Math.max(size[LayoutUtil.MIN], sgSize[LayoutUtil.MIN]);
+ sgSize[LayoutUtil.PREF] = Math.max(size[LayoutUtil.PREF], sgSize[LayoutUtil.PREF]);
+ sgSize[LayoutUtil.MAX] = Math.min(size[LayoutUtil.MAX], sgSize[LayoutUtil.MAX]);
+ }
+ }
+
+ private HashMap<String, Integer> addToEndGroup(HashMap<String, Integer> endGroups, String endGroup, int end)
+ {
+ if (endGroup != null) {
+ if (endGroups == null)
+ endGroups = new HashMap<String, Integer>(2);
+
+ Integer oldEnd = endGroups.get(endGroup);
+ if (oldEnd == null || end > oldEnd.intValue())
+ endGroups.put(endGroup, new Integer(end));
+ }
+ return endGroups;
+ }
+
+ /** Calculates Min, Preferred and Max size for the columns OR rows.
+ * @param defGrow The default grow weight if the specs does not have anyone that will grow. Comes from "push" in the CC.
+ * @return The sizes in a {@link net.miginfocom.layout.Grid.FlowSizeSpec}.
+ */
+ private FlowSizeSpec calcRowsOrColsSizes(ArrayList<LinkedDimGroup>[] groupsLists, Float[] defGrow, int refSize, boolean isHor)
+ {
+ DimConstraint[] primDCs = (isHor? colConstr : rowConstr).getConstaints();
+ TreeSet<Integer> primIdexes = isHor ? colIndexes : rowIndexes;
+
+ int[][] rowColBoundSizes = new int[primIdexes.size()][];
+ HashMap<String, int[]> sizeGroupMap = new HashMap<String, int[]>(2);
+ DimConstraint[] allDCs = new DimConstraint[primIdexes.size()];
+
+ Iterator<Integer> primIt = primIdexes.iterator();
+ for (int r = 0; r < rowColBoundSizes.length; r++) {
+ int cellIx = primIt.next().intValue();
+ int[] rowColSizes = new int[3];
+
+ if (cellIx >= -MAX_GRID && cellIx <= MAX_GRID) { // If not dock cell
+ allDCs[r] = primDCs[cellIx >= primDCs.length ? primDCs.length - 1 : cellIx];
+ } else {
+ allDCs[r] = DOCK_DIM_CONSTRAINT;
+ }
+
+ ArrayList<LinkedDimGroup> groups = groupsLists[r];
+
+ int[] groupSizes = new int[] {
+ getTotalGroupsSizeParallel(groups, LayoutUtil.MIN, false),
+ getTotalGroupsSizeParallel(groups, LayoutUtil.PREF, false),
+ LayoutUtil.INF};
+
+ correctMinMax(groupSizes);
+ BoundSize dimSize = allDCs[r].getSize();
+
+ for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.MAX; sType++) {
+
+ int rowColSize = groupSizes[sType];
+
+ UnitValue uv = dimSize != null ? dimSize.getSize(sType) : null;
+ if (uv != null) {
+ // If the size of the column is a link to some other size, use that instead
+ int unit = uv.getUnit();
+ if (unit == UnitValue.PREF_SIZE) {
+ rowColSize = groupSizes[LayoutUtil.PREF];
+ } else if (unit == UnitValue.MIN_SIZE) {
+ rowColSize = groupSizes[LayoutUtil.MIN];
+ } else if (unit == UnitValue.MAX_SIZE) {
+ rowColSize = groupSizes[LayoutUtil.MAX];
+ } else {
+ rowColSize = uv.getPixels(refSize, container, null);
+ }
+ } else if (cellIx >= -MAX_GRID && cellIx <= MAX_GRID && rowColSize == 0) {
+ rowColSize = LayoutUtil.isDesignTime(container) ? LayoutUtil.getDesignTimeEmptySize() : 0; // Empty rows with no size set gets XX pixels if design time
+ }
+
+ rowColSizes[sType] = rowColSize;
+ }
+
+ correctMinMax(rowColSizes);
+ addToSizeGroup(sizeGroupMap, allDCs[r].getSizeGroup(), rowColSizes);
+
+ rowColBoundSizes[r] = rowColSizes;
+ }
+
+ // Set/equalize the size groups to same the values.
+ if (sizeGroupMap.size() > 0) {
+ for (int r = 0; r < rowColBoundSizes.length; r++) {
+ if (allDCs[r].getSizeGroup() != null)
+ rowColBoundSizes[r] = sizeGroupMap.get(allDCs[r].getSizeGroup());
+ }
+ }
+
+ // Add the gaps
+ ResizeConstraint[] resConstrs = getRowResizeConstraints(allDCs);
+
+ boolean[] fillInPushGaps = new boolean[allDCs.length + 1];
+ int[][] gapSizes = getRowGaps(allDCs, refSize, isHor, fillInPushGaps);
+
+
+ FlowSizeSpec fss = mergeSizesGapsAndResConstrs(resConstrs, fillInPushGaps, rowColBoundSizes, gapSizes, refSize, container);
+
+ // Spanning components are not handled yet. Check and adjust the multi-row min/pref they enforce.
+ adjustMinPrefForSpanningComps(allDCs, defGrow, fss, groupsLists);
+
+ return fss;
+ }
+
+ private static int[] getMinPrefMaxSumSize(int[][] sizes)
+ {
+ int[] retSizes = new int[3];
+
+ for (int i = 0; i < sizes.length; i++) {
+ if (sizes[i] != null) {
+ int[] size = sizes[i];
+ for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.MAX; sType++) {
+ int s = size[sType];
+
+ if (s != LayoutUtil.NOT_SET) {
+ if (sType == LayoutUtil.PREF) {
+ int bnd = size[LayoutUtil.MAX];
+ if (bnd != LayoutUtil.NOT_SET && bnd < s)
+ s = bnd;
+
+ bnd = size[LayoutUtil.MIN];
+ if (bnd > s) // Includes s == LayoutUtil.NOT_SET since < 0.
+ s = bnd;
+ }
+
+ retSizes[sType] += s; // MAX compensated below.
+ }
+ }
+
+ // So that MAX is always correct.
+ if (size[LayoutUtil.MAX] == LayoutUtil.NOT_SET)
+ retSizes[LayoutUtil.MAX] = LayoutUtil.INF;
+ }
+ }
+
+ return retSizes;
+ }
+
+ private static ResizeConstraint[] getRowResizeConstraints(DimConstraint[] specs)
+ {
+ ResizeConstraint[] resConsts = new ResizeConstraint[specs.length];
+ for (int i = 0; i < resConsts.length; i++)
+ resConsts[i] = specs[i].resize;
+ return resConsts;
+ }
+
+ private static ResizeConstraint[] getComponentResizeConstraints(ArrayList<CompWrap> compWraps, boolean isHor)
+ {
+ ResizeConstraint[] resConsts = new ResizeConstraint[compWraps.size()];
+ for (int i = 0; i < resConsts.length; i++) {
+ CC fc = compWraps.get(i).cc;
+ resConsts[i] = fc.getDimConstraint(isHor).resize;
+
+ // Always grow docking components in the correct dimension.
+ int dock = fc.getDockSide();
+ if (isHor ? (dock == 0 || dock == 2) : (dock == 1 || dock == 3)) {
+ ResizeConstraint dc = resConsts[i];
+ resConsts[i] = new ResizeConstraint(dc.shrinkPrio, dc.shrink, dc.growPrio, ResizeConstraint.WEIGHT_100);
+ }
+ }
+ return resConsts;
+ }
+
+ private static boolean[] getComponentGapPush(ArrayList<CompWrap> compWraps, boolean isHor)
+ {
+ // Make one element bigger and or the after gap with the next before gap.
+ boolean[] barr = new boolean[compWraps.size() + 1];
+ for (int i = 0; i < barr.length; i++) {
+
+ boolean push = i > 0 ? compWraps.get(i - 1).isPushGap(isHor, false) : false;
+
+ if (push == false && i < (barr.length - 1))
+ push = compWraps.get(i).isPushGap(isHor, true);
+
+ barr[i] = push;
+ }
+ return barr;
+ }
+
+ /** Returns the row gaps in pixel sizes. One more than there are <code>specs</code> sent in.
+ * @param specs
+ * @param refSize
+ * @param isHor
+ * @param fillInPushGaps If the gaps are pushing. <b>NOTE!</b> this argument will be filled in and thus changed!
+ * @return The row gaps in pixel sizes. One more than there are <code>specs</code> sent in.
+ */
+ private int[][] getRowGaps(DimConstraint[] specs, int refSize, boolean isHor, boolean[] fillInPushGaps)
+ {
+ BoundSize defGap = isHor ? lc.getGridGapX() : lc.getGridGapY();
+ if (defGap == null)
+ defGap = isHor ? PlatformDefaults.getGridGapX() : PlatformDefaults.getGridGapY();
+ int[] defGapArr = defGap.getPixelSizes(refSize, container, null);
+
+ boolean defIns = !hasDocks();
+
+ UnitValue firstGap = LayoutUtil.getInsets(lc, isHor ? 1 : 0, defIns);
+ UnitValue lastGap = LayoutUtil.getInsets(lc, isHor ? 3 : 2, defIns);
+
+ int[][] retValues = new int[specs.length + 1][];
+
+ for (int i = 0, wgIx = 0; i < retValues.length; i++) {
+ DimConstraint specBefore = i > 0 ? specs[i - 1] : null;
+ DimConstraint specAfter = i < specs.length ? specs[i] : null;
+
+ // No gap if between docking components.
+ boolean edgeBefore = (specBefore == DOCK_DIM_CONSTRAINT || specBefore == null);
+ boolean edgeAfter = (specAfter == DOCK_DIM_CONSTRAINT || specAfter == null);
+ if (edgeBefore && edgeAfter)
+ continue;
+
+ BoundSize wrapGapSize = (wrapGapMap == null || isHor == lc.isFlowX() ? null : wrapGapMap.get(new Integer(wgIx++)));
+
+ if (wrapGapSize == null) {
+
+ int[] gapBefore = specBefore != null ? specBefore.getRowGaps(container, null, refSize, false) : null;
+ int[] gapAfter = specAfter != null ? specAfter.getRowGaps(container, null, refSize, true) : null;
+
+ if (edgeBefore && gapAfter == null && firstGap != null) {
+
+ int bef = firstGap.getPixels(refSize, container, null);
+ retValues[i] = new int[] {bef, bef, bef};
+
+ } else if (edgeAfter && gapBefore == null && firstGap != null) {
+
+ int aft = lastGap.getPixels(refSize, container, null);
+ retValues[i] = new int[] {aft, aft, aft};
+
+ } else {
+ retValues[i] = gapAfter != gapBefore ? mergeSizes(gapAfter, gapBefore) : new int[] {defGapArr[0], defGapArr[1], defGapArr[2]};
+ }
+
+ if (specBefore != null && specBefore.isGapAfterPush() || specAfter != null && specAfter.isGapBeforePush())
+ fillInPushGaps[i] = true;
+ } else {
+
+ if (wrapGapSize.isUnset()) {
+ retValues[i] = new int[] {defGapArr[0], defGapArr[1], defGapArr[2]};
+ } else {
+ retValues[i] = wrapGapSize.getPixelSizes(refSize, container, null);
+ }
+ fillInPushGaps[i] = wrapGapSize.getGapPush();
+ }
+ }
+ return retValues;
+ }
+
+ private static int[][] getGaps(ArrayList<CompWrap> compWraps, boolean isHor)
+ {
+ int compCount = compWraps.size();
+ int[][] retValues = new int[compCount + 1][];
+
+ retValues[0] = compWraps.get(0).getGaps(isHor, true);
+ for (int i = 0; i < compCount; i++) {
+ int[] gap1 = compWraps.get(i).getGaps(isHor, false);
+ int[] gap2 = i < compCount - 1 ? compWraps.get(i + 1).getGaps(isHor, true) : null;
+
+ retValues[i + 1] = mergeSizes(gap1, gap2);
+ }
+
+ return retValues;
+ }
+
+ private boolean hasDocks()
+ {
+ return (dockOffX > 0 || dockOffY > 0 || rowIndexes.last() > MAX_GRID || colIndexes.last() > MAX_GRID);
+ }
+
+ /**
+ * @param specs The specs for the columns or rows. Last index will be used of <code>count</code> is greater than this array's length.
+ * @param defGrow The default grow weight if the specs does not have anyone that will grow. Comes from "push" in the CC.
+ * @param fss
+ * @param groupsLists
+ */
+ private void adjustMinPrefForSpanningComps(DimConstraint[] specs, Float[] defGrow, FlowSizeSpec fss, ArrayList<LinkedDimGroup>[] groupsLists)
+ {
+ for (int r = 0; r < groupsLists.length; r++) {
+ ArrayList<LinkedDimGroup> groups = groupsLists[r];
+
+ for (int i = 0; i < groups.size(); i++) {
+ LinkedDimGroup group = groups.get(i);
+ if (group.span == 1)
+ continue;
+
+ int cSize = group.getMinPrefMax()[LayoutUtil.PREF];
+ if (cSize == LayoutUtil.NOT_SET)
+ continue;
+
+ int rowSize = 0;
+ int sIx = (r << 1) + 1;
+ int len = Math.min((group.span << 1), fss.sizes.length - sIx) - 1;
+ for (int j = sIx; j < sIx + len; j++) {
+ int sz = fss.sizes[j][LayoutUtil.PREF];
+ if (sz != LayoutUtil.NOT_SET)
+ rowSize += sz;
+ }
+
+ if (rowSize < cSize) {
+ for (int eag = 0, newRowSize = 0; eag < 4 && newRowSize < cSize; eag++)
+ newRowSize = fss.expandSizes(specs, defGrow, cSize, sIx, len, LayoutUtil.PREF, eag);
+ }
+ }
+ }
+ }
+
+ /** For one dimension divide the component wraps into logical groups. One group for component wraps that share a common something,
+ * line the property to layout by base line.
+ * @param isRows If rows, and not columns, are to be divided.
+ * @return One <code>ArrayList<LinkedDimGroup></code> for every row/column.
+ */
+ private ArrayList<LinkedDimGroup>[] divideIntoLinkedGroups(boolean isRows)
+ {
+ boolean fromEnd = !(isRows ? lc.isTopToBottom() : LayoutUtil.isLeftToRight(lc, container));
+ TreeSet<Integer> primIndexes = isRows ? rowIndexes : colIndexes;
+ TreeSet<Integer> secIndexes = isRows ? colIndexes : rowIndexes;
+ DimConstraint[] primDCs = (isRows ? rowConstr : colConstr).getConstaints();
+
+ ArrayList<LinkedDimGroup>[] groupLists = new ArrayList[primIndexes.size()];
+
+ int gIx = 0;
+ for (Iterator<Integer> primIt = primIndexes.iterator(); primIt.hasNext();) {
+ int i = primIt.next().intValue();
+
+ DimConstraint dc;
+ if (i >= -MAX_GRID && i <= MAX_GRID) { // If not dock cell
+ dc = primDCs[i >= primDCs.length ? primDCs.length - 1 : i];
+ } else {
+ dc = DOCK_DIM_CONSTRAINT;
+ }
+
+ ArrayList<LinkedDimGroup> groupList = new ArrayList<LinkedDimGroup>(2);
+ groupLists[gIx++] = groupList;
+
+ for (Iterator<Integer> secIt = secIndexes.iterator(); secIt.hasNext();) {
+ int j = secIt.next().intValue();
+ Cell cell = isRows ? getCell(i, j) : getCell(j, i);
+ if (cell == null || cell.compWraps.size() == 0)
+ continue;
+
+ int span = (isRows ? cell.spany : cell.spanx);
+ if (span > 1)
+ span = convertSpanToSparseGrid(i, span, primIndexes);
+
+ boolean isPar = cell.flowx == isRows;
+
+ if ((isPar == false && cell.compWraps.size() > 1) || span > 1) {
+
+ int linkType = isPar ? LinkedDimGroup.TYPE_PARALLEL : LinkedDimGroup.TYPE_SERIAL;
+ LinkedDimGroup lg = new LinkedDimGroup("p," + j, span, linkType, !isRows, fromEnd);
+ lg.setCompWraps(cell.compWraps);
+ groupList.add(lg);
+ } else {
+ for (int cwIx = 0; cwIx < cell.compWraps.size(); cwIx++) {
+ CompWrap cw = cell.compWraps.get(cwIx);
+ boolean rowBaselineAlign = (isRows && dc.getAlignOrDefault(!isRows) == UnitValue.BASELINE_IDENTITY);
+
+ String linkCtx = cw.getLinkContext(rowBaselineAlign);
+
+ // Find a group with same link context and put it in that group.
+ boolean foundList = false;
+ for (int glIx = 0, lastGl = groupList.size() - 1; glIx <= lastGl ; glIx++) {
+ LinkedDimGroup group = groupList.get(glIx);
+ if (group.linkCtx == linkCtx || linkCtx != null && linkCtx.equals(group.linkCtx)) {
+ group.addCompWrap(cw);
+ foundList = true;
+ break;
+ }
+ }
+
+ // If none found and at last add a new group.
+ if (foundList == false) {
+
+ int linkType = LinkedDimGroup.TYPE_PARALLEL;
+ if (isRows && cw.isBaselineAlign(rowBaselineAlign))
+ linkType = LinkedDimGroup.TYPE_BASELINE;
+
+ LinkedDimGroup lg = new LinkedDimGroup(linkCtx, 1, linkType, !isRows, fromEnd);
+ lg.addCompWrap(cw);
+ groupList.add(lg);
+ }
+ }
+ }
+ }
+ }
+ return groupLists;
+ }
+
+ /** Spanning is specified in the uncompressed grid number. They can for instance be more than 60000 for the outer
+ * edge dock grid cells. When the grid is compressed and indexed after only the cells that area occupied the span
+ * is erratic. This method use the row/col indexes and corrects the span to be correct for the compressed grid.
+ * @param span The span un the uncompressed grid. <code>LayoutUtil.INF</code> will be interpreted to span the rest
+ * of the column/row excluding the surrounding docking components.
+ * @param indexes The indexes in the correct dimension.
+ * @return The converted span.
+ */
+ private int convertSpanToSparseGrid(int curIx, int span, TreeSet<Integer> indexes)
+ {
+ int lastIx = curIx + span;
+ int retSpan = 1;
+
+ for (Iterator<Integer> it = indexes.iterator(); it.hasNext();) {
+ int ix = it.next();
+ if (ix <= curIx)
+ continue; // We have not arrive to the correct index yet
+
+ if (ix >= lastIx)
+ break;
+
+ retSpan++;
+ }
+ return retSpan;
+ }
+
+ private final boolean isCellFree(int r, int c, ArrayList<int[]> occupiedRects)
+ {
+ if (getCell(r, c) != null)
+ return false;
+
+ for (int i = 0; i < occupiedRects.size(); i++) {
+ int[] rect = occupiedRects.get(i);
+ if (rect[0] <= c && rect[1] <= r && rect[0] + rect[2] > c && rect[1] + rect[3] > r)
+ return false;
+ }
+ return true;
+ }
+
+ private Cell getCell(int r, int c)
+ {
+ return grid.get(new Integer((r << 16) + c));
+ }
+
+ private void setCell(int r, int c, Cell cell)
+ {
+ if (c < 0 || c > MAX_GRID || r < 0 || r > MAX_GRID)
+ throw new IllegalArgumentException("Cell position out of bounds. row: " + r + ", col: " + c);
+
+ rowIndexes.add(new Integer(r));
+ colIndexes.add(new Integer(c));
+
+ grid.put(new Integer((r << 16) + c), cell);
+ }
+
+ /** Adds a docking cell. That cell is outside the normal cell indexes.
+ * @param dockInsets The current dock insets. Will be updated!
+ * @param side top == 0, left == 1, bottom = 2, right = 3.
+ * @param cw The compwrap to put in a cell and add.
+ */
+ private void addDockingCell(int[] dockInsets, int side, CompWrap cw)
+ {
+ int r, c, spanx = 1, spany = 1;
+ switch (side) {
+ case 0:
+ case 2:
+ r = side == 0 ? dockInsets[0]++ : dockInsets[2]--;
+ c = dockInsets[1];
+ spanx = dockInsets[3] - dockInsets[1] + 1; // The +1 is for cell 0.
+ colIndexes.add(new Integer(dockInsets[3])); // Make sure there is a receiving cell
+ break;
+
+ case 1:
+ case 3:
+ c = side == 1 ? dockInsets[1]++ : dockInsets[3]--;
+ r = dockInsets[0];
+ spany = dockInsets[2] - dockInsets[0] + 1; // The +1 is for cell 0.
+ rowIndexes.add(new Integer(dockInsets[2])); // Make sure there is a receiving cell
+ break;
+
+ default:
+ throw new IllegalArgumentException("Internal error 123.");
+ }
+
+ rowIndexes.add(new Integer(r));
+ colIndexes.add(new Integer(c));
+
+ grid.put(new Integer((r << 16) + c), new Cell(cw, spanx, spany, spanx > 1));
+ }
+
+ /** A simple representation of a cell in the grid. Contains a number of component wraps and if they span more than one cell.
+ */
+ private static class Cell
+ {
+ private final int spanx, spany;
+ private final boolean flowx;
+ private final ArrayList<CompWrap> compWraps = new ArrayList<CompWrap>(1);
+
+ private boolean hasTagged = false; // If one or more components have styles and need to be checked by the component sorter
+
+ private Cell(CompWrap cw)
+ {
+ this(cw, 1, 1, true);
+ }
+
+ private Cell(int spanx, int spany, boolean flowx)
+ {
+ this(null, spanx, spany, flowx);
+ }
+
+ private Cell(CompWrap cw, int spanx, int spany, boolean flowx)
+ {
+ if (cw != null)
+ compWraps.add(cw);
+ this.spanx = spanx;
+ this.spany = spany;
+ this.flowx = flowx;
+ }
+ }
+
+ /** A number of component wraps that share a layout "something" <b>in one dimension</b>
+ */
+ private static class LinkedDimGroup
+ {
+ private static final int TYPE_SERIAL = 0;
+ private static final int TYPE_PARALLEL = 1;
+ private static final int TYPE_BASELINE = 2;
+
+ private final String linkCtx;
+ private final int span;
+ private final int linkType;
+ private final boolean isHor, fromEnd;
+
+ private ArrayList<CompWrap> _compWraps = new ArrayList<CompWrap>(4);
+
+ private int[] sizes = null;
+ private int lStart = 0, lSize = 0; // Currently mostly for debug painting
+
+ private LinkedDimGroup(String linkCtx, int span, int linkType, boolean isHor, boolean fromEnd)
+ {
+ this.linkCtx = linkCtx;
+ this.span = span;
+ this.linkType = linkType;
+ this.isHor = isHor;
+ this.fromEnd = fromEnd;
+ }
+
+ private void addCompWrap(CompWrap cw)
+ {
+ _compWraps.add(cw);
+ sizes = null;
+ }
+
+ private void setCompWraps(ArrayList<CompWrap> cws)
+ {
+ if (_compWraps != cws) {
+ _compWraps = cws;
+ sizes = null;
+ }
+ }
+
+ private void layout(DimConstraint dc, int start, int size, int spanCount)
+ {
+ lStart = start;
+ lSize = size;
+
+ if (_compWraps.size() == 0)
+ return;
+
+ ContainerWrapper parent = _compWraps.get(0).comp.getParent();
+ if (linkType == TYPE_PARALLEL) {
+ layoutParallel(parent, _compWraps, dc, start, size, isHor, spanCount, fromEnd);
+ } else if (linkType == TYPE_BASELINE) {
+ layoutBaseline(parent, _compWraps, dc, start, size, LayoutUtil.PREF, spanCount);
+ } else {
+ layoutSerial(parent, _compWraps, dc, start, size, isHor, spanCount, fromEnd);
+ }
+ }
+
+ /** Returns the min/pref/max sizes for this cell. Returned array <b>must not be altered</b>
+ * @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.
+ */
+ private int[] getMinPrefMax()
+ {
+ if (sizes == null && _compWraps.size() > 0) {
+ sizes = new int[3];
+ for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.PREF; sType++) {
+ if (linkType == TYPE_PARALLEL) {
+ sizes[sType] = getTotalSizeParallel(_compWraps, sType, isHor);
+ } else if (linkType == TYPE_BASELINE) {
+ int[] aboveBelow = getBaselineAboveBelow(_compWraps, sType, false);
+ sizes[sType] = aboveBelow[0] + aboveBelow[1];
+ } else {
+ sizes[sType] = getTotalSizeSerial(_compWraps, sType, isHor);
+ }
+ }
+ sizes[LayoutUtil.MAX] = LayoutUtil.INF;
+ }
+ return sizes;
+ }
+ }
+
+ /** Wraps a {@link java.awt.Component} together with its constraint. Caches a lot of information about the component so
+ * for instance not the preferred size has to be calculated more than once.
+ */
+ private final static class CompWrap
+ {
+ private final ComponentWrapper comp;
+ private final CC cc;
+ private final UnitValue[] pos;
+ private int[][] gaps; // [top,left(actually before),bottom,right(actually after)][min,pref,max]
+
+ private final int[] horSizes = new int[3];
+ private final int[] verSizes = new int[3];
+
+ private int x = LayoutUtil.NOT_SET, y = LayoutUtil.NOT_SET, w = LayoutUtil.NOT_SET, h = LayoutUtil.NOT_SET;
+
+ private int forcedPushGaps = 0; // 1 == before, 2 = after. Bitwise.
+
+ private CompWrap(ComponentWrapper c, CC cc, int eHideMode, UnitValue[] pos, BoundSize[] callbackSz)
+ {
+ this.comp = c;
+ this.cc = cc;
+ this.pos = pos;
+
+ if (eHideMode <= 0) {
+ BoundSize hBS = (callbackSz != null && callbackSz[0] != null) ? callbackSz[0] : cc.getHorizontal().getSize();
+ BoundSize vBS = (callbackSz != null && callbackSz[1] != null) ? callbackSz[1] : cc.getVertical().getSize();
+
+ for (int i = LayoutUtil.MIN; i <= LayoutUtil.MAX; i++) {
+ horSizes[i] = getSize(hBS, i, true);
+ verSizes[i] = getSize(vBS, i, false);
+ }
+
+ correctMinMax(horSizes);
+ correctMinMax(verSizes);
+ }
+
+ if (eHideMode > 1) {
+ gaps = new int[4][];
+ for (int i = 0; i < gaps.length; i++)
+ gaps[i] = new int[3];
+ }
+ }
+
+ private final int getSize(BoundSize uvs, int sizeType, boolean isHor)
+ {
+ if (uvs == null || uvs.getSize(sizeType) == null) {
+ switch(sizeType) {
+ case LayoutUtil.MIN:
+ return isHor ? comp.getMinimumWidth() : comp.getMinimumHeight();
+ case LayoutUtil.PREF:
+ return isHor ? comp.getPreferredWidth() : comp.getPreferredHeight();
+ default:
+ return isHor ? comp.getMaximumWidth() : comp.getMaximumHeight();
+ }
+ }
+
+ ContainerWrapper par = comp.getParent();
+ return uvs.getSize(sizeType).getPixels(isHor ? par.getWidth() : par.getHeight(), par, comp);
+ }
+
+ private final void calcGaps(ComponentWrapper before, CC befCC, ComponentWrapper after, CC aftCC, String tag, boolean flowX, boolean isLTR)
+ {
+ ContainerWrapper par = comp.getParent();
+ int parW = par.getWidth();
+ int parH = par.getHeight();
+
+ BoundSize befGap = before != null ? (flowX ? befCC.getHorizontal() : befCC.getVertical()).getGapAfter() : null;
+ BoundSize aftGap = after != null ? (flowX ? aftCC.getHorizontal() : aftCC.getVertical()).getGapBefore() : null;
+
+ mergeGapSizes(cc.getVertical().getComponentGaps(par, comp, befGap, (flowX ? null : before), tag, parH, 0, isLTR), false, true);
+ mergeGapSizes(cc.getHorizontal().getComponentGaps(par, comp, befGap, (flowX ? before : null), tag, parW, 1, isLTR), true, true);
+ mergeGapSizes(cc.getVertical().getComponentGaps(par, comp, aftGap, (flowX ? null : after), tag, parH, 2, isLTR), false, false);
+ mergeGapSizes(cc.getHorizontal().getComponentGaps(par, comp, aftGap, (flowX ? after : null), tag, parW, 3, isLTR), true, false);
+ }
+
+ private final void setDimBounds(int start, int size, boolean isHor)
+ {
+ if (isHor) {
+ x = start;
+ w = size;
+ } else {
+ y = start;
+ h = size;
+ }
+ }
+
+ private final boolean isPushGap(boolean isHor, boolean isBefore)
+ {
+ if (isHor && ((isBefore ? 1 : 2) & forcedPushGaps) != 0)
+ return true; // Forced
+
+ DimConstraint dc = cc.getDimConstraint(isHor);
+ BoundSize s = isBefore ? dc.getGapBefore() : dc.getGapAfter();
+ return s != null && s.getGapPush();
+ }
+
+ /**
+ * @return If the preferred size have changed because of the new bounds (needed for SWT only)
+ */
+ private final boolean transferBounds(boolean checkPrefChange)
+ {
+ comp.setBounds(x, y, w, h);
+
+ if (checkPrefChange && w != horSizes[LayoutUtil.PREF]) {
+ BoundSize vSz = cc.getVertical().getSize();
+ if ((vSz == null || vSz.getPreferred() == null)) {
+ if (comp.getPreferredHeight() != verSizes[LayoutUtil.PREF]) {
+ //System.out.println(comp.getComponent() + "oldPrefH: " + verSizes[LayoutUtil.PREF] + ", new: " + comp.getPreferredHeight());
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private final void setSizes(int[] sizes, boolean isHor)
+ {
+ if (sizes == null)
+ return;
+
+ int[] s = isHor ? horSizes : verSizes;
+ for (int i = 0; i < 3; i++)
+ s[i] = sizes[i];
+ }
+
+ private void setGaps(int[] minPrefMax, int ix)
+ {
+ if (gaps == null)
+ gaps = new int[][] {null, null, null, null};
+
+ gaps[ix] = minPrefMax;
+ }
+
+ private final void mergeGapSizes(int[] sizes, boolean isHor, boolean isTL)
+ {
+ if (gaps == null)
+ gaps = new int[][] {null, null, null, null};
+
+ if (sizes == null)
+ return;
+
+ int gapIX = getGapIx(isHor, isTL);
+ int[] oldGaps = gaps[gapIX];
+ if (oldGaps == null) {
+ oldGaps = new int[] {0, 0, LayoutUtil.INF};
+ gaps[gapIX] = oldGaps;
+ }
+
+ oldGaps[LayoutUtil.MIN] = Math.max(sizes[LayoutUtil.MIN], oldGaps[LayoutUtil.MIN]);
+ oldGaps[LayoutUtil.PREF] = Math.max(sizes[LayoutUtil.PREF], oldGaps[LayoutUtil.PREF]);
+ oldGaps[LayoutUtil.MAX] = Math.min(sizes[LayoutUtil.MAX], oldGaps[LayoutUtil.MAX]);
+ }
+
+ private final int getGapIx(boolean isHor, boolean isTL)
+ {
+ return isHor ? (isTL ? 1 : 3) : (isTL ? 0 : 2);
+ }
+
+ private final String getLinkContext(boolean defBaseline)
+ {
+ return isBaselineAlign(defBaseline) ? "baseline" : null;
+ }
+
+ private final int getSizeInclGaps(int sizeType, boolean isHor)
+ {
+ return filter(sizeType, getGapBefore(sizeType, isHor) + getSize(sizeType, isHor) + getGapAfter(sizeType, isHor));
+ }
+
+ private final int getSize(int sizeType, boolean isHor)
+ {
+ return filter(sizeType, isHor ? horSizes[sizeType] : verSizes[sizeType]);
+ }
+
+ private final int getGapBefore(int sizeType, boolean isHor)
+ {
+ int[] gaps = getGaps(isHor, true);
+ return gaps != null ? filter(sizeType, gaps[sizeType]) : 0;
+ }
+
+ private final int getGapAfter(int sizeType, boolean isHor)
+ {
+ int[] gaps = getGaps(isHor, false);
+ return gaps != null ? filter(sizeType, gaps[sizeType]) : 0;
+ }
+
+ private final int[] getGaps(boolean isHor, boolean isTL)
+ {
+ return gaps[getGapIx(isHor, isTL)];
+ }
+
+ private final int filter(int sizeType, int size)
+ {
+ if (size == LayoutUtil.NOT_SET)
+ return sizeType != LayoutUtil.MAX ? 0 : LayoutUtil.INF;
+ return constrainSize(size);
+ }
+
+ private final boolean isBaselineAlign(boolean defValue)
+ {
+ Float g = cc.getVertical().getGrow();
+ if (g != null && g.intValue() != 0)
+ return false;
+
+ UnitValue al = cc.getVertical().getAlign();
+ return comp.hasBaseline() && (al != null ? al == UnitValue.BASELINE_IDENTITY : defValue);
+ }
+
+ private final int getBaseline(int sizeType)
+ {
+ return comp.getBaseline(getSize(sizeType, true), getSize(sizeType, false));
+ }
+ }
+
+ //***************************************************************************************
+ //* Helper Methods
+ //***************************************************************************************
+
+ private static void layoutBaseline(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, int sizeType, int spanCount)
+ {
+ int[] aboveBelow = getBaselineAboveBelow(compWraps, sizeType, true);
+ int blRowSize = aboveBelow[0] + aboveBelow[1];
+
+ CC cc = compWraps.get(0).cc;
+
+ // Align for the whole baseline component array
+ UnitValue align = cc.getVertical().getAlign();
+ if (spanCount == 1 && align == null)
+ align = dc.getAlignOrDefault(false);
+ if (align == UnitValue.BASELINE_IDENTITY)
+ align = UnitValue.CENTER;
+
+ int offset = start + aboveBelow[0] + (align != null ? Math.max(0, align.getPixels(size - blRowSize, parent, null)) : 0);
+ for (int i = 0, iSz = compWraps.size(); i < iSz; i++) {
+ CompWrap cw = compWraps.get(i);
+ cw.y += offset;
+
+ if (cw.y + cw.h > start + size)
+ cw.h = start + size - cw.y;
+ }
+ }
+
+ private static void layoutSerial(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, boolean isHor, int spanCount, boolean fromEnd)
+ {
+ FlowSizeSpec fss = mergeSizesGapsAndResConstrs(
+ getComponentResizeConstraints(compWraps, isHor),
+ getComponentGapPush(compWraps, isHor),
+ getComponentSizes(compWraps, isHor),
+ getGaps(compWraps, isHor),
+ 0, null);
+
+ Float[] growW = dc.isFill() ? GROW_100 : null;
+ int[] sizes = LayoutUtil.calculateSerial(fss.sizes, fss.resConstsInclGaps, growW, LayoutUtil.PREF, size);
+ setCompWrapBounds(parent, sizes, compWraps, dc.getAlignOrDefault(isHor), start, size, isHor, fromEnd);
+ }
+
+ private static void setCompWrapBounds(ContainerWrapper parent, int[] allSizes, ArrayList<CompWrap> compWraps, UnitValue rowAlign, int start, int size, boolean isHor, boolean fromEnd)
+ {
+ int totSize = LayoutUtil.sum(allSizes);
+ CC cc = compWraps.get(0).cc;
+ UnitValue align = correctAlign(cc, rowAlign, isHor, fromEnd);
+
+ int cSt = start;
+ int slack = size - totSize;
+ if (slack > 0 && align != null) {
+ int al = Math.min(slack, Math.max(0, align.getPixels(slack, parent, null)));
+ cSt += (fromEnd ? -al : al);
+ }
+
+ for (int i = 0, bIx = 0, iSz = compWraps.size(); i < iSz; i++) {
+ CompWrap cw = compWraps.get(i);
+ if (fromEnd ) {
+ cSt -= allSizes[bIx++];
+ cw.setDimBounds(cSt - allSizes[bIx], allSizes[bIx], isHor);
+ cSt -= allSizes[bIx++];
+ } else {
+ cSt += allSizes[bIx++];
+ cw.setDimBounds(cSt, allSizes[bIx], isHor);
+ cSt += allSizes[bIx++];
+ }
+ }
+ }
+
+ private static void layoutParallel(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, boolean isHor, int spanCount, boolean fromEnd)
+ {
+ int[][] sizes = new int[compWraps.size()][]; // [compIx][gapBef,compSize,gapAft]
+
+ for (int i = 0; i < sizes.length; i++) {
+ CompWrap cw = compWraps.get(i);
+
+ DimConstraint cDc = cw.cc.getDimConstraint(isHor);
+
+ ResizeConstraint[] resConstr = new ResizeConstraint[] {
+ cw.isPushGap(isHor, true) ? GAP_RC_CONST_PUSH : GAP_RC_CONST,
+ cDc.resize,
+ cw.isPushGap(isHor, false) ? GAP_RC_CONST_PUSH : GAP_RC_CONST,
+ };
+
+ int[][] sz = new int[][] {
+ cw.getGaps(isHor, true), (isHor ? cw.horSizes : cw.verSizes), cw.getGaps(isHor, false)
+ };
+
+ Float[] growW = dc.isFill() ? GROW_100 : null;
+
+ sizes[i] = LayoutUtil.calculateSerial(sz, resConstr, growW, LayoutUtil.PREF, size);
+ }
+
+ UnitValue rowAlign = dc.getAlignOrDefault(isHor);
+ setCompWrapBounds(parent, sizes, compWraps, rowAlign, start, size, isHor, fromEnd);
+ }
+
+ private static void setCompWrapBounds(ContainerWrapper parent, int[][] sizes, ArrayList<CompWrap> compWraps, UnitValue rowAlign, int start, int size, boolean isHor, boolean fromEnd)
+ {
+ for (int i = 0; i < sizes.length; i++) {
+ CompWrap cw = compWraps.get(i);
+
+ UnitValue align = correctAlign(cw.cc, rowAlign, isHor, fromEnd);
+
+ int[] cSizes = sizes[i];
+ int gapBef = cSizes[0];
+ int cSize = cSizes[1]; // No Math.min(size, cSizes[1]) here!
+ int gapAft = cSizes[2];
+
+ int cSt = fromEnd ? start - gapBef : start + gapBef;
+ int slack = size - cSize - gapBef - gapAft;
+ if (slack > 0 && align != null) {
+ int al = Math.min(slack, Math.max(0, align.getPixels(slack, parent, null)));
+ cSt += (fromEnd ? -al : al);
+ }
+
+ cw.setDimBounds(fromEnd ? cSt - cSize : cSt, cSize, isHor);
+ }
+ }
+
+ private static UnitValue correctAlign(CC cc, UnitValue rowAlign, boolean isHor, boolean fromEnd)
+ {
+ UnitValue align = (isHor ? cc.getHorizontal() : cc.getVertical()).getAlign();
+ if (align == null)
+ align = rowAlign;
+ if (align == UnitValue.BASELINE_IDENTITY)
+ align = UnitValue.CENTER;
+
+ if (fromEnd) {
+ if (align == UnitValue.LEFT)
+ align = UnitValue.RIGHT;
+ else if (align == UnitValue.RIGHT)
+ align = UnitValue.LEFT;
+ }
+ return align;
+ }
+
+ private static int[] getBaselineAboveBelow(ArrayList<CompWrap> compWraps, int sType, boolean centerBaseline)
+ {
+ int maxAbove = Short.MIN_VALUE;
+ int maxBelow = Short.MIN_VALUE;
+ for (int i = 0, iSz = compWraps.size(); i < iSz; i++) {
+ CompWrap cw = compWraps.get(i);
+
+ int height = cw.getSize(sType, false);
+ if (height >= LayoutUtil.INF)
+ return new int[] {LayoutUtil.INF / 2, LayoutUtil.INF / 2};
+
+ int baseline = cw.getBaseline(sType);
+ int above = baseline + cw.getGapBefore(sType, false);
+ maxAbove = Math.max(above, maxAbove);
+ maxBelow = Math.max(height - baseline + cw.getGapAfter(sType, false), maxBelow);
+
+ if (centerBaseline)
+ cw.setDimBounds(-baseline, height, false);
+ }
+ return new int[] {maxAbove, maxBelow};
+ }
+
+ private static int getTotalSizeParallel(ArrayList<CompWrap> compWraps, int sType, boolean isHor)
+ {
+ int size = sType == LayoutUtil.MAX ? LayoutUtil.INF : 0;
+
+ for (int i = 0, iSz = compWraps.size(); i < iSz; i++) {
+ CompWrap cw = compWraps.get(i);
+ int cwSize = cw.getSizeInclGaps(sType, isHor);
+ if (cwSize >= LayoutUtil.INF)
+ return LayoutUtil.INF;
+
+ if (sType == LayoutUtil.MAX ? cwSize < size : cwSize > size)
+ size = cwSize;
+ }
+ return constrainSize(size);
+ }
+
+ private static final int getTotalSizeSerial(ArrayList<CompWrap> compWraps, int sType, boolean isHor)
+ {
+ int totSize = 0;
+ for (int i = 0, iSz = compWraps.size(), lastGapAfter = 0; i < iSz; i++) {
+ CompWrap wrap = compWraps.get(i);
+ int gapBef = wrap.getGapBefore(sType, isHor);
+ if (gapBef > lastGapAfter)
+ totSize += gapBef - lastGapAfter;
+
+ totSize += wrap.getSize(sType, isHor);
+ totSize += (lastGapAfter = wrap.getGapAfter(sType, isHor));
+
+ if (totSize >= LayoutUtil.INF)
+ return LayoutUtil.INF;
+ }
+ return constrainSize(totSize);
+ }
+
+ private static final int getTotalGroupsSizeParallel(ArrayList<LinkedDimGroup> groups, int sType, boolean countSpanning)
+ {
+ int size = sType == LayoutUtil.MAX ? LayoutUtil.INF : 0;
+ for (int i = 0, iSz = groups.size(); i < iSz; i++) {
+ LinkedDimGroup group = groups.get(i);
+ if (countSpanning || group.span == 1) {
+ int grpSize = group.getMinPrefMax()[sType];
+ if (grpSize >= LayoutUtil.INF)
+ return LayoutUtil.INF;
+
+ if (sType == LayoutUtil.MAX ? grpSize < size : grpSize > size)
+ size = grpSize;
+ }
+ }
+ return constrainSize(size);
+ }
+
+ /**
+ * @param compWraps
+ * @param isHor
+ * @return Might contain LayoutUtil.NOT_SET
+ */
+ private static int[][] getComponentSizes(ArrayList<CompWrap> compWraps, boolean isHor)
+ {
+ int[][] compSizes = new int[compWraps.size()][];
+ for (int i = 0; i < compSizes.length; i++) {
+ CompWrap cw = compWraps.get(i);
+ compSizes[i] = isHor ? cw.horSizes : cw.verSizes;
+ }
+ return compSizes;
+ }
+
+ /** Merges sizes and gaps together with Resize Constraints. For gaps {@link #GAP_RC_CONST} is used.
+ * @param resConstr One resize constriant for every row/component. Can be lesser in length and the last element should be used for missing elements.
+ * @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!
+ * @param minPrefMaxSizes The sizes (min/pref/max) for every row/component.
+ * @param gapSizes The gaps before and after each row/component packed in one double sized array.
+ * @param refSize The reference size to get the gap in pixels.
+ * @param parent The parent used to get the gap in pixels.
+ * @return A holder for the merged values.
+ */
+ private static FlowSizeSpec mergeSizesGapsAndResConstrs(ResizeConstraint[] resConstr, boolean[] gapPush, int[][] minPrefMaxSizes, int[][] gapSizes, int refSize, ContainerWrapper parent)
+ {
+ int[][] sizes = new int[(minPrefMaxSizes.length << 1) + 1][]; // Make room for gaps around.
+ ResizeConstraint[] resConstsInclGaps = new ResizeConstraint[sizes.length];
+
+ sizes[0] = gapSizes[0];
+ for (int i = 0, crIx = 1; i < minPrefMaxSizes.length; i++, crIx += 2) {
+
+ // Component bounds and constraints
+ resConstsInclGaps[crIx] = resConstr[i];
+ sizes[crIx] = minPrefMaxSizes[i];
+
+ sizes[crIx + 1] = gapSizes[i + 1];
+
+ if (sizes[crIx - 1] != null)
+ resConstsInclGaps[crIx - 1] = gapPush[i < gapPush.length ? i : gapPush.length - 1] ? GAP_RC_CONST_PUSH : GAP_RC_CONST;
+
+ if (i == (minPrefMaxSizes.length - 1) && sizes[crIx + 1] != null)
+ resConstsInclGaps[crIx + 1] = gapPush[(i + 1) < gapPush.length ? (i + 1) : gapPush.length - 1] ? GAP_RC_CONST_PUSH : GAP_RC_CONST;
+ }
+
+ // Check for null and set it to 0, 0, 0.
+ for (int i = 0; i < sizes.length; i++) {
+ if (sizes[i] == null)
+ sizes[i] = new int[3];
+ }
+
+ return new FlowSizeSpec(sizes, resConstsInclGaps);
+ }
+
+ private static final int[] mergeSizes(int[] oldValues, int[] newValues)
+ {
+ if (oldValues == null)
+ return newValues;
+
+ if (newValues == null)
+ return oldValues;
+
+ int[] ret = new int[oldValues.length];
+ for (int i = 0; i < ret.length; i++)
+ ret[i] = mergeSizes(oldValues[i], newValues[i], true);
+
+ return ret;
+ }
+
+ private static final int mergeSizes(int oldValue, int newValue, boolean toMax)
+ {
+ if (oldValue == LayoutUtil.NOT_SET || oldValue == newValue)
+ return newValue;
+
+ if (newValue == LayoutUtil.NOT_SET)
+ return oldValue;
+
+ return toMax != oldValue > newValue ? newValue : oldValue;
+ }
+
+ private static final int constrainSize(int s)
+ {
+ return s > 0 ? (s < LayoutUtil.INF ? s : LayoutUtil.INF) : 0;
+ }
+
+ private static final void correctMinMax(int s[])
+ {
+ if (s[LayoutUtil.MIN] > s[LayoutUtil.MAX])
+ s[LayoutUtil.MIN] = s[LayoutUtil.MAX]; // Since MAX is almost always explicitly set use tha
+
+ if (s[LayoutUtil.PREF] < s[LayoutUtil.MIN])
+ s[LayoutUtil.PREF] = s[LayoutUtil.MIN];
+
+ if (s[LayoutUtil.PREF] > s[LayoutUtil.MAX])
+ s[LayoutUtil.PREF] = s[LayoutUtil.MAX];
+ }
+
+ private static final class FlowSizeSpec
+ {
+ private final int[][] sizes;
+ private final ResizeConstraint[] resConstsInclGaps;
+
+ private FlowSizeSpec(int[][] sizes, ResizeConstraint[] resConstsInclGaps)
+ {
+ this.sizes = sizes;
+ this.resConstsInclGaps = resConstsInclGaps;
+ }
+
+ /**
+ * @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.
+ * @param targetSize The size to try to meat.
+ * @param defGrow The default grow weight if the specs does not have anyone that will grow. Comes from "push" in the CC.
+ * @param fromIx
+ * @param len
+ * @param sizeType
+ * @param eagerness How eager the algorithm should be to try to expand the sizes.
+ * <ul>
+ * <li>0 - Grow only rows/columns which has the <code>sizeType</code> set to be the containing components AND which has a grow weight > 0.
+ * <li>1 - Grow only rows/columns which has the <code>sizeType</code> set to be the containing components AND which has a grow weight > 0 OR unspecified.
+ * <li>2 - Grow all rows/columns that has a grow weight > 0.
+ * <li>3 - Grow all rows/columns that has a grow weight > 0 OR unspecified.
+ * </ul>
+ * @return The new size.
+ */
+ private final int expandSizes(DimConstraint[] specs, Float[] defGrow, int targetSize, int fromIx, int len, int sizeType, int eagerness)
+ {
+ ResizeConstraint[] resConstr = new ResizeConstraint[len];
+ int[][] sizesToExpand = new int[len][];
+ for (int i = 0; i < len; i++) {
+ int size = sizes[i + fromIx][sizeType];
+ sizesToExpand[i] = new int[] {size, size, sizes[i + fromIx][LayoutUtil.MAX]};
+
+ if (eagerness <= 1 && i % 2 == 0) { // // (i % 2 == 0) means only odd indexes, which is only rows/col indexes and not gaps.
+ int cIx = (i + fromIx - 1) >> 1;
+ DimConstraint spec = (DimConstraint) LayoutUtil.getIndexSafe(specs, cIx);
+
+ if ( (sizeType == LayoutUtil.MIN && spec.getSize() != null && spec.getSize().getMin() != null && spec.getSize().getMin().getUnit() != UnitValue.MIN_SIZE) ||
+ (sizeType == LayoutUtil.PREF && spec.getSize() != null && spec.getSize().getPreferred() != null && spec.getSize().getPreferred().getUnit() != UnitValue.PREF_SIZE)) {
+ continue;
+ }
+ }
+ resConstr[i] = (ResizeConstraint) LayoutUtil.getIndexSafe(resConstsInclGaps, i + fromIx);
+ }
+
+ Float[] growW = (eagerness == 1 || eagerness == 3) ? extractSubArray(specs, defGrow, fromIx, len): null;
+ int[] newSizes = LayoutUtil.calculateSerial(sizesToExpand, resConstr, growW, sizeType, targetSize);
+ int newSize = 0;
+
+ for (int i = 0; i < len; i++) {
+ int s = newSizes[i];
+ sizes[i + fromIx][sizeType] = s;
+ newSize += s;
+ }
+ return newSize;
+ }
+ }
+
+ private static Float[] extractSubArray(DimConstraint[] specs, Float[] arr, int ix, int len)
+ {
+ if (arr == null || arr.length < ix + len) {
+ Float[] growLastArr = new Float[len];
+
+ // Handle a group where some rows (first one/few and/or last one/few) are docks.
+ for (int i = ix + len - 1; i >= 0; i -= 2) {
+ int specIx = (i >> 1);
+ if (specs[specIx] != DOCK_DIM_CONSTRAINT) {
+ growLastArr[i - ix] = ResizeConstraint.WEIGHT_100;
+ return growLastArr;
+ }
+ }
+ return growLastArr;
+ }
+
+ Float[] newArr = new Float[len];
+ for (int i = 0; i < len; i++)
+ newArr[i] = arr[ix + i];
+ return newArr;
+ }
+
+ private static WeakHashMap[] PARENT_ROWCOL_SIZES_MAP = null;
+ static synchronized void putSizesAndIndexes(Object parComp, int[] sizes, int[] ixArr, boolean isRows)
+ {
+ if (PARENT_ROWCOL_SIZES_MAP == null) // Lazy since only if designing in IDEs
+ PARENT_ROWCOL_SIZES_MAP = new WeakHashMap[] {new WeakHashMap(4), new WeakHashMap(4)};
+
+ PARENT_ROWCOL_SIZES_MAP[isRows ? 0 : 1].put(parComp, new int[][] {ixArr, sizes});
+ }
+
+ static synchronized int[][] getSizesAndIndexes(Object parComp, boolean isRows)
+ {
+ if (PARENT_ROWCOL_SIZES_MAP == null)
+ return null;
+
+ return (int[][]) PARENT_ROWCOL_SIZES_MAP[isRows ? 0 : 1].get(parComp);
+ }
+}
|