package com.hero;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.KeyEventDispatcher;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Properties;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JWindow;
import javax.swing.KeyStroke;
import javax.swing.UIDefaults;
import javax.swing.UIManager;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import com.hero.event.HeroDesignerEventListenerI;
import com.hero.objects.GenericObject;
import com.hero.objects.modifiers.Modifier;
import com.hero.ui.MainContentPanel;
import com.hero.ui.dialog.AppPrefsDialog;
import com.hero.ui.dialog.CombatRecordDialog;
import com.hero.ui.dialog.ExportTemplateDialog;
import com.hero.ui.dialog.RulesDialog;
import com.hero.util.AutoUpdater;
import com.hero.util.BrowserControl;
import com.hero.util.HTMLWriter;
import com.hero.util.Utility;

import com.hero.pdf.HeroDesignerIntegration;
import com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel;

/**
 * Copyright (c) 2000 - 2005, CompNet Design, Inc. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, is prohibited unless the following conditions are met: 1.
 * Express written consent of CompNet Design, Inc. is obtained by the developer.
 * 2. Redistributions must retain this copyright notice. THIS SOFTWARE IS
 * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * @author CompNet Design, Inc.
 * @version $Revision$
 */

public class HeroDesigner {

	private static JFrame frame;

	public static JFrame getAppFrame() {
		return HeroDesigner.frame;
	}

	private static final long serialVersionUID = -669790253180486994L;

	private class PluginInfo {

		private String author;

		private String contact;

		private String description;

		private String fileName;

		private String mainClass;

		private String name;

		public PluginInfo(JarFile jar, String path) throws IOException {
			Manifest manifest = jar.getManifest();
			fileName = path;
			name = manifest.getMainAttributes().getValue("Plugin-Name");
			author = manifest.getMainAttributes().getValue("Plugin-Author");
			contact = manifest.getMainAttributes().getValue("Plugin-Contact");
			description = manifest.getMainAttributes().getValue(
					"Plugin-Description");
			mainClass = manifest.getMainAttributes().getValue("Main-Class");
			if (name == null) {
				name = "";
			}
			if (author == null) {
				author = "";
			}
			if (contact == null) {
				contact = "";
			}
			if (description == null) {
				description = "";
			}
			if (mainClass == null) {
				mainClass = "";
			}
		}

		public String getAuthor() {
			return author;
		}

		public String getContact() {
			return contact;
		}

		public String getDescription() {
			return description;
		}

		public String getFileName() {
			return fileName;
		}

		public String getMainClass() {
			return mainClass;
		}

		public String getName() {
			return name;
		}
	}

	/**
	 * @author Dan Simon Inner class to handle the loading and unloading of
	 *         Prefabs.
	 */
	protected class PrefabActionListener implements ActionListener {

		private Prefab prefab;

		private JMenuItem prefabMI;

		public PrefabActionListener(Prefab prefab, JMenuItem mi) {
			this.prefab = prefab;
			prefabMI = mi;
		}

		public void actionPerformed(ActionEvent e) {
			loadedPrefabs.remove(prefab);
			unloadPrefabMI.remove(prefabMI);
			mainPanel.updatePrefabs();
		}
	}

	private class RecentlySelectedMenuItem extends JMenuItem {

		private static final long serialVersionUID = -6417429614143547439L;

		File file;

		public RecentlySelectedMenuItem(File f) {
			super(f.getAbsolutePath());
			file = f;
			addActionListener(new ActionListener() {

				public void actionPerformed(ActionEvent e) {
					if (file != null && file.exists()) {
						openHero(file, true);
					}
				}
			});
		}
	}

	public class MemoryMonitor extends Thread {
		public void run() {
			while (true) {
				BigDecimal mm = new BigDecimal(Runtime.getRuntime().maxMemory()
						/ (1024d * 1024d));
				mm = mm.setScale(0, mm.ROUND_HALF_UP);
				BigDecimal um = new BigDecimal((Runtime.getRuntime()
						.totalMemory() - Runtime.getRuntime().freeMemory())
						/ (1024d * 1024d));
				um = um.setScale(0, um.ROUND_HALF_UP);
				memoryUsagePB.setString(um.toString() + "M of " + mm.toString()
						+ "M");
				memoryUsagePB.setValue(um.intValue());
				if (mm.intValue() - um.intValue() < 5) {
					System.gc();
					mm = new BigDecimal(Runtime.getRuntime().maxMemory()
							/ (1024d * 1024d));
					mm = mm.setScale(0, mm.ROUND_HALF_UP);
					um = new BigDecimal(
							(Runtime.getRuntime().totalMemory() - Runtime
									.getRuntime().freeMemory())
									/ (1024d * 1024d));
					um = um.setScale(0, um.ROUND_HALF_UP);
					memoryUsagePB.setString(um.toString() + "M of "
							+ mm.toString() + "M");
					memoryUsagePB.setValue(um.intValue());

					if (mm.intValue() - um.intValue() < 5) {
						lowMemory = true;
						for (int i = unloadPrefabMI.getItemCount() - 1; i >= 0; i--) {
							unloadPrefabMI.getItem(i).doClick();
						}
						String message = "<html>Warning!  HD is running low on memory.<br><br>To help prevent data loss, all prefabs have been unloaded.<br><br>It is recommended that you save your work and restart the application in the not-too-distant future.</html>";
						JOptionPane.showMessageDialog(
								HeroDesigner.getAppFrame(), message,
								"Low Memory Warning",
								JOptionPane.WARNING_MESSAGE);
						if (HeroDesigner.activeHero != null) {
							mainPanel.updatePrefabs();
						}
					}
				} else {
					lowMemory = false;
				}
				try {
					Thread.sleep(10000);
				} catch (InterruptedException exp) {
				}
			}
		}
	}

	private boolean lowMemory = false;

	/**
	 * only one character file is ever "active"....much of the application is
	 * keyed off of this concept.
	 */
	public static Hero activeHero;

	/**
	 * only one template is ever "active"....much of the application is keyed
	 * off of this concept.
	 */
	private static Template activeTemplate;

	/**
	 * anything that is copied goes into this buffer. This is in addition to the
	 * System copy buffer and is used internally on the app
	 */
	private static GenericObject copyBuffer;

	private static ArrayList<Modifier> copiedMods;

	/**
	 * path to the documentation. Used in Help -> View Documentation...
	 */
	public static String docsFilePath = "HDDocs.pdf";

	/**
	 * time (long) of last edit to a character. This is used in the caching of
	 * results for "expensive" operations.
	 */
	public static long lastEdit;

	private static ArrayList<PluginInfo> loadedPlugins;

	protected static JProgressBar loadProgress;

	/**
	 * boolean check as to whether the system is running Mac OSX -- set during
	 * main()
	 */
	public static boolean macOSX;
	static int fontSize = 12;

	static Thread runner = null;

	/**
	 * This class is designed to only ever have one instance active. It controls
	 * the entire app.
	 */
	private static HeroDesigner singleton;

	/**
	 * This version number is compared against an online database to determine
	 * if updates are available
	 */
	public static String version = "version.swap";

	private JMenuItem aboutMI;

	protected JWindow aboutWindow;

	private JRadioButtonMenuItem aiMI;
	private JRadioButtonMenuItem ai6EMI;

	private JRadioButtonMenuItem automatonMI;
	private JRadioButtonMenuItem automaton6EMI;

	private JRadioButtonMenuItem baseMI;
	private JRadioButtonMenuItem base6EMI;

	JLabel button;

	private JMenuItem checkAllModsMI;

	private JMenuItem applyPackageDealMI;

	private JMenuItem chooseTemplateMI;

	private JMenuItem addTemplateMI;

	private JMenuItem closeAllMI;

	private JMenuItem saveAllMI;

	private JMenuItem closeMI;

	private JMenuItem combatRecordExportMI;

	private JRadioButtonMenuItem computerMI;
	private JRadioButtonMenuItem computer6EMI;

	private JMenuItem discussionBoardMI;

	KeyEventDispatcher dispatcher;

	private JMenuItem exitMI;

	private JMenuItem exportToFileMI;

	private JMenu fileMenu;

	// private JMenuItem getUpdatesMI;

	private JMenuItem getNewExportTemplatesMI;

	private JMenuItem getNewPackageDealsMI;

	private JMenuItem getNewCharactersMI;

	private JMenuItem getNewPrefabsMI;

	private JMenuItem getNewTemplatesMI;

	private JMenuItem gotoHeroGamesMI;

	private JFileChooser hdcChooser;

	private JFileChooser hdpChooser;

	private JMenu helpMenu;

	ImageIcon icon;

	/**
	 * all prefabs go into this Vector as they are loaded. They are removed when
	 * they are unloaded
	 */
	protected ArrayList<Prefab> loadedPrefabs;

	protected JMenuItem loadPrefabMI;

	/**
	 * This is the main UI for the application. It contains the tab pane.
	 */
	public MainContentPanel mainPanel;

	private JMenuBar menu;

	private JMenuItem newFromTemplateMI;

	private JMenuItem newMI;

	private JMenu newCharacterMenu;

	private JMenuItem newPrefabMI;

	/**
	 * all open characters go into this Vector as they are loaded. They are
	 * removed during the close operation.
	 */
	protected ArrayList<Hero> openHeroes;

	private JMenuItem openMI;

	private JMenuItem openPrefabMI;

	private JRadioButtonMenuItem packageTemplateMI;

	private JMenu prefabMenu;

	/**
	 * This class stores the user's preferences for the application.
	 */
	private AppPrefs prefs;

	private JMenuItem previewCombatRecordMI;

	private JMenuItem previewExportMI;

	private JMenuItem printToPDFMI;

	private JMenu recentMI;

	private JMenuItem rulesDefaultMI;

	private JMenuItem rulesLoadMI;

	private JMenu rulesMenu;

	private JMenuItem rulesViewMI;

	private JMenuItem saveAsMI;

	private JMenuItem saveAsPrefabMI;

	private JMenuItem saveAsTemplateMI;

	protected JMenuItem saveMI;

	private JMenuItem setExportTemplateMI;

	private JMenuItem setPrefsMI;

	private JRadioButtonMenuItem standardHeroMI;
	private JRadioButtonMenuItem standardHero6EMI;

	private JRadioButtonMenuItem standardNormalMI;

	private JRadioButtonMenuItem standardSuperMI;
	private JRadioButtonMenuItem standardSuper6EMI;

	private JLabel statusLbl;

	private JProgressBar memoryUsagePB;

	private JMenuItem tearOffMI;

	private ButtonGroup templateGroup;

	protected JMenu templateMenu;

	protected JMenu standardTemplateMenu;
	protected JMenu standard6ETemplateMenu;

	protected JMenu nonStandardTemplateMenu;

	private JMenu toolsMenu;

	protected JMenu unloadPrefabMI;

	protected JMenuItem unloadAllPrefabsMI;

	private JRadioButtonMenuItem vehicleMI;
	private JRadioButtonMenuItem vehicle6EMI;

	protected JMenuItem viewDocsMI;

	protected ButtonGroup windowGroup;

	protected JMenu windowMenu;

	protected JMenu newStandardRulesMenu;
	protected JMenu newStandard6ERulesMenu;

	protected JMenu newCustomRulesMenu;

	protected JMenuItem newSuperheroicMI;
	protected JMenuItem newSuperheroic6EMI;

	protected JMenuItem newNormalMI;

	protected JMenuItem newHeroicMI;
	protected JMenuItem newHeroic6EMI;

	protected JMenuItem newComputerMI;
	protected JMenuItem newComputer6EMI;

	protected JMenuItem newAIMI;
	protected JMenuItem newAI6EMI;

	protected JMenuItem newAutomatonMI;
	protected JMenuItem newAutomaton6EMI;

	protected JMenuItem newVehicleMI;
	protected JMenuItem newVehicle6EMI;

	protected JMenuItem newBaseMI;
	protected JMenuItem newBase6EMI;

	protected JMenu saveMenu;

	protected JMenu exportMenu;

	protected JMenu characterMenu;

	protected JMenuItem saveAsPackageDealMI;

	private ArrayList<HeroDesignerEventListenerI> mListeners = new ArrayList<HeroDesignerEventListenerI>();

	public void addListener(HeroDesignerEventListenerI pListener) {
		mListeners.add(pListener);
	}

	public void removeListener(HeroDesignerEventListenerI pListener) {
		mListeners.remove(pListener);
	}

	public void fireCharacterLoaded(Hero pChar, boolean pDisplayed) {
		for (int i = mListeners.size() - 1; i >= 0; i--) {
			mListeners.get(i).characterLoaded(pChar, pDisplayed);
		}
	}

	public void fireTemplateChanged(Template pTemplate) {
		for (int i = mListeners.size() - 1; i >= 0; i--) {
			mListeners.get(i).templateChanged(pTemplate);
		}
	}

	public void fireDisplayReset() {
		for (int i = mListeners.size() - 1; i >= 0; i--) {
			mListeners.get(i).displayReset();
		}
	}

	public void fireAppPrefsChanged() {
		for (int i = mListeners.size() - 1; i >= 0; i--) {
			mListeners.get(i).preferencesChanged();
		}
	}

	public void fireTabChanged() {
		for (int i = mListeners.size() - 1; i >= 0; i--) {
			mListeners.get(i).tabChanged();
		}
	}

	public void firePrefabLoaded(Prefab pPrefab) {
		for (int i = mListeners.size() - 1; i >= 0; i--) {
			mListeners.get(i).prefabLoaded(pPrefab);
		}
	}

	public static boolean headless = false;

	private HeroDesigner(boolean headless) throws Exception {
		HeroDesigner.headless = true;
		HeroDesigner.singleton = this;
		openHeroes = new ArrayList<Hero>();
		AppPrefs p = new AppPrefs();
		p.setCheckModsDuringEdit(false);
		p.setConfirmDelete(false);
		p.setDisplayActivePoints(true);
		p.setLoadCharacters(false);
		p.setLoadPrefabs(false);
		p.setModifierIntelligenceOn(false);
		p.setNumberDigitsForRounding(2);
		p.setPromptToExportImage(false);
		p.setShowCommonLimitations(true);
		p.setWarnOnMultiple(false);
		prefs = p;
		setTemplate("builtIn.Superheroic6E.hdt", true, false, false);
		Hero holder = new Hero();
		HeroDesigner.activeHero = holder;
		prefs = p;
		p.setSources(GenericObject.getAllSources());
	}

	private HeroDesigner() {
		try {
			HeroDesigner.frame = new JFrame("HERO Designer");
		} catch (Throwable exp) {
			System.out.println("Switching to Metal LNF...");
			System.setProperty("swing.defaultlaf",
					"javax.swing.plaf.metal.MetalLookAndFeel");
			try {
				UIManager
						.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out
					.println("Metal LNF system settings in place, constructing frame...");
			HeroDesigner.frame = new JFrame("HERO Designer");
			System.out.println("Now using Metal LNF.");
		}
		HeroDesigner.singleton = this;
		prefs = new AppPrefs();
		prefs.restorePrefs();
		if (!HeroDesigner.headless) {
			try {
				Image image = Toolkit.getDefaultToolkit().createImage(
						ClassLoader.getSystemResource("about.png"));
				icon = new ImageIcon(image);
			} catch (Exception exp) {
				icon = new ImageIcon("about.png");
			}
			button = new JLabel(icon);
			button.setOpaque(false);
			HeroDesigner.loadProgress = new JProgressBar(
					JProgressBar.HORIZONTAL);
			HeroDesigner.loadProgress.setIndeterminate(true);
			HeroDesigner.loadProgress.setStringPainted(true);
			HeroDesigner.loadProgress.setBorderPainted(false);
			// loadProgress.se tBackground(Color.black);
			// loadProgress.setForeground(Color.red.darker());
			HeroDesigner.loadProgress.setMinimumSize(new Dimension(200, 20));
			HeroDesigner.loadProgress.setForeground(Color.black);
			HeroDesigner.loadProgress.setVisible(true);
			JPanel panel = new JPanel(new GridBagLayout());
			panel.setOpaque(false);
			GridBagConstraints gbc = new GridBagConstraints();
			gbc.gridx = 0;
			gbc.gridy = 0;
			gbc.weightx = 1;
			gbc.weighty = 1;
			gbc.fill = gbc.NONE;
			gbc.anchor = gbc.CENTER;
			panel.add(button, gbc);
			gbc.gridy++;
			gbc.fill = gbc.BOTH;
			panel.add(HeroDesigner.loadProgress, gbc);
			HeroDesigner.loadProgress.setString("Starting up");
			panel.setBorder(BorderFactory.createLineBorder(Color.black, 1));
			aboutWindow = new JWindow();

			aboutWindow.setContentPane(panel);
			try { // an attempt to catch errors due to the lack of Nimbus
				aboutWindow.pack();
				aboutWindow.setBackground(new Color(0f, 0f, 0f, 0.3f));
				aboutWindow.addWindowFocusListener(new WindowFocusListener() {

					public void windowGainedFocus(WindowEvent e) {
					}

					public void windowLostFocus(WindowEvent e) {
						// if (ready) aboutWindow.setVisible(false);
					}
				});
				Toolkit tool = Toolkit.getDefaultToolkit();
				Dimension screen = tool.getScreenSize();
				aboutWindow.setLocation(
						screen.width / 2 - aboutWindow.getPreferredSize().width
								/ 2,
						screen.height / 2
								- aboutWindow.getPreferredSize().height / 2);
				aboutWindow.setVisible(true);
			} catch (Exception exp) {
				System.setProperty("swing.defaultlaf",
						"javax.swing.plaf.metal.MetalLookAndFeel");
				try {
					UIManager
							.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
				} catch (Exception e) {
					e.printStackTrace();
				}
				aboutWindow.pack();
				aboutWindow.setBackground(new Color(0f, 0f, 0f, 0.3f));
				aboutWindow.addWindowFocusListener(new WindowFocusListener() {

					public void windowGainedFocus(WindowEvent e) {
					}

					public void windowLostFocus(WindowEvent e) {
						// if (ready) aboutWindow.setVisible(false);
					}
				});
				Toolkit tool = Toolkit.getDefaultToolkit();
				Dimension screen = tool.getScreenSize();
				aboutWindow.setLocation(
						screen.width / 2 - aboutWindow.getPreferredSize().width
								/ 2,
						screen.height / 2
								- aboutWindow.getPreferredSize().height / 2);
				aboutWindow.setVisible(true);
			}

			/*
			 * Security check here... This must always be in place....removing
			 * it will allow people to circumvent the security check on
			 * beta/test copies of the app by simply applying the current update
			 * for the live version.
			 */
			HeroDesigner.loadProgress.setString("Performing security check");
			HeroDesigner.frame.setIconImage(Toolkit.getDefaultToolkit()
					.createImage(ClassLoader.getSystemResource("icon.png")));
			HeroDesigner.frame.setTitle("HERO Designer 6 [build "
					+ HeroDesigner.version + "]");
			HeroDesigner.frame
					.setDefaultCloseOperation(HeroDesigner.frame.DO_NOTHING_ON_CLOSE);
		}
		openHeroes = new ArrayList<Hero>();
		loadedPrefabs = new ArrayList<Prefab>();
		initWidgets();
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress.setString("Restoring preferences");
		}

		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress.setString("Initializing components");
		}
		try {

			setTemplate("builtIn.Superheroic6E.hdt", true, false, false);
			Hero holder = new Hero();
			HeroDesigner.activeHero = holder;
			if (!HeroDesigner.headless) {
				HeroDesigner.loadProgress.setString("Constructing interface");
			}
			mainPanel = new MainContentPanel();
			try {
				Image image = Toolkit.getDefaultToolkit().createImage(
						ClassLoader.getSystemResource("logo.gif"));
				icon = new ImageIcon(image);
			} catch (Exception exp) {
				icon = new ImageIcon("logo.gif");
			}
			if (!HeroDesigner.headless) {
				JPanel content = new JPanel(new GridBagLayout());

				content.add(mainPanel, new GridBagConstraints(0, 0, 2, 1, 1, 1,
						GridBagConstraints.CENTER, GridBagConstraints.BOTH,
						new Insets(0, 0, 0, 0), 0, 0));
				statusLbl = new JLabel("");
				content.add(statusLbl, new GridBagConstraints(0, 1, 1, 1, 1, 0,
						GridBagConstraints.SOUTH,
						GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
						0, 0));
				BigDecimal mm = new BigDecimal(Runtime.getRuntime().maxMemory()
						/ (1024d * 1024d));
				mm = mm.setScale(0, mm.ROUND_HALF_UP);
				BigDecimal um = new BigDecimal((Runtime.getRuntime()
						.totalMemory() - Runtime.getRuntime().freeMemory())
						/ (1024d * 1024d));
				um = um.setScale(0, um.ROUND_HALF_UP);

				memoryUsagePB = new JProgressBar(0, mm.intValue());
				memoryUsagePB.setStringPainted(true);
				memoryUsagePB.setString(um.toString() + "M of " + um.toString()
						+ "M");
				memoryUsagePB.setValue(um.intValue());
				memoryUsagePB.setMinimumSize(new Dimension(80, memoryUsagePB
						.getMinimumSize().height));
				memoryUsagePB.setVisible(false);

				content.add(memoryUsagePB, new GridBagConstraints(1, 1, 1, 1,
						.1, 0, GridBagConstraints.SOUTHEAST,
						GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0),
						0, 0));

				HeroDesigner.frame.setContentPane(content);
				mainPanel.setVisible(false);
				statusLbl.setVisible(false);

				MemoryMonitor mon = new MemoryMonitor();
				mon.start();
			}
			prefs.finishRestorePrefs();

			if (HeroDesigner.activeHero.equals(holder)) {
				HeroDesigner.activeHero = null;
			}
		} catch (Exception exp) {
			exp.printStackTrace();
		}
		HeroDesigner.loadProgress.setString("Loading initial screens");
		initListeners();
		if (HeroDesigner.getActiveTemplate() == null) {
			if (!HeroDesigner.headless) {
				HeroDesigner.loadProgress
						.setString("Loading Superheroic template");
			}
			setTemplate("builtIn.Superheroic6E.hdt", true, false, true);
		}
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress.setString("Loading plugins...");
		}
		// plugin load
		if (!HeroDesigner.headless) {
			HeroDesigner.loadedPlugins = new ArrayList<PluginInfo>();
			File pluginDir = new File("plugins");
			if (!pluginDir.exists()) {
				pluginDir.mkdir();
			}
			File[] plugins = pluginDir.listFiles();
			PLUGINLOOP: for (int i = 0; i < plugins.length; i++) {
				File plugin = plugins[i];
				if (plugin.getName().trim().toUpperCase().endsWith("JAR")) {
					try {
						JarFile jar = new JarFile(plugin);
						PluginInfo info = new PluginInfo(jar,
								plugin.getAbsolutePath());
						if (info.getMainClass().trim().length() == 0) {
							continue;
						}
						URLClassLoader loader = new URLClassLoader(
								new URL[] { plugin.toURL() },
								ClassLoader.getSystemClassLoader());
						Class cl = loader.loadClass(info.getMainClass());
						// Object p = cl.newInstance();
						Method[] methods = cl.getMethods();
						for (int j = 0; j < methods.length; j++) {
							Method method = methods[j];
							if (method.getName().equals("main")) {
								String[] args = new String[0];
								Object[] passedArgs = new Object[] { args };
								method.invoke(null, passedArgs);
								HeroDesigner.loadedPlugins.add(info);
								logMemory("Added Plugin (" + info.getName()
										+ ")");
								continue PLUGINLOOP;
							}
						}
					} catch (Exception exp) {
						exp.printStackTrace();
						JOptionPane.showMessageDialog(
								HeroDesigner.getAppFrame(),
								"An error occurred loading the "
										+ plugin.getName()
										+ " plugin.\n\nError Message: "
										+ exp.getMessage(), "Error occurred",
								JOptionPane.ERROR_MESSAGE);
					}
				}
			}
			HeroDesigner.frame.pack();
			if (prefs.isRestoreWindow()) {
				HeroDesigner.frame.setSize(new Dimension(prefs.getSizeX(),
						prefs.getSizeY()));
				if (prefs.getScreenX() > 0 && prefs.getScreenY() > 0) {
					HeroDesigner.frame.setLocation(prefs.getScreenX(),
							prefs.getScreenY());
				} else {
					Toolkit tool = Toolkit.getDefaultToolkit();
					Dimension screen = tool.getScreenSize();
					HeroDesigner.frame.setLocation(screen.width / 2 - 400,
							screen.height / 2 - 300);
				}
			} else {
				HeroDesigner.frame.setSize(950, 650);
				Toolkit tool = Toolkit.getDefaultToolkit();
				Dimension screen = tool.getScreenSize();
				HeroDesigner.frame.setLocation(screen.width / 2 - 400,
						screen.height / 2 - 300);
			}
			HeroDesigner.frame.setVisible(true);
			if (prefs.isRestoreWindow()) {
				HeroDesigner.frame.setExtendedState(prefs.getState());
			}

			updateRecentMenu();
		}
		if (HeroDesigner.activeHero != null) {
			mainPanel.updatePrefabs();
			mainPanel.updateTabs();
		}

		HeroDesigner.loadProgress.setString("Initializing PDF subsystem...");
		refreshResourcesDirectory();
		initializePdfPrinter();
	}

	private void refreshResourcesDirectory() {
		try {
			if (!(new File("HD6.jar").exists())) { // not running from the jar
													// file...
				return;
			}
			File check = new File("resources");
			if (!check.exists()) {
				check.mkdir();
			}
			String path = check.getAbsolutePath();
			// Resources broken down by category...
			// Configuration file -- maybe leave this alone in case it's
			// been customized?
			check = new File("resources"+File.separator+"config.yml");
			if (!check.exists() || check.length() == 406) {
				writeFileFromJar("config.yml", path, 406);
			}
			// Hero System resources and graphics
			writeFileFromJar("charsheet5.pdf", path, 47120);
			writeFileFromJar("logo5.stamp", path, 36737);
			writeFileFromJar("charsheet6.pdf", path, 43818);
			writeFileFromJar("logo6.stamp", path, 9510);
			// Export formats
			check = new File("resources"+File.separator+"pdf_format_5.hde");
			if (!check.exists() || check.length() == 8898) {
				writeFileFromJar("pdf_format_5.hde", path, 8898);
			}
			check = new File("resources"+File.separator+"pdf_format_6.hde");
			if (!check.exists() || check.length() == 8806) {
				writeFileFromJar("pdf_format_6.hde", path, 8806);
			}
			// Fonts, licenses are in pdfomatic/LICENSES
			// From Apostrophic Labs:
			writeFileFromJar("komika_text_kaps.ttf", path, 65904);
			writeFileFromJar("komika_text_tight.ttf", path, 63536);
			writeFileFromJar("komika_text.ttf", path, 65672);
			// From GUST:
			writeFileFromJar("TeXGyreHerosCondensed-Bold.ttf", path, 146024);
			writeFileFromJar("TeXGyreHerosCondensed-Regular.ttf", path, 148332);
			// From Manfred Klein:
			writeFileFromJar("BorisBlackBloxx.ttf", path, 62616);
		} catch (Exception exp) {
			exp.printStackTrace();
		}
	}

	private void writeFileFromJar(String fileName, String path, int length) {
		try {
			HeroDesigner.loadProgress.setString("Checking ./resources/"
					+ fileName + " ...");
			File check = new File(path + File.separator + fileName);
			if (!check.exists() || check.length() != length) {
				if (check.exists()) {
					check.delete();
				}
				FileOutputStream fos = new FileOutputStream(check);
				InputStream is = ClassLoader
						.getSystemResourceAsStream(fileName);
				int bytes = 0;
				while (bytes < length) {
					byte[] data = new byte[length];
					int read = is.read(data);
					fos.write(data, 0, read);
					bytes += read;
				}
				is.close();
				fos.close();
			}
		} catch (Exception exp) {
			exp.printStackTrace();
		}
	}

	/**
	 * Clears the contents of the internal copy buffer.
	 */
	public static void clearCopyBuffer() {
		HeroDesigner.copyBuffer = null;
	}

	/**
	 * @return the currently active character/prefab
	 */
	public static Hero getActiveHero() {
		return HeroDesigner.activeHero;
	}

	/**
	 * @return the currently active template
	 */
	public static Template getActiveTemplate() {
		return HeroDesigner.activeTemplate;
	}

	/**
	 * If the copy buffer has been filled, its contents are cloned and their ids
	 * are changed. This is necessary to maintain integrity of the id structure
	 * within a given character.
	 * 
	 * @return the contents of the copy buffer -- may be null
	 */
	public static GenericObject getCopyBuffer() {
		GenericObject ret = null;
		if (HeroDesigner.copyBuffer != null) {
			try {
				ret = HeroDesigner.copyBuffer.clone();
				ret.setParent(null);
				ret.resetID();
				if (ret instanceof com.hero.objects.List) {
					ArrayList<GenericObject> vec = ((com.hero.objects.List) ret)
							.getObjects();
					ArrayList<GenericObject> newVec = new ArrayList<GenericObject>();
					for (GenericObject o : vec) {
						o = o.clone();
						o.resetID();
						o.setParent((com.hero.objects.List) ret);
						newVec.add(o);
					}
					((com.hero.objects.List) ret).setObjects(newVec);
				}
			} catch (Exception exp) {
			}
		}
		return ret;
	}

	/**
	 * singleton accessor to the class (constructor is private)
	 * 
	 * @return reference to the singleton instance of HeroDesigner (creating one
	 *         if necessary).
	 */
	public static HeroDesigner getInstance() {
		if (HeroDesigner.singleton == null) {
			new HeroDesigner();
		}
		return HeroDesigner.singleton;
	}

	public static HeroDesigner getInstance(boolean headless) throws Exception {
		if (HeroDesigner.singleton == null) {
			System.out.println("Trying headless startup");
			new HeroDesigner(true);
		}
		return HeroDesigner.singleton;
	}

	/**
	 * main startup method for the application. Handles default settings,
	 * reading in command-line parameters, etc.
	 * 
	 * @param args
	 *            the name(s) of one or more character files may be passed in at
	 *            the command line and opened by the application.
	 */
	public static void main(String[] args) {
		HeroDesigner.macOSX = System.getProperty("os.name").toUpperCase()
				.indexOf("MAC") >= 0;

		

		try {
			FileOutputStream fos = new FileOutputStream("trace.log");
			File check = new File("HD6.jar");
			PrintStream ps = new PrintStream(fos);
			if (check.exists()) {
				System.setErr(ps);
			}
			if (check.exists()) {
				System.setOut(ps);
			}
			System.out.println("HERO Designer 6 log file (build "
					+ HeroDesigner.version + ")");
			try {
				
				if (args.length > 0) {
					System.out.println("args[0] = "+args[0]);
					fontSize = Integer.parseInt(args[0]);
				}
				System.setProperty("swing.defaultlaf",
						"com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
				UIManager.setLookAndFeel(new NimbusLookAndFeel() {
					@Override
					public UIDefaults getDefaults() {
						UIDefaults ret = super.getDefaults();
						Font def = (Font)super.getDefaults().get("defaultFont");
						ret.put("defaultFont", def.deriveFont((float)fontSize)); 
						return ret;
					}
				});
				JFrame.setDefaultLookAndFeelDecorated(true);

			} catch (Exception exp) {
				System.setProperty("swing.defaultlaf",
						"javax.swing.plaf.metal.MetalLookAndFeel");
			}
			System.out.println("Current system properties:  ");
			Properties props = System.getProperties();
			Iterator iter = props.keySet().iterator();
			while (iter.hasNext()) {
				String key = iter.next().toString();
				System.out.println("     " + key + " = "
						+ System.getProperty(key));
			}
		} catch (Throwable e) {
			// do nothing
		}
		HeroDesigner designer = HeroDesigner.getInstance();

		try {
			OUTER: for (int i = 0; i < args.length; i++) {
				String s = args[i];
				File test = new File(s);
				if (test.exists()
						&& (test.getName().trim().toUpperCase()
								.endsWith(".HDC")
								|| test.getName().trim().toUpperCase()
										.endsWith(".HDZ")
								|| test.getName().trim().toUpperCase()
										.endsWith(".HDT") || test.getName()
								.trim().toUpperCase().endsWith(".HDP"))) {
					for (Hero h : designer.openHeroes) {
						if (h.getSaveFile() != null
								&& h.getSaveFile()
										.getAbsolutePath()
										.toUpperCase()
										.equals(test.getAbsolutePath()
												.toUpperCase())) {
							if (i == args.length - 1) {
								designer.setActiveHero(h);
							}
							continue OUTER;
						}
					}
					designer.openHero(test, i == args.length - 1);
				}
			}
		} catch (Exception exp) {
			exp.printStackTrace();
			JOptionPane
					.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred in the application.\nPlease restart HERO Designer.\n\nError Message: "
									+ exp.getMessage(), "Error occurred",
							JOptionPane.ERROR_MESSAGE);
		}
	}

	/**
	 * simple utility class to convert old (v1) template names to the new
	 * format.
	 * 
	 * @param old
	 *            the v1 template name
	 * @return the v2 template name
	 */
	private String convertOldNames(String old) {
		if (old.indexOf("Normal") >= 0) {
			return "builtIn.Normal.hdt";
		} else if (old.indexOf("Super") >= 0) {
			return "builtIn.Superheroic.hdt";
		} else if (old.indexOf("Hero") >= 0) {
			return "builtIn.Heroic.hdt";
		} else {
			return old;
		}
	}

	/**
	 * Sets up all listeners on objects contained by this class.
	 */
	private void initListeners() {
		tearOffMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				mainPanel.tearOff();
			}
		});
		rulesViewMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				RulesDialog dialog = new RulesDialog();
				dialog.setLocationRelativeTo(HeroDesigner.frame);
				dialog.setVisible(true);
				if (!dialog.cancelButtonClicked()) {
					HeroDesigner.getActiveHero().setDirty(true);
					mainPanel.reset();
					updateStatus();
				}
			}
		});
		rulesDefaultMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				prefs.setLastRule("");
				HeroDesigner.getActiveHero().getRules().useDefault();

				Hero active = HeroDesigner.getActiveHero();
				active.setBasePoints(active.getRules().getBasePoints());
				active.setDisadPoints(active.getRules().getDisadPoints());
				active.setNCMSelected(active.getRules().isNCMSelected());
				mainPanel.reset();
				updateStatus();
			}
		});
		rulesLoadMI.addActionListener(new ActionListener() {

			JFileChooser chooser;

			public void actionPerformed(ActionEvent e) {
				File openFile = null;
				while (openFile == null) {
					if (chooser == null) {
						chooser = new JFileChooser();
						chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HDR")) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								return "HERO Designer Campaign Rules";
							}
						});
						chooser.setApproveButtonText("Load Campaign Rules");
						chooser.setDialogTitle("Load Campaign Rules");
						chooser.setFileSelectionMode(chooser.FILES_ONLY);
						chooser.setMultiSelectionEnabled(false);
						chooser.setAcceptAllFileFilterUsed(false);
						chooser.setCurrentDirectory(new File(getPrefs()
								.getRulesDir()));
					}
					int ret = chooser.showOpenDialog(HeroDesigner.frame);
					if (ret == chooser.CANCEL_OPTION) {
						return;
					} else if (ret == chooser.APPROVE_OPTION) {
						openFile = chooser.getSelectedFile();
						if (!openFile.exists()) {
							openFile = null;
						} else {
							prefs.setRulesDir(openFile.getParent());
							prefs.setLastRule(openFile.getAbsolutePath());
						}
					}
				}
				try {
					HeroDesigner.frame.setCursor(Cursor
							.getPredefinedCursor(Cursor.WAIT_CURSOR));
					HeroDesigner.getActiveHero().getRules()
							.loadRulesDefinition(openFile.getAbsolutePath());

					Hero active = HeroDesigner.getActiveHero();
					active.setBasePoints(active.getRules().getBasePoints());
					active.setDisadPoints(active.getRules().getDisadPoints());
					active.setNCMSelected(active.getRules().isNCMSelected());
					mainPanel.reset();
					updateStatus();
				} catch (Exception exp) {
					exp.printStackTrace();
					if (!HeroDesigner.headless) {
						JOptionPane.showMessageDialog(
								HeroDesigner.getAppFrame(),
								"An error occurred loading the selected rules file.\nPlease restart HERO Designer and try again.\n\nError Message: "
										+ exp.getMessage(),
								"Error loading campaign rules",
								JOptionPane.ERROR_MESSAGE);
					}
				} finally {
					if (!HeroDesigner.headless) {
						HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
					}
				}
			}
		});
		getNewPrefabsMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				BrowserControl
						.displayURL("http://www.herogames.com/forums/files/category/5-prefabs/");
			}
		});
		getNewTemplatesMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				BrowserControl
						.displayURL("http://www.herogames.com/forums/files/category/4-templates/");
			}
		});
		getNewExportTemplatesMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				BrowserControl
						.displayURL("http://www.herogames.com/forums/files/category/2-export-formats/");
			}
		});
		getNewPackageDealsMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				BrowserControl
						.displayURL("http://www.herogames.com/forums/files/category/1-hero-designer/");
			}
		});
		// getUpdatesMI.addActionListener(new ActionListener() {
		// public void actionPerformed(ActionEvent e) {
		// BrowserControl
		// .displayURL("http://www.herogames.com/hdUpdates.htm");
		// }
		// });
		getNewCharactersMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				BrowserControl
						.displayURL("http://www.herogames.com/forums/files/category/3-characters/");
			}
		});
		gotoHeroGamesMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				BrowserControl.displayURL("http://www.herogames.com");
			}
		});
		discussionBoardMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				BrowserControl
						.displayURL("http://www.herogames.com/forums/forum/14-hero-designer/");
			}
		});
		HeroDesigner.frame.addWindowListener(new WindowListener() {

			public void windowActivated(WindowEvent e) {
				// if (aboutWindow != null) aboutWindow.dispose();
			}

			public void windowClosed(WindowEvent e) {
				if (aboutWindow != null) {
					aboutWindow.dispose();
				}
			}

			public void windowClosing(WindowEvent e) {
				exitMI.doClick();
			}

			public void windowDeactivated(WindowEvent e) {
				// if (aboutWindow != null) aboutWindow.dispose();
			}

			public void windowDeiconified(WindowEvent e) {
				if (aboutWindow != null) {
					aboutWindow.dispose();
				}
			}

			public void windowIconified(WindowEvent e) {
				if (aboutWindow != null) {
					aboutWindow.dispose();
				}
			}

			public void windowOpened(WindowEvent e) {
				if (aboutWindow != null) {
					aboutWindow.dispose();
				}
			}
		});
		HeroDesigner.frame.addFocusListener(new FocusListener() {

			public void focusGained(FocusEvent e) {
				// if (aboutWindow != null) aboutWindow.dispose();
			}

			public void focusLost(FocusEvent e) {
				// if (aboutWindow != null) aboutWindow.dispose();
			}
		});
		setExportTemplateMI.addActionListener(new ActionListener() {

			ExportTemplateDialog dialog = null;

			public void actionPerformed(ActionEvent e) {
				dialog = new ExportTemplateDialog();
				dialog.showOpenDialog(HeroDesigner.frame);
				if (dialog.canceled()) {
					return;
				} else {
					String path = dialog.getSelectedPath();
					if (path != null && path.trim().length() > 0) {
						HeroDesigner.getActiveHero().setExportTemplate(path);
						prefs.setExportTemplateDir(new File(path).getParent());
						setDirty(true);
					}
				}
			}
		});
		exportToFileMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able\n to export your character to any number of different\n formats (HTML, plain text, RTF, etc.), view it in your \nsystem's native viewer, and (if desired) print out\n the resulting character sheet.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				if (HeroDesigner.getActiveHero().hasFileAssociations()) {
					int check = JOptionPane
							.showConfirmDialog(
									HeroDesigner.frame,
									"Would you like to export the character files that have been associated with the current character?",
									"Export Associated Files?",
									JOptionPane.YES_NO_CANCEL_OPTION);
					if (check == JOptionPane.CANCEL_OPTION) {
						return;
					} else if (check == JOptionPane.NO_OPTION) {
						exportActiveCharacter();
						return;
					} else {
						ArrayList<File> files = HeroDesigner.getActiveHero()
								.getFileAssociations();
						exportActiveCharacter();
						for (File f : files) {
							openHero(f, true);
							exportActiveCharacter();
						}
					}
				} else {
					exportActiveCharacter();
				}
			}
		});
		previewCombatRecordMI.addActionListener(new ActionListener() {

			ArrayList<String> exts;

			CombatRecordDialog selector;

			public void actionPerformed(ActionEvent e) {
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able \nto export your character to any number of different \nformats (HTML, plain text, RTF, etc.), view it in your \nsystem's native viewer, and (if desired) print out \nthe resulting character sheet.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				selector = new CombatRecordDialog((ArrayList<Hero>) openHeroes
						.clone());
				selector.setLocationRelativeTo(HeroDesigner.frame);
				selector.pack();
				selector.setVisible(true);
				if (selector.cancelButtonClicked()) {
					return;
				}
				File saveFile = null;
				ExportTemplateDialog dialog = null;
				String exportTemplate = null;
				while (exportTemplate == null
						|| exportTemplate.trim().length() == 0) {
					if (dialog == null) {
						dialog = new ExportTemplateDialog(".HCR");
					}
					dialog.showOpenDialog(HeroDesigner.frame);
					if (dialog.canceled()) {
						return;
					} else {
						String path = dialog.getSelectedPath();
						if (path != null && path.trim().length() > 0) {
							File test = new File(path);
							if (test.exists()) {
								exportTemplate = path;
								prefs.setExportTemplateDir(test.getParent());
							}
						}
					}
				}
				while (saveFile == null) {
					try {
						HTMLWriter writer = new HTMLWriter(new File(
								exportTemplate));
						exts = writer.getFileExtensions();
						String ext = exts.get(0);
						saveFile = File.createTempFile(
								"" + System.currentTimeMillis(), "." + ext,
								new File(prefs.getExportTemplateDir()));
						saveFile.deleteOnExit();
						if (!saveFile.getParentFile().exists()) {
							saveFile.getParentFile().mkdirs();
						}
					} catch (Exception exp) {
					}
				}
				try {
					HeroDesigner.frame.setCursor(Cursor
							.getPredefinedCursor(Cursor.WAIT_CURSOR));
					HTMLWriter writer = new HTMLWriter(new File(exportTemplate));
					String html = writer.generateCombatRecord(selector
							.getCharacters());
					FileOutputStream fos = new FileOutputStream(saveFile);
					fos.write(html.getBytes());
					fos.flush();
					fos.close();
					String urlString = "file://" + saveFile.getAbsolutePath();
					urlString = urlString.replace('\\', '/');
					BrowserControl.displayURL(urlString);
				} catch (Exception ex) {
					ex.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred during the combat record export.\nPlease try again.\n\nError Message: "
									+ ex.getMessage(),
							"Error exporting combat record",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		combatRecordExportMI.addActionListener(new ActionListener() {

			JFileChooser chooser;

			ArrayList<String> exts;

			CombatRecordDialog selector;

			public void actionPerformed(ActionEvent e) {
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able \nto export your character to any number of different \nformats (HTML, plain text, RTF, etc.), view it in your \nsystem's native viewer, and (if desired) print out \nthe resulting character sheet.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				selector = new CombatRecordDialog((ArrayList<Hero>) openHeroes
						.clone());
				selector.setLocationRelativeTo(HeroDesigner.frame);
				selector.pack();
				selector.setVisible(true);
				if (selector.cancelButtonClicked()) {
					return;
				}
				File saveFile = null;
				ExportTemplateDialog dialog = null;
				String exportTemplate = null;
				while (exportTemplate == null
						|| exportTemplate.trim().length() == 0) {
					if (dialog == null) {
						dialog = new ExportTemplateDialog(".HCR");
					}
					dialog.showOpenDialog(HeroDesigner.frame);
					if (dialog.canceled()) {
						return;
					} else {
						String path = dialog.getSelectedPath();
						if (path != null && path.trim().length() > 0) {
							File test = new File(path);
							if (test.exists()) {
								exportTemplate = path;
								prefs.setExportTemplateDir(test.getParent());
							}
						}
					}
				}
				while (saveFile == null) {
					try {
						chooser = new JFileChooser();
						HTMLWriter writer = new HTMLWriter(new File(
								exportTemplate));
						exts = writer.getFileExtensions();
						chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (exts == null || exts.size() == 0) {
									return true;
								}
								String name = file.getName();
								String ex = name.substring(
										name.lastIndexOf(".") + 1,
										name.length());
								if (exts.contains(ex.trim().toUpperCase())) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								String des = "";
								if (exts == null || exts.size() == 0) {
									return "All Files";
								} else {
									for (String s : exts) {
										if (des.length() > 0) {
											des += ", ";
										}
										des += "*." + s;
									}
								}
								return des;
							}
						});
						chooser.setApproveButtonText("Save");
						chooser.setDialogTitle("Save");
						chooser.setFileSelectionMode(chooser.FILES_ONLY);
						chooser.setMultiSelectionEnabled(false);
						chooser.setAcceptAllFileFilterUsed(false);
						if (prefs.getExportDir() != null
								&& prefs.getExportDir().trim().length() > 0) {
							try {
								chooser.setCurrentDirectory(new File(prefs
										.getExportDir()));
							} catch (Exception ex) {
							}
						}
						int ret = chooser.showSaveDialog(HeroDesigner.frame);
						if (ret == chooser.CANCEL_OPTION) {
							return;
						} else if (ret == chooser.APPROVE_OPTION) {
							saveFile = chooser.getSelectedFile();
							prefs.setExportDir(saveFile.getParent());
							String name = saveFile.getName();
							String ext = ".txt";
							if (exts != null && exts.size() > 0) {
								ext = "." + exts.get(0);
							}
							if (name.indexOf(".") < 0) {
								name = name + ext;
							}
							if (!name.equals(saveFile.getName())) {
								String path = "";
								if (saveFile.getParentFile() != null) {
									path = saveFile.getParent();
									if (!path.endsWith(saveFile.separator)) {
										path = path + saveFile.separator;
									}
								}
								saveFile = new File(path + name);
							}
							if (saveFile.exists()) {
								int response = JOptionPane.showConfirmDialog(
										HeroDesigner.frame,
										saveFile.getName()
												+ " already exists.\n\nOverwrite?");
								if (response == JOptionPane.CANCEL_OPTION) {
									return;
								} else if (response == JOptionPane.NO_OPTION) {
									saveFile = null;
								}
							}
						}
					} catch (Exception exp) {
					}
				}
				try {
					HeroDesigner.frame.setCursor(Cursor
							.getPredefinedCursor(Cursor.WAIT_CURSOR));
					HTMLWriter writer = new HTMLWriter(new File(exportTemplate));
					String html = writer.generateCombatRecord(selector
							.getCharacters());
					FileOutputStream fos = new FileOutputStream(saveFile);
					fos.write(html.getBytes());
					fos.flush();
					fos.close();
				} catch (Exception ex) {
					ex.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred during the combat record export.\nPlease try again.\n\nError Message: "
									+ ex.getMessage(),
							"Error exporting combat record",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		printToPDFMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able\n to preview your character in any number of different\n formats (HTML, plain text, RTF, etc.), view it in your \nsystem's native viewer, and (if desired) print out\n the resulting character sheet.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				try {
					HeroDesigner.frame.setCursor(Cursor
							.getPredefinedCursor(Cursor.WAIT_CURSOR));
					String format;
					if (getActiveTemplate().is6E()) {
						format = "pdf_format_6.hde";
					} else {
						format = "pdf_format_5.hde";
					}
					HTMLWriter writer = new HTMLWriter(new File("resources"
							+ File.separator + format));
					String ext = "";
					if (writer.getFileExtensions().size() > 0) {
						ext = "." + writer.getFileExtensions().get(0);
					} else {
						ext = ".txt";
					}
					File saveFile = File.createTempFile("pdf", ext);
					try {
						saveFile.deleteOnExit();
					} catch (Throwable ex) {
						ex.printStackTrace(System.out);
					}
					File imageSaveFile = null;
					if (HeroDesigner.getActiveHero().getImageData() != null
							&& HeroDesigner.getActiveHero().getImageData().length > 0) {
						String imgExt = HeroDesigner
								.getActiveHero()
								.getImageFileName()
								.substring(
										HeroDesigner.getActiveHero()
												.getImageFileName()
												.lastIndexOf("."),
										HeroDesigner.getActiveHero()
												.getImageFileName().length());
						imageSaveFile = File.createTempFile("img", imgExt);
						try {
							imageSaveFile.deleteOnExit();
						} catch (Throwable ex) {
							ex.printStackTrace(System.out);
						}
					}
					String html = writer
							.generateOutput(imageSaveFile == null ? ""
									: imageSaveFile.getName());
					FileOutputStream fos = new FileOutputStream(saveFile);
					fos.write(html.getBytes(Charset.forName("UTF-8")));
					fos.flush();
					fos.close();
					if (imageSaveFile != null) {
						// copy the image file...
						FileOutputStream out = new FileOutputStream(
								imageSaveFile);
						out.write(HeroDesigner.getActiveHero().getImageData());
						out.flush();
						out.close();
					}
					File pdf = null;
					while (pdf == null) {
						try {
							JFileChooser chooser = new JFileChooser();
							chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {
								public boolean accept(File file) {
									if (file.isDirectory()
											&& !file.getName().toUpperCase()
													.endsWith(".PKG")
											&& !file.getName().toUpperCase()
													.endsWith(".APP")) {
										return true;
									}
									String name = file.getName();
									String ex = name.substring(
											name.lastIndexOf(".") + 1,
											name.length());
									if (ex.trim().toUpperCase().equals("PDF")) {
										return true;
									} else {
										return false;
									}
								}

								public String getDescription() {
									return "PDF Files";
								}
							});
							chooser.setApproveButtonText("Save");
							chooser.setDialogTitle("Save");
							chooser.setFileSelectionMode(chooser.FILES_ONLY);
							chooser.setMultiSelectionEnabled(false);
							chooser.setAcceptAllFileFilterUsed(false);
							if (prefs.getExportDir() != null
									&& prefs.getExportDir().trim().length() > 0) {
								try {
									chooser.setCurrentDirectory(new File(prefs
											.getExportDir()));
								} catch (Exception ex) {
								}
							}
							int ret = chooser
									.showSaveDialog(HeroDesigner.frame);
							if (ret == chooser.CANCEL_OPTION) {
								return;
							} else if (ret == chooser.APPROVE_OPTION) {
								pdf = chooser.getSelectedFile();
								// prefs.setExportDir(saveFile.getParent());
								String name = pdf.getName();
								String ext2 = ".pdf";
								if (name.indexOf(".") < 0) {
									name = name + ext2;
								}
								if (!name.equals(pdf.getName())) {
									String path = "";
									if (pdf.getParentFile() != null) {
										path = pdf.getParent();
										if (!path.endsWith(pdf.separator)) {
											path = path + pdf.separator;
										}
									}
									pdf = new File(path + name);
								}
								if (pdf.exists()) {
									int response = JOptionPane.showConfirmDialog(
											HeroDesigner.frame,
											pdf.getName()
													+ " already exists.\n\nOverwrite?");
									if (response == JOptionPane.CANCEL_OPTION) {
										return;
									} else if (response == JOptionPane.NO_OPTION) {
										pdf = null;
									}
								}
							}
						} catch (Exception exp) {
						}
					}
					if (getActiveTemplate().is6E()) {
						getPdfOMatic().characterSixth(
								saveFile.getAbsolutePath(),
								pdf.getAbsolutePath());
					} else {
						getPdfOMatic().characterFifth(
								saveFile.getAbsolutePath(),
								pdf.getAbsolutePath());
					}
					saveFile.delete();
					if (imageSaveFile != null && imageSaveFile.exists())
						imageSaveFile.delete();
					BrowserControl.displayURL("file://" + pdf.getAbsolutePath());
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				} catch (Exception ex) {
					ex.printStackTrace();
					JOptionPane.showMessageDialog(HeroDesigner.getAppFrame(),
							"An error occurred during print process.\nPlease try again.\n\nError Message: "
									+ ex.getMessage(), "Error printing to PDF",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		previewExportMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able\n to preview your character in any number of different\n formats (HTML, plain text, RTF, etc.), view it in your \nsystem's native viewer, and (if desired) print out\n the resulting character sheet.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				File saveFile = null;
				File imageSaveFile = null;
				ExportTemplateDialog dialog = null;
				if (HeroDesigner.getActiveHero().getExportTemplate() == null) {
					setDefaultExportFormat(HeroDesigner.getActiveHero());
				}
				if (HeroDesigner.getActiveHero().getExportTemplate() != null) {
					File test = new File(HeroDesigner.getActiveHero()
							.getExportTemplate());
					if (!test.exists() || test.isDirectory()) {
						HeroDesigner.getActiveHero().setExportTemplate(null);
					}
				}
				while (HeroDesigner.getActiveHero().getExportTemplate() == null
						|| HeroDesigner.getActiveHero().getExportTemplate()
								.trim().length() == 0) {
					if (dialog == null) {
						dialog = new ExportTemplateDialog();
					}
					dialog.showOpenDialog(HeroDesigner.frame);
					if (dialog.canceled()) {
						return;
					} else {
						String path = dialog.getSelectedPath();
						if (path != null && path.trim().length() > 0) {
							HeroDesigner.getActiveHero()
									.setExportTemplate(path);
							prefs.setExportTemplateDir(new File(path)
									.getParent());
						}
					}
				}
				try {
					HeroDesigner.frame.setCursor(Cursor
							.getPredefinedCursor(Cursor.WAIT_CURSOR));
					HTMLWriter writer = new HTMLWriter(new File(HeroDesigner
							.getActiveHero().getExportTemplate()));
					String save = prefs.getExportTemplateDir() + File.separator;
					save += System.currentTimeMillis();
					String ext = "";
					if (writer.getFileExtensions().size() > 0) {
						ext = "." + writer.getFileExtensions().get(0);
					} else {
						ext = ".txt";
					}
					save += ext;
					saveFile = File.createTempFile(
							"" + System.currentTimeMillis(), ext, new File(
									prefs.getExportTemplateDir()));
					try {
						saveFile.deleteOnExit();
					} catch (Throwable ex) {
						ex.printStackTrace(System.out);
					}
					if (HeroDesigner.getActiveHero().getImageData() != null
							&& HeroDesigner.getActiveHero().getImageData().length > 0) {
						save = prefs.getExportTemplateDir() + File.separator;
						String unique = "" + System.currentTimeMillis();
						save += unique;
						String imgExt = "";
						imgExt = HeroDesigner
								.getActiveHero()
								.getImageFileName()
								.substring(
										HeroDesigner.getActiveHero()
												.getImageFileName()
												.lastIndexOf("."),
										HeroDesigner.getActiveHero()
												.getImageFileName().length());
						save += imgExt;
						imageSaveFile = File.createTempFile(unique, imgExt,
								new File(prefs.getExportTemplateDir()));
						try {
							imageSaveFile.deleteOnExit();
						} catch (Throwable ex) {
							ex.printStackTrace(System.out);
						}
					}
					String html = writer
							.generateOutput(imageSaveFile == null ? ""
									: imageSaveFile.getName());
					FileOutputStream fos = new FileOutputStream(saveFile);
					fos.write(html.getBytes());
					fos.flush();
					fos.close();
					if (imageSaveFile != null) {
						// copy the image file...
						FileOutputStream out = new FileOutputStream(
								imageSaveFile);
						out.write(HeroDesigner.getActiveHero().getImageData());
						out.flush();
						out.close();
					}
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
					String urlString = saveFile.getAbsolutePath();
					urlString = urlString.replace('\\', '/');
					urlString = "file://" + urlString;
					BrowserControl.displayURL(urlString);
				} catch (Exception ex) {
					ex.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred during export preview.\nPlease try again.\n\nError Message: "
									+ ex.getMessage(),
							"Error exporting to file",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		setPrefsMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				for (Hero h : openHeroes) {
					if (h.isDirty()) {
						setActiveHero(h);
					}
					INNER: while (h.isDirty() && !AutoUpdater.isDemo()) {
						String name = h.getCharacterName();
						if (name.trim().length() == 0) {
							name = "[unknown]";
						}
						int response = JOptionPane
								.showConfirmDialog(
										HeroDesigner.frame,
										name
												+ " has been changed!\n\nSave changes before editing the application preferences (changes will be lost, otherwise)?");
						if (response == JOptionPane.CANCEL_OPTION) {
							HeroDesigner.frame.setCursor(Cursor
									.getDefaultCursor());
							return;
						} else if (response == JOptionPane.YES_OPTION) {
							if (h instanceof Prefab) {
								saveAsHDC();
							} else {
								saveAsHDC(h);
							}
						} else {
							break INNER;
						}
					}
				}

				AppPrefsDialog dialog = new AppPrefsDialog();
				if (dialog.getSize().getHeight() > HeroDesigner.frame.getSize()
						.getHeight()
						|| dialog.getSize().getWidth() > HeroDesigner.frame
								.getSize().getWidth()) {
					dialog.setSize(HeroDesigner.frame.getSize());
					dialog.setLocationRelativeTo(HeroDesigner.frame);
				}

				dialog.setVisible(true);
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				String orig = HeroDesigner.getActiveTemplate().getId();
				HeroDesigner.activeTemplate = null;
				setTemplate(orig, orig.startsWith("builtIn"), false, false);

				Hero active = HeroDesigner.getActiveHero();

				for (Hero h : openHeroes) {
					setActiveHero(h);
					h.resetTemplate();
					h.setDirty(true);
				}
				for (Prefab p : loadedPrefabs) {
					p.resetTemplate();
				}
				if (active != null) {
					HeroDesigner.activeHero = null;
					setActiveHero(active);
				}
				setActiveCharacterMenu();
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
				fireAppPrefsChanged();
			}
		});

		exitMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				if (AutoUpdater.isDemo()) {
					prefs.savePrefs();
					for (int i = mListeners.size() - 1; i >= 0; i--) {
						mListeners.get(i).applicationShutdown();
					}

					System.exit(0);
				}
				for (Hero h : openHeroes) {
					if (h.isDirty()) {
						setActiveHero(h);
					}
					INNER: while (h.isDirty() && !AutoUpdater.isDemo()) {
						String name = h.getCharacterName();
						if (name.trim().length() == 0) {
							name = "[unknown]";
						}
						int response = JOptionPane
								.showConfirmDialog(
										HeroDesigner.frame,
										name
												+ " has been changed!\n\nSave changes before closing?");
						if (response == JOptionPane.CANCEL_OPTION) {
							HeroDesigner.frame.setCursor(Cursor
									.getDefaultCursor());
							return;
						} else if (response == JOptionPane.YES_OPTION) {
							if (h instanceof Prefab) {
								saveAsHDC();
							} else {
								saveAsHDC(h);
							}
						} else {
							break INNER;
						}
					}
				}
				mainPanel.hideFrames();
				prefs.savePrefs();
				System.exit(0);
			}
		});
		helpMenu.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				File docsFile = new File(HeroDesigner.docsFilePath);
				if (!docsFile.exists()) {
					viewDocsMI.setEnabled(false);
				} else {
					viewDocsMI.setEnabled(true);
				}
			}
		});
		viewDocsMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				File docsFile = new File(HeroDesigner.docsFilePath);
				if (!docsFile.exists()) {
					viewDocsMI.setEnabled(false);
					return;
				}
				String urlString = "file://" + docsFile.getAbsolutePath();
				urlString = urlString.replace('\\', '/');
				BrowserControl.displayURL(urlString);
			}
		});
		applyPackageDealMI.addActionListener(new ActionListener() {
			JFileChooser chooser;

			public void actionPerformed(ActionEvent e) {
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able to apply package deals to your character using this function.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				File[] openFiles = null;
				while (openFiles == null || openFiles.length == 0) {
					if (chooser == null) {
						chooser = new JFileChooser();
						chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HPK")) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								return "HERO Designer Package Deal Files";
							}
						});
						chooser.setApproveButtonText("Apply Package Deal");
						chooser.setDialogTitle("Apply Package Deal");
						chooser.setFileSelectionMode(chooser.FILES_ONLY);
						chooser.setMultiSelectionEnabled(true);
						chooser.setAcceptAllFileFilterUsed(false);
						if (prefs.getSaveDir() != null
								&& prefs.getSaveDir().trim().length() > 0) {
							try {
								chooser.setCurrentDirectory(new File(prefs
										.getSaveDir()));
							} catch (Exception ex) {
							}
						}
					}
					int ret = chooser.showOpenDialog(HeroDesigner.frame);
					if (ret == chooser.CANCEL_OPTION) {
						return;
					} else if (ret == chooser.APPROVE_OPTION) {
						openFiles = chooser.getSelectedFiles();
						if (!openFiles[0].exists()) {
							openFiles = null;
						}
					}
				}
				if (openFiles == null || openFiles.length == 0) {
					return;
				}
				for (int counter = 0; counter < openFiles.length; counter++) {
					if (openFiles[counter].isDirectory()) {
						continue;
					}
					HeroDesigner.frame.setCursor(Cursor
							.getPredefinedCursor(Cursor.WAIT_CURSOR));
					File file = openFiles[counter];
					if (file != null && file.exists() && file.canRead()) {
						try {
							SAXBuilder builder = new SAXBuilder(false);
							Document doc = builder.build(file);
							builder = null;
							Element root = doc.getRootElement();
							Hero pack = new Hero(root);
							HeroDesigner.getActiveHero().applyPackageDeal(pack);
						} catch (Exception exp) {
							exp.printStackTrace();
						}
					}
				}
				mainPanel.updateTabs();
				mainPanel.updatePrefabs();
				HeroDesigner.getActiveHero().setDirty(true);
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		});
		checkAllModsMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				if (HeroDesigner.activeHero == null) {
					return;
				}
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				boolean orig = prefs.isModifierIntelligenceOn();
				prefs.setModifierIntelligenceOn(true);
				int badCount = 0;
				for (GenericObject obj : HeroDesigner.activeHero.getSkills()) {
					boolean check = obj.verifyModifiers();
					if (!check) {
						badCount++;
					}
				}
				for (GenericObject obj : HeroDesigner.activeHero.getPerks()) {
					boolean check = obj.verifyModifiers();
					if (!check) {
						badCount++;
					}
				}
				for (GenericObject obj : HeroDesigner.activeHero.getTalents()) {
					boolean check = obj.verifyModifiers();
					if (!check) {
						badCount++;
					}
				}
				for (GenericObject obj : HeroDesigner.activeHero.getManeuvers()) {
					boolean check = obj.verifyModifiers();
					if (!check) {
						badCount++;
					}
				}
				for (GenericObject obj : HeroDesigner.activeHero.getPowers()) {
					boolean check = obj.verifyModifiers();
					if (!check) {
						badCount++;
					}
				}
				for (GenericObject obj : HeroDesigner.activeHero.getEquipment()) {
					boolean check = obj.verifyModifiers();
					if (!check) {
						badCount++;
					}
				}
				prefs.setModifierIntelligenceOn(orig);
				JOptionPane.showMessageDialog(HeroDesigner.getAppFrame(),
						"Modifier validation complete!\n\nErrors found: "
								+ badCount, "Modifier Intelligence Check",
						JOptionPane.INFORMATION_MESSAGE);
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
			}
		});
		aboutMI.addActionListener(new ActionListener() {

			JDialog dialog;

			public void actionPerformed(ActionEvent e) {
				try {
					String credits = "<html><body>"
							+ "<table border=0 cellpadding=0 cellspacing=0>"
							+ "<tr><td align=\"left\"><b><font size=\"8\" face=\"arial, helvetica, sans-serif\">HERO Designer 6</font></b></td></tr>"
							+ "<tr><td align=\"right\"><b><font face=\"arial, helvetica, sans-serif\">Build "
							+ HeroDesigner.version
							+ "</font></b></td></tr>"
							+ "</table><br>"
							+ "<table border=0 cellpadding=2 cellspacing=0>"
							+ "<tr><td align=\"right\"><font face=\"arial, helvetica, sans-serif\"><b>Created By:</b></td><td><font face=\"arial, helvetica, sans-serif\">Dan Simon</font></td></tr>"
							+ "<tr><td align=\"right\"><font face=\"arial, helvetica, sans-serif\"><b>Concept:</b></td><td><font face=\"arial, helvetica, sans-serif\">Steven S. Long and Dan Simon</font></td></tr>"
							+ "<tr><td align=\"right\"><font face=\"arial, helvetica, sans-serif\"><b>PDF Generation:</b></td><td><font face=\"arial, helvetica, sans-serif\">B Neumeier</font></td></tr>";
					if (HeroDesigner.loadedPlugins.size() > 0) {
						credits = credits
								+ "<tr><td align=\"right\"><font face=\"arial, helvetica, sans-serif\"><b>Plugins:</b></td><td><font face=\"arial, helvetica, sans-serif\"><dl>";

						for (int i = 0; i < HeroDesigner.loadedPlugins.size(); i++) {
							PluginInfo info = HeroDesigner.loadedPlugins.get(i);
							credits = credits + "<dt><b>" + info.getName()
									+ "</b></dt>";
							credits = credits + "<dd>by: " + info.getAuthor()
									+ "<br />";
							credits = credits + info.getDescription()
									+ "<br /><br />Contact: "
									+ info.getContact() + "</dd>";
						}
						credits = credits + "</dl></font></td></tr>";
					}
					credits = credits
							+ "</table>"
							+ "<br><br><blockquote>&copy;2001-2006.  All Rights Reserved.  HERO Designer and the HERO Designer logos are trademarks of Hero Games.</blockquote>"
							+ "<br><br><div align=\"center\"><font face=\"arial, helvetica, sans-serif\">Please send any questions or comments to \"support@herodesigner.com\"</font></div>"
							+ "</body></html";
					JEditorPane editor = new JEditorPane("text/html", credits);
					editor.setEditable(false);
					JScrollPane scroll = new JScrollPane(editor);
					dialog = new JDialog(HeroDesigner.frame, "About", true);
					JPanel panel = new JPanel(new BorderLayout());
					panel.add(scroll, "Center");
					JButton button = new JButton("Close");
					button.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							dialog.setVisible(false);
						}
					});
					JPanel buttonPanel = new JPanel(new FlowLayout(
							FlowLayout.CENTER));
					buttonPanel.add(button);
					panel.add(buttonPanel, "South");
					panel.setPreferredSize(new Dimension(400, 400));
					dialog.setContentPane(panel);
					dialog.setLocationRelativeTo(HeroDesigner.frame);
					Toolkit tool = Toolkit.getDefaultToolkit();
					Dimension screen = tool.getScreenSize();
					dialog.setLocation(screen.width / 2 - 200,
							screen.height / 2 - 200);
					dialog.pack();
					dialog.setVisible(true);
				} catch (Exception exp) {
					exp.printStackTrace();
				}
			}
		});
		newMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});

		newSuperheroicMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Superheroic.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newSuperheroic6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Superheroic6E.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newNormalMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Normal.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newHeroicMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Heroic.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newHeroic6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Heroic6E.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newComputerMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Computer.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newComputer6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Computer6E.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newAIMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("AI.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newAI6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("AI6E.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newAutomatonMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Automaton.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newAutomaton6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Automaton6E.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newVehicleMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Vehicle.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newVehicle6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Vehicle6E.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newBaseMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Base.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		newBase6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Hero hero = new Hero();
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Hero"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Character");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					setTemplate("Base6E.hdt", true, false, true);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Character");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new character.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new character",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});

		newPrefabMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				try {
					Prefab hero = new Prefab("New Prefab");
					openHeroes.add(hero);
					String name = hero.getCharacterName().trim().length() == 0 ? "New Prefab"
							: hero.getCharacterName();
					JRadioButtonMenuItem item = new JRadioButtonMenuItem(name);
					String c = "" + openHeroes.size();
					if (openHeroes.size() < 10) {
						item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
								java.awt.event.KeyEvent.CTRL_MASK, false));
					}
					windowGroup.add(item);
					windowMenu.add(item);
					item.setSelected(true);
					item.addActionListener(new ActionListener() {

						public void actionPerformed(ActionEvent e) {
							int count = windowMenu.getItemCount();
							for (int i = 0; i < count; i++) {
								if (windowMenu.getItem(i) instanceof JRadioButtonMenuItem) {
									JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
											.getItem(i);
									if (check.isSelected()) {
										Hero current = openHeroes.get(i);
										setActiveHero(current);
										logMemory("Change to New Prefab");
										return;
									}
								}
							}
						}
					});
					setActiveHero(hero);
					hero.setDirty(false);
					setDirty(false);
					logMemory("New Prefab");
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred creating the new prefab.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error creating new prefab",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});

		closeMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				if (HeroDesigner.getActiveHero() == null) {
					return;
				}
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				LOOP: while (HeroDesigner.activeHero.isDirty()
						&& !AutoUpdater.isDemo()) {
					String name = HeroDesigner.activeHero.getCharacterName();
					if (name.trim().length() == 0) {
						name = "[unknown]";
					}
					int response = JOptionPane
							.showConfirmDialog(
									HeroDesigner.frame,
									name
											+ " has been changed!\n\nSave changes before closing?");
					if (response == JOptionPane.CANCEL_OPTION) {
						HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
						return;
					}
					if (response == JOptionPane.NO_OPTION) {
						break LOOP;
					} else if (response == JOptionPane.YES_OPTION) {
						if (HeroDesigner.getActiveHero().getSaveFile() != null
								&& HeroDesigner.getActiveHero().getSaveFile()
										.getName().toUpperCase()
										.endsWith(".HDP")) {
							saveAsPrefab();
						} else {
							saveAsHDC();
						}
					}
				}
				closeHero();
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		});
		saveAllMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (openHeroes == null) {
					return;
				}
				Hero orig = HeroDesigner.getActiveHero();
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				for (int i = openHeroes.size() - 1; i >= 0; --i) {
					Hero hero = openHeroes.get(i);
					HeroDesigner.activeHero = null;
					setActiveHero(hero);
					if (!AutoUpdater.isDemo()) {
						String name = HeroDesigner.activeHero
								.getCharacterName();
						if (name.trim().length() == 0) {
							name = "[unknown]";
						}
						if (HeroDesigner.getActiveHero().getSaveFile() != null
								&& HeroDesigner.getActiveHero().getSaveFile()
										.getName().toUpperCase()
										.endsWith(".HDP")) {
							saveAsPrefab();
						} else {
							saveAsHDC();
						}
					}
				}
				setActiveHero(orig);
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		});
		closeAllMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				if (openHeroes == null) {
					return;
				}
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				for (int i = openHeroes.size() - 1; i >= 0; --i) {
					Hero hero = openHeroes.get(i);
					HeroDesigner.activeHero = hero;
					LOOP: while (HeroDesigner.activeHero.isDirty()
							&& !AutoUpdater.isDemo()) {
						String name = HeroDesigner.activeHero
								.getCharacterName();
						if (name.trim().length() == 0) {
							name = "[unknown]";
						}
						int response = JOptionPane
								.showConfirmDialog(
										HeroDesigner.frame,
										name
												+ " has been changed!\n\nSave changes before closing?");
						if (response == JOptionPane.CANCEL_OPTION) {
							HeroDesigner.activeHero = null;
							setActiveHero(hero);
							HeroDesigner.frame.setCursor(Cursor
									.getDefaultCursor());
							return;
						}
						if (response == JOptionPane.NO_OPTION) {
							break LOOP;
						} else if (response == JOptionPane.YES_OPTION) {
							if (HeroDesigner.getActiveHero().getSaveFile() != null
									&& HeroDesigner.getActiveHero()
											.getSaveFile().getName()
											.toUpperCase().endsWith(".HDP")) {
								saveAsPrefab();
							} else {
								saveAsHDC();
							}
						}
					}
					openHeroes.remove(i);
					JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
							.getItem(i);
					windowGroup.remove(check);
					windowMenu.remove(check);
					logMemory("Close Character");
				}
				if (openHeroes.size() > 0) {
					setActiveHero(openHeroes.get(0));
				} else {
					setActiveHero(null);
				}
				setActiveCharacterMenu();
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		});
		saveMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able to save your character using this function.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				if (HeroDesigner.getActiveHero() == null) {
					return;
				}
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				saveAsHDC();
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		});
		saveAsPrefabMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				if (HeroDesigner.getActiveHero() == null) {
					return;
				}
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able to save your character as a prefab using this function.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				saveAsPrefab();
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		});
		saveAsPackageDealMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				if (HeroDesigner.getActiveHero() == null) {
					return;
				}
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able to create a new package deal using this function.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				saveAsPackageDeal();
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		});
		saveAsTemplateMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				if (HeroDesigner.getActiveHero() == null) {
					return;
				}
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able to create a new template using this function.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				saveAsTemplate();
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		});
		saveAsMI.addActionListener(new ActionListener() {

			JFileChooser chooser;

			public void actionPerformed(ActionEvent e) {
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able to save your character using this function.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				File saveFile = null;
				String origSaveFile = "";
				if (HeroDesigner.getActiveHero().getSaveFile() != null) {
					origSaveFile = HeroDesigner.getActiveHero().getSaveFile()
							.getName();
				}
				while (saveFile == null) {
					if (chooser == null) {
						chooser = new JFileChooser();
						if (HeroDesigner.getActiveHero().isPrefab()) {
							chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

								public boolean accept(File file) {
									if (file.isDirectory()
											&& !file.getName().toUpperCase()
													.endsWith(".PKG")
											&& !file.getName().toUpperCase()
													.endsWith(".APP")) {
										return true;
									}
									if (file.getName().toUpperCase()
											.endsWith(".HDP")) {
										return true;
									} else {
										return false;
									}
								}

								public String getDescription() {
									return "HERO Designer Prefab Files";
								}
							});
						} else {
							chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

								public boolean accept(File file) {
									if (file.isDirectory()
											&& !file.getName().toUpperCase()
													.endsWith(".PKG")
											&& !file.getName().toUpperCase()
													.endsWith(".APP")) {
										return true;
									}
									if (file.getName().toUpperCase()
											.endsWith(".HDC")) {
										return true;
									} else {
										return false;
									}
								}

								public String getDescription() {
									return "HERO Designer Character Files";
								}
							});
						}
						chooser.setApproveButtonText("Save");
						if (HeroDesigner.getActiveHero().isPrefab()) {
							chooser.setDialogTitle("Save Prefab");
						} else {
							chooser.setDialogTitle("Save Character");
						}
						chooser.setFileSelectionMode(chooser.FILES_ONLY);
						chooser.setMultiSelectionEnabled(false);
						chooser.setAcceptAllFileFilterUsed(false);
						if (HeroDesigner.getActiveHero().isPrefab()) {
							if (prefs.getPrefabDir() != null
									&& prefs.getPrefabDir().trim().length() > 0) {
								try {
									chooser.setCurrentDirectory(new File(prefs
											.getPrefabDir()));
								} catch (Exception ex) {
								}
							}
						} else {
							if (prefs.getSaveDir() != null
									&& prefs.getSaveDir().trim().length() > 0) {
								try {
									chooser.setCurrentDirectory(new File(prefs
											.getSaveDir()));
								} catch (Exception ex) {
								}
							}
						}
					}
					if (origSaveFile.trim().length() > 0) {
						chooser.setSelectedFile(new File(origSaveFile));
					}
					int ret = chooser.showSaveDialog(HeroDesigner.frame);
					if (ret == chooser.CANCEL_OPTION) {
						return;
					} else if (ret == chooser.APPROVE_OPTION) {
						saveFile = chooser.getSelectedFile();
						if (HeroDesigner.getActiveHero().isPrefab()) {
							prefs.setPrefabDir(saveFile.getParent());
						} else {
							prefs.setSaveDir(saveFile.getParent());
						}
						String name = saveFile.getName();
						if (name.indexOf(".") < 0) {
							if (HeroDesigner.getActiveHero().isPrefab()) {
								name = name + ".hdp";
							} else {
								name = name + ".hdc";
							}
						} else {
							String ext = name.substring(name.lastIndexOf("."),
									name.length());
							if (HeroDesigner.getActiveHero().isPrefab()) {
								if (!ext.equalsIgnoreCase(".hdp")) {
									name = name.substring(0,
											name.lastIndexOf("."));
									name = name + ".hdp";
								}
							} else {
								if (!ext.equalsIgnoreCase(".hdc")
										&& !ext.equalsIgnoreCase(".hdz")) {
									name = name.substring(0,
											name.lastIndexOf("."));
									name = name + ".hdc";
								}
							}
						}
						if (!name.equals(saveFile.getName())) {
							String path = "";
							if (saveFile.getParentFile() != null) {
								path = saveFile.getParent();
								if (!path.endsWith(saveFile.separator)) {
									path = path + saveFile.separator;
								}
							}
							saveFile = new File(path + name);
						}
						if (saveFile.exists()) {
							int response = JOptionPane.showConfirmDialog(
									HeroDesigner.frame, saveFile.getName()
											+ " already exists.\n\nOverwrite?");
							if (response == JOptionPane.CANCEL_OPTION) {
								return;
							} else if (response == JOptionPane.NO_OPTION) {
								saveFile = null;
							}
						}
					}
				}
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				if (saveFile.getName().toUpperCase().endsWith(".HDC")) {
					if (origSaveFile.toUpperCase().endsWith(".HDZ")) {
						if (HeroDesigner.getActiveTemplate().getId()
								.indexOf("temp") >= 0) {
							int response = JOptionPane
									.showConfirmDialog(
											HeroDesigner.frame,
											"In order to convert from a package to a character file\nyou must save the template file from the package.\n\nWould you like to do this now?");
							if (response == JOptionPane.CANCEL_OPTION) {
								return;
							} else if (response == JOptionPane.NO_OPTION) {
								return;
							} else {
								File templateFile = null;
								JFileChooser templateChooser = null;
								while (templateFile == null) {
									if (templateChooser == null) {
										templateChooser = new JFileChooser();
										templateChooser
												.setFileFilter(new javax.swing.filechooser.FileFilter() {

													public boolean accept(
															File file) {
														if (file.isDirectory()
																&& !file.getName()
																		.toUpperCase()
																		.endsWith(
																				".PKG")
																&& !file.getName()
																		.toUpperCase()
																		.endsWith(
																				".APP")) {
															return true;
														}
														if (file.getName()
																.toUpperCase()
																.endsWith(
																		".HDT")) {
															return true;
														} else {
															return false;
														}
													}

													public String getDescription() {
														return "HERO Designer Template Files";
													}
												});
										templateChooser
												.setApproveButtonText("Save Template");
										templateChooser
												.setDialogTitle("Save Template");
										templateChooser
												.setFileSelectionMode(templateChooser.FILES_ONLY);
										templateChooser
												.setMultiSelectionEnabled(false);
										templateChooser
												.setAcceptAllFileFilterUsed(false);
										if (prefs.getTemplateDir() != null
												&& prefs.getTemplateDir()
														.trim().length() > 0) {
											try {
												chooser.setCurrentDirectory(new File(
														prefs.getTemplateDir()));
											} catch (Exception ex) {
											}
										}
									}
									String def = new File(HeroDesigner
											.getActiveTemplate().getId())
											.getName();
									try {
										def = def.substring(0,
												def.length() - 17);
										def = def + ".jpg";
									} catch (Exception ex) {
									}
									templateChooser.setSelectedFile(new File(
											def));
									int ret = templateChooser
											.showSaveDialog(HeroDesigner.frame);
									if (ret == templateChooser.CANCEL_OPTION) {
										return;
									} else if (ret == templateChooser.APPROVE_OPTION) {
										templateFile = templateChooser
												.getSelectedFile();
										String name2 = templateFile.getName();
										if (name2.indexOf(".") < 0) {
											name2 = name2 + ".hdt";
										} else {
											String ext = name2.substring(
													name2.lastIndexOf("."),
													name2.length());
											if (!ext.equalsIgnoreCase(".hdt")) {
												name2 = name2.substring(0,
														name2.lastIndexOf("."));
												name2 = name2 + ".hdt";
											}
										}
										if (!name2.equals(templateFile
												.getName())) {
											String path = "";
											if (templateFile.getParentFile() != null) {
												path = templateFile.getParent();
												if (!path
														.endsWith(templateFile.separator)) {
													path = path
															+ templateFile.separator;
												}
											}
											templateFile = new File(path
													+ name2);
										}
										if (templateFile.exists()) {
											int response2 = JOptionPane
													.showConfirmDialog(
															HeroDesigner.frame,
															templateFile
																	.getName()
																	+ " already exists.\n\nOverwrite?");
											if (response2 == JOptionPane.CANCEL_OPTION) {
												return;
											} else if (response2 == JOptionPane.NO_OPTION) {
												templateFile = null;
											}
										}
									}
								}
								File current = new File(HeroDesigner
										.getActiveTemplate().getId());
								current.renameTo(templateFile);
								setTemplate(templateFile.getAbsolutePath(),
										false, false, false);
								HeroDesigner.getActiveHero().resetTemplate();
							}
						}
						if (HeroDesigner.getActiveHero().getImageData() != null
								&& HeroDesigner.getActiveHero().getImageData().length > 0) {
							int response = JOptionPane
									.showConfirmDialog(
											HeroDesigner.frame,
											"In order to convert from a package to a character file\nyou must save the image file from the package.\n\nWould you like to do this now?");
							if (response == JOptionPane.CANCEL_OPTION) {
								return;
							} else if (response == JOptionPane.NO_OPTION) {
								return;
							} else {
								File imageFile = null;
								JFileChooser imageChooser = null;
								while (imageFile == null) {
									if (imageChooser == null) {
										imageChooser = new JFileChooser();
										imageChooser
												.setFileFilter(new javax.swing.filechooser.FileFilter() {

													public boolean accept(
															File file) {
														if (file.isDirectory()
																&& !file.getName()
																		.toUpperCase()
																		.endsWith(
																				".PKG")
																&& !file.getName()
																		.toUpperCase()
																		.endsWith(
																				".APP")) {
															return true;
														}
														if (file.getName()
																.toUpperCase()
																.endsWith(
																		".JPG")) {
															return true;
														} else if (file
																.getName()
																.toUpperCase()
																.endsWith(
																		".GIF")) {
															return true;
														} else if (file
																.getName()
																.toUpperCase()
																.endsWith(
																		".PNG")) {
															return true;
														} else {
															return false;
														}
													}

													public String getDescription() {
														return "Image FIles";
													}
												});
										imageChooser
												.setApproveButtonText("Save Image");
										imageChooser
												.setDialogTitle("Save Image");
										imageChooser
												.setFileSelectionMode(imageChooser.FILES_ONLY);
										imageChooser
												.setMultiSelectionEnabled(false);
										imageChooser
												.setAcceptAllFileFilterUsed(false);
										if (prefs.getImageDir() != null
												&& prefs.getImageDir().trim()
														.length() > 0) {
											try {
												imageChooser
														.setCurrentDirectory(new File(
																prefs.getImageDir()));
											} catch (Exception ex) {
											}
										}
									}

									imageChooser.setSelectedFile(new File(
											HeroDesigner.getActiveHero()
													.getImageFileName()));

									int ret = imageChooser
											.showSaveDialog(HeroDesigner.frame);
									if (ret == imageChooser.CANCEL_OPTION) {
										return;
									} else if (ret == imageChooser.APPROVE_OPTION) {
										imageFile = imageChooser
												.getSelectedFile();
										String origExt = HeroDesigner
												.getActiveHero()
												.getImageFileName()
												.substring(
														HeroDesigner
																.getActiveHero()
																.getImageFileName()
																.lastIndexOf(
																		"."),
														HeroDesigner
																.getActiveHero()
																.getImageFileName()
																.length());
										String name2 = imageFile.getName();
										if (name2.indexOf(".") < 0) {
											name2 = name2 + origExt;
										} else {
											String ext = name2.substring(
													name2.lastIndexOf("."),
													name2.length());
											if (!ext.equalsIgnoreCase(origExt)) {
												name2 = name2.substring(0,
														name2.lastIndexOf("."));
												name2 = name2 + origExt;
											}
										}
										if (!name2.equals(imageFile.getName())) {
											String path = "";
											if (imageFile.getParentFile() != null) {
												path = imageFile.getParent();
												if (!path
														.endsWith(imageFile.separator)) {
													path = path
															+ imageFile.separator;
												}
											}
											imageFile = new File(path + name2);
										}
										if (imageFile.exists()) {
											int response2 = JOptionPane
													.showConfirmDialog(
															HeroDesigner.frame,
															imageFile.getName()
																	+ " already exists.\n\nOverwrite?");
											if (response2 == JOptionPane.CANCEL_OPTION) {
												return;
											} else if (response2 == JOptionPane.NO_OPTION) {
												imageFile = null;
											}
										}
									}
								}
								try {
									FileOutputStream out = new FileOutputStream(
											imageFile);
									out.write(HeroDesigner.getActiveHero()
											.getImageData());
									HeroDesigner.getActiveHero().setImageData(
											HeroDesigner.getActiveHero()
													.getImageData(), imageFile);
								} catch (FileNotFoundException exp) {
									exp.printStackTrace();
								} catch (IOException exp) {
									exp.printStackTrace();
								}
							}
						}
					}
					HeroDesigner.activeHero.setSaveFile(saveFile);
					saveAsHDC();
				} else {
					HeroDesigner.activeHero.setSaveFile(saveFile);
					saveAsHDC();
				}
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				setActiveName(HeroDesigner.activeHero.getCharacterName());
			}
		});
		openMI.addActionListener(new ActionListener() {

			JFileChooser chooser;

			public void actionPerformed(ActionEvent e) {
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able to open character files and packaged files using this function.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				File[] openFiles = null;
				while (openFiles == null || openFiles.length == 0) {
					if (chooser == null) {
						chooser = new JFileChooser();
						chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HDC")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HDZ")) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								return "HERO Designer Files";
							}
						});
						chooser.setApproveButtonText("Open Character");
						chooser.setDialogTitle("Open");
						chooser.setFileSelectionMode(chooser.FILES_ONLY);
						chooser.setMultiSelectionEnabled(true);
						chooser.setAcceptAllFileFilterUsed(false);
						if (prefs.getSaveDir() != null
								&& prefs.getSaveDir().trim().length() > 0) {
							try {
								chooser.setCurrentDirectory(new File(prefs
										.getSaveDir()));
							} catch (Exception ex) {
							}
						}
					}
					int ret = chooser.showOpenDialog(HeroDesigner.frame);
					if (ret == chooser.CANCEL_OPTION) {
						return;
					} else if (ret == chooser.APPROVE_OPTION) {
						openFiles = chooser.getSelectedFiles();
						if (!openFiles[0].exists()) {
							openFiles = null;
						} else {
							prefs.setSaveDir(openFiles[0].getParent());
						}
					}
				}
				if (openFiles == null || openFiles.length == 0) {
					return;
				}
				OUTER: for (int counter = 0; counter < openFiles.length; counter++) {
					if (openFiles[counter].isDirectory()) {
						continue;
					}
					for (int i = 0; i < openHeroes.size(); i++) {
						Hero hero = openHeroes.get(i);
						if (hero.getSaveFile() != null
								&& hero.getSaveFile()
										.getAbsolutePath()
										.equals(openFiles[counter]
												.getAbsolutePath())) {
							windowMenu.getItem(i).doClick();
							continue OUTER;
						}
					}
					HeroDesigner.frame.setCursor(Cursor
							.getPredefinedCursor(Cursor.WAIT_CURSOR));
					openHero(openFiles[counter], true);
				}
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		});
		newFromTemplateMI.addActionListener(new ActionListener() {

			JFileChooser chooser;

			public void actionPerformed(ActionEvent e) {
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able to create a new character from a template using this function.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				File openFile = null;
				while (openFile == null) {
					if (chooser == null) {
						chooser = new JFileChooser();
						chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HDT")) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								return "HERO Designer Templates";
							}
						});
						chooser.setApproveButtonText("Create Character");
						chooser.setDialogTitle("Create Character From Template");
						chooser.setFileSelectionMode(chooser.FILES_ONLY);
						chooser.setMultiSelectionEnabled(false);
						chooser.setAcceptAllFileFilterUsed(false);
						if (prefs.getTemplateDir() != null
								&& prefs.getTemplateDir().trim().length() > 0) {
							try {
								chooser.setCurrentDirectory(new File(prefs
										.getTemplateDir()));
							} catch (Exception ex) {
							}
						}
					}
					int ret = chooser.showOpenDialog(HeroDesigner.frame);
					if (ret == chooser.CANCEL_OPTION) {
						return;
					} else if (ret == chooser.APPROVE_OPTION) {
						openFile = chooser.getSelectedFile();
						if (!openFile.exists()) {
							openFile = null;
						}
					}
				}
				if (openFile == null) {
					return;
				}
				HeroDesigner.frame.setCursor(Cursor
						.getPredefinedCursor(Cursor.WAIT_CURSOR));
				openHero(openFile, true);

				prefs.setTemplateDir(openFile.getParent());
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		});
		openPrefabMI.addActionListener(new ActionListener() {

			JFileChooser chooser;

			public void actionPerformed(ActionEvent e) {
				if (AutoUpdater.isDemo()) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.frame,
									"This is a demo version of HERO Designer.\n\nIf this were a fully licensed copy, you would be able to open a prefab file for edit using this function.",
									"Demo Version",
									JOptionPane.INFORMATION_MESSAGE);
					return;
				}
				File[] openFiles = null;
				while (openFiles == null || openFiles.length == 0) {
					if (chooser == null) {
						chooser = new JFileChooser();
						chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HDP")) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								return "HERO Designer Prefab Files";
							}
						});
						chooser.setApproveButtonText("Open Prefab");
						chooser.setDialogTitle("Open Prefab");
						chooser.setFileSelectionMode(chooser.FILES_ONLY);
						chooser.setMultiSelectionEnabled(true);
						chooser.setAcceptAllFileFilterUsed(false);
						if (prefs.getPrefabDir() != null
								&& prefs.getPrefabDir().trim().length() > 0) {
							try {
								chooser.setCurrentDirectory(new File(prefs
										.getPrefabDir()));
							} catch (Exception ex) {
							}
						}
					}
					int ret = chooser.showOpenDialog(HeroDesigner.frame);
					if (ret == chooser.CANCEL_OPTION) {
						return;
					} else if (ret == chooser.APPROVE_OPTION) {
						openFiles = chooser.getSelectedFiles();
						if (!openFiles[0].exists()) {
							openFiles = null;
						} else {
							prefs.setPrefabDir(openFiles[0].getParent());
						}
					}
				}
				if (openFiles == null || openFiles.length == 0) {
					return;
				}
				OUTER: for (int counter = 0; counter < openFiles.length; counter++) {
					if (openFiles[counter].isDirectory()) {
						continue;
					}
					for (int i = 0; i < openHeroes.size(); i++) {
						Hero hero = openHeroes.get(i);
						if (hero.getSaveFile() != null
								&& hero.getSaveFile()
										.getAbsolutePath()
										.equals(openFiles[counter]
												.getAbsolutePath())) {
							windowMenu.getItem(i).doClick();
							continue OUTER;
						}
					}
					HeroDesigner.frame.setCursor(Cursor
							.getPredefinedCursor(Cursor.WAIT_CURSOR));
					openHero(openFiles[counter], true);
				}
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		});
		standardNormalMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Normal.hdt", true, true, true);
			}
		});
		standardHeroMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Heroic.hdt", true, true, true);
			}
		});
		standardHero6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Heroic6E.hdt", true, true, true);
			}
		});
		standardSuperMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Superheroic.hdt", true, true, true);
			}
		});
		standardSuper6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Superheroic6E.hdt", true, true, true);
			}
		});
		vehicleMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Vehicle.hdt", true, true, true);
			}
		});
		vehicle6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Vehicle6E.hdt", true, true, true);
			}
		});
		baseMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Base.hdt", true, true, true);
			}
		});
		base6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Base6E.hdt", true, true, true);
			}
		});
		automatonMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Automaton.hdt", true, true, true);
			}
		});
		automaton6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Automaton6E.hdt", true, true, true);
			}
		});
		computerMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Computer.hdt", true, true, true);
			}
		});
		computer6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("Computer6E.hdt", true, true, true);
			}
		});
		aiMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("AI.hdt", true, true, true);
			}
		});
		ai6EMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate("AI6E.hdt", true, true, true);
			}
		});
		packageTemplateMI.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				setTemplate(HeroDesigner.getActiveHero()
						.getOriginalTemplateID(), false, true, true);
			}
		});
		chooseTemplateMI.addActionListener(new ActionListener() {

			JFileChooser chooser;

			public void actionPerformed(ActionEvent e) {
				File openFile = null;
				while (openFile == null) {
					if (chooser == null) {
						chooser = new JFileChooser();
						chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HDT")) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								return "HERO Designer Templates";
							}
						});
						chooser.setApproveButtonText("Load Template");
						chooser.setDialogTitle("Load Template");
						chooser.setFileSelectionMode(chooser.FILES_ONLY);
						chooser.setMultiSelectionEnabled(false);
						chooser.setAcceptAllFileFilterUsed(false);
						if (prefs.getTemplateDir() != null
								&& prefs.getTemplateDir().trim().length() > 0) {
							try {
								chooser.setCurrentDirectory(new File(prefs
										.getTemplateDir()));
							} catch (Exception ex) {
							}
						}
					}
					int ret = chooser.showOpenDialog(HeroDesigner.frame);
					if (ret == chooser.CANCEL_OPTION) {
						return;
					} else if (ret == chooser.APPROVE_OPTION) {
						openFile = chooser.getSelectedFile();
						if (!openFile.exists()) {
							openFile = null;
						}
					}
				}
				setTemplate(openFile.getAbsolutePath(), false, true, true);
			}
		});
		addTemplateMI.addActionListener(new ActionListener() {

			JFileChooser chooser;

			public void actionPerformed(ActionEvent e) {
				File openFile = null;
				while (openFile == null) {
					if (chooser == null) {
						chooser = new JFileChooser();
						chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HDT")) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								return "HERO Designer Templates";
							}
						});
						chooser.setApproveButtonText("Load Template");
						chooser.setDialogTitle("Load Template");
						chooser.setFileSelectionMode(chooser.FILES_ONLY);
						chooser.setMultiSelectionEnabled(false);
						chooser.setAcceptAllFileFilterUsed(false);
						if (prefs.getTemplateDir() != null
								&& prefs.getTemplateDir().trim().length() > 0) {
							try {
								chooser.setCurrentDirectory(new File(prefs
										.getTemplateDir()));
							} catch (Exception ex) {
							}
						}
					}
					int ret = chooser.showOpenDialog(HeroDesigner.frame);
					if (ret == chooser.CANCEL_OPTION) {
						return;
					} else if (ret == chooser.APPROVE_OPTION) {
						openFile = chooser.getSelectedFile();
						if (!openFile.exists()) {
							openFile = null;
						}
					}
				}
				addTemplate(openFile.getAbsolutePath());
			}
		});
		loadPrefabMI.addActionListener(new ActionListener() {

			JFileChooser chooser;

			public void actionPerformed(ActionEvent e) {
				File[] openFiles = null;
				while (openFiles == null || openFiles.length == 0) {
					if (chooser == null) {
						chooser = new JFileChooser();
						chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HDP")) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								return "HERO Designer Prefabs";
							}
						});
						chooser.setApproveButtonText("Load Prefab");
						chooser.setDialogTitle("Load Prefab");
						chooser.setFileSelectionMode(chooser.FILES_ONLY);
						chooser.setMultiSelectionEnabled(true);
						chooser.setAcceptAllFileFilterUsed(false);
						if (prefs.getPrefabDir() != null
								&& prefs.getPrefabDir().trim().length() > 0) {
							try {
								chooser.setCurrentDirectory(new File(prefs
										.getPrefabDir()));
							} catch (Exception ex) {
							}
						}
					}
					int ret = chooser.showOpenDialog(HeroDesigner.frame);
					if (ret == chooser.CANCEL_OPTION) {
						return;
					} else if (ret == chooser.APPROVE_OPTION) {
						openFiles = chooser.getSelectedFiles();
						if (!openFiles[0].exists()) {
							openFiles = null;
						} else {
							prefs.setPrefabDir(openFiles[0].getParent());
						}
					}
				}
				try {
					OUTER: for (int counter = 0; counter < openFiles.length; counter++) {
						if (lowMemory) {
							continue;
						}
						for (Prefab prefab : loadedPrefabs) {
							if (prefab.getSaveFile() != null
									&& prefab
											.getSaveFile()
											.getAbsolutePath()
											.equals(openFiles[counter]
													.getAbsolutePath())) {
								continue OUTER;
							}
						}
						HeroDesigner.frame.setCursor(Cursor
								.getPredefinedCursor(Cursor.WAIT_CURSOR));
						Prefab prefab = null;
						if (openFiles[counter].getName().toUpperCase()
								.endsWith(".HDP")) {
							prefab = new Prefab(openFiles[counter], true);
						}
						if (lowMemory) {
							continue;
						}
						if (prefab != null) {
							loadedPrefabs.add(prefab);
							firePrefabLoaded(prefab);
						}
						String name = prefab.getName().trim().length() == 0 ? "[unknown]"
								: prefab.getName();
						JMenuItem item = new JMenuItem(name);
						item.addActionListener(new PrefabActionListener(prefab,
								item));
						if (lowMemory) {
							continue;
						}
						unloadPrefabMI.add(item);
					}
					if (HeroDesigner.activeHero != null && !lowMemory) {
						mainPanel.updatePrefabs();
					}
				} catch (Exception exp) {
					exp.printStackTrace();
					JOptionPane.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred loading the selected prefab.\nPlease restart HERO Designer and try again.\n\nError Message: "
									+ exp.getMessage(), "Error loading prefab",
							JOptionPane.ERROR_MESSAGE);
				} finally {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
			}
		});
		unloadAllPrefabsMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				for (int i = unloadPrefabMI.getItemCount() - 1; i >= 0; i--) {
					unloadPrefabMI.getItem(i).doClick();
				}
			}
		});
	}

	/**
	 * Initializes/constructs all objects contained by this class. Also
	 * orders/constructs the menus/menu bar
	 */
	private void initWidgets() {
		saveMenu = new JMenu("Save", false);
		exportMenu = new JMenu("Export", false);
		characterMenu = new JMenu("Current File", false);
		characterMenu.setMnemonic('C');

		fileMenu = new JMenu("File", false);
		fileMenu.setMnemonic('F');
		newMI = new JMenuItem("New Character (current template)");
		newMI.setAccelerator(KeyStroke.getKeyStroke('N',
				java.awt.event.KeyEvent.CTRL_MASK, false));

		newStandardRulesMenu = new JMenu(
				"New Character (Hero System 5th Edition Rules)");
		newStandard6ERulesMenu = new JMenu(
				"New Character (Hero System 6th Edition Rules)");
		newCustomRulesMenu = new JMenu(
				"New Character (Custom/Non-Standard Rules)");
		newNormalMI = new JMenuItem("Normal");
		newStandardRulesMenu.add(newNormalMI);
		newHeroicMI = new JMenuItem("Heroic");
		newStandardRulesMenu.add(newHeroicMI);
		newSuperheroicMI = new JMenuItem("Superheroic");
		newStandardRulesMenu.add(newSuperheroicMI);
		newStandardRulesMenu.addSeparator();
		newVehicleMI = new JMenuItem("Vehicle");
		newStandardRulesMenu.add(newVehicleMI);
		newBaseMI = new JMenuItem("Base");
		newStandardRulesMenu.add(newBaseMI);
		newAutomatonMI = new JMenuItem("Automaton");
		newStandardRulesMenu.add(newAutomatonMI);
		newComputerMI = new JMenuItem("Computer");
		newStandardRulesMenu.add(newComputerMI);
		newAIMI = new JMenuItem("AI");
		newStandardRulesMenu.add(newAIMI);

		newHeroic6EMI = new JMenuItem("Heroic");
		newStandard6ERulesMenu.add(newHeroic6EMI);
		newSuperheroic6EMI = new JMenuItem("Superheroic");
		newStandard6ERulesMenu.add(newSuperheroic6EMI);
		newStandard6ERulesMenu.addSeparator();
		newVehicle6EMI = new JMenuItem("Vehicle");
		newStandard6ERulesMenu.add(newVehicle6EMI);
		newBase6EMI = new JMenuItem("Base");
		newStandard6ERulesMenu.add(newBase6EMI);
		newAutomaton6EMI = new JMenuItem("Automaton");
		newStandard6ERulesMenu.add(newAutomaton6EMI);
		newComputer6EMI = new JMenuItem("Computer");
		newStandard6ERulesMenu.add(newComputer6EMI);
		newAI6EMI = new JMenuItem("AI");
		newStandard6ERulesMenu.add(newAI6EMI);

		newFromTemplateMI = new JMenuItem("Choose File...");
		newCustomRulesMenu.add(newFromTemplateMI);
		newCharacterMenu = new JMenu("New Character");
		newCharacterMenu.add(newMI);
		newCharacterMenu.add(newStandard6ERulesMenu);
		newCharacterMenu.add(newStandardRulesMenu);
		newCharacterMenu.add(newCustomRulesMenu);
		newPrefabMI = new JMenuItem("New Prefab");
		openMI = new JMenuItem("Open Character...");
		openMI.setAccelerator(KeyStroke.getKeyStroke('O',
				java.awt.event.KeyEvent.CTRL_MASK, false));
		recentMI = new JMenu("Open Recent");
		openPrefabMI = new JMenuItem("Open Prefab...");
		closeMI = new JMenuItem("Close Character");
		closeMI.setEnabled(false);
		closeMI.setAccelerator(KeyStroke.getKeyStroke('W',
				java.awt.event.KeyEvent.CTRL_MASK, false));
		closeAllMI = new JMenuItem("Close All Files");
		closeAllMI.setEnabled(false);
		saveAllMI = new JMenuItem("Save All Files");
		saveAllMI.setEnabled(false);
		saveMI = new JMenuItem("Save Character");
		saveMI.setEnabled(false);
		saveMI.setAccelerator(KeyStroke.getKeyStroke('S',
				java.awt.event.KeyEvent.CTRL_MASK, false));
		saveAsMI = new JMenuItem("Save Character As...");
		saveAsMI.setAccelerator(KeyStroke.getKeyStroke('S',
				java.awt.event.KeyEvent.ALT_MASK, false));
		saveAsMI.setEnabled(false);
		saveAsPackageDealMI = new JMenuItem("Save As Package Deal...");
		saveAsPackageDealMI.setEnabled(false);
		saveAsPrefabMI = new JMenuItem("Save As Prefab...");
		saveAsPrefabMI.setEnabled(false);
		saveAsPrefabMI.setAccelerator(KeyStroke.getKeyStroke('P',
				java.awt.event.KeyEvent.ALT_MASK, false));
		saveAsTemplateMI = new JMenuItem("Save As Template...");
		saveAsTemplateMI.setEnabled(false);
		templateMenu = new JMenu("Change Current Template", false);
		standard6ETemplateMenu = new JMenu("Hero System 6th Edition Rules");
		standardTemplateMenu = new JMenu("Hero System 5th Edition Rules");
		nonStandardTemplateMenu = new JMenu("Custom/Non-Standard Rules");
		templateMenu.add(standard6ETemplateMenu);
		templateMenu.add(standardTemplateMenu);
		templateMenu.add(nonStandardTemplateMenu);

		templateGroup = new ButtonGroup();
		standardNormalMI = new JRadioButtonMenuItem("Normal");
		templateGroup.add(standardNormalMI);
		standardTemplateMenu.add(standardNormalMI);
		standardHeroMI = new JRadioButtonMenuItem("Heroic");
		templateGroup.add(standardHeroMI);
		standardTemplateMenu.add(standardHeroMI);
		standardSuperMI = new JRadioButtonMenuItem("Superheroic");
		templateGroup.add(standardSuperMI);
		standardTemplateMenu.add(standardSuperMI);
		standardTemplateMenu.addSeparator();
		vehicleMI = new JRadioButtonMenuItem("Vehicle");
		templateGroup.add(vehicleMI);
		standardTemplateMenu.add(vehicleMI);
		baseMI = new JRadioButtonMenuItem("Base");
		templateGroup.add(baseMI);
		standardTemplateMenu.add(baseMI);
		automatonMI = new JRadioButtonMenuItem("Automaton");
		templateGroup.add(automatonMI);
		standardTemplateMenu.add(automatonMI);
		computerMI = new JRadioButtonMenuItem("Computer");
		templateGroup.add(computerMI);
		standardTemplateMenu.add(computerMI);
		aiMI = new JRadioButtonMenuItem("AI");
		templateGroup.add(aiMI);
		standardTemplateMenu.add(aiMI);
		packageTemplateMI = new JRadioButtonMenuItem(
				"Revert to Original Template");
		templateGroup.add(packageTemplateMI);
		templateMenu.add(packageTemplateMI);

		standardHero6EMI = new JRadioButtonMenuItem("Heroic");
		templateGroup.add(standardHero6EMI);
		standard6ETemplateMenu.add(standardHero6EMI);
		standardSuper6EMI = new JRadioButtonMenuItem("Superheroic");
		templateGroup.add(standardSuper6EMI);
		standard6ETemplateMenu.add(standardSuper6EMI);
		standard6ETemplateMenu.addSeparator();
		vehicle6EMI = new JRadioButtonMenuItem("Vehicle");
		templateGroup.add(vehicle6EMI);
		standard6ETemplateMenu.add(vehicle6EMI);
		base6EMI = new JRadioButtonMenuItem("Base");
		templateGroup.add(base6EMI);
		standard6ETemplateMenu.add(base6EMI);
		automaton6EMI = new JRadioButtonMenuItem("Automaton");
		templateGroup.add(automaton6EMI);
		standard6ETemplateMenu.add(automaton6EMI);
		computer6EMI = new JRadioButtonMenuItem("Computer");
		templateGroup.add(computer6EMI);
		standard6ETemplateMenu.add(computer6EMI);
		ai6EMI = new JRadioButtonMenuItem("AI");
		templateGroup.add(ai6EMI);
		standard6ETemplateMenu.add(ai6EMI);

		addTemplateMI = new JMenuItem("Add Extension Template...");
		chooseTemplateMI = new JMenuItem("Choose File...");
		nonStandardTemplateMenu.add(chooseTemplateMI);
		exitMI = new JMenuItem("Exit");
		setPrefsMI = new JMenuItem("Preferences...");
		exitMI.setAccelerator(KeyStroke.getKeyStroke('X',
				java.awt.event.KeyEvent.CTRL_MASK, false));
		setExportTemplateMI = new JMenuItem("Set Export Format...");
		setExportTemplateMI.setEnabled(false);
		setExportTemplateMI.setAccelerator(KeyStroke.getKeyStroke('E',
				java.awt.event.KeyEvent.ALT_MASK
						+ java.awt.event.KeyEvent.CTRL_MASK, false));
		exportToFileMI = new JMenuItem("Export to File...");
		exportToFileMI.setEnabled(false);
		exportToFileMI.setAccelerator(KeyStroke.getKeyStroke('E',
				java.awt.event.KeyEvent.CTRL_MASK, false));
		combatRecordExportMI = new JMenuItem("Combat Record Export...");
		combatRecordExportMI.setEnabled(false);
		previewCombatRecordMI = new JMenuItem("Preview Combat Record...");
		previewCombatRecordMI.setEnabled(false);

		printToPDFMI = new JMenuItem("Print to PDF...");
		printToPDFMI.setEnabled(false);

		previewExportMI = new JMenuItem("Preview Character...");
		previewExportMI.setEnabled(false);
		previewExportMI.setAccelerator(KeyStroke.getKeyStroke('E',
				java.awt.event.KeyEvent.ALT_MASK, false));
		fileMenu.add(newCharacterMenu);
		fileMenu.add(openMI);
		fileMenu.add(recentMI);
		fileMenu.addSeparator();
		fileMenu.add(newPrefabMI);
		fileMenu.add(openPrefabMI);
		fileMenu.addSeparator();
		fileMenu.add(saveAllMI);
		fileMenu.add(closeAllMI);
		fileMenu.addSeparator();
		saveMenu.add(saveMI);
		saveMenu.add(saveAsMI);
		saveMenu.addSeparator();
		saveMenu.add(saveAsPackageDealMI);
		saveMenu.add(saveAsPrefabMI);
		saveMenu.add(saveAsTemplateMI);
		exportMenu.add(setExportTemplateMI);
		exportMenu.add(previewExportMI);
		exportMenu.add(exportToFileMI);
		fileMenu.add(previewCombatRecordMI);
		fileMenu.add(combatRecordExportMI);
		fileMenu.addSeparator();
		fileMenu.add(setPrefsMI);
		fileMenu.addSeparator();
		fileMenu.add(exitMI);
		prefabMenu = new JMenu("Prefabs", false);
		loadPrefabMI = new JMenuItem("Load Prefab...");
		loadPrefabMI.setAccelerator(KeyStroke.getKeyStroke('L',
				java.awt.event.KeyEvent.CTRL_MASK, false));
		unloadPrefabMI = new JMenu("Unload Prefab", false);
		unloadAllPrefabsMI = new JMenuItem("Unload All Prefabs");
		prefabMenu.add(loadPrefabMI);
		prefabMenu.add(unloadPrefabMI);
		prefabMenu.addSeparator();
		prefabMenu.add(unloadAllPrefabsMI);
		rulesMenu = new JMenu("Campaign Rules", false);
		rulesLoadMI = new JMenuItem("Load Campaign Rules...");
		rulesDefaultMI = new JMenuItem("Set to Hero System Default");
		rulesViewMI = new JMenuItem("View/Edit Campaign Rules");
		rulesMenu.add(rulesLoadMI);
		rulesMenu.add(rulesViewMI);
		rulesMenu.addSeparator();
		rulesMenu.add(rulesDefaultMI);
		helpMenu = new JMenu("Help", false);
		helpMenu.setMnemonic('H');
		aboutMI = new JMenuItem("About");
		aboutMI.setAccelerator(KeyStroke.getKeyStroke('A',
				java.awt.event.KeyEvent.ALT_MASK, false));
		viewDocsMI = new JMenuItem("View Documentation...");
		viewDocsMI.setAccelerator(KeyStroke.getKeyStroke('D',
				java.awt.event.KeyEvent.CTRL_MASK, false));
		helpMenu.add(viewDocsMI);
		helpMenu.addSeparator();
		discussionBoardMI = new JMenuItem("Go to HERO Designer Forum...");
		discussionBoardMI.setAccelerator(KeyStroke.getKeyStroke('D',
				java.awt.event.KeyEvent.ALT_MASK, false));
		helpMenu.add(discussionBoardMI);
		gotoHeroGamesMI = new JMenuItem("Go to HERO Games Website...");
		gotoHeroGamesMI.setAccelerator(KeyStroke.getKeyStroke('H',
				java.awt.event.KeyEvent.ALT_MASK
						+ java.awt.event.KeyEvent.SHIFT_MASK, false));
		helpMenu.add(gotoHeroGamesMI);
		helpMenu.addSeparator();
		helpMenu.add(aboutMI);
		toolsMenu = new JMenu("Tools", false);
		toolsMenu.setMnemonic('T');
		// toolsMenu.add(templateMenu);

		tearOffMI = new JMenuItem("Tear Off Selected Tab");
		tearOffMI.setEnabled(false);
		checkAllModsMI = new JMenuItem("Validate all Modifiers on Character");
		checkAllModsMI.setEnabled(false);
		applyPackageDealMI = new JMenuItem("Apply Package Deal...");
		applyPackageDealMI.setEnabled(false);
		// toolsMenu.add(checkAllModsMI);
		// toolsMenu.addSeparator();
		// getUpdatesMI = new JMenuItem("Get Application Updates...");
		// toolsMenu.add(getUpdatesMI);
		toolsMenu.addSeparator();
		getNewTemplatesMI = new JMenuItem("Get New Character Templates...");
		getNewTemplatesMI.setAccelerator(KeyStroke.getKeyStroke('T',
				java.awt.event.KeyEvent.ALT_MASK
						+ java.awt.event.KeyEvent.SHIFT_MASK, false));
		toolsMenu.add(getNewTemplatesMI);
		getNewPrefabsMI = new JMenuItem("Get New Prefabs...");
		getNewPrefabsMI.setAccelerator(KeyStroke.getKeyStroke('P',
				java.awt.event.KeyEvent.ALT_MASK
						+ java.awt.event.KeyEvent.SHIFT_MASK, false));
		toolsMenu.add(getNewPrefabsMI);
		getNewExportTemplatesMI = new JMenuItem("Get New Export Formats...");
		getNewExportTemplatesMI.setAccelerator(KeyStroke.getKeyStroke('E',
				java.awt.event.KeyEvent.ALT_MASK
						+ java.awt.event.KeyEvent.SHIFT_MASK, false));
		toolsMenu.add(getNewExportTemplatesMI);
		getNewPackageDealsMI = new JMenuItem("Get New Package Deals...");
		toolsMenu.add(getNewPackageDealsMI);
		getNewCharactersMI = new JMenuItem(
				"Find Pre-Defined Character Files...");
		toolsMenu.add(getNewCharactersMI);

		toolsMenu.addSeparator();
		// if (!macOSX)
		toolsMenu.add(tearOffMI);

		windowMenu = new JMenu("Open Files", false);
		windowMenu.setMnemonic('C');
		windowGroup = new ButtonGroup();
		templateMenu.setEnabled(false);
		prefabMenu.setEnabled(false);
		rulesMenu.setEnabled(false);
		windowMenu.setEnabled(false);
		menu = new JMenuBar();
		menu.add(fileMenu);

		menu.add(characterMenu);
		characterMenu.add(saveMenu);
		characterMenu.add(printToPDFMI);
		characterMenu.add(exportMenu);
		characterMenu.addSeparator();
		characterMenu.add(templateMenu);
		characterMenu.add(addTemplateMI);
		characterMenu.add(rulesMenu);
		characterMenu.add(applyPackageDealMI);
		characterMenu.add(checkAllModsMI);
		characterMenu.addSeparator();
		characterMenu.add(closeMI);

		menu.add(windowMenu);
		// menu.add(templateMenu);
		menu.add(prefabMenu);
		menu.add(toolsMenu);
		menu.add(helpMenu);
		HeroDesigner.frame.setJMenuBar(menu);
	}

	/**
	 * Reloads each of the loaded prefabs. This is a good thing to do whenever
	 * the template is changed as changing the active template can have an
	 * effect on the display of the prefabs...and even which abilities are
	 * available.
	 */
	private void resetPrefabs() {
		if (loadedPrefabs == null) {
			loadedPrefabs = new ArrayList<Prefab>();
		}
		for (Prefab prefab : loadedPrefabs) {
			prefab.initFromSave();
			if (HeroDesigner.activeHero != null) {
				mainPanel.updatePrefabs();
			}
		}
	}

	/**
	 * Saves the active character as an .hdc character file.
	 */
	private void saveAsHDC() {
		saveAsHDC(HeroDesigner.getActiveHero());
	}

	/**
	 * Saves the specified character as an .hdc character file
	 * 
	 * @param hero
	 *            the character to save.
	 */
	private void saveAsHDC(Hero hero) {
		if (hero == null) {
			return;
		}
		String name = hero.getCharacterName();
		if (name.trim().length() == 0) {
			name = "[unknown]";
		}
		File saveFile = hero.getSaveFile();
		hdcChooser = null;
		while (saveFile == null) {
			if (hdcChooser == null) {
				hdcChooser = new JFileChooser();
				if (hero.isPrefab()) {
					hdcChooser
							.setFileFilter(new javax.swing.filechooser.FileFilter() {

								public boolean accept(File file) {
									if (file.isDirectory()
											&& !file.getName().toUpperCase()
													.endsWith(".PKG")
											&& !file.getName().toUpperCase()
													.endsWith(".APP")) {
										return true;
									}
									if (file.getName().toUpperCase()
											.endsWith(".HDP")) {
										return true;
									} else {
										return false;
									}
								}

								public String getDescription() {
									return "HERO Designer Prefab Files";
								}
							});
				} else {
					hdcChooser
							.setFileFilter(new javax.swing.filechooser.FileFilter() {

								public boolean accept(File file) {
									if (file.isDirectory()
											&& !file.getName().toUpperCase()
													.endsWith(".PKG")
											&& !file.getName().toUpperCase()
													.endsWith(".APP")) {
										return true;
									}
									if (file.getName().toUpperCase()
											.endsWith(".HDC")) {
										return true;
									} else {
										return false;
									}
								}

								public String getDescription() {
									return "HERO Designer Character Files";
								}
							});
				}
				hdcChooser.setApproveButtonText("Save");
				if (hero.isPrefab()) {
					hdcChooser.setDialogTitle("Save Prefab");
				} else {
					hdcChooser.setDialogTitle("Save Character");
				}
				hdcChooser.setFileSelectionMode(hdcChooser.FILES_ONLY);
				hdcChooser.setMultiSelectionEnabled(false);
				hdcChooser.setAcceptAllFileFilterUsed(false);
			}
			if (hero.isPrefab()) {
				if (prefs.getPrefabDir() != null
						&& prefs.getPrefabDir().trim().length() > 0) {
					try {
						hdcChooser.setCurrentDirectory(new File(prefs
								.getPrefabDir()));
					} catch (Exception ex) {
					}
				}
			} else {
				if (prefs.getSaveDir() != null
						&& prefs.getSaveDir().trim().length() > 0) {
					try {
						hdcChooser.setCurrentDirectory(new File(prefs
								.getSaveDir()));
					} catch (Exception ex) {
					}
				}
			}
			if (hero.getCharacterName() != null
					&& hero.getCharacterName().trim().length() > 0) {
				try {
					if (hero.isPrefab()) {
						File f = new File(prefs.getPrefabDir() + File.separator
								+ hero.getCharacterName() + ".hdp");
						hdcChooser.setSelectedFile(f);
					} else {
						File f = new File(prefs.getSaveDir() + File.separator
								+ hero.getCharacterName() + ".hdc");
						hdcChooser.setSelectedFile(f);
					}
				} catch (Exception ex) {
				}
			}
			int ret = hdcChooser.showSaveDialog(HeroDesigner.frame);
			if (ret == hdcChooser.CANCEL_OPTION) {
				return;
			} else if (ret == hdcChooser.APPROVE_OPTION) {
				saveFile = hdcChooser.getSelectedFile();
				prefs.setSaveDir(saveFile.getParent());
				String name2 = saveFile.getName();
				if (name2.indexOf(".") < 0) {
					name2 = name2 + (hero.isPrefab() ? ".hdp" : ".hdc");
				} else {
					String ext = name2.substring(name2.lastIndexOf("."),
							name2.length());
					if (hero.isPrefab()) {
						if (!ext.equalsIgnoreCase(".hdp")) {
							name2 = name2.substring(0, name2.lastIndexOf("."));
							name2 = name2 + ".hdp";
						}
					} else {
						if (!ext.equalsIgnoreCase(".hdc")) {
							name2 = name2.substring(0, name2.lastIndexOf("."));
							name2 = name2 + ".hdc";
						}
					}
				}
				if (!name2.equals(saveFile.getName())) {
					String path = "";
					if (saveFile.getParentFile() != null) {
						path = saveFile.getParent();
						if (!path.endsWith(saveFile.separator)) {
							path = path + saveFile.separator;
						}
					}
					saveFile = new File(path + name2);
				}
				if (saveFile.exists()) {
					int response2 = JOptionPane.showConfirmDialog(
							HeroDesigner.frame, saveFile.getName()
									+ " already exists.\n\nOverwrite?");
					if (response2 == JOptionPane.CANCEL_OPTION) {
						return;
					} else if (response2 == JOptionPane.NO_OPTION) {
						saveFile = null;
					}
				}
			}
		}
		hero.setSaveFile(saveFile);
		try {
			Element root = hero.getSaveXML();
			Format format = Format.getPrettyFormat();
			format.setEncoding("UTF-16");
			XMLOutputter outputter = new XMLOutputter(format);
			FileOutputStream out = new FileOutputStream(saveFile);
			outputter.output(new Document(root), out);
			out.close();
			hero.setDirty(false);
			prefs.addRecentFile(saveFile);
		} catch (Exception exp) {
			exp.printStackTrace();
			JOptionPane
					.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred during the save process.\nPlease try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error saving "
									+ (hero.isPrefab() ? "prefab" : "character"),
							JOptionPane.ERROR_MESSAGE);
		}
		updateStatus();
	}

	/**
	 * Saves the currently active character as a Prefab file.
	 */
	private void saveAsPrefab() {
		if (HeroDesigner.getActiveHero() == null) {
			return;
		}
		String name = HeroDesigner.getActiveHero().getCharacterName();
		while (name != null && name.trim().length() == 0) {
			name = JOptionPane.showInputDialog(HeroDesigner.frame,
					"Please enter a name for the Prefab", "Save As Prefab",
					JOptionPane.PLAIN_MESSAGE);
		}
		if (name == null) {
			return;
		}
		File saveFile = HeroDesigner.getActiveHero().getSaveFile();
		if (saveFile != null
				&& !saveFile.getName().toUpperCase().endsWith(".HDP")) {
			saveFile = null;
		}
		while (saveFile == null) {
			if (hdpChooser == null) {
				hdpChooser = new JFileChooser();
				hdpChooser
						.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HDP")) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								return "HERO Designer Prefab Files";
							}
						});
				hdpChooser.setApproveButtonText("Save Prefab");
				hdpChooser.setDialogTitle("Save As Prefab");
				hdpChooser.setFileSelectionMode(hdcChooser.FILES_ONLY);
				hdpChooser.setMultiSelectionEnabled(false);
				hdpChooser.setAcceptAllFileFilterUsed(false);
				if (prefs.getPrefabDir() != null
						&& prefs.getPrefabDir().trim().length() > 0) {
					try {
						hdpChooser.setCurrentDirectory(new File(prefs
								.getPrefabDir()));
					} catch (Exception ex) {
					}
				}
			}
			File init = new File(hdpChooser.getCurrentDirectory()
					.getAbsolutePath() + File.separator + name + ".hdp");
			hdpChooser.setSelectedFile(init);
			int ret = hdpChooser.showSaveDialog(HeroDesigner.frame);
			if (ret == hdpChooser.CANCEL_OPTION) {
				return;
			} else if (ret == hdpChooser.APPROVE_OPTION) {
				saveFile = hdpChooser.getSelectedFile();
				prefs.setPrefabDir(saveFile.getParent());
				String name2 = saveFile.getName();
				if (name2.indexOf(".") < 0) {
					name2 = name2 + ".hdp";
				} else {
					String ext = name2.substring(name2.lastIndexOf("."),
							name2.length());
					if (!ext.equalsIgnoreCase(".hdp")) {
						name2 = name2.substring(0, name2.lastIndexOf("."));
						name2 = name2 + ".hdp";
					}
				}
				if (!name2.equals(saveFile.getName())) {
					String path = "";
					if (saveFile.getParentFile() != null) {
						path = saveFile.getParent();
						if (!path.endsWith(saveFile.separator)) {
							path = path + saveFile.separator;
						}
					}
					saveFile = new File(path + name2);
				}
				if (saveFile.exists()) {
					int response2 = JOptionPane.showConfirmDialog(
							HeroDesigner.frame, saveFile.getName()
									+ " already exists.\n\nOverwrite?");
					if (response2 == JOptionPane.CANCEL_OPTION) {
						return;
					} else if (response2 == JOptionPane.NO_OPTION) {
						saveFile = null;
					}
				}
			}
		}
		try {
			Prefab prefab = new Prefab(name);
			prefab.setSaveFile(saveFile);
			prefab.setSkills(HeroDesigner.getActiveHero().getSkills());
			prefab.setPerks(HeroDesigner.getActiveHero().getPerks());
			prefab.setTalents(HeroDesigner.getActiveHero().getTalents());
			prefab.setManeuvers(HeroDesigner.getActiveHero().getManeuvers());
			prefab.setPowers(HeroDesigner.getActiveHero().getPowers());
			prefab.setDisads(HeroDesigner.getActiveHero().getDisads());
			prefab.setEquipment(HeroDesigner.getActiveHero().getEquipment());
			Element root = prefab.getSaveXML();
			Format format = Format.getPrettyFormat();
			format.setEncoding("UTF-16");
			XMLOutputter outputter = new XMLOutputter(format);
			FileOutputStream out = new FileOutputStream(saveFile);
			outputter.output(new Document(root), out);
			out.close();
			JMenuItem item = new JMenuItem(name);
			item.addActionListener(new PrefabActionListener(prefab, item));
			mainPanel.updatePrefabs();
			HeroDesigner.getActiveHero().setDirty(false);
			prefs.addRecentFile(saveFile);
		} catch (Exception exp) {
			exp.printStackTrace();
			JOptionPane
					.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred during the save process.\nPlease try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error saving character", JOptionPane.ERROR_MESSAGE);
		}
		updateStatus();
	}

	/**
	 * Saves the currently active character and template as a new template file.
	 */
	private void saveAsTemplate() {
		if (HeroDesigner.getActiveHero() == null) {
			return;
		}
		File saveFile = null;
		JFileChooser hdtChooser = null;

		// have the user specify a name for their new template...
		String name = "";
		name = JOptionPane.showInputDialog(HeroDesigner.getAppFrame(),
				"Please enter a name for this template", "Enter Template Name",
				JOptionPane.PLAIN_MESSAGE);
		if (name == null) {
			return;
		}
		while (saveFile == null) {
			if (hdtChooser == null) {
				hdtChooser = new JFileChooser();
				hdtChooser
						.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HDT")) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								return "HERO Designer Template Files";
							}
						});
				hdtChooser.setApproveButtonText("Save Template");
				hdtChooser.setDialogTitle("Save As Template");
				hdtChooser.setFileSelectionMode(hdcChooser.FILES_ONLY);
				hdtChooser.setMultiSelectionEnabled(false);
				hdtChooser.setAcceptAllFileFilterUsed(false);
				if (prefs.getTemplateDir() != null
						&& prefs.getTemplateDir().trim().length() > 0) {
					try {
						hdtChooser.setCurrentDirectory(new File(prefs
								.getTemplateDir()));
					} catch (Exception ex) {
					}
				}
			}
			hdtChooser.setSelectedFile(new File(name + ".hdt"));
			int ret = hdtChooser.showSaveDialog(HeroDesigner.frame);
			if (ret == hdtChooser.CANCEL_OPTION) {
				return;
			} else if (ret == hdtChooser.APPROVE_OPTION) {
				saveFile = hdtChooser.getSelectedFile();
				String name2 = saveFile.getName();
				if (name2.indexOf(".") < 0) {
					name2 = name2 + ".hdt";
				} else {
					String ext = name2.substring(name2.lastIndexOf("."),
							name2.length());
					if (!ext.equalsIgnoreCase(".hdt")) {
						name2 = name2.substring(0, name2.lastIndexOf("."));
						name2 = name2 + ".hdt";
					}
				}
				if (!name2.equals(saveFile.getName())) {
					String path = "";
					if (saveFile.getParentFile() != null) {
						path = saveFile.getParent();
						if (!path.endsWith(saveFile.separator)) {
							path = path + saveFile.separator;
						}
					}
					saveFile = new File(path + name2);
				}
				if (saveFile.exists()) {
					int response2 = JOptionPane.showConfirmDialog(
							HeroDesigner.frame, saveFile.getName()
									+ " already exists.\n\nOverwrite?");
					if (response2 == JOptionPane.CANCEL_OPTION) {
						return;
					} else if (response2 == JOptionPane.NO_OPTION) {
						saveFile = null;
					}
				}
			}
		}
		try {
			Element character = HeroDesigner.getActiveHero().getSaveXML();
			character.removeChildren("TEMPLATE");
			character.removeChildren("IMAGE");
			character.removeChildren("RULES");
			Element basic = character.getChild("BASIC_CONFIGURATION");
			basic.setAttribute("RULES", "");
			basic.setAttribute("TEMPLATE", "");
			Element rules = HeroDesigner.getActiveHero().getRules()
					.getRulesXML();
			Element root = HeroDesigner.getActiveTemplate().getTemplateXML();
			root.removeChildren("CHARACTER");
			root.removeChildren("RULES");
			root.removeChildren("PREFAB");
			root.addContent(character);
			root.addContent(rules);
			root.setAttribute("name", name);

			if (loadedPrefabs.size() > 0) {
				// need to prompt to see if the user wants to include prefabs
				int response = JOptionPane
						.showConfirmDialog(
								HeroDesigner.frame,
								"<html>Would you like to include the currently loaded prefabs in your template?"
										+ "<br><br>All included prefabs will be automatically loaded when the template is selected.</html>");
				if (response == JOptionPane.CANCEL_OPTION) {
					return;
				} else if (response == JOptionPane.YES_OPTION) {
					for (Prefab p : loadedPrefabs) {
						Element pr = p.getSaveXML();
						pr.removeChildren("TEMPLATE");
						pr.removeChildren("IMAGE");
						pr.removeChildren("RULES");
						basic = pr.getChild("BASIC_CONFIGURATION");
						basic.setAttribute("RULES", "");
						basic.setAttribute("TEMPLATE", "");
						root.addContent(pr);
					}
				}
			}

			if (HeroDesigner.getActiveTemplate().getPrefabs().size() > 0) {
				// need to prompt to see if the user wants to include prefabs
				int response = JOptionPane
						.showConfirmDialog(
								HeroDesigner.frame,
								"<html>Would you like to include the prefabs defined in the active template?"
										+ "<br><br>All included prefabs will be automatically loaded when the template is selected.</html>");
				if (response == JOptionPane.CANCEL_OPTION) {
					return;
				} else if (response == JOptionPane.YES_OPTION) {
					for (Prefab p : HeroDesigner.getActiveTemplate()
							.getPrefabs()) {
						root.addContent(p.getSaveXML());
					}
				}
			}
			Document doc = root.getDocument();
			if (doc == null) {
				doc = new Document(root);
			}
			Format format = Format.getPrettyFormat();
			format.setEncoding("UTF-16");
			XMLOutputter outputter = new XMLOutputter(format);
			FileOutputStream out = new FileOutputStream(saveFile);
			prefs.setTemplateDir(saveFile.getParent());
			outputter.output(doc, out);
			out.close();
		} catch (Exception exp) {
			exp.printStackTrace();
			JOptionPane
					.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred during the save process.\nPlease try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error saving template", JOptionPane.ERROR_MESSAGE);
		}
		updateStatus();
	}

	/**
	 * Saves the currently active character and as a new package deal file.
	 */
	private void saveAsPackageDeal() {
		if (HeroDesigner.getActiveHero() == null) {
			return;
		}
		File saveFile = null;
		JFileChooser hpkChooser = null;

		// have the user specify a name for their new template...
		String name = "";
		name = JOptionPane.showInputDialog(HeroDesigner.getAppFrame(),
				"Please enter a name for this package deal",
				"Enter Package Deal Name", JOptionPane.PLAIN_MESSAGE);
		if (name == null) {
			return;
		}
		while (saveFile == null) {
			if (hpkChooser == null) {
				hpkChooser = new JFileChooser();
				hpkChooser
						.setFileFilter(new javax.swing.filechooser.FileFilter() {

							public boolean accept(File file) {
								if (file.isDirectory()
										&& !file.getName().toUpperCase()
												.endsWith(".PKG")
										&& !file.getName().toUpperCase()
												.endsWith(".APP")) {
									return true;
								}
								if (file.getName().toUpperCase()
										.endsWith(".HPK")) {
									return true;
								} else {
									return false;
								}
							}

							public String getDescription() {
								return "HERO Designer Package Deal Files";
							}
						});
				hpkChooser.setApproveButtonText("Save Package Deal");
				hpkChooser.setDialogTitle("Save As Package Deal");
				hpkChooser.setFileSelectionMode(hpkChooser.FILES_ONLY);
				hpkChooser.setMultiSelectionEnabled(false);
				hpkChooser.setAcceptAllFileFilterUsed(false);
				if (prefs.getPrefabDir() != null
						&& prefs.getPrefabDir().trim().length() > 0) {
					try {
						hpkChooser.setCurrentDirectory(new File(prefs
								.getPrefabDir()));
					} catch (Exception ex) {
					}
				}
			}
			hpkChooser.setSelectedFile(new File(name + ".hpk"));
			int ret = hpkChooser.showSaveDialog(HeroDesigner.frame);
			if (ret == hpkChooser.CANCEL_OPTION) {
				return;
			} else if (ret == hpkChooser.APPROVE_OPTION) {
				saveFile = hpkChooser.getSelectedFile();
				prefs.setPrefabDir(saveFile.getParent());
				String name2 = saveFile.getName();
				if (name2.indexOf(".") < 0) {
					name2 = name2 + ".hpk";
				} else {
					String ext = name2.substring(name2.lastIndexOf("."),
							name2.length());
					if (!ext.equalsIgnoreCase(".hpk")) {
						name2 = name2.substring(0, name2.lastIndexOf("."));
						name2 = name2 + ".hpk";
					}
				}
				if (!name2.equals(saveFile.getName())) {
					String path = "";
					if (saveFile.getParentFile() != null) {
						path = saveFile.getParent();
						if (!path.endsWith(saveFile.separator)) {
							path = path + saveFile.separator;
						}
					}
					saveFile = new File(path + name2);
				}
				if (saveFile.exists()) {
					int response2 = JOptionPane.showConfirmDialog(
							HeroDesigner.frame, saveFile.getName()
									+ " already exists.\n\nOverwrite?");
					if (response2 == JOptionPane.CANCEL_OPTION) {
						return;
					} else if (response2 == JOptionPane.NO_OPTION) {
						saveFile = null;
					}
				}
			}
		}
		try {
			Element character = HeroDesigner.getActiveHero().getSaveXML();
			character.removeChildren("TEMPLATE");
			character.removeChildren("IMAGE");
			character.removeChildren("RULES");
			Element basic = character.getChild("BASIC_CONFIGURATION");
			basic.setAttribute("RULES", "");
			basic.setAttribute("TEMPLATE", "");
			Element info = character.getChild("CHARACTER_INFO");
			info.setAttribute("CHARACTER_NAME", name);

			if (loadedPrefabs.size() > 0) {
				// need to prompt to see if the user wants to include prefabs
				int response = JOptionPane
						.showConfirmDialog(
								HeroDesigner.frame,
								"<html>Would you like to include the currently loaded prefabs in your package deal?"
										+ "<br><br>All included prefabs will be automatically loaded when the package deal is selected/applied.</html>");
				if (response == JOptionPane.CANCEL_OPTION) {
					return;
				} else if (response == JOptionPane.YES_OPTION) {
					for (Prefab p : loadedPrefabs) {
						Element pr = p.getSaveXML();
						pr.removeChildren("TEMPLATE");
						pr.removeChildren("IMAGE");
						pr.removeChildren("RULES");
						basic = pr.getChild("BASIC_CONFIGURATION");
						basic.setAttribute("RULES", "");
						basic.setAttribute("TEMPLATE", "");
						character.addContent(pr);
					}
				}
			}

			Document doc = character.getDocument();
			if (doc == null) {
				doc = new Document(character);
			}
			Format format = Format.getPrettyFormat();
			format.setEncoding("UTF-16");
			XMLOutputter outputter = new XMLOutputter(format);
			FileOutputStream out = new FileOutputStream(saveFile);
			prefs.setTemplateDir(saveFile.getParent());
			outputter.output(doc, out);
			out.close();
		} catch (Exception exp) {
			exp.printStackTrace();
			JOptionPane
					.showMessageDialog(
							HeroDesigner.getAppFrame(),
							"An error occurred during the save process.\nPlease try again.\n\nError Message: "
									+ exp.getMessage(),
							"Error saving package deal",
							JOptionPane.ERROR_MESSAGE);
		}
		updateStatus();
	}

	/**
	 * Utility method to iterate through the open characters and sync them up
	 * with the active character menu.
	 */
	private void setActiveCharacterMenu() {
		Enumeration enumer = windowGroup.getElements();
		while (enumer.hasMoreElements()) {
			windowGroup.remove((AbstractButton) enumer.nextElement());
		}
		windowMenu.removeAll();
		for (int i = 0; i < openHeroes.size(); i++) {
			Hero hero = openHeroes.get(i);
			String name = hero.getCharacterName().trim().length() == 0 ? "[unknown]"
					: hero.getCharacterName();
			JRadioButtonMenuItem item = new JRadioButtonMenuItem(name
					+ (hero.getSaveFile() != null ? " ["
							+ hero.getSaveFile().getName() + "]" : ""));
			item.addActionListener(new ActionListener() {

				public void actionPerformed(ActionEvent e) {
					int count = windowMenu.getItemCount();
					for (int i = 0; i < count; i++) {
						JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
								.getItem(i);
						if (check.isSelected()) {
							Hero current = openHeroes.get(i);
							setActiveHero(current);
							setTemplateMenuSelection();
							logMemory("Change to File-based Character");
							return;
						}
					}
				}
			});
			String c = "" + (i + 1);
			if (i < 9) {
				item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
						java.awt.event.KeyEvent.CTRL_MASK, false));
			}
			windowGroup.add(item);
			windowMenu.add(item);
			if (HeroDesigner.getActiveHero() != null
					&& hero.equals(HeroDesigner.getActiveHero())) {
				item.setSelected(true);
			}
		}
	}

	/**
	 * Utility method to sync the template menu with the currently active
	 * template.
	 */
	private void setTemplateMenuSelection() {
		standardNormalMI.setSelected(false);
		standardHeroMI.setSelected(false);
		standardSuperMI.setSelected(false);
		vehicleMI.setSelected(false);
		baseMI.setSelected(false);
		automatonMI.setSelected(false);
		computerMI.setSelected(false);
		aiMI.setSelected(false);
		packageTemplateMI.setSelected(false);
		packageTemplateMI.setText("Revert to Original Template");
		if (HeroDesigner.getActiveTemplate() == null) {
			return;
		}
		if (HeroDesigner.getActiveTemplate().getId() == null) {
			return;
		}
		if (HeroDesigner.getActiveTemplate().getId()
				.equals(HeroDesigner.getActiveHero().getOriginalTemplateID())) {
			packageTemplateMI.setVisible(false);
		} else {
			packageTemplateMI.setVisible(true);
		}
		if (HeroDesigner.getActiveTemplate().getId()
				.equals("builtIn.Normal.hdt")) {
			standardNormalMI.setSelected(true);
			return;
		}
		if (HeroDesigner.getActiveTemplate().getId()
				.equals("builtIn.Heroic.hdt")) {
			standardHeroMI.setSelected(true);
			return;
		}
		if (HeroDesigner.getActiveTemplate().getId()
				.equals("builtIn.Superheroic.hdt")) {
			standardSuperMI.setSelected(true);
			return;
		}
		if (HeroDesigner.getActiveTemplate().getId()
				.equals("builtIn.Vehicle.hdt")) {
			vehicleMI.setSelected(true);
			return;
		}
		if (HeroDesigner.getActiveTemplate().getId().equals("builtIn.Base.hdt")) {
			baseMI.setSelected(true);
			return;
		}
		if (HeroDesigner.getActiveTemplate().getId()
				.equals("builtIn.Automaton.hdt")) {
			automatonMI.setSelected(true);
			return;
		}
		if (HeroDesigner.getActiveTemplate().getId()
				.equals("builtIn.Computer.hdt")) {
			computerMI.setSelected(true);
			return;
		}
		if (HeroDesigner.getActiveTemplate().getId().equals("builtIn.AI.hdt")) {
			aiMI.setSelected(true);
			return;
		}
		packageTemplateMI.setText(HeroDesigner.getActiveTemplate().getId());
		packageTemplateMI.setEnabled(true);
		packageTemplateMI.setSelected(true);
	}

	/**
	 * closes the active character. It is assumed that any save checks have been
	 * made before this point.
	 */
	protected void closeHero() {
		int count = windowMenu.getItemCount();
		for (int i = 0; i < count; i++) {
			JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
					.getItem(i);
			if (check.isSelected()) {
				windowGroup.remove(check);
				windowMenu.remove(check);
				openHeroes.remove(HeroDesigner.activeHero);
				Hero active = null;
				if (openHeroes.size() > i) {
					active = openHeroes.get(i);
					JRadioButtonMenuItem activeMI = (JRadioButtonMenuItem) windowMenu
							.getItem(i);
					activeMI.setSelected(true);
				} else if (openHeroes.size() > 0) {
					active = openHeroes.get(openHeroes.size() - 1);
					JRadioButtonMenuItem activeMI = (JRadioButtonMenuItem) windowMenu
							.getItem(openHeroes.size() - 1);
					activeMI.setSelected(true);
				}
				setActiveHero(active);
				logMemory("Close Character");
				break;
			}
		}
		setActiveCharacterMenu();
	}

	/**
	 * Opens the specified character file. If display is true, the character
	 * will be set as the active character and displayed.
	 * 
	 * @param openFile
	 *            The character file to open
	 * @param display
	 *            Whether to set the character to active or not (and display)
	 */
	public void openHero(File openFile, boolean display) {
		if (lowMemory) {
			// just return...save on space
			return;
		}
		for (int i = 0; i < openHeroes.size(); i++) {
			Hero hero = openHeroes.get(i);
			if (hero.getSaveFile() != null
					&& hero.getSaveFile().getAbsolutePath()
							.equals(openFile.getAbsolutePath())) {
				if (!HeroDesigner.headless) {
					windowMenu.getItem(i).doClick();
				}
				return;
			}
		}
		Utility.garbageCollect("Starting Character Open...");
		try {
			Hero hero = null;
			if (display)
				activeHero = null;
			if (openFile.getName().toUpperCase().endsWith(".HDC")
					|| openFile.getName().toUpperCase().endsWith(".HDT")) {
				hero = new Hero(openFile);
			} else if (openFile.getName().toUpperCase().endsWith(".HDP")) {
				hero = new Prefab(openFile);
			} else {
				File tempDir = new File("temp");
				if (!tempDir.exists()) {
					tempDir.mkdirs();
				}
				ZipInputStream zis = new ZipInputStream(new FileInputStream(
						openFile));
				ZipEntry entry = zis.getNextEntry();
				File charFile = null;
				File templateFile = null;
				File imageFile = null;
				while (entry != null) {
					File file = null;
					if (entry.getName().toUpperCase().endsWith(".HDC")) {
						String prefix = entry.getName().substring(0,
								entry.getName().toUpperCase().indexOf(".HDC"));
						file = new File(tempDir + File.separator + prefix
								+ System.currentTimeMillis() + ".hdc");
						charFile = file;
						file.deleteOnExit();
					} else if (entry.getName().toUpperCase().endsWith(".HDT")) {
						String prefix = entry.getName().substring(0,
								entry.getName().toUpperCase().indexOf(".HDT"));
						file = new File(tempDir + File.separator + prefix
								+ System.currentTimeMillis() + ".hdt");
						file.deleteOnExit();
						templateFile = file;
					} else if (entry.getName().toUpperCase().endsWith(".JPG")) {
						String prefix = entry.getName().substring(0,
								entry.getName().toUpperCase().indexOf(".JPG"));
						file = new File(tempDir + File.separator + prefix
								+ System.currentTimeMillis() + ".jpg");
						file.deleteOnExit();
						imageFile = file;
					} else if (entry.getName().toUpperCase().endsWith(".GIF")) {
						String prefix = entry.getName().substring(0,
								entry.getName().toUpperCase().indexOf(".GIF"));
						file = new File(tempDir + File.separator + prefix
								+ System.currentTimeMillis() + ".gif");
						file.deleteOnExit();
						imageFile = file;
					} else if (entry.getName().toUpperCase().endsWith(".PNG")) {
						String prefix = entry.getName().substring(0,
								entry.getName().toUpperCase().indexOf(".PNG"));
						file = new File(tempDir + File.separator + prefix
								+ System.currentTimeMillis() + ".png");
						file.deleteOnExit();
						imageFile = file;
					}
					if (file != null) {
						FileOutputStream fos = new FileOutputStream(file);
						int data = 0;
						int count = 0;
						while ((data = zis.read()) != -1) {
							count++;
							fos.write(data);
						}
						fos.flush();
						fos.close();
						zis.closeEntry();
					}
					entry = zis.getNextEntry();
				}
				hero = new Hero(charFile, templateFile, imageFile, openFile);
			}
			hero.setDirty(false);
			openHeroes.add(hero);
			String name = hero.getCharacterName().trim().length() == 0 ? "[unknown]"
					: hero.getCharacterName();
			if (!HeroDesigner.headless) {
				JRadioButtonMenuItem item = new JRadioButtonMenuItem(name
						+ (hero.getSaveFile() != null ? " ["
								+ hero.getSaveFile().getName() + "]" : ""));
				item.addActionListener(new ActionListener() {

					public void actionPerformed(ActionEvent e) {
						int count = windowMenu.getItemCount();
						for (int i = 0; i < count; i++) {
							JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
									.getItem(i);
							if (check.isSelected()) {
								Hero current = openHeroes.get(i);
								setActiveHero(current);
								setTemplateMenuSelection();
								return;
							}
						}
					}
				});
				String c = "" + openHeroes.size();
				if (openHeroes.size() < 10) {
					item.setAccelerator(KeyStroke.getKeyStroke(c.charAt(0),
							java.awt.event.KeyEvent.CTRL_MASK, false));
				}
				windowGroup.add(item);
				windowMenu.add(item);

				if (display) {
					item.setSelected(true);
					HeroDesigner.activeHero = null;
					setActiveHero(hero);
					mainPanel.verify();
					logMemory("Open Character");
					setTemplateMenuSelection();
					hero.setDirty(false);
					setDirty(false);
				} else {
					fireCharacterLoaded(hero, false);
				}
			} else {
				fireCharacterLoaded(hero, false);
			}
			prefs.addRecentFile(openFile);
		} catch (Exception exp) {
			exp.printStackTrace();
			if (!HeroDesigner.headless) {
				JOptionPane
						.showMessageDialog(
								HeroDesigner.getAppFrame(),
								"An error occurred loading your character.\nData loss may occur if the character is saved.\n\nPlease restart HERO Designer and try again.\n\n\nError Message: "
										+ exp.getMessage(),
								"Error loading preferences",
								JOptionPane.ERROR_MESSAGE);
			}
		}
		Utility.garbageCollect("Done Opening Character");
		HeroDesigner.lastEdit = System.currentTimeMillis();

	}

	protected void updateRecentMenu() {
		if (recentMI == null) {
			return;
		}
		recentMI.removeAll();
		for (String path : prefs.getRecentFileList()) {
			File check = new File(path);
			if (check.exists() && check.canRead()) {
				RecentlySelectedMenuItem mi = new RecentlySelectedMenuItem(
						check);
				recentMI.add(mi);
			}
		}
	}

	/**
	 * Updates the status bar.
	 */
	protected void updateStatus() {
		if (statusLbl == null) {
			return;
		}
		String charName = "<b>Character:</b> ";
		String ruleName = "<b>Rules:</b> ";
		String lastSaved = "<b>Last Saved:</b> ";
		String export = "<b>Export Format:</b> ";

		if (HeroDesigner.getActiveHero() != null) {
			if (HeroDesigner.getActiveHero().getSaveFile() != null) {
				try {
					SimpleDateFormat formatter = new SimpleDateFormat(
							"EEE, d MMM yyyy HH:mm:ss");
					if (HeroDesigner.getActiveHero().isDirty()) {
						lastSaved += "<font color=\"red\">";
					}
					lastSaved += " "
							+ formatter.format(new Date(HeroDesigner
									.getActiveHero().getSaveFile()
									.lastModified()));
					if (HeroDesigner.getActiveHero().isDirty()) {
						lastSaved += "</font>";
					}
				} catch (Exception exp) {
					lastSaved += " [unknown]";
				}
			} else {
				lastSaved += " Never";
			}
			if (HeroDesigner.getActiveHero().getExportTemplate() != null) {
				File check = new File(HeroDesigner.getActiveHero()
						.getExportTemplate());
				if (check.exists() && check.isFile()) {
					export += check.getName();
				} else {
					export += "None";
				}
			} else {
				export += "None";
			}
			if (HeroDesigner.getActiveHero().isPrefab()) {
				charName = "<b>Prefab:</b> ";
			}
			if (HeroDesigner.getActiveHero().getCharacterName().trim().length() > 0) {
				charName += HeroDesigner.getActiveHero().getCharacterName();
			} else {
				charName += "[unknown]";
			}

			if (HeroDesigner.getActiveHero().getRules().getName() != null
					&& HeroDesigner.getActiveHero().getRules().getName().trim()
							.length() > 0) {
				ruleName += HeroDesigner.getActiveHero().getRules().getName()
						.trim();
			} else if (HeroDesigner.getActiveHero().getRules().getCampaignSet()
					.trim().length() > 0) {
				ruleName += "Campaign Rules defined in "
						+ HeroDesigner.getActiveHero().getRules()
								.getCampaignSet();
			} else if (HeroDesigner.getActiveHero().getRules().getCurrentSet()
					.equals("Default")) {
				ruleName += "Default Hero System";
			} else {
				String temp = HeroDesigner.getActiveHero().getRules()
						.getCurrentSet();
				while (temp.indexOf(".") < temp.lastIndexOf(".")) {
					temp = temp.substring(temp.indexOf(".") + 1, temp.length());
				}
				if (temp.indexOf(File.separator) > 0) {
					temp = temp.substring(temp.lastIndexOf(File.separator) + 1,
							temp.length());
				}
				ruleName += temp;
			}
		} else {
			charName += " [none]";
		}
		String template = "<b>Template:</b> ";
		if (HeroDesigner.getActiveTemplate() != null) {
			template += HeroDesigner.getActiveTemplate().getName();
		} else {
			template += "[none]";
		}
		statusLbl.setText("<html>" + template + " -- " + ruleName + " -- "
				+ export + " -- " + lastSaved + "</html>");
	}

	/**
	 * Sets the active template to the specified value. This is not a
	 * recommended method to call and may break the application if used
	 * incorrectly. This is only used during an initial load on a Template to
	 * ensure that Characteristics load properly.
	 * 
	 * @param val
	 *            The template to be made active.
	 */
	void setActiveTemplate(Template val) {
		HeroDesigner.activeTemplate = val;
	}

	public JMenuBar getMainMenuBar() {
		return menu;
	}

	/**
	 * Returns a reference to the main content panel (contains the tab panes).
	 */
	public MainContentPanel getMainPanel() {
		return mainPanel;
	}

	/**
	 * Runs through the open prefab list and constructs a Vector containing all
	 * prefabs that have Disadvantages defined.
	 * 
	 * @return aVector of all prefab Disadvantages, each prefab in its own List
	 */
	public ArrayList<GenericObject> getPrefabDisads() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		for (Prefab prefab : loadedPrefabs) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getDisads() == null || prefab.getDisads().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getDisads()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveTemplate().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getDisads() == null || prefab.getDisads().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getDisads()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveHero().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getDisads() == null || prefab.getDisads().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getDisads()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		return ret;
	}

	/**
	 * Runs through the open prefab list and constructs a Vector containing all
	 * prefabs that have Equipment defined.
	 * 
	 * @return aVector of all prefab Equipment, each prefab in its own List
	 */
	public ArrayList<GenericObject> getPrefabEquipment() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		for (Prefab prefab : loadedPrefabs) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getEquipment() == null
					|| prefab.getEquipment().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getEquipment()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveTemplate().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getEquipment() == null
					|| prefab.getEquipment().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getEquipment()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveHero().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getEquipment() == null
					|| prefab.getEquipment().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getEquipment()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		if (getPrefs().getShowPowersPrefabsOnEquipment()) {
			for (Prefab prefab : loadedPrefabs) {
				com.hero.objects.List list = new com.hero.objects.List(
						prefab.getName());
				if (prefab.getPowers() == null
						|| prefab.getPowers().size() == 0) {
					continue;
				}
				ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
				for (GenericObject o : prefab.getPowers()) {
					if (o.getParentList() == null) {
						vec.add(o);
					}
				}
				list.setObjects(vec);
				ret.add(list);
			}
			for (Prefab prefab : HeroDesigner.getActiveTemplate().getPrefabs()) {
				com.hero.objects.List list = new com.hero.objects.List(
						prefab.getName());
				if (prefab.getPowers() == null
						|| prefab.getPowers().size() == 0) {
					continue;
				}
				ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
				for (GenericObject o : prefab.getPowers()) {
					if (o.getParentList() == null) {
						vec.add(o);
					}
				}
				list.setObjects(vec);
				ret.add(list);
			}
			for (Prefab prefab : HeroDesigner.getActiveHero().getPrefabs()) {
				com.hero.objects.List list = new com.hero.objects.List(
						prefab.getName());
				if (prefab.getPowers() == null
						|| prefab.getPowers().size() == 0) {
					continue;
				}
				ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
				for (GenericObject o : prefab.getPowers()) {
					if (o.getParentList() == null) {
						vec.add(o);
					}
				}
				list.setObjects(vec);
				ret.add(list);
			}
		}
		return ret;
	}

	/**
	 * Runs through the open prefab list and constructs a Vector containing all
	 * prefabs that have Maneuvers defined.
	 * 
	 * @return aVector of all prefab Maneuvers, each prefab in its own List
	 */
	public ArrayList<GenericObject> getPrefabManeuvers() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		// if (activeHero == null) return ret;
		for (Prefab prefab : loadedPrefabs) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getManeuvers() == null
					|| prefab.getManeuvers().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getManeuvers()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveTemplate().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getManeuvers() == null
					|| prefab.getManeuvers().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getManeuvers()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveHero().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getManeuvers() == null
					|| prefab.getManeuvers().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getManeuvers()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		return ret;
	}

	/**
	 * Runs through the open prefab list and constructs a Vector containing all
	 * prefabs that have Perks defined.
	 * 
	 * @return aVector of all prefab Perks, each prefab in its own List
	 */
	public ArrayList<GenericObject> getPrefabPerks() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		for (Prefab prefab : loadedPrefabs) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getPerks() == null || prefab.getPerks().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getPerks()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveTemplate().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getPerks() == null || prefab.getPerks().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getPerks()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveHero().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getPerks() == null || prefab.getPerks().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getPerks()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		return ret;
	}

	/**
	 * Runs through the open prefab list and constructs a Vector containing all
	 * prefabs that have Powers defined.
	 * 
	 * @return aVector of all prefab Powers, each prefab in its own List
	 */
	public ArrayList<GenericObject> getPrefabPowers() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		for (Prefab prefab : loadedPrefabs) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getPowers() == null || prefab.getPowers().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getPowers()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveTemplate().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getPowers() == null || prefab.getPowers().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getPowers()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveHero().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getPowers() == null || prefab.getPowers().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getPowers()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		if (getPrefs().getShowEquipmentPrefabsOnPowers()) {
			for (Prefab prefab : loadedPrefabs) {
				com.hero.objects.List list = new com.hero.objects.List(
						prefab.getName());
				if (prefab.getEquipment() == null
						|| prefab.getEquipment().size() == 0) {
					continue;
				}
				ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
				for (GenericObject o : prefab.getEquipment()) {
					if (o.getParentList() == null) {
						vec.add(o);
					}
				}
				list.setObjects(vec);
				ret.add(list);
			}
			for (Prefab prefab : HeroDesigner.getActiveTemplate().getPrefabs()) {
				com.hero.objects.List list = new com.hero.objects.List(
						prefab.getName());
				if (prefab.getEquipment() == null
						|| prefab.getEquipment().size() == 0) {
					continue;
				}
				ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
				for (GenericObject o : prefab.getEquipment()) {
					if (o.getParentList() == null) {
						vec.add(o);
					}
				}
				list.setObjects(vec);
				ret.add(list);
			}
			for (Prefab prefab : HeroDesigner.getActiveHero().getPrefabs()) {
				com.hero.objects.List list = new com.hero.objects.List(
						prefab.getName());
				if (prefab.getEquipment() == null
						|| prefab.getEquipment().size() == 0) {
					continue;
				}
				ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
				for (GenericObject o : prefab.getEquipment()) {
					if (o.getParentList() == null) {
						vec.add(o);
					}
				}
				list.setObjects(vec);
				ret.add(list);
			}
		}
		return ret;
	}

	/**
	 * Runs through the open prefab list and constructs a Vector containing all
	 * prefabs that have Skills defined.
	 * 
	 * @return aVector of all prefab Skills, each prefab in its own List
	 */
	public ArrayList<GenericObject> getPrefabSkills() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		for (Prefab prefab : loadedPrefabs) {
			if (prefab.getSkills() == null || prefab.getSkills().size() == 0) {
				continue;
			}
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getSkills()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveTemplate().getPrefabs()) {
			if (prefab.getSkills() == null || prefab.getSkills().size() == 0) {
				continue;
			}
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getSkills()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveHero().getPrefabs()) {
			if (prefab.getSkills() == null || prefab.getSkills().size() == 0) {
				continue;
			}
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getSkills()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		return ret;
	}

	/**
	 * Runs through the open prefab list and constructs a Vector containing all
	 * prefabs that have Talents defined.
	 * 
	 * @return aVector of all prefab talents, each prefab in its own List
	 */
	public ArrayList<GenericObject> getPrefabTalents() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		for (Prefab prefab : loadedPrefabs) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getTalents() == null || prefab.getTalents().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getTalents()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveTemplate().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getTalents() == null || prefab.getTalents().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getTalents()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		for (Prefab prefab : HeroDesigner.getActiveHero().getPrefabs()) {
			com.hero.objects.List list = new com.hero.objects.List(
					prefab.getName());
			if (prefab.getTalents() == null || prefab.getTalents().size() == 0) {
				continue;
			}
			ArrayList<GenericObject> vec = new ArrayList<GenericObject>();
			for (GenericObject o : prefab.getTalents()) {
				if (o.getParentList() == null) {
					vec.add(o);
				}
			}
			list.setObjects(vec);
			ret.add(list);
		}
		return ret;
	}

	/**
	 * @return The application preferences that the user has defined.
	 */
	public AppPrefs getPrefs() {
		return prefs;
	}

	public ArrayList<Hero> getOpenHeroes() {
		return (ArrayList<Hero>) openHeroes.clone();
	}

	/**
	 * Utility method to log the current memory usage to System.out
	 * 
	 * @param tag
	 *            Identifier for this log message -- included in the output.
	 */
	public void logMemory(String tag) {
		if (HeroDesigner.headless) {
			return;
		}
		SimpleDateFormat formatter = new SimpleDateFormat(
				"EEE, MMM d '@'h:mm:ss a (Z)");
		BigDecimal mm = new BigDecimal(Runtime.getRuntime().maxMemory()
				/ (1024d * 1024d));
		mm = mm.setScale(2, mm.ROUND_HALF_UP);
		BigDecimal tm = new BigDecimal(Runtime.getRuntime().totalMemory()
				/ (1024d * 1024d));
		tm = tm.setScale(2, tm.ROUND_HALF_UP);
		BigDecimal fm = new BigDecimal(Runtime.getRuntime().freeMemory()
				/ (1024d * 1024d));
		fm = fm.setScale(2, tm.ROUND_HALF_UP);
		BigDecimal um = new BigDecimal(
				(Runtime.getRuntime().totalMemory() - Runtime.getRuntime()
						.freeMemory()) / (1024d * 1024d));
		um = um.setScale(2, tm.ROUND_HALF_UP);
		System.out.println(tag + ":  " + formatter.format(new Date())
				+ " build " + HeroDesigner.version + " Memory Usage ("
				+ openHeroes.size() + " characters, " + loadedPrefabs.size()
				+ " prefabs): " + tm.toString() + "MB (total), "
				+ fm.toString() + "MB (free) = " + um.toString() + "MB (used)");
	}

	/**
	 * Sets the active hero to the specified character. If necessary, a screen
	 * refresh will be performed.
	 * 
	 * @param hero
	 *            The character to be made active. Can be null.
	 */
	public void setActiveHero(Hero hero) {
		boolean dirty = false;
		if (hero != null) {
			dirty = hero.isDirty();
		}
		if (!HeroDesigner.headless
				&& mainPanel.isShowing()
				&& HeroDesigner.activeHero != null
				&& hero != null
				&& HeroDesigner.activeHero.getSaveFile() != null
				&& hero.getSaveFile() != null
				&& HeroDesigner.activeHero.getSaveFile().getAbsolutePath()
						.equals(hero.getSaveFile().getAbsolutePath())
				&& HeroDesigner.activeHero.getSaveFile().getName()
						.equals(hero.getSaveFile().getName())) {
			return;
		}
		HeroDesigner.activeHero = hero;
		if (HeroDesigner.activeHero != null) {
			HeroDesigner.lastEdit = hero.lastEdit;
			// if (!hero.getRules().isDefault()) {
			// hero.getRules().loadRulesDefinition(
			// hero.getRules().getCurrentSet());
			// }
			if (HeroDesigner.activeHero.getTemplatePath() != null
					&& HeroDesigner.activeHero.getTemplatePath().trim()
							.length() > 0) {
				setTemplate(
						HeroDesigner.activeHero.getTemplatePath(),
						HeroDesigner.activeHero.getTemplatePath().startsWith(
								"builtIn."), false, false);
			} else {
				HeroDesigner.activeHero.setTemplatePath(HeroDesigner
						.getActiveTemplate().getId());
			}
			HeroDesigner.frame.setCursor(Cursor
					.getPredefinedCursor(Cursor.WAIT_CURSOR));
			setTemplateMenuSelection();
			mainPanel.reset();
			statusLbl.setText("");
			statusLbl.setBorder(BorderFactory.createLoweredBevelBorder());
			statusLbl.setOpaque(true);
			mainPanel.setVisible(true);
			statusLbl.setVisible(true);
			memoryUsagePB.setVisible(true);
			checkAllModsMI.setEnabled(true);
			applyPackageDealMI.setEnabled(true);
			closeMI.setEnabled(true);
			closeAllMI.setEnabled(true);
			saveAllMI.setEnabled(true);
			saveAsMI.setEnabled(true);
			saveAsPackageDealMI.setEnabled(true);
			characterMenu.setEnabled(true);
			tearOffMI.setEnabled(true);
			templateMenu.setEnabled(true);
			prefabMenu.setEnabled(true);
			rulesMenu.setEnabled(true);
			windowMenu.setEnabled(true);
			hero.setDirty(dirty);
			setDirty(hero.isDirty());
			for (GenericObject obj : HeroDesigner.activeHero.getSkills()) {
				obj.verifyModifiers();
			}
			for (GenericObject obj : HeroDesigner.activeHero.getPerks()) {
				obj.verifyModifiers();
			}
			for (GenericObject obj : HeroDesigner.activeHero.getTalents()) {
				obj.verifyModifiers();
			}
			for (GenericObject obj : HeroDesigner.activeHero.getManeuvers()) {
				obj.verifyModifiers();
			}
			for (GenericObject obj : HeroDesigner.activeHero.getPowers()) {
				obj.verifyModifiers();
			}
			for (GenericObject obj : HeroDesigner.activeHero.getEquipment()) {
				obj.verifyModifiers();
			}
			HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			updateStatus();
			if (HeroDesigner.activeHero.isPrefab()) {
				saveAsPrefabMI.setEnabled(false);
				saveAsTemplateMI.setEnabled(false);
				saveMI.setText("Save Prefab");
				saveAsMI.setText("Save Prefab As..");
				characterMenu.setText("Current Prefab");
				closeMI.setText("Close Prefab");
				exportToFileMI.setEnabled(false);
				previewCombatRecordMI.setEnabled(false);
				combatRecordExportMI.setEnabled(false);
				previewExportMI.setEnabled(false);
				printToPDFMI.setEnabled(false);
				setExportTemplateMI.setEnabled(false);
			} else {
				saveAsPrefabMI.setEnabled(true);
				saveAsTemplateMI.setEnabled(true);
				saveMI.setText("Save Character");
				saveAsMI.setText("Save Character As..");
				characterMenu.setText("Current Character");
				closeMI.setText("Close Character");
				exportToFileMI.setEnabled(true);
				previewCombatRecordMI.setEnabled(true);
				combatRecordExportMI.setEnabled(true);
				previewExportMI.setEnabled(true);
				printToPDFMI.setEnabled(true);
				setExportTemplateMI.setEnabled(true);
			}
			fireCharacterLoaded(HeroDesigner.activeHero, true);
		} else if (!HeroDesigner.headless) {
			mainPanel.setVisible(false);
			mainPanel.hideFrames();
			statusLbl.setVisible(false);
			memoryUsagePB.setVisible(false);
			saveMI.setEnabled(false);
			tearOffMI.setEnabled(false);
			checkAllModsMI.setEnabled(false);
			applyPackageDealMI.setEnabled(false);
			closeMI.setEnabled(false);
			closeAllMI.setEnabled(false);
			saveAllMI.setEnabled(false);
			saveAsPrefabMI.setEnabled(false);
			saveAsTemplateMI.setEnabled(false);
			saveAsMI.setEnabled(false);
			saveAsPackageDealMI.setEnabled(false);
			characterMenu.setEnabled(false);
			exportToFileMI.setEnabled(false);
			previewCombatRecordMI.setEnabled(false);
			combatRecordExportMI.setEnabled(false);
			previewExportMI.setEnabled(false);
			printToPDFMI.setEnabled(false);
			setExportTemplateMI.setEnabled(false);
			templateMenu.setEnabled(false);
			prefabMenu.setEnabled(false);
			rulesMenu.setEnabled(false);
			windowMenu.setEnabled(false);
			HeroDesigner.frame.validate();
		}
		if (!HeroDesigner.headless && HeroDesigner.activeHero != null) {
			HeroDesigner.frame.setTitle("HERO Designer 6 [build "
					+ HeroDesigner.version
					+ "] -- "
					+ HeroDesigner.activeHero.getCharacterName()
					+ (HeroDesigner.activeHero.getSaveFile() != null ? " ["
							+ HeroDesigner.activeHero.getSaveFile().getName()
							+ "]" : ""));
		} else if (!HeroDesigner.headless) {
			HeroDesigner.frame.setTitle("HERO Designer 6 [build "
					+ HeroDesigner.version + "] -- [no characters open]");
		}
		updateStatus();
		Utility.garbageCollect("Changed Characters");
	}

	/**
	 * Sets the name of the active character. This is really only used to update
	 * the status/title bar.
	 * 
	 * @param name
	 *            the name of the active character.
	 */
	public void setActiveName(String name) {
		int count = windowMenu.getItemCount();
		for (int i = 0; i < count; i++) {
			JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
					.getItem(i);
			if (check.isSelected()) {
				if (name != null && name.trim().length() > 0) {
					check.setText(name
							+ (HeroDesigner.activeHero.getSaveFile() != null ? " ["
									+ HeroDesigner.activeHero.getSaveFile()
											.getName() + "]"
									: ""));
					HeroDesigner.frame
							.setTitle("HERO Designer 6 [build "
									+ HeroDesigner.version
									+ "] -- "
									+ HeroDesigner.activeHero
											.getCharacterName()
									+ (HeroDesigner.activeHero.getSaveFile() != null ? " ["
											+ HeroDesigner.activeHero
													.getSaveFile().getName()
											+ "]" : ""));
				} else {
					check.setText("[unknown]");
					HeroDesigner.frame.setTitle("HERO Designer 6 [build "
							+ HeroDesigner.version + "] -- [unknown]");
				}
				return;
			}
		}
		updateStatus();
	}

	public void setCopyModBuffer(ArrayList<Modifier> mods) {
		mods = (ArrayList<Modifier>) mods.clone();
		for (int i = 0; i < mods.size(); i++) {
			Modifier mod = mods.get(i);
			mod = (Modifier) mod.clone();
			mod.setParent(null);
			mods.set(i, mod);
		}
		copiedMods = mods;
	}

	public ArrayList<Modifier> getCopyModBuffer() {
		return copiedMods;
	}

	/**
	 * Sets the internal copy buffer.
	 * 
	 * @param o
	 *            the value for the internal copy buffer. Can be null.
	 */
	public void setCopyBuffer(GenericObject o) {
		if (o != null) {
			GenericObject cl = o.clone();
			cl.setParent(null);
			cl.setMainPower(null);
			cl.resetID();
			cl.setIsEquipment(false);
			cl.setPower(false);
			if (cl instanceof com.hero.objects.List) {
				com.hero.objects.List list = (com.hero.objects.List) cl;
				ArrayList<GenericObject> vec = list.getObjects();
				list.setObjects(new ArrayList<GenericObject>());
				for (int i = 0; i < vec.size(); i++) {
					GenericObject o2 = vec.get(i).clone();
					o2.setPower(false);
					o2.setIsEquipment(false);
					list.addObject(o);
				}
			}
			HeroDesigner.copyBuffer = cl;
		}
		HeroDesigner.copyBuffer = o;
	}

	/**
	 * Sepcifies whether changes have been made to the active character/prefab.
	 * Sets the state of the File menu items appropriately.
	 * 
	 * @param dirty
	 */
	public void setDirty(boolean dirty) {
		if (dirty) {
			HeroDesigner.lastEdit = System.currentTimeMillis();
			updateStatus();
		}
		saveMI.setEnabled(dirty);
	}

	/**
	 * Adds the specified template to the template extension chain. If an
	 * existing extension hierarchy exists, the new template hierarchy is added
	 * to the end of the existing one, before any built-in templates.
	 * 
	 * @param file
	 *            The template file to load
	 */
	public void addTemplate(String file) {
		if (file == null) {
			return;
		}
		if (file.startsWith("builtIn")) {
			return;
		}
		if (file.indexOf("Sample Templates") > 0) {
			JOptionPane
					.showMessageDialog(
							HeroDesigner.frame,
							"You appear to be using one of the sample templates"
									+ " for your character.\n  It is strongly recommended that you do not do this.\n\n"
									+ " Either use the built in templates (via the Template menu), or use a custom template.\n\n"
									+ " If you are storing your custom templates in the Sample Templates directory,\n "
									+ "you should move them to prevent this error from repeating.",
							"Sample Templates use detected",
							JOptionPane.WARNING_MESSAGE);
		}
		if (!HeroDesigner.headless) {
			HeroDesigner.frame.setCursor(Cursor
					.getPredefinedCursor(Cursor.WAIT_CURSOR));
		}
		if (HeroDesigner.getActiveHero() != null) {
			HeroDesigner.getActiveHero().setIsLoading(true);
		}
		String baseFile = file;

		// if (mainPanel != null) mainPanel.updatePrefabs();

		try {
			if (HeroDesigner.getActiveTemplate() != null) {
				if (HeroDesigner.getActiveTemplate().getId().equals(file)) {
					if (!HeroDesigner.headless) {
						HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
					}
					return;
				}
			}
			if (HeroDesigner.getActiveHero() != null) {
				CHECK: while (HeroDesigner.getActiveHero().isDirty()) {
					String name = HeroDesigner.getActiveHero()
							.getCharacterName();
					if (name.trim().length() == 0) {
						name = "[unknown]";
					}
					int response = JOptionPane.NO_OPTION;
					if (!HeroDesigner.headless) {
						response = JOptionPane
								.showConfirmDialog(
										HeroDesigner.frame,
										name
												+ " has been changed!\n\nSave changes before adding in the new template?");
					}
					if (response == JOptionPane.CANCEL_OPTION) {
						setTemplateMenuSelection();
						if (!HeroDesigner.headless) {
							HeroDesigner.frame.setCursor(new Cursor(
									Cursor.DEFAULT_CURSOR));
						}
						return;
					} else if (response == JOptionPane.YES_OPTION) {
						saveMI.doClick();
					} else if (response == JOptionPane.NO_OPTION) {
						break CHECK;
					}
				}
			}
			Hero hero = HeroDesigner.getActiveHero();
			Template temp = null;
			// if (activeTemplate != null)
			// activeTemplate.destroy();
			// activeTemplate = null;
			logMemory("Loading template extension (" + file + ")");
			Template current = HeroDesigner.getActiveTemplate();
			if (current.getId().startsWith("builtIn")) {
				current = current.clone();
			}

			temp = Template.getTemplate(file);
			logMemory("Template file loaded.");
			if (temp == null) {
				HeroDesigner.getActiveHero().setIsLoading(false);
				if (!HeroDesigner.headless) {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
				if (!HeroDesigner.headless) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.getAppFrame(),
									"An error occurred loading the template \""
											+ file
											+ "\".\nThe template file specified was not found on the system.\n\nThe currently loaded template will be used instead.",
									"Error loading template",
									JOptionPane.ERROR_MESSAGE);
				}
			} else {
				logMemory("Adding template to extension hierarchy...");
				current.addExtensionTemplate(temp);
				HeroDesigner.getInstance().setActiveTemplate(current);
				logMemory("Resetting prefabs...");
				resetPrefabs();
				logMemory("Resetting character...");
				hero.resetTemplate();
				hero.setDirty(true);
				logMemory("Resetting UI...");
				mainPanel.recalcAll();
				logMemory("Re-Checking Modifiers...");
				statusLbl.setText("");
				setDirty(hero.isDirty());
				for (GenericObject obj : HeroDesigner.activeHero.getSkills()) {
					obj.verifyModifiers();
				}
				for (GenericObject obj : HeroDesigner.activeHero.getPerks()) {
					obj.verifyModifiers();
				}
				for (GenericObject obj : HeroDesigner.activeHero.getTalents()) {
					obj.verifyModifiers();
				}
				for (GenericObject obj : HeroDesigner.activeHero.getManeuvers()) {
					obj.verifyModifiers();
				}
				for (GenericObject obj : HeroDesigner.activeHero.getPowers()) {
					obj.verifyModifiers();
				}
				for (GenericObject obj : HeroDesigner.activeHero.getEquipment()) {
					obj.verifyModifiers();
				}
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				HeroDesigner.getActiveHero().setIsLoading(false);
				fireTemplateChanged(HeroDesigner.activeTemplate);
				logMemory("Character reset, template extension added.");
			}
		} catch (Exception exp) {
			JOptionPane.showMessageDialog(HeroDesigner.getAppFrame(),
					"An error occurred loading the selected template.\n\nError Message: "
							+ exp.getMessage(), "Error loading template",
					JOptionPane.ERROR_MESSAGE);
			exp.printStackTrace();
		} finally {
			if (!HeroDesigner.headless) {
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
			HeroDesigner.getActiveHero().setIsLoading(false);
		}
		Utility.garbageCollect("Set Template");
		if (!HeroDesigner.headless) {
			updateStatus();
		}
	}

	/**
	 * Sets the active template. This method will fully load the specified
	 * template file and wil perform all operations necessary to refresh any
	 * affected character files (active character and loaded prefabs)
	 * 
	 * @param file
	 *            The template file to load
	 * @param builtIn
	 *            Whether the template is a builtin template file.
	 * @param warn
	 *            Warn if the character is unsaved.
	 * @param setHero
	 *            Set the character to be active after the template change.
	 */
	public void setTemplate(String file, boolean builtIn, boolean warn,
			boolean setHero) {
		if (file == null) {
			return;
		}
		if ((builtIn || file.startsWith("builtIn"))
				&& !(file.indexOf("6E") > 0)) {
			file = convertOldNames(file);
		}
		if (file.indexOf("Sample Templates") > 0) {
			JOptionPane
					.showMessageDialog(
							HeroDesigner.frame,
							"You appear to be using one of the sample templates"
									+ " for your character.\n  It is strongly recommended that you do not do this.\n\n"
									+ " Either use the built in templates (via the Template menu), or use a custom template.\n\n"
									+ " If you are storing your custom templates in the Sample Templates directory,\n "
									+ "you should move them to prevent this error from repeating.",
							"Sample Templates use detected",
							JOptionPane.WARNING_MESSAGE);
		}
		if (!HeroDesigner.headless) {
			HeroDesigner.frame.setCursor(Cursor
					.getPredefinedCursor(Cursor.WAIT_CURSOR));
		}
		if (HeroDesigner.getActiveHero() != null) {
			HeroDesigner.getActiveHero().setIsLoading(true);
		}
		String baseFile = file;
		ArrayList<Prefab> holder = loadedPrefabs;
		loadedPrefabs = new ArrayList<Prefab>();

		// if (mainPanel != null) mainPanel.updatePrefabs();
		if (baseFile.startsWith("builtIn.")) {
			baseFile = baseFile.substring(baseFile.indexOf(".") + 1,
					baseFile.length());
		}
		try {
			if (builtIn && !file.startsWith("builtIn.")) {
				file = "builtIn." + file;
			}
			if (HeroDesigner.getActiveTemplate() != null) {
				if (HeroDesigner.getActiveTemplate().getId().equals(file)) {
					if (!HeroDesigner.headless) {
						HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
					}
					return;
				}
			}
			if (HeroDesigner.getActiveHero() != null) {
				CHECK: while (HeroDesigner.getActiveHero().isDirty() && warn) {
					String name = HeroDesigner.getActiveHero()
							.getCharacterName();
					if (name.trim().length() == 0) {
						name = "[unknown]";
					}
					int response = JOptionPane.NO_OPTION;
					if (!HeroDesigner.headless) {
						response = JOptionPane
								.showConfirmDialog(
										HeroDesigner.frame,
										name
												+ " has been changed!\n\nSave changes before switching templates?");
					}
					if (response == JOptionPane.CANCEL_OPTION) {
						setTemplateMenuSelection();
						if (!HeroDesigner.headless) {
							HeroDesigner.frame.setCursor(new Cursor(
									Cursor.DEFAULT_CURSOR));
						}
						return;
					} else if (response == JOptionPane.YES_OPTION) {
						saveMI.doClick();
					} else if (response == JOptionPane.NO_OPTION) {
						break CHECK;
					}
				}
			}
			Hero hero = HeroDesigner.getActiveHero();
			if (setHero) {
				setActiveHero(null);
			}
			Template temp = null;
			// if (activeTemplate != null)
			// activeTemplate.destroy();
			// activeTemplate = null;
			if (file.startsWith("builtIn.")) {
				temp = Template.getTemplate(
						ClassLoader.getSystemResourceAsStream(baseFile), file);
			} else {
				temp = Template.getTemplate(file);
			}
			if (temp == null) {
				if (!HeroDesigner.headless) {
					HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
				}
				loadedPrefabs = holder;
				if (!HeroDesigner.headless) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.getAppFrame(),
									"An error occurred loading the template \""
											+ file
											+ "\".\nThe template file specified was not found on the system.\n\nThe currently loaded template will be used instead.",
									"Error loading template",
									JOptionPane.ERROR_MESSAGE);
				}
			} else {
				HeroDesigner.activeTemplate = temp;
				if (HeroDesigner.activeTemplate.isVersion1Style()) {
					if (!HeroDesigner.headless) {
						JOptionPane
								.showMessageDialog(
										HeroDesigner.frame,
										"<html>You appear to be using a template from an earlier version of HERO Designer.<br><br>"
												+ "It is <b>strongly</b> recommended that you do not do this.<br><br>"
												+ " Either use the built in templates (via the Template menu), or use a custom template"
												+ " which was created for HERO Designer version 2 or higher.</html>",
										"Version 1 Template Detected",
										JOptionPane.WARNING_MESSAGE);
					}
				}
				resetPrefabs();
				if (setHero
						&& hero != null
						&& (HeroDesigner.activeTemplate == null
								|| hero.getTemplatePath() == null || !hero
								.getTemplatePath().equals(
										HeroDesigner.activeTemplate.getId()))) {
					hero.resetTemplate();
				}
				if (setHero
						&& hero != null
						&& (HeroDesigner.activeTemplate == null
								|| hero.getTemplatePath() == null || !hero
								.getTemplatePath().equals(
										HeroDesigner.activeTemplate.getId()))) {
					hero.setDirty(true);
				}
				if (setHero) {
					setActiveHero(hero);
				}
				// else if (mainPanel != null) {
				// mainPanel.reset();
				// }
				if (setHero && hero != null
						&& HeroDesigner.activeTemplate != null) {
					hero.setTemplatePath(HeroDesigner.activeTemplate.getId());
				}
				fireTemplateChanged(HeroDesigner.activeTemplate);
			}
		} catch (Exception exp) {
			JOptionPane.showMessageDialog(HeroDesigner.getAppFrame(),
					"An error occurred loading the selected template.\n\nError Message: "
							+ exp.getMessage(), "Error loading template",
					JOptionPane.ERROR_MESSAGE);
			exp.printStackTrace();
		} finally {
			if (!HeroDesigner.headless) {
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
			if (holder == null) {
				holder = new ArrayList<Prefab>();
			}
			for (int i = holder.size() - 1; i >= 0; i--) {
				try {
					Prefab p = holder.get(i);
					p.initFromSave();
				} catch (Exception exp) {
					holder.remove(i);
				}
			}
			loadedPrefabs = holder;
			if (HeroDesigner.activeHero != null) {
				if (!HeroDesigner.headless) {
					mainPanel.reset();
					mainPanel.updateTabs();
					mainPanel.updatePrefabs();
				}
				HeroDesigner.getActiveHero().setIsLoading(false);
			}
		}
		Utility.garbageCollect("Set Template");
		if (!HeroDesigner.headless) {
			updateStatus();
		}
	}

	private ArrayList<String> exts = null;

	private void setDefaultExportFormat(Hero hero) {
		String formatPath = getDefaultExportFormatPath(HeroDesigner
				.getActiveTemplate());
		if (formatPath != null) {
			File test = new File(formatPath);
			if (!test.exists() || test.isDirectory()) {
				test = new File(getPrefs().getExportTemplateDir()
						+ File.separator + test.getName());
				if (!test.exists() || test.isDirectory()) {
					HeroDesigner.getActiveHero().setExportTemplate(null);
				} else {
					HeroDesigner.getActiveHero().setExportTemplate(
							test.getAbsolutePath());
				}
			} else {
				HeroDesigner.getActiveHero().setExportTemplate(
						test.getAbsolutePath());
			}
		}
	}

	private String getDefaultExportFormatPath(Template temp) {
		String id = temp.getId();
		if (id.startsWith("builtIn")) {
			id = id.substring(8, id.length());
		}

		if (id.startsWith("AI")) {
			return getPrefs().getDefaultAIFormatPath();
		} else if (id.startsWith("Automaton")) {
			return getPrefs().getDefaultAutomatonFormatPath();
		} else if (id.startsWith("Base")) {
			return getPrefs().getDefaultBaseFormatPath();
		} else if (id.startsWith("Computer")) {
			return getPrefs().getDefaultComputerFormatPath();
		} else if (id.startsWith("Heroic")) {
			return getPrefs().getDefaultHeroicFormatPath();
		} else if (id.startsWith("Normal")) {
			return getPrefs().getDefaultNormalFormatPath();
		} else if (id.startsWith("Superheroic")) {
			return getPrefs().getDefaultSuperheroicFormatPath();
		} else if (id.startsWith("Main")) {
			return getPrefs().getDefaultSuperheroicFormatPath();
		} else if (id.startsWith("Vehicle")) {
			return getPrefs().getDefaultVehicleFormatPath();
		} else if (temp.getParentTemplates() != null) {
			for (Template t : temp.getParentTemplates()) {
				String test = getDefaultExportFormatPath(t);
				if (test != null) {
					return test;
				}
			}
		}

		return null;
	}

	private void exportActiveCharacter() {
		JFileChooser chooser = null;
		JFileChooser imageChooser = null;
		File saveFile = null;
		File imageSaveFile = null;
		ExportTemplateDialog dialog = null;
		if (HeroDesigner.getActiveHero().getExportTemplate() == null) {
			setDefaultExportFormat(HeroDesigner.getActiveHero());
		}
		if (HeroDesigner.getActiveHero().getExportTemplate() != null) {
			File test = new File(HeroDesigner.getActiveHero()
					.getExportTemplate());
			if (!test.exists() || test.isDirectory()) {
				test = new File(getPrefs().getExportTemplateDir()
						+ File.separator + test.getName());
				if (!test.exists() || test.isDirectory()) {
					HeroDesigner.getActiveHero().setExportTemplate(null);
				} else {
					HeroDesigner.getActiveHero().setExportTemplate(
							test.getAbsolutePath());
				}
			}
		}
		while (HeroDesigner.getActiveHero().getExportTemplate() == null
				|| HeroDesigner.getActiveHero().getExportTemplate().trim()
						.length() == 0) {
			if (dialog == null) {
				dialog = new ExportTemplateDialog();
			}

			dialog.setDialogTitle("Select Export Format for "
					+ HeroDesigner.getActiveHero().getCharacterName());
			dialog.showOpenDialog(HeroDesigner.frame);
			if (dialog.canceled()) {
				return;
			} else {
				String path = dialog.getSelectedPath();
				if (path != null && path.trim().length() > 0) {
					File test = new File(path);
					if (test.exists()) {
						HeroDesigner.getActiveHero().setExportTemplate(path);
						prefs.setExportTemplateDir(new File(path).getParent());
					}
				}
			}
		}
		while (saveFile == null) {
			try {
				chooser = new JFileChooser();
				HTMLWriter writer = new HTMLWriter(new File(HeroDesigner
						.getActiveHero().getExportTemplate()));
				exts = writer.getFileExtensions();
				chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {

					public boolean accept(File file) {
						if (file.isDirectory()
								&& !file.getName().toUpperCase()
										.endsWith(".PKG")
								&& !file.getName().toUpperCase()
										.endsWith(".APP")) {
							return true;
						}
						if (exts == null || exts.size() == 0) {
							return true;
						}
						String name = file.getName();
						String ex = name.substring(name.lastIndexOf(".") + 1,
								name.length());
						if (exts.contains(ex.trim().toUpperCase())) {
							return true;
						} else {
							return false;
						}
					}

					public String getDescription() {
						String des = "";
						if (exts == null || exts.size() == 0) {
							return "All Files";
						} else {
							for (String s : exts) {
								if (des.length() > 0) {
									des += ", ";
								}
								des += "*." + s;
							}
						}
						return des;
					}
				});
				chooser.setApproveButtonText("Export");
				chooser.setDialogTitle("Export "
						+ HeroDesigner.getActiveHero().getCharacterName()
						+ " to File");
				chooser.setFileSelectionMode(chooser.FILES_ONLY);
				chooser.setMultiSelectionEnabled(false);
				chooser.setAcceptAllFileFilterUsed(false);
				if (prefs.getExportDir() != null
						&& prefs.getExportDir().trim().length() > 0) {
					try {
						chooser.setCurrentDirectory(new File(prefs
								.getExportDir()));
					} catch (Exception ex) {
					}
				}
				if (HeroDesigner.getActiveHero().getCharacterName() != null
						&& HeroDesigner.getActiveHero().getCharacterName()
								.trim().length() > 0) {
					try {
						File f = new File(HeroDesigner.getActiveHero()
								.getCharacterName() + "." + exts.get(0));
						chooser.setSelectedFile(f);
					} catch (Exception ex) {
					}
				}
				int ret = chooser.showSaveDialog(HeroDesigner.frame);
				if (ret == chooser.CANCEL_OPTION) {
					return;
				} else if (ret == chooser.APPROVE_OPTION) {
					saveFile = chooser.getSelectedFile();
					prefs.setExportDir(saveFile.getParent());
					String name = saveFile.getName();
					String ext = ".txt";
					if (exts != null && exts.size() > 0) {
						ext = "." + exts.get(0);
					}
					if (name.indexOf(".") < 0) {
						name = name + ext;
					}
					if (!name.equals(saveFile.getName())) {
						String path = "";
						if (saveFile.getParentFile() != null) {
							path = saveFile.getParent();
							if (!path.endsWith(saveFile.separator)) {
								path = path + saveFile.separator;
							}
						}
						saveFile = new File(path + name);
					}
					if (saveFile.exists()) {
						int response = JOptionPane.YES_OPTION;
						if (!HeroDesigner.headless) {
							JOptionPane.showConfirmDialog(HeroDesigner.frame,
									saveFile.getName()
											+ " already exists.\n\nOverwrite?");
						}
						if (response == JOptionPane.CANCEL_OPTION) {
							return;
						} else if (response == JOptionPane.NO_OPTION) {
							saveFile = null;
						}
					}
				}
			} catch (Exception exp) {
			}
		}
		if (HeroDesigner.getActiveHero().getImageData() != null
				&& HeroDesigner.getActiveHero().getImageData().length > 0
				&& HeroDesigner.getActiveHero().getImageFileName().trim()
						.length() > 0) {

			int response = JOptionPane.YES_OPTION;
			if (getPrefs().getPromptToExportImage() && HeroDesigner.headless) {
				response = JOptionPane
						.showConfirmDialog(
								HeroDesigner.frame,
								"Do you wish to include the image associated\nwith this character with the export file?");
			}
			if (response == JOptionPane.YES_OPTION) {

				if (imageChooser == null) {
					imageChooser = new JFileChooser();
					imageChooser
							.setFileFilter(new javax.swing.filechooser.FileFilter() {

								public boolean accept(File file) {
									if (file.isDirectory()
											&& !file.getName().toUpperCase()
													.endsWith(".PKG")
											&& !file.getName().toUpperCase()
													.endsWith(".APP")) {
										return true;
									}
									if (file.getName().toUpperCase()
											.endsWith(".GIF")) {
										return true;
									}
									if (file.getName().toUpperCase()
											.endsWith(".JPG")) {
										return true;
									}
									if (file.getName().toUpperCase()
											.endsWith(".JPEG")) {
										return true;
									} else {
										return false;
									}
								}

								public String getDescription() {
									return "Images Only";
								}
							});
					imageChooser.setApproveButtonText("Save Image Copy");
					imageChooser.setDialogTitle("Save Image Copy for "
							+ HeroDesigner.getActiveHero().getCharacterName()
							+ " Export");
					imageChooser.setFileSelectionMode(chooser.FILES_ONLY);
					imageChooser.setMultiSelectionEnabled(false);
					imageChooser.setAcceptAllFileFilterUsed(false);
				}
				if (prefs.getExportDir() != null
						&& prefs.getExportDir().trim().length() > 0) {
					try {
						imageChooser.setCurrentDirectory(saveFile
								.getParentFile());
					} catch (Exception ex) {
					}
				}
				String dir = saveFile.getParent();
				imageSaveFile = null;
				if (HeroDesigner.getActiveHero().getImageFileName() != null
						&& HeroDesigner.getActiveHero().getImageFileName()
								.trim().length() > 0) {
					imageChooser.setSelectedFile(new File(HeroDesigner
							.getActiveHero().getImageFileName()));
				}
				while (imageSaveFile == null) {
					int ret = imageChooser.showSaveDialog(HeroDesigner.frame);
					if (ret == imageChooser.CANCEL_OPTION) {
						return;
					} else if (ret == imageChooser.APPROVE_OPTION) {
						imageSaveFile = imageChooser.getSelectedFile();
						String name = imageSaveFile.getName();
						String origName = HeroDesigner.getActiveHero()
								.getImageFileName();
						String origExt = "";
						if (origName == null || origName.trim().length() == 0
								|| origName.lastIndexOf(".") < 0) {
							origExt = ".jpg";
						} else {
							origExt = origName.substring(
									origName.lastIndexOf("."),
									origName.length());
						}
						if (name.indexOf(".") < 0) {
							name = name + origExt;
						}
						if (!name.equals(imageSaveFile.getName())) {
							String path = "";
							if (imageSaveFile.getParentFile() != null) {
								path = imageSaveFile.getParent();
								if (!path.endsWith(imageSaveFile.separator)) {
									path = path + imageSaveFile.separator;
								}
							}
							imageSaveFile = new File(path + name);
						}
						if (imageSaveFile.exists()) {
							int res = JOptionPane.YES_OPTION;
							if (!HeroDesigner.headless) {
								JOptionPane
										.showConfirmDialog(
												HeroDesigner.frame,
												imageSaveFile.getName()
														+ " already exists.\n\nOverwrite?");
							}
							if (res == JOptionPane.CANCEL_OPTION) {
								return;
							} else if (res == JOptionPane.NO_OPTION) {
								imageSaveFile = null;
							}
						}
					}
				}

			} else {
				imageSaveFile = null;
			}

		}
		try {
			HeroDesigner.frame.setCursor(Cursor
					.getPredefinedCursor(Cursor.WAIT_CURSOR));
			HTMLWriter writer = new HTMLWriter(new File(HeroDesigner
					.getActiveHero().getExportTemplate()));
			String html = writer.generateOutput(imageSaveFile == null ? ""
					: imageSaveFile.getName());
			FileOutputStream fos = new FileOutputStream(saveFile);
			fos.write(html.getBytes());
			fos.flush();
			fos.close();
			if (imageSaveFile != null) {
				// copy the image file...
				FileOutputStream out = new FileOutputStream(imageSaveFile);
				out.write(HeroDesigner.getActiveHero().getImageData());
				out.flush();
				out.close();
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			if (!HeroDesigner.headless) {
				JOptionPane.showMessageDialog(HeroDesigner.getAppFrame(),
						"An error occurred during export.\nPlease try again.\n\nError Message: "
								+ ex.getMessage(), "Error exporting to file",
						JOptionPane.ERROR_MESSAGE);
			}
		} finally {
			if (!HeroDesigner.headless) {
				HeroDesigner.frame.setCursor(Cursor.getDefaultCursor());
			}
		}
	}

	public String getActiveExport(File exportFormat, File image)
			throws Exception {
		HTMLWriter writer = new HTMLWriter(exportFormat);
		String html = writer.generateOutput(image == null ? "" : image
				.getName());
		return html;
	}

	public void closeAll() {
		for (int i = openHeroes.size() - 1; i >= 0; --i) {
			Hero hero = openHeroes.get(i);
			HeroDesigner.activeHero = hero;

			openHeroes.remove(i);
			if (!HeroDesigner.headless) {
				JRadioButtonMenuItem check = (JRadioButtonMenuItem) windowMenu
						.getItem(i);
				windowGroup.remove(check);
				windowMenu.remove(check);
			}
			logMemory("Close Character");
		}
		setActiveHero(null);
		if (!HeroDesigner.headless) {
			setActiveCharacterMenu();
		}
	}

	private HeroDesignerIntegration pdfOMatic;
	private Thread pdfScriptRunnerThread;

	private void initializePdfPrinter() {
		pdfScriptRunnerThread = new Thread() {
			public void run() {
				pdfOMatic = new HeroDesignerIntegration();
			}
		};
		pdfScriptRunnerThread.start();
	}

	private HeroDesignerIntegration getPdfOMatic() {
		try {
			if (pdfOMatic == null) {
				pdfScriptRunnerThread.join();
			}
		} catch (InterruptedException e) {
			System.err.println("Error initializing PDF subsystem");
		}
		return pdfOMatic;
	}
}
