// **********************************************************************
//
// <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/EditableOMGraphic.java,v $
// $RCSfile: EditableOMGraphic.java,v $
// $Revision: 1.11 $
// $Date: 2005/12/09 21:09:04 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.omGraphics;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.util.MissingResourceException;
import javax.swing.JComponent;
import javax.swing.JToolBar;
import com.bbn.openmap.Environment;
import com.bbn.openmap.I18n;
import com.bbn.openmap.MapBean;
import com.bbn.openmap.event.MapMouseAdapter;
import com.bbn.openmap.event.MapMouseEvent;
import com.bbn.openmap.event.UndoEvent;
import com.bbn.openmap.event.UndoStack;
import com.bbn.openmap.gui.GridBagToolBar;
import com.bbn.openmap.omGraphics.editable.EOMGStateMachine;
import com.bbn.openmap.omGraphics.event.EOMGEvent;
import com.bbn.openmap.omGraphics.event.EOMGListener;
import com.bbn.openmap.omGraphics.event.EOMGListenerSupport;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.ComponentFactory;
import com.bbn.openmap.util.Debug;
/**
* The EditableOMGraphic is a shell that controls actions to edit or create a graphic. This class contains a state
* machine that defines how mouse events will be interpreted to modify the OMGraphic contained within. Any class that
* extends this one is responsible for assigning the appropriate state machine and OMGraphic to itself. Also, an
* EditableOMGraphic has a notion of a list of GrabPoints, which can be used as handles to the OMGraphic to provide
* controlled modifications.
*/
public abstract class EditableOMGraphic
extends MapMouseAdapter {
/**
* If the grab points should be rendered differently than the default, the DrawingAttributes should be stored in the
* edited OMGraphic under this attribute key.
*/
public final static String GRAB_POINT_DRAWING_ATTRIBUTES_ATTRIBUTE = "gpdaa";
/**
* If the EditableOMGraphic supports the notion of a selected GrabPoint (to highlight a node), and that selection
* should change the appearance of a GrabPoint, the DrawingAttributes for that selection appearance should be stored
* in the edited OMGraphic attributes under this attribute key.
*/
public final static String SELECTED_GRAB_POINT_DRAWING_ATTRIBUTES_ATTRIBUTE = "sgpdaa";
/**
* The state machine that interprets the mouse events (and other events) and modifies the OMGraphics accordingly.
*
* @see com.bbn.openmap.util.stateMachine.StateMachine
*/
protected EOMGStateMachine stateMachine;
/**
* This is here for the MapMouseListener interface. This may not be important, depending on what is funneling mouse
* events to the graphic.
*/
protected String[] mouseModeServiceList;
/**
* The array of GrabPoints.
*/
protected GrabPoint[] gPoints;
/**
* The projection of the map. This can be retrieved from the mouse events, provided that the mouse events source is
* the MapBean.
*/
protected Projection projection;
/**
* This GrabPoint is one that has been grabbed by the mouse, and is being moved.
*/
protected GrabPoint movingPoint = null;
protected EOMGListenerSupport listeners = null;
/**
* Flag to indicate whether a GUI for this EOMG should be presented to allow edits to it's attributes.
*/
protected boolean showGUI = true;
/**
* Flag to let states know if the edges of the graphic can be grabbed directly, for movement or manipulation, as
* opposed to just allowing those actions through the grab points.
*/
protected boolean canGrabGraphic = true;
/**
* The component to notify for changes made to the OMGraphic, so they can be undone if desired.
*/
protected UndoStack undoStack;
protected I18n i18n = Environment.getI18n();
/**
* A little flag to let the EOMG that a popup menu is up on the map. If the menu is up, and the menu is not clicked
* on, we don't really want to deactivate the drawing tool right then - let a free click go by first, which will
* dismiss the menu.
*/
protected boolean popupIsUp = false;
/**
* Action mask for this graphic. Used as a holder for modifying objects to let this EditableOMGraphic know what is
* being done to it.
*/
protected int actionMask = 0;
protected boolean DEBUG = false;
protected boolean DEBUG_DETAIL = false;
protected boolean xorRendering = true;
protected EditableOMGraphic() {
DEBUG = Debug.debugging("eomg");
DEBUG_DETAIL = Debug.debugging("eomgdetail");
}
/**
* Set the StateMachine for this EditableOMGraphic.
*
* @param sm StateMachine.
* @see com.bbn.openmap.util.stateMachine.StateMachine
*/
public void setStateMachine(EOMGStateMachine sm) {
stateMachine = sm;
}
/**
* Get the state machine for this EditableOMGraphic.
*/
public EOMGStateMachine getStateMachine() {
return stateMachine;
}
/**
* Set the list of MouseMode names that this EditableOMGraphic will respond to, if it is dealing directly with a
* MouseDelegator.
*/
public void setMouseModeServiceList(String[] list) {
mouseModeServiceList = list;
}
/**
* Get the list of MouseMode names that this EditableOMGraphic will respond to, if it is dealing directly with a
* MouseDelegator.
*/
public String[] getMouseModeServiceList() {
return mouseModeServiceList;
}
/**
* Set whether this EOMG should provide a user interface to have the attributes modified.
*
* @param set true if the GUI should be shown.
*/
public void setShowGUI(boolean set) {
showGUI = set;
OMGraphic graphic = getGraphic();
if (graphic != null) {
graphic.setShowEditablePalette(set);
}
}
public boolean getShowGUI() {
if (getGraphic() != null) {
return getGraphic().getShowEditablePalette();
} else {
return showGUI;
}
}
/**
* Set whether a graphic can be manipulated by its edges, rather than just by its grab points. Used internally.
*/
public void setCanGrabGraphic(boolean set) {
canGrabGraphic = set;
}
/**
* Get whether a graphic can be manipulated by its edges, rather than just by its grab points.
*/
public boolean getCanGrabGraphic() {
return canGrabGraphic;
}
/**
* Set the OMGraphic that is being modified by the EditableOMGraphic. The type of OMGraphic needs to match what the
* EditableOMGraphic is expecting. Assume that if the graphic passed in is null, that a proper graphic will be
* created.
*
* @param graphic OMGraphic.
*/
public abstract void setGraphic(OMGraphic graphic);
/**
* Create the OMGraphic that is to be modified by the EditableOMGraphic.
*
* @param ga GraphicAttributes, describing the graphic to be created.
*/
public abstract void createGraphic(GraphicAttributes ga);
/**
* Get the OMGraphic that is being created/modified by the EditableOMGraphic.
*/
public abstract OMGraphic getGraphic();
/**
* Remove all changes and put graphic as it was before modifications. If the graphic is being created, start over.
*/
public void reset() {
Debug.output("EditableOMGraphic.reset(): not yet supported");
}
/**
* Set the grab point objects within the EditableOMGraphic array. The size and layout of the points in the array are
* carefully determined by the EditableOMGraphic, so this method merely replaces objects within the array, not
* replacing the array itself, so that you cannot reset the number of grab points an EditableOMGraphic uses for a
* particular OMGraphic.
*
* @param points a GrabPoint[]
* @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) {
if (points == null || gPoints == null) {
return false;
}
System.arraycopy(points, 0, gPoints, 0, Math.min(points.length, gPoints.length));
return (points.length == gPoints.length);
}
/**
* Method to allow objects to set OMAction masks on this editable graphic.
*/
public void setActionMask(int mask) {
actionMask = mask;
}
/**
* Get the OMAction mask for this graphic.
*/
public int getActionMask() {
return actionMask;
}
/**
* Tells the EditableOMGraphic that the locations of the grab points have been modified, and that the parameters of
* the OMGraphic need to be modified accordingly.
*/
public abstract void setGrabPoints();
/**
* Get the array of grab points used for the EditableOMGraphic. Given a mouse event, you can see if one of these is
* affected, and move it accordingly. Call setGrabPoints() when modifications are done, so that the OMGraphic is
* modified.
*/
public GrabPoint[] getGrabPoints() {
return gPoints;
}
/**
* Set the GrabPoint at a particule index of the array. This can be used to tie two different grab points together.
*
* @param gb GrabPoint to assign within array.
* @param index the index of the array to put the GrabPoint. The EditableOMGraphic should be able to provide the
* description of the proper placement indexes.
* @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) {
if (gPoints != null && gb != null && index >= 0 && index < gPoints.length) {
gPoints[index] = gb;
return true;
} else {
return false;
}
}
/**
* 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 outside the range of the array,
* null is returned.
*/
public GrabPoint getGrabPoint(int index) {
if (gPoints != null && index >= 0 && index < gPoints.length) {
return gPoints[index];
} else {
return null;
}
}
/**
* 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) {
}
/**
* 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) {
}
/**
* Set the GrabPoint that is in the middle of being modified, as a result of a mouseDragged event, or other
* selection.
*/
public void setMovingPoint(GrabPoint gp) {
movingPoint = gp;
}
/**
* Get the GrabPoint that is being moved. If it's null, then there isn't one.
*/
public GrabPoint getMovingPoint() {
return movingPoint;
}
/**
* Notification that a MouseEvent was used to trigger creation or edit of this EditableOMGraphic, and this is the
* first MouseEvent received. If the EditableOMGraphic can handle it, it should. Otherwise, it should put itself in
* the right state to let the user know it's active.
*/
public void handleInitialMouseEvent(MouseEvent e) {
getStateMachine().setEdit();
if (e != null) {
GrabPoint gp = getMovingPoint(e);
if (gp == null) {
move(e);
} else {
getStateMachine().setSelected();
}
} else {
getStateMachine().setSelected();
}
}
/**
* Given a MouseEvent, find a GrabPoint that it is touching, and set the moving point to that GrabPoint. Called when
* a MouseEvent happens like a mousePressed or mouseReleased, and you want to find out if a GrabPoint should be used
* to make modifications to the graphic or its position. This method should only be called to establish a moving
* point. getMovingPoint() should be called to check to see if one has been established, and then redraw(MouseEvent)
* would be called to move that moving point.
*
* @param e MouseEvent
* @return GrabPoint that is touched by the MouseEvent, null if none are.
*/
public GrabPoint getMovingPoint(MouseEvent e) {
return _getMovingPoint(e);
}
/**
* Given a MouseEvent, find a GrabPoint that it is touching, and set the moving point to that GrabPoint. A version
* for grandchild classes.
*
* @param e MouseEvent that the GrabPoint should attach to.
* @see #getMovingPoint(MouseEvent)
*/
public GrabPoint _getMovingPoint(MouseEvent e) {
movingPoint = null;
GrabPoint[] gb = getGrabPoints();
Point2D pnt = getProjectionPoint(e);
double x = pnt.getX();
double y = pnt.getY();
for (int i = gb.length - 1; i >= 0; i--) {
if (gb[i] != null && gb[i].distance(x, y) == 0) {
setMovingPoint(gb[i]);
// in case the points are on top of each other, the
// last point in the array will take precedence.
return gb[i];
}
}
setMovingPoint(null);
return null;
}
/**
* 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.
*/
public abstract void move(MouseEvent e);
/**
* Clean the surface all the painting is taking place over.
*/
public void cleanMap(MouseEvent e) {
Object obj = e.getSource();
if (!(obj instanceof MapBean)) {
return;
}
// Could call repaint(), but I think we should paint in this
// thread...
MapBean map = (MapBean) obj;
// Gets the buffer cleaned out.
map.setBufferDirty(true);
map.paintChildren(map.getGraphics(true));
}
/**
* Same as redraw(e, false)
*/
public void redraw(MouseEvent e) {
redraw(e, false);
}
public void redraw(MouseEvent e, boolean firmPaint) {
redraw(e, firmPaint, true);
}
/**
* A DrawingAttributes object used to hold OMGraphic settings while it is being moved. When an OMGraphic is being
* moved, basic (DEFAULT) settings are put on the OMGraphic to make it as light and uncomplicated as possible.
*/
protected DrawingAttributes holder = new DrawingAttributes();
/**
* Given a MouseEvent, check the source, and if it's a MapBean, then grab the projection and java.awt.Graphics from
* it to use for generation and rendering of the EditableOMGraphic objects.
*
* @param e MouseEvent
* @param firmPaint true if the graphic is being rendered at rest, with fill colors and true colors, with the grab
* point if the state allows it. If false, then the fill color will not be used, and just the graphic will be drawn.
* Use false for graphics that are moving.
*/
public void redraw(MouseEvent e, boolean firmPaint, boolean drawXOR) {
if (DEBUG) {
Debug.output("EditableOMGraphic.redraw(" + (firmPaint ? "firmPaint)" : ")"));
}
drawXOR = drawXOR && isXorRendering();
if (e == null) {
if (lastMouseEvent == null) {
return;
}
e = lastMouseEvent;
}
Object obj = e.getSource();
if (!(obj instanceof MapBean)) {
return;
}
MapBean map = (MapBean) obj;
Graphics g = map.getGraphics(true);
OMGraphic graphic = getGraphic();
// Seeing if we can make for a better rendering look by repainting the
// mapBean all the time. With tiling, it works.
if (!isXorRendering()) {
map.repaint();
}
if (firmPaint) {
// So, with a firm paint, we want to clean the screen. If
// the map is being buffered, we need to clean out the
// buffer, which is why we set the Request paint to true,
// to get the image rebuilt. Otherwise, a copy of the
// graphic remains.
map.setBufferDirty(true);
graphic.generate(getProjection());
map.repaint();
} else {
// If we get here, we are painting a moving object, so we
// only want to do the outline to make it as fast as
// possible.
holder.setFrom(graphic);
DrawingAttributes.DEFAULT.setTo(graphic);
modifyOMGraphicForEditRender();
graphic.regenerate(getProjection());
if (drawXOR) {
g.setXORMode(Color.lightGray);
g.setColor((Color) graphic.getDisplayPaint());
render(g);
}
GrabPoint gp = getMovingPoint();
if (gp != null) {
Point2D pnt = getProjectionPoint(e);
double x = pnt.getX();
double y = pnt.getY();
gp.set((int) x, (int) y);
if (gp instanceof OffsetGrabPoint) {
((OffsetGrabPoint) gp).moveOffsets();
}
setGrabPoints();
}
}
if (!firmPaint) {
generate(getProjection());
render(g);
holder.setTo(graphic);
}
resetOMGraphicAfterEditRender();
g.dispose();
lastMouseEvent = e;
}
protected MouseEvent lastMouseEvent;
/**
* A convenience method that gives an EditableOMGraphic a chance to modify the OMGraphic so it can be drawn quickly,
* by turning off labels, etc, right before the XORpainting happens. The OMGraphic should be configured so that the
* render method does the least amount of painting possible. Note that the DrawingAttributes for the OMGraphic have
* already been set to DrawingAttributes.DEFAULT (black line, clear fill).
*/
protected void modifyOMGraphicForEditRender() {
}
/**
* A convenience method that gives an EditableOMGraphic a chance to reset the OMGraphic so it can be rendered
* normally, after it has been modified for quick paints. The DrawingAttributes for the OMGraphic have already been
* reset to their normal settings, from the DrawingAttributes.DEFAULT settings that were used for the quick paint.
*/
protected void resetOMGraphicAfterEditRender() {
}
public void repaint() {
if (lastMouseEvent != null) {
redraw(lastMouseEvent, true);
}
}
protected void finalize() {
if (getGraphic() != null) {
getGraphic().setVisible(true);
}
if (Debug.debugging("gc")) {
Debug.output("EditableOMGraphic gone.");
}
}
/**
* 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 abstract boolean generate(Projection proj);
/**
* Given a new projection, the grab points may need to be repositioned off the current position of the graphic.
* Called when the projection changes. IMPORTANT! Set the GrabPoints for the graphic here.
*/
public abstract void regenerate(Projection proj);
public void repaintRender(Graphics g) {
render(g);
}
/**
*/
public abstract void render(Graphics g);
public boolean isXorRendering() {
return xorRendering;
}
/**
* Set whether the painting will occur using XOR rendering. If false, the mapbean will be repainted on every
* movement. Looks better, but you need to make sure the repaint burden on the mapbean doesn't slow the drawing down
* too much.
*
* @param xorRendering if true, XOR rendering will be used. Otherwise, the old location won't be drawn.
*/
public void setXorRendering(boolean xorRendering) {
this.xorRendering = xorRendering;
}
/**
* Set the current projection.
*/
public void setProjection(Projection proj) {
projection = proj;
// This is important. In the EditableOMGraphics, the
// GrabPoints are set when regenerate is called.
regenerate(proj);
}
/**
* Get the current projection.
*/
public Projection getProjection() {
return projection;
}
// Mouse Listener events
// //////////////////////
/**
*/
public boolean mousePressed(MouseEvent e) {
if (DEBUG_DETAIL) {
Debug.output(getClass().getName() + ".mousePressed()");
}
if (!mouseOnMap) {
return false;
}
return stateMachine.getState().mousePressed(e);
}
/**
*/
public boolean mouseReleased(MouseEvent e) {
if (DEBUG_DETAIL) {
Debug.output(getClass().getName() + ".mouseReleased()");
}
if (!mouseOnMap) {
return false;
}
return stateMachine.getState().mouseReleased(e);
}
/**
*/
public boolean mouseClicked(MouseEvent e) {
if (DEBUG_DETAIL) {
Debug.output(getClass().getName() + ".mouseClicked()");
}
if (!mouseOnMap) {
return false;
}
return stateMachine.getState().mouseClicked(e);
}
boolean mouseOnMap = true;
/**
*/
public void mouseEntered(MouseEvent e) {
if (DEBUG_DETAIL) {
Debug.output(getClass().getName() + ".mouseEntered()");
}
mouseOnMap = true;
stateMachine.getState().mouseEntered(e);
}
/**
*/
public void mouseExited(MouseEvent e) {
if (DEBUG_DETAIL) {
Debug.output(getClass().getName() + ".mouseExited()");
}
mouseOnMap = false;
stateMachine.getState().mouseExited(e);
}
// Mouse Motion Listener events
// /////////////////////////////
/**
*/
public boolean mouseDragged(MouseEvent e) {
if (DEBUG_DETAIL) {
Debug.output(getClass().getName() + ".mouseDragged()");
}
if (!mouseOnMap) {
return false;
}
return stateMachine.getState().mouseDragged(e);
}
/**
*/
public boolean mouseMoved(MouseEvent e) {
if (DEBUG_DETAIL) {
Debug.output(getClass().getName() + ".mouseMoved()");
}
if (!mouseOnMap) {
return false;
}
return stateMachine.getState().mouseMoved(e);
}
/**
*/
public void mouseMoved() {
if (DEBUG_DETAIL) {
Debug.output(getClass().getName() + ".mouseMoved()");
}
if (!mouseOnMap) {
return;
}
stateMachine.getState().mouseMoved();
}
/**
* Add a EOMGListener.
*
* @param l EOMGListener
*/
public synchronized void addEOMGListener(EOMGListener l) {
if (listeners == null) {
listeners = new EOMGListenerSupport(this);
}
listeners.add(l);
}
/**
* Remove a EOMGListener.
*
* @param l EOMGListener
*/
public synchronized void removeEOMGListener(EOMGListener l) {
if (listeners == null) {
return;
}
listeners.remove(l);
}
/**
* The method to call if you want to let listeners know that the state has changed. Usually called when a graphic is
* selected or not, so that GUIs can be directed.
*/
public void fireEvent(EOMGEvent event) {
if (listeners != null) {
listeners.fireEvent(event);
}
if (event.getStatus() == EOMGEvent.EOMG_UNDO) {
updateCurrentState(null);
}
}
/**
* Create the event with a Cursor and/or message, and then fire it.
*
* @param cursor Cursor to be used.
* @param message an instruction/error to be displayed to the user.
* @param status the current status of the EditableOMGraphic.
*/
public void fireEvent(Cursor cursor, String message, int status) {
fireEvent(cursor, message, null, status);
}
/**
* Create the event with the Cursor, message and/or MouseEvent.
*
* @param cursor Cursor to be used.
* @param message an instruction/error to be displayed to the user.
* @param mouseEvent where that caused the EOMGEvent. May be null.
* @param status the current status of the EditableOMGraphic.
*/
public void fireEvent(Cursor cursor, String message, MouseEvent mouseEvent, int status) {
if (listeners != null) {
EditableOMGraphic theSource = listeners.getEOMG();
EOMGEvent event = new EOMGEvent(theSource, cursor, message, mouseEvent, status);
fireEvent(event);
}
}
/**
* Create the event with no cursor change or message to be displayed.
*/
public void fireEvent(int status) {
fireEvent(null, null, null, status);
}
/**
* If this EditableOMGraphic has parameters that can be manipulated that are independent of other EditableOMGraphic
* types, then you can provide the widgets to control those parameters here. By default, this method returns null,
* which indicates that you can extend this method to return a Component that controls parameters for the
* EditableOMGraphic other than the GraphicAttribute parameters. Should return something like a toolbar, small.
*
* @return Component to control EOMG parameters, without the GraphicAttribute GUI.
*/
public Component getGUI() {
return getGUI(null);
}
/**
* If this EditableOMGraphic has parameters that can be manipulated that are independent of other EditableOMGraphic
* types, then you can provide the widgets to control those parameters here. By default, returns the
* GraphicAttributes GUI widgets. If you don't want a GUI to appear when a widget is being created/edited, then don't
* call this method from the EditableOMGraphic implementation, and return a null Component from getGUI.
*
* @param graphicAttributes the GraphicAttributes that could be used to get the GUI widget from to control those
* parameters for this EOMG. The GraphicAttributes used to provide the GUI widget, but it doesn't anymore. Subclasses
* can take this opportunity to reset the GraphicAttributes interface for a new OMGraphic.
*
* @return Component to use to control parameters for this EOMG, generally a JPanel with a toolbar.
*/
public Component getGUI(GraphicAttributes graphicAttributes) {
if (graphicAttributes != null) {
graphicAttributes.setLineMenuAdditions(null);
// Used to return the toolbar gui, now the color and line options provided on the right click menu.
// return graphicAttributes.getGUI();
}
return null;
}
public Point2D getProjectionPoint(MouseEvent e) {
Point2D pnt = null;
if (e instanceof MapMouseEvent && ((MapMouseEvent) e).mapIsRotated()) {
MapMouseEvent mme = (MapMouseEvent) e;
pnt = mme.getProjectedLocation();
}
if (pnt == null) {
pnt = new Point2D.Double(e.getX(), e.getY());
}
return pnt;
}
public boolean isMouseEventTouching(MouseEvent e) {
Point2D pnt = getProjectionPoint(e);
return getGraphic().distance(pnt.getX(), pnt.getY()) <= 2;
}
public boolean isMouseEventTouchingTheEdge(MouseEvent e) {
Point2D pnt = getProjectionPoint(e);
return getGraphic().distanceToEdge(pnt.getX(), pnt.getY()) <= 2;
}
/**
* This method should be overwritten for each EditableOMGraphic to save the state of the current OMGraphic, in case
* the user wants to revert to this state. Called from the updateCurrentState method.
*
* @param whatHappened String describing what got you here. You can leave this null if you just want to go with the default.
* @return UndoEvent reflecting current state. May be null if undo isn't handled. Returning null is the default
* action.
*/
protected UndoEvent createUndoEventForCurrentState(String whatHappened) {
if (whatHappened == null) {
whatHappened = i18n.get(this.getClass(), "omgraphicUndoString", "Edit");
}
try {
return new OMGraphicUndoEvent(this, whatHappened);
} catch (MissingResourceException mre) {
// Return null so event won't be registered.
return null;
}
}
/**
* Called by anything that knows that the EOMG has arrived at a stable state that should be kept for Undo actions.
*
* @param whatHappened a description of was done to get to this state. If null, the EOMG will create a default string
* to use.
*/
public void updateCurrentState(String whatHappened) {
UndoEvent undoEvent = createUndoEventForCurrentState(whatHappened);
if (undoEvent != null && undoStack != null) {
undoStack.setTheWayThingsAre(undoEvent);
}
}
/**
* @return the undoStack
*/
public UndoStack getUndoStack() {
return undoStack;
}
/**
* @param undoStack the undoStack to set
*/
public void setUndoStack(UndoStack undoStack) {
this.undoStack = undoStack;
}
/**
* A little flag to let the EOMG that a popup menu is up on the map. If the menu is up, and the menu is not clicked
* on, we don't really want to deactivate the drawing tool right then - let a free click go by first, which will
* dismiss the menu.
*
* @param popupIsUp
*/
public void setPopupIsUp(boolean popupIsUp) {
this.popupIsUp = popupIsUp;
}
public boolean isPopupIsUp() {
return this.popupIsUp;
}
public JComponent createAttributePanel(GraphicAttributes graphicAttributes) {
// JPanel palette = new JPanel();
// GridBagLayout gridbag = new GridBagLayout();
// GridBagConstraints c = new GridBagConstraints();
// palette.setLayout(gridbag);
JToolBar toolbar = new GridBagToolBar();
if (graphicAttributes != null) {
int orientation = graphicAttributes.getOrientation();
toolbar.setOrientation(orientation);
// if (orientation == SwingConstants.VERTICAL) {
// c.gridwidth = GridBagConstraints.REMAINDER;
// }
}
// gridbag.setConstraints(toolbar, c);
// palette.add(toolbar);
return toolbar;
}
/**
* Generic undo event for basic OMGraphics.
*
* @author dietrick
*/
public static class OMGraphicUndoEvent
implements UndoEvent {
protected EditableOMGraphic eomg;
protected OMGraphic stateHolder;
protected String description;
public OMGraphicUndoEvent(EditableOMGraphic eomg, String description)
throws MissingResourceException {
this.eomg = eomg;
this.description = description;
OMGraphic omg = eomg.getGraphic();
stateHolder = (OMGraphic) ComponentFactory.create(omg.getClass().getName());
if (stateHolder != null) {
// stateHolder = new OMPoly();
stateHolder.restore(eomg.getGraphic());
} else {
throw new MissingResourceException(eomg.getClass().getName() + " can't provide UndoEvents", eomg.getClass().getName(), "");
}
}
/*
* (non-Javadoc)
*
* @see com.bbn.openmap.event.UndoEvent#getDescription()
*/
public String getDescription() {
return description;
}
/*
* (non-Javadoc)
*
* @see com.bbn.openmap.event.UndoEvent#setState()
*/
public void setState() {
this.eomg.getGraphic().restore(stateHolder);
setSubclassState();
this.eomg.regenerate(this.eomg.getProjection());
this.eomg.repaint();
}
/**
* Called from setState before repaint() is called, so subclasses can update anything in their EditableOMGraphic
* state for the restored OMGraphic.
*/
protected void setSubclassState() {
// noop, for subclasses to use so we don't waste a repaint.
}
/**
* @return the stateHolder
*/
public OMGraphic getStateHolder() {
return stateHolder;
}
/**
* @param stateHolder the stateHolder to set
*/
public void setStateHolder(OMGraphic stateHolder) {
this.stateHolder = stateHolder;
}
/**
* @return the eomg
*/
public EditableOMGraphic getEomg() {
return eomg;
}
/**
* @param description the description to set
*/
public void setDescription(String description) {
this.description = description;
}
}
}