0001: /**
0002: * Copyright 2007 Pieter-Jan Savat
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */package be.savat.components;
0016:
0017: import java.awt.*;
0018: import java.awt.event.ActionEvent;
0019: import java.awt.event.ActionListener;
0020: import java.awt.event.KeyEvent;
0021: import java.awt.event.MouseEvent;
0022: import java.awt.geom.GeneralPath;
0023: import java.awt.image.BufferedImage;
0024: import java.awt.image.ImageObserver;
0025:
0026: import javax.swing.*;
0027: import javax.swing.event.MouseInputListener;
0028:
0029: /**
0030: * The BookPanel uses the page turn effect to navigate between pages. The images used
0031: * as pages are set by specifying their location, the basename and the extension.
0032: * Each image should have the same basename followed by its index, ranging 1 to x.
0033: * If a 0 index image exists at the location then it is used (even though it is not
0034: * counted in the nrOfPages for the setPages() method).
0035: *
0036: * Navigating through the book can be done by clicking in the bottomright or
0037: * bottomleft corner, by using the 4 arrow keys or by dragging and dropping
0038: * a page.
0039: * To turn pages programmatically either call nextPage() or previousPage(). Jumping
0040: * to a page can be done by calling setLeftPageIndex().
0041: *
0042: * By default soft clipping is off and the borders at the edges of the papers are visible.
0043: *
0044: * TODO fade drop shadow on percentage turned (see passedThreshold for calculations)
0045: *
0046: * @author Pieter-Jan Savat
0047: */
0048: public class JBookPanel extends JPanel implements MouseInputListener,
0049: ActionListener {
0050:
0051: protected static final double PI_DIV_2 = Math.PI / 2.0;
0052: //protected enum AutomatedAction {AUTO_TURN, AUTO_DROP_GO_BACK, AUTO_DROP_BUT_TURN}
0053:
0054: protected static final int AUTO_TURN = 0;
0055: protected static final int AUTO_DROP_GO_BACK = 1;
0056: protected static final int AUTO_DROP_BUT_TURN = 2;
0057:
0058: protected int rotationX;
0059: protected double nextPageAngle;
0060: protected double backPageAngle;
0061:
0062: protected Timer timer;
0063: //protected AutomatedAction action;
0064: protected Integer action;
0065: protected Point autoPoint;
0066: protected Point tmpPoint;
0067:
0068: protected int leftPageIndex;
0069:
0070: protected Image currentLeftImage;
0071: protected Image currentRightImage;
0072: protected Image nextLeftImage;
0073: protected Image nextRightImage;
0074: protected Image previousLeftImage;
0075: protected Image previousRightImage;
0076:
0077: protected String pageLocation;
0078: protected String pageName;
0079: protected String pageExtension;
0080: protected int nrOfPages;
0081: protected boolean leftPageTurn;
0082: protected int refreshSpeed;
0083:
0084: // used to store the bounds of the book
0085: protected Rectangle bookBounds;
0086: protected int pageWidth;
0087:
0088: protected int shadowWidth;
0089: protected int cornerRegionSize;
0090: protected boolean softClipping;
0091: protected boolean borderLinesVisible;
0092:
0093: // vars for optimization and reuse
0094: protected Color shadowDarkColor;
0095: protected Color shadowNeutralColor;
0096: protected GeneralPath clipPath;
0097:
0098: /**
0099: * Constructor
0100: */
0101: public JBookPanel() {
0102: super ();
0103: this .addMouseMotionListener(this );
0104: this .addMouseListener(this );
0105: this .getInputMap().put(
0106: KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
0107: "nextPage");
0108: this .getInputMap().put(
0109: KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "nextPage");
0110: this .getActionMap().put("nextPage", new AbstractAction() {
0111: public void actionPerformed(ActionEvent e) {
0112: leftPageTurn = false;
0113: nextPage();
0114: }
0115: });
0116: this .getInputMap().put(
0117: KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
0118: "previousPage");
0119: this .getInputMap().put(
0120: KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
0121: "previousPage");
0122: this .getActionMap().put("previousPage", new AbstractAction() {
0123: public void actionPerformed(ActionEvent e) {
0124: previousPage();
0125: }
0126: });
0127:
0128: refreshSpeed = 25;
0129: shadowWidth = 100;
0130: cornerRegionSize = 20;
0131: bookBounds = new Rectangle();
0132: softClipping = false;
0133: borderLinesVisible = true;
0134: clipPath = new GeneralPath();
0135: shadowDarkColor = new Color(0, 0, 0, 130);
0136: shadowNeutralColor = new Color(255, 255, 255, 0);
0137: this .setBackground(Color.LIGHT_GRAY);
0138:
0139: timer = new Timer(refreshSpeed, this );
0140: timer.stop();
0141: leftPageIndex = 1;
0142:
0143: setPages(null, null, null, 13, 210, 342);
0144: setMargins(70, 80);
0145: }
0146:
0147: ///////////////////
0148: // PAINT METHODS //
0149: ///////////////////
0150:
0151: public void paintComponent(Graphics g) {
0152: Graphics2D g2 = (Graphics2D) g;
0153: setGraphicsHints(g2);
0154:
0155: // background
0156: g.setColor(this .getBackground());
0157: g.fillRect(0, 0, this .getWidth(), this .getHeight());
0158:
0159: // page 1
0160: paintPage(g2, currentLeftImage, bookBounds.x, bookBounds.y,
0161: pageWidth, bookBounds.height, this , false);
0162:
0163: // page 2
0164: paintPage(g2, currentRightImage, bookBounds.x + pageWidth,
0165: bookBounds.y, pageWidth, bookBounds.height, this , true);
0166:
0167: if (leftPageTurn) {
0168: if (softClipping) {
0169: paintLeftPageSoftClipped(g2);
0170: } else {
0171: paintLeftPage(g2);
0172: }
0173: } else {
0174: if (softClipping) {
0175: paintRightPageSoftClipped(g2);
0176: } else {
0177: paintRightPage(g2);
0178: }
0179: }
0180: }
0181:
0182: protected void paintLeftPage(Graphics2D g2) {
0183: // translate to rotation point
0184: g2.translate(bookBounds.width + bookBounds.x - rotationX
0185: + bookBounds.x, bookBounds.y + bookBounds.height);
0186:
0187: // rotate over back of page A angle
0188: g2.rotate(-backPageAngle);
0189:
0190: // translate the amount already done
0191: int done = bookBounds.width + bookBounds.x - rotationX;
0192: g2.translate(done - pageWidth, 0);
0193:
0194: // page 3 (= back of page 1)
0195: clipPath.reset();
0196: clipPath.moveTo(pageWidth, 0);
0197: clipPath.lineTo(pageWidth - done, 0);
0198: clipPath.lineTo(pageWidth, -1
0199: * (int) (Math.tan(PI_DIV_2 - nextPageAngle) * done));
0200: clipPath.closePath();
0201: Shape s = g2.getClip();
0202: g2.clip(clipPath);
0203: paintPage(g2, previousRightImage, 0, 0 - bookBounds.height,
0204: pageWidth, bookBounds.height, this , false);
0205: g2.setClip(s);
0206:
0207: // translate back
0208: g2.translate(pageWidth - done, 0);
0209:
0210: // rotate back
0211: g2.rotate(backPageAngle);
0212:
0213: // translate back
0214: g2.translate(rotationX - bookBounds.width - bookBounds.x
0215: - bookBounds.x, -bookBounds.y - bookBounds.height);
0216:
0217: // page 4
0218: clipPath.reset();
0219: clipPath.moveTo(bookBounds.x, bookBounds.height + bookBounds.y);
0220: clipPath.lineTo(bookBounds.x + done, bookBounds.height
0221: + bookBounds.y);
0222: clipPath.lineTo(bookBounds.x, bookBounds.height + bookBounds.y
0223: - (int) (Math.tan(PI_DIV_2 - nextPageAngle) * done));
0224: clipPath.closePath();
0225: g2.clip(clipPath);
0226: paintPage(g2, previousLeftImage, bookBounds.x, bookBounds.y,
0227: pageWidth, bookBounds.height, this , true);
0228:
0229: // add drop shadow
0230: GradientPaint grad = new GradientPaint(
0231: bookBounds.x + done,
0232: bookBounds.height + bookBounds.y,
0233: shadowDarkColor,
0234: bookBounds.x + done - shadowWidth,
0235: bookBounds.height
0236: + bookBounds.y
0237: + (int) (Math.cos(PI_DIV_2 - nextPageAngle) * shadowWidth),
0238: shadowNeutralColor, false);
0239: g2.setPaint(grad);
0240: g2.fillRect(bookBounds.x, bookBounds.y, pageWidth,
0241: bookBounds.height);
0242: }
0243:
0244: protected void paintLeftPageSoftClipped(Graphics2D g2) {
0245:
0246: // calculate amount done (traveled)
0247: int done = bookBounds.width + bookBounds.x - rotationX;
0248:
0249: ///////////////////////////////
0250: // page 3 (= back of page 1) //
0251: ///////////////////////////////
0252:
0253: // init clip path
0254: clipPath.reset();
0255: clipPath.moveTo(pageWidth, 0);
0256: clipPath.lineTo(pageWidth - done, 0);
0257: clipPath.lineTo(pageWidth, -1
0258: * (int) (Math.tan(PI_DIV_2 - nextPageAngle) * done));
0259: clipPath.closePath();
0260:
0261: // init soft clip image
0262: GraphicsConfiguration gc = g2.getDeviceConfiguration();
0263: BufferedImage img = gc.createCompatibleImage(this .getWidth(),
0264: this .getHeight(), Transparency.TRANSLUCENT);
0265: Graphics2D gImg = img.createGraphics();
0266:
0267: // translate to rotation point
0268: gImg.translate(bookBounds.width + bookBounds.x - rotationX
0269: + bookBounds.x, bookBounds.y + bookBounds.height);
0270:
0271: // rotate over back of page A angle
0272: gImg.rotate(-backPageAngle);
0273:
0274: // translate the amount already done
0275: gImg.translate(done - pageWidth, 0);
0276:
0277: // init area on which may be painted
0278: gImg.setComposite(AlphaComposite.Clear);
0279: gImg.fillRect(0, 0, this .getWidth(), this .getHeight());
0280: gImg.setComposite(AlphaComposite.Src);
0281: gImg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0282: RenderingHints.VALUE_ANTIALIAS_ON);
0283: gImg.setColor(Color.WHITE);
0284: gImg.fill(clipPath);
0285: gImg.setColor(new Color(255, 255, 255, 0));
0286: gImg.fillRect(0, 0 - bookBounds.height - bookBounds.height,
0287: pageWidth + 10, bookBounds.height); // remove the top margin from allowed area
0288: gImg.setComposite(AlphaComposite.SrcAtop);
0289:
0290: // paint page
0291: paintPage(gImg, previousRightImage, 0, 0 - bookBounds.height,
0292: pageWidth, bookBounds.height, this , false);
0293:
0294: // translate back
0295: gImg.translate(pageWidth - done, 0);
0296:
0297: // rotate back
0298: gImg.rotate(backPageAngle);
0299:
0300: // translate back
0301: gImg.translate(rotationX - bookBounds.width - bookBounds.x
0302: - bookBounds.x, -bookBounds.y - bookBounds.height);
0303:
0304: gImg.dispose();
0305: g2.drawImage(img, 0, 0, null);
0306:
0307: ////////////
0308: // page 4 //
0309: ////////////
0310:
0311: // init clip path
0312: clipPath.reset();
0313: clipPath.moveTo(bookBounds.x, bookBounds.height + bookBounds.y);
0314: clipPath.lineTo(bookBounds.x + done, bookBounds.height
0315: + bookBounds.y);
0316: clipPath.lineTo(bookBounds.x, bookBounds.height + bookBounds.y
0317: - (int) (Math.tan(PI_DIV_2 - nextPageAngle) * done));
0318: clipPath.closePath();
0319:
0320: // init soft clip image
0321: img = gc.createCompatibleImage(this .getWidth(), this
0322: .getHeight(), Transparency.TRANSLUCENT);
0323: gImg = img.createGraphics();
0324: gImg.setComposite(AlphaComposite.Clear);
0325: gImg.fillRect(0, 0, this .getWidth(), this .getHeight());
0326: gImg.setComposite(AlphaComposite.Src);
0327: gImg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0328: RenderingHints.VALUE_ANTIALIAS_ON);
0329: gImg.setColor(Color.WHITE);
0330: gImg.fill(clipPath); // init area on which may be painted
0331: gImg.setColor(new Color(255, 255, 255, 0));
0332: gImg.fillRect(0, 0, this .getWidth(), bookBounds.y); // remove the top margin from allowed area
0333: gImg.setComposite(AlphaComposite.SrcAtop);
0334:
0335: // paint page
0336: paintPage(gImg, previousLeftImage, bookBounds.x, bookBounds.y,
0337: pageWidth, bookBounds.height, this , true);
0338:
0339: // paint shadow
0340: GradientPaint grad = new GradientPaint(
0341: bookBounds.x + done,
0342: bookBounds.height + bookBounds.y,
0343: shadowDarkColor,
0344: bookBounds.x + done - shadowWidth,
0345: bookBounds.height
0346: + bookBounds.y
0347: + (int) (Math.cos(PI_DIV_2 - nextPageAngle) * shadowWidth),
0348: shadowNeutralColor, false);
0349: gImg.setPaint(grad);
0350: gImg.fillRect(bookBounds.x, bookBounds.y, pageWidth,
0351: bookBounds.height);
0352:
0353: gImg.dispose();
0354: g2.drawImage(img, 0, 0, null);
0355: }
0356:
0357: protected void paintRightPage(Graphics2D g2) {
0358:
0359: // translate to rotation point
0360: g2.translate(rotationX, bookBounds.y + bookBounds.height);
0361:
0362: // rotate over back of page A angle
0363: g2.rotate(backPageAngle);
0364:
0365: // translate the amount already done
0366: int done = bookBounds.width + bookBounds.x - rotationX;
0367: g2.translate(-done, 0);
0368:
0369: // page 3 (= back of page 1)
0370: clipPath.reset();
0371: clipPath.moveTo(0, 0);
0372: clipPath.lineTo(done, 0);
0373: clipPath.lineTo(0, -1
0374: * (int) (Math.tan(PI_DIV_2 - nextPageAngle) * done));
0375: clipPath.closePath();
0376: Shape s = g2.getClip();
0377: g2.clip(clipPath);
0378: paintPage(g2, nextLeftImage, 0, 0 - bookBounds.height,
0379: pageWidth, bookBounds.height, this , false);
0380: g2.setClip(s);
0381:
0382: // translate back
0383: g2.translate(done, 0);
0384:
0385: // rotate back
0386: g2.rotate(-backPageAngle);
0387:
0388: // translate back
0389: g2.translate(-rotationX, -bookBounds.y - bookBounds.height);
0390:
0391: // page 4
0392: clipPath.reset();
0393: clipPath.moveTo(bookBounds.width + bookBounds.x,
0394: bookBounds.height + bookBounds.y);
0395: clipPath.lineTo(rotationX, bookBounds.height + bookBounds.y);
0396: clipPath
0397: .lineTo(bookBounds.width + bookBounds.x,
0398: bookBounds.height
0399: + bookBounds.y
0400: - (int) (Math.tan(PI_DIV_2
0401: - nextPageAngle) * done));
0402: clipPath.closePath();
0403: g2.clip(clipPath);
0404: paintPage(g2, nextRightImage, pageWidth + bookBounds.x,
0405: bookBounds.y, pageWidth, bookBounds.height, this , true);
0406:
0407: // add drop shadow
0408: GradientPaint grad = new GradientPaint(
0409: rotationX,
0410: bookBounds.height + bookBounds.y,
0411: shadowDarkColor,
0412: rotationX + shadowWidth,
0413: bookBounds.height
0414: + bookBounds.y
0415: + (int) (Math.cos(PI_DIV_2 - nextPageAngle) * shadowWidth),
0416: shadowNeutralColor, false);
0417: g2.setPaint(grad);
0418: g2.fillRect(pageWidth + bookBounds.x, bookBounds.y, pageWidth,
0419: bookBounds.height);
0420: }
0421:
0422: protected void paintRightPageSoftClipped(Graphics2D g2) {
0423:
0424: int done = bookBounds.width + bookBounds.x - rotationX;
0425:
0426: ///////////////////////////////
0427: // page 3 (= back of page 1) //
0428: ///////////////////////////////
0429:
0430: // init clip path
0431: clipPath.reset();
0432: clipPath.moveTo(0, 0);
0433: clipPath.lineTo(done, 0);
0434: clipPath.lineTo(0, -1
0435: * (int) (Math.tan(PI_DIV_2 - nextPageAngle) * done));
0436: clipPath.closePath();
0437:
0438: // init soft clip image
0439: GraphicsConfiguration gc = g2.getDeviceConfiguration();
0440: BufferedImage img = gc.createCompatibleImage(this .getWidth(),
0441: this .getHeight(), Transparency.TRANSLUCENT);
0442: Graphics2D gImg = img.createGraphics();
0443:
0444: // translate to rotation point
0445: gImg.translate(rotationX, bookBounds.y + bookBounds.height);
0446:
0447: // rotate over back of page A angle
0448: gImg.rotate(backPageAngle);
0449:
0450: // translate the amount already done
0451: gImg.translate(-done, 0);
0452:
0453: // init area on which may be painted
0454: gImg.setComposite(AlphaComposite.Clear);
0455: gImg.fillRect(0, 0, this .getWidth(), this .getHeight());
0456: gImg.setComposite(AlphaComposite.Src);
0457: gImg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0458: RenderingHints.VALUE_ANTIALIAS_ON);
0459: gImg.setColor(Color.WHITE);
0460: gImg.fill(clipPath);
0461: gImg.setColor(new Color(255, 255, 255, 0));
0462: gImg.fillRect(-10, 0 - bookBounds.height - bookBounds.height,
0463: pageWidth + 10, bookBounds.height); // remove the top margin from allowed area
0464: gImg.setComposite(AlphaComposite.SrcAtop);
0465:
0466: // paint page
0467: paintPage(gImg, nextLeftImage, 0, 0 - bookBounds.height,
0468: pageWidth, bookBounds.height, this , false);
0469:
0470: // translate back
0471: gImg.translate(done, 0);
0472:
0473: // rotate back
0474: gImg.rotate(-backPageAngle);
0475:
0476: // translate back
0477: gImg.translate(-rotationX, -bookBounds.y - bookBounds.height);
0478:
0479: gImg.dispose();
0480: g2.drawImage(img, 0, 0, null);
0481:
0482: ////////////
0483: // page 4 //
0484: ////////////
0485:
0486: // init clip path
0487: clipPath.reset();
0488: clipPath.moveTo(bookBounds.width + bookBounds.x,
0489: bookBounds.height + bookBounds.y);
0490: clipPath.lineTo(rotationX, bookBounds.height + bookBounds.y);
0491: clipPath
0492: .lineTo(bookBounds.width + bookBounds.x,
0493: bookBounds.height
0494: + bookBounds.y
0495: - (int) (Math.tan(PI_DIV_2
0496: - nextPageAngle) * done));
0497: clipPath.closePath();
0498:
0499: // init soft clip image
0500: img = gc.createCompatibleImage(this .getWidth(), this
0501: .getHeight(), Transparency.TRANSLUCENT);
0502: gImg = img.createGraphics();
0503: gImg.setComposite(AlphaComposite.Clear);
0504: gImg.fillRect(0, 0, this .getWidth(), this .getHeight());
0505: gImg.setComposite(AlphaComposite.Src);
0506: gImg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0507: RenderingHints.VALUE_ANTIALIAS_ON);
0508: gImg.setColor(Color.WHITE);
0509: gImg.fill(clipPath); // init area on which may be painted
0510: gImg.setColor(new Color(255, 255, 255, 0));
0511: gImg.fillRect(0, 0, this .getWidth(), bookBounds.y); // remove the top margin from allowed area
0512: gImg.setComposite(AlphaComposite.SrcAtop);
0513:
0514: // paint page
0515: paintPage(gImg, nextRightImage, pageWidth + bookBounds.x,
0516: bookBounds.y, pageWidth, bookBounds.height, this , true);
0517:
0518: // add drop shadow
0519: GradientPaint grad = new GradientPaint(
0520: rotationX,
0521: bookBounds.height + bookBounds.y,
0522: shadowDarkColor,
0523: rotationX + shadowWidth,
0524: bookBounds.height
0525: + bookBounds.y
0526: + (int) (Math.cos(PI_DIV_2 - nextPageAngle) * shadowWidth),
0527: shadowNeutralColor, false);
0528: gImg.setPaint(grad);
0529: gImg.fillRect(pageWidth + bookBounds.x, bookBounds.y,
0530: pageWidth, bookBounds.height);
0531:
0532: gImg.dispose();
0533: g2.drawImage(img, 0, 0, null);
0534: }
0535:
0536: protected void paintPage(Graphics2D g, Image img, int x, int y,
0537: int w, int h, ImageObserver o, boolean rightPage) {
0538: if (img == null) {
0539: Color oldColor = g.getColor();
0540: g.setColor(this .getBackground());
0541: g.fillRect(x, y, w + 10, h + 10);
0542: g.setColor(oldColor);
0543: return;
0544: }
0545: Color oldColor = g.getColor();
0546: g.drawImage(img, x, y, w, h, o);
0547:
0548: // stop if no borders are needed
0549: if (!borderLinesVisible) {
0550: return;
0551: }
0552:
0553: if (rightPage) {
0554: g.setColor(Color.GRAY);
0555: g.drawLine(x + w, y, x + w, y + h);
0556: g.drawLine(x, y, x + w, y);
0557: g.drawLine(x, y + h, x + w, y + h);
0558:
0559: g.setColor(Color.LIGHT_GRAY);
0560: g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
0561: g.drawLine(x, y + 1, x + w - 1, y + 1);
0562: g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
0563:
0564: g.drawLine(x, y + 2, x, y + h - 2);
0565:
0566: g.setColor(oldColor);
0567:
0568: } else {
0569: g.setColor(Color.GRAY);
0570: g.drawLine(x, y, x, y + h);
0571: g.drawLine(x, y, x + w, y);
0572: g.drawLine(x, y + h, x + w, y + h);
0573:
0574: g.setColor(Color.LIGHT_GRAY);
0575: g.drawLine(x + 1, y + 1, x + 1, y + h - 1);
0576: g.drawLine(x + 1, y + 1, x + w - 1, y + 1);
0577: g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
0578:
0579: g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 2);
0580:
0581: g.setColor(oldColor);
0582: }
0583: }
0584:
0585: protected void paintPageNumber(Graphics g, int index, int width,
0586: int height) {
0587: g.setFont(new Font("Arial", Font.BOLD, 11));
0588: int w = (int) g.getFontMetrics().getStringBounds(
0589: String.valueOf(index), g).getWidth();
0590: int x = (index % 2 == 0) ? width - 8 - w : 8;
0591: int y = height - 10;
0592: g.setColor(Color.GRAY);
0593: g.drawString(String.valueOf(index), x, y);
0594: }
0595:
0596: ///////////////////
0597: // EVENT METHODS //
0598: ///////////////////
0599:
0600: public void mouseEntered(MouseEvent e) {
0601: }
0602:
0603: public void mousePressed(MouseEvent e) {
0604: }
0605:
0606: public void mouseExited(MouseEvent e) {
0607: }
0608:
0609: public void mouseClicked(MouseEvent e) {
0610: if (isMouseInRegion(e)) {
0611: nextPage();
0612: }
0613: }
0614:
0615: public void mouseDragged(MouseEvent e) {
0616:
0617: // if action busy then dont intervene
0618: if (action != null) {
0619: return;
0620: }
0621:
0622: // decide whether to grab left page or right page
0623: if (this .rotationX == bookBounds.x + bookBounds.width) {
0624: if (e.getPoint().x < bookBounds.x + pageWidth) {
0625: this .leftPageTurn = true;
0626: }
0627: }
0628:
0629: if (isMouseInBook(e)) {
0630: if (this .getCursor().getType() != Cursor.HAND_CURSOR) {
0631: this .setCursor(Cursor
0632: .getPredefinedCursor(Cursor.HAND_CURSOR));
0633: }
0634: calculate(e.getPoint());
0635: }
0636: }
0637:
0638: public void mouseMoved(MouseEvent e) {
0639:
0640: // if action busy then dont intervene
0641: if (action != null) {
0642: return;
0643: }
0644:
0645: if (isMouseInRegion(e)) {
0646: int xOffset = (leftPageTurn) ? 10 : -10;
0647: Point p = new Point(e.getX() + xOffset, e.getY() - 10);
0648: calculate(p);
0649: } else if (rotationX != bookBounds.width + bookBounds.x) {
0650: rotationX = bookBounds.width + bookBounds.x;
0651: this .repaint();
0652: }
0653: }
0654:
0655: public void mouseReleased(MouseEvent e) {
0656: if (this .getCursor().getType() != Cursor.DEFAULT_CURSOR) {
0657: this .setCursor(Cursor
0658: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
0659: }
0660:
0661: // drop page back or forward depending on...
0662: if (rotationX != (bookBounds.width + bookBounds.x)
0663: && rotationX != bookBounds.x) {
0664:
0665: // base decision on amount of surface showing
0666: if (passedThreshold()) {
0667: System.out.println("drop turn");
0668: action = new Integer(AUTO_DROP_BUT_TURN);
0669: autoPoint = (Point) e.getPoint().clone();
0670:
0671: if (leftPageTurn) {
0672: autoPoint.x = this .transformIndex(autoPoint.x);
0673: }
0674:
0675: tmpPoint = (Point) autoPoint.clone();
0676: this .timer.restart();
0677:
0678: } else {
0679: System.out.println("drop go back");
0680: action = new Integer(AUTO_DROP_GO_BACK);
0681: autoPoint = (Point) e.getPoint().clone();
0682:
0683: if (leftPageTurn) {
0684: autoPoint.x = this .transformIndex(autoPoint.x);
0685: }
0686:
0687: tmpPoint = (Point) autoPoint.clone();
0688: this .timer.restart();
0689: }
0690: }
0691: }
0692:
0693: public void actionPerformed(ActionEvent e) {
0694:
0695: switch (action.intValue()) {
0696:
0697: case AUTO_TURN:
0698:
0699: // update autoPoint
0700: double nextX = autoPoint.getX() - 15.0;
0701: double x = nextX;
0702: x = x - bookBounds.x - pageWidth;
0703: double y = -1.0 * Math.pow(x / pageWidth, 2);
0704: y += 1;
0705: y *= 50;
0706:
0707: if (leftPageTurn) {
0708: nextX = this .transformIndex(nextX);
0709: }
0710:
0711: autoPoint.setLocation(nextX, bookBounds.y
0712: + bookBounds.height - y);
0713:
0714: if (nextX <= bookBounds.x
0715: || nextX >= bookBounds.x + bookBounds.width) {
0716: timer.stop();
0717: action = null;
0718: switchImages();
0719: autoPoint.x = bookBounds.x;
0720: calculate(autoPoint);
0721: initRotationX();
0722: this .repaint();
0723: return;
0724: }
0725:
0726: // calculate using new point
0727: calculate(autoPoint);
0728:
0729: break;
0730:
0731: case AUTO_DROP_GO_BACK:
0732:
0733: int xDiff = bookBounds.width + bookBounds.x - autoPoint.x;
0734: int stepsNeeded = 1 + (xDiff / 15);
0735: if (stepsNeeded == 0) {
0736: stepsNeeded = 1;
0737: }
0738: int yStep = (bookBounds.y + bookBounds.height - autoPoint.y)
0739: / stepsNeeded;
0740:
0741: tmpPoint.x = tmpPoint.x + 15;
0742: tmpPoint.y = tmpPoint.y + yStep;
0743: if (tmpPoint.x >= bookBounds.width + bookBounds.x) {
0744: timer.stop();
0745: action = null;
0746: }
0747:
0748: Point newPoint = (Point) tmpPoint.clone();
0749:
0750: if (leftPageTurn) {
0751: newPoint.x = this .transformIndex(tmpPoint.x);
0752: }
0753:
0754: // calculate using new point
0755: calculate(newPoint);
0756:
0757: break;
0758:
0759: case AUTO_DROP_BUT_TURN:
0760:
0761: int xDiff2 = autoPoint.x - bookBounds.x;
0762: int stepsNeeded2 = 1 + (xDiff2 / 15);
0763: if (stepsNeeded2 == 0) {
0764: stepsNeeded2 = 1;
0765: }
0766: int yStep2 = (bookBounds.y + bookBounds.height - autoPoint.y)
0767: / stepsNeeded2;
0768:
0769: tmpPoint.x = tmpPoint.x - 15;
0770: tmpPoint.y = tmpPoint.y + yStep2;
0771: if (tmpPoint.x <= bookBounds.x) {
0772: timer.stop();
0773: action = null;
0774: switchImages();
0775: if (leftPageTurn) {
0776: tmpPoint.x = this .transformIndex(bookBounds.x);
0777: } else {
0778: tmpPoint.x = bookBounds.x;
0779: }
0780: tmpPoint.y = bookBounds.y + bookBounds.height;
0781: calculate(tmpPoint);
0782: initRotationX();
0783: this .repaint();
0784: return;
0785: }
0786:
0787: Point newPoint2 = (Point) tmpPoint.clone();
0788:
0789: if (leftPageTurn) {
0790: newPoint2.x = this .transformIndex(tmpPoint.x);
0791: }
0792:
0793: // calculate using new point
0794: calculate(newPoint2);
0795:
0796: break;
0797: }
0798: }
0799:
0800: ////////////////////
0801: // HELPER METHODS //
0802: ////////////////////
0803:
0804: protected boolean isMouseInRegion(MouseEvent e) {
0805: int x = e.getX();
0806: int y = e.getY();
0807:
0808: int maxX = bookBounds.x + bookBounds.width;
0809: int minX = maxX - cornerRegionSize;
0810: int maxY = bookBounds.y + bookBounds.height;
0811: int minY = maxY - cornerRegionSize;
0812:
0813: int minX2 = bookBounds.x;
0814: int maxX2 = minX2 + cornerRegionSize;
0815:
0816: leftPageTurn = ((x < maxX2) && (x > minX2));
0817:
0818: return ((y < maxY) && (y > minY) && (((x < maxX) && (x > minX)) || ((x < maxX2) && (x > minX2))));
0819: }
0820:
0821: protected boolean isMouseInBook(MouseEvent e) {
0822: return bookBounds.contains(e.getX(), e.getY());
0823: }
0824:
0825: protected double transformIndex(double x) {
0826: return bookBounds.width + bookBounds.x - x + bookBounds.x;
0827: }
0828:
0829: protected int transformIndex(int x) {
0830: return bookBounds.width + bookBounds.x - x + bookBounds.x;
0831: }
0832:
0833: protected void calculate(Point p) {
0834:
0835: if (leftPageTurn) {
0836: p.x = transformIndex(p.x);
0837: }
0838:
0839: // if no page to turn available then dont
0840: // allow turn effect
0841: if (currentRightImage == null && !leftPageTurn) {
0842: rotationX = bookBounds.width + bookBounds.x;
0843: nextPageAngle = 0;
0844: backPageAngle = 0;
0845: return;
0846: } else if (currentLeftImage == null && leftPageTurn) {
0847: rotationX = bookBounds.width + bookBounds.x;
0848: nextPageAngle = 0;
0849: backPageAngle = 0;
0850: return;
0851: }
0852:
0853: Point cp = new Point(bookBounds.width + bookBounds.x,
0854: bookBounds.y + bookBounds.height);
0855: double bRico = 1.0 * (cp.x - p.getX()) / (cp.y - p.getY());
0856: bRico = -bRico;
0857: Point mid = new Point(0, 0);
0858: mid.x = (int) ((cp.x + p.getX()) / 2.0);
0859: mid.y = (int) ((cp.y + p.getY()) / 2.0);
0860: double c = mid.y - bRico * mid.x;
0861: rotationX = Math.max((int) ((cp.y - c) / bRico), pageWidth
0862: + bookBounds.x);
0863:
0864: nextPageAngle = PI_DIV_2 - Math.abs(Math.atan(bRico));
0865: backPageAngle = 2.0 * nextPageAngle;
0866: this .repaint();
0867: }
0868:
0869: public void nextPage() {
0870: action = new Integer(AUTO_TURN);
0871: autoPoint = new Point(bookBounds.x + bookBounds.width,
0872: bookBounds.y + bookBounds.height);
0873: this .timer.restart();
0874: }
0875:
0876: public void previousPage() {
0877: leftPageTurn = true;
0878: nextPage();
0879: }
0880:
0881: protected void initRotationX() {
0882: rotationX = bookBounds.width + bookBounds.x;
0883: }
0884:
0885: protected boolean passedThreshold() {
0886: int done = bookBounds.width + bookBounds.x - rotationX;
0887: double X = Math.min(Math.tan(PI_DIV_2 - nextPageAngle) * done,
0888: bookBounds.height);
0889: X *= done * 2;
0890: double threshold = bookBounds.height * pageWidth;
0891: return X > threshold;
0892: }
0893:
0894: protected void switchImages() {
0895: if (leftPageTurn) {
0896: leftPageIndex -= 2;
0897: currentLeftImage = previousLeftImage;
0898: currentRightImage = previousRightImage;
0899:
0900: } else {
0901: leftPageIndex += 2;
0902: currentLeftImage = nextLeftImage;
0903: currentRightImage = nextRightImage;
0904: }
0905:
0906: nextLeftImage = getPage(leftPageIndex + 2);
0907: nextRightImage = getPage(leftPageIndex + 3);
0908: previousLeftImage = getPage(leftPageIndex - 2);
0909: previousRightImage = getPage(leftPageIndex - 1);
0910: }
0911:
0912: protected BufferedImage getBlankPage(int index) {
0913: BufferedImage img = new BufferedImage(pageWidth,
0914: bookBounds.height, BufferedImage.TYPE_3BYTE_BGR);
0915: Graphics gfx = img.getGraphics();
0916: gfx.setColor(Color.WHITE);
0917: gfx.fillRect(0, 0, img.getWidth(), img.getHeight());
0918: return img;
0919: }
0920:
0921: protected void setGraphicsHints(Graphics2D g2) {
0922: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0923: RenderingHints.VALUE_ANTIALIAS_ON);
0924: g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
0925: RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
0926: g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
0927: RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
0928: g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
0929: RenderingHints.VALUE_COLOR_RENDER_QUALITY);
0930: g2.setRenderingHint(RenderingHints.KEY_RENDERING,
0931: RenderingHints.VALUE_RENDER_QUALITY);
0932: }
0933:
0934: protected Image loadPage(int index) {
0935: return new ImageIcon(JBookPanel.class.getResource(pageLocation
0936: + pageName + index + "." + pageExtension)).getImage();
0937: }
0938:
0939: protected Image getPage(int index) {
0940:
0941: // if request goes beyond available pages return null
0942: if (index > nrOfPages) {
0943:
0944: // if back of existing page then return a blank
0945: if ((index - 1) % 2 == 0) {
0946: return getBlankPage(index);
0947: } else {
0948: return null;
0949: }
0950: }
0951: if (index < 1) {
0952: if (index == 0) {
0953: try {
0954: return loadPage(index);
0955: } catch (Exception e) {
0956: return getBlankPage(index);
0957: }
0958: } else {
0959: return null;
0960: }
0961: }
0962:
0963: // if no images specified return blank ones
0964: if (pageLocation == null || pageName == null
0965: || pageExtension == null) {
0966:
0967: // create the blank page
0968: BufferedImage img = getBlankPage(index);
0969:
0970: // draw page number
0971: Graphics gfx = img.getGraphics();
0972: Graphics2D g2 = (Graphics2D) gfx;
0973: setGraphicsHints(g2);
0974: paintPageNumber(gfx, index, img.getWidth(), img.getHeight());
0975:
0976: return img;
0977:
0978: } else {
0979: return loadPage(index);
0980: }
0981: }
0982:
0983: /////////////////////////
0984: // GETTERS AND SETTERS //
0985: /////////////////////////
0986:
0987: public void setMargins(int top, int left) {
0988: bookBounds.x = left;
0989: bookBounds.y = top;
0990:
0991: initRotationX();
0992: }
0993:
0994: public void setPages(String pageLocation, String pageName,
0995: String pageExtension, int nrOfPages, int pageWidth,
0996: int pageHeight) {
0997:
0998: this .pageLocation = pageLocation;
0999: this .pageName = pageName;
1000: this .pageExtension = pageExtension;
1001: this .nrOfPages = nrOfPages;
1002:
1003: this .pageWidth = pageWidth;
1004: bookBounds.width = 2 * pageWidth;
1005: bookBounds.height = pageHeight;
1006:
1007: initRotationX();
1008:
1009: currentLeftImage = getPage(leftPageIndex);
1010: currentRightImage = getPage(leftPageIndex + 1);
1011: nextLeftImage = getPage(leftPageIndex + 2);
1012: nextRightImage = getPage(leftPageIndex + 3);
1013: previousLeftImage = getPage(leftPageIndex - 2);
1014: previousRightImage = getPage(leftPageIndex - 1);
1015: }
1016:
1017: public int getRefreshSpeed() {
1018: return refreshSpeed;
1019: }
1020:
1021: public void setRefreshSpeed(int refreshSpeed) {
1022: this .refreshSpeed = refreshSpeed;
1023: }
1024:
1025: public Color getShadowDarkColor() {
1026: return shadowDarkColor;
1027: }
1028:
1029: public void setShadowDarkColor(Color shadowDarkColor) {
1030: this .shadowDarkColor = shadowDarkColor;
1031: }
1032:
1033: public Color getShadowNeutralColor() {
1034: return shadowNeutralColor;
1035: }
1036:
1037: public void setShadowNeutralColor(Color shadowNeutralColor) {
1038: this .shadowNeutralColor = shadowNeutralColor;
1039: }
1040:
1041: public int getShadowWidth() {
1042: return shadowWidth;
1043: }
1044:
1045: public void setShadowWidth(int shadowWidth) {
1046: this .shadowWidth = shadowWidth;
1047: }
1048:
1049: public boolean isSoftClipping() {
1050: return softClipping;
1051: }
1052:
1053: public void setSoftClipping(boolean softClipping) {
1054: this .softClipping = softClipping;
1055: }
1056:
1057: public boolean isBorderLinesVisible() {
1058: return borderLinesVisible;
1059: }
1060:
1061: public void setBorderLinesVisible(boolean borderLinesVisible) {
1062: this .borderLinesVisible = borderLinesVisible;
1063: }
1064:
1065: public int getLeftPageIndex() {
1066: return leftPageIndex;
1067: }
1068:
1069: public void setLeftPageIndex(int leftPageIndex) {
1070: if (leftPageIndex <= -1) {
1071: leftPageIndex = -1;
1072: }
1073: this .leftPageIndex = leftPageIndex;
1074:
1075: previousLeftImage = getPage(leftPageIndex - 2);
1076: previousRightImage = getPage(leftPageIndex - 1);
1077: currentLeftImage = getPage(leftPageIndex);
1078: ;
1079: currentRightImage = getPage(leftPageIndex + 1);
1080: ;
1081: nextLeftImage = getPage(leftPageIndex + 2);
1082: nextRightImage = getPage(leftPageIndex + 3);
1083: }
1084: }
|