// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/omGraphics/EditableOMPoly.java,v $
// $RCSfile: EditableOMPoly.java,v $
// $Revision: 1.9.2.7 $
// $Date: 2008/03/12 17:33:16 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.omGraphics;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import com.bbn.openmap.LatLonPoint;
import com.bbn.openmap.gui.GridBagToolBar;
import com.bbn.openmap.layer.util.stateMachine.State;
import com.bbn.openmap.omGraphics.editable.GraphicEditState;
import com.bbn.openmap.omGraphics.editable.GraphicSelectedState;
import com.bbn.openmap.omGraphics.editable.PolyAddNodeState;
import com.bbn.openmap.omGraphics.editable.PolyDeleteNodeState;
import com.bbn.openmap.omGraphics.editable.PolyStateMachine;
import com.bbn.openmap.omGraphics.editable.PolyUndefinedState;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;
/**
* The EditableOMPoly encompasses an OMPoly, providing methods for modifying or
* creating it.
*/
public class EditableOMPoly extends EditableOMAbstractLine {
protected ArrayList polyGrabPoints;
protected OffsetGrabPoint gpo; // offset
protected OffsetGrabPoint gpm; // for grabbing the poly and
// moving
// it.
protected OMPoly poly;
/**
* Whether the poly is a polygon, as opposed to a polyline. If the poly
* color is not clear (OMColor.clear) then it will be a polygon. If it is
* clear, then it can be set as a polygon - it's otherwise assumed to be a
* polyline.
*/
protected boolean manualEnclosed = false;
// We'll have to handle this differently...
public static int OFFSET_POINT_INDEX = -1;
/**
* Create the EditableOMPoly, setting the state machine to create the poly
* off of the gestures.
*/
public EditableOMPoly() {
createGraphic(null);
}
/**
* Create an EditableOMPoly with the polyType and renderType parameters in
* the GraphicAttributes object.
*/
public EditableOMPoly(GraphicAttributes ga) {
createGraphic(ga);
}
/**
* Create the EditableOMPoly with an OMPoly already defined, ready for
* editing.
*
* @param omp OMPoly that should be edited.
*/
public EditableOMPoly(OMPoly omp) {
setGraphic(omp);
}
/**
* Create and initialize the state machine that interprets the
*
* modifying gestures/commands, as well as ititialize the grab points. Also
* allocates the grab point array needed by the EditableOMPoly.
*/
public void init() {
Debug.message("eomg", "EditableOMPoly.init()");
setStateMachine(new PolyStateMachine(this));
gPoints = new GrabPoint[1];
}
/**
* Set the graphic within the state machine. If the graphic is null, then
* one shall be created, and located off screen until the gestures driving
* the state machine place it on the map.
*/
public void setGraphic(OMGraphic graphic) {
init();
if (graphic instanceof OMPoly) {
poly = (OMPoly) graphic;
poly.setDoShapes(true);
stateMachine.setSelected();
setGrabPoints(poly);
} else {
createGraphic(null);
}
}
/**
* Method checks if the polygon should be enclosed, and then adds an
* addition point to the end of the polygon, setting the end point on top of
* the beginning point. The two points are OffsetGrabPoints that are tied to
* each other's position.
*/
public boolean evaluateEnclosed() {
deletePoint();
boolean enclosed = false;
if (isEnclosed()) {
enclose(true);
enclosed = true;
}
return enclosed;
}
/**
* Method connects the last point to the first point. Make sure they are
* both OffsetGrabPoints. Return true if the points cover the same pixel and
* if they were successfully joined.
*/
protected boolean syncEnclosed() {
try {
OffsetGrabPoint gb0 = (OffsetGrabPoint) polyGrabPoints.get(0);
OffsetGrabPoint ogb = (OffsetGrabPoint) polyGrabPoints.get(polyGrabPoints.size() - 1);
// Check to see if they are over the same point.
if (gb0.getX() == ogb.getX() && gb0.getY() == ogb.getY()) {
// Cross connect them...
gb0.addGrabPoint(ogb);
ogb.addGrabPoint(gb0);
return true;
}
} catch (ClassCastException cce) {
} catch (IndexOutOfBoundsException ioobe) {
}
return false;
}
/**
* Method disconnects the last point from the first point. Make sure they
* are both OffsetGrabPoints.
*/
protected boolean unsyncEnclosed() {
try {
OffsetGrabPoint gb0 = (OffsetGrabPoint) polyGrabPoints.get(0);
OffsetGrabPoint ogb = (OffsetGrabPoint) polyGrabPoints.get(polyGrabPoints.size() - 1);
// disconnect them...
if (gb0.getX() == ogb.getX() && gb0.getY() == ogb.getY()) {
gb0.removeGrabPoint(ogb);
ogb.removeGrabPoint(gb0);
return true;
}
} catch (ClassCastException cce) {
} catch (ArrayIndexOutOfBoundsException aioobe) {
}
return false;
}
public void enclose(boolean e) {
setEnclosed(e);
if (polyGrabPoints == null) {
return;
}
OffsetGrabPoint gb0 = (OffsetGrabPoint) polyGrabPoints.get(0);
OffsetGrabPoint ogb;
if (e) {
// If they should be enclosed...
if (!syncEnclosed()) {
// And they are not already, then add a point, joined
// to the beginning.
ogb = new OffsetGrabPoint(gb0.getX(), gb0.getY());
// Add the new point to end of the poly
addPoint(ogb);
syncEnclosed();
repaint();
} // Else nothing to do...
} else {
// They shouldn't be hooked up, so check to see if they
// are, and disconnect if necessary.
if (unsyncEnclosed()) {
deletePoint(); // Delete attached duplicate point
repaint();
} // else nothing to do.
}
}
/**
* Set the flag to make the polygon enclosed, which automatically connects
* the last point with the first point.
*/
public void setEnclosed(boolean set) {
manualEnclosed = set;
}
/**
* Returns whether the graphic will be a polygon, instead of a polyline.
*/
public boolean isEnclosed() {
return manualEnclosed;
}
/**
* Create and set the graphic within the state machine. The
* GraphicAttributes describe the type of poly to create.
*/
public void createGraphic(GraphicAttributes ga) {
init();
stateMachine.setUndefined();
int renderType = OMGraphic.RENDERTYPE_LATLON;
int lineType = OMGraphic.LINETYPE_GREATCIRCLE;
if (ga != null) {
renderType = ga.getRenderType();
lineType = ga.getLineType();
}
if (Debug.debugging("eomg")) {
Debug.output("EditableOMPoly.createGraphic(): rendertype = "
+ renderType);
}
if (lineType == OMGraphic.LINETYPE_UNKNOWN) {
lineType = OMGraphic.LINETYPE_GREATCIRCLE;
ga.setLineType(OMGraphic.LINETYPE_GREATCIRCLE);
}
this.poly = (OMPoly) createGraphic(renderType, lineType);
if (ga != null) {
ga.setRenderType(poly.getRenderType());
ga.setTo(poly, true);
}
}
/**
* Extendable method to create specific subclasses of OMPolys.
*/
public OMGraphic createGraphic(int renderType, int lineType) {
OMGraphic g = null;
switch (renderType) {
case (OMGraphic.RENDERTYPE_LATLON):
g = new OMPoly(new float[0], OMGraphic.RADIANS, lineType);
break;
case (OMGraphic.RENDERTYPE_OFFSET):
g = new OMPoly(90f, -180f, new int[0], OMPoly.COORDMODE_ORIGIN);
break;
default:
g = new OMPoly(new int[0]);
}
((OMPoly) g).setDoShapes(true);
return g;
}
/**
* Get the OMGraphic being created/modified by the EditableOMPoly.
*/
public OMGraphic getGraphic() {
return poly;
}
/**
* Attach to the Moving OffsetGrabPoint so if it moves, it will move this
* EditableOMGraphic with it. EditableOMGraphic version doesn't do anything,
* each subclass has to decide which of its OffsetGrabPoints should be
* attached to it.
*/
public void attachToMovingGrabPoint(OffsetGrabPoint gp) {
gp.addGrabPoint(gpo);
}
/**
* Detach from a Moving OffsetGrabPoint. The EditableOMGraphic version
* doesn't do anything, each subclass should remove whatever GrabPoint it
* would have attached to an OffsetGrabPoint.
*/
public void detachFromMovingGrabPoint(OffsetGrabPoint gp) {
gp.removeGrabPoint(gpo);
}
/**
* Set the GrabPoint that is in the middle of being modified, as a result of
* a mouseDragged event, or other selection process.
*/
public void setMovingPoint(GrabPoint gp) {
super.setMovingPoint(gp);
gpm = null;
}
/**
* Given a MouseEvent, find a GrabPoint that it is touching, and set the
* moving point to that GrabPoint. Called when a MouseEvent happens, and you
* want to find out if a GrabPoint should be used to make modifications to
* the graphic or its position.
*
* @param e MouseEvent
* @return GrabPoint that is touched by the MouseEvent, null if none are.
*/
public GrabPoint getMovingPoint(MouseEvent e) {
GrabPoint gb = super.getMovingPoint(e);
// Since there may be an extra point enclosing the polygon, we
// want to make sure that the start of the polygon is
// returned, instead of the duplicate ending point.
int lastPointIndex = polyGrabPoints.size() - 1;
if (gb != null && gb == (GrabPoint) polyGrabPoints.get(lastPointIndex)
&& isEnclosed()) {
gb = (GrabPoint) polyGrabPoints.get(0);
setMovingPoint(gb);
}
return gb;
}
/**
* Check to make sure the grab points are not null. If they are, allocate
* them, and them assign them to the array.
*/
public void assertGrabPoints() {
// This gets called alot. Usually, for EditableOMGraphics
// that have the same number of GrabPoints, they can just be
// allocated here. I think we'll have to look at the OMPoly,
// find out how many points have been defined for it (since
// it's variable), and make sure everything's OK.
if (polyGrabPoints == null) {
polyGrabPoints = new ArrayList();
}
// At least we know about this one.
if (gpo == null) {
gpo = new OffsetGrabPoint(-1, -1);
}
}
/**
* An internal method that trys to make sure that the grab point for the
* first node, and for the last, in case of an enclosed polygon, are
* OffsetGrabPoints. All of the other points will be regular GrabPoints.
* Usually called when assigning points to a previously defined poly.
*
* @param x the horizontal pixel location of the grab point.
* @param y the vertical pixel location of the grab point.
* @param index the index of the grab point.
* @param last the index of the last point.
*/
protected GrabPoint createGrabPoint(int x, int y, int index, int last) {
if (index == 0 || (index == last && (isEnclosed()))) {
return new OffsetGrabPoint(x, y);
} else {
return new GrabPoint(x, y);
}
}
/**
* Set the grab points for the graphic provided, setting them on the extents
* of the graphic. Called when you want to set the grab points off the
* points of the graphic.
*/
public void setGrabPoints(OMGraphic graphic) {
if (!(graphic instanceof OMPoly)) {
return;
}
assertGrabPoints();
polyGrabPoints.clear();
arrayCleared = true;
gpo.clear();
OMPoly poly = (OMPoly) graphic;
boolean ntr = poly.getNeedToRegenerate();
int renderType = poly.getRenderType();
Point p = new Point();
GrabPoint gb;
int i;
int npts;
if (ntr == false) {
if (renderType == OMGraphic.RENDERTYPE_LATLON) {
Debug.message("eomg", "EditableOMPoly: modifying lat/lon line");
if (projection != null) {
float[] ll = poly.getLatLonArray();
gb = null; // reset for this loop
for (i = 0; i < ll.length; i += 2) {
projection.forward(ll[i], ll[i + 1], p, true);
// Need to add a grab point for this
// coordinate
gb = new OffsetGrabPoint((int) p.getX(), (int) p.getY());
polyGrabPoints.add(gb);
}
}
} else if (renderType == OMGraphic.RENDERTYPE_OFFSET) {
// Grab the projected endpoints
Debug.message("eomg", "EditableOMPoly: modifying offset poly");
int x;
int y;
npts = poly.xs.length;
// Check to see if the poly is a offset poly, and set
// the
// offset grab point accordingly.
if (projection != null) {
projection.forward(poly.lat, poly.lon, p, true);
gpo.set((int) p.getX(), (int) p.getY());
if (poly.coordMode == OMPoly.COORDMODE_ORIGIN) {
for (i = 0; i < npts; i++) {
x = poly.xs[i] + p.x;
y = poly.ys[i] + p.y;
gb = new OffsetGrabPoint(x, y);
polyGrabPoints.add(gb);
}
} else { // CMode Previous offset deltas
int lastX = p.x;
int lastY = p.y;
for (i = 0; i < npts; i++) {
x = poly.xs[i] + lastX;
y = poly.ys[i] + lastY;
gb = new OffsetGrabPoint(x, y);
polyGrabPoints.add(gb);
lastX += x;
lastY += y;
}
}
}
} else {
npts = poly.xs.length;
Debug.message("eomg", "EditableOMPoly: modifying x/y poly");
for (i = 0; i < npts; i++) {
gb = new OffsetGrabPoint(poly.xs[i], poly.ys[i]);
polyGrabPoints.add(gb);
}
}
// Add the || to maintain manualEnclosed if it was
// externally set before the OMPoly is actually defined,
// indicating that the user wants to draw a polygon.
setEnclosed(syncEnclosed() || isEnclosed());
addPolyGrabPointsToOGP(gpo);
} else {
Debug.message("eomg",
"EditableOMPoly.setGrabPoints: graphic needs to be regenerated ");
}
}
/**
* Take the current location of the GrabPoints, and modify the location
* parameters of the OMPoly with them. Called when you want the graphic to
* change according to the grab points.
*/
public void setGrabPoints() {
int i;
GrabPoint gb; // just to use a temp marker
LatLonPoint llp = new LatLonPoint();
int renderType = poly.getRenderType();
if (renderType == OMGraphic.RENDERTYPE_LATLON) {
if (projection != null) {
float[] floats = new float[polyGrabPoints.size() * 2];
for (i = 0; i < polyGrabPoints.size(); i++) {
gb = (GrabPoint) polyGrabPoints.get(i);
projection.inverse(gb.getX(), gb.getY(), llp);
floats[2 * i] = llp.radlat_;
floats[2 * i + 1] = llp.radlon_;
}
poly.setLocation((float[]) floats, OMGraphic.RADIANS);
} else {
Debug.message("eomg",
"EditableOMPoly.setGrabPoints: projection is null, can't figure out LATLON points for poly.");
}
} else if (renderType == OMGraphic.RENDERTYPE_OFFSET) {
// Do the offset point.
if (projection != null) {
projection.inverse(gpo.getX(), gpo.getY(), llp);
} else {
Debug.message("eomg",
"EditableOMPoly.setGrabPoints: projection is null, can't figure out LATLON points for poly offset.");
}
}
if (renderType == OMGraphic.RENDERTYPE_XY
|| renderType == OMGraphic.RENDERTYPE_OFFSET) {
int[] ints = new int[polyGrabPoints.size() * 2];
if (renderType == OMGraphic.RENDERTYPE_OFFSET && gpo != null) {
// If offset rendertype, the x-y have to be offset
// distances, not screen pixel locations. For the
// polygon, you also need to take into account that
// the ints can represent 2 different things: distance
// from the origin (Offset) or distance from the
// previous point. Need to check with the poly to
// find out which to do.
GrabPoint previous = gpo;
for (i = 0; i < polyGrabPoints.size(); i++) {
gb = (GrabPoint) polyGrabPoints.get(i);
if (poly.coordMode == OMPoly.COORDMODE_PREVIOUS) {
ints[2 * i] = gb.getX() - previous.getX();
ints[2 * i + 1] = gb.getY() - previous.getY();
previous = gb;
} else {
ints[2 * i] = gb.getX() - gpo.getX();
ints[2 * i + 1] = gb.getY() - gpo.getY();
}
}
poly.setLocation(llp.radlat_,
llp.radlon_,
OMGraphic.RADIANS,
ints);
} else {
for (i = 0; i < polyGrabPoints.size(); i++) {
gb = (GrabPoint) polyGrabPoints.get(i);
ints[2 * i] = gb.getX();
ints[2 * i + 1] = gb.getY();
}
poly.setLocation(ints);
}
}
}
/**
* Add a point to the end of the polyline/polygon and then make it the
* moving one.
*
* @return the index for the point in the polygon, starting with 0.
*/
public int addMovingPoint(int x, int y) {
int position = addPoint(x, y);
setMovingPoint((GrabPoint) polyGrabPoints.get(position));
return position;
}
/**
* Add a point to the end of the polyline/polygon.
*
* @return the index for the point in the polygon, starting with 0.
*/
public int addPoint(int x, int y) {
return addPoint(x, y, Integer.MAX_VALUE);
}
/**
* Add a point at a certain point in the polygon coordinate list. If the
* position is less than zero, the point will be the starting point. If the
* position is greater than the list of current points, the point will be
* added to the end of the poly.
*
* @return the index for the point in the polygon, starting with 0.
*/
public int addPoint(int x, int y, int position) {
return addPoint(new OffsetGrabPoint(x, y), position);
}
/**
* Add a point at a certain point in the polygon coordinate list. If the
* position is less than zero, the point will be the starting point. If the
* position is greater than the list of current points, the point will be
* added to the end of the poly. This method is convenient because it lets
* you define the GrabPoint object to use for the node, in case you need a
* special type of GrabPoint.
*
* @param gp the GrabPoint set to the screen coordinates of the point to be
* added.
* @return the index for the point in the polygon, starting with 0.
*/
public int addPoint(GrabPoint gp) {
return addPoint(gp, Integer.MAX_VALUE);
}
/**
* Add a point at a certain point in the polygon coordinate list. If the
* position is less than zero, the point will be the starting point. If the
* position is greater than the list of current points, the point will be
* added to the end of the poly. This method is convenient because it lets
* you define the GrabPoint object to use for the node, in case you need a
* special type of GrabPoint.
*
* @return the index for the point in the polygon, starting with 0.
*/
public int addPoint(GrabPoint gp, int position) {
if (gp == null) {
return -1;
}
int x = gp.getX();
int y = gp.getY();
int renderType = poly.getRenderType();
if (renderType == OMGraphic.RENDERTYPE_LATLON) {
Debug.message("eomg",
"EditableOMPoly: adding point to lat/lon poly");
if (projection != null) {
float[] ll = poly.getLatLonArray();
int actualPosition = (position == Integer.MAX_VALUE ? ll.length
: position * 2);
LatLonPoint llpnt = projection.inverse(x, y);
if (Debug.debugging("eomp")) {
Debug.output("EditableOMPoly: adding point to lat/lon poly at "
+ x + ", " + y + ": " + llpnt + ", at the end of ");
for (int j = 0; j < ll.length; j += 2) {
Debug.output(ll[j] + ", " + ll[j + 1]);
}
}
float[] newll = new float[ll.length + 2];
if (actualPosition >= ll.length) {
// Put the new points at the end
if (ll.length != 0) {
System.arraycopy(ll, 0, newll, 0, ll.length);
}
newll[ll.length] = llpnt.radlat_;
newll[ll.length + 1] = llpnt.radlon_;
position = ll.length / 2;
} else if (actualPosition <= 0) {
// Put the new point at the beginning
System.arraycopy(ll, 0, newll, 2, ll.length);
newll[0] = llpnt.radlat_;
newll[1] = llpnt.radlon_;
position = 0;
} else {
// actualPosition because there are 2 floats for
// every
// position.
newll[actualPosition] = llpnt.radlat_;
newll[actualPosition + 1] = llpnt.radlon_;
System.arraycopy(ll, 0, newll, 0, actualPosition);
System.arraycopy(ll,
actualPosition,
newll,
actualPosition + 2,
ll.length - actualPosition);
}
poly.setLocation((float[]) newll, OMGraphic.RADIANS);
}
} else if (renderType == OMGraphic.RENDERTYPE_XY) {
// Grab the projected endpoints
Debug.message("eomg", "EditableOMPoly: adding point to x/y poly");
int currentLength = poly.xs.length;
int[] newxs = new int[currentLength + 1];
int[] newys = new int[currentLength + 1];
if (position >= currentLength) {
// Put the new points at the end
System.arraycopy(poly.xs, 0, newxs, 0, currentLength);
System.arraycopy(poly.ys, 0, newys, 0, currentLength);
newxs[currentLength] = x;
newys[currentLength] = y;
position = currentLength;
} else if (position <= 0) {
// Put the new points at the beginning
System.arraycopy(poly.xs, 0, newxs, 1, currentLength);
System.arraycopy(poly.ys, 0, newys, 1, currentLength);
newxs[0] = x;
newys[0] = y;
position = 0;
} else {
newxs[position] = x;
newys[position] = y;
System.arraycopy(poly.xs, 0, newxs, 0, position);
System.arraycopy(poly.xs,
position,
newxs,
position + 1,
currentLength - position);
System.arraycopy(poly.ys, 0, newys, 0, position);
System.arraycopy(poly.ys,
position,
newys,
position + 1,
currentLength - position);
}
poly.setLocation(newxs, newys);
} else {
// Rendertype is offset...
// Grab the projected endpoints
Debug.message("eomg", "EditableOMPoly: adding point to offset poly");
int currentLength = poly.xs.length;
int[] newxs = new int[currentLength + 1];
int[] newys = new int[currentLength + 1];
if (position >= currentLength) {
// Put the new points at the end
position = currentLength;
System.arraycopy(poly.xs, 0, newxs, 0, currentLength);
System.arraycopy(poly.ys, 0, newys, 0, currentLength);
} else if (position <= 0) {
// Put the new points at the beginning
position = 0;
System.arraycopy(poly.xs, 0, newxs, 1, currentLength);
System.arraycopy(poly.ys, 0, newys, 1, currentLength);
} else {
System.arraycopy(poly.xs, 0, newxs, 0, position);
System.arraycopy(poly.xs,
position,
newxs,
position + 1,
currentLength - position);
System.arraycopy(poly.ys, 0, newys, 0, position);
System.arraycopy(poly.ys,
position,
newys,
position + 1,
currentLength - position);
}
int offsetX;
int offsetY;
if (gpo.getX() == -1 && gpo.getY() == -1) {
offsetX = projection.getWidth() / 2;
offsetY = projection.getHeight() / 2;
} else {
offsetX = gpo.getX();
offsetY = gpo.getY();
}
if (poly.coordMode == OMPoly.COORDMODE_ORIGIN || position == 0) { // cover
// the
// first
// point
newxs[position] = x - offsetX;
newys[position] = y - offsetY;
} else { // CMode Previous offset deltas
newxs[position] = x - offsetX - newxs[position - 1];
newys[position] = y - offsetY - newys[position - 1];
}
if (position == 0) {
// Could call projection.getCenter() but that might
// break if/when we make other projection
// libraries/paradigms active.
LatLonPoint llpnt = projection.inverse(offsetX, offsetY);
poly.lat = llpnt.radlat_;
poly.lon = llpnt.radlon_;
}
poly.setLocation(poly.lat,
poly.lon,
OMGraphic.RADIANS,
newxs,
newys);
}
// Need to reset the arrowhead when an end point is added,
// removing it from the shape when the point gets added.
// Otherwise, you end up with a arrowhead at each junction of
// the polygon.
OMArrowHead omah = poly.getArrowHead();
poly.setArrowHead(null);
// This is the standard call that needs to be made here, the
// arrowhead changes are around this.
poly.regenerate(projection);
// Reset the arrowhead so it will get drawn on the new
// segment.
poly.setArrowHead(omah);
polyGrabPoints.add(position, gp);
if (gpo != null) {
gpo.addGrabPoint(gp);
}
return position;
}
/**
* Delete a point off the end of the polyline/polygon.
*/
public void deletePoint() {
deletePoint(Integer.MAX_VALUE);
}
/**
* Delete a point at a certain point in the polygon coordinate list. If the
* position is less than zero, the deleted point will be the starting point.
* If the position is greater than the list of current points, the point
* will be deleted from the end of the poly.
*/
public void deletePoint(int position) {
int renderType = poly.getRenderType();
boolean needToHookUp = false;
if (position <= 0 && isEnclosed()) {
// if the position is 0 and the polygon is enclosed, we
// need to disengage the two points, then reattach.
enclose(false);
needToHookUp = true;
}
if (renderType == OMGraphic.RENDERTYPE_LATLON) {
Debug.message("eomg",
"EditableOMPoly: adding point to lat/lon poly");
if (projection != null) {
float[] ll = poly.getLatLonArray();
float[] newll = new float[ll.length - 2];
int actualPosition = (position == Integer.MAX_VALUE ? ll.length
: position * 2);
if (actualPosition >= ll.length) {
// Pull the new points off the end
System.arraycopy(ll, 0, newll, 0, ll.length - 2);
position = (ll.length - 2) / 2;
} else if (actualPosition <= 0) {
// Pull the new points off the beginning
System.arraycopy(ll, 2, newll, 0, ll.length - 2);
position = 0;
} else {
// actualPosition because there are 2 floats for
// every
// position.
System.arraycopy(ll, 0, newll, 0, actualPosition);
System.arraycopy(ll,
actualPosition + 2,
newll,
actualPosition,
ll.length - actualPosition - 2);
}
poly.setLocation((float[]) newll, OMGraphic.RADIANS);
}
} else {
// Grab the projected endpoints
Debug.message("eomg",
"EditableOMPoly: adding point to x/y or offset poly");
int currentLength = poly.xs.length;
int[] newxs = new int[currentLength - 1];
int[] newys = new int[currentLength - 1];
if (position >= currentLength) {
// Pull the points from the end...
System.arraycopy(poly.xs, 0, newxs, 0, currentLength - 1);
System.arraycopy(poly.ys, 0, newys, 0, currentLength - 1);
position = currentLength - 1;
} else if (position <= 0) {
// Pull the points from the beginning...
System.arraycopy(poly.xs, 1, newxs, 0, currentLength - 1);
System.arraycopy(poly.ys, 1, newys, 0, currentLength - 1);
position = 0;
} else {
System.arraycopy(poly.xs, 0, newxs, 0, position);
System.arraycopy(poly.xs,
position + 1,
newxs,
position,
currentLength - position - 1);
System.arraycopy(poly.ys, 0, newys, 0, position);
System.arraycopy(poly.ys,
position + 1,
newys,
position,
currentLength - position - 1);
}
if (poly.getRenderType() == OMGraphic.RENDERTYPE_OFFSET) {
poly.setLocation(poly.lat,
poly.lon,
OMGraphic.RADIANS,
newxs,
newys);
} else {
poly.setLocation(newxs, newys);
}
}
if (projection != null) {
poly.regenerate(projection);
}
// Remove the GrabPoint for the deleted spot.
GrabPoint gp = (GrabPoint) polyGrabPoints.remove(position);
if (gpo != null && gp != null) {
gpo.removeGrabPoint(gp);
}
if (needToHookUp) {
enclose(true);
}
}
/**
* Called to set the OffsetGrabPoint to the current mouse location, and
* update the OffsetGrabPoint with all the other GrabPoint locations, so
* everything can shift smoothly. Should also set the OffsetGrabPoint to the
* movingPoint. Should be called only once at the beginning of the general
* movement, in order to set the movingPoint. After that, redraw(e) should
* just be called, and the movingPoint will make the adjustments to the
* graphic that are needed.
*/
public void move(MouseEvent e) {
// Need to check to see if the OffsetGrabPoint is currently
// being used. If not, just use it, otherwise, will need to
// create a special one for the move.
if (poly.getRenderType() == OMGraphic.RENDERTYPE_OFFSET) {
gpm = new OffsetGrabPoint(e.getX(), e.getY());
gpm.clear();
} else {
gpm = gpo;
gpm.clear();
gpm.set(e.getX(), e.getY());
}
// Move all the other points along with the offset point...
addPolyGrabPointsToOGP(gpm);
movingPoint = gpm;
}
/**
* This method adds all the GrabPoints associated with the polygon nodes and
* adds them to the offset GrabPoint representing the lat/lon anchor point.
*/
protected void addPolyGrabPointsToOGP(OffsetGrabPoint ogp) {
if (ogp == null)
return;
// Reset the points to the offset point.
int count = 0;
Iterator gps = polyGrabPoints.iterator();
while (gps.hasNext()) {
GrabPoint gb = (GrabPoint) gps.next();
ogp.addGrabPoint(gb);
count++;
}
ogp.updateOffsets();
}
/**
* Use the current projection to place the graphics on the screen. Has to be
* called to at least assure the graphics that they are ready for rendering.
* Called when the graphic position changes.
*
* @param proj com.bbn.openmap.proj.Projection
* @return true
*/
public boolean generate(Projection proj) {
Debug.message("eomg", "EditableOMPoly.generate()");
if (poly != null) {
poly.generate(proj);
}
// Generate all the grab points
Iterator gps = polyGrabPoints.iterator();
while (gps.hasNext()) {
GrabPoint gb = (GrabPoint) gps.next();
if (gb != null)
gb.generate(proj);
}
if (gpo != null) {
gpo.generate(proj);
gpo.updateOffsets();
}
;
return true;
}
/**
* Given a new projection, the grab points may need to be repositioned off
* the current position of the graphic. Called when the projection changes.
*/
public void regenerate(Projection proj) {
Debug.message("eomg", "EditableOMPoly.regenerate()");
if (poly != null) {
poly.generate(proj);
setGrabPoints(poly);
}
// Generate all the grab points
Iterator gps = polyGrabPoints.iterator();
while (gps.hasNext()) {
GrabPoint gb = (GrabPoint) gps.next();
if (gb != null) {
gb.generate(proj);
}
}
if (gpo != null) {
gpo.generate(proj);
gpo.updateOffsets();
}
}
/**
* Draw the EditableOMPoly parts into the java.awt.Graphics object. The grab
* points are only rendered if the poly machine state is
* PolySelectedState.POLY_SELECTED.
*
* @param graphics java.awt.Graphics.
*/
public void render(java.awt.Graphics graphics) {
Debug.message("eomg", "EditableOMPoly.render()");
State state = getStateMachine().getState();
if (poly != null && !(state instanceof PolyUndefinedState)) {
poly.setVisible(true);
poly.render(graphics);
poly.setVisible(false);
} else {
Debug.message("eomg",
"EditableOMPoly.render: null or undefined poly.");
return;
}
// Render the points actually on the polygon
if (state instanceof GraphicSelectedState
|| state instanceof PolyAddNodeState
|| state instanceof PolyDeleteNodeState) {
Iterator gps = polyGrabPoints.iterator();
while (gps.hasNext()) {
GrabPoint gb = (GrabPoint) gps.next();
if (gb != null) {
gb.setVisible(true);
poly.render(graphics);
gb.render(graphics);
gb.setVisible(false);
}
}
}
// In certain condiditions, render the offset grab point.
if (state instanceof GraphicSelectedState
|| state instanceof GraphicEditState /*
* || state instanceof
* PolySetOffsetState
*/) {
if (gpo != null
&& poly.getRenderType() == OMGraphic.RENDERTYPE_OFFSET) {
gpo.setVisible(true);
gpo.render(graphics);
gpo.setVisible(false);
}
}
}
// ///////////// Special Grab Point functions
// /////////////////////
// Since the GrabPoints only refer to the points actually on the
// polygon, we have to make sure that the generic
// EditableOMGraphic grab point methods handle that. The
// OffsetGrabPointIndex is -1, so we have to look out for that and
// use the gpo when appropriate.
// /////////////////////////////////////////////////////////////////
/**
* Set the grab point objects within the EditableOMGraphic array. For the
* EditableOMPoly, with its variable number of GrabPoints, this just sets up
* a new list of all the grab points to look at. It's different than the
* polyGrabPoints, which are the grab points just on the polygon. This list
* includes the offset grab point. This method should be called when a new
* point gets added to the polygon, and should take an array of all the
* GrabPoints created. It will add the offsetGrabPoint to the end of the
* array.
*
* @param points a GrabPoint[] for the points on the polygon.
* @return true if the grab point array was exactly what the
* EditableOMGraphic was expecting, in terms of length of the
* GrabPoint array length. The method copies the array values that
* fit into the resident array.
*/
public boolean setGrabPoints(GrabPoint[] points) {
gPoints = new GrabPoint[points.length + 1];
System.arraycopy(gPoints, 0, points, 0, points.length);
gPoints[points.length] = gpo;
return true;
}
/**
* Flag to keep track of when the grab point array has been rebuilt in setGrabPoints().
*/
boolean arrayCleared = true;
/**
* Get the array of grab points used for the EditableOMGraphic. Creates the
* array by copying all the grab points out of the ArrayList, and tacking
* the offset grab point to the end.
*/
public GrabPoint[] getGrabPoints() {
int size = polyGrabPoints.size();
// The second half of the test is the fix to the bug that caused
// OMEditablePolys to be unresponsive when the colors changed. Thanks,
// Stephane!
// if (gPoints.length != size + 1
// || ((size > 0) && (!gPoints[0].equals(polyGrabPoints.get(0))))) {
if (gPoints.length != size + 1 || arrayCleared) {
arrayCleared = false;
Debug.message("eomg",
"EditableOMPoly.getGrabPoints(): recreating grab points");
gPoints = new GrabPoint[size + 1];
int counter = 0;
Iterator obj = polyGrabPoints.iterator();
while (obj.hasNext()) {
gPoints[counter++] = (GrabPoint) obj.next();
}
gPoints[counter] = gpo;
}
return gPoints;
}
/**
* Set the GrabPoint at a particule index of the array. This can be used to
* tie two different grab points together. This used to work with the
* gPoints array declared in EditableOMGraphic - no longer. If the index is
* -1, the offset grab point is set, and any other index refers to the
* concurrent polygon point.
*
* @param gb GrabPoint to assign within array.
* @param index the index of the array to put the GrabPoint. This index
* should be -1 for the offset grab point, or the index of the corner
* of the poly, in order starting from 0.
* @return If the grab point or array is null, or if the index is outside
* the range of the array, false is returned. If everything goes OK,
* then true is returned.
*/
public boolean setGrabPoint(GrabPoint gb, int index) {
// We might have to take care of the offset grab point
// connections here...
if (index == OFFSET_POINT_INDEX) {
gpo = (OffsetGrabPoint) gb;
return true;
} else {
return super.setGrabPoint(gb, index);
}
}
/**
* Given a grab point, return its index into the polygon array. If its not
* in the array, the next available index is returned.
*/
public int whichGrabPoint(GrabPoint gp) {
GrabPoint[] points = getGrabPoints();
for (int i = 0; i < points.length; i++) {
if (gp == points[i]) {
if (gp == gpo) {
return OFFSET_POINT_INDEX;
} else {
return i;
}
}
}
return points.length;
}
/**
* Return a particular GrabPoint at a particular point in the array. The
* EditableOMGraphic should describe which indexes refer to which grab
* points in the EOMG GrabPoint array. If the index is OFFSET_POINT_INDEX,
* the offset point is returned. If the index is otherwise outside the range
* of the array, null is returned.
*/
public GrabPoint getGrabPoint(int index) {
if (index == OFFSET_POINT_INDEX) {
return gpo;
} else {
return super.getGrabPoint(index);
}
}
/**
* Adds widgets to modify polygon.
*
* @param graphicAttributes the GraphicAttributes to use to get the GUI
* widget from to control those parameters for this EOMG.
* @return Component to use to control parameters for this EOMG.
*/
public Component getGUI(GraphicAttributes graphicAttributes) {
Debug.message("eomg", "EditableOMPoly.getGUI");
if (graphicAttributes != null) {
JMenu ahm = getArrowHeadMenu();
graphicAttributes.setLineMenuAdditions(new JMenu[] { ahm });
Component gaGUI = graphicAttributes.getGUI();
((JComponent) gaGUI).add(getPolyGUI());
return gaGUI;
} else {
return getPolyGUI();
}
}
JToggleButton polygonButton = null;
JButton addButton = null;
JButton deleteButton = null;
public void enablePolygonButton(boolean enable) {
if (polygonButton != null) {
polygonButton.setEnabled(enable);
}
}
public void enablePolygonEditButtons(boolean enable) {
if (addButton != null) {
addButton.setEnabled(enable);
}
if (deleteButton != null) {
deleteButton.setEnabled(enable);
}
}
public JToolBar getPolyGUI() {
return getPolyGUI(true, true, true);
}
public JToolBar getPolyGUI(boolean includeEnclose, boolean includeAdd,
boolean includeDelete) {
JToolBar buttonBox = new GridBagToolBar();
URL url;
ImageIcon imageIcon;
if (polygonButton == null) {
url = getImageURL("enclosepoly.gif");
imageIcon = new ImageIcon(url);
polygonButton = new JToggleButton(imageIcon);
polygonButton.setToolTipText(i18n.get(EditableOMPoly.class,
"polygonButton.tooltip",
"Automatically link first and last nodes"));
}
polygonButton.setSelected(isEnclosed());
polygonButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (getStateMachine().getState() instanceof GraphicSelectedState) {
enclose(((JToggleButton) e.getSource()).isSelected());
} else {
setEnclosed(((JToggleButton) e.getSource()).isSelected());
}
}
});
if (includeEnclose) {
buttonBox.add(polygonButton);
}
if (addButton == null) {
url = getImageURL("addpoint.gif");
imageIcon = new ImageIcon(url);
addButton = new JButton(imageIcon);
addButton.setToolTipText(i18n.get(EditableOMPoly.class,
"addButton.tooltip",
"Add a node to the polygon"));
}
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
((PolyStateMachine) stateMachine).setAddNode();
enablePolygonEditButtons(false);
}
});
addButton.setEnabled(false);
if (includeAdd) {
buttonBox.add(addButton);
}
if (deleteButton == null) {
url = getImageURL("deletepoint.gif");
imageIcon = new ImageIcon(url);
deleteButton = new JButton(imageIcon);
deleteButton.setToolTipText(i18n.get(EditableOMPoly.class,
"deleteButton.tooltip",
"Delete a node from the polygon"));
}
deleteButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
((PolyStateMachine) stateMachine).setDeleteNode();
enablePolygonEditButtons(false);
}
});
deleteButton.setEnabled(false);
if (includeDelete) {
buttonBox.add(deleteButton);
}
return buttonBox;
}
public java.net.URL getImageURL(String imageName) {
return EditableOMPoly.class.getResource(imageName);
}
}