package jrummikub.view.impl; import java.awt.Insets; import java.awt.Point; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.geom.Rectangle2D; import java.util.Collections; import javax.swing.JPanel; import jrummikub.model.Position; import jrummikub.model.Stone; import jrummikub.util.Event1; import jrummikub.util.Event2; import jrummikub.util.Pair; import jrummikub.view.IClickable; import jrummikub.view.IStonePanel; /** * Base class for panels that draw stones */ @SuppressWarnings("serial") abstract class AbstractStonePanel extends JPanel implements IStonePanel, IClickable { private StonePainter stonePainter; private Event1 clickEvent = new Event1(); private Event2 stoneClickEvent = new Event2(); private Event2 rangeClickEvent = new Event2(); private Event2 setClickEvent = new Event2(); private Iterable> stones = Collections.emptySet(); private Stone hoveredStone = null; /** * @return the stone painter */ protected StonePainter getStonePainter() { return stonePainter; } /** * Create a new StonePanel with default scale factor */ public AbstractStonePanel() { this(1); } /** * Create a new StonePanel with a given scale factor * * @param scale * the grid scale */ public AbstractStonePanel(float scale) { super(true); // Set double buffered stonePainter = new StonePainter(scale); addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { clickAt(e.getPoint(), e.getClickCount(), e.isShiftDown(), e.isControlDown(), e.isAltDown()); } @Override public void mouseExited(MouseEvent e) { setHoveredStone(null); } }); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { Insets insets = getInsets(); Pair trans = getTranslation(); Position pos = stonePainter.calculatePosition(e.getX() - insets.left - trans.getFirst(), e.getY() - insets.top - trans.getSecond()); setHoveredStone(getStoneAt(pos)); handleOtherMoveEvent(pos); } }); } /** * clickAt is called when a click has occurred * * @param p * the point in component coordinates * @param clickCount * the click count * @param shift * is shift down? * @param control * is control down? * @param alt * is alt down? */ protected void clickAt(Point p, int clickCount, boolean shift, boolean control, boolean alt) { Insets insets = getInsets(); Pair trans = getTranslation(); Position pos = stonePainter.calculatePosition( p.x - insets.left - trans.getFirst(), p.y - insets.top - trans.getSecond()); if (alt) { clickEvent.emit(pos); return; } Stone stone = getStoneAt(pos); if (stone == null) { if (!handleOtherClickEvent(pos)) clickEvent.emit(pos); return; } Event2 event = stoneClickEvent; if (shift) event = rangeClickEvent; else if (clickCount >= 2) event = setClickEvent; event.emit(stone, control); } /** * Sets the stone over which the cursor hovers * * @param stone * stone under cursor */ private void setHoveredStone(Stone stone) { Stone oldStone = hoveredStone; hoveredStone = stone; if (oldStone != hoveredStone) repaint(); } /** * Returns the stone the mouse pointer is hovering over * * @return the hovered stone */ protected Stone getHoveredStone() { return hoveredStone; } /** * Overwrite this method to signal if special zone was clicked * * @param pos * the clicked position * * @return special zone clicked */ protected boolean handleOtherClickEvent(Position pos) { return false; } /** * Overwrite this method to signal if special zone was hovered * * @param pos * the hovered position */ protected void handleOtherMoveEvent(Position pos) { } /** * Gets the stone at the specified position * * @param pos * position * @return the stone */ private Stone getStoneAt(Position pos) { for (Pair entry : stones) { Stone stone = entry.getFirst(); Position p = entry.getSecond(); Rectangle2D rect = new Rectangle2D.Double(p.getX(), p.getY(), 1, 1); if (rect.contains(pos.getX(), pos.getY())) return stone; } return null; } /** * Sets the list of stones that can be clicked on * * @param stones * the stones and positions */ protected void setStones(Iterable> stones) { this.stones = stones; } /** * Returns the translation in pixels the stones in this panel are painted * with * * @return the translation */ protected Pair getTranslation() { return new Pair(0, 0); } /** * Returns the list of stones and positions currently set * * @return the stones */ protected Iterable> getStones() { return stones; } @Override public Event1 getClickEvent() { return clickEvent; } @Override public Event2 getStoneClickEvent() { return stoneClickEvent; } @Override public Event2 getRangeClickEvent() { return rangeClickEvent; } @Override public Event2 getSetClickEvent() { return setClickEvent; } }