pax_global_header00006660000000000000000000000064116073165450014522gustar00rootroot0000000000000052 comment=8c5f67d3a8a40a29bc6117781b434af81756e537 swt-paperclips-1.0.4/000077500000000000000000000000001160731654500145015ustar00rootroot00000000000000swt-paperclips-1.0.4/README.txt000066400000000000000000000052411160731654500162010ustar00rootroot00000000000000======================================== PaperClips Core 1.0.4 http://code.google.com/p/swt-paperclips/ ======================================== Copyright (c) 2005-2009 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 A copy is found in the file epl-v10.html distributed in this package. Contributors: Matthew Hall - initial API and implementation This copyright notice MUST APPEAR in all copies of the file! Introduction ------------ Welcome to PaperClips: a simple, light weight, extensible Java printing library for SWT. PaperClips hides the complexity of laying out and rendering documents on the printer, helping you focus on what to print instead of how to print it. In a nutshell, PaperClips provides an assortment of document "building blocks", which you can tweak and combine to form a custom document. The assembled document is then sent to PaperClips for printing. PaperClips includes support for printing text, images, borders, headers and footers, column layouts and grid layouts, to name a few. It can also be extended with your own printable classes. With PaperClips you do not have to track cursors, calculate line breaking, fool around with font metrics, or manage system resources--it's all handled internally. And unlike report-generation tools, you are not constrained to a predefined document structure (like report bands). Every document is custom and the layout is up to you. Requirements: * Java 1.4 or later. * SWT 3.2 or later. SWT may be downloaded at http://www.eclipse.org/swt/. Installation ------------ The PaperClips binary build jars can be used as regular jars, or as Eclipse plugins. If used as regular jars, the SWT classes must be available in the classpath. To use PaperClips as a binary plugin, simply save the jar into the plugins folder of your target platform. If you are extending PaperClips (or you want the latest bleeding edge features), your best bet is to checkout the sources from from Mercurial and import each project into your workspace. Mercurial repository URL: https://swt-paperclips.googlecode.com/hg/ Import these folders: net.sf.paperclips/ # core (required) net.sf.paperclips.ui/ # screen widgets net.sf.paperclips.examples/ # snippets net.sf.paperclips.tests/ # unit tests Credits ------- PaperClips development team: Matthew Hall - Developer / Project Admin swt-paperclips-1.0.4/changelog000066400000000000000000000300611160731654500163530ustar00rootroot00000000000000Version 1.0.4 * Bugs fixed: * [Mac OSX] Disposing a PrintPreview sends a blank page to the printer. * Autoscaled ScalePrints were not computing minimum sizes correctly which distorted some layouts * PaperClips and PrintPreview now use the first system printer listed if no system default printer has been selected (can be the case on Linux, Mac) * Fixed compiler compliance levels so PaperClips can run in Java 1.4 JREs. * GridColumn's conversion of inches, millimeters and centimeters sizes to point was wrong, causing those columns to be displayed at the wrong size. * Fixed GridColumn's recognition of column sizes with a decimal part. * New Features: * Page numbers are internationalized in English, French and German. * TextStyle.create(String) creates a TextPrint * Added DebugPrint helper class for troubleshooting documents that won't print * Known issues: * [Linux GTK] Printed text scales up or down depending on what DPI the screen is configured to * [Linux GTK] When a PrintPreview changes printers, print jobs, or is disposed, a blank page is emitted on the printer. This is due to missing API in GTK for cancelling a print job. Version 1.0.3 * Bugs fixed: * Fixed integer overflow error in GridPrint which sometimes caused printing of very long strings to fail. * Fixed column size distribution in GridPrint when paper space is scarce. * New Features * PrintPreview now supports lazy layout of print jobs. This can be used to speed up previews of very large print jobs. * Changes: * Implement equals() and hashCode() in all Print objects. * Performance tuning in text and grid layouts * Snippets no longer implement Print. This was an unnecessary detail and tends to confuse newcomers. * Known issues: * PrintPreview issues a page feed whenever a PrintPreview control is disposed or changes printers. This is due to missing API in GTK for cancelling a print job. Version 1.0.2 * Bugs fixed: * Clipping problems on Mac OS X. * PrintPreview.getPageCount() returns 0 before pages are first drawn. * PrintPreview spits out a blank page on Linux when the window is closed. * BorderPrint sometimes showed an open bottom border even though the target was completely shown. * PrintViewer performance improvements when print document is vertically greedy. * New Features: * GridPrint.setCellClippingEnabled() controls whether grid cells may be broken across pages. See GridPrintCellClippingExample.java. * DefaultGridLook.setCellPadding() * PrintPreview.setHorizontalPageCount() and setVerticalPageCount() controls how many pages are shown on screen. * Experimental PaperClips.setDebug() API helps troubleshoot documents that won't lay out properly ("Unable to layout on page x" errors). * BasicGridLookPainter simplifies implementing custom GridLooks. * StyledTextPrint for mixing text with different font sizes, styles, colors and decorations. Other printable objects such as ImagePrint may be embedded inline with the text. * TextPrint and StyledTextPrint now support underline and strikeout text. * TextPrint.setWordSplitting() controls whether words may be split between rows. This feature only applies when space is very limited. * Unified error reporting to PaperClips.error() methods. Custom Print implementations should use these methods to act uniformly with the rest of the library. * Example snippets: * Changed ImageCaptureExample.java to capture JPG since PNG was not fully supported until SWT version 3.3 (PaperClips is developed against 3.2). * Snippet7 (print preview example): * Support scrolling with the mouse wheel (horizontally with Shift+Wheel) * Support zooming with Ctrl+Wheel Version 1.0.1 * Resolved printing problems on Mac OS X. * Added public accessor APIs for all Print classes. Version 1.0.0 * Vertical cell alignment in GridPrint, including SWT.FILL alignment to allow embedding vertically greedy prints like SWT.VERTICAL LinePrints. * SidewaysPrint - a non-greedy version of RotatePrint. Very handy for putting sideways text in grids. * TextPrint's and PageNumberPrint's horizontally greedy behavior had to be removed in order for SidewaysPrint to work correctly. This will not cause any compilation problems, however you may see some unexpected layout behavior. * Updated Snippet7 (print preview example) in examples plug-in to clean up some of the odd behavior when resizing the window in Fit-to-Width or Fit-to-Height mode. Also added code for scrolling around the page using mouse drag. * Bug fix: PaperClips.getPages() throws the wrong exception when a document fails to lay out properly. * Bug fix: PrintPreview does not redraw when setScale is called to change the display scale. * Bug fix: Headers/footers in a PagePrint do not display any contents after the page number. Version 0.6.1 * PrintPreview control: * ComputeSize is now implemented properly, which helps determine the proper sizing of the control depending on the viewing scale. Snippet7 has been updated to demonstrate this feature. * The performance problems (read: major lags) when zooming in very close are resolved. Go ahead, zoom to ridiculous levels with confidence! We promise not to tell anyone. * Bugfix: page disappears after a call to setPrinterData. * ColumnPrint behavior of compressing the last page of content to the minimum possible height can now be disabled using the "compressed" property. * GridPrint now has addColumn and addColumns methods supporting column modifications after construction. Some of the snippets were modified to use this new API. Version 0.6.0 * JDK 1.4 compliance. * New WYSIWYG (what you see is what you get) PrintPreview control in the net.sf.paperclips.ui plugin. * Other existing UI controls (in the net.sf.paperclips.swt package) have been moved to the net.sf.paperclips.ui, into the net.sf.paperclips.ui package. * The PrintUtil class has been replaced with the PaperClips and PrintJob classes. * The new PrintJob class holds information about the job name, document, page margins, and paper orientation. * The new PaperClips class provides a simpler API for printing documents. * The new Margins class provides fine control over margins on each edge of the paper. * Deprecated package net.sf.paperclips.preview has been removed. Version 0.5.4 * Bugfix: GridPrint fails to generate last page if the final row of content finishes on previous page, but there was only enough room to print an open bottom border. (This bug was discovered while trying to run Snippet4) * Added SimplePageDecoration, a simple wrapper for static page headers and footers. This class can be used in lieu of creating a custom PageDecoration class for simple page numbering. * The DefaultPageNumberFormat class (the default PageNumberFormat class used by PageNumberPrint) is now a top-level, public class. * Added SimplePageDecoration class, a PageDecoration which displays a Print you provide on each page. Version 0.5.3 * TextPrint - partially reversed a change in 0.5.2 which made TextPrints horizontally greedy. Being horizontally greedy is appropriate for center- and right-alignment, but not for left-alignment (the default). Version 0.5.2 * CellBackgroundProvider interface - an interface for programmatic control of the background color in each grid cell. Default implementation is provided in the DefaultBackgroundProvider class. * DefaultGridLook - now supports control of the header, body, and footer background colors using either setHeaderBackground(RGB) or setHeaderBackgroundProvider(CellBackgroundProvider). * Prints with greedy layout behavior now have documentation indicating the behavior in the javadocs. * Bugfix to GridPrint which sometimes cause grids to overlap with other prints. Version 0.5.1 * GridPrint - fixed bug which caused a NullPointerException whenever the cells in a row are not all consumed (completely displayed) on the same page. * Added net.sf.paperclips.decorator package to list of exported packages in plugin. Version 0.5.0 * ScalePrint - wrapper that scales a print down to fit on the page, or scales larger or smaller depending on a scaling factor. Requires SWT 3.2M3 or later. * RotatePrint - wrapper that rotates the target by 90, 180, or 270 degrees. Since SWT doesn't provide API for setting the page orientation, this will be very useful for landscape layouts. Requires SWT 3.2M3 or later. * BackgroundPrint - wrapper that draws a background color behind it's target. * BigPrint - wrapper that splits it's target across multiple pages if it's too large to fit on one. Use this if you have a document that's too big to print (i.e. a GridPrint with too many columns). * Decorator package (net.sf.paperclips.decorator) allows you to create decorator factories which can apply decorations to prints without explicitly calling the decorator print's constructor. Provided you use the decorator uniformly throughout your document, the style of the document can be changed by substituting another decorator. * TextPrint default font is the system default font instead of hard-coded "Times" font. * Updated semantics of PrintPiece.dispose() to allow PrintPiece and BorderPainter re-use. Anybody who writes their own Print classes should check out the javadocs for this method and update their code to comply with the new semantics. * GridPrint got a massive overhaul: * Can now add headers and footers that repeat on every page through the addHeader(...) and addFooter(...) methods (they work just like the add(...) methods). * Configure the appearance of your GridPrint using the GridLook interface. A default look, DefaultGridLook, lets you configure the cell spacing, cell border, and background color of grid cells (separate color for header, body, and footer cells). * Public fields horizontalSpacing and verticalSpacing were deprecated. * Fixed bug 1480013: illogical layout of grid rows when they are broken across pages. Sometimes some content would be on the end of one page and the other content would be at the beginning of the next page. This led to sometimes confusing printouts. This fix essentially "glues" the row together so it doesn't get separated like this any more. Version 0.4.3 * Bugfix: Whenever the print space available to a GridPrint was smaller than it's calculated minimum size, the grid would print larger than the available space, in certain cases. Version 0.4.2 * NoBreakPrint is a wrapper which prevents it's target print from being broken up between pages (or columns). * BreakPrint adds a page break or column break. * PaperClips sources are now version controlled through Subversion (finally!). Version 0.4.1 * Bugfix: if a GridPrint is configured with a cell border and added to a ColumnPrint, an infinite loop will occur when attempting to print. Version 0.4.0 * All public API are documented in javadocs. * Custom cell borders in GridPrint - uses the same Border interface as BorderPrint. Version 0.3 beta * AlignPrint - vertical and horizontal alignment of a child print. * ColumnPrint - lays out a child print into multiple columns. * GapBorder - a blank border with configurable margin size (see API docs for BorderPrint) Version 0.2 alpha * PagePrint - page headers and footers (including page numbering) * BorderPrint - decorating document elements with borders. * LayerPrint - a layout element which renders document elements one above the other. Version 0.1 alpha Basic document elements: * TextPrint - for printing text * ImagePrint - for printing images * LinePrint - for printing horizontal and (sometimes) vertical rules. * GridPrint - arranges it's child prints into a grid. * LayerPrint - displays it's child prints on top of eachother. * SeriesPrint - displays multiple prints, one after another, with only one to a page. * FactoryPrint - aids in composition of Prints.swt-paperclips-1.0.4/src/000077500000000000000000000000001160731654500152705ustar00rootroot00000000000000swt-paperclips-1.0.4/src/net/000077500000000000000000000000001160731654500160565ustar00rootroot00000000000000swt-paperclips-1.0.4/src/net/sf/000077500000000000000000000000001160731654500164665ustar00rootroot00000000000000swt-paperclips-1.0.4/src/net/sf/paperclips/000077500000000000000000000000001160731654500206305ustar00rootroot00000000000000swt-paperclips-1.0.4/src/net/sf/paperclips/AbstractBorderPainter.java000066400000000000000000000037301160731654500257220ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.graphics.GC; /** * Abstract implementation of BorderPainter providing implementation of helper * methods. * * @author Matthew Hall */ public abstract class AbstractBorderPainter implements BorderPainter { /** * Paints a border around the specified region. Depending on the type of * border, the top and bottom of may be painted differently depending on the * values of topOpen and bottomOpen. */ public abstract void paint(GC gc, int x, int y, int width, int height, boolean topOpen, boolean bottomOpen); /** * Returns the border inset, in pixels, from the left. */ public abstract int getLeft(); /** * Returns the border inset, in pixels, from the right. */ public abstract int getRight(); /** * Returns the sum of the left and right border insets. */ public final int getWidth() { return getLeft() + getRight(); } /** * Returns the border inset, in pixels, from the top. */ public abstract int getTop(boolean open); /** * Returns the border inset, in pixels, from the bottom. */ public abstract int getBottom(boolean open); /** * Returns the sum of the top and bottom border insets. */ public final int getHeight(boolean topOpen, boolean bottomOpen) { return getTop(topOpen) + getBottom(bottomOpen); } /** * Returns the sum of the maximum top and bottom border insets. */ public final int getMaxHeight() { return Math.max(getTop(false), getTop(true)) + Math.max(getBottom(false), getBottom(true)); } } swt-paperclips-1.0.4/src/net/sf/paperclips/AbstractIterator.java000066400000000000000000000027711160731654500247570ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; /** * An abstract PrintIterator class which maintains references to the device and * gc arguments passed to {@link Print#iterator(Device, GC) }. * * @author Matthew Hall */ public abstract class AbstractIterator implements PrintIterator { /** * The device being printed to. */ protected final Device device; /** * A GC used for measuring document elements. */ protected final GC gc; /** * Constructs an AbstractIterator with the given Device and GC. * * @param device * the device being printed to. * @param gc * a GC used for drawing on the print device. */ protected AbstractIterator(Device device, GC gc) { Util.notNull(device, gc); this.device = device; this.gc = gc; } /** * Copy constructor. * * @param that * the AbstractIterator being copied. */ protected AbstractIterator(AbstractIterator that) { this.device = that.device; this.gc = that.gc; } }swt-paperclips-1.0.4/src/net/sf/paperclips/AbstractPiece.java000066400000000000000000000035521160731654500242110ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * An abstract PrintPiece class. * * @author Matthew Hall */ public abstract class AbstractPiece implements PrintPiece { /** * The device being printed to. */ protected final Device device; /** * A GC for drawing on the print device. * * @deprecated use the local GC in the * {@link PrintPiece#paint(GC, int, int)} method instead. */ protected final GC gc; private final Point size; /** * Constructs an AbstractPiece. * * @param device * the device being printed to. * @param gc * a GC for drawing on the print device. * @param size * the value to be returned by getSize(). */ protected AbstractPiece(Device device, GC gc, Point size) { Util.notNull(device, gc, size); this.device = device; this.gc = gc; this.size = size; } /** * Constructos an AbstractPiece. * * @param iter * an AbstractIterator containing references to a Device and GC * which will be used for printing. * @param size * the value to be returned by getSize(). */ protected AbstractPiece(AbstractIterator iter, Point size) { this(iter.device, iter.gc, size); } public final Point getSize() { return new Point(size.x, size.y); } } swt-paperclips-1.0.4/src/net/sf/paperclips/AlignPrint.java000066400000000000000000000121051160731654500235410ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A wrapper print that aligns its target vertically and/or horizontally. An * AlignPrint is vertically greedy when the vertical alignment is SWT.CENTER or * SWT.BOTTOM, and horizontally greedy when the horizontal alignment is * SWT.CENTER and SWT.RIGHT. * * @author Matthew Hall */ public class AlignPrint implements Print { private static final int DEFAULT_HORIZONTAL_ALIGN = SWT.LEFT; private static final int DEFAULT_VERTICAL_ALIGN = SWT.TOP; final Print target; final int hAlign; final int vAlign; /** * Constructs a new AlignPrint. * * @param target * the print being aligned. * @param hAlign * the horizontal alignment. One of SWT.LEFT, SWT.CENTER, * SWT.RIGHT, or SWT.DEFAULT. * @param vAlign * the vertical alignment. One of SWT.TOP, SWT.CENTER, * SWT.BOTTOM, or SWT.DEFAULT. */ public AlignPrint(Print target, int hAlign, int vAlign) { Util.notNull(target); this.target = target; this.hAlign = checkHAlign(hAlign); this.vAlign = checkVAlign(vAlign); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + hAlign; result = prime * result + ((target == null) ? 0 : target.hashCode()); result = prime * result + vAlign; return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; AlignPrint other = (AlignPrint) obj; if (hAlign != other.hAlign) return false; if (target == null) { if (other.target != null) return false; } else if (!target.equals(other.target)) return false; if (vAlign != other.vAlign) return false; return true; } /** * Returns the wrapped print being aligned * * @return the wrapped print being aligned */ public Print getTarget() { return target; } /** * Returns a Point with the x and y fields set to the horizontal and * vertical alignment, respectively. * * @return a Point with the x and y fields set to the horizontal and * vertical alignment, respectively. */ public Point getAlignment() { return new Point(hAlign, vAlign); } private static int checkHAlign(int hAlign) { if (hAlign == SWT.LEFT || hAlign == SWT.CENTER || hAlign == SWT.RIGHT) return hAlign; if (hAlign == SWT.DEFAULT) return DEFAULT_HORIZONTAL_ALIGN; PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "hAlign must be one of SWT.LEFT, SWT.CENTER or SWT.RIGHT"); //$NON-NLS-1$ return hAlign; } private static int checkVAlign(int vAlign) { if (vAlign == SWT.TOP || vAlign == SWT.CENTER || vAlign == SWT.BOTTOM) return vAlign; if (vAlign == SWT.DEFAULT) return DEFAULT_VERTICAL_ALIGN; PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "vAlign must be one of SWT.TOP, SWT.CENTER or SWT.BOTTOM"); //$NON-NLS-1$ return vAlign; } public PrintIterator iterator(Device device, GC gc) { return new AlignIterator(this, device, gc); } } class AlignIterator implements PrintIterator { private final PrintIterator target; private final int hAlign; private final int vAlign; AlignIterator(AlignPrint print, Device device, GC gc) { this.target = print.target.iterator(device, gc); this.hAlign = print.hAlign; this.vAlign = print.vAlign; } AlignIterator(AlignIterator that) { this.target = that.target.copy(); this.hAlign = that.hAlign; this.vAlign = that.vAlign; } public boolean hasNext() { return target.hasNext(); } public Point minimumSize() { return target.minimumSize(); } public Point preferredSize() { return target.preferredSize(); } public PrintPiece next(int width, int height) { PrintPiece piece = PaperClips.next(target, width, height); if (piece == null) return null; Point size = piece.getSize(); Point offset = new Point(0, 0); if (hAlign == SWT.CENTER) offset.x = (width - size.x) / 2; else if (hAlign == SWT.RIGHT) offset.x = width - size.x; if (hAlign != SWT.LEFT) size.x = width; if (vAlign == SWT.CENTER) offset.y = (height - size.y) / 2; else if (vAlign == SWT.BOTTOM) offset.y = height - size.x; if (vAlign != SWT.TOP) size.y = height; CompositeEntry entry = new CompositeEntry(piece, offset); return new CompositePiece(new CompositeEntry[] { entry }, size); } public PrintIterator copy() { return new AlignIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/BackgroundPrint.java000066400000000000000000000110321160731654500245640ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; 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; /** * A decorator that paints a background color behind it's target. * * @author Matthew Hall */ public class BackgroundPrint implements Print { Print target; RGB background; /** * Constructs a BackgroundPrint with the given target and background color. * * @param target * the * @param background */ public BackgroundPrint(Print target, RGB background) { Util.notNull(target, background); this.target = target; this.background = background; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((background == null) ? 0 : background.hashCode()); result = prime * result + ((target == null) ? 0 : target.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; BackgroundPrint other = (BackgroundPrint) obj; if (background == null) { if (other.background != null) return false; } else if (!background.equals(other.background)) return false; if (target == null) { if (other.target != null) return false; } else if (!target.equals(other.target)) return false; return true; } /** * Returns the wrapped print to which the background color is being applied. * * @return the wrapped print to which the background color is being applied. */ public Print getTarget() { return target; } /** * Returns the background color. * * @return the background color. */ public RGB getBackground() { return background; } /** * Sets the background color. * * @param background * the new background color. */ public void setBackground(RGB background) { Util.notNull(background); this.background = background; } public PrintIterator iterator(Device device, GC gc) { return new BackgroundIterator(this, device, gc); } } class BackgroundIterator implements PrintIterator { private final PrintIterator target; private final RGB background; private final Device device; BackgroundIterator(BackgroundPrint print, Device device, GC gc) { Util.notNull(print, device, gc); this.device = device; this.target = print.target.iterator(device, gc); this.background = print.background; } BackgroundIterator(BackgroundIterator that) { this.target = that.target.copy(); this.background = that.background; this.device = that.device; } public Point minimumSize() { return target.minimumSize(); } public Point preferredSize() { return target.preferredSize(); } public boolean hasNext() { return target.hasNext(); } public PrintPiece next(int width, int height) { PrintPiece targetPiece = PaperClips.next(target, width, height); if (targetPiece == null) return null; return new BackgroundPiece(targetPiece, background, device); } public PrintIterator copy() { return new BackgroundIterator(this); } } class BackgroundPiece implements PrintPiece { private final PrintPiece target; private final Device device; private final RGB background; BackgroundPiece(PrintPiece target, RGB background, Device device) { Util.notNull(target, background, device); this.target = target; this.device = device; this.background = background; } public Point getSize() { return target.getSize(); } public void paint(GC gc, int x, int y) { paintBackground(gc, x, y); target.paint(gc, x, y); } private void paintBackground(GC gc, int x, int y) { Color oldBackground = gc.getBackground(); gc.setBackground(ResourcePool.forDevice(device).getColor(background)); Point size = getSize(); gc.fillRectangle(x, y, size.x, size.y); gc.setBackground(oldBackground); } public void dispose() { target.dispose(); } }swt-paperclips-1.0.4/src/net/sf/paperclips/BasicGridLookPainter.java000066400000000000000000000174511160731654500255020ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.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. *

* Subclasses must have the following methods implemented: *

* * @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); }swt-paperclips-1.0.4/src/net/sf/paperclips/BigPrint.java000066400000000000000000000135111160731654500232120ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Region; /** * A wrapper for prints whose minimum size is too large to fit on one page. The * target's content is divided across multiple pages like a spreadsheet. Pages * are printed in order left-to-right, then top-to-bottom. *

* Note that this print lays out content under the assumption that every page will have the same * pixel width and height. If a BigPrint is wrapped in a print that * violates this expectation, it is likely that the output will skip and/or * repeat certain portions of the target's content. Some examples of this * behavior: *

* * @author Matthew Hall */ public final class BigPrint implements Print { private final Print target; /** * Constructs a BigPrint. * * @param target */ public BigPrint(Print target) { Util.notNull(target); this.target = target; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((target == null) ? 0 : target.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; BigPrint other = (BigPrint) obj; if (target == null) { if (other.target != null) return false; } else if (!target.equals(other.target)) return false; return true; } /** * Returns the wrapped print which is being split across pages. * * @return the wrapped print which is being split across pages. */ public Print getTarget() { return target; } public PrintIterator iterator(Device device, GC gc) { return new BigIterator(target, device, gc); } } class BigIterator implements PrintIterator { private final PrintIterator target; private final Device device; private PrintPiece currentPiece; private int xOffset; private int yOffset; BigIterator(Print target, Device device, GC gc) { Util.notNull(device, gc, target); this.target = target.iterator(device, gc); this.device = device; currentPiece = null; xOffset = 0; yOffset = 0; } BigIterator(BigIterator that) { this.target = that.target.copy(); this.device = that.device; this.currentPiece = that.currentPiece; this.xOffset = that.xOffset; this.yOffset = that.yOffset; } public Point minimumSize() { return target.minimumSize(); } public Point preferredSize() { return target.preferredSize(); } public boolean hasNext() { return currentPiece != null || target.hasNext(); } // Returns a point whose x and y fields represent the required pages wide // and tall, respectively private Point estimatePagesRequired(int width, int height) { if (width <= 0 || height <= 0) return new Point(0, 0); Point pref = target.preferredSize(); Point prefPages = new Point(pref.x / width, pref.y / height); Point min = target.minimumSize(); // Adding width-1 rounds up page count w/out floating point op // Same goes for adding height-1 Point minPages = new Point(Math.max((min.x + width - 1) / width, 1), Math.max((min.y + height - 1) / height, 1)); return new Point(Math.max(prefPages.x, minPages.x), Math.max( prefPages.y, minPages.y)); } public PrintPiece next(int width, int height) { if (!hasNext()) PaperClips.error("No more content"); //$NON-NLS-1$ if (currentPiece == null) { Point pages = estimatePagesRequired(width, height); currentPiece = PaperClips.next(target, width * pages.x, height * pages.y); if (currentPiece == null) return null; // Iteration fails // Reset the offset for the new piece. xOffset = 0; yOffset = 0; } PrintPiece result = new BigPiece(currentPiece, new Point(width, height), xOffset, yOffset); // Advance cursor on current piece. xOffset += width; if (xOffset >= currentPiece.getSize().x) { xOffset = 0; yOffset += height; } if (yOffset >= currentPiece.getSize().y) { currentPiece = null; } return result; } public PrintIterator copy() { return new BigIterator(this); } } class BigPiece implements PrintPiece { private final PrintPiece target; private final Point size; private final Point offset; BigPiece(PrintPiece target, Point size, int xOffset, int yOffset) { Util.notNull(target, size); this.target = target; this.size = new Point(size.x, size.y); this.offset = new Point(xOffset, yOffset); } public Point getSize() { return new Point(size.x, size.y); } public void paint(GC gc, int x, int y) { // Remember clipping region Region region = new Region(); gc.getClipping(region); // Set clipping region so only the portion of the target we want is // printed. gc.setClipping(x, y, size.x, size.y); // Paint the target. target.paint(gc, x - offset.x, y - offset.y); // Restore clipping region gc.setClipping(region); region.dispose(); } public void dispose() { target.dispose(); } }swt-paperclips-1.0.4/src/net/sf/paperclips/Border.java000066400000000000000000000020441160731654500227100ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; /** * Interface for drawing borders, used by BorderPaint and GridPrint for drawing * borders a child print and grid cells, respectively. * * @author Matthew Hall */ public interface Border { /** * Creates a BorderPainter which uses the given Device and GC. * * @param device * the print device. * @param gc * a GC for drawing to the print device. * @return a BorderPainter for painting the border on the given Device and * GC. */ public BorderPainter createPainter(Device device, GC gc); } swt-paperclips-1.0.4/src/net/sf/paperclips/BorderIterator.java000066400000000000000000000075701160731654500244330ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; class BorderIterator implements PrintIterator { private final BorderPainter border; private PrintIterator target; private boolean opened; BorderIterator(BorderPrint print, Device device, GC gc) { this.border = print.border.createPainter(device, gc); this.target = print.target.iterator(device, gc); this.opened = false; } BorderIterator(BorderIterator that) { this.border = that.border; this.target = that.target.copy(); this.opened = that.opened; } public boolean hasNext() { return target.hasNext(); } public Point minimumSize() { return addBorderMargin(target.minimumSize()); } public Point preferredSize() { return addBorderMargin(target.preferredSize()); } private Point addBorderMargin(Point targetSize) { return new Point(targetSize.x + border.getWidth(), targetSize.y + border.getMaxHeight()); } public PrintPiece next(int width, int height) { if (!hasNext()) PaperClips.error("No more content"); //$NON-NLS-1$ PrintPiece piece = next(width, height, false /* closed bottom border */); if (piece == null) piece = next(width, height, true /* open bottom border */); if (piece != null) opened = true; return piece; } private PrintPiece next(int width, int height, boolean bottomBorderOpen) { // Adjust iteration area for border dimensions. width -= border.getWidth(); height -= border.getHeight(opened, bottomBorderOpen); if (width < 0 || height < 0) return null; PrintIterator iter = target.copy(); PrintPiece piece = PaperClips.next(iter, width, height); if (piece == null) return null; if (bottomBorderOpen && !iter.hasNext()) { // The target content was consumed, but the bottom border is open // (suggesting that there is more // content): find the largest piece that *doesn't* consume all the // target's content, and show it with // an open bottom border. piece.dispose(); piece = getTallestPieceNotCompletelyConsumingTarget(width, height); if (piece == null) return null; } else if (!bottomBorderOpen && iter.hasNext()) { // Bottom border is closed but the target has more content: fail so // calling method can try again with // an open bottom border. piece.dispose(); return null; } else { this.target = iter; } // Decorate the target print piece with border piece = new BorderPiece(piece, border, opened, bottomBorderOpen); return piece; } private PrintPiece getTallestPieceNotCompletelyConsumingTarget( final int width, final int height) { int low = 0; int high = height - 1; PrintIterator bestIterator = null; PrintPiece bestPiece = null; while (low + 1 < high) { int testHeight = (low + high + 1) / 2; PrintIterator testIterator = target.copy(); PrintPiece testPiece = PaperClips.next(testIterator, width, testHeight); if (testPiece == null) { low = testHeight + 1; } else if (testIterator.hasNext()) { low = testHeight; if (bestPiece != null) bestPiece.dispose(); bestIterator = testIterator; bestPiece = testPiece; } else { // !testIterator.hasNext() high = testPiece.getSize().y - 1; } } if (bestPiece != null) this.target = bestIterator; return bestPiece; } public PrintIterator copy() { return new BorderIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/BorderPainter.java000066400000000000000000000111131160731654500242300ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * Interface for calculating and drawing borders in a BorderPrint. * * @author Matthew Hall */ public interface BorderPainter { /** * Returns the border inset, in pixels, from the left. * * @return the border inset, in pixels, from the left. */ public int getLeft(); /** * Returns the border inset, in pixels, from the right. * * @return the border inset, in pixels, from the right. */ public int getRight(); /** * Returns the sum of the left and right border insets. * * @return the sum of the left and right border insets. */ public int getWidth(); /** * Returns the border inset, in pixels, from the top. * * @param open * If true, the inset of an open border will be returned. If * false, the inset of a closed border will be returned. * @return the border inset, in pixels, from the top. */ public int getTop(boolean open); /** * Returns the border inset, in pixels, from the bottom. * * @param open * If true, the inset of an open border will be returned. If * false, the inset of a closed border will be returned. * @return the border inset, in pixels, from the bottom. */ public int getBottom(boolean open); /** * Returns the sum of the top and bottom border insets. * * @param topOpen * If true, the inset of an open border will be returned. If * false, the inset of a closed border will be returned. * @param bottomOpen * If true, the inset of an open border will be returned. If * false, the inset of a closed border will be returned. * @return the sum of the top and bottom border insets. */ public int getHeight(boolean topOpen, boolean bottomOpen); /** * Returns the sum of the maximum top and bottom border insets. * * @return the sum of the maximum top and bottom border insets. */ public int getMaxHeight(); /** * Returns the x and y distance that two of the same BorderPainters would * overlap to create the appearance of a single border between the two. This * method is used by GridPrint whenever the horizontal and/or vertical * spacing fields are set to {@link GridPrint#BORDER_OVERLAP }. * * @return the distance that this border painter would overlap an adjacent * one. */ public Point getOverlap(); /** * Paints a border around the specified region. Depending on the type of * border, the top and bottom of may be painted differently depending on the * values of topOpen and bottomOpen. * * @param gc * The graphics context to paint on. * @param x * The x coordinate of the top left corner of the border. * @param y * The y coordinate of the top left corner of the border. * @param width * The width of the border to paint * @param height * The height of the border to paint * @param topOpen * If true, the top border should be drawn "open," to indicate * that this is the continuation of a border in a previous * iteration. If false, the border should be drawn "closed" to * indicate that this is the first iteration on the BorderPrint's * target. * @param bottomOpen * If true, the bottom border should be drawn "open," to indicate * that the BorderPrint's target was not consumed in this * iteration. If false, the bottom border should be drawn * "closed," to indicate that the BorderPrint's target completed * during this iteration. */ public void paint(GC gc, int x, int y, int width, int height, boolean topOpen, boolean bottomOpen); /** * Disposes the system resources allocated by this BorderPainter. The * dispose method is not a permanent disposal of a BorderPainter. It * is intended to reclaim system resources, however future calls to * paint(GC,int,int) may require that the resources be allocated again. */ public void dispose(); }swt-paperclips-1.0.4/src/net/sf/paperclips/BorderPiece.java000066400000000000000000000027321160731654500236620ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; class BorderPiece implements PrintPiece { private final PrintPiece target; private final BorderPainter border; private final boolean topOpen; private final boolean bottomOpen; private final Point size; BorderPiece(PrintPiece target, BorderPainter border, boolean topOpen, boolean bottomOpen) { Util.notNull(target, border); this.target = target; this.border = border; this.topOpen = topOpen; this.bottomOpen = bottomOpen; Point targetSize = target.getSize(); this.size = new Point(targetSize.x + border.getWidth(), targetSize.y + border.getHeight(topOpen, bottomOpen)); } public Point getSize() { return new Point(size.x, size.y); } public void paint(GC gc, int x, int y) { border.paint(gc, x, y, size.x, size.y, topOpen, bottomOpen); target.paint(gc, x + border.getLeft(), y + border.getTop(topOpen)); } public void dispose() { border.dispose(); target.dispose(); } }swt-paperclips-1.0.4/src/net/sf/paperclips/BorderPrint.java000066400000000000000000000045761160731654500237410ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.decorator.BorderDecorator; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; /** * A decorator that draws a border around the target print. * * @see BorderDecorator * @author Matthew Hall */ public class BorderPrint implements Print { final Print target; final Border border; /** * Constructs a BorderPrint with the given target and border. * * @param target * the print to decorate with a border. * @param border * the border which will be drawn around the target. */ public BorderPrint(Print target, Border border) { Util.notNull(target, border); this.target = target; this.border = border; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((border == null) ? 0 : border.hashCode()); result = prime * result + ((target == null) ? 0 : target.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; BorderPrint other = (BorderPrint) obj; if (border == null) { if (other.border != null) return false; } else if (!border.equals(other.border)) return false; if (target == null) { if (other.target != null) return false; } else if (!target.equals(other.target)) return false; return true; } /** * Returns the wrapped print to which the border is being applied. * * @return the wrapped print to which the border is being applied. */ public Print getTarget() { return target; } /** * Returns the border being applied to the target. * * @return the border being applied to the target. */ public Border getBorder() { return border; } public PrintIterator iterator(Device device, GC gc) { return new BorderIterator(this, device, gc); } } swt-paperclips-1.0.4/src/net/sf/paperclips/BreakPrint.java000066400000000000000000000033641160731654500235420ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A print which inserts a page break (or a column break, if inside a * ColumnPrint). *

* This class is horizontally and vertically greedy. Greedy prints take up all * the available space on the page. * * @author Matthew Hall */ public class BreakPrint implements Print { /** * Constructs a BreakPrint. */ public BreakPrint() { // Nothing to do } public boolean equals(Object obj) { return Util.sameClass(this, obj); } public int hashCode() { return 39 * 29; } public PrintIterator iterator(Device device, GC gc) { return new BreakIterator(); } } class BreakIterator implements PrintIterator { boolean hasNext; BreakIterator() { hasNext = true; } public PrintIterator copy() { return hasNext ? new BreakIterator() : this; } public boolean hasNext() { return hasNext; } public Point minimumSize() { return new Point(0, 0); } public Point preferredSize() { return new Point(0, 0); } public PrintPiece next(int width, int height) { if (!hasNext) PaperClips.error("No more content"); //$NON-NLS-1$ hasNext = false; return new EmptyPiece(new Point(width, height)); } }swt-paperclips-1.0.4/src/net/sf/paperclips/CellBackgroundProvider.java000066400000000000000000000023431160731654500260670ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import org.eclipse.swt.graphics.RGB; /** * Instances of this interface provide background colors to be drawn behind * cells in a grid. This interface is used by DefaultGridLook to provide * pluggable cell background behavior. * * @author Matthew Hall */ public interface CellBackgroundProvider { /** * Returns the background color to display for the given grid cell. * * @param row * the row index (zero-based) * @param column * the column index (zero-based). This is the grid column index, * not the cell index within the row. * @param colspan * the number of grid columns that the cell occupies. * @return the background color to display for the given header cell. */ public RGB getCellBackground(int row, int column, int colspan); } swt-paperclips-1.0.4/src/net/sf/paperclips/ColumnPrint.java000066400000000000000000000232431160731654500237510ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A wrapper Print which splits its child print into multiple columns. *

* This class is horizontally greedy. Greedy prints take up all the available * space on the page. *

* ColumnPrint attempts to use the minimum possible vertical space on the page * if isCompressed() returns true (the default). This behavior can be disabled * by calling setCompressed(false). * * @author Matthew Hall */ public class ColumnPrint implements Print { final Print target; final int columns; final int spacing; boolean compressed; /** * Constructs a ColumnPrint with the given target, number of columns, and * column spacing (expressed in points). 72 points = 1". * * @param target * the print which will be split into columns. * @param columns * the number of columns to display * @param spacing * the spacing between each column. */ public ColumnPrint(Print target, int columns, int spacing) { this(target, columns, spacing, true); } /** * Constructs a ColumnPrint with the given target, column count, column * spacing, and compression. * * @param target * the print to display in columns. * @param columns * the number of columns to display. * @param spacing * the spacing between each column, expressed in points. 72 * points = 1". * @param compressed * whether the columns on the final page are to be */ public ColumnPrint(Print target, int columns, int spacing, boolean compressed) { Util.notNull(target); if (spacing < 0) PaperClips .error(SWT.ERROR_INVALID_ARGUMENT, "spacing must be >= 0"); //$NON-NLS-1$ if (columns < 2) PaperClips .error(SWT.ERROR_INVALID_ARGUMENT, "columns must be >= 2"); //$NON-NLS-1$ this.target = target; this.spacing = spacing; this.columns = columns; this.compressed = compressed; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + columns; result = prime * result + (compressed ? 1231 : 1237); result = prime * result + spacing; result = prime * result + ((target == null) ? 0 : target.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ColumnPrint other = (ColumnPrint) obj; if (columns != other.columns) return false; if (compressed != other.compressed) return false; if (spacing != other.spacing) return false; if (target == null) { if (other.target != null) return false; } else if (!target.equals(other.target)) return false; return true; } /** * Returns the target print being split into columns. * * @return the target print being split into columns. */ public Print getTarget() { return target; } /** * Returns the number of columns per page. * * @return the number of columns per page. */ public int getColumnCount() { return columns; } /** * Returns the spacing between columns, in points. 72 points = 1". * * @return the spacing between columns, in points. */ public int getColumnSpacing() { return spacing; } /** * Returns whether the columns are compressed to the smallest possible * height on the last page. * * @return whether the columns are compressed to the smallest possible * height on the last page. */ public boolean isCompressed() { return compressed; } /** * Sets whether the columns are compressed to the smallest possible height * on the last page. * * @param compressed * whether to compress the columns. */ public void setCompressed(boolean compressed) { this.compressed = compressed; } public PrintIterator iterator(Device device, GC gc) { return new ColumnIterator(this, device, gc); } } class ColumnIterator implements PrintIterator { private PrintIterator target; private final int columns; private final int spacing; private final boolean compressed; ColumnIterator(ColumnPrint print, Device device, GC gc) { this.target = print.target.iterator(device, gc); this.columns = print.columns; this.spacing = Math.round(print.spacing * device.getDPI().x / 72f); this.compressed = print.compressed; } ColumnIterator(ColumnIterator that) { this.target = that.target.copy(); this.columns = that.columns; this.spacing = that.spacing; this.compressed = that.compressed; } public Point minimumSize() { return computeSize(target.minimumSize()); } public Point preferredSize() { return computeSize(target.preferredSize()); } private Point computeSize(Point targetSize) { return new Point(targetSize.x * columns + spacing * (columns - 1), targetSize.y); } public boolean hasNext() { return target.hasNext(); } int[] computeColSizes(int width) { int[] colSizes = new int[columns]; int availableWidth = width - spacing * (columns - 1); for (int i = 0; i < colSizes.length; i++) { colSizes[i] = availableWidth / (columns - i); availableWidth -= colSizes[i]; } return colSizes; } Point[] computeColOffsets(int[] colSizes) { Point[] colOffsets = new Point[columns]; int xOffset = 0; for (int i = 0; i < columns; i++) { colOffsets[i] = new Point(xOffset, 0); xOffset += colSizes[i] + spacing; } return colOffsets; } /** * Iterates across the given column sizes and returns an array of * PrintPieces to fill those columns, or null if there was insufficient room * to continue iterating. A backup of the given iterator should be taken * before invoking this method! If null is returned, the given iterator is * corrupt and should no longer be used! * * @param colSizes * an array of column sizes * @param height * the height * @return an array of PrintPieces for the given column sizes, or null */ PrintPiece[] nextColumns(PrintIterator iterator, int[] colSizes, int height) { List pieces = new ArrayList(); for (int i = 0; i < columns && iterator.hasNext(); i++) { PrintPiece piece = PaperClips.next(iterator, colSizes[i], height); if (piece == null) return disposePieces(pieces); pieces.add(piece); } return (PrintPiece[]) pieces.toArray(new PrintPiece[pieces.size()]); } private PrintPiece[] disposePieces(List pieces) { for (Iterator iter = pieces.iterator(); iter.hasNext();) { PrintPiece piece = (PrintPiece) iter.next(); piece.dispose(); } return null; } PrintPiece createResult(PrintPiece[] pieces, int[] colSizes) { CompositeEntry[] entries = new CompositeEntry[pieces.length]; Point[] offsets = computeColOffsets(colSizes); for (int i = 0; i < pieces.length; i++) entries[i] = new CompositeEntry(pieces[i], offsets[i]); return new CompositePiece(entries); } public PrintPiece next(int width, int height) { int[] colSizes = computeColSizes(width); // Iterate on a copy in case any column fails to layout. PrintIterator iter = target.copy(); PrintPiece[] columns = nextColumns(iter, colSizes, height); if (columns == null) return null; // The target was completely consumed. If compressed property is true, // close the gap until we find the // smallest height that completely consumes the target's contents. if (!iter.hasNext() && compressed) return nextCompressed(colSizes, iter, columns); this.target = iter; return createResult(columns, colSizes); } private PrintPiece nextCompressed(int[] colSizes, PrintIterator iter, PrintPiece[] columns) { int highestInvalidHeight = 0; int lowestValidHeight = getMaxHeight(columns); // Remember the best results PrintIterator bestIteration = iter; PrintPiece[] bestColumns = columns; while (lowestValidHeight > highestInvalidHeight + 1) { int testHeight = (lowestValidHeight + highestInvalidHeight + 1) / 2; iter = target.copy(); columns = nextColumns(iter, colSizes, testHeight); if (columns == null) { highestInvalidHeight = testHeight; } else if (iter.hasNext()) { highestInvalidHeight = testHeight; disposePieces(columns); } else { disposePieces(bestColumns); bestIteration = iter; bestColumns = columns; lowestValidHeight = getMaxHeight(bestColumns); } } // Now that we've narrowed down the target's best iteration, we can // update the state of this iterator and // return the result. this.target = bestIteration; return createResult(bestColumns, colSizes); } private int getMaxHeight(PrintPiece[] pieces) { int result = 0; for (int i = 0; i < pieces.length; i++) result = Math.max(result, pieces[i].getSize().y); return result; } private void disposePieces(PrintPiece[] pieces) { for (int i = 0; i < pieces.length; i++) pieces[i].dispose(); } public PrintIterator copy() { return new ColumnIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/CompositeEntry.java000066400000000000000000000025541160731654500244650ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; /** * An entry in a CompositePiece. * * @author Matthew Hall */ public class CompositeEntry { final PrintPiece piece; final Point offset; /** * Constructs a CompositeEntry with the given PrintPiece and offset. * * @param piece * the PrintPiece for this entry. * @param offset * the painting offset within the CompositePrint. */ public CompositeEntry(PrintPiece piece, Point offset) { Util.notNull(piece, offset); checkOffset(offset); this.piece = piece; this.offset = offset; } private void checkOffset(Point offset) { if (offset.x < 0 || offset.y < 0) PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Offset cannot be negative: " + offset); //$NON-NLS-1$ } /** * Disposes this entry's print piece. */ public void dispose() { piece.dispose(); } }swt-paperclips-1.0.4/src/net/sf/paperclips/CompositePiece.java000066400000000000000000000075031160731654500244100ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import java.util.ArrayList; import java.util.List; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A composite PrintPiece for displaying child PrintPieces. This class is * especially useful for Print implementations that perform layout of multiple * child Prints. * * @author Matthew Hall */ public class CompositePiece implements PrintPiece { private final Point size; private final CompositeEntry[] entries; /** * Constructs a CompositePiece with the given entries. * * @param entries * an array of entries that make up this PrintPiece. */ public CompositePiece(CompositeEntry[] entries) { this(createList(entries)); } /** * Constructs a CompositePrintPiece with the given entries and explicit * size. This constructor will increase the explicit size to completely * contain any child entries which extend outside the given size. * * @param entries * an array of entries that make up this PrintPiece. * @param size */ public CompositePiece(CompositeEntry[] entries, Point size) { this(createList(entries), size); } private static List createList(CompositeEntry[] entries) { List result = new ArrayList(); for (int i = 0; i < entries.length; i++) result.add(entries[i]); return result; } /** * Constructs a composite PrintPiece with the given entries. * * @param entries * an array of entries that make up this PrintPiece. */ public CompositePiece(List entries) { this(entries, new Point(0, 0)); } /** * Constructs a composite PrintPiece with the given entries and minimum * size. * * @param entries * a list of CompositeEntry objects describing the child * PrintPieces. * @param size * a hint indicating the minimum size that should be reported * from getSize(). This constructor increase this size to fit any * entries that extend outside the given size. */ public CompositePiece(List entries, Point size) { Util.noNulls(entries); this.entries = (CompositeEntry[]) entries .toArray(new CompositeEntry[entries.size()]); this.size = new Point(size.x, size.y); for (int i = 0; i < this.entries.length; i++) { CompositeEntry entry = this.entries[i]; Point pieceSize = entry.piece.getSize(); this.size.x = Math.max(this.size.x, entry.offset.x + pieceSize.x); this.size.y = Math.max(this.size.y, entry.offset.y + pieceSize.y); } } public Point getSize() { return new Point(size.x, size.y); } public void paint(GC gc, int x, int y) { // SWT on OSX has problems with the clipping. A GC(Printer) always // returns a clipping rectangle of // [0,0,0,0] so that inhibits our ability to check each entry against // the hit rectangle. In addition it // appears that a GC(Image(Printer))'s clipping on OSX is not affected // by the GC's transform, so that // screws up the hit clip as well. For this reason we are no longer // checking entries to see if they // intersect the clipping region before drawing them. for (int i = 0; i < entries.length; i++) { CompositeEntry entry = entries[i]; entry.piece.paint(gc, x + entry.offset.x, y + entry.offset.y); } } public void dispose() { for (int i = 0; i < entries.length; i++) entries[i].dispose(); } } swt-paperclips-1.0.4/src/net/sf/paperclips/DebugPrint.java000066400000000000000000000043131160731654500235370ustar00rootroot00000000000000/* * Copyright (c) 2009 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 net.sf.paperclips; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * Helper Print for debugging documents which fail to layout. Clients may set * breakpoints inside the methods in this class (as well as DebugIterator and * DebugPiece), then step into the target object's methods to trace the problem. * * @author Matthew Hall * @deprecated Reminder to remove references to DebugPrint when you're done * debugging a print job. */ public class DebugPrint implements Print { private final Print target; /** * @param target * the Print object to debug */ public DebugPrint(Print target) { this.target = target; } public PrintIterator iterator(Device device, GC gc) { PrintIterator iterator = target.iterator(device, gc); return new DebugIterator(iterator); } } class DebugIterator implements PrintIterator { private final PrintIterator target; DebugIterator(PrintIterator target) { this.target = target; } public PrintIterator copy() { return new DebugIterator(target.copy()); } public boolean hasNext() { return target.hasNext(); } public Point minimumSize() { return target.minimumSize(); } public PrintPiece next(int width, int height) { PrintPiece piece = target.next(width, height); return piece == null ? null : new DebugPiece(piece); } public Point preferredSize() { return target.preferredSize(); } } class DebugPiece implements PrintPiece { private final PrintPiece target; DebugPiece(PrintPiece target) { this.target = target; } public void dispose() { target.dispose(); } public Point getSize() { return target.getSize(); } public void paint(GC gc, int x, int y) { target.paint(gc, x, y); } }swt-paperclips-1.0.4/src/net/sf/paperclips/DefaultCellBackgroundProvider.java000066400000000000000000000057361160731654500274050ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import org.eclipse.swt.graphics.RGB; /** * Default implementation of the CellBackgroundProvider interface. * * @author Matthew Hall */ public class DefaultCellBackgroundProvider implements CellBackgroundProvider { private final CellBackgroundProvider chain; private RGB background; /** * Constructs a DefaultGridBackgroundProvider with a null background. */ public DefaultCellBackgroundProvider() { this.chain = null; this.background = null; } /** * Constructs a DefaultGridBackgroundProvider which chains to the argument * if this instance has a null background color. (DefaultGridLook uses this * constructor to cause header and footer background colors to default to * the body background color.) * * @param chain * the provider to chain a getCellBackground(...) call to if this * instance would return null. Ignored if null. */ public DefaultCellBackgroundProvider(CellBackgroundProvider chain) { this.chain = chain; this.background = null; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((background == null) ? 0 : background.hashCode()); result = prime * result + ((chain == null) ? 0 : chain.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DefaultCellBackgroundProvider other = (DefaultCellBackgroundProvider) obj; if (background == null) { if (other.background != null) return false; } else if (!background.equals(other.background)) return false; if (chain == null) { if (other.chain != null) return false; } else if (!chain.equals(other.chain)) return false; return true; } /** * Returns the value in the background property. If the background property * is null, the chained provider will be consulted to obtain a background * color. */ public RGB getCellBackground(int row, int column, int colspan) { RGB result = getBackground(); if (result == null && chain != null) result = chain.getCellBackground(row, column, colspan); return result; } /** * Returns the background color. * * @return the background color. */ public RGB getBackground() { return background; } /** * Sets the background color to the argument. * * @param background * the new background color. */ public void setBackground(RGB background) { this.background = background; } } swt-paperclips-1.0.4/src/net/sf/paperclips/DefaultGridLook.java000066400000000000000000000342231160731654500245160ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; /** * A GridLook which draws a border around grid cells, with configurable * background colors for body, header, and footer cells. * * @author Matthew Hall */ public class DefaultGridLook implements GridLook { /** * Constant cell spacing value indicating that the borders of adjacent cells * should overlap so the appear continuous. */ public static final int BORDER_OVERLAP = -1; Point cellSpacing = new Point(BORDER_OVERLAP, BORDER_OVERLAP); Rectangle cellPadding = new Rectangle(0, 0, 0, 0); int headerGap = BORDER_OVERLAP; int footerGap = BORDER_OVERLAP; Border cellBorder = new GapBorder(); DefaultCellBackgroundProvider defaultBodyBackgroundProvider; DefaultCellBackgroundProvider defaultHeaderBackgroundProvider; DefaultCellBackgroundProvider defaultFooterBackgroundProvider; CellBackgroundProvider bodyBackgroundProvider; CellBackgroundProvider headerBackgroundProvider; CellBackgroundProvider footerBackgroundProvider; /** * Constructs a DefaultGridLook with no border, no cell spacing, and no * background colors. */ public DefaultGridLook() { this.bodyBackgroundProvider = defaultBodyBackgroundProvider = new DefaultCellBackgroundProvider(); this.headerBackgroundProvider = defaultHeaderBackgroundProvider = new DefaultCellBackgroundProvider( bodyBackgroundProvider); this.footerBackgroundProvider = defaultFooterBackgroundProvider = new DefaultCellBackgroundProvider( bodyBackgroundProvider); } /** * Constructs a DefaultGridLook with the given cell spacing, and no border * or background colors. * * @param horizontalSpacing * the horizontal cell spacing. * @param verticalSpacing * the vertical cell spacing. */ public DefaultGridLook(int horizontalSpacing, int verticalSpacing) { this(); setCellSpacing(horizontalSpacing, verticalSpacing); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((bodyBackgroundProvider == null) ? 0 : bodyBackgroundProvider.hashCode()); result = prime * result + ((cellBorder == null) ? 0 : cellBorder.hashCode()); result = prime * result + ((cellPadding == null) ? 0 : cellPadding.hashCode()); result = prime * result + ((cellSpacing == null) ? 0 : cellSpacing.hashCode()); result = prime * result + ((footerBackgroundProvider == null) ? 0 : footerBackgroundProvider.hashCode()); result = prime * result + footerGap; result = prime * result + ((headerBackgroundProvider == null) ? 0 : headerBackgroundProvider.hashCode()); result = prime * result + headerGap; return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DefaultGridLook other = (DefaultGridLook) obj; if (bodyBackgroundProvider == null) { if (other.bodyBackgroundProvider != null) return false; } else if (!bodyBackgroundProvider.equals(other.bodyBackgroundProvider)) return false; if (cellBorder == null) { if (other.cellBorder != null) return false; } else if (!cellBorder.equals(other.cellBorder)) return false; if (cellPadding == null) { if (other.cellPadding != null) return false; } else if (!cellPadding.equals(other.cellPadding)) return false; if (cellSpacing == null) { if (other.cellSpacing != null) return false; } else if (!cellSpacing.equals(other.cellSpacing)) return false; if (footerBackgroundProvider == null) { if (other.footerBackgroundProvider != null) return false; } else if (!footerBackgroundProvider .equals(other.footerBackgroundProvider)) return false; if (footerGap != other.footerGap) return false; if (headerBackgroundProvider == null) { if (other.headerBackgroundProvider != null) return false; } else if (!headerBackgroundProvider .equals(other.headerBackgroundProvider)) return false; if (headerGap != other.headerGap) return false; return true; } /** * Returns the cell border. Default is an empty border with no margins. * * @return the cell border. */ public Border getCellBorder() { return cellBorder; } /** * Sets the cell border. * * @param border * the cell border. */ public void setCellBorder(Border border) { this.cellBorder = border; } /** * Returns the border spacing, in points, between adjacent grid cells. * Default is (x=BORDER_OVERLAP, y=BORDER_OVERLAP). * * @return the border spacing, in points, between adjacent grid cells. */ public Point getCellSpacing() { return new Point(cellSpacing.x, cellSpacing.y); } /** * Sets the border spacing, in points, between adjacent grid cells. A value * of {@link #BORDER_OVERLAP} causes the borders to overlap, making the * border appear continuous throughout the grid. A value of 0 or more causes * the cell borders to be spaced that many points apart. 72 points = 1". * * @param cellSpacing * a point whose x and y elements indicate the horizontal and * vertical spacing between grid cells. */ public void setCellSpacing(Point cellSpacing) { setCellSpacing(cellSpacing.x, cellSpacing.y); } /** * Sets the border spacing, in points, between adjacent grid cells. A value * of {@link #BORDER_OVERLAP} causes the borders to overlap, making the * border appear continuous throughout the grid. A value of 0 or more causes * the cell borders to be spaced that many points apart. 72 points = 1". * * @param horizontal * the horizontal cell spacing. * @param vertical * the vertical cell spacing. */ public void setCellSpacing(int horizontal, int vertical) { if (horizontal == BORDER_OVERLAP || horizontal >= 0) this.cellSpacing.x = horizontal; if (vertical == BORDER_OVERLAP || vertical >= 0) this.cellSpacing.y = vertical; } /** * Returns a rectangle whose public fields denote the left (x), top (y), * right (width) and bottom (height) cell padding, expressed in points. 72 * points = 1" = 2.54cm. * * @return a rectangle whose public fields denote the cell padding at each * edge. */ public Rectangle getCellPadding() { return new Rectangle(cellPadding.x, cellPadding.y, cellPadding.width, cellPadding.height); } /** * Sets the cell padding to the values in the public fields of the argument. * * @param cellPadding * the new cell padding. */ public void setCellPadding(Rectangle cellPadding) { setCellPadding(cellPadding.x, cellPadding.y, cellPadding.width, cellPadding.height); } /** * Sets the cell padding to the given horizontal and vertical values. This * is equivalent to calling setCellPadding(horizontalPadding, * verticalPadding, horizontalPadding, verticalPadding). * * @param horizontalPadding * the amount of padding to add to the left and right of each * cell, in points. * @param verticalPadding * the amount padding to add to the top and bottom each cell, in * points. */ public void setCellPadding(int horizontalPadding, int verticalPadding) { setCellPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding); } /** * Sets the cell padding to the specified values. * * @param left * the left cell padding, in points. * @param top * the top cell padding, in points. * @param right * the right cell padding, in points. * @param bottom * the bottom cell padding, in points. */ public void setCellPadding(int left, int top, int right, int bottom) { cellPadding.x = left; cellPadding.y = top; cellPadding.width = right; cellPadding.height = bottom; } /** * Returns the header background color. If null, the body background color * is used. Default is null. * * @return the header background color. */ public RGB getHeaderBackground() { return defaultHeaderBackgroundProvider.getBackground(); } /** * Sets the header background color. Calls to this method override any * previous calls to setHeaderBackgroundProvider(...). * * @param headerBackground * the new background color. If null, the body background color * will be used. */ public void setHeaderBackground(RGB headerBackground) { defaultHeaderBackgroundProvider.setBackground(headerBackground); this.headerBackgroundProvider = defaultHeaderBackgroundProvider; } /** * Returns the header background color provider. * * @return the header background color provider. */ public CellBackgroundProvider getHeaderBackgroundProvider() { return headerBackgroundProvider; } /** * Sets the header background color provider. Calls to this method override * any previous calls to setHeaderBackground(RGB). Setting this property to * null restores the default background provider. * * @param headerBackgroundProvider * the new background color provider. */ public void setHeaderBackgroundProvider( CellBackgroundProvider headerBackgroundProvider) { this.headerBackgroundProvider = headerBackgroundProvider == null ? defaultHeaderBackgroundProvider : headerBackgroundProvider; } /** * Returns the vertical gap between the header and body cells. Default is * BORDER_OVERLAP. * * @return the vertical gap between the header and body cells. */ public int getHeaderGap() { return headerGap; } /** * Sets the vertical gap between the header and body cells. A value of * {@link #BORDER_OVERLAP} causes the borders to overlap, making the border * appear continuous in the transition from the header cells to the body * cells. * * @param headerGap * the new header gap. */ public void setHeaderGap(int headerGap) { this.headerGap = headerGap; } /** * Returns the body background color. Default is null (no background color). * * @return the body background color. */ public RGB getBodyBackground() { return defaultBodyBackgroundProvider.getBackground(); } /** * Sets the body background color. Calls to this method override any * previous calls to setBodyBackgroundProvider(...). * * @param bodyBackground * the new background color. */ public void setBodyBackground(RGB bodyBackground) { defaultBodyBackgroundProvider.setBackground(bodyBackground); this.bodyBackgroundProvider = defaultBodyBackgroundProvider; } /** * Returns the body background color provider. * * @return the body background color provider. */ public CellBackgroundProvider getBodyBackgroundProvider() { return bodyBackgroundProvider; } /** * Sets the body background color provider. Calls to this method override * any previous calls to setBodyBackground(RGB). Setting this property to * null restores the default background provider. * * @param bodyBackgroundProvider * the new background color provider. */ public void setBodyBackgroundProvider( CellBackgroundProvider bodyBackgroundProvider) { this.bodyBackgroundProvider = bodyBackgroundProvider == null ? defaultBodyBackgroundProvider : bodyBackgroundProvider; } /** * Returns the vertical gap between the body and footer cells. Default is * BORDER_OVERLAP. * * @return the vertical gap between the header and body cells. */ public int getFooterGap() { return footerGap; } /** * Sets the vertical gap between the header and body cells. A value of * {@link #BORDER_OVERLAP} causes the borders to overlap, making the border * appear continuous in the transition from the body cells to the footer * cells. * * @param footerGap */ public void setFooterGap(int footerGap) { this.footerGap = footerGap; } /** * Returns the footer background color. If null, the body background color * is used. Default is null. * * @return the footer background color. */ public RGB getFooterBackground() { return defaultFooterBackgroundProvider.getBackground(); } /** * Sets the footer background color. Calls to this method override any * previous calls to setFooterBackgroundProvider(...). * * @param footerBackground * the new background color. If null, the body background color * will be used. */ public void setFooterBackground(RGB footerBackground) { defaultFooterBackgroundProvider.setBackground(footerBackground); this.footerBackgroundProvider = defaultFooterBackgroundProvider; } /** * Returns the footer background color provider. * * @return the footer background color provider. */ public CellBackgroundProvider getFooterBackgroundProvider() { return footerBackgroundProvider; } /** * Sets the footer background color provider. Calls to this method override * any previous calls to setFooterBackground(RGB). Setting this property to * null restores the default background provider. * * @param footerBackgroundProvider * the new background color provider. */ public void setFooterBackgroundProvider( CellBackgroundProvider footerBackgroundProvider) { this.footerBackgroundProvider = footerBackgroundProvider == null ? defaultFooterBackgroundProvider : footerBackgroundProvider; } public GridLookPainter getPainter(Device device, GC gc) { return new DefaultGridLookPainter(this, device, gc); } } swt-paperclips-1.0.4/src/net/sf/paperclips/DefaultGridLookPainter.java000066400000000000000000000116121160731654500260360ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.ResourcePool; 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; import org.eclipse.swt.graphics.Rectangle; class DefaultGridLookPainter extends BasicGridLookPainter { private final Rectangle cellPadding; private final BorderPainter border; private final CellBackgroundProvider headerBackground; private final CellBackgroundProvider bodyBackground; private final CellBackgroundProvider footerBackground; private final GridMargins margins; private final ResourcePool resources; DefaultGridLookPainter(DefaultGridLook look, Device device, GC gc) { super(device); Point dpi = device.getDPI(); this.border = look.cellBorder.createPainter(device, gc); this.cellPadding = calculateCellPadding(look, dpi); this.margins = calculateGridMargins(look, dpi); this.bodyBackground = look.bodyBackgroundProvider; this.headerBackground = look.headerBackgroundProvider; this.footerBackground = look.footerBackgroundProvider; this.resources = ResourcePool.forDevice(device); } private Rectangle calculateCellPadding(DefaultGridLook look, Point dpi) { Rectangle cellPadding = new Rectangle(look.cellPadding.x * dpi.x / 72, look.cellPadding.y * dpi.y / 72, look.cellPadding.width * dpi.x / 72, look.cellPadding.height * dpi.y / 72); return cellPadding; } private GridMargins calculateGridMargins(DefaultGridLook look, Point dpi) { final Point cellSpacing = new Point( border.getWidth() + (look.cellSpacing.x == DefaultGridLook.BORDER_OVERLAP ? -border .getOverlap().x : dpi.x * look.cellSpacing.x / 72), border.getHeight(false, false) + (look.cellSpacing.y == DefaultGridLook.BORDER_OVERLAP ? -border .getOverlap().y : dpi.y * look.cellSpacing.y / 72)); final int headerClosedSpacing = border.getHeight(false, false) + (look.headerGap == DefaultGridLook.BORDER_OVERLAP ? -border .getOverlap().y : dpi.y * look.headerGap / 72); final int headerOpenSpacing = border.getHeight(true, false) + (look.headerGap == DefaultGridLook.BORDER_OVERLAP ? dpi.y / 72 : dpi.y * look.headerGap / 72); final int footerClosedSpacing = border.getHeight(false, false) + (look.footerGap == DefaultGridLook.BORDER_OVERLAP ? -border .getOverlap().y : dpi.y * look.footerGap / 72); final int footerOpenSpacing = border.getHeight(false, true) + (look.footerGap == DefaultGridLook.BORDER_OVERLAP ? dpi.y / 72 : dpi.y * look.footerGap / 72); return new DefaultGridMargins(border, cellSpacing, cellPadding, headerClosedSpacing, headerOpenSpacing, footerClosedSpacing, footerOpenSpacing); } public GridMargins getMargins() { return margins; } protected void paintHeaderCell(GC gc, Rectangle bounds, int row, int col, int colspan) { RGB background = headerBackground.getCellBackground(row, col, colspan); paintCell(gc, background, bounds, false, false); } protected void paintBodyCell(GC gc, Rectangle bounds, int row, int col, int colspan, boolean topOpen, boolean bottomOpen) { RGB background = bodyBackground.getCellBackground(row, col, colspan); paintCell(gc, background, bounds, topOpen, bottomOpen); } protected void paintFooterCell(GC gc, Rectangle bounds, int row, int col, int colspan) { RGB background = footerBackground.getCellBackground(row, col, colspan); paintCell(gc, background, bounds, false, false); } private void paintCell(GC gc, RGB background, Rectangle bounds, boolean topOpen, boolean bottomOpen) { // Compute effective cell rectangle int x = bounds.x - border.getLeft() - cellPadding.x; int y = bounds.y - border.getTop(topOpen) - (topOpen ? 0 : cellPadding.y); int width = bounds.width + border.getWidth() + cellPadding.x + cellPadding.width; int height = bounds.height + border.getHeight(topOpen, bottomOpen) + (bottomOpen ? 0 : cellPadding.y + cellPadding.height); // Paint background Color backgroundColor = resources.getColor(background); if (backgroundColor != null) { Color oldBackground = gc.getBackground(); gc.setBackground(backgroundColor); gc.fillRectangle(x, y, width, height); gc.setBackground(oldBackground); } // Paint border border.paint(gc, x, y, width, height, topOpen, bottomOpen); } public void dispose() { border.dispose(); } }swt-paperclips-1.0.4/src/net/sf/paperclips/DefaultGridMargins.java000066400000000000000000000050371160731654500252130ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; class DefaultGridMargins implements GridMargins { private final BorderPainter border; private final Point cellSpacing; private final Rectangle cellPadding; private final int headerClosedSpacing; private final int headerOpenSpacing; private final int footerClosedSpacing; private final int footerOpenSpacing; DefaultGridMargins(BorderPainter border, Point cellSpacing, Rectangle cellPadding, int headerClosedSpacing, int headerOpenSpacing, int footerClosedSpacing, int footerOpenSpacing) { this.border = border; this.cellSpacing = cellSpacing; this.cellPadding = cellPadding; this.headerClosedSpacing = headerClosedSpacing; this.headerOpenSpacing = headerOpenSpacing; this.footerClosedSpacing = footerClosedSpacing; this.footerOpenSpacing = footerOpenSpacing; } public int getLeft() { return border.getLeft() + cellPadding.x; } public int getHorizontalSpacing() { return cellSpacing.x + cellPadding.x + cellPadding.width; } public int getRight() { return border.getRight() + cellPadding.width; } public int getHeaderTop() { return border.getTop(false) + cellPadding.y; } public int getHeaderVerticalSpacing() { return cellSpacing.y + cellPadding.y + cellPadding.height; } public int getBodyTop(boolean headerPresent, boolean open) { return headerPresent ? open ? headerOpenSpacing : headerClosedSpacing + cellPadding.y : open ? border.getTop(true) : border .getTop(false) + cellPadding.y; } public int getBodyVerticalSpacing() { return cellSpacing.y + cellPadding.y + cellPadding.height; } public int getBodyBottom(boolean footerPresent, boolean open) { return footerPresent ? open ? footerOpenSpacing : footerClosedSpacing + cellPadding.height : open ? border.getBottom(true) : border .getBottom(false) + cellPadding.height; } public int getFooterVerticalSpacing() { return cellSpacing.y + cellPadding.y + cellPadding.height; } public int getFooterBottom() { return border.getBottom(false) + cellPadding.height; } }swt-paperclips-1.0.4/src/net/sf/paperclips/DefaultPageNumberFormat.java000066400000000000000000000022301160731654500261730ustar00rootroot00000000000000/* * Copyright (c) 2006-2008 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 net.sf.paperclips; import java.text.MessageFormat; import net.sf.paperclips.internal.Util; /** * The default PageNumberFormat used by PageNumberPrints. *

* This class formats page numbers as "Page x of y". * * @author Matthew Hall */ public final class DefaultPageNumberFormat implements PageNumberFormat { private static MessageFormat messageFormat = new MessageFormat(Messages .getString(Messages.PAGE_X_OF_Y)); public String format(PageNumber pageNumber) { return messageFormat.format(new Object[] { new Integer(pageNumber.getPageNumber() + 1), new Integer(pageNumber.getPageCount()) }); } public boolean equals(Object obj) { return Util.sameClass(this, obj); } public int hashCode() { return 47 * 41; } }swt-paperclips-1.0.4/src/net/sf/paperclips/EmptyPiece.java000066400000000000000000000015631160731654500235440ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; class EmptyPiece implements PrintPiece { private final Point size; EmptyPiece(Point size) { Util.notNull(size); this.size = size; } public Point getSize() { return new Point(size.x, size.y); } public void paint(GC gc, int x, int y) { // Nothing to paint } public void dispose() { // Nothing to dispose } }swt-paperclips-1.0.4/src/net/sf/paperclips/EmptyPrint.java000066400000000000000000000063141160731654500236120ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A Print which displays nothing but takes up space. Useful for putting blank * cells in a GridPrint. * * @author Matthew */ public class EmptyPrint implements Print { final int width; final int height; /** * Constructs an EmptyPrint with size (0, 0). */ public EmptyPrint() { this(0, 0); } /** * Constructs an EmptyPrint with the given size. * * @param width * width of the Print, in points (72pts = 1"). * @param height * height of the Print, in points (72pts = 1"). */ public EmptyPrint(int width, int height) { this.width = checkDimension(width); this.height = checkDimension(height); } /** * Constructs an EmptyPrint with the given size. * * @param size * the size, in points (72pts = 1"). */ public EmptyPrint(Point size) { this(size.x, size.y); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + height; result = prime * result + width; return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; EmptyPrint other = (EmptyPrint) obj; if (height != other.height) return false; if (width != other.width) return false; return true; } /** * Returns the size of the empty space. * * @return the size of the empty space. */ public Point getSize() { return new Point(width, height); } private int checkDimension(int dim) { if (dim < 0) PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "EmptyPrint dimensions must be >= 0"); //$NON-NLS-1$ return dim; } public PrintIterator iterator(Device device, GC gc) { return new EmptyIterator(device, this); } } class EmptyIterator implements PrintIterator { private final Point size; private boolean hasNext = true; EmptyIterator(Device device, EmptyPrint target) { Point dpi = device.getDPI(); this.size = new Point(Math.round(target.width * dpi.x / 72f), Math .round(target.height * dpi.y / 72f)); } EmptyIterator(EmptyIterator that) { this.size = that.size; this.hasNext = that.hasNext; } public boolean hasNext() { return hasNext; } public PrintPiece next(int width, int height) { if (size.x > width || size.y > height) return null; hasNext = false; return new EmptyPiece(size); } public Point minimumSize() { return new Point(size.x, size.y); } public Point preferredSize() { return new Point(size.x, size.y); } public PrintIterator copy() { return new EmptyIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/FactoryPrint.java000066400000000000000000000257601160731654500241310ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import java.io.InputStream; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.Point; /** * An ill-conceived class I wish I could take back. * * @author Matthew * @deprecated This class will be removed in a future release. */ public abstract class FactoryPrint implements Print { private Print print; /** * Default constructor. */ public FactoryPrint() { } /** * Returns the Print created by this factory. * * @return the Print created by this factory. */ public Print getPrint() { if (print == null) print = createPrint(); return print; } /** * Returns a PrintIterator for the Print returned from a call to * createPrint(). */ public PrintIterator iterator(Device device, GC gc) { return getPrint().iterator(device, gc); } /** * Compose and return a Print appropriate for the concrete class. Subclasses * must override this method to create the Print. * * @return a Print appropriate for the concrete class. */ protected abstract Print createPrint(); /** * Converts the argument to a String. This method is used by the TextPrint * factory methods which accept an Object. * * @param obj * the object to convert. * @return This implementation returns the result of obj.toString(), or an * empty string if the argument is null. Override this method to * change this behavior. */ protected String convertToString(Object obj) { return (obj == null) ? "" : obj.toString(); //$NON-NLS-1$ } /** * Returns the default text font. All TextPrints returned from the text(...) * methods are set to this font. This implementation uses * TextPrint.DEFAULT_FONT_DATA as the default text font. Override this * method to change the default text font. * * @return the default text font. */ protected FontData getDefaultTextFont() { return TextPrint.DEFAULT_FONT_DATA; } /** * Returns the default label font. All TextPrints returned from the * label(...) methods are set to this font. This implementation uses * TextPrint.DEFAULT_FONT_DATA as the default label font. Override this * method to change the default label font. * * @return the default label font. */ protected FontData getDefaultLabelFont() { return TextPrint.DEFAULT_FONT_DATA; } /** * Returns the default spacing, in points, for GridPrints. All GridPrints * returned from the grid(String) method(s) have their vertical and * horizontal spacing set to this value. This implementation returns 0 as * the default spacing. Override this method to change the default grid * spacing. * * @return the default spacing, in points, between GridPrint cells. */ protected int getDefaultGridSpacing() { return 2; } /** * Returns a TextPrint whose text represents the given parameter. The * returned object will be set to the default text font. * * @param obj * the object that the returned TextPrint will represent. * @return a TextPrint whose text represents the given parameter. * @see #convertToString(Object) * @see #getDefaultTextFont() */ protected TextPrint text(Object obj) { return text(convertToString(obj)); } /** * Returns a TextPrint whose text represents the given parameter. The * returned object will be set to the default text font. * * @param obj * the object that the returned TextPrint will represent. * @param align * the alignment property for the returned TextPrint. * @return a TextPrint whose text represents the given parameter. * @see #convertToString(Object) * @see #getDefaultTextFont() */ protected TextPrint text(Object obj, int align) { return text(convertToString(obj), align); } /** * Returns a TextPrint with the given text. The returned object will be set * to the default text font. * * @param text * the text property for the returned TextPrint * @return a TextPrint with the given text. * @see #getDefaultTextFont() */ protected TextPrint text(String text) { return text(text, SWT.DEFAULT); } /** * Returns a TextPrint with the given text and alignment. The returned * object will be set to the default text font. * * @param text * the text property for the returned TextPrint * @param align * the alignment property for the returned TextPrint. * @return a TextPrint with the given text and alignment. * @see #convertToString(Object) * @see #getDefaultTextFont() */ protected TextPrint text(String text, int align) { return new TextPrint(text, getDefaultTextFont(), align); } /** * Returns a TextPrint with the given text. The returned object will be set * to the default label font. * * @param text * the text property for the returned TextPrint * @return a TextPrint with the given text. * @see #convertToString(Object) * @see #getDefaultLabelFont() */ protected TextPrint label(String text) { return label(text, SWT.DEFAULT); } /** * Returns a TextPrint with the given text and alignment. The returned * object will be set to the default label font. * * @param text * the text property for the returned TextPrint * @param align * the alignment property for the returned TextPrint. * @return a TextPrint with the given text and alignment. * @see #convertToString(Object) * @see #getDefaultLabelFont() */ protected TextPrint label(String text, int align) { return new TextPrint(text, getDefaultLabelFont(), align); } /** * Returns the default image DPI. All ImagePrints returned from the * image(...) methods are set to this DPI. This implementation uses (300, * 300) as the default DPI. Override this method to change the default image * DPI. * * @return the default image DPI. */ protected Point getDefaultImageDPI() { return new Point(300, 300); } /** * Creates and returns an ImageData using the given filename. All * ImagePrints returned from the image(...) methods are set to this DPI. * This implementation uses the ImageData(String filename) constructor to * generate the ImageData. Override this method to change this behavior. * * @param filename * the filename of the image to load. * @return an ImageData containing the image from the given filename. */ protected ImageData getImageData(String filename) { return new ImageData(filename); } /** * Returns an ImagePrint with the given image, and the default DPI. * * @param filename * the filename of the image to load. * @return an ImagePrint with the given image. * @see #getDefaultImageDPI() */ protected ImagePrint image(String filename) { return image(filename, getDefaultImageDPI()); } /** * Returns an ImagePrint with the given image and DPI. * * @param filename * the filename of the image to load. * @param dpi * the DPI at which the image is to be printed. * @return an ImagePrint with the given image and DPI. */ protected ImagePrint image(String filename, Point dpi) { return new ImagePrint(getImageData(filename), dpi); } /** * Returns an ImagePrint with the given image, and the default DPI. * * @param is * an input stream the image will be loaded from. * @return an ImagePrint with the given image, and the default DPI. */ protected ImagePrint image(InputStream is) { return image(is, getDefaultImageDPI()); } /** * Returns an ImagePrint with the given image and DPI. * * @param is * an InputStream which the image will be loaded from. * @param dpi * the DPI the image will be printed at. * @return an ImagePrint with the given image and DPI. */ protected ImagePrint image(InputStream is, Point dpi) { return new ImagePrint(new ImageData(is), dpi); } /** * Returns an ImagePrint with the given image data, using the default DPI. * * @param imageData * the ImageData of the image to print. * @return an ImagePrint with the given image data, using the default DPI. */ protected ImagePrint image(ImageData imageData) { return image(imageData, getDefaultImageDPI()); } /** * Returns an ImagePrint with the given image data and DPI. * * @param imageData * the ImageData of the image to print. * @param dpi * the DPI the image will be printed at. * @return an ImagePrint with the given image data and DPI. */ protected ImagePrint image(ImageData imageData, Point dpi) { return new ImagePrint(imageData, dpi); } /** * Returns a GridPrint with columns using the given argument. The returned * object will have the horizontal and vertical cell spacing set to the * default spacing. * * @param columns * comma-separated list of column specs. * @return a GridPrint with columns using the given argument. * @see GridColumn#parse(String) * @see #getDefaultGridSpacing() */ protected GridPrint grid(String columns) { return grid(columns, getDefaultGridSpacing()); } /** * Returns a GridPrint with the given columns and spacing. The returned * object will have the horizontal and vertical cell spacing set to the * default spacing. * * @param columns * comma-separated list of column specs. * @param spacing * the spacing, in points, between cells in he GridPrint. * @return a GridPrint with the specified column configuration and spacing. * @see GridColumn#parse(String) */ protected GridPrint grid(String columns, int spacing) { return new GridPrint(columns, new DefaultGridLook(spacing, spacing)); } /** * Constructs and returns a new LayerPrint. * * @return a new LayerPrint. */ protected LayerPrint layer() { return new LayerPrint(); } /** * Constructs and returns an EmptyPrint of size (0, 0). * * @return a new EmptyPrint of size (0, 0). */ protected EmptyPrint empty() { return empty(0, 0); } /** * Constructs and returns an EmptyPrint with the given size. * * @param width * the width, in points. * @param height * the height, in points. * @return a new EmptyPrint with the given size. */ protected EmptyPrint empty(int width, int height) { return new EmptyPrint(width, height); } } swt-paperclips-1.0.4/src/net/sf/paperclips/GapBorder.java000066400000000000000000000103151160731654500233400ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A border which leaves a gap around the target Print. * * @author Matthew Hall */ public class GapBorder implements Border { /** The top gap of a closed border, expressed in points. */ public int top = 0; /** The bottom gap of a closed border, expressed in points. */ public int bottom = 0; /** The left side gap, expressed in points. */ public int left = 0; /** The right side gap, expressed in points. */ public int right = 0; /** The top gap of an open border, expressed in points. */ public int openTop = 0; /** The bottom gap of an open border, expressed in points. */ public int openBottom = 0; /** * Constructs a GapBorder with 0 gap around all sides. */ public GapBorder() { this(0); } /** * Constructs a GapBorder with the given gap around all sides. * * @param gap * the gap, expressed in points. */ public GapBorder(int gap) { setGap(gap); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + bottom; result = prime * result + left; result = prime * result + openBottom; result = prime * result + openTop; result = prime * result + right; result = prime * result + top; return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; GapBorder other = (GapBorder) obj; if (bottom != other.bottom) return false; if (left != other.left) return false; if (openBottom != other.openBottom) return false; if (openTop != other.openTop) return false; if (right != other.right) return false; if (top != other.top) return false; return true; } /** * Sets the left, right, closed top and closed bottom gaps to he argument. * * @param gap * the gap, expressed in points. */ public void setGap(int gap) { top = left = bottom = right = checkGap(gap); } int checkGap(int gap) { if (gap < 0) PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Gap must be >= 0"); //$NON-NLS-1$ return gap; } public BorderPainter createPainter(Device device, GC gc) { return new GapBorderPainter(this, device); } } class GapBorderPainter extends AbstractBorderPainter { final int top; final int openTop; final int bottom; final int openBottom; final int left; final int right; GapBorderPainter(GapBorder target, Device device) { Point dpi = device.getDPI(); this.top = toPixels(target.top, dpi.y); this.bottom = toPixels(target.bottom, dpi.y); this.openTop = toPixels(target.openTop, dpi.y); this.openBottom = toPixels(target.openBottom, dpi.y); this.left = toPixels(target.left, dpi.x); this.right = toPixels(target.right, dpi.x); } GapBorderPainter(GapBorderPainter that) { this.top = that.top; this.bottom = that.bottom; this.left = that.left; this.right = that.right; this.openTop = that.openTop; this.openBottom = that.openBottom; } static int toPixels(int points, int dpi) { return Math.max(0, points) * dpi / 72; } public int getBottom(boolean open) { return open ? openBottom : bottom; } public int getLeft() { return left; } public int getRight() { return right; } public int getTop(boolean open) { return open ? openTop : top; } public Point getOverlap() { return new Point(Math.min(left, right), Math.max(top, bottom)); } public void paint(GC gc, int x, int y, int width, int height, boolean topOpen, boolean bottomOpen) { // Nothing to paint. } public void dispose() { // Nothing to dispose } }swt-paperclips-1.0.4/src/net/sf/paperclips/GridCell.java000066400000000000000000000103031160731654500231550ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.PaperClipsUtil; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * Instances of this class represent a single cell in a GridPrint. * * @author Matthew Hall */ public class GridCell { final int hAlignment; final int vAlignment; final Print target; final int colspan; GridCell(int hAlignment, int vAlignment, Print target, int colspan) { Util.notNull(target); this.hAlignment = checkHorizontalAlignment(hAlignment); this.vAlignment = checkVerticalAlignment(vAlignment); this.target = target; this.colspan = checkColspan(colspan); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + colspan; result = prime * result + hAlignment; result = prime * result + ((target == null) ? 0 : target.hashCode()); result = prime * result + vAlignment; return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; GridCell other = (GridCell) obj; if (colspan != other.colspan) return false; if (hAlignment != other.hAlignment) return false; if (target == null) { if (other.target != null) return false; } else if (!target.equals(other.target)) return false; if (vAlignment != other.vAlignment) return false; return true; } /** * Returns a Point representing the horizontal and vertical alignment * applied to the cell's content. * * @return a Point representing the horizontal and vertical alignment * applied to the cell's content. */ public Point getAlignment() { return new Point(hAlignment, vAlignment); } /** * Returns the horizontal alignment applied to the cell content. * * @return the horizontal alignment applied to the cell content. */ public int getHorizontalAlignment() { return hAlignment; } /** * Returns the vertical alignment applied to the cell content. * * @return the vertical alignment applied to the cell content. */ public int getVerticalAlignment() { return vAlignment; } /** * Returns the content print of the cell. * * @return the content print of the cell. */ public Print getContent() { return target; } /** * Returns the number of columns this cell spans across. * * @return the number of columns this cell spans across. */ public int getColSpan() { return colspan; } private static int checkHorizontalAlignment(int hAlignment) { hAlignment = PaperClipsUtil.firstMatch(hAlignment, new int[] { SWT.DEFAULT, SWT.LEFT, SWT.CENTER, SWT.RIGHT }, 0); if (hAlignment == 0) PaperClips .error( SWT.ERROR_INVALID_ARGUMENT, "Alignment argument must be one of SWT.LEFT, SWT.CENTER, SWT.RIGHT, or SWT.DEFAULT"); //$NON-NLS-1$ return hAlignment; } private static int checkVerticalAlignment(int vAlignment) { vAlignment = PaperClipsUtil.firstMatch(vAlignment, new int[] { SWT.DEFAULT, SWT.TOP, SWT.CENTER, SWT.BOTTOM, SWT.FILL }, 0); if (vAlignment == 0) PaperClips .error( SWT.ERROR_INVALID_ARGUMENT, "Alignment argument must be one of SWT.TOP, SWT.CENTER, SWT.BOTTOM, SWT.DEFAULT, or SWT.FILL"); //$NON-NLS-1$ return vAlignment; } private int checkColspan(int colspan) { if (colspan <= 0 && colspan != GridPrint.REMAINDER) PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "colspan must be a positive number or GridPrint.REMAINDER"); //$NON-NLS-1$ return colspan; } GridCellIterator iterator(Device device, GC gc) { return new GridCellIterator(this, device, gc); } }swt-paperclips-1.0.4/src/net/sf/paperclips/GridCellIterator.java000066400000000000000000000021361160731654500246740ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; class GridCellIterator { final int hAlignment; final int vAlignment; final PrintIterator target; final int colspan; GridCellIterator(GridCell cell, Device device, GC gc) { this.hAlignment = cell.hAlignment; this.vAlignment = cell.vAlignment; this.target = cell.target.iterator(device, gc); this.colspan = cell.colspan; } private GridCellIterator(GridCellIterator that) { this.hAlignment = that.hAlignment; this.vAlignment = that.vAlignment; this.target = that.target.copy(); this.colspan = that.colspan; } GridCellIterator copy() { return new GridCellIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/GridColumn.java000066400000000000000000000251011160731654500235350ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sf.paperclips.internal.PaperClipsUtil; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; /** * Describes the properties of a column in a GridPrint. * * @author Matthew Hall */ public class GridColumn { /** * The default alignment used when alignment is not specified. Value is * SWT.LEFT. */ public static final int DEFAULT_ALIGN = SWT.LEFT; /** * The default size used when size is not specified. Value is SWT.DEFAULT. */ public static final int DEFAULT_SIZE = SWT.DEFAULT; /** * The default weight used when weight is not specified. Value is 0. */ public static final int DEFAULT_WEIGHT = 0; /** * The size property for this GridColumn. Possible values: *

*/ public final int size; /** * The default alignment for Prints in this column. Possible values are * SWT.LEFT, SWT.CENTER, SWT.RIGHT, or SWT.DEFAULT. Note that alignment * affects the placement of PrintPieces within the grid's cell--the * alignment elements of the PrintPiece themselves are not affected. Thus, * in order to achieve the desired effect, a Print having an alignment * property should be set to the same alignment as the grid cell it is added * to. For example, a TextPrint in a right-aligned grid cell should be set * to right alignment as well. *

* Cells that span multiple columns use the alignment of the left-most cell * in the cell span. */ public final int align; /** * The weight of this column. If the available print space is wider than the * grid's preferred width, this field determines how much of that extra * space should be given to this column. A larger weight causes the column * to receive more of the extra width. A value of 0 indicates that the * column should not be given any excess width. */ public final int weight; /** * Constructs a GridColumn. * * @param align * The default alignment for Prints in this column. * @param size * The size this column should be given. * @param weight * The weight this column should be given. */ public GridColumn(int align, int size, int weight) { this.align = checkAlign(align); this.size = checkSize(size); this.weight = checkWeight(weight); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + align; result = prime * result + size; result = prime * result + weight; return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; GridColumn other = (GridColumn) obj; if (align != other.align) return false; if (size != other.size) return false; if (weight != other.weight) return false; return true; } private static int checkAlign(int align) { align = PaperClipsUtil.firstMatch(align, new int[] { SWT.LEFT, SWT.CENTER, SWT.RIGHT, SWT.DEFAULT }, 0); if (align == 0) PaperClips .error( SWT.ERROR_INVALID_ARGUMENT, "Alignment argument must be one of SWT.LEFT, SWT.CENTER, SWT.RIGHT, or SWT.DEFAULT"); //$NON-NLS-1$ if (align == SWT.DEFAULT) return DEFAULT_ALIGN; return align; } private static int checkSize(int size) { if (size != SWT.DEFAULT && size != GridPrint.PREFERRED && size <= 0) PaperClips .error(SWT.ERROR_INVALID_ARGUMENT, "Size argument must be SWT.DEFAULT, GridPrint.PREFERRED, or > 0"); //$NON-NLS-1$ return size; } private static int checkWeight(int grow) { if (grow < 0) PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Weight argument must be >= 0"); //$NON-NLS-1$ return grow; } /** * Parses the given column spec and returns a GridColumn matching that spec. *

* Format: * *

	 *  [align:]size[:grow]
	 *  
	 *  align  = L | LEFT |
	 *           C | CENTER |
	 *           R | RIGHT
	 *  size   = P | PREF | PREFERRED |
	 *           D | DEF | DEFAULT |
	 *           (Positive number)[PT|IN|INCH|CM|MM]
	 *  weight = N | NONE |
	 *           G | GROW | G(#) | GROW(#)
	 * 
* * The default alignment is LEFT. The * * weight argument expresses the weight property: NONE * indicates a weight of 0; GROW indicates a weight of 1; and GROW(3) * indicates a weight of 3. The default weight (if weight is * omitted) is 0. *

* Examples: * *

	 * LEFT:DEFAULT:GROW // left-aligned, default size, weight=1
	 *  R:72PT:N          // light-aligned, 72 points (1") wide, weight=0
	 *  right:72          // identical to previous line
	 *  c:pref:none       // center-aligned, preferred size, weight=0
	 *  p                 // left-aligned (default), preferred size, weight=0
	 *  r:2inch           // right-aligned, 2 inches (50.8mm)
	 *  r:50.8mm          // right-aligned, 50.8 mm (2")
	 * 
* * @param spec * the column spec that will be parsed. * @return a GridColumn matching the column spec. * @see #align * @see #size * @see #weight */ public static GridColumn parse(String spec) { Util.notNull(spec); String[] matches = spec.split("\\s*:\\s*"); //$NON-NLS-1$ if (matches.length == 0) PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Missing column spec"); //$NON-NLS-1$ int align = DEFAULT_ALIGN; int size = DEFAULT_SIZE; int grow = DEFAULT_WEIGHT; if (matches.length == 1) { // One option: must be size size = parseSize(matches[0]); } else if (matches.length == 2) { // Two possible scenarios: // 1. align:size // 2. size:weight if (isAlign(matches[0])) { align = parseAlign(matches[0]); size = parseSize(matches[1]); } else { size = parseSize(matches[0]); grow = parseWeight(matches[1]); } } else if (matches.length == 3) { align = parseAlign(matches[0]); size = parseSize(matches[1]); grow = parseWeight(matches[2]); } return new GridColumn(align, size, grow); } // Alignment patterns private static final Pattern LEFT_ALIGN_PATTERN = Pattern.compile( "^l(eft)?$", //$NON-NLS-1$ Pattern.CASE_INSENSITIVE); private static final Pattern CENTER_ALIGN_PATTERN = Pattern.compile( "^c(enter)?$", //$NON-NLS-1$ Pattern.CASE_INSENSITIVE); private static final Pattern RIGHT_ALIGN_PATTERN = Pattern.compile( "^r(ight)?$", //$NON-NLS-1$ Pattern.CASE_INSENSITIVE); private static final Pattern ANY_ALIGN_PATTERN = Pattern.compile( "^l(eft)?|c(enter)?|r(ight)?$", //$NON-NLS-1$ Pattern.CASE_INSENSITIVE); private static boolean isAlign(String alignmentString) { return ANY_ALIGN_PATTERN.matcher(alignmentString).matches(); } private static int parseAlign(String alignmentString) { if (LEFT_ALIGN_PATTERN.matcher(alignmentString).matches()) return SWT.LEFT; else if (CENTER_ALIGN_PATTERN.matcher(alignmentString).matches()) return SWT.CENTER; else if (RIGHT_ALIGN_PATTERN.matcher(alignmentString).matches()) return SWT.RIGHT; PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Unknown alignment \"" + alignmentString + "\""); //$NON-NLS-1$//$NON-NLS-2$ return 0; // unreachable } // Size patterns. private static final Pattern DEFAULT_SIZE_PATTERN = Pattern.compile( "^d(ef(ault)?)?$", //$NON-NLS-1$ Pattern.CASE_INSENSITIVE); private static final Pattern PREFERRED_SIZE_PATTERN = Pattern.compile( "^p(ref(erred)?)?", //$NON-NLS-1$ Pattern.CASE_INSENSITIVE); private static final Pattern EXPLICIT_SIZE_PATTERN = Pattern.compile( "^(\\d+(\\.\\d+)?)\\s*(pt|in(ch)?|mm|cm)?$", //$NON-NLS-1$ Pattern.CASE_INSENSITIVE); private static int parseSize(String sizeString) { Matcher matcher; if (DEFAULT_SIZE_PATTERN.matcher(sizeString).matches()) return SWT.DEFAULT; else if (PREFERRED_SIZE_PATTERN.matcher(sizeString).matches()) return GridPrint.PREFERRED; else if ((matcher = EXPLICIT_SIZE_PATTERN.matcher(sizeString)) .matches()) { return (int) Math.ceil(convertToPoints(Double.parseDouble(matcher .group(1)), matcher.group(3))); } else { PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Unknown size pattern: \"" + sizeString + "\""); //$NON-NLS-1$ //$NON-NLS-2$ return 0; // unreachable } } private static double convertToPoints(double value, String unit) { if (unit == null || unit.length() == 0 || unit.equalsIgnoreCase("pt")) //$NON-NLS-1$ return value; else if (unit.equalsIgnoreCase("in") || unit.equalsIgnoreCase("inch")) //$NON-NLS-1$ //$NON-NLS-2$ return 72 * value; else if (unit.equalsIgnoreCase("cm")) //$NON-NLS-1$ return 72 * value / 2.54; else if (unit.equalsIgnoreCase("mm")) //$NON-NLS-1$ return 72 * value / 25.4; PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Unknown unit \"" + unit + "\"."); //$NON-NLS-1$ //$NON-NLS-2$ return 0; } private static final Pattern WEIGHTLESS_PATTERN = Pattern.compile( "n(one)?", //$NON-NLS-1$ Pattern.CASE_INSENSITIVE); private static final Pattern WEIGHTED_PATTERN = Pattern.compile( "(g(row)?)(\\((\\d+)\\))?", // yikes //$NON-NLS-1$ Pattern.CASE_INSENSITIVE); private static int parseWeight(String weightString) { Matcher matcher; if (WEIGHTLESS_PATTERN.matcher(weightString).matches()) return 0; else if ((matcher = WEIGHTED_PATTERN.matcher(weightString)).matches()) { String weight = matcher.group(4); return (weight == null) ? 1 : Integer.parseInt(weight); } else { PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Illegal grow pattern: \"" + weightString //$NON-NLS-1$ + "\""); //$NON-NLS-1$ return 0; // unreachable } } GridColumn copy() { return new GridColumn(align, size, weight); } }swt-paperclips-1.0.4/src/net/sf/paperclips/GridIterator.java000066400000000000000000001011601160731654500240710ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import net.sf.paperclips.internal.PaperClipsUtil; import net.sf.paperclips.internal.PrintSizeStrategy; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; class GridIterator implements PrintIterator { final Device device; final Point dpi; final GridColumn[] columns; final int[][] columnGroups; final GridLookPainter look; final GridCellIterator[][] header; final GridCellIterator[][] body; final GridCellIterator[][] footer; final boolean cellClippingEnabled; final int[] minimumColSizes; // PIXELS final int[] preferredColSizes; // PIXELS final Point minimumSize; // PIXELS final Point preferredSize; // PIXELS // This is the cursor! private int row; // Determines whether top edge of cell border is drawn open or closed for // current row. private boolean rowStarted; GridIterator(GridPrint grid, Device device, GC gc) { this.device = device; this.dpi = device.getDPI(); this.columns = (GridColumn[]) grid.columns .toArray(new GridColumn[grid.columns.size()]); this.columnGroups = grid.getColumnGroups(); this.header = createGridCellIterators(grid.header, device, gc); this.body = createGridCellIterators(grid.body, device, gc); this.footer = createGridCellIterators(grid.footer, device, gc); this.cellClippingEnabled = grid.cellClippingEnabled; this.look = grid.getLook().getPainter(device, gc); this.minimumColSizes = computeColumnSizes(PrintSizeStrategy.MINIMUM); this.preferredColSizes = computeColumnSizes(PrintSizeStrategy.PREFERRED); this.minimumSize = computeSize(PrintSizeStrategy.MINIMUM, minimumColSizes); this.preferredSize = computeSize(PrintSizeStrategy.PREFERRED, preferredColSizes); row = 0; rowStarted = false; } private static GridCellIterator[][] createGridCellIterators(List gridCells, Device device, GC gc) { GridCellIterator[][] result = new GridCellIterator[gridCells.size()][]; for (int rowIndex = 0; rowIndex < result.length; rowIndex++) result[rowIndex] = createRowCellIterators((List) gridCells .get(rowIndex), device, gc); return result; } private static GridCellIterator[] createRowCellIterators(List rowCells, Device device, GC gc) { GridCellIterator[] result = new GridCellIterator[rowCells.size()]; for (int cellIndex = 0; cellIndex < rowCells.size(); cellIndex++) result[cellIndex] = ((GridCell) rowCells.get(cellIndex)).iterator( device, gc); return result; } /** Copy constructor (used by copy() only) */ private GridIterator(GridIterator that) { this.device = that.device; this.dpi = that.dpi; this.columns = that.columns; this.columnGroups = that.columnGroups; this.header = that.header; // never directly modified, clone not // necessary this.body = cloneRows(that.body, that.row); // Only need to deep copy // the unconsumed rows. this.footer = that.footer; // never directly modified, clone not // necessary this.cellClippingEnabled = that.cellClippingEnabled; this.look = that.look; this.minimumColSizes = that.minimumColSizes; this.preferredColSizes = that.preferredColSizes; this.minimumSize = that.minimumSize; this.preferredSize = that.preferredSize; this.row = that.row; this.rowStarted = that.rowStarted; } private static GridCellIterator[][] cloneRows(GridCellIterator[][] rows, int firstRow) { GridCellIterator[][] result = (GridCellIterator[][]) rows.clone(); // Cloning the outer array is all that's necessary. The inner arrays // (rows) are cloned every time a row // is laid out, so all we have to do is make sure different // GridIterators have distinct outer arrays. // for ( int i = firstRow; i < result.length; i++ ) // result[i] = cloneRow( result[i] ); return result; } /** * Compute the size of a column, respecting the constraints of the * GridColumn. */ private int computeCellWidth(GridCellIterator entry, GridColumn col, PrintSizeStrategy strategy) { if (col.size == SWT.DEFAULT) return strategy.computeSize(entry.target).x; if (col.size == GridPrint.PREFERRED) return entry.target.preferredSize().x; return Math.round(col.size * device.getDPI().x / 72f); } private static boolean isExplicitSize(GridColumn col) { return col.size > 0; } private void applyColumnGrouping(int[] columnSizes) { for (int groupIndex = 0; groupIndex < columnGroups.length; groupIndex++) { int[] group = columnGroups[groupIndex]; // find max column width in group int maxSize = 0; for (int columnInGroupIndex = 0; columnInGroupIndex < group.length; columnInGroupIndex++) { int col = group[columnInGroupIndex]; maxSize = Math.max(maxSize, columnSizes[col]); } // grow all columns to max column width for (int columnInGroupIndex = 0; columnInGroupIndex < group.length; columnInGroupIndex++) { int col = group[columnInGroupIndex]; columnSizes[col] = maxSize; } } } private boolean isColumnGrouped(int col) { for (int groupIndex = 0; groupIndex < columnGroups.length; groupIndex++) { int[] group = columnGroups[groupIndex]; for (int columnInGroupIndex = 0; columnInGroupIndex < group.length; columnInGroupIndex++) { int groupedColumn = group[columnInGroupIndex]; if (groupedColumn == col) return true; } } return false; } private int[] computeColumnSizes(PrintSizeStrategy strategy) { final int[] result = new int[columns.length]; final GridCellIterator[][] rows = aggregateHeaderBodyAndFooterCells(); calculateExplicitlySizedColumnWidths(result); calculateColumnWidthsForCellsSpanningOneColumn(result, rows, strategy); applyColumnGrouping(result); calculateColumnWidthsForCellsSpanningMultipleColumns(result, rows, strategy); applyColumnGrouping(result); return result; } private GridCellIterator[][] aggregateHeaderBodyAndFooterCells() { GridCellIterator[][] rows = new GridCellIterator[body.length + header.length + footer.length][]; int offset = 0; System.arraycopy(body, 0, rows, offset, body.length); offset += body.length; System.arraycopy(header, 0, rows, offset, header.length); offset += header.length; System.arraycopy(footer, 0, rows, offset, footer.length); return rows; } private void calculateColumnWidthsForCellsSpanningMultipleColumns( final int[] colSizes, final GridCellIterator[][] rows, final PrintSizeStrategy strategy) { int horizontalSpacing = look.getMargins().getHorizontalSpacing(); for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) { GridCellIterator[] row = rows[rowIndex]; int columnIndex = 0; for (int cellIndex = 0; cellIndex < row.length; cellIndex++) { GridCellIterator entry = row[cellIndex]; int colspan = entry.colspan; if (colspan > 1) { int currentWidth = PaperClipsUtil.sum(colSizes, columnIndex, colspan); // Subtract column spacing so the weighted distribution of // extra width stays proportional. int minimumWidth = strategy.computeSize(entry.target).x - horizontalSpacing * (colspan - 1); if (currentWidth < minimumWidth) { int extraWidth = minimumWidth - currentWidth; int[] indices = getExpandableColumnIndices(columnIndex, colspan); int totalWidth = PaperClipsUtil.sumByIndex(colSizes, indices); if (totalWidth == 0) resizeColumnsEqually(colSizes, extraWidth, indices); else resizeColumnsProportionateToCurrentSizes(colSizes, indices, extraWidth, totalWidth); } } columnIndex += colspan; } } } private void resizeColumnsProportionateToCurrentSizes(final int[] colSizes, final int[] columnIndices, int adjustment, int totalWidth) { for (int i = 0; i < columnIndices.length && totalWidth != 0 && adjustment != 0; i++) { int columnIndex = columnIndices[i]; int addedWidth = (int) ((long) adjustment * colSizes[columnIndex] / totalWidth); // Adjust extraWidth and totalCurrentWidth for future iterations. totalWidth -= colSizes[columnIndex]; adjustment -= addedWidth; // NOW we can add the added width. colSizes[columnIndex] += addedWidth; } } private void resizeColumnsEqually(final int[] colSizes, int adjustment, int[] expandableColumns) { int expandableCols = expandableColumns.length; for (int expandableColIndex = 0; expandableColIndex < expandableCols; expandableColIndex++) { int expandableColumn = expandableColumns[expandableColIndex]; int addedWidth = adjustment / expandableCols; colSizes[expandableColumn] = addedWidth; adjustment -= addedWidth; expandableCols--; } } private void calculateColumnWidthsForCellsSpanningOneColumn(int[] colSizes, GridCellIterator[][] rows, PrintSizeStrategy strategy) { for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) { GridCellIterator[] row = rows[rowIndex]; int col = 0; for (int cellIndex = 0; cellIndex < row.length; cellIndex++) { GridCellIterator entry = row[cellIndex]; // ignore explicitly sized cols if (entry.colspan == 1 && !isExplicitSize(columns[col])) { colSizes[col] = Math.max(colSizes[col], computeCellWidth( entry, columns[col], strategy)); } col += entry.colspan; } } } private void calculateExplicitlySizedColumnWidths(int[] colSizes) { for (int col = 0; col < columns.length; col++) if (isExplicitSize(columns[col])) colSizes[col] = Math.round(columns[col].size * dpi.x / 72f); } private int[] getExpandableColumnIndices(int firstColumn, int colspan) { Condition[] conditions = getExpandableColumnConditions(); for (int i = 0; i < conditions.length; i++) { int[] columns = findColumns(firstColumn, colspan, conditions[i]); if (columns != null && columns.length > 0) return columns; } return new int[0]; } private interface Condition { /** * Returns whether the column at the specified index satisfies the * condition. * * @param col * the index of the column to test. * @return whether the column at the specified index satisfies the * condition. */ boolean satisfiedBy(int col); } private Condition[] getExpandableColumnConditions() { return new Condition[] { new Condition() { public boolean satisfiedBy(int col) { // Ungrouped columns with nonzero weight are first choice for // expansion. return !isColumnGrouped(col) && columns[col].weight > 0; } }, new Condition() { public boolean satisfiedBy(int col) { // Grouped columns with nonzero weight are next choice return isColumnGrouped(col) && columns[col].weight > 0; } }, new Condition() { public boolean satisfiedBy(int col) { // Ungrouped columns with GridPrint.PREFERRED size are next // choice. return !isColumnGrouped(col) && columns[col].size == GridPrint.PREFERRED; } }, new Condition() { public boolean satisfiedBy(int col) { // Grouped columns with GridPrint.PREFERRED size are next // choice. return isColumnGrouped(col) && columns[col].size == GridPrint.PREFERRED; } }, new Condition() { public boolean satisfiedBy(int col) { // Ungrouped columns with SWT.DEFAULT size are next choice. return !isColumnGrouped(col) && columns[col].size == SWT.DEFAULT; } }, new Condition() { public boolean satisfiedBy(int col) { // Grouped columns with SWT.DEFAULT size are last choice. return isColumnGrouped(col) && columns[col].size == SWT.DEFAULT; } } }; } private int[] findColumns(Condition condition) { return findColumns(0, columns.length, condition); } private int[] findColumns(int start, int count, Condition condition) { int[] resultTemp = null; int matches = 0; final int end = start + count; for (int index = start; index < end; index++) if (condition.satisfiedBy(index)) { if (resultTemp == null) resultTemp = new int[count]; resultTemp[matches++] = index; } if (matches == 0) return new int[0]; int[] result = new int[matches]; System.arraycopy(resultTemp, 0, result, 0, matches); return result; } private Point computeSize(PrintSizeStrategy strategy, int[] colSizes) { final GridMargins margins = look.getMargins(); int width = computeMarginWidth() + PaperClipsUtil.sum(colSizes); int height = 0; // This algorithm is not strictly accurate but probably good enough. The // header and footer row heights // are being calculated using getMinimumSize() and getPreferredSize(), // which do not necessarily return // the total content height. if (header.length > 0) height += computeHeaderHeight(margins, strategy); else height += Math.max(margins.getBodyTop(false, true), margins .getBodyTop(false, false)); height += computeMaxBodyRowHeight(strategy); if (footer.length > 0) height += computeFooterHeight(strategy, margins); else height += Math.max(margins.getBodyBottom(false, false), margins .getBodyBottom(false, true)); return new Point(width, height); } private int computeHeaderHeight(final GridMargins margins, PrintSizeStrategy strategy) { int headerHeight = margins.getHeaderTop() + margins.getHeaderVerticalSpacing() * (header.length - 1) + Math.max(margins.getBodyTop(true, true), margins.getBodyTop( true, false)); for (int rowIndex = 0; rowIndex < header.length; rowIndex++) { GridCellIterator[] row = header[rowIndex]; int col = 0; int rowHeight = 0; for (int cellIndex = 0; cellIndex < row.length; cellIndex++) { GridCellIterator entry = row[cellIndex]; // Find tallest cell in row. rowHeight = Math.max(rowHeight, strategy .computeSize(entry.target).y); col += entry.colspan; } headerHeight += rowHeight; } return headerHeight; } private int computeMaxBodyRowHeight(PrintSizeStrategy strategy) { int maxBodyRowHeight = 0; for (int rowIndex = 0; rowIndex < body.length; rowIndex++) { GridCellIterator[] row = body[rowIndex]; int col = 0; for (int cellIndex = 0; cellIndex < row.length; cellIndex++) { GridCellIterator entry = row[cellIndex]; // Find the greatest height of all cells' calculated sizes. maxBodyRowHeight = Math.max(maxBodyRowHeight, strategy .computeSize(entry.target).y); col += entry.colspan; } } return maxBodyRowHeight; } private int computeFooterHeight(PrintSizeStrategy strategy, final GridMargins margins) { int footerHeight = Math.max(margins.getBodyBottom(true, false), margins .getBodyBottom(true, true)) + margins.getFooterVerticalSpacing() * (footer.length - 1) + margins.getFooterBottom(); for (int rowIndex = 0; rowIndex < footer.length; rowIndex++) { GridCellIterator[] row = footer[rowIndex]; int col = 0; int rowHeight = 0; for (int cellIndex = 0; cellIndex < row.length; cellIndex++) { GridCellIterator entry = row[cellIndex]; // Find tallest cell in row. rowHeight = Math.max(rowHeight, strategy .computeSize(entry.target).y); col += entry.colspan; } footerHeight += rowHeight; } return footerHeight; } public Point minimumSize() { return new Point(minimumSize.x, minimumSize.y); } public Point preferredSize() { return new Point(preferredSize.x, preferredSize.y); } private Condition[] getShrinkableColumnConditions() { /* * Disabled: new Condition() { public boolean satisfiedBy( int col ) { * // Search first for columns with DEFAULT size. return * columns[col].size == SWT.DEFAULT; } }, */ return new Condition[] { new Condition() { public boolean satisfiedBy(int col) { // Search next for columns with DEFAULT or PREFERRED size. int size = columns[col].size; return size == SWT.DEFAULT || size == GridPrint.PREFERRED; } } }; } private int[] findShrinkableColumns(int extraWidth) { Condition[] conditions = getShrinkableColumnConditions(); for (int i = 0; i < conditions.length; i++) { int[] indices = findColumns(conditions[i]); if (PaperClipsUtil.sumByIndex(minimumColSizes, indices) >= extraWidth) return indices; } return findAllColumns(); } private int[] findAllColumns() { int[] result = new int[columns.length]; for (int i = 0; i < result.length; i++) result[i] = i; return result; } private int[] computeColumnWidths(int width) { int minimumWidth = PaperClipsUtil.sum(minimumColSizes); int preferredWidth = PaperClipsUtil.sum(preferredColSizes); if (width < minimumWidth) return reduceMinimumColumnWidths(minimumWidth - width); else if (width == minimumWidth) return minimumColSizes; else if (width < preferredWidth) return expandMinimumColumnWidths(width - minimumWidth); else if (preferredWidth == width) return preferredColSizes; else // ( preferredWidth < width ) return expandPreferredColumnWidthsByWeight(width - preferredWidth); } private int[] expandPreferredColumnWidthsByWeight(int extraWidth) { int[] weightedCols = findColumns(new Condition() { public boolean satisfiedBy(int col) { return columns[col].weight > 0; } }); int totalWeight = 0; for (int i = 0; i < weightedCols.length; i++) totalWeight += columns[weightedCols[i]].weight; int[] colSizes = PaperClipsUtil.copy(preferredColSizes); for (int weightedColIndex = 0; weightedColIndex < weightedCols.length; weightedColIndex++) { int columnIndex = weightedCols[weightedColIndex]; int columnWeight = columns[columnIndex].weight; int addWidth = (int) ((long) extraWidth * columnWeight / totalWeight); colSizes[columnIndex] += addWidth; // adjust extraWidth and totalWeight - eliminates round-off error extraWidth -= addWidth; totalWeight -= columnWeight; } return colSizes; } private int[] expandMinimumColumnWidths(int expansion) { int difference = PaperClipsUtil.sum(preferredColSizes) - PaperClipsUtil.sum(minimumColSizes); int[] colSizes = PaperClipsUtil.copy(minimumColSizes); for (int i = 0; i < columns.length && difference != 0 && expansion != 0; i++) { int columnDifference = preferredColSizes[i] - minimumColSizes[i]; int change = (int) ((long) expansion * columnDifference / difference); colSizes[i] += change; // adjust extraWidth and difference - eliminates round-off error expansion -= change; difference -= columnDifference; } return colSizes; } private int computeMarginWidth() { GridMargins margins = look.getMargins(); return margins.getLeft() + margins.getRight() + margins.getHorizontalSpacing() * (columns.length - 1); } private int[] reduceMinimumColumnWidths(int reduction) { int[] colSizes = PaperClipsUtil.copy(minimumColSizes); int[] shrinkableCols = findShrinkableColumns(reduction); int shrinkableWidth = PaperClipsUtil.sumByIndex(colSizes, shrinkableCols); for (int i = 0; i < shrinkableCols.length && shrinkableWidth != 0 && reduction != 0; i++) { int col = shrinkableCols[i]; int columnReduction = (int) ((long) colSizes[col] * reduction / shrinkableWidth); shrinkableWidth -= colSizes[col]; colSizes[col] -= columnReduction; reduction -= columnReduction; } return colSizes; } public boolean hasNext() { return row < body.length; } private PrintPiece nextRow(final GridCellIterator[] cells, final int[] columnWidths, final int height, final boolean bottomOpen) { if (bottomOpen && rowContainsNonDefaultVertAlignment(cells)) return null; if (height < 0) return null; final int[] cellWidths = calculateCellWidths(cells, columnWidths); PrintPiece[] pieces = layoutCellsWithNonFillVertAlignment(cells, height, bottomOpen, cellWidths); if (pieces == null) return null; final int rowHeight = calculateRowHeight(pieces, cells); pieces = layoutCellsWithFillVertAlignment(cells, rowHeight, cellWidths, pieces); if (pieces == null) return null; final int[] xOffsets = new int[cells.length]; final int[] yOffsets = new int[cells.length]; applyCellAlignment(cells, cellWidths, pieces, rowHeight, xOffsets, yOffsets); return createRowResult(pieces, xOffsets, yOffsets); } private static boolean rowContainsNonDefaultVertAlignment( final GridCellIterator[] cells) { for (int i = 0; i < cells.length; i++) if (!isDefaultVerticalAlignment(cells[i].vAlignment)) return true; return false; } private static boolean isDefaultVerticalAlignment(int vAlignment) { return vAlignment == SWT.DEFAULT || vAlignment == SWT.TOP; } private int[] calculateCellWidths(final GridCellIterator[] cells, final int[] columnWidths) { final int[] result = new int[cells.length]; final int horzSpacing = look.getMargins().getHorizontalSpacing(); int col = 0; for (int cellIndex = 0; cellIndex < cells.length; cellIndex++) { int colspan = cells[cellIndex].colspan; result[cellIndex] = (colspan - 1) * horzSpacing + PaperClipsUtil.sum(columnWidths, col, colspan); col += colspan; } return result; } private static PrintPiece[] layoutCellsWithNonFillVertAlignment( final GridCellIterator[] cells, final int height, final boolean bottomOpen, final int[] cellWidths) { final PrintPiece[] pieces = new PrintPiece[cells.length]; for (int cellIndex = 0; cellIndex < cells.length; cellIndex++) { final GridCellIterator cell = cells[cellIndex]; final PrintIterator iter = cell.target; final int cellWidth = cellWidths[cellIndex]; if (iter.hasNext() && cell.vAlignment != SWT.FILL) { PrintPiece piece = pieces[cellIndex] = PaperClips.next(iter, cellWidth, height); if ((piece == null) || (iter.hasNext() && !bottomOpen)) { PaperClipsUtil.dispose(piece, pieces); return null; } } } return pieces; } private static int calculateRowHeight(final PrintPiece[] cellPieces, final GridCellIterator[] cells) { int maxHeight = 0; for (int cellIndex = 0; cellIndex < cells.length; cellIndex++) { GridCellIterator cell = cells[cellIndex]; if (cell.vAlignment == SWT.FILL) maxHeight = Math.max(maxHeight, cell.target.minimumSize().y); else if (cellPieces[cellIndex] != null) maxHeight = Math.max(maxHeight, cellPieces[cellIndex].getSize().y); } return maxHeight; } private static PrintPiece[] layoutCellsWithFillVertAlignment( final GridCellIterator[] cells, final int height, final int[] cellWidths, final PrintPiece[] cellPieces) { for (int cellIndex = 0; cellIndex < cells.length; cellIndex++) { GridCellIterator cell = cells[cellIndex]; PrintIterator iter = cell.target; if (cell.vAlignment == SWT.FILL) { PrintPiece piece = cellPieces[cellIndex] = PaperClips.next( iter, cellWidths[cellIndex], height); if (piece == null || iter.hasNext()) { PaperClipsUtil.dispose(piece, cellPieces); return null; } } } return cellPieces; } private void applyCellAlignment(final GridCellIterator[] cells, final int[] cellWidths, final PrintPiece[] pieces, final int rowHeight, final int[] xOffsets, final int[] yOffsets) { final int horzSpacing = look.getMargins().getHorizontalSpacing(); int x = 0; int col = 0; for (int cellIndex = 0; cellIndex < cells.length; cellIndex++) { xOffsets[cellIndex] = x; yOffsets[cellIndex] = 0; GridCellIterator cell = cells[cellIndex]; PrintPiece piece = pieces[cellIndex]; if (piece != null) { Point size = piece.getSize(); int hAlignment = resolveHorzAlignment(cell.hAlignment, columns[col].align); xOffsets[cellIndex] += getHorzAlignmentOffset(hAlignment, size.x, cellWidths[cellIndex]); yOffsets[cellIndex] += getVertAlignmentOffset(cell.vAlignment, size.y, rowHeight); } x += cellWidths[cellIndex] + horzSpacing; col += cell.colspan; } } private static int resolveHorzAlignment(int cellAlignment, int columnAlignment) { return cellAlignment == SWT.DEFAULT ? columnAlignment : cellAlignment; } private static int getHorzAlignmentOffset(int alignment, int pieceWidth, int totalWidth) { if (alignment == SWT.CENTER) return (totalWidth - pieceWidth) / 2; else if (alignment == SWT.RIGHT) return totalWidth - pieceWidth; return 0; } private static int getVertAlignmentOffset(final int alignment, final int pieceHeight, final int cellHeight) { int offset = 0; if (alignment == SWT.CENTER) { offset = (cellHeight - pieceHeight) / 2; } else if (alignment == SWT.BOTTOM) { offset = cellHeight - pieceHeight; } return offset; } private static PrintPiece createRowResult(final PrintPiece[] pieces, final int[] xOffsets, final int[] yOffsets) { List result = new ArrayList(); for (int cellIndex = 0; cellIndex < pieces.length; cellIndex++) if (pieces[cellIndex] != null) result.add(new CompositeEntry(pieces[cellIndex], new Point( xOffsets[cellIndex], yOffsets[cellIndex]))); return new CompositePiece(result); } private static boolean hasNext(GridCellIterator[] cells) { for (int i = 0; i < cells.length; i++) if (cells[i].target.hasNext()) return true; return false; } public PrintPiece next(final int width, int height) { if (!hasNext()) PaperClips.error(SWT.ERROR_UNSPECIFIED, "No more content"); //$NON-NLS-1$ GridMargins margins = look.getMargins(); int[] colSizes = computeColumnWidths(width - computeMarginWidth()); final boolean headerPresent = header.length > 0; final int[] headerHeights = new int[header.length]; final int[][] headerColSpans = new int[header.length][]; PrintPiece headerPiece = null; if (headerPresent) { height -= margins.getHeaderTop(); headerPiece = nextHeaderPiece(colSizes, height, headerHeights, headerColSpans); if (headerPiece == null) return null; height -= headerPiece.getSize().y; } final boolean footerPresent = footer.length > 0; final int[] footerHeights = new int[footer.length]; final int[][] footerColSpans = new int[footer.length][]; PrintPiece footerPiece = null; if (footerPresent) { height -= margins.getFooterBottom(); footerPiece = nextFooterPiece(colSizes, height, footerHeights, footerColSpans); if (footerPiece == null) { PaperClipsUtil.dispose(headerPiece); return null; } height -= footerPiece.getSize().y; } final int firstRow = row; final boolean topOpen = rowStarted; final List bodyRows = new ArrayList(); final List bodyColSpans = new ArrayList(); height -= margins.getBodyTop(headerPresent, topOpen); final PrintPiece bodyPiece = nextBodyPiece(colSizes, height, bodyRows, bodyColSpans, footerPresent); if (bodyPiece == null) return null; final boolean bottomOpen = rowStarted; return createResult(colSizes, headerPiece, headerHeights, headerColSpans, firstRow, topOpen, bodyPiece, PaperClipsUtil .toIntArray(bodyRows), PaperClipsUtil .toIntIntArray(bodyColSpans), bottomOpen, footerPiece, footerHeights, footerColSpans); } private PrintPiece nextHeaderPiece(final int[] colSizes, final int height, final int[] rowHeights, final int[][] colSpans) { return nextHeaderOrFooterPiece(colSizes, height, rowHeights, colSpans, look.getMargins().getHeaderVerticalSpacing(), header); } private PrintPiece nextFooterPiece(final int[] colSizes, final int height, final int[] rowHeights, final int[][] colSpans) { return nextHeaderOrFooterPiece(colSizes, height, rowHeights, colSpans, look.getMargins().getFooterVerticalSpacing(), footer); } private PrintPiece nextHeaderOrFooterPiece(final int[] colSizes, final int height, final int[] rowHeights, final int[][] colSpans, final int rowSpacing, GridCellIterator[][] headerOrFooter) { int y = 0; List entries = new ArrayList(); for (int rowIndex = 0; rowIndex < headerOrFooter.length; rowIndex++) { GridCellIterator[] row = cloneRow(headerOrFooter[rowIndex]); colSpans[rowIndex] = new int[row.length]; for (int cellIndex = 0; cellIndex < row.length; cellIndex++) colSpans[rowIndex][cellIndex] = row[cellIndex].colspan; PrintPiece rowPiece = nextRow(row, colSizes, height - y, false); boolean hasNext = hasNext(row); if (rowPiece == null || hasNext) { PaperClipsUtil.dispose(rowPiece); for (Iterator iter = entries.iterator(); iter.hasNext();) { CompositeEntry entry = (CompositeEntry) iter.next(); entry.dispose(); } return null; } int rowHeight = rowHeights[rowIndex] = rowPiece.getSize().y; entries.add(new CompositeEntry(rowPiece, new Point(0, y))); y += rowHeight + rowSpacing; } return new CompositePiece(entries); } private PrintPiece nextBodyPiece(int[] colSizes, final int height, final List rowHeights, final List colSpans, final boolean footerPresent) { final GridMargins margins = look.getMargins(); final int rowSpacing = margins.getBodyVerticalSpacing(); final int bodyBottomSpacingOpen = margins.getBodyBottom(footerPresent, true); final int bodyBottomSpacingClosed = margins.getBodyBottom( footerPresent, false); int y = 0; List entries = new ArrayList(); while (hasNext()) { GridCellIterator[] thisRow = cloneRow(body[row]); PrintPiece rowPiece = nextRow(thisRow, colSizes, height - y - bodyBottomSpacingClosed, rowStarted); boolean hasNext = hasNext(thisRow); if ((cellClippingEnabled || entries.isEmpty()) && (rowPiece == null || hasNext)) { thisRow = cloneRow(body[row]); rowPiece = nextRow(thisRow, colSizes, height - y - bodyBottomSpacingOpen, true); hasNext = true; } if (rowPiece == null) break; entries.add(new CompositeEntry(rowPiece, new Point(0, y))); body[row] = thisRow; final int[] rowColSpans = new int[thisRow.length]; for (int cellIndex = 0; cellIndex < rowColSpans.length; cellIndex++) rowColSpans[cellIndex] = thisRow[cellIndex].colspan; colSpans.add(rowColSpans); final int rowHeight = rowPiece.getSize().y; rowHeights.add(new Integer(rowHeight)); rowStarted = hasNext; if (hasNext) break; y += rowHeight + rowSpacing; row++; } if (entries.isEmpty()) return null; return new CompositePiece(entries); } private static GridCellIterator[] cloneRow(GridCellIterator[] row) { GridCellIterator[] result = (GridCellIterator[]) row.clone(); for (int i = 0; i < result.length; i++) result[i] = result[i].copy(); return result; } private PrintPiece createResult(final int[] colSizes, final PrintPiece headerPiece, final int[] headerRows, final int[][] headerColSpans, final int firstRow, final boolean topOpen, final PrintPiece bodyPiece, final int[] bodyRows, final int[][] bodyColSpans, final boolean bottomOpen, final PrintPiece footerPiece, final int[] footerRows, final int[][] footerColSpans) { if (bodyPiece == null) { if (headerPiece != null) headerPiece.dispose(); if (footerPiece != null) footerPiece.dispose(); return null; } List sections = new ArrayList(); PrintPiece lookPiece = new GridLookPainterPiece(look, colSizes, headerRows, headerColSpans, firstRow, topOpen, bodyRows, bodyColSpans, bottomOpen, footerRows, footerColSpans); sections.add(new CompositeEntry(lookPiece, new Point(0, 0))); GridMargins margins = look.getMargins(); final int x = margins.getLeft(); int y = 0; if (headerPiece != null) { y = margins.getHeaderTop(); sections.add(new CompositeEntry(headerPiece, new Point(x, y))); y += headerPiece.getSize().y; } y += margins.getBodyTop(headerPiece != null, topOpen); sections.add(new CompositeEntry(bodyPiece, new Point(x, y))); y += bodyPiece.getSize().y + margins.getBodyBottom(footerPiece != null, bottomOpen); if (footerPiece != null) sections.add(new CompositeEntry(footerPiece, new Point(x, y))); return new CompositePiece(sections); } public PrintIterator copy() { return new GridIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/GridLook.java000066400000000000000000000016321160731654500232070ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; /** * A pluggable "look" for a GridPrint. * * @author Matthew Hall */ public interface GridLook { /** * Returns a GridLookPainter for painting the GridLook. * * @param device * the device to paint on. * @param gc * the graphics context for painting. * @return a GridLookPainter for painting the GridLook. */ public GridLookPainter getPainter(Device device, GC gc); }swt-paperclips-1.0.4/src/net/sf/paperclips/GridLookPainter.java000066400000000000000000000067771160731654500245510ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import org.eclipse.swt.graphics.GC; /** * Interface for drawing a GridLook. * * @author Matthew Hall */ public interface GridLookPainter { /** * Returns the grid margins used for the GridLook. * * @return the grid margins used for the GridLook. * @see GridMargins */ public GridMargins getMargins(); /** * Paints the grid look onto the GC. * * @param gc * the graphics context to paint on. * @param x * the x coordinate of the top-left of the grid. * @param y * the y coordinate of the top-left of the grid. * @param columns * the column widths. The left and right margins of each cell are * included in the column widths. * @param headerRows * the header row heights. * @param headerColSpans * a two-dimensional array of cell spans in the header. Each * element in the outer array is a header row. Each element of an * inner array is a cell, where the element value indicates how * many columns the cell spans. * @param firstRowIndex * the zero-based index of the first row displayed on the page. * @param topOpen * whether the top body row should be drawn with the top edge of * the cell border "open." An open top border is a visual * indication that the top row is being continued from the * previous page. * @param bodyRows * the body row heights. * @param bodyColSpans * a two-dimensional array of cell spans in the body. Each * element in the outer array is a body row. Each element of an * inner array is a cell, where the element value indicates how * many columns the cell spans. * @param bottomOpen * whether the bottom body row should be drawn with the bottom * edge of the cell border "open." An open bottom border is a * visual indication that the bottom row will be continued on the * next page. * @param footerRows * the footer row heights. * @param footerColSpans * a two-dimensional array of cell spans in the footer. Each * element in the outer array is a footer row. Each element of an * inner array is a cell, where the element value indicates how * many columns the cell spans. */ public void paint(final GC gc, final int x, final int y, final int[] columns, final int[] headerRows, final int[][] headerColSpans, final int firstRowIndex, final boolean topOpen, final int[] bodyRows, final int[][] bodyColSpans, final boolean bottomOpen, final int[] footerRows, final int[][] footerColSpans); /** * Disposes the system resources allocated by this GridLookPainter. The * dispose method is not a permanent disposal of a GridLookPainter. * It is intended to reclaim system resources, however future calls to * paint(GC,int,int) may require that the resources be allocated again. */ public void dispose(); } swt-paperclips-1.0.4/src/net/sf/paperclips/GridLookPainterPiece.java000066400000000000000000000076561160731654500255140ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.PaperClipsUtil; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; class GridLookPainterPiece implements PrintPiece { final GridLookPainter look; final int[] columns; final int[] headerRows; final int[][] headerColSpans; final int firstRowIndex; final boolean topOpen; final int[] bodyRows; final int[][] bodyColSpans; final boolean bottomOpen; final int[] footerRows; final int[][] footerColSpans; final Point size; GridLookPainterPiece(GridLookPainter look, int[] colSizes, int[] headerRows, int[][] headerColSpans, int firstRowIndex, boolean topOpen, int[] bodyRows, int[][] bodyColSpans, boolean bottomOpen, int[] footerRows, int[][] footerColSpans) { Util.notNull(look); this.look = look; this.columns = PaperClipsUtil.copy(colSizes); this.headerRows = PaperClipsUtil.copy(headerRows); this.headerColSpans = PaperClipsUtil.copy(headerColSpans); this.firstRowIndex = firstRowIndex; this.topOpen = topOpen; this.bodyRows = PaperClipsUtil.copy(bodyRows); this.bodyColSpans = PaperClipsUtil.copy(bodyColSpans); this.bottomOpen = bottomOpen; this.footerRows = PaperClipsUtil.copy(footerRows); this.footerColSpans = PaperClipsUtil.copy(footerColSpans); GridMargins margins = look.getMargins(); Point size = calculateSize(margins, colSizes, headerRows, topOpen, bodyRows, bottomOpen, footerRows); this.size = size; } private static Point calculateSize(GridMargins margins, int[] columns, int[] headerRows, boolean topOpen, int[] bodyRows, boolean bottomOpen, int[] footerRows) { final boolean headerPresent = headerRows.length > 0; final boolean footerPresent = footerRows.length > 0; int width = calculateWidth(margins, columns); int height = calculateBodyHeight(margins, topOpen, bodyRows, bottomOpen, headerPresent, footerPresent); if (headerPresent) height += calculateHeaderHeight(margins, headerRows); if (footerPresent) height += calculateFooterHeight(margins, footerRows); return new Point(width, height); } private static int calculateWidth(GridMargins margins, int[] columns) { return margins.getLeft() + margins.getHorizontalSpacing() * (columns.length - 1) + margins.getRight() + PaperClipsUtil.sum(columns); } private static int calculateBodyHeight(GridMargins margins, boolean topOpen, int[] bodyRows, boolean bottomOpen, final boolean headerPresent, final boolean footerPresent) { return margins.getBodyTop(headerPresent, topOpen) + margins.getBodyVerticalSpacing() * (bodyRows.length - 1) + margins.getBodyBottom(footerPresent, bottomOpen) + PaperClipsUtil.sum(bodyRows); } private static int calculateHeaderHeight(GridMargins margins, int[] headerRows) { return margins.getHeaderTop() + margins.getHeaderVerticalSpacing() * (headerRows.length - 1) + PaperClipsUtil.sum(headerRows); } private static int calculateFooterHeight(GridMargins margins, int[] footerRows) { return margins.getFooterVerticalSpacing() * (footerRows.length - 1) + margins.getFooterBottom() + PaperClipsUtil.sum(footerRows); } public void dispose() { look.dispose(); } public Point getSize() { return new Point(size.x, size.y); } public void paint(GC gc, int x, int y) { look.paint(gc, x, y, columns, headerRows, headerColSpans, firstRowIndex, topOpen, bodyRows, bodyColSpans, bottomOpen, footerRows, footerColSpans); } } swt-paperclips-1.0.4/src/net/sf/paperclips/GridMargins.java000066400000000000000000000074571160731654500237160ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; /** * An interface for informing a GridPrint what cell margins to use for the * GridLook. * * @author Matthew Hall */ public interface GridMargins { /** * Returns the margin, in pixels, at the left side of the grid. * * @return the margin, in pixels, at the left side of the grid. */ public int getLeft(); /** * Returns the horizontal spacing, in pixels, between grid cells. * * @return the horizontal spacing, in pixels, between grid cells. */ public int getHorizontalSpacing(); /** * Returns the margin, in pixels, at the right side of the grid. * * @return the margin, in pixels, at the right side of the grid. */ public int getRight(); /** * Returns the margin, in pixels, at the top of the header cells. If a grid * has no header cells, this value is ignored. * * @return the margin, in pixels, at the top of the header cells. */ public int getHeaderTop(); /** * Returns the vertical spacing, in pixels, between rows in the header. * * @return the vertical spacing, in pixels, between rows in the header. */ public int getHeaderVerticalSpacing(); /** * Returns the margin, in pixels, at the top of the body cells. If a header * is present, this is the spacing, in pixels, between the last header row * and the first body row. If a header is not present, this is the margin, * in pixels, at the top of the grid. * * @param headerPresent * whether a header is present. * @param open * whether the top row of body cells are "open." That is, whether * the top row was started on a previous page and is continuing * on this page. A GridLook may choose to show a visual * indication for cells that were "opened" on previous pages. * @return the margin, in pixels, at the top of the body cells. */ public int getBodyTop(boolean headerPresent, boolean open); /** * Returns the vertical spacing, in pixels, between rows in the body. * * @return the vertical spacing, in pixels, between rows in the body. */ public int getBodyVerticalSpacing(); /** * Returns the margin, in pixels, at the bottom of the body cells. If a * footer is present, this is the spacing, in pixels, between the last body * row and the first footer row. If a header is not present, this is the * margin, in pixels, at the bottom of the grid. * * @param footerPresent * whether a footer is present. * @param open * whether the bottom row of body cells are "open." That is, * whether the bottom row still has more content to display on * the next page. A GridLook may choose to show a visual * indication for cells that will be "continued" on the next * page. * @return the margin, in pixels, at the bottom of the body cells. */ public int getBodyBottom(boolean footerPresent, boolean open); /** * Returns the vertical spacing, in pixels, between rows in the footer. * * @return the vertical spacing, in pixels, between rows in the footer. */ public int getFooterVerticalSpacing(); /** * Returns the margin, in pixels, at the bottom of the footer cells. If a * grid has no footer cells, this value is ignored. * * @return the margin, in pixels, at the bottom of the footer cells. */ public int getFooterBottom(); } swt-paperclips-1.0.4/src/net/sf/paperclips/GridPrint.java000066400000000000000000001307221160731654500234020ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import net.sf.paperclips.internal.PaperClipsUtil; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.RGB; /** * 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. *

* GridPrint uses a column sizing algorithm based on the W3C * recommendation 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 less 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). *

* GridPrint offers three basic methods of specifying column size. *

    *
  1. 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. *
  2. 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. *
  3. Explicit size. The column will be the size you specify, expressed in * points. 72 points = 1". *
* Example: GridPrint grid = new GridPrint("d, p, 72pts"); *

* 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. *

* 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. *

* 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 final DefaultGridLook defaultLook; private GridLook look; /** The columns for this grid. */ final List columns; // List /** 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 > /** 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 > /** 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 > 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 spacing. * * @param columns * a comma-separated list of column specs. * @param spacing * the spacing (in points) between grid cells. * @see #BORDER_OVERLAP * @deprecated use GridPrint(String) instead, then set a DefaultGridLook on * the grid with the desired cell spacing. */ public GridPrint(String columns, int spacing) { this(parseColumns(columns), spacing, spacing); } /** * Constructs a GridPrint with the given columns and spacing. * * @param columns * a comma-separated list of column specs. * @param horizontalSpacing * the horizontal spacing (in points) between grid cells. * @param verticalSpacing * the vertical spacing (in points) between grid cells. * @see GridColumn#parse(String) * @see #BORDER_OVERLAP * @deprecated use GridPrint(String) instead, then set a DefaultGridLook on * the grid with the desired cell spacing. */ public GridPrint(String columns, int horizontalSpacing, int verticalSpacing) { this(parseColumns(columns), horizontalSpacing, verticalSpacing); } /** * 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 = defaultLook = 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); } /** * Constructs a GridPrint with the given columns and spacing. * * @param columns * the columns for the new grid. * @param spacing * the spacing (in points) between grid cells. * @see #BORDER_OVERLAP * @deprecated use GridPrint(GridColumn[]) instead, then set a * DefaultGridLook on the grid with the desired cell spacing. */ public GridPrint(GridColumn[] columns, int spacing) { this(columns, spacing, spacing); } /** * Construct a GridPrint with the given columns and spacing. * * @param columns * the columns for the new grid. * @param horizontalSpacing * the horizontal spacing (in points) between grid cells. * @param verticalSpacing * the vertical spacing (in points) between grid cells. * @see #BORDER_OVERLAP * @deprecated use GridPrint(GridColumn[]) instead, then set a * DefaultGridLook on the grid with the desired cell spacing. */ public GridPrint(GridColumn[] columns, int horizontalSpacing, int verticalSpacing) { this(columns); defaultLook.setCellSpacing(horizontalSpacing, verticalSpacing); } 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 + ((defaultLook == null) ? 0 : defaultLook.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 (defaultLook == null) { if (other.defaultLook != null) return false; } else if (!defaultLook.equals(other.defaultLook)) 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.colspan; // 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 GridCell(cell.hAlignment, cell.vAlignment, cell.target, cell.colspan + 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 GridColumns which are the columns in the * receiver. * * @return an array of GridColumns 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); } /** * 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}. * @deprecated Use {@link #addHeader(int, Print, int)} instead. GridPrint's * addHeader method signatures have been rearranged to coincide * with the GridColumn column spec format: * [alignment]:content:[colspan] */ public void addHeader(Print cell, int colspan, int hAlignment) { headerCol = add(header, headerCol, hAlignment, SWT.DEFAULT, 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; } /** * Returns the background color of the header cells (defaults to the body * background if null). * * @return the background color of the header cells (defaults to the body * background if null). * @deprecated this functionality has been moved to DefaultGridLook. */ public RGB getHeaderBackground() { return defaultLook.getHeaderBackground(); } /** * Sets the background color of the header cells. * * @param headerBackground * the new background color (defaults to the body background if * null). * @deprecated this functionality has been moved to DefaultGridLook. Set a * DefaultGridLook on the grid, then call setHeaderBackground on * the grid look. */ public void setHeaderBackground(RGB headerBackground) { defaultLook.setHeaderBackground(headerBackground); } /** * 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); } /** * Adds the Print to the grid body, 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}. * @deprecated Use {@link #add(int, Print, int)} instead. GridPrint's add * method signatures have been rearranged to coincide with the * GridColumn column spec format: [alignment]:content:[colspan] */ public void add(Print cell, int colspan, int hAlignment) { bodyCol = add(body, bodyCol, hAlignment, SWT.DEFAULT, cell, colspan); } /** * Returns the background color of the body cells (no background color if * null). * * @return the background color of the body cells (no background color if * null). * @deprecated this functionality has been moved to DefaultGridLook. */ public RGB getBodyBackground() { return defaultLook.getBodyBackground(); } /** * Sets the background color of the body cells. * * @param bodyBackground * the new background color (no background is drawn if null). * @deprecated this functionality has been moved to DefaultGridLook. Set a * DefaultGridLook on the grid, then call setBodyBackground on * the grid look. */ public void setBodyBackground(RGB bodyBackground) { defaultLook.setBodyBackground(bodyBackground); } /** * 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); } /** * Adds the Print to the grid footer, 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}. * @deprecated Use {@link #addFooter(int, Print, int)} instead. GridPrint's * addFooter method signatures have been rearranged to coincide * with the GridColumn column spec format: * [alignment]:content:[colspan] */ public void addFooter(Print cell, int colspan, int hAlignment) { footerCol = add(footer, footerCol, hAlignment, SWT.DEFAULT, cell, colspan); } /** * Returns the background color of the footer cells. * * @return the background color of the footer cells (defaults to body * background if null). * @deprecated this functionality has been moved to DefualtGridLook. */ public RGB getFooterBackground() { return defaultLook.getFooterBackground(); } /** * Sets the background color of the footer cells. * * @param footerBackground * the new background color (defaults to body background if * null). * @deprecated this functionality has been moved to DefaultGridLook. Set a * DefaultGridLook on the grid, then call setFooterBackground on * the grid look. */ public void setFooterBackground(RGB footerBackground) { defaultLook.setBodyBackground(footerBackground); } /* * 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 GridCell(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 + " too wide at column " //$NON-NLS-1$ //$NON-NLS-2$ + startColumn + " (" + columns.size() + " columns total)"); //$NON-NLS-1$//$NON-NLS-2$ } 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 + " too wide at column " //$NON-NLS-1$ //$NON-NLS-2$ + startColumn + " (" + columns.size() + " columns total)"); //$NON-NLS-1$//$NON-NLS-2$ } 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. *

* The following statement causes columns 0 and 2 to be the same size, and * columns 1 and 3 to be the same size. * *

	 * grid.setColumnGroups(new int[][] { { 0, 2 }, { 1, 3 } });
	 * 
* *

* The behavior of this property is undefined when a column belongs to more * than one group. *

* Note: Column grouping is enforced before 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 border used around each cell. * * @return the border used around each cell. * @deprecated this functionality has been moved to DefaultGridLook. */ public Border getCellBorder() { return defaultLook.getCellBorder(); } /** * Sets the border around each of the grid's cells to the argument. * * @param border * the new body cell border. * @deprecated this functionality has been moved to DefaultGridLook. Set a * DefaultGridLook on the grid, then call setCellBorder on the * grid look. */ public void setCellBorder(Border border) { defaultLook.setCellBorder(border); } /** * Returns the horizontal spacing between grid cells. * * @return the horizontal spacing between grid cells. * @deprecated this functionality has been moved to DefaultGridLook. */ public int getHorizontalSpacing() { return defaultLook.getCellSpacing().x; } /** * Sets the horizontal spacing between grid cells. * * @param horizontalSpacing * the new horizontal spacing. A value of {@link #BORDER_OVERLAP} * indicates that the borders should be overlapped instead of * spaced. * @deprecated this functionality has been moved to DefaultGridLook. Set a * DefaultGridLook on the grid, then call setCellSpacing(Point) * on the grid look. */ public void setHorizontalSpacing(int horizontalSpacing) { defaultLook.setCellSpacing(horizontalSpacing, defaultLook .getCellSpacing().y); } /** * Returns the vertical spacing between grid cells. * * @return the vertical spacing between grid cells. * @deprecated this functionality has been moved to DefaultGridLook. */ public int getVerticalSpacing() { return defaultLook.getCellSpacing().y; } /** * Sets the vertical spacing between grid cells. * * @param verticalSpacing * the new vertical spacing. A value of {@link #BORDER_OVERLAP} * indicates that the borders should be overlapped instead of * spaced. * @deprecated this functionality has been moved to DefaultGridLook. Set a * DefaultGridLook on the grid, then call setCellSpacing(Point) * on the grid look. */ public void setVerticalSpacing(int verticalSpacing) { defaultLook.setCellSpacing(defaultLook.getCellSpacing().x, verticalSpacing); } /** * 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); } } swt-paperclips-1.0.4/src/net/sf/paperclips/ImagePrint.java000066400000000000000000000143021160731654500235320ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.SWTUtil; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.Point; /** * A Print for displaying images. * * @author Matthew Hall */ public class ImagePrint implements Print { ImageData imageData; Point dpi; Point size; /** * Constructs an ImagePrint with the given imageData, initialized at 72dpi. * * @param imageData * the image to be displayed. */ public ImagePrint(ImageData imageData) { this(imageData, new Point(72, 72)); } /** * Constructs an ImagePrint with the given imageData and dpi. * * @param imageData * the image to be displayed. * @param dpi * the DPI that the image will be displayed at. */ public ImagePrint(ImageData imageData, Point dpi) { Util.notNull(imageData, dpi); this.imageData = imageData; setDPI(dpi); } /** * Returns the ImageData of the image being printed. * * @return the ImageData of the image being printed. */ public ImageData getImageData() { return imageData; } public boolean equals(Object obj) { if (obj == this) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ImagePrint that = (ImagePrint) obj; return Util.equal(this.dpi, that.dpi) && Util.equal(this.size, that.size) && SWTUtil.equal(this.imageData, that.imageData); } public int hashCode() { int prime = 31; int result = 1; result = prime * result + dpi.hashCode(); result = prime * result + size.hashCode(); result = prime * result + SWTUtil.hashCode(imageData); return result; } /** * Sets the ImagePrint to render the image at the given size, in points. 72 * points = 1". * * @param size * the explicit size, in points, that the image be printed at. */ public void setSize(Point size) { // The DPI is rounded up, so that the specified width and height will // not be exceeded. Util.notNull(size); dpi = new Point((int) Math.ceil(imageData.width * 72d / size.x), (int) Math.ceil(imageData.height * 72d / size.y)); this.size = size; } /** * Sets the ImagePrint to render the image at the given size, in points. 72 * points = 1". * * @param width * the explicit width, in points, that the image will be printed * at. * @param height * the explicit height, in points, that the image will be printed * at. */ public void setSize(int width, int height) { setSize(new Point(width, height)); } /** * Returns the size that the image will be rendered at, in points. 72 points * = 1". * * @return the size of the image, in points. */ public Point getSize() { return size; } /** * Sets the ImagePrint to render the image at the DPI of the argument. * * @param dpi * the DPI of the image. */ public void setDPI(Point dpi) { Util.notNull(dpi); this.dpi = dpi; size = new Point((int) Math.ceil(imageData.width * 72d / dpi.x), (int) Math.ceil(imageData.height * 72d / dpi.y)); } /** * Sets the ImagePrint to render the image at the given DPI. * * @param dpiX * the horizontal DPI the image will be rendered at. * @param dpiY * the vertical DPI the image will be rendered at. */ public void setDPI(int dpiX, int dpiY) { setDPI(new Point(dpiX, dpiY)); } /** * Returns the DPI that this image will be rendered at. * * @return the DPI the image will be rendered at. */ public Point getDPI() { return dpi; } public PrintIterator iterator(Device device, GC gc) { return new ImageIterator(this, device); } } class ImageIterator implements PrintIterator { final Device device; final ImageData imageData; final Point size; boolean hasNext; ImageIterator(ImagePrint print, Device device) { Util.notNull(print, device); this.device = device; this.imageData = print.imageData; Point dpi = device.getDPI(); this.size = new Point(print.size.x * dpi.x / 72, print.size.y * dpi.y / 72); this.hasNext = true; } ImageIterator(ImageIterator that) { this.device = that.device; this.imageData = that.imageData; this.size = that.size; this.hasNext = that.hasNext; } public boolean hasNext() { return hasNext; } public PrintPiece next(int width, int height) { if (!hasNext()) PaperClips.error("No more content."); //$NON-NLS-1$ if (width < size.x || height < size.y) return null; hasNext = false; return new ImagePiece(device, imageData, size); } public Point minimumSize() { return new Point(size.x, size.y); } public Point preferredSize() { return new Point(size.x, size.y); } public PrintIterator copy() { return hasNext ? new ImageIterator(this) : this; } } class ImagePiece implements PrintPiece { private final Device device; private final ImageData imageData; private final Point size; private Image image; ImagePiece(Device device, ImageData imageData, Point size) { Util.notNull(device, imageData, size); this.device = device; this.imageData = imageData; this.size = size; } public Point getSize() { return new Point(size.x, size.y); } private Image getImage() { if (image == null) image = new Image(device, imageData); return image; } public void paint(GC gc, int x, int y) { gc.drawImage(getImage(), 0, 0, imageData.width, imageData.height, x, y, size.x, size.y); } public void dispose() { if (image != null) { image.dispose(); image = null; } } }swt-paperclips-1.0.4/src/net/sf/paperclips/LayerEntry.java000066400000000000000000000044261160731654500235770ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.PaperClipsUtil; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; /** * Instances in this class represent an entry in a LayerPrint. * * @author Matthew Hall */ public class LayerEntry { final Print target; final int align; LayerEntry(Print target, int align) { Util.notNull(target); this.target = target; this.align = checkAlign(align); } LayerEntry(LayerEntry that) { this.target = that.target; this.align = that.align; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + align; result = prime * result + ((target == null) ? 0 : target.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; LayerEntry other = (LayerEntry) obj; if (align != other.align) return false; if (target == null) { if (other.target != null) return false; } else if (!target.equals(other.target)) return false; return true; } /** * Returns the target print of this entry. * * @return the target print of this entry. */ public Print getTarget() { return target; } /** * Returns the horizontal alignment applied to the target. * * @return the horizontal alignment applied to the target. */ public int getHorizontalAlignment() { return align; } private static int checkAlign(int align) { return PaperClipsUtil.firstMatch(align, new int[] { SWT.LEFT, SWT.CENTER, SWT.RIGHT }, SWT.LEFT); } LayerEntry copy() { return new LayerEntry(this); } LayerEntryIterator iterator(Device device, GC gc) { return new LayerEntryIterator(this, device, gc); } }swt-paperclips-1.0.4/src/net/sf/paperclips/LayerPrint.java000066400000000000000000000136631160731654500235750ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import net.sf.paperclips.internal.PrintSizeStrategy; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A Print which displays its child Prints on top each other. * * @author Matthew Hall */ public class LayerPrint implements Print { /** * Constant for the default alignment of child Prints. Value is SWT.LEFT. */ public static final int DEFAULT_ALIGN = SWT.LEFT; // List final List entries = new ArrayList(); /** * Constructs a new LayerPrint. */ public LayerPrint() { } /** * Adds the given Print to this LayerPrint using the default alignment. * * @param print * the Print to add. * @see #DEFAULT_ALIGN */ public void add(Print print) { entries.add(new LayerEntry(print, DEFAULT_ALIGN)); } /** * Adds the given Print to this LayerPrint using the specified alignment. * * @param print * the Print to add. * @param align * the alignment for the Print. May be one of SWT.LEFT, * SWT.CENTER, or SWT.RIGHT. */ public void add(Print print, int align) { entries.add(new LayerEntry(print, align)); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((entries == null) ? 0 : entries.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; LayerPrint other = (LayerPrint) obj; if (entries == null) { if (other.entries != null) return false; } else if (!entries.equals(other.entries)) return false; return true; } /** * Returns an array of entries in this LayerPrint. * * @return an array of entries in this LayerPrint. */ public LayerEntry[] getEntries() { return (LayerEntry[]) entries.toArray(new LayerEntry[entries.size()]); } public PrintIterator iterator(Device device, GC gc) { return new LayerIterator(this, device, gc); } } class LayerEntryIterator { final PrintIterator target; final int alignment; LayerEntryIterator(LayerEntry entry, Device device, GC gc) { this.target = entry.target.iterator(device, gc); this.alignment = entry.getHorizontalAlignment(); } LayerEntryIterator(LayerEntryIterator that) { this.target = that.target.copy(); this.alignment = that.alignment; } LayerEntryIterator copy() { return new LayerEntryIterator(this); } } class LayerIterator implements PrintIterator { LayerEntryIterator[] entries; LayerIterator(LayerPrint print, Device device, GC gc) { entries = new LayerEntryIterator[print.entries.size()]; LayerEntry[] e = print.getEntries(); for (int i = 0; i < entries.length; i++) { entries[i] = e[i].iterator(device, gc); } } LayerIterator(LayerIterator that) { this.entries = (LayerEntryIterator[]) that.entries.clone(); for (int i = 0; i < entries.length; i++) if (entries[i].target.hasNext()) entries[i] = entries[i].copy(); } public boolean hasNext() { for (int i = 0; i < entries.length; i++) if (entries[i].target.hasNext()) return true; return false; } public PrintPiece next(int width, int height) { if (!hasNext()) PaperClips.error("No more content"); //$NON-NLS-1$ PrintPiece[] pieces = nextPieces(width, height); if (pieces == null) return null; CompositeEntry[] entries = new CompositeEntry[pieces.length]; for (int i = 0; i < entries.length; i++) { PrintPiece piece = pieces[i]; int offset = getHorzAlignmentOffset(this.entries[i].alignment, piece.getSize().x, width); entries[i] = new CompositeEntry(piece, new Point(offset, 0)); } return new CompositePiece(entries); } private PrintPiece[] nextPieces(int width, int height) { LayerEntryIterator[] entries = (LayerEntryIterator[]) this.entries .clone(); List pieces = new ArrayList(); for (int i = 0; i < entries.length; i++) { LayerEntryIterator entry = entries[i]; if (entry.target.hasNext()) { PrintPiece piece = PaperClips.next(entry.target, width, height); if (piece == null) { for (Iterator iter = pieces.iterator(); iter.hasNext();) ((PrintPiece) iter.next()).dispose(); return null; } pieces.add(piece); } } // Replace instance entries with the entries that were just consumed. this.entries = entries; return (PrintPiece[]) pieces.toArray(new PrintPiece[pieces.size()]); } private int getHorzAlignmentOffset(int alignment, int pieceWidth, int totalWidth) { int offset = 0; switch (alignment) { case SWT.CENTER: offset = (totalWidth - pieceWidth) / 2; break; case SWT.RIGHT: offset = totalWidth - pieceWidth; break; } return offset; } Point computeSize(PrintSizeStrategy strategy) { Point size = new Point(0, 0); for (int i = 0; i < entries.length; i++) { LayerEntryIterator entry = entries[i]; Point entrySize = strategy.computeSize(entry.target); size.x = Math.max(size.x, entrySize.x); size.y = Math.max(size.y, entrySize.y); } return size; } public Point minimumSize() { return computeSize(PrintSizeStrategy.MINIMUM); } public Point preferredSize() { return computeSize(PrintSizeStrategy.PREFERRED); } public PrintIterator copy() { return new LayerIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/LineBorder.java000066400000000000000000000121221160731654500235160ustar00rootroot00000000000000/* * 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 net.sf.paperclips; 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; /** * A border that draws a rectangle around a print. * * @author Matthew Hall */ public class LineBorder implements Border { RGB rgb; int lineWidth = 1; // in points int gapSize = 5; // in points /** * Constructs a LineBorder with a black border and 5-pt insets. (72 pts = * 1") */ public LineBorder() { this(new RGB(0, 0, 0)); // black } /** * Constructs a LineBorder with 5-pt insets. (72 pts = 1") * * @param rgb * the color to use for the border. */ public LineBorder(RGB rgb) { setRGB(rgb); } 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.gapSize) return false; if (lineWidth != other.lineWidth) return false; if (rgb == null) { if (other.rgb != null) return false; } else if (!rgb.equals(other.rgb)) 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); } } class LineBorderPainter extends AbstractBorderPainter { private final Device device; private final RGB rgb; private final Point lineWidth; private final Point borderWidth; LineBorderPainter(LineBorder border, Device device, GC gc) { Util.notNull(border, device, gc); this.rgb = border.rgb; this.device = device; 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 gc.fillRectangle(x, y, lineWidth.x, height); gc.fillRectangle(x + width - lineWidth.x, y, lineWidth.x, height); // Top & bottom 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 } swt-paperclips-1.0.4/src/net/sf/paperclips/LineBreakPrint.java000066400000000000000000000061041160731654500243450ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.ResourcePool; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A class for adding line breaks corresponding to a particular font size. * Currently this class is used internally by StyledTextPrint to implement the * newline() feature. * * @author Matthew Hall */ public class LineBreakPrint implements Print { final FontData font; /** * Constructs a new LineBreakPrint on the given font. * * @param font * the font which determines the height of the line break. */ public LineBreakPrint(FontData font) { Util.notNull(font); this.font = font; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((font == null) ? 0 : font.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; LineBreakPrint other = (LineBreakPrint) obj; if (font == null) { if (other.font != null) return false; } else if (!font.equals(other.font)) return false; return true; } public PrintIterator iterator(Device device, GC gc) { return new LineBreakIterator(this, device, gc); } } class LineBreakIterator implements PrintIterator { private static final int MIN_HEIGHT = 0; private static final int MIN_WIDTH = 1; private final int lineHeight; private boolean hasNext = true; LineBreakIterator(LineBreakPrint print, Device device, GC gc) { this(calculateLineHeight(print, device, gc)); } private LineBreakIterator(int lineHeight) { this.lineHeight = lineHeight; } private static int calculateLineHeight(LineBreakPrint print, Device device, GC gc) { Font oldFont = gc.getFont(); gc.setFont(ResourcePool.forDevice(device).getFont(print.font)); int result = gc.getFontMetrics().getHeight(); gc.setFont(oldFont); return result; } public Point minimumSize() { return new Point(MIN_WIDTH, MIN_HEIGHT); } public Point preferredSize() { return new Point(MIN_WIDTH, lineHeight); } public boolean hasNext() { return hasNext; } public PrintPiece next(int width, int height) { if (width < MIN_WIDTH || height < MIN_HEIGHT) return null; hasNext = false; return new EmptyPiece(new Point(width, Math.min(height, lineHeight))); } public PrintIterator copy() { return hasNext ? new LineBreakIterator(lineHeight) : this; } }swt-paperclips-1.0.4/src/net/sf/paperclips/LinePrint.java000066400000000000000000000141651160731654500234060ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.ResourcePool; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; 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; /** * A Print for drawing horizontal and vertical lines. *

* LinePrints are either horizontally or vertically greedy, according to the * orientation of the line. Greedy prints take up all the available space on the * page. * * @author Matthew Hall */ public class LinePrint implements Print { final int orientation; double thickness; RGB rgb = new RGB(0, 0, 0); /** * Constructs a horizontal LinePrint. */ public LinePrint() { this(SWT.HORIZONTAL); } /** * Constructs a LinePrint with the given orientation and 1-point thickness. * * @param orientation * one of SWT#HORIZONTAL or SWT#VERTICAL. */ public LinePrint(int orientation) { this(orientation, 1.0); } /** * Constructs a LinePrint with the given orientation and thickness. * * @param orientation * one of SWT#HORIZONTAL or SWT#VERTICAL. * @param thickness * the line thickness, expressed in points. */ public LinePrint(int orientation, double thickness) { this.orientation = checkOrientation(orientation); this.thickness = checkThickness(thickness); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + orientation; result = prime * result + ((rgb == null) ? 0 : rgb.hashCode()); long temp; temp = Double.doubleToLongBits(thickness); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; LinePrint other = (LinePrint) obj; if (orientation != other.orientation) return false; if (rgb == null) { if (other.rgb != null) return false; } else if (!rgb.equals(other.rgb)) return false; if (Double.doubleToLongBits(thickness) != Double .doubleToLongBits(other.thickness)) return false; return true; } /** * Returns the line orientation (one of {@link SWT#HORIZONTAL} or * {@link SWT#VERTICAL}). * * @return the line orientation. */ public int getOrientation() { return orientation; } private int checkOrientation(int orientation) { if ((orientation & SWT.HORIZONTAL) == SWT.HORIZONTAL) return SWT.HORIZONTAL; else if ((orientation & SWT.VERTICAL) == SWT.VERTICAL) return SWT.VERTICAL; else return SWT.HORIZONTAL; } private double checkThickness(double thickness) { if (thickness < 0) return 0; return thickness; } /** * Returns the line thickness, in points. 72 points = 1". * * @return the line thickness, in points. */ public double getThickness() { return thickness; } /** * Sets the line thickness, in points. 72 points = 1". * * @param thickness * the line thickness, in points. */ public void setThickness(double thickness) { this.thickness = thickness; } /** * Sets the line color to the argument. * * @param foreground * the new line color. */ public void setRGB(RGB foreground) { Util.notNull(foreground); this.rgb = foreground; } /** * Returns the line color. * * @return the line color. */ public RGB getRGB() { return rgb; } public PrintIterator iterator(Device device, GC gc) { return new LineIterator(this, device, gc); } } class LineIterator extends AbstractIterator { final int orientation; final Point thickness; final RGB rgb; private boolean hasNext = true; LineIterator(LinePrint print, Device device, GC gc) { super(device, gc); this.orientation = print.orientation; this.rgb = print.rgb; Point dpi = device.getDPI(); // (convert from points to pixels on device) this.thickness = new Point(Math.max(1, (int) Math.round(print.thickness * dpi.x / 72)), Math.max(1, (int) Math.round(print.thickness * dpi.y / 72))); } LineIterator(LineIterator that) { super(that); this.orientation = that.orientation; this.rgb = that.rgb; this.hasNext = that.hasNext; this.thickness = that.thickness; } public boolean hasNext() { return hasNext; } Point getSize(int width, int height) { return orientation == SWT.VERTICAL ? new Point(thickness.x, height) : new Point(width, thickness.y); } public PrintPiece next(int width, int height) { if (!hasNext()) PaperClips.error("No more content"); //$NON-NLS-1$ // Make sure the line fits :) Point size = getSize(width, height); if (size.x > width || size.y > height) return null; PrintPiece result = new LinePiece(this, size); hasNext = false; return result; } public Point minimumSize() { return new Point(thickness.x, thickness.y); } public Point preferredSize() { return new Point(thickness.x, thickness.y); } public PrintIterator copy() { return new LineIterator(this); } } class LinePiece extends AbstractPiece { private final RGB rgb; LinePiece(LineIterator iter, Point size) { super(iter, size); this.rgb = iter.rgb; } public void paint(GC gc, int x, int y) { Color oldBackground = gc.getBackground(); Point size = getSize(); try { gc.setBackground(ResourcePool.forDevice(device).getColor(rgb)); gc.fillRectangle(x, y, size.x, size.y); } finally { gc.setBackground(oldBackground); } } public void dispose() { } // Shared resources, nothing to dispose }swt-paperclips-1.0.4/src/net/sf/paperclips/Margins.java000066400000000000000000000044601160731654500230770ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; /** * Instances of this class represent the page margins to follow when processing * a print job. * * @author Matthew Hall */ public class Margins { /** The top margin. */ public int top; /** The left margin. */ public int left; /** The right margin. */ public int right; /** The bottom margin. */ public int bottom; /** * Constructs a Margins with all sides set to 1" margins. */ public Margins() { this(72); } /** * Constructs a Margins with all sides set to the argument. * * @param margins * the page margins, expressed in points. 72 points = 1". */ public Margins(int margins) { top = left = right = bottom = margins; } /** * Returns a Margins that is the result of rotating this Margins * counter-clockwise 90 degrees. A job which is rotated 90 degrees (e.g. for * landscape printing) needs to have its margins rotated to match. This is a * convenience method for that purpose. * * @return a Margins that is the result of rotating this Margins * counter-clockwise 90 degrees. */ public Margins rotate() { Margins result = new Margins(); result.top = right; result.left = top; result.right = bottom; result.bottom = left; return result; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + bottom; result = prime * result + left; result = prime * result + right; result = prime * result + top; return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Margins other = (Margins) obj; if (bottom != other.bottom) return false; if (left != other.left) return false; if (right != other.right) return false; if (top != other.top) return false; return true; } }swt-paperclips-1.0.4/src/net/sf/paperclips/Messages.java000066400000000000000000000026051160731654500232450ustar00rootroot00000000000000/* * Copyright (c) 2008 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 net.sf.paperclips; import java.util.MissingResourceException; import java.util.ResourceBundle; /** * Convenience methods for retrieving locale-specific messages. * * @author Matthew Hall * @since 1.0.4 */ public class Messages { private static final String BUNDLE_NAME = "net.sf.paperclips.messages"; //$NON-NLS-1$ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle .getBundle(BUNDLE_NAME); /** * Key for "Page {x} of {y}" used by DefaultPageNumberFormat. */ public static final String PAGE_X_OF_Y = "PAGE_X_OF_Y"; //$NON-NLS-1$ private Messages() { } /** * Returns the locale-specific messages for the given key. * * @param key * the key identifying the string to be retrieved. * @return the locale-specific messages for the given key. */ public static String getString(String key) { try { return RESOURCE_BUNDLE.getString(key); } catch (MissingResourceException e) { return '!' + key + '!'; } } } swt-paperclips-1.0.4/src/net/sf/paperclips/NoBreakPrint.java000066400000000000000000000061101160731654500240270ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A print wrapper which prevents its target from being broken into multiple * pieces when printed. If there isn't enough room to print the target in one * piece on the current page (or column, if it's inside a ColumnPrint), it will * be printed on the next page (or column). * *

* Care must be taken when using this class to avoid unprintable documents. If * the target of a NoBreakPrint does not fit in the available space on the print * device, the entire document will fail to print. * * @author Matthew Hall */ public class NoBreakPrint implements Print { private final Print target; /** * Constructs a NoBreakPrint with the given target. * * @param target * the print to */ public NoBreakPrint(Print target) { Util.notNull(target); this.target = target; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((target == null) ? 0 : target.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; NoBreakPrint other = (NoBreakPrint) obj; if (target == null) { if (other.target != null) return false; } else if (!target.equals(other.target)) return false; return true; } /** * Returns the print which will not be broken across pages. * * @return the print which will not be broken across pages. */ public Print getTarget() { return target; } public PrintIterator iterator(Device device, GC gc) { return new NoBreakIterator(target.iterator(device, gc)); } } class NoBreakIterator implements PrintIterator { private PrintIterator target; NoBreakIterator(PrintIterator target) { Util.notNull(target); this.target = target; } public PrintIterator copy() { return new NoBreakIterator(target.copy()); } public boolean hasNext() { return target.hasNext(); } public Point minimumSize() { return target.minimumSize(); } public Point preferredSize() { return target.preferredSize(); } public PrintPiece next(int width, int height) { // Use a test iterator so we preserve the original iterator PrintIterator iter = target.copy(); PrintPiece result = PaperClips.next(iter, width, height); if (result == null) return result; if (iter.hasNext()) // Failed to layout the whole target in one piece return null; this.target = iter; return result; } }swt-paperclips-1.0.4/src/net/sf/paperclips/NullPrintPiece.java000066400000000000000000000012571160731654500243750ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; final class NullPrintPiece implements PrintPiece { public Point getSize() { return new Point(0, 0); } public void paint(GC gc, int x, int y) { } public void dispose() { } }swt-paperclips-1.0.4/src/net/sf/paperclips/PageDecoration.java000066400000000000000000000022051160731654500243560ustar00rootroot00000000000000/* * 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 net.sf.paperclips; /** * An interface for creating page decorations. Instances of this interface are * used as headers and footers in conjunction with the PagePrint class. * * @see PagePrint * @see SimplePageDecoration * @see PageNumberPageDecoration * @author Matthew Hall */ public interface PageDecoration { /** * Returns a decorator Print for the page with the given page number, or * null if no decoration is provided for the given page. * * @param pageNumber * the page number of the page being decorated. * @return a decorator Print for the page with the given page number, or * null if no decoration is provided for the given page. */ public Print createPrint(PageNumber pageNumber); } swt-paperclips-1.0.4/src/net/sf/paperclips/PageEnumeration.java000066400000000000000000000074421160731654500245650ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.printing.Printer; /** * An enumeration of pages for given print job on the given printer device. Each * element in the enumeration has already had the page orientation and page * margins applied. Therefore, when calling the paint(GC, int, int) method on * each page, the printer's trim should be provided as the x and y arguments. In * other words, the trim is taken as a minimum margin while applying calculating * margins, but the position where the page's content is drawn is determined * solely by the margin, and is not offset by the trim. This behavior is helpful * for screen display, and is already compensated for in the * {@link PaperClips#print(PrintJob, Printer) } method. * * @see PaperClips#getPages(PrintJob, Printer) * @author Matthew Hall */ public class PageEnumeration { private PrintIterator document; private Rectangle marginBounds; private Rectangle paperBounds; private boolean hasNext; PageEnumeration(PrintJob job, Printer printer, GC gc) { // Rotate the document (and margins with it) depending on print job // orientation. job = applyOrientation(job, printer); Margins margins = job.getMargins(); marginBounds = PaperClips.getMarginBounds(margins, printer); paperBounds = PaperClips.getPaperBounds(printer); document = job.getDocument().iterator(printer, gc); hasNext = document.hasNext(); } /** * Returns whether any pages remain. * * @return whether any pages remain. */ public boolean hasNext() { return hasNext; } /** * Returns the next page. * * @return the next page. */ public PrintPiece nextPage() { if (!hasNext) return null; PrintPiece page = PaperClips.next(document, marginBounds.width, marginBounds.height); hasNext = notNull(page) && notDebugPiece(page) && document.hasNext(); PrintPiece result = page == null ? null : createPagePiece(page); if (!hasNext) { document = null; marginBounds = null; paperBounds = null; } return result; } private PrintPiece createPagePiece(PrintPiece page) { Point offset = new Point(marginBounds.x - paperBounds.x, marginBounds.y - paperBounds.y); CompositeEntry entry = new CompositeEntry(page, offset); Point size = new Point(paperBounds.width, paperBounds.height); return new CompositePiece(new CompositeEntry[] { entry }, size); } private static boolean notNull(PrintPiece page) { return page != null; } private static boolean notDebugPiece(PrintPiece page) { return !(PaperClips.debug && page instanceof NullPrintPiece); } private static PrintJob applyOrientation(PrintJob printJob, Printer printer) { int orientation = printJob.getOrientation(); Rectangle paperBounds = PaperClips.getPaperBounds(printer); if (((orientation == PaperClips.ORIENTATION_LANDSCAPE) && (paperBounds.width < paperBounds.height)) || ((orientation == PaperClips.ORIENTATION_PORTRAIT) && (paperBounds.height < paperBounds.width))) { String name = printJob.getName(); Print document = new RotatePrint(printJob.getDocument()); Margins margins = printJob.getMargins().rotate(); printJob = new PrintJob(name, document).setMargins(margins) .setOrientation(PaperClips.ORIENTATION_DEFAULT); } return printJob; } }swt-paperclips-1.0.4/src/net/sf/paperclips/PageNumber.java000066400000000000000000000020041160731654500235140ustar00rootroot00000000000000/* * 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 net.sf.paperclips; /** * Instances of this class represent a page index in the output of a PagePrint. * * @author Matthew Hall */ public interface PageNumber { /** * Returns the zero-based page index. * * @return the zero-based page index. */ public int getPageNumber(); /** * Returns the total number of pages. Note that this method may not return * an accurate value until all pages have been laid out. Therefore this * method should not be used inside * {@link PageDecoration#createPrint(PageNumber)}. * * @return the total number of pages. */ public int getPageCount(); } swt-paperclips-1.0.4/src/net/sf/paperclips/PageNumberFormat.java000066400000000000000000000015351160731654500246750ustar00rootroot00000000000000/* * 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 net.sf.paperclips; /** * Interface for formatting a PageNumber instance into a printable string. * * @author Matthew Hall */ public interface PageNumberFormat { /** * Returns a formatted String representing the pageNumber argument. * * @param pageNumber * the page number to be formatted into a String. * @return a formatted String representing the pageNumber argument. */ public String format(PageNumber pageNumber); } swt-paperclips-1.0.4/src/net/sf/paperclips/PageNumberPageDecoration.java000066400000000000000000000104431160731654500263270ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import net.sf.paperclips.internal.PaperClipsUtil; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.RGB; /** * A PageDecoration which displays the page number. This convenience class helps * avoid the need for writing a new PageDecoration class if only a page number * is needed. Getter and setter methods are provided for all the properties * available in the PagePrint class itself. * * @author Matthew Hall */ public class PageNumberPageDecoration implements PageDecoration { FontData fontData = new FontData(); int align = SWT.LEFT; RGB rgb = new RGB(0, 0, 0); // black PageNumberFormat format = new DefaultPageNumberFormat(); /** * Constructs a PageNumberPageDecoration with default font, alignment, and * page number format. */ public PageNumberPageDecoration() { } /** * Constructs a PageNumberPageDecoration with the given alignment. * * @param align * horizontal text alignment. */ public PageNumberPageDecoration(int align) { setAlign(align); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + align; result = prime * result + ((fontData == null) ? 0 : fontData.hashCode()); result = prime * result + ((format == null) ? 0 : format.hashCode()); 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; PageNumberPageDecoration other = (PageNumberPageDecoration) obj; if (align != other.align) return false; if (fontData == null) { if (other.fontData != null) return false; } else if (!fontData.equals(other.fontData)) return false; if (format == null) { if (other.format != null) return false; } else if (!format.equals(other.format)) return false; if (rgb == null) { if (other.rgb != null) return false; } else if (!rgb.equals(other.rgb)) return false; return true; } /** * Returns the font. * * @return the font. */ public FontData getFontData() { return fontData; } /** * Sets the font. * * @param fontData * the new font. */ public void setFontData(FontData fontData) { Util.notNull(fontData); this.fontData = fontData; } /** * Returns the horizontal text alignment. * * @return the horizontal text alignment. */ public int getAlign() { return align; } /** * Sets the horizontal text alignment. * * @param align * the horizontal text alignment. */ public void setAlign(int align) { align = checkAlign(align); this.align = align; } private int checkAlign(int align) { return PaperClipsUtil.firstMatch(align, new int[] { SWT.LEFT, SWT.CENTER, SWT.RIGHT }, SWT.LEFT); } /** * Returns the text color. * * @return the text color. */ public RGB getRGB() { return rgb; } /** * Sets the text color. * * @param rgb * the new text color. */ public void setRGB(RGB rgb) { Util.notNull(rgb); this.rgb = rgb; } /** * Returns the page number format. * * @return the page number format. */ public PageNumberFormat getFormat() { return format; } /** * Sets the page number format. * * @param format * the page number format. */ public void setFormat(PageNumberFormat format) { Util.notNull(format); this.format = format; } public Print createPrint(PageNumber pageNumber) { PageNumberPrint result = new PageNumberPrint(pageNumber); result.setFontData(fontData); result.setAlign(align); result.setPageNumberFormat(format); result.setRGB(rgb); return result; } }swt-paperclips-1.0.4/src/net/sf/paperclips/PageNumberPrint.java000066400000000000000000000263631160731654500245470ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.PaperClipsUtil; import net.sf.paperclips.internal.ResourcePool; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; /** * Displays the page number and page count within the context of a * {@link PagePrint}. To properly display page numbers, instances of this class * should be created using the {@link PageNumber} argument which is passed to * the {@link PageDecoration#createPrint(PageNumber)} method by PagePrint. *

* PageNumberPrints are never greedy with layout space, even with center- or * right-alignment. (Greedy prints take up all the available space on the page.) * Therefore, when center- or right-alignment is required, it is necessary to * wrap the page number in a Print which will enforce the same alignment. * Usually this is a center:default:grow or right:default:grow column in a * GridPrint. * * @author Matthew Hall * @see PagePrint * @see PageDecoration * @see PageNumber * @see PageNumberFormat * @see DefaultPageNumberFormat */ public class PageNumberPrint implements Print { /** The default font data for a PageNumberPrint. Value is device-dependent. */ public static final FontData DEFAULT_FONT_DATA = new FontData(); /** The default alignment for a PageNumberPrint. Value is SWT.LEFT. */ public static final int DEFAULT_ALIGN = SWT.LEFT; /** The default text style. Value is device-dependent. */ public static final TextStyle DEFAULT_TEXT_STYLE = new TextStyle().font( DEFAULT_FONT_DATA).align(DEFAULT_ALIGN); PageNumber pageNumber; TextStyle textStyle; PageNumberFormat format; /** * Constructs a PageNumberPrint for the given page number. * * @param pageNumber * the page number of the page this Print will appear on. */ public PageNumberPrint(PageNumber pageNumber) { this(pageNumber, DEFAULT_TEXT_STYLE); } /** * Constructs a PageNumberPrint for the given page number and font. * * @param pageNumber * the page number of the page this Print will appear on. * @param fontData * the font that this Print will appear in. */ public PageNumberPrint(PageNumber pageNumber, FontData fontData) { this(pageNumber, DEFAULT_TEXT_STYLE.font(fontData)); } /** * Constructs a PageNumberPrint for the given page number and alignment. * * @param pageNumber * the page number of the page this Print will appear on. * @param align * the horizontal alignment of the text. */ public PageNumberPrint(PageNumber pageNumber, int align) { this(pageNumber, DEFAULT_TEXT_STYLE.align(align)); } /** * Constructs a PageNumberPrint for the given page number, font and * alignment. * * @param pageNumber * the page number of the page this Print will appear on. * @param fontData * the font that this Print will appear in. * @param align * the horizontal alignment of the text. */ public PageNumberPrint(PageNumber pageNumber, FontData fontData, int align) { this(pageNumber, DEFAULT_TEXT_STYLE.font(fontData).align(align)); } /** * Constructs a PageNumberPrint for the given page number and text style. * * @param pageNumber * the page number of the page this Print will appear on. * @param textStyle * the text style that this Print will appear in. */ public PageNumberPrint(PageNumber pageNumber, TextStyle textStyle) { setPageNumber(pageNumber); setTextStyle(textStyle); setPageNumberFormat(new DefaultPageNumberFormat()); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((pageNumber == null) ? 0 : pageNumber.hashCode()); result = prime * result + ((textStyle == null) ? 0 : textStyle.hashCode()); result = prime * result + ((format == null) ? 0 : format.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; PageNumberPrint other = (PageNumberPrint) obj; if (pageNumber == null) { if (other.pageNumber != null) return false; } else if (!pageNumber.equals(other.pageNumber)) return false; if (textStyle == null) { if (other.textStyle != null) return false; } else if (!textStyle.equals(other.textStyle)) return false; if (format == null) { if (other.format != null) return false; } else if (!format.equals(other.format)) return false; return true; } /** * Sets the page number to the argument. * * @param pageNumber * the new page number. */ public void setPageNumber(PageNumber pageNumber) { Util.notNull(pageNumber); this.pageNumber = pageNumber; } /** * Returns the page number of this Print. * * @return the page number of this Print. */ public PageNumber getPageNumber() { return pageNumber; } /** * Sets the text font to the argument. * * @param fontData * the new text font. */ public void setFontData(FontData fontData) { Util.notNull(fontData); setTextStyle(textStyle.font(fontData)); } /** * Returns the text font. * * @return the text font. */ public FontData getFontData() { return textStyle.getFontData(); } /** * Sets the horizontal text alignment to the argument. * * @param align * the horizontal alignment. Must be one of {@link SWT#LEFT }, * {@link SWT#CENTER } or {@link SWT#RIGHT }. */ public void setAlign(int align) { setTextStyle(textStyle.align(checkAlign(align))); } /** * Returns the horizontal text alignment. * * @return the horizontal text alignment. */ public int getAlign() { return textStyle.getAlignment(); } private int checkAlign(int align) { return PaperClipsUtil.firstMatch(align, new int[] { SWT.LEFT, SWT.CENTER, SWT.RIGHT }, SWT.LEFT); } /** * Returns the text style that will be used to render the page number * * @return the text style that will be used to render the page number */ public TextStyle getTextStyle() { return textStyle; } /** * Sets the text style that will be used to render the page number * * @param textStyle * the text style */ public void setTextStyle(TextStyle textStyle) { Util.notNull(textStyle); this.textStyle = textStyle; } /** * Sets the format that will be used to convert the page number to a text * string. * * @param format * the new page number format. */ public void setPageNumberFormat(PageNumberFormat format) { Util.notNull(format); this.format = format; } /** * Returns the page number format. This property determines how the * PageNumber will be converted into a String representing the page number. * The default value of this property formats page numbers as follows:
* *

	 * Page 1 of 5
	 * 
* * @return the page number format. */ public PageNumberFormat getPageNumberFormat() { return format; } /** * Sets the text color. * * @param foreground * the new text color. */ public void setRGB(RGB foreground) { Util.notNull(foreground); setTextStyle(textStyle.foreground(foreground)); } /** * Returns the text color. * * @return the text color. */ public RGB getRGB() { return textStyle.getForeground(); } public PrintIterator iterator(Device device, GC gc) { return new PageNumberIterator(this, device, gc); } } class PageNumberIterator extends AbstractIterator { final PageNumber pageNumber; final TextStyle textStyle; final PageNumberFormat format; final Point size; boolean hasNext = true; PageNumberIterator(PageNumberPrint print, Device device, GC gc) { super(device, gc); this.pageNumber = print.pageNumber; this.textStyle = print.textStyle; this.format = print.format; // Calculate the size for the largest possible page number string. Font oldFont = gc.getFont(); try { gc.setFont(ResourcePool.forDevice(device).getFont( textStyle.getFontData())); size = gc.textExtent(format.format(new PageNumber() { public int getPageCount() { return 9999; } public int getPageNumber() { return 9998; } // (zero-based index) })); } finally { gc.setFont(oldFont); } } PageNumberIterator(PageNumberIterator that) { super(that); this.pageNumber = that.pageNumber; this.textStyle = that.textStyle; this.format = that.format; this.size = that.size; this.hasNext = that.hasNext; } public boolean hasNext() { return hasNext; } public Point minimumSize() { return size; } public Point preferredSize() { return size; } public PrintPiece next(int width, int height) { if (width < size.x || height < size.y) return null; Point size = new Point(this.size.x, this.size.y); int align = textStyle.getAlignment(); if (align == SWT.CENTER || align == SWT.RIGHT) size.x = width; PageNumberPiece piece = new PageNumberPiece(this, size); hasNext = false; return piece; } public PrintIterator copy() { return new PageNumberIterator(this); } } class PageNumberPiece extends AbstractPiece { private final PageNumber pageNumber; private final TextStyle textStyle; private final PageNumberFormat format; PageNumberPiece(PageNumberIterator iter, Point size) { super(iter, size); this.pageNumber = iter.pageNumber; this.textStyle = iter.textStyle; this.format = iter.format; } public void paint(final GC gc, final int x, final int y) { Font oldFont = gc.getFont(); Color oldForeground = gc.getForeground(); Point size = getSize(); try { ResourcePool resources = ResourcePool.forDevice(device); gc.setFont(resources.getFont(textStyle.getFontData())); gc.setForeground(resources.getColor(textStyle.getForeground())); String text = format.format(pageNumber); gc.drawText(text, x + getHorzAlignmentOffset(gc.textExtent(text).x, size.x), y, true); } finally { gc.setFont(oldFont); gc.setForeground(oldForeground); } } private int getHorzAlignmentOffset(int textWidth, int totalWidth) { int offset = 0; switch (textStyle.getAlignment()) { case SWT.CENTER: offset = (totalWidth - textWidth) / 2; break; case SWT.RIGHT: offset = totalWidth - textWidth; break; } return offset; } public void dispose() { } // Shared resources, nothing to dispose }swt-paperclips-1.0.4/src/net/sf/paperclips/PagePrint.java000066400000000000000000000353051160731654500233720ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import java.util.ArrayList; import java.util.List; import net.sf.paperclips.internal.PaperClipsUtil; import net.sf.paperclips.internal.PrintSizeStrategy; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A decorator Print which displays page headers and footers around a document * body, with page numbering capabilities. *

* PagePrint is horizontally and vertically greedy. Greedy prints take up all * the available space on the page. *

* Note: Avoid wrapping PagePrint in prints with space-optimizing * semantics (e.g. ColumnPrint equalizes columns on the last page), as this may * cause the total page count to be incorrect on some pages. At this time there * is no known fix. If wrapping a PagePrint is unavoidable, consider using a * custom PageNumberFormat which does not display the total page count. * * @author Matthew Hall */ public class PagePrint implements Print { private static final int DEFAULT_GAP = 1; PageDecoration header; int headerGap = DEFAULT_GAP; // in points Print body; int footerGap = DEFAULT_GAP; // in points PageDecoration footer; /** * Constructs a PagePrint with the given header and body. * * @param header * a PageDecoration for creating the header. May be null. * @param headerGap * the gap between the header and body, in points. * @param body * the Print being decorated. */ public PagePrint(PageDecoration header, int headerGap, Print body) { this(header, headerGap, body, DEFAULT_GAP, null); } /** * Constructs a PagePrint with the given header and body. * * @param body * the Print being decorated. * @param header * a PageDecoration for creating the header. May be null. */ public PagePrint(PageDecoration header, Print body) { this(header, DEFAULT_GAP, body); } /** * Constructs a PagePrint with the given body. * * @param body * the Print being decorated. */ public PagePrint(Print body) { this(null, body, null); } /** * Constructs a PagePrint with the given body and footer. * * @param body * the Print being decorated. * @param footer * a PageDecoration for creating the footer. may be null. */ public PagePrint(Print body, PageDecoration footer) { this(body, DEFAULT_GAP, footer); } /** * Constructs a PagePrint with the given body, header and footer. * * @param body * the Print being decorated. * @param footerGap * the gap between the body and footer, in points. * @param footer * a PageDecoration for creating the footer. May be null. */ public PagePrint(Print body, int footerGap, PageDecoration footer) { this(null, DEFAULT_GAP, body, footerGap, footer); } /** * Constructs a PagePrint with the given body, header and footer. * * @param body * the Print being decorated. * @param header * a PageDecoration for creating the header. May be null. * @param footer * a PageDecoration for creating the footer. may be null. * @deprecated PagePrint(PageDecoration, Print, PageDecoration) instead. */ public PagePrint(Print body, PageDecoration header, PageDecoration footer) { this(header, body, footer); } /** * Constructs a PagePrint with the given body, header and footer. * * @param header * a PageDecoration for creating the header. May be null. * @param body * the Print being decorated. * @param footer * a PageDecoration for creating the footer. may be null. */ public PagePrint(PageDecoration header, Print body, PageDecoration footer) { this(header, DEFAULT_GAP, body, DEFAULT_GAP, footer); } /** * Constructs a PagePrint with the given body, header and footer. * * @param header * a PageDecoration for creating the header. May be null. * @param headerGap * the gap between the header and body, in points. * @param body * the Print being decorated. * @param footerGap * the gap between the body and footer, in points. * @param footer * a PageDecoration for creating the footer. May be null. */ public PagePrint(PageDecoration header, int headerGap, Print body, int footerGap, PageDecoration footer) { setHeader(header); setHeaderGap(headerGap); setBody(body); setFooterGap(footerGap); setFooter(footer); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((body == null) ? 0 : body.hashCode()); result = prime * result + ((footer == null) ? 0 : footer.hashCode()); result = prime * result + footerGap; result = prime * result + ((header == null) ? 0 : header.hashCode()); result = prime * result + headerGap; return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; PagePrint other = (PagePrint) obj; if (body == null) { if (other.body != null) return false; } else if (!body.equals(other.body)) return false; if (footer == null) { if (other.footer != null) return false; } else if (!footer.equals(other.footer)) return false; if (footerGap != other.footerGap) return false; if (header == null) { if (other.header != null) return false; } else if (!header.equals(other.header)) return false; if (headerGap != other.headerGap) return false; return true; } /** * Returns the page header. * * @return the page header. */ public PageDecoration getHeader() { return header; } /** * Sets the page header to the argument. * * @param header * a PageDecoration which creates the header. May be null. */ public void setHeader(PageDecoration header) { this.header = header; } /** * Returns the gap between the header and body, expressed in points. * * @return the gap between the header and body, expressed in points. */ public int getHeaderGap() { return headerGap; } /** * Sets the gap between the header and body to the argument, expressed in * points. * * @param points * the new gap between the header and body, expressed in points. * 72 points = 1". */ public void setHeaderGap(int points) { this.headerGap = checkGap(points); } /** * Returns the page body. * * @return the page body. */ public Print getBody() { return body; } /** * Sets the page body to the argument. * * @param body * the new page body. */ public void setBody(Print body) { Util.notNull(body); this.body = body; } /** * Returns the page footer. * * @return the page footer. */ public PageDecoration getFooter() { return footer; } /** * Sets the page footer to the argument. * * @param footer * a PageDecoration which creates the footer. May be null. */ public void setFooter(PageDecoration footer) { this.footer = footer; } /** * Returns the gap between the body and footer, expressed in points. * * @return the gap between the body and footer, expressed in points. */ public int getFooterGap() { return footerGap; } /** * Sets the gap between the body and footer to the argument, expressed in * points. * * @param points * the new gap between the body and footer (if there is a * footer). */ public void setFooterGap(int points) { this.footerGap = checkGap(points); } private static int checkGap(int gap) { if (gap < 0) PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Gap must be >= 0 (value is " + gap + ")"); //$NON-NLS-1$ //$NON-NLS-2$ return gap; } public PrintIterator iterator(Device device, GC gc) { if (header == null && footer == null) return body.iterator(device, gc); return new PageIterator(this, device, gc); } } class PageIterator implements PrintIterator { class PageNumberer { int pageCount = 0; synchronized PageNumber next() { return new InnerPageNumber(); } class InnerPageNumber implements PageNumber { final int pageNumber = pageCount++; // POST-increment public int getPageCount() { return pageCount; } public int getPageNumber() { return pageNumber; } } PageNumberer copy() { PageNumberer result = new PageNumberer(); result.pageCount = this.pageCount; return result; } } final Device device; final GC gc; final PageDecoration header; final int headerGap; // pixels final PrintIterator body; final int footerGap; // pixels final PageDecoration footer; final PageNumberer numberer; final Point minimumSize; final Point preferredSize; PageIterator(PagePrint print, Device device, GC gc) { this.device = device; this.gc = gc; Point dpi = device.getDPI(); body = print.body.iterator(device, gc); header = print.header; headerGap = header == null ? 0 : print.headerGap * dpi.y / 72; footer = print.footer; footerGap = footer == null ? 0 : print.footerGap * dpi.y / 72; this.numberer = new PageNumberer(); this.minimumSize = computeSize(PrintSizeStrategy.MINIMUM); this.preferredSize = computeSize(PrintSizeStrategy.PREFERRED); } PageIterator(PageIterator that) { this.device = that.device; this.gc = that.gc; this.body = that.body.copy(); this.header = that.header; this.headerGap = that.headerGap; this.footer = that.footer; this.footerGap = that.footerGap; // FIXME: Wrapping PagePrint in a class with space-optimizing semantics // (ColumnPrint) can fork the total // page count. i.e. if the copied PageIterator is chosen as the optimal // layout, then previous pages will // have a page number spawned from a different page numberer. Thus the // total page count for those // previous pages will no longer be incremented with each new page. this.numberer = that.numberer.copy(); this.pageNumber = that.pageNumber; this.minimumSize = that.minimumSize; this.preferredSize = that.preferredSize; } private Point computeSize(PrintSizeStrategy strategy) { Point size = strategy.computeSize(body); PageNumber samplePageNumber = new PageNumber() { public int getPageCount() { return 1; } public int getPageNumber() { return 0; } }; if (header != null) { Print headerPrint = header.createPrint(samplePageNumber); if (headerPrint != null) { PrintIterator iter = headerPrint.iterator(device, gc); size.y += headerGap; Point headerSize = strategy.computeSize(iter); size.x = Math.max(size.x, headerSize.x); size.y += headerSize.y; } } if (footer != null) { Print footerPrint = footer.createPrint(samplePageNumber); if (footerPrint != null) { PrintIterator iter = footerPrint.iterator(device, gc); size.y += footerGap; Point footerSize = strategy.computeSize(iter); size.x = Math.max(size.x, footerSize.x); size.y += footerSize.y; } } return size; } public boolean hasNext() { return body.hasNext(); } public Point minimumSize() { return new Point(minimumSize.x, minimumSize.y); } public Point preferredSize() { return new Point(preferredSize.x, preferredSize.y); } public PrintPiece next(int width, final int height) { PageNumber pageNumber = getCurrentPageNumber(); // HEADER PrintPiece headerPiece = null; int availableHeight = height; if (header != null) { Print headerPrint = header.createPrint(pageNumber); if (headerPrint != null) { headerPiece = getDecorationPrintPiece(headerPrint, width, availableHeight); if (headerPiece == null) return null; availableHeight -= (heightOf(headerPiece) + headerGap); } } // FOOTER PrintPiece footerPiece = null; if (footer != null) { Print footerPrint = footer.createPrint(pageNumber); if (footerPrint != null) { footerPiece = getDecorationPrintPiece(footerPrint, width, availableHeight); if (footerPiece == null) { PaperClipsUtil.dispose(headerPiece); return null; } availableHeight -= (heightOf(footerPiece) + footerGap); } } // BODY PrintPiece bodyPiece = PaperClips.next(body, width, availableHeight); if (bodyPiece == null) { PaperClipsUtil.dispose(headerPiece, footerPiece); return null; } PrintPiece result = createResult(height, headerPiece, bodyPiece, footerPiece); advancePageNumber(); return result; } private int heightOf(PrintPiece piece) { return piece.getSize().y; } PageNumber pageNumber; private void advancePageNumber() { // Null the pageNumber field so the next iteration advances to the next // page. pageNumber = null; } private PageNumber getCurrentPageNumber() { if (pageNumber == null) pageNumber = numberer.next(); return pageNumber; } private PrintPiece createResult(int height, PrintPiece headerPiece, PrintPiece bodyPiece, PrintPiece footerPiece) { if (headerPiece == null && footerPiece == null) return bodyPiece; List entries = new ArrayList(); if (headerPiece != null) entries.add(createEntry(headerPiece, 0)); int y = headerPiece == null ? 0 : heightOf(headerPiece) + headerGap; entries.add(createEntry(bodyPiece, y)); if (footerPiece != null) { y = height - heightOf(footerPiece); entries.add(createEntry(footerPiece, y)); } return new CompositePiece(entries); } private CompositeEntry createEntry(PrintPiece piece, int y) { return new CompositeEntry(piece, new Point(0, y)); } private PrintPiece getDecorationPrintPiece(Print decoration, int width, int height) { PrintIterator iterator = decoration.iterator(device, gc); PrintPiece piece = PaperClips.next(iterator, width, height); if (piece == null) return null; if (iterator.hasNext()) { piece.dispose(); return null; } return piece; } public PrintIterator copy() { return new PageIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/PaperClips.java000066400000000000000000000400551160731654500235410ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import java.util.ArrayList; import java.util.List; import net.sf.paperclips.internal.PaperClipsUtil; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.printing.Printer; import org.eclipse.swt.printing.PrinterData; /** * This class contains static constants and methods for preparing and printing * documents. Methods in this class supersede those in PrintUtil. * * @author Matthew Hall */ public class PaperClips { private PaperClips() { } // no instances static boolean debug = false; /** * Indicates that the printer's default page orientation should be used. */ public static final int ORIENTATION_DEFAULT = SWT.DEFAULT; /** * Indicates portrait page orientation. */ public static final int ORIENTATION_PORTRAIT = SWT.VERTICAL; /** * Indicates landscape page orientation. */ public static final int ORIENTATION_LANDSCAPE = SWT.HORIZONTAL; /** * Triggers an appropriate exception based on the passed in error code. * * @param code * the SWT error code. */ public static void error(int code) { error(code, null); } /** * Triggers an unspecified exception with the passed in detail. * * @param detail * more information about error. */ public static void error(String detail) { error(SWT.ERROR_UNSPECIFIED, detail); } /** * Triggers an appropriate exception based on the passed in error code. * * @param code * the SWT error code. * @param detail * more information about error. */ public static void error(int code, String detail) { SWT.error(code, null, detail); } /** * EXPERIMENTAL: Sets whether debug mode is enabled. This mode may be * used for troubleshooting documents that cannot be laid out for some * reason (e.g. "Cannot layout page x" error occurs). * *

* THIS API IS EXPERIMENTAL AND MAY BE REMOVED OR CHANGED IN THE * FUTURE. * * @param debug * true to enable debug mode, false to disable it. */ public static void setDebug(boolean debug) { PaperClips.debug = debug; } /** * EXPERIMENTAL: Returns whether debug mode is enabled. * *

* THIS API IS EXPERIMENTAL AND MAY BE REMOVED OR CHANGED IN THE * FUTURE. * * @return whether debug mode is enabled. */ public static boolean getDebug() { return debug; } /** * Returns a PrinterData for the system-default printer, or the first * printer if no default printer is configured. * * @return a PrinterData for the system-default printer, or the first * printer if no default printer is configured. */ public static PrinterData getDefaultPrinterData() { PrinterData printerData = Printer.getDefaultPrinterData(); if (printerData == null) { // Linux may have one or more printers without a default printer PrinterData[] list = Printer.getPrinterList(); if (list.length > 0) printerData = list[0]; } return printerData; } /** * Calls iterator.next(width, height) and returns the result. This method * checks multiple conditions to ensure proper usage and behavior of * PrintIterators. *

* This method is intended to be used by PrintIterator classes, as a * results-checking alternative to calling next(int, int) directly on the * target iterator. All PrintIterator classes in the PaperClips library use * this method instead of directly calling the * {@link PrintIterator#next(int, int)} method. * * @param iterator * the PrintIterator * @param width * the available width. * @param height * the available height. * @return the next portion of the Print, or null if the width and height * are not enough to display any of the iterator's contents. */ public static PrintPiece next(PrintIterator iterator, int width, int height) { Util.notNull(iterator); if (width < 0 || height < 0) error(SWT.ERROR_INVALID_ARGUMENT, "PrintPiece size " + width + "x" + height + " not possible"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ if (!iterator.hasNext()) error("Iterator " + iterator + " has no more content."); //$NON-NLS-1$ //$NON-NLS-2$ PrintPiece result = iterator.next(width, height); if (result != null) { Point size = result.getSize(); if (size.x > width || size.y > height) error("Iterator " + iterator + " produced a " + size.x + "x" + size.y + " piece for a " + width //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + "x" + height + " area."); //$NON-NLS-1$//$NON-NLS-2$ } else if (debug) { return new NullPrintPiece(); } return result; } /** * Prints the print job to the given printer. This method constructs a * Printer, forwards to {@link #print(PrintJob, Printer)}, and disposes the * printer before returning. * * @param printJob * the print job. * @param printerData * the PrinterData of the selected printer. */ public static void print(PrintJob printJob, PrinterData printerData) { Printer printer = new Printer(printerData); try { print(printJob, printer); } finally { printer.dispose(); } } /** * Prints the print job to the given printer. * * @param printJob * the print job. * @param printer * the printer device. */ public static void print(PrintJob printJob, Printer printer) { // Bug in SWT on OSX: If Printer.startJob() is not called first, the GC // will be disposed by // default. startJob(printer, printJob.getName()); boolean completed = false; try { GC gc = createAndConfigureGC(printer); try { print(printJob, printer, gc); } finally { gc.dispose(); } printer.endJob(); completed = true; } finally { if (!completed) cancelJob(printer); } } private static void startJob(Printer printer, String jobName) { if (!printer.startJob(jobName)) error("Unable to start print job"); //$NON-NLS-1$ } private static void cancelJob(Printer printer) { if (isGTK()) printer.endJob(); // Printer.cancelJob() not implemented on GTK else printer.cancelJob(); } private static GC createAndConfigureGC(Printer printer) { GC gc = new GC(printer); gc.setAdvanced(true); return gc; } /** * Prints the print job to the specified printer using the GC. This method * does not manage the print job lifecycle (it does not call startJob or * endJob). * * @param printJob * the print job * @param printer * the printer * @param gc * the GC */ private static void print(PrintJob printJob, Printer printer, final GC gc) { final PrinterData printerData = printer.getPrinterData(); PrintPiece[] pages = getPages(printJob, printer, gc); int startPage = 0; int endPage = pages.length - 1; if (printerData.scope == PrinterData.PAGE_RANGE) { // Convert from PrinterData's one-based indices to our zero-based // indices startPage = Math.max(startPage, printerData.startPage - 1); endPage = Math.min(endPage, printerData.endPage - 1); } final int collatedCopies; final int noncollatedCopies; if (printerData.collate) { // always false if printer driver performs // collation collatedCopies = printerData.copyCount; // always 1 if printer // driver handles copy count noncollatedCopies = 1; } else { noncollatedCopies = printerData.copyCount; // always 1 if printer // driver handles copy // count collatedCopies = 1; } printPages(printer, gc, pages, startPage, endPage, collatedCopies, noncollatedCopies); } private static void printPages(final Printer printer, final GC gc, final PrintPiece[] pages, final int startPage, final int endPage, final int collatedCopies, final int noncollatedCopies) { disposeUnusedPages(pages, startPage, endPage); Rectangle paperBounds = getPaperBounds(printer); final int x = paperBounds.x; final int y = paperBounds.y; try { for (int collated = 0; collated < collatedCopies; collated++) { for (int pageIndex = startPage; pageIndex <= endPage; pageIndex++) { for (int noncollated = 0; noncollated < noncollatedCopies; noncollated++) { if (printer.startPage()) { pages[pageIndex].paint(gc, x, y); pages[pageIndex].dispose(); printer.endPage(); } else { error("Unable to start page " + pageIndex); //$NON-NLS-1$ } } } } } finally { PaperClipsUtil.dispose(pages); } } private static void disposeUnusedPages(PrintPiece[] pages, int startPage, int endPage) { PaperClipsUtil.dispose(pages, 0, startPage); PaperClipsUtil.dispose(pages, endPage + 1, pages.length); } /** * Processes the print job and returns an array of pages for the given * printer device. Each element in the returned array has already had the * page orientation and page margins applied. Therefore, when calling the * paint(GC, int, int) method on each page, the printer's trim should be * provided as the x and y arguments. In other words, the trim is taken as a * minimum margin while applying calculating margins, but the position where * the page's content is drawn is determined solely by the margin, and is * not offset by the trim. This behavior is helpful for screen display, and * is already compensated for in the {@link #print(PrintJob, Printer)} * method. * * @param printer * the printing device. * @param printJob * the print job. * @return an array of all pages of the print job. Each element of the * returned array represents one page in the printed document. */ public static PrintPiece[] getPages(PrintJob printJob, Printer printer) { startDummyJob(printer, printJob.getName()); try { GC gc = createAndConfigureGC(printer); try { return getPages(printJob, printer, gc); } finally { gc.dispose(); } } finally { endDummyJob(printer); } } /** * Starts a dummy job on the given Printer if the platform requires it. * Dummy jobs allow the various Print components of PaperClips to perform * measurements required for document layout, without actually sending a job * to the printer. Only Mac OS X Carbon and Linux GTK+ are known to require * dummy jobs. * * @param printer * the Printer hosting the dummy print job. * @param name * the name of the dummy print job. */ public static void startDummyJob(Printer printer, String name) { // On Mac OS X Carbon and Linux GTK+, created GC is disposed unless // Printer.startJob() is called // first. if (isCarbon() || isGTK()) startJob(printer, name); } /** * Ends a dummy job on the given Printer if the platform requires a dummy * job. * * @param printer * the Printer hosting the dummy print job. */ public static void endDummyJob(Printer printer) { if (isGTK()) { // Linux GTK // Printer.cancelJob() is not implemented in SWT since GTK has no // API for cancelling a print // job. // For now we must use endJob(), even though it spits out an empty // page. // http://sourceforge.net/tracker/index.php?func=detail&aid=1773091&group_id=148509&atid=771872 // printer.cancelJob(); // Not implemented in SWT on GTK printer.endJob(); // See also: // http://bugzilla.gnome.org/show_bug.cgi?id=339323 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=212594 } else if (isCarbon()) // Mac OSX // 2007-04-30: A bug in SWT on Mac OSX prior to 3.3 renders Printer // instances useless after a call to cancelJob(). // Therefore on Mac OSX we call endJob() instead of cancelJob(). if (SWT.getVersion() < 3346) { // Version 3.3 printer.endJob(); } else { printer.cancelJob(); } } private static boolean isCarbon() { return SWT.getPlatform().equals("carbon"); //$NON-NLS-1$ } private static boolean isGTK() { return SWT.getPlatform().equals("gtk"); //$NON-NLS-1$ } private static PrintPiece[] getPages(PrintJob printJob, Printer printer, GC gc) { PageEnumeration enumeration = new PageEnumeration(printJob, printer, gc); List pages = new ArrayList(); while (enumeration.hasNext()) { PrintPiece page = enumeration.nextPage(); if (page == null) { int pageNumber = pages.size() + 1; PaperClipsUtil.dispose(pages); error("Unable to layout page " + pageNumber); //$NON-NLS-1$ } pages.add(page); } return (PrintPiece[]) pages.toArray(new PrintPiece[pages.size()]); } /** * Returns a {@link PageEnumeration} for the passed in PrintJob on the given * Printer, using the given GC. The Printer and GC must not be disposed * while the enumeration is in use. * * @param printJob * the print job * @param printer * the Printer device, which must not be disposed while the * PageEnumeration is in use. * @param gc * the GC, which must not be disposed while the PageEnumeration * is in use. * @return a {@link PageEnumeration} for the passed in PrintJob. */ public static PageEnumeration getPageEnumeration(PrintJob printJob, Printer printer, GC gc) { return new PageEnumeration(printJob, printer, gc); } /** * Returns the bounding rectangle of the paper, including non-printable * margins. * * @param printer * the printer device. * @return a rectangle whose edges correspond to the edges of the paper. */ public static Rectangle getPaperBounds(Printer printer) { Rectangle rect = getPrintableBounds(printer); return printer.computeTrim(rect.x, rect.y, rect.width, rect.height); } /** * Returns the bounding rectangle of the printable area on the paper. * * @param printer * the printer device. * @return the bounding rectangle of the printable area on the paper. */ public static Rectangle getPrintableBounds(Printer printer) { return printer.getClientArea(); } /** * Returns the bounding rectangle of the printable area which is inside the * given margins on the paper. The printer's minimum margins are reflected * in the returned rectangle. * * @param printer * the printer device. * @param margins * the desired page margins. * @return the bounding rectangle on the printable area which is within the * margins. */ public static Rectangle getMarginBounds(Margins margins, Printer printer) { Rectangle paperBounds = getPaperBounds(printer); // Calculate the pixel coordinates for the margins Point dpi = printer.getDPI(); int top = paperBounds.y + (margins.top * dpi.y / 72); int left = paperBounds.x + (margins.left * dpi.x / 72); int right = paperBounds.x + paperBounds.width - (margins.right * dpi.x / 72); int bottom = paperBounds.y + paperBounds.height - (margins.bottom * dpi.y / 72); // Enforce the printer's minimum margins. Rectangle printableBounds = getPrintableBounds(printer); if (top < printableBounds.y) top = printableBounds.y; if (left < printableBounds.x) left = printableBounds.x; if (right > printableBounds.x + printableBounds.width) right = printableBounds.x + printableBounds.width; if (bottom > printableBounds.y + printableBounds.height) bottom = printableBounds.y + printableBounds.height; return new Rectangle(left, top, right - left, bottom - top); } }swt-paperclips-1.0.4/src/net/sf/paperclips/Print.java000066400000000000000000000022721160731654500225720ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; /** * Interface for printable elements. * * @author Matthew Hall */ public interface Print { /** * Returns a PrintIterator for laying out the contents of this Print. The * iterator uses a snapshot of the print at the time this method is invoked, * so subsequent changes to the Print will not affect the output of the * iterator. * * @param device * the graphics device this Print will be drawn onto. * @param gc * the graphics context to be used for calculating layout and * drawing the Print's contents. * @return a PrintIterator for laying out the contents of this Print. */ public PrintIterator iterator(Device device, GC gc); }swt-paperclips-1.0.4/src/net/sf/paperclips/PrintIterator.java000066400000000000000000000106501160731654500243030ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * Splits a Print into multiple PrintPieces, according to the space available on * the graphics device. PrintIterators are created by * {@link Print#iterator(Device, GC)}, and are initialized with the graphics * device passed to that method. * * @author Matthew Hall */ public interface PrintIterator { /** * Identifies whether any PrintPieces remain. * * @return whether any PrintPieces remain. */ public boolean hasNext(); /** * Returns the next PrintPiece for the Print. *

* If all of the remaining contents of the Print will fit in the given * space, the returned PrintPiece will include all remaining contents, and * subsequent calls to {@link PrintIterator#hasNext() } will return * false. *

* If some, but not all of the remaining contents will fit in the given * space, the returned PrintPiece will contain as much of the contents as * possible, and subsequent calls to {@link PrintIterator#hasNext() } will * return true. *

* If there is insufficient space for any of the remaining contents in the * given space, null is returned, and subsequent calls to * {@link PrintIterator#hasNext() } will return true. *

* If subsequent calls to PrintIterator#hasNext() return true, * this PrintIterator cannot fit any more in the given print area. Future * calls to this method should provide a fresh print area. At the top level, * each returned PrintPiece contains an entire page. *

* Note: PrintIterator classes should call * {@link PaperClips#next(PrintIterator, int, int)} instead of calling this * method directly, to gain automatic results checking to ensure all Print * classes are well-behaved. * * @param width * the width available on the graphics device for this iteration. * @param height * the height available on the graphics device for this * iteration. * @return a PrintPiece that paints the next part of the Print, or null if * the print area is too small. The size of the returned PrintPiece * must NOT exceed the width and height indicated. */ public PrintPiece next(int width, int height); /** * Returns the minimum size PrintPiece that this Print should be broken * into. *

* Note that the size calculated by this method is a "preferred minimum," or * the smallest size that the Print should normally be broken into. For a * TextPrint, this is the size of the widest individual word, in pixels. *

* This is distinct from the "absolute minimum," which is the smallest size * that a Print could possibly be broken into. For a TextPrint, this is the * size of the widest individual letter, in pixels. * * @return a Point indicating the minimum size PrintPiece this PrintIterator * should be broken into. */ public Point minimumSize(); /** * Returns the smallest size PrintPiece that this Print would be broken into * if print space was unlimited. *

* For a TextPrint, this is the size of the widest line (or the whole * TextPrint, if there are no line breaks), in pixels. * * @return a Point indicating the smallest size PrintPiece that this Print * would be broken into if print space was unlimited. */ public Point preferredSize(); /** * Returns a copy of this PrintIterator, with all relevant internal states. * This method allows a containing iterator to "back up" the current state * of its child iterators before invoking next(int, int) on * them. The containing iterator can then safely attempt iterating its * child(ren) in a variety of ways before selecting which way is the most * appropriate. * * @return a deep clone of the target with all relevant internal states. */ public PrintIterator copy(); }swt-paperclips-1.0.4/src/net/sf/paperclips/PrintJob.java000066400000000000000000000104701160731654500232240ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; /** * Instances of this class represent a prepared print job. * * @author Matthew Hall */ public class PrintJob { private final String name; private final Print document; private Margins margins = new Margins(); private int orientation = PaperClips.ORIENTATION_DEFAULT; /** * Constructs a PrintJob for the given document. * * @param name * the name of the print job, which will appear in the print * queue of the operating system. * @param document * the document to be printed. */ public PrintJob(String name, Print document) { Util.notNull(name, document); this.name = name; this.document = document; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((document == null) ? 0 : document.hashCode()); result = prime * result + ((margins == null) ? 0 : margins.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + orientation; return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; PrintJob other = (PrintJob) obj; if (document == null) { if (other.document != null) return false; } else if (!document.equals(other.document)) return false; if (margins == null) { if (other.margins != null) return false; } else if (!margins.equals(other.margins)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (orientation != other.orientation) return false; return true; } /** * Returns the name of the print job. * * @return the name of the print job. */ public String getName() { return name; } /** * Returns the document to be printed. * * @return the document to be printed. */ public Print getDocument() { return document; } /** * Returns the page orientation. * * @return the page orientation. */ public int getOrientation() { return orientation; } /** * Sets the page orientation. * * @param orientation * the page orientation. Must be one of * {@link PaperClips#ORIENTATION_DEFAULT }, * {@link PaperClips#ORIENTATION_PORTRAIT } or * {@link PaperClips#ORIENTATION_LANDSCAPE }. Values other than * these choices will be automatically changed to * {@link PaperClips#ORIENTATION_DEFAULT }. * @return this PrintJob (for chaining method calls) */ public PrintJob setOrientation(int orientation) { this.orientation = checkOrientation(orientation); return this; } private int checkOrientation(int orientation) { switch (orientation) { case PaperClips.ORIENTATION_LANDSCAPE: case PaperClips.ORIENTATION_PORTRAIT: case PaperClips.ORIENTATION_DEFAULT: return orientation; default: return PaperClips.ORIENTATION_DEFAULT; } } /** * Returns the page margins, expressed in points. 72 points = 1". * * @return the page margins, expressed in points. 72 points = 1". */ public Margins getMargins() { return margins; } /** * Sets the page margins. * * @param margins * the new page margins. * @return this PrintJob (for chaining method calls) */ public PrintJob setMargins(Margins margins) { Util.notNull(margins); this.margins = margins; return this; } /** * Sets the top, left, right, and bottom margins to the argument. * * @param margins * the margins, in points. 72 points = 1 inch. * @return this PrintJob (for chaining method calls) */ public PrintJob setMargins(int margins) { this.margins = new Margins(margins); return this; } } swt-paperclips-1.0.4/src/net/sf/paperclips/PrintPiece.java000066400000000000000000000030541160731654500235370ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A piece of a Print, which is capable of drawing itself on a graphics device. * PrintPiece objects are created by a PrintIterator. * * @author Matthew */ public interface PrintPiece { /** * Returns the dimensions of this PrintPiece, in pixels. * * @return the dimensions of this PrintPiece, in pixels. */ public Point getSize(); /** * Draws this PrintPiece on the given graphics device, at the given * coordinates. * * @param gc * a graphics context for the graphics device. * @param x * the x coordinate where this PrintPiece will be drawn. * @param y * the x coordinate where this PrintPiece will be drawn. */ public void paint(GC gc, int x, int y); /** * Disposes the system resources allocated by this PrintPiece. The dispose * method is not a permanent disposal of a PrintPiece. It is intended * to reclaim system resources, however future calls to paint(GC,int,int) * may require that the resources be allocated again. */ public void dispose(); } swt-paperclips-1.0.4/src/net/sf/paperclips/PrintUtil.java000066400000000000000000000136651160731654500234400ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import org.eclipse.swt.printing.Printer; import org.eclipse.swt.printing.PrinterData; /** * Deprecated class methods for printing documents--use the {@link PaperClips} * class instead. * * @author Matthew Hall * @deprecated Create {@link PrintJob} instances, and print them with the * {@link PaperClips#print(PrintJob, PrinterData)} method. */ public class PrintUtil { private PrintUtil() { } private static final String DEFAULT_JOB_NAME = "PaperClips printing"; //$NON-NLS-1$ /** * Prints the argument to the default printer with 1" margins. The Print's * toString() result will be used as the print job name. * * @param print * the item to print. * @deprecated use {@link PaperClips#print(PrintJob, PrinterData)} instead. */ public static void print(Print print) { PaperClips.print(new PrintJob(DEFAULT_JOB_NAME, print), PaperClips .getDefaultPrinterData()); } /** * Prints the argument to the default printer. The Print's toString() result * will be used as the print job name. * * @param print * the item to print. * @param margins * the page margins, in points. * @deprecated use {@link PaperClips#print(PrintJob, PrinterData)} instead. */ public static void print(Print print, int margins) { PaperClips.print(new PrintJob(DEFAULT_JOB_NAME, print) .setMargins(new Margins(margins)), PaperClips .getDefaultPrinterData()); } /** * Prints the argument to the given printer with 1" margins. The Print's * toString() result will be used as the print job name. * * @param printer * the device to print on. * @param print * the item to print. * @deprecated Use {@link PaperClips#print(PrintJob, PrinterData)} instead. */ public static void printTo(Printer printer, Print print) { PaperClips.print(new PrintJob(DEFAULT_JOB_NAME, print), printer); } /** * Prints the argument to the given printer. The Print's toString() result * will be used as the print job name. * * @param printer * the device to print on. * @param print * the item to print. * @param margins * the page margins, in points. * @deprecated Use {@link PaperClips#print(PrintJob, PrinterData)} instead. */ public static void printTo(Printer printer, Print print, int margins) { PaperClips.print(new PrintJob(DEFAULT_JOB_NAME, print) .setMargins(margins), printer); } /** * Prints the argument to the default printer with 1" margins. * * @param jobName * the print job name. * @param print * the item to print. * @deprecated Use {@link PaperClips#print(PrintJob, PrinterData)} instead. */ public static void print(String jobName, Print print) { PaperClips.print(new PrintJob(jobName, print), PaperClips .getDefaultPrinterData()); } /** * Prints the argument to the default Printer. * * @param jobName * the print job name. * @param print * the item to print. * @param margins * the page margins, in points. 72 pts = 1". * @deprecated Use {@link PaperClips#print(PrintJob, PrinterData)} instead. */ public static void print(String jobName, Print print, int margins) { PaperClips.print(new PrintJob(jobName, print).setMargins(margins), PaperClips.getDefaultPrinterData()); } /** * Prints the argument to the given printer, with 1" margins. * * @param jobName * the print job name. * @param printerData * the printer to print to. * @param print * the item to print. * @deprecated Use {@link PaperClips#print(PrintJob, PrinterData)} instead. */ public static void printTo(String jobName, PrinterData printerData, Print print) { PaperClips.print(new PrintJob(jobName, print), printerData); } /** * Prints the argument to the given printer. * * @param jobName * the print job name. * @param printerData * PrinterData of the printer to print to. * @param print * the item to print. * @param margins * the page margins, in points. 72 pts = 1". * @deprecated Use {@link PaperClips#print(PrintJob, PrinterData)} instead. */ public static void printTo(String jobName, PrinterData printerData, Print print, int margins) { PrintJob job = new PrintJob(jobName, print); job.setMargins(margins); PaperClips.print(job, printerData); } /** * Prints the argument to the given printer with 1" margins. * * @param jobName * the print job name. * @param printer * the device to print on. * @param print * the item to print. * @deprecated Use {@link PaperClips#print(PrintJob, PrinterData)} instead. */ public static void printTo(String jobName, Printer printer, Print print) { PaperClips.print(new PrintJob(jobName, print), printer); } /** * Print the argument to the given Printer. * * @param jobName * the print job name. * @param printer * the device to print on. * @param print * the item to print. * @param margins * the page margins, in points. 72 pts = 1". * @deprecated Use {@link PaperClips#print(PrintJob, PrinterData)} instead. */ public static void printTo(String jobName, Printer printer, Print print, int margins) { PaperClips.print(new PrintJob(jobName, print).setMargins(margins), printer); } }swt-paperclips-1.0.4/src/net/sf/paperclips/RotatePiece.java000066400000000000000000000047201160731654500237020ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Transform; final class RotatePiece implements PrintPiece { private final Device device; private final PrintPiece target; private final int angle; private final Point size; private Transform oldTransform; private Transform transform; RotatePiece(Device device, PrintPiece target, int angle, Point size) { Util.notNull(device, target, size); this.device = device; this.target = target; this.angle = angle; this.size = size; } public Point getSize() { return new Point(size.x, size.y); } private Transform getOldTransform() { if (oldTransform == null) oldTransform = new Transform(device); return oldTransform; } private Transform getTransform() { if (transform == null) transform = new Transform(device); return transform; } public void paint(GC gc, int x, int y) { Transform oldTransform = getOldTransform(); gc.getTransform(oldTransform); Transform transform = getTransform(); gc.getTransform(transform); transform.translate(x, y); rotateTransform(transform); gc.setTransform(transform); target.paint(gc, 0, 0); gc.setTransform(oldTransform); } private void rotateTransform(Transform transform) { switch (angle) { case 90: transform.translate(0, size.y); break; case 180: transform.translate(size.x, size.y); break; case 270: transform.translate(size.x, 0); break; default: PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Rotation angle must be 90, 180 or 270."); //$NON-NLS-1$ } transform.rotate(-angle); // reverse the angle since Transform.rotate // goes clockwise } public void dispose() { if (oldTransform != null) { oldTransform.dispose(); oldTransform = null; } if (transform != null) { transform.dispose(); transform = null; } target.dispose(); } }swt-paperclips-1.0.4/src/net/sf/paperclips/RotatePrint.java000066400000000000000000000131431160731654500237500ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A decorator print that rotates it's target by increments of 90 degrees. *

* Note: On Windows, this class depends on a bugfix available as of * Eclipse build 3.2, release candidate 3 (2006-04-28). Prior to this release, * using RotatePrint triggers the bug, causing the document to scale very large * on paper. This bug only manifests itself on paper, not with on-screen * viewing. *

* RotatePrints are horizontally and vertically greedy. Greedy prints take up * all the available space on the page. * * @author Matthew Hall */ public final class RotatePrint implements Print { private final Print target; private final int angle; /** * Constructs a RotatePrint that rotates it's target 90 degrees * counter-clockwise. * * @param target * the print to rotate. */ public RotatePrint(Print target) { this(target, 90); } /** * Constructs a RotatePrint. * * @param target * the print to rotate. * @param angle * the angle by which the target will be rotated, expressed in * degrees counter-clockwise. Positive values rotate * counter-clockwise, and negative values rotate clockwise. Must * be a multiple of 90. */ public RotatePrint(Print target, int angle) { Util.notNull(target); this.target = target; this.angle = checkAngle(angle); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + angle; result = prime * result + ((target == null) ? 0 : target.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; RotatePrint other = (RotatePrint) obj; if (angle != other.angle) return false; if (target == null) { if (other.target != null) return false; } else if (!target.equals(other.target)) return false; return true; } /** * Returns the print to be rotated. * * @return the print to be rotated. */ public Print getTarget() { return target; } /** * Returns the angle by which the target will be rotated (one of 0, 90, 180, * or 270). * * @return the angle by which the target will be rotated. */ public int getAngle() { return angle; } private static int checkAngle(int angle) { // Make sure angle is a multiple of 90. if (Math.abs(angle) % 90 != 0) PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Angle must be a multiple of 90 degrees"); //$NON-NLS-1$ // Bring angle within the range [0, 360) if (angle < 0) angle = 360 - (-angle % 360); if (angle >= 360) angle = angle % 360; return angle; } public PrintIterator iterator(Device device, GC gc) { if (angle == 0) return target.iterator(device, gc); return new RotateIterator(target, angle, device, gc); } } final class RotateIterator implements PrintIterator { private final Device device; private final PrintIterator target; private final int angle; private final Point minimumSize; private final Point preferredSize; RotateIterator(Print target, int angle, Device device, GC gc) { Util.notNull(target, device, gc); this.device = device; this.target = target.iterator(device, gc); this.angle = checkAngle(angle); // returns 90, 180, or 270 only Point min = this.target.minimumSize(); Point pref = this.target.preferredSize(); if (this.angle == 180) { this.minimumSize = new Point(min.x, min.y); this.preferredSize = new Point(pref.x, pref.y); } else { // flip x and y sizes if rotating by 90 or 270 degrees this.minimumSize = new Point(min.y, min.x); this.preferredSize = new Point(pref.y, pref.x); } } private RotateIterator(RotateIterator that) { this.device = that.device; this.target = that.target.copy(); this.angle = that.angle; this.minimumSize = that.minimumSize; this.preferredSize = that.preferredSize; } private static int checkAngle(int angle) { switch (angle) { case 90: case 180: case 270: break; default: PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Angle must be 90, 180, or 270"); //$NON-NLS-1$ } return angle; } public Point minimumSize() { return new Point(minimumSize.x, minimumSize.y); } public Point preferredSize() { return new Point(preferredSize.x, preferredSize.y); } public boolean hasNext() { return target.hasNext(); } public PrintPiece next(int width, int height) { PrintPiece target; if (angle == 180) // angle may only be init'd to 90, 180, of 270 target = PaperClips.next(this.target, width, height); else // flip width and height if rotating by 90 or 270 target = PaperClips.next(this.target, height, width); if (target == null) return null; return new RotatePiece(device, target, angle, new Point(width, height)); } public PrintIterator copy() { return new RotateIterator(this); } } swt-paperclips-1.0.4/src/net/sf/paperclips/ScalePrint.java000066400000000000000000000160101160731654500235350ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Transform; /** * A decorator print that scales it's target larger or smaller. *

* Note: On Windows, this class depends on a bugfix available as of * Eclipse build 3.2, release candidate 3 (2006-04-28). Prior to this release, * using ScalePrint triggers the bug, causing the document to scale very large * on paper. This bug manifests itself only on paper, not with on-screen * viewing. * * @author Matthew Hall */ public class ScalePrint implements Print { final Print target; final Double scale; /** * Constructs a ScalePrint which scales down it's target to print at it's * preferred size. This constructor is equivalent to calling new * ScalePrint(target, null). * * @param target * the print to scale down. */ public ScalePrint(Print target) { this(target, null); } /** * Constructs a ScalePrint which scales it's target by the given factor. * * @param target * @param scale * the scale factor (must be >0). A value of 2.0 draws at double * the size, and a value of 0.5 draws at half the size. A null * value automatically scales down so the target is rendered at * it's preferred size. */ public ScalePrint(Print target, Double scale) { Util.notNull(target); if (scale != null && !(scale.doubleValue() > 0)) PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Scale " + scale + " must be > 0"); //$NON-NLS-1$ //$NON-NLS-2$ this.target = target; this.scale = scale; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((scale == null) ? 0 : scale.hashCode()); result = prime * result + ((target == null) ? 0 : target.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ScalePrint other = (ScalePrint) obj; if (scale == null) { if (other.scale != null) return false; } else if (!scale.equals(other.scale)) return false; if (target == null) { if (other.target != null) return false; } else if (!target.equals(other.target)) return false; return true; } /** * Returns the print being scaled. * * @return the print being scaled. */ public Print getTarget() { return target; } /** * Returns the scale by which the target will be scaled, or null (indicating * automatic scale down to fit). * * @return the scale by which the target will be scaled, or null (indicating * automatic scale down to fit). */ public Double getScale() { return scale; } public PrintIterator iterator(Device device, GC gc) { return new ScaleIterator(this, device, gc); } } class ScaleIterator implements PrintIterator { private final Device device; private final PrintIterator target; private final Double scale; private final Point minimumSize; private final Point preferredSize; ScaleIterator(ScalePrint print, Device device, GC gc) { Util.notNull(print, device, gc); this.device = device; this.target = print.target.iterator(device, gc); this.scale = print.scale; Point min = target.minimumSize(); Point pref = target.preferredSize(); if (scale == null) { // auto-scale minimumSize = new Point(1, 1); preferredSize = pref; } else { // specific scale double s = scale.doubleValue(); minimumSize = new Point((int) Math.ceil(min.x * s), (int) Math .ceil(min.y * s)); preferredSize = new Point((int) Math.ceil(pref.x * s), (int) Math .ceil(pref.y * s)); } } private ScaleIterator(ScaleIterator that) { this.device = that.device; this.target = that.target.copy(); this.scale = that.scale; this.minimumSize = that.minimumSize; this.preferredSize = that.preferredSize; } public Point minimumSize() { return minimumSize; } public Point preferredSize() { return preferredSize; } public boolean hasNext() { return target.hasNext(); } public PrintPiece next(int width, int height) { // Find out what scale we're going to iterate at. double scale; Point pref = target.preferredSize(); if (this.scale == null) scale = Math.min(Math.min((double) width / (double) pref.x, (double) height / (double) pref.y), 1.0); else scale = this.scale.doubleValue(); // Calculate the width and height to be passed to the target. final int scaledWidth = (int) Math.ceil(width / scale); final int scaledHeight = (int) Math.ceil(height / scale); PrintPiece target = PaperClips.next(this.target, scaledWidth, scaledHeight); if (target == null) return null; return new ScalePiece(device, target, scale, width, height); } public PrintIterator copy() { return new ScaleIterator(this); } } final class ScalePiece implements PrintPiece { private final Device device; private final PrintPiece target; private final double scale; private final Point size; private Transform oldTransform; private Transform transform; ScalePiece(Device device, PrintPiece target, double scale, int maxWidth, int maxHeight) { Util.notNull(device, target); this.device = device; this.target = target; this.scale = scale; Point targetSize = target.getSize(); this.size = new Point(Math.min((int) Math.ceil(targetSize.x * scale), maxWidth), Math.min((int) Math.ceil(targetSize.y * scale), maxHeight)); } public Point getSize() { return new Point(size.x, size.y); } private Transform getOldTransform() { if (oldTransform == null) oldTransform = new Transform(device); return oldTransform; } private Transform getTransform() { if (transform == null) transform = new Transform(device); return transform; } public void paint(GC gc, int x, int y) { Transform oldTransform = getOldTransform(); gc.getTransform(oldTransform); Transform transform = getTransform(); gc.getTransform(transform); transform.translate(x, y); transform.scale((float) scale, (float) scale); gc.setTransform(transform); target.paint(gc, 0, 0); gc.setTransform(oldTransform); } public void dispose() { if (oldTransform != null) { oldTransform.dispose(); oldTransform = null; } if (transform != null) { transform.dispose(); transform = null; } target.dispose(); } }swt-paperclips-1.0.4/src/net/sf/paperclips/SeriesPrint.java000066400000000000000000000102151160731654500237410ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import java.util.ArrayList; import java.util.List; import net.sf.paperclips.internal.PrintSizeStrategy; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A Print which displays its child prints in series. Each element in the series * is displayed one at a time (no more than one child per page, although one * Print may span several pages). *

* Use this class as the top-level Print when several distinct Prints should be * batched into one print job, but printed on separate pages. * * @author Matthew Hall */ public class SeriesPrint implements Print { final List items = new ArrayList(); public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((items == null) ? 0 : items.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SeriesPrint other = (SeriesPrint) obj; if (items == null) { if (other.items != null) return false; } else if (!items.equals(other.items)) return false; return true; } /** * Adds the given prints to this SeriesPrint. * * @param items * the Prints to add */ public void add(Print[] items) { Util.noNulls(items); for (int i = 0; i < items.length; i++) this.items.add(items[i]); } /** * Adds the given print to this SeriesPrint. * * @param item * the Print to add */ public void add(Print item) { Util.notNull(item); items.add(item); } /** * Returns the number of Prints that have been added to this SeriesPrint. * * @return the number of Prints that have been added to this SeriesPrint. */ public int size() { return items.size(); } /** * Returns an array of items in the series. * * @return an array of items in the series. */ public Print[] getItems() { return (Print[]) items.toArray(new Print[items.size()]); } public PrintIterator iterator(Device device, GC gc) { return new SeriesIterator(this, device, gc); } } class SeriesIterator implements PrintIterator { final PrintIterator[] iters; int index; SeriesIterator(SeriesPrint print, Device device, GC gc) { this.iters = new PrintIterator[print.items.size()]; for (int i = 0; i < iters.length; i++) iters[i] = ((Print) print.items.get(i)).iterator(device, gc); this.index = 0; } SeriesIterator(SeriesIterator that) { this.iters = (PrintIterator[]) that.iters.clone(); for (int i = index; i < iters.length; i++) this.iters[i] = that.iters[i].copy(); this.index = that.index; } public boolean hasNext() { return index < iters.length; } private Point computeSize(PrintSizeStrategy strategy) { int width = 0; int height = 0; for (int i = 0; i < iters.length; i++) { PrintIterator iter = iters[i]; Point printSize = strategy.computeSize(iter); width = Math.max(width, printSize.x); height = Math.max(height, printSize.y); } return new Point(width, height); } public Point minimumSize() { return computeSize(PrintSizeStrategy.MINIMUM); } public Point preferredSize() { return computeSize(PrintSizeStrategy.PREFERRED); } public PrintPiece next(int width, int height) { if (!hasNext()) PaperClips.error("No more content"); //$NON-NLS-1$ PrintIterator iter = iters[index]; PrintPiece printPiece = PaperClips.next(iter, width, height); if (printPiece != null && !iter.hasNext()) index++; return printPiece; } public PrintIterator copy() { return new SeriesIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/SidewaysPrint.java000066400000000000000000000133051160731654500243020ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A decorator print that rotates it's target by increments of 90 degrees. *

* Note: On Windows, this class depends on a bugfix available as of * Eclipse build 3.2, release candidate 3 (2006-04-28). Prior to this release, * using SidewaysPrint triggers the bug, causing the document to scale very * large on paper. This bug only manifests itself on paper, not with on-screen * viewing. *

* SidewaysPrint, unlike RotatePrint, is neither horizontally nor vertically * greedy. Greedy prints take up all the available space on the page. * * @author Matthew Hall */ public final class SidewaysPrint implements Print { private final Print target; private final int angle; /** * Constructs a SidewaysPrint that rotates it's target 90 degrees * counter-clockwise. * * @param target * the print to rotate. */ public SidewaysPrint(Print target) { this(target, 90); } /** * Constructs a SidewaysPrint. * * @param target * the print to rotate. * @param angle * the angle by which the target will be rotated, expressed in * degrees counter-clockwise. Positive values rotate * counter-clockwise, and negative values rotate clockwise. Must * be a multiple of 90. */ public SidewaysPrint(Print target, int angle) { Util.notNull(target); this.target = target; this.angle = checkAngle(angle); } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + angle; result = prime * result + ((target == null) ? 0 : target.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SidewaysPrint other = (SidewaysPrint) obj; if (angle != other.angle) return false; if (target == null) { if (other.target != null) return false; } else if (!target.equals(other.target)) return false; return true; } private static int checkAngle(int angle) { // Make sure angle is a multiple of 90. if (Math.abs(angle) % 90 != 0) PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Angle must be a multiple of 90 degrees"); //$NON-NLS-1$ // Bring angle within the range [0, 360) if (angle < 0) angle = 360 - (-angle % 360); if (angle >= 360) angle = angle % 360; return angle; } /** * Returns the print to be rotated. * * @return the print to be rotated. */ public Print getTarget() { return target; } /** * Returns the angle by which the target will be rotated (one of 0, 90, 180, * or 270). * * @return the angle by which the target will be rotated. */ public int getAngle() { return angle; } public PrintIterator iterator(Device device, GC gc) { if (angle == 0) return target.iterator(device, gc); return new SidewaysIterator(target, angle, device, gc); } } final class SidewaysIterator implements PrintIterator { private final Device device; private final PrintIterator target; private final int angle; private final Point minimumSize; private final Point preferredSize; SidewaysIterator(Print target, int angle, Device device, GC gc) { Util.notNull(target, device, gc); this.device = device; this.target = target.iterator(device, gc); this.angle = checkAngle(angle); // returns 90, 180, or 270 only Point min = this.target.minimumSize(); Point pref = this.target.preferredSize(); if (this.angle == 180) { this.minimumSize = new Point(min.x, min.y); this.preferredSize = new Point(pref.x, pref.y); } else { // flip x and y sizes if rotating by 90 or 270 degrees this.minimumSize = new Point(min.y, min.x); this.preferredSize = new Point(pref.y, pref.x); } } private SidewaysIterator(SidewaysIterator that) { this.device = that.device; this.target = that.target.copy(); this.angle = that.angle; this.minimumSize = that.minimumSize; this.preferredSize = that.preferredSize; } private static int checkAngle(int angle) { switch (angle) { case 90: case 180: case 270: break; default: PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Angle must be 90, 180, or 270"); //$NON-NLS-1$ } return angle; } public Point minimumSize() { return new Point(minimumSize.x, minimumSize.y); } public Point preferredSize() { return new Point(preferredSize.x, preferredSize.y); } public boolean hasNext() { return target.hasNext(); } public PrintPiece next(int width, int height) { PrintPiece target; if (angle == 180) target = PaperClips.next(this.target, width, height); else // flip width and height if rotating by 90 or 270 target = PaperClips.next(this.target, height, width); if (target == null) return null; Point size = target.getSize(); if (angle == 90 || angle == 270) size = new Point(size.y, size.x); return new RotatePiece(device, target, angle, size); } public PrintIterator copy() { return new SidewaysIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/SimplePageDecoration.java000066400000000000000000000034361160731654500255370ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips; import net.sf.paperclips.internal.Util; /** * A PageDecoration which displays the same decoration on every page (ignoring * the page number). *

* Typically the page number will be in either the header or footer, but not in * both. Often the page number is the only thing that changes from page to page * in a header. Use this class for a header or footer which does not display the * page number. * * @author Matthew Hall */ public class SimplePageDecoration implements PageDecoration { private final Print print; /** * Constructs a BasicPageDecoration. * * @param print * the decoration which will appear on every page. */ public SimplePageDecoration(Print print) { Util.notNull(print); this.print = print; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((print == null) ? 0 : print.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SimplePageDecoration other = (SimplePageDecoration) obj; if (print == null) { if (other.print != null) return false; } else if (!print.equals(other.print)) return false; return true; } public Print createPrint(PageNumber pageNumber) { return print; } } swt-paperclips-1.0.4/src/net/sf/paperclips/StyledTextPrint.java000066400000000000000000000173401160731654500246260ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import java.util.ArrayList; import java.util.List; import net.sf.paperclips.internal.PrintSizeStrategy; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; /** * A class for printing styled text. Text of varying size and style are aligned * along the baseline. * * @author Matthew Hall */ public class StyledTextPrint implements Print { private final List elements = new ArrayList(); private TextStyle style = new TextStyle(); /** * Constructs a new StyledTextPrint. */ public StyledTextPrint() { } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((elements == null) ? 0 : elements.hashCode()); result = prime * result + ((style == null) ? 0 : style.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; StyledTextPrint other = (StyledTextPrint) obj; if (elements == null) { if (other.elements != null) return false; } else if (!elements.equals(other.elements)) return false; if (style == null) { if (other.style != null) return false; } else if (!style.equals(other.style)) return false; return true; } /** * Sets the text style that will be applied to text added through the * {@link #append(String)} * * @param style * the new text style. * @return this StyledTextPrint, for chaining method calls. */ public StyledTextPrint setStyle(TextStyle style) { Util.notNull(style); this.style = style; return this; } /** * Appends the given text to the end of the document, using the default * style. This method is equivalent to calling append(text, getStyle()). * * @param text * the text to append. * @return this StyledTextPrint, for chaining method calls. */ public StyledTextPrint append(String text) { return append(text, style); } /** * Appends the given text to the end of the document, using the given style. * * @param text * the text to append. * @param style * the text style. * @return this StyledTextPrint, for chaining method calls. */ public StyledTextPrint append(String text, TextStyle style) { TextPrint textPrint = new TextPrint(text, style); textPrint.setWordSplitting(false); return append(textPrint); } /** * Appends a line break to the document. If a line break produces a blank * line, that line will take the height of the font in the default text * style. * * @return this StyledTextPrint, for chaining method calls. */ public StyledTextPrint newline() { return append(new LineBreakPrint(style.getFontData())); } /** * Appends the given element to the document. * * @param element * the element to append. * @return this StyledTextPrint, for chaining method calls. */ public StyledTextPrint append(Print element) { elements.add(element); return this; } public PrintIterator iterator(Device device, GC gc) { return new StyledTextIterator((Print[]) elements .toArray(new Print[elements.size()]), device, gc); } } class StyledTextIterator implements PrintIterator { private final PrintIterator[] elements; private final Point minimumSize; private final Point preferredSize; private int cursor = 0; StyledTextIterator(Print[] elements, Device device, GC gc) { this.elements = new PrintIterator[elements.length]; for (int i = 0; i < elements.length; i++) this.elements[i] = elements[i].iterator(device, gc); minimumSize = computeSize(PrintSizeStrategy.MINIMUM); preferredSize = computeSize(PrintSizeStrategy.PREFERRED); } private StyledTextIterator(StyledTextIterator that) { elements = new PrintIterator[that.elements.length - that.cursor]; minimumSize = that.minimumSize; preferredSize = that.preferredSize; for (int i = 0; i < elements.length; i++) elements[i] = that.elements[that.cursor + i].copy(); } private Point computeSize(PrintSizeStrategy strategy) { Point result = new Point(0, 0); for (int i = 0; i < elements.length; i++) { Point current = strategy.computeSize(elements[i]); result.x = Math.max(result.x, current.x); result.y = Math.max(result.y, current.y); } return result; } public Point minimumSize() { return new Point(minimumSize.x, minimumSize.y); } public Point preferredSize() { return new Point(preferredSize.x, preferredSize.y); } public boolean hasNext() { advanceCursor(); return cursor < elements.length; } public PrintPiece next(int width, int height) { if (width < 0 || height < 0) return null; int y = 0; List rows = new ArrayList(); while (y < height) { PrintPiece row = nextRow(width, height - y); if (row == null) break; rows.add(new CompositeEntry(row, new Point(0, y))); y += row.getSize().y; } if (rows.size() == 0) return null; return new CompositePiece(rows); } private PrintPiece nextRow(int width, int height) { int x = 0; int maxAscent = 0; int maxDescent = 0; final int backupCursor = cursor; final List backup = new ArrayList(); List rowElements = new ArrayList(); while (hasNext()) { // hasNext advances cursor internally PrintIterator element = elements[cursor]; Point preferredSize = element.preferredSize(); if (preferredSize.y > height) break; PrintIterator elementBackup = element.copy(); PrintPiece piece = PaperClips.next(element, width - x, preferredSize.y); if (piece == null) break; rowElements.add(piece); backup.add(elementBackup); maxAscent = Math.max(maxAscent, getAscent(piece)); maxDescent = Math.max(maxDescent, getDescent(piece)); if (maxAscent + maxDescent > height) { restoreBackup(backupCursor, backup); return null; } if (element.hasNext()) break; x += piece.getSize().x; } return createRowResult(maxAscent, rowElements); } private PrintPiece createRowResult(int rowAscent, List rowElements) { int x; if (rowElements.size() == 0) return null; x = 0; for (int i = 0; i < rowElements.size(); i++) { PrintPiece piece = (PrintPiece) rowElements.get(i); int ascent = getAscent(piece); rowElements.set(i, new CompositeEntry(piece, new Point(x, rowAscent - ascent))); x += piece.getSize().x; } return new CompositePiece(rowElements); } private void restoreBackup(final int backupCursor, final List backup) { for (int i = 0; i < backup.size(); i++) elements[backupCursor + i] = (PrintIterator) backup.get(i); cursor = backupCursor; } private int getAscent(PrintPiece piece) { if (piece instanceof TextPrintPiece) return ((TextPrintPiece) piece).getAscent(); return piece.getSize().y; } private int getDescent(PrintPiece piece) { if (piece instanceof TextPrintPiece) return piece.getSize().y - ((TextPrintPiece) piece).getAscent(); return 0; } private void advanceCursor() { while (cursor < elements.length && !elements[cursor].hasNext()) cursor++; } public PrintIterator copy() { return new StyledTextIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/TextPiece.java000066400000000000000000000077521160731654500234000ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.ResourcePool; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; class TextPiece implements TextPrintPiece { private final Point size; private final String[] lines; private final TextStyle style; private final int ascent; private final ResourcePool resources; TextPiece(Device device, TextStyle style, String[] text, Point size, int ascent) { Util.notNull(device, size, style); Util.noNulls(text); this.size = size; this.lines = text; this.style = style; this.ascent = ascent; this.resources = ResourcePool.forDevice(device); } public Point getSize() { return new Point(size.x, size.y); } public int getAscent() { return ascent; } public void paint(final GC gc, final int x, final int y) { Font oldFont = gc.getFont(); Color oldForeground = gc.getForeground(); Color oldBackground = gc.getBackground(); final int width = getSize().x; final int align = style.getAlignment(); try { boolean transparent = initGC(gc); FontMetrics fm = gc.getFontMetrics(); int lineHeight = fm.getHeight(); boolean strikeout = style.getStrikeout(); boolean underline = style.getUnderline(); int lineThickness = Math.max(1, fm.getDescent() / 3); int strikeoutOffset = fm.getLeading() + fm.getAscent() / 2; int underlineOffset = ascent + lineThickness; for (int i = 0; i < lines.length; i++) { String line = lines[i]; int lineWidth = gc.stringExtent(line).x; int offset = getHorzAlignmentOffset(align, lineWidth, width); gc.drawString(lines[i], x + offset, y + lineHeight * i, transparent); if (strikeout || underline) { Color saveBackground = gc.getBackground(); gc.setBackground(gc.getForeground()); if (strikeout) gc.fillRectangle(x + offset, y + lineHeight * i + strikeoutOffset, lineWidth, lineThickness); if (underline) gc.fillRectangle(x + offset, y + lineHeight * i + underlineOffset, lineWidth, lineThickness); gc.setBackground(saveBackground); } } } finally { restoreGC(gc, oldFont, oldForeground, oldBackground); } } private boolean initGC(final GC gc) { initGCFont(gc); initGCForeground(gc); boolean transparent = initGCBackground(gc); return transparent; } private void restoreGC(final GC gc, Font font, Color foreground, Color background) { gc.setFont(font); gc.setForeground(foreground); gc.setBackground(background); } private int getHorzAlignmentOffset(int align, int lineWidth, int totalWidth) { if (align == SWT.CENTER) return (totalWidth - lineWidth) / 2; else if (align == SWT.RIGHT) return totalWidth - lineWidth; return 0; } private boolean initGCBackground(GC gc) { Color background = resources.getColor(style.getBackground()); boolean transparent = (background == null); if (!transparent) gc.setBackground(background); return transparent; } private void initGCForeground(GC gc) { Color foreground = resources.getColor(style.getForeground()); if (foreground != null) gc.setForeground(foreground); } private void initGCFont(GC gc) { Font font = resources.getFont(style.getFontData()); if (font != null) gc.setFont(font); } public void dispose() { } // Shared resources, nothing to dispose. }swt-paperclips-1.0.4/src/net/sf/paperclips/TextPrint.java000066400000000000000000000355461160731654500234510ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import java.util.ArrayList; import java.util.List; import net.sf.paperclips.internal.ResourcePool; import net.sf.paperclips.internal.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; /** * A Print for displaying text. *

* TextPrints are never greedy with layout space, even with center- or * right-alignment. (Greedy prints take up all the available space on the page.) * Therefore, when center- or right-alignment is required, it is necessary to * wrap the text in a Print which will enforce the same alignment. Usually this * is a center:default:grow or right:default:grow column in a GridPrint. * * @author Matthew Hall */ public class TextPrint implements Print { /** The default text for a TextPrint. Value is "". */ public static final String DEFAULT_TEXT = ""; //$NON-NLS-1$ /** The default font data for a TextPrint. Value is device-dependent. */ public static final FontData DEFAULT_FONT_DATA = new FontData(); /** The default alignment for TextPrint. Value is SWT.LEFT. */ public static final int DEFAULT_ALIGN = SWT.LEFT; private static final TextStyle DEFAULT_STYLE = new TextStyle(); String text; TextStyle style; boolean wordSplitting; /** * Constructs a TextPrint with the default properties. */ public TextPrint() { this(DEFAULT_TEXT); } /** * Constructs a TextPrint with the given text. * * @param text * the text to print. */ public TextPrint(String text) { this(text, DEFAULT_STYLE); } /** * Constructs a TextPrint with the given text and font data. * * @param text * the text to print. * @param fontData * the font that will be used to print the text. */ public TextPrint(String text, FontData fontData) { this(text, DEFAULT_STYLE.font(fontData)); } /** * Constructs a TextPrint with the give text and alignment. * * @param text * the text to print. * @param align * the horizontal text alignment. Must be one of {@link SWT#LEFT} * , {@link SWT#CENTER} or {@link SWT#RIGHT}. */ public TextPrint(String text, int align) { this(text, DEFAULT_STYLE.align(align)); } /** * Constructs a TextPrint with the given text, font data, and alignment. * * @param text * the text to print. * @param fontData * the font that will be used to print the text. * @param align * the horizontal text alignment. Must be one of {@link SWT#LEFT} * , {@link SWT#CENTER} or {@link SWT#RIGHT}. */ public TextPrint(String text, FontData fontData, int align) { this(text, DEFAULT_STYLE.font(fontData).align(align)); } /** * Constructs a TextPrint with the given text and style. * * @param text * the text to print. * @param style * the style to apply to the text. */ public TextPrint(String text, TextStyle style) { Util.notNull(text, style); this.text = text; this.style = style; this.wordSplitting = true; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((style == null) ? 0 : style.hashCode()); result = prime * result + ((text == null) ? 0 : text.hashCode()); result = prime * result + (wordSplitting ? 1231 : 1237); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TextPrint other = (TextPrint) obj; if (style == null) { if (other.style != null) return false; } else if (!style.equals(other.style)) return false; if (text == null) { if (other.text != null) return false; } else if (!text.equals(other.text)) return false; if (wordSplitting != other.wordSplitting) return false; return true; } /** * Returns the text that will be printed. * * @return the text that will be printed. */ public String getText() { return text; } /** * Sets the text that will be printed. * * @param text * the text to print. */ public void setText(String text) { Util.notNull(text); this.text = text; } /** * Returns the text style. * * @return the text style. */ public TextStyle getStyle() { return style; } /** * Sets the text style to the argument. * * @param style * the new text style. */ public void setStyle(TextStyle style) { Util.notNull(style); this.style = style; } /** * Returns the font that will be used to print the text. * * @return the font that will be used to print the text. */ public FontData getFontData() { return style.getFontData(); } /** * Sets the font that will be used to print the text. * * @param fontData * the font that will be used to print the text. */ public void setFontData(FontData fontData) { setStyle(style.font(fontData)); } /** * Returns the horizontal text alignment. Possible values include * {@link SWT#LEFT}, {@link SWT#CENTER} or {@link SWT#RIGHT}. * * @return the horizontal text alignment. * @deprecated Use {@link #getAlignment()} instead. */ public int getAlign() { return style.getAlignment(); } /** * Sets the horizontal text alignment. * * @param alignment * the horizontal text alignment. Must be one of {@link SWT#LEFT} * , {@link SWT#CENTER} or {@link SWT#RIGHT}. * @deprecated Use {@link #setAlignment(int)} instead. */ public void setAlign(int alignment) { setAlignment(alignment); } /** * Returns the horizontal text alignment. Possible values include * {@link SWT#LEFT}, {@link SWT#CENTER} or {@link SWT#RIGHT}. * * @return the horizontal text alignment. */ public int getAlignment() { return style.getAlignment(); } /** * Sets the horizontal text alignment. * * @param alignment * the horizontal text alignment. Must be one of {@link SWT#LEFT} * , {@link SWT#CENTER} or {@link SWT#RIGHT}. */ public void setAlignment(int alignment) { setStyle(style.align(alignment)); } /** * Returns the foreground color. * * @return the foreground color. * @deprecated Use {@link #getForeground()} instead. */ public RGB getRGB() { return getForeground(); } /** * Sets the foreground color to the argument. * * @param foreground * the new foreground color. * @deprecated Use {@link #setForeground(RGB)} instead. */ public void setRGB(RGB foreground) { setForeground(foreground); } /** * Returns the foreground color. A null value indicates that the foreground * color is inherited. * * @return the foreground color. */ public RGB getForeground() { return style.getForeground(); } /** * Sets the foreground color to the argument. * * @param foreground * the new foreground color. A null value causes the foreground * color to be inherited. */ public void setForeground(RGB foreground) { setStyle(style.foreground(foreground)); } /** * Returns the background color. A null value indicates that the background * is transparent. * * @return the background color. */ public RGB getBackground() { return style.getBackground(); } /** * Sets the background color to the argument. * * @param background * the new background color. A null value causes the background * to be transparent. */ public void setBackground(RGB background) { style = style.background(background); } /** * Returns the underline flag. * * @return the underline flag. */ public boolean getUnderline() { return style.getUnderline(); } /** * Sets the underline flag to the argument. * * @param underline * the underline flag. */ public void setUnderline(boolean underline) { style = style.underline(underline); } /** * Returns the strikout flag. * * @return the strikout flag. */ public boolean getStrikeout() { return style.getStrikeout(); } /** * Sets the strikeout flag to the argument. * * @param strikeout * the strikeout flag. */ public void setStrikeout(boolean strikeout) { style = style.strikeout(strikeout); } /** * Returns whether word splitting is enabled. Default is true. * * @return whether word splitting is enabled. */ public boolean getWordSplitting() { return wordSplitting; } /** * Sets whether word splitting is enabled. * * @param wordBreaking * whether to allow word splitting. */ public void setWordSplitting(boolean wordBreaking) { this.wordSplitting = wordBreaking; } public PrintIterator iterator(Device device, GC gc) { return new TextIterator(this, device, gc); } } class TextIterator extends AbstractIterator { final String text; final String[] lines; final TextStyle style; final boolean wordSplitting; final Point minimumSize; final Point preferredSize; int row; int col; TextIterator(TextPrint print, Device device, GC gc) { super(device, gc); this.text = print.text; this.lines = print.text.split("(\r)?\n"); //$NON-NLS-1$ this.style = print.style; this.wordSplitting = print.wordSplitting; this.minimumSize = maxExtent(text.split("\\s")); //$NON-NLS-1$ this.preferredSize = maxExtent(lines); this.row = 0; this.col = 0; } TextIterator(TextIterator that) { super(that); this.text = that.text; this.lines = that.lines; this.style = that.style; this.wordSplitting = that.wordSplitting; this.minimumSize = that.minimumSize; this.preferredSize = that.preferredSize; this.row = that.row; this.col = that.col; } public boolean hasNext() { return row < lines.length; } public PrintPiece next(int width, int height) { if (!hasNext()) PaperClips.error("No more content."); //$NON-NLS-1$ Font oldFont = initGC(); PrintPiece result = internalNext(width, height); restoreGC(oldFont); return result; } private PrintPiece internalNext(int width, int height) { FontMetrics fm = gc.getFontMetrics(); final int lineHeight = fm.getHeight(); if (height < lineHeight) return null; final int maxLines = height / lineHeight; String[] nextLines = nextLines(width, maxLines); if (nextLines.length == 0) return null; int maxWidth = maxExtent(nextLines).x; Point size = new Point(maxWidth, nextLines.length * lineHeight); int ascent = fm.getAscent() + fm.getLeading(); return new TextPiece(device, style, nextLines, size, ascent); } private Font initGC() { Font oldFont = gc.getFont(); FontData fontData = style.getFontData(); if (fontData != null) gc.setFont(ResourcePool.forDevice(device).getFont(fontData)); return oldFont; } private void restoreGC(Font oldFont) { gc.setFont(oldFont); } private String[] nextLines(final int width, final int maxLines) { List nextLines = new ArrayList(Math.min(lines.length, maxLines)); while ((nextLines.size() < maxLines) && (row < lines.length)) { String line = lines[row].substring(col); // Find out how much text will fit on one line. int charCount = findLineBreak(gc, line, width); // If none of the text could fit in the current line, terminate this // iteration. if (line.length() > 0 && charCount == 0) break; // Get the text that fits on this line. String thisLine = line.substring(0, charCount); nextLines.add(thisLine); // Move cursor past the text we just consumed. col += charCount; skipWhitespace(); advanceToNextRowIfCurrentRowCompleted(); } return (String[]) nextLines.toArray(new String[nextLines.size()]); } private void skipWhitespace() { while (col < lines[row].length() && Character.isWhitespace(lines[row].charAt(col))) col++; } private void advanceToNextRowIfCurrentRowCompleted() { if (col >= lines[row].length()) { row++; col = 0; } } public Point minimumSize() { return new Point(minimumSize.x, minimumSize.y); } public Point preferredSize() { return new Point(preferredSize.x, preferredSize.y); } private Point maxExtent(String[] text) { Font oldFont = gc.getFont(); try { initGC(); FontMetrics fm = gc.getFontMetrics(); int maxWidth = 0; for (int i = 0; i < text.length; i++) { String textPiece = text[i]; maxWidth = Math.max(maxWidth, gc.stringExtent(textPiece).x); } return new Point(maxWidth, fm.getHeight()); } finally { restoreGC(oldFont); } } private int findLineBreak(GC gc, String text, int width) { // Offsets within the string int loIndex = 0; int hiIndex = text.length(); // Pixel width of entire string int pixelWidth = gc.stringExtent(text).x; // Does the whole string fit? if (pixelWidth <= width) // I'll take it return hiIndex; // Do a binary search to find the maximum characters that will fit // within the given width. while (loIndex < hiIndex) { int midIndex = (loIndex + hiIndex + 1) / 2; int midWidth = gc.stringExtent(text.substring(0, midIndex)).x; if (midWidth < width) // don't add 1, the next character could make it too big loIndex = midIndex; else if (midWidth > width) // subtract 1, we already know midIndex makes it too big hiIndex = midIndex - 1; else { // perfect fit loIndex = hiIndex = midIndex; } } return findWordBreak(text, loIndex); } int findWordBreak(String text, int maxLength) { // If the max length is the string length, no break // (we mainly check this to avoid an exception in for-loop) if (maxLength == text.length()) return maxLength; // Otherwise, break string at the last whitespace at or before // maxLength. for (int i = maxLength; i >= 0; i--) if (Character.isWhitespace(text.charAt(i))) return i; // No whitespace? Break at max length (if word breaking is allowed) if (wordSplitting) return maxLength; return 0; } public PrintIterator copy() { return new TextIterator(this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/TextPrintPiece.java000066400000000000000000000011701160731654500244010ustar00rootroot00000000000000/* * 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 net.sf.paperclips; interface TextPrintPiece extends PrintPiece { /** * Returns the ascent of the first line of text, in pixels. * * @return the ascent of the first line of text, in pixels. */ public int getAscent(); } swt-paperclips-1.0.4/src/net/sf/paperclips/TextStyle.java000066400000000000000000000304031160731654500234400ustar00rootroot00000000000000/* * 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 net.sf.paperclips; import net.sf.paperclips.internal.PaperClipsUtil; import net.sf.paperclips.internal.SWTUtil; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.RGB; /** * Defines a set of styles that can be applied to text. Instances of this class * are immutable. * * @author Matthew Hall */ public class TextStyle { private FontData fontData; private RGB foreground; private RGB background; private int alignment; private boolean underline; private boolean strikeout; /** * Constructs a new TextStyle with default font (device-dependent), black * foreground, transparent background, default alignment, and the strikeout * and underline flags set to false. */ public TextStyle() { fontData = SWTUtil.copy(TextPrint.DEFAULT_FONT_DATA); foreground = new RGB(0, 0, 0); background = null; alignment = TextPrint.DEFAULT_ALIGN; underline = false; strikeout = false; } private TextStyle(TextStyle that) { this.fontData = that.fontData; this.foreground = that.foreground; this.background = that.background; this.alignment = that.alignment; this.underline = that.underline; this.strikeout = that.strikeout; } private TextStyle internalFont(FontData fontData) { TextStyle result = new TextStyle(this); result.fontData = fontData; return result; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + alignment; result = prime * result + ((background == null) ? 0 : background.hashCode()); result = prime * result + ((fontData == null) ? 0 : fontData.hashCode()); result = prime * result + ((foreground == null) ? 0 : foreground.hashCode()); result = prime * result + (strikeout ? 1231 : 1237); result = prime * result + (underline ? 1231 : 1237); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TextStyle other = (TextStyle) obj; if (alignment != other.alignment) return false; if (background == null) { if (other.background != null) return false; } else if (!background.equals(other.background)) return false; if (fontData == null) { if (other.fontData != null) return false; } else if (!fontData.equals(other.fontData)) return false; if (foreground == null) { if (other.foreground != null) return false; } else if (!foreground.equals(other.foreground)) return false; if (strikeout != other.strikeout) return false; if (underline != other.underline) return false; return true; } /** * Returns a copy of this TextStyle, with the font changed to the font * described by the arguments. This method is equivalent to calling font( * new FontData( name, height, style ) ). * * @param name * the name of the font (must not be null) * @param height * the font height in points * @param style * a bit or combination of NORMAL, BOLD, ITALIC * @return a copy of this TextStyle, with the font changed to the font * described by the arguments. */ public TextStyle font(String name, int height, int style) { return internalFont(new FontData(name, height, style)); } /** * Returns a copy of this TextStyle, with the font changed to the argument. * * @param fontData * the new font. A null value causes the font to be inherited * from the enclosing elements of the document. * @return a copy of this TextStyle, with the font changed to the argument. */ public TextStyle font(FontData fontData) { return internalFont(SWTUtil.copy(fontData)); } /** * Returns a copy of this TextStyle, with the font name changed to the * argument. * * @param name * the new font name (must not be null) * @return a copy of this TextStyle, with the font name changed to the * argument. */ public TextStyle fontName(String name) { return font(name, fontData.getHeight(), fontData.getStyle()); } /** * Returns a copy of this TextStyle, with the font height changed to the * argument. * * @param height * the new font height in points * @return a copy of this TextStyle, with the font height changed to the * argument. */ public TextStyle fontHeight(int height) { return font(fontData.getName(), height, fontData.getStyle()); } /** * Returns a copy of this TextStyle, with the font style changed to the * argument. * * @param style * a bit or combination of NORMAL, BOLD, ITALIC * @return a copy of this TextStyle, with the font style changed to the * argument. */ public TextStyle fontStyle(int style) { return font(fontData.getName(), fontData.getHeight(), style); } private TextStyle internalForeground(RGB foreground) { TextStyle result = new TextStyle(this); result.foreground = foreground; return result; } /** * Returns a copy of this TextStyle, with the foreground changed to the * argument. * * @param foreground * the new foreground. A null value causes the foreground to be * inherited from the enclosing elements of the document. * @return a copy of this TextStyle, with the foreground changed to the * argument. */ public TextStyle foreground(RGB foreground) { return internalForeground(SWTUtil.copy(foreground)); } /** * Returns a copy of this TextStyle, with the foreground changed to the * color described by the arguments. This method is equivalent to calling * foreground(new RGB(red, green, blue)). * * @param red * the red component of the new foreground color * @param green * the green component of the new foreground color * @param blue * the blue component of the new foreground color * @return a copy of this TextStyle, with the foreground changed to the * color described by the arguments. */ public TextStyle foreground(int red, int green, int blue) { return internalForeground(new RGB(red, green, blue)); } /** * Returns a copy of this TextStyle, with the foreground changed to the * color described by the argument. * * @param rgb * an integer containing the red, green and blue components in * the 0xFF0000, 0x00FF00, and 0x0000FF positions, respectively. * @return a copy of this TextStyle, with the foreground changed to the * color described by the argument. */ public TextStyle foreground(int rgb) { return internalForeground(SWTUtil.deriveRGB(rgb)); } private TextStyle internalBackground(RGB background) { TextStyle result = new TextStyle(this); result.background = background; return result; } /** * Returns a copy of this TextStyle, with the background changed to the * argument. * * @param background * the new background. A null value causes the text background to * be transparent. * @return a copy of this TextStyle, with the background changed to the * argument. */ public TextStyle background(RGB background) { return internalBackground(SWTUtil.copy(background)); } /** * Returns a copy of this TextStyle, with the background changed to the * color described by the arguments. This method is equivalent to calling * background(new RGB(red, green, blue) * * @param red * the red component of the new background color * @param green * the green component of the new background color * @param blue * the blue component of the new background color * @return a copy of this TextStyle, with the background changed to the * color described by the arguments. */ public TextStyle background(int red, int green, int blue) { return internalBackground(new RGB(red, green, blue)); } /** * Returns a copy of this TextStyle, with the background changed to the * color described by the argument. * * @param rgb * an integer containing the red, green and blue components in * the 0xFF0000, 0x00FF00, and 0x0000FF positions, respectively. * @return a copy of this TextStyle, with the background changed to the * color described by the argument. */ public TextStyle background(int rgb) { return internalBackground(SWTUtil.deriveRGB(rgb)); } /** * Returns a copy of this TextStyle, with the alignment changed to the * argument. * * @param alignment * the new alignment. Must be one of SWT.LEFT, SWT.CENTER, or * SWT.RIGHT. Invalid values will be changed to SWT.LEFT. * @return a copy of this TextStyle, with the alignment changed to the * argument. */ public TextStyle align(int alignment) { TextStyle result = new TextStyle(this); result.alignment = PaperClipsUtil.firstMatch(alignment, new int[] { SWT.LEFT, SWT.CENTER, SWT.RIGHT }, SWT.LEFT); return result; } /** * Returns a copy of this TextStyle, with the underline flag set to true. * * @return a copy of this TextStyle, with the underline flag set to true. */ public TextStyle underline() { return underline(true); } /** * Returns a copy of this TextStyle, with the underline flag set to the * argument. * * @param underline * the new underline flag. * @return a copy of this TextStyle, with the underline flag set to the * argument. */ public TextStyle underline(boolean underline) { TextStyle result = new TextStyle(this); result.underline = underline; return result; } /** * Returns a copy of this TextStyle, with the strikeout flag set to true. * * @return a copy of this TextStyle, with the strikeout flag set to true. */ public TextStyle strikeout() { return strikeout(true); } /** * Returns a copy of this TextStyle, with the strikeout flag set to the * argument. * * @param strikeout * the new strikeout flag. * @return a copy of this TextStyle, with the strikeout flag set to the * argument. */ public TextStyle strikeout(boolean strikeout) { TextStyle result = new TextStyle(this); result.strikeout = strikeout; return result; } /** * Returns the font applied to the text. * * @return the font applied to the text. */ public FontData getFontData() { return SWTUtil.copy(fontData); } /** * Returns the text foreground color. A null value indicates that the * foreground color will be inherited from the enclosing elements of the * document. * * @return the text foreground color. */ public RGB getForeground() { return SWTUtil.copy(foreground); } /** * Returns the text background color. A null value indicates that the * background will be transparent. * * @return the text background color. A null value indicates that the * background will be transparent. */ public RGB getBackground() { return SWTUtil.copy(background); } /** * Returns the text alignment. Possible values include SWT.LEFT, SWT.CENTER, * or SWT.RIGHT. * * @return the text alignment. */ public int getAlignment() { return alignment; } /** * Returns the underline flag. * * @return the underline flag. */ public boolean getUnderline() { return underline; } /** * Returns the strikeout flag. * * @return the strikeout flag. */ public boolean getStrikeout() { return strikeout; } /** * Returns a TextPrint of the given text in this text style * * @param text * the text * @return a TextPrint of the given text in this text style */ public TextPrint create(String text) { return new TextPrint(text, this); } }swt-paperclips-1.0.4/src/net/sf/paperclips/decorator/000077500000000000000000000000001160731654500226125ustar00rootroot00000000000000swt-paperclips-1.0.4/src/net/sf/paperclips/decorator/BackgroundDecorator.java000066400000000000000000000021421160731654500273760ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips.decorator; import net.sf.paperclips.BackgroundPrint; import net.sf.paperclips.Print; import net.sf.paperclips.internal.Util; import org.eclipse.swt.graphics.RGB; /** * Decorates prints with a background color. * * @author Matthew Hall */ public class BackgroundDecorator implements PrintDecorator { private final RGB background; /** * Constructs a BackgroundDecorator with the given background. * * @param background * the background color. */ public BackgroundDecorator(RGB background) { Util.notNull(background); this.background = background; } public Print decorate(Print target) { return new BackgroundPrint(target, background); } } swt-paperclips-1.0.4/src/net/sf/paperclips/decorator/BorderDecorator.java000066400000000000000000000020601160731654500265330ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips.decorator; import net.sf.paperclips.Border; import net.sf.paperclips.BorderPrint; import net.sf.paperclips.Print; import net.sf.paperclips.internal.Util; /** * Decorates prints with a border. * * @author Matthew Hall * @see BorderPrint * @see Border */ public class BorderDecorator implements PrintDecorator { private final Border border; /** * Constructs a BorderDecorator. * * @param border * the initial border */ public BorderDecorator(Border border) { Util.notNull(border); this.border = border; } public Print decorate(Print target) { return new BorderPrint(target, border); } } swt-paperclips-1.0.4/src/net/sf/paperclips/decorator/CompoundDecorator.java000066400000000000000000000024151160731654500271060ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips.decorator; import net.sf.paperclips.Print; import net.sf.paperclips.internal.Util; /** * Decorates prints with multiple decorators. * * @author Matthew Hall */ public class CompoundDecorator implements PrintDecorator { private final PrintDecorator[] decorators; /** * Constructs a CompoundDecorator. * * @param decorators * the decorators, in order from innermost to outermost. */ public CompoundDecorator(PrintDecorator[] decorators) { Util.noNulls(decorators); this.decorators = defensiveCopy(decorators); } private PrintDecorator[] defensiveCopy(PrintDecorator[] decorators) { return (PrintDecorator[]) decorators.clone(); } public Print decorate(Print target) { Print result = target; for (int i = 0; i < decorators.length; i++) result = decorators[i].decorate(target); return result; } } swt-paperclips-1.0.4/src/net/sf/paperclips/decorator/PrintDecorator.java000066400000000000000000000017731160731654500264240ustar00rootroot00000000000000/* * Copyright (c) 2006 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 net.sf.paperclips.decorator; import net.sf.paperclips.Print; /** * Interface for wrapping a print in a decoration. This interface is useful for * applying decorations uniformly without having to explicitly call constructors * for each item being decorated. * * @author Matthew Hall */ public interface PrintDecorator { /** * Wraps the target in a decoration. The decoration depends on the runtime * class of the decorator. * * @param target * the print to wrap with a decoration. * @return the target wrapped in a decoration. */ public Print decorate(Print target); } swt-paperclips-1.0.4/src/net/sf/paperclips/decorator/package.html000066400000000000000000000001651160731654500250750ustar00rootroot00000000000000 Decorators aid in applying styles uniformly across across documents. swt-paperclips-1.0.4/src/net/sf/paperclips/internal/000077500000000000000000000000001160731654500224445ustar00rootroot00000000000000swt-paperclips-1.0.4/src/net/sf/paperclips/internal/PaperClipsUtil.java000066400000000000000000000134761160731654500262220ustar00rootroot00000000000000/* * Copyright (c) 2007-2008 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 net.sf.paperclips.internal; import java.util.Iterator; import java.util.List; import net.sf.paperclips.PrintPiece; /** * Convenience methods specific to PaperClips * * @author Matthew Hall */ public class PaperClipsUtil { private PaperClipsUtil() { } // no instances /** * Disposes the print piece if not null. * * @param piece * the print piece to dispose. */ public static void dispose(final PrintPiece piece) { if (piece != null) piece.dispose(); } /** * Disposes the arguments that are not null. * * @param p1 * print piece to dispose * @param p2 * print piece to dispose */ public static void dispose(PrintPiece p1, PrintPiece p2) { dispose(p1); dispose(p2); } /** * Disposes the print pieces that are not null. * * @param pieces * array of print pieces to dispose. */ public static void dispose(final PrintPiece[] pieces) { if (pieces != null) for (int i = 0; i < pieces.length; i++) dispose(pieces[i]); } /** * Disposes the print pieces in the array from start (inclusive) to end * (exclusive). * * @param pages * array of print pieces to dispose. * @param start * the start index. * @param end * the end index. */ public static void dispose(PrintPiece[] pages, int start, int end) { for (int i = start; i < end; i++) pages[i].dispose(); } /** * Disposes the print pieces in the list. * * @param pages * list of print pieces to dispose. */ public static void dispose(List pages) { for (Iterator it = pages.iterator(); it.hasNext();) ((PrintPiece) it.next()).dispose(); pages.clear(); } /** * Disposes the print pieces that are not null. * * @param piece * a print piece to dispose * @param pieces * array of print pieces to dispose */ public static void dispose(PrintPiece piece, final PrintPiece[] pieces) { dispose(piece); dispose(pieces); } /** * Returns a copy of the array. * * @param array * the array to copy * @return a copy of the array. */ public static int[] copy(int[] array) { Util.notNull(array); return (int[]) array.clone(); } /** * Returns a deep copy of the array. * * @param array * the array to copy * @return a copy of the array. */ public static int[][] copy(int[][] array) { Util.notNull(array); int[][] result = (int[][]) array.clone(); for (int i = 0; i < result.length; i++) result[i] = copy(result[i]); return result; } /** * Returns the sum of all elements in the array. * * @param array * the array * @return the sum of all elements in the array. */ public static int sum(int[] array) { return PaperClipsUtil.sum(array, 0, array.length); } /** * Returns the sum of all elements in the array in the range * [start, start+count). * * @param array * the array containing the elements to add up. * @param start * the index of the first element to add. * @param count * the number of elements to add. * @return the sum of all elements in the array in the specified range. */ public static int sum(final int[] array, final int start, final int count) { Util.notNull(array); int result = 0; final int end = start + count; for (int i = start; i < end; i++) result += array[i]; return result; } /** * Returns the sum of all elements in the array at the given indices. * * @param array * the array of elements to add up. * @param indices * the indices of the elements in the array to add up. * @return the sum of all elements in the array at the given indices. */ public static int sumByIndex(final int[] array, final int[] indices) { Util.notNull(array); int result = 0; for (int i = 0; i < indices.length; i++) result += array[indices[i]]; return result; } /** * Converts the argument to an int[] array. * * @param list * a List of Integers. * @return a primitive int[] array. */ public static int[] toIntArray(List list) { final int[] array = new int[list.size()]; for (int i = 0; i < array.length; i++) array[i] = ((Integer) list.get(i)).intValue(); return array; } /** * Converts the argument to an int[][] array. * * @param list * a List of int[] arrays. * @return a primitive int[][] array. */ public static int[][] toIntIntArray(List list) { final int[][] array = new int[list.size()][]; for (int i = 0; i < array.length; i++) array[i] = (int[]) list.get(i); return array; } /** * Returns the first element in masks where (value & mask[index]) == * mask[index]. * * @param value * the value to match * @param masks * the possible values. * @param defaultMask * the value to return if no match is found. * @return the first value in possibleValues which is a bitwise match to * value, or 0 if none is found. */ public static int firstMatch(int value, int[] masks, int defaultMask) { Util.notNull(masks); for (int i = 0; i < masks.length; i++) { int mask = masks[i]; if ((value & mask) == mask) return mask; } return defaultMask; } } swt-paperclips-1.0.4/src/net/sf/paperclips/internal/PrintSizeStrategy.java000066400000000000000000000026311160731654500267630ustar00rootroot00000000000000/* * 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 net.sf.paperclips.internal; import net.sf.paperclips.PrintIterator; import org.eclipse.swt.graphics.Point; /** * The static instance members of this class aid in the calculation of prints * and help abstract out the minimum/preferred size concepts to simplify * algorithms. * * @author Matthew Hall */ public abstract class PrintSizeStrategy { /** Compute the minimum size */ public static final PrintSizeStrategy MINIMUM = new PrintSizeStrategy() { public Point computeSize(PrintIterator iter) { return iter.minimumSize(); } }; /** Compute the preferred size. */ public static final PrintSizeStrategy PREFERRED = new PrintSizeStrategy() { public Point computeSize(PrintIterator iter) { return iter.preferredSize(); } }; private PrintSizeStrategy() { } /** * Computes the size of the PrintIterator. * * @param print * the iterator * @return the computed size of the PrintIterator. */ public abstract Point computeSize(PrintIterator print); }swt-paperclips-1.0.4/src/net/sf/paperclips/internal/ResourcePool.java000066400000000000000000000055741160731654500257430ustar00rootroot00000000000000/* * Copyright (c) 2007-2008 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 net.sf.paperclips.internal; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import net.sf.paperclips.PaperClips; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.RGB; /** * Manages a pool of graphics resources for a graphics device (fonts, colors). * * @author Matthew Hall */ public class ResourcePool { private static Map devices = new WeakHashMap(); // Map /** * Returns a SharedGraphics which creates resources on the given device. * * @param device * the device which resources will be created on. * @return a SharedGraphics which creates resources on the given device. */ public synchronized static ResourcePool forDevice(Device device) { Util.notNull(device); notDisposed(device); ResourcePool sharedGraphics = (ResourcePool) devices.get(device); if (sharedGraphics == null) { sharedGraphics = new ResourcePool(device); devices.put(device, sharedGraphics); } return sharedGraphics; } private static void notDisposed(Device device) { if (device.isDisposed()) PaperClips.error(SWT.ERROR_DEVICE_DISPOSED); } private final Device device; private final Map fonts; // Map private final Map colors; // Map private ResourcePool(Device device) { this.device = device; this.fonts = new HashMap(); this.colors = new HashMap(); } /** * Returns a font for the passed in FontData. * * @param fontData * FontData describing the required font. * @return a font for the passed in FontData. */ public Font getFont(FontData fontData) { if (fontData == null) return null; notDisposed(device); Font font = (Font) fonts.get(fontData); if (font == null) { font = new Font(device, fontData); fonts.put(SWTUtil.copy(fontData), font); } return font; } /** * Returns a color for the passed in RGB. * * @param rgb * RGB describing the required color. * @return a color for the passed in RGB. */ public Color getColor(RGB rgb) { if (rgb == null) return null; notDisposed(device); Color color = (Color) colors.get(rgb); if (color == null) { color = new Color(device, rgb); colors.put(SWTUtil.copy(rgb), color); } return color; } }swt-paperclips-1.0.4/src/net/sf/paperclips/internal/SWTUtil.java000066400000000000000000000126111160731654500246230ustar00rootroot00000000000000/* * Copyright (c) 2008 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 net.sf.paperclips.internal; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.PaletteData; import org.eclipse.swt.graphics.RGB; /** * Utility methods for dealing with SWT objects * * @author Matthew Hall */ public class SWTUtil { /** * Returns a defensive copy of the passed in FontData. * * @param fontData * the FontData to copy. May be null. * @return a copy of the passed in FontData, or null if the argument was * null. */ public static FontData copy(FontData fontData) { return fontData == null ? null : new FontData(fontData.getName(), fontData.getHeight(), fontData.getStyle()); } /** * Returns a defensive copy of the passed in RGB. * * @param rgb * the RGB to copy. May be null. * @return a copy of the passed in RGB, or null if the argument was null. */ public static RGB copy(RGB rgb) { return rgb == null ? null : new RGB(rgb.red, rgb.green, rgb.blue); } /** * Returns an RGB representing the color described by the argument. *

* Sample colors:
* 0xFFFFFF: white
* 0x000000: black
* 0xFF0000: red
* 0x00FF00: green
* 0x0000FF: blue * * @param rgb * an integer containing the red, green and blue components in * the 0xFF0000, 0x00FF00, and 0x0000FF positions, respectively. * @return an RGB representing the color described by the argument. */ public static RGB deriveRGB(final int rgb) { int red = (rgb >> 16) & 0xFF; int green = (rgb >> 8) & 0xFF; int blue = rgb & 0xFF; return new RGB(red, green, blue); } /** * Returns whether the PaletteData arguments are equivalent. * * @param left * the left PaletteData * @param right * the right PaletteData * @return whether the PaletteData arguments are equivalent. */ public static boolean equal(PaletteData left, PaletteData right) { if (left == right) return true; if (left == null || right == null) return false; return left.isDirect == right.isDirect && left.blueMask == right.blueMask && left.blueShift == right.blueShift && left.greenMask == right.greenMask && left.greenShift == right.greenShift && left.redMask == right.redMask && left.redShift == right.redShift && Util.equal(left.colors, right.colors); } /** * Returns a hash code for the PaletteData. * * @param data * the PaletteData * @return a hash code for the PaletteData. */ public static int hashCode(PaletteData data) { final int prime = 31; int result = 1; result = prime * result + (data.isDirect ? 1231 : 1237); result = prime * result + data.blueMask; result = prime * result + data.blueShift; result = prime * result + data.greenMask; result = prime * result + data.greenShift; result = prime * result + data.redMask; result = prime * result + data.redShift; result = prime * result + hashCode(data.colors); return result; } private static int hashCode(Object[] 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 : array[index].hashCode()); } return result; } /** * Returns whether the ImageData arguments are equivalent. * * @param left * the left ImageData * @param right * the right ImageData * @return whether the ImageData arguments are equivalent. */ public static boolean equal(ImageData left, ImageData right) { if (left == right) return true; if (left == null || right == null) return false; if (left.width != right.width || left.height != right.height) return false; if (!equal(left.palette, right.palette)) return false; final int width = left.width; int[] leftPixels = new int[width]; int[] rightPixels = new int[width]; byte[] leftAlphas = new byte[width]; byte[] rightAlphas = new byte[width]; for (int y = 0; y < left.height; y++) { left.getAlphas(0, y, width, leftAlphas, 0); right.getAlphas(0, y, width, rightAlphas, 0); if (!Util.equal(leftAlphas, rightAlphas)) return false; left.getPixels(0, y, width, leftPixels, 0); right.getPixels(0, y, width, rightPixels, 0); if (!Util.equal(leftPixels, rightPixels)) { for (int x = 0; x < width; x++) { if (leftAlphas[x] != 0 && leftPixels[x] != rightPixels[x]) return false; } } } return true; } /** * Returns a hash code for the ImageData * * @param data * the ImageData * @return a hash code for the ImageData */ public static int hashCode(ImageData data) { final int prime = 31; int result = 1; result = prime * result + data.width; result = prime * result + data.height; result = prime * result + hashCode(data.palette); // Neglect pixel data return result; } } swt-paperclips-1.0.4/src/net/sf/paperclips/internal/Util.java000066400000000000000000000112021160731654500242200ustar00rootroot00000000000000/* * Copyright (c) 2007-2008 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 net.sf.paperclips.internal; import java.util.Arrays; import java.util.List; import net.sf.paperclips.PaperClips; import org.eclipse.swt.SWT; /** * General use convenience methods: null checking, equality * * @author Matthew Hall */ public class Util { /** * Returns whether the objects are of the same class. * * @param left * object to test * @param right * object to test * @return whether the objects are of the same class. */ public static boolean sameClass(Object left, Object right) { if (left == right) return true; if (left == null || right == null) return false; return left.getClass() == right.getClass(); } /** * Returns whether the arguments are equal. * * @param left * object to test * @param right * object to test * @return whether the arguments are equal. */ public static boolean equal(Object left, Object right) { if (!sameClass(left, right)) return false; if (left == right) return true; Class clazz = left.getClass(); if (clazz.isArray()) { Class componentType = clazz.getComponentType(); if (componentType.isPrimitive()) { if (componentType == Byte.TYPE) return Arrays.equals((byte[]) left, (byte[]) right); if (componentType == Short.TYPE) return Arrays.equals((short[]) left, (short[]) right); if (componentType == Integer.TYPE) return Arrays.equals((int[]) left, (int[]) right); if (componentType == Long.TYPE) return Arrays.equals((long[]) left, (long[]) right); if (componentType == Character.TYPE) return Arrays.equals((char[]) left, (char[]) right); if (componentType == Float.TYPE) return Arrays.equals((float[]) left, (float[]) right); if (componentType == Double.TYPE) return Arrays.equals((double[]) left, (double[]) right); if (componentType == Boolean.TYPE) return Arrays.equals((boolean[]) left, (boolean[]) right); } return equal((Object[]) left, (Object[]) right); } return left.equals(right); } private static boolean equal(Object[] left, Object[] right) { int length = left.length; if (length != right.length) return false; for (int i = 0; i < length; i++) if (!equal(left[i], right[i])) return false; return true; } /** * Returns whether the arguments are equal. * * @param left * double value to test * @param right * double value to test * @return whether the arguments are equal. */ public static boolean equal(double left, double right) { return Double.doubleToLongBits(left) == Double.doubleToLongBits(right); } /** * Triggers a SWT.ERROR_NULL_ARGUMENT exception if the argument or any of * its elements is null. * * @param list * a list to test for null elements. */ public static void noNulls(List list) { notNull(list); if (list.contains(null)) PaperClips.error(SWT.ERROR_NULL_ARGUMENT); } /** * Triggers a SWT.ERROR_NULL_ARGUMENT exception if the argument or any of * its elements is null. * * @param objs * an array to test for null elements. */ public static void noNulls(Object[] objs) { notNull(objs); for (int i = 0; i < objs.length; i++) notNull(objs[i]); } /** * Triggers a SWT.ERROR_NULL_ARGUMENT exception if the argument is null. * * @param obj * the object to test for null. */ public static void notNull(Object obj) { if (obj == null) PaperClips.error(SWT.ERROR_NULL_ARGUMENT); } /** * Triggers a SWT.ERROR_NULL_ARGUMENT exception if any argument is null. * * @param o1 * an object to test for null. * @param o2 * an object to test for null. */ public static void notNull(Object o1, Object o2) { notNull(o1); notNull(o2); } /** * Triggers a SWT.ERROR_NULL_ARGUMENT exception if any argument is null. * * @param o1 * an object to test for null. * @param o2 * an object to test for null. * @param o3 * an object to test for null. */ public static void notNull(Object o1, Object o2, Object o3) { notNull(o1); notNull(o2); notNull(o3); } } swt-paperclips-1.0.4/src/net/sf/paperclips/messages.properties000066400000000000000000000000351160731654500245530ustar00rootroot00000000000000PAGE_X_OF_Y=Page {0} of {1} swt-paperclips-1.0.4/src/net/sf/paperclips/messages_de.properties000066400000000000000000000000371160731654500252250ustar00rootroot00000000000000PAGE_X_OF_Y=Seite {0} von {1} swt-paperclips-1.0.4/src/net/sf/paperclips/messages_en.properties000066400000000000000000000000351160731654500252350ustar00rootroot00000000000000PAGE_X_OF_Y=Page {0} of {1} swt-paperclips-1.0.4/src/net/sf/paperclips/messages_fr.properties000066400000000000000000000000361160731654500252430ustar00rootroot00000000000000PAGE_X_OF_Y=Page {0} sur {1} swt-paperclips-1.0.4/src/net/sf/paperclips/package.html000066400000000000000000000001371160731654500231120ustar00rootroot00000000000000 Core classes for creating printable documents.