/*
* $Id$
*
* Copyright (c) 2006 Rodney Kinney
*
* 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.build.module;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import VASSAL.build.AbstractConfigurable;
import VASSAL.build.Buildable;
import VASSAL.build.GameModule;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.command.Command;
import VASSAL.configure.StringArrayConfigurer;
import VASSAL.i18n.Resources;
import VASSAL.tools.LaunchButton;
import VASSAL.tools.NamedKeyStroke;
import VASSAL.tools.ToolBarComponent;
/**
* Takes buttons from the toolbar of a Map or the main module and places
* them into a popup menu
*
* @author rkinney
*
*/
public class ToolbarMenu extends AbstractConfigurable
implements ContainerListener,
PropertyChangeListener,
GameComponent {
public static final String BUTTON_TEXT = "text"; //$NON-NLS-1$
public static final String BUTTON_ICON = "icon"; //$NON-NLS-1$
public static final String BUTTON_HOTKEY = "hotkey"; //$NON-NLS-1$
public static final String TOOLTIP = "tooltip"; //$NON-NLS-1$
public static final String MENU_ITEMS = "menuItems"; //$NON-NLS-1$
public static final String DESCRIPTION = "description"; //$NON-NLS-1$
/** Buttons where this property contains a JPopupMenu will turn into sub-menus */
public static final String MENU_PROPERTY = "ToolbarMenu.popup"; //$NON-NLS-1$
public static final String HIDDEN_BY_TOOLBAR = "hidden"; //$NON-NLS-1$
protected List<String> menuItems = new ArrayList<String>();
protected Map<AbstractButton,JMenuItem> buttonsToMenuMap =
new HashMap<AbstractButton,JMenuItem>();
protected LaunchButton launch;
protected JToolBar toolbar;
protected JPopupMenu menu;
protected Runnable menuBuilder;
public ToolbarMenu() {
launch = new LaunchButton(Resources.getString(Resources.MENU), TOOLTIP, BUTTON_TEXT, BUTTON_HOTKEY, BUTTON_ICON, new ActionListener() {
public void actionPerformed(ActionEvent e) {
launch();
}
});
menu = new JPopupMenu();
launch.putClientProperty(MENU_PROPERTY, menu);
launch.setToolTipText("Display Menu Options");
GameModule.getGameModule().getGameState().addGameComponent(this);
}
public void launch() {
menu.show(launch, 0, 0);
}
public String[] getAttributeDescriptions() {
return new String[] {
Resources.getString(Resources.DESCRIPTION),
Resources.getString(Resources.BUTTON_TEXT),
Resources.getString(Resources.TOOLTIP_TEXT),
Resources.getString(Resources.BUTTON_ICON),
Resources.getString(Resources.HOTKEY_LABEL),
Resources.getString("Editor.ToolbarMenu.menu_entries")}; //$NON-NLS-1$
}
public Class<?>[] getAttributeTypes() {
return new Class<?>[] {
String.class,
String.class,
String.class,
Icon.class,
NamedKeyStroke.class,
String[].class
};
}
public String[] getAttributeNames() {
return new String[] {
DESCRIPTION,
BUTTON_TEXT,
TOOLTIP,
BUTTON_ICON,
BUTTON_HOTKEY,
MENU_ITEMS
};
}
public String getAttributeValueString(String key) {
if (MENU_ITEMS.equals(key)) {
return StringArrayConfigurer.arrayToString(
menuItems.toArray(new String[menuItems.size()]));
}
else if(DESCRIPTION.equals(key)) {
return getConfigureName();
}
else {
return launch.getAttributeValueString(key);
}
}
public void setAttribute(String key, Object value) {
if (MENU_ITEMS.equals(key)) {
if (value instanceof String) {
value = StringArrayConfigurer.stringToArray((String) value);
}
menuItems = new ArrayList<String>(Arrays.asList((String[]) value));
if (toolbar != null) {
scheduleBuildMenu();
}
}
else if (BUTTON_TEXT.equals(key)) {
launch.setAttribute(key, value);
}
else if(DESCRIPTION.equals(key)) {
setConfigureName((String) value);
}
else {
launch.setAttribute(key, value);
}
}
public void addTo(Buildable parent) {
if (parent instanceof ToolBarComponent) {
toolbar = ((ToolBarComponent) parent).getToolBar();
}
toolbar.add(launch);
toolbar.addContainerListener(this);
scheduleBuildMenu();
}
public Class<?>[] getAllowableConfigureComponents() {
return new Class<?>[0];
}
public HelpFile getHelpFile() {
return HelpFile.getReferenceManualPage("ToolbarMenu.htm"); //$NON-NLS-1$
}
public static String getConfigureTypeName() {
return Resources.getString("Editor.ToolbarMenu.component_type"); //$NON-NLS-1$
}
public void removeFrom(Buildable parent) {
toolbar.remove(launch);
toolbar.removeContainerListener(this);
}
protected void buildMenu() {
for (AbstractButton b : buttonsToMenuMap.keySet()) {
b.removePropertyChangeListener(this);
b.setVisible(true);
b.putClientProperty(HIDDEN_BY_TOOLBAR, null);
}
buttonsToMenuMap.clear();
menu.removeAll();
HashMap<String,JButton> nameToButton = new HashMap<String,JButton>();
if (toolbar != null) {
for (int i = 0, n = toolbar.getComponentCount(); i < n; ++i) {
if (toolbar.getComponentAtIndex(i) instanceof JButton) {
JButton b = ((JButton) toolbar.getComponentAtIndex(i));
String text =
(String) b.getClientProperty(LaunchButton.UNTRANSLATED_TEXT);
if (text == null) {
text = b.getText();
}
nameToButton.put(text, b);
}
}
}
for (String item : menuItems) {
final JButton b = nameToButton.get(item);
if (b != null) {
Object property = b.getClientProperty(MENU_PROPERTY);
b.addPropertyChangeListener(this);
b.setVisible(false);
b.putClientProperty(HIDDEN_BY_TOOLBAR, new Boolean(true));
if (property instanceof JPopupMenu) {
// This button corresponds to another ToolbarMenu button.
// Turn it into a submenu.
JPopupMenu toolbarMenu = (JPopupMenu) property;
toolbarMenu.addContainerListener(this);
JMenu subMenu = new JMenu(b.getText());
Component[] items = toolbarMenu.getComponents();
for (int i = 0; i < items.length; i++) {
final JMenuItem otherItem = (JMenuItem) items[i];
JMenuItem myItem =
new JMenuItem(otherItem.getText(), otherItem.getIcon());
myItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
otherItem.doClick();
}
});
subMenu.add(myItem);
buttonsToMenuMap.put(otherItem, myItem);
}
buttonsToMenuMap.put(b, subMenu);
menu.add(subMenu);
}
else {
JMenuItem mi = new JMenuItem(b.getText(), b.getIcon());
mi.setEnabled(b.isEnabled());
mi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
b.doClick();
}
});
buttonsToMenuMap.put(b, mi);
menu.add(mi);
}
}
}
}
protected void scheduleBuildMenu() {
if (menuBuilder == null) {
menuBuilder = new Runnable() {
public void run() {
buildMenu();
menuBuilder = null;
}
};
SwingUtilities.invokeLater(menuBuilder);
}
}
public void componentAdded(ContainerEvent e) {
scheduleBuildMenu();
}
public void componentRemoved(ContainerEvent e) {
scheduleBuildMenu();
}
public void propertyChange(PropertyChangeEvent evt) {
JButton b = (JButton) evt.getSource();
JMenuItem mi = buttonsToMenuMap.get(b);
if (mi != null) {
if (AbstractButton.TEXT_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
scheduleBuildMenu();
}
else if ("enabled".equals(evt.getPropertyName())) { //$NON-NLS-1$
mi.setEnabled(b.isEnabled());
}
else if (AbstractButton.ICON_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
mi.setIcon(b.getIcon());
}
}
}
public void setup(boolean gameStarting) {
// Prevent our Toolbar buttons from becoming visible on Game close/reopen
scheduleBuildMenu();
}
public Command getRestoreCommand() {
return null;
}
}