package com.drawbridge.utils;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import javax.swing.border.AbstractBorder;
import com.drawbridge.correspondance.RadialSpotlight;
import com.drawbridge.dm.DMObject;
import com.drawbridge.dm.DMSimpleObject;
import com.drawbridge.dm.DMSimplePanel;
import com.drawbridge.dm.ManipulationSource;
enum HandleName
{
N, S, E, W, NE, NW, SE, SW
}
public class HandleBorder extends AbstractBorder
{
private static final long serialVersionUID = 1L;
private BufferedImage handleImage;
private int mBorderThickness;
private int mHandleHeight = 0;
private boolean mDetailsVisible;
private boolean mVisible = false;
private Circle mHandleNW;
private Circle mHandleNE;
private Circle mHandleW;
private Circle mHandleE;
private Circle mHandleN;
private Circle mHandleS;
private Circle mHandleSE;
private Circle mHandleSW;
private Rectangle mSurroundingRect;
public Point mMouseDown = null;
private HandleName mSelectedHandle = null;
public boolean mXSpotlight = false;
public boolean mYSpotlight = false;
public boolean mWidthSpotlight = false;
public boolean mHeightSpotlight = false;
public void clearSpotlights()
{
mXSpotlight = false;
mYSpotlight = false;
mWidthSpotlight = false;
mHeightSpotlight = false;
}
public HandleBorder(boolean detailsVisible) {
mDetailsVisible = detailsVisible;
handleImage = GraphicUtils.loadImageFromResource("/Assets/handle.png");
mHandleHeight = handleImage.getHeight();
if (detailsVisible)
{
mBorderThickness = handleImage.getHeight() + 50;
}
else
{
mBorderThickness = handleImage.getHeight();
}
}
public void paintBorder(Component comp, Graphics g, int x, int y, int width, int height)
{
if (mVisible)
{
updateCirclePositions(width, height);
Graphics2D g2 = (Graphics2D) g;
if (mDetailsVisible)
{
final int stakeWidth = 20;
final int padding = 5;
int radius = mHandleHeight / 2;
int textHeight = g2.getFontMetrics().getHeight();
g2.setColor(Color.black);
// Set the text
String xText = "(" + ((DMSimpleObject) comp).getCounterpart().getX() + ",";
String yText = ((DMSimpleObject) comp).getCounterpart().getY() + ")";
String widthText = "" + (comp.getWidth() - ((mBorderThickness - handleImage.getHeight()) * 2));
String heightText = "" + (comp.getHeight() - ((mBorderThickness - handleImage.getHeight()) * 2));
int widthTextWidth = g2.getFontMetrics().stringWidth(widthText);
final int widthTextX = (mHandleNW.x + mHandleNE.x - widthTextWidth) / 2;
final int heightTextWidth = g2.getFontMetrics().stringWidth(heightText);
final int heightTextY = (mHandleNW.y + mHandleSW.y - textHeight) / 2;
// Paint Spotlights
int reach = 20;
int xTextWidth = g2.getFontMetrics().stringWidth(xText);
int yTextWidth = g2.getFontMetrics().stringWidth(yText);
int widthLineY = comp.getHeight() - mBorderThickness + 30;
int heightLineX = comp.getWidth() - mBorderThickness + 30;
Point topLeftPoint = new Point(mBorderThickness - padding - 30, mBorderThickness - textHeight);
// x
if (mXSpotlight)
new RadialSpotlight(topLeftPoint.x + (xTextWidth / 2), topLeftPoint.y,
(xTextWidth / 2) - 4, reach).paintRadialSpotlight(g2);
// y
if (mYSpotlight)
new RadialSpotlight(topLeftPoint.x + xTextWidth + (yTextWidth / 2), topLeftPoint.y,
(yTextWidth / 2) - 4,
reach).paintRadialSpotlight(g2);
// width
if (mWidthSpotlight)
new RadialSpotlight(widthTextX + padding + (widthTextWidth / 2), widthLineY + 2,
(widthTextWidth / 2) - 4,
reach).paintRadialSpotlight(g2);
// height
if (mHeightSpotlight)
new RadialSpotlight(heightLineX, heightTextY + textHeight - 4,
(heightTextWidth / 2) - 4,
reach).paintRadialSpotlight(g2);
g2.drawString(xText + yText, topLeftPoint.x, topLeftPoint.y);
g2.setColor(Color.gray);
// Bottom left and right
g2.drawLine(mHandleNW.x + radius, widthLineY - (stakeWidth / 2), mHandleNW.x + radius, widthLineY + (stakeWidth / 2));
g2.drawLine(mHandleNE.x + radius, widthLineY - (stakeWidth / 2), mHandleNE.x + radius, widthLineY + (stakeWidth / 2));
// Right top and bottom
g2.drawLine(heightLineX - (stakeWidth / 2), mHandleNW.y + radius, heightLineX + (stakeWidth / 2), mHandleNW.y + radius);
g2.drawLine(heightLineX - (stakeWidth / 2), mHandleSW.y + radius, heightLineX + (stakeWidth / 2), mHandleSW.y + radius);
// Width Label
g2.drawLine(mHandleNW.x + radius + padding, widthLineY, widthTextX, widthLineY);
g2.drawLine(widthTextX + widthTextWidth + padding, widthLineY, mHandleNE.x - padding, widthLineY);
g2.setColor(Color.black);
g2.drawString(widthText, widthTextX + padding, widthLineY + (textHeight / 2));
// Width Arrow Heads
g2.drawLine(mHandleNW.x + radius + padding, widthLineY, mHandleNW.x + radius + padding + 5, widthLineY - 5);
g2.drawLine(mHandleNW.x + radius + padding, widthLineY, mHandleNW.x + radius + padding + 5, widthLineY + 5);
g2.drawLine(mHandleNE.x - padding, widthLineY, mHandleNE.x - padding - 5, widthLineY - 5);
g2.drawLine(mHandleNE.x - padding, widthLineY, mHandleNE.x - padding - 5, widthLineY + 5);
// Height Label
g2.setColor(Color.gray);
g2.drawLine(heightLineX, mHandleNW.y + radius + padding, heightLineX, heightTextY);
g2.drawLine(heightLineX, heightTextY + padding + textHeight, heightLineX, mHandleSW.y);
g2.setColor(Color.black);
g2.drawString(heightText, heightLineX - (heightTextWidth / 2), heightTextY + textHeight);
// Height Arrow Heads
g2.drawLine(heightLineX, mHandleNW.y + radius + padding, heightLineX - 5, mHandleNW.y + radius + padding + 5);
g2.drawLine(heightLineX, mHandleNW.y + radius + padding, heightLineX + 5, mHandleNW.y + radius + padding + 5);
g2.drawLine(heightLineX, mHandleSW.y, heightLineX - 5, mHandleSW.y - padding);
g2.drawLine(heightLineX, mHandleSW.y, heightLineX + 5, mHandleSW.y - padding);
}
// Border
g2.setColor(Color.decode("#3E9CE0"));
g2.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, null, 0.0f));
g2.drawRect(mSurroundingRect.x, mSurroundingRect.y, mSurroundingRect.width, mSurroundingRect.height);
// North West
g2.drawImage((Image) handleImage, mHandleNW.x, mHandleNW.y, mHandleHeight, mHandleHeight, null);
// North East
g2.drawImage((Image) handleImage, mHandleNE.x, mHandleNE.y, mHandleHeight, mHandleHeight, null);
// East
g2.drawImage((Image) handleImage, mHandleE.x, mHandleE.y, mHandleHeight, mHandleHeight, null);
// West
g2.drawImage((Image) handleImage, mHandleW.x, mHandleW.y, mHandleHeight, mHandleHeight, null);
// North
g2.drawImage((Image) handleImage, mHandleN.x, mHandleN.y, mHandleHeight, mHandleHeight, null);
// South
g2.drawImage((Image) handleImage, mHandleS.x, mHandleS.y, mHandleHeight, mHandleHeight, null);
// South West
g2.drawImage((Image) handleImage, mHandleSW.x, mHandleSW.y, mHandleHeight, mHandleHeight, null);
// South East
g2.drawImage((Image) handleImage, mHandleSE.x, mHandleSE.y, mHandleHeight, mHandleHeight, null);
}
}
public void updateCirclePositions(int width, int height)
{
int radius = mHandleHeight / 2;
int right = width - mBorderThickness;
int left = mBorderThickness - mHandleHeight;
int bottom = height - mBorderThickness;
int top = mBorderThickness - mHandleHeight;
int midX = (right + left) / 2;
int midY = (top + bottom) / 2;
mHandleNW = new Circle(left, top, radius);
mHandleNE = new Circle(right, top, radius);
mHandleE = new Circle(right, midY, radius);
mHandleW = new Circle(left, midY, radius);
mHandleN = new Circle(midX, top, radius);
mHandleS = new Circle(midX, bottom, radius);
mHandleSW = new Circle(left, bottom, radius);
mHandleSE = new Circle(right, bottom, radius);
mSurroundingRect = new Rectangle(left + radius, top + radius, right - left, bottom - top);
}
public Insets getBorderInsets(Component c)
{
return new Insets(mBorderThickness, mBorderThickness, mBorderThickness, mBorderThickness);
}
public boolean contains(int x, int y)
{
if (mVisible)
{
if (mHandleNW != null && mHandleNW.contains(x, y))
return true;
else if (mHandleNE != null && mHandleNE.contains(x, y))
return true;
else if (mHandleN != null && mHandleN.contains(x, y))
return true;
else if (mHandleS != null && mHandleS.contains(x, y))
return true;
else if (mHandleS != null && mHandleE.contains(x, y))
return true;
else if (mHandleW != null && mHandleW.contains(x, y))
return true;
else if (mHandleSW != null && mHandleSW.contains(x, y))
return true;
else if (mHandleSE != null && mHandleSE.contains(x, y))
return true;
}
return false;
}
public void mouseDragged(DMObject object, int x, int y)
{
if (mVisible && mMouseDown != null)
{
stretchBorder(object, mSelectedHandle, x, y);
object.repaint();
}
}
public void mouseMoved(DMObject object, int x, int y)
{
if (mVisible)
{
if (mHandleNW.contains(x, y))
object.setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
else if (mHandleNE.contains(x, y))
object.setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
else if (mHandleN.contains(x, y))
object.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
else if (mHandleS.contains(x, y))
object.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
else if (mHandleE.contains(x, y))
object.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
else if (mHandleW.contains(x, y))
object.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
else if (mHandleSW.contains(x, y))
object.setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
else if (mHandleSE.contains(x, y))
object.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
}
}
public void mousePressed(int x, int y)
{
if (mVisible)
{
mMouseDown = new Point(x, y);
if (mHandleNW.contains(x, y))
mSelectedHandle = HandleName.NW;
else if (mHandleNE.contains(x, y))
mSelectedHandle = HandleName.NE;
else if (mHandleN.contains(x, y))
mSelectedHandle = HandleName.N;
else if (mHandleS.contains(x, y))
mSelectedHandle = HandleName.S;
else if (mHandleE.contains(x, y))
mSelectedHandle = HandleName.E;
else if (mHandleW.contains(x, y))
mSelectedHandle = HandleName.W;
else if (mHandleSW.contains(x, y))
mSelectedHandle = HandleName.SW;
else if (mHandleSE.contains(x, y))
mSelectedHandle = HandleName.SE;
else
Utils.out.println(this.getClass(), "error");
}
}
public void mouseReleased(int x, int y)
{
if (mVisible)
{
mMouseDown = null;
mSelectedHandle = null;
}
}
// Incoming x and y are relative to the DMObject
public void stretchBorder(DMObject mObject, HandleName n, int x, int y)
{
int offset = mBorderThickness - (mHandleHeight / 2);
switch (n)
{
case NE:
Point newLocation = new Point(mObject.getX(), mObject.getY() + y - offset);
mObject.setLocation(newLocation);
if (DMSimplePanel.getInstance().isStoppedAndEmpty())
{
mObject.setPreferredLocation(newLocation, ManipulationSource.SOURCE_HUMAN);
}
setSizeWithinConstraints(mObject, x + offset, mObject.getHeight() - y + offset);
break;
case NW:
newLocation = new Point(mObject.getX() + x - offset, mObject.getY() + y - offset);
mObject.setLocation(newLocation);
setSizeWithinConstraints(mObject, mObject.getWidth() - x + offset, mObject.getHeight() - y + offset);
if (DMSimplePanel.getInstance().isStoppedAndEmpty())
{
mObject.setPreferredLocation(newLocation, ManipulationSource.SOURCE_HUMAN);
}
break;
case N:
newLocation = new Point(mObject.getX(), mObject.getY() + y - offset);
mObject.setLocation(newLocation);
setSizeWithinConstraints(mObject, mObject.getWidth(), mObject.getHeight() - y + offset);
if (DMSimplePanel.getInstance().isStoppedAndEmpty())
{
mObject.setPreferredLocation(newLocation, ManipulationSource.SOURCE_HUMAN);
}
break;
case SE:
setSizeWithinConstraints(mObject, x + offset, y + offset);
break;
case SW:
newLocation = new Point(mObject.getX() + x - offset, mObject.getY());
mObject.setLocation(newLocation);
setSizeWithinConstraints(mObject, mObject.getWidth() - x + offset, y + offset);
if (DMSimplePanel.getInstance().isStoppedAndEmpty())
{
mObject.setPreferredLocation(newLocation, ManipulationSource.SOURCE_HUMAN);
}
break;
case S:
setSizeWithinConstraints(mObject, mObject.getWidth(), y + offset);
break;
case E:
setSizeWithinConstraints(mObject, x + offset, mObject.getHeight());
break;
case W:
newLocation = new Point(mObject.getX() + x - offset, mObject.getY());
mObject.setLocation(newLocation);
setSizeWithinConstraints(mObject, mObject.getWidth() - x + offset, mObject.getHeight());
if (DMSimplePanel.getInstance().isStoppedAndEmpty())
{
mObject.setPreferredLocation(newLocation, ManipulationSource.SOURCE_HUMAN);
}
break;
}
}
// Makes sure it's not possible to reduce an object to less than its minimum
// size
private void setSizeWithinConstraints(DMObject mObject, int width, int height)
{
mObject.setSize(Math.max(width, DMObject.MINIMUM_OBJECT_SIZE.width), Math.max(height, DMObject.MINIMUM_OBJECT_SIZE.height));
}
public int getThickness()
{
return mBorderThickness;
}
public void setVisible(boolean visible)
{
mVisible = visible;
}
}