package jas2.util.multifunctionpanel;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import jas2.util.multibuttontoolbar.*;
import jas2.plot.EditableLabel;
import java.beans.*;
/**
* A UberPanel contains objects which can be moved around and resized
* without any effort (code) on their part. This panel can be placed inside
* a content pane of anything with a JRootPane (e.g. a JFrame). The
* UberPanel will temporarily substitute its own glass pane (an inner
* class) for that of the JRootPane that contains it in order to move the
* objects around.
*
* @author Peter Armstrong
* @author Tony Johnson
*/
public class UberPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
//
//constructors
//
public UberPanel() {
this(Color.blue);
}
public UberPanel(Color color) {
super(null); // no layout manager
enableEvents(AWTEvent.CONTAINER_EVENT_MASK |
AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK);
mogp = new UberGlassPane(color);
cursorState = UberPanelStates.DEFAULT;
arrowHeadDrawState = ArrowheadDrawStates.NONE;
arrowHeadFillState = false;
labelBorderDrawState = LabelBorderDrawStates.NONE;
}
public void addNotify() {
super.addNotify();
previousGlassPane = getRootPane().getGlassPane();
getRootPane().setGlassPane(mogp);
mogp.setVisible(true);
}
public void removeNotify() {
super.removeNotify();
getRootPane().setGlassPane(previousGlassPane);
}
//
//member variables
//
private UberGlassPane mogp;
private Component previousGlassPane;
private Point dragOffset;
private Component selectedComponent;
private JPopupMenu pm;
private int cursorState;
private int arrowHeadDrawState;
private int labelBorderDrawState;
private Point dragStart;
private EditableLabel tempLabel;
private UberArrow tempArrow;
private boolean arrowHeadFillState;
private float arrowHeadLength = 15.0f;
private int numRepaints = 0;
private static final int REPAINT_FUDGE_FACTOR = 10;
private static final int FULL_REPAINT = 5;
private static int numEvents = 0;//debug
//
//methods
//
public int getCursorState() {
return cursorState;
}
public void setCursorState(int newState) {
cursorState = newState;
UberPanel.this.setCursor(Cursor.getPredefinedCursor(getCursorDisplayType()));
}
public int getArrowHeadDrawState() {
return arrowHeadDrawState;
}
public void setArrowHeadDrawState(int newState) {
arrowHeadDrawState = newState;
}
public float getArrowHeadLength() {
return arrowHeadLength;
}
public void setArrowHeadLength(float newLength) {
arrowHeadLength = newLength;
}
public boolean getArrowHeadFillState() {
return arrowHeadFillState;
}
public void setArrowHeadFillState(boolean newState) {
arrowHeadFillState = newState;
}
public int getLabelBorderDrawState() {
return labelBorderDrawState;
}
public void setLabelBorderDrawState(int newState) {
labelBorderDrawState = newState;
}
public void showArrowConfigDialog() {
}
/**
* @return the type of cursor to display
*/
public int getCursorDisplayType() {
switch (getCursorState()) {
default:
case UberPanelStates.DEFAULT :
System.out.println("DEFAULT");
return Cursor.DEFAULT_CURSOR;
case UberPanelStates.ARROW_DRAW :
System.out.println("ARROW_DRAW");
return Cursor.CROSSHAIR_CURSOR;
case UberPanelStates.LABEL_DRAW :
System.out.println("LABEL_DRAW");
return Cursor.CROSSHAIR_CURSOR;
}
}
protected void processMouseEvent(MouseEvent e) {
if (e.isPopupTrigger()) {
pm.show(e.getComponent(), e.getX(), e.getY());
} else {
switch (e.getID()) {
case MouseEvent.MOUSE_CLICKED :
if (!mogp.theMOB.isCursorOverTheBorder(e.getPoint())) {
selectedComponent = null;
this.requestFocus();
}
break;
case MouseEvent.MOUSE_PRESSED :
if (getCursor().getType() == Cursor.CROSSHAIR_CURSOR) {
dragStart = e.getPoint();
switch (getCursorState()) {
case UberPanelStates.ARROW_DRAW :
tempArrow = new UberArrow(dragStart);
tempArrow.setLocation(dragStart);
//tempArrow.setBorder(BorderFactory.createLineBorder(Color.black));//debug
add(tempArrow);
tempArrow.setVisible(true);
break;
case UberPanelStates.LABEL_DRAW :
tempLabel = new jas2.plot.EditableLabel("Click here to enter some text.","");
switch (labelBorderDrawState) {
case LabelBorderDrawStates.NONE :
break;
case LabelBorderDrawStates.THIN :
tempLabel.setBorder(BorderFactory.createLineBorder(Color.black, 1));
break;
case LabelBorderDrawStates.THICK :
tempLabel.setBorder(BorderFactory.createLineBorder(Color.black, 5));
break;
default :
}
tempLabel.setLocation(dragStart);
tempLabel.setVisible(true);
tempLabel.setRequestFocusEnabled(true);
add(tempLabel);
default :
}
}
break;
case MouseEvent.MOUSE_RELEASED :
if (getCursor().getType() == Cursor.CROSSHAIR_CURSOR) {
switch (getCursorState()) {
case UberPanelStates.ARROW_DRAW :
case UberPanelStates.LABEL_DRAW :
repaint();
break;
default :
}
}
break;
default :
}
}
}
protected void processMouseMotionEvent(MouseEvent e) {
if (e.getID() == e.MOUSE_DRAGGED) {
if (getCursor().getType() == Cursor.CROSSHAIR_CURSOR) {
switch (getCursorState()) {
case UberPanelStates.ARROW_DRAW :
updateArrowSizeAndLocation(tempArrow, e.getPoint());
//repainting just a rect is more efficient but it
//doesn't work very well, so every FULL_REPAINT times
//we do a (guess what!) full repaint
if (((++numRepaints) % FULL_REPAINT) == 0) {
repaint();
} else {
repaint(
new Rectangle(
dragStart.x - REPAINT_FUDGE_FACTOR,
dragStart.y - REPAINT_FUDGE_FACTOR,
e.getPoint().x - dragStart.x + 2*REPAINT_FUDGE_FACTOR,
e.getPoint().y - dragStart.y + 2*REPAINT_FUDGE_FACTOR));
}
break;
case UberPanelStates.LABEL_DRAW :
Point end = e.getPoint();
if ((end.x > dragStart.x) && (end.y > dragStart.y)) {
tempLabel.setSize(end.x - dragStart.x, end.y - dragStart.y);
}
validate();
break;
default :
}
}
}
}
/**
* Even though the start point is fixed, we might need to change the
* location of the tempArrow. Note that the arrow always goes between
* 2 diagonally opposite corners of the tempArrow. However, WHICH corners
* it goes between are not fixed.
*/
private void updateArrowSizeAndLocation(UberArrow p_arrow, Point p_newFinish) {
//System.out.println("updateArrowSizeAndLocation");
Point start = p_arrow.getStart();
p_arrow.updateFinish(p_newFinish);
if (start.x < p_newFinish.x) {
if (start.y < p_newFinish.y) {
p_arrow.setLocation(start.x, start.y);
} else {
p_arrow.setLocation(start.x, p_newFinish.y);
}
} else {
if (start.y < p_newFinish.y) {
p_arrow.setLocation(p_newFinish.x, start.y);
} else {
p_arrow.setLocation(p_newFinish.x, p_newFinish.y);
}
}
int buf = p_arrow.getArrowHeadBuffer();
p_arrow.setLocation(p_arrow.getLocation().x - buf, p_arrow.getLocation().y - buf);
p_arrow.setSize((Math.abs(p_arrow.getStart().x - p_arrow.getFinish().x) + 2*buf),
(Math.abs(p_arrow.getStart().y - p_arrow.getFinish().y) + 2*buf));
//who cares if our components can get partially past the edge of the
//window, the part of them that does is invisible!
p_arrow.updateThetaAndArrowHeads();
p_arrow.invalidate();
}
//
//inner classes
//
class UberComponent extends JComponent {
/**
* We need to request focus when clicked so that things like handles
* work for the UberComponent just like they do for all other subclasses
* of JComponent (which also request focus when clicked).
*/
protected void processMouseEvent(MouseEvent e) {
switch (e.getID()) {
case MouseEvent.MOUSE_CLICKED :
requestFocus();
break;
default :
super.processMouseEvent(e);
}
}
/**
* When the delete key is pressed lose focus and remove yourself from the Uber.
*/
protected void processKeyEvent(KeyEvent e) {
switch (e.getKeyChar()) {
case KeyEvent.VK_DELETE :
UberPanel.this.requestFocus();
UberPanel.this.remove(this);
break;
default :
super.processKeyEvent(e);
}
}
}
/**
* UberArrow is, not surprisingly, an arrow on an UberPanel.
* It extends JComponent in order to override the contains(int x, int y) method.
*
* @author Peter Armstrong
* @see ArrowHeadSpec.vsd
* @see ArrowPointContainmentAlgorithm.vsd
*/
class UberArrow extends UberComponent {
//
//public UberArrow methods
//
/**
* @param start the point which is the start of the arrow
*/
public UberArrow(Point start) {
this(start, 10.0);
}
/**
* @param start the point which is the start of the arrow
* @param alpha the angle (0 <= deg <= 89) of half the arrowhead.
*/
public UberArrow(Point start, double alpha) {
m1_finish = m1_start = start;
m1_alpha = alpha;
switch (getArrowHeadDrawState()) {
case ArrowheadDrawStates.NONE:
break;
case ArrowheadDrawStates.START :
m1_startArrowHead = new UberArrowHead(getArrowHeadLength(), getArrowHeadFillState(), false);
m1_arrowHeadBuffer = m1_startArrowHead.getArrowHeadBuffer();
break;
case ArrowheadDrawStates.FINISH :
m1_finishArrowHead = new UberArrowHead(getArrowHeadLength(), getArrowHeadFillState(), true);
m1_arrowHeadBuffer = m1_finishArrowHead.getArrowHeadBuffer();
break;
case ArrowheadDrawStates.BOTH :
m1_startArrowHead = new UberArrowHead(getArrowHeadLength(), getArrowHeadFillState(), false);
m1_finishArrowHead = new UberArrowHead(getArrowHeadLength(), getArrowHeadFillState(), true);
//could use either arrowhead since they're same length:
m1_arrowHeadBuffer = m1_startArrowHead.getArrowHeadBuffer();
break;
default :
System.out.println("ERROR: illegal arrowHeadState");//!PA - error handling later
break;
}
enableEvents(AWTEvent.KEY_EVENT_MASK |
AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK);
setBorder(BorderFactory.createEmptyBorder());
}
public int getArrowHeadBuffer() {
return m1_arrowHeadBuffer;
}
/**
* @return true if the point is part of the arrow (or is close enough)
* else return false
*/
public boolean contains(int x, int y) {
return contains(new Point(x,y));
}
/**
* @return true if the point is part of the arrow (or is close enough)
* else return false (this overrides the method which just uses the
* rectangular shape of the Component)
*/
public boolean contains(Point p) {
return(isPointInLineArea(p) ||
((m1_startArrowHead == null) ? false : m1_startArrowHead.contains(p)) ||
((m1_finishArrowHead == null) ? false : m1_finishArrowHead.contains(p)));
}
/**
* @param start the point which is the beginning of the arrow
*/
public void updateStart(Point start) {
m1_start = start;
}
/**
* @param finish the point which is the end of the arrow
*/
public void updateFinish(Point finish) {
m1_finish = finish;
}
/**
* Call this method to update the member variable which stores theta
* whenever you change the shape of the arrow.
*/
public void updateThetaAndArrowHeads() {
calcTheta();
if (m1_startArrowHead != null) {
m1_startArrowHead.recalculateArea();
}
if (m1_finishArrowHead != null) {
m1_finishArrowHead.recalculateArea();
}
}
/**
* Draw the arrow.
*/
public void paintComponent(Graphics g) {
switch (getQuadrant()) {
case 1:
g.drawLine(m1_arrowHeadBuffer, getSize().height - m1_arrowHeadBuffer,
getSize().width - m1_arrowHeadBuffer, m1_arrowHeadBuffer);
break;
case 2:
g.drawLine(getSize().width - m1_arrowHeadBuffer, getSize().height - m1_arrowHeadBuffer,
m1_arrowHeadBuffer, m1_arrowHeadBuffer);
break;
case 3:
g.drawLine(getSize().width - m1_arrowHeadBuffer, m1_arrowHeadBuffer,
m1_arrowHeadBuffer, getSize().height - m1_arrowHeadBuffer);
break;
case 4:
g.drawLine(m1_arrowHeadBuffer, m1_arrowHeadBuffer,
getSize().width - m1_arrowHeadBuffer, getSize().height - m1_arrowHeadBuffer);
break;
default:
}
if (m1_startArrowHead != null) {
m1_startArrowHead.drawYourself(g);
}
if (m1_finishArrowHead != null) {
m1_finishArrowHead.drawYourself(g);
}
}
/**
* @return the quadrant that the line would end in if it started at
* the origin (vertical lines are considered to be in Q1 or Q2;
* horizontal lines are considered to be in Q1 or Q4).
*
* |
* 2 | 1
* -----------
* 3 | 4
* |
*/
public int getQuadrant() {
if (getXlen() >= 0) {
if (getYlen() > 0) {
return 4;
} else {
return 1;
}
} else {
if (getYlen() > 0) {
return 3;
} else {
return 2;
}
}
}
//
//private UberArrow methods
//
private boolean isPointInLineArea(Point p) {
int[] poly_x = new int[4];
int[] poly_y = new int[4];
Polygon bounds;
switch (getQuadrant()) {
case 1:
poly_x[0] = m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_x[1] = m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_y[0] = getSize().height - m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_y[1] = getSize().height - m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_x[3] = getSize().width - m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_x[2] = getSize().width - m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_y[3] = m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_y[2] = m1_arrowHeadBuffer + m1_lineAreaWidth;
break;
case 2:
poly_x[0] = getSize().width - m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_x[1] = getSize().width - m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_y[0] = getSize().height - m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_y[1] = getSize().height - m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_x[3] = m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_x[2] = m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_y[3] = m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_y[2] = m1_arrowHeadBuffer + m1_lineAreaWidth;
break;
case 3:
poly_x[0] = getSize().width - m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_x[1] = getSize().width - m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_y[0] = m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_y[1] = m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_x[3] = m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_x[2] = m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_y[3] = getSize().height - m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_y[2] = getSize().height - m1_arrowHeadBuffer + m1_lineAreaWidth;
break;
case 4:
poly_x[0] = m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_x[1] = m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_y[0] = m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_y[1] = m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_x[3] = getSize().width - m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_x[2] = getSize().width - m1_arrowHeadBuffer + m1_lineAreaWidth;
poly_y[3] = getSize().height - m1_arrowHeadBuffer - m1_lineAreaWidth;
poly_y[2] = getSize().height - m1_arrowHeadBuffer + m1_lineAreaWidth;
break;
default:
}
bounds = new Polygon(poly_x, poly_y, 4);
//getGraphics().drawPolygon(bounds);//debug
return bounds.contains(p);
}
private void calcTheta() {
int x = getXlen();
int y = getYlen();
//System.out.println("calc theta: x = " + x + ", y = " + y);
if (x == 0) {//if line is vertical
if (y > 0) {
m1_theta = Math.PI/2.0;
} else {
m1_theta = 1.5*Math.PI;
}
} else {
m1_theta = Math.atan(((double)y)/((double)x));
switch (getQuadrant()) {
case 2:
case 3:
m1_theta += Math.PI;
break;
case 4:
m1_theta += 2.0*Math.PI;
break;
default:
}
}
}
public Point getStart() {
return m1_start;
};
public Point getFinish() {
return m1_finish;
};
/**
* WARNING: this can return + and - values! Determine the quadrant accordingly!
*/
private int getXlen() {
return m1_finish.x - m1_start.x;
}
/**
* WARNING: this can return + and - values! Determine the quadrant accordingly!
*/
private int getYlen() {
return m1_finish.y - m1_start.y;
}
//
//private UberArrow member variables
//
private double m1_alpha, m1_theta; //see ArrowPointContainmentAlgorithm.vsd
private Point m1_start; // .--> the . on this left to right arrow
private Point m1_finish; // -->. the . on this left to right arrow
private UberArrowHead m1_startArrowHead; // <-- the < on this left to right arrow
private UberArrowHead m1_finishArrowHead;// --> the > on this left to right arrow
private int m1_lineAreaWidth = 5; //see ArrowPointContainmentAlgorithm.vsd
private int m1_arrowHeadBuffer = 0; //space around arrowhead so it draws correctly
//
//inner classes
//
private class UberArrowHead {
/**
* @param len the arrowhead length (x1,y1) to (x3,y3) on ArrowHeadSpec.vsd
* @param filled true if the arrowhead is filled, else false
* @param finish true if the arrowhead is where the arrow finishes, not where it starts
* (for a left to right arrow this is --> but not <--)
*/
public UberArrowHead(double len, boolean filled, boolean finish) {
m2_len = len;
m2_filled = filled;
m2_finish = finish;
//Now we need to determine the extra space to put around the
//arrowhead in the component so that the arrowhead is not
//chopped off by the border of the JComponent (which it would
//be in almost all cases!). Rather than making this an exact
//value, we just upper bound it as the distance from (x2,y2)
//to (x3,y3) on ArrowHeadSpec.vsd.
//
//NOTE: It IS safe to just do this once in the constructor
//since m1_alpha is fixed to the value it gets in the
//UberArrow constructor.
m2_arrowHeadBuffer = (int)(Math.ceil(m2_len * Math.tan(m1_alpha)));
//System.out.println("m2_arrowheadBuffer = " + m2_arrowHeadBuffer + " pixels.");//debug
}
public int getArrowHeadBuffer() {
return m2_arrowHeadBuffer;
}
public void recalculateArea() {
//System.out.println("recalc");
Point pt = null;
if (m2_finish) {
switch (getQuadrant()) {
case 1:
pt = new Point(UberArrow.this.getSize().width - m2_arrowHeadBuffer, m2_arrowHeadBuffer);
break;
case 2:
pt = new Point(m2_arrowHeadBuffer, m2_arrowHeadBuffer);
break;
case 3:
pt = new Point(m2_arrowHeadBuffer, UberArrow.this.getSize().height - m2_arrowHeadBuffer);
break;
case 4:
pt = new Point(UberArrow.this.getSize().width - m2_arrowHeadBuffer,
UberArrow.this.getSize().height - m2_arrowHeadBuffer);
break;
default:
}
} else {
switch (getQuadrant()) {
case 1:
pt = new Point(m2_arrowHeadBuffer, UberArrow.this.getSize().height - m2_arrowHeadBuffer);
break;
case 2:
pt = new Point(UberArrow.this.getSize().width - m2_arrowHeadBuffer,
UberArrow.this.getSize().height - m2_arrowHeadBuffer);
break;
case 3:
pt = new Point(UberArrow.this.getSize().width - m2_arrowHeadBuffer, m2_arrowHeadBuffer);
break;
case 4:
pt = new Point(m2_arrowHeadBuffer, m2_arrowHeadBuffer);
break;
default:
}
}
//now pt is the point at the tip of the arrow (relative to JComponent!!)
//Note: On ArrowHeadSpec.vsd, x(i+1) = xs[i] here. Same for ys.
double cosA = Math.cos(m1_alpha);
int[] xs = new int[]{
pt.x,
addOrSubtract(!m2_finish, pt.x, (int)((m2_len/(Math.cos(m1_alpha)))*(Math.cos(m1_theta - m1_alpha)))),
addOrSubtract(!m2_finish, pt.x, (int)(m2_len * Math.cos(m1_theta))),
addOrSubtract(!m2_finish, pt.x, (int)((m2_len/(Math.cos(m1_alpha)))*(Math.cos(m1_theta + m1_alpha))))
};
int[] ys = new int[]{
pt.y,
addOrSubtract(!m2_finish, pt.y, (int)((m2_len/(Math.cos(m1_alpha)))*(Math.sin(m1_theta - m1_alpha)))),
addOrSubtract(!m2_finish, pt.y, (int)(m2_len * Math.sin(m1_theta))),
addOrSubtract(!m2_finish, pt.y, (int)((m2_len/(Math.cos(m1_alpha)))*(Math.sin(m1_theta + m1_alpha))))
};
//System.out.println("reassigning arrowhead");
m2_arrowHead = new Polygon(xs, ys, 4);
}
public void drawYourself(Graphics g) {
if (m2_filled) {
g.fillPolygon(m2_arrowHead);
} else {
g.drawPolygon(m2_arrowHead);
}
}
public boolean contains(Point p1) {
return m2_arrowHead.contains(p1);
}
public boolean contains(int x, int y) {
return m2_arrowHead.contains(x,y);
}
//
//private stuff
//
private int addOrSubtract(boolean add, int a, int b) {
return (add ? a + b : a - b);
}
private Polygon m2_arrowHead;
private double m2_len;
private boolean m2_filled, m2_finish;
private int m2_arrowHeadBuffer;
}//class UberArrowHead
}//class UberArrow
public interface UberPanelStates {
int DEFAULT = 0, ARROW_DRAW = 1, LABEL_DRAW = 2;
}
public interface ArrowheadDrawStates {
int NONE = 0, FINISH = 1, START = 2, BOTH = 3;
}
public interface LabelBorderDrawStates {
int NONE = 0, THIN = 1, THICK = 2;
}
/**
* The GlassPane of the UberPanel.
*/
class UberGlassPane extends JComponent {
//
//constructors
//
UberGlassPane() {
this(Color.blue);
}
UberGlassPane(Color color) {
enableEvents(AWTEvent.CONTAINER_EVENT_MASK |
AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK);
theMOB = new MovableObjectBorder(color, 7, 7, 5, Color.red, 8, 6);
}
//
//member variables
//
Toolkit toolkit = Toolkit.getDefaultToolkit();
Point offsetFromTopLeftCorner;
//The offsetFromTopLeftCorner is used to adjust the location of
//the component being MOVED (not resized). We compute this offset
//once and store it in this point, and then whenever we move the
//component we need to adjust it by this offset. This creates
//the effect of the cursor staying at the same spot on the
//component as it was when the user started dragging.
Rectangle initialSize = null;
MovableObjectBorder theMOB; //"I'm gonna make you a border you can't refuse."
private int currentCursor;
private int repaintExtra = 20; //the number of pixels around the component to repaint
//
//methods
//
public void paint(Graphics g) {
theMOB.paint(g);
}
/**
* Returns the type of cursor (guaranteed not to return null).
*
* @param p the point that the cursor is over
*/
public int getCursor(final Point p) {
if (theMOB != null) {
return theMOB.getCursor(p);
} else {
Cursor theCursor = super.getCursor();
if (theCursor == null) {
return Cursor.DEFAULT_CURSOR;
} else {
return theCursor.getType();
}
}
}
/**
* All I want to do is get from point a to point b :)
*
* @param a the point we're finding the distance from
* @param b the point whose distance from a we're finding
*/
private Point findOffset(Point a, Point b) {
return new Point((b.x - a.x), (b.y - a.y));
}
protected void processMouseEvent(MouseEvent e) {
if (e.isPopupTrigger()) {
System.out.println("popup from glass pane!");
//theToolBarPopupMenu.show(e.getComponent(), e.getXlen(), e.getYlen());
} else {
switch (e.getID()) {
case MouseEvent.MOUSE_CLICKED :
redispatchMouseEvent(e);
break;
case MouseEvent.MOUSE_ENTERED :
redispatchMouseEvent(e);
break;
case MouseEvent.MOUSE_EXITED :
redispatchMouseEvent(e);
break;
case MouseEvent.MOUSE_PRESSED :
if (mogp.getCursor() != null) {
switch (mogp.getCursor().getType()) {
case Cursor.MOVE_CURSOR :
Point topLeftCorner = selectedComponent.getLocation();
offsetFromTopLeftCorner = findOffset(topLeftCorner, e.getPoint());
break;
case Cursor.E_RESIZE_CURSOR :
case Cursor.W_RESIZE_CURSOR :
case Cursor.N_RESIZE_CURSOR :
case Cursor.S_RESIZE_CURSOR :
case Cursor.NE_RESIZE_CURSOR :
case Cursor.NW_RESIZE_CURSOR :
case Cursor.SE_RESIZE_CURSOR :
case Cursor.SW_RESIZE_CURSOR :
initialSize = selectedComponent.getBounds();
break;
case Cursor.CROSSHAIR_CURSOR :
dragStart = e.getPoint();
break;
default:
redispatchMouseEvent(e);
break;
}
}
break;
case MouseEvent.MOUSE_RELEASED :
repaint();
if (mogp.getCursor() != null) {
if (mogp.getCursor().getType() == Cursor.MOVE_CURSOR) {
moveSelectedComponent(e.getPoint());
//moveWithParent = false;//!PA - figure out how to set this in object
} else {
redispatchMouseEvent(e);
}
} else {
redispatchMouseEvent(e);
}
break;
default :
break;
}
}
}
protected void processMouseMotionEvent(MouseEvent e) {
switch (e.getID()) {
case MouseEvent.MOUSE_MOVED :
redispatchMouseEvent(e);
int newCursor = theMOB.getCursor(e.getPoint());
if (newCursor != currentCursor) {
mogp.setCursor(Cursor.getPredefinedCursor(newCursor));
currentCursor = newCursor;
}
break;
case MouseEvent.MOUSE_DRAGGED :
Rectangle r = theMOB.getBorderRectangleOnTheGlassPane();
repaint(r.x - repaintExtra,
r.y - repaintExtra,
r.width + repaintExtra * 2,
r.height + repaintExtra * 2);
if (mogp.getCursor().getType() == Cursor.MOVE_CURSOR) {
moveSelectedComponent(e.getPoint());
} else {
switch (mogp.getCursor().getType()) {
case Cursor.N_RESIZE_CURSOR :
resizeVertical(e.getPoint(), true);
break;
case Cursor.E_RESIZE_CURSOR :
resizeHorizontal(e.getPoint(), false);
break;
case Cursor.S_RESIZE_CURSOR :
resizeVertical(e.getPoint(), false);
break;
case Cursor.W_RESIZE_CURSOR :
resizeHorizontal(e.getPoint(), true);
break;
case Cursor.NE_RESIZE_CURSOR :
resizeVertical(e.getPoint(), true);
resizeHorizontal(e.getPoint(), false);
break;
case Cursor.NW_RESIZE_CURSOR :
resizeVertical(e.getPoint(), true);
resizeHorizontal(e.getPoint(), true);
break;
case Cursor.SE_RESIZE_CURSOR :
resizeVertical(e.getPoint(), false);
resizeHorizontal(e.getPoint(), false);
break;
case Cursor.SW_RESIZE_CURSOR :
resizeVertical(e.getPoint(), false);
resizeHorizontal(e.getPoint(), true);
break;
default:
redispatchMouseEvent(e);
return;
}
// moveWithParent = false;//!PA - replace with new version of this
validate();
}
break;
default :
break;
}
}
private boolean isPointInWindow(Point thePoint) {
return (thePoint.x <= UberPanel.this.getSize().width &&
thePoint.x > 0 &&
thePoint.y <= UberPanel.this.getSize().height &&
thePoint.y > 0);
}
private void moveSelectedComponent(Point currentPoint) {
Point topLeftCorner = new Point(currentPoint.x - offsetFromTopLeftCorner.x,
currentPoint.y - offsetFromTopLeftCorner.y);
int buttonLeftEdge = topLeftCorner.x;
int buttonRightEdge = topLeftCorner.x + selectedComponent.getBounds().width;
int buttonTopEdge = topLeftCorner.y;
int buttonBottomEdge = topLeftCorner.y + selectedComponent.getBounds().height;
if (buttonLeftEdge < 0) {
topLeftCorner.x = 0;
} else if (buttonRightEdge >= UberPanel.this.getSize().width) {
topLeftCorner.x = UberPanel.this.getSize().width - selectedComponent.getBounds().width;
}
if (buttonTopEdge < 0) {
topLeftCorner.y = 0;
} else if (buttonBottomEdge >= UberPanel.this.getSize().height) {
topLeftCorner.y = UberPanel.this.getSize().height - selectedComponent.getBounds().height;
}
selectedComponent.setLocation(topLeftCorner);
}
/**
* Transforms the cursor location to the closest location on the
* content pane.
*
* @param currentPoint the actual cursor location (point)
* @return the transformed point
*/
private Point mapToPointInWindow(Point currentPoint) {
Point retval = new Point();
if (currentPoint.x > UberPanel.this.getSize().width) {
retval.x = UberPanel.this.getSize().width;
} else if (currentPoint.x < 0) {
retval.x = 0;
} else {
retval.x = currentPoint.x;
}
if (currentPoint.y > UberPanel.this.getSize().height) {
retval.y = UberPanel.this.getSize().height;
} else if (currentPoint.y < 0) {
retval.y = 0;
} else {
retval.y = currentPoint.y;
}
return retval;
}
private void resizeSelectedComponentIfArrow() {
//Yeah, yeah, this is a bit of a hack but I can't figure out how to get the UberArrow
//to listen to other components calling setBounds() on it. If I could then I wouldn't
//have to do this...
if (selectedComponent instanceof UberArrow) {
int buf = ((UberArrow)selectedComponent).getArrowHeadBuffer();
int startx = 0;
int starty = 0;
int finishx = 0;
int finishy = 0;
switch (((UberArrow)selectedComponent).getQuadrant()) {
case 1:
System.out.println("q1");
startx = selectedComponent.getBounds().x + buf;
starty = selectedComponent.getBounds().y + selectedComponent.getBounds().height - 2*buf;
finishx = selectedComponent.getBounds().x + selectedComponent.getBounds().width - 2*buf;
finishy = selectedComponent.getBounds().y + buf;
break;
case 2:
System.out.println("q2");
startx = selectedComponent.getBounds().x + selectedComponent.getBounds().width - 2*buf;
starty = selectedComponent.getBounds().y + selectedComponent.getBounds().height - 2*buf;
finishx = selectedComponent.getBounds().x + buf;
finishy = selectedComponent.getBounds().y + buf;
break;
case 3:
System.out.println("q3");
startx = selectedComponent.getBounds().x + selectedComponent.getBounds().width - 2*buf;
starty = selectedComponent.getBounds().y + buf;
finishx = selectedComponent.getBounds().x + buf;
finishy = selectedComponent.getBounds().y + selectedComponent.getBounds().height - 2*buf;
break;
case 4:
System.out.println("q4");
startx = selectedComponent.getBounds().x + buf;
starty = selectedComponent.getBounds().y + buf;
finishx = selectedComponent.getBounds().x + selectedComponent.getBounds().width - 2*buf;
finishy = selectedComponent.getBounds().y + selectedComponent.getBounds().height - 2*buf;
break;
default:
break;
}
((UberArrow)selectedComponent).updateStart(new Point(startx, starty));
((UberArrow)selectedComponent).updateFinish(new Point(finishx, finishy));
((UberArrow)selectedComponent).updateThetaAndArrowHeads();
}
}
/**
* Resize either north or south. If and when Java gets a parameterized
* type mechanism this method should be combined with resizeHorizontal().
*/
private void resizeVertical(Point currentPoint, boolean isNorthHandle) {
Point currentWindowPoint = mapToPointInWindow(currentPoint);
Point initialWindowPoint =
SwingUtilities.convertPoint(
UberPanel.this,
new Point(initialSize.x, initialSize.y),
mogp);
Rectangle b = selectedComponent.getBounds();
int newHeight;
if (isNorthHandle) {
newHeight = initialSize.height + initialWindowPoint.y - currentWindowPoint.y;
} else {
newHeight = currentWindowPoint.y - initialWindowPoint.y;
}
if (newHeight >= selectedComponent.getMinimumSize().height) {
if (isNorthHandle) {
b.y = SwingUtilities.convertPoint(mogp, currentWindowPoint, UberPanel.this).y;
}
b.height = newHeight;
} else {
if (isNorthHandle) {
b.y = SwingUtilities.convertPoint(
mogp,
new Point(initialWindowPoint.x,
initialWindowPoint.y +
initialSize.height -
selectedComponent.getMinimumSize().height),
UberPanel.this).y;
}
b.height = selectedComponent.getMinimumSize().height;
}
selectedComponent.setBounds(b);
resizeSelectedComponentIfArrow();
}
/**
* Resize either east or west. If and when Java gets a parameterized
* type mechanism this method should be combined with resizeVertical().
*/
private void resizeHorizontal(Point currentPoint, boolean isWestHandle) {
Point currentWindowPoint = mapToPointInWindow(currentPoint);
Point initialWindowPoint =
SwingUtilities.convertPoint(
UberPanel.this,
new Point(initialSize.x, initialSize.y),
mogp);
Rectangle b = selectedComponent.getBounds();
int newWidth;
if (isWestHandle) {
newWidth = initialSize.width + initialWindowPoint.x - currentWindowPoint.x;
} else {
newWidth = currentWindowPoint.x - initialWindowPoint.x;
}
if (newWidth >= selectedComponent.getMinimumSize().width) {
if (isWestHandle) {
b.x = SwingUtilities.convertPoint(mogp, currentWindowPoint, UberPanel.this).x;
}
b.width = newWidth;
} else {
if (isWestHandle) {
b.x = SwingUtilities.convertPoint(
mogp,
new Point(initialWindowPoint.x +
initialSize.width -
selectedComponent.getMinimumSize().width,
initialWindowPoint.y),
UberPanel.this).x;
}
b.width = selectedComponent.getMinimumSize().width;
}
selectedComponent.setBounds(b);
resizeSelectedComponentIfArrow();
}
private void redispatchMouseEvent(MouseEvent e) {
Point glassPanePoint = e.getPoint();
Component component = null;
Container container = UberPanel.this;
Point containerPoint = SwingUtilities.convertPoint(mogp, glassPanePoint, UberPanel.this);
if (containerPoint.y < 0) {
container = getRootPane().getJMenuBar();
containerPoint = SwingUtilities.convertPoint(mogp, glassPanePoint, getRootPane().getJMenuBar());
}
//XXX: If the event is from a component in a popped-up menu,
//XXX: then the container should probably be the menu's
//XXX: JPopupMenu, and containerPoint should be adjusted
//XXX: accordingly.
if ((container != null) && ((containerPoint.y >= 0) || (getRootPane().getJMenuBar() != null))) {
component = SwingUtilities.getDeepestComponentAt(
container, containerPoint.x, containerPoint.y);
}
if (component == null) return;
Point componentPoint = SwingUtilities.convertPoint(mogp, glassPanePoint, component);
component.dispatchEvent(new MouseEvent( component, e.getID(), e.getWhen(), e.getModifiers(),
componentPoint.x, componentPoint.y,
e.getClickCount(), e.isPopupTrigger()));
return;
}//redispatchMouseEvent()
//
//inner classes of UberGlassPane
//
/**
* A MovableObjectBorder is a border but not a Border.
*/
class MovableObjectBorder
{
//
//constructors
//
MovableObjectBorder(
Color lineColor, int lineWidth, int lineHeight,
int insetsFromRealBorder, Color handleColor,
int cornerHandleSideLength, int sideHandleSideLength)
{
this.lineColor = lineColor;
this.lineWidth = lineWidth;
this.lineHeight = lineHeight;
this.insetsFromRealBorder = insetsFromRealBorder;
this.handleColor = handleColor;
this.cornerHandleSideLength = cornerHandleSideLength;
this.sideHandleSideLength = sideHandleSideLength;
for (int i=0; i= r.x - extra) &&
(p.x <= r.x + r.width + extra) &&
(p.y >= r.y - extra) &&
(p.y <= r.y + r.height + extra) )
{
//we are within the border area so we have a chance
if ( (p.x <= r.x + lineWidth) || //left edge
(p.x >= r.x + r.width - lineWidth) || //right edge
(p.y <= r.y + lineHeight) || //top edge
(p.y >= r.y + r.height - lineHeight) //bottom edge
)
{
return true;
} else {
return false;
}
} else {
return false;
}
}
//
//inner classes
//
final private class Handle extends Rectangle
{
Handle(int size)
{
setSize(size-1,size-1);
thick2 = size/2;
}
void set(int type, int x, int y)
{
setLocation(x - thick2, y - thick2);
this.type = type;
}
int getCursor()
{
return type;
}
private int thick2;
private int type;
}
}//MovableObjectBorder
}//UberGlassPane
}//UberPanel
Ads help maintain this website.