/**************************************************************************** * Copyright (c) 2008, 2009 Jeremy Dowdall * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Jeremy Dowdall <jeremyd@aspencloud.com> - initial API and implementation *****************************************************************************/ package org.eclipse.nebula.cwt.v; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; public class VGridLayout extends VLayout { /** * numColumns specifies the number of cell columns in the layout. If * numColumns has a value less than 1, the layout will not set the size and * position of any controls. * * The default value is 1. */ public int numColumns = 1; /** * makeColumnsEqualWidth specifies whether all columns in the layout will be * forced to have the same width. * * The default value is false. */ public boolean makeColumnsEqualWidth = false; /** * marginWidth specifies the number of pixels of horizontal margin that will * be placed along the left and right edges of the layout. * * The default value is 5. */ public int marginWidth = 5; /** * marginHeight specifies the number of pixels of vertical margin that will * be placed along the top and bottom edges of the layout. * * The default value is 5. */ public int marginHeight = 5; /** * marginLeft specifies the number of pixels of horizontal margin that will * be placed along the left edge of the layout. * * The default value is 0. * * @since 3.1 */ public int marginLeft = 0; /** * marginTop specifies the number of pixels of vertical margin that will be * placed along the top edge of the layout. * * The default value is 0. * * @since 3.1 */ public int marginTop = 0; /** * marginRight specifies the number of pixels of horizontal margin that will * be placed along the right edge of the layout. * * The default value is 0. * * @since 3.1 */ public int marginRight = 0; /** * marginBottom specifies the number of pixels of vertical margin that will * be placed along the bottom edge of the layout. * * The default value is 0. * * @since 3.1 */ public int marginBottom = 0; /** * horizontalSpacing specifies the number of pixels between the right edge * of one cell and the left edge of its neighboring cell to the right. * * The default value is 5. */ public int horizontalSpacing = 5; /** * verticalSpacing specifies the number of pixels between the bottom edge of * one cell and the top edge of its neighboring cell underneath. * * The default value is 5. */ public int verticalSpacing = 5; private Map<Object, Point> sizes; public VGridLayout() { } public VGridLayout(int numColumns, boolean makeColumnsEqualWidth) { this.numColumns = numColumns; this.makeColumnsEqualWidth = makeColumnsEqualWidth; } @Override protected Point computeSize(VPanel panel, int wHint, int hHint, boolean flushCache) { VControl[] children = getChildren(panel); if(flushCache || (sizes == null)) { loadChildSizes(children); } Point size = new Point(0, 0); int col = 0; int row = 0; int rowWidth = 0; int rowHeight = 0; Set<Integer> taken = new HashSet<Integer>(children.length); for(VControl child : children) { while(taken.contains((row * numColumns) + col)) { col++; } Point p = sizes.get(child); GridData data = child.getLayoutData(); int w = (data.widthHint != SWT.DEFAULT) ? data.widthHint : (p.x + data.horizontalIndent); int h = (data.heightHint != SWT.DEFAULT) ? data.heightHint : (p.y + data.verticalIndent); rowWidth += w; rowHeight = Math.max(rowHeight, h); for(int i = 1; i < data.verticalSpan; i++) { taken.add(((row + i) * numColumns) + col); } col += data.horizontalSpan; if(col >= numColumns) { size.x = Math.max(size.x, rowWidth); size.y += rowHeight; col = 0; row++; rowWidth = 0; rowHeight = 0; } } if(col != 0) { size.x = Math.max(size.x, rowWidth); size.y += rowHeight; } int numRows = getNumRows(children); size.x += (marginLeft + (2 * marginWidth) + marginRight + (horizontalSpacing * (numColumns - 1))); size.y += (marginTop + (2 * marginHeight) + marginBottom + (verticalSpacing * (numRows - 1))); return size; } private void doLayout(VPanel parent, VControl[] children) { Point size = parent.getClientSize(); int border = 0; // TODO if(size.x == 0 || size.y == 0) { return; } int[] widths = new int[numColumns]; int[] heights = new int[getNumRows(children)]; Arrays.fill(widths, 0); Arrays.fill(heights, 0); int col = 0; int row = 0; Set<Integer> takenSet = new HashSet<Integer>(children.length); for(VControl child : children) { while(takenSet.contains((row * numColumns) + col)) { col++; } Point p = sizes.get(child); GridData data = child.getLayoutData(); if(widths[col] > -1) { if(makeColumnsEqualWidth || data.grabExcessHorizontalSpace) { widths[col] = -1; } else { int w = ((data.widthHint != SWT.DEFAULT) ? data.widthHint : p.x) + data.horizontalIndent; widths[col] = Math.max(widths[col], w); } } if(heights[row] > -1) { if(data.grabExcessVerticalSpace) { heights[row] = -1; } else { int h = ((data.heightHint != SWT.DEFAULT) ? data.heightHint : p.y) + data.verticalIndent; heights[row] = Math.max(heights[row], h); } } for(int i = 1; i < data.verticalSpan; i++) { takenSet.add(((row + i) * numColumns) + col); } col += data.horizontalSpan; if(col >= numColumns) { col = 0; row++; } } int xconsumed = marginLeft + (2 * marginWidth) + marginRight + (horizontalSpacing * (widths.length - 1)); int yconsumed = marginTop + (2 * marginHeight) + marginBottom + (verticalSpacing * (heights.length - 1)); int xgrabbers = 0; int ygrabbers = 0; for(int i : widths) { if(i == -1) { xgrabbers++; } else { xconsumed += i; } } for(int i : heights) { if(i == -1) { ygrabbers++; } else { yconsumed += i; } } int grabWidth = (xgrabbers > 0) ? (size.x - xconsumed) / xgrabbers : 0; int grabHeight = (ygrabbers > 0) ? (size.y - yconsumed) / ygrabbers : 0; col = 0; row = 0; int xslop = makeColumnsEqualWidth ? (int) Math.ceil((size.x - border - xconsumed - (grabWidth * xgrabbers)) / 2) + 1 : 0; int xoffset = (parent instanceof VPanel) ? ((VPanel) parent).getBounds().x : 0; int yoffset = (parent instanceof VPanel) ? ((VPanel) parent).getBounds().y : 0; int initX = marginLeft + marginWidth; int cellX = initX; int cellY = marginTop + marginHeight; int cellWidth = 0; int cellHeight = 0; int[] taken = new int[(heights.length * widths.length) + 1]; Arrays.fill(taken, -1); for(VControl child : children) { while(taken[(row*numColumns)+col] > 0) { cellX += taken[(row*numColumns)+col] + horizontalSpacing; col++; } Point p = sizes.get(child); GridData data = child.getLayoutData(); cellWidth = 0; for(int i = 0; i < data.horizontalSpan && (col + i) < widths.length; i++) { if((col + i) == 0) { cellWidth += xslop; } else if((col + i) == widths.length - 1) { cellWidth = size.x - border - cellX - marginWidth - marginRight - 1; break; } cellWidth += (widths[col + i] == -1) ? grabWidth : widths[col + i]; } cellHeight = 0; for(int i = 0; i < data.verticalSpan && (row + i) < heights.length; i++) { if((row + i) == heights.length - 1) { cellHeight = size.y - border - cellY - marginTop - marginBottom - 1; } else { cellHeight += (heights[row + i] == -1) ? grabHeight : heights[row + i]; } if(i > 0) { taken[((row+i)*numColumns)+col] = cellWidth; } } int w1 = (SWT.FILL == data.horizontalAlignment) ? (cellWidth - data.horizontalIndent) : p.x; int h1 = (SWT.FILL == data.verticalAlignment) ? (cellHeight - data.verticalIndent) : p.y; child.setBounds( xoffset + getX(data, cellX, cellWidth, w1), yoffset + getY(data, cellY, cellHeight, h1), w1, h1 ); col += data.horizontalSpan; if(col >= numColumns) { row++; col = 0; cellX = initX; cellY += (cellHeight + verticalSpacing); } else { cellX += (cellWidth + horizontalSpacing); } } } private VControl[] getChildren(VPanel parent) { VControl[] ca = parent.getChildren(); if(ca != null) { List<VControl> children = new ArrayList<VControl>(); for(VControl child : ca) { GridData data = child.getLayoutData(); if(data != null && !data.exclude) { children.add(child); } } return children.toArray(new VControl[children.size()]); } else { return new VControl[0]; } } private int getNumRows(VControl[] children) { int c = 0; int r = 0; int xc = 0; Set<Integer> taken = new HashSet<Integer>(children.length); for(VControl child : children) { while(taken.contains((r * numColumns) + c)) { c++; } GridData data = child.getLayoutData(); xc = data.horizontalSpan; for(int i = 1; i < data.verticalSpan; i++) { taken.add(((r + i) * numColumns) + c); } c += xc; if(c >= numColumns) { c = 0; r++; } } if(c != 0) { r++; } return r; } private int getX(GridData data, int cellStart, int cellSpan, int controlSpan) { switch(data.horizontalAlignment) { case SWT.FILL: case SWT.LEFT: case SWT.BEGINNING: return cellStart + data.horizontalIndent; case SWT.RIGHT: case SWT.END: return cellStart + cellSpan - controlSpan; case SWT.CENTER: default: return cellStart + ((cellSpan - controlSpan) / 2); } } private int getY(GridData data, int cellStart, int cellSpan, int controlSpan) { switch(data.verticalAlignment) { case SWT.FILL: case SWT.TOP: case SWT.BEGINNING: return cellStart + data.verticalIndent; case SWT.BOTTOM: case SWT.END: return cellStart + cellSpan - controlSpan; case SWT.CENTER: default: return cellStart + ((cellSpan - controlSpan) / 2); } } @Override protected void layout(VPanel panel, boolean flushCache) { VControl[] children = getChildren(panel); if(flushCache || (sizes == null)) { loadChildSizes(children); } if(children.length > 0) { doLayout(panel, children); panel.redraw(); } } private void loadChildSizes(VControl[] children) { if(sizes != null) { sizes.clear(); } sizes = new HashMap<Object, Point>(); for(VControl child : children) { sizes.put(child, child.computeSize(-1, -1)); } } }