/*
* $Id$
*
* Copyright (c) 2000-2012 by Rodney Kinney, Brent Easton
*
* 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.map;
import java.awt.Font;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import javax.swing.Action;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import VASSAL.build.Buildable;
import VASSAL.build.module.Map;
import VASSAL.counters.Deck;
import VASSAL.counters.EventFilter;
import VASSAL.counters.GamePiece;
import VASSAL.counters.KeyCommand;
import VASSAL.counters.KeyCommandSubMenu;
import VASSAL.counters.PieceFinder;
import VASSAL.counters.Properties;
public class MenuDisplayer extends MouseAdapter implements Buildable {
public static Font POPUP_MENU_FONT = new Font("Dialog", 0, 10);
protected Map map;
protected PieceFinder targetSelector;
public void addTo(Buildable b) {
targetSelector = createTargetSelector();
map = (Map) b;
map.addLocalMouseListener(this);
}
/**
* Return a {@link PieceFinder} instance that will select a
* {@link GamePiece} whose menu will be displayed when the
* user clicks on the map
* @return
*/
protected PieceFinder createTargetSelector() {
return new PieceFinder.PieceInStack() {
public Object visitDeck(Deck d) {
Point pos = d.getPosition();
Point p = new Point(pt.x - pos.x, pt.y - pos.y);
if (d.getShape().contains(p)) {
return d;
}
else {
return null;
}
}
};
}
public void add(Buildable b) {
}
public Element getBuildElement(Document doc) {
return doc.createElement(getClass().getName());
}
public void build(Element e) {
}
public static JPopupMenu createPopup(GamePiece target) {
return createPopup(target, false);
}
/**
*
* @param target
* @param global If true, then apply the KeyCommands globally,
* i.e. to all selected pieces
* @return
*/
public static JPopupMenu createPopup(GamePiece target, boolean global) {
JPopupMenu popup = new JPopupMenu();
KeyCommand c[] = (KeyCommand[]) target.getProperty(Properties.KEY_COMMANDS);
if (c != null) {
ArrayList<JMenuItem> commands = new ArrayList<JMenuItem>();
ArrayList<KeyStroke> strokes = new ArrayList<KeyStroke>();
// Maps instances of KeyCommandSubMenu to corresponding JMenu
HashMap<KeyCommandSubMenu,JMenu> subMenus =
new HashMap<KeyCommandSubMenu,JMenu>();
// Maps name to a list of commands with that name
HashMap<String,ArrayList<JMenuItem>> commandNames =
new HashMap<String,ArrayList<JMenuItem>>();
for (int i = 0; i < c.length; ++i) {
c[i].setGlobal(global);
KeyStroke stroke = c[i].getKeyStroke();
JMenuItem item = null;
if (c[i] instanceof KeyCommandSubMenu) {
JMenu subMenu = new JMenu(c[i].getLocalizedMenuText());
subMenu.setFont(POPUP_MENU_FONT);
subMenus.put((KeyCommandSubMenu) c[i], subMenu);
item = subMenu;
commands.add(item);
strokes.add(KeyStroke.getKeyStroke('\0'));
}
else {
if (strokes.contains(stroke)) {
JMenuItem command = commands.get(strokes.indexOf(stroke));
Action action = command.getAction();
if (action != null) {
String commandName =
(String) command.getAction().getValue(Action.NAME);
if (commandName == null ||
commandName.length() < c[i].getName().length()) {
item = new JMenuItem(c[i].getLocalizedMenuText());
item.addActionListener(c[i]);
item.setFont(POPUP_MENU_FONT);
item.setEnabled(c[i].isEnabled());
commands.set(strokes.indexOf(stroke), item);
}
}
}
else {
strokes.add(stroke != null ? stroke : KeyStroke.getKeyStroke('\0'));
item = new JMenuItem(c[i].getLocalizedMenuText());
item.addActionListener(c[i]);
item.setFont(POPUP_MENU_FONT);
item.setEnabled(c[i].isEnabled());
commands.add(item);
}
}
if (c[i].getName() != null &&
c[i].getName().length() > 0 &&
item != null) {
ArrayList<JMenuItem> l = commandNames.get(c[i].getName());
if (l == null) {
l = new ArrayList<JMenuItem>();
commandNames.put(c[i].getName(), l);
}
l.add(item);
}
}
// Move commands from main menu into submenus
for (java.util.Map.Entry<KeyCommandSubMenu,JMenu> e :
subMenus.entrySet()) {
final KeyCommandSubMenu menuCommand = e.getKey();
final JMenu subMenu = e.getValue();
for (Iterator<String> it2 = menuCommand.getCommands(); it2.hasNext();) {
final ArrayList<JMenuItem> matchingCommands =
commandNames.get(it2.next());
if (matchingCommands != null) {
for (JMenuItem item : matchingCommands) {
subMenu.add(item);
commands.remove(item);
}
}
}
}
// Promote contents of a single submenu [Removed as per Bug 4775]
// if (commands.size() == 1) {
// if (commands.get(0) instanceof JMenu) {
// JMenu submenu = (JMenu) commands.get(0);
// commands.remove(submenu);
// for (int i = 0; i < submenu.getItemCount(); i++) {
// commands.add(submenu.getItem(i));
// }
// }
// }
for (JMenuItem item : commands) {
popup.add(item);
}
}
return popup;
}
public void mouseReleased(MouseEvent e) {
if (e.isMetaDown()) {
final GamePiece p = map.findPiece(e.getPoint(), targetSelector);
if (p != null) {
EventFilter filter = (EventFilter) p.getProperty(Properties.SELECT_EVENT_FILTER);
if (filter == null
|| !filter.rejectEvent(e)) {
JPopupMenu popup = createPopup(p, true);
Point pt = map.componentCoordinates(e.getPoint());
popup.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
public void popupMenuCanceled
(javax.swing.event.PopupMenuEvent evt) {
map.repaint();
}
public void popupMenuWillBecomeInvisible
(javax.swing.event.PopupMenuEvent evt) {
map.repaint();
}
public void popupMenuWillBecomeVisible
(javax.swing.event.PopupMenuEvent evt) {
}
});
// It is possible for the map to close before the menu is displayed
if (map.getView().isShowing()) {
popup.show(map.getView(), pt.x, pt.y);
}
e.consume();
}
}
}
}
}