/* * 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.ui.token; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.TexturePaint; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.geom.Area; import java.awt.image.BufferedImage; import javax.swing.JPanel; import javax.swing.SwingUtilities; import com.t3.client.AppStyle; import com.t3.client.TabletopTool; import com.t3.model.Token; import com.t3.model.Zone; import com.t3.swing.SwingUtil; import com.t3.util.ImageManager; /** * Support class used by the token editor dialog on the "Properties" tab to allow a token's image to be moved around * within a one-cell grid area. Scaling is supported using the mousewheel and position is supported using left-drag. We * should add rotation ability using Shift-mousewheel as well. * * @author trevor */ public class TokenLayoutPanel extends JPanel { private Token token; private int dragOffsetX; private int dragOffsetY; public TokenLayoutPanel() { addMouseWheelListener(new MouseWheelListener() { @Override public void mouseWheelMoved(MouseWheelEvent e) { // Not for snap-to-scale if (!token.isSnapToScale()) { return; } double delta = e.getWheelRotation() > 0 ? -.1 : .1; if (SwingUtil.isShiftDown(e)) { // Nothing yet, as changing the facing isn't the right way to handle it -- // the image itself really should be rotated. And it's probably better to // not simply store a Transform but to create a new image. We could // store an AffineTransform until the dialog is closed and then create // the new image. But the amount of rotation needs to be saved so // that future adjustments can return back to the original image (as // a way of reducing round off error from multiple rotations). } double scale = token.getSizeScale() + delta; // Range scale = Math.max(.1, scale); scale = Math.min(3, scale); token.setSizeScale(scale); repaint(); } }); addMouseListener(new MouseAdapter() { String old; @Override public void mousePressed(MouseEvent e) { dragOffsetX = e.getX(); dragOffsetY = e.getY(); } @Override public void mouseEntered(MouseEvent e) { old = TabletopTool.getFrame().getStatusMessage(); TabletopTool.getFrame().setStatusMessage("Mouse Wheel to zoom; double-LClick to reset position and zoom"); } @Override public void mouseExited(MouseEvent e) { if (old != null) TabletopTool.getFrame().setStatusMessage(old); } @Override public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) { dragOffsetX = 0; dragOffsetY = 0; token.setAnchor(0, 0); token.setSizeScale(1.0); repaint(); } } }); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { int dx = e.getX() - dragOffsetX; int dy = e.getY() - dragOffsetY; Zone zone = TabletopTool.getFrame().getCurrentZoneRenderer().getZone(); int gridSize = zone.getGrid().getSize(); int halfGridSize = gridSize / 2; int maxXoff = Math.max(halfGridSize, token.getBounds(zone).width - gridSize); int maxYoff = Math.max(halfGridSize, token.getBounds(zone).height - gridSize); int offX = Math.min(maxXoff, Math.max(token.getAnchor().x + dx, -maxXoff)); int offY = Math.min(maxYoff, Math.max(token.getAnchor().y + dy, -maxYoff)); token.setAnchor(offX, offY); dragOffsetX = e.getX(); dragOffsetY = e.getY(); repaint(); } }); } public double getSizeScale() { return token.getSizeScale(); } public int getAnchorX() { return token.getAnchor().x; } public int getAnchorY() { return token.getAnchor().y; } public void setToken(Token token) { this.token = new Token(token); } @Override protected void paintComponent(Graphics g) { Dimension size = getSize(); Zone zone = TabletopTool.getFrame().getCurrentZoneRenderer().getZone(); // Gather info BufferedImage image = ImageManager.getImage(token.getImageAssetId()); Rectangle tokenSize = token.getBounds(zone); Dimension imgSize = new Dimension(image.getWidth(), image.getHeight()); SwingUtil.constrainTo(imgSize, tokenSize.width, tokenSize.height); Point centerPoint = new Point(size.width / 2, size.height / 2); Graphics2D g2d = (Graphics2D) g; // Background ((Graphics2D) g).setPaint(new TexturePaint(AppStyle.panelTexture, new Rectangle(0, 0, AppStyle.panelTexture.getWidth(), AppStyle.panelTexture.getHeight()))); g2d.fillRect(0, 0, size.width, size.height); AppStyle.shadowBorder.paintWithin((Graphics2D) g, 0, 0, size.width, size.height); // Grid if (zone.getGrid().getCapabilities().isSnapToGridSupported()) { Area gridShape = zone.getGrid().getCellShape(); int offsetX = (size.width - gridShape.getBounds().width) / 2; int offsetY = (size.height - gridShape.getBounds().height) / 2; g2d.setColor(Color.black); // Add horizontal and vertical lines to help with centering g2d.drawLine(0, size.height / 2, size.width, size.height / 2); g2d.drawLine(size.width / 2, 0, size.width / 2, size.height); g2d.translate(offsetX, offsetY); g2d.draw(gridShape); g2d.translate(-offsetX, -offsetY); } // Token g2d.drawImage(image, centerPoint.x - imgSize.width / 2 + token.getAnchor().x, centerPoint.y - imgSize.height / 2 + token.getAnchor().y, imgSize.width, imgSize.height, this); } }