/* * Copyright (c) 2005 Matthew Hall and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Matthew Hall - initial API and implementation */ package org.eclipse.nebula.paperclips.widgets; import org.eclipse.nebula.paperclips.core.PaperClips; import org.eclipse.nebula.paperclips.core.Print; import org.eclipse.nebula.paperclips.core.PrintIterator; import org.eclipse.nebula.paperclips.core.PrintPiece; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; /** * A JFace-style {@link Print} viewer which displays a Print in a scrollable * pane. * * @author Matthew */ public class PrintViewer { private final ScrolledComposite sc; private final PrintPieceCanvas canvas; private Print print; private int canvasWidth; private BackgroundUpdater backgroundUpdater; /** * Constructs a PrintPreview with the given parent and style. * * @param parent * the parent component of the scroll pane. * @param style * the style of the scroll pane. */ public PrintViewer(Composite parent, int style) { sc = new ScrolledComposite(parent, style | SWT.V_SCROLL | SWT.H_SCROLL); sc.setExpandHorizontal(true); sc.setExpandVertical(true); sc.addListener(SWT.Resize, new Listener() { public void handleEvent(Event event) { if (sc.getClientArea().width != canvasWidth) updateCanvas(); } }); canvas = new PrintPieceCanvas(sc, SWT.DOUBLE_BUFFERED); sc.setContent(canvas); } /** * Returns the viewer component wrapped by this PrintPreview. * * @return the viewer component wrapped by this PrintPreview. */ public Control getControl() { return sc; } /** * Sets the Print to be displayed. * * @param print * the Print to display. */ public void setPrint(Print print) { this.print = print; updateCanvas(); } /** * Returns the Print being displayed. * * @return the Print being displayed. */ public Print getPrint() { return print; } void updateCanvas() { if (print == null) { sc.setMinSize(0, 0); canvas.setPrintPiece(null); return; } GC gc = null; try { gc = new GC(canvas); PrintIterator iterator = print.iterator(canvas.getDisplay(), gc); sc.setMinWidth(iterator.minimumSize().x); int canvasWidth = Math.max(iterator.minimumSize().x, sc .getClientArea().width); if (this.canvasWidth == canvasWidth) return; this.canvasWidth = canvasWidth; if (backgroundUpdater != null) { backgroundUpdater.cancelled = true; backgroundUpdater = null; } PrintPiece piece = PaperClips.next(iterator, canvasWidth, Integer.MAX_VALUE); boolean printIsVerticallyGreedy = piece != null && piece.getSize().y == Integer.MAX_VALUE; if (printIsVerticallyGreedy) sc.getDisplay().timerExec(50, backgroundUpdater = new BackgroundUpdater()); setPrintPiece(piece, !printIsVerticallyGreedy); } finally { if (gc != null) gc.dispose(); } } private void setPrintPiece(PrintPiece piece, boolean updateMinHeight) { if (updateMinHeight) sc.setMinHeight(piece == null ? 0 : piece.getSize().y); canvas.setPrintPiece(piece); } private class BackgroundUpdater implements Runnable { private boolean cancelled = false; private int minHeight; private int maxHeight; private PrintIterator iterator; private PrintPiece piece; public void run() { if (cancelled || print == null) return; GC gc = null; try { gc = new GC(canvas); iterator = print.iterator(canvas.getDisplay(), gc); piece = canvas.getPrintPiece(); determineValidHeightRange(); binarySearchRangeForSmallestValidHeight(); setPrintPiece(piece, true); } finally { if (gc != null) gc.dispose(); } } private void determineValidHeightRange() { minHeight = iterator.preferredSize().y; maxHeight = Math.max(minHeight, 4096); while (true) { PrintIterator testIter = iterator.copy(); PrintPiece testPiece = PaperClips.next(testIter, canvasWidth, maxHeight); final int factor = 4; if (testPiece == null) { // Theoretically this will never happen since we started at // preferred height minHeight = maxHeight + 1; maxHeight = minHeight * factor; } else if (testIter.hasNext()) { testPiece.dispose(); minHeight = maxHeight + 1; maxHeight = minHeight * factor; } else { piece.dispose(); piece = testPiece; break; } } } private void binarySearchRangeForSmallestValidHeight() { while (minHeight < maxHeight) { int testHeight = minHeight + (maxHeight - minHeight) / 2; PrintIterator testIter = iterator.copy(); PrintPiece testPiece = PaperClips.next(testIter, canvasWidth, testHeight); if (testPiece == null) { minHeight = testHeight + 1; } else if (testIter.hasNext()) { testPiece.dispose(); minHeight = testHeight + 1; } else { maxHeight = Math.min(testHeight, testPiece.getSize().y); piece.dispose(); piece = testPiece; } } } } }