/** Calculate the minimum and preferred size for every row and column. */ private void calculateSizes() { Dimension dim[] = new Dimension[child.size()]; for (int i = 0; i < dim.length; i++) dim[i] = child.get(i).widget.getMinimumSize(); minRowSize = calculateRequiredSizes(dim, true); minColSize = calculateRequiredSizes(dim, false); for (int i = 0; i < dim.length; i++) { ChildInfo info = child.get(i); LayoutInfo layout = (info.layout == null ? defaultLayout : info.layout); dim[i] = layout.getPreferredSize(info.widget); } prefRowSize = calculateRequiredSizes(dim, true); prefColSize = calculateRequiredSizes(dim, false); }
/** * Remove a child Widget from this container. * * @param index the index of the Widget to remove */ public void remove(int index) { Widget w = child.get(index).widget; getComponent().remove(w.getComponent()); child.remove(index); removeAsParent(w); invalidateSize(); }
/** * Set the range of cells occupied by a Widget. * * @param index the index of the Widget for which to set the cells * @param cells a Rectangle specifying the range of rows and columns to be occupied by the Widget */ public void setChildCells(int index, Rectangle cells) { ChildInfo info = child.get(index); info.x = cells.x; info.y = cells.y; info.width = cells.width; info.height = cells.height; invalidateSize(); }
/** * Calculate the minimum or preferred size of every row or column. * * @param dim the minimum or preferred size of every child * @param row true if row sizes should be calculated, false if column sizes should be calculated * @return the size required for every row or column */ private int[] calculateRequiredSizes(Dimension dim[], boolean row) { // Build a linked list of size requirements of every child. LinkedList<int[]> requiredList = new LinkedList<int[]>(); for (int i = 0; i < dim.length; i++) { ChildInfo info = child.get(i); if (row) requiredList.addLast(new int[] {info.y, info.height, dim[i].height}); else requiredList.addLast(new int[] {info.x, info.width, dim[i].width}); } // Find the required size for each row or column. int width[] = new int[row ? rowWeight.length : colWeight.length]; double weight[] = (row ? rowWeight : colWeight); for (int currentWidth = 1; requiredList.size() > 0; currentWidth++) { // Apply constraints for all children which occupy currentWidth rows or columns. Iterator<int[]> iter = requiredList.iterator(); while (iter.hasNext()) { int req[] = iter.next(); if (req[1] != currentWidth) continue; iter.remove(); if (currentWidth == 1) { width[req[0]] = Math.max(width[req[0]], req[2]); continue; } // Find how much space is currently available. int total = 0; for (int i = 0; i < currentWidth; i++) total += width[req[0] + i]; if (total >= req[2]) continue; // It is already wide enough. // Allocate additional space to the rows or columns, based on their weights. double totalWeight = 0.0; for (int i = 0; i < currentWidth; i++) totalWeight += weight[req[0] + i]; int extra[] = new int[currentWidth]; int totalExtra = 0; for (int i = 0; i < currentWidth - 1; i++) { double w = (totalWeight > 0.0 ? weight[req[0] + i] / totalWeight : 1.0 / currentWidth); extra[i] += w * (req[2] - total); totalExtra += extra[i]; } extra[extra.length - 1] = req[2] - total - totalExtra; for (int i = 0; i < currentWidth; i++) width[req[0] + i] += extra[i]; } } return width; }
/** * Set the number of columns in this FormContainer. If this increases the number of columns, the * new columns will have weights of 1.0 by default. If this decreases the number of columns, any * child Widgets that extend beyond the last column will be removed. */ public void setColumnCount(int columns) { double newWeight[] = new double[columns]; if (columns > colWeight.length) { for (int i = 0; i < colWeight.length; i++) newWeight[i] = colWeight[i]; for (int i = colWeight.length; i < newWeight.length; i++) newWeight[i] = 1.0; } else { for (int i = child.size() - 1; i >= 0; i--) { ChildInfo info = child.get(i); if (info.x + info.width > columns) remove(i); } for (int i = 0; i < newWeight.length; i++) newWeight[i] = colWeight[i]; } colWeight = newWeight; }
/** * Set the number of rows in this FormContainer. If this increases the number of rows, the new * rows will have weights of 1.0 by default. If this decreases the number of rows, any child * Widgets that extend beyond the last row will be removed. */ public void setRowCount(int rows) { double newWeight[] = new double[rows]; if (rows > rowWeight.length) { for (int i = 0; i < rowWeight.length; i++) newWeight[i] = rowWeight[i]; for (int i = rowWeight.length; i < newWeight.length; i++) newWeight[i] = 1.0; } else { for (int i = child.size() - 1; i >= 0; i--) { ChildInfo info = child.get(i); if (info.y + info.height > rows) remove(i); } for (int i = 0; i < newWeight.length; i++) newWeight[i] = rowWeight[i]; } rowWeight = newWeight; }
/** * Layout the child Widgets. This may be invoked whenever something has changed (the size of this * WidgetContainer, the preferred size of one of its children, etc.) that causes the layout to no * longer be correct. If a child is itself a WidgetContainer, its layoutChildren() method will be * called in turn. */ public void layoutChildren() { if (minColSize == null) calculateSizes(); Dimension size = getComponent().getSize(); int rowPos[] = calculatePositions(minRowSize, prefRowSize, rowWeight, size.height); int colPos[] = calculatePositions(minColSize, prefColSize, colWeight, size.width); Rectangle cell = new Rectangle(); for (int i = 0; i < child.size(); i++) { ChildInfo info = child.get(i); LayoutInfo layout = (info.layout == null ? defaultLayout : info.layout); cell.x = (info.x == 0 ? 0 : colPos[info.x - 1]); cell.y = (info.y == 0 ? 0 : rowPos[info.y - 1]); cell.width = colPos[info.x + info.width - 1] - cell.x; cell.height = rowPos[info.y + info.height - 1] - cell.y; info.widget.getComponent().setBounds(layout.getWidgetLayout(info.widget, cell)); if (info.widget instanceof WidgetContainer) ((WidgetContainer) info.widget).layoutChildren(); } }
/** Get a Collection containing all child Widgets of this container. */ public Collection<Widget> getChildren() { ArrayList<Widget> list = new ArrayList<Widget>(child.size()); for (int i = 0; i < child.size(); i++) list.add(child.get(i).widget); return list; }
/** Get the i'th child of this container. */ public Widget getChild(int i) { return child.get(i).widget; }
/** * Get the index of a particular Widget. * * @param widget the Widget to locate * @return the position of the Widget within this container */ public int getWidgetIndex(Widget widget) { for (int i = 0; i < child.size(); i++) if (child.get(i).widget == widget) return i; return -1; }
/** Remove all child Widgets from this container. */ public void removeAll() { getComponent().removeAll(); for (int i = 0; i < child.size(); i++) removeAsParent(child.get(i).widget); child.clear(); invalidateSize(); }
/** * Get the range of cells occupied by a Widget. * * @param index the index of the Widget for which to get the cells * @return a Rectangle specifying the range of rows and columns occupied by the Widget */ public Rectangle getChildCells(int index) { ChildInfo info = child.get(index); return new Rectangle(info.x, info.y, info.width, info.height); }
/** * Set the LayoutInfo for a particular Widget. * * @param widget the Widget for which to set the LayoutInfo * @param layout the new LayoutInfo. If null, the default LayoutInfo will be used */ public void setChildLayout(Widget widget, LayoutInfo layout) { int index = getWidgetIndex(widget); if (index == -1) return; child.get(index).layout = layout; invalidateSize(); }
/** * Get the LayoutInfo for a particular Widget. * * @param widget the Widget for which to get the LayoutInfo * @return the LayoutInfo being used for that Widget. This may return null, which indicates that * the default LayoutInfo is being used. It will also return null if the specified Widget is * not a child of this container. */ public LayoutInfo getChildLayout(Widget widget) { int index = getWidgetIndex(widget); if (index == -1) return null; return child.get(index).layout; }
/** * Set the LayoutInfo for a particular Widget. * * @param index the index of the Widget for which to set the LayoutInfo * @param layout the new LayoutInfo. If null, the default LayoutInfo will be used */ public void setChildLayout(int index, LayoutInfo layout) { child.get(index).layout = layout; invalidateSize(); }
/** * Get the LayoutInfo for a particular Widget. * * @param index the index of the Widget for which to get the LayoutInfo * @return the LayoutInfo being used for that Widget. This may return null, which indicates that * the default LayoutInfo is being used. */ public LayoutInfo getChildLayout(int index) { return child.get(index).layout; }