/*
* 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 org.eclipse.nebula.paperclips.core;
import org.eclipse.nebula.paperclips.core.internal.RotatePiece;
import org.eclipse.nebula.paperclips.core.internal.util.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.
* <p>
* <em>Note</em>: 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.
* <p>
* 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);
}
}