/*
* Copyright (c) 2007 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 org.eclipse.nebula.paperclips.core.internal.util.Util;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
/**
* A abstract GridLookPainter which simplifies implementation of custom
* GridLooks.
* <p>
* Subclasses must have the following methods implemented:
* <ul>
* <li>getMargins() - these margins are referenced by GridPrint for determining
* proper layout of the cells, as well as by the paint() method.
* <li>paintHeaderCell() - will be called by the paint() method for each header
* cell.
* <li>paintBodyCell() - will be called by the paint() method for each body
* cell.
* <li>paintFooterCell() - will be called by the paint() method for each footer
* cell.
* <li>dispose() - must dispose any SWT resources created by the subclass.
* </ul>
*
* @author Matthew Hall
*/
public abstract class BasicGridLookPainter implements GridLookPainter {
/**
* The printer device on which the look is being painted. This is the device
* that was passed as an argument to the constructor.
*/
protected final Device device;
/**
* Constructs a BasicGridLook painter.
*
* @param device
* the printer device (may not be null). This argument will be
* saved in the protected {@link #device} field.
*/
public BasicGridLookPainter(Device device) {
Util.notNull(device);
this.device = device;
}
public void paint(GC gc, int x, int y, int[] columns, int[] headerRows,
int[][] headerColSpans, int firstRowIndex, boolean topOpen,
int[] bodyRows, int[][] bodyColSpans, boolean bottomOpen,
int[] footerRows, int[][] footerColSpans) {
GridMargins margins = getMargins();
final boolean headerPresent = headerRows.length > 0;
final boolean footerPresent = footerRows.length > 0;
x += margins.getLeft();
if (headerPresent)
y = paintHeader(gc, x, y, columns, headerRows, headerColSpans);
y += margins.getBodyTop(headerPresent, topOpen);
y = paintBody(gc, x, y, columns, bodyRows, bodyColSpans, firstRowIndex,
topOpen, bottomOpen);
y += margins.getBodyBottom(footerPresent, bottomOpen);
if (footerPresent)
paintFooter(gc, x, y, columns, footerRows, footerColSpans);
}
private int paintHeader(GC gc, int x, int y, int[] columns, int[] rows,
int[][] colSpans) {
GridMargins margins = getMargins();
y += margins.getHeaderTop();
for (int i = 0; i < rows.length; i++) {
int h = rows[i];
paintHeaderRow(gc, x, y, columns, h, i, colSpans[i]);
y += h;
if (i < rows.length - 1)
y += margins.getHeaderVerticalSpacing();
}
return y;
}
private int paintBody(GC gc, int x, int y, int[] columns, int[] rows,
int[][] colSpans, int firstRowIndex, boolean topOpen,
boolean bottomOpen) {
GridMargins margins = getMargins();
for (int i = 0; i < rows.length; i++) {
final int h = rows[i];
paintBodyRow(gc, x, y, columns, h, colSpans[i], firstRowIndex + i,
i == 0 && topOpen, i == rows.length - 1 && bottomOpen);
y += h;
if (i < rows.length - 1)
y += margins.getBodyVerticalSpacing();
}
return y;
}
private void paintFooter(GC gc, final int x, int y, int[] columns,
int[] rows, int[][] colSpans) {
GridMargins margins = getMargins();
for (int i = 0; i < rows.length; i++) {
final int h = rows[i];
paintFooterRow(gc, x, y, columns, h, i, colSpans[i]);
y += h;
y += margins.getFooterVerticalSpacing();
}
}
private void paintHeaderRow(GC gc, int x, int y, int[] columns,
final int h, int rowIndex, int[] colSpans) {
GridMargins margins = getMargins();
int col = 0;
for (int i = 0; i < colSpans.length; i++) {
final int colSpan = colSpans[i];
final int w = sum(columns, col, colSpan) + (colSpan - 1)
* margins.getHorizontalSpacing();
paintHeaderCell(gc, new Rectangle(x, y, w, h), rowIndex, col,
colSpan);
col += colSpan;
x += w + margins.getHorizontalSpacing();
}
}
private void paintBodyRow(GC gc, int x, int y, int[] columns, final int h,
int[] colSpans, int rowIndex, final boolean topOpen,
final boolean bottomOpen) {
GridMargins margins = getMargins();
int col = 0;
for (int i = 0; i < colSpans.length; i++) {
final int colSpan = colSpans[i];
final int w = sum(columns, col, colSpan) + (colSpan - 1)
* margins.getHorizontalSpacing();
paintBodyCell(gc, new Rectangle(x, y, w, h), rowIndex, col,
colSpan, topOpen, bottomOpen);
col += colSpan;
x += w + margins.getHorizontalSpacing();
}
}
private void paintFooterRow(GC gc, int x, int y, int[] columns,
final int h, int rowIndex, int[] colSpans) {
GridMargins margins = getMargins();
int col = 0;
for (int i = 0; i < colSpans.length; i++) {
final int colSpan = colSpans[i];
final int w = sum(columns, col, colSpan) + (colSpan - 1)
* margins.getHorizontalSpacing();
paintFooterCell(gc, new Rectangle(x, y, w, h), rowIndex, col,
colSpan);
col += colSpan;
x += w + margins.getHorizontalSpacing();
}
}
private int sum(int[] elements, int start, int length) {
int sum = 0;
for (int j = 0; j < length; j++)
sum += elements[start + j];
return sum;
}
/**
* Paint the decorations for the described header cell.
*
* @param gc
* the graphics context to use for painting.
* @param bounds
* the bounds of the cell, excluding margins.
* @param row
* the row offset of the cell within the header.
* @param col
* the column offset of the cell within the header.
* @param colspan
* the number of columns that this cell spans.
*/
protected abstract void paintHeaderCell(GC gc, Rectangle bounds, int row,
int col, int colspan);
/**
* Paint the decorations for the described body cell.
*
* @param gc
* the graphics context to use for painting.
* @param bounds
* the bounds of the cell, excluding margins.
* @param row
* the row offset of the cell within the header.
* @param col
* the column offset of the cell within the header.
* @param colspan
* the number of columns that this cell spans.
* @param topOpen
* whether the cell should be drawn with the top edge of the cell
* border "open." An open top border is a visual cue that the
* cell is being continued from the previous page.
* @param bottomOpen
* whether the cell should be drawn with the bottom edge of the
* cell border "open." An open bottom border is a visual cue that
* the cell will be continued on the next page.
*/
protected abstract void paintBodyCell(GC gc, Rectangle bounds, int row,
int col, int colspan, boolean topOpen, boolean bottomOpen);
/**
* Paint the decorations for the described footer cell.
*
* @param gc
* the graphics context to use for painting.
* @param bounds
* the bounds of the cell, excluding margins.
* @param row
* the row offset of the cell within the header.
* @param col
* the column offset of the cell within the header.
* @param colspan
* the number of columns that this cell spans.
*/
protected abstract void paintFooterCell(GC gc, Rectangle bounds, int row,
int col, int colspan);
}