/*
* $Id$
*
* Copyright (c) 2004-2009 by Rodney Kinney, Joel Uckelman
*
* 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.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.datatransfer.StringSelection;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DragSourceMotionListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import VASSAL.build.AbstractConfigurable;
import VASSAL.build.AutoConfigurable;
import VASSAL.build.BadDataReport;
import VASSAL.build.Buildable;
import VASSAL.build.Configurable;
import VASSAL.build.GameModule;
import VASSAL.build.module.GameComponent;
import VASSAL.build.module.Map;
import VASSAL.build.module.NewGameIndicator;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.build.module.map.boardPicker.Board;
import VASSAL.build.module.map.boardPicker.board.MapGrid;
import VASSAL.build.module.map.boardPicker.board.MapGrid.BadCoords;
import VASSAL.build.widget.PieceSlot;
import VASSAL.command.Command;
import VASSAL.configure.AutoConfigurer;
import VASSAL.configure.Configurer;
import VASSAL.configure.StringEnum;
import VASSAL.configure.ValidationReport;
import VASSAL.configure.VisibilityCondition;
import VASSAL.counters.GamePiece;
import VASSAL.counters.PieceCloner;
import VASSAL.counters.Properties;
import VASSAL.counters.Stack;
import VASSAL.i18n.ComponentI18nData;
import VASSAL.i18n.Resources;
import VASSAL.tools.AdjustableSpeedScrollPane;
import VASSAL.tools.ErrorDialog;
import VASSAL.tools.UniqueIdManager;
import VASSAL.tools.image.ImageUtils;
import VASSAL.tools.menu.MenuManager;
/**
* This is the "At-Start Stack" component, which initializes a Map or Board with a specified stack.
* Because it uses a regular stack, this component is better suited for limited-force-pool collections
* of counters than a {@link DrawPile}
*
*/
public class SetupStack extends AbstractConfigurable implements GameComponent, UniqueIdManager.Identifyable {
private static UniqueIdManager idMgr = new UniqueIdManager("SetupStack");
public static final String COMMAND_PREFIX = "SETUP_STACK\t";
protected Point pos = new Point();
public static final String OWNING_BOARD = "owningBoard";
public final static String X_POSITION = "x";
public final static String Y_POSITION = "y";
protected Map map;
protected String owningBoardName;
protected String id;
public static final String NAME = "name";
protected static NewGameIndicator indicator;
protected StackConfigurer stackConfigurer;
protected JButton configureButton;
protected String location;
protected boolean useGridLocation;
public static final String LOCATION = "location";
public static final String USE_GRID_LOCATION = "useGridLocation";
@Override
public VisibilityCondition getAttributeVisibility(String name) {
if (USE_GRID_LOCATION.equals(name)) {
return new VisibilityCondition() {
public boolean shouldBeVisible() {
Board b = getConfigureBoard();
if (b == null)
return false;
else
return b.getGrid() != null;
}
};
}
else if (LOCATION.equals(name)) {
return new VisibilityCondition() {
public boolean shouldBeVisible() {
return isUseGridLocation();
}
};
}
else if (X_POSITION.equals(name) || Y_POSITION.equals(name)) {
return new VisibilityCondition() {
public boolean shouldBeVisible() {
return !isUseGridLocation();
}
};
}
else
return super.getAttributeVisibility(name);
}
// must have a useable board with a grid
protected boolean isUseGridLocation() {
if (!useGridLocation)
return false;
Board b = getConfigureBoard();
if (b == null)
return false;
MapGrid g = b.getGrid();
if (g == null)
return false;
else
return true;
}
// only update the position if we're using the location name
protected void updatePosition() {
if (isUseGridLocation() && location != null && !location.equals("")) {
try {
pos = getConfigureBoard().getGrid().getLocation(location);
}
catch (BadCoords e) {
ErrorDialog.dataError(new BadDataReport(this, "Error.setup_stack_position_error", location,e));
}
}
}
@Override
public void validate(Buildable target, ValidationReport report) {
if (isUseGridLocation()) {
if (location == null) {
report.addWarning(getConfigureName() + Resources.getString("SetupStack.null_location"));
}
else {
try {
getConfigureBoard().getGrid().getLocation(location);
}
catch (BadCoords e) {
String msg = "Bad location name "+location+" in "+getConfigureName();
if (e.getMessage() != null) {
msg += ": "+e.getMessage();
}
report.addWarning(msg);
}
}
}
super.validate(target, report);
}
protected void updateLocation() {
Board b = getConfigureBoard();
if (b != null) {
MapGrid g = b.getGrid();
if (g != null)
location = g.locationName(pos);
}
}
public void setup(boolean gameStarting) {
if (gameStarting && indicator.isNewGame() && isOwningBoardActive()) {
Stack s = initializeContents();
updatePosition();
Point p = new Point(pos);
if (owningBoardName != null) {
Rectangle r = map.getBoardByName(owningBoardName).bounds();
p.translate(r.x, r.y);
}
if (placeNonStackingSeparately()) {
for (int i=0;i<s.getPieceCount();++i) {
GamePiece piece = s.getPieceAt(i);
if (Boolean.TRUE.equals(piece.getProperty(Properties.NO_STACK))) {
s.remove(piece);
piece.setParent(null);
map.placeAt(piece,p);
i--;
}
}
}
map.placeAt(s, p);
}
}
protected boolean placeNonStackingSeparately() {
return true;
}
public Command getRestoreCommand() {
return null;
}
public String[] getAttributeDescriptions() {
return new String[]{
Resources.getString(Resources.NAME_LABEL),
Resources.getString("Editor.StartStack.board"), //$NON-NLS-1$
Resources.getString("Editor.StartStack.grid"), //$NON-NLS-1$
Resources.getString("Editor.StartStack.location"), //$NON-NLS-1$
Resources.getString("Editor.StartStack.position_x"), //$NON-NLS-1$
Resources.getString("Editor.StartStack.position_y"), //$NON-NLS-1$
};
}
public Class<?>[] getAttributeTypes() {
return new Class<?>[]{
String.class,
OwningBoardPrompt.class,
Boolean.class,
String.class,
Integer.class,
Integer.class
};
}
public String[] getAttributeNames() {
return new String[]{
NAME,
OWNING_BOARD,
USE_GRID_LOCATION,
LOCATION,
X_POSITION,
Y_POSITION
};
}
public String getAttributeValueString(String key) {
if (NAME.equals(key)) {
return getConfigureName();
}
else if (OWNING_BOARD.equals(key)) {
return owningBoardName;
}
else if (USE_GRID_LOCATION.equals(key)) {
return Boolean.toString(useGridLocation);
}
else if (LOCATION.equals(key)) {
return location;
}
else if (X_POSITION.equals(key)) {
return String.valueOf(pos.x);
}
else if (Y_POSITION.equals(key)) {
return String.valueOf(pos.y);
}
else {
return null;
}
}
public void setAttribute(String key, Object value) {
if (NAME.equals(key)) {
setConfigureName((String) value);
}
else if (OWNING_BOARD.equals(key)) {
if (OwningBoardPrompt.ANY.equals(value)) {
owningBoardName = null;
}
else {
owningBoardName = (String) value;
}
updateConfigureButton();
}
else if (USE_GRID_LOCATION.equals(key)) {
if (value instanceof String) {
value = Boolean.valueOf((String) value);
}
useGridLocation = ((Boolean) value).booleanValue();
}
else if (LOCATION.equals(key)) {
location = (String) value;
}
else if (X_POSITION.equals(key)) {
if (value instanceof String) {
value = Integer.valueOf((String) value);
}
pos.x = ((Integer) value).intValue();
}
else if (Y_POSITION.equals(key)) {
if (value instanceof String) {
value = Integer.valueOf((String) value);
}
pos.y = ((Integer) value).intValue();
}
}
public void add(Buildable child) {
super.add(child);
updateConfigureButton();
}
public void addTo(Buildable parent) {
if (indicator == null) {
indicator = new NewGameIndicator(COMMAND_PREFIX);
}
map = (Map) parent;
idMgr.add(this);
GameModule.getGameModule().getGameState().addGameComponent(this);
setAttributeTranslatable(NAME, false);
}
public Class<?>[] getAllowableConfigureComponents() {
return new Class<?>[]{PieceSlot.class};
}
public HelpFile getHelpFile() {
return HelpFile.getReferenceManualPage("SetupStack.htm");
}
public static String getConfigureTypeName() {
return Resources.getString("Editor.StartStack.component_type"); //$NON-NLS-1$
}
public void removeFrom(Buildable parent) {
idMgr.remove(this);
GameModule.getGameModule().getGameState().removeGameComponent(this);
}
protected boolean isOwningBoardActive() {
boolean active = false;
if (owningBoardName == null) {
active = true;
}
else if (map.getBoardByName(owningBoardName) != null) {
active = true;
}
return active;
}
protected Stack initializeContents() {
Stack s = createStack();
Configurable[] c = getConfigureComponents();
for (int i = 0; i < c.length; ++i) {
if (c[i] instanceof PieceSlot) {
PieceSlot slot = (PieceSlot) c[i];
GamePiece p = slot.getPiece();
p = PieceCloner.getInstance().clonePiece(p);
GameModule.getGameModule().getGameState().addPiece(p);
s.add(p);
}
}
GameModule.getGameModule().getGameState().addPiece(s);
return s;
}
protected Stack createStack() {
return new Stack();
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
// public static class GridPrompt extends StringEnum {
// public static final String NONE = "<none>";
// public static final String ZONE = "(Zone)";
// public static final String BOARD = "(Board)";
//
// public GridPrompt() {
// }
//
// @Override
// public String[] getValidValues(AutoConfigurable target) {
// ArrayList<String> values = new ArrayList<String>();
// values.add(NONE);
// if (target instanceof SetupStack) {
// SetupStack stack = (SetupStack) target;
// BoardPicker bp = stack.map.getBoardPicker();
// if (stack.owningBoardName != null) {
// Board b = bp.getBoard(stack.owningBoardName);
// MapGrid grid = b.getGrid();
// if (grid != null) {
// GridNumbering gn = grid.getGridNumbering();
// if (gn != null)
// values.add(BOARD + " " + b.getName());
// if (grid instanceof ZonedGrid) {
// ZonedGrid zg = (ZonedGrid) grid;
// for (Iterator i = zg.getZones(); i.hasNext(); ) {
// Zone z = (Zone) i.next();
// if (!z.isUseParentGrid() && z.getGrid() != null && z.getGrid().getGridNumbering() != null)
// values.add(ZONE + " " + z.getName());
// }
// }
// }
// }
// }
// return values.toArray(new String[values.size()]);
// }
// }
//
public static class OwningBoardPrompt extends StringEnum {
public static final String ANY = "<any>";
public OwningBoardPrompt() {
}
public String[] getValidValues(AutoConfigurable target) {
String[] values;
if (target instanceof SetupStack) {
ArrayList<String> l = new ArrayList<String>();
l.add(ANY);
Map m = ((SetupStack) target).map;
if (m != null) {
l.addAll(Arrays.asList(m.getBoardPicker().getAllowableBoardNames()));
}
else {
for (Map m2 : Map.getMapList()) {
l.addAll(
Arrays.asList(m2.getBoardPicker().getAllowableBoardNames()));
}
}
values = l.toArray(new String[l.size()]);
}
else {
values = new String[]{ANY};
}
return values;
}
}
/*
* GUI Stack Placement Configurer
*/
protected Configurer xConfig, yConfig, locationConfig;
public Configurer getConfigurer() {
config = null; // Don't cache the Configurer so that the list of available boards won't go stale
Configurer c = super.getConfigurer();
xConfig = ((AutoConfigurer) c).getConfigurer(X_POSITION);
yConfig = ((AutoConfigurer) c).getConfigurer(Y_POSITION);
locationConfig = ((AutoConfigurer) c).getConfigurer(LOCATION);
updateConfigureButton();
((Container) c.getControls()).add(configureButton);
return c;
}
protected void updateConfigureButton() {
if (configureButton == null) {
configureButton = new JButton("Reposition Stack");
configureButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
configureStack();
}
});
}
configureButton.setEnabled(getConfigureBoard() != null && buildComponents.size() > 0);
}
protected void configureStack() {
stackConfigurer = new StackConfigurer(this);
stackConfigurer.init();
stackConfigurer.setVisible(true);
}
protected PieceSlot getTopPiece() {
Iterator<PieceSlot> i =
getAllDescendantComponentsOf(PieceSlot.class).iterator();
return i.hasNext() ? i.next() : null;
}
/*
* Return a board to configure the stack on.
*/
protected Board getConfigureBoard() {
Board board = null;
if (map != null && !OwningBoardPrompt.ANY.equals(owningBoardName)) {
board = map.getBoardPicker().getBoard(owningBoardName);
}
if (board == null && map != null) {
String[] allBoards = map.getBoardPicker().getAllowableBoardNames();
if (allBoards.length > 0) {
board = map.getBoardPicker().getBoard(allBoards[0]);
}
}
return board;
}
protected static final Dimension DEFAULT_SIZE = new Dimension(800, 600);
protected static final int DELTA = 1;
protected static final int FAST = 10;
protected static final int FASTER = 5;
protected static final int DEFAULT_DUMMY_SIZE = 50;
public class StackConfigurer extends JFrame implements ActionListener, KeyListener, MouseListener {
private static final long serialVersionUID = 1L;
protected Board board;
protected View view;
protected JScrollPane scroll;
protected SetupStack myStack;
protected PieceSlot mySlot;
protected GamePiece myPiece;
protected Point savePosition;
protected Dimension dummySize;
protected BufferedImage dummyImage;
public StackConfigurer(SetupStack stack) {
super("Adjust At-Start Stack");
setJMenuBar(MenuManager.getInstance().getMenuBarFor(this));
myStack = stack;
mySlot = stack.getTopPiece();
if (mySlot != null) {
myPiece = mySlot.getPiece();
}
myStack.updatePosition();
savePosition = new Point(myStack.pos);
if (stack instanceof DrawPile) {
dummySize = new Dimension(((DrawPile) stack).getSize());
}
else {
dummySize = new Dimension(DEFAULT_DUMMY_SIZE, DEFAULT_DUMMY_SIZE);
}
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
cancel();
}
});
}
// Main Entry Point
protected void init() {
board = getConfigureBoard();
view = new View(board, myStack);
view.addKeyListener(this);
view.addMouseListener(this);
view.setFocusable(true);
scroll =
new AdjustableSpeedScrollPane(
view,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scroll.setPreferredSize(DEFAULT_SIZE);
add(scroll, BorderLayout.CENTER);
Box textPanel = Box.createVerticalBox();
textPanel.add(new JLabel("Arrow Keys - Move Stack"));
textPanel.add(new JLabel("Ctrl/Shift Keys - Move Stack Faster "));
Box displayPanel = Box.createHorizontalBox();
Box buttonPanel = Box.createHorizontalBox();
JButton snapButton = new JButton("Snap to grid");
snapButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
snap();
view.grabFocus();
}
});
buttonPanel.add(snapButton);
JButton okButton = new JButton("Ok");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
StackConfigurer.this.setVisible(false);
// Update the Component configurer to reflect the change
xConfig.setValue(String.valueOf(myStack.pos.x));
yConfig.setValue(String.valueOf(myStack.pos.y));
if (locationConfig != null) { // DrawPile's do not have a location
updateLocation();
locationConfig.setValue(location);
}
}
});
JPanel okPanel = new JPanel();
okPanel.add(okButton);
JButton canButton = new JButton("Cancel");
canButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cancel();
StackConfigurer.this.setVisible(false);
}
});
okPanel.add(canButton);
Box controlPanel = Box.createHorizontalBox();
controlPanel.add(textPanel);
controlPanel.add(displayPanel);
controlPanel.add(buttonPanel);
Box mainPanel = Box.createVerticalBox();
mainPanel.add(controlPanel);
mainPanel.add(okPanel);
add(mainPanel, BorderLayout.SOUTH);
scroll.revalidate();
updateDisplay();
pack();
repaint();
}
protected void cancel() {
myStack.pos.x = savePosition.x;
myStack.pos.y = savePosition.y;
}
public void updateDisplay() {
if (!view.getVisibleRect().contains(myStack.pos)) {
view.center(new Point(myStack.pos.x, myStack.pos.y));
}
}
protected void snap() {
MapGrid grid = board.getGrid();
if (grid != null) {
Point snapTo = grid.snapTo(pos);
pos.x = snapTo.x;
pos.y = snapTo.y;
updateDisplay();
repaint();
}
}
public JScrollPane getScroll() {
return scroll;
}
/*
* If the piece to be displayed does not have an Image, then we
* need to supply a dummy one.
*/
public BufferedImage getDummyImage() {
if (dummyImage == null) {
dummyImage = ImageUtils.createCompatibleTranslucentImage(
dummySize.width*2, dummySize.height*2);
final Graphics2D g = dummyImage.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, dummySize.width, dummySize.height);
g.setColor(Color.black);
g.drawRect(0, 0, dummySize.width, dummySize.height);
g.dispose();
}
return dummyImage;
}
public void drawDummyImage(Graphics g, int x, int y) {
drawDummyImage(g, x-dummySize.width/2, y-dummySize.height/2, null, 1.0);
}
public void drawDummyImage(Graphics g, int x, int y, Component obs, double zoom) {
g.drawImage(getDummyImage(), x, y, obs);
}
public void drawImage(Graphics g, int x, int y, Component obs, double zoom) {
Rectangle r = myPiece == null ? null : myPiece.boundingBox();
if (r == null || r.width == 0 || r.height == 0) {
drawDummyImage(g, x, y);
}
else {
myPiece.draw(g, x, y, obs, zoom);
}
}
public Rectangle getPieceBoundingBox() {
Rectangle r = myPiece == null ? new Rectangle() : myPiece.getShape().getBounds();
if (r == null || r.width == 0 || r.height == 0) {
r.x = 0 - dummySize.width/2;
r.y = 0 - dummySize.height/2;
r.width = dummySize.width;
r.height = dummySize.height;
}
return r;
}
public void actionPerformed(ActionEvent e) {
}
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
adjustY(-1, e);
break;
case KeyEvent.VK_DOWN:
adjustY(1, e);
break;
case KeyEvent.VK_LEFT:
adjustX(-1, e);
break;
case KeyEvent.VK_RIGHT:
adjustX(1, e);
break;
default :
if (myPiece != null) {
myPiece.keyEvent(KeyStroke.getKeyStrokeForEvent(e));
}
break;
}
updateDisplay();
repaint();
e.consume();
}
protected void adjustX(int direction, KeyEvent e) {
int delta = direction * DELTA;
if (e.isShiftDown()) {
delta *= FAST;
}
if (e.isControlDown()) {
delta *= FASTER;
}
int newX = myStack.pos.x + delta;
if (newX < 0) newX = 0;
if (newX >= board.getSize().getWidth()) newX = (int) board.getSize().getWidth() - 1;
myStack.pos.x = newX;
}
protected void adjustY(int direction, KeyEvent e) {
int delta = direction * DELTA;
if (e.isShiftDown()) {
delta *= FAST;
}
if (e.isControlDown()) {
delta *= FASTER;
}
int newY = myStack.pos.y + delta;
if (newY < 0) newY = 0;
if (newY >= board.getSize().getHeight()) newY = (int) board.getSize().getHeight() - 1;
myStack.pos.y = newY;
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
Rectangle r = getPieceBoundingBox();
r.translate(pos.x, pos.y);
if (myPiece != null && e.isMetaDown() && r.contains(e.getPoint())) {
JPopupMenu popup = MenuDisplayer.createPopup(myPiece);
popup.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) {
view.repaint();
}
public void popupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt) {
view.repaint();
}
public void popupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) {
}
});
if (view.isShowing()) {
popup.show(view, e.getX(), e.getY());
}
}
}
public void mouseReleased(MouseEvent e) {
}
}
public ComponentI18nData getI18nData() {
ComponentI18nData myI18nData = super.getI18nData();
myI18nData.setAttributeTranslatable(LOCATION, false);
return myI18nData;
}
// FIXME: check for duplication with PieceMover
public static class View extends JPanel implements DropTargetListener, DragGestureListener, DragSourceListener, DragSourceMotionListener {
private static final long serialVersionUID = 1L;
protected static final int CURSOR_ALPHA = 127;
protected static final int EXTRA_BORDER = 4;
protected Board myBoard;
protected MapGrid myGrid;
protected SetupStack myStack;
protected GamePiece myPiece;
protected PieceSlot slot;
protected DragSource ds = DragSource.getDefaultDragSource();
protected boolean isDragging = false;
protected JLabel dragCursor;
protected JLayeredPane drawWin;
protected Point drawOffset = new Point();
protected Rectangle boundingBox;
protected int currentPieceOffsetX;
protected int currentPieceOffsetY;
protected int originalPieceOffsetX;
protected int originalPieceOffsetY;
protected Point lastDragLocation = new Point();
public View(Board b, SetupStack s) {
myBoard = b;
myGrid = b.getGrid();
myStack = s;
slot = myStack.getTopPiece();
if (slot != null) {
myPiece = slot.getPiece();
}
new DropTarget(this, DnDConstants.ACTION_MOVE, this);
ds.createDefaultDragGestureRecognizer(this,
DnDConstants.ACTION_MOVE, this);
setFocusTraversalKeysEnabled(false);
}
public void paint(Graphics g) {
myBoard.draw(g, 0, 0, 1.0, this);
Rectangle bounds = new Rectangle(new Point(),myBoard.bounds().getSize());
if (myGrid != null) {
myGrid.draw(g,bounds,bounds,1.0,false);
}
int x = myStack.pos.x;
int y = myStack.pos.y;
myStack.stackConfigurer.drawImage(g, x, y, this, 1.0);
}
public void update(Graphics g) {
// To avoid flicker, don't clear the display first *
paint(g);
}
public Dimension getPreferredSize() {
return new Dimension(
myBoard.bounds().width,
myBoard.bounds().height);
}
public void center(Point p) {
Rectangle r = this.getVisibleRect();
if (r.width == 0) {
r.width = DEFAULT_SIZE.width;
r.height = DEFAULT_SIZE.height;
}
int x = p.x-r.width/2;
int y = p.y-r.height/2;
if (x < 0) x = 0;
if (y < 0) y = 0;
scrollRectToVisible(new Rectangle(x, y, r.width, r.height));
}
public void dragEnter(DropTargetDragEvent arg0) {
return;
}
public void dragOver(DropTargetDragEvent e) {
scrollAtEdge(e.getLocation(), 15);
}
public void scrollAtEdge(Point evtPt, int dist) {
JScrollPane scroll = myStack.stackConfigurer.getScroll();
Point p = new Point(evtPt.x - scroll.getViewport().getViewPosition().x,
evtPt.y - scroll.getViewport().getViewPosition().y);
int dx = 0, dy = 0;
if (p.x < dist && p.x >= 0)
dx = -1;
if (p.x >= scroll.getViewport().getSize().width - dist
&& p.x < scroll.getViewport().getSize().width)
dx = 1;
if (p.y < dist && p.y >= 0)
dy = -1;
if (p.y >= scroll.getViewport().getSize().height - dist
&& p.y < scroll.getViewport().getSize().height)
dy = 1;
if (dx != 0 || dy != 0) {
Rectangle r = new Rectangle(scroll.getViewport().getViewRect());
r.translate(2 * dist * dx, 2 * dist * dy);
r = r.intersection(new Rectangle(new Point(0, 0), getPreferredSize()));
scrollRectToVisible(r);
}
}
public void dropActionChanged(DropTargetDragEvent arg0) {
return;
}
public void drop(DropTargetDropEvent event) {
removeDragCursor();
Point pos = event.getLocation();
pos.translate(currentPieceOffsetX, currentPieceOffsetY);
myStack.pos.x = pos.x;
myStack.pos.y = pos.y;
myStack.stackConfigurer.updateDisplay();
repaint();
return;
}
public void dragExit(DropTargetEvent arg0) {
return;
}
public void dragEnter(DragSourceDragEvent arg0) {
return;
}
public void dragOver(DragSourceDragEvent arg0) {
return;
}
public void dropActionChanged(DragSourceDragEvent arg0) {
return;
}
public void dragDropEnd(DragSourceDropEvent arg0) {
removeDragCursor();
return;
}
public void dragExit(DragSourceEvent arg0) {
return;
}
public void dragGestureRecognized(DragGestureEvent dge) {
Point mousePosition = dge.getDragOrigin();
Point piecePosition = new Point(myStack.pos);
// Check drag starts inside piece
Rectangle r = myStack.stackConfigurer.getPieceBoundingBox();
r.translate(piecePosition.x, piecePosition.y);
if (!r.contains(mousePosition)) {
return;
}
originalPieceOffsetX = piecePosition.x - mousePosition.x;
originalPieceOffsetY = piecePosition.y - mousePosition.y;
drawWin = null;
makeDragCursor();
setDragCursor();
SwingUtilities.convertPointToScreen(mousePosition, drawWin);
moveDragCursor(mousePosition.x, mousePosition.y);
// begin dragging
try {
dge.startDrag(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR),
new StringSelection(""), this); // DEBUG
dge.getDragSource().addDragSourceMotionListener(this);
}
catch (InvalidDnDOperationException e) {
ErrorDialog.bug(e);
}
}
protected void setDragCursor() {
JRootPane rootWin = SwingUtilities.getRootPane(this);
if (rootWin != null) {
// remove cursor from old window
if (dragCursor.getParent() != null) {
dragCursor.getParent().remove(dragCursor);
}
drawWin = rootWin.getLayeredPane();
calcDrawOffset();
dragCursor.setVisible(true);
drawWin.add(dragCursor, JLayeredPane.DRAG_LAYER);
}
}
/** Moves the drag cursor on the current draw window */
protected void moveDragCursor(int dragX, int dragY) {
if (drawWin != null) {
dragCursor.setLocation(dragX - drawOffset.x, dragY - drawOffset.y);
}
}
private void removeDragCursor() {
if (drawWin != null) {
if (dragCursor != null) {
dragCursor.setVisible(false);
drawWin.remove(dragCursor);
}
drawWin = null;
}
}
/** calculates the offset between cursor dragCursor positions */
private void calcDrawOffset() {
if (drawWin != null) {
// drawOffset is the offset between the mouse location during a drag
// and the upper-left corner of the cursor
// accounts for difference betwen event point (screen coords)
// and Layered Pane position, boundingBox and off-center drag
drawOffset.x = -boundingBox.x - currentPieceOffsetX + EXTRA_BORDER;
drawOffset.y = -boundingBox.y - currentPieceOffsetY + EXTRA_BORDER;
SwingUtilities.convertPointToScreen(drawOffset, drawWin);
}
}
private BufferedImage featherDragImage(BufferedImage src,
int w, int h, int b) {
// FIXME: duplicated from PieceMover!
final BufferedImage dst =
ImageUtils.createCompatibleTranslucentImage(w, h);
final Graphics2D g = dst.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// paint the rectangle occupied by the piece at specified alpha
g.setColor(new Color(0xff, 0xff, 0xff, CURSOR_ALPHA));
g.fillRect(0, 0, w, h);
// feather outwards
for (int f = 0; f < b; ++f) {
final int alpha = CURSOR_ALPHA * (f + 1) / b;
g.setColor(new Color(0xff, 0xff, 0xff, alpha));
g.drawRect(f, f, w-2*f, h-2*f);
}
// paint in the source image
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN));
g.drawImage(src, 0, 0, null);
g.dispose();
return dst;
}
private void makeDragCursor() {
//double zoom = 1.0;
// create the cursor if necessary
if (dragCursor == null) {
dragCursor = new JLabel();
dragCursor.setVisible(false);
}
//dragCursorZoom = zoom;
currentPieceOffsetX = originalPieceOffsetX;
currentPieceOffsetY = originalPieceOffsetY;
// Record sizing info and resize our cursor
boundingBox = myStack.stackConfigurer.getPieceBoundingBox();
calcDrawOffset();
final int w = boundingBox.width + EXTRA_BORDER * 2;
final int h = boundingBox.height + EXTRA_BORDER * 2;
BufferedImage cursorImage =
ImageUtils.createCompatibleTranslucentImage(w, h);
final Graphics2D g = cursorImage.createGraphics();
myStack.stackConfigurer.drawImage(
g,
EXTRA_BORDER - boundingBox.x,
EXTRA_BORDER - boundingBox.y, dragCursor, 1.0
);
g.dispose();
dragCursor.setSize(w, h);
cursorImage = featherDragImage(cursorImage, w, h, EXTRA_BORDER);
// store the bitmap in the cursor
dragCursor.setIcon(new ImageIcon(cursorImage));
}
public void dragMouseMoved(DragSourceDragEvent event) {
if (!event.getLocation().equals(lastDragLocation)) {
lastDragLocation = event.getLocation();
moveDragCursor(event.getX(), event.getY());
if (dragCursor != null && !dragCursor.isVisible()) {
dragCursor.setVisible(true);
}
}
}
}
}