/*
* $Id$
*
* Copyright (c) 2000-2008 by Brent Easton, Rodney Kinney, Joel Uckelman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.launch;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import org.apache.commons.lang.SystemUtils;
import VASSAL.build.GameModule;
import VASSAL.build.module.Documentation;
import VASSAL.build.module.documentation.HelpWindow;
import VASSAL.configure.ConfigureTree;
import VASSAL.configure.ModuleUpdaterDialog;
import VASSAL.configure.SaveAction;
import VASSAL.configure.SaveAsAction;
import VASSAL.configure.ShowHelpAction;
import VASSAL.configure.ValidationReport;
import VASSAL.configure.ValidationReportDialog;
import VASSAL.i18n.Resources;
import VASSAL.tools.ApplicationIcons;
import VASSAL.tools.ErrorDialog;
import VASSAL.tools.URLUtils;
import VASSAL.tools.menu.ChildProxy;
import VASSAL.tools.menu.MenuBarProxy;
import VASSAL.tools.menu.MenuManager;
import VASSAL.tools.menu.MenuProxy;
/**
* EditorWindow is the base class for the three top-level component
* editors :- ModuleEditorWindow, ExtensionEditorWindow, PluginEditorWindow
*
* @author Brent Easton
*/
public abstract class EditorWindow extends JFrame {
private static final long serialVersionUID = 1L;
protected SaveAction saveAction;
protected SaveAsAction saveAsAction;
protected JMenuItem componentHelpItem;
protected Action createUpdater;
protected final HelpWindow helpWindow = new HelpWindow(
Resources.getString("Editor.ModuleEditor.reference_manual"), //$NON-NLS-1$
null
);
protected ConfigureTree tree;
public abstract String getEditorType();
protected final JToolBar toolBar = new JToolBar();
protected final JScrollPane scrollPane;
protected EditorWindow() {
setTitle("VASSAL " + getEditorType() + " Editor");
setLayout(new BorderLayout());
ApplicationIcons.setFor(this);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
close();
}
});
toolBar.setFloatable(false);
add(toolBar, BorderLayout.NORTH);
// setup menubar and actions
final MenuManager mm = MenuManager.getInstance();
final MenuBarProxy mb = mm.getMenuBarProxyFor(this);
// file menu
if (SystemUtils.IS_OS_MAC_OSX) {
mm.addToSection("Editor.File", mm.addKey("Editor.save"));
mm.addToSection("Editor.File", mm.addKey("Editor.save_as"));
}
else {
final MenuProxy fileMenu =
new MenuProxy(Resources.getString("General.file"));
// FIMXE: setting nmemonic from first letter could cause collisions in
// some languages
fileMenu.setMnemonic(Resources.getString("General.file.shortcut").charAt(0));
fileMenu.add(mm.addKey("Editor.save"));
fileMenu.add(mm.addKey("Editor.save_as"));
fileMenu.addSeparator();
fileMenu.add(mm.addKey("General.quit"));
mb.add(fileMenu);
}
// edit menu
final MenuProxy editMenu =
new MenuProxy(Resources.getString("General.edit"));
editMenu.setMnemonic(Resources.getString("General.edit.shortcut").charAt(0));
editMenu.add(mm.addKey("Editor.cut"));
editMenu.add(mm.addKey("Editor.copy"));
editMenu.add(mm.addKey("Editor.paste"));
editMenu.add(mm.addKey("Editor.move"));
editMenu.addSeparator();
editMenu.add(mm.addKey("Editor.ModuleEditor.properties"));
editMenu.add(mm.addKey("Editor.ModuleEditor.translate"));
// tools menu
final MenuProxy toolsMenu =
new MenuProxy(Resources.getString("General.tools"));
toolsMenu.setMnemonic(Resources.getString("General.tools.shortcut").charAt(0));
toolsMenu.add(mm.addKey("create_module_updater"));
toolsMenu.add(mm.addKey("Editor.ModuleEditor.update_saved"));
if (SystemUtils.IS_OS_MAC_OSX) {
mm.addToSection("Editor.MenuBar", editMenu);
mm.addToSection("Editor.MenuBar", toolsMenu);
}
else {
mb.add(editMenu);
mb.add(toolsMenu);
}
// help menu
if (SystemUtils.IS_OS_MAC_OSX) {
mm.addToSection("Documentation.VASSAL",
mm.addKey("Editor.ModuleEditor.reference_manual"));
}
else {
final MenuProxy helpMenu =
new MenuProxy(Resources.getString("General.help"));
// FIMXE: setting nmemonic from first letter could cause collisions in
// some languages
helpMenu.setMnemonic(Resources.getString("General.help.shortcut").charAt(0));
helpMenu.add(mm.addKey("General.help"));
helpMenu.add(mm.addKey("Help.user_guide"));
helpMenu.add(mm.addKey("Editor.ModuleEditor.reference_manual"));
helpMenu.addSeparator();
helpMenu.add(mm.addKey("AboutScreen.about_vassal"));
mb.add(helpMenu);
}
final int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
saveAction = new SaveAction() {
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
save();
treeStateChanged(false);
}
};
saveAction.setEnabled(false);
saveAction.putValue(Action.ACCELERATOR_KEY,
KeyStroke.getKeyStroke(KeyEvent.VK_S, mask));
mm.addAction("Editor.save", saveAction);
toolBar.add(saveAction);
saveAsAction = new SaveAsAction() {
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
saveAs();
treeStateChanged(false);
}
};
saveAsAction.setEnabled(false);
saveAsAction.putValue(Action.ACCELERATOR_KEY,
KeyStroke.getKeyStroke(KeyEvent.VK_A, mask));
mm.addAction("Editor.save_as", saveAsAction);
toolBar.add(saveAsAction);
mm.addAction("General.quit", new ShutDownAction());
// FXIME: mnemonics should be language-dependant
// mm.getAction("General.quit").setMnemonic('Q');
createUpdater = new AbstractAction(
"Create " + getEditorType() + " updater") {
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
new ModuleUpdaterDialog(EditorWindow.this).setVisible(true);
}
};
createUpdater.setEnabled(false);
mm.addAction("create_module_updater", createUpdater);
try {
final URL url = new File(Documentation.getDocumentationBaseDir(),
"README.html").toURI().toURL();
mm.addAction("General.help", new ShowHelpAction(url, null));
}
catch (MalformedURLException e) {
ErrorDialog.bug(e);
}
try {
final URL url = new File(Documentation.getDocumentationBaseDir(),
"userguide/userguide.pdf").toURI().toURL();
mm.addAction("Help.user_guide",
new ShowHelpAction("Help.user_guide", url, null));
}
catch (MalformedURLException e) {
ErrorDialog.bug(e);
}
try {
final URL url = new File(Documentation.getDocumentationBaseDir(),
"ReferenceManual/index.htm").toURI().toURL();
final ShowHelpAction helpAction = new ShowHelpAction(
url,
helpWindow.getClass().getResource("/images/Help16.gif")
);
helpAction.putValue(Action.SHORT_DESCRIPTION, Resources.getString(
"Editor.ModuleEditor.reference_manual")); //$NON-NLS-1$
toolBar.add(helpAction);
}
catch (MalformedURLException e) {
ErrorDialog.bug(e);
}
mm.addAction("AboutScreen.about_vassal", new AboutVASSALAction(this));
setJMenuBar(mm.getMenuBarFor(this));
// the presence of the panel prevents a NullPointerException on packing
final JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(250,400));
scrollPane = new JScrollPane(
panel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
add(scrollPane, BorderLayout.CENTER);
pack();
}
protected MenuProxy findMenuProxy(String name, MenuBarProxy mb) {
for (ChildProxy<?> c : mb.getChildren()) {
if (c instanceof MenuProxy) {
final MenuProxy m = (MenuProxy) c;
if (name.equals(m.getText())) return m;
}
}
return null;
}
/*
* Each component must Save, SaveAs and close itself
*/
protected abstract void save();
protected abstract void saveAs();
protected void close() {
GameModule.getGameModule().quit();
}
protected void saver(final Runnable save) {
final ValidationReport report = new ValidationReport();
GameModule.getGameModule().validate(GameModule.getGameModule(), report);
if (report.getWarnings().size() == 0) {
save.run();
}
else {
new ValidationReportDialog(report,
new ValidationReportDialog.CallBack() {
public void ok() {
save.run();
}
public void cancel() {
}
}
).setVisible(true);
}
}
/**
* Called by the enclosed ConfigureTree or ExtensionTree when it's dirty
* state is changed. The implementing class should override this if they
* need to take action like changing menu availability.
*
* @param changed true if the tree is in a changed (dirty) state
*/
public void treeStateChanged(boolean changed) { }
}