|
As we already needed this a while back I have an implementation using PaperClips, I offer you the code I wrote. I'm not including all classes we use here, just the raw print class, I think you can figure out the missing methods etc from our wrapping "GlazedNatGridViewer", otherwise let me know. This code also deals with printing the grid exactly as is, which means that colum widths in printed doc are same as in the table. This can be removed of course if not wanted. Hope it's useful, I'm posting the code as it'd be nice to have it inside Nat, assuming it's still customizable enough for users to add their own tweaks.. let me know! GridPrinter.java public class GridPrinter { private static final String EXPANDABLE_COLUMN = "d:g"; private static final String DOT = "..."; private static final String EMPTY = ""; public static final int PRINT_OK = 0; public static final int ERROR_NO_DATA_TO_PRINT = 1; public static final int ERROR_PRINTING = 2; // custom print images get disposed when we're finished or we'll have a memory leak private Image _DisposeImage; private boolean _DisposePrintImage = false; private RGB _EvenRows; private RGB _OddRows; private RGB _HeaderBgColor; private boolean _PrintColHeaders; //private boolean _PrintRowHeaders; // unused right now, hence eclipse saying "yellow" private boolean _PrintVerticalLines; private boolean _PrintHorizontalLines; private String _footerText; private FontData _headerFontData; private FontData _cellFontData; private IPrintHandler _printHandler; public GridPrinter(IPrintHandler handler) { _printHandler = handler; _PrintColHeaders = handler.isPrintColumnHeaders(); //_PrintRowHeaders = handler.isPrintRowHeaders(); _PrintVerticalLines = handler.isPrintVerticalLines(); _PrintHorizontalLines = handler.isPrintHorizontalLines(); _footerText = handler.getFooterText(); _headerFontData = handler.getHeaderFontData(); _cellFontData = handler.getCellFontData(); _HeaderBgColor = new RGB(240, 240, 240); _OddRows = new RGB(230, 230, 230); _EvenRows = new RGB(255, 255, 255); } public int printNatTable(GlazedNatGridViewer viewer, boolean printSelectionOnly, boolean showPreview, String docTitle, final SmallProgressWindow spw, final Shell parentShell) { try { int columnCount = viewer.getColumnCount(); boolean useFullColumnWidths = false; StringBuffer buf = new StringBuffer(); List<Integer> cols = null; List<Integer> widths = new ArrayList<Integer>(); Map<Integer, Integer> widthMap = new HashMap<Integer, Integer>(); // figure out size of grid (how many columns it has) if (printSelectionOnly) { if (viewer.getCellSelectionCount() == 0) { return ERROR_NO_DATA_TO_PRINT; } Point[] points = viewer.getCellSelection(); cols = new ArrayList<Integer>(); for (Point p : points) { if (!cols.contains(p.x)) { cols.add(p.x); } } columnCount = cols.size(); } else { cols = viewer.getVisibleColumnOrder(); columnCount = cols.size(); } // order columns that have selection in same order as table order // we do this as we can have a selection like this: // // - 0 1 2 3 4 // 0 x x x x // 1 x x x // 2 x x x x // // and if we didn't know 3 came before 4 even though it doesn't have a selection // until row #2 we'd have an order of 0 1 2 4 3 instead of the correct 0 1 2 3 4 // get the full order regardless of column visibility, we need the real order List<Integer> visibleOrder = viewer.getColumnOrder(); List<Integer> correctOrderedCols = new ArrayList<Integer>(); for (Integer correct : visibleOrder) { if (cols.contains(correct)) { correctOrderedCols.add(correct); } } cols = correctOrderedCols; // create grid for (int i = 0; i < cols.size(); i++) { int column = cols.get(i); widths.add(viewer.getModel().getBodyColumnWidth(column)); widthMap.put(column, viewer.getModel().getBodyColumnWidth(column)); if (useFullColumnWidths) { buf.append(EXPANDABLE_COLUMN); } else { int colWidthPix = viewer.getModel().getBodyColumnWidth(column); float pts = convertToPoints(colWidthPix); buf.append("L:"); buf.append((int) pts); buf.append(":N"); } if (i != columnCount - 1) { buf.append(", "); } } // create the printable grid GridPrint print = null; GC concatGc = new GC(parentShell); Font headerFont = new Font(Display.getDefault(), _headerFontData); Font cellFont = new Font(Display.getDefault(), _cellFontData); DefaultGridLook look = new DefaultGridLook(0, 0); if (_PrintHorizontalLines && _PrintVerticalLines) { look.setCellBorder(new LineBorder()); } else if (_PrintHorizontalLines) { look.setCellBorder(new CustomLineBorder(true)); } else if (_PrintVerticalLines) { look.setCellBorder(new CustomLineBorder(false)); } print = new GridPrint(buf.toString(), look); look.setHeaderBackgroundProvider(new CellBackgroundProvider() { @Override public RGB getCellBackground(int row, int column, int colspan) { return _HeaderBgColor; } }); // Alternate between light yellow and light blue every 5 rows look.setBodyBackgroundProvider(new CellBackgroundProvider() { @Override public RGB getCellBackground(int row, int col, int colspan) { return row % 2 == 0 ? _EvenRows : _OddRows; } }); // fill the grid if (printSelectionOnly) { Point[] points = viewer.getCellSelection(); spw.setMax(points.length * 2); if (_PrintColHeaders) { for (int i = 0; i < cols.size(); i++) { String name = concat(viewer.getColumn(cols.get(i)).getName(), widths.get(i), concatGc, headerFont); print.addHeader(new TextPrint(name, _headerFontData)); } } // make a map out of what is selected on each row Map<Integer, List<Integer>> selMap = new HashMap<Integer, List<Integer>>(); for (int i = 0; i < points.length; i++) { spw.increase(); Point p = points[i]; boolean newEntry = !selMap.containsKey(p.y); List<Integer> toUse = newEntry ? new ArrayList<Integer>() : selMap.get(p.y); if (!toUse.contains(p.x)) { toUse.add(p.x); } selMap.put(p.y, toUse); } List<Integer> printedRows = new ArrayList<Integer>(); // fill in the cells for (int i = 0; i < points.length; i++) { spw.increase(); Point p = points[i]; if (printedRows.contains(p.y)) { continue; } for (int col : cols) { // this cell is not selected, but we need to print an empty cell // or alignment gets completely crazy later List<Integer> selColsForRow = selMap.get(p.y); if (!selColsForRow.contains(col)) { print.add(tp(" ")); continue; } IDataObject gi = viewer.getItem(p.y); String value = concat(gi.getColumnText(col), widthMap.get(col), concatGc, cellFont); Image image = gi.getColumnImage(col); if (image != null) { print.add(new ImagePrint(image.getImageData(), Display.getDefault().getDPI())); } else { print.add(tp(value, _cellFontData)); } } printedRows.add(p.y); } } else { List<IDataObject> all = viewer.getAllItems(); spw.setMax(all.size()); IDataObject[] sel = new IDataObject[all.size()]; sel = new IDataObject[all.size()]; for (int i = 0; i < all.size(); i++) sel[i] = all.get(i); if (_PrintColHeaders) { for (int i = 0; i < cols.size(); i++) { String name = concat(viewer.getColumn(cols.get(i)).getName(), widths.get(i), concatGc, headerFont); print.addHeader(new TextPrint(name, _headerFontData)); } } for (IDataObject gi : sel) { spw.increase(); for (int i = 0; i < cols.size(); i++) { String colText = gi.getColumnText(cols.get(i)); if (colText == null) { colText = ""; } String text = concat(colText, widths.get(i), concatGc, cellFont); print.add(tp(text, _cellFontData)); } } } // dispose resources concatGc.dispose(); headerFont.dispose(); cellFont.dispose(); // we need to space things out, it's too tight usually, so we create a wrapper to // force the printout over multiple pages when it doesn't fit BigPrint big = new BigPrint(print); // add header, footer, spacing PagePrint pp = new PagePrint(new PrintHeader(_printHandler, docTitle), big, new PrintFooter(getFooterText())); pp.setHeaderGap(9); pp.setFooterGap(18); if (showPreview) { spw.setDescription("Creating Preview..."); // show preview dialog, user prints from there PrintPreviewDialog ppd = new PrintPreviewDialog(pp, parentShell, _printHandler); ppd.open(); spw.dispose(); } else { spw.setDescription("Creating Print..."); spw.dispose(); PrintDialog dialog = new PrintDialog(parentShell, SWT.NONE); PrinterData printerData = dialog.open(); if (printerData != null) { PaperClips.print(new PrintJob("Printing", pp).setMargins(36), printerData); } } if (_DisposePrintImage) { _DisposeImage.dispose(); } return PRINT_OK; } catch (Exception err) { err.printStackTrace(); } return ERROR_PRINTING; } // checks whether text needs concatenation or not private String concat(String str, int pixWidth, GC gc, Font f) { if (str == null || str.length() == 0) { return str; } gc.setFont(f); Point size = gc.stringExtent(str); // text fits, dispose & return if (size.x <= pixWidth) { return str; } else { // text doesn't fit, concatenate with ... and return String ret = getAvailableTextToDisplay(gc, new Rectangle(0, 0, pixWidth, 20), str); return ret; } } private String getAvailableTextToDisplay(final GC gc, final Rectangle rectangle, final String text) { int width = gc.textExtent(text).x; boolean displayDot = width > rectangle.width; String ret = null; try { if (displayDot) { BufferedReader bufferedReader = new BufferedReader(new StringReader(text)); StringBuffer output = new StringBuffer(); String line = ""; while ((line = bufferedReader.readLine()) != null) { width = gc.textExtent(line).x; if (width > rectangle.width) { int textLen = line.length(); for (int i = textLen - 1; i >= 0; i--) { String temp = line.substring(0, i) + DOT; width = gc.textExtent(temp).x; if (width < rectangle.width) { line = temp; break; } else if (i == 0) { line = EMPTY; } } } output.append(line); output.append('\n'); } ret = output.toString(); } } catch (IOException e) { e.printStackTrace(); } return ret; } private int convertToPoints(int pixels) { return 72 * pixels / Display.getDefault().getDPI().x; } private TextPrint tp(String str) { return new TextPrint(str == null ? "" : (str.equals("") ? "-" : str)); } private TextPrint tp(String str, FontData fd) { return new TextPrint(str == null ? "" : (str.equals("") ? "-" : str), fd); } public String getFooterText() { return _footerText; } } Emil Oh, you might need this too: CustomLineBorder.java import net.sf.paperclips.AbstractBorderPainter; import net.sf.paperclips.Border; import net.sf.paperclips.BorderPainter; import net.sf.paperclips.LineBorder; import net.sf.paperclips.internal.ResourcePool; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; public class CustomLineBorder implements Border { RGB rgb; int lineWidth = 1; // in points int gapSize = 5; // in points boolean _horizontal = false; /** * Constructs a LineBorder with a black border and 5-pt insets. (72 pts = 1") */ public CustomLineBorder(boolean horizontal) { this(new RGB(0, 0, 0), horizontal); // black } /** * Constructs a LineBorder with 5-pt insets. (72 pts = 1") * * @param rgb * the color to use for the border. */ public CustomLineBorder(RGB rgb, boolean horizontal) { setRGB(rgb); _horizontal = horizontal; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + gapSize; result = prime * result + lineWidth; result = prime * result + ((rgb == null) ? 0 : rgb.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; LineBorder other = (LineBorder) obj; if (gapSize != other.getGapSize()) return false; if (lineWidth != other.getLineWidth()) return false; if (rgb == null) { if (other.getRGB() != null) return false; } else if (!rgb.equals(other.getRGB())) return false; return true; } /** * Sets the border color to the argument. * * @param rgb * the new border color. */ public void setRGB(RGB rgb) { this.rgb = new RGB(rgb.red, rgb.green, rgb.blue); } /** * Returns the border color. * * @return the border color. */ public RGB getRGB() { return new RGB(rgb.red, rgb.green, rgb.blue); } /** * Sets the line width to the argument. * * @param points * the line width, in points. */ public void setLineWidth(int points) { if (points < 1) points = 1; this.lineWidth = points; } /** * Returns the line width of the border, expressed in points. * * @return the line width of the border, expressed in points. */ public int getLineWidth() { return lineWidth; } /** * Sets the size of the gap between the line border and the target print. * * @param points * the gap size, expressed in points. */ public void setGapSize(int points) { if (points < 1) points = 1; this.gapSize = points; } /** * Returns the size of the gap between the line border and the target print, expressed in points. * * @return the gap size between the line border and the target print. */ public int getGapSize() { return Math.max(lineWidth, gapSize); } public BorderPainter createPainter(Device device, GC gc) { return new LineBorderPainter(this, device, gc, _horizontal); } } class LineBorderPainter extends AbstractBorderPainter { private final Device device; private final RGB rgb; private final Point lineWidth; private final Point borderWidth; private final boolean _horizontalOnly; LineBorderPainter(CustomLineBorder border, Device device, GC gc, boolean horizontalOnly) { Util.notNull(border, device, gc); this.rgb = border.rgb; this.device = device; this._horizontalOnly = horizontalOnly; int lineWidthPoints = border.getLineWidth(); int borderWidthPoints = border.getGapSize(); Point dpi = device.getDPI(); lineWidth = new Point(Math.round(lineWidthPoints * dpi.x / 72f), Math.round(lineWidthPoints * dpi.y / 72f)); borderWidth = new Point(Math.round(borderWidthPoints * dpi.x / 72f), Math.round(borderWidthPoints * dpi.y / 72f)); } public int getLeft() { return borderWidth.x; } public int getRight() { return borderWidth.x; } public int getTop(boolean open) { return open ? 0 : borderWidth.y; } public int getBottom(boolean open) { return open ? 0 : borderWidth.y; } public void paint(GC gc, int x, int y, int width, int height, boolean topOpen, boolean bottomOpen) { Color oldColor = gc.getBackground(); try { gc.setBackground(ResourcePool.forDevice(device).getColor(rgb)); // Left & right (vertical) if (!_horizontalOnly) { gc.fillRectangle(x, y, lineWidth.x, height); gc.fillRectangle(x + width - lineWidth.x, y, lineWidth.x, height); } if (_horizontalOnly) { // Top & bottom (horizontal) if (!topOpen) gc.fillRectangle(x, y, width, lineWidth.y); if (!bottomOpen) gc.fillRectangle(x, y + height - lineWidth.y, width, lineWidth.y); } } finally { gc.setBackground(oldColor); } } public Point getOverlap() { return new Point(lineWidth.x, lineWidth.y); } public void dispose() { } // Shared resources -- nothing to dispose } Hi Thanks a lot for the code. While some of the internals have changed I think we can still use chunks from the code you supplied. Expect to see it in the 2.0 release ! Results from the Spike. Swt paperclip is a good solution My initial approach was to print the table as in image. While this works it does not scale at all. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Starting by investigating swt-paperclip