* $Id: PDFViewer.java,v 1.10 2009-08-07 23:18:33 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Rectangle2D;
import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.awt.Toolkit;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.SwingUtilities;
import com.sun.pdfview.action.GoToAction;
import com.sun.pdfview.action.PDFAction;
import java.lang.reflect.InvocationTargetException;
/**
* A demo PDF Viewer application.
*/
public class PDFViewer extends JFrame
implements KeyListener, TreeSelectionListener,
PageChangeListener {
public final static String TITLE = "SwingLabs PDF Viewer";
/** The current PDFFile */
PDFFile curFile;
/** the name of the current document */
String docName;
/** The split between thumbs and page */
JSplitPane split;
/** The thumbnail scroll pane */
JScrollPane thumbscroll;
/** The thumbnail display */
ThumbPanel thumbs;
/** The page display */
PagePanel page;
/** The full screen page display, or null if not in full screen mode */
PagePanel fspp;
// Thread anim;
/** The current page number (starts at 0), or -1 if no page */
int curpage = -1;
/** the full screen button */
JToggleButton fullScreenButton;
/** the current page number text field */
JTextField pageField;
/** the full screen window, or null if not in full screen mode */
FullScreenWindow fullScreen;
/** the root of the outline, or null if there is no outline */
OutlineNode outline = null;
/** The page format for printing */
PageFormat pformat = PrinterJob.getPrinterJob().defaultPage();
/** true if the thumb panel should exist at all */
boolean doThumb = true;
/** flag to indicate when a newly added document has been announced */
Flag docWaiter;
/** a thread that pre-loads the next page for faster response */
PagePreparer pagePrep;
/** the window containing the pdf outline, or null if one doesn't exist */
JDialog olf;
/** the document menu */
JMenu docMenu;
/**
* utility method to get an icon from the resources of this class
* @param name the name of the icon
* @return the icon, or null if the icon wasn't found.
*/
public Icon getIcon(String name) {
Icon icon = null;
URL url = null;
try {
url = getClass().getResource(name);
icon = new ImageIcon(url);
if (icon == null) {
System.out.println("Couldn't find " + url);
}
} catch (Exception e) {
System.out.println("Couldn't find " + getClass().getName() + "/" + name);
e.printStackTrace();
}
return icon;
}
/// FILE MENU
Action openAction = new AbstractAction("Open...") {
public void actionPerformed(ActionEvent evt) {
doOpen();
}
};
Action pageSetupAction = new AbstractAction("Page setup...") {
public void actionPerformed(ActionEvent evt) {
doPageSetup();
}
};
Action printAction = new AbstractAction("Print...", getIcon("gfx/print.gif")) {
public void actionPerformed(ActionEvent evt) {
doPrint();
}
};
Action closeAction = new AbstractAction("Close") {
public void actionPerformed(ActionEvent evt) {
doClose();
}
};
Action quitAction = new AbstractAction("Quit") {
public void actionPerformed(ActionEvent evt) {
doQuit();
}
};
class ZoomAction extends AbstractAction {
double zoomfactor = 1.0;
public ZoomAction(String name, double factor) {
super(name);
zoomfactor = factor;
}
public ZoomAction(String name, Icon icon, double factor) {
super(name, icon);
zoomfactor = factor;
}
public void actionPerformed(ActionEvent evt) {
doZoom(zoomfactor);
}
}
ZoomAction zoomInAction = new ZoomAction("Zoom in",
getIcon("gfx/zoomin.gif"),
2.0);
ZoomAction zoomOutAction = new ZoomAction("Zoom out",
getIcon("gfx/zoomout.gif"),
0.5);
Action zoomToolAction = new AbstractAction("", getIcon("gfx/zoom.gif")) {
public void actionPerformed(ActionEvent evt) {
doZoomTool();
}
};
Action fitInWindowAction = new AbstractAction("Fit in window",
getIcon("gfx/fit.gif")) {
public void actionPerformed(ActionEvent evt) {
doFitInWindow();
}
};
class ThumbAction extends AbstractAction
implements PropertyChangeListener {
boolean isOpen = true;
public ThumbAction() {
super("Hide thumbnails");
}
public void propertyChange(PropertyChangeEvent evt) {
int v = ((Integer) evt.getNewValue()).intValue();
if (v <= 1) {
isOpen = false;
putValue(ACTION_COMMAND_KEY, "Show thumbnails");
putValue(NAME, "Show thumbnails");
} else {
isOpen = true;
putValue(ACTION_COMMAND_KEY, "Hide thumbnails");
putValue(NAME, "Hide thumbnails");
}
}
public void actionPerformed(ActionEvent evt) {
doThumbs(!isOpen);
}
}
ThumbAction thumbAction = new ThumbAction();
Action fullScreenAction = new AbstractAction("Full screen",
getIcon("gfx/fullscrn.gif")) {
public void actionPerformed(ActionEvent evt) {
doFullScreen((evt.getModifiers() & evt.SHIFT_MASK) != 0);
}
};
Action nextAction = new AbstractAction("Next", getIcon("gfx/next.gif")) {
public void actionPerformed(ActionEvent evt) {
doNext();
}
};
Action firstAction = new AbstractAction("First", getIcon("gfx/first.gif")) {
public void actionPerformed(ActionEvent evt) {
doFirst();
}
};
Action lastAction = new AbstractAction("Last", getIcon("gfx/last.gif")) {
public void actionPerformed(ActionEvent evt) {
doLast();
}
};
Action prevAction = new AbstractAction("Prev", getIcon("gfx/prev.gif")) {
public void actionPerformed(ActionEvent evt) {
doPrev();
}
};
/**
* Create a new PDFViewer based on a user, with or without a thumbnail
* panel.
* @param useThumbs true if the thumb panel should exist, false if not.
*/
public PDFViewer(boolean useThumbs) {
super(TITLE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
doQuit();
}
});
doThumb = useThumbs;
init();
}
/**
* Initialize this PDFViewer by creating the GUI.
*/
protected void init() {
page = new PagePanel();
page.addKeyListener(this);
if (doThumb) {
split = new JSplitPane(split.HORIZONTAL_SPLIT);
split.addPropertyChangeListener(split.DIVIDER_LOCATION_PROPERTY,
thumbAction);
split.setOneTouchExpandable(true);
thumbs = new ThumbPanel(null);
thumbscroll = new JScrollPane(thumbs,
thumbscroll.VERTICAL_SCROLLBAR_ALWAYS,
thumbscroll.HORIZONTAL_SCROLLBAR_NEVER);
split.setLeftComponent(thumbscroll);
split.setRightComponent(page);
getContentPane().add(split, BorderLayout.CENTER);
} else {
getContentPane().add(page, BorderLayout.CENTER);
}
JToolBar toolbar = new JToolBar();
toolbar.setFloatable(false);
JButton jb;
jb = new JButton(firstAction);
jb.setText("");
toolbar.add(jb);
jb = new JButton(prevAction);
jb.setText("");
toolbar.add(jb);
pageField = new JTextField("-", 3);
// pageField.setEnabled(false);
pageField.setMaximumSize(new Dimension(45, 32));
pageField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
doPageTyped();
}
});
toolbar.add(pageField);
jb = new JButton(nextAction);
jb.setText("");
toolbar.add(jb);
jb = new JButton(lastAction);
jb.setText("");
toolbar.add(jb);
toolbar.add(Box.createHorizontalGlue());
fullScreenButton = new JToggleButton(fullScreenAction);
fullScreenButton.setText("");
toolbar.add(fullScreenButton);
fullScreenButton.setEnabled(true);
toolbar.add(Box.createHorizontalGlue());
JToggleButton jtb;
ButtonGroup bg = new ButtonGroup();
jtb = new JToggleButton(zoomToolAction);
jtb.setText("");
bg.add(jtb);
toolbar.add(jtb);
jtb = new JToggleButton(fitInWindowAction);
jtb.setText("");
bg.add(jtb);
jtb.setSelected(true);
toolbar.add(jtb);
toolbar.add(Box.createHorizontalGlue());
jb = new JButton(printAction);
jb.setText("");
toolbar.add(jb);
getContentPane().add(toolbar, BorderLayout.NORTH);
JMenuBar mb = new JMenuBar();
JMenu file = new JMenu("File");
file.add(openAction);
file.add(closeAction);
file.addSeparator();
file.add(pageSetupAction);
file.add(printAction);
file.addSeparator();
file.add(quitAction);
mb.add(file);
JMenu view = new JMenu("View");
JMenu zoom = new JMenu("Zoom");
zoom.add(zoomInAction);
zoom.add(zoomOutAction);
zoom.add(fitInWindowAction);
zoom.setEnabled(false);
view.add(zoom);
view.add(fullScreenAction);
if (doThumb) {
view.addSeparator();
view.add(thumbAction);
}
mb.add(view);
setJMenuBar(mb);
setEnabling();
pack();
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
int x = (screen.width - getWidth()) / 2;
int y = (screen.height - getHeight()) / 2;
setLocation(x, y);
if (SwingUtilities.isEventDispatchThread()) {
setVisible(true);
} else {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
setVisible(true);
}
});
} catch (InvocationTargetException ie) {
// ignore
} catch (InterruptedException ie) {
// ignore
}
}
}
/**
* Changes the displayed page, desyncing if we're not on the
* same page as a presenter.