/*
* GMGenSystem.java - main class for GMGen
* Copyright (C) 2003 Devon Jones, Emily Smirle
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Created on May 24, 2003
*/
package gmgen;
import gmgen.gui.PreferencesDialog;
import gmgen.gui.PreferencesRootTreeNode;
import gmgen.pluginmgr.messages.AddMenuItemToGMGenToolsMenuMessage;
import gmgen.pluginmgr.messages.EditMenuCopySelectionMessage;
import gmgen.pluginmgr.messages.EditMenuCutSelectionMessage;
import gmgen.pluginmgr.messages.EditMenuPasteSelectionMessage;
import gmgen.pluginmgr.messages.FileMenuNewMessage;
import gmgen.pluginmgr.messages.FileMenuOpenMessage;
import gmgen.pluginmgr.messages.FileMenuSaveMessage;
import gmgen.pluginmgr.messages.GMGenBeingClosedMessage;
import gmgen.pluginmgr.messages.RequestAddPreferencesPanelMessage;
import gmgen.pluginmgr.messages.RequestAddTabToGMGenMessage;
import gmgen.util.LogUtilities;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.Method;
import java.util.EventObject;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import org.apache.commons.lang3.SystemUtils;
import pcgen.core.SettingsHandler;
import pcgen.gui2.PCGenActionMap;
import pcgen.gui2.tools.CommonMenuText;
import pcgen.gui2.tools.Icons;
import pcgen.gui2.tools.Utility;
import pcgen.pluginmgr.PCGenMessage;
import pcgen.pluginmgr.PCGenMessageHandler;
import pcgen.pluginmgr.PluginManager;
import pcgen.pluginmgr.messages.FocusOrStateChangeOccurredMessage;
import pcgen.pluginmgr.messages.RequestFileOpenedMessageForCurrentlyOpenedPCsMessage;
import pcgen.system.LanguageBundle;
import pcgen.system.PCGenPropBundle;
import pcgen.util.Logging;
import pcgen.util.SwingWorker;
/**
* {@code GMGenSystem} is the main class of the GMGen application.
*
* It holds the controller for every tab as well as the menu bar.
*/
public final class GMGenSystem extends JFrame implements ChangeListener,
MenuListener, ActionListener, PCGenMessageHandler {
// Serial UID
private static final long serialVersionUID = -7372446160499882872L;
// menu elements used with CommonMenuText.name(...)
private static final String MNU_SAVE = "mnuSave"; //$NON-NLS-1$
private static final String MNU_OPEN = "mnuOpen"; //$NON-NLS-1$
private static final String MNU_EXIT = "mnuClose"; //$NON-NLS-1$
private static final String MNU_NEW = "mnuNew"; //$NON-NLS-1$
private static final String MNU_CUT = "mnuCut"; //$NON-NLS-1$
private static final String MNU_COPY = "mnuCopy"; //$NON-NLS-1$
private static final String MNU_PASTE = "mnuPaste"; //$NON-NLS-1$
// Settings keys
private static final String SETTING_WINDOW_STATE = "WindowState"; //$NON-NLS-1$
private static final String SETTING_WINDOW_HEIGHT = "WindowHeight"; //$NON-NLS-1$
private static final String SETTING_WINDOW_WIDTH = "WindowWidth"; //$NON-NLS-1$
private static final String WINDOW_Y = "WindowY"; //$NON-NLS-1$
private static final String SETTING_WINDOW_X = "WindowX"; //$NON-NLS-1$
private static final String SETTING_LOGGING_ON = "Logging.On"; //$NON-NLS-1$
/**
* Holds an instance of the top window, so components and windows can get
* their parent frame.
*/
public static GMGenSystem inst;
// The main <code>JPanel</code> view for the system.
private GMGenSystemView theView;
/** {@code true} if this is a Mac OS X system. */
private static final boolean MAC_OS_X = SystemUtils.IS_OS_MAC_OSX;
/** GMGen Application name */
public static final String APPLICATION_NAME = "GMGen"; //$NON-NLS-1$
private JMenuBar systemMenuBar;
// Menus
private JMenu editMenu;
private JMenu fileMenu;
private JMenu toolsMenu;
// File menu items
private JMenuItem copyEditItem;
private JMenuItem cutEditItem;
private JMenuItem pasteEditItem;
private JMenuItem preferencesEditItem;
private JMenuItem exitFileItem;
private JMenuItem versionToolsItem;
/**
* The new menu item in the file menu.
*/
public JMenuItem newFileItem;
/**
* The open menu item in the file menu.
*/
public JMenuItem openFileItem;
/**
* The save menu item in the file menu.
*/
public JMenuItem saveFileItem;
// Separators
private JSeparator editSeparator1;
private JSeparator fileSeparator1;
private JSeparator fileSeparator2;
private JSeparator toolsSeparator1;
// Tree for the preferences dialog
private PreferencesRootTreeNode rootNode = new PreferencesRootTreeNode();
private final PCGenMessageHandler messageHandler;
private PluginManager pluginManager;
/**
* Constructor
*
* Creates a JFrame TODO comment correctly.
* Starts the GMGen renderer
*/
public GMGenSystem() {
super(LanguageBundle.getFormattedString("in_gmgen_frameTitle", APPLICATION_NAME)); //$NON-NLS-1$
pluginManager = PluginManager.getInstance();
messageHandler = pluginManager.getPostbox();
new Renderer().start();
}
private void initialize() {
if (MAC_OS_X) {
initialiseMacOS();
}
Utility.setApplicationTitle(APPLICATION_NAME);
inst = this;
initLogger();
createMenuBar();
theView = new GMGenSystemView();
pluginManager.addMember(this);
PluginManager.getInstance().startAllPlugins();
initComponents();
initSettings();
messageHandler.handleMessage(new RequestFileOpenedMessageForCurrentlyOpenedPCsMessage(this));
messageHandler.handleMessage(new FocusOrStateChangeOccurredMessage(this, editMenu));
inst.setVisible(true);
}
/*
* Fixes for Mac OS X look-and-feel menu problems.sk4p 12 Dec 2002
*/
// TODO factorize. PCGen must be doing the same thing.
private void initialiseMacOS() {
System.setProperty("com.apple.mrj.application.growbox.intrudes", //$NON-NLS-1$
"false"); //$NON-NLS-1$
System.setProperty("com.apple.mrj.application.live-resize", "false"); //$NON-NLS-1$ //$NON-NLS-2$
System.setProperty("com.apple.macos.smallTabs", "true"); //$NON-NLS-1$ //$NON-NLS-2$
System.setProperty("apple.laf.useScreenMenuBar", "true"); //$NON-NLS-1$ //$NON-NLS-2$
System.setProperty("com.apple.mrj.application.apple.menu.about.name", //$NON-NLS-1$
APPLICATION_NAME);
macOSXRegistration();
}
/**
* Returns the GMGen version as a human-readable string.
*
* @return The version
*/
public static String getVersion() {
return PCGenPropBundle.getVersionNumber();
}
/**
* Calls the appropriate methods depending on the actions that happened on
* the GUI.
*
* @param event
* event that took place
*/
@Override
public void actionPerformed(ActionEvent event) {
if (event.getSource() == openFileItem) {
messageHandler.handleMessage(new FileMenuOpenMessage(this));
} else if (event.getSource() == exitFileItem) {
messageHandler.handleMessage(new GMGenBeingClosedMessage(this));
} else if (event.getSource() == newFileItem) {
messageHandler.handleMessage(new FileMenuNewMessage(this));
} else if (event.getSource() == saveFileItem) {
messageHandler.handleMessage(new FileMenuSaveMessage(this));
} else if (event.getSource() == cutEditItem) {
messageHandler.handleMessage(new EditMenuCutSelectionMessage(this));
} else if (event.getSource() == copyEditItem) {
messageHandler.handleMessage(new EditMenuCopySelectionMessage(this));
} else if (event.getSource() == pasteEditItem) {
messageHandler.handleMessage(new EditMenuPasteSelectionMessage(this));
}
}
/**
* Clears the edit menu to allow a plugin to populate it.
*/
public void clearEditMenu() {
editMenu.removeAll();
/**
* Preferences on the Macintosh is in the application menu. See
* macOSXRegistration()
*/
if (!MAC_OS_X) {
editMenu.add(editSeparator1);
CommonMenuText.name(preferencesEditItem, PCGenActionMap.MNU_TOOLS_PREFERENCES);
editMenu.add(preferencesEditItem);
preferencesEditItem.setEnabled(true);
ActionListener[] listenerArray = preferencesEditItem
.getActionListeners();
for (final ActionListener aListenerArray : listenerArray)
{
preferencesEditItem.removeActionListener(aListenerArray);
}
preferencesEditItem
.addActionListener(this::mPreferencesActionPerformed);
}
}
/**
* Exits GMGen, the Mac way.
*/
public void exitFormMac() {
this.setVisible(false);
}
/**
* Message handler for the GMBus.
*
* @param message
* The message passed in from the bus
*/
@Override
public void handleMessage(PCGenMessage message) {
// A plugin is asking for the creation of a new tab
if (message instanceof RequestAddTabToGMGenMessage) {
RequestAddTabToGMGenMessage tmessage = (RequestAddTabToGMGenMessage) message;
Logging.debugPrint("Creating Tab "
+ GMGenSystemView.getTabPane().getTabCount());
theView.insertPane(tmessage.getName(), tmessage.getPane(),
GMGenSystemView.getTabPane().getTabCount());
} else if (message instanceof RequestAddPreferencesPanelMessage) {
RequestAddPreferencesPanelMessage pmessage = (RequestAddPreferencesPanelMessage) message;
Logging.debugPrint("Creating Preferences Panel");
rootNode.addPanel(pmessage.getName(), pmessage.getPrefsPanel());
}
// A plugin is asking for the creation of a new option in the tool menu
else if (message instanceof AddMenuItemToGMGenToolsMenuMessage) {
AddMenuItemToGMGenToolsMenuMessage mmessage = (AddMenuItemToGMGenToolsMenuMessage) message;
toolsMenu.add(mmessage.getMenuItem());
} else if (message instanceof GMGenBeingClosedMessage) {
setCloseSettings();
// Karianna 07/03/2008 - Added a call to exitForm passing in no
// window event
// TODO This sequence of calls simply hides GMGen as opposed to
// unloading it
exitForm(null);
}
}
/**
* Handles the clicking on the tool menu.
*
*/
public void handleToolsMenu() {
// TODO
}
/**
* launches the preferences dialog on a mac.
*/
public void mPreferencesActionPerformedMac() {
PreferencesDialog dialog = new PreferencesDialog(this, true, rootNode);
dialog.setVisible(true);
}
/**
* Generic registration with the Mac OS X application menu. Checks the
* platform, then attempts to register with the Apple EAWT.
*
* This method calls OSXAdapter.registerMacOSXApplication() and
* OSXAdapter.enablePrefs(). See OSXAdapter.java for the signatures of these
* methods.
*/
private void macOSXRegistration() {
try {
Class<?> osxAdapter = Class.forName("gmgen.util.OSXAdapter");
Class<?>[] defArgs = { GMGenSystem.class };
Method registerMethod = osxAdapter.getDeclaredMethod(
"registerMacOSXApplication", defArgs);
if (registerMethod != null) {
Object[] args = { this };
registerMethod.invoke(osxAdapter, args);
}
// This is slightly gross. to reflectively access methods with
// boolean args,
// use "boolean.class", then pass a Boolean object in as the arg,
// which apparently
// gets converted for you by the reflection system.
defArgs[0] = boolean.class;
Method prefsEnableMethod = osxAdapter.getDeclaredMethod(
"enablePrefs", defArgs);
if (prefsEnableMethod != null) {
Object[] args = { Boolean.TRUE };
prefsEnableMethod.invoke(osxAdapter, args);
}
} catch (NoClassDefFoundError | ClassNotFoundException e) {
// This will be thrown first if the OSXAdapter is loaded on a system
// without the EAWT
// because OSXAdapter extends ApplicationAdapter in its def
// TODO Use Logging
System.err
.println("This version of Mac OS X does not support the Apple EAWT. Application Menu handling has been disabled ("
+ e + ")");
} catch (Exception e) {
// TODO Use Logging
System.err.println("Exception while loading the OSXAdapter = ["
+ e.getMessage() + "]");
}
}
/**
* Handles a menu canceled event.
*
* @param e
* menu canceled event
*/
@Override
public void menuCanceled(MenuEvent e) {
// TODO
}
/**
* Handles a menu de-selected event.
*
* @param e
* Menu Deselected event
*/
@Override
public void menuDeselected(MenuEvent e) {
// TODO
}
/**
* Listens for menus to be clicked and calls the appropriate handlers.
*
* @param e
* the menu event that happened.
*/
@Override
public void menuSelected(MenuEvent e) {
if (e.getSource() == toolsMenu) {
handleToolsMenu();
}
}
/**
* Calls the necessary methods if an item on the GUI or model has changed.
*
* @param event - The event that has happened.
*/
@Override
public void stateChanged(ChangeEvent event) {
stateUpdate(event);
}
/**
* Calls the necessary methods if an item on the GUI or model has changed.
*
* @param event - The event that has happened.
*/
private void stateUpdate(EventObject event) {
newFileItem.setEnabled(false);
openFileItem.setEnabled(false);
saveFileItem.setEnabled(false);
clearEditMenu();
messageHandler.handleMessage(new FocusOrStateChangeOccurredMessage(this, editMenu));
}
// Sets a bunch of properties based on the status of GMGen at close.
private void setCloseSettings() {
SettingsHandler.setGMGenOption(SETTING_WINDOW_X, this.getX());
SettingsHandler.setGMGenOption(WINDOW_Y, this.getY());
SettingsHandler.setGMGenOption(SETTING_WINDOW_WIDTH, this.getSize().width);
SettingsHandler.setGMGenOption(SETTING_WINDOW_HEIGHT, this.getSize().height);
// Maximized state of the window
if ((getExtendedState() & Frame.MAXIMIZED_BOTH) != 0) {
SettingsHandler.setGMGenOption(SETTING_WINDOW_STATE, Frame.MAXIMIZED_BOTH);
} else if ((getExtendedState() & Frame.MAXIMIZED_HORIZ) != 0) {
SettingsHandler
.setGMGenOption(SETTING_WINDOW_STATE, Frame.MAXIMIZED_HORIZ);
} else if ((getExtendedState() & Frame.MAXIMIZED_VERT) != 0) {
SettingsHandler.setGMGenOption(SETTING_WINDOW_STATE, Frame.MAXIMIZED_VERT);
} else {
SettingsHandler.setGMGenOption(SETTING_WINDOW_STATE, Frame.NORMAL);
}
}
// Sets all the panes on the GUI in the correct order.
private void setTabbedPanes() {
try {
GMGenSystemView.getTabPane().setSelectedIndex(0);
theView.showPane();
} catch (RuntimeException e) {
// TODO
}
}
// Creates the MenuBar for the application.
private void createMenuBar() {
systemMenuBar = new JMenuBar();
createFileMenu();
createEditMenu();
createToolsMenu();
setJMenuBar(systemMenuBar);
setDefaultEnablementOfMenuItems();
pack();
}
// Enable or Disable menu items at initialization time
private void setDefaultEnablementOfMenuItems() {
openFileItem.setEnabled(true);
saveFileItem.setEnabled(false);
newFileItem.setEnabled(false);
cutEditItem.setEnabled(false);
copyEditItem.setEnabled(false);
pasteEditItem.setEnabled(false);
preferencesEditItem.setEnabled(true);
versionToolsItem.setEnabled(false);
}
// Create tools menu
private void createToolsMenu() {
toolsMenu = new JMenu();
toolsSeparator1 = new JSeparator();
versionToolsItem = new JMenuItem();
CommonMenuText.name(toolsMenu, PCGenActionMap.MNU_TOOLS);
toolsMenu.addMenuListener(this);
CommonMenuText.name(versionToolsItem, "mnuGetNew"); //$NON-NLS-1$
toolsMenu.add(versionToolsItem);
toolsMenu.add(toolsSeparator1);
systemMenuBar.add(toolsMenu);
}
// Create the edit menu
private void createEditMenu() {
editMenu = new JMenu();
cutEditItem = new JMenuItem();
copyEditItem = new JMenuItem();
pasteEditItem = new JMenuItem();
editSeparator1 = new JSeparator();
preferencesEditItem = new JMenuItem();
// EDIT MENU
CommonMenuText.name(editMenu, PCGenActionMap.MNU_EDIT);
editMenu.addMenuListener(this);
CommonMenuText.name(cutEditItem, MNU_CUT);
editMenu.add(cutEditItem);
CommonMenuText.name(copyEditItem, MNU_COPY);
editMenu.add(copyEditItem);
CommonMenuText.name(pasteEditItem, MNU_PASTE);
editMenu.add(pasteEditItem);
// Preferences... on MAC OS X is in the application menu. See macOSXRegistration()
if (!MAC_OS_X) {
editMenu.add(editSeparator1);
CommonMenuText.name(preferencesEditItem, PCGenActionMap.MNU_TOOLS_PREFERENCES);
editMenu.add(preferencesEditItem);
preferencesEditItem.setEnabled(true);
ActionListener[] listenerArray = preferencesEditItem
.getActionListeners();
for (final ActionListener aListenerArray : listenerArray)
{
preferencesEditItem.removeActionListener(aListenerArray);
}
preferencesEditItem.addActionListener(this::mPreferencesActionPerformed);
}
systemMenuBar.add(editMenu);
}
// Create the file menu
private void createFileMenu() {
fileMenu = new JMenu();
newFileItem = new JMenuItem();
openFileItem = new JMenuItem();
fileSeparator1 = new JSeparator();
saveFileItem = new JMenuItem();
fileSeparator2 = new JSeparator();
exitFileItem = new JMenuItem();
CommonMenuText.name(fileMenu, PCGenActionMap.MNU_FILE);
fileMenu.addMenuListener(this);
createFileNewMenuItem();
createFileOpenMenuItem();
fileMenu.add(fileSeparator1);
createFileSaveMenuItem();
// Exit is quit on the Macintosh is in the application menu. See macOSXRegistration()
if (!MAC_OS_X) {
exitForMacOSX();
}
systemMenuBar.add(fileMenu);
}
/**
*
*/
private void createFileSaveMenuItem() {
CommonMenuText.name(saveFileItem, MNU_SAVE);
fileMenu.add(saveFileItem);
saveFileItem.addActionListener(this);
}
/**
*
*/
private void createFileOpenMenuItem() {
CommonMenuText.name(openFileItem, MNU_OPEN);
fileMenu.add(openFileItem);
openFileItem.addActionListener(this);
}
/**
*
*/
private void createFileNewMenuItem() {
CommonMenuText.name(newFileItem, MNU_NEW);
newFileItem.addActionListener(this);
fileMenu.add(newFileItem);
}
/**
*
*/
private void exitForMacOSX() {
fileMenu.add(fileSeparator2);
CommonMenuText.name(exitFileItem, MNU_EXIT);
fileMenu.add(exitFileItem);
exitFileItem.addActionListener(this);
}
/**
* Closes and exits the application cleanly.
*
* @param event
* - a window close event
*/
private void exitForm(WindowEvent event) {
this.setVisible(false);
}
/**
* Initializes all the GUI components and places them in the correct place
* on the GUI.
*
*/
private void initComponents() {
getContentPane().setLayout(new BorderLayout());
setTabbedPanes();
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent evt) {
exitForm(evt);
}
});
addWindowFocusListener(new java.awt.event.WindowFocusListener() {
@Override
public void windowGainedFocus(java.awt.event.WindowEvent e) {
stateUpdate(e);
}
@Override
public void windowLostFocus(java.awt.event.WindowEvent e) {
// Intentionally left blank because WindowFocusListener requires
// the method to be implemented.
}
});
// sourceView.getLoadButton().addActionListener(this);
// sourceView.getUnloadAllButton().addActionListener(this);
// sourceView.getRemoveAllButton().addActionListener(this);
GMGenSystemView.getTabPane().addChangeListener(this);
getContentPane().add(theView, BorderLayout.CENTER);
setIconImage(Icons.gmgen_icon.getImageIcon().getImage());
}
// Initializes the Logger component.
private void initLogger() {
boolean logging = SettingsHandler.getGMGenOption(SETTING_LOGGING_ON, false);
LogUtilities.inst().setLogging(logging);
}
// Initializes the settings, and implements their commands.
private void initSettings() {
int iWinX = SettingsHandler.getGMGenOption(SETTING_WINDOW_X, 0);
int iWinY = SettingsHandler.getGMGenOption(WINDOW_Y, 0);
setLocation(iWinX, iWinY);
int iWinWidth = SettingsHandler.getGMGenOption(SETTING_WINDOW_WIDTH, 750);
int iWinHeight = SettingsHandler.getGMGenOption(SETTING_WINDOW_HEIGHT, 580);
setSize(iWinWidth, iWinHeight);
int windowState = SettingsHandler.getGMGenOption(SETTING_WINDOW_STATE,
Frame.NORMAL);
if (windowState != Frame.NORMAL) {
setExtendedState(windowState);
}
}
private void mPreferencesActionPerformed(ActionEvent event) {
PreferencesDialog dialog = new PreferencesDialog(this, true, rootNode);
dialog.setVisible(true);
}
private class Renderer extends SwingWorker {
@Override
public Object construct() {
return "";
}
@Override
public void finished() {
GMGenSystem.this.initialize();
}
}
}