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; } }