/* * PCGenFrame.java * Copyright 2008 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 Aug 14, 2008, 1:00:34 PM */ package pcgen.gui2; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Observer; import java.util.logging.LogRecord; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.KeyStroke; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.WindowConstants; import javax.swing.filechooser.FileFilter; import org.apache.commons.lang3.StringUtils; import org.lobobrowser.html.HtmlRendererContext; import org.lobobrowser.html.gui.HtmlPanel; import org.lobobrowser.html.test.SimpleHtmlRendererContext; import org.lobobrowser.html.test.SimpleUserAgentContext; import pcgen.cdom.base.Constants; import pcgen.cdom.content.Sponsor; import pcgen.core.Globals; import pcgen.facade.core.CampaignFacade; import pcgen.facade.core.CharacterFacade; import pcgen.facade.core.CharacterStubFacade; import pcgen.facade.core.ChooserFacade; import pcgen.facade.core.CompanionFacade; import pcgen.facade.core.DataSetFacade; import pcgen.facade.util.DefaultReferenceFacade; import pcgen.facade.core.EquipmentBuilderFacade; import pcgen.facade.core.GameModeFacade; import pcgen.facade.core.PartyFacade; import pcgen.facade.util.ReferenceFacade; import pcgen.facade.core.SourceSelectionFacade; import pcgen.facade.core.SpellBuilderFacade; import pcgen.facade.core.UIDelegate; import pcgen.facade.util.event.ReferenceEvent; import pcgen.facade.util.event.ReferenceListener; import pcgen.facade.util.ListFacade; import pcgen.core.utils.ShowMessageDelegate; import pcgen.gui2.dialog.AboutDialog; import pcgen.gui2.dialog.ChooserDialog; import pcgen.gui2.dialog.EquipCustomizerDialog; import pcgen.gui2.dialog.PostLevelUpDialog; import pcgen.gui2.dialog.RadioChooserDialog; import pcgen.gui2.dialog.SpellChoiceDialog; import pcgen.gui2.dialog.TipOfTheDay; import pcgen.gui2.sources.SourceSelectionDialog; import pcgen.gui2.tabs.InfoTabbedPane; import pcgen.gui2.tools.Icons; import pcgen.gui2.tools.Utility; import pcgen.gui2.util.ShowMessageGuiObserver; import pcgen.gui2.util.SwingWorker; import pcgen.io.PCGFile; import pcgen.persistence.SourceFileLoader; import pcgen.system.CharacterManager; import pcgen.system.ConfigurationSettings; import pcgen.system.FacadeFactory; import pcgen.system.LanguageBundle; import pcgen.system.Main; import pcgen.system.PCGenPropBundle; import pcgen.system.PCGenSettings; import pcgen.system.PropertyContext; import pcgen.util.Logging; import pcgen.util.chooser.ChoiceHandler; import pcgen.util.chooser.ChooserFactory; /** * The main window for PCGen. In addition this class is responsible for providing * global UI functions such as message dialogs. * * <br> * @author Connor Petty <cpmeister@users.sourceforge.net> */ public final class PCGenFrame extends JFrame implements UIDelegate { private final PCGenActionMap actionMap; private final CharacterTabs characterTabs; private final PCGenStatusBar statusBar; private final DefaultReferenceFacade<SourceSelectionFacade> currentSourceSelection; private final DefaultReferenceFacade<CharacterFacade> currentCharacterRef; private final DefaultReferenceFacade<DataSetFacade> currentDataSetRef; private final FilenameListener filenameListener; //Since creating a new JFileChooser requires a bit of overhead we reuse the same one every time private final JFileChooser chooser; private JDialog sourceSelectionDialog = null; private SourceLoadWorker sourceLoader = null; private String section15 = null; private String lastCharacterPath = null; public PCGenFrame() { Globals.setRootFrame(this); this.currentSourceSelection = new DefaultReferenceFacade<>(); this.currentCharacterRef = new DefaultReferenceFacade<>(); this.currentDataSetRef = new DefaultReferenceFacade<>(); this.actionMap = new PCGenActionMap(this); this.characterTabs = new CharacterTabs(this); this.statusBar = new PCGenStatusBar(this); this.filenameListener = new FilenameListener(); this.chooser = new JFileChooser(); Observer messageObserver = new ShowMessageGuiObserver(this); ShowMessageDelegate.getInstance().addObserver(messageObserver); ChooserFactory.setDelegate(this); initComponents(); pack(); initSettings(); } private void initComponents() { setLayout(new BorderLayout()); JComponent root = getRootPane(); root.setActionMap(actionMap); root.setInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, createInputMap(actionMap)); characterTabs.add(new InfoGuidePane(this)); setJMenuBar(new PCGenMenuBar(this)); add(new PCGenToolBar(this), BorderLayout.NORTH); add(characterTabs, BorderLayout.CENTER); add(statusBar, BorderLayout.SOUTH); updateTitle(); setIconImage(Icons.PCGenApp.getImageIcon().getImage()); } /** * This checks to make sure that the given rectangle will be visible * on the current graphics environment */ private boolean checkBounds(Rectangle rect) { if (rect.isEmpty()) { return false; } GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); for (GraphicsDevice device : env.getScreenDevices()) { Rectangle bounds = device.getDefaultConfiguration().getBounds(); if (bounds.contains(rect) || bounds.intersects(rect)) { return true; } } return false; } private void initSettings() { final UIPropertyContext frameContext = UIPropertyContext.createContext("PCGenFrame"); Rectangle screenBounds = getGraphicsConfiguration().getBounds(); setSize(1060, 725); //this is the default frame dimensions setLocationRelativeTo(null); //center the frame if (!screenBounds.contains(getBounds())) { setSize((5 * screenBounds.width) / 6, (5 * screenBounds.height) / 6); setLocationRelativeTo(null); //center the frame } Rectangle frameBounds = getBounds(); frameBounds.x = frameContext.initInt("bounds.x", frameBounds.x); frameBounds.y = frameContext.initInt("bounds.y", frameBounds.x); frameBounds.width = frameContext.initInt("bounds.width", frameBounds.width); frameBounds.height = frameContext.initInt("bounds.height", frameBounds.height); int extendedState = frameContext.initInt("extendedState", NORMAL); if (extendedState == ICONIFIED) { extendedState = NORMAL; frameContext.setInt("extendedState", NORMAL); } setExtendedState(extendedState); if (checkBounds(frameBounds)) { setBounds(frameBounds); } addPropertyChangeListener("extendedState", frameContext); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { updateBounds(); } @Override public void componentMoved(ComponentEvent e) { updateBounds(); } private void updateBounds() { Rectangle bounds = getBounds(); frameContext.setInt("bounds.x", bounds.x); frameContext.setInt("bounds.y", bounds.y); frameContext.setInt("bounds.width", bounds.width); frameContext.setInt("bounds.height", bounds.height); } }); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { PCGenUIManager.closePCGen(); } }); } /** * This is called after initialization and starts up the PCGenFrame * by setting it visible and then performing other startup actions * as the user's preferences dictate. */ public void startPCGenFrame() { setVisible(true); new StartupWorker().start(); } /** * This thread does the work of starting up the UI. * If command arguments are present it handles them, otherwise it starts up normally. */ private class StartupWorker extends Thread { @Override public void run() { try { boolean alternateStartup = false; alternateStartup |= maybeLoadCampaign(); alternateStartup |= maybeLoadOrCreateCharacter(); alternateStartup |= maybeStartInNPCGen(); alternateStartup |= maybeStartInGMGen(); alternateStartup |= maybeAutoLoadSources(); if (!alternateStartup) { //Do a default startup SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (TipOfTheDay.showTipOfTheDay()) { showTipsOfTheDay(); } if (!SourceSelectionDialog.skipSourceSelection()) { showSourceSelectionDialog(); } } }); } } catch (InterruptedException ex) { Logging.errorPrint("Unexepected exception", ex); } catch (InvocationTargetException ex) { Logging.errorPrint("Unexepected exception", ex.getCause()); } } /** * If the preference to auto load sources at start is set, find the * sources that were last loaded and load them now. * @return true if the sources have been loaded, false if not. * @throws InterruptedException If the load was interrupted. */ private boolean maybeAutoLoadSources() throws InterruptedException { boolean autoLoadSources = PCGenSettings.OPTIONS_CONTEXT.initBoolean( PCGenSettings.OPTION_AUTOLOAD_SOURCES_AT_START, false); if (autoLoadSources) { String gameModeName = PCGenSettings.getInstance().getProperty( PCGenSettings.LAST_LOADED_GAME); String sourcesNameString = PCGenSettings.getInstance().getProperty( PCGenSettings.LAST_LOADED_SOURCES); if (StringUtils.isEmpty(gameModeName) || StringUtils.isEmpty(sourcesNameString)) { return false; } GameModeFacade gameMode = null; for (Iterator<GameModeFacade> iterator = FacadeFactory.getGameModes().iterator(); iterator .hasNext();) { GameModeFacade facade = iterator.next(); if (gameModeName.equals(facade.toString())) { gameMode = facade; break; } } if (gameMode == null) { return false; } List<CampaignFacade> campaigns = new ArrayList<>(); String[] sourceNames = sourcesNameString.split("\\|"); //$NON-NLS-1$ for (Iterator<CampaignFacade> iterator = FacadeFactory.getCampaigns().iterator(); iterator .hasNext();) { CampaignFacade camp = iterator.next(); for (String name : sourceNames) { if (name.equals(camp.toString())) { campaigns.add(camp); break; } } } SourceSelectionFacade selection = FacadeFactory .createSourceSelection(gameMode, campaigns); if (selection != null) { loadSourceSelection(selection); sourceLoader.join(); return true; } } return false; } private boolean maybeLoadCampaign() throws InterruptedException { String camp = Main.getStartupCampaign(); if (camp != null) { SourceSelectionFacade selection = null; ListFacade<SourceSelectionFacade> sources = FacadeFactory.getSourceSelections(); for (SourceSelectionFacade sourceSelectionFacade : sources) { if (sourceSelectionFacade.toString().equals(camp)) { selection = sourceSelectionFacade; break; } } if (selection != null) { loadSourceSelection(selection); sourceLoader.join(); return true; } else { //did not find source Logging .errorPrint("Ignoring invalid campaign requested in -m flag: '" + camp + "'."); return false; } } return false; } /** * loads or creates a character based upon the command line arguments. * This also handles the start in character sheet command option * @return * @throws InterruptedException * @throws InvocationTargetException */ private boolean maybeLoadOrCreateCharacter() throws InterruptedException, InvocationTargetException { if (Main.getStartupCharacterFile() == null) { return false; } final File file = new File(Main.getStartupCharacterFile()); final DataSetFacade dataset = currentDataSetRef.get(); if (!file.exists() && dataset == null) { //TODO: complain about it return false; } if (Main.shouldStartInCharacterSheet()) { String key = UIPropertyContext.C_PROP_INITIAL_TAB; key = UIPropertyContext.createFilePropertyKey(file, key); UIPropertyContext.getInstance().setInt(key, InfoTabbedPane.CHARACTER_SHEET_TAB); } SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { if (!file.exists()) { createNewCharacter(file); } else if (dataset == null) { loadCharacterFromFile(file); } else { openCharacter(file, dataset); } } }); return true; } private boolean maybeStartInNPCGen() { if (!Main.shouldStartInNPCGen()) { return false; } //TODO: finish this return true; } private boolean maybeStartInGMGen() { if (!Main.shouldStartInGMGen()) { return false; } PCGenUIManager.displayGmGen(); return true; } } private static InputMap createInputMap(ActionMap actionMap) { InputMap inputMap = new InputMap(); for (Object obj : actionMap.keys()) { KeyStroke key = (KeyStroke) actionMap.get(obj).getValue(Action.ACCELERATOR_KEY); if (key != null) { inputMap.put(key, obj); } } return inputMap; } public PCGenActionMap getActionMap() { return actionMap; } public void setSelectedCharacter(CharacterFacade character) { if (currentCharacterRef.get() != null) { currentCharacterRef.get().getFileRef().removeReferenceListener(filenameListener); } currentCharacterRef.set(character); updateTitle(); if (character != null && character.getFileRef() != null) { character.getFileRef().addReferenceListener(filenameListener); } } /** * @return A reference to the currently loaded sources. */ public ReferenceFacade<SourceSelectionFacade> getCurrentSourceSelectionRef() { return currentSourceSelection; } /** * * @return a reference to the selected character */ public ReferenceFacade<CharacterFacade> getSelectedCharacterRef() { return currentCharacterRef; } /** * * @return a reference to the currently loaded data set */ public ReferenceFacade<DataSetFacade> getLoadedDataSetRef() { return currentDataSetRef; } /** * @return the status bar for the main PCGen frame */ public PCGenStatusBar getStatusBar() { return statusBar; } /** * Unload any currently loaded sources. */ public void unloadSources() { //make sure all characters are closed before unloading sources. if (closeAllCharacters()) { currentSourceSelection.set(null); currentDataSetRef.set(null); Globals.emptyLists(); updateTitle(); } } /** * Loads a selection of sources into PCGen asynchronously and * tracks the load progress on the status bar. While sources * are being loaded any calls to this method are ignored until * sources are finished loading. * @param sources a SourceSelectionFacade specifying the sources to load * @return true if the sources are loaded or are loading */ public boolean loadSourceSelection(SourceSelectionFacade sources) { if (sources == null) { return false; } if (sourceLoader != null && sourceLoader.isAlive()) { return checkSourceEquality(sources, sourceLoader.sources); } if (checkSourceEquality(sources, currentSourceSelection.get())) { return true; } //make sure all characters are closed before loading new sources. if (closeAllCharacters()) { sourceLoader = new SourceLoadWorker(sources, this); sourceLoader.start(); return true; } return false; } private boolean checkSourceEquality(SourceSelectionFacade source1, SourceSelectionFacade source2) { if (source1 == source2) { return true; } if (source1 == null ^ source2 == null) { return false; } //we use reference equality since GameModeFacades come from a fixed database if (source1.getGameMode().get() != source2.getGameMode().get()) { return false; } ListFacade<CampaignFacade> campaigns1 = source1.getCampaigns(); ListFacade<CampaignFacade> campaigns2 = source2.getCampaigns(); if (campaigns1.getSize() != campaigns2.getSize()) { return false; } for (CampaignFacade campaignFacade : campaigns1) { if (!campaigns2.containsElement(campaignFacade)) { return false; } } return true; } private boolean checkGameModeEquality(SourceSelectionFacade source1, SourceSelectionFacade source2) { if (source1 == source2) { return true; } if (source1 == null ^ source2 == null) { return false; } //we use reference equality since GameModeFacades come from a fixed database if (source1.getGameMode().get() != source2.getGameMode().get()) { return false; } return true; } public boolean saveCharacter(CharacterFacade character) { if (!CharacterManager.characterFilenameValid(character)) { return showSaveCharacterChooser(character); } // We must have a file name before we prepare. prepareForSave(character, false); if (!reallySaveCharacter(character)) { return showSaveCharacterChooser(character); } return true; } /** * Wraps the CharacterManager with GUI progress updates * @param character * @return value from CharacterManager.saveCharacter() */ public boolean reallySaveCharacter(CharacterFacade character) { boolean result = false; // KAW TODO externalize and NLS the msg final String msg = "Saving character..."; statusBar.startShowingProgress(msg, true); try { result = CharacterManager.saveCharacter(character); } catch (Exception e) { Logging.errorPrint(e.getLocalizedMessage(), e); } finally { statusBar.endShowingProgress(); } return result; } /** * Prepare the character for a save. This is primarily concerned with * ensuring all companions (or masters) have file names before the save is * done. * @param character The character being saved. */ private void prepareForSave(CharacterFacade character, boolean savingAll) { List<CompanionFacade> tobeSaved = new ArrayList<>(); for (CompanionFacade comp : character.getCompanionSupport().getCompanions()) { if (StringUtils.isEmpty(comp.getFileRef().get().getName()) && CharacterManager.getCharacterMatching(comp) != null) { tobeSaved.add(comp); } } if (!tobeSaved.isEmpty()) { if (savingAll || showMessageConfirm(Constants.APPLICATION_NAME, LanguageBundle.getString("in_unsavedCompanions"))) //$NON-NLS-1$ { for (CompanionFacade companionFacade : tobeSaved) { CharacterFacade compChar = CharacterManager.getCharacterMatching(companionFacade); showSaveCharacterChooser(compChar); } } } CharacterStubFacade master = character.getMaster(); if (master != null && (master.getFileRef().get() == null || StringUtils .isEmpty(master.getFileRef().get().getName()))) { if (savingAll || showMessageConfirm(Constants.APPLICATION_NAME, LanguageBundle.getString("in_unsavedMaster"))) //$NON-NLS-1$ { CharacterFacade masterChar = CharacterManager.getCharacterMatching(master); showSaveCharacterChooser(masterChar); } } } public void closeCharacter(CharacterFacade character) { if (character.isDirty()) { int ret = JOptionPane.showConfirmDialog(this, LanguageBundle .getFormattedString("in_savePcChoice", character //$NON-NLS-1$ .getNameRef().get()), Constants.APPLICATION_NAME, JOptionPane.YES_NO_CANCEL_OPTION); if (ret == JOptionPane.CANCEL_OPTION) { return; } if (ret == JOptionPane.YES_OPTION) { saveCharacter(character); } } CharacterManager.removeCharacter(character); } public boolean closeAllCharacters() { final int CLOSE_OPT_CHOOSE = 2; ListFacade<CharacterFacade> characters = CharacterManager.getCharacters(); if (characters.isEmpty()) { return true; } int saveAllChoice = CLOSE_OPT_CHOOSE; List<CharacterFacade> characterList = new ArrayList<>(); List<CharacterFacade> unsavedPCs = new ArrayList<>(); for (CharacterFacade characterFacade : characters) { if (characterFacade.isDirty()) { unsavedPCs.add(characterFacade); } else { characterList.add(characterFacade); } } if (unsavedPCs.size() > 1) { Object[] options = new Object[] { LanguageBundle.getString("in_closeOptSaveAll"), //$NON-NLS-1$ LanguageBundle.getString("in_closeOptSaveNone"), //$NON-NLS-1$ LanguageBundle.getString("in_closeOptChoose"), //$NON-NLS-1$ LanguageBundle.getString("in_cancel") //$NON-NLS-1$ }; saveAllChoice = JOptionPane.showOptionDialog(this, LanguageBundle.getString("in_closeOptSaveTitle"), //$NON-NLS-1$ Constants.APPLICATION_NAME, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); } if (saveAllChoice == 3) { // Cancel return false; } if (saveAllChoice == 1) { // Save none CharacterManager.removeAllCharacters(); return true; } for (CharacterFacade character : unsavedPCs) { int saveSingleChoice = JOptionPane.YES_OPTION; if (saveAllChoice == CLOSE_OPT_CHOOSE) { saveSingleChoice = JOptionPane.showConfirmDialog(this, LanguageBundle .getFormattedString("in_savePcChoice", character //$NON-NLS-1$ .getNameRef().get()), Constants.APPLICATION_NAME, JOptionPane.YES_NO_CANCEL_OPTION); } if (saveSingleChoice == JOptionPane.YES_OPTION) {//If you get here then the user either selected "Yes to All" or "Yes" if (saveCharacter(character)) { characterList.add(character); } } else if (saveSingleChoice == JOptionPane.NO_OPTION) { characterList.add(character); } else if (saveSingleChoice == JOptionPane.CANCEL_OPTION) { return false; } } for (CharacterFacade character : characterList) { CharacterManager.removeCharacter(character); } return characters.isEmpty(); } boolean showSavePartyChooser() { PartyFacade party = CharacterManager.getCharacters(); PCGenSettings context = PCGenSettings.getInstance(); String parentPath = context.getProperty(PCGenSettings.PCP_SAVE_PATH); chooser.setCurrentDirectory(new File(parentPath)); File file = party.getFileRef().get(); chooser.setSelectedFile(file); chooser.resetChoosableFileFilters(); FileFilter filter = new PcpFileFilter(); chooser.addChoosableFileFilter(filter); chooser.setFileFilter(filter); int ret = chooser.showSaveDialog(this); if (ret != JFileChooser.APPROVE_OPTION) { return false; } file = chooser.getSelectedFile(); if (!file.getName().endsWith(Constants.EXTENSION_PARTY_FILE)) { file = new File(file.getParent(), file.getName() + Constants.EXTENSION_PARTY_FILE); } if (file.isDirectory()) { showErrorMessage(Constants.APPLICATION_NAME, LanguageBundle.getString("in_savePcDirOverwrite")); //$NON-NLS-1$ return showSavePartyChooser(); } if (file.exists()) { boolean overwrite = showWarningConfirm(LanguageBundle.getFormattedString( "in_savePcConfirmOverTitle", file.getName()), //$NON-NLS-1$ LanguageBundle.getFormattedString( "in_savePcConfirmOverMsg", file.getName())); //$NON-NLS-1$ if (!overwrite) { return showSavePartyChooser(); } } party.setFile(file); context.setProperty(PCGenSettings.PCP_SAVE_PATH, file.getParent()); if (!saveAllCharacters()) { showErrorMessage(LanguageBundle.getString("in_savePartyFailTitle"), //$NON-NLS-1$ LanguageBundle.getString("in_savePartyFailMsg")); //$NON-NLS-1$ return false; } if (!CharacterManager.saveCurrentParty()) { return showSavePartyChooser(); } return true; } boolean saveAllCharacters() { boolean ok = true; for (CharacterFacade character : CharacterManager.getCharacters()) { File file = character.getFileRef().get(); if (file == null || StringUtils.isEmpty(file.getName())) { ok &= showSaveCharacterChooser(character); } else { prepareForSave(character, true); ok &= reallySaveCharacter(character); } } return ok; } /** * This brings up a file chooser allows the user to select * the location that a character should be saved to. * @param character the character to be saved */ boolean showSaveCharacterChooser(CharacterFacade character) { PCGenSettings context = PCGenSettings.getInstance(); String parentPath = lastCharacterPath; if (parentPath == null) { parentPath = context.getProperty(PCGenSettings.PCG_SAVE_PATH); } chooser.setCurrentDirectory(new File(parentPath)); File file = character.getFileRef().get(); File prevFile = file; if (file == null || StringUtils.isEmpty(file.getName())) { file = new File(parentPath, character.getNameRef().get() + Constants.EXTENSION_CHARACTER_FILE); } chooser.setSelectedFile(file); chooser.resetChoosableFileFilters(); FileFilter filter = new PcgFileFilter(); chooser.addChoosableFileFilter(filter); chooser.setFileFilter(filter); int ret = chooser.showSaveDialog(this); if (ret == JFileChooser.APPROVE_OPTION) { file = chooser.getSelectedFile(); if (!file.getName().endsWith(Constants.EXTENSION_CHARACTER_FILE)) { file = new File(file.getParent(), file.getName() + Constants.EXTENSION_CHARACTER_FILE); } UIDelegate delegate = character.getUIDelegate(); if (file.isDirectory()) { delegate.showErrorMessage(Constants.APPLICATION_NAME, LanguageBundle.getString("in_savePcDirOverwrite")); //$NON-NLS-1$ return showSaveCharacterChooser(character); } if (file.exists() && (prevFile == null || !file.getName().equals( prevFile.getName()))) { boolean overwrite = delegate.showWarningConfirm(LanguageBundle .getFormattedString("in_savePcConfirmOverTitle", //$NON-NLS-1$ file.getName()), LanguageBundle .getFormattedString("in_savePcConfirmOverMsg", //$NON-NLS-1$ file.getName())); if (!overwrite) { return showSaveCharacterChooser(character); } } try { character.setFile(file); prepareForSave(character, false); if (!reallySaveCharacter(character)) { return showSaveCharacterChooser(character); } lastCharacterPath = chooser.getCurrentDirectory().toString(); return true; } catch (Exception e) { Logging.errorPrint("Error saving character to new file " + file, e); //$NON-NLS-1$ delegate.showErrorMessage( Constants.APPLICATION_NAME, LanguageBundle.getFormattedString( "in_saveFailMsg", file.getName())); //$NON-NLS-1$ } } return false; } /** * Revert the character to the previous save. If no previous save, open a * new character tab. * @param character The character being saved. */ public void revertCharacter(CharacterFacade character) { if (character.isDirty()) { int ret = JOptionPane.showConfirmDialog(this, LanguageBundle .getFormattedString("in_revertPcChoice", character //$NON-NLS-1$ .getNameRef().get()), Constants.APPLICATION_NAME, JOptionPane.YES_NO_OPTION); if (ret == JOptionPane.YES_OPTION) { CharacterManager.removeCharacter(character); if (character.getFileRef().get() != null && character.getFileRef().get().exists()) { openCharacter(character.getFileRef().get(), currentDataSetRef.get()); } else { createNewCharacter(); } } } } void showOpenCharacterChooser() { PCGenSettings context = PCGenSettings.getInstance(); String path = lastCharacterPath; if (path == null) { path = context.getProperty(PCGenSettings.PCG_SAVE_PATH); } chooser.setCurrentDirectory(new File(path)); chooser.setSelectedFile(new File("")); //$NON-NLS-1$ chooser.resetChoosableFileFilters(); FileFilter filter = new PcgFileFilter(); chooser.addChoosableFileFilter(filter); chooser.setFileFilter(filter); int ret = chooser.showOpenDialog(this); if (ret == JFileChooser.APPROVE_OPTION) { File file = chooser.getSelectedFile(); loadCharacterFromFile(file); lastCharacterPath = chooser.getCurrentDirectory().toString(); } } void showOpenPartyChooser() { PCGenSettings context = PCGenSettings.getInstance(); chooser.setCurrentDirectory(new File(context.getProperty(PCGenSettings.PCP_SAVE_PATH))); chooser.resetChoosableFileFilters(); chooser.setFileFilter(new PcpFileFilter()); int returnVal = chooser.showOpenDialog(this); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = chooser.getSelectedFile(); loadPartyFromFile(file); } } public void createNewCharacter() { createNewCharacter(null); } /** * creates a new character and sets its file if possible * then sets the character as the currently selected character * @param file the File for this character */ private void createNewCharacter(File file) { DataSetFacade data = getLoadedDataSetRef().get(); CharacterFacade character = CharacterManager.createNewCharacter(this, data); //This is called before the we set it as the selected character so //the InfoTabbedPane can catch any character specific properties when //it is first displayed if (file != null) { character.setFile(file); } //Because CharacterManager adds the new character to the character //list before it returns, it is not necessary to update the character //tabs since they will catch that event before the call to //setSelectedCharacter is called setSelectedCharacter(character); } /** * Loads a character from a file. Any sources that are required for * this character are loaded first, then the character is loaded * from the file and a tab is opened for it. * @param pcgFile a file specifying the character to be loaded */ public void loadCharacterFromFile(final File pcgFile) { if (!PCGFile.isPCGenCharacterFile(pcgFile)) { JOptionPane.showMessageDialog(this, LanguageBundle.getFormattedString("in_loadPcInvalid", pcgFile), //$NON-NLS-1$ LanguageBundle.getString("in_loadPcFailTtile"), //$NON-NLS-1$ JOptionPane.ERROR_MESSAGE); return; } if (!pcgFile.canRead()) { JOptionPane.showMessageDialog(this, LanguageBundle.getFormattedString("in_loadPcNoRead", pcgFile), //$NON-NLS-1$ LanguageBundle.getString("in_loadPcFailTtile"), //$NON-NLS-1$ JOptionPane.ERROR_MESSAGE); return; } SourceSelectionFacade sources = CharacterManager.getRequiredSourcesForCharacter(pcgFile, this); if (sources == null) { JOptionPane.showMessageDialog(this, LanguageBundle .getFormattedString("in_loadPcNoSources", pcgFile), //$NON-NLS-1$ LanguageBundle.getString("in_loadPcFailTtile"), //$NON-NLS-1$ JOptionPane.ERROR_MESSAGE); } else if (!sources.getCampaigns().isEmpty()) { // Check if the user has asked that sources not be loaded with the character boolean dontLoadSources = currentSourceSelection.get() != null && !PCGenSettings.OPTIONS_CONTEXT .initBoolean( PCGenSettings.OPTION_AUTOLOAD_SOURCES_WITH_PC, true); boolean sourcesSame = checkSourceEquality(sources, currentSourceSelection.get()); boolean gameModesSame = checkGameModeEquality(sources, currentSourceSelection.get()); if (!dontLoadSources && !sourcesSame && gameModesSame) { Object[] btnNames = new Object[]{ LanguageBundle .getString("in_loadPcDiffSourcesLoaded"), LanguageBundle .getString("in_loadPcDiffSourcesCharacter"), LanguageBundle.getString("in_cancel")}; int choice = JOptionPane.showOptionDialog(this, LanguageBundle .getFormattedString("in_loadPcDiffSources", getFormattedCampaigns(currentSourceSelection .get()), getFormattedCampaigns(sources)), LanguageBundle .getString("in_loadPcSourcesLoadTitle"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, btnNames, null); if (choice == JOptionPane.CANCEL_OPTION) { return; } if (choice == JOptionPane.YES_OPTION) { openCharacter(pcgFile, currentDataSetRef.get()); return; } } if (dontLoadSources) { if (!checkSourceEquality(sources, currentSourceSelection.get())) { Logging.log( Logging.WARNING, "Loading character with different sources. Character: " + sources + " current: " + currentSourceSelection.get()); } openCharacter(pcgFile, currentDataSetRef.get()); } else if (loadSourceSelection(sources)) { loadSourcesThenCharacter(pcgFile); } else { JOptionPane.showMessageDialog(this, LanguageBundle.getString("in_loadPcIncompatSource"), //$NON-NLS-1$ LanguageBundle.getString("in_loadPcFailTtile"), //$NON-NLS-1$ JOptionPane.ERROR_MESSAGE); } } else if (currentDataSetRef.get() != null) { if (showWarningConfirm(Constants.APPLICATION_NAME, LanguageBundle.getFormattedString("in_loadPcSourcesLoadQuery", //$NON-NLS-1$ pcgFile))) { openCharacter(pcgFile, currentDataSetRef.get()); } } else { // No character sources and no sources loaded. JOptionPane.showMessageDialog(this, LanguageBundle.getFormattedString("in_loadPcNoSources", pcgFile), //$NON-NLS-1$ LanguageBundle.getString("in_loadPcFailTtile"), //$NON-NLS-1$ JOptionPane.ERROR_MESSAGE); } } /** * @param the character's pcgFile * @param data set reference */ private void openCharacter(final File pcgFile, final DataSetFacade reference) { final String msg = LanguageBundle.getFormattedString("in_loadPcLoadingFile", pcgFile.getName()); statusBar.startShowingProgress(msg, false); statusBar.getProgressBar().getModel() .setRangeProperties(0, 1, 0, 2, false); statusBar.getProgressBar().setString( LanguageBundle.getString("in_loadPcOpening")); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { CharacterManager.openCharacter(pcgFile, PCGenFrame.this, reference); statusBar.getProgressBar().getModel() .setRangeProperties(1, 1, 0, 2, false); } catch (Exception e) { Logging.errorPrint( "Error loading character: " + pcgFile.getName(), e); } finally { statusBar.endShowingProgress(); } } }); } private String getFormattedCampaigns(SourceSelectionFacade sources) { StringBuilder campList = new StringBuilder(100); campList.append("<UL>"); int count = 1; final int maxListLen = 6; for (CampaignFacade facade : sources.getCampaigns()) { campList.append("<li>"); if (count >= maxListLen && sources.getCampaigns().getSize() > maxListLen) { int numExtra = sources.getCampaigns().getSize()-maxListLen+1; campList.append(LanguageBundle.getFormattedString( "in_loadPcDiffSourcesExcessSources", String.valueOf(numExtra))); break; } campList.append(facade.toString()); campList.append("</li>"); count++; } campList.append("</UL>"); return campList.toString(); } /** * Asynchronously load the sources required for a character and then load the character. * @param pcgFile The character to be loaded. */ private void loadSourcesThenCharacter(final File pcgFile) { new Thread() { @Override public void run() { try { sourceLoader.join(); SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { final String msg = LanguageBundle.getFormattedString( "in_loadPcLoadingFile", pcgFile.getName()); statusBar.startShowingProgress(msg, false); statusBar.getProgressBar().getModel() .setRangeProperties(0, 1, 0, 2, false); statusBar.getProgressBar().setString( LanguageBundle.getString("in_loadPcOpening")); } }); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { CharacterManager.openCharacter(pcgFile, PCGenFrame.this, currentDataSetRef.get()); statusBar.getProgressBar().getModel() .setRangeProperties(1, 1, 0, 2, false); } catch (Exception e) { Logging.errorPrint("Error loading character: " + pcgFile.getName(), e); } finally { statusBar.endShowingProgress(); } } }); } catch (InterruptedException ex) { //Do nothing } catch (InvocationTargetException e1) { Logging.errorPrint("Error showing progress bar.", e1); } } }.start(); } public void loadPartyFromFile(final File pcpFile) { if (!PCGFile.isPCGenPartyFile(pcpFile)) { JOptionPane.showMessageDialog(this, LanguageBundle .getFormattedString("in_loadPartyInvalid", pcpFile), //$NON-NLS-1$ LanguageBundle.getString("in_loadPartyFailTtile"), //$NON-NLS-1$ JOptionPane.ERROR_MESSAGE); return; } if (!pcpFile.canRead()) { JOptionPane.showMessageDialog(this, LanguageBundle .getFormattedString("in_loadPartyNoRead", pcpFile), //$NON-NLS-1$ LanguageBundle.getString("in_loadPartyFailTtile"), //$NON-NLS-1$ JOptionPane.ERROR_MESSAGE); return; } SourceSelectionFacade sources = CharacterManager.getRequiredSourcesForParty(pcpFile, this); if (sources == null) { JOptionPane.showMessageDialog(this, LanguageBundle .getFormattedString("in_loadPartyNoSources", pcpFile), //$NON-NLS-1$ LanguageBundle.getString("in_loadPartyFailTtile"), //$NON-NLS-1$ JOptionPane.ERROR_MESSAGE); } else if (!sources.getCampaigns().isEmpty()) { // Check if the user has asked that sources not be loaded with the character boolean dontLoadSources = currentSourceSelection.get() != null && !PCGenSettings.OPTIONS_CONTEXT .initBoolean( PCGenSettings.OPTION_AUTOLOAD_SOURCES_WITH_PC, true); if (dontLoadSources) { if (!checkSourceEquality(sources, currentSourceSelection.get())) { Logging.log( Logging.WARNING, "Loading party with different sources. Party: " + sources + " current: " + currentSourceSelection.get()); } CharacterManager.openParty(pcpFile, PCGenFrame.this, currentDataSetRef.get()); } else if (loadSourceSelection(sources)) { new Thread() { @Override public void run() { try { sourceLoader.join(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { CharacterManager.openParty(pcpFile, PCGenFrame.this, currentDataSetRef.get()); } }); } catch (InterruptedException ex) { //Do nothing } } }.start(); } else { JOptionPane.showMessageDialog(this, LanguageBundle.getString("in_loadPcIncompatSource"), //$NON-NLS-1$ LanguageBundle.getString("in_loadPartyFailTtile"), //$NON-NLS-1$ JOptionPane.ERROR_MESSAGE); } } } /** * Set the frame's title based on the current character and source/game mode. * @param characterFileName The file name (without path) of the active character * @param sourceName The name of the source selection. */ private void updateTitle() { StringBuilder title = new StringBuilder(100); File characterFile = null; String characterFileName = null; String sourceName = null; if (currentCharacterRef != null && currentCharacterRef.get() != null) { characterFile = currentCharacterRef.get().getFileRef().get(); if (characterFile == null || StringUtils.isEmpty(characterFile.getName())) { characterFileName = LanguageBundle.getString("in_unsaved_char"); //$NON-NLS-1$ } else { characterFileName = characterFile.getName(); } } if (currentSourceSelection.get() != null) { sourceName = currentSourceSelection.get().toString(); } if (characterFileName != null && !characterFileName.isEmpty()) { title.append(characterFileName); title.append(" - "); } if (sourceName != null && !sourceName.isEmpty()) { title.append(sourceName); title.append(" - "); } title.append("PCGen v"); title.append(PCGenPropBundle.getVersionNumber()); setTitle(title.toString()); } /** * display the tips of the day dialog to the user */ public void showTipsOfTheDay() { TipOfTheDay tips = new TipOfTheDay(this); Utility.setComponentRelativeLocation(this, tips); tips.setVisible(true); } /** * display the source selection dialog to the user */ public void showSourceSelectionDialog() { if (sourceSelectionDialog == null) { sourceSelectionDialog = new SourceSelectionDialog(this); } Utility.setComponentRelativeLocation(this, sourceSelectionDialog); sourceSelectionDialog.setVisible(true); } //TODO: This should be in a utility class. /** * Builds a JPanel containing the supplied message, split at each new * line and an optional checkbox, suitable for use in a showMessageDialog * call. This is generally useful for showing messges which can turned * off either in preferences or when they are displayed. * * @param message The message to be displayed. * @param checkbox The optional checkbox to be added - may be null. * @return JPanel A panel containing the message and the checkbox. */ public static JPanel buildMessageLabelPanel(String message, JCheckBox checkbox) { JPanel panel = new JPanel(); JLabel label; String part; panel.setLayout(new GridBagLayout()); GridBagConstraints cons = new GridBagConstraints(); cons.gridx = cons.gridy = 0; cons.gridwidth = GridBagConstraints.REMAINDER; cons.gridheight = 1; cons.anchor = GridBagConstraints.WEST; cons.insets = new Insets(0, 0, 3, 0); cons.weightx = 1; cons.weighty = 0; cons.fill = GridBagConstraints.NONE; int start = 0; int sepPos = -1; do { sepPos = message.indexOf("\n", start); //$NON-NLS-1$ if (sepPos >= 0) { part = message.substring(start, sepPos); start = sepPos + 1; } else { part = message.substring(start); start = -1; } label = new JLabel(part, SwingConstants.LEADING); panel.add(label, cons); cons.gridy++; } while (start >= 0); if (checkbox != null) { label = new JLabel("", SwingConstants.LEADING); //$NON-NLS-1$ panel.add(label, cons); cons.gridy++; panel.add(checkbox, cons); cons.gridy++; } return panel; } @Override public Boolean maybeShowWarningConfirm(String title, String message, String checkBoxText, final PropertyContext context, final String contextProp) { if (!context.getBoolean(contextProp, true)) { return null; } final JCheckBox checkBox = new JCheckBox(checkBoxText, true); checkBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { context.setBoolean(contextProp, checkBox.isSelected()); } }); JPanel panel = buildMessageLabelPanel(message, checkBox); int ret = JOptionPane.showConfirmDialog(this, panel, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); return ret == JOptionPane.YES_OPTION; } @Override public boolean showWarningConfirm(String title, String message) { JComponent msgComp = getComponentForMessage(message); int ret = JOptionPane.showConfirmDialog(this, msgComp, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); return ret == JOptionPane.YES_OPTION; } /** * Create a component to display the message within the bounds of the * screen. If the message is too big for the screen a suitably sized * scroll pane will be returned. * @param message The text of the message. * @return The component containing the text. */ private JComponent getComponentForMessage(String message) { JLabel jLabel = new JLabel(message); JScrollPane scroller = new JScrollPane(jLabel); Dimension size = jLabel.getPreferredSize(); final int decorationHeight = 80; final int decorationWidth = 70; Rectangle screenBounds = GraphicsEnvironment.getLocalGraphicsEnvironment() .getMaximumWindowBounds(); boolean scrollerNeeded = false; if (size.height > screenBounds.height - decorationHeight) { size.height = screenBounds.height - decorationHeight; scrollerNeeded = true; } if (size.width > screenBounds.width - decorationWidth) { size.width = screenBounds.width - decorationWidth; scrollerNeeded = true; } else if (scrollerNeeded) { scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); } scroller.setPreferredSize(size); return scrollerNeeded ? scroller : jLabel; } private boolean showMessageConfirm(String title, String message) { JComponent msgComp = getComponentForMessage(message); int ret = JOptionPane.showConfirmDialog(this, msgComp, title, JOptionPane.YES_NO_OPTION); return ret == JOptionPane.YES_OPTION; } @Override public void showErrorMessage(String title, String message) { JComponent msgComp = getComponentForMessage(message); JOptionPane.showMessageDialog(this, msgComp, title, JOptionPane.ERROR_MESSAGE); } @Override public void showInfoMessage(String title, String message) { JComponent msgComp = getComponentForMessage(message); JOptionPane.showMessageDialog(this, msgComp, title, JOptionPane.INFORMATION_MESSAGE); } @Override public void showWarningMessage(String title, String message) { JComponent msgComp = getComponentForMessage(message); JOptionPane.showMessageDialog(this, msgComp, title, JOptionPane.WARNING_MESSAGE); } @Override public boolean showWarningPrompt(String title, String message) { JComponent msgComp = getComponentForMessage(message); int ret = JOptionPane.showConfirmDialog(this, msgComp, title, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); return ret == JOptionPane.OK_OPTION; } @Override public String showInputDialog(String title, String message, String initialValue) { Object ret = JOptionPane.showInputDialog(this, message, title, JOptionPane.QUESTION_MESSAGE, null, null, initialValue); return ret == null ? null : String.valueOf(ret); } @Override public void showLevelUpInfo(CharacterFacade character, int oldLevel) { PostLevelUpDialog.showPostLevelUpDialog(this, character, oldLevel); } @Override public boolean showGeneralChooser(ChooserFacade chooserFacade) { // Check for an override of the chooser to be used ChoiceHandler choiceHandler = ChooserFactory.getChoiceHandler(); if (choiceHandler != null) { return choiceHandler.makeChoice(chooserFacade); } if (chooserFacade.isPreferRadioSelection() && chooserFacade.getAvailableList().getSize() <= 20 && chooserFacade.getRemainingSelections().get() == 1) { RadioChooserDialog dialog = new RadioChooserDialog(this, chooserFacade); Utility.setComponentRelativeLocation(this, dialog); dialog.setVisible(true); return dialog.isCommitted(); } else { ChooserDialog dialog = new ChooserDialog(this, chooserFacade); Utility.setComponentRelativeLocation(this, dialog); dialog.setVisible(true); return dialog.isCommitted(); } } /* * This thread does all work of loading sources that the user * has selected. After the sources are loaded the thread then * displays the licenses for the data. */ private class SourceLoadWorker extends Thread { private final SourceSelectionFacade sources; private final SourceFileLoader loader; private final SwingWorker<List<LogRecord>> worker; private final UIDelegate delegate; public SourceLoadWorker(SourceSelectionFacade sources, UIDelegate delegate) { this.sources = sources; this.delegate = delegate; loader = new SourceFileLoader(sources, delegate); worker = statusBar.createWorker(LanguageBundle.getString("in_taskLoadSources"), loader); //$NON-NLS-1$ } @Override public void run() { worker.start(); //wait until the worker finish and post any errors that occurred statusBar.setSourceLoadErrors(worker.get()); //now that the SourceFileLoader has finished //handle licenses and whatnot StringBuilder sec15 = new StringBuilder(" "); sec15.append(readTextFromFile(ConfigurationSettings.getSystemsDir() + File.separator + "opengaminglicense.10a.txt")); sec15.append(loader.getOGL()); section15 = sec15.toString(); try { showLicenses(); } catch (Throwable e) { Logging.errorPrint("Failed to show licences.", e); } DataSetFacade data = loader.getDataSetFacade(); if (data != null) { currentSourceSelection.set(sources); StringBuilder sourceString = new StringBuilder(100); ListFacade<CampaignFacade> campaigns = sources.getCampaigns(); for (int i = 0; i < campaigns.getSize(); i++) { if (i > 0) { sourceString.append('|'); } sourceString.append(campaigns.getElementAt(i)); } PCGenSettings.getInstance().setProperty( PCGenSettings.LAST_LOADED_GAME, sources.getGameMode().toString()); PCGenSettings.getInstance().setProperty( PCGenSettings.LAST_LOADED_SOURCES, sourceString.toString()); } else { currentSourceSelection.set(null); } currentDataSetRef.set(data); updateTitle(); } private void showLicenses() { PropertyContext context = PCGenSettings.OPTIONS_CONTEXT; if (context.initBoolean(PCGenSettings.OPTION_SHOW_LICENSE, true)) { if (loader.hasOGLCampaign()) { showOGLDialog(); } if (loader.hasLicensedCampaign()) { String licenses = loader.getLicenses(); if (!licenses.trim().isEmpty()) { showLicenseDialog(LanguageBundle.getString("in_specialLicenses"), licenses); //$NON-NLS-1$ } for (String license : loader.getOtherLicenses()) { showLicenseDialog(LanguageBundle.getString("in_specialLicenses"), license); //$NON-NLS-1$ } } } if (loader.hasMatureCampaign() && context.initBoolean(PCGenSettings.OPTION_SHOW_MATURE_ON_LOAD, true)) { showMatureDialog(loader.getMatureInfo()); } if (context.initBoolean(PCGenSettings.OPTION_SHOW_SPONSORS_ON_LOAD, true)) { showSponsorsDialog(); } } } public void showOGLDialog() { showLicenseDialog(LanguageBundle.getString("in_oglTitle"), section15); //$NON-NLS-1$ } private void showLicenseDialog(String title, String htmlString) { if (htmlString == null) { htmlString = LanguageBundle.getString("in_licNoInfo"); //$NON-NLS-1$ } final PropertyContext context = PCGenSettings.OPTIONS_CONTEXT; final JDialog aFrame = new JDialog(this, title, true); final JButton jClose = new JButton(LanguageBundle.getString("in_close")); //$NON-NLS-1$ jClose.setMnemonic(LanguageBundle.getMnemonic("in_mn_close")); //$NON-NLS-1$ final JPanel jPanel = new JPanel(); final JCheckBox jCheckBox = new JCheckBox(LanguageBundle.getString("in_licShowOnLoad")); //$NON-NLS-1$ jPanel.add(jCheckBox); jCheckBox.setSelected(context.getBoolean(PCGenSettings.OPTION_SHOW_LICENSE)); jCheckBox.addItemListener(evt -> context.setBoolean(PCGenSettings.OPTION_SHOW_LICENSE, jCheckBox.isSelected())); jPanel.add(jClose); jClose.addActionListener(evt -> aFrame.dispose()); HtmlPanel htmlPanel = new HtmlPanel(); HtmlRendererContext theRendererContext = new SimpleHtmlRendererContext(htmlPanel, new SimpleUserAgentContext()); htmlPanel.setHtml(htmlString, "", theRendererContext); aFrame.getContentPane().setLayout(new BorderLayout()); aFrame.getContentPane().add(htmlPanel, BorderLayout.CENTER); aFrame.getContentPane().add(jPanel, BorderLayout.SOUTH); aFrame.setSize(new Dimension(700, 500)); aFrame.setLocationRelativeTo(this); Utility.setComponentRelativeLocation(this, aFrame); aFrame.getRootPane().setDefaultButton(jClose); Utility.installEscapeCloseOperation(aFrame); aFrame.setVisible(true); } private void showMatureDialog(String text) { Logging.errorPrint("Warning: The following datasets contains mature themes. User discretion is advised."); Logging.errorPrint(text); final JDialog aFrame = new JDialog(this, LanguageBundle.getString("in_matureTitle"), true); final JPanel jPanel1 = new JPanel(); final JPanel jPanel3 = new JPanel(); final JLabel jLabel1 = new JLabel(LanguageBundle.getString("in_matureWarningLine1"), //$NON-NLS-1$ SwingConstants.CENTER); final JLabel jLabel2 = new JLabel(LanguageBundle.getString("in_matureWarningLine2"), //$NON-NLS-1$ SwingConstants.CENTER); final JCheckBox jCheckBox1 = new JCheckBox(LanguageBundle.getString("in_licShowOnLoad")); //$NON-NLS-1$ final JButton jClose = new JButton(LanguageBundle.getString("in_close")); //$NON-NLS-1$ jClose.setMnemonic(LanguageBundle.getMnemonic("in_mn_close")); //$NON-NLS-1$ jPanel1.setLayout(new BorderLayout()); jPanel1.add(jLabel1, BorderLayout.NORTH); jPanel1.add(jLabel2, BorderLayout.SOUTH); HtmlPanel htmlPanel = new HtmlPanel(); HtmlRendererContext theRendererContext = new SimpleHtmlRendererContext(htmlPanel, new SimpleUserAgentContext()); htmlPanel.setHtml(text, "", theRendererContext); jPanel3.add(jCheckBox1); jPanel3.add(jClose); final PropertyContext context = PCGenSettings.OPTIONS_CONTEXT; jCheckBox1.setSelected(context.getBoolean(PCGenSettings.OPTION_SHOW_MATURE_ON_LOAD)); jClose.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { aFrame.dispose(); } }); jCheckBox1.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent evt) { context.setBoolean(PCGenSettings.OPTION_SHOW_MATURE_ON_LOAD, jCheckBox1.isSelected()); } }); aFrame.getContentPane().setLayout(new BorderLayout()); aFrame.getContentPane().add(jPanel1, BorderLayout.NORTH); aFrame.getContentPane().add(htmlPanel, BorderLayout.CENTER); aFrame.getContentPane().add(jPanel3, BorderLayout.SOUTH); aFrame.setSize(new Dimension(456, 176)); Utility.setComponentRelativeLocation(this, aFrame); aFrame.setVisible(true); } public void showSponsorsDialog() { Collection<Sponsor> sponsors = Globals.getGlobalContext().getReferenceContext().getConstructedCDOMObjects(Sponsor.class); if (sponsors.size() <= 1) { return; } String title = LanguageBundle.getString("in_sponsorTitle"); //$NON-NLS-1$ final JDialog aFrame = new JDialog(this, title, true); final JButton jClose = new JButton(LanguageBundle.getString("in_close")); //$NON-NLS-1$ jClose.setMnemonic(LanguageBundle.getMnemonic("in_mn_close")); //$NON-NLS-1$ final JPanel jPanel = new JPanel(); final JCheckBox jCheckBox = new JCheckBox(LanguageBundle.getString("in_licShowOnLoad")); //$NON-NLS-1$ jPanel.add(jCheckBox); final PropertyContext context = PCGenSettings.OPTIONS_CONTEXT; jCheckBox.setSelected(context.getBoolean(PCGenSettings.OPTION_SHOW_SPONSORS_ON_LOAD)); jCheckBox.addItemListener(evt -> context.setBoolean(PCGenSettings.OPTION_SHOW_SPONSORS_ON_LOAD, jCheckBox.isSelected())); jPanel.add(jClose); jClose.addActionListener(evt -> aFrame.dispose()); StringBuilder sb = new StringBuilder(500); sb.append("<html>"); for (Sponsor sponsor : sponsors) { if (!"PCGEN".equals(sponsor.getKeyName())) { continue; } sb.append("<img src='").append(sponsor.getBannerImage()).append("'><br>"); } String s = ""; if (sponsors.size() > 2) { s = "s"; } sb.append("<H2><CENTER>") .append(LanguageBundle.getString("in_sponsorThanks")).append(s) //$NON-NLS-1$ .append(":</CENTER></h2>"); int size = 172; for (Sponsor sponsor : sponsors) { if ("PCGEN".equals(sponsor.getKeyName())) { continue; } size += 70; sb.append("<img src='").append(sponsor.getBannerImage()).append("'><br>"); } sb.append("</html>"); HtmlPanel htmlPanel = new HtmlPanel(); HtmlRendererContext theRendererContext = new SimpleHtmlRendererContext(htmlPanel, new SimpleUserAgentContext()); htmlPanel.setHtml(sb.toString(), "", theRendererContext); aFrame.getContentPane().setLayout(new BorderLayout()); aFrame.getContentPane().add(htmlPanel, BorderLayout.CENTER); aFrame.getContentPane().add(jPanel, BorderLayout.SOUTH); aFrame.setSize(new Dimension(505, size)); Utility.setComponentRelativeLocation(this, aFrame); aFrame.setVisible(true); } public void showAboutDialog() { new AboutDialog(this).setVisible(true); } @Override public CustomEquipResult showCustomEquipDialog(CharacterFacade character, EquipmentBuilderFacade equipBuilder) { EquipCustomizerDialog eqDialog = new EquipCustomizerDialog(this, character, equipBuilder); Utility.setComponentRelativeLocation(this, eqDialog); eqDialog.setVisible(true); CustomEquipResult result = eqDialog.isCancelled() ? CustomEquipResult.CANCELLED : eqDialog .isPurchase() ? CustomEquipResult.PURCHASE : CustomEquipResult.OK; return result; } @Override public boolean showCustomSpellDialog(SpellBuilderFacade spellBuilderFI) { SpellChoiceDialog spellDialog = new SpellChoiceDialog(this, spellBuilderFI); Utility.setComponentRelativeLocation(this, spellDialog); spellDialog.setVisible(true); return !spellDialog.isCancelled(); } private static String readTextFromFile(String fileName) { String aString; final File aFile = new File(fileName); if (!aFile.exists()) { Logging.errorPrint("Could not find license at " + fileName); aString = LanguageBundle.getString("in_licNoInfo"); //$NON-NLS-1$ return aString; } try { BufferedReader theReader = new BufferedReader(new InputStreamReader(new FileInputStream(aFile), "UTF-8")); final int length = (int) aFile.length(); final char[] inputLine = new char[length]; theReader.read(inputLine, 0, length); theReader.close(); aString = new String(inputLine); } catch (IOException e) { Logging.errorPrint("Could not read license at " + fileName, e); aString = "No license information found"; } return aString; } /** * The Class {@code FilenameListener} is used to update the frame title each time the * current character's file name is changed. */ private class FilenameListener implements ReferenceListener<File> { /* (non-Javadoc) * @see pcgen.core.facade.event.ReferenceListener#referenceChanged(pcgen.core.facade.event.ReferenceEvent) */ @Override public void referenceChanged(ReferenceEvent<File> e) { PCGenFrame.this.updateTitle(); } } }