/* * Copyright 2003 (C) Devon Jones * * 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 * * $Id$ */ package plugin.pcgtracker; import java.awt.Component; import java.awt.Cursor; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import javax.swing.JFileChooser; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JTabbedPane; import gmgen.GMGenSystem; import gmgen.GMGenSystemView; import gmgen.gui.ImagePreview; import gmgen.io.SimpleFileFilter; import gmgen.pluginmgr.messages.AddMenuItemToGMGenToolsMenuMessage; import gmgen.pluginmgr.messages.FileMenuOpenMessage; import gmgen.pluginmgr.messages.GMGenBeingClosedMessage; import gmgen.pluginmgr.messages.RequestAddTabToGMGenMessage; import pcgen.cdom.base.Constants; import pcgen.core.PlayerCharacter; import pcgen.core.SettingsHandler; import pcgen.gui2.tools.Utility; import pcgen.io.PCGFile; import pcgen.io.PCGIOHandler; import pcgen.pluginmgr.InteractivePlugin; import pcgen.pluginmgr.PCGenMessage; import pcgen.pluginmgr.PCGenMessageHandler; import pcgen.pluginmgr.messages.FocusOrStateChangeOccurredMessage; import pcgen.pluginmgr.messages.PlayerCharacterWasClosedMessage; import pcgen.pluginmgr.messages.PlayerCharacterWasLoadedMessage; import pcgen.pluginmgr.messages.RequestOpenPlayerCharacterMessage; import pcgen.system.LanguageBundle; import pcgen.system.PCGenSettings; import pcgen.util.Logging; import plugin.pcgtracker.gui.PCGTrackerView; /** * The {@code ExperienceAdjusterController} handles the functionality of * the Adjusting of experience. This class is called by the {@code GMGenSystem * } and will have it's own model and view.<br> * Created on February 26, 2003<br> * Updated on February 26, 2003 * @author Expires 2003 */ public class PCGTrackerPlugin implements InteractivePlugin, java.awt.event.ActionListener { public static final String LOG_NAME = "PCG_Tracker"; //$NON-NLS-1$ private static final String OPTION_NAME_SYSTEM = LOG_NAME + ".System"; //$NON-NLS-1$ private static final String OPTION_NAME_LOADORDER = LOG_NAME + ".LoadOrder"; //$NON-NLS-1$ private static final String FILENAME_PCP = "pcp"; //$NON-NLS-1$ private static final String FILENAME_PCG = "pcg"; //$NON-NLS-1$ /** The plugin menu item in the tools menu. */ private JMenuItem charToolsItem = new JMenuItem(); private PCGTrackerModel model = new PCGTrackerModel(); private PCGTrackerView theView; /** The English name of the plugin. */ private static final String NAME = "Character Tracker"; //$NON-NLS-1$ /** Key of plugin tab. */ private static final String IN_NAME = "in_plugin_pcgtracker_name"; //$NON-NLS-1$ /** The version number of the plugin. */ private static final String version = "01.00.99.01.00"; //$NON-NLS-1$ private PCGenMessageHandler messageHandler; /** * Creates a new instance of PCGTrackerPlugin */ public PCGTrackerPlugin() { // Do Nothing } /** * Starts the plugin, registering itself with the {@code TabAddMessage}. */ @Override public void start(PCGenMessageHandler mh) { messageHandler = mh; theView = new PCGTrackerView(); theView.getLoadedList().setModel(model); initListeners(); messageHandler.handleMessage(new RequestAddTabToGMGenMessage(this, getLocalizedName(), getView())); initMenus(); } @Override public void stop() { messageHandler = null; } @Override public int getPriority() { return SettingsHandler.getGMGenOption(OPTION_NAME_LOADORDER, 1000); } /** * Accessor for name * @return name */ @Override public String getPluginName() { return NAME; } private String getLocalizedName() { return LanguageBundle.getString(IN_NAME); } /** * Gets the view that this class is using. * @return the view. */ public Component getView() { return theView; } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == theView.getRemoveButton()) { removeSelected(); } if (e.getSource() == theView.getSaveButton()) { for (Object obj : theView.getLoadedList().getSelectedValuesList()) { PlayerCharacter pc = model.get(obj); savePC(pc, false); } } if (e.getSource() == theView.getSaveAsButton()) { for (Object obj : theView.getLoadedList().getSelectedValuesList()) { PlayerCharacter pc = model.get(obj); savePC(pc, true); } } if (e.getSource() == theView.getLoadButton()) { handleOpen(); } theView.getLoadedList().repaint(); } public void handleClose() { /* * TODO This method seems like a "dead" chain of events - the PCs are * fetched, but nothing happens. As best I can tell, none of these * methods have side effects (that is good), but that means this method * does nothing. - thpr 10/26/06 */ if (!model.isEmpty()) { GMGenSystemView.getTabPane().setSelectedComponent(theView); } for (int i = 0; i < model.size(); i++) { model.get(i); } } /** * listens to messages from the GMGen system, and handles them as needed * @param message the source of the event from the system */ @Override public void handleMessage(PCGenMessage message) { if (message instanceof FileMenuOpenMessage) { if (isActive()) { handleOpen(); } } else if (message instanceof PlayerCharacterWasLoadedMessage) { PlayerCharacterWasLoadedMessage cmessage = (PlayerCharacterWasLoadedMessage) message; model.add(cmessage.getPc()); } else if (message instanceof FocusOrStateChangeOccurredMessage) { if (isActive()) { charToolsItem.setEnabled(false); try { GMGenSystem.inst.openFileItem.setEnabled(true); } catch (Exception e) { // TODO Handle this? } } else { charToolsItem.setEnabled(true); } } else if (message instanceof GMGenBeingClosedMessage) { handleClose(); } /*else if (message instanceof SavePCGRequestMessage) { SavePCGRequestMessage smessage = (SavePCGRequestMessage) message; savePC(smessage.getPC(), false); }*/ else if (message instanceof PlayerCharacterWasClosedMessage) { PlayerCharacterWasClosedMessage cmessage = (PlayerCharacterWasClosedMessage) message; model.remove(cmessage.getPC()); } } public boolean isActive() { JTabbedPane tp = Utility.getTabbedPaneFor(theView); return tp != null && JOptionPane.getFrameForComponent(tp).isFocused() && tp.getSelectedComponent().equals(theView); } /** * Handles the clicking of the <b>Add</b> button on the GUI. */ public void handleOpen() { File defaultFile = new File(PCGenSettings.getPcgDir()); JFileChooser chooser = new JFileChooser(); chooser.setCurrentDirectory(defaultFile); String[] pcgs = new String[]{FILENAME_PCG, FILENAME_PCP}; SimpleFileFilter ff = new SimpleFileFilter(pcgs, LanguageBundle.getString("in_pcgen_file")); //$NON-NLS-1$ chooser.addChoosableFileFilter(ff); chooser.setFileFilter(ff); chooser.setMultiSelectionEnabled(true); Component component = GMGenSystem.inst; Cursor originalCursor = component.getCursor(); component.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); int option = chooser.showOpenDialog(GMGenSystem.inst); if (option == JFileChooser.APPROVE_OPTION) { for (File selectedFile : chooser.getSelectedFiles()) { if (PCGFile.isPCGenCharacterOrPartyFile(selectedFile)) { messageHandler.handleMessage(new RequestOpenPlayerCharacterMessage(this, selectedFile, false)); } } } else { /* this means the file is invalid */ } GMGenSystem.inst.setCursor(originalCursor); } /** * Registers all the listeners for any actions. */ public void initListeners() { theView.getRemoveButton().addActionListener(this); theView.getSaveButton().addActionListener(this); theView.getSaveAsButton().addActionListener(this); theView.getLoadButton().addActionListener(this); } public void removeSelected() { for (Object obj : theView.getLoadedList().getSelectedValuesList()) { PlayerCharacter pc = model.get(obj); model.removeElement(obj); messageHandler.handleMessage(new PlayerCharacterWasClosedMessage(this, pc)); } } /** * Checks whether a character can be saved, and if so, calls * it's {@code save} method. * * @param aPC The PlayerCharacter to save * @param saveas boolean if {@code true}, ask for file name * * @return {@code true} if saved; <code>false</code> if save as cancelled */ // TODO use pcgen save methods rather than implementing it again public boolean savePC(PlayerCharacter aPC, boolean saveas) { boolean newPC = false; File prevFile; File file = null; String aPCFileName = aPC.getFileName(); if (aPCFileName.isEmpty()) { prevFile = new File(PCGenSettings.getPcgDir(), aPC.getDisplay().getDisplayName() + Constants.EXTENSION_CHARACTER_FILE); aPCFileName = prevFile.getAbsolutePath(); newPC = true; } else { prevFile = new File(aPCFileName); } if (saveas || newPC) { JFileChooser fc = ImagePreview.decorateWithImagePreview(new JFileChooser()); String[] pcgs = new String[]{FILENAME_PCG}; SimpleFileFilter ff = new SimpleFileFilter(pcgs, LanguageBundle.getString("in_pcgen_file_char")); //$NON-NLS-1$ fc.setFileFilter(ff); fc.setSelectedFile(prevFile); FilenameChangeListener listener = new FilenameChangeListener(aPCFileName, fc); fc.addPropertyChangeListener(listener); int returnVal = fc.showSaveDialog(GMGenSystem.inst); fc.removePropertyChangeListener(listener); if (returnVal == JFileChooser.APPROVE_OPTION) { file = fc.getSelectedFile(); if (!PCGFile.isPCGenCharacterFile(file)) { file = new File(file.getParent(), file.getName() + Constants.EXTENSION_CHARACTER_FILE); } if (file.isDirectory()) { JOptionPane.showMessageDialog(null, LanguageBundle.getString("in_savePcDirOverwrite"), //$NON-NLS-1$ Constants.APPLICATION_NAME, JOptionPane.ERROR_MESSAGE); return false; } if (file.exists() && (newPC || !file.getName().equals(prevFile.getName()))) { int reallyClose = JOptionPane .showConfirmDialog( GMGenSystem.inst, LanguageBundle.getFormattedString("in_savePcConfirmOverMsg", //$NON-NLS-1$ file.getName()), LanguageBundle.getFormattedString("in_savePcConfirmOverTitle", file.getName()), //$NON-NLS-1$ JOptionPane.YES_NO_OPTION); if (reallyClose != JOptionPane.YES_OPTION) { return false; } } aPC.setFileName(file.getAbsolutePath()); } else { // not saving return false; } } else { // simple save file = prevFile; } try { (new PCGIOHandler()).write(aPC, null, null, file); } catch (Exception ex) { String formattedString = LanguageBundle.getFormattedString("in_saveFailMsg", aPC.getDisplay().getDisplayName()); //$NON-NLS-1$ JOptionPane.showMessageDialog(null, formattedString, Constants.APPLICATION_NAME, JOptionPane.ERROR_MESSAGE); Logging.errorPrint(formattedString); Logging.errorPrint(ex.getMessage(), ex); return false; } return true; } public void toolMenuItem(ActionEvent evt) { JTabbedPane tp = GMGenSystemView.getTabPane(); for (int i = 0; i < tp.getTabCount(); i++) { if (tp.getComponentAt(i) instanceof PCGTrackerView) { tp.setSelectedIndex(i); } } } private void initMenus() { charToolsItem.setMnemonic(LanguageBundle.getMnemonic("in_mn_plugin_pcgtracker_name")); //$NON-NLS-1$ charToolsItem.setText(LanguageBundle.getString("in_plugin_pcgtracker_name")); //$NON-NLS-1$ charToolsItem.addActionListener(this::toolMenuItem); messageHandler.handleMessage(new AddMenuItemToGMGenToolsMenuMessage(this, charToolsItem)); } /** * Property change listener for the event "selected file * changed". Ensures that the filename doesn't get changed * when a directory is selected. * * @author Dmitry Jemerov <yole@spb.cityline.ru> */ static final class FilenameChangeListener implements PropertyChangeListener { private JFileChooser fileChooser; private String lastSelName; FilenameChangeListener(String aFileName, JFileChooser aFileChooser) { lastSelName = aFileName; fileChooser = aFileChooser; } @Override public void propertyChange(PropertyChangeEvent evt) { String propName = evt.getPropertyName(); if (propName.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) { onSelectedFileChange(evt); } else if (propName.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) { onDirectoryChange(); } } private void onDirectoryChange() { fileChooser.setSelectedFile(new File(fileChooser .getCurrentDirectory(), lastSelName)); } private void onSelectedFileChange(PropertyChangeEvent evt) { File newSelFile = (File) evt.getNewValue(); if ((newSelFile != null) && !newSelFile.isDirectory()) { lastSelName = newSelFile.getName(); } } } /** * Gets the name of the data directory for Plugin object * *@return The data directory name */ public File getDataDirectory() { File dataDir = new File(SettingsHandler.getGmgenPluginDir(), getPluginName()); return dataDir; } }