/* * InfoPaneHandler.java * Copyright 2010 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 May 23, 2010, 3:16:32 PM */ package pcgen.gui2.tabs.summary; import javax.swing.JEditorPane; import pcgen.facade.core.CharacterFacade; import pcgen.facade.core.CharacterLevelFacade; import pcgen.facade.core.GameModeFacade; import pcgen.facade.core.StatFacade; import pcgen.facade.util.event.ListEvent; import pcgen.facade.util.event.ListListener; import pcgen.facade.util.event.ReferenceEvent; import pcgen.facade.util.event.ReferenceListener; import pcgen.gui2.tabs.models.HtmlSheetSupport; import pcgen.system.LanguageBundle; /** * Manages the information pane of the summary tab. This is an output sheet * that is displayed in the summary tab to advise the user of important * stats for their character. The output sheet to be displayed is specified in * the game mode miscinfo.lst file using the INFOSHEET tag. * <br> * * @author Connor Petty <cpmeister@users.sourceforge.net> */ public class InfoPaneHandler implements ReferenceListener<Object>, ListListener<CharacterLevelFacade> { private boolean installed = false; private HtmlSheetSupport support; private String currentInfoTemplateFile; private CharacterFacade character; /** * Create a new info pane handler instance for a character. * @param character The character the pane is to display information for. * @param htmlPane the pane that displays the information */ public InfoPaneHandler(CharacterFacade character, JEditorPane htmlPane) { this.character = character; GameModeFacade game = character.getDataSet().getGameMode(); support = new HtmlSheetSupport(character, htmlPane, game.getInfoSheet()); support.setMissingSheetMsg(LanguageBundle.getFormattedString("in_sumNoInfoSheet", //$NON-NLS-1$ character.getDataSet().getGameMode().getName())); registerListeners(); } /** * Initialise our display component. Any expected UI behaviour/ * configuration is enforced here. Note that this is a utility function for * use by SummaryInfoTab. While there is a handler for each character * displayed, there is only a single instance of each display component. * * @param htmlPane The editor panel that will display the sheet. */ public static void initializeEditorPane(JEditorPane htmlPane) { htmlPane.setOpaque(false); htmlPane.setEditable(false); htmlPane.setFocusable(false); htmlPane.setContentType("text/html"); //$NON-NLS-1$ } /** * Link this handler with our display component and schedule a refresh of * the contents for the character. */ public void install() { support.install(); installed = true; scheduleRefresh(); } /** * Register with the things we want to be notified of changes about. */ private void registerListeners() { character.getRaceRef().addReferenceListener(this); character.getGenderRef().addReferenceListener(this); character.getAlignmentRef().addReferenceListener(this); for (StatFacade stat : character.getDataSet().getStats()) { character.getScoreBaseRef(stat).addReferenceListener(this); } character.getCharacterLevelsFacade().addListListener(this); character.getHandedRef().addReferenceListener(this); character.getAgeRef().addReferenceListener(this); } /** * Start an update of the contents of the info pane for this character. The * update will happen in a new thread and will not be started if one is * already running. */ public void scheduleRefresh() { support.refresh(); } /** * Register that we are no longer the active character. */ public void uninstall() { support.uninstall(); installed = false; } /* (non-Javadoc) * @see pcgen.core.facade.event.ReferenceListener#referenceChanged(pcgen.core.facade.event.ReferenceEvent) */ @Override public void referenceChanged(ReferenceEvent<Object> e) { scheduleRefresh(); } /* (non-Javadoc) * @see pcgen.core.facade.event.ListListener#elementAdded(pcgen.core.facade.event.ListEvent) */ @Override public void elementAdded(ListEvent<CharacterLevelFacade> e) { scheduleRefresh(); } /* (non-Javadoc) * @see pcgen.core.facade.event.ListListener#elementRemoved(pcgen.core.facade.event.ListEvent) */ @Override public void elementRemoved(ListEvent<CharacterLevelFacade> e) { scheduleRefresh(); } /* (non-Javadoc) * @see pcgen.core.facade.event.ListListener#elementModified(pcgen.core.facade.event.ListEvent) */ @Override public void elementModified(ListEvent<CharacterLevelFacade> e) { scheduleRefresh(); } /* (non-Javadoc) * @see pcgen.core.facade.event.ListListener#elementsChanged(pcgen.core.facade.event.ListEvent) */ @Override public void elementsChanged(ListEvent<CharacterLevelFacade> e) { scheduleRefresh(); } // // /** // * A cache for images loaded onto the info pane. // */ // private static class ImageCache extends Dictionary<URL, Image> // { // // private HashMap<URL, Image> cache = new HashMap<URL, Image>(); // // @Override // public int size() // { // return cache.size(); // } // // @Override // public boolean isEmpty() // { // return cache.isEmpty(); // } // // @Override // public Enumeration<URL> keys() // { // return Collections.enumeration(cache.keySet()); // } // // @Override // public Enumeration<Image> elements() // { // return Collections.enumeration(cache.values()); // } // // @Override // public Image get(Object key) // { // if (!(key instanceof URL)) // { // return null; // } // URL src = (URL) key; // if (!cache.containsKey(src)) // { // Image newImage = Toolkit.getDefaultToolkit().createImage(src); // if (newImage != null) // { // // Force the image to be loaded by using an ImageIcon. // ImageIcon ii = new ImageIcon(); // ii.setImage(newImage); // } // cache.put(src, newImage); // } // return cache.get(src); // } // // @Override // public Image put(URL key, Image value) // { // return cache.put(key, value); // } // // @Override // public Image remove(Object key) // { // return cache.remove(key); // } // // } // // private class TempInfoPaneRefresher implements Runnable // { // // private File templateFile = new File(currentInfoTemplateFile); // // public void run() // { // try // { // SwingUtilities.invokeAndWait(new Runnable() // { // // public void run() // { // StringWriter writer = new StringWriter(); // character.export(new ExportHandler(templateFile), new BufferedWriter(writer)); // StringReader reader = new StringReader(writer.toString()); // EditorKit kit = htmlPane.getEditorKit(); // HTMLDocument doc = new HTMLDocument(); // try // { // doc.setBase(templateFile.getParentFile().toURL()); // // XXX - This is a hack specific to Sun's JDK 5.0 and in no // // way should be trusted to work in future java releases // // (though it still might) - Connor Petty // doc.putProperty("imageCache", cache); // kit.read(reader, doc, 0); // } // catch (IOException ex) // { // Logging.errorPrint("Could not get parent of load template file " + // "for info panel.", ex); // } // catch (BadLocationException ex) // { // //This should never happen // } // if (installed) // { // htmlPane.setDocument(doc); // } // } // // }); // } // catch (InterruptedException ex) // { // //do nothing // } // catch (InvocationTargetException ex) // { // //do nothing // } // } // // } // // private class InfoRefreshTask extends FutureTask<HTMLDocument> // { // // public InfoRefreshTask() // { // super(new DocumentBuilder()); // } // // @Override // protected void done() // { // try // { // final HTMLDocument doc = get(); // if (!isCancelled()) // { // SwingUtilities.invokeLater(new Runnable() // { // // public void run() // { // if (installed) // { // htmlPane.setDocument(doc); // } // } // // }); // } // } // catch (InterruptedException ex) // { // //This can't happen // } // catch (ExecutionException ex) // { // try // { // throw ex.getCause(); // } // catch (InterruptedIOException e) // { // //this is normal ignore it // } // catch (IOException e) // { // } // catch (Throwable e) // { // Logging.errorPrint("Unexpected error occurred", e); // } // // } // } // // } // // private class DocumentBuilder implements Callable<HTMLDocument>, Runnable // { // // private File templateFile = new File(currentInfoTemplateFile); // private PipedWriter writer = new PipedWriter(); // // public HTMLDocument call() throws Exception // { // PipedReader reader = new PipedReader(writer); // new Thread(this).start(); // // EditorKit kit = htmlPane.getEditorKit(); // HTMLDocument doc = new HTMLDocument(); // doc.setBase(templateFile.getParentFile().toURL()); // // XXX - This is a hack specific to Sun's JDK 5.0 and in no // // way should be trusted to work in future java releases // // (though it still might) - Connor Petty // doc.putProperty("imageCache", cache); // kit.read(reader, doc, 0); // reader.close(); // return doc; // } // // public void run() // { // BufferedWriter bw = new BufferedWriter(writer, 1); // try // { // character.export(new ExportHandler(templateFile), bw); // } // finally // { // try // { // bw.close(); // } // catch (IOException ex) // { // Logging.errorPrint("Unable to close PipedWriter", ex); // } // } // } // // } }