package jrummikub.view.impl; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.RenderingHints; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.swing.ImageIcon; import javax.swing.JLabel; import jrummikub.model.Position; import jrummikub.model.Stone; import jrummikub.model.StoneSet; import jrummikub.util.Event1; import jrummikub.util.Pair; import jrummikub.view.IStoneCollectionPanel; import jrummikub.view.ITablePanel; /** * The implementation of the table */ @SuppressWarnings("serial") class TablePanel extends AbstractStonePanel implements ITablePanel { private final static ImageIcon background = new ImageIcon( HandPanel.class.getResource("/jrummikub/resource/felt.png")); private final static float MIN_VISIBLE_WIDTH = 15; private final static float MIN_VISIBLE_HEIGHT = 7.5f; private final static float HORIZONTAL_MARGIN = 1.5f; private final static float VERTICAL_MARGIN = 1; private final static float CONNECTOR_WIDTH = 0.25f; private final float COLLECTION_RATIO = 0.1f; private final int COLLECTION_GAP = 5; private JLabel leftPlayerLabel, topPlayerLabel, rightPlayerLabel; private StoneCollectionPanel stoneCollection; private Map stoneSets = Collections.emptyMap(); private Collection selectedStones = Collections.emptyList(); private Event1 leftConnectorClickEvent = new Event1(); private Event1 rightConnectorClickEvent = new Event1(); @Override public void setLeftPlayerName(String playerName) { leftPlayerLabel.setText(playerName); } @Override public void setTopPlayerName(String playerName) { topPlayerLabel.setText(playerName); } @Override public void setRightPlayerName(String playerName) { rightPlayerLabel.setText(playerName); } @Override public Event1 getLeftConnectorClickEvent() { return leftConnectorClickEvent; } @Override public Event1 getRightConnectorClickEvent() { return rightConnectorClickEvent; } @Override public void setStoneSets(Map stoneSets) { Map stones = new HashMap(); for (Map.Entry entry : stoneSets.entrySet()) { float x = entry.getValue().getX(), y = entry.getValue().getY(); for (Stone stone : entry.getKey()) { stones.put(stone, new Position(x, y)); x++; } } setStones(stones); this.stoneSets = stoneSets; repaint(); } @Override public IStoneCollectionPanel getStoneCollectionPanel() { return stoneCollection; } /** * Sets the currently selected stones * * @param stones * the selected stones */ void setSelectedStones(Collection stones) { selectedStones = stones; stoneCollection.setSelectedStones(stones); repaint(); } /** * Creates a new Table instance */ TablePanel() { setLayout(null); leftPlayerLabel = new JLabel(); leftPlayerLabel.setForeground(Color.WHITE); leftPlayerLabel.setHorizontalAlignment(JLabel.LEFT); leftPlayerLabel.setHorizontalTextPosition(JLabel.LEFT); add(leftPlayerLabel); topPlayerLabel = new JLabel(); topPlayerLabel.setHorizontalAlignment(JLabel.CENTER); topPlayerLabel.setHorizontalTextPosition(JLabel.CENTER); topPlayerLabel.setVerticalAlignment(JLabel.TOP); topPlayerLabel.setVerticalTextPosition(JLabel.TOP); topPlayerLabel.setForeground(Color.WHITE); add(topPlayerLabel); rightPlayerLabel = new JLabel(); rightPlayerLabel.setForeground(Color.WHITE); rightPlayerLabel.setHorizontalAlignment(JLabel.RIGHT); rightPlayerLabel.setHorizontalTextPosition(JLabel.RIGHT); add(rightPlayerLabel); stoneCollection = new StoneCollectionPanel(); add(stoneCollection); ComponentListener rescaleListener = new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { rescale(); } }; addComponentListener(rescaleListener); stoneCollection.addComponentListener(rescaleListener); } private Rectangle2D calculateTableExtent() { float minx = -MIN_VISIBLE_WIDTH / 2, maxx = MIN_VISIBLE_WIDTH / 2; float miny = -MIN_VISIBLE_HEIGHT / 2, maxy = MIN_VISIBLE_HEIGHT / 2; for (Map.Entry entry : stoneSets.entrySet()) { Position p = entry.getValue(); StoneSet stoneSet = entry.getKey(); if (p.getX() < minx) minx = p.getX(); if (p.getY() < miny) miny = p.getY(); if (p.getX() + stoneSet.size() > maxx) maxx = p.getX() + stoneSet.size(); if (p.getY() + 1 > maxy) maxy = p.getY() + 1; } return new Rectangle2D.Float(minx - HORIZONTAL_MARGIN, miny - VERTICAL_MARGIN, maxx - minx + 2 * HORIZONTAL_MARGIN, maxy - miny + 2 * VERTICAL_MARGIN); } private void rescale() { Insets insets = getInsets(); int x = insets.left, y = insets.top, width = getWidth() - insets.left - insets.right, height = getHeight() - insets.top - insets.bottom; leftPlayerLabel.setBounds(x, y, width, height); topPlayerLabel.setBounds(x, y, width, height); rightPlayerLabel.setBounds(x, y, width, height); stoneCollection.setLocation(x + width / 2 - stoneCollection.getWidth() / 2, y + height - stoneCollection.getHeight() - COLLECTION_GAP); Rectangle2D extent = calculateTableExtent(); float widthScale = width / (float) extent.getWidth() * StonePainter.WIDTH_SCALE; float heightScale = height * (1 - COLLECTION_RATIO) / (float) extent.getHeight() * StonePainter.HEIGHT_SCALE; getStonePainter().setScale(Math.min(widthScale, heightScale)); stoneCollection.setStoneHeight((int) (height * COLLECTION_RATIO)); repaint(); } protected boolean handleOtherClickEvent(Position pos) { for (Map.Entry entry : stoneSets.entrySet()) { Position p = entry.getValue(); StoneSet stoneSet = entry.getKey(); float x = p.getX(), y = p.getY(); // left connector Rectangle2D rect = new Rectangle2D.Float(x - CONNECTOR_WIDTH, y, CONNECTOR_WIDTH, 1); if (rect.contains(pos.getX(), pos.getY())) { leftConnectorClickEvent.emit(stoneSet); return true; } // right connector rect = new Rectangle2D.Float(x + stoneSet.size(), y, CONNECTOR_WIDTH, 1); if (rect.contains(pos.getX(), pos.getY())) { rightConnectorClickEvent.emit(stoneSet); return true; } } return false; } @Override protected Pair getTranslation() { Insets insets = getInsets(); int width = getWidth() - insets.left - insets.right, height = getHeight() - insets.top - insets.bottom; int stoneWidth = getStonePainter().getStoneWidth(), stoneHeight = getStonePainter() .getStoneHeight(); Rectangle2D extent = calculateTableExtent(); return new Pair((int) (width / 2 - extent.getCenterX() * stoneWidth), (int) ((height * (1 - COLLECTION_RATIO)) / 2 - extent.getCenterY() * stoneHeight)); } private void paintStoneSet(Graphics2D g, StoneSet stoneSet, Position pos) { float x = pos.getX(); int width = getStonePainter().getStoneWidth(), height = getStonePainter() .getStoneHeight(); g.setColor(new Color(0, 0, 0, 0.25f)); g.fillRect((int) (x * width - width * CONNECTOR_WIDTH), (int) (pos.getY() * height), (int) (width * CONNECTOR_WIDTH), height); for (Stone stone : stoneSet) { getStonePainter().paintStone(g, stone, new Position(x, pos.getY()), selectedStones.contains(stone)); x++; } g.setColor(new Color(0, 0, 0, 0.25f)); g.fillRect((int) (x * width), (int) (pos.getY() * height), (int) (width * CONNECTOR_WIDTH), height); } @Override protected void paintComponent(Graphics g1) { Graphics2D g = (Graphics2D) g1; for (int x = 0; x < getWidth(); x += background.getIconWidth()) { for (int y = 0; y < getHeight(); y += background.getIconHeight()) { background.paintIcon(this, g, x, y); } } AffineTransform oldTransform = g.getTransform(); Pair translation = getTranslation(); g.translate(translation.getFirst(), translation.getSecond()); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); for (Map.Entry entry : stoneSets.entrySet()) { paintStoneSet(g, entry.getKey(), entry.getValue()); } g.setTransform(oldTransform); } }