/************************************************************************** * Copyright (c) 2001, 2002, 2003 by Punch Telematix. All rights reserved. * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * 1. Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * 3. Neither the name of Punch Telematix nor the names of * * other contributors may be used to endorse or promote products * * derived from this software without specific prior written permission.* * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * * IN NO EVENT SHALL PUNCH TELEMATIX OR OTHER CONTRIBUTORS BE LIABLE * * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************/ package java.awt; import java.io.ObjectOutputStream; import java.io.IOException; import java.util.Hashtable; /** * Difference with JDK 1.2.2: the <code>GridBagLayoutInfo</code> object that * is written to the serialzed form is always null; it is assumed that no one * is interested in intermediate states of a <code>GridBagLayoutInfo</code> object * and that a deserialized <code>GridBagLayout</code> object will always * recalculate it before using it. */ public class GridBagLayout implements LayoutManager2, java.io.Serializable { // static field ensuring compatibility between java and wonka generated serialized objects private static final long serialVersionUID = 8838754796412211005L; // serialized fields public int columnWidths[]; public int rowHeights[]; public double columnWeights[]; public double rowWeights[]; protected java.util.Hashtable comptable; protected GridBagConstraints defaultConstraints = null; protected GridBagLayoutInfo layoutInfo = null; protected static final int MAXGRIDSIZE = 512; protected static final int MINSIZE = 1; protected static final int PREFERREDSIZE = 2; // non serialzed fields private transient int rows = 0; private transient int cols = 0; /** * Method called when writing the serial form of an object of this class. * Instead of the actual field 'layoutInfo' a null object is written. * It is assumed that no one will create or will be interested in the * serialized form of a GridBagLayoutInfo object in a state intermediate * to start and end of the layout process. We do not even know how the * serialized form of such an object would look like, what fields it * would contain. No public API or serialized form is specified for class * GridBagLayoutInfo. */ private void writeObject(ObjectOutputStream os) throws IOException{ GridBagLayoutInfo info = layoutInfo; layoutInfo = null; os.defaultWriteObject(); layoutInfo = info; } // ------------- public methods ---------------- public GridBagLayout() { super(); comptable = new Hashtable(); defaultConstraints = new GridBagConstraints(); // use default as created by constructor } /** * @status Implemented * @remark If the component parameter is null a NullPointerException is thrown by a lower * level method. It is not caught by this method. */ public void addLayoutComponent(Component comp, Object constraints) { // The method just stores the constraints object if one is given. Of course the constraints // will be retrievable via the associated component's reference. if (constraints != null) { if (constraints instanceof GridBagConstraints) { setConstraints(comp, (GridBagConstraints)constraints); } else { throw new IllegalArgumentException("illegal parameter type"); } } } /** * @status Implemented * @remark Only defined in order to satisfy the LayoutManager interface. * Does nothing. */ public void addLayoutComponent(String name, Component comp) { // Adds the specified component with the specified name to the layout. } /** * @status Implemented * @remark If the component parameter is null a NullPointerException will be thrown * by a lower level method. It is not caught by this method. */ public GridBagConstraints getConstraints(Component component) { // Gets a copy of the constraints for the specified component. return (GridBagConstraints)(lookupConstraints(component).clone()); } public float getLayoutAlignmentX(Container container) { // Returns the alignment along the x axis. return Component.CENTER_ALIGNMENT; } public float getLayoutAlignmentY(Container container) { // Returns the alignment along the y axis. return Component.CENTER_ALIGNMENT; } public int[][] getLayoutDimensions() { // Retrieves the dimensions of each column and row in the gridbag. if (layoutInfo == null) { return new int[2][0]; } else { int[] widths = layoutInfo.columnWidths; int[] heights = layoutInfo.rowHeights; int[][] dimensions = new int [2][]; dimensions[0] = new int[widths.length]; dimensions[1] = new int[heights.length]; for (int i=0; i < widths.length; i++ ) { dimensions[0][i] = widths[i]; } for (int j=0; j < heights.length; j++ ) { dimensions[1][j] = heights[j]; } return dimensions; } } public Point getLayoutOrigin() { // Determines the origin of the layout grid. if (layoutInfo == null) { return new Point(0, 0); } else { return new Point(layoutInfo.xLeft, layoutInfo.yTop); } } public double[][] getLayoutWeights() { // Determines the weights of the layout grid's columns and rows. if (layoutInfo == null) { return new double[2][0]; // check that both lengths are zero! } else { double[] xWeights = layoutInfo.columnWeights; double[] yWeights = layoutInfo.rowWeights; double[][] weights = new double[2][]; weights[0] = new double[xWeights.length]; weights[1] = new double[yWeights.length]; for (int i=0; i < xWeights.length; i++ ) { weights[0][i] = xWeights[i]; } for (int j=0; j < yWeights.length; j++ ) { weights[1][j] = yWeights[j]; } return weights; } } public void invalidateLayout(Container target) { // Invalidates the layout, indicating that if the layout manager has cached information // about state or layout, it should be discarded. // For gridBagLayout by default this method does nothing. // shouldn't it set the layoutInfo = null ?? } public void layoutContainer(Container container) { // Lays out the container's components using the components' constraints. // This method is called by container when the layout is invalidated and needs // to be redone. It uses the components' preferred sizes. this.arrangeGrid(container); } public Point location(int x, int y) { // Determines which cell in the layout grid contains the point specified by (x, y). if (layoutInfo == null) { return new Point(0,0); } else { int i; int j; int k; int l; k = layoutInfo.xLeft; i = 0; while (i < layoutInfo.ncols && x >= k) { k += layoutInfo.columnWidths[i]; i++; } l = layoutInfo.yTop; j = 0; while (j < layoutInfo.nrows && y >= l) { l += layoutInfo.rowHeights[j]; j++; } return new Point(i, j); } } public Dimension maximumLayoutSize(Container container) { // Returns the maximum dimensions for this layout given the components in the specified target container. // return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } public Dimension minimumLayoutSize(Container container) { // Determines the minimum size of the target container using this grid bag layout. if (container == null) { return new Dimension(0,0); } else { GridBagLayoutInfo info = getLayoutInfo(container, MINSIZE); return getMinSize(container, info); } } public Dimension preferredLayoutSize(Container container) { // Determines the preferred size of the target container using this grid bag layout. if (container == null) { return new Dimension(0,0); } else { GridBagLayoutInfo info = getLayoutInfo(container, PREFERREDSIZE); return getMinSize(container, info); } } public void removeLayoutComponent(Component comp) { // Removes the specified component from this layout. // According to the specs, this method does nothing for this layout manager // shoudn't it remove the component with its constraints from the hashtable 'comptable'? } /** * @status Implemented * @remark If the component parameter is null a NullPointerException is thrown by a lower * level method. It is not caught by this method. In addition, to satisfy the Sun specs * of this method, a NullPointerException is thrown by this method if the constraints * parameter is null. */ public void setConstraints(Component comp, GridBagConstraints constraints) { // Sets the constraints for the specified component in this layout. if (constraints == null) { throw new NullPointerException(); } else { GridBagConstraints cclone = (GridBagConstraints)constraints.clone(); int x = cclone.gridx; int y = cclone.gridy; if (x < 0) x = GridBagConstraints.RELATIVE; if (y < 0) y = GridBagConstraints.RELATIVE; int w = cclone.gridwidth; int h = cclone.gridheight; if (w == 0) w = GridBagConstraints.REMAINDER; if (w < 0 && w != GridBagConstraints.REMAINDER && w != GridBagConstraints.RELATIVE) w = 1; if (h == 0) h = GridBagConstraints.REMAINDER; if (h < 0 && h != GridBagConstraints.REMAINDER && h != GridBagConstraints.RELATIVE) h = 1; cclone.gridx = x; cclone.gridy = y; cclone.gridwidth = w; cclone.gridheight = h; comptable.put(comp, cclone); // overwrites if already present } } // TODO public String toString() { // Returns a string representation of this grid bag layout's values. return getClass().getName() + "[columnWidths = " + columnWidths + ", rowHeights = " + rowHeights + ", columnWeights = " + columnWeights + ", rowWeights = " + rowWeights + "]"; } // ------------- protected methods ---------------- /** * @status Implemented * @remark Throws an 'IllegalArgumentException' if an illegal value for an anchor * constraint is detected. */ protected void adjustForGravity(GridBagConstraints constraints, Rectangle displayArea) { // Apply gridbag constraints (insets, fill, ipad, and anchor) to a displayArea in order // to derive its sub area that will actually be occupied by a component. int restWidth; int restHeight; Dimension compSize = constraints.compSize; displayArea.x += constraints.insets.left; displayArea.width -= (constraints.insets.left + constraints.insets.right); displayArea.y += constraints.insets.top; displayArea.height -= (constraints.insets.top + constraints.insets.bottom); restWidth = displayArea.width - compSize.width - constraints.ipadx; if ( (restWidth > 0) && !(constraints.fill == GridBagConstraints.HORIZONTAL || constraints.fill == GridBagConstraints.BOTH ) ) { displayArea.width = compSize.width + constraints.ipadx; } else { restWidth = 0; } restHeight = displayArea.height - compSize.height - constraints.ipady; if ( (restHeight > 0) && !(constraints.fill == GridBagConstraints.VERTICAL || constraints.fill == GridBagConstraints.BOTH ) ) { displayArea.height = compSize.height + constraints.ipady; } else { restHeight = 0; } switch (constraints.anchor) { case GridBagConstraints.CENTER: displayArea.x += restWidth/2; displayArea.y += restHeight/2; break; case GridBagConstraints.NORTH: displayArea.x += restWidth/2; break; case GridBagConstraints.NORTHEAST: displayArea.x += restWidth; break; case GridBagConstraints.EAST: displayArea.x += restWidth; displayArea.y += restHeight/2; break; case GridBagConstraints.SOUTHEAST: displayArea.x += restWidth; displayArea.y += restHeight; break; case GridBagConstraints.SOUTH: displayArea.x += restWidth/2; displayArea.y += restHeight; break; case GridBagConstraints.SOUTHWEST: displayArea.y += restHeight; break; case GridBagConstraints.WEST: displayArea.y += restHeight/2; break; case GridBagConstraints.NORTHWEST: break; default: throw new IllegalArgumentException("illegal anchor value"); } } protected void arrangeGrid(Container container) { /* * Equivalent of 'layoutContainer' * Lays out the container's components based on their preferred sizes (making use * of 'getLayoutInfo()', resulting in a, say, preferred gridbag size. * Adapts the gridbag's columnWidths and rowHeights to the actual window area * available as defined by the container's size, resulting * in the actual gridbag size. * Calculates actual available pixel areas for each component based on the actual * gribag size, and adapts each component's bounds to its actual pixel area, using the * 'adjustForGravity()' method. */ int i; int k; int l; int first; int last; int extraWidth; int deltaWidth; int restWidth; int extraHeight; int deltaHeight; int restHeight; double dz; double totalWeight; Component components[] = container.getComponents(); Component component; GridBagLayoutInfo info; Dimension infoDim; GridBagConstraints constraints; Rectangle bounds = new Rectangle(); Rectangle displayArea; Insets containerInsets = container.getInsets(); /* * If the container has no components, then nothing needs to be done. * If any columnWidths or rowHeights are given however, the gridBag is laid out * anyway. */ if (components.length == 0 && (this.columnWidths == null || this.columnWidths.length == 0) && (this.rowHeights == null || this.rowHeights.length == 0)) { return; } /* * get the preferred size for the gridbag. */ info = getLayoutInfo(container, PREFERREDSIZE); if (info.ncols == 0 && info.nrows == 0) { return; } layoutInfo = info; infoDim = getMinSize(container, info); /* * If the current preferred dimensions of the gridbag, including the container's * insets, don't match the container dimensions, then, if the total weight of all * columns (rows) is greater than zero, adjust the layoutInfo * widths (heights) arrays according to their column (row) weights. These * weights determine how columns (rows) of the gridbag stretch and shrink. * Distribute any extra (positive or negative) space over the columns (rows) of * the gridbag. If after that, any extra space remains, then disribute it * symmetrically among the first and last columns (rows) of the gridbag. * * If the current preferred dimensions of the gridbag, including the container's * insets, don't match the container dimensions, then, if the total weight of all * columns (rows) is zero, then center the gridbag horizontally (vertically) in * the container. */ /* // dump container and gridbag sizes before resizing the components. System.out.println("GridBagLayout.arrangeGrid:"); System.out.println(" container width=" + container.width + " container height=" + container.height); System.out.println(" gridbag width =" + infoDim.width + " gridgbag height =" + infoDim.height); */ extraWidth = container.width - infoDim.width; restWidth = extraWidth; if (extraWidth != 0) { totalWeight = 0.0; for (i = 0; i < info.ncols; i++) totalWeight += info.columnWeights[i]; if (totalWeight > 0.0) { dz=0; for (i = 0; i < info.ncols; i++) { if (info.columnWeights[i] > 0) { // include any remains of the non-integer width increment for the previous column, in // the non-integer width increment of the current column dz = (( ((double)extraWidth) * info.columnWeights[i]) / totalWeight + dz ); deltaWidth = (int)dz; dz -= deltaWidth; if (info.columnWidths[i] + deltaWidth < 0) { restWidth += (info.columnWidths[i] + deltaWidth); info.columnWidths[i] = 0; } else { restWidth -= deltaWidth; info.columnWidths[i] += deltaWidth; } } } info.columnWidths[0] += restWidth/2; info.columnWidths[info.ncols-1] += (restWidth - restWidth/2); restWidth = 0; } } extraHeight = container.height - infoDim.height; restHeight = extraHeight; if (extraHeight != 0) { totalWeight = 0.0; for (i = 0; i < info.nrows; i++) totalWeight += info.rowWeights[i]; if (totalWeight > 0.0) { dz=0; for (i = 0; i < info.nrows; i++) { if (info.rowWeights[i] > 0) { // include any remains of the non-integer height increment for the previous row, in // the non-integer height increment of the current row dz = (( ((double)extraHeight) * info.rowWeights[i]) / totalWeight + dz); deltaHeight = (int)dz; dz -= deltaHeight; if (info.rowHeights[i] + deltaHeight < 0) { restHeight += (info.rowHeights[i] + deltaHeight); info.rowHeights[i] = 0; } else { restHeight -= deltaHeight; info.rowHeights[i] += deltaHeight; } } } info.rowHeights[0] += restHeight/2; info.rowHeights[info.nrows-1] += (restHeight - restHeight/2); restHeight = 0; } } info.xLeft = containerInsets.left + restWidth/2; info.yTop = containerInsets.top + restHeight/2; /* * Finally, calculate bounds for each (visible) component in the container, * based on its display area in the gridbag. * A displayarea's size is based on the widths and heights * of the grid cells that are covered by a component. * Recall from 'getGridBagLayoutInfo()' that the width and height of a grid * cell was based on the preferred size, the insets and ipad constraints of the * "largest" component in the corresponding grid column and of the "largest" * component in the corresponding grid row. Consequently, the actual component * that populates a particular cell(s) will in general be smaller than the * corresponding pixel size of that cell(s). Besides, by adapting * the gridbag to the actual container size, even these "largest" components * may not fit anymore in their cell(s). * Every component's bounds thus have to be adapted to the pixel size of the * gridbag cell(s) it populates. The component's gridbag constraints have to be * accounted for in this calculation. */ for (i = 0 ; i < components.length ; i++) { component = components[i]; if (component.isVisible()) { constraints = lookupConstraints(component); displayArea = constraints.displayArea; /* * Calculate the display area in pixels */ first = displayArea.x; last = displayArea.x + displayArea.width; bounds.x = info.xLeft; for(k = 0; k < first; k++) { bounds.x += info.columnWidths[k]; } bounds.width = 0; for(k = first; k < last; k++) { bounds.width += info.columnWidths[k]; } first = displayArea.y; last = displayArea.y + displayArea.height; bounds.y = info.yTop; for(l = 0; l < first; l++) { bounds.y += info.rowHeights[l]; } bounds.height = 0; for(l = first; l < last; l++) { bounds.height += info.rowHeights[l]; } /* * Adjust the position and size of a component in its pixel area by applying * its individual preferred size and constraints to its pixel area. */ adjustForGravity(constraints, bounds); /* * If the resulting bounds's sizes are strictly positive * then make the component fit to that display area. * Else shrink the component to zero size */ if ((bounds.width > 0) && (bounds.height > 0)) { component.setBounds(bounds.x, bounds.y, bounds.width, bounds.height); } else { component.setBounds(0, 0, 0, 0); } } // end if (component.isVisible()) } } /** * @remark Calculates the gridbag by tentatively laying out all the components in the * gridbag using their preferred sizes, and independent of the actual size of the * components' container. */ protected java.awt.GridBagLayoutInfo getLayoutInfo(Container container, int sizeflag) { if (sizeflag != MINSIZE && sizeflag != PREFERREDSIZE) { throw new IllegalArgumentException("illegal parameter value"); // what else can I do? } else { synchronized (container.getTreeLock()) { int ncols = 0; // current number of gridbag columns int nrows = 0; // current number of gridbag rows int gridx; // gridx constraint of current component int gridy; // gridy constraint of current component int gridwidth; // gridwidth constraint of current component int gridheight; // gridheight constraint of current component int i; int j; int col; // index in the gridbag columns int row; // index in the gridbag rows double spanWeight; // weight of the range of columns (rows) covered by a component double extraWeight; // difference between a component's column (row) weight and the spanWeight. double restWeight; // rest of the extraWeight after distribution over the columns (rows) covered by a component double deltaWeight; // fraction of extraWeight added to a column (row) covered by a component int compWidth; // width of a component including its preferred size, inset and ipadx constraints int compHeight; // height of a component including its preferred size, inset and ipady constraints int spanWidth; // width of the range of columns covered by a component int extraWidth; // difference between compWidth and spanWidth int restWidth; // rest of extraWidth after distribution over the columns covered by a component int deltaWidth; // fraction of extraWidth added to a column covered by a component int spanHeight; // height of the range of rows covered by a component int extraHeight; // difference between compHeight and spanHeight int restHeight; // rest of extraHeight after distribution over the rows covered by a component int deltaHeight; // fraction of extraHeight added to a row covered by a component GridBagLayoutInfo info; // the GriBagLayoutInfo to be returned GridBagConstraints constr = null; // variable used to point to the components constraints. Component[] components = container.getComponents(); // the container's component's Dimension compSize; // the minimum or preferred size of a component Rectangle displayArea; // the range of columns and rows covered by a component in the gridbag. int[] maxColInRow = new int[MAXGRIDSIZE]; // array containing for each row of the gridbag, the index of the maximum occupied column + 1 int[] maxRowInCol = new int[MAXGRIDSIZE]; // array containing for each column of the gridbag, the index of the maximum occupied row + 1 int firsty; // first gridbag row occupied by current component int lasty; // gridbag row next to the last row occupied by current component int firstx; // first gridbag column occupied by current component int lastx; // gridbag column next to the last column occupied by current component int x = 0; // current column for positioning component with relative coordinates int y = 0; // current row for positioning component with relative coordinates boolean incrementColumn = true; // indicates current direction of relative positioning (more info below) /* * 1. Determine the required number of columns and rows in the gridbag. * * This is straightforward if all gridx, gridy, gridwidth and gridheight * constraints are absolute values. * If some gridx and gridy constraints * have value RELATIVE and some gridwidth and gridheight constraints have * value REMAINDER, calculations become more difficult since * some components have to be positioned relative to other components. * Positions and sizes of such components have to be calculated in order to * derive their contribution to the final gridbag size. * Let us call a component with gridx and/or gridy constraints of value * RELATIVE, a component with relative coordinates. The x(y) position of * such a component is not influenced by the (absolute or relative) * position of components that are added to the container later than this * component. Neither is the x (y) position of such a component influenced * by components that are already absolutely positioned on a row and column * that are higher than the current row and the current column. * If the component is the first component added, its position is (0,0). * Components with relative coordinates that are added after one or more components * are present already, are positioned relative to these present components. * That relative position depends mainly on the presence of components with * gridwidth or gridheight constraints of value GridBagConstraints.REMAINDER. * The boolean variable "incrementColumn", remembers the fact that it was a * component's gridwidth constraint or a component's gridheight constraint that * most recently occured with a value GridBagConstraints.REMAINDER. * It will become true when a gridwidth constraint of value * GridBagConstraints.REMAINDER occurs. It will become false when a gridheight * constraint of value GridBagConstraints.REMAINDER occurs. Its initial value * is chosen to be 'true'. * For components to be positioned relative, a current column 'x' and row 'y' is * maintained. * If a component causes 'incrementColumn' to become 'true' then, as long as no * other gridwidth or gridheight constraint of value GridBagConstraints.REMAINDER * occurs, subsequent components with relative coordinates, must all be placed * on the row next to that of the component with the special gridwidth * constraint. To determine the column to be assigned to that component, one * must consider the contiguous area of free cells near the end of each of * the rows covered by that conmponent, and select the first column that is free * in all these areas simultaneously. * If a component causes 'incrementColumn' to become 'false', then, as long as no other * gridwidth or gridheight constraint of value GridBagConstraints.REMAINDER occurs, * subsequent components with relative coordinates, must all be placed * on the column next to that of the component with the special gridheight * constraint. To determine the row to be assigned to that component, one * must consider the contiguous area of free cells near the end of each of * the columns covered by that conmponent, and select the first row that is free * in all these areas simultaneously. */ for (i = 0; i < components.length; i++) { if (components[i].isVisible()) { constr = lookupConstraints(components[i]); gridx = constr.gridx; gridy = constr.gridy; gridwidth = constr.gridwidth; gridheight = constr.gridheight; /* // dump status before positioning the current component System.out.println("++++++++++++++++++++++++++++++++++++"); System.out.println("Component number="+(i+1)+"/"+components.length); System.out.println("constr.gridx ="+gridx +" constr.gridy="+gridy); System.out.println("constr.gridwidth="+gridwidth+" constr.gridheight="+gridheight); System.out.println("First free row in each column & first free column in each row "); System.out.print(" "); for (j = 0; j < ncols; j++) System.out.print(maxRowInCol[j] + " "); System.out.println(); for (j = 0; j < nrows; j++) System.out.println(maxColInRow[j]+ " "); System.out.println(); */ /* * The actual width (height) of a component having gridwidth (gridheight) constraint * of value REMAINDER or RELATIVE is not known since the final number of columns and * rows of the gridbag is not known yet. * For now it is sufficient to assume a width (height) of 1 for the display area * of such a component. If the grid column (row) of that component, or a higher * columns (row) contains another component with a greater gridwidth (gridheight), then the width * (height) of the former component will maximally be that of the latter component. * That latter component's width (height) then determines the gridbag width * (height). * If it turns out that there are no other columns (rows) behind this component, * then the width (height) of that component will effectively be 1. */ if (gridwidth == GridBagConstraints.REMAINDER || gridwidth == GridBagConstraints.RELATIVE) { gridwidth = 1; } if (gridheight == GridBagConstraints.REMAINDER || gridheight == GridBagConstraints.RELATIVE) { gridheight = 1; } // calculate the top-left grid coordinate (firstx,firsty) to be occupied by the current component if (gridx == GridBagConstraints.RELATIVE && gridy == GridBagConstraints.RELATIVE){ if (incrementColumn) { firsty = y; lasty = firsty + gridheight; // choose as firstx for this component, the first column not occupied by any // component, in the rows, starting with the current row, occupied by this component. firstx = 0; for (j = firsty; j < lasty; j++) { if (maxColInRow[j] > firstx) firstx = maxColInRow[j]; } } else { firstx = x; lastx = firstx + gridwidth; // choose as firsty for this component, the first row not occupied by any // component, in the columns, starting with the current column, occupied by this component firsty = 0; for (j = firstx; j < lastx; j++) { if (maxRowInCol[j] > firsty) firsty = maxRowInCol[j]; } } } else if (gridx == GridBagConstraints.RELATIVE) { // y coordinate is specified as absolute value // don't treat a component different if incrementColumn is true or false; // even if it is false, shift the component some columns to the right if required. firsty = gridy; lasty = firsty + gridheight; firstx = 0; for (j = firsty; j < lasty; j++) { if (maxColInRow[j] > firstx) firstx = maxColInRow[j]; } } else if (gridy == GridBagConstraints.RELATIVE) { // x coordinate is specified as absolute value // don't treat a component different if incrementColumn is false or true; // even if it is true, shift the component some rows downwards if required. firstx = gridx; lastx = firstx + gridwidth; firsty = 0; for (j = firstx; j < lastx; j++) { if (maxRowInCol[j] > firsty) firsty = maxRowInCol[j]; } } else { // no relative positioning at all. firstx = gridx; firsty = gridy; } // calculate the bottom-right grid coordinate (lastx,lasty) to be occupied by the // current component and update the currently known gridbag dimensions if needed // (the goal of this iteration over the components). lastx = firstx + gridwidth; lasty = firsty + gridheight; if (lastx > ncols) ncols = lastx; if (lasty > nrows) nrows = lasty; // All subsequent processing is preparation for the next iteration. // update the cached values of highest occupied columns (rows) in the rows (columns) // occupied by the present component. // difference with sun jdk 1.2.2 implementation: "only update the cache if the // new values are higher than the old ones". for (j = firsty; j < lasty; j++) { if (lastx > maxColInRow[j]) // different from sun jdk 1.2.2 maxColInRow[j] = lastx; } for (j = firstx; j < lastx; j++) { if (lasty > maxRowInCol[j]) // different from sun jdk 1.2.2 maxRowInCol[j] = lasty; } // set initial values for the next relative position if (constr.gridwidth == GridBagConstraints.REMAINDER && constr.gridheight == GridBagConstraints.REMAINDER) { // the current component must occupy all remaining columns and all remaining rows; // no other component can be positioned relative to (after and below) this component; // let's hope no one tries to and let's return to initial conditions to play save. x = 0; y = 0; incrementColumn = true; } else if (constr.gridwidth == GridBagConstraints.REMAINDER) { x = 0; y = lasty; incrementColumn = true; } else if (constr.gridheight == GridBagConstraints.REMAINDER) { x = lastx; y = 0; incrementColumn = false; } else if (incrementColumn) { x = lastx; } else { y = lasty; } /* // dump status after positioning the current component System.out.println("DisplayArea:"); System.out.println(" left col ="+firstx+" right col ="+(lastx-1)); System.out.println(" top row ="+firsty+" bottom row ="+(lasty-1)); System.out.println("Current gridSize: "+ncols+" x "+nrows); System.out.println("First free row in each column & first free column in each row "); System.out.print(" "); for (j = 0; j < ncols; j++) System.out.print(maxRowInCol[j] + " "); System.out.println(); for (j = 0; j < nrows; j++) System.out.println(maxColInRow[j]); System.out.println(); */ } } // the size of the gridbag must at least be the size of the widths and heights // arrays, if they are given. if (this.columnWidths != null && this.columnWidths.length > ncols) ncols = this.columnWidths.length; if (this.rowHeights != null && this.rowHeights.length > nrows) nrows = this.rowHeights.length; info = new GridBagLayoutInfo(ncols, nrows); /* * 2. Calculate display areas for all visible components. * * To do so, position components with relative coordinates as it was done in the * first iteration. Sinds the actual size of the gridbag is known at this point, * actual widths and heights for components having gridwidth and/or gridheight * constraints of value GridBagConstraints.RELATIVE and GridBagConstraints.REMAINDER * can now be assigned. */ incrementColumn = true; x = y = 0; maxColInRow = new int[MAXGRIDSIZE]; maxRowInCol = new int[MAXGRIDSIZE]; for (i = 0; i < components.length; i++) { if (components[i].isVisible()) { constr = lookupConstraints(components[i]); gridx = constr.gridx; gridy = constr.gridy; gridwidth = constr.gridwidth; gridheight = constr.gridheight; /* // dump status before positioning the current component System.out.println("++++++++++++++++++++++++++++++++++++"); System.out.println("Component number="+(i+1)+"/"+components.length); System.out.println("constr.gridx ="+gridx +" constr.gridy="+gridy); System.out.println("constr.gridwidth="+gridwidth+" constr.gridheight="+gridheight); System.out.println("First free row in each column & first free column in each row "); System.out.print(" "); for (j = 0; j < ncols; j++) System.out.print(maxRowInCol[j] + " "); System.out.println(); for (j = 0; j < nrows; j++) System.out.println(maxColInRow[j]+ " "); System.out.println(); */ /* * This time, the final number of columns and rows of the gridbag are known, * so that actual width (height) of the display area can be calculated for a * component having gridwidth (gridheight) constraint of value * GridBagConstraints.REMAINDER or GridBagConstraints.RELATIVE. Start * calculating the lastx (lasty) value of their displayArea. */ if (gridwidth == GridBagConstraints.RELATIVE) lastx = ncols -1; else if (gridwidth == GridBagConstraints.REMAINDER) lastx = ncols; else lastx = 0; if (gridheight == GridBagConstraints.RELATIVE) lasty = nrows - 1; else if (gridheight == GridBagConstraints.REMAINDER) lasty = nrows; else lasty = 0; // calculate the top-left grid coordinate (firstx,firsty) to be occupied by the current component if (gridx == GridBagConstraints.RELATIVE && gridy == GridBagConstraints.RELATIVE){ if (incrementColumn) { firsty = y; if (gridheight != GridBagConstraints.RELATIVE && gridheight != GridBagConstraints.REMAINDER) lasty = firsty + gridheight; // choose as firstx for this component, the first column not occupied by any // component, in the rows, starting with the current row, occupied by this component. firstx = 0; for (j = firsty; j < lasty; j++) { if (maxColInRow[j] > firstx) firstx = maxColInRow[j]; } } else { firstx = x; if (gridwidth != GridBagConstraints.RELATIVE && gridwidth != GridBagConstraints.REMAINDER) lastx = firstx + gridwidth; // choose as firsty for this component, the first row not occupied by any // component, in the columns, starting with the current column, occupied by this component firsty = 0; for (j = firstx; j < lastx; j++) { if (maxRowInCol[j] > firsty) firsty = maxRowInCol[j]; } } } else if (gridx == GridBagConstraints.RELATIVE) { // y coordinate is specified as absolute value // don't treat a component different if incrementColumn is true or false; // even if it is false, shift the component some columns to the right if required. firsty = gridy; if (gridheight != GridBagConstraints.RELATIVE && gridheight != GridBagConstraints.REMAINDER) lasty = firsty + gridheight; firstx = 0; for (j = firsty; j < lasty; j++) { if (maxColInRow[j] > firstx) firstx = maxColInRow[j]; } } else if (gridy == GridBagConstraints.RELATIVE) { // x coordinate is specified as absolute value // don't treat a component different if incrementColumn is false or true; // even if it is true, shift the component some rows downwards if required. firstx = gridx; if (gridwidth != GridBagConstraints.RELATIVE && gridwidth != GridBagConstraints.REMAINDER) lastx = firstx + gridwidth; firsty = 0; for (j = firstx; j < lastx; j++) { if (maxRowInCol[j] > firsty) firsty = maxRowInCol[j]; } } else { // no relative positioning at all. firstx = gridx; firsty = gridy; } // calculate the absolute gridwidth and gridheight for the current component // if it is unknown yet. if (gridwidth == GridBagConstraints.RELATIVE || gridwidth == GridBagConstraints.REMAINDER) gridwidth = lastx - firstx; if (gridheight == GridBagConstraints.RELATIVE || gridheight == GridBagConstraints.REMAINDER) gridheight = lasty - firsty; // store the component's display area as a rectangle with its constraints. constr.displayArea = new Rectangle(firstx, firsty, gridwidth, gridheight); // store the component's size with its constraints for later use if (sizeflag == PREFERREDSIZE) constr.compSize = components[i].getPreferredSize(); else constr.compSize = components[i].getMinimumSize(); // All subsequent processing is preparing for the next iteration. // update the cached values of highest occupied columns (rows) in the rows (columns) // occupied by the present component. // difference with sun jdk 1.2.2 implementation: "only update the cache if the // new values are higher than the old ones". lastx = firstx + gridwidth; // lastx might not be known yet in some cases lasty = firsty + gridheight; // lasty might not be known yet in some cases for (j = firsty; j < lasty; j++) { if (lastx > maxColInRow[j]) // different from sun jdk 1.2.2 maxColInRow[j] = lastx; } for (j = firstx; j < lastx; j++) { if (lasty > maxRowInCol[j]) // different from sun jdk 1.2.2 maxRowInCol[j] = lasty; } // set initial values for the next relative position if (constr.gridwidth == GridBagConstraints.REMAINDER && constr.gridheight == GridBagConstraints.REMAINDER) { // the current component must cover all remaining columns and all remaining rows in // in the gridbag; no other component can be positioned relative to (after and below) // this component; let's hope no one tries to and let's return to initial conditions // to play save. y = 0; x = 0; incrementColumn = true; } else if (constr.gridwidth == GridBagConstraints.REMAINDER) { x = 0; y = lasty; incrementColumn = true; } else if (constr.gridheight == GridBagConstraints.REMAINDER) { x = lastx; y = 0; incrementColumn = false; } else if (incrementColumn) { x = lastx; } else { y = lasty; } /* // dump status after positioning the current component System.out.println("DisplayArea:"); System.out.println(" left col ="+firstx+" right col ="+(lastx-1)); System.out.println(" top row ="+firsty+" bottom row ="+(lasty-1)); System.out.println("Current gridSize: "+ncols+" x "+nrows); System.out.println("First free row in each column & first free column in each row "); System.out.print(" "); for (j = 0; j < ncols; j++) System.out.print(maxRowInCol[j] + " "); System.out.println(); for (j = 0; j < nrows; j++) System.out.println(maxColInRow[j]); System.out.println(); */ } } /* * 3. Calculate weights of all gridbag columns & rows based on the weight * constraints of the components in the container. * Start with all components width displayarea width (height) constraint equal to 1; then * consider components width gridwith (gridheight) constraint > 1, in the order that * they were inserted in the container */ // possible optimisation to applying overwrites after weights are calculated: // apply overwrites before weights are calculated, i.e. initialise the weights // with the overwrites here: // // if (columnWeights != null) // System.arraycopy(this.columnWeights, 0, info.columnWeights, 0, this.columnWeights.length); // if (rowWeights != null) // System.arraycopy(this.rowWeights, 0, info.rowWeights, 0, this.rowWeights.length); for (i = 0; i < components.length; i++) { if (components[i].isVisible()) { constr = lookupConstraints(components[i]); displayArea = constr.displayArea; if (displayArea.width == 1 && constr.weightx > info.columnWeights[displayArea.x]) info.columnWeights[displayArea.x] = constr.weightx; if (displayArea.height == 1 && constr.weighty > info.rowWeights[displayArea.y]) info.rowWeights[displayArea.y] = constr.weighty; } } for (i = 0; i < components.length; i++) { if (components[i].isVisible()) { constr = lookupConstraints(components[i]); displayArea = constr.displayArea; if (displayArea.width > 1) { firstx = displayArea.x; lastx = displayArea.x + displayArea.width; spanWeight = 0.0; for (col = firstx; col < lastx; col++){ spanWeight += info.columnWeights[col]; } extraWeight = constr.weightx - spanWeight; if (extraWeight > 0.0) { restWeight = extraWeight; if (spanWeight > 0.0){ for (col = firstx; col < lastx; col++){ if (info.columnWeights[col] > 0.0) { deltaWeight = (extraWeight * info.columnWeights[col]) / spanWeight; info.columnWeights[col] += deltaWeight; restWeight -= deltaWeight; } } } info.columnWeights[lastx - 1] += restWeight; } } if (displayArea.height > 1) { firsty = displayArea.y; lasty = displayArea.y + displayArea.height; spanWeight = 0.0; for (row = firsty; row < lasty; row++){ spanWeight += info.rowWeights[row]; } extraWeight = constr.weighty - spanWeight; if (extraWeight > 0.0) { restWeight = extraWeight; if (spanWeight > 0.0) { for (row = firsty; row < lasty; row++){ if (info.rowWeights[row] > 0.0) { deltaWeight = (extraWeight * info.rowWeights[row]) / spanWeight; info.rowWeights[row] += deltaWeight; restWeight -= deltaWeight; } } } info.rowWeights[lasty - 1] += restWeight; } } } // end if (comnponents[i].isVisible()) } // end for (int i = 0; i < components.length; i++) { /* // dump calculated column and row weights for (col = 0; col < info.ncols; col++) System.out.println("info.columnWeights["+col+"]="+info.columnWeights[col]); for (row = 0; row < info.nrows; row++) System.out.println("info.rowWeights["+row+"]="+info.rowWeights[row]); */ // Overwrite the calculated values if overwrites for the weights are given // Nothing is known about the length of the overwrite arrays; the maximum // number of columnWeights (rowWeights) that can be overwritten is the length // of the shortest of the arrays this.columnWeights and info.columnWeights // (this.rowWeights and info.rowWeights) if (this.columnWeights != null) { lastx = this.columnWeights.length < info.columnWeights.length ? this.columnWeights.length : info.columnWeights.length; for (col = 0; col < lastx; col++) { if (this.columnWeights[col] > info.columnWeights[col]) info.columnWeights[col] = this.columnWeights[col]; } } if (this.rowWeights != null) { lasty = this.rowWeights.length < info.rowWeights.length ? this.rowWeights.length : info.rowWeights.length; for (row = 0; row < lasty; row++) { if (this.rowWeights[row] > info.rowWeights[row]) info.rowWeights[row] = this.rowWeights[row]; } } /* * 4. Calculate widths of all columns and heights of all rows of the gridbag; make * use of the width (height) of all the components in a column (row) to do so, * and take inset and ipad constraints of components into account. * It is not specified how to distribute a component's width (height) over several * columns (rows) of the grid, in case the components' gridwidth (gridheight) * constraint is > 1; let's do it the same way the weights are distributed higher; * that seems a good idea. * Start with all components completely contained in one grid column (row) i.e. * components with gridwith (gridheight) constraint equal to 1; then * consider components covering several grid columns (rows) i.e. components with * gridwith (gridheight) constraint > 1, in the order that they were inserted in * the container; the chance that such a component adds any extra width (height) * to the columns (rows) of its span is rather small if one * of these columns (rows) contains a complete component already; * remark that initial values in info.columnWidths and info.rowHeights are zero. */ for (i = 0; i < components.length; i++) { if (components[i].isVisible()) { constr = lookupConstraints(components[i]); displayArea = constr.displayArea; compSize = constr.compSize; if (displayArea.width == 1) { compWidth = compSize.width + constr.insets.left + constr.insets.right + constr.ipadx; if (compWidth > info.columnWidths[displayArea.x]) info.columnWidths[displayArea.x] = compWidth; } if (displayArea.height == 1) { compHeight = compSize.height + constr.insets.top + constr.insets.bottom + constr.ipady; if (compHeight > info.rowHeights[displayArea.y]) info.rowHeights[displayArea.y] = compHeight; } } } for (i = 0; i < components.length; i++) { if (components[i].isVisible()) { constr = lookupConstraints(components[i]); displayArea = constr.displayArea; compSize = constr.compSize; if (displayArea.width > 1) { compWidth = compSize.width + constr.insets.left + constr.insets.right + constr.ipadx; firstx = displayArea.x; lastx = displayArea.x + displayArea.width; spanWidth = 0; for (col = firstx; col < lastx; col++){ spanWidth += info.columnWidths[col]; } extraWidth = compWidth - spanWidth; if (extraWidth > 0) { spanWeight = 0.0; for (col = firstx; col < lastx; col++){ spanWeight += info.columnWeights[col]; } restWidth = extraWidth; if (spanWeight > 0.0){ for (col = firstx; col < lastx; col++){ if (info.columnWeights[col] > 0.0) { deltaWidth = (int)( (((double)extraWidth) * info.columnWeights[col]) / spanWeight ); info.columnWidths[col] += deltaWidth; restWidth -= deltaWidth; } } } info.columnWidths[lastx - 1] += restWidth; } } if (displayArea.height > 1) { compHeight = compSize.height + constr.insets.top + constr.insets.bottom + constr.ipady; firsty = displayArea.y; lasty = displayArea.y + displayArea.height; spanHeight = 0; for (row = firsty; row < lasty; row++){ spanHeight += info.rowHeights[row]; } extraHeight = compHeight - spanHeight; if (extraHeight > 0) { spanWeight = 0.0; for (row = firsty; row < lasty; row++){ spanWeight += info.rowWeights[row]; } restHeight = extraHeight; if (spanWeight > 0.0){ for (row = firsty; row < lasty; row++){ if (info.rowWeights[row] > 0.0) { deltaHeight = (int)( (((double)extraHeight) * info.rowWeights[row]) / spanWeight ); info.rowHeights[row] += deltaHeight; restHeight -= deltaHeight; } } } info.rowHeights[lasty - 1] += restHeight; } } } } // for (int i = 0; i < components.length; i++) { // Overwrite the calculated values if overwrites for the widths and heights are given if (this.columnWidths != null) { // length of info.columnswidths is at least that of this.columnWidths lastx = this.columnWidths.length; for (col = 0; col < lastx; col++) { if (this.columnWidths[col] > info.columnWidths[col]) info.columnWidths[col] = this.columnWidths[col]; } } if (this.rowHeights != null) { // length of info.rowHeights is at least that of this.rowHeights lasty = this.rowHeights.length; for (row = 0; row < lasty; row++) { if (this.rowHeights[row] > info.rowHeights[row]) info.rowHeights[row] = this.rowHeights[row]; } } /* // dump calculated column widths and row heights for (col = 0; col < info.ncols; col++) System.out.println("info.columnWidths["+col+"]="+info.columnWidths[col]); for (row = 0; row < info.nrows; row++) System.out.println("info.rowHeights["+row+"]="+info.rowHeights[row]); */ return info; } } } /** * @remark Retrieves the minimum dimension of a gridbag taking into account the widths * and heights of the columns and rows in the GridBagLayoputInfo, and the insets of the * container. */ protected Dimension getMinSize(Container container, GridBagLayoutInfo info) { // Retrieves the minimum dimension of a gridbag. if (container == null || info == null) { return new Dimension(0, 0); } else { Insets insets = container.getInsets(); int w = 0; int h = 0; for (int i = 0; i < info.ncols; i++) { w += info.columnWidths[i]; } for (int j = 0; j < info.nrows; j++) { h += info.rowHeights[j]; } return new Dimension(insets.left + w + insets.right, insets.top + h + insets.bottom); } } /** * @status Implemented * @remark If the 'component' parameter is null, a NullPointerException will be thrown * by a lower level method. It is not caught by this method. */ protected GridBagConstraints lookupConstraints(Component component) { // Retrieves the constraints (not a copy) for the specified component. GridBagConstraints c = (GridBagConstraints)(comptable.get(component)); if (c == null) { setConstraints(component, defaultConstraints); c = (GridBagConstraints)comptable.get(component); } return c; } }