/*
* Copyright (c) 2007 Matthew Hall and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthew Hall - initial API and implementation
*/
package org.eclipse.nebula.paperclips.core.border.internal;
import org.eclipse.nebula.paperclips.core.PaperClips;
import org.eclipse.nebula.paperclips.core.PrintIterator;
import org.eclipse.nebula.paperclips.core.PrintPiece;
import org.eclipse.nebula.paperclips.core.border.BorderPainter;
import org.eclipse.nebula.paperclips.core.border.BorderPrint;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
public class BorderIterator implements PrintIterator {
private final BorderPainter border;
private PrintIterator target;
private boolean opened;
public BorderIterator(BorderPrint print, Device device, GC gc) {
this.border = print.getBorder().createPainter(device, gc);
this.target = print.getTarget().iterator(device, gc);
this.opened = false;
}
public 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);
}
}