/* * Copyright (c) 2014 tabletoptool.com team. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * rptools.com team - initial implementation * tabletoptool.com team - further development */ package com.t3.client.tool; import java.awt.Dimension; import java.awt.Image; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.io.IOException; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JRadioButton; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.JTextComponent; import com.jeta.forms.components.panel.FormPanel; import com.t3.client.AppState; import com.t3.client.ScreenPoint; import com.t3.client.TabletopTool; import com.t3.client.ui.zone.ZoneRenderer; import com.t3.image.ImageUtil; import com.t3.model.Zone; import com.t3.model.ZonePoint; import com.t3.model.drawing.DrawablePaint; import com.t3.model.drawing.DrawableTexturePaint; import com.t3.model.grid.Grid; import com.t3.util.ImageManager; import com.t3.util.StringUtil; /** * Allows user to re-position the background map (internally called the * 'board'). This entire class should be 'transient'... should this be in the * var names, or in the reference to the BoardTool instance? */ public class BoardTool extends DefaultTool { private static final long serialVersionUID = 98389912045059L; // Context variables private static Zone zone; private static boolean oldShowGrid; // Status variables private static Point boardPosition = new Point(0, 0); private static Dimension snap = new Dimension(1, 1); // Action control variables private Point dragStart; private Dimension dragOffset; private Point boardStart; // UI button fields private final JTextField boardPositionXTextField; private final JTextField boardPositionYTextField; private final FormPanel controlPanel; private final JRadioButton snapNoneButton; private final JRadioButton snapGridButton; private final JRadioButton snapTileButton; /** * Initialize the panel and set up the actions. */ public BoardTool() { try { setIcon(new ImageIcon(ImageUtil.getImage("com/t3/client/image/board.png"))); } catch (IOException ioe) { ioe.printStackTrace(); } // Create the control panel controlPanel = new FormPanel("com/t3/client/ui/forms/adjustBoardControlPanel.xml"); // controlPanel.setBorder(BorderFactory.createLineBorder(Color.black)); boardPositionXTextField = controlPanel.getTextField("offsetX"); boardPositionXTextField.addKeyListener(new UpdateBoardListener()); boardPositionYTextField = controlPanel.getTextField("offsetY"); boardPositionYTextField.addKeyListener(new UpdateBoardListener()); ActionListener enforceRules = new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { enforceButtonRules(); } }; snapNoneButton = controlPanel.getRadioButton("snapNone"); snapNoneButton.addActionListener(enforceRules); snapGridButton = controlPanel.getRadioButton("snapGrid"); snapGridButton.addActionListener(enforceRules); snapTileButton = controlPanel.getRadioButton("snapTile"); snapTileButton.addActionListener(enforceRules); JButton closeButton = (JButton) controlPanel.getComponentByName("closeButton"); closeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { resetTool(); } }); } @Override protected void installKeystrokes(Map<KeyStroke, Action> actionMap) { super.installKeystrokes(actionMap); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), new boardPositionAction(Direction.Up)); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), new boardPositionAction(Direction.Left)); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), new boardPositionAction(Direction.Down)); actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), new boardPositionAction(Direction.Right)); } /** * Figure out how big the repeating board tile image is. */ private Dimension getTileSize() { Zone zone = renderer.getZone(); Dimension tileSize = null; if (zone != null) { DrawablePaint paint = zone.getBackgroundPaint(); DrawableTexturePaint dummy = new DrawableTexturePaint(); if (paint.getClass() == dummy.getClass()) { Image bgTexture = ImageManager.getImage(((DrawableTexturePaint) paint).getAsset().getId()); tileSize = new Dimension(bgTexture.getWidth(null), bgTexture.getHeight(null)); } } return tileSize; } /** * Moves the board to the nearest snap intersection. Modifies GUI. */ private void snapBoard() { boardPosition.x = (boardPosition.x / snap.width) * snap.width; boardPosition.y = (boardPosition.y / snap.height) * snap.height; updateGUI(); } /** * Sets the snap mode with independent x/y snaps and adjusts the board * position appropriately. * * @param x * the new x snap amount * @param y * the new y snap amount */ private void setSnap(int x, int y) { snap.width = x; snap.height = y; snapBoard(); } private void updateGUI() { boardPositionXTextField.setText(Integer.toString(boardPosition.x)); boardPositionYTextField.setText(Integer.toString(boardPosition.y)); } /** * Copies the current board (map image as set in "New Map/Edit Map") info to * the tool so we have the appropriate starting info. Should be called each * time the tool is un-hidden. */ private void copyBoardToControlPanel() { boardPosition.x = zone.getBoardX(); boardPosition.y = zone.getBoardY(); snapBoard(); updateGUI(); } private void copyControlPanelToBoard() { boardPosition.x = getInt(boardPositionXTextField, 0); boardPosition.y = getInt(boardPositionYTextField, 0); zone.setBoard(boardPosition); } @Override public String getTooltip() { return "tool.boardtool.tooltip"; } @Override public String getInstructions() { return "tool.boardtool.instructions"; } /** * Parses the text field of the component into a number, returning the * default value if the text field is _not_ a number. */ private int getInt(JTextComponent component, int defaultValue) { // Get the string from the component, then // call the more-generic getInt-from-a-string return StringUtil.parseInteger(component.getText(), defaultValue); } /* * private double getDouble(String value, double defaultValue) { try { * return value.length() > 0 ? Double.parseDouble(value.trim()) : * defaultValue; } catch (NumberFormatException e) { return 0; } } */ /* * (non-Javadoc) * * @see com.t3.client.Tool#attachTo(com.t3.client.ZoneRenderer) */ @Override protected void attachTo(ZoneRenderer renderer) { super.attachTo(renderer); zone = renderer.getZone(); copyBoardToControlPanel(); oldShowGrid = AppState.isShowGrid(); AppState.setShowGrid(true); // Find out if it is already aligned to grid or background tile, and // default to keeping that same alignment. final int offset = zone.getBoardX(); final Dimension tileSize = getTileSize(); final int gridSize = zone.getGrid().getSize(); if ((tileSize != null) && ((offset % tileSize.width) == 0)) { setSnap(tileSize.width, tileSize.height); snapTileButton.setSelected(true); } else if ((offset % gridSize) == 0) { setSnap(gridSize, gridSize); snapGridButton.setSelected(true); } else { setSnap(1, 1); snapNoneButton.setSelected(true); } TabletopTool.getFrame().showControlPanel(controlPanel); } /* * (non-Javadoc) * * @see tabletoptool.client.Tool#detachFrom(tabletoptool.client.ZoneRenderer) */ @Override protected void detachFrom(ZoneRenderer renderer) { TabletopTool.getFrame().hideControlPanel(); TabletopTool.serverCommand().setBoard(zone.getId(), zone.getMapAssetId(), zone.getBoardX(), zone.getBoardY()); AppState.setShowGrid(oldShowGrid); super.detachFrom(renderer); } //// // MOUSE LISTENER @Override public void mousePressed(java.awt.event.MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { ZonePoint zp = new ScreenPoint(e.getX(), e.getY()).convertToZone(renderer); Grid grid = renderer.getZone().getGrid(); dragStart = new Point(zp.x - grid.getOffsetX(), zp.y - grid.getOffsetY()); boardStart = new Point(boardPosition); dragOffset = new Dimension(0, 0); } else { super.mousePressed(e); } } //// // MOUSE MOTION LISTENER @Override public void mouseDragged(java.awt.event.MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { ZonePoint zp = new ScreenPoint(e.getX(), e.getY()).convertToZone(renderer); dragOffset.width = zp.x - dragStart.x; dragOffset.height = zp.y - dragStart.y; boardPosition.x = boardStart.x + dragOffset.width; boardPosition.y = boardStart.y + dragOffset.height; snapBoard(); updateGUI(); zone.setBoard(boardPosition); } else { super.mouseDragged(e); } } @Override public void mouseMoved(java.awt.event.MouseEvent e) { mouseX = e.getX(); mouseY = e.getY(); } /** * A simple enum for correlating keys with directions */ private static enum Direction { Left, Right, Up, Down }; /** * Constructs actions to attach to key-presses. */ @SuppressWarnings("serial") private class boardPositionAction extends AbstractAction { private final Direction direction; public boardPositionAction(Direction direction) { this.direction = direction; } @Override public void actionPerformed(ActionEvent e) { switch (direction) { case Left: boardPosition.x -= snap.width; break; case Right: boardPosition.x += snap.width; break; case Up: boardPosition.y -= snap.height; break; case Down: boardPosition.y += snap.height; break; } updateGUI(); zone.setBoard(boardPosition); } } //// // ACTIONS private class UpdateBoardListener implements KeyListener, ChangeListener, FocusListener { @Override public void keyPressed(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { copyControlPanelToBoard(); } @Override public void keyTyped(KeyEvent e) { } @Override public void stateChanged(ChangeEvent e) { copyControlPanelToBoard(); } @Override public void focusLost(FocusEvent e) { copyControlPanelToBoard(); } @Override public void focusGained(FocusEvent e) { } } private void enforceButtonRules() { if (snapGridButton.isSelected()) { final int gridSize = zone.getGrid().getSize(); setSnap(gridSize, gridSize); } else if (snapTileButton.isSelected()) { final Dimension tileSize = getTileSize(); if (tileSize != null) setSnap(tileSize.width, tileSize.height); else setSnap(1, 1); } else { setSnap(1, 1); } updateGUI(); zone.setBoard(boardPosition); } }