package de.axxeed.animosy.gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.text.NumberFormat;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.Timer;
import org.apache.log4j.Logger;
import de.axxeed.animosy.model.BoardModel;
import de.axxeed.animosy.model.Game;
import de.axxeed.animosy.model.Manager;
import de.axxeed.animosy.model.Move;
/**
* @author Markus J. Luzius
* Created 25.04.2006 18:14:09
*
*/
public class MapPanel extends JPanel implements MouseListener, MouseMotionListener, MouseWheelListener {
private static final long serialVersionUID = -6456650170894306985L;
private static Logger log = Logger.getLogger(MapPanel.class);
private static Point offset = new Point(0,0);
private boolean isToFit = false;
private Point prevPos = new Point(-1, -1);
private long timeFirstClick = -1;
private boolean isDoubleclick = false;
private static final long dblClickTimeMs = 250;
private Board boardUI = new Board();
private double scale = 1.0;
private static final int PULSE_TIME = 150;
Timer pulseTimer = new Timer(PULSE_TIME, new PulseListener());
public MapPanel() {
init();
}
public void init() {
this.setBackground(Color.LIGHT_GRAY);
// this.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1));
this.setMinimumSize(new Dimension(300,250));
this.addMouseListener(this);
this.addMouseMotionListener(this);
this.addMouseWheelListener(this);
this.addKeyListener(GUIEventListener.getInstance());
}
public void updatePositions() {
boardUI.setChanged();
this.repaint();
}
private void switchFitToScreenSize() {
if(!isToFit) {
double xs = (double) this.getWidth() / boardUI.getImage().getWidth();
double ys = (double) this.getHeight() / boardUI.getImage().getHeight();
if(xs<ys) {
scale = xs;
}
else {
scale = ys;
}
}
else {
scale = 1.0;
}
isToFit = !isToFit;
log.debug("New scale factor: "+scale);
updateOffset(0, 0);
}
public double getZoom() {
return scale;
}
public String getZoomInfo() {
NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(2);
nf.setMinimumFractionDigits(2);
return nf.format(scale*100)+"%";
}
public Image getMiniImage(int w, int h) {
double scale = getMiniImageZoom(w, h);
return boardUI.getImage().getScaledInstance((int) (boardUI.getImage().getWidth()*scale), (int) (boardUI.getImage().getHeight()*scale), BufferedImage.SCALE_FAST);
}
public double getMiniImageZoom(int w, int h) {
double dx = (double) w / boardUI.getImage().getWidth();
double dy = (double) h / boardUI.getImage().getHeight();
return Math.min(dx, dy);
}
private void updateOffset(int x, int y) {
if(isToFit) {
PanelRepository.get(PanelRepository.STATUS_PANEL).repaint();
return;
}
int pixX = (int)((double) this.getWidth() / scale);
int pixY = (int)((double) this.getHeight() / scale);
int maxX = boardUI.getImage().getWidth();
int maxY = boardUI.getImage().getHeight();
if(!isToFit) {
// log.debug("changing offset: "+x+"/"+y);
offset.translate(x, y);
if(offset.x>maxX-pixX) {
offset.x = Math.max(0, maxX-pixX);
}
else if(offset.x<0) {
offset.x = 0;
}
if(offset.y>maxY-pixY) {
offset.y = Math.max(0, maxY-pixY);
}
else if(offset.y<0) {
offset.y = 0;
}
}
PanelRepository.get(PanelRepository.STATUS_PANEL).repaint();
this.repaint();
}
public Point getOffset() {
return offset;
}
public void paintComponent ( Graphics g )
{
super.paintComponent(g);
// long ts = System.currentTimeMillis();
if(boardUI==null) return;
int state = Manager.getGame().getState();
if(state>Game.MOVE_MRX && state<Game.WIN_MRX && !pulseTimer.isRunning()) {
pulseTimer.start();
}
else if((state==Game.MOVE_MRX || state==Game.NO_GAME || state==Game.WIN_MRX) && pulseTimer.isRunning()) {
pulseTimer.stop();
}
else if(state==Game.WIN_DET) {
pulseTimer.setDelay(PULSE_TIME * 5);
}
// log.debug("Painting map ("+this.getWidth()+"x"+this.getHeight()+")...");
Graphics2D g2d = (Graphics2D) g;
BufferedImage mapImage = boardUI.getImage();
AffineTransform at;
// System.out.println("scale factor: "+scale);
at = AffineTransform.getScaleInstance(scale, scale);
int pixX = (int)((double) this.getWidth() / scale);
int pixY = (int)((double) this.getHeight() / scale);
// log.debug("Max image size on screen ("+this.getWidth()+"/"+this.getHeight()+") with current zoom: "+pixX+"/"+pixY);
if(pixX>mapImage.getWidth() && pixY>mapImage.getHeight()) {
g2d.drawImage (mapImage, new AffineTransformOp(at, null), 0, 0);
}
else if(pixX>mapImage.getWidth()) {
g2d.drawImage (mapImage.getSubimage(0, offset.y, mapImage.getWidth(), pixY), new AffineTransformOp(at, null), 0, 0);
}
else if(pixY>mapImage.getHeight()) {
g2d.drawImage (mapImage.getSubimage(offset.x, 0, pixX, mapImage.getHeight()), new AffineTransformOp(at, null), 0, 0);
}
else {
int maxX = pixX;
int maxY = pixY;
if(mapImage.getWidth()<maxX) maxX = mapImage.getWidth()-1;
if(mapImage.getHeight()<maxY) maxY = mapImage.getHeight()-1;
g2d.drawImage (mapImage.getSubimage(offset.x, offset.y, maxX, maxY), new AffineTransformOp(at, null), 0, 0);
}
g2d.dispose();
// log.debug("mapPanel: "+(System.currentTimeMillis()-ts)+"ms");
}
public void mouseClicked(MouseEvent e) {
// XXX Auto-generated method stub
if(e.getButton()==1) {
if(timeFirstClick>0 && System.currentTimeMillis()-timeFirstClick<dblClickTimeMs) {
// double click
isDoubleclick = true;
log.debug("Resizing map image... "+(System.currentTimeMillis()-timeFirstClick)+"ms");
switchFitToScreenSize();
this.repaint();
timeFirstClick = -1;
}
else {
timeFirstClick = System.currentTimeMillis();
// set timer for other action
Timer timer = new Timer((int) dblClickTimeMs+10, new DoubleClickListener(new Point(e.getX(), e.getY())));
timer.start();
}
}
else {
int pos = boardUI.getPos((int)(scale*(e.getPoint().x+offset.x)), (int)(scale*(e.getPoint().y+offset.y)));
int state = Manager.getGame().getState();
if(pos>0 && state>Game.MOVE_MRX && state<Game.WIN_MRX) {
JPopupMenu popup = new JPopupMenu();
if(Manager.getGame().getActiveDetective().canMoveTo(pos)) {
Set<Move> possibleMoves = Manager.getGame().getBoard().getDetectives()[state-1].getPossibleMoves();
// log.debug("Moves for detective ["+state+"]: "+possibleMoves);
for(Move m: possibleMoves) {
log.debug("node "+m.getNode()+", pos="+pos);
if(m.getNode()!=pos) continue;
StringBuilder linkText = new StringBuilder();
linkText.append("move to ").append(m.getNode()).append(" (");
switch(m.getType()) {
case BoardModel.TAXI: linkText.append("TAXI").append(")"); break;
case BoardModel.BUS: linkText.append("BUS").append(")"); break;
case BoardModel.UG: linkText.append("UG").append(")"); break;
case BoardModel.BLACK: break;
case BoardModel.INF: linkText.append("INF").append(")"); break;
default: linkText.append("???").append(")"); break;
}
// detectiveMenu.add( new JMenuItem(linkText.toString()) );
AbstractAction action = new AbstractAction(linkText.toString())
{
private static final long serialVersionUID = -4379938472159497577L;
public void actionPerformed( ActionEvent e ) {
log.debug("Selected move: "+((Move)this.getValue(e.getActionCommand())));
Manager.getGame().moveDetective((Move)this.getValue(e.getActionCommand()));
PanelRepository.get(PanelRepository.STATUS_PANEL).repaint();
}
};
action.putValue(linkText.toString(), m);
action.putValue("Detective", Manager.getGame().getBoard().getDetectives()[state-1]);
popup.add( action );
}
}
else {
JMenuItem item = new JMenuItem("can't move to ["+pos+"]");
item.setEnabled(false);
popup.add(item);
}
if (popup != null) {
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
// FIXME here I am!!!! xx
timeFirstClick = -1;
}
}
public void mouseEntered(MouseEvent e) {
// XXX Auto-generated method stub
}
public void mouseExited(MouseEvent e) {
// XXX Auto-generated method stub
}
public void mousePressed(MouseEvent e) {
if(e.getButton()==1) {
log.debug("Setting initial drag point..."+e.getPoint());
prevPos.setLocation(e.getPoint());
}
}
public void mouseReleased(MouseEvent e) {
// log.debug("dragging is over!");
prevPos.setLocation(-1, -1);
}
public void mouseDragged(MouseEvent arg0) {
// log.debug("dragged..."+arg0.getX()+"/"+arg0.getY());
updateOffset(prevPos.x-arg0.getX(), prevPos.y-arg0.getY());
prevPos.setLocation(arg0.getPoint());
}
public void mouseMoved(MouseEvent arg0) {
// XXX Auto-generated method stub
if(timeFirstClick > 0) timeFirstClick = -1;
}
private class DoubleClickListener implements ActionListener {
private Point pos;
public DoubleClickListener(Point p) {
pos = p;
}
public void actionPerformed(ActionEvent e) {
((Timer) e.getSource()).stop();
if(isDoubleclick) {
isDoubleclick = false;
}
else {
log.debug("Single click on left mouse button at pos "+pos+"...");
PanelRepository.get(PanelRepository.STATUS_PANEL).repaint();
}
}
}
private class PulseListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
boardUI.pulse();
boardUI.setChanged();
repaint();
}
}
public void mouseWheelMoved(MouseWheelEvent mwe) {
isToFit = false;
if(mwe.getWheelRotation()<0) {
// shrink
if(scale*10!=Math.rint(scale*100)) {
scale = Math.rint((scale-0.05)*100.) / 100.;
}
else {
scale-=0.01;
}
if(scale<0.3) scale = 0.3;
}
else {
// blow up
if(scale*10!=Math.rint(scale*100)) {
scale = Math.rint((scale+0.05)*100.) / 100.;
}
else {
scale+=0.01;
}
if(scale>1.5) scale = 1.5;
}
// log.debug("New scale factor: "+scale);
updateOffset(0, 0);
}
}