/*
* 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.adjustgrid;
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.RenderingHints;
import java.awt.event.ActionEvent;
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.image.BufferedImage;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import com.t3.client.AppActions;
import com.t3.client.ui.Scale;
public class AdvancedAdjustGridPanel extends JComponent implements MouseListener, MouseMotionListener, MouseWheelListener {
private static final int MINIMUM_GRID_SIZE = 5;
private int gridCountX = 10;
private int gridCountY = 10;
private boolean showGrid = true;
private int mouseX;
private int mouseY;
private BufferedImage image;
private Scale scale;
private enum Handle {
TOP, BOTTOM, LEFT, RIGHT
}
private int topGrid;
private int bottomGrid;
private int leftGrid;
private int rightGrid;
private boolean showRows = true;
private boolean showCols = true;
private Handle draggingHandle;
public AdvancedAdjustGridPanel() {
setOpaque(false);
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(AppActions.ZOOM_OUT.getKeyStroke(), "zoomOut");
getActionMap().put("zoomOut", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
zoomOut();
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(AppActions.ZOOM_IN.getKeyStroke(), "zoomIn");
getActionMap().put("zoomIn", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
zoomIn();
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(AppActions.ZOOM_RESET.getKeyStroke(), "zoomReset");
getActionMap().put("zoomReset", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
zoomReset();
}
});
}
public Rectangle getGridBounds() {
return new Rectangle(topGrid, leftGrid, rightGrid - leftGrid, bottomGrid - topGrid);
}
public void setZoneImage(BufferedImage image) {
this.image = image;
topGrid = 0;
bottomGrid = image.getHeight();
leftGrid = 0;
rightGrid = image.getWidth();
scale = new Scale(image.getWidth(), image.getHeight());
}
public void setShowRows(boolean show) {
showRows = show;
}
public void setShowCols(boolean show) {
showCols = show;
}
@Override
protected void paintComponent(Graphics g) {
if (image == null) {
return;
}
Dimension size = getSize();
scale.initialize(size.width, size.height);
// CALCULATIONS
Dimension imageSize = getScaledImageSize();
Point imagePosition = getScaledImagePosition();
double imgRatio = scale.getScale();
// handles
int top = (int) (topGrid * imgRatio);
int bottom = (int) (bottomGrid * imgRatio);
int left = (int) (leftGrid * imgRatio);
int right = (int) (rightGrid * imgRatio);
// SETUP
Graphics2D g2d = (Graphics2D) g;
// BG FILL
g2d.setColor(getBackground());
g2d.fillRect(0, 0, size.width, size.height);
// IMAGE
g2d.drawImage(image, imagePosition.x, imagePosition.y, imageSize.width, imageSize.height, null);
// GRID
g2d.setColor(Color.blue);
double dx = ((rightGrid - leftGrid) / (float) gridCountX) * imgRatio;
double dy = ((bottomGrid - topGrid) / (float) gridCountY) * imgRatio;
// across
if (showCols) {
int x = imagePosition.x + left;
for (int i = 0; i < gridCountX; i++) {
g2d.drawLine(x + (int) (i * dx), imagePosition.y + top, x + (int) (i * dx), imagePosition.y + bottom);
}
}
// down
if (showRows) {
int y = imagePosition.y + top;
for (int i = 0; i < gridCountY; i++) {
g2d.drawLine(imagePosition.x + left, y + (int) (i * dy), imagePosition.x + right, y + (int) (i * dy));
}
}
// HANDLES
int handleSize = 10;
Object oldValue = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.red);
// Top
int halfHandleX = imagePosition.x + imageSize.width / 2;
g2d.fillRect(halfHandleX - handleSize, top + imagePosition.y - 3, handleSize * 2, 3);
g2d.drawLine(imagePosition.x - handleSize, top + imagePosition.y, imagePosition.x + imageSize.width + handleSize, top + imagePosition.y);
// Bottom
g2d.fillRect(halfHandleX - handleSize, bottom + imagePosition.y + 1, handleSize * 2, 3);
g2d.drawLine(imagePosition.x - handleSize, bottom + imagePosition.y, imagePosition.x + imageSize.width + handleSize, bottom + imagePosition.y);
// Left
int halfHandleY = imagePosition.y + imageSize.height / 2;
g2d.fillRect(left + imagePosition.x - 3, halfHandleY - handleSize, 3, handleSize * 2);
g2d.drawLine(left + imagePosition.x, imagePosition.y - handleSize, left + imagePosition.x, imagePosition.y + imageSize.height + handleSize);
// Right
g2d.fillRect(right + 1 + imagePosition.x, halfHandleY - handleSize, 3, handleSize * 2);
g2d.drawLine(right + imagePosition.x, imagePosition.y - handleSize, right + imagePosition.x, imagePosition.y + imageSize.height + handleSize);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldValue);
}
@Override
public boolean isFocusable() {
return true;
}
public void setGridCountX(int count) {
gridCountX = count;
repaint();
}
public void setGridCountY(int count) {
gridCountY = count;
repaint();
}
private float getScaledImageRatio() {
return getScaledImageSize().width / (float) image.getWidth();
}
private Dimension getScaledImageSize() {
Dimension imageSize = new Dimension(image.getWidth(), image.getHeight());
imageSize.width *= scale.getScale();
imageSize.height *= scale.getScale();
return imageSize;
}
private Point getScaledImagePosition() {
int imgX = scale.getOffsetX();
int imgY = scale.getOffsetY();
return new Point(imgX, imgY);
}
private void updateHandles(MouseEvent e) {
// Convert
float imgRatio = getScaledImageRatio();
Point imgPosition = getScaledImagePosition();
Point location = e.getPoint();
location.translate(-imgPosition.x, -imgPosition.y);
location = new Point((int) (location.x / imgRatio), (int) (location.y / imgRatio));
switch (draggingHandle) {
case TOP: {
if (location.y < 0) {
location.y = 0;
}
if (location.y > bottomGrid - MINIMUM_GRID_SIZE) {
location.y = bottomGrid - MINIMUM_GRID_SIZE;
}
topGrid = location.y;
break;
}
case BOTTOM: {
if (location.y < topGrid + MINIMUM_GRID_SIZE) {
location.y = topGrid + MINIMUM_GRID_SIZE;
}
if (location.y > image.getHeight()) {
location.y = image.getHeight();
}
bottomGrid = location.y;
break;
}
case LEFT: {
if (location.x < 0) {
location.x = 0;
}
if (location.x > rightGrid - MINIMUM_GRID_SIZE) {
location.x = rightGrid - MINIMUM_GRID_SIZE;
}
leftGrid = location.x;
break;
}
case RIGHT: {
if (location.x < leftGrid + MINIMUM_GRID_SIZE) {
location.x = leftGrid + MINIMUM_GRID_SIZE;
}
if (location.x > image.getWidth()) {
location.x = image.getWidth();
}
rightGrid = location.x;
break;
}
}
repaint();
}
public void zoomIn() {
scale.zoomIn(mouseX, mouseY);
repaint();
}
public void zoomOut() {
scale.zoomOut(mouseX, mouseY);
repaint();
}
public void zoomReset() {
scale.reset();
repaint();
}
////
// MOUSE LISTENER
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
float imgRatio = getScaledImageRatio();
Point imgPos = getScaledImagePosition();
int top = (int) (topGrid * imgRatio) + imgPos.y;
int bottom = (int) (bottomGrid * imgRatio) + imgPos.y;
int left = (int) (leftGrid * imgRatio) + imgPos.x;
int right = (int) (rightGrid * imgRatio) + imgPos.x;
int distTop = Math.abs(e.getY() - top);
int distBottom = Math.abs(e.getY() - bottom);
int distLeft = Math.abs(e.getX() - left);
int distRight = Math.abs(e.getX() - right);
int dist = distTop;
draggingHandle = Handle.TOP;
if (distBottom < dist) {
dist = distBottom;
draggingHandle = Handle.BOTTOM;
}
if (distLeft < dist) {
dist = distLeft;
draggingHandle = Handle.LEFT;
}
if (distRight < dist) {
dist = distRight;
draggingHandle = Handle.RIGHT;
}
updateHandles(e);
} else {
mouseX = e.getX();
mouseY = e.getY();
}
}
@Override
public void mouseReleased(MouseEvent e) {
draggingHandle = null;
repaint();
}
////
// MOUSE MOTION LISTENER
@Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
updateHandles(e);
} else {
int offsetX = scale.getOffsetX() + e.getX() - mouseX;
int offsetY = scale.getOffsetY() + e.getY() - mouseY;
scale.setOffset(offsetX, offsetY);
mouseX = e.getX();
mouseY = e.getY();
}
repaint();
}
@Override
public void mouseMoved(MouseEvent e) {
Dimension imgSize = getScaledImageSize();
Point imgPos = getScaledImagePosition();
boolean insideMap = e.getX() > imgPos.x && e.getX() < imgPos.x + imgSize.width && e.getY() > imgPos.y && e.getY() < imgPos.y + imgSize.height;
if ((insideMap && showGrid) || (!insideMap && !showGrid)) {
showGrid = !insideMap;
repaint();
}
mouseX = e.getX();
mouseY = e.getY();
}
////
// MOUSE WHEEL LISTENER
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getWheelRotation() > 0) {
scale.zoomOut(e.getX(), e.getY());
} else {
scale.zoomIn(e.getX(), e.getY());
}
repaint();
}
}