/* * $Id$ * * Copyright (c) 2000-2003 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.awt.Point; import java.awt.Shape; import java.util.Iterator; import VASSAL.build.module.Map; /** * This interface defines selection criteria for finding a GamePiece in a Map */ public interface PieceFinder { /** Return the argument GamePiece (or one of its children if a Stack) found at the given point on the given Map */ public GamePiece select(Map map, GamePiece piece, Point pt); /** Return a Stack overlapping the given point */ public static final PieceFinder STACK_ONLY = new StackOnly(); /** * If a Stack overlaps the given point, return the piece containing that point if expanded, * or the top piece if not expanded. * */ public static final PieceFinder PIECE_IN_STACK = new PieceInStack(); /** Returns a Stack if unexpanded and overlapping the given point, * or a piece within that stack if expanded and overlapping the given point */ public static final PieceFinder MOVABLE = new Movable(); public static class StackOnly extends Movable { public Object visitDefault(GamePiece piece) { return null; } public Object visitStack(Stack s) { GamePiece selected = (GamePiece) super.visitStack(s); if (selected != null && selected.getParent() == s) { selected = s; } return selected; } } public static class PieceInStack extends Movable { public Object visitStack(Stack s) { GamePiece selected = (GamePiece) super.visitStack(s); if (selected == s && !s.isExpanded()) { selected = s.topPiece(); } return selected; } } public static class Movable implements PieceFinder, DeckVisitor { protected Shape[] shapes = new Shape[0]; protected Map map; protected Point pt; protected DeckVisitorDispatcher dispatcher = new DeckVisitorDispatcher(this); // This constructor is safe only if using the PieceFinder.select() method public Movable() { this(null,null); } public Movable(Map map, Point pt) { this.map = map; this.pt = pt; } public Object visitDeck(Deck d) { return null; } public Object visitDefault(GamePiece piece) { GamePiece selected = null; Shape s = piece.getShape(); Point pos = piece.getPosition(); Point p = new Point(pt.x - pos.x, pt.y - pos.y); if (s.contains(p)) { selected = piece; } return selected; } public Object visitStack(Stack s) { GamePiece selected = null; if (shapes.length < s.getPieceCount()) { shapes = new Shape[s.getPieceCount()]; } map.getStackMetrics().getContents(s, null, shapes, null, s.getPosition().x, s.getPosition().y); for (Iterator<GamePiece> i = s.getPiecesInVisibleOrderIterator(); i.hasNext();) { GamePiece child = i.next(); // Pieces can be moved by background threads causing the size of // the Stack to change after the Iterator is generated. // FIXME: This is a workaround. We should fix the threading bug // which causes. final int index = s.indexOf(child); if (index >= 0 && index < shapes.length) { if (shapes[index].contains(pt)) { selected = s.isExpanded() ? child : s; break; } } } return selected; } public GamePiece select(Map map, GamePiece piece, Point pt) { this.map = map; this.pt = pt; return (GamePiece) dispatcher.accept(piece); } } }