diff options
author | evrhel <ethanvrhel@gmail.com> | 2020-07-23 18:57:52 -0700 |
---|---|---|
committer | evrhel <ethanvrhel@gmail.com> | 2020-09-24 15:23:37 -0700 |
commit | f1441ed36302f75d94cf4aa4a8047150bf5b7585 (patch) | |
tree | 740520b5368000f5e828aeb8177b87e296879a75 | |
parent | ea3eecbc30b1a4a4747dfd2818262c2f6eea112c (diff) | |
download | ghostpdl-f1441ed36302f75d94cf4aa4a8047150bf5b7585.tar.gz |
Basic viewer working
The viewer has basic capabilities, but still lacks loading
high resolution images, spacing components, and resizing correctly.
9 files changed, 504 insertions, 130 deletions
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java index 2d5035e19..7b6f2c041 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java @@ -17,20 +17,34 @@ import com.artifex.gsjava.util.BytePointer; import com.artifex.gsjava.util.LongReference; import com.artifex.gsviewer.ImageUtil.ImageParams; +/** + * A Document stores an ordered list of Pages. + * + * @author Ethan Vrhel + * + */ public class Document implements List<Page> { private static final Object slock = new Object(); private static final DocumentLoader documentLoader = new DocumentLoader(); private static final int format = GS_COLORS_RGB | GS_DISPLAY_DEPTH_8 | GS_DISPLAY_BIGENDIAN; - public static void loadDocumentAsync(final File file, final DocAsyncLoadCallback cb) + /** + * Loads a document on a separate thread. + * + * @param file The file to load. + * @param cb The callback when loading the document has finished. + * @param dlb The callback signifying when progress has occurred while loading. + * @throws NullPointerException When <code>cb</code> is <code>null</code>. + */ + public static void loadDocumentAsync(final File file, final DocAsyncLoadCallback cb, final DocLoadProgressCallback dlb) throws NullPointerException { Objects.requireNonNull(cb, "DocAsyncLoadCallback"); final Thread t = new Thread(() -> { Document doc = null; Exception exception = null; try { - doc = new Document(file); + doc = new Document(file, dlb); } catch (FileNotFoundException | IllegalStateException | NullPointerException e) { exception = e; } @@ -41,17 +55,69 @@ public class Document implements List<Page> { t.start(); } + /** + * Loads a document on a separate thread. + * + * @param file The file to load. + * @param cb The callback when loading the document has finished. + * @throws NullPointerException When <code>cb</code> is <code>null</code>. + */ + public static void loadDocumentAsync(final File file, final DocAsyncLoadCallback cb) + throws NullPointerException { + loadDocumentAsync(file, cb, null); + } + + /** + * Loads a document on a separate thread. + * + * @param filename The name of the file to load. + * @param cb The callback when loading the document has finished. + * @param dlb The callback signifying when progress has occurred while loading. + * @throws NullPointerException When <code>cb</code> is <code>null</code>. + */ + public static void loadDocumentAsync(final String filename, final DocAsyncLoadCallback cb, final DocLoadProgressCallback dlb) + throws NullPointerException { + loadDocumentAsync(new File(filename), cb, dlb); + } + + /** + * Loads a document on a separate thread. + * + * @param filename The name of the file to load. + * @param cb The callback when loading the document has finished. + * @throws NullPointerException When <code>cb</code> is <code>null</code>. + */ public static void loadDocumentAsync(final String filename, final DocAsyncLoadCallback cb) throws NullPointerException { - loadDocumentAsync(new File(filename), cb); + loadDocumentAsync(filename, cb, null); } @FunctionalInterface public static interface DocAsyncLoadCallback { + /** + * Called when a document has finished loading on a separate + * thread. + * + * @param doc The document which was loaded. <code>null</code> if the document + * failed to load. + * @param exception An exception that ocurred while loading, <code>null</code> + * if no exception was thrown. + */ public void onDocumentLoad(Document doc, Exception exception); } + @FunctionalInterface + public static interface DocLoadProgressCallback { + + /** + * Called when the document has made progress while loading. + * + * @param progress The amount of progress (from 0 to 100). + */ + public void onDocumentProgress(int progress); + } + private static void initDocInstance(LongReference instanceRef) throws IllegalStateException { int code = gsapi_new_instance(instanceRef, GS_NULL); if (code != GS_ERROR_OK) { @@ -76,6 +142,8 @@ public class Document implements List<Page> { private int pageWidth, pageHeight, pageRaster; private BytePointer pimage; private List<BufferedImage> images; + private int progress; + private DocLoadProgressCallback callback; private DocumentLoader() { reset(); @@ -87,6 +155,8 @@ public class Document implements List<Page> { this.pageRaster = 0; this.pimage = null; this.images = new LinkedList<>(); + this.progress = 0; + this.callback = null; return this; } @@ -104,6 +174,11 @@ public class Document implements List<Page> { public int onDisplayPage(long handle, long device, int copies, boolean flush) { byte[] data = (byte[]) pimage.toArrayNoConvert(); images.add(ImageUtil.createImage(data, new ImageParams(pageWidth, pageHeight, pageRaster, BufferedImage.TYPE_3BYTE_BGR))); + + progress += (100 - progress) / 2; + if (callback != null) + callback.onDocumentProgress(progress); + return 0; } } @@ -111,7 +186,16 @@ public class Document implements List<Page> { private File file; private List<Page> pages; - public Document(final File file) + /** + * Creates and loads a new document. + * + * @param file The file to load. + * @param loadCallback The callback to indicate when progress has occurred while loading. + * @throws FileNotFoundException When <code>file</code> does not exist. + * @throws IllegalStateException When Ghostscript fails to intialize or load the document. + * @throws NullPointerException When <code>file</code> is <code>null</code>. + */ + public Document(final File file, final DocLoadProgressCallback loadCallback) throws FileNotFoundException, IllegalStateException, NullPointerException { this.file = Objects.requireNonNull(file, "file"); if (!file.exists()) @@ -124,6 +208,8 @@ public class Document implements List<Page> { LongReference instanceRef = new LongReference(); initDocInstance(instanceRef); + documentLoader.callback = loadCallback; + int code = gsapi_init_with_args(instanceRef.value, gargs); gsapi_exit(instanceRef.value); gsapi_delete_instance(instanceRef.value); @@ -135,13 +221,58 @@ public class Document implements List<Page> { for (BufferedImage img : documentLoader.images) { pages.add(new Page(img)); } - } - } - public Document(final String filename) throws FileNotFoundException, NullPointerException { - this(new File(Objects.requireNonNull(filename, "filename"))); + if (documentLoader.callback != null) + documentLoader.callback.onDocumentProgress(100); + } } + /** + * Creates and loads a document from a filename. + * + * @param file The file to load. + * @throws FileNotFoundException If the given file does not exist. + * @throws NullPointerException If <code>file</code> is <code>null</code>. + */ + public Document(final File file) + throws FileNotFoundException, NullPointerException { + this(file, null); + } + + /** + * Creates and loads a document from a filename. + * + * @param filename The name of the file to load. + * @param loadCallback The callback to indicate when progress has occurred while loading. + * @throws FileNotFoundException If the given filename does not exist. + * @throws NullPointerException If <code>filename</code> is <code>null</code>. + */ + public Document(final String filename, final DocLoadProgressCallback loadCallback) + throws FileNotFoundException, NullPointerException { + this(new File(Objects.requireNonNull(filename, "filename")), loadCallback); + } + + /** + * Creates and loads a document from a filename. + * + * @param filename The name of the file to load. + * @throws FileNotFoundException If the given filename does not exist. + * @throws NullPointerException If <code>filename</code> is <code>null</code>. + */ + public Document(final String filename) + throws FileNotFoundException, NullPointerException { + this(filename, null); + } + + /** + * Loads the high resolution images in a range of images. + * + * @param startPage The first page to load. + * @param endPage The end page to load. + * @throws IndexOutOfBoundsException When <code>firstPage</code> or <code>endPage</code> + * are not in the document or <code>endPage</code> is less than <code>firstPage</code>. + * @throws IllegalStateException When Ghostscript fails to initialize or load the document. + */ public void loadHighRes(int startPage, int endPage) throws IndexOutOfBoundsException, IllegalStateException { checkBounds(startPage, endPage); final String[] gargs = { "gs", "-dNOPAUSE", "-dSAFER", "-I%rom%Resource%/Init/", "-dBATCH", "-r" + Page.PAGE_HIGH_DPI, @@ -166,10 +297,26 @@ public class Document implements List<Page> { } } + /** + * Loads the high resolution image of a singular page. + * + * @param page The page to load. + * @throws IndexOutOfBoundsException When <code>page</code> is not in the document. + * @throws IllegalStateException When Ghostscript fails to initialize or load the + * images. + */ public void loadHighRes(int page) throws IndexOutOfBoundsException, IllegalStateException { loadHighRes(page, page); } + /** + * Loads the high resolution images of a list of pages. + * + * @param pages The pages to load. + * @throws IndexOutOfBoundsException When any page is not a page in the document. + * @throws IllegalStateException When Ghostscript fails to initialize or load the + * images. + */ public void loadHighResList(final int... pages) throws IndexOutOfBoundsException, IllegalStateException { if (pages.length > 0) { final StringBuilder builder = new StringBuilder(); @@ -208,6 +355,14 @@ public class Document implements List<Page> { } } + /** + * Unloads the high resolution images in a range of pages. + * + * @param startPage The start page to unload the high resolution image from. + * @param endPage The end page to unload the high resolution image from. + * @throws IndexOutOfBoundsException When <code>startPage</code> and <code>endPage</code> + * are outside document or <code>endPage</code> is less than <code>startPage</code>. + */ public void unloadHighRes(int startPage, int endPage) throws IndexOutOfBoundsException { checkBounds(startPage, endPage); for (int i = startPage - 1; i < endPage; i++) { @@ -215,12 +370,30 @@ public class Document implements List<Page> { } } + /** + * Unloads a high resolution image at the given page. + * + * @param page The page to unload the high resolution image. + * @throws IndexOutOfBoundsException When page is not a page in the document. + */ public void unloadHighRes(int page) throws IndexOutOfBoundsException { unloadHighRes(page, page); } + /** + * Unloads the document's resources. The document will be unusable after this + * call. + */ + public void unload() { + for (Page p : pages) { + p.unloadLowRes(); + p.unloadHighRes(); + } + pages.clear(); + } + private void checkBounds(int start, int end) throws IndexOutOfBoundsException { - if (start < 1 || start >= pages.size()) + if (start < 1 || start > pages.size()) throw new IndexOutOfBoundsException("start=" + start); if (end < 1 || end > pages.size()) throw new IndexOutOfBoundsException("end=" + end); @@ -362,4 +535,9 @@ public class Document implements List<Page> { public int hashCode() { return pages.hashCode(); } + + @Override + public void finalize() { + unload(); + } }
\ No newline at end of file diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/GSFileFilter.java b/demos/java/gsviewer/src/com/artifex/gsviewer/GSFileFilter.java index 6136e973b..acdab56ac 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/GSFileFilter.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/GSFileFilter.java @@ -16,12 +16,12 @@ public class GSFileFilter extends FileFilter { final int ind = name.lastIndexOf('.'); final String ext = name.substring(ind); return - equals(ext, "pdf") || - equals(ext, "eps") || - equals(ext, "ps") || - equals(ext, "xps") || - equals(ext, "oxps") || - equals(ext, "bin"); + equals(ext, ".pdf") || + equals(ext, ".eps") || + equals(ext, ".ps") || + equals(ext, ".xps") || + equals(ext, ".oxps") || + equals(ext, ".bin"); } @Override diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java index b1c3014e9..e8c63b7af 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java @@ -1,10 +1,5 @@ package com.artifex.gsviewer; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; - -import javax.imageio.ImageIO; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; @@ -13,34 +8,17 @@ import com.artifex.gsviewer.gui.ViewerWindow; public class Main { - public static void main(String[] args) throws FileNotFoundException { + public static void main(String[] args) { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException - | UnsupportedLookAndFeelException e1) { - e1.printStackTrace(); + | UnsupportedLookAndFeelException e) { + System.err.println("Failed to set Look and Feel: " + e); } - ViewerWindow win = new ViewerWindow(new ViewerGUIListenerImpl()); + ViewerWindow win = new ViewerWindow(new ViewerController()); SwingUtilities.invokeLater(() -> { win.setVisible(true); }); - - - //Document doc = new Document("010104_momentum_concepts.pdf"); - //doc.loadHighRes(1, 3); - //doc.unloadHighRes(2); - //doc.loadHighResList(1, 2, 3); - - //int pageNum = 1; - //for (Page page : doc) { - // try { - // ImageIO.write(page.getDisplayableImage(), "PNG", new File("page-" + pageNum + ".png")); - // } catch (IOException e) { - // System.err.println("Failed to write page " + pageNum); - // } - // pageNum++; - //} - } } diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java index 3bb0be37e..63b716cad 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java @@ -1,11 +1,26 @@ package com.artifex.gsviewer; +import java.awt.Dimension; import java.awt.image.BufferedImage; import com.artifex.gsviewer.ImageUtil.ImageParams; +/** + * A Page represents an individual page within a Document. It stores a high resolution image + * and a low resolution preview image. + * + * @author Ethan Vrhel + * + */ public class Page { + /** + * The high-resolution DPI to use. + */ public static final int PAGE_HIGH_DPI = 72; + + /** + * The low-resolution DPI to use. + */ public static final int PAGE_LOW_DPI = 10; private BufferedImage lowRes; @@ -56,6 +71,11 @@ public class Page { } } + public void unloadAll() { + unloadLowRes(); + unloadHighRes(); + } + public BufferedImage getLowResImage() { return lowRes; } @@ -68,6 +88,29 @@ public class Page { return highRes == null ? lowRes : highRes; } + public Dimension getLowResSize() { + return lowRes == null ? null : new Dimension(lowRes.getWidth(), lowRes.getHeight()); + } + + public Dimension getHighResSize() { + return highRes == null ? null : new Dimension(highRes.getWidth(), highRes.getHeight()); + } + + public Dimension getDisplayableSize() { + return highRes == null ? getLowResSize() : getHighResSize(); + } + + public Dimension getSize() { + Dimension size = getHighResSize(); + if (size != null) + return size; + size = getLowResSize(); + if (size == null) + return new Dimension(0, 0); + return new Dimension(size.width * PAGE_HIGH_DPI / PAGE_LOW_DPI, + size.height * PAGE_HIGH_DPI / PAGE_LOW_DPI); + } + @Override public String toString() { return "Page[lowResLoaded=" + (lowRes != null) + ",highResLoaded=" + (highRes != null) + "]"; @@ -93,7 +136,6 @@ public class Page { @Override public void finalize() { - unloadLowRes(); - unloadHighRes(); + unloadAll(); } }
\ No newline at end of file diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/StdIO.java b/demos/java/gsviewer/src/com/artifex/gsviewer/StdIO.java index 68912954e..4a3951074 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/StdIO.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/StdIO.java @@ -4,6 +4,13 @@ import com.artifex.gsjava.callbacks.IStdErrFunction; import com.artifex.gsjava.callbacks.IStdInFunction; import com.artifex.gsjava.callbacks.IStdOutFunction; +/** + * Implementation of <code>IStdOutFunction</code>, <code>IStdErrFunction</code>, and + * <code>IStdInFunction</code> to get IO data from Ghostscript. + * + * @author Ethan Vrhel + * + */ public class StdIO implements IStdOutFunction, IStdErrFunction, IStdInFunction { @Override diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java b/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java new file mode 100644 index 000000000..6353d5849 --- /dev/null +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java @@ -0,0 +1,93 @@ +package com.artifex.gsviewer; + +import java.awt.Point; +import java.io.File; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; + +import com.artifex.gsviewer.gui.ViewerGUIListener; +import com.artifex.gsviewer.gui.ViewerWindow; + +public class ViewerController implements ViewerGUIListener { + + private ViewerWindow source; + private Document currentDocument; + + public void open(final File file) { + if (currentDocument != null) + close(); + Document.loadDocumentAsync(file, (final Document doc, final Exception exception) -> { + source.setLoadProgress(0); + if (exception != null) { + JOptionPane.showMessageDialog(source, exception.toString(), + "Failed to load", JOptionPane.ERROR_MESSAGE); + } else { + System.out.println("Loaded document"); + this.currentDocument = doc; + source.loadDocumentToViewer(doc); + } + }, (int progress) -> { + source.setLoadProgress(progress); + }); + } + + public void close() { + if (currentDocument != null) { + source.loadDocumentToViewer(null); + currentDocument.unload(); + currentDocument = null; + } + } + + public Document getCurrentDocument() { + return currentDocument; + } + + @Override + public void onViewerAdd(ViewerWindow source) { + this.source = source; + } + + @Override + public void onPageChange(int oldPage, int newPage) { + System.out.println("Page change: " + oldPage + " to " + newPage); + } + + @Override + public void onZoomChange(double oldZoom, double newZoom) { + System.out.println("Zoom change: " + oldZoom + " to " + newZoom); + } + + @Override + public void onScrollChange(Point oldScroll, Point newScroll) { + System.out.println("Scroll change: " + oldScroll + " to " + newScroll); + } + + @Override + public void onOpenFile() { + JFileChooser chooser = new JFileChooser(); + chooser.setFileFilter(GSFileFilter.INSTANCE); + chooser.setCurrentDirectory(new File("").getAbsoluteFile()); + int status = chooser.showOpenDialog(source); + if (status == JFileChooser.APPROVE_OPTION) + open(chooser.getSelectedFile()); + } + + @Override + public void onCloseFile() { + close(); + } + + @Override + public void onClosing() { + source.dispose(); + System.exit(0); + } + + @Override + public void onSettingsOpen() { + System.out.println("Settings open"); + } + +} diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerGUIListenerImpl.java b/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerGUIListenerImpl.java deleted file mode 100644 index 108be1974..000000000 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerGUIListenerImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.artifex.gsviewer; - -import java.awt.Point; -import java.io.File; - -import javax.swing.JFileChooser; -import javax.swing.JOptionPane; - -import com.artifex.gsviewer.gui.ViewerGUIListener; -import com.artifex.gsviewer.gui.ViewerWindow; - -public class ViewerGUIListenerImpl implements ViewerGUIListener { - - @Override - public void onPageChange(ViewerWindow source, int oldPage, int newPage) { - System.out.println("Page change: " + oldPage + " to " + newPage); - } - - @Override - public void onZoomChange(ViewerWindow source, double oldZoom, double newZoom) { - System.out.println("Zoom change: " + oldZoom + " to " + newZoom); - } - - @Override - public void onScrollChange(ViewerWindow source, Point oldScroll, Point newScroll) { - System.out.println("Scroll change: " + oldScroll + " to " + newScroll); - } - - @Override - public void onOpenFile(ViewerWindow source) { - System.out.println("Open file"); - JFileChooser chooser = new JFileChooser(); - chooser.setFileFilter(GSFileFilter.INSTANCE); - chooser.setCurrentDirectory(new File("").getAbsoluteFile()); - int status = chooser.showOpenDialog(source); - if (status == JFileChooser.APPROVE_OPTION) { - File toOpen = chooser.getSelectedFile(); - System.out.println("To open: " + toOpen.getAbsolutePath()); - Document.loadDocumentAsync(toOpen, (final Document doc, final Exception exception) -> { - if (exception != null) { - JOptionPane.showMessageDialog(source, exception.toString(), - "Failed to load", JOptionPane.ERROR_MESSAGE); - } else { - System.out.println("Loaded document"); - } - }); - } - } - - @Override - public void onCloseFile(ViewerWindow source) { - System.out.println("Close file"); - } - - @Override - public void onClosing(ViewerWindow source) { - System.out.println("Closing"); - source.dispose(); - System.exit(0); - } - - @Override - public void onSettingsOpen(ViewerWindow source) { - System.out.println("Settings open"); - } - -} diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerGUIListener.java b/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerGUIListener.java index f56bbc5cb..7acaa587b 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerGUIListener.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerGUIListener.java @@ -4,17 +4,19 @@ import java.awt.Point; public interface ViewerGUIListener { - public void onPageChange(ViewerWindow source, int oldPage, int newPage); + public void onViewerAdd(ViewerWindow source); - public void onZoomChange(ViewerWindow source, double oldZoom, double newZoom); + public void onPageChange(int oldPage, int newPage); - public void onScrollChange(ViewerWindow source, Point oldScroll, Point newScroll); + public void onZoomChange(double oldZoom, double newZoom); - public void onOpenFile(ViewerWindow source); + public void onScrollChange(Point oldScroll, Point newScroll); - public void onCloseFile(ViewerWindow source); + public void onOpenFile(); - public void onClosing(ViewerWindow source); + public void onCloseFile(); - public void onSettingsOpen(ViewerWindow source); + public void onClosing(); + + public void onSettingsOpen(); } diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java b/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java index ca144d683..49932244c 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java @@ -1,5 +1,23 @@ package com.artifex.gsviewer.gui; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JPanel; +import javax.swing.RepaintManager; + +import com.artifex.gsviewer.Document; +import com.artifex.gsviewer.Page; + /** * Auto-generated form using NetBeans. */ @@ -11,6 +29,10 @@ public class ViewerWindow extends javax.swing.JFrame { private int currentPage, maxPage; private double currentZoom; + private Document loadedDocument; + private List<PagePanel> viewerPagePanels; + private List<PagePanel> miniViewerPagePanels; + /** * Creates new form ViewerWindow */ @@ -20,10 +42,13 @@ public class ViewerWindow extends javax.swing.JFrame { public ViewerWindow(final ViewerGUIListener listener) { initComponents(); - this.guiListener = listener; this.currentPage = 0; this.maxPage = 0; this.currentZoom = 1.0; + this.viewerPagePanels = new ArrayList<>(); + this.miniViewerPagePanels = new ArrayList<>(); + + setGUIListener(listener); } /** @@ -267,7 +292,7 @@ public class ViewerWindow extends javax.swing.JFrame { currentPage = Math.max(currentPage - 1, 1); pageNumberField.setText(new StringBuilder().append(currentPage).toString()); if (guiListener != null) - guiListener.onPageChange(this, oldPage, currentPage); + guiListener.onPageChange(oldPage, currentPage); } else { pageNumberField.setText(new StringBuilder().append(0).toString()); } @@ -279,7 +304,7 @@ public class ViewerWindow extends javax.swing.JFrame { currentPage = Math.min(currentPage + 1, maxPage); pageNumberField.setText(new StringBuilder().append(currentPage).toString()); if (guiListener != null) - guiListener.onPageChange(this, oldPage, currentPage); + guiListener.onPageChange(oldPage, currentPage); } else { pageNumberField.setText(new StringBuilder().append(0).toString()); } @@ -287,7 +312,7 @@ public class ViewerWindow extends javax.swing.JFrame { private void formWindowClosing(java.awt.event.WindowEvent evt) {// GEN-FIRST:event_formWindowClosing if (guiListener != null) - guiListener.onClosing(this); + guiListener.onClosing(); }// GEN-LAST:event_formWindowClosing private void pageNumberFieldKeyPressed(java.awt.event.KeyEvent evt) {// GEN-FIRST:event_pageNumberFieldKeyPressed @@ -300,7 +325,7 @@ public class ViewerWindow extends javax.swing.JFrame { final int oldPage = currentPage; this.currentPage = newPage; if (guiListener != null) - guiListener.onPageChange(this, oldPage, currentPage); + guiListener.onPageChange(oldPage, currentPage); } catch (IllegalArgumentException e) { System.err.println(new StringBuilder().append("Invalid page number \""). append(text).append('\"').toString()); @@ -315,7 +340,7 @@ public class ViewerWindow extends javax.swing.JFrame { final double oldZoom = currentZoom; this.currentZoom = sliderValue; if (guiListener != null && oldZoom != currentZoom) - guiListener.onZoomChange(this, oldZoom, currentZoom); + guiListener.onZoomChange(oldZoom, currentZoom); }// GEN-LAST:event_zoomSliderMouseReleased private void increaseZoomButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_increaseZoomButtonActionPerformed @@ -323,7 +348,7 @@ public class ViewerWindow extends javax.swing.JFrame { this.currentZoom = Math.min(Math.ceil((oldZoom * 10) + 1) / 10, 2.0); zoomSlider.setValue((int)(currentZoom * 50)); if (guiListener != null && oldZoom != currentZoom) - guiListener.onZoomChange(this, oldZoom, currentZoom); + guiListener.onZoomChange(oldZoom, currentZoom); }// GEN-LAST:event_increaseZoomButtonActionPerformed private void decreaseZoomButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_decreaseZoomButtonActionPerformed @@ -331,27 +356,27 @@ public class ViewerWindow extends javax.swing.JFrame { this.currentZoom = Math.max(Math.floor((oldZoom * 10) - 1) / 10, 0.0); zoomSlider.setValue((int)(currentZoom * 50)); if (guiListener != null && oldZoom != currentZoom) - guiListener.onZoomChange(this, oldZoom, currentZoom); + guiListener.onZoomChange(oldZoom, currentZoom); }// GEN-LAST:event_decreaseZoomButtonActionPerformed private void openMenuActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_openMenuActionPerformed if (guiListener != null) - guiListener.onOpenFile(this); + guiListener.onOpenFile(); }// GEN-LAST:event_openMenuActionPerformed private void closeMenuItemActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_closeMenuItemActionPerformed if (guiListener != null) - guiListener.onCloseFile(this); + guiListener.onCloseFile(); }// GEN-LAST:event_closeMenuItemActionPerformed private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_exitMenuItemActionPerformed if (guiListener != null) - guiListener.onClosing(this); + guiListener.onClosing(); }// GEN-LAST:event_exitMenuItemActionPerformed private void settingsMenuItemActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_settingsMenuItemActionPerformed if (guiListener != null) - guiListener.onSettingsOpen(this); + guiListener.onSettingsOpen(); }// GEN-LAST:event_settingsMenuItemActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables @@ -378,8 +403,63 @@ public class ViewerWindow extends javax.swing.JFrame { private javax.swing.JSlider zoomSlider; // End of variables declaration//GEN-END:variables + private class PagePanel extends JPanel { + + private static final long serialVersionUID = 1L; + + private static final int VIEWER_MODE = 0, + MINI_VIEWER_MODE = 1; + + private Object lock; + private Page page; + private int mode; + + private PagePanel(final Page page, final Dimension size, final int mode) { + this.lock = new Object(); + this.page = page; + this.mode = mode; + setPreferredSize(size); + + setBackground(Color.WHITE); + + pack(); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + synchronized (lock) { + if (page != null) { + Dimension size = getSize(); + BufferedImage img; + switch (mode) { + case VIEWER_MODE: + img = page.getDisplayableImage(); + if (img != null) + g.drawImage(img.getScaledInstance(size.width, size.height, Image.SCALE_FAST), 0, 0, this); + break; + case MINI_VIEWER_MODE: + img = page.getLowResImage(); + if (img != null) + g.drawImage(img, 0, 0, this); + break; + default: + throw new IllegalStateException("Illegal mode"); + } + } + } + } + + @Override + public void invalidate() { + super.invalidate(); + System.out.println("Invalidated!"); + } + } + public void setGUIListener(final ViewerGUIListener listener) { this.guiListener = listener; + listener.onViewerAdd(this); } public void setPage(final int newPage) { @@ -389,7 +469,7 @@ public class ViewerWindow extends javax.swing.JFrame { final int oldPage = currentPage; currentPage = newPage; if (guiListener != null) - guiListener.onPageChange(this, oldPage, currentPage); + guiListener.onPageChange(oldPage, currentPage); } catch (IllegalArgumentException e) { System.err.println(new StringBuilder().append("Invalid page number \""). append(newPage).append('\"').toString()); @@ -401,4 +481,65 @@ public class ViewerWindow extends javax.swing.JFrame { public void setMaxPage(final int max) { this.maxPage = max; } + + public void setLoadProgress(final int progress) { + progressBar.setValue(progress); + } + + public void loadDocumentToViewer(final Document document) { + if (document == null) { + unloadViewerDocument(); + return; + } + + for (final PagePanel panel : viewerPagePanels) { + synchronized (panel.lock) { + panel.page = null; + } + } + viewerContentPane.removeAll(); + viewerPagePanels.clear(); + + for (final PagePanel panel : miniViewerPagePanels) { + synchronized (panel.lock) { + panel.page = null; + } + } + miniViewerContentPane.removeAll(); + viewerPagePanels.clear(); + + this.loadedDocument = document; + + setResizable(false); + + viewerContentPane.setLayout(new BoxLayout(viewerContentPane, BoxLayout.Y_AXIS)); + + for (final Page page : document) { + final PagePanel panel = new PagePanel(page, page.getSize(), PagePanel.VIEWER_MODE); + viewerContentPane.add(panel); + viewerContentPane.add(Box.createVerticalStrut(10)); + + viewerPagePanels.add(panel); + } + + miniViewerContentPane.setLayout(new BoxLayout(miniViewerContentPane, BoxLayout.Y_AXIS)); + + for (final Page page : document) { + final PagePanel panel = new PagePanel(page, page.getLowResSize(), PagePanel.MINI_VIEWER_MODE); + miniViewerContentPane.add(panel); + miniViewerContentPane.add(Box.createVerticalStrut(10)); + + miniViewerPagePanels.add(panel); + } + + setResizable(true); + } + + public void unloadViewerDocument() { + this.loadedDocument = null; + } + + public Document getLoadedDocument() { + return loadedDocument; + } } |