/*
Copyright (c) 2003-2008 ITerative Consulting Pty Ltd. All Rights Reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package DisplayProject.controls;
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Stroke;
import javax.swing.JComponent;
import DisplayProject.Constants;
import DisplayProject.UIutils;
import DisplayProject.actions.ActionMgr;
import DisplayProject.actions.ColourChange;
import DisplayProject.actions.FloatOverText;
import DisplayProject.actions.LineConfigure;
import DisplayProject.actions.MouseCursor;
import DisplayProject.actions.Parent;
import DisplayProject.actions.WidgetState;
import DisplayProject.factory.GraphicFactory;
import Framework.CloneHelper;
import Framework.TextData;
/**
* The Line class defines a single-segment line.
*/
public class Line extends Route {
/**
*
*/
private static final long serialVersionUID = 316467462652843968L;
/**
* End Y location of the line, in mils
*/
private int endY = 0;
/**
* End X location of the line, in mils
*/
private int endX = 0;
/**
* Start Y location of the line, in mils
*/
private int startY = 0;
/**
* Start X location of the line, in mils
*/
private int startX = 0;
/**
* End Y location of the line, in pixels
*/
private int endYPixels = 0;
/**
* End X location of the line, in pixels
*/
private int endXPixels = 0;
/**
* Start Y location of the line, in pixels
*/
private int startYPixels = 0;
/**
* Start X location of the line, in pixels
*/
private int startXPixels = 0;
/** The smaller of endXPixels and startXPixels */
private int minX;
/** The smaller of endYPixels and startYPixels */
private int minY;
/** The line weight in pixels */
private int lineWeightPixels;
/** The stroke used to draw this line */
private Stroke stroke;
private int maxHeightInPixels;
private int maxWidthInPixels;
public static class qq_Resolver {
public static final int cAPPDATA_ENDX_ENDY_LINESTYLE_LINEWEIGHT_MOUSECURSOR_PENCOLOR_STARTX_STARTY_STATE = 1;
public static final int cAPPDATA_ENDX_ENDY_LINESTYLE_MOUSECURSOR_PENCOLOR_STARTX_STARTY_STATE = 2;
public static final int cAPPDATA_ENDX_FLOATOVERTEXT_LINEWEIGHT_MOUSECURSOR_PENCOLOR_STARTX_STATE_Y = 3;
public static final int cAPPDATA_ENDX_LINEWEIGHT_MOUSECURSOR_PENCOLOR_STARTX_STATE_Y = 4;
public static final int cAPPDATA_ENDX_LINESTYLE_MOUSECURSOR_PENCOLOR_STARTX_STATE_Y = 5;
public static final int cENDX_ENDY_LINEWEIGHT_PARENT_STARTX_STARTY = 6;
public static final int cFILLCOLOR_PENCOLOR = 7;
public static final int cNAME = 8;
}
public Line( Object appData, int endX, int endY, int lineStyle, int lineWeight, int mouseCursor, int penColor, int startX, int startY, int state, int resolver) {
this();
if (resolver == qq_Resolver.cAPPDATA_ENDX_ENDY_LINESTYLE_LINEWEIGHT_MOUSECURSOR_PENCOLOR_STARTX_STARTY_STATE) {
this.setAppData(appData);
this.setLineStyle(lineStyle);
this.setLineWeight(lineWeight);
MouseCursor.set(this, mouseCursor);
ColourChange.setForeground(this, penColor);
WidgetState.set(this, state);
this.setPoints(startX, startY, endX, endY);
}
}
public Line( Object appData, int endX, int endY, int lineStyle, int mouseCursor, int penColor, int startX, int startY, int state, int resolver) {
this();
if (resolver == qq_Resolver.cAPPDATA_ENDX_ENDY_LINESTYLE_MOUSECURSOR_PENCOLOR_STARTX_STARTY_STATE) {
this.setAppData(appData);
this.setLineStyle(lineStyle);
MouseCursor.set(this, mouseCursor);
ColourChange.setForeground(this, penColor);
WidgetState.set(this, state);
this.setPoints(startX, startY, endX, endY);
}
}
public Line( Object appData, int endX, String floatOverText, int lineWeight, int mouseCursor, int penColor, int startX, int state, int y, int resolver) {
this();
if (resolver == qq_Resolver.cAPPDATA_ENDX_FLOATOVERTEXT_LINEWEIGHT_MOUSECURSOR_PENCOLOR_STARTX_STATE_Y) {
this.setAppData(appData);
FloatOverText.set(this, floatOverText);
this.setLineWeight(lineWeight);
MouseCursor.set(this, mouseCursor);
ColourChange.setForeground(this, penColor);
WidgetState.set(this, state);
this.setPoints(startX, y, endX, y);
}
}
public Line( Object appData, int endX, TextData floatOverText, int lineWeight, int mouseCursor, int penColor, int startX, int state, int y, int resolver) {
this(appData, endX, TextData.valueOf(floatOverText), lineWeight, mouseCursor, penColor, startX, startX, y, resolver);
}
public Line( int endX, int endY, int lineWeight, JComponent parent, int startX, int startY, int resolver) {
this();
if (resolver == qq_Resolver.cENDX_ENDY_LINEWEIGHT_PARENT_STARTX_STARTY) {
this.setLineWeight(lineWeight);
this.setPoints(startX, startY, endX, endY);
Parent.set(this, parent);
}
}
public Line( Object appData, int endX, int lineWeight, int mouseCursor, int penColor, int startX, int state, int y, int resolver) {
this();
if (resolver == qq_Resolver.cAPPDATA_ENDX_LINEWEIGHT_MOUSECURSOR_PENCOLOR_STARTX_STATE_Y) {
this.setAppData(appData);
this.setLineWeight(lineWeight);
MouseCursor.set(this, mouseCursor);
ColourChange.setForeground(this, penColor);
WidgetState.set(this, state);
this.setPoints(startX, y, endX, y);
}
else if (resolver == qq_Resolver.cAPPDATA_ENDX_LINESTYLE_MOUSECURSOR_PENCOLOR_STARTX_STATE_Y) {
this.setAppData(appData);
this.setLineWeight(lineWeight);
MouseCursor.set(this, mouseCursor);
ColourChange.setForeground(this, penColor);
WidgetState.set(this, state);
this.setPoints(startX, y, endX, y);
}
}
public Line( int color1, int color2, int resolver )
{
this();
switch (resolver){
case qq_Resolver.cFILLCOLOR_PENCOLOR:
this.setForeground(UIutils.forteToJavaColor(color2));
this.setBackground(UIutils.forteToJavaColor(color1));
break;
}
}
public Line( TextData name, int resolver )
{
this();
switch (resolver){
case qq_Resolver.cNAME:
this.setName(name.toString());
break;
}
}
/**
* Return the minimum X coordinate of the line, in mils
* @return
*/
public int getMinX() {
return Math.min(this.startX, this.endX);
}
/**
* Set the minimum X location of the line in mils
* <br>
* In forte, X, XPixel, XAbsolute, StartX and EndX all work in conjunction with one another,
* so we need to be able to use this method to compensate for when X is being set.
* @param pValue
*/
public void setMinX(int pValue) {
int min = Math.min(this.startX, this.endX);
this.setPoints(this.startX-min+pValue, this.startY, this.endX-min+pValue, this.endY);
}
/**
* Return the minimum Y coordinate of the line, in mils
* @return
*/
public int getMinY() {
return Math.min(this.startY, this.endY);
}
/**
* Set the minimum Y location of the line in mils
* <br>
* In forte, Y, YPixel, YAbsolute, StartY and EndY all work in conjunction with one another,
* so we need to be able to use this method to compensate for when X is being set.
* @param pValue
*/
public void setMinY(int pValue) {
int min = Math.min(this.startY, this.endY);
this.setPoints(this.startX, this.startY-min+pValue, this.endX, this.endY-min+pValue);
}
/**
* Return the X coordinate of one endpoint of the line in mils
* @return
*/
public int getEndX() {
return endX;
}
public void setEndX(int endX) {
this.setPoints(this.startX, this.startY, endX, this.endY);
}
/**
* Return the Y coordinate of one endpoint of the line in mils
* @return
*/
public int getEndY() {
return endY;
}
public void setEndY(int endY) {
this.setPoints(this.startX, this.startY, this.endX, endY);
}
/**
* Return the X coordinate of one endpoint of the line in mils
* @return
*/
public int getStartX() {
return startX;
}
public void setStartX(int startX) {
this.setPoints(startX, this.startY, this.endX, this.endY);
}
/**
* Return the Y coordinate of one endpoint of the line in mils
* @return
*/
public int getStartY() {
return startY;
}
public void setStartY(int startY) {
this.setPoints(this.startX, startY, this.endX, this.endY);
}
public Line(int startX, int startY, int endX, int endY) {
this();
setPoints(startX, startY, endX, endY);
updateUI();
}
public Line() {
super();
setFocusable(false);
this.setLineWeight(Constants.W_DEFAULT);
// TF:15/12/2009:DET-141:Set this up to allow inheriting of popup menu
this.setInheritsPopupMenu(true);
}
public void setPoints(int startX, int startY, int endX, int endY) {
this.endY = endY;
this.endX = endX;
this.startY = startY;
this.startX = startX;
ActionMgr.addAction(new LineConfigure(this));
startXPixels = UIutils.milsToPixels(this.startX);
startYPixels = UIutils.milsToPixels(this.startY);
endXPixels = UIutils.milsToPixels(this.endX);
endYPixels = UIutils.milsToPixels(this.endY);
minX = Math.min(startXPixels, endXPixels);
minY = Math.min(startYPixels, endYPixels);
maxWidthInPixels = Math.abs(startXPixels - endXPixels) + lineWeightPixels + 1;
maxHeightInPixels = Math.abs(startYPixels - endYPixels) + lineWeightPixels + 1;
}
@Override
public void setLineStyle(int lineStyle) {
super.setLineStyle(lineStyle);
// Refresh the stroke cache
this.refreshStroke();
}
@Override
public void setLineWeight(int lineWeight) {
super.setLineWeight(lineWeight);
lineWeightPixels = UIutils.getLineWeightInPixels(this.lineWeight);
this.refreshStroke();
}
private void refreshStroke() {
// TF:15/02/2010:Forte would round out the ends of wide lines, so we want to also. However, if the line width is 1 or 0
// we don't care about this, so use the simpler and presumably faster CAP_BUTT style
this.stroke = UIutils.makeStroke(this.lineWeight, this.lineStyle, lineWeightPixels > 1 ? BasicStroke.CAP_ROUND : BasicStroke.CAP_BUTT);
}
/*
* CraigM:10/12/2008 - Corrected line calculations.
* @see javax.swing.JComponent#paint(java.awt.Graphics)
*/
public void paint(Graphics g) {
Insets insets = this.getInsets();
g.setColor( this.getForeground() );
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(stroke);
// Lines are drawn from the centre of their widths
// TF:15/02/2010:Optimised this code.
int halfLineWeight = lineWeightPixels/2;
int xOffset = insets.left + halfLineWeight - minX;
int yOffset = insets.top + halfLineWeight - minY;
if (g.hitClip(xOffset + minX, yOffset + minY, maxWidthInPixels, maxHeightInPixels)) {
g.drawLine(xOffset + startXPixels,
yOffset + startYPixels,
xOffset + endXPixels,
yOffset + endYPixels);
}
// CraigM:21/02/2009 - Draw the arrows if they exist
if (this.getBeginArrow() != null) {
this.getBeginArrow().paint(g, this);
}
if (this.getEndArrow() != null) {
this.getEndArrow().paint(g, this);
}
}
public boolean contains(int x, int y) {
/*
gradient = y2-y1 / x2- x1
y0-y1 = m(x0-x1)
*/
Point loc = getLocation();
int sx = startXPixels - loc.x;
int ex = endXPixels - loc.x;
int sy = startYPixels - loc.y;
int ey = endYPixels - loc.y;
int wdt = UIutils.getLineWeightInPixels(this.lineWeight);
boolean result;
if (sx == ex){
result = (sx-wdt < x)&&(x<sx+wdt)&&(Math.min(sy, ey)-wdt < y)&&(y < Math.max(sy, ey)+wdt);
}
else {
// gradient = (y1 - y0) / (x1 - x0), but this needs to be reversed for screen coordinates
if (x >= Math.min(sx, ex) - CLICK_COEFICENT - wdt && x <= Math.max(sx, ex) + CLICK_COEFICENT + wdt) {
double m = ((double)(ey - sy)) / (sx - ex);
result = (Math.abs((ey-y) - m * (x-ex)) < CLICK_COEFICENT+wdt);
}
else {
// Outside of the range of the line, result in false
return false;
}
}
return result;
}
public Line cloneComponent(){
Line clone = GraphicFactory.newLine(this.startX, this.startY, this.endX, endY);
CloneHelper.cloneComponent(this, clone, new String[]{"parent"});
return clone;
}
}