0001: /*
0002: * $Id: PDFViewer.java,v 1.5 2007/12/20 18:33:33 rbair Exp $
0003: *
0004: * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
0005: * Santa Clara, California 95054, U.S.A. All rights reserved.
0006: *
0007: * This library is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU Lesser General Public
0009: * License as published by the Free Software Foundation; either
0010: * version 2.1 of the License, or (at your option) any later version.
0011: *
0012: * This library is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this library; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
0020: */
0021:
0022: package com.sun.pdfview;
0023:
0024: import java.awt.BorderLayout;
0025: import java.awt.Color;
0026: import java.awt.Dimension;
0027: import java.awt.event.ActionEvent;
0028: import java.awt.event.ActionListener;
0029: import java.awt.event.KeyEvent;
0030: import java.awt.event.KeyListener;
0031: import java.awt.event.WindowAdapter;
0032: import java.awt.event.WindowEvent;
0033: import java.awt.geom.Rectangle2D;
0034: import java.awt.print.Book;
0035: import java.awt.print.PageFormat;
0036: import java.awt.print.PrinterException;
0037: import java.awt.print.PrinterJob;
0038: import java.awt.Toolkit;
0039: import java.beans.PropertyChangeEvent;
0040: import java.beans.PropertyChangeListener;
0041: import java.io.File;
0042: import java.io.FileOutputStream;
0043: import java.io.IOException;
0044: import java.io.RandomAccessFile;
0045: import java.net.Socket;
0046: import java.net.URL;
0047: import java.nio.ByteBuffer;
0048: import java.nio.channels.FileChannel;
0049: import java.util.Date;
0050: import java.util.HashMap;
0051:
0052: import javax.swing.AbstractAction;
0053: import javax.swing.Action;
0054: import javax.swing.Box;
0055: import javax.swing.ButtonGroup;
0056: import javax.swing.Icon;
0057: import javax.swing.ImageIcon;
0058: import javax.swing.JButton;
0059: import javax.swing.JCheckBox;
0060: import javax.swing.JCheckBoxMenuItem;
0061: import javax.swing.JDialog;
0062: import javax.swing.JFileChooser;
0063: import javax.swing.JFrame;
0064: import javax.swing.JMenu;
0065: import javax.swing.JMenuBar;
0066: import javax.swing.JMenuItem;
0067: import javax.swing.JOptionPane;
0068: import javax.swing.JScrollPane;
0069: import javax.swing.JSplitPane;
0070: import javax.swing.JTextField;
0071: import javax.swing.JToggleButton;
0072: import javax.swing.JToolBar;
0073: import javax.swing.JTree;
0074: import javax.swing.event.TreeSelectionEvent;
0075: import javax.swing.event.TreeSelectionListener;
0076: import javax.swing.filechooser.FileFilter;
0077: import javax.swing.SwingUtilities;
0078:
0079: import com.sun.pdfview.action.GoToAction;
0080: import com.sun.pdfview.action.PDFAction;
0081: import java.lang.reflect.InvocationTargetException;
0082:
0083: /**
0084: * A PDF Viewer application that integrates with the
0085: * <a href="http://sunlabs.east/mc/">Meeting Central</a> project.
0086: */
0087: public class PDFViewer extends JFrame implements KeyListener,
0088: TreeSelectionListener, PageChangeListener {
0089:
0090: public final static String TITLE = "SwingLabs PDF Viewer";
0091:
0092: /** The current PDFFile */
0093: PDFFile curFile;
0094: /** the name of the current document */
0095: String docName;
0096: /** The split between thumbs and page */
0097: JSplitPane split;
0098: /** The thumbnail scroll pane */
0099: JScrollPane thumbscroll;
0100: /** The thumbnail display */
0101: ThumbPanel thumbs;
0102: /** The page display */
0103: PagePanel page;
0104: /** The full screen page display, or null if not in full screen mode */
0105: PagePanel fspp;
0106:
0107: // Thread anim;
0108: /** The current page number (starts at 0), or -1 if no page */
0109: int curpage = -1;
0110: /** the full screen button */
0111: JToggleButton fullScreenButton;
0112: /** the current page number text field */
0113: JTextField pageField;
0114: /** the full screen window, or null if not in full screen mode */
0115: FullScreenWindow fullScreen;
0116: /** the root of the outline, or null if there is no outline */
0117: OutlineNode outline = null;
0118: /** The page format for printing */
0119: PageFormat pformat = PrinterJob.getPrinterJob().defaultPage();
0120: /** true if the thumb panel should exist at all */
0121: boolean doThumb = true;
0122: /** flag to indicate when a newly added document has been announced */
0123: Flag docWaiter;
0124: /** a thread that pre-loads the next page for faster response */
0125: PagePreparer pagePrep;
0126: /** the window containing the pdf outline, or null if one doesn't exist */
0127: JDialog olf;
0128: /** the document menu */
0129: JMenu docMenu;
0130:
0131: /**
0132: * utility method to get an icon from the resources of this class
0133: * @param name the name of the icon
0134: * @return the icon, or null if the icon wasn't found.
0135: */
0136: public Icon getIcon(String name) {
0137: Icon icon = null;
0138: URL url = null;
0139: try {
0140: url = getClass().getResource(name);
0141:
0142: icon = new ImageIcon(url);
0143: if (icon == null) {
0144: System.out.println("Couldn't find " + url);
0145: }
0146: } catch (Exception e) {
0147: System.out.println("Couldn't find " + getClass().getName()
0148: + "/" + name);
0149: e.printStackTrace();
0150: }
0151: return icon;
0152: }
0153:
0154: /// FILE MENU
0155: Action openAction = new AbstractAction("Open...") {
0156:
0157: public void actionPerformed(ActionEvent evt) {
0158: doOpen();
0159: }
0160: };
0161: Action pageSetupAction = new AbstractAction("Page setup...") {
0162:
0163: public void actionPerformed(ActionEvent evt) {
0164: doPageSetup();
0165: }
0166: };
0167: Action printAction = new AbstractAction("Print...",
0168: getIcon("gfx/print.gif")) {
0169:
0170: public void actionPerformed(ActionEvent evt) {
0171: doPrint();
0172: }
0173: };
0174: Action closeAction = new AbstractAction("Close") {
0175:
0176: public void actionPerformed(ActionEvent evt) {
0177: doClose();
0178: }
0179: };
0180: Action quitAction = new AbstractAction("Quit") {
0181:
0182: public void actionPerformed(ActionEvent evt) {
0183: doQuit();
0184: }
0185: };
0186:
0187: class ZoomAction extends AbstractAction {
0188:
0189: double zoomfactor = 1.0;
0190:
0191: public ZoomAction(String name, double factor) {
0192: super (name);
0193: zoomfactor = factor;
0194: }
0195:
0196: public ZoomAction(String name, Icon icon, double factor) {
0197: super (name, icon);
0198: zoomfactor = factor;
0199: }
0200:
0201: public void actionPerformed(ActionEvent evt) {
0202: doZoom(zoomfactor);
0203: }
0204: }
0205:
0206: ZoomAction zoomInAction = new ZoomAction("Zoom in",
0207: getIcon("gfx/zoomin.gif"), 2.0);
0208: ZoomAction zoomOutAction = new ZoomAction("Zoom out",
0209: getIcon("gfx/zoomout.gif"), 0.5);
0210: Action zoomToolAction = new AbstractAction("",
0211: getIcon("gfx/zoom.gif")) {
0212:
0213: public void actionPerformed(ActionEvent evt) {
0214: doZoomTool();
0215: }
0216: };
0217: Action fitInWindowAction = new AbstractAction("Fit in window",
0218: getIcon("gfx/fit.gif")) {
0219:
0220: public void actionPerformed(ActionEvent evt) {
0221: doFitInWindow();
0222: }
0223: };
0224:
0225: class ThumbAction extends AbstractAction implements
0226: PropertyChangeListener {
0227:
0228: boolean isOpen = true;
0229:
0230: public ThumbAction() {
0231: super ("Hide thumbnails");
0232: }
0233:
0234: public void propertyChange(PropertyChangeEvent evt) {
0235: int v = ((Integer) evt.getNewValue()).intValue();
0236: if (v <= 1) {
0237: isOpen = false;
0238: putValue(ACTION_COMMAND_KEY, "Show thumbnails");
0239: putValue(NAME, "Show thumbnails");
0240: } else {
0241: isOpen = true;
0242: putValue(ACTION_COMMAND_KEY, "Hide thumbnails");
0243: putValue(NAME, "Hide thumbnails");
0244: }
0245: }
0246:
0247: public void actionPerformed(ActionEvent evt) {
0248: doThumbs(!isOpen);
0249: }
0250: }
0251:
0252: ThumbAction thumbAction = new ThumbAction();
0253: Action fullScreenAction = new AbstractAction("Full screen",
0254: getIcon("gfx/fullscrn.gif")) {
0255:
0256: public void actionPerformed(ActionEvent evt) {
0257: doFullScreen((evt.getModifiers() & evt.SHIFT_MASK) != 0);
0258: }
0259: };
0260: Action nextAction = new AbstractAction("Next",
0261: getIcon("gfx/next.gif")) {
0262:
0263: public void actionPerformed(ActionEvent evt) {
0264: doNext();
0265: }
0266: };
0267: Action firstAction = new AbstractAction("First",
0268: getIcon("gfx/first.gif")) {
0269:
0270: public void actionPerformed(ActionEvent evt) {
0271: doFirst();
0272: }
0273: };
0274: Action lastAction = new AbstractAction("Last",
0275: getIcon("gfx/last.gif")) {
0276:
0277: public void actionPerformed(ActionEvent evt) {
0278: doLast();
0279: }
0280: };
0281: Action prevAction = new AbstractAction("Prev",
0282: getIcon("gfx/prev.gif")) {
0283:
0284: public void actionPerformed(ActionEvent evt) {
0285: doPrev();
0286: }
0287: };
0288:
0289: /**
0290: * Create a new PDFViewer based on a user, with or without a thumbnail
0291: * panel.
0292: * @param useThumbs true if the thumb panel should exist, false if not.
0293: */
0294: public PDFViewer(boolean useThumbs) {
0295: super (TITLE);
0296: addWindowListener(new WindowAdapter() {
0297:
0298: public void windowClosing(WindowEvent evt) {
0299: doQuit();
0300: }
0301: });
0302: doThumb = useThumbs;
0303: init();
0304: }
0305:
0306: /**
0307: * Initialize this PDFViewer by creating the GUI.
0308: */
0309: protected void init() {
0310: page = new PagePanel();
0311: page.addKeyListener(this );
0312:
0313: if (doThumb) {
0314: split = new JSplitPane(split.HORIZONTAL_SPLIT);
0315: split.addPropertyChangeListener(
0316: split.DIVIDER_LOCATION_PROPERTY, thumbAction);
0317: split.setOneTouchExpandable(true);
0318: thumbs = new ThumbPanel(null);
0319: thumbscroll = new JScrollPane(thumbs,
0320: thumbscroll.VERTICAL_SCROLLBAR_ALWAYS,
0321: thumbscroll.HORIZONTAL_SCROLLBAR_NEVER);
0322: split.setLeftComponent(thumbscroll);
0323: split.setRightComponent(page);
0324: getContentPane().add(split, BorderLayout.CENTER);
0325: } else {
0326: getContentPane().add(page, BorderLayout.CENTER);
0327: }
0328:
0329: JToolBar toolbar = new JToolBar();
0330: toolbar.setFloatable(false);
0331:
0332: JButton jb;
0333:
0334: jb = new JButton(firstAction);
0335: jb.setText("");
0336: toolbar.add(jb);
0337: jb = new JButton(prevAction);
0338: jb.setText("");
0339: toolbar.add(jb);
0340: pageField = new JTextField("-", 3);
0341: // pageField.setEnabled(false);
0342: pageField.setMaximumSize(new Dimension(45, 32));
0343: pageField.addActionListener(new ActionListener() {
0344:
0345: public void actionPerformed(ActionEvent evt) {
0346: doPageTyped();
0347: }
0348: });
0349: toolbar.add(pageField);
0350: jb = new JButton(nextAction);
0351: jb.setText("");
0352: toolbar.add(jb);
0353: jb = new JButton(lastAction);
0354: jb.setText("");
0355: toolbar.add(jb);
0356:
0357: toolbar.add(Box.createHorizontalGlue());
0358:
0359: fullScreenButton = new JToggleButton(fullScreenAction);
0360: fullScreenButton.setText("");
0361: toolbar.add(fullScreenButton);
0362: fullScreenButton.setEnabled(true);
0363:
0364: toolbar.add(Box.createHorizontalGlue());
0365:
0366: JToggleButton jtb;
0367: ButtonGroup bg = new ButtonGroup();
0368:
0369: jtb = new JToggleButton(zoomToolAction);
0370: jtb.setText("");
0371: bg.add(jtb);
0372: toolbar.add(jtb);
0373: jtb = new JToggleButton(fitInWindowAction);
0374: jtb.setText("");
0375: bg.add(jtb);
0376: jtb.setSelected(true);
0377: toolbar.add(jtb);
0378:
0379: toolbar.add(Box.createHorizontalGlue());
0380:
0381: jb = new JButton(printAction);
0382: jb.setText("");
0383: toolbar.add(jb);
0384:
0385: getContentPane().add(toolbar, BorderLayout.NORTH);
0386:
0387: JMenuBar mb = new JMenuBar();
0388: JMenu file = new JMenu("File");
0389: file.add(openAction);
0390: file.add(closeAction);
0391: file.addSeparator();
0392: file.add(pageSetupAction);
0393: file.add(printAction);
0394: file.addSeparator();
0395: file.add(quitAction);
0396: mb.add(file);
0397: JMenu view = new JMenu("View");
0398: JMenu zoom = new JMenu("Zoom");
0399: zoom.add(zoomInAction);
0400: zoom.add(zoomOutAction);
0401: zoom.add(fitInWindowAction);
0402: zoom.setEnabled(false);
0403: view.add(zoom);
0404: view.add(fullScreenAction);
0405:
0406: if (doThumb) {
0407: view.addSeparator();
0408: view.add(thumbAction);
0409: }
0410:
0411: mb.add(view);
0412: setJMenuBar(mb);
0413: setEnabling();
0414: pack();
0415: Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
0416: int x = (screen.width - getWidth()) / 2;
0417: int y = (screen.height - getHeight()) / 2;
0418: setLocation(x, y);
0419: if (SwingUtilities.isEventDispatchThread()) {
0420: show();
0421: } else {
0422: try {
0423: SwingUtilities.invokeAndWait(new Runnable() {
0424:
0425: public void run() {
0426: show();
0427: }
0428: });
0429: } catch (InvocationTargetException ie) {
0430: // ignore
0431: } catch (InterruptedException ie) {
0432: // ignore
0433: }
0434: }
0435: }
0436:
0437: /**
0438: * Changes the displayed page, desyncing if we're not on the
0439: * same page as a presenter.
0440: * @param pagenum the page to display
0441: */
0442: public void gotoPage(int pagenum) {
0443: if (pagenum < 0) {
0444: pagenum = 0;
0445: } else if (pagenum >= curFile.getNumPages()) {
0446: pagenum = curFile.getNumPages() - 1;
0447: }
0448: forceGotoPage(pagenum);
0449: }
0450:
0451: /**
0452: * Changes the displayed page.
0453: * @param pagenum the page to display
0454: */
0455: public void forceGotoPage(int pagenum) {
0456: if (pagenum <= 0) {
0457: pagenum = 0;
0458: } else if (pagenum >= curFile.getNumPages()) {
0459: pagenum = curFile.getNumPages() - 1;
0460: }
0461: // System.out.println("Going to page " + pagenum);
0462: curpage = pagenum;
0463:
0464: // update the page text field
0465: pageField.setText(String.valueOf(curpage + 1));
0466:
0467: // fetch the page and show it in the appropriate place
0468: PDFPage pg = curFile.getPage(pagenum + 1);
0469: if (fspp != null) {
0470: fspp.showPage(pg);
0471: fspp.requestFocus();
0472: } else {
0473: page.showPage(pg);
0474: page.requestFocus();
0475: }
0476:
0477: // update the thumb panel
0478: if (doThumb) {
0479: thumbs.pageShown(pagenum);
0480: }
0481:
0482: // stop any previous page prepper, and start a new one
0483: if (pagePrep != null) {
0484: pagePrep.quit();
0485: }
0486: pagePrep = new PagePreparer(pagenum);
0487: pagePrep.start();
0488:
0489: setEnabling();
0490: }
0491:
0492: /**
0493: * A class to pre-cache the next page for better UI response
0494: */
0495: class PagePreparer extends Thread {
0496:
0497: int waitforPage;
0498: int prepPage;
0499:
0500: /**
0501: * Creates a new PagePreparer to prepare the page after the current
0502: * one.
0503: * @param waitforPage the current page number, 0 based
0504: */
0505: public PagePreparer(int waitforPage) {
0506: setDaemon(true);
0507:
0508: this .waitforPage = waitforPage;
0509: this .prepPage = waitforPage + 1;
0510: }
0511:
0512: public void quit() {
0513: waitforPage = -1;
0514: }
0515:
0516: public void run() {
0517: Dimension size = null;
0518: Rectangle2D clip = null;
0519:
0520: // wait for the current page
0521: // System.out.println("Preparer waiting for page " + (waitforPage + 1));
0522: if (fspp != null) {
0523: fspp.waitForCurrentPage();
0524: size = fspp.getCurSize();
0525: clip = fspp.getCurClip();
0526: } else if (page != null) {
0527: page.waitForCurrentPage();
0528: size = page.getCurSize();
0529: clip = page.getCurClip();
0530: }
0531:
0532: if (waitforPage == curpage) {
0533: // don't go any further if the user changed pages.
0534: // System.out.println("Preparer generating page " + (prepPage + 2));
0535: PDFPage pdfPage = curFile.getPage(prepPage + 1, true);
0536: if (pdfPage != null && waitforPage == curpage) {
0537: // don't go any further if the user changed pages
0538: // System.out.println("Generating image for page " + (prepPage + 2));
0539:
0540: pdfPage.getImage(size.width, size.height, clip,
0541: null, true, true);
0542: // System.out.println("Generated image for page "+ (prepPage+2));
0543: }
0544: }
0545: }
0546: }
0547:
0548: /**
0549: * Enable or disable all of the actions based on the current state.
0550: */
0551: public void setEnabling() {
0552: boolean fileavailable = curFile != null;
0553: boolean pageshown = ((fspp != null) ? fspp.getPage() != null
0554: : page.getPage() != null);
0555: boolean printable = fileavailable && curFile.isPrintable();
0556:
0557: pageField.setEnabled(fileavailable);
0558: printAction.setEnabled(printable);
0559: closeAction.setEnabled(fileavailable);
0560: fullScreenAction.setEnabled(pageshown);
0561: prevAction.setEnabled(pageshown);
0562: nextAction.setEnabled(pageshown);
0563: firstAction.setEnabled(fileavailable);
0564: lastAction.setEnabled(fileavailable);
0565: zoomToolAction.setEnabled(pageshown);
0566: fitInWindowAction.setEnabled(pageshown);
0567: zoomInAction.setEnabled(pageshown);
0568: zoomOutAction.setEnabled(pageshown);
0569: }
0570:
0571: /**
0572: * Open a specific pdf file. Creates a DocumentInfo from the file,
0573: * and opens that.
0574: * @param file the file to open
0575: */
0576: public void openFile(File file) throws IOException {
0577: // first open the file for random access
0578: RandomAccessFile raf = new RandomAccessFile(file, "r");
0579:
0580: // extract a file channel
0581: FileChannel channel = raf.getChannel();
0582:
0583: // now memory-map a byte-buffer
0584: ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0,
0585: channel.size());
0586:
0587: // create a PDFFile from the data
0588: PDFFile newfile = null;
0589: try {
0590: newfile = new PDFFile(buf);
0591: } catch (IOException ioe) {
0592: openError(file.getPath()
0593: + " doesn't appear to be a PDF file.");
0594: return;
0595: }
0596:
0597: // Now that we're reasonably sure this document is real, close the
0598: // old one.
0599: doClose();
0600:
0601: // set up our document
0602: this .curFile = newfile;
0603: docName = file.getName();
0604: setTitle(TITLE + ": " + docName);
0605:
0606: // set up the thumbnails
0607: if (doThumb) {
0608: thumbs = new ThumbPanel(curFile);
0609: thumbs.addPageChangeListener(this );
0610: thumbscroll.getViewport().setView(thumbs);
0611: thumbscroll.getViewport().setBackground(Color.gray);
0612: }
0613:
0614: setEnabling();
0615:
0616: // display page 1.
0617: forceGotoPage(0);
0618:
0619: // if the PDF has an outline, display it.
0620: try {
0621: outline = curFile.getOutline();
0622: } catch (IOException ioe) {
0623: }
0624: if (outline != null) {
0625: if (outline.getChildCount() > 0) {
0626: olf = new JDialog(this , "Outline");
0627: olf
0628: .setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
0629: olf.setLocation(this .getLocation());
0630: JTree jt = new JTree(outline);
0631: jt.setRootVisible(false);
0632: jt.addTreeSelectionListener(this );
0633: JScrollPane jsp = new JScrollPane(jt);
0634: olf.getContentPane().add(jsp);
0635: olf.pack();
0636: olf.setVisible(true);
0637: } else {
0638: if (olf != null) {
0639: olf.setVisible(false);
0640: olf = null;
0641: }
0642: }
0643: }
0644: }
0645:
0646: /**
0647: * Display a dialog indicating an error.
0648: */
0649: public void openError(String message) {
0650: JOptionPane.showMessageDialog(split, message,
0651: "Error opening file", JOptionPane.ERROR_MESSAGE);
0652: }
0653:
0654: /**
0655: * A file filter for PDF files.
0656: */
0657: FileFilter pdfFilter = new FileFilter() {
0658:
0659: public boolean accept(File f) {
0660: return f.isDirectory() || f.getName().endsWith(".pdf");
0661: }
0662:
0663: public String getDescription() {
0664: return "Choose a PDF file";
0665: }
0666: };
0667: private File prevDirChoice;
0668:
0669: /**
0670: * Ask the user for a PDF file to open from the local file system
0671: */
0672: public void doOpen() {
0673: try {
0674: JFileChooser fc = new JFileChooser();
0675: fc.setCurrentDirectory(prevDirChoice);
0676: fc.setFileFilter(pdfFilter);
0677: fc.setMultiSelectionEnabled(false);
0678: int returnVal = fc.showOpenDialog(this );
0679: if (returnVal == JFileChooser.APPROVE_OPTION) {
0680: try {
0681: prevDirChoice = fc.getSelectedFile();
0682: openFile(fc.getSelectedFile());
0683: } catch (IOException ioe) {
0684: ioe.printStackTrace();
0685: }
0686: }
0687: } catch (Exception e) {
0688: JOptionPane.showMessageDialog(split,
0689: "Opening files from your local "
0690: + "disk is not available\nfrom the "
0691: + "Java Web Start version of this "
0692: + "program.\n", "Error opening directory",
0693: JOptionPane.ERROR_MESSAGE);
0694: e.printStackTrace();
0695: }
0696: }
0697:
0698: /**
0699: * Open a local file, given a string filename
0700: * @param name the name of the file to open
0701: */
0702: public void doOpen(String name) {
0703: try {
0704: openFile(new File(name));
0705: } catch (IOException ioe) {
0706: }
0707: }
0708:
0709: /**
0710: * Posts the Page Setup dialog
0711: */
0712: public void doPageSetup() {
0713: PrinterJob pjob = PrinterJob.getPrinterJob();
0714: pformat = pjob.pageDialog(pformat);
0715: }
0716:
0717: /**
0718: * A thread for printing in.
0719: */
0720: class PrintThread extends Thread {
0721:
0722: PDFPrintPage ptPages;
0723: PrinterJob ptPjob;
0724:
0725: public PrintThread(PDFPrintPage pages, PrinterJob pjob) {
0726: ptPages = pages;
0727: ptPjob = pjob;
0728: }
0729:
0730: public void run() {
0731: try {
0732: ptPages.show(ptPjob);
0733: ptPjob.print();
0734: } catch (PrinterException pe) {
0735: JOptionPane.showMessageDialog(PDFViewer.this ,
0736: "Printing Error: " + pe.getMessage(),
0737: "Print Aborted", JOptionPane.ERROR_MESSAGE);
0738: }
0739: ptPages.hide();
0740: }
0741: }
0742:
0743: /**
0744: * Print the current document.
0745: */
0746: public void doPrint() {
0747: PrinterJob pjob = PrinterJob.getPrinterJob();
0748: pjob.setJobName(docName);
0749: Book book = new Book();
0750: PDFPrintPage pages = new PDFPrintPage(curFile);
0751: book.append(pages, pformat, curFile.getNumPages());
0752:
0753: pjob.setPageable(book);
0754: if (pjob.printDialog()) {
0755: new PrintThread(pages, pjob).start();
0756: }
0757: }
0758:
0759: /**
0760: * Close the current document.
0761: */
0762: public void doClose() {
0763: if (thumbs != null) {
0764: thumbs.stop();
0765: }
0766: if (olf != null) {
0767: olf.setVisible(false);
0768: olf = null;
0769: }
0770: if (doThumb) {
0771: thumbs = new ThumbPanel(null);
0772: thumbscroll.getViewport().setView(thumbs);
0773: }
0774:
0775: setFullScreenMode(false, false);
0776: page.showPage(null);
0777: curFile = null;
0778: setTitle(TITLE);
0779: setEnabling();
0780: }
0781:
0782: /**
0783: * Shuts down all known threads. This ought to cause the JVM to quit
0784: * if the PDFViewer is the only application running.
0785: */
0786: public void doQuit() {
0787: // if (thumbs != null) {
0788: // thumbs.stop();
0789: // }
0790: doClose();
0791: dispose();
0792: System.exit(0);
0793: }
0794:
0795: /**
0796: * Turns on zooming
0797: */
0798: public void doZoomTool() {
0799: if (fspp == null) {
0800: page.useZoomTool(true);
0801: }
0802: }
0803:
0804: /**
0805: * Turns off zooming; makes the page fit in the window
0806: */
0807: public void doFitInWindow() {
0808: if (fspp == null) {
0809: page.useZoomTool(false);
0810: page.setClip(null);
0811: }
0812: }
0813:
0814: /**
0815: * Shows or hides the thumbnails by moving the split pane divider
0816: */
0817: public void doThumbs(boolean show) {
0818: if (show) {
0819: split
0820: .setDividerLocation((int) thumbs.getPreferredSize().width
0821: + (int) thumbscroll.getVerticalScrollBar()
0822: .getWidth() + 4);
0823: } else {
0824: split.setDividerLocation(0);
0825: }
0826: }
0827:
0828: /**
0829: * Enter full screen mode
0830: * @param force true if the user should be prompted for a screen to
0831: * use in a multiple-monitor setup. If false, the user will only be
0832: * prompted once.
0833: */
0834: public void doFullScreen(boolean force) {
0835: setFullScreenMode(fullScreen == null, force);
0836: }
0837:
0838: public void doZoom(double factor) {
0839: }
0840:
0841: // public void doOpenMeetingDoc(DocumentInfo doc) {
0842: // }
0843:
0844: /**
0845: * Goes to the next page
0846: */
0847: public void doNext() {
0848: gotoPage(curpage + 1);
0849: }
0850:
0851: /**
0852: * Goes to the previous page
0853: */
0854: public void doPrev() {
0855: gotoPage(curpage - 1);
0856: }
0857:
0858: /**
0859: * Goes to the first page
0860: */
0861: public void doFirst() {
0862: gotoPage(0);
0863: }
0864:
0865: /**
0866: * Goes to the last page
0867: */
0868: public void doLast() {
0869: gotoPage(curFile.getNumPages() - 1);
0870: }
0871:
0872: /**
0873: * Goes to the page that was typed in the page number text field
0874: */
0875: public void doPageTyped() {
0876: int pagenum = -1;
0877: try {
0878: pagenum = Integer.parseInt(pageField.getText()) - 1;
0879: } catch (NumberFormatException nfe) {
0880: }
0881: if (pagenum >= curFile.getNumPages()) {
0882: pagenum = curFile.getNumPages() - 1;
0883: }
0884: if (pagenum >= 0) {
0885: if (pagenum != curpage) {
0886: gotoPage(pagenum);
0887: }
0888: } else {
0889: pageField.setText(String.valueOf(curpage));
0890: }
0891: }
0892:
0893: /**
0894: * Runs the FullScreenMode change in another thread
0895: */
0896: class PerformFullScreenMode implements Runnable {
0897:
0898: boolean force;
0899:
0900: public PerformFullScreenMode(boolean forcechoice) {
0901: force = forcechoice;
0902: }
0903:
0904: public void run() {
0905: fspp = new PagePanel();
0906: fspp.setBackground(Color.black);
0907: page.showPage(null);
0908: fullScreen = new FullScreenWindow(fspp, force);
0909: fspp.addKeyListener(PDFViewer.this );
0910: gotoPage(curpage);
0911: fullScreenAction.setEnabled(true);
0912: }
0913: }
0914:
0915: /**
0916: * Starts or ends full screen mode.
0917: * @param full true to enter full screen mode, false to leave
0918: * @param force true if the user should be prompted for a screen
0919: * to use the second time full screen mode is entered.
0920: */
0921: public void setFullScreenMode(boolean full, boolean force) {
0922: // curpage= -1;
0923: if (full && fullScreen == null) {
0924: fullScreenAction.setEnabled(false);
0925: new Thread(new PerformFullScreenMode(force)).start();
0926: fullScreenButton.setSelected(true);
0927: } else if (!full && fullScreen != null) {
0928: fullScreen.close();
0929: fspp = null;
0930: fullScreen = null;
0931: gotoPage(curpage);
0932: fullScreenButton.setSelected(false);
0933: }
0934: }
0935:
0936: public static void main(String args[]) {
0937: String fileName = null;
0938: boolean useThumbs = true;
0939:
0940: for (int i = 0; i < args.length; i++) {
0941: if (args[i].equalsIgnoreCase("-noThumb")) {
0942: useThumbs = false;
0943: } else if (args[i].equalsIgnoreCase("-help")
0944: || args[i].equalsIgnoreCase("-h")
0945: || args[i].equalsIgnoreCase("-?")) {
0946: System.out
0947: .println("java com.sun.awc.PDFViewer [flags] [file]");
0948: System.out
0949: .println("flags: [-noThumb] [-help or -h or -?]");
0950: System.exit(0);
0951: } else {
0952: fileName = args[i];
0953: }
0954: }
0955: // start the viewer
0956: PDFViewer viewer;
0957: viewer = new PDFViewer(useThumbs);
0958: if (fileName != null) {
0959: viewer.doOpen(fileName);
0960: }
0961: }
0962:
0963: /**
0964: * Handle a key press for navigation
0965: */
0966: public void keyPressed(KeyEvent evt) {
0967: int code = evt.getKeyCode();
0968: if (code == evt.VK_LEFT) {
0969: doPrev();
0970: } else if (code == evt.VK_RIGHT) {
0971: doNext();
0972: } else if (code == evt.VK_UP) {
0973: doPrev();
0974: } else if (code == evt.VK_DOWN) {
0975: doNext();
0976: } else if (code == evt.VK_HOME) {
0977: doFirst();
0978: } else if (code == evt.VK_END) {
0979: doLast();
0980: } else if (code == evt.VK_PAGE_UP) {
0981: doPrev();
0982: } else if (code == evt.VK_PAGE_DOWN) {
0983: doNext();
0984: } else if (code == evt.VK_SPACE) {
0985: doNext();
0986: } else if (code == evt.VK_ESCAPE) {
0987: setFullScreenMode(false, false);
0988: }
0989: }
0990:
0991: /**
0992: * Combines numeric key presses to build a multi-digit page number.
0993: */
0994: class PageBuilder implements Runnable {
0995:
0996: int value = 0;
0997: long timeout;
0998: Thread anim;
0999: static final long TIMEOUT = 500;
1000:
1001: /** add the digit to the page number and start the timeout thread */
1002: public synchronized void keyTyped(int keyval) {
1003: value = value * 10 + keyval;
1004: timeout = System.currentTimeMillis() + TIMEOUT;
1005: if (anim == null) {
1006: anim = new Thread(this );
1007: anim.start();
1008: }
1009: }
1010:
1011: /**
1012: * waits for the timeout, and if time expires, go to the specified
1013: * page number
1014: */
1015: public void run() {
1016: long now, then;
1017: synchronized (this ) {
1018: now = System.currentTimeMillis();
1019: then = timeout;
1020: }
1021: while (now < then) {
1022: try {
1023: Thread.sleep(timeout - now);
1024: } catch (InterruptedException ie) {
1025: }
1026: synchronized (this ) {
1027: now = System.currentTimeMillis();
1028: then = timeout;
1029: }
1030: }
1031: synchronized (this ) {
1032: gotoPage(value - 1);
1033: anim = null;
1034: value = 0;
1035: }
1036: }
1037: }
1038:
1039: PageBuilder pb = new PageBuilder();
1040:
1041: public void keyReleased(KeyEvent evt) {
1042: }
1043:
1044: /**
1045: * gets key presses and tries to build a page if they're numeric
1046: */
1047: public void keyTyped(KeyEvent evt) {
1048: char key = evt.getKeyChar();
1049: if (key >= '0' && key <= '9') {
1050: int val = key - '0';
1051: pb.keyTyped(val);
1052: }
1053: }
1054:
1055: /**
1056: * Someone changed the selection of the outline tree. Go to the new
1057: * page.
1058: */
1059: public void valueChanged(TreeSelectionEvent e) {
1060: if (e.isAddedPath()) {
1061: OutlineNode node = (OutlineNode) e.getPath()
1062: .getLastPathComponent();
1063: if (node == null) {
1064: return;
1065: }
1066:
1067: try {
1068: PDFAction action = node.getAction();
1069: if (action == null) {
1070: return;
1071: }
1072:
1073: if (action instanceof GoToAction) {
1074: PDFDestination dest = ((GoToAction) action)
1075: .getDestination();
1076: if (dest == null) {
1077: return;
1078: }
1079:
1080: PDFObject page = dest.getPage();
1081: if (page == null) {
1082: return;
1083: }
1084:
1085: int pageNum = curFile.getPageNumber(page);
1086: if (pageNum >= 0) {
1087: gotoPage(pageNum);
1088: }
1089: }
1090: } catch (IOException ioe) {
1091: ioe.printStackTrace();
1092: }
1093: }
1094: }
1095: }
|