/* * ExportDialog.java * Copyright 2011 Connor Petty <cpmeister@users.sourceforge.net> * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on Nov 6, 2011, 12:26:57 PM */ package pcgen.gui2.dialog; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Desktop; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.net.URI; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.DefaultListCellRenderer; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.ListSelectionModel; import javax.swing.SwingWorker; import javax.swing.WindowConstants; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.FileFilterUtils; import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.commons.io.filefilter.SuffixFileFilter; import org.apache.commons.io.filefilter.TrueFileFilter; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import pcgen.cdom.base.Constants; import pcgen.core.Globals; import pcgen.core.SettingsHandler; import pcgen.facade.core.CharacterFacade; import pcgen.facade.core.PartyFacade; import pcgen.gui2.PCGenFrame; import pcgen.gui2.UIPropertyContext; import pcgen.gui2.tools.Utility; import pcgen.gui2.util.FacadeComboBoxModel; import pcgen.io.ExportUtilities; import pcgen.system.BatchExporter; import pcgen.system.CharacterManager; import pcgen.system.ConfigurationSettings; import pcgen.system.PCGenSettings; import pcgen.util.Logging; /** * The dialog provides the list of output sheets for a character or party to * be exported to. * @author Connor Petty <cpmeister@users.sourceforge.net> */ @SuppressWarnings("serial") public final class ExportDialog extends JDialog implements ActionListener, ListSelectionListener { private static final String PDF_EXPORT_DIR_PROP = "pdfExportDir"; private static final String HTML_EXPORT_DIR_PROP = "htmlExportDir"; private static final String PARTY_COMMAND = "PARTY"; private static final String EXPORT_TO_COMMAND = "EXPORT_TO"; private static final String EXPORT_COMMAND = "EXPORT"; private static final String CLOSE_COMMAND = "CLOSE"; public static void showExportDialog(PCGenFrame parent) { ExportDialog dialog = new ExportDialog(parent); Utility.setComponentRelativeLocation(parent, dialog); dialog.setVisible(true); } private final PCGenFrame pcgenFrame; private final FacadeComboBoxModel<CharacterFacade> characterBoxModel; private final JComboBox characterBox; private final JCheckBox partyBox; private final JComboBox exportBox; private final JList fileList; private final JProgressBar progressBar; private final JButton exportButton; private final JButton closeButton; private final FileSearcher fileSearcher; private Collection<File> allTemplates = null; private ExportDialog(PCGenFrame parent) { super(parent, true); this.pcgenFrame = parent; this.characterBoxModel = new FacadeComboBoxModel<>(CharacterManager.getCharacters(), parent.getSelectedCharacterRef()); this.characterBox = new JComboBox(characterBoxModel); this.partyBox = new JCheckBox("Entire Party"); this.exportBox = new JComboBox(SheetFilter.values()); this.fileList = new JList(); this.progressBar = new JProgressBar(); this.exportButton = new JButton("Export"); this.closeButton = new JButton("Close"); this.fileSearcher = new FileSearcher(); initComponents(); initLayout(); fileSearcher.execute(); Utility.installEscapeCloseOperation(this); } @Override public void dispose() { super.dispose(); fileSearcher.cancel(false); characterBoxModel.setReference(null); characterBoxModel.setListFacade(null); } private void initComponents() { characterBox.setRenderer(new DefaultListCellRenderer() { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { CharacterFacade character = (CharacterFacade) value; return super.getListCellRendererComponent(list, character.getNameRef().get(), index, isSelected, cellHasFocus); } }); fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); fileList.addListSelectionListener(this); exportButton.setDefaultCapable(true); getRootPane().setDefaultButton(exportButton); partyBox.setActionCommand(PARTY_COMMAND); exportBox.setActionCommand(EXPORT_TO_COMMAND); exportButton.setActionCommand(EXPORT_COMMAND); closeButton.setActionCommand(CLOSE_COMMAND); exportBox.addActionListener(this); partyBox.addActionListener(this); exportButton.addActionListener(this); closeButton.addActionListener(this); exportButton.setEnabled(false); progressBar.setStringPainted(true); progressBar.setString("Loading Templates"); progressBar.setIndeterminate(true); setTitle("Export a PC or Party"); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); UIPropertyContext context = UIPropertyContext.createContext("ExportDialog"); String defaultOSType = context.getProperty(UIPropertyContext.DEFAULT_OS_TYPE); if (defaultOSType != null) { for (SheetFilter filter : SheetFilter.values()) { if (defaultOSType.equals(filter.toString())) { exportBox.setSelectedItem(filter); } } } } private void initLayout() { Container contentPane = getContentPane(); contentPane.setLayout(new BorderLayout()); Box topPanel = Box.createHorizontalBox(); topPanel.add(new JLabel("Select Character:")); topPanel.add(Box.createHorizontalStrut(5)); topPanel.add(characterBox); topPanel.add(Box.createHorizontalStrut(5)); topPanel.add(partyBox); topPanel.add(Box.createHorizontalGlue()); topPanel.add(Box.createHorizontalStrut(50)); topPanel.add(new JLabel("Export to:")); topPanel.add(Box.createHorizontalStrut(5)); topPanel.add(exportBox); contentPane.add(topPanel, BorderLayout.NORTH); JScrollPane scrollPane = new JScrollPane(fileList); scrollPane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Templates"), scrollPane.getBorder())); contentPane.add(scrollPane, BorderLayout.CENTER); Box bottomPanel = Box.createHorizontalBox(); bottomPanel.add(progressBar); bottomPanel.add(Box.createHorizontalGlue()); bottomPanel.add(Box.createHorizontalStrut(5)); bottomPanel.add(exportButton); bottomPanel.add(Box.createHorizontalStrut(5)); bottomPanel.add(closeButton); contentPane.add(bottomPanel, BorderLayout.SOUTH); topPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); bottomPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); pack(); } @Override public void valueChanged(ListSelectionEvent e) { exportButton.setEnabled(fileList.getSelectedIndex() != -1); } @Override public void actionPerformed(ActionEvent e) { if (PARTY_COMMAND.equals(e.getActionCommand())) { characterBox.setEnabled(!partyBox.isSelected()); refreshFiles(); } else if (EXPORT_TO_COMMAND.equals(e.getActionCommand())) { UIPropertyContext context = UIPropertyContext.createContext("ExportDialog"); context.setProperty(UIPropertyContext.DEFAULT_OS_TYPE, exportBox.getSelectedItem().toString()); refreshFiles(); } else if (EXPORT_COMMAND.equals(e.getActionCommand())) { doExport(); } else if (CLOSE_COMMAND.equals(e.getActionCommand())) { dispose(); } } private void doExport() { export(SheetFilter.PDF == exportBox.getSelectedItem()); } private void setWorking(boolean working) { progressBar.setVisible(working); exportButton.setEnabled(!working); fileList.setEnabled(!working); exportBox.setEnabled(!working); } private void export(boolean pdf) { UIPropertyContext context = UIPropertyContext.createContext("ExportDialog"); final JFileChooser fcExport = new JFileChooser(); fcExport.setFileSelectionMode(JFileChooser.FILES_ONLY); File baseDir = null; { String path; if (pdf) { path = context.getProperty(PDF_EXPORT_DIR_PROP); } else { path = context.getProperty(HTML_EXPORT_DIR_PROP); } if (path != null) { baseDir = new File(path); } } if (baseDir == null || !baseDir.isDirectory()) { baseDir = SystemUtils.getUserHome(); } fcExport.setCurrentDirectory(baseDir); URI uri = (URI) fileList.getSelectedValue(); String extension = ExportUtilities.getOutputExtension(uri.toString(), pdf); if (pdf) { FileFilter fileFilter = new FileNameExtensionFilter("PDF Documents (*.pdf)", "pdf"); fcExport.addChoosableFileFilter(fileFilter); fcExport.setFileFilter(fileFilter); } else if ("htm".equalsIgnoreCase(extension) || "html".equalsIgnoreCase(extension)) { FileFilter fileFilter = new FileNameExtensionFilter("HTML Documents (*.htm, *.html)", "htm", "html"); fcExport.addChoosableFileFilter(fileFilter); fcExport.setFileFilter(fileFilter); } else if ("xml".equalsIgnoreCase(extension)) { FileFilter fileFilter = new FileNameExtensionFilter("XML Documents (*.xml)", "xml"); fcExport.addChoosableFileFilter(fileFilter); fcExport.setFileFilter(fileFilter); } else { String desc = extension + " Files (*." + extension + ")"; fcExport.addChoosableFileFilter(new FileNameExtensionFilter(desc, extension)); } String name; File path; if (!partyBox.isSelected()) { CharacterFacade character = (CharacterFacade) characterBox.getSelectedItem(); path = character.getFileRef().get(); if (path != null) { path = path.getParentFile(); } else { path = new File(PCGenSettings.getPcgDir()); } name = character.getTabNameRef().get(); if (name == null || "".equals(name)) { name = character.getNameRef().get(); } } else { path = new File(PCGenSettings.getPcgDir()); name = "Entire Party"; } if (pdf) { fcExport.setSelectedFile(new File(path, name + ".pdf")); } else { fcExport.setSelectedFile(new File(path, name + "." + extension)); } fcExport.setDialogTitle("Export " + name); if (fcExport.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) { return; } final File outFile = fcExport.getSelectedFile(); if (pdf) { context.setProperty(PDF_EXPORT_DIR_PROP, outFile.getParent()); } else { context.setProperty(HTML_EXPORT_DIR_PROP, outFile.getParent()); } if (StringUtils.isEmpty(outFile.getName())) { pcgenFrame.showErrorMessage("PCGen", "You must set a filename."); return; } if (outFile.isDirectory()) { pcgenFrame.showErrorMessage("PCGen", "You cannot overwrite a directory with a file."); return; } if (outFile.exists() && SettingsHandler.getAlwaysOverwrite() == false) { int reallyClose = JOptionPane.showConfirmDialog(this, "The file " + outFile.getName() + " already exists, are you sure you want to overwrite it?", "Confirm overwriting " + outFile.getName(), JOptionPane.YES_NO_OPTION); if (reallyClose != JOptionPane.YES_OPTION) { return; } } try { if (pdf) { new PDFExporter(outFile, extension, name).execute(); } else { if (!printToFile(outFile)) { String message = "The character export failed. Please see the log for details."; pcgenFrame.showErrorMessage(Constants.APPLICATION_NAME, message); return; } maybeOpenFile(outFile); Globals.executePostExportCommandStandard(outFile.getAbsolutePath()); } } catch (IOException ex) { pcgenFrame.showErrorMessage("PCGen", "Could not export " + name + ". Try another filename."); Logging.errorPrint("Could not export " + name, ex); } } private void maybeOpenFile(File file) { UIPropertyContext context = UIPropertyContext.getInstance(); String value = context.getProperty(UIPropertyContext.ALWAYS_OPEN_EXPORT_FILE); Boolean openFile = StringUtils.isEmpty(value) ? null : Boolean.valueOf(value); if (openFile == null) { JCheckBox checkbox = new JCheckBox(); checkbox.setText("Always perform this action"); JPanel message = PCGenFrame.buildMessageLabelPanel( "Do you want to open " + file.getName() + "?", checkbox); int ret = JOptionPane.showConfirmDialog(this, message, "Select an Option", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (ret == JOptionPane.CLOSED_OPTION) { return; } openFile = BooleanUtils.toBoolean(ret, JOptionPane.YES_OPTION, JOptionPane.NO_OPTION); if (checkbox.isSelected()) { context.setBoolean(UIPropertyContext.ALWAYS_OPEN_EXPORT_FILE, openFile); } } if (!openFile) { return; } if (!Desktop.isDesktopSupported() || !Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) { pcgenFrame.showErrorMessage("Cannot Open " + file.getName(), "Operating System does not support this operation"); return; } try { Desktop.getDesktop().open(file); } catch (IOException ex) { String message = "Failed to open " + file.getName(); pcgenFrame.showErrorMessage(Constants.APPLICATION_NAME, message); Logging.errorPrint(message, ex); } } private File getSelectedTemplate() { File osDir; String outputSheetDirectory = SettingsHandler.getGame().getOutputSheetDirectory(); if (outputSheetDirectory == null) { osDir = new File(ConfigurationSettings.getOutputSheetsDir()); outputSheetDirectory = ""; } else { osDir = new File(ConfigurationSettings.getOutputSheetsDir(), outputSheetDirectory); } URI osPath = new File(osDir, ((SheetFilter) exportBox.getSelectedItem()).getPath()).toURI(); URI uri = (URI) fileList.getSelectedValue(); return new File(osPath.resolve(uri)); } private boolean printToFile(File outFile) throws IOException { File template = getSelectedTemplate(); if (partyBox.isSelected()) { SettingsHandler.setSelectedPartyHTMLOutputSheet(template.getAbsolutePath()); PartyFacade party = CharacterManager.getCharacters(); return BatchExporter.exportPartyToNonPDF(party, outFile, template); } else { CharacterFacade character = (CharacterFacade) characterBox.getSelectedItem(); return BatchExporter.exportCharacterToNonPDF(character, outFile, template); } } private void refreshFiles() { if (allTemplates != null) { String outputSheetsDir; SheetFilter sheetFilter = (SheetFilter) exportBox.getSelectedItem(); IOFileFilter ioFilter = FileFilterUtils.asFileFilter(sheetFilter); IOFileFilter prefixFilter; String defaultSheet = null; String outputSheetDirectory = SettingsHandler.getGame().getOutputSheetDirectory(); if (outputSheetDirectory == null) { outputSheetsDir = ConfigurationSettings.getOutputSheetsDir() + "/" + sheetFilter.getPath(); } else { outputSheetsDir = ConfigurationSettings.getOutputSheetsDir() + "/" + outputSheetDirectory + "/" + sheetFilter.getPath(); } if (partyBox.isSelected()) { prefixFilter = FileFilterUtils.prefixFileFilter(Constants.PARTY_TEMPLATE_PREFIX); } else { CharacterFacade character = (CharacterFacade) characterBox.getSelectedItem(); prefixFilter = FileFilterUtils.prefixFileFilter(Constants.CHARACTER_TEMPLATE_PREFIX); defaultSheet = character.getDefaultOutputSheet(sheetFilter == SheetFilter.PDF); if (StringUtils.isEmpty(defaultSheet)) { defaultSheet = outputSheetsDir + "/" + SettingsHandler.getGame().getOutputSheetDefault(sheetFilter.getTag()); } } IOFileFilter filter = FileFilterUtils.and(prefixFilter, ioFilter); List<File> files = FileFilterUtils.filterList(filter, allTemplates); Collections.sort(files); URI osPath = new File(outputSheetsDir).toURI(); Object[] uriList = new Object[files.size()]; for (int i = 0; i < uriList.length; i++) { uriList[i] = osPath.relativize(files.get(i).toURI()); } fileList.setListData(uriList); if (StringUtils.isNotEmpty(defaultSheet)) { URI defaultPath = new File(defaultSheet).toURI(); fileList.setSelectedValue(osPath.relativize(defaultPath), true); } } } private class PDFExporter extends SwingWorker<Object, Object> { private final File saveFile; private final String name; public PDFExporter(File saveFile, String extension, String name) { this.saveFile = saveFile; this.name = name; progressBar.setString("Exporting to PDF"); setWorking(true); } @Override protected Object doInBackground() throws Exception { Boolean result = false; if (partyBox.isSelected()) { PartyFacade party = CharacterManager.getCharacters(); result = BatchExporter.exportPartyToPDF(party, saveFile, getSelectedTemplate()); } else { CharacterFacade character = (CharacterFacade) characterBox.getSelectedItem(); result = BatchExporter.exportCharacterToPDF(character, saveFile, getSelectedTemplate()); } return result; } @Override protected void done() { boolean exception = true; try { if (!((Boolean) get())) { pcgenFrame.showErrorMessage("Could not export " + name, "Error occurred while exporting. See log for details."); } else { exception = false; } } catch (InterruptedException ex) { // Take no action as we are probably being asked to shut down. } catch (ExecutionException ex) { Logging.errorPrint("Could not export " + name, ex.getCause()); pcgenFrame.showErrorMessage("Could not export " + name, "Error occurred while exporting. See log for details."); } finally { if (!exception) { Globals.executePostExportCommandPDF(saveFile.getAbsolutePath()); } setWorking(false); if (!exception) { maybeOpenFile(saveFile); } } } } private class FileSearcher extends SwingWorker<Collection<File>, Object> { @Override protected Collection<File> doInBackground() throws Exception { File dir; String outputSheetDirectory = SettingsHandler.getGame().getOutputSheetDirectory(); if (outputSheetDirectory == null) { Logging.errorPrint("OUTPUTSHEET|DIRECTORY not defined for game mode " + SettingsHandler.getGame()); dir = new File(ConfigurationSettings.getOutputSheetsDir()); outputSheetDirectory = ""; } else { dir = new File(ConfigurationSettings.getOutputSheetsDir(), outputSheetDirectory); if (!dir.isDirectory()) { Logging.errorPrint("Unable to find game mode outputsheets at " + dir.getCanonicalPath() + ". Trying base."); dir = new File(ConfigurationSettings.getOutputSheetsDir()); outputSheetDirectory = ""; } } if (!dir.isDirectory()) { Logging.errorPrint("Unable to find outputsheets folder at " + dir.getCanonicalPath() + "."); return Collections.emptyList(); } IOFileFilter fileFilter = FileFilterUtils.notFileFilter(new SuffixFileFilter(".fo")); IOFileFilter dirFilter = FileFilterUtils.makeSVNAware(TrueFileFilter.INSTANCE); return FileUtils.listFiles(dir, fileFilter, dirFilter); } @Override protected void done() { if (isCancelled()) { return; } try { allTemplates = get(); progressBar.setVisible(false); refreshFiles(); } catch (InterruptedException ex) { Logging.errorPrint("failed to search files", ex); } catch (ExecutionException ex) { Logging.errorPrint("failed to search files", ex.getCause()); } } } private enum SheetFilter implements FilenameFilter { HTMLXML("htmlxml", "Standard", "HTM"), PDF("pdf", "PDF", "PDF"), TEXT("text", "Text", "TXT"); private final String dirFilter; private final String description; private final String tag; private SheetFilter(String dirFilter, String description, String tag) { this.dirFilter = dirFilter; this.description = description; this.tag = tag; } public String getPath() { return dirFilter; } @Override public String toString() { return description; } public String getTag() { return tag; } @Override public boolean accept(File dir, String name) { return dir.getName().equalsIgnoreCase(dirFilter) && !name.endsWith("~"); } } }