From 765d86b489dda64913cff9a0973a6ad820b9200d Mon Sep 17 00:00:00 2001 From: Ethan Vrhel Date: Mon, 24 Aug 2020 21:16:56 -0700 Subject: Updated to newer Ghostscript version and fixed a bug Updated to the newest Ghostscript version and fixed an ArrayIndexOutOfBoundsException bug when unloading zoomed pages. --- .../src/com/artifex/gsviewer/Document.java | 32 ++- .../gsviewer/src/com/artifex/gsviewer/Main.java | 18 ++ .../src/com/artifex/gsviewer/PDFFileFilter.java | 28 ++ .../gsviewer/src/com/artifex/gsviewer/Page.java | 10 - .../src/com/artifex/gsviewer/ViewerController.java | 51 +++- .../src/com/artifex/gsviewer/gui/ViewerWindow.java | 312 ++++++++++++--------- 6 files changed, 294 insertions(+), 157 deletions(-) create mode 100644 demos/java/gsviewer/src/com/artifex/gsviewer/PDFFileFilter.java diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java index f6dfd3cbf..6000d3e44 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java @@ -6,6 +6,7 @@ import java.awt.image.BufferedImage; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -22,6 +23,8 @@ import com.artifex.gsjava.util.BytePointer; import com.artifex.gsjava.util.Reference; import com.artifex.gsviewer.ImageUtil.ImageParams; +import sun.misc.Unsafe; + /** *

A Document stores an ordered list of Pages. This class implements * java.util.List, so it inherits all of a list's capabilities, @@ -561,7 +564,7 @@ public class Document implements List { documentLoader.callback = loadCallback; - code = gsInstance.set_param("HWResolution", Page.PAGE_LOW_DPI_STR, GS_SPT_PARSED); + code = gsInstance.set_param("HWResolution", Page.toDPIString(Page.PAGE_LOW_DPI), GS_SPT_PARSED); if (code != GS_ERROR_OK) throw new IllegalStateException("Failed to set HWResolution (code = " + code + ")"); @@ -571,8 +574,14 @@ public class Document implements List { if (code != GS_ERROR_OK) throw new IllegalStateException("Failed to run file (code = " + code + ")"); - gsInstance.set_param("TextAlphaBits", 4, GS_SPT_INT); - gsInstance.set_param("GraphicsAlphaBits", 4, GS_SPT_INT); + boolean aa = Settings.SETTINGS.getSetting("antialiasing"); + if (aa) { + gsInstance.set_param("TextAlphaBits", 4, GS_SPT_INT); + gsInstance.set_param("GraphicsAlphaBits", 4, GS_SPT_INT); + } else { + gsInstance.set_param("TextAlphaBits", 0, GS_SPT_INT); + gsInstance.set_param("GraphicsAlphaBits", 0, GS_SPT_INT); + } this.pages = new ArrayList<>(documentLoader.images.size()); for (BufferedImage img : documentLoader.images) { @@ -769,7 +778,7 @@ public class Document implements List { */ public void unloadZoomed(int startPage, int endPage) throws IndexOutOfBoundsException { checkBounds(startPage, endPage); - for (int i = startPage; i <= endPage; i++) { + for (int i = startPage; i < endPage; i++) { pages.get(i).unloadZoomed(); } } @@ -1024,8 +1033,19 @@ public class Document implements List { } @Override - public List subList(int fromIndex, int toIndex) { - return pages.subList(fromIndex, toIndex); + public Document subList(int fromIndex, int toIndex) { + try { + Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + Unsafe unsafe = (Unsafe)unsafeField.get(null); + Document doc = (Document)unsafe.allocateInstance(Document.class); + doc.pages = pages.subList(fromIndex, toIndex); + doc.file = file; + return doc; + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException + | IllegalAccessException | InstantiationException e) { + throw new RuntimeException("Failed to create sublist: " + e); + } } @Override diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java index e8c63b7af..beecdd3b0 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java @@ -1,5 +1,7 @@ package com.artifex.gsviewer; +import java.io.IOException; + import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; @@ -9,6 +11,8 @@ import com.artifex.gsviewer.gui.ViewerWindow; public class Main { public static void main(String[] args) { + Runtime.getRuntime().addShutdownHook(new Thread(new Shutdown())); + try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException @@ -21,4 +25,18 @@ public class Main { win.setVisible(true); }); } + + private static class Shutdown implements Runnable { + + @Override + public void run() { + try { + Settings.SETTINGS.save(); + } catch (IOException e) { + System.err.println("Failed to write settings file"); + e.printStackTrace(); + } + } + + } } diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/PDFFileFilter.java b/demos/java/gsviewer/src/com/artifex/gsviewer/PDFFileFilter.java new file mode 100644 index 000000000..e7bd7046f --- /dev/null +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/PDFFileFilter.java @@ -0,0 +1,28 @@ +package com.artifex.gsviewer; + +import java.io.File; + +import javax.swing.filechooser.FileFilter; + +public class PDFFileFilter extends FileFilter { + + public static final PDFFileFilter INSTANCE = new PDFFileFilter(); + + @Override + public boolean accept(File f) { + if (f.isDirectory()) + return true; + String filename = f.getName(); + int ind = filename.lastIndexOf('.'); + if (ind == -1) + return false; + String ext = filename.substring(ind); + return ext.equalsIgnoreCase(".pdf"); + } + + @Override + public String getDescription() { + return "PDF Files"; + } + +} diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java index 612029af3..a3435f56c 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java @@ -21,21 +21,11 @@ public class Page { */ public static final int PAGE_HIGH_DPI = 72; - /** - * The high-resolution DPI to use as a Ghostscript-friendly string - */ - public static final String PAGE_HIGH_DPI_STR = toDPIString(PAGE_HIGH_DPI); - /** * The low-resolution DPI to use. */ public static final int PAGE_LOW_DPI = 10; - /** - * The low-resolution DPI to use as a Ghostscript-friendly string - */ - public static final String PAGE_LOW_DPI_STR = toDPIString(PAGE_LOW_DPI); - /** * Converts a dpi value to a Ghostscript-friendly string. * diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java b/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java index f69615580..f2801426c 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java @@ -11,6 +11,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.swing.JFileChooser; +import com.artifex.gsviewer.gui.SettingsDialog; import com.artifex.gsviewer.gui.ViewerGUIListener; import com.artifex.gsviewer.gui.ViewerWindow; @@ -46,6 +47,7 @@ public class ViewerController implements ViewerGUIListener { private ViewerWindow source; // The viewer window where documents will be displayed private Document currentDocument; // The current document loaded in the viewer private SmartLoader smartLoader; // The SmartLoader used to load pages based on scroll and zoom + private boolean loadingDocument; /** *

Opens a document from a files asynchronously.

@@ -66,6 +68,10 @@ public class ViewerController implements ViewerGUIListener { public void open(File file) throws NullPointerException, IllegalArgumentException, FileNotFoundException, IOException, HeadlessException, RuntimeException { + if (loadingDocument) { + source.showWarningDialog("Error", "A document is already loading"); + return; + } if (Document.shouldDistill(file)) { int ret = source.showConfirmDialog("Distill", "Would you like to distill this document before opening?"); if (ret == ViewerWindow.CANCEL) @@ -98,16 +104,22 @@ public class ViewerController implements ViewerGUIListener { if (currentDocument != null) close(); + loadingDocument = true; Document.loadDocumentAsync(file, (final Document doc, final Exception exception) -> { - source.loadDocumentToViewer(doc); - source.setLoadProgress(0); - if (exception != null) { - source.showErrorDialog("Failed to load", exception.toString()); - } else { - this.currentDocument = doc; - dispatchSmartLoader(); + try { + source.loadDocumentToViewer(doc); + source.setLoadProgress(0); + if (exception != null) { + throw new RuntimeException("Failed to load", exception); + //source.showErrorDialog("Failed to load", exception.toString()); + } else { + this.currentDocument = doc; + dispatchSmartLoader(); + } + source.revalidate(); + } finally { + loadingDocument = false; } - source.revalidate(); }, (int progress) -> { source.setLoadProgress(progress); }, Document.OPERATION_RETURN); @@ -118,6 +130,11 @@ public class ViewerController implements ViewerGUIListener { * is loaded, this method does nothing. */ public void close() { + if (loadingDocument) { + source.showWarningDialog("Error", "A document is already loading"); + return; + } + if (smartLoader != null) { smartLoader.stop(); smartLoader = null; @@ -192,7 +209,10 @@ public class ViewerController implements ViewerGUIListener { } @Override - public void onSettingsOpen() { } + public void onSettingsOpen() { + SettingsDialog dialog = new SettingsDialog(source, true); + dialog.setVisible(true); + } /** * Starts the SmartLoader on the currently loaded document. If a @@ -290,9 +310,7 @@ public class ViewerController implements ViewerGUIListener { System.out.println("Smart loader dispatched."); while (shouldRun) { int currentPage = source.getCurrentPage(); - int[] toLoad = new int[] { - currentPage, currentPage - 1, currentPage + 1, - currentPage - 2, currentPage + 2 }; + int[] toLoad = genToLoad(currentPage); if (loaded.length != currentDocument.size()) throw new IllegalStateException("Array is size " + loaded.length + " while doc size is " + currentDocument.size()); @@ -341,5 +359,14 @@ public class ViewerController implements ViewerGUIListener { } } } + + int[] genToLoad(int page) { + int range = Settings.SETTINGS.getSetting("preloadRange"); + int[] arr = new int[range * 2 + 1]; + for (int i = -range; i <= range; i++) { + arr[i + range] = page + i; + } + return arr; + } } } 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 27557b566..48b399534 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java @@ -10,6 +10,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import javax.swing.Box; @@ -25,6 +26,7 @@ import javax.swing.SwingUtilities; import com.artifex.gsviewer.Document; import com.artifex.gsviewer.Page; import com.artifex.gsviewer.PageUpdateCallback; +import com.artifex.gsviewer.Settings; /** *

Used to display documents into a window.

@@ -74,6 +76,8 @@ public class ViewerWindow extends javax.swing.JFrame { private boolean gotoPage; // Whether the viewer should jump to currentPage + private boolean isLoading; + /** * Creates new ViewerWindow. */ @@ -97,6 +101,7 @@ public class ViewerWindow extends javax.swing.JFrame { // Adjustment listener looks for any change in the scroll value of the scrollbar this.viewerScrollPane.getVerticalScrollBar().addAdjustmentListener((AdjustmentEvent evt) -> { + this.viewerScrollPane.getVerticalScrollBar().setUnitIncrement(Settings.SETTINGS.getSetting("scrollSens")); if (scrollMap != null) { Adjustable adjustable = evt.getAdjustable(); int currentPage = scrollMap.getPageFor(adjustable.getValue()); @@ -132,28 +137,28 @@ public class ViewerWindow extends javax.swing.JFrame { } /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ - // //GEN-BEGIN:initComponents - private void initComponents() { + // + private void initComponents() { viewerScrollPane = new javax.swing.JScrollPane(); viewerContentPane = new javax.swing.JPanel(); miniViewerScrollPane = new javax.swing.JScrollPane(); miniViewerContentPane = new javax.swing.JPanel(); + toolbarPanel = new javax.swing.JPanel(); + decreaseZoomButton = new javax.swing.JButton(); zoomSlider = new javax.swing.JSlider(); increaseZoomButton = new javax.swing.JButton(); - decreaseZoomButton = new javax.swing.JButton(); - lastPageButton = new javax.swing.JButton(); - maxPagesLabel = new javax.swing.JTextField(); - nextPageButton = new javax.swing.JButton(); progressBar = new javax.swing.JProgressBar(); + lastPageButton = new javax.swing.JButton(); pageNumberField = new javax.swing.JTextField(); pageSlashLabel = new javax.swing.JLabel(); + maxPagesLabel = new javax.swing.JTextField(); + nextPageButton = new javax.swing.JButton(); menuBar = new javax.swing.JMenuBar(); fileMenu = new javax.swing.JMenu(); openMenu = new javax.swing.JMenuItem(); @@ -179,7 +184,7 @@ public class ViewerWindow extends javax.swing.JFrame { viewerContentPane.setLayout(viewerContentPaneLayout); viewerContentPaneLayout.setHorizontalGroup( viewerContentPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 502, Short.MAX_VALUE) + .addGap(0, 623, Short.MAX_VALUE) ); viewerContentPaneLayout.setVerticalGroup( viewerContentPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -203,6 +208,14 @@ public class ViewerWindow extends javax.swing.JFrame { miniViewerScrollPane.setViewportView(miniViewerContentPane); + decreaseZoomButton.setText("-"); + decreaseZoomButton.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + decreaseZoomButtonActionPerformed(evt); + } + }); + zoomSlider.setMajorTickSpacing(25); zoomSlider.setMinorTickSpacing(5); zoomSlider.setPaintTicks(true); @@ -213,12 +226,6 @@ public class ViewerWindow extends javax.swing.JFrame { zoomSliderMouseReleased(evt); } }); - zoomSlider.addKeyListener(new java.awt.event.KeyAdapter() { - @Override - public void keyReleased(java.awt.event.KeyEvent evt) { - zoomSliderKeyReleased(evt); - } - }); increaseZoomButton.setText("+"); increaseZoomButton.addActionListener(new java.awt.event.ActionListener() { @@ -228,22 +235,24 @@ public class ViewerWindow extends javax.swing.JFrame { } }); - decreaseZoomButton.setText("-"); - decreaseZoomButton.addActionListener(new java.awt.event.ActionListener() { + lastPageButton.setText("<"); + lastPageButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { - decreaseZoomButtonActionPerformed(evt); + lastPageButtonActionPerformed(evt); } }); - lastPageButton.setText("<"); - lastPageButton.addActionListener(new java.awt.event.ActionListener() { + pageNumberField.setText("0"); + pageNumberField.addKeyListener(new java.awt.event.KeyAdapter() { @Override - public void actionPerformed(java.awt.event.ActionEvent evt) { - lastPageButtonActionPerformed(evt); + public void keyPressed(java.awt.event.KeyEvent evt) { + pageNumberFieldKeyPressed(evt); } }); + pageSlashLabel.setText("/"); + maxPagesLabel.setEditable(false); maxPagesLabel.setText("0"); @@ -255,15 +264,50 @@ public class ViewerWindow extends javax.swing.JFrame { } }); - pageNumberField.setText("0"); - pageNumberField.addKeyListener(new java.awt.event.KeyAdapter() { - @Override - public void keyPressed(java.awt.event.KeyEvent evt) { - pageNumberFieldKeyPressed(evt); - } - }); - - pageSlashLabel.setText("/"); + javax.swing.GroupLayout toolbarPanelLayout = new javax.swing.GroupLayout(toolbarPanel); + toolbarPanel.setLayout(toolbarPanelLayout); + toolbarPanelLayout.setHorizontalGroup( + toolbarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(toolbarPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(toolbarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(toolbarPanelLayout.createSequentialGroup() + .addComponent(decreaseZoomButton, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, 111, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(increaseZoomButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(lastPageButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pageNumberField, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pageSlashLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(maxPagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 47, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(nextPageButton)) + .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) + ); + toolbarPanelLayout.setVerticalGroup( + toolbarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(toolbarPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(toolbarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(toolbarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(increaseZoomButton) + .addComponent(lastPageButton) + .addComponent(pageNumberField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(pageSlashLabel) + .addComponent(maxPagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(nextPageButton)) + .addComponent(zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(decreaseZoomButton)) + .addGap(17, 17, 17) + .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) + ); fileMenu.setText("File"); @@ -318,31 +362,11 @@ public class ViewerWindow extends javax.swing.JFrame { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() + .addComponent(miniViewerScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addComponent(miniViewerScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(2, 2, 2) - .addComponent(decreaseZoomButton, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, 111, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(increaseZoomButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(lastPageButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pageNumberField, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pageSlashLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(maxPagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 47, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(4, 4, 4) - .addComponent(nextPageButton) - .addGap(2, 2, 2)) - .addComponent(viewerScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addComponent(toolbarPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(viewerScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 642, Short.MAX_VALUE)) .addContainerGap()) ); layout.setVerticalGroup( @@ -350,23 +374,10 @@ public class ViewerWindow extends javax.swing.JFrame { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(miniViewerScrollPane) - .addComponent(viewerScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(miniViewerScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 592, Short.MAX_VALUE) + .addComponent(viewerScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 592, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(decreaseZoomButton) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(increaseZoomButton) - .addComponent(nextPageButton) - .addComponent(maxPagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(pageNumberField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lastPageButton) - .addComponent(pageSlashLabel))) - .addComponent(zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(14, 14, 14) - .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + .addComponent(toolbarPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); pack(); @@ -408,10 +419,6 @@ public class ViewerWindow extends javax.swing.JFrame { tryChangeZoom(zoomSlider.getValue() / 50.0); }// GEN-LAST:event_zoomSliderMouseReleased - private void zoomSliderKeyReleased(java.awt.event.KeyEvent evt) {// GEN-FIRST:event_zoomSliderKeyReleased - tryChangeZoom(zoomSlider.getValue() / 50.0); - }// GEN-LAST:event_zoomSliderKeyReleased - private void increaseZoomButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_increaseZoomButtonActionPerformed if (tryChangeZoom(Math.min(Math.ceil((currentZoom * 10) + 1) / 10, 2.0))) zoomSlider.setValue((int)(currentZoom * 50)); @@ -440,6 +447,7 @@ public class ViewerWindow extends javax.swing.JFrame { private void settingsMenuItemActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_settingsMenuItemActionPerformed if (guiListener != null) guiListener.onSettingsOpen(); + tryChangeZoom(0.1); }// GEN-LAST:event_settingsMenuItemActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables @@ -461,6 +469,7 @@ public class ViewerWindow extends javax.swing.JFrame { private javax.swing.JLabel pageSlashLabel; private javax.swing.JProgressBar progressBar; private javax.swing.JMenuItem settingsMenuItem; + private javax.swing.JPanel toolbarPanel; private javax.swing.JPanel viewerContentPane; private javax.swing.JScrollPane viewerScrollPane; private javax.swing.JSlider zoomSlider; @@ -524,7 +533,7 @@ public class ViewerWindow extends javax.swing.JFrame { Image result = img; if (img == page.getLowResImage() || currentZoom < 1.0 || (img == page.getHighResImage() && currentZoom > 1.0 && page.getZoomedImage() == null)) { - result = img.getScaledInstance(actualSize.width, actualSize.height, Image.SCALE_FAST); + result = img.getScaledInstance(actualSize.width, actualSize.height, Image.SCALE_SMOOTH); } return result; } else { @@ -645,60 +654,76 @@ public class ViewerWindow extends javax.swing.JFrame { * @param document The document to load into the viewer. */ public void loadDocumentToViewer(final Document document) { - unloadViewerDocument(); - if (document == null) - return; - - lockSize(); - - this.loadedDocument = document; - viewerContentPane.setLayout(new BoxLayout(viewerContentPane, BoxLayout.Y_AXIS)); + isLoading = true; + try { + unloadViewerDocument(); + if (document == null) + return; + lockSize(); + + this.loadedDocument = document; + viewerContentPane.setLayout(new BoxLayout(viewerContentPane, BoxLayout.Y_AXIS)); + + // Generate the viewer page components + /*for (final Page page : document) { + final PagePanel panel = new PagePanel(page, 1.0); + viewerContentPane.add(panel); + viewerContentPane.add(Box.createVerticalStrut(VIEWER_PAGE_GAP)); + + viewerPagePanels.add(panel); + }*/ + + miniViewerContentPane.setLayout(new BoxLayout(miniViewerContentPane, BoxLayout.Y_AXIS)); + + int height = 0; + // Generate the mini viewer icons + int pageNum = 1; + for (final Page page : document) { + + final PagePanel panel = new PagePanel(page, 1.0); + viewerContentPane.add(panel); + viewerContentPane.add(Box.createVerticalStrut(VIEWER_PAGE_GAP)); + + viewerPagePanels.add(panel); + height += page.getSize().height; + height += VIEWER_PAGE_GAP; + + ImageIcon icon = new ImageIcon(page.getLowResImage()); + JButton button = new JButton(icon); + button.setPreferredSize(page.getLowResSize()); + button.addActionListener(new MiniViewerActionListener(pageNum++)); + button.setFocusable(false); + + miniViewerContentPane.add(button); + miniViewerContentPane.add(Box.createVerticalStrut(MINI_VIEWER_PAGE_GAP)); + } + System.out.println(height); - // Generate the viewer page components - for (final Page page : document) { - final PagePanel panel = new PagePanel(page, 1.0); - viewerContentPane.add(panel); - viewerContentPane.add(Box.createVerticalStrut(VIEWER_PAGE_GAP)); + // Generate the scroll map + invokeAndWait(() -> { + revalidate(); + this.scrollMap = new ScrollMap(document, this, VIEWER_PAGE_GAP); + }); + assumePage(this.currentPage = 1); + assumeMaxPages(this.maxPage = document.size()); - viewerPagePanels.add(panel); - } + setTitle("Viewer - " + document.getName()); - miniViewerContentPane.setLayout(new BoxLayout(miniViewerContentPane, BoxLayout.Y_AXIS)); + // Enable all widgets + zoomSlider.setEnabled(true); + increaseZoomButton.setEnabled(true); + decreaseZoomButton.setEnabled(true); + zoomSlider.setValue(50); + zoomSlider.setFocusable(true); - // Generate the mini viewer icons - int pageNum = 1; - for (final Page page : document) { - ImageIcon icon = new ImageIcon(page.getLowResImage()); - JButton button = new JButton(icon); - button.setPreferredSize(page.getLowResSize()); - button.addActionListener(new MiniViewerActionListener(pageNum++)); - button.setFocusable(false); + nextPageButton.setEnabled(true); + lastPageButton.setEnabled(true); + pageNumberField.setEditable(true); - miniViewerContentPane.add(button); - miniViewerContentPane.add(Box.createVerticalStrut(MINI_VIEWER_PAGE_GAP)); + refreshButtons(); + } finally { + isLoading = false; } - - // Generate the scroll map - this.scrollMap = new ScrollMap(document, this, VIEWER_PAGE_GAP); - this.scrollMap.getScroll(1); - - assumePage(this.currentPage = 1); - assumeMaxPages(this.maxPage = document.size()); - - setTitle("Viewer - " + document.getName()); - - // Enable all widgets - zoomSlider.setEnabled(true); - increaseZoomButton.setEnabled(true); - decreaseZoomButton.setEnabled(true); - zoomSlider.setValue(50); - zoomSlider.setFocusable(true); - - nextPageButton.setEnabled(true); - lastPageButton.setEnabled(true); - pageNumberField.setEditable(true); - - refreshButtons(); } /** @@ -738,13 +763,14 @@ public class ViewerWindow extends javax.swing.JFrame { System.gc(); - SwingUtilities.invokeLater(() -> { + invokeAndWait(() -> { viewerContentPane.revalidate(); viewerContentPane.repaint(); miniViewerContentPane.revalidate(); miniViewerContentPane.repaint(); }); + } /** @@ -884,6 +910,22 @@ public class ViewerWindow extends javax.swing.JFrame { return currentZoom; } + /** + * Returns whether the window is loading a document. + * + * @return true if the window is loading a document + * and false otherwise. + */ + public boolean isLoading() { + return isLoading; + } + + public void apply(Settings settings) { + int sens = settings.getSetting("scrollSens"); + this.viewerScrollPane.getVerticalScrollBar().setUnitIncrement(sens); + this.miniViewerScrollPane.getVerticalScrollBar().setUnitIncrement(sens); + } + /** * Refresh each button's state. */ @@ -920,7 +962,7 @@ public class ViewerWindow extends javax.swing.JFrame { viewerPagePanels.add(panel); } - SwingUtilities.invokeLater(() -> { + invokeAndWait(() -> { gotoPage = true; revalidate(); scrollMap.genMap(1.0); @@ -963,4 +1005,16 @@ public class ViewerWindow extends javax.swing.JFrame { private void assumeMaxPages(int pageCount) { this.maxPagesLabel.setText(new StringBuilder().append(pageCount).toString()); } + + private void invokeAndWait(Runnable r) { + if (!SwingUtilities.isEventDispatchThread()) { + try { + SwingUtilities.invokeAndWait(r); + } catch (InvocationTargetException | InterruptedException e) { + r.run(); + } + } else { + r.run(); + } + } } -- cgit v1.2.1