You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ti...@apache.org on 2015/04/14 19:41:30 UTC

svn commit: r1673479 - in /pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools: PDFReader.java util/ util/RecentFiles.java

Author: tilman
Date: Tue Apr 14 17:41:29 2015
New Revision: 1673479

URL: http://svn.apache.org/r1673479
Log:
PDFBOX-2748: add reusable "recent files" feature to PDFReader, as suggested by Khyrul Bashar

Added:
    pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/util/
    pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/util/RecentFiles.java   (with props)
Modified:
    pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/PDFReader.java

Modified: pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/PDFReader.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/PDFReader.java?rev=1673479&r1=1673478&r2=1673479&view=diff
==============================================================================
--- pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/PDFReader.java (original)
+++ pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/PDFReader.java Tue Apr 14 17:41:29 2015
@@ -20,11 +20,15 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.Toolkit;
 import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
 import java.awt.image.BufferedImage;
 import java.awt.print.PrinterException;
 import java.awt.print.PrinterJob;
+
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 
 import javax.swing.JFileChooser;
 import javax.swing.JFrame;
@@ -44,6 +48,7 @@ import javax.swing.AbstractAction;
 import org.apache.pdfbox.pdmodel.PDPageTree;
 import org.apache.pdfbox.printing.PDFPrinter;
 import org.apache.pdfbox.rendering.PDFRenderer;
+import org.apache.pdfbox.tools.util.RecentFiles;
 import org.apache.pdfbox.tools.gui.PageWrapper;
 import org.apache.pdfbox.tools.gui.ReaderBottomPanel;
 import org.apache.pdfbox.pdmodel.PDDocument;
@@ -61,6 +66,7 @@ public class PDFReader extends JFrame
     private JMenu fileMenu;
     private JMenuBar menuBar;
     private JMenuItem openMenuItem;
+    private JMenu recentFileSubmenu;
     private JMenuItem printMenuItem;
     private JMenu viewMenu;
     private JMenuItem nextPageItem;
@@ -77,6 +83,7 @@ public class PDFReader extends JFrame
     private int currentPage = 0;
     private int numberOfPages = 0;
     private String currentFilename = null;
+    private String currentFilePath = null;
 
     private static final String PASSWORD = "-password";
 
@@ -88,6 +95,8 @@ public class PDFReader extends JFrame
     private static final String FIRST_PAGE = "first_page";
     private static final String LAST_PAGE = "last_page";
 
+    private RecentFiles recentFiles;
+
     /**
      * Constructor.
      */
@@ -101,6 +110,7 @@ public class PDFReader extends JFrame
     {
         menuBar = new JMenuBar();
         fileMenu = new JMenu();
+        recentFileSubmenu = new JMenu();
         openMenuItem = new JMenuItem();
         saveAsImageMenuItem = new JMenuItem();
         exitMenuItem = new JMenuItem();
@@ -143,6 +153,20 @@ public class PDFReader extends JFrame
 
         fileMenu.add(openMenuItem);
 
+        try
+        {
+            recentFiles = new RecentFiles(this.getClass(), 5);
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+
+        recentFileSubmenu.setText("Open recent Files");
+        recentFileSubmenu.setEnabled(false);
+        addRecentFileItems();
+        fileMenu.add(recentFileSubmenu);
+        fileMenu.addSeparator();
         printMenuItem.setText("Print");
         printMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P,
                                      Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
@@ -325,9 +349,77 @@ public class PDFReader extends JFrame
         setBounds((screenSize.width - 700) / 2, (screenSize.height - 600) / 2, 700, 600);
     }
 
+    private void addRecentFileItems()
+    {
+        Action recentMenuAction = new AbstractAction()
+        {
+            @Override
+            public void actionPerformed(ActionEvent actionEvent)
+            {
+                String filePath = (String) ((JComponent) actionEvent.getSource()).getClientProperty("path");
+                try
+                {
+                    openPDFFile(filePath, "");
+                }
+                catch (IOException e)
+                {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+
+        MouseListener mouseListener = new MouseListener()
+        {
+            @Override
+            public void mouseClicked(MouseEvent mouseEvent)
+            {
+            }
+
+            @Override
+            public void mousePressed(MouseEvent mouseEvent)
+            {
+            }
+
+            @Override
+            public void mouseReleased(MouseEvent mouseEvent)
+            {
+            }
+
+            @Override
+            public void mouseEntered(MouseEvent mouseEvent)
+            {
+                String filePath = (String) ((JComponent) mouseEvent.getSource()).getClientProperty("path");
+                bottomStatusPanel.getStatusLabel().setText(filePath);
+            }
+
+            @Override
+            public void mouseExited(MouseEvent mouseEvent)
+            {
+                bottomStatusPanel.getStatusLabel().setText("");
+            }
+        };
+
+        if (!recentFiles.isEmpty())
+        {
+            recentFileSubmenu.removeAll();
+            ArrayList<String> files = recentFiles.getFiles();
+            for (int i = files.size() - 1; i >= 0; i--)
+            {
+                String path = files.get(i);
+                String name = new File(path).getName();
+                JMenuItem recentFileMenuItem = new JMenuItem(name);
+                recentFileMenuItem.putClientProperty("path", path);
+                recentFileMenuItem.addActionListener(recentMenuAction);
+                recentFileMenuItem.addMouseListener(mouseListener);
+                recentFileSubmenu.add(recentFileMenuItem);
+            }
+            recentFileSubmenu.setEnabled(true);
+        }
+    }
+
     private void updateTitle()
     {
-        setTitle(BASETITLE + ": " + currentFilename + " " + 
+        setTitle(BASETITLE + ": " + currentFilename + " " +
                 "(" + (currentPage + 1) + "/" + numberOfPages + ")");
     }
 
@@ -401,7 +493,9 @@ public class PDFReader extends JFrame
             if (document != null)
             {
                 document.close();
+                recentFiles.addFile(currentFilePath);
             }
+            recentFiles.close();
         }
         catch (IOException io)
         {
@@ -451,9 +545,12 @@ public class PDFReader extends JFrame
         {
             document.close();
             documentPanel.removeAll();
+            recentFiles.addFile(currentFilePath);
         }
 
         File file = new File(filename);
+        currentFilePath = file.getPath();
+        recentFiles.removeFile(file.getPath());
         parseDocument(file, password);
         pages = document.getPages();
         numberOfPages = document.getNumberOfPages();
@@ -461,6 +558,7 @@ public class PDFReader extends JFrame
         currentPage = 0;
         updateTitle();
         showPage(0);
+        addRecentFileItems();
     }
 
     private void showPage(int pageNumber) throws IOException

Added: pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/util/RecentFiles.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/util/RecentFiles.java?rev=1673479&view=auto
==============================================================================
--- pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/util/RecentFiles.java (added)
+++ pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/util/RecentFiles.java Tue Apr 14 17:41:29 2015
@@ -0,0 +1,188 @@
+package org.apache.pdfbox.tools.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.prefs.Preferences;
+
+/**
+ * A class to save recent file history in preference using java Preference api.
+ */
+public class RecentFiles
+{
+    private static final String KEY = "recent_files_";
+    private static final String PATH_KEY = "recent_files_%d_%d";
+    private static final String PIECES_LENGTH_KEY = "recent_files_%d_length";
+    private static final String HISTORY_LENGTH = "history_length";
+
+    private final Preferences pref;
+    private Queue<String> filePaths;
+    private final int maximum;
+
+    /**
+     * Constructor.
+     *
+     * @param className the class for which this Recentfiles object is created and it will be used
+     * to create preference instance.
+     * @param maximumFile the number of recent files to remember.
+     */
+    public RecentFiles(Class className, int maximumFile)
+    {
+        this.maximum = maximumFile;
+        this.pref = Preferences.userNodeForPackage(className);
+        filePaths = readHistoryFromPref();
+        if (filePaths == null)
+        {
+            filePaths = new ConcurrentLinkedDeque<String>();
+        }
+    }
+
+    /**
+     * Clear the previous recent file history.
+     */
+    public void removeAll()
+    {
+        filePaths.clear();
+    }
+
+    /**
+     * Check if file history is empty.
+     *
+     * @return if history is empty return true otherwise return false.
+     */
+    public boolean isEmpty()
+    {
+        return filePaths.isEmpty();
+    }
+
+    /**
+     * Add a new file in recent file history.
+     *
+     * @param path path to the file. this path means File#getPath() method returned String.
+     */
+    public void addFile(String path)
+    {
+        if (filePaths.size() >= maximum + 1 && path != null)
+        {
+            filePaths.remove();
+        }
+
+        filePaths.add(path);
+    }
+
+    /**
+     * Remove a file from recent file history.
+     *
+     * @param path path string to the file. this path means File#getPath() method returned String.
+     */
+    public void removeFile(String path)
+    {
+        if (filePaths.contains(path))
+        {
+            filePaths.remove(path);
+        }
+    }
+
+    /**
+     * This gives the file in descending order where order is according to the time it is added.
+     * This checks for file's existence in file history.
+     *
+     * @return return the file paths in an ArrayList.
+     */
+    public ArrayList<String> getFiles()
+    {
+        if (!isEmpty())
+        {
+            ArrayList<String> files = new ArrayList<String>();
+            for (String path : filePaths)
+            {
+                File file = new File(path);
+                if (file.exists())
+                {
+                    files.add(path);
+                }
+            }
+            if (files.size() > maximum)
+            {
+                files.remove(0);
+            }
+            return files;
+        }
+        return null;
+    }
+
+    /**
+     * This method save the present recent file history in the preference. To get the recent file
+     * history in next session this method must be called.
+     *
+     * @throws IOException if saving in preference doesn't success.
+     */
+    public void close() throws IOException
+    {
+        writeHistoryToPref(filePaths);
+    }
+
+    private String[] breakString(String fullPath)
+    {
+        int allowedStringLength = Preferences.MAX_VALUE_LENGTH;
+        ArrayList<String> pieces = new ArrayList<String>();
+        int beginIndex = 0;
+        int remainingLength = fullPath.length();
+        int endIndex = 0;
+        while (remainingLength > 0)
+        {
+            endIndex += remainingLength >= allowedStringLength ? allowedStringLength : remainingLength;
+            pieces.add(fullPath.substring(beginIndex, endIndex));
+            beginIndex = endIndex;
+            remainingLength = fullPath.length() - endIndex;
+        }
+        return pieces.toArray(new String[pieces.size()]);
+    }
+
+    private void writeHistoryToPref(Queue<String> filePaths)
+    {
+        if (filePaths.size() == 0)
+        {
+            return;
+        }
+        Preferences node = pref.node(KEY);
+        node.putInt(HISTORY_LENGTH, filePaths.size());
+        int fileCount = 1;
+        for (String path : filePaths)
+        {
+            String[] pieces = breakString(path);
+            node.putInt(String.format(PIECES_LENGTH_KEY, fileCount), pieces.length);
+            for (int i = 0; i < pieces.length; i++)
+            {
+                node.put(String.format(PATH_KEY, fileCount, i), pieces[i]);
+            }
+            fileCount++;
+        }
+    }
+
+    private Queue<String> readHistoryFromPref()
+    {
+        Preferences node = pref.node(KEY);
+        int historyLength = node.getInt(HISTORY_LENGTH, 0);
+        if (historyLength == 0)
+        {
+            return null;
+        }
+        Queue<String> history = new ConcurrentLinkedDeque<String>();
+
+        for (int i = 1; i <= historyLength; i++)
+        {
+            int totalPieces = node.getInt(String.format(PIECES_LENGTH_KEY, i), 0);
+            StringBuilder stringBuilder = new StringBuilder();
+            for (int j = 0; j < totalPieces; j++)
+            {
+                String piece = node.get(String.format(PATH_KEY, i, j), "");
+                stringBuilder.append(piece);
+            }
+            history.add(stringBuilder.toString());
+        }
+        return history;
+    }
+}

Propchange: pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/util/RecentFiles.java
------------------------------------------------------------------------------
    svn:eol-style = native