/* * Copyright (c) 2005 Matthew Hall and others. * 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: * Matthew Hall - initial API and implementation */ package org.eclipse.nebula.paperclips.core.grid; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.nebula.paperclips.core.PaperClips; import org.eclipse.nebula.paperclips.core.Print; import org.eclipse.nebula.paperclips.core.PrintIterator; import org.eclipse.nebula.paperclips.core.grid.internal.GridCellImpl; import org.eclipse.nebula.paperclips.core.grid.internal.GridIterator; import org.eclipse.nebula.paperclips.core.internal.util.PaperClipsUtil; import org.eclipse.nebula.paperclips.core.internal.util.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; /** * A Print which arranges child prints into a grid. A grid is initialized with a * series of GridColumns, and child prints are laid out into those columns by * invoking the add(...) methods. * <p> * GridPrint uses a column sizing algorithm based on the <a * href=http://www.w3.org/TR/html4/appendix/notes.html#h-B.5.2.2>W3C * recommendation</a> for automatic layout of tables. GridPrint deviates from * the recommendation on one important point: if there is less width available * on the print device than the calculated "minimum" size of the grid, the * columns will be scaled down to <em>less</em> than their calculated minimum * widths. Only when one of the columns goes below its "absolute minimum" will * the grid fail to print ( {@link PrintIterator#next(int, int)} returns null). * <p> * GridPrint offers three basic methods of specifying column size. * <ol> * <li>Default size. The column will be somewhere between it's minimum and * preferred width. GridPrint will determine the optimum widths for all default * size columns, using the modified W3C recommendation described above. This is * the recommended option for most cases. * <li>Preferred size. The column will be sized to it's preferred width. This * option is sometimes appropriate, for example when certain portions of text * should not be allowed to line-wrap. In cases where only a few cells in a * column need to be prevented from line wrapping, consider wrapping them in a * NoBreakPrint instead. * <li>Explicit size. The column will be the size you specify, expressed in * points. 72 points = 1". * </ol> * Example: GridPrint grid = new GridPrint("d, p, 72pts"); * <p> * In addition, any column can be given a grow attribute. In the event a grid is * not as wide as the page, those columns with the grow attribute set will be * widened to fill the extra space. * <p> * Because GridPrint scales columns according to their minimum sizes in the * worst-case scenario, the absolute minimum size of a GridPrint is dependant on * its child prints and is not clearly defined. * <p> * If a grid has one of more columns with the grow attribute set, the grid is * horizontally greedy. Greedy prints take up all the available space on the * page. * * @author Matthew Hall * @see GridColumn * @see PrintIterator#minimumSize() * @see PrintIterator#preferredSize() */ public final class GridPrint implements Print { /** * Constant colspan value indicating that all remaining columns in the row * should be used. */ public static final int REMAINDER = -1; /** * Constant column size value indicating that the column should be given its * preferred size. (In the context of W3C's autolayout recommendation, this * has the effect of setting the columns minimum width to its preferred * width. This value is used in the GridColumn constructor. */ public static final int PREFERRED = 0; /** * Constant cell spacing value indicating that the borders of adjacent cells * should overlap. */ public static final int BORDER_OVERLAP = -1; private GridLook look; /** The columns for this grid. */ final List columns; // List<GridColumn> /** Array of column groups. */ int[][] columnGroups = new int[0][]; /** * Two-dimension list of all header cells. Each element of this list * represents a row in the header. Each element of a row represents a * cellspan in that row. */ final List header = new ArrayList(); // List <List <GridCell>> /** Column cursor - the column that the next added header cell will go into. */ private int headerCol = 0; /** * Two-dimensional list of all body cells. Each element of this list * represents a row in the body. Each element of a row represents a cellspan * in that row. */ final List body = new ArrayList(); // List <List <GridCell>> /** Column cursor - the column that the next added print will go into. */ private int bodyCol = 0; boolean cellClippingEnabled = true; /** * Two-dimension list of all footer cells. Each element of this list * represents a row in the footer. Each element of a row represents a * cellspan in that row. */ // List <List <GridCell>> final List footer = new ArrayList(); /** Column cursor - the column that the next added footer cell will go into. */ private int footerCol = 0; /** * Constructs a GridPrint with no columns and a default look. */ public GridPrint() { this(new GridColumn[0]); } /** * Constructs a GridPrint with no columns and the given look. * * @param look * the look to apply to the constructed grid. */ public GridPrint(GridLook look) { this(new GridColumn[0], look); } /** * Constructs a GridPrint with the given columns and a default look. * * @param columns * a comma-separated list of parseable column specs. * @see GridColumn#parse(String) */ public GridPrint(String columns) { this(parseColumns(columns)); } /** * Constructs a GridPrint with the given columns and look. * * @param columns * a comma-separated list of parseable column specs. * @param look * the look to apply to the constructed grid. * @see GridColumn#parse(String) */ public GridPrint(String columns, GridLook look) { this(parseColumns(columns), look); } /** * Constructs a GridPrint with the given columns and a default look. * * @param columns * the columns for the new grid. */ public GridPrint(GridColumn[] columns) { Util.noNulls(columns); this.columns = new ArrayList(); for (int i = 0; i < columns.length; i++) this.columns.add(columns[i]); this.look = new DefaultGridLook(); } /** * Constructs a GridPrint with the given columns and look. * * @param columns * the columns for the new grid. * @param look * the look to apply to the constructed grid. */ public GridPrint(GridColumn[] columns, GridLook look) { this(columns); setLook(look); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((body == null) ? 0 : body.hashCode()); result = prime * result + bodyCol; result = prime * result + (cellClippingEnabled ? 1231 : 1237); result = prime * result + GridPrint.hashCode(columnGroups); result = prime * result + ((columns == null) ? 0 : columns.hashCode()); result = prime * result + ((footer == null) ? 0 : footer.hashCode()); result = prime * result + footerCol; result = prime * result + ((header == null) ? 0 : header.hashCode()); result = prime * result + headerCol; result = prime * result + ((look == null) ? 0 : look.hashCode()); return result; } private static int hashCode(int[][] array) { int prime = 31; if (array == null) return 0; int result = 1; for (int index = 0; index < array.length; index++) { result = prime * result + (array[index] == null ? 0 : hashCode(array[index])); } return result; } private static int hashCode(int[] array) { int prime = 31; if (array == null) return 0; int result = 1; for (int index = 0; index < array.length; index++) { result = prime * result + array[index]; } return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; GridPrint other = (GridPrint) obj; if (body == null) { if (other.body != null) return false; } else if (!body.equals(other.body)) return false; if (bodyCol != other.bodyCol) return false; if (cellClippingEnabled != other.cellClippingEnabled) return false; if (!Util.equal(columnGroups, other.columnGroups)) return false; if (columns == null) { if (other.columns != null) return false; } else if (!columns.equals(other.columns)) return false; if (footer == null) { if (other.footer != null) return false; } else if (!footer.equals(other.footer)) return false; if (footerCol != other.footerCol) return false; if (header == null) { if (other.header != null) return false; } else if (!header.equals(other.header)) return false; if (headerCol != other.headerCol) return false; if (look == null) { if (other.look != null) return false; } else if (!look.equals(other.look)) return false; return true; } /** * Adds the column on the right edge of the grid. Any cells which have been * added to the grid prior to adding the column will be adjusted as follows: * the right-hand cell of each completed row will have it's colspan expanded * to fill the added column. * * @param column * the column to add to the grid. * @see GridColumn#parse(String) */ public void addColumn(String column) { addColumn(columns.size(), GridColumn.parse(column)); } /** * Adds the column on the right edge of the grid. Any cells which have been * added to the grid prior to adding the column will be adjusted as follows: * the right-hand cell of each completed row will have it's colspan expanded * to fill the added column. * * @param column * the column to add to the grid. */ public void addColumn(GridColumn column) { addColumn(columns.size(), column); } /** * Inserts the column at the specified position in the grid. Any cells which * have been added to the grid prior to adding the column will be adjusted * as follows: on each row, the cell which overlaps or whose right edge * touches the insert position will be expanded to fill the added column. * * @param index * the insert position. * @param column * the column to be inserted. * @see GridColumn#parse(String) */ public void addColumn(int index, String column) { addColumn(index, GridColumn.parse(column)); } /** * Inserts the column at the specified position in the grid. Any cells which * have been added to the grid prior to adding the column will be adjusted * as follows: on each row, the cell which overlaps or whose right edge * touches the insert position will be expanded to fill the added column. * * @param index * the insert position. * @param column * the column to be inserted. */ public void addColumn(int index, GridColumn column) { checkColumnInsert(index); Util.notNull(column); this.columns.add(index, column); adjustForColumnInsert(index, 1); } /** * Adds the columns on the right edge of the grid. Any cells which have been * added to the grid prior to adding the columns will be adjusted as * follows: the right-hand cell of each completed row will have it's colspan * expanded to fill the added columns. * * @param columns * the columns to add to the grid. * @see GridColumn#parse(String) */ public void addColumns(String columns) { addColumns(this.columns.size(), parseColumns(columns)); } /** * Adds the columns on the right edge of the grid. Any cells which have been * added to the grid prior to adding the columns will be adjusted as * follows: the right-hand cell of each completed row will have it's colspan * expanded to fill the added columns. * * @param columns * the columns to add to the grid. */ public void addColumns(GridColumn[] columns) { addColumns(this.columns.size(), columns); } /** * Inserts the columns at the specified position in the grid. Any cells * which have been added to the grid prior to adding the columns will be * adjusted as follows: on each row, the cell which overlaps or whose right * edge touches the insert position will be expanded to fill the added * columns. * * @param index * the insert position. * @param columns * the columns to be inserted. * @see GridColumn#parse(String) */ public void addColumns(int index, String columns) { addColumns(index, parseColumns(columns)); } /** * Inserts the columns at the specified position in the grid. Any cells * which have been added to the grid prior to adding the columns will be * adjusted as follows: on each row, the cell which overlaps or whose right * edge touches the insert position will be expanded to fill the added * columns. * * @param index * the insert position. * @param columns * the columns to be inserted. * @see GridColumn#parse(String) */ public void addColumns(int index, GridColumn[] columns) { checkColumnInsert(index); Util.noNulls(columns); this.columns.addAll(index, Arrays.asList(columns)); adjustForColumnInsert(index, columns.length); } private void checkColumnInsert(int index) { if (index < 0 || index > this.columns.size()) PaperClips.error(SWT.ERROR_INVALID_RANGE, "index = " + index + ", size = " + this.columns.size()); //$NON-NLS-1$ //$NON-NLS-2$ } private void adjustForColumnInsert(int index, int count) { adjustCellsForColumnInsert(header, index, count); adjustCellsForColumnInsert(body, index, count); adjustCellsForColumnInsert(footer, index, count); adjustColumnGroupsForColumnInsert(index, count); if (bodyCol > index) bodyCol += count; if (headerCol > index) headerCol += count; if (footerCol > index) footerCol += count; } private void adjustCellsForColumnInsert(List rows, int index, int count) { for (int rowI = 0; rowI < rows.size(); rowI++) { List row = (List) rows.get(rowI); int col = 0; for (int cellI = 0; cellI < row.size(); cellI++) { GridCell cell = (GridCell) row.get(cellI); col += cell.getColSpan(); // Adjust the cell which extends through the insert point, or // whose right side touches the insert // point. Except on the last row, don't adjust the final cell if // it only touches the insert point // (the user may be adding columns right before s/he adds column // headers). if ( // cell overlaps insert point, or (col > index) || // right side touches insert point but is not the final cell. (col == index && (rowI + 1 < rows.size() || cellI + 1 < row .size()))) { row.set(cellI, new GridCellImpl(cell.getHorizontalAlignment(), cell.getVerticalAlignment(), cell .getContent(), cell.getColSpan() + count)); break; } } } } private void adjustColumnGroupsForColumnInsert(int index, int count) { for (int groupI = 0; groupI < columnGroups.length; groupI++) { int[] group = columnGroups[groupI]; for (int i = 0; i < group.length; i++) if (group[i] >= index) group[i] += count; } } /** * Separates the comma-separated argument and parses each piece to obtain an * array of GridColumns. * * @param columns * the comma-separated list of column specs. * @return GridColumn array with the requested columns. */ private static GridColumn[] parseColumns(String columns) { Util.notNull(columns); String[] cols = columns.split("\\s*,\\s*"); //$NON-NLS-1$ GridColumn[] result = new GridColumn[cols.length]; for (int i = 0; i < cols.length; i++) result[i] = GridColumn.parse(cols[i]); return result; } /** * Returns an array of <code>GridColumn</code>s which are the columns in the * receiver. * * @return an array of <code>GridColumn</code>s which are the columns in the * receiver. */ public GridColumn[] getColumns() { return (GridColumn[]) columns.toArray(new GridColumn[columns.size()]); } /** * Adds the Print to the grid header, with default alignment and a colspan * of 1. * * @param cell * the print to add. */ public void addHeader(Print cell) { headerCol = add(header, headerCol, SWT.DEFAULT, SWT.DEFAULT, cell, 1); } /** * Adds the Print to the grid header, using the given alignment. * * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. * @param cell * the print to add. */ public void addHeader(int hAlignment, Print cell) { headerCol = add(header, headerCol, hAlignment, SWT.DEFAULT, cell, 1); } /** * Adds the Print to the grid header, using the given alignment. * * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. * @param vAlignment * the vertical alignment of the print within the grid cell. One * of {@link SWT#DEFAULT}, {@link SWT#TOP}, {@link SWT#CENTER}, * {@link SWT#BOTTOM}, or {@link SWT#FILL}. A value of FILL * indicates that the cell is vertically greedy, so GridPrint * will limit the cell's height to the tallest non-FILL cell in * the row. * @param cell * the print to add. */ public void addHeader(int hAlignment, int vAlignment, Print cell) { headerCol = add(header, headerCol, hAlignment, vAlignment, cell, 1); } /** * Adds the Print to the grid header, with the given colspan and the default * alignment. * * @param cell * the print to add. * @param colspan * the number of columns to span, or {@link GridPrint#REMAINDER} * to span the rest of the row. */ public void addHeader(Print cell, int colspan) { headerCol = add(header, headerCol, SWT.DEFAULT, SWT.DEFAULT, cell, colspan); } /** * Adds the Print to the grid header, using the given colspan and alignment. * * @param cell * the print to add. * @param colspan * the number of columns to span, or {@link GridPrint#REMAINDER} * to span the rest of the row. * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. */ public void addHeader(int hAlignment, Print cell, int colspan) { headerCol = add(header, headerCol, hAlignment, SWT.DEFAULT, cell, colspan); } /** * Adds the Print to the grid header, using the given colspan and alignment. * * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. * @param vAlignment * the vertical alignment of the print within the grid cell. One * of {@link SWT#DEFAULT}, {@link SWT#TOP}, {@link SWT#CENTER}, * {@link SWT#BOTTOM}, or {@link SWT#FILL}. A value of FILL * indicates that the cell is vertically greedy, so GridPrint * will limit the cell's height to the tallest non-FILL cell in * the row. * @param cell * the print to add. * @param colspan * the number of columns to span, or {@link GridPrint#REMAINDER} * to span the rest of the row. */ public void addHeader(int hAlignment, int vAlignment, Print cell, int colspan) { headerCol = add(header, headerCol, hAlignment, vAlignment, cell, colspan); } /** * Returns an array containing the header cells in this grid. Each inner * array represents one row in the header. * * @return an array containing the header cells in this grid. */ public GridCell[][] getHeaderCells() { return getGridCellArray(header); } /** * Returns an array containing the body cells in the grid. Each inner array * represents one row in the body. * * @return an array containing the body cells in the grid. */ public GridCell[][] getBodyCells() { return getGridCellArray(body); } /** * Returns an array containing the footer cells in the grid. Each inner * array represents one row in the footer. * * @return an array containing the footer cells in the grid. */ public GridCell[][] getFooterCells() { return getGridCellArray(footer); } private static GridCell[][] getGridCellArray(List list) { GridCell[][] cells = new GridCell[list.size()][]; for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) { List row = (List) list.get(rowIndex); GridCell[] rowCells = new GridCell[row.size()]; for (int cellIndex = 0; cellIndex < rowCells.length; cellIndex++) rowCells[cellIndex] = (GridCell) row.get(cellIndex); cells[rowIndex] = rowCells; } return cells; } /** * Adds the Print to the grid body, with the default alignment and a colspan * of 1. * * @param cell * the print to add. */ public void add(Print cell) { bodyCol = add(body, bodyCol, SWT.DEFAULT, SWT.DEFAULT, cell, 1); } /** * Adds the Print to the grid body, using the given colspan and alignment. * * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. * @param cell * the print to add. */ public void add(int hAlignment, Print cell) { bodyCol = add(body, bodyCol, hAlignment, SWT.DEFAULT, cell, 1); } /** * Adds the Print to the grid body, using the given colspan and alignment. * * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. * @param vAlignment * the vertical alignment of the print within the grid cell. One * of {@link SWT#DEFAULT}, {@link SWT#TOP}, {@link SWT#CENTER}, * {@link SWT#BOTTOM}, or {@link SWT#FILL}. A value of FILL * indicates that the cell is vertically greedy, so GridPrint * will limit the cell's height to the tallest non-FILL cell in * the row. * @param cell * the print to add. */ public void add(int hAlignment, int vAlignment, Print cell) { bodyCol = add(body, bodyCol, hAlignment, vAlignment, cell, 1); } /** * Adds the Print to the grid body, with the given colspan and the default * alignment. * * @param cell * the print to add. * @param colspan * the number of columns to span, or {@link GridPrint#REMAINDER} * to span the rest of the row. */ public void add(Print cell, int colspan) { bodyCol = add(body, bodyCol, SWT.DEFAULT, SWT.DEFAULT, cell, colspan); } /** * Adds the Print to the grid body, using the given colspan and alignment. * * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. * @param cell * the print to add. * @param colspan * the number of columns to span, or {@link GridPrint#REMAINDER} * to span the rest of the row. */ public void add(int hAlignment, Print cell, int colspan) { bodyCol = add(body, bodyCol, hAlignment, SWT.DEFAULT, cell, colspan); } /** * Adds the Print to the grid body, using the given colspan and alignment. * * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. * @param vAlignment * the vertical alignment of the print within the grid cell. One * of {@link SWT#DEFAULT}, {@link SWT#TOP}, {@link SWT#CENTER}, * {@link SWT#BOTTOM}, or {@link SWT#FILL}. A value of FILL * indicates that the cell is vertically greedy, so GridPrint * will limit the cell's height to the tallest non-FILL cell in * the row. * @param cell * the print to add. * @param colspan * the number of columns to span, or {@link GridPrint#REMAINDER} * to span the rest of the row. */ public void add(int hAlignment, int vAlignment, Print cell, int colspan) { bodyCol = add(body, bodyCol, hAlignment, vAlignment, cell, colspan); } /** * Returns whether individual body cells in the grid may be broken across * pages. Defaults to true. * * @return whether individual body cells in the grid may be broken across * pages. */ public boolean isCellClippingEnabled() { return cellClippingEnabled; } /** * Sets whether individual body cells in the grid may be broken across * pages. * * @param cellClippingEnabled * whether to enabled cell clipping. */ public void setCellClippingEnabled(boolean cellClippingEnabled) { this.cellClippingEnabled = cellClippingEnabled; } /** * Adds the Print to the grid footer, with the default alignment and a * colspan of 1. * * @param cell * the print to add. */ public void addFooter(Print cell) { footerCol = add(footer, footerCol, SWT.DEFAULT, SWT.DEFAULT, cell, 1); } /** * Adds the Print to the grid footer, using the given colspan and alignment. * * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. * @param cell * the print to add. */ public void addFooter(int hAlignment, Print cell) { footerCol = add(footer, footerCol, hAlignment, SWT.DEFAULT, cell, 1); } /** * Adds the Print to the grid footer, using the given colspan and alignment. * * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. * @param vAlignment * the vertical alignment of the print within the grid cell. One * of {@link SWT#DEFAULT}, {@link SWT#TOP}, {@link SWT#CENTER}, * {@link SWT#BOTTOM}, or {@link SWT#FILL}. A value of FILL * indicates that the cell is vertically greedy, so GridPrint * will limit the cell's height to the tallest non-FILL cell in * the row. * @param cell * the print to add. */ public void addFooter(int hAlignment, int vAlignment, Print cell) { footerCol = add(footer, footerCol, hAlignment, vAlignment, cell, 1); } /** * Adds the Print to the grid footer, with the given colspan and the default * alignment. * * @param cell * the print to add. * @param colspan * the number of columns to span, or {@link GridPrint#REMAINDER} * to span the rest of the row. */ public void addFooter(Print cell, int colspan) { footerCol = add(footer, footerCol, SWT.DEFAULT, SWT.DEFAULT, cell, colspan); } /** * Adds the Print to the grid footer, using the given colspan and alignment. * * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. * @param cell * the print to add. * @param colspan * the number of columns to span, or {@link GridPrint#REMAINDER} * to span the rest of the row. */ public void addFooter(int hAlignment, Print cell, int colspan) { footerCol = add(footer, footerCol, hAlignment, SWT.DEFAULT, cell, colspan); } /** * Adds the Print to the grid footer, using the given colspan and alignment. * * @param hAlignment * the horizontal alignment of the print within the grid cell. * One of {@link SWT#DEFAULT} , {@link SWT#LEFT}, * {@link SWT#CENTER} or {@link SWT#RIGHT}. * @param vAlignment * the vertical alignment of the print within the grid cell. One * of {@link SWT#DEFAULT}, {@link SWT#TOP}, {@link SWT#CENTER}, * {@link SWT#BOTTOM}, or {@link SWT#FILL}. A value of FILL * indicates that the cell is vertically greedy, so GridPrint * will limit the cell's height to the tallest non-FILL cell in * the row. * @param cell * the print to add. * @param colspan * the number of columns to span, or {@link GridPrint#REMAINDER} * to span the rest of the row. */ public void addFooter(int hAlignment, int vAlignment, Print cell, int colspan) { footerCol = add(footer, footerCol, hAlignment, vAlignment, cell, colspan); } /* * Returns the column number that we've advanced to, after adding the new * cell. */ private int add( List rows, // List of List of GridCell int startColumn, int hAlignment, int vAlignment, Print cellContents, int colspan) { startColumn = startNewRowIfCurrentRowFull(startColumn); checkColumnSpan(startColumn, colspan); List row = getOpenRow(rows, startColumn); colspan = convertRemainderToExplicitColSpan(startColumn, colspan); GridCell cell = new GridCellImpl(hAlignment, vAlignment, cellContents, colspan); row.add(cell); startColumn += colspan; // Make sure column number is valid. if (startColumn > columns.size()) { // THIS SHOULD NOT HAPPEN--ABOVE LOGIC SHOULD PREVENT THIS CASE // ..but just in case. row.remove(row.size() - 1); if (row.size() == 0) rows.remove(row); PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Colspan " + colspan //$NON-NLS-1$ + " too wide at column " + startColumn + " (" //$NON-NLS-1$ //$NON-NLS-2$ + columns.size() + " columns total)"); //$NON-NLS-1$ } return startColumn; } private int convertRemainderToExplicitColSpan(int startColumn, int colspan) { if (colspan == REMAINDER) colspan = columns.size() - startColumn; return colspan; } private int startNewRowIfCurrentRowFull(int startColumn) { // If we're at the end of a row, start a new row. if (startColumn == columns.size()) startColumn = 0; return startColumn; } private void checkColumnSpan(int startColumn, int colspan) { if (startColumn + colspan > columns.size()) PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Colspan " + colspan //$NON-NLS-1$ + " too wide at column " + startColumn + " (" //$NON-NLS-1$ //$NON-NLS-2$ + columns.size() + " columns total)"); //$NON-NLS-1$ } private List getOpenRow(List rows, int startColumn) { List row; // the row we will add the cell to. if (startColumn == 0) // Start a new row if back at column 0. rows.add(row = new ArrayList(columns.size())); else // Get the incomplete row. row = (List) rows.get(rows.size() - 1); // List of GridCell return row; } /** * Returns current column groups. The returned array may be modified without * affecting this GridPrint. * * @return the column groups. */ public int[][] getColumnGroups() { return PaperClipsUtil.copy(columnGroups); } /** * Sets the column groups to the given two-dimension array. Each int[] array * is a group. Columns in a group will be the same size when laid out on the * print device. * <p> * The following statement causes columns 0 and 2 to be the same size, and * columns 1 and 3 to be the same size. * * <pre> * grid.setColumnGroups(new int[][] { { 0, 2 }, { 1, 3 } }); * </pre> * * <p> * The behavior of this property is undefined when a column belongs to more * than one group. * <p> * <b>Note:</b> Column grouping is enforced <i>before</i> column weights. * Therefore, columns in the same group should be given the same weight to * ensure they are laid out at the same width. * * @param columnGroups * the new column groups. */ public void setColumnGroups(int[][] columnGroups) { checkColumnGroups(columnGroups); this.columnGroups = PaperClipsUtil.copy(columnGroups); } private void checkColumnGroups(int[][] columnGroups) { Util.notNull(columnGroups); for (int groupIndex = 0; groupIndex < columnGroups.length; groupIndex++) checkColumnGroup(columnGroups[groupIndex]); } private void checkColumnGroup(int[] columnGroup) { Util.notNull(columnGroup); for (int columnInGroupIndex = 0; columnInGroupIndex < columnGroup.length; columnInGroupIndex++) checkColumnIndex(columnGroup[columnInGroupIndex]); } private void checkColumnIndex(int columnIndex) { if (columnIndex < 0 || columnIndex >= columns.size()) PaperClips.error(SWT.ERROR_INVALID_RANGE, "Column index in column group must be " + "0 <= " //$NON-NLS-1$ //$NON-NLS-2$ + columnIndex + " < " + columns.size()); //$NON-NLS-1$ } /** * Returns the grid's look. A GridLook determines what decorations will * appear around the grid's contents. Default is a DefaultGridLook with no * cell spacing, no cell borders, and no background colors. * * @return the look of this grid. */ public GridLook getLook() { return look; } /** * Sets the grid's look. * * @param look * the new look. */ public void setLook(GridLook look) { Util.notNull(look); this.look = look; } public PrintIterator iterator(Device device, GC gc) { return new GridIterator(this, device, gc); } }