# 3
Ok guys, i got somecode from the internet and i modified it to fit my needs... here is the code, but still contains some bugs, i am working on it.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.Toolkit;
import javax.swing.*;
import javax.swing.event.MouseInputListener;
public class JBookPanel extends JPanel implements MouseInputListener, ActionListener {
protected static final double PI_DIV_2 = Math.PI / 2;
protected enum AutomatedAction {AUTO_TURN, AUTO_DROP_GO_BACK, AUTO_DROP_BUT_TURN}
protected int rotationX;
protected double nextPageAngle;
protected double backPageAngle;
protected Timer timer;
protected AutomatedAction action;
protected Point autoPoint;
protected Point tmpPoint;
protected int rightPageIndex;
protected Image currentLeftImage;
protected Image currentRightImage;
protected Image nextLeftImage;
protected Image nextRightImage;
protected Image previousLeftImage;
protected Image previousRightImage;
protected String pageLocation;
protected String pageName;
protected String pageExtension;
protected int nrOfPages;
protected boolean leftPageTurn;
protected int refreshSpeed;
// used to store the bounds of the book
protected Rectangle bookBounds;
protected int pageWidth;
protected int shadowWidth;
protected int cornerRegionSize;
protected boolean softClipping;
protected boolean borderLinesVisible;
// vars for optimization and reuse
protected Color shadowDarkColor;
protected Color shadowNeutralColor;
protected GeneralPath clipPath;
/**
* Constructor
*/
public JBookPanel() {
super();
this.addMouseMotionListener(this);
this.addMouseListener(this);
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "nextPage");
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "nextPage");
this.getActionMap().put("nextPage", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
leftPageTurn = false;
nextPage();
}});
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "previousPage");
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "previousPage");
/* this.getActionMap().put("previousPage", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
previousPage();
}});*/
refreshSpeed = 25;
shadowWidth = 100;
cornerRegionSize = 20;
bookBounds = new Rectangle();
softClipping = true;
borderLinesVisible = true;
clipPath = new GeneralPath();
shadowDarkColor = new Color(0,0,0,130);
shadowNeutralColor = new Color(255,255,255,0);
borderLinesVisible=true;
this.setBackground(Color.LIGHT_GRAY);
timer = new Timer(refreshSpeed, this);
timer.stop();
rightPageIndex = 1;
leftPageTurn=true;
setPages(null,null,null,10,210,342);
setMargins(20, 20);
}
///////////////////
// PAINT METHODS //
///////////////////
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
setGraphicsHints(g2);
// background
g.setColor(this.getBackground());
g.fillRect(0, 0, this.getWidth(), this.getHeight());
paintPage(g2, currentRightImage, bookBounds.x, bookBounds.y, bookBounds.width, bookBounds.height, this, true);
paintRightPage(g2);
}
protected void paintRightPage(Graphics2D g2) {
// translate to rotation point
g2.translate(rotationX, bookBounds.height);
// rotate over back of page A angle
g2.rotate(backPageAngle);
// translate the amount already done
int done = bookBounds.width + bookBounds.x - rotationX;
g2.translate(-done, 0);
// page 3 (= back of page 1)
clipPath.reset();
clipPath.moveTo(0, 0);
clipPath.lineTo(done, 0);
clipPath.lineTo(0,-1 * (int)(Math.tan(PI_DIV_2 - nextPageAngle) * done));
clipPath.closePath();
Shape s = g2.getClip();
g2.clip(clipPath);
//////////// importnat line to draw the right page from the back
paintPage(g2, null, 0, 0 - bookBounds.height, pageWidth, bookBounds.height, this, false);
//paintPage(g2, currentRightImage, bookBounds.x, bookBounds.y, bookBounds.width, bookBounds.height, this, false);
g2.setClip(s);
// translate back
g2.translate(done, 0);
// rotate back
g2.rotate(-backPageAngle);
// translate back
g2.translate(-rotationX, -bookBounds.y - bookBounds.height);
// page 4
clipPath.reset();
clipPath.moveTo(bookBounds.width + bookBounds.x, bookBounds.height + bookBounds.y);
clipPath.lineTo(rotationX, bookBounds.height + bookBounds.y);
clipPath.lineTo(bookBounds.width + bookBounds.x, bookBounds.height + bookBounds.y - (int)(Math.tan(PI_DIV_2 - nextPageAngle) * done));
clipPath.closePath();
g2.clip(clipPath);
////////////////////////////////////////////////////////////////
///////////// /////////////////////////////////////////////////
paintPage(g2, nextRightImage, pageWidth + bookBounds.x, bookBounds.y,pageWidth, bookBounds.height, this, true);
// add drop shadow
GradientPaint grad = new GradientPaint(rotationX, bookBounds.height + bookBounds.y, shadowDarkColor,
rotationX + shadowWidth,
bookBounds.height + bookBounds.y + (int)(Math.cos(PI_DIV_2 - nextPageAngle) * shadowWidth),
shadowNeutralColor, false);
g2.setPaint(grad);
//g2.fillRect(pageWidth + bookBounds.x, bookBounds.y, pageWidth, bookBounds.height);
g2.fillRect(bookBounds.x, bookBounds.y, pageWidth, bookBounds.height);
}
protected void paintPage(Graphics2D g, Image img, int x, int y, int w, int h, ImageObserver o,boolean rightPage) {
if (img == null) {
Color oldColor = g.getColor();
//g.setColor(this.getBackground());
g.setColor(Color.WHITE);
g.fillRect(x, y, w+10, h+10);
g.setColor(oldColor);
return;
}
Color oldColor = g.getColor();
g.drawImage(img, x, y, w, h, o);
if (!borderLinesVisible) {
return;
}
if (rightPage) {
g.setColor(Color.GRAY);
g.drawLine(x + w, y, x + w, y + h);
g.drawLine(x, y, x + w, y);
g.drawLine(x, y + h, x + w, y + h);
g.setColor(Color.LIGHT_GRAY);
g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
g.drawLine(x, y + 1, x + w - 1, y + 1);
g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
g.drawLine(x, y + 2, x, y + h - 2);
g.setColor(oldColor);
}
else {
g.setColor(Color.GRAY);
g.drawLine(x, y,
x, y + h);
g.drawLine(x, y,
x + w, y);
g.drawLine(x, y + h,
x + w, y + h);
g.setColor(Color.LIGHT_GRAY);
g.drawLine(x + 1, y + 1,
x + 1, y + h - 1);
g.drawLine(x + 1, y + 1,
x + w - 1, y + 1);
g.drawLine(x + 1, y + h - 1,
x + w - 1, y + h - 1);
g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 2);
g.setColor(oldColor);
}
}
//////////////////
// EVENT METHODS //
///////////////////
public void mouseEntered(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {
if (isMouseInRegion(e)) {
nextPage();
}
}
public void mouseDragged(MouseEvent e) {
// if action busy then dont intervene
if (action != null) {
return;
}
if (isMouseInBook(e)) {
if (this.getCursor().getType() != Cursor.HAND_CURSOR) {
this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
calculate(e.getPoint());
}
}
public void mouseMoved(MouseEvent e) {
// if action busy then dont intervene
if (action != null) {
return;
}
if (isMouseInRegion(e)) {
Point p = new Point(e.getX() - 10 , e.getY() - 10);
calculate(p);
}
else if (rotationX != bookBounds.width + bookBounds.x) {
rotationX = bookBounds.width + bookBounds.x;
this.repaint();
}
}
public void mouseReleased(MouseEvent e) {
if (this.getCursor().getType() != Cursor.DEFAULT_CURSOR) {
this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
// drop page back or forward depending on...
if (rotationX != (bookBounds.width + bookBounds.x)
&& rotationX != bookBounds.x) {
// base decision on amount of surface showing
if (passedThreshold()) {
action = AutomatedAction.AUTO_DROP_BUT_TURN;
autoPoint = (Point) e.getPoint().clone();
if (leftPageTurn) {
autoPoint.x = this.transformIndex(autoPoint.x);
}
tmpPoint = (Point) autoPoint.clone();
this.timer.restart();
} else {
action = AutomatedAction.AUTO_DROP_GO_BACK;
autoPoint = (Point) e.getPoint().clone();
if (leftPageTurn) {
autoPoint.x = this.transformIndex(autoPoint.x);
}
tmpPoint = (Point) autoPoint.clone();
this.timer.restart();
}
}
}
public void actionPerformed(ActionEvent e) {
switch (action) {
case AUTO_TURN:
// update autoPoint
double nextX = autoPoint.getX() - 15.0;
double x = nextX;
x = x - bookBounds.x - pageWidth;
double y = -1.0 * Math.pow(x / pageWidth, 2);
y += 1;
y *= 50;
autoPoint.setLocation(nextX, bookBounds.y + bookBounds.height - y);
if (nextX <= bookBounds.x || nextX >= bookBounds.x + bookBounds.width) {
timer.stop();
action = null;
switchImages();
autoPoint.x = bookBounds.x;
calculate(autoPoint);
initRotationX();
this.repaint();
return;
}
// calculate using new point
calculate(autoPoint);
break;
case AUTO_DROP_GO_BACK:
int xDiff = bookBounds.width + bookBounds.x - autoPoint.x;
int stepsNeeded = 1 + (xDiff / 15);
if (stepsNeeded == 0) {
stepsNeeded = 1;
}
int yStep = (bookBounds.y + bookBounds.height - autoPoint.y) / stepsNeeded;
tmpPoint.x = tmpPoint.x + 15;
tmpPoint.y = tmpPoint.y + yStep;
if (tmpPoint.x >= bookBounds.width + bookBounds.x) {
timer.stop();
action = null;
}
Point newPoint = (Point) tmpPoint.clone();
if (leftPageTurn) {
newPoint.x = this.transformIndex(tmpPoint.x);
}
// calculate using new point
calculate(newPoint);
break;
case AUTO_DROP_BUT_TURN:
int xDiff2 = autoPoint.x - bookBounds.x;
int stepsNeeded2 = 1 + (xDiff2 / 15);
if (stepsNeeded2 == 0) {
stepsNeeded2 = 1;
}
int yStep2 = (bookBounds.y + bookBounds.height - autoPoint.y) / stepsNeeded2;
tmpPoint.x = tmpPoint.x - 15;
tmpPoint.y = tmpPoint.y + yStep2;
if (tmpPoint.x <= bookBounds.x) {
timer.stop();
action = null;
switchImages();
if (leftPageTurn) {
tmpPoint.x = this.transformIndex(bookBounds.x);
} else {
tmpPoint.x = bookBounds.x;
}
tmpPoint.y = bookBounds.y + bookBounds.height;
calculate(tmpPoint);
initRotationX();
this.repaint();
return;
}
Point newPoint2 = (Point) tmpPoint.clone();
if (leftPageTurn) {
newPoint2.x = this.transformIndex(tmpPoint.x);
}
// calculate using new point
calculate(newPoint2);
break;
}
}
////////////////////
// HELPER METHODS //
////////////////////
protected boolean isMouseInRegion(MouseEvent e) {
int x = e.getX();
int y = e.getY();
int maxX = bookBounds.x + bookBounds.width;
int minX = maxX - cornerRegionSize;
int maxY = bookBounds.y + bookBounds.height;
int minY = maxY - cornerRegionSize;
int minX2 = bookBounds.x;
int maxX2 = minX2 + cornerRegionSize;
leftPageTurn = ((x < maxX2) && (x > minX2));
return ( (y < maxY)
&& (y > minY)
&& ( ((x < maxX) && (x > minX))
|| ((x < maxX2) && (x > minX2))
) );
}
protected boolean isMouseInBook(MouseEvent e) {
return bookBounds.contains(e.getX(), e.getY());
}
protected double transformIndex(double x) {
return bookBounds.width + bookBounds.x - x + bookBounds.x;
}
protected int transformIndex(int x) {
return bookBounds.width + bookBounds.x - x + bookBounds.x;
}
protected void calculate(Point p) {
/// if no page to turn available then dont
// allow turn effect
if (currentRightImage == null) {
rotationX = bookBounds.width + bookBounds.x;
nextPageAngle = 0;
backPageAngle = 0;
return;
}
Point cp = new Point(bookBounds.width + bookBounds.x, bookBounds.y + bookBounds.height);
double bRico = 1.0 * (cp.x - p.getX()) / (cp.y - p.getY());
bRico = -bRico;
Point mid = new Point(0,0);
mid.x = (int)((cp.x + p.getX()) / 2.0);
mid.y = (int)((cp.y + p.getY()) / 2.0);
double c = mid.y - bRico * mid.x;
rotationX = Math.max((int)((cp.y - c)/ bRico), pageWidth + bookBounds.x);
nextPageAngle = PI_DIV_2 - Math.abs(Math.atan(bRico));
backPageAngle = 2.0 * nextPageAngle;
this.repaint();
}
public void nextPage() {
action = AutomatedAction.AUTO_TURN;
// @@ autoPoint = new Point(bookBounds.x + bookBounds.width,bookBounds.y + bookBounds.height);
autoPoint = new Point(bookBounds.x , bookBounds.y + bookBounds.height);
this.timer.restart();
}
protected void initRotationX() {
// @@ rotationX = bookBounds.width + bookBounds.x;
rotationX = bookBounds.x;
}
protected boolean passedThreshold() {
int done = bookBounds.width + bookBounds.x - rotationX;
double X = Math.min(Math.tan(PI_DIV_2 - nextPageAngle) * done, bookBounds.height);
X *= done * 2;
double threshold = bookBounds.height * pageWidth;
return X > threshold;
}
protected void switchImages() {
rightPageIndex ++;
currentRightImage = nextRightImage;
nextRightImage = getPage(rightPageIndex + 1);
previousRightImage = getPage(rightPageIndex - 1);
}
protected BufferedImage getBlankPage(int index) {
BufferedImage img = new BufferedImage(pageWidth, bookBounds.height, BufferedImage.TYPE_3BYTE_BGR);
Graphics gfx = img.getGraphics();
gfx.setColor(Color.WHITE);
gfx.fillRect(0, 0, img.getWidth(), img.getHeight());
return img;
}
protected void setGraphicsHints(Graphics2D g2) {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
}
protected Image loadPage(int index) {
//JOptionPane.showMessageDialog(this,pageLocation + pageName + index + "." + pageExtension);
Image img= Toolkit.getDefaultToolkit().getImage(pageLocation + pageName + index + "." + pageExtension);
return img;
}
protected Image getPage(int index) {
// if request goes beyond available pages return null
if (index > nrOfPages) {
// if back of existing page then return a blank
if ( (index - 1) % 2 == 0) {
return getBlankPage(index);
} else {
return null;
}
}
if (index < 1) {
if (index == 0) {
try {
return loadPage(index);
} catch (Exception e) {
return getBlankPage(index);
}
} else {
return null;
}
}
// if no images specified return blank ones
if (pageLocation == null
|| pageName == null
|| pageExtension == null ) {
// create the blank page
BufferedImage img = getBlankPage(index);
// draw page number
Graphics gfx = img.getGraphics();
Graphics2D g2 = (Graphics2D) gfx;
setGraphicsHints(g2);
return img;
} else {
return loadPage(index);
}
}
/////////////////////////
// GETTERS AND SETTERS //
/////////////////////////
public void setMargins(int top, int left) {
bookBounds.x = left;
bookBounds.y = top;
initRotationX();
}
public void setPages(String pageLocation, String pageName, String pageExtension, int nrOfPages,int pageWidth, int pageHeight) {
this.pageLocation = pageLocation;
this.pageName = pageName;
this.pageExtension = pageExtension;
this.nrOfPages = nrOfPages;
this.pageWidth = pageWidth;
bookBounds.width = 2* pageWidth;
bookBounds.height = pageHeight;
initRotationX();
//currentLeftImage = getPage(rightPageIndex);
currentRightImage = getPage(rightPageIndex);
//nextLeftImage = getPage(rightPageIndex + 2);
nextRightImage = getPage(rightPageIndex + 1);
//reviousLeftImage = getPage(rightPageIndex - 2);
previousRightImage = getPage(rightPageIndex - 1);
}
public int getRefreshSpeed() {
return refreshSpeed;
}
public void setRefreshSpeed(int refreshSpeed) {
this.refreshSpeed = refreshSpeed;
}
public Color getShadowDarkColor() {
return shadowDarkColor;
}
public void setShadowDarkColor(Color shadowDarkColor) {
this.shadowDarkColor = shadowDarkColor;
}
public Color getShadowNeutralColor() {
return shadowNeutralColor;
}
public void setShadowNeutralColor(Color shadowNeutralColor) {
this.shadowNeutralColor = shadowNeutralColor;
}
public int getShadowWidth() {
return shadowWidth;
}
public void setShadowWidth(int shadowWidth) {
this.shadowWidth = shadowWidth;
}
public boolean isSoftClipping() {
return softClipping;
}
public void setSoftClipping(boolean softClipping) {
this.softClipping = softClipping;
}
public boolean isBorderLinesVisible() {
return borderLinesVisible;
}
public void setBorderLinesVisible(boolean borderLinesVisible) {
this.borderLinesVisible = borderLinesVisible;
}
public int getrightPageIndex() {
return rightPageIndex;
}
public void setrightPageIndex(int rightPageIndex) {
if (rightPageIndex <= -1) {
rightPageIndex = -1;
}
this.rightPageIndex = rightPageIndex;
previousRightImage = getPage(rightPageIndex - 1);
currentRightImage = getPage(rightPageIndex + 1);;
nextRightImage = getPage(rightPageIndex + 1);
}
}