ec56de4e8ecce377813bfb4fd83ccc310d3e3610
[jalview.git] / src / net / miginfocom / layout / Grid.java
1 package net.miginfocom.layout;
2
3
4 import java.lang.ref.WeakReference;
5 import java.util.*;
6 /*
7  * License (BSD):
8  * ==============
9  *
10  * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
11  * All rights reserved.
12  *
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.
23  *
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
33  * OF SUCH DAMAGE.
34  *
35  * @version 1.0
36  * @author Mikael Grev, MiG InfoCom AB
37  *         Date: 2006-sep-08
38  */
39
40 /** Holds components in a grid. Does most of the logic behind the layout manager.
41  */
42 public final class Grid
43 {
44         public static final boolean TEST_GAPS = true;
45
46         private static final Float[] GROW_100 = new Float[] {ResizeConstraint.WEIGHT_100};
47
48         private static final DimConstraint DOCK_DIM_CONSTRAINT = new DimConstraint();
49         static {
50                 DOCK_DIM_CONSTRAINT.setGrowPriority(0);
51         }
52
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.
55          */
56         private static final int MAX_GRID = 30000;
57
58         /** Docking components will use the grid coordinates <code>-MAX_DOCK_GRID -> 0</code> and <code>MAX_GRID -> MAX_DOCK_GRID</code>.
59          */
60         private static final int MAX_DOCK_GRID = 32767;
61
62         /** A constraint used for gaps.
63          */
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);
66
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.
68          */
69         private static final CC DEF_CC = new CC();
70
71         /** The constraints. Never <code>null</code>.
72          */
73         private final LC lc;
74
75         /** The parent that is layout out and this grid is done for. Never <code>null</code>.
76          */
77         private final ContainerWrapper container;
78
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).
80          */
81         private final LinkedHashMap<Integer, Cell> grid = new LinkedHashMap<Integer, Cell>();   // [(y << 16) + x] -> Cell. null key for absolute positioned compwraps
82
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.
84
85         /** The size of the grid. Row count and column count.
86          */
87         private final TreeSet<Integer> rowIndexes = new TreeSet<Integer>(), colIndexes = new TreeSet<Integer>();
88
89         /** The row and column specifications.
90          */
91         private final AC rowConstr, colConstr;
92
93         /** The in the constructor calculated min/pref/max sizes of the rows and columns.
94          */
95         private FlowSizeSpec colFlowSpecs = null, rowFlowSpecs = null;
96
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.
99          */
100         private final ArrayList<LinkedDimGroup>[] colGroupLists, rowGroupLists;   //[(start)row/col number]
101
102         /** The in the constructor calculated min/pref/max size of the whole grid.
103          */
104         private int[] width = null, height = null;
105
106         /** If debug is on contains the bounds for things to paint when calling {@link ContainerWrapper#paintDebugCell(int, int, int, int)}
107           */
108         private ArrayList<int[]> debugRects = null; // [x, y, width, height]
109
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.
113          * <p>
114          * The boolean is true for groups id:s and null for normal id:s.
115          */
116         private HashMap<String, Boolean> linkTargetIDs = null;
117
118         private final int dockOffY, dockOffX;
119
120         private final Float[] pushXs, pushYs;
121
122         private final ArrayList<LayoutCallback> callbackList;
123
124         /** Constructor.
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
130          * cached one.
131          * @param callbackList A list of callbacks or <code>null</code> if none. Will not be altered.
132          */
133         public Grid(ContainerWrapper container, LC lc, AC rowConstr, AC colConstr, Map<? extends ComponentWrapper, CC> ccMap, ArrayList<LayoutCallback> callbackList)
134         {
135                 this.lc = lc;
136                 this.rowConstr = rowConstr;
137                 this.colConstr = colConstr;
138                 this.container = container;
139                 this.callbackList = callbackList;
140
141                 int wrap = lc.getWrapAfter() != 0 ? lc.getWrapAfter() : (lc.isFlowX() ? colConstr : rowConstr).getConstaints().length;
142                 boolean useVisualPadding = lc.isVisualPadding();
143
144                 final ComponentWrapper[] comps = container.getComponents();
145
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);
151
152                 final DimConstraint[] specs = (lc.isFlowX() ? rowConstr : colConstr).getConstaints();
153
154                 int sizeGroupsX = 0, sizeGroupsY = 0;
155                 int[] dockInsets = null;    // top, left, bottom, right insets for docks.
156
157                 LinkHandler.clearTemporaryBounds(container.getLayout());
158
159                 for (int i = 0; i < comps.length;) {
160                         ComponentWrapper comp = comps[i];
161                         CC rootCc = getCC(comp, ccMap);
162
163                         addLinkIDs(rootCc);
164
165                         int hideMode = comp.isVisible() ? -1 : rootCc.getHideMode() != -1 ? rootCc.getHideMode() : lc.getHideMode();
166
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());
169                                 i++;
170                                 continue;   // The "external" component should not be handled further.
171                         }
172
173                         if (rootCc.getHorizontal().getSizeGroup() != null)
174                                 sizeGroupsX++;
175                         if (rootCc.getVertical().getSizeGroup() != null)
176                                 sizeGroupsY++;
177
178                         // Special treatment of absolute positioned components.
179                         if (getPos(comp, rootCc) != null || rootCc.isExternal()) {
180
181                                 CompWrap cw = new CompWrap(comp, rootCc, hideMode, useVisualPadding);
182                                 Cell cell = grid.get(null);
183                                 if (cell == null) {
184                                         grid.put(null, new Cell(cw));
185                                 } else {
186                                         cell.compWraps.add(cw);
187                                 }
188
189                                 if (!rootCc.isBoundsInGrid() || rootCc.isExternal()) {
190                                         setLinkedBounds(comp, rootCc, comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight(), rootCc.isExternal());
191                                         i++;
192                                         continue;
193                                 }
194                         }
195
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};
199
200                                 addDockingCell(dockInsets, rootCc.getDockSide(), new CompWrap(comp, rootCc, hideMode, useVisualPadding));
201                                 i++;
202                                 continue;
203                         }
204
205                         Boolean cellFlowX = rootCc.getFlowX();
206                         Cell cell = null;
207
208                         if (rootCc.isNewline()) {
209                                 wrap(cellXY, rootCc.getNewlineGapSize());
210                         } else if (hitEndOfRow) {
211                                 wrap(cellXY, null);
212                         }
213                         hitEndOfRow = false;
214
215                         final boolean isRowInGridMode = !lc.isNoGrid() && !((DimConstraint) LayoutUtil.getIndexSafe(specs, lc.isFlowX() ? cellXY[1] : cellXY[0])).isNoGrid();
216
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)
223                                                 wrap(cellXY, null);
224                                 }
225                         } else {
226                                 if (cx >= 0 && cy >= 0) {
227                                         if (cy >= 0) {
228                                                 cellXY[0] = cx;
229                                                 cellXY[1] = cy;
230                                         } else {    // Only one coordinate is specified. Use the current row (flowx) or column (flowy) to fill in.
231                                                 if (lc.isFlowX()) {
232                                                         cellXY[0] = cx;
233                                                 } else {
234                                                         cellXY[1] = cx;
235                                                 }
236                                         }
237                                         ensureIndexSizes(cx, cy);
238                                 }
239                                 cell = getCell(cellXY[1], cellXY[0]);   // Might be null
240                         }
241
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++) {
244                                 do {
245                                         if (Math.abs(increase(cellXY, 1)) >= wrap)
246                                                 wrap(cellXY, null);
247                                 } while (!isCellFree(cellXY[1], cellXY[0], spannedRects));
248                         }
249
250                         // If cell is not created yet, create it and set it.
251                         if (cell == null) {
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]);
254
255                                 cell = new Cell(spanx, spany, cellFlowX != null ? cellFlowX : lc.isFlowX());
256
257                                 setCell(cellXY[1], cellXY[0], cell);
258
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});
262                         }
263
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;
269
270                         for (; splitLeft >= 0 && i < comps.length; splitLeft--) {
271                                 ComponentWrapper compAdd = comps[i];
272                                 CC cc = getCC(compAdd, ccMap);
273
274                                 addLinkIDs(cc);
275
276                                 boolean visible = compAdd.isVisible();
277                                 hideMode = visible ? -1 : cc.getHideMode() != -1 ? cc.getHideMode() : lc.getHideMode();
278
279                                 if (cc.isExternal() || hideMode == 3) {
280                                         i++;
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.
283                                 }
284
285                                 hasPushX |= (visible || hideMode > 1) && (cc.getPushX() != null);
286                                 hasPushY |= (visible || hideMode > 1) && (cc.getPushY() != null);
287
288                                 if (cc != rootCc) { // If not first in a cell
289                                         if (cc.isNewline() || !cc.isBoundsInGrid() || cc.getDockSide() != -1)
290                                                 break;
291
292                                         if (splitLeft > 0 && cc.getSkip() > 0) {
293                                                 splitExit = true;
294                                                 break;
295                                         }
296                                 }
297
298                                 CompWrap cw = new CompWrap(compAdd, cc, hideMode, useVisualPadding);
299                                 cell.compWraps.add(cw);
300                                 cell.hasTagged |= cc.getTag() != null;
301                                 hasTagged |= cell.hasTagged;
302
303                                 if (cc != rootCc) {
304                                         if (cc.getHorizontal().getSizeGroup() != null)
305                                                 sizeGroupsX++;
306                                         if (cc.getVertical().getSizeGroup() != null)
307                                                 sizeGroupsY++;
308                                 }
309
310                                 i++;
311
312                                 if ((cc.isWrap() || (spanRestOfRow && splitLeft == 0))) {
313                                         if (cc.isWrap()) {
314                                                 wrap(cellXY, cc.getWrapGapSize());
315                                         } else {
316                                                 hitEndOfRow = true;
317                                         }
318                                         wrapHandled = true;
319                                         break;
320                                 }
321                         }
322
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) {
326                                         hitEndOfRow = true;
327                                 } else {
328                                         increase(cellXY, splitExit ? span - 1 : span);
329                                 }
330                         }
331                 }
332
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));
338
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();
344
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);
351                                         }
352                                 }
353                         }
354
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
361                         }
362                 } // Component loop
363
364                 if (hasTagged)
365                         sortCellsByPlatform(grid.values(), container);
366
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;
371
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;
376
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;
380
381                                 cw.calcGaps(cwBef, ccBef, cwAft, ccAft, tag, cell.flowx, ltr);
382                         }
383                 }
384
385                 dockOffX = getDockInsets(colIndexes);
386                 dockOffY = getDockInsets(rowIndexes);
387
388                 // Add synthetic indexes for empty rows and columns so they can get a size
389                 ensureIndexSizes(colConstr.getCount(), rowConstr.getCount());
390
391                 colGroupLists = divideIntoLinkedGroups(false);
392                 rowGroupLists = divideIntoLinkedGroups(true);
393
394                 pushXs = hasPushX || lc.isFillX() ? getDefaultPushWeights(false) : null;
395                 pushYs = hasPushY || lc.isFillY() ? getDefaultPushWeights(true) : null;
396
397                 if (LayoutUtil.isDesignTime(container))
398                         saveGrid(container, grid);
399         }
400
401         private void ensureIndexSizes(int colCount, int rowCount)
402         {
403                 for (int i = 0; i < colCount; i++)
404                         colIndexes.add(i);
405                 for (int i = 0; i < rowCount; i++)
406                         rowIndexes.add(i);
407         }
408
409         private static CC getCC(ComponentWrapper comp, Map<? extends ComponentWrapper, CC> ccMap)
410         {
411                 CC cc = ccMap.get(comp);
412                 return cc != null ? cc : DEF_CC;
413         }
414
415         private void addLinkIDs(CC cc)
416         {
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);
422                 }
423         }
424
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.
427          * <p>
428          * If any component can have changed cell the grid needs to be recreated.
429          */
430         public void invalidateContainerSize()
431         {
432                 colFlowSpecs = null;
433                 invalidateComponentSizes();
434         }
435
436         private void invalidateComponentSizes()
437         {
438                 for (Cell cell : grid.values()) {
439                         for (CompWrap compWrap : cell.compWraps)
440                                 compWrap.invalidateSizes();
441                 }
442         }
443
444         /**
445          * @deprecated since 5.0 Last boolean is not needed and is gotten from the new {@link net.miginfocom.layout.ComponentWrapper#getContentBias()} instead;
446          */
447         public boolean layout(int[] bounds, UnitValue alignX, UnitValue alignY, boolean debug, boolean notUsed)
448         {
449                 return layoutImpl(bounds, alignX, alignY, debug, false);
450         }
451
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()}.
459          * @since 5.0
460          */
461         public boolean layout(int[] bounds, UnitValue alignX, UnitValue alignY, boolean debug)
462         {
463                 return layoutImpl(bounds, alignX, alignY, debug, false);
464         }
465
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()}.
475          * @since 5.0
476          */
477         private boolean layoutImpl(int[] bounds, UnitValue alignX, UnitValue alignY, boolean debug, boolean trialRun)
478         {
479                 if (debug)
480                         debugRects = new ArrayList<int[]>();
481
482                 if (colFlowSpecs == null)
483                         checkSizeCalcs(bounds[2], bounds[3]);
484
485                 resetLinkValues(true, true);
486
487                 layoutInOneDim(bounds[2], alignX, false, pushXs);
488                 layoutInOneDim(bounds[3], alignY, true, pushYs);
489
490                 HashMap<String, Integer> endGrpXMap = null, endGrpYMap = null;
491                 int compCount = container.getComponentCount();
492
493                 // Transfer the calculated bound from the ComponentWrappers to the actual Components.
494                 boolean addVisualPadding = lc.isVisualPadding();
495                 boolean layoutAgain = false;
496                 if (compCount > 0) {
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
498                                 boolean doAgain;
499                                 int count = 0;
500                                 do {
501                                         doAgain = false;
502                                         for (Cell cell : grid.values()) {
503                                                 for (CompWrap cw : cell.compWraps) {
504                                                         if (j == 0) {
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);
509
510                                                                         if (cw.cc.getVertical().getEndGroup() != null)
511                                                                                 endGrpYMap = addToEndGroup(endGrpYMap, cw.cc.getVertical().getEndGroup(), cw.y + cw.h);
512                                                                 }
513
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"))) {
517                                                                         layoutAgain = true;
518                                                                 }
519                                                         }
520
521                                                         if (linkTargetIDs == null || j == 1) {
522                                                                 if (cw.cc.getHorizontal().getEndGroup() != null)
523                                                                         cw.w = endGrpXMap.get(cw.cc.getHorizontal().getEndGroup()) - cw.x;
524
525                                                                 if (cw.cc.getVertical().getEndGroup() != null)
526                                                                         cw.h = endGrpYMap.get(cw.cc.getVertical().getEndGroup()) - cw.y;
527
528                                                                 cw.x += bounds[0];
529                                                                 cw.y += bounds[1];
530
531                                                                 if (!trialRun)
532                                                                         cw.transferBounds(addVisualPadding);
533
534                                                                 if (callbackList != null) {
535                                                                         for (LayoutCallback callback : callbackList)
536                                                                                 callback.correctBounds(cw.comp);
537                                                                 }
538                                                         }
539                                                 }
540                                         }
541                                         clearGroupLinkBounds();
542                                         if (++count > ((compCount << 3) + 10)) {
543                                                 System.err.println("Unstable cyclic dependency in absolute linked values.");
544                                                 break;
545                                         }
546
547                                 } while (doAgain);
548                         }
549                 }
550
551                 // Add debug shapes for the "cells". Use the CompWraps as base for inding the cells.
552                 if (debug) {
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);
558
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});
561                                 }
562                         }
563                 }
564                 return layoutAgain;
565         }
566
567         public void paintDebug()
568         {
569                 if (debugRects != null) {
570                         container.paintDebugOutline(lc.isVisualPadding());
571
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]);
576                                         painted.add(r);
577                                 }
578                         }
579
580                         for (Cell cell : grid.values()) {
581                                 ArrayList<CompWrap> compWraps = cell.compWraps;
582                                 for (CompWrap compWrap : compWraps)
583                                         compWrap.comp.paintDebugOutline(lc.isVisualPadding());
584                         }
585                 }
586         }
587
588         public ContainerWrapper getContainer()
589         {
590                 return container;
591         }
592
593         public final int[] getWidth()
594         {
595                 return getWidth(lastRefHeight);
596         }
597
598         public final int[] getWidth(int refHeight)
599         {
600                 checkSizeCalcs(lastRefWidth, refHeight);
601                 return width.clone();
602         }
603
604         public final int[] getHeight()
605         {
606                 return getHeight(lastRefWidth);
607         }
608
609         public final int[] getHeight(int refWidth)
610         {
611                 checkSizeCalcs(refWidth, lastRefHeight);
612                 return height.clone();
613         }
614
615         private int lastRefWidth = 0, lastRefHeight = 0;
616
617         private void checkSizeCalcs(int refWidth, int refHeight)
618         {
619                 if (colFlowSpecs == null)
620                         calcGridSizes(refWidth, refHeight);
621
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);
626                 }
627
628                 lastRefWidth = refWidth;
629                 lastRefHeight = refHeight;
630         }
631
632         private void calcGridSizes(int refWidth, int refHeight)
633         {
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);
637
638                 colFlowSpecs = colSpecs;
639                 rowFlowSpecs = rowSpecs;
640
641                 width = getMinPrefMaxSumSize(true, colSpecs.sizes);
642                 height = getMinPrefMaxSumSize(false, rowSpecs.sizes);
643
644                 if (linkTargetIDs == null) {
645                         resetLinkValues(false, true);
646                 } else {
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
649                         // I know of.
650                         layout(new int[]{0, 0, refWidth, refHeight}, null, null, false);
651                         resetLinkValues(false, false);
652                 }
653
654                 adjustSizeForAbsolute(true);
655                 adjustSizeForAbsolute(false);
656         }
657
658         private UnitValue[] getPos(ComponentWrapper cw, CC cc)
659         {
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!
664                 }
665
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;
670
671                 // Merge
672                 for (int i = 0; i < 4; i++) {
673                         UnitValue cbUv = callbackPos[i];
674                         if (cbUv != null)
675                                 ccPos[i] = cbUv;
676                 }
677
678                 return ccPos;
679         }
680
681         private BoundSize[] getCallbackSize(ComponentWrapper cw)
682         {
683                 if (callbackList != null) {
684                         for (LayoutCallback callback : callbackList) {
685                                 BoundSize[] bs = callback.getSize(cw);   // NOT a copy!
686                                 if (bs != null)
687                                         return bs;
688                         }
689                 }
690                 return null;
691         }
692
693         private static int getDockInsets(TreeSet<Integer> set)
694         {
695                 int c = 0;
696                 for (Integer i : set) {
697                         if (i < -MAX_GRID) {
698                                 c++;
699                         } else {
700                                 break;  // Since they are sorted we can break
701                         }
702                 }
703                 return c;
704         }
705
706         /**
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.
711          */
712         private boolean setLinkedBounds(ComponentWrapper cw, CC cc, int x, int y, int w, int h, boolean external)
713         {
714                 String id = cc.getId() != null ? cc.getId() : cw.getLinkId();
715                 if (id == null)
716                         return false;
717
718                 String gid = null;
719                 int grIx = id.indexOf('.');
720                 if (grIx != -1 ) {
721                         gid = id.substring(0, grIx);
722                         id = id.substring(grIx + 1);
723                 }
724
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);
729
730                 if (gid != null && (external || (linkTargetIDs != null && linkTargetIDs.containsKey(gid)))) {
731                         if (linkTargetIDs == null)
732                                 linkTargetIDs = new HashMap<String, Boolean>(4);
733
734                         linkTargetIDs.put(gid, Boolean.TRUE);
735                         changed |= LinkHandler.setBounds(lay, gid, x, y, w, h, !external, true);
736                 }
737
738                 return changed;
739         }
740
741         /** Go to next cell.
742          * @param p The point to increase
743          * @param cnt How many cells to advance.
744          * @return The new value in the "increasing" dimension.
745          */
746         private int increase(int[] p, int cnt)
747         {
748                 return lc.isFlowX() ? (p[0] += cnt) : (p[1] += cnt);
749         }
750
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.
754          */
755         private void wrap(int[] cellXY, BoundSize gapSize)
756         {
757                 boolean flowx = lc.isFlowX();
758                 cellXY[0] = flowx ? 0 : cellXY[0] + 1;
759                 cellXY[1] = flowx ? cellXY[1] + 1 : 0;
760
761                 if (gapSize != null) {
762                         if (wrapGapMap == null)
763                                 wrapGapMap = new HashMap<Integer, BoundSize>(8);
764
765                         wrapGapMap.put(cellXY[flowx ? 1 : 0], gapSize);
766                 }
767
768                 // add the row/column so that the gap in the last row/col will not be removed.
769                 if (flowx) {
770                         rowIndexes.add(cellXY[1]);
771                 } else {
772                         colIndexes.add(cellXY[0]);
773                 }
774         }
775
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.
779          */
780         private static void sortCellsByPlatform(Collection<Cell> cells, ContainerWrapper parent)
781         {
782                 String order = PlatformDefaults.getButtonOrder();
783                 String orderLo = order.toLowerCase();
784
785                 int unrelSize = PlatformDefaults.convertToPixels(1, "u", true, 0, parent, null);
786
787                 if (unrelSize == UnitConverter.UNABLE)
788                         throw new IllegalArgumentException("'unrelated' not recognized by PlatformDefaults!");
789
790                 int[] gapUnrel = new int[] {unrelSize, unrelSize, LayoutUtil.NOT_SET};
791                 int[] flGap = new int[] {0, 0, LayoutUtil.NOT_SET};
792
793                 for (Cell cell : cells) {
794                         if (!cell.hasTagged)
795                                 continue;
796
797                         CompWrap prevCW = null;
798                         boolean nextUnrel = false;
799                         boolean nextPush = false;
800                         ArrayList<CompWrap> sortedList = new ArrayList<CompWrap>(cell.compWraps.size());
801
802                         for (int i = 0, iSz = orderLo.length(); i < iSz; i++) {
803                                 char c = orderLo.charAt(i);
804                                 if (c == '+' || c == '_') {
805                                         nextUnrel = true;
806                                         if (c == '+')
807                                                 nextPush = true;
808                                 } else {
809                                         String tag = PlatformDefaults.getTagForChar(c);
810                                         if (tag != null) {
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));
816
817                                                                 sortedList.add(cw);
818
819                                                                 if (nextUnrel) {
820                                                                         (prevCW != null ? prevCW : cw).mergeGapSizes(gapUnrel, cell.flowx, prevCW == null);
821                                                                         if (nextPush) {
822                                                                                 cw.forcedPushGaps = 1;
823                                                                                 nextUnrel = false;
824                                                                                 nextPush = false;
825                                                                         }
826                                                                 }
827
828                                                                 // "unknown" components will always get an Unrelated gap.
829                                                                 if (c == 'u')
830                                                                         nextUnrel = true;
831                                                                 prevCW = cw;
832                                                         }
833                                                 }
834                                         }
835                                 }
836                         }
837
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);
841                                 if (nextUnrel) {
842                                         cw.mergeGapSizes(gapUnrel, cell.flowx, false);
843                                         if (nextPush)
844                                                 cw.forcedPushGaps |= 2;
845                                 }
846
847                                 // Remove first and last gap if not set explicitly.
848                                 if (cw.cc.getHorizontal().getGapAfter() == null)
849                                         cw.setGaps(flGap, 3);
850
851                                 cw = sortedList.get(0);
852                                 if (cw.cc.getHorizontal().getGapBefore() == null)
853                                         cw.setGaps(flGap, 1);
854                         }
855
856                         // Exchange the unsorted CompWraps for the sorted one.
857                         if (cell.compWraps.size() == sortedList.size()) {
858                                 cell.compWraps.clear();
859                         } else {
860                                 cell.compWraps.removeAll(sortedList);
861                         }
862                         cell.compWraps.addAll(sortedList);
863                 }
864         }
865
866         private Float[] getDefaultPushWeights(boolean isRows)
867         {
868                 ArrayList<LinkedDimGroup>[] groupLists = isRows ? rowGroupLists : colGroupLists;
869
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();
878
879                                         Float pushWeight = hideMode < 2 ? (isRows ? cw.cc.getPushY() : cw.cc.getPushX()) : null;
880                                         if (rowPushWeight == null || (pushWeight != null && pushWeight > rowPushWeight))
881                                                 rowPushWeight = pushWeight;
882                                 }
883                         }
884
885                         if (rowPushWeight != null) {
886                                 if (pushWeightArr == GROW_100)
887                                         pushWeightArr = new Float[(groupLists.length << 1) + 1];
888                                 pushWeightArr[ix] = rowPushWeight;
889                         }
890                 }
891
892                 return pushWeightArr;
893         }
894
895         private void clearGroupLinkBounds()
896         {
897                 if (linkTargetIDs == null)
898                         return;
899
900                 for (Map.Entry<String, Boolean> o : linkTargetIDs.entrySet()) {
901                         if (o.getValue() == Boolean.TRUE)
902                                 LinkHandler.clearBounds(container.getLayout(), o.getKey());
903                 }
904         }
905
906         private void resetLinkValues(boolean parentSize, boolean compLinks)
907         {
908                 Object lay = container.getLayout();
909                 if (compLinks)
910                         LinkHandler.clearTemporaryBounds(lay);
911
912                 boolean defIns = !hasDocks();
913
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;
916
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);
921
922                 LinkHandler.setBounds(lay, "visual", insX, insY, visW, visH, true, false);
923                 LinkHandler.setBounds(lay, "container", 0, 0, parW, parH, true, false);
924         }
925
926         /** Returns the {@link net.miginfocom.layout.Grid.LinkedDimGroup} that has the {@link net.miginfocom.layout.Grid.CompWrap}
927          * <code>cw</code>.
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.
931          */
932         private static LinkedDimGroup getGroupContaining(ArrayList<LinkedDimGroup>[] groupLists, CompWrap cw)
933         {
934                 for (ArrayList<LinkedDimGroup> groups : groupLists) {
935             for (LinkedDimGroup group : groups) {
936                 ArrayList<CompWrap> cwList = group._compWraps;
937                 for (CompWrap aCwList : cwList) {
938                     if (aCwList == cw)
939                         return group;
940                 }
941             }
942                 }
943                 return null;
944         }
945
946         private boolean doAbsoluteCorrections(CompWrap cw, int[] bounds)
947         {
948                 boolean changed = false;
949
950                 int[] stSz = getAbsoluteDimBounds(cw, bounds[2], true);
951                 if (stSz != null)
952                         cw.setDimBounds(stSz[0], stSz[1], true);
953
954                 stSz = getAbsoluteDimBounds(cw, bounds[3], false);
955                 if (stSz != null)
956                         cw.setDimBounds(stSz[0], stSz[1], false);
957
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);
961
962                 return changed;
963         }
964
965         /** Adjust grid's width or height for the absolute components' positions.
966          */
967         private void adjustSizeForAbsolute(boolean isHor)
968         {
969                 int[] curSizes = isHor ? width : height;
970
971                 Cell absCell = grid.get(null);
972                 if (absCell == null || absCell.compWraps.size() == 0)
973                         return;
974
975                 ArrayList<CompWrap> cws = absCell.compWraps;
976
977                 int maxEnd = 0;
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];
984                                 if (maxEnd < end)
985                                         maxEnd = end;
986
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);
990                         }
991                         if (!doAgain)
992                                 break;
993
994                         // We need to check this again since the coords may be smaller this round.
995                         maxEnd = 0;
996                         clearGroupLinkBounds();
997                 }
998
999                 maxEnd += LayoutUtil.getInsets(lc, isHor ? 3 : 2, !hasDocks()).getPixels(0, container, null);
1000
1001                 if (curSizes[LayoutUtil.MIN] < maxEnd)
1002                         curSizes[LayoutUtil.MIN] = maxEnd;
1003                 if (curSizes[LayoutUtil.PREF] < maxEnd)
1004                         curSizes[LayoutUtil.PREF] = maxEnd;
1005         }
1006
1007         private int[] getAbsoluteDimBounds(CompWrap cw, int refSize, boolean isHor)
1008         {
1009                 if (cw.cc.isExternal()) {
1010                         if (isHor) {
1011                                 return new int[] {cw.comp.getX(), cw.comp.getWidth()};
1012                         } else {
1013                                 return new int[] {cw.comp.getY(), cw.comp.getHeight()};
1014                         }
1015                 }
1016
1017                 UnitValue[] pad = cw.cc.getPadding();
1018
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)
1022                         return null;
1023
1024                 // Set start
1025                 int st = isHor ? cw.x : cw.y;
1026                 int sz = isHor ? cw.w : cw.h;
1027
1028                 // If absolute, use those coordinates instead.
1029                 if (pos != null) {
1030                         UnitValue stUV = pos[isHor ? 0 : 1];
1031                         UnitValue endUV = pos[isHor ? 2 : 3];
1032
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);
1036
1037                         if (stUV != null) {
1038                                 st = stUV.getPixels(stUV.getUnit() == UnitValue.ALIGN ? sz : refSize, container, cw.comp);
1039
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);
1042                         }
1043
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);
1047                                 } else {
1048                                         st = endUV.getPixels(refSize, container, cw.comp) - sz;
1049                                 }
1050                         }
1051                 }
1052
1053                 // If constraint has padding -> correct the start/size
1054                 if (pad != null) {
1055                         UnitValue uv = pad[isHor ? 1 : 0];
1056                         int p = uv != null ? uv.getPixels(refSize, container, cw.comp) : 0;
1057                         st += p;
1058                         uv = pad[isHor ? 3 : 2];
1059                         sz += -p + (uv != null ? uv.getPixels(refSize, container, cw.comp) : 0);
1060                 }
1061
1062                 return new int[] {st, sz};
1063         }
1064
1065         private void layoutInOneDim(int refSize, UnitValue align, boolean isRows, Float[] defaultPushWeights)
1066         {
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;
1071
1072                 int[] rowColSizes = LayoutUtil.calculateSerial(fss.sizes, fss.resConstsInclGaps, defaultPushWeights, LayoutUtil.PREF, refSize);
1073
1074                 if (LayoutUtil.isDesignTime(container)) {
1075                         TreeSet<Integer> indexes = isRows ? rowIndexes : colIndexes;
1076                         int[] ixArr = new int[indexes.size()];
1077                         int ix = 0;
1078                         for (Integer i : indexes)
1079                                 ixArr[ix++] = i;
1080
1081                         putSizesAndIndexes(container.getComponent(), rowColSizes, ixArr, isRows);
1082                 }
1083
1084                 int curPos = align != null ? align.getPixels(refSize - LayoutUtil.sum(rowColSizes), container, null) : 0;
1085
1086                 if (fromEnd)
1087                         curPos = refSize - curPos;
1088
1089                 for (int i = 0 ; i < rowCols.length; i++) {
1090                         ArrayList<LinkedDimGroup> linkedGroups = rowCols[i];
1091                         int scIx = i - (isRows ? dockOffY : dockOffX);
1092
1093                         int bIx = i << 1;
1094                         int bIx2 = bIx + 1;
1095
1096                         curPos += (fromEnd ? -rowColSizes[bIx] : rowColSizes[bIx]);
1097
1098                         DimConstraint primDC = scIx >= 0 ? primDCs[scIx >= primDCs.length ? primDCs.length - 1 : scIx] : DOCK_DIM_CONSTRAINT;
1099
1100                         int rowSize = rowColSizes[bIx2];
1101
1102                         for (LinkedDimGroup group : linkedGroups) {
1103                                 int groupSize = rowSize;
1104                                 if (group.span > 1)
1105                                         groupSize = LayoutUtil.sum(rowColSizes, bIx2, Math.min((group.span << 1) - 1, rowColSizes.length - bIx2 - 1));
1106
1107                                 group.layout(primDC, curPos, groupSize, group.span);
1108                         }
1109
1110                         curPos += (fromEnd ? -rowSize : rowSize);
1111                 }
1112         }
1113
1114         private static void addToSizeGroup(HashMap<String, int[]> sizeGroups, String sizeGroup, int[] size)
1115         {
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]});
1119                 } else {
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]);
1123                 }
1124         }
1125
1126         private static HashMap<String, Integer> addToEndGroup(HashMap<String, Integer> endGroups, String endGroup, int end)
1127         {
1128                 if (endGroup != null) {
1129                         if (endGroups == null)
1130                                 endGroups = new HashMap<String, Integer>(4);
1131
1132                         Integer oldEnd = endGroups.get(endGroup);
1133                         if (oldEnd == null || end > oldEnd)
1134                                 endGroups.put(endGroup, end);
1135                 }
1136                 return endGroups;
1137         }
1138
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}.
1143          */
1144         private FlowSizeSpec calcRowsOrColsSizes(boolean isHor, int containerSize)
1145         {
1146                 ArrayList<LinkedDimGroup>[] groupsLists = isHor ? colGroupLists : rowGroupLists;
1147                 Float[] defPush = isHor ? pushXs : pushYs;
1148
1149                 if (containerSize <= 0)
1150                         containerSize = isHor ? container.getWidth() : container.getHeight();
1151
1152                 BoundSize cSz = isHor ? lc.getWidth() : lc.getHeight();
1153                 if (!cSz.isUnset())
1154                         containerSize = cSz.constrain(containerSize, getParentSize(container, isHor), container);
1155
1156                 DimConstraint[] primDCs = (isHor? colConstr : rowConstr).getConstaints();
1157                 TreeSet<Integer> primIndexes = isHor ? colIndexes : rowIndexes;
1158
1159                 int[][] rowColBoundSizes = new int[primIndexes.size()][];
1160                 HashMap<String, int[]> sizeGroupMap = new HashMap<String, int[]>(4);
1161                 DimConstraint[] allDCs = new DimConstraint[primIndexes.size()];
1162
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];
1167
1168                         if (cellIx >= -MAX_GRID && cellIx <= MAX_GRID) {  // If not dock cell
1169                                 allDCs[r] = primDCs[cellIx >= primDCs.length ? primDCs.length - 1 : cellIx];
1170                         } else {
1171                                 allDCs[r] = DOCK_DIM_CONSTRAINT;
1172                         }
1173
1174                         ArrayList<LinkedDimGroup> groups = groupsLists[r];
1175
1176                         int[] groupSizes = new int[] {
1177                                         getTotalGroupsSizeParallel(groups, LayoutUtil.MIN, false),
1178                                         getTotalGroupsSizeParallel(groups, LayoutUtil.PREF, false),
1179                                         LayoutUtil.INF};
1180
1181                         correctMinMax(groupSizes);
1182                         BoundSize dimSize = allDCs[r].getSize();
1183
1184                         for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.MAX; sType++) {
1185
1186                                 int rowColSize = groupSizes[sType];
1187
1188                                 UnitValue uv = dimSize.getSize(sType);
1189                                 if (uv != null) {
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];
1198                                         } else {
1199                                                 rowColSize = uv.getPixels(containerSize, container, null);
1200                                         }
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
1203                                 }
1204
1205                                 rowColSizes[sType] = rowColSize;
1206                         }
1207
1208                         correctMinMax(rowColSizes);
1209                         addToSizeGroup(sizeGroupMap, allDCs[r].getSizeGroup(), rowColSizes);
1210
1211                         rowColBoundSizes[r] = rowColSizes;
1212                 }
1213
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());
1219                         }
1220                 }
1221
1222                 // Add the gaps
1223                 ResizeConstraint[] resConstrs = getRowResizeConstraints(allDCs);
1224
1225                 boolean[] fillInPushGaps = new boolean[allDCs.length + 1];
1226                 int[][] gapSizes = getRowGaps(allDCs, containerSize, isHor, fillInPushGaps);
1227
1228                 FlowSizeSpec fss = mergeSizesGapsAndResConstrs(resConstrs, fillInPushGaps, rowColBoundSizes, gapSizes);
1229
1230                 // Spanning components are not handled yet. Check and adjust the multi-row min/pref they enforce.
1231                 adjustMinPrefForSpanningComps(allDCs, defPush, fss, groupsLists);
1232
1233                 return fss;
1234         }
1235
1236         private static int getParentSize(ComponentWrapper cw, boolean isHor)
1237         {
1238                 ContainerWrapper p = cw.getParent();
1239                 return p != null ? (isHor ? cw.getWidth() : cw.getHeight()) : 0;
1240         }
1241
1242         private int[] getMinPrefMaxSumSize(boolean isHor, int[][] sizes)
1243         {
1244                 int[] retSizes = new int[3];
1245
1246                 BoundSize sz = isHor ? lc.getWidth() : lc.getHeight();
1247
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) {
1253                                                 if (i == 0)
1254                                                         retSizes[sType] = sz.getSize(sType).getPixels(getParentSize(container, isHor), container, null);
1255                                         } else {
1256                                                 int s = size[sType];
1257
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)
1262                                                                         s = bnd;
1263
1264                                                                 bnd = size[LayoutUtil.MIN];
1265                                                                 if (bnd > s)    // Includes s == LayoutUtil.NOT_SET since < 0.
1266                                                                         s = bnd;
1267                                                         }
1268
1269                                                         retSizes[sType] += s;   // MAX compensated below.
1270                                                 }
1271
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;
1275                                         }
1276                                 }
1277                         }
1278                 }
1279
1280                 correctMinMax(retSizes);
1281
1282                 return retSizes;
1283         }
1284
1285         private static ResizeConstraint[] getRowResizeConstraints(DimConstraint[] specs)
1286         {
1287                 ResizeConstraint[] resConsts = new ResizeConstraint[specs.length];
1288                 for (int i = 0; i < resConsts.length; i++)
1289                         resConsts[i] = specs[i].resize;
1290                 return resConsts;
1291         }
1292
1293         private static ResizeConstraint[] getComponentResizeConstraints(ArrayList<CompWrap> compWraps, boolean isHor)
1294         {
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;
1299
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);
1305                         }
1306                 }
1307                 return resConsts;
1308         }
1309
1310         private static boolean[] getComponentGapPush(ArrayList<CompWrap> compWraps, boolean isHor)
1311         {
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++) {
1315
1316                         boolean push = i > 0 && compWraps.get(i - 1).isPushGap(isHor, false);
1317
1318                         if (!push && i < (barr.length - 1))
1319                                 push = compWraps.get(i).isPushGap(isHor, true);
1320
1321                         barr[i] = push;
1322                 }
1323                 return barr;
1324         }
1325
1326         /** Returns the row gaps in pixel sizes. One more than there are <code>specs</code> sent in.
1327          * @param specs
1328          * @param refSize
1329          * @param isHor
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.
1332          */
1333         private int[][] getRowGaps(DimConstraint[] specs, int refSize, boolean isHor, boolean[] fillInPushGaps)
1334         {
1335                 BoundSize defGap = isHor ? lc.getGridGapX() : lc.getGridGapY();
1336                 if (defGap == null)
1337                         defGap = isHor ? PlatformDefaults.getGridGapX() : PlatformDefaults.getGridGapY();
1338                 int[] defGapArr = defGap.getPixelSizes(refSize, container, null);
1339
1340                 boolean defIns = !hasDocks();
1341
1342                 UnitValue firstGap = LayoutUtil.getInsets(lc, isHor ? 1 : 0, defIns);
1343                 UnitValue lastGap = LayoutUtil.getInsets(lc, isHor ? 3 : 2, defIns);
1344
1345         int[][] retValues = new int[specs.length + 1][];
1346
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;
1350
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)
1355                                 continue;
1356
1357                         BoundSize wrapGapSize = (wrapGapMap == null || isHor == lc.isFlowX() ? null : wrapGapMap.get(wgIx++));
1358
1359                         if (wrapGapSize == null) {
1360
1361                                 int[] gapBefore = specBefore != null ? specBefore.getRowGaps(container, null, refSize, false) : null;
1362                                 int[] gapAfter = specAfter != null ? specAfter.getRowGaps(container, null, refSize, true) : null;
1363
1364                                 if (edgeBefore && gapAfter == null && firstGap != null) {
1365
1366                                         int bef = firstGap.getPixels(refSize, container, null);
1367                                         retValues[i] = new int[] {bef, bef, bef};
1368
1369                                 } else if (edgeAfter && gapBefore == null && firstGap != null) {
1370
1371                                         int aft = lastGap.getPixels(refSize, container, null);
1372                                         retValues[i] = new int[] {aft, aft, aft};
1373
1374                                 } else {
1375                                         retValues[i] = gapAfter != gapBefore ? mergeSizes(gapAfter, gapBefore) : new int[] {defGapArr[0], defGapArr[1], defGapArr[2]};
1376                                 }
1377
1378                                 if (specBefore != null && specBefore.isGapAfterPush() || specAfter != null && specAfter.isGapBeforePush())
1379                                         fillInPushGaps[i] = true;
1380                         } else {
1381
1382                                 if (wrapGapSize.isUnset()) {
1383                                         retValues[i] = new int[] {defGapArr[0], defGapArr[1], defGapArr[2]};
1384                                 } else {
1385                                         retValues[i] = wrapGapSize.getPixelSizes(refSize, container, null);
1386                                 }
1387                                 fillInPushGaps[i] = wrapGapSize.getGapPush();
1388                         }
1389         }
1390         return retValues;
1391     }
1392
1393         private static int[][] getGaps(ArrayList<CompWrap> compWraps, boolean isHor)
1394         {
1395                 int compCount = compWraps.size();
1396         int[][] retValues = new int[compCount + 1][];
1397
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;
1402
1403                         retValues[i + 1] = mergeSizes(gap1, gap2);
1404         }
1405
1406         return retValues;
1407     }
1408
1409         private boolean hasDocks()
1410         {
1411                 return (dockOffX > 0 || dockOffY > 0 || rowIndexes.last() > MAX_GRID || colIndexes.last() > MAX_GRID);
1412         }
1413
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.
1417          * @param fss
1418          * @param groupsLists
1419          */
1420         private void adjustMinPrefForSpanningComps(DimConstraint[] specs, Float[] defPush, FlowSizeSpec fss, ArrayList<LinkedDimGroup>[] groupsLists)
1421         {
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];
1424
1425                         for (LinkedDimGroup group : groups) {
1426                                 if (group.span == 1)
1427                                         continue;
1428
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)
1433                                                 continue;
1434
1435                                         int rowSize = 0;
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)
1441                                                         rowSize += sz;
1442                                         }
1443
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);
1447                                         }
1448                                 }
1449                         }
1450                 }
1451         }
1452
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.
1457          */
1458         private ArrayList<LinkedDimGroup>[] divideIntoLinkedGroups(boolean isRows)
1459         {
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();
1464
1465                 @SuppressWarnings("unchecked")
1466                 ArrayList<LinkedDimGroup>[] groupLists = new ArrayList[primIndexes.size()];
1467
1468                 int gIx = 0;
1469                 for (int i : primIndexes) {
1470
1471                         DimConstraint dc;
1472                         if (i >= -MAX_GRID && i <= MAX_GRID) {  // If not dock cell
1473                                 dc = primDCs[i >= primDCs.length ? primDCs.length - 1 : i];
1474                         } else {
1475                                 dc = DOCK_DIM_CONSTRAINT;
1476                         }
1477
1478                         ArrayList<LinkedDimGroup> groupList = new ArrayList<LinkedDimGroup>(4);
1479                         groupLists[gIx++] = groupList;
1480
1481                         for (Integer ix : secIndexes) {
1482                                 Cell cell = isRows ? getCell(i, ix) : getCell(ix, i);
1483                                 if (cell == null || cell.compWraps.size() == 0)
1484                                         continue;
1485
1486                                 int span = (isRows ? cell.spany : cell.spanx);
1487                                 if (span > 1)
1488                                         span = convertSpanToSparseGrid(i, span, primIndexes);
1489
1490                                 boolean isPar = (cell.flowx == isRows);
1491
1492                                 if ((!isPar && cell.compWraps.size() > 1) || span > 1) {
1493
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);
1497                                         groupList.add(lg);
1498                                 } else {
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);
1503
1504                                                 String linkCtx = isBaseline ? "baseline" : null;
1505
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);
1512                                                                 foundList = true;
1513                                                                 break;
1514                                                         }
1515                                                 }
1516
1517                                                 // If none found and at last add a new group.
1518                                                 if (!foundList) {
1519                                                         int linkType = isBaseline ? LinkedDimGroup.TYPE_BASELINE : LinkedDimGroup.TYPE_PARALLEL;
1520                                                         LinkedDimGroup lg = new LinkedDimGroup(linkCtx, 1, linkType, !isRows, fromEnd);
1521                                                         lg.addCompWrap(cw);
1522                                                         groupList.add(lg);
1523                                                 }
1524                                         }
1525                                 }
1526                         }
1527                 }
1528                 return groupLists;
1529         }
1530
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.
1538          */
1539         private static int convertSpanToSparseGrid(int curIx, int span, TreeSet<Integer> indexes)
1540         {
1541                 int lastIx = curIx + span;
1542                 int retSpan = 1;
1543
1544                 for (Integer ix : indexes) {
1545                         if (ix <= curIx)
1546                                 continue;   // We have not arrived to the correct index yet
1547
1548                         if (ix >= lastIx)
1549                                 break;
1550
1551                         retSpan++;
1552                 }
1553                 return retSpan;
1554         }
1555
1556         private boolean isCellFree(int r, int c, ArrayList<int[]> occupiedRects)
1557         {
1558                 if (getCell(r, c) != null)
1559                         return false;
1560
1561                 for (int[] rect : occupiedRects) {
1562                         if (rect[0] <= c && rect[1] <= r && rect[0] + rect[2] > c && rect[1] + rect[3] > r)
1563                                 return false;
1564                 }
1565                 return true;
1566         }
1567
1568         private Cell getCell(int r, int c)
1569         {
1570                 return grid.get((r << 16) + (c & 0xffff));
1571         }
1572
1573         private void setCell(int r, int c, Cell cell)
1574         {
1575                 if (c < 0 || r < 0)
1576                         throw new IllegalArgumentException("Cell position cannot be negative. row: " + r + ", col: " + c);
1577
1578                 if (c > MAX_GRID || r > MAX_GRID)
1579                         throw new IllegalArgumentException("Cell position out of bounds. Out of cells. row: " + r + ", col: " + c);
1580
1581                 rowIndexes.add(r);
1582                 colIndexes.add(c);
1583
1584                 grid.put((r << 16) + (c & 0xffff), cell);
1585         }
1586
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.
1591          */
1592         private void addDockingCell(int[] dockInsets, int side, CompWrap cw)
1593         {
1594                 int r, c, spanx = 1, spany = 1;
1595                 switch (side) {
1596                         case 0:
1597                         case 2:
1598                                 r = side == 0 ? dockInsets[0]++ : dockInsets[2]--;
1599                                 c = dockInsets[1];
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
1602                                 break;
1603
1604                         case 1:
1605                         case 3:
1606                                 c = side == 1 ? dockInsets[1]++ : dockInsets[3]--;
1607                                 r = dockInsets[0];
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
1610                                 break;
1611
1612                         default:
1613                                 throw new IllegalArgumentException("Internal error 123.");
1614                 }
1615
1616                 rowIndexes.add(r);
1617                 colIndexes.add(c);
1618
1619                 grid.put((r << 16) + (c & 0xffff), new Cell(cw, spanx, spany, spanx > 1));
1620         }
1621
1622         /** A simple representation of a cell in the grid. Contains a number of component wraps, if they span more than one cell.
1623          */
1624         private static class Cell
1625         {
1626                 private final int spanx, spany;
1627                 private final boolean flowx;
1628                 private final ArrayList<CompWrap> compWraps = new ArrayList<CompWrap>(2);
1629
1630                 private boolean hasTagged = false;  // If one or more components have styles and need to be checked by the component sorter
1631
1632                 private Cell(CompWrap cw)
1633                 {
1634                         this(cw, 1, 1, true);
1635                 }
1636
1637                 private Cell(int spanx, int spany, boolean flowx)
1638                 {
1639                         this(null, spanx, spany, flowx);
1640                 }
1641
1642                 private Cell(CompWrap cw, int spanx, int spany, boolean flowx)
1643                 {
1644                         if (cw != null)
1645                                 compWraps.add(cw);
1646                         this.spanx = spanx;
1647                         this.spany = spany;
1648                         this.flowx = flowx;
1649                 }
1650         }
1651
1652         /** A number of component wraps that share a layout "something" <b>in one dimension</b>
1653          */
1654         private static class LinkedDimGroup
1655         {
1656                 private static final int TYPE_SERIAL = 0;
1657                 private static final int TYPE_PARALLEL = 1;
1658                 private static final int TYPE_BASELINE = 2;
1659
1660                 private final String linkCtx;
1661                 private final int span;
1662                 private final int linkType;
1663                 private final boolean isHor, fromEnd;
1664
1665                 private final ArrayList<CompWrap> _compWraps = new ArrayList<CompWrap>(4);
1666
1667                 private int lStart = 0, lSize = 0;  // Currently mostly for debug painting
1668
1669                 private LinkedDimGroup(String linkCtx, int span, int linkType, boolean isHor, boolean fromEnd)
1670                 {
1671                         this.linkCtx = linkCtx;
1672                         this.span = span;
1673                         this.linkType = linkType;
1674                         this.isHor = isHor;
1675                         this.fromEnd = fromEnd;
1676                 }
1677
1678                 private void addCompWrap(CompWrap cw)
1679                 {
1680                         _compWraps.add(cw);
1681                 }
1682
1683                 private void setCompWraps(ArrayList<CompWrap> cws)
1684                 {
1685                         if (_compWraps != cws) {
1686                                 _compWraps.clear();
1687                                 _compWraps.addAll(cws);
1688                         }
1689                 }
1690
1691                 private void layout(DimConstraint dc, int start, int size, int spanCount)
1692                 {
1693                         lStart = start;
1694                         lSize = size;
1695
1696                         if (_compWraps.isEmpty())
1697                                 return;
1698
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);
1704                         } else {
1705                                 layoutSerial(parent, _compWraps, dc, start, size, isHor, spanCount, fromEnd);
1706                         }
1707                 }
1708
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.
1711                  */
1712                 private int[] getMinPrefMax()
1713                 {
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();
1722                                         } else {
1723                                                 sizes[sType] = getTotalSizeSerial(_compWraps, sType, isHor);
1724                                         }
1725                                 }
1726                                 sizes[LayoutUtil.MAX] = LayoutUtil.INF;
1727                         }
1728                         return sizes;
1729                 }
1730         }
1731
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.
1734          *
1735          * Note! Does not ask the min/pref/max sizes again after the constructor. This means that
1736          */
1737         private final class CompWrap
1738         {
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;
1745
1746                 private int[][] gaps; // [top,left(actually before),bottom,right(actually after)][min,pref,max]
1747
1748                 private final int[] horSizes = new int[3];
1749                 private final int[] verSizes = new int[3];
1750
1751                 private int x = LayoutUtil.NOT_SET, y = LayoutUtil.NOT_SET, w = LayoutUtil.NOT_SET, h = LayoutUtil.NOT_SET;
1752
1753                 private int forcedPushGaps = 0;   // 1 == before, 2 = after. Bitwise.
1754
1755                 /**
1756                  * @param c
1757                  * @param cc
1758                  * @param eHideMode Effective hide mode. <= 0 means visible.
1759                  * @param useVisualPadding
1760                  */
1761                 private CompWrap(ComponentWrapper c, CC cc, int eHideMode, boolean useVisualPadding)
1762                 {
1763                         this.comp = c;
1764                         this.cc = cc;
1765                         this.eHideMode = eHideMode;
1766                         this.useVisualPadding = useVisualPadding;
1767                         this.isAbsolute = cc.getHorizontal().getSize().isAbsolute() && cc.getVertical().getSize().isAbsolute();
1768
1769                         if (eHideMode > 1) {
1770                                 gaps = new int[4][];
1771                                 for (int i = 0; i < gaps.length; i++)
1772                                         gaps[i] = new int[3];
1773                         }
1774                 }
1775
1776                 private int[] getSizes(boolean isHor)
1777                 {
1778                         validateSize();
1779                         return isHor ? horSizes : verSizes;
1780                 }
1781
1782                 private void validateSize()
1783                 {
1784                         BoundSize[] callbackSz = getCallbackSize(comp);
1785
1786                         if (isAbsolute && sizesOk && callbackSz == null)
1787                                 return;
1788
1789                         if (eHideMode <= 0) {
1790                                 int contentBias = comp.getContentBias();
1791
1792                                 int sizeHint = contentBias == -1 ? -1 : (contentBias == 0 ? (w != LayoutUtil.NOT_SET ? w : comp.getWidth()) : (h != LayoutUtil.NOT_SET ? h : comp.getHeight()));
1793
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();
1796
1797                                 for (int i = LayoutUtil.MIN; i <= LayoutUtil.MAX; i++) {
1798                                         switch (contentBias) {
1799                                                 case -1: // None
1800                                                 default:
1801                                                         horSizes[i] = getSize(hBS, i, true, useVisualPadding, -1);
1802                                                         verSizes[i] = getSize(vBS, i, false, useVisualPadding, -1);
1803                                                         break;
1804                                                 case 0: // Hor
1805                                                         horSizes[i] = getSize(hBS, i, true, useVisualPadding, -1);
1806                                                         verSizes[i] = getSize(vBS, i, false, useVisualPadding, sizeHint > 0 ? sizeHint : horSizes[i]);
1807                                                         break;
1808                                                 case 1: // Ver
1809                                                         verSizes[i] = getSize(vBS, i, false, useVisualPadding, -1);
1810                                                         horSizes[i] = getSize(hBS, i, true, useVisualPadding, sizeHint > 0 ? sizeHint : verSizes[i]);
1811                                                         break;
1812                                         }
1813                                 }
1814
1815                                 correctMinMax(horSizes);
1816                                 correctMinMax(verSizes);
1817                         } else {
1818                                 Arrays.fill(horSizes, 0); // Needed if component goes from visible -> invisible without recreating the grid.
1819                                 Arrays.fill(verSizes, 0);
1820                         }
1821                         sizesOk = true;
1822                 }
1823
1824                 private int getSize(BoundSize uvs, int sizeType, boolean isHor, boolean useVP, int sizeHint)
1825                 {
1826                         int size;
1827                         if (uvs == null || uvs.getSize(sizeType) == null) {
1828                                 switch(sizeType) {
1829                                         case LayoutUtil.MIN:
1830                                                 size = isHor ? comp.getMinimumWidth(sizeHint) : comp.getMinimumHeight(sizeHint);
1831                                                 break;
1832                                         case LayoutUtil.PREF:
1833                                                 size = isHor ? comp.getPreferredWidth(sizeHint) : comp.getPreferredHeight(sizeHint);
1834                                                 break;
1835                                         default:
1836                                                 size = isHor ? comp.getMaximumWidth(sizeHint) : comp.getMaximumHeight(sizeHint);
1837                                                 break;
1838                                 }
1839                                 if (useVP) {
1840                                         //Do not include visual padding when calculating layout
1841                                         int[] visualPadding = comp.getVisualPadding();
1842
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]);
1846                                 }
1847                         } else {
1848                                 ContainerWrapper par = comp.getParent();
1849                                 float refValue = isHor ? par.getWidth() : par.getHeight();
1850                                 size = uvs.getSize(sizeType).getPixels(refValue, par, comp);
1851                         }
1852                         return size;
1853                 }
1854
1855
1856                 private void calcGaps(ComponentWrapper before, CC befCC, ComponentWrapper after, CC aftCC, String tag, boolean flowX, boolean isLTR)
1857                 {
1858                         ContainerWrapper par = comp.getParent();
1859                         int parW = par.getWidth();
1860                         int parH = par.getHeight();
1861
1862                         BoundSize befGap = before != null ? (flowX ? befCC.getHorizontal() : befCC.getVertical()).getGapAfter() : null;
1863                         BoundSize aftGap = after != null ? (flowX ? aftCC.getHorizontal() : aftCC.getVertical()).getGapBefore() : null;
1864
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);
1869                 }
1870
1871                 private void setDimBounds(int start, int size, boolean isHor)
1872                 {
1873                         if (isHor) {
1874                                 if (start != x || w != size) {
1875                                         x = start;
1876                                         w = size;
1877                                         if (comp.getContentBias() == LayoutUtil.HORIZONTAL)
1878                                                 invalidateSizes(); // Only for components that have a bias the sizes will have changed.
1879                                 }
1880                         } else {
1881                                 if (start != y || h != size) {
1882                                         y = start;
1883                                         h = size;
1884                                         if (comp.getContentBias() == LayoutUtil.VERTICAL)
1885                                                 invalidateSizes(); // Only for components that have a bias the sizes will have changed.
1886                                 }
1887                         }
1888                 }
1889
1890                 void invalidateSizes()
1891                 {
1892                         sizesOk = false;
1893                 }
1894
1895                 private boolean isPushGap(boolean isHor, boolean isBefore)
1896                 {
1897                         if (isHor && ((isBefore ? 1 : 2) & forcedPushGaps) != 0)
1898                                 return true;    // Forced
1899
1900                         DimConstraint dc = cc.getDimConstraint(isHor);
1901                         BoundSize s = isBefore ? dc.getGapBefore() : dc.getGapAfter();
1902                         return s != null && s.getGapPush();
1903                 }
1904
1905                 /** Transfers the bounds to the component
1906                  */
1907                 private void transferBounds(boolean addVisualPadding)
1908                 {
1909                         if (cc.isExternal())
1910                                 return;
1911
1912                         int compX = x;
1913                         int compY = y;
1914                         int compW = w;
1915                         int compH = h;
1916
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]);
1926                                 }
1927                         }
1928
1929                         comp.setBounds(compX, compY, compW, compH);
1930                 }
1931
1932                 private void setForcedSizes(int[] sizes, boolean isHor)
1933                 {
1934                         if (sizes == null)
1935                                 return;
1936
1937                         System.arraycopy(sizes, 0, getSizes(isHor), 0, 3);
1938                         sizesOk = true;
1939                 }
1940
1941                 private void setGaps(int[] minPrefMax, int ix)
1942                 {
1943                         if (gaps == null)
1944                                 gaps = new int[][] {null, null, null, null};
1945
1946                         gaps[ix] = minPrefMax;
1947                 }
1948
1949                 private void mergeGapSizes(int[] sizes, boolean isHor, boolean isTL)
1950                 {
1951                         if (gaps == null)
1952                                 gaps = new int[][] {null, null, null, null};
1953
1954                         if (sizes == null)
1955                                 return;
1956
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;
1962                         }
1963
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]);
1967                 }
1968
1969                 private int getGapIx(boolean isHor, boolean isTL)
1970                 {
1971                         return isHor ? (isTL ? 1 : 3) : (isTL ? 0 : 2);
1972                 }
1973
1974                 private int getSizeInclGaps(int sizeType, boolean isHor)
1975                 {
1976                         return filter(sizeType, getGapBefore(sizeType, isHor) + getSize(sizeType, isHor) + getGapAfter(sizeType, isHor));
1977                 }
1978
1979                 private int getSize(int sizeType, boolean isHor)
1980                 {
1981                         return filter(sizeType, getSizes(isHor)[sizeType]);
1982                 }
1983
1984                 private int getGapBefore(int sizeType, boolean isHor)
1985                 {
1986                         int[] gaps = getGaps(isHor, true);
1987                         return gaps != null ? filter(sizeType, gaps[sizeType]) : 0;
1988                 }
1989
1990                 private int getGapAfter(int sizeType, boolean isHor)
1991                 {
1992                         int[] gaps = getGaps(isHor, false);
1993                         return gaps != null ? filter(sizeType, gaps[sizeType]) : 0;
1994                 }
1995
1996                 private int[] getGaps(boolean isHor, boolean isTL)
1997                 {
1998                         return gaps[getGapIx(isHor, isTL)];
1999                 }
2000
2001                 private int filter(int sizeType, int size)
2002                 {
2003                         if (size == LayoutUtil.NOT_SET)
2004                                 return sizeType != LayoutUtil.MAX ? 0 : LayoutUtil.INF;
2005                         return constrainSize(size);
2006                 }
2007
2008                 private boolean isBaselineAlign(boolean defValue)
2009                 {
2010                         Float g = cc.getVertical().getGrow();
2011                         if (g != null && g.intValue() != 0)
2012                                 return false;
2013
2014                         UnitValue al = cc.getVertical().getAlign();
2015                         return (al != null ? al == UnitValue.BASELINE_IDENTITY : defValue) && comp.hasBaseline();
2016                 }
2017
2018                 private int getBaseline(int sizeType)
2019                 {
2020                         return comp.getBaseline(getSize(sizeType, true), getSize(sizeType, false));
2021                 }
2022
2023                 void adjustMinHorSizeUp(int minSize)
2024                 {
2025                         int[] sz = getSizes(true);
2026                         if (sz[LayoutUtil.MIN] < minSize)
2027                                 sz[LayoutUtil.MIN] = minSize;
2028                         correctMinMax(sz);
2029                 }
2030         }
2031
2032         //***************************************************************************************
2033         //* Helper Methods
2034         //***************************************************************************************
2035
2036         private static void layoutBaseline(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, int sizeType, int spanCount)
2037         {
2038                 AboveBelow aboveBelow = getBaselineAboveBelow(compWraps, sizeType, true);
2039                 int blRowSize = aboveBelow.sum();
2040
2041                 CC cc = compWraps.get(0).cc;
2042
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;
2049
2050                 int offset = start + aboveBelow.maxAbove + (align != null ? Math.max(0, align.getPixels(size - blRowSize, parent, null)) : 0);
2051                 for (CompWrap cw : compWraps) {
2052                         cw.y += offset;
2053                         if (cw.y + cw.h > start + size)
2054                                 cw.h = start + size - cw.y;
2055                 }
2056         }
2057
2058         private static void layoutSerial(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, boolean isHor, int spanCount, boolean fromEnd)
2059         {
2060                 FlowSizeSpec fss = mergeSizesGapsAndResConstrs(
2061                                 getComponentResizeConstraints(compWraps, isHor),
2062                         getComponentGapPush(compWraps, isHor),
2063                                 getComponentSizes(compWraps, isHor),
2064                                 getGaps(compWraps, isHor));
2065
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);
2069         }
2070
2071         private static void setCompWrapBounds(ContainerWrapper parent, int[] allSizes, ArrayList<CompWrap> compWraps, UnitValue rowAlign,  int start, int size, boolean isHor, boolean fromEnd)
2072         {
2073                 int totSize = LayoutUtil.sum(allSizes);
2074                 CC cc = compWraps.get(0).cc;
2075                 UnitValue align = correctAlign(cc, rowAlign, isHor, fromEnd);
2076
2077                 int cSt = start;
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);
2082                 }
2083
2084                 for (int i = 0, bIx = 0, iSz = compWraps.size(); i < iSz; i++) {
2085                         CompWrap cw = compWraps.get(i);
2086                         if (fromEnd ) {
2087                                 cSt -= allSizes[bIx++];
2088                                 cw.setDimBounds(cSt - allSizes[bIx], allSizes[bIx], isHor);
2089                                 cSt -= allSizes[bIx++];
2090                         } else {
2091                                 cSt += allSizes[bIx++];
2092                                 cw.setDimBounds(cSt, allSizes[bIx], isHor);
2093                                 cSt += allSizes[bIx++];
2094                         }
2095                 }
2096         }
2097
2098         private static void layoutParallel(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, boolean isHor, boolean fromEnd)
2099         {
2100                 int[][] sizes = new int[compWraps.size()][];    // [compIx][gapBef,compSize,gapAft]
2101
2102                 for (int i = 0; i < sizes.length; i++) {
2103                         CompWrap cw = compWraps.get(i);
2104
2105                         DimConstraint cDc = cw.cc.getDimConstraint(isHor);
2106
2107                         ResizeConstraint[] resConstr = new ResizeConstraint[] {
2108                                         cw.isPushGap(isHor, true) ? GAP_RC_CONST_PUSH : GAP_RC_CONST,
2109                                         cDc.resize,
2110                                         cw.isPushGap(isHor, false) ? GAP_RC_CONST_PUSH : GAP_RC_CONST,
2111                         };
2112
2113                         int[][] sz = new int[][] {
2114                                 cw.getGaps(isHor, true), cw.getSizes(isHor), cw.getGaps(isHor, false)
2115                         };
2116
2117                         Float[] pushW = dc.isFill() ? GROW_100 : null;
2118
2119                         sizes[i] = LayoutUtil.calculateSerial(sz, resConstr, pushW, LayoutUtil.PREF, size);
2120                 }
2121
2122                 UnitValue rowAlign = dc.getAlignOrDefault(isHor);
2123                 setCompWrapBounds(parent, sizes, compWraps, rowAlign, start, size, isHor, fromEnd);
2124         }
2125
2126         private static void setCompWrapBounds(ContainerWrapper parent, int[][] sizes, ArrayList<CompWrap> compWraps, UnitValue rowAlign,  int start, int size, boolean isHor, boolean fromEnd)
2127         {
2128                 for (int i = 0; i < sizes.length; i++) {
2129                         CompWrap cw = compWraps.get(i);
2130
2131                         UnitValue align = correctAlign(cw.cc, rowAlign, isHor, fromEnd);
2132
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];
2137
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);
2143                         }
2144
2145                         cw.setDimBounds(fromEnd ? cSt - cSize : cSt, cSize, isHor);
2146                 }
2147         }
2148
2149         private static UnitValue correctAlign(CC cc, UnitValue rowAlign, boolean isHor, boolean fromEnd)
2150         {
2151                 UnitValue align = (isHor ? cc.getHorizontal() : cc.getVertical()).getAlign();
2152                 if (align == null)
2153                         align = rowAlign;
2154                 if (align == UnitValue.BASELINE_IDENTITY)
2155                         align = UnitValue.CENTER;
2156
2157                 if (fromEnd) {
2158                         if (align == UnitValue.LEFT)
2159                                 align = UnitValue.RIGHT;
2160                         else if (align == UnitValue.RIGHT)
2161                                 align = UnitValue.LEFT;
2162                 }
2163                 return align;
2164         }
2165
2166     private static class AboveBelow {
2167             int maxAbove;
2168             int maxBelow;
2169
2170             AboveBelow(int maxAbove, int maxBelow) {
2171             this.maxAbove = maxAbove;
2172             this.maxBelow = maxBelow;
2173         }
2174
2175         int sum() {
2176                 return maxAbove + maxBelow;
2177         }
2178     }
2179
2180         private static AboveBelow getBaselineAboveBelow(ArrayList<CompWrap> compWraps, int sType, boolean centerBaseline)
2181         {
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);
2188
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);
2193
2194                         if (centerBaseline)
2195                                 cw.setDimBounds(-baseline, height, false);
2196                 }
2197                 return new AboveBelow(maxAbove, maxBelow);
2198         }
2199
2200         private static int getTotalSizeParallel(ArrayList<CompWrap> compWraps, int sType, boolean isHor)
2201         {
2202                 int size = sType == LayoutUtil.MAX ? LayoutUtil.INF : 0;
2203
2204                 for (CompWrap cw : compWraps) {
2205                         int cwSize = cw.getSizeInclGaps(sType, isHor);
2206                         if (cwSize >= LayoutUtil.INF)
2207                                 return LayoutUtil.INF;
2208
2209                         if (sType == LayoutUtil.MAX ? cwSize < size : cwSize > size)
2210                                 size = cwSize;
2211                 }
2212                 return constrainSize(size);
2213         }
2214
2215         private static int getTotalSizeSerial(ArrayList<CompWrap> compWraps, int sType, boolean isHor)
2216         {
2217                 int totSize = 0;
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;
2223
2224                         totSize += wrap.getSize(sType, isHor);
2225                         totSize += (lastGapAfter = wrap.getGapAfter(sType, isHor));
2226
2227                         if (totSize >= LayoutUtil.INF)
2228                                 return LayoutUtil.INF;
2229                 }
2230                 return constrainSize(totSize);
2231         }
2232
2233         private static int getTotalGroupsSizeParallel(ArrayList<LinkedDimGroup> groups, int sType, boolean countSpanning)
2234         {
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;
2241
2242                                 if (sType == LayoutUtil.MAX ? grpSize < size : grpSize > size)
2243                                         size = grpSize;
2244                         }
2245                 }
2246                 return constrainSize(size);
2247         }
2248
2249         /**
2250          * @param compWraps
2251          * @param isHor
2252          * @return Might contain LayoutUtil.NOT_SET
2253          */
2254         private static int[][] getComponentSizes(ArrayList<CompWrap> compWraps, boolean isHor)
2255         {
2256                 int[][] compSizes = new int[compWraps.size()][];
2257                 for (int i = 0; i < compSizes.length; i++)
2258                         compSizes[i] = compWraps.get(i).getSizes(isHor);
2259                 return compSizes;
2260         }
2261
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.
2268          */
2269         private static FlowSizeSpec mergeSizesGapsAndResConstrs(ResizeConstraint[] resConstr, boolean[] gapPush, int[][] minPrefMaxSizes, int[][] gapSizes)
2270         {
2271                 int[][] sizes = new int[(minPrefMaxSizes.length << 1) + 1][];  // Make room for gaps around.
2272                 ResizeConstraint[] resConstsInclGaps = new ResizeConstraint[sizes.length];
2273
2274                 sizes[0] = gapSizes[0];
2275                 for (int i = 0, crIx = 1; i < minPrefMaxSizes.length; i++, crIx += 2) {
2276
2277                         // Component bounds and constraints
2278                         resConstsInclGaps[crIx] = resConstr[i];
2279                         sizes[crIx] = minPrefMaxSizes[i];
2280
2281                         sizes[crIx + 1] = gapSizes[i + 1];
2282
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;
2285
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;
2288                 }
2289
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];
2294                 }
2295
2296                 return new FlowSizeSpec(sizes, resConstsInclGaps);
2297         }
2298
2299         private static int[] mergeSizes(int[] oldValues, int[] newValues)
2300         {
2301                 if (oldValues == null)
2302                         return newValues;
2303
2304                 if (newValues == null)
2305                         return oldValues;
2306
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);
2310
2311                 return ret;
2312         }
2313
2314         private static int mergeSizes(int oldValue, int newValue, boolean toMax)
2315         {
2316                 if (oldValue == LayoutUtil.NOT_SET || oldValue == newValue)
2317                         return newValue;
2318
2319                 if (newValue == LayoutUtil.NOT_SET)
2320                         return oldValue;
2321
2322                 return toMax != oldValue > newValue ? newValue : oldValue;
2323         }
2324
2325         private static int constrainSize(int s)
2326         {
2327                 return s > 0 ? (s < LayoutUtil.INF ? s : LayoutUtil.INF) : 0;
2328         }
2329
2330         private static void correctMinMax(int s[])
2331         {
2332                 if (s[LayoutUtil.MIN] > s[LayoutUtil.MAX])
2333                         s[LayoutUtil.MIN] = s[LayoutUtil.MAX];  // Since MAX is almost always explicitly set use that
2334
2335                 if (s[LayoutUtil.PREF] < s[LayoutUtil.MIN])
2336                         s[LayoutUtil.PREF] = s[LayoutUtil.MIN];
2337
2338                 if (s[LayoutUtil.PREF] > s[LayoutUtil.MAX])
2339                         s[LayoutUtil.PREF] = s[LayoutUtil.MAX];
2340         }
2341
2342         private static final class FlowSizeSpec
2343         {
2344                 private final int[][] sizes;  // [row/col index][min, pref, max]
2345                 private final ResizeConstraint[] resConstsInclGaps;  // [row/col index]
2346
2347                 private FlowSizeSpec(int[][] sizes, ResizeConstraint[] resConstsInclGaps)
2348                 {
2349                         this.sizes = sizes;
2350                         this.resConstsInclGaps = resConstsInclGaps;
2351                 }
2352
2353                 /**
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.
2357                  * @param fromIx
2358                  * @param len
2359                  * @param sizeType
2360                  * @param eagerness How eager the algorithm should be to try to expand the sizes.
2361                  * <ul>
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 &gt; 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 &gt; 0 OR unspecified.
2364                  * <li>2 - Grow all rows/columns that have a grow weight &gt; 0.
2365                  * <li>3 - Grow all rows/columns that have a grow weight &gt; 0 OR unspecified.
2366                  * </ul>
2367                  * @return The new size.
2368                  */
2369                 private int expandSizes(DimConstraint[] specs, Float[] defGrow, int targetSize, int fromIx, int len, int sizeType, int eagerness)
2370                 {
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]};
2376
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);
2380
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)) {
2384                                                 continue;
2385                                         }
2386                                 }
2387                                 resConstr[i] = (ResizeConstraint) LayoutUtil.getIndexSafe(resConstsInclGaps, i + fromIx);
2388                         }
2389
2390                         Float[] growW = (eagerness == 1 || eagerness == 3) ? extractSubArray(specs, defGrow, fromIx, len): null;
2391                         int[] newSizes = LayoutUtil.calculateSerial(sizesToExpand, resConstr, growW, LayoutUtil.PREF, targetSize);
2392                         int newSize = 0;
2393
2394                         for (int i = 0; i < len; i++) {
2395                                 int s = newSizes[i];
2396                                 sizes[i + fromIx][sizeType] = s;
2397                                 newSize += s;
2398                         }
2399                         return newSize;
2400                 }
2401         }
2402
2403         private static Float[] extractSubArray(DimConstraint[] specs, Float[] arr, int ix, int len)
2404         {
2405                 if (arr == null || arr.length < ix + len) {
2406                         Float[] growLastArr = new Float[len];
2407
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;
2413                                         return growLastArr;
2414                                 }
2415                         }
2416                         return growLastArr;
2417                 }
2418
2419                 Float[] newArr = new Float[len];
2420         System.arraycopy(arr, ix, newArr, 0, len);
2421                 return newArr;
2422         }
2423
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)
2427         {
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)};
2430
2431                 PARENT_ROWCOL_SIZES_MAP[isRows ? 0 : 1].put(parComp, new int[][]{ixArr, sizes});
2432         }
2433
2434         static synchronized int[][] getSizesAndIndexes(Object parComp, boolean isRows)
2435         {
2436                 if (PARENT_ROWCOL_SIZES_MAP == null)
2437                         return null;
2438
2439                 return PARENT_ROWCOL_SIZES_MAP[isRows ? 0 : 1].get(parComp);
2440         }
2441
2442         private static WeakHashMap<Object, ArrayList<WeakCell>> PARENT_GRIDPOS_MAP = null;
2443         private static synchronized void saveGrid(ComponentWrapper parComp, LinkedHashMap<Integer, Cell> grid)
2444         {
2445                 if (PARENT_GRIDPOS_MAP == null)    // Lazy since only if designing in IDEs
2446                         PARENT_GRIDPOS_MAP = new WeakHashMap<Object, ArrayList<WeakCell>>(4);
2447
2448                 ArrayList<WeakCell> weakCells = new ArrayList<WeakCell>(grid.size());
2449
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;
2456
2457                                 for (CompWrap cw : cell.compWraps)
2458                                         weakCells.add(new WeakCell(cw.comp.getComponent(), x, y, cell.spanx, cell.spany));
2459                         }
2460                 }
2461
2462                 PARENT_GRIDPOS_MAP.put(parComp.getComponent(), weakCells);
2463         }
2464
2465         static synchronized HashMap<Object, int[]> getGridPositions(Object parComp)
2466         {
2467                 ArrayList<WeakCell> weakCells = PARENT_GRIDPOS_MAP != null ? PARENT_GRIDPOS_MAP.get(parComp) : null;
2468                 if (weakCells == null)
2469                         return null;
2470
2471                 HashMap<Object, int[]> retMap = new HashMap<Object, int[]>();
2472
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});
2477                 }
2478
2479                 return retMap;
2480         }
2481
2482         private static class WeakCell
2483         {
2484                 private final WeakReference<Object> componentRef;
2485                 private final int x, y, spanX, spanY;
2486
2487                 private WeakCell(Object component, int x, int y, int spanX, int spanY)
2488                 {
2489                         this.componentRef = new WeakReference<Object>(component);
2490                         this.x = x;
2491                         this.y = y;
2492                         this.spanX = spanX;
2493                         this.spanY = spanY;
2494                 }
2495         }
2496 }