/* Copyright (C) 2003-2012 JabRef contributors. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package net.sf.jabref; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.print.PrinterException; import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.standard.JobName; import javax.swing.*; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import net.sf.jabref.export.layout.Layout; import net.sf.jabref.export.layout.LayoutHelper; import net.sf.jabref.export.ExportFormats; import net.sf.jabref.gui.PreviewPanelTransferHandler; /** * Displays an BibtexEntry using the given layout format. */ public class PreviewPanel extends JPanel implements VetoableChangeListener, SearchTextListener, EntryContainer { /** * The bibtex entry currently shown */ BibtexEntry entry; MetaData metaData; /** * If a database is set, the preview will attempt to resolve strings in the * previewed entry using that database. */ BibtexDatabase database; Layout layout; String layoutFile; public JEditorPane previewPane; JScrollPane scrollPane; PdfPreviewPanel pdfPreviewPanel; BasePanel panel; /** * @param database * (may be null) Optionally used to resolve strings. * @param entry * (may be null) If given this entry is shown otherwise you have * to call setEntry to make something visible. * @param panel * (may be null) If not given no toolbar is shown on the right * hand side. * @param metaData * (must be given) Used for resolving pdf directories for links. * @param layoutFile * (must be given) Used for layout */ public PreviewPanel(BibtexDatabase database, BibtexEntry entry, BasePanel panel, MetaData metaData, String layoutFile) { this(database, entry, panel, metaData, layoutFile, false); } /** * @param database * (may be null) Optionally used to resolve strings. * @param entry * (may be null) If given this entry is shown otherwise you have * to call setEntry to make something visible. * @param panel * (may be null) If not given no toolbar is shown on the right * hand side. * @param metaData * (must be given) Used for resolving pdf directories for links. * @param layoutFile * (must be given) Used for layout * @param withPDFPreview if true, a PDF preview is included in the PreviewPanel */ public PreviewPanel(BibtexDatabase database, BibtexEntry entry, BasePanel panel, MetaData metaData, String layoutFile, boolean withPDFPreview) { this(panel, metaData, layoutFile, withPDFPreview); this.database = database; setEntry(entry); } /** * * @param panel * (may be null) If not given no toolbar is shown on the right * hand side. * @param metaData * (must be given) Used for resolving pdf directories for links. * @param layoutFile * (must be given) Used for layout */ public PreviewPanel(BasePanel panel, MetaData metaData, String layoutFile) { this(panel, metaData, layoutFile, false); } /** * * @param panel * (may be null) If not given no toolbar is shown on the right * hand side. * @param metaData * (must be given) Used for resolving pdf directories for links. * @param layoutFile * (must be given) Used for layout * @param withPDFPreview if true, a PDF preview is included in the PreviewPanel. * The user can override this setting by setting the config setting JabRefPreferences.PDF_PREVIEW to false. */ private PreviewPanel(BasePanel panel, MetaData metaData, String layoutFile, boolean withPDFPreview) { super(new BorderLayout(), true); withPDFPreview = withPDFPreview && JabRefPreferences.getInstance().getBoolean(JabRefPreferences.PDF_PREVIEW); this.panel = panel; this.metaData = metaData; this.layoutFile = layoutFile; this.previewPane = createPreviewPane(); if (withPDFPreview) { this.pdfPreviewPanel = new PdfPreviewPanel(metaData); } else { this.pdfPreviewPanel = null; } if (panel != null) { // dropped files handler only created for main window // not for Windows as like the search results window this.previewPane.setTransferHandler(new PreviewPanelTransferHandler(panel.frame(), this, this.previewPane.getTransferHandler())); } // Set up scroll pane for preview pane scrollPane = new JScrollPane(previewPane, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setBorder(null); /* * If we have been given a panel and the preference option * previewPrintButton is set, show the tool bar */ if (panel != null && JabRefPreferences.getInstance().getBoolean("previewPrintButton")) { add(createToolBar(), BorderLayout.LINE_START); } if (withPDFPreview) { JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, scrollPane, pdfPreviewPanel); splitPane.setOneTouchExpandable(true); // int oneThird = panel.getWidth()/3; int oneThird = 400; // arbitrarily set as panel.getWidth() always // returns 0 at this point splitPane.setDividerLocation(oneThird*2); // Provide minimum sizes for the two components in the split pane // Dimension minimumSize = new Dimension(oneThird * 2, 50); // scrollPane.setMinimumSize(minimumSize); // minimumSize = new Dimension(oneThird, 50); // pdfScrollPane.setMinimumSize(minimumSize); add(splitPane); } else { add(scrollPane, BorderLayout.CENTER); } } class PrintAction extends AbstractAction { public PrintAction() { super(Globals.lang("Print Preview"), GUIGlobals.getImage("psSmall")); putValue(SHORT_DESCRIPTION, Globals.lang("Print Preview")); } //DocumentPrinter printerService; public void actionPerformed(ActionEvent arg0) { // Background this, as it takes a while. new Thread() { public void run() { try { PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet(); pras.add(new JobName(entry.getCiteKey(), null)); previewPane.print(null, null, true, null, pras, false); } catch (PrinterException e) { // Inform the user... we don't know what to do. JOptionPane.showMessageDialog(PreviewPanel.this, Globals.lang("Could not print preview") + ".\n" + e.getMessage(), Globals .lang("Printing Entry Preview"), JOptionPane.ERROR_MESSAGE); } } }.start(); } } Action printAction; public Action getPrintAction() { if (printAction == null) printAction = new PrintAction(); return printAction; } class CloseAction extends AbstractAction { public CloseAction() { super(Globals.lang("Close window"), GUIGlobals.getImage("close")); putValue(SHORT_DESCRIPTION, Globals.lang("Close window")); } public void actionPerformed(ActionEvent e) { panel.hideBottomComponent(); } } Action closeAction; private ArrayList<String> wordsToHighlight = null; public Action getCloseAction() { if (closeAction == null) closeAction = new CloseAction(); return closeAction; } JPopupMenu createPopupMenu() { JPopupMenu menu = new JPopupMenu(); menu.add(getPrintAction()); if (panel != null) { menu.add(panel.frame.switchPreview); } return menu; } JToolBar createToolBar() { JToolBar tlb = new JToolBar(JToolBar.VERTICAL); JabRefPreferences prefs = JabRefPreferences.getInstance(); Action printAction = getPrintAction(); Action closeAction = getCloseAction(); tlb.setMargin(new Insets(0, 0, 0, 2)); // The toolbar carries all the key bindings that are valid for the whole // window. ActionMap am = tlb.getActionMap(); InputMap im = tlb.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); im.put(prefs.getKey("Close entry preview"), "close"); am.put("close", closeAction); im.put(prefs.getKey("Print entry preview"), "print"); am.put("print", printAction); tlb.setFloatable(false); // Add actions (and thus buttons) tlb.add(closeAction); tlb.addSeparator(); tlb.add(printAction); Component[] comps = tlb.getComponents(); for (int i = 0; i < comps.length; i++) ((JComponent) comps[i]).setOpaque(false); return tlb; } JEditorPane createPreviewPane() { JEditorPane previewPane = new JEditorPane() { public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } }; previewPane.setMargin(new Insets(3, 3, 3, 3)); previewPane.setComponentPopupMenu(createPopupMenu()); previewPane.setEditable(false); previewPane.setDragEnabled(true); // this has an effect only, if no custom transfer handler is registered. We keep the statement if the transfer handler is removed. previewPane.setContentType("text/html"); previewPane.addHyperlinkListener(new HyperlinkListener() { public void hyperlinkUpdate(HyperlinkEvent hyperlinkEvent) { if (hyperlinkEvent.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { try { String address = hyperlinkEvent.getURL().toString(); Util.openExternalViewer(PreviewPanel.this.metaData, address, "url"); } catch (IOException e) { e.printStackTrace(); } } } }); return previewPane; } public void setDatabase(BibtexDatabase db) { database = db; } public void setMetaData(MetaData metaData) { this.metaData = metaData; } public void readLayout(String layoutFormat) throws Exception { layoutFile = layoutFormat; readLayout(); } public void readLayout() throws Exception { StringReader sr = new StringReader(layoutFile.replaceAll("__NEWLINE__", "\n")); layout = new LayoutHelper(sr) .getLayoutFromText(Globals.FORMATTER_PACKAGE); } public void setLayout(Layout layout) { this.layout = layout; } public void setEntry(BibtexEntry newEntry) { if (newEntry != entry) { if (entry != null) entry.removePropertyChangeListener(this); newEntry.addPropertyChangeListener(this); } entry = newEntry; try { readLayout(); update(); } catch (Exception ex) { ex.printStackTrace(); } } public BibtexEntry getEntry() { return this.entry; } public void update() { StringBuffer sb = new StringBuffer(); ExportFormats.entryNumber = 1; // Set entry number in case that is included in the preview layout. if (entry != null) { sb.append(layout.doLayout(entry, database, wordsToHighlight)); } previewPane.setText(sb.toString()); previewPane.revalidate(); // Scroll to top: final JScrollBar bar = scrollPane.getVerticalScrollBar(); SwingUtilities.invokeLater(new Runnable() { public void run() { bar.setValue(0); } }); // update pdf preview if (pdfPreviewPanel != null) { pdfPreviewPanel.updatePanel(entry); } } public boolean hasEntry() { return (entry != null); } /** * The PreviewPanel has registered itself as an event listener with the * currently displayed BibtexEntry. If the entry changes, an event is * received here, and we can update the preview immediately. */ public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { // TODO updating here is not really necessary isn't it? // Only if we are visible. update(); } @Override public void searchText(ArrayList<String> words) { if (Globals.prefs.getBoolean("highLightWords")) { this.wordsToHighlight = words; update(); } else { if (this.wordsToHighlight != null) { // setting of "highLightWords" seems to have changed. // clear all highlights and remember the clearing (by wordsToHighlight = null) this.wordsToHighlight = null; update(); } } } }