/*
* $Id$
*
* Copyright (c) 2005 by 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.counters;
import java.util.Iterator;
import javax.swing.KeyStroke;
import VASSAL.build.GameModule;
import VASSAL.build.module.Chatter;
import VASSAL.build.module.Map;
import VASSAL.build.module.properties.PropertySource;
import VASSAL.command.Command;
import VASSAL.command.NullCommand;
import VASSAL.tools.FormattedString;
import VASSAL.tools.NamedKeyStroke;
import VASSAL.tools.RecursionLimitException;
import VASSAL.tools.RecursionLimiter;
import VASSAL.tools.RecursionLimiter.Loopable;
/**
* Applies a given keyboard command to all counters on a map
*/
public class GlobalCommand {
protected KeyStroke keyStroke;
protected boolean reportSingle;
protected int selectFromDeck = -1;
protected FormattedString reportFormat = new FormattedString();
protected Loopable owner;
protected PropertySource source;
public GlobalCommand(Loopable l) {
this (l, null);
}
public GlobalCommand(Loopable l, PropertySource p) {
owner = l;
source = p;
}
public void setPropertySource(PropertySource ps) {
source = ps;
}
public void setKeyStroke(KeyStroke keyStroke) {
this.keyStroke = keyStroke;
}
public void setKeyStroke(NamedKeyStroke keyStroke) {
this.keyStroke = keyStroke.getKeyStroke();
}
public void setReportFormat(String format) {
this.reportFormat.setFormat(format);
}
public KeyStroke getKeyStroke() {
return keyStroke;
}
public String getReportFormat() {
return reportFormat.getFormat();
}
public boolean isReportSingle() {
return reportSingle;
}
public void setReportSingle(boolean reportSingle) {
this.reportSingle = reportSingle;
}
public Command apply(Map m, PieceFilter filter) {
return apply(new Map[]{m},filter);
}
/**
* Apply the key command to all pieces that pass the given filter on all the given maps
*
* @param m
* @param filter
* @return a the corresponding {@link Command}
*/
public Command apply(Map[] m, PieceFilter filter) {
Command c = new NullCommand();
try {
if (reportSingle) {
Map.setChangeReportingEnabled(false);
}
RecursionLimiter.startExecution(owner);
String reportText = reportFormat.getLocalizedText(source);
if (reportText.length() > 0) {
c = new Chatter.DisplayText(
GameModule.getGameModule().getChatter(), "*" + reportText);
c.execute();
}
for (int mapI = 0; mapI < m.length; ++mapI) {
Visitor visitor = new Visitor(c, filter, keyStroke);
DeckVisitorDispatcher dispatcher = new DeckVisitorDispatcher(visitor);
GamePiece[] p = m[mapI].getPieces();
for (int i = 0; i < p.length; ++i) {
dispatcher.accept(p[i]);
}
visitor.getTracker().repaint();
c = visitor.getCommand();
}
}
catch (RecursionLimitException e) {
RecursionLimiter.infiniteLoop(e);
}
finally {
RecursionLimiter.endExecution();
if (reportSingle) {
Map.setChangeReportingEnabled(true);
}
}
return c;
}
protected class Visitor implements DeckVisitor {
private Command command;
private BoundsTracker tracker;
private PieceFilter filter;
private KeyStroke stroke;
public Visitor(Command command, PieceFilter filter, KeyStroke stroke) {
this.command = command;
tracker = new BoundsTracker();
this.filter = filter;
this.stroke = stroke;
}
public Object visitDeck(Deck d) {
Object target = null;
if (selectFromDeck != 0) {
d.setDragCount(selectFromDeck < 0 ? d.getPieceCount() : selectFromDeck);
for (PieceIterator it = d.drawCards(); it.hasMoreElements();) {
apply(it.nextPiece());
}
}
return target;
}
public Object visitStack(Stack s) {
for (Iterator<GamePiece> i = s.getPiecesIterator(); i.hasNext();) {
apply(i.next());
}
return null;
}
public Object visitDefault(GamePiece p) {
apply(p);
return null;
}
private void apply(GamePiece p) {
if (filter == null || filter.accept(p)) {
tracker.addPiece(p);
p.setProperty(Properties.SNAPSHOT, PieceCloner.getInstance().clonePiece(p));
command.append(p.keyEvent(stroke));
tracker.addPiece(p);
}
}
public Command getCommand() {
return command;
}
public BoundsTracker getTracker() {
return tracker;
}
}
public int getSelectFromDeck() {
return selectFromDeck;
}
/**
* Set the number of pieces to select from a deck that the command will apply to. A value <0 means to apply to all pieces in the deck
* @param selectFromDeck
*/
public void setSelectFromDeck(int selectFromDeck) {
this.selectFromDeck = selectFromDeck;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((keyStroke == null) ? 0 : keyStroke.hashCode());
result = prime * result
+ ((reportFormat == null) ? 0 : reportFormat.hashCode());
result = prime * result + (reportSingle ? 1231 : 1237);
result = prime * result + selectFromDeck;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GlobalCommand other = (GlobalCommand) obj;
if (keyStroke == null) {
if (other.keyStroke != null)
return false;
} else if (!keyStroke.equals(other.keyStroke))
return false;
if (reportFormat == null) {
if (other.reportFormat != null)
return false;
} else if (!reportFormat.equals(other.reportFormat))
return false;
if (reportSingle != other.reportSingle)
return false;
if (selectFromDeck != other.selectFromDeck)
return false;
return true;
}
}