Package com.bbn.openmap.tools.drawing

Source Code of com.bbn.openmap.tools.drawing.OMDrawingTool

// **********************************************************************
//
// <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/tools/drawing/OMDrawingTool.java,v $
// $RCSfile: OMDrawingTool.java,v $
// $Revision: 1.25.2.11 $
// $Date: 2009/03/03 18:59:54 $
// $Author: dietrick $
//
// **********************************************************************

package com.bbn.openmap.tools.drawing;

import java.awt.Component;
import java.awt.Cursor;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;

import com.bbn.openmap.Environment;
import com.bbn.openmap.I18n;
import com.bbn.openmap.InformationDelegator;
import com.bbn.openmap.MapBean;
import com.bbn.openmap.MapHandler;
import com.bbn.openmap.MouseDelegator;
import com.bbn.openmap.event.MapMouseMode;
import com.bbn.openmap.event.PaintListener;
import com.bbn.openmap.event.ProjectionEvent;
import com.bbn.openmap.event.ProjectionListener;
import com.bbn.openmap.gui.OMToolComponent;
import com.bbn.openmap.gui.WindowSupport;
import com.bbn.openmap.gui.WindowSupport.WSDisplay;
import com.bbn.openmap.omGraphics.DrawingAttributes;
import com.bbn.openmap.omGraphics.EditableOMGraphic;
import com.bbn.openmap.omGraphics.EditableOMGraphicList;
import com.bbn.openmap.omGraphics.GraphicAttributes;
import com.bbn.openmap.omGraphics.OMAction;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicConstants;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.omGraphics.event.EOMGEvent;
import com.bbn.openmap.omGraphics.event.EOMGListener;
import com.bbn.openmap.omGraphics.event.SelectionListener;
import com.bbn.openmap.omGraphics.event.SelectionProvider;
import com.bbn.openmap.omGraphics.event.SelectionSupport;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;
import com.bbn.openmap.util.PropUtils;

/**
* The OMDrawingTool implements the DrawingTool interface, and can be used to
* adjust the drawing parameters of OMGraphics. It is basically a manager for
* directing MouseEvents and MouseMotionEvents from a Component to a
* EditableOMGraphic. The EditableOMGraphic is responsible for interpreting the
* events and making the adjustments to the OMGraphic it has wrapped within. The
* OMDrawingTool also tries to keep the presentation of the OMGraphic up to
* date, by managing the repaints of the Component to include the graphic being
* modified.
* <P>
*
* The OMDrawingTool is also a com.bbn.openmap.gui.Tool, which allows it to
* appear in the OpenMap toolbar. The OMDrawingTool keeps track of whether it is
* a tool, and appears accordingly:
* <P>
*
* If the OMDrawingTool is being used as a tool (getUseAsTool() == true), then
* it will set itself to be visible. If you are putting an OMDrawingTool in the
* OpenMap application and you want the color/line/graphic options to be visible
* in the toolbar, use the itTool property for the OMDrawingTool in the
* properties file. If you are using your own OMDrawingTool, in your
* EditorLayerTool for instance, you should set useAsTool(true) programmatically
* to get the visiblity of the tool to appear. There is a property to tell the
* OMDrawingTool to be visible when it is inactive, and that flag is true by
* default. You can set that property (visibleWhenInactive) to change this
* behavior.
*
* If the OMDrawingTool is not being used as a tool, it can be brought up in a
* window. This window can be brought up with a right click or control-click on
* the object being edited.
* <P>
*
* If the OMGraphic being edited doesn't want to have the OMDrawingTool visible,
* it won't be. Neither the tool nor the option to bring the window up won't be
* displayed with a right/control click.
* <P>
*
* The OMDrawingTool uses a behavior mask to give control over how it behaves.
* You can control if the attribute palette appears, if a popup gui appears by
* default when the editing is complete, or appear when the alt+mouse key or
* right mouse key is pressed. You should set this mask if you are not sure
* about the values that other components may have set on the OMDrawingTool.
* <P>
*
* The OMDrawingTool uses EditToolLoaders to determine what EditableOMGraphic
* can be used for a particular class name or OMGraphic type. If a loader for an
* OMGraphic type is not found, then that OMGraphic type won't be handled, and
* the tool will react to a create() or edit() call with a null object pointer.
* If a loader is found, and the OMgraphic can be edited or modified, then the
* create() or edit() methods will return a pointer to the OMGraphic being
* modified.
* <P>
*
* The GUI for the OMDrawingTool is multi-layered. The OMDrawingTool contains a
* GraphicsAttributes object, which is an extension of the GraphicAttributes
* object. The GraphicAttributes GUI within the tool lets you change the colors,
* line width and line dash pattern of the current OMGraphic. The
* GraphicAttributes conttribution to the GUI is not yet implemented, but will
* let you change the render type and line type of the OMGraphic. Finally, the
* EditableOMGraphic is given an opportunity to change and set parameters of the
* OMGraphic that is knows about - for instance, the EditableOMLine object will
* soon provide an interface to set arrowheads on the lines, as well as set the
* amount of arc a line has (it's currently not implemented).
* <P>
*/
public class OMDrawingTool extends OMToolComponent implements DrawingTool,
        Serializable, PropertyChangeListener, ProjectionListener, EOMGListener,
        PaintListener, SelectionProvider {

    I18n i18n = Environment.getI18n();
    /**
     * A GraphicAttributes object that describes the current coloring parameters
     * for the current graphic.
     */
    protected GraphicAttributes graphicAttributes = GraphicAttributes.getGADefaultClone();

    /** The current graphic being modified. */
    protected EditableOMGraphic currentEditable;
    /**
     * The MouseDelegator to use to get mouse events directed to the
     * DrawingTool.
     */
    protected MouseDelegator mouseDelegator;
    /**
     * A placeholder for the last mouse mode active before the drawing tool took
     * over.
     */
    protected MapMouseMode formerMouseMode = null;
    /**
     * The JComponent the drawing tool is servicing, usually the MapBean.
     */
    protected JComponent canvas;
    /**
     * The objects that know how to create a EditableOMGraphic for a particular
     * class name or OMGraphic.
     */
    protected Hashtable loaders = new Hashtable();
    /**
     * The ordered list of EditToolLoaders, for notification. Preservers order,
     * no duplicates.
     */
    protected Vector rawLoaders = new Vector();
    /**
     * The MouseMode used for the drawing tool.
     */
    protected OMDrawingToolMouseMode dtmm;

    /**
     * The component to notify when the drawing tool is finished.
     */
    protected DrawingToolRequestor requestor = null;
    /** The current projection. */
    protected Projection projection = null;
    /**
     * A support object to handle telling listeners that the drawing tool is in
     * the process of editing an object, hence making it selected.
     */
    protected SelectionSupport selectionSupport = null;

    /**
     * A behavior mask to show the GUI for the OMDrawingTool. Since the
     * OMDrawingTool is a com.bbn.openmap.gui.Tool object, it will only appear
     * on the tool panel if it has been added to it, and if it is being used as
     * a tool.
     */
    public final static int SHOW_GUI_BEHAVIOR_MASK = 1 << 0; // + 1
    /**
     * A behavior mask to add a menu item to the popup that will allow the GUI
     * to appear. If the OMDrawingTool is not being used as a tool and this is
     * set along with USE_POPUP_BEHAVIOR_MASK or ALT_POPUP_BEHAVIOR_MASK, then
     * the OMDrawingTool will appear in a window when the <B>Change Appearance
     * </B> option is selected in the popup menu.
     */
    public final static int GUI_VIA_POPUP_BEHAVIOR_MASK = 1 << 1; // + 2
    /**
     * Flag to tell the OMDrawingTool to display a popup when
     * gesturing/modifications appear to be over. Was the default action of the
     * tool, but was moved to only happening when the ctrl key or right mouse
     * button is pressed. You can force the old behavior by setting this.
     */
    public final static int USE_POPUP_BEHAVIOR_MASK = 1 << 2; // + 4
    /**
     * Allow a GUI popup to appear over the map when the gesturing/modifications
     * appear to be over, and when the ctrl key or right mouse button is
     * pressed.
     */
    public final static int ALT_POPUP_BEHAVIOR_MASK = 1 << 3; // + 8
    /**
     * Set the flag for the behavior that will tell the OMDrawingTool to *NOT*
     * add the OMDrawingToolMouseMode to the MouseDelegator as the active mouse
     * mode when activated. Should be called before create/edit is called, and
     * then you have to make sure that you provide MouseEvents to the
     * OMDrawingToolMouseMode or EditableOMGraphic in order to modify the
     * OMGraphic. Don't call this if you have already started using the tool,
     * the tool won't do anything if anything else is currently being modified.
     */
    public final static int PASSIVE_MOUSE_EVENT_BEHAVIOR_MASK = 1 << 4; // + 16
    /**
     * This behavior is used internally, when the OMDrawingTool should be told
     * to clean up as soon as it is safe.
     */
    public final static int DEACTIVATE_ASAP_BEHAVIOR_MASK = 1 << 5; // + 32
    /**
     * A convenience value that tells the OMDrawingTool to show the GUI if it is
     * a tool, or to only display the popup with the ctrl key or right mouse
     * button if it isn't. A combination of SHOW_GUI, GUI_VIA_POPUP and
     * ALT_POPUP.
     */
    public final static int DEFAULT_BEHAVIOR_MASK = 11;
    /**
     * A convenience value that tells the OMDrawingTool to not show the GUI, but
     * show the popup with the alt key, and the popup has the ability to delete
     * the OMGraphic. A combination of GUI_VIA_POPUP and ALT_POPUP.
     */
    public final static int QUICK_CHANGE_BEHAVIOR_MASK = 10;

    /**
     * A integer that is looked at, bitwise, to determine different behaviors.
     */
    protected int behaviorMask = DEFAULT_BEHAVIOR_MASK;
    /**
     * Used for property change notifications.
     */
    public final static String LoadersProperty = "OMDrawingTool.loaders";

    /**
     * Debug flag turned on when <B>drawingtool </B> debug flag enabled.
     */
    protected boolean DEBUG = false;

    /**
     * A handle to the InformationDelegator to use for status messages.
     */
    protected InformationDelegator informationDelegator = null;

    /**
     * A Vector of Classes that can be handled by the OMDrawingTool. Constructed
     * the first time canEdit() is called after an EditToolLoader is added or
     * removed.
     */
    protected Vector possibleEditableClasses = null;

    /**
     * Just a helper flag to reduce work caused by unnecessary deactivate calls.
     * Set internally in activate() and deactivate().
     */
    protected boolean activated = false;

    /**
     * Tell the drawing tool to be invisible when it is inactive. True by
     * default.
     */
    protected boolean visibleWhenInactive = true;

    /**
     * The property, visibleWhenIactive, to set to false if you want that
     * behavior.
     */
    public final static String VisibleWhenInactiveProperty = "visibleWhenInactive";

    /**
     * The property list defining behavior mask values that should be set.
     */
    public final static String BehaviorProperty = "behavior";

    /**
     * Flag to tell tool to reset the GUI when it is deactivated. The only time
     * you would want this to be false (true is default) is when you are
     * creating many objects of the same type, and don't want the gui to keep
     * going back and forth between the default and special settings. Usually
     * set to in the drawingComplete method of an EditorTool. Reset to true when
     * showPalette is called.
     */
    protected boolean resetGUIWhenDeactivated = true;

    /**
     * Create a OpenMap Drawing Tool.
     */
    public OMDrawingTool() {
        super();
        setBorder(BorderFactory.createEmptyBorder());
        DEBUG = Debug.debugging("drawingtool");
        selectionSupport = new SelectionSupport(this);
        setAttributes(new GraphicAttributes());
        setMouseMode(createMouseMode());

        // Shouldn't assume that the drawing tool is a tool. This can
        // be set in the properties if it should be. Otherwise, the
        // default action is to appear on a right click called from
        // the GUI.
        setUseAsTool(false);
    }

    /**
     * Create the mouse mode used with the drawing tool. Called in the default
     * empty constructor, returns a OMDrawingToolMouseMode by default.
     */
    protected OMDrawingToolMouseMode createMouseMode() {
        return new OMDrawingToolMouseMode(this);
    }

    /**
     * Create a new OMGraphic, encased in a new EditableOMGraphic that can
     * modify it. If a loader cannot be found that can handle a graphic with the
     * given classname, this method will return a null object. If you aren't
     * sure of the behavior mask set in the tool, and you want a particular
     * behavior, set it before calling this method.
     *
     * @param classname the classname of the graphic to create.
     * @param requestor the Component that is requesting the OMGraphic. The
     *        requestor gets notified when the user is finished with the
     *        DrawingTool and the graphic is ready.
     * @return OMGraphic of the classname given, null if the DrawingTool can't
     *         create it.
     */
    public OMGraphic create(String classname, DrawingToolRequestor requestor) {
        return create(classname, null, requestor);
    }

    /**
     * Create a new OMGraphic, encased in a new EditableOMGraphic that can
     * modify it. If a loader cannot be found that can handle a graphic with the
     * given classname, this method will return a null object. If you aren't
     * sure of the behavior mask set in the tool, and you want a particular
     * behavior, set it before calling this method.
     *
     * @param classname the classname of the graphic to create.
     * @param ga GraphicAttributes object that contains more information about
     *        the type of line to be created.
     * @param requestor the Component that is requesting the OMGraphic. The
     *        requestor gets notified when the user is finished with the
     *        DrawingTool and the graphic is ready.
     * @return OMGraphic of the classname given, null if the DrawingTool can't
     *         create it.
     */
    public OMGraphic create(String classname, GraphicAttributes ga,
                            DrawingToolRequestor requestor) {
        return create(classname, ga, requestor, isMask(SHOW_GUI_BEHAVIOR_MASK));
    }

    /**
     * Create a new OMGraphic, encased in a new EditableOMGraphic that can
     * modify it. If a loader cannot be found that can handle a graphic with the
     * given classname, this method will return a null object. This method gives
     * you the option of supressing the GUI for the EditableOMGraphic. If you
     * aren't sure of the behavior mask set in the tool, and you want a
     * particular behavior, set it before calling this method.
     *
     * @param classname the classname of the graphic to create.
     * @param ga GraphicAttributes object that contains more information about
     *        the type of line to be created.
     * @param requestor the Component that is requesting the OMGraphic. The
     *        requestor gets notified when the user is finished with the
     *        DrawingTool and the graphic is ready.
     * @param showGUI set to true (default) if a GUI showing attribute controls
     *        should be displayed. The behaviorMask will be adjusted
     *        accordingly.
     * @return OMGraphic of the classname given, null if the DrawingTool can't
     *         create it.
     */
    public OMGraphic create(String classname, GraphicAttributes ga,
                            DrawingToolRequestor requestor, boolean showGUI) {

        if (getCurrentEditable() != null) {
            if (DEBUG) {
                Debug.output("OMDrawingTool.edit(): can't create " + classname
                        + ", drawing tool busy with another graphic.");
            }
            return null;
        }

        if (DEBUG) {
            Debug.output("OMDrawingTool.create(" + classname + ")");
        }

        if (showGUI) {
            if (DEBUG)
                Debug.output("OMDrawingTool.create(): showing GUI per request");
            setMask(SHOW_GUI_BEHAVIOR_MASK);
        } else {
            if (DEBUG)
                Debug.output("OMDrawingTool.create(): NOT showing GUI per request");
            unsetMask(SHOW_GUI_BEHAVIOR_MASK);
        }

        EditableOMGraphic eomg = getEditableGraphic(classname, ga);

        if (eomg == null || eomg.getGraphic() == null) {
            return null;
        }

        setAttributes(ga);
        eomg.setShowGUI(isMask(SHOW_GUI_BEHAVIOR_MASK));
        eomg.setActionMask(OMGraphic.ADD_GRAPHIC_MASK);

        return edit(eomg, requestor);
    }

    /**
     * Given an OMGraphic, wrap it in the applicable EditableOMGraphic, allow
     * the user to make modifications, and then call
     * requestor.drawingComplete(). If you aren't sure of the behavior mask set
     * in the tool, and you want a particular behavior, set it before calling
     * this method.
     *
     * @param g OMGraphic to modify
     * @param requestor the Component that is requesting the OMGraphic. The
     *        requestor gets notified when the user is finished with the
     *        DrawingTool and the graphic is ready.
     * @return OMGraphic being modified, null if the OMDrawingTool can't figure
     *         out what to use for the modifications.
     */
    public OMGraphic edit(OMGraphic g, DrawingToolRequestor requestor) {
        return edit(g, requestor, g.getShowEditablePalette());
    }

    /**
     * Given an OMGraphic, wrap it in the applicable EditableOMGraphic, allow
     * the user to make modifications, and then call
     * requestor.drawingComplete(). This methods gives you the option to supress
     * the GUI from the EditableOMGraphic. If you aren't sure of the behavior
     * mask set in the tool, and you want a particular behavior, set it before
     * calling this method.
     *
     * @param g OMGraphic to modify
     * @param requestor the Component that is requesting the OMGraphic. The
     *        requestor gets notified when the user is finished with the
     *        DrawingTool and the graphic is ready.
     * @param showGUI set to true (default) if a GUI showing attribute controls
     *        should be displayed. The behaviorMask will be adjusted
     *        accordingly.
     * @return OMGraphic being modified, null if the OMDrawingTool can't figure
     *         out what to use for the modifications.
     */
    public OMGraphic edit(OMGraphic g, DrawingToolRequestor requestor,
                          boolean showGUI) {

        if (g == null) {
            if (DEBUG) {
                Debug.output("OMDrawingTool.edit(): can't edit null OMGraphic.");
            }
            return null;
        }

        if (getCurrentEditable() != null) {
            if (DEBUG) {
                Debug.output("OMDrawingTool.edit(): can't edit "
                        + g.getClass().getName()
                        + ", drawing tool busy with another graphic.");
            }
            return null;
        }

        this.requestor = requestor;

        if (showGUI) {
            if (DEBUG)
                Debug.output("OMDrawingTool.edit(): showing GUI per request");
            setMask(SHOW_GUI_BEHAVIOR_MASK);
        } else {
            if (DEBUG)
                Debug.output("OMDrawingTool.edit(): NOT showing GUI per request");
            unsetMask(SHOW_GUI_BEHAVIOR_MASK);
        }

        EditableOMGraphic eomg = getEditableGraphic(g);

        if (eomg != null) {
            eomg.setShowGUI(isMask(SHOW_GUI_BEHAVIOR_MASK));
            eomg.setActionMask(OMGraphic.UPDATE_GRAPHIC_MASK);
            return edit(eomg, requestor);
        }

        return null;
    }

    /**
     * Given an EditableOMGraphic, use it to make modifications, and then call
     * requestor.drawingComplete(). The requestor is responsible for setting up
     * the correct initial state of the EditableOMGraphic. The requestor will be
     * given the action mask that is set in the EditableOMGraphic at this point,
     * if no other external modifications to it are made. If you aren't sure of
     * the behavior mask set in the tool, and you want a particular behavior,
     * set it before calling this method.
     *
     * This method is called by other edit methods.
     *
     * @param eomg OMGraphic to modify
     * @param requestor the Component that is requesting the OMGraphic. The
     *        requestor gets notified when the user is finished with the
     *        DrawingTool and the graphic is ready.
     * @return OMGraphic being modified contained within the EditableOMGraphic.
     */
    public OMGraphic edit(EditableOMGraphic eomg, DrawingToolRequestor requestor) {

        if (setCurrentEditable(eomg)) {

            // resetGUI() for current EOMG doesn't need to be called
            // here, it's called later from activate

            if (DEBUG) {
                Debug.output("OMDrawingTool.edit success");
            }

            this.requestor = requestor;

            if (currentEditable != null) {
                graphicAttributes.setFrom(currentEditable.getGraphic());
                activate();

                // Check currentEditable in case activating caused
                // something strange to happen, most likely with
                // activating the MouseModes.
                if (currentEditable != null) {
                    return currentEditable.getGraphic();
                }
            }
        }

        if (DEBUG) {
            Debug.output("OMDrawingTool.edit(): can't edit "
                    + eomg.getClass().getName()
                    + ", drawing tool busy with another graphic.");
        }

        return null;
    }

    /**
     * A slightly different edit method, where the EditableOMGraphic is put
     * directly into edit mode, and the mouse events immediately start making
     * modifications to the OMGraphic. The palette is not shown, but if you set
     * the GUI_VIA_POPUP_BEHAVIOR_MASK on the OMDrawingTool, the option to bring
     * up the drawing tool palette will be presented to the user. If you aren't
     * sure of the behavior mask set in the tool, and you want a particular
     * behavior, set it before calling this method.
     *
     * @param g OMGraphic to modify
     * @param requestor the Component that is requesting the OMGraphic. The
     *        requestor gets notified when the user is finished with the
     *        DrawingTool and the graphic is ready.
     * @param e MouseEvent to use to start editing with.
     * @return OMGraphic being modified.
     */
    public OMGraphic edit(OMGraphic g, DrawingToolRequestor requestor,
                          MouseEvent e) {

        OMGraphic ret = null;

        if (getCurrentEditable() == null) {
            EditableOMGraphic eomg = getEditableGraphic(g);
            if (eomg != null) {
                ret = edit(eomg, requestor, e);
            }
        }

        return ret;
    }

    /**
     * A slightly different edit method, where the EditableOMGraphic is put
     * directly into edit mode, and the mouse events immediately start making
     * modifications to the OMGraphic. If you aren't sure of the behavior mask
     * set in the tool, and you want a particular behavior, set it before
     * calling this method.
     *
     * @param eomg EditableOMGraphic to modify
     * @param requestor the Component that is requesting the OMGraphic. The
     *        requestor gets notified when the user is finished with the
     *        DrawingTool and the graphic is ready.
     * @param e MouseEvent to use to start editing with.
     * @return OMGraphic being modified contained within the EditableOMGraphic.
     */
    public OMGraphic edit(EditableOMGraphic eomg,
                          DrawingToolRequestor requestor, MouseEvent e) {

        OMGraphic ret = null;
        if (eomg != null) {
            eomg.setActionMask(OMGraphic.UPDATE_GRAPHIC_MASK);

            ret = edit(eomg, requestor);

            if (ret != null) {
                currentEditable.handleInitialMouseEvent(e);
            }
        }

        return ret;
    }

    /**
     * Returns true of the OMGraphic is being edited, or is on an
     * EditableOMGraphicList being manipulated.
     */
    public boolean isEditing(OMGraphic omg) {
        boolean ret = false;
        EditableOMGraphic eomg = getCurrentEditable();
        if (eomg != null
                && eomg.getGraphic() == omg
                || (eomg instanceof EditableOMGraphicList && ((OMGraphicList) ((EditableOMGraphicList) eomg).getGraphic()).contains(omg))) {
            ret = true;
        }
        return ret;
    }

    public void deselect(OMGraphic omg) {
        if (DEBUG) {
            Debug.output("OMDrawingTool.deselect()");
        }

        if (getCurrentEditable() != null) {
            if (currentEditable.getGraphic() == omg) {
                deactivate();
            } else {
                if (currentEditable instanceof EditableOMGraphicList) {
                    ((EditableOMGraphicList) currentEditable).remove(omg);
                    canvas.repaint();
                }
            }
        }
    }

    /**
     * @return true if the OMDrawingTool is editing where it wasn't before.
     */
    public boolean select(OMGraphic omg, DrawingToolRequestor req, MouseEvent e) {

        if (DEBUG) {
            Debug.output("OMDrawingTool.select()");
        }

        OMGraphic ret = null;
        boolean currentlyEditing = (getCurrentEditable() != null);
        if (currentlyEditing) {
            boolean repaintCanvas = true;
            if (!(currentEditable instanceof EditableOMGraphicList)) {
                if (DEBUG) {
                    Debug.output("OMDrawingTool.select:  already working on OMGraphic, creating an EditableOMGraphicList for selection mode");
                }

                EditableOMGraphicList eomgl = new EditableOMGraphicList(new OMGraphicList());
                eomgl.setProjection(getProjection());
                DrawingToolRequestorList rl = new DrawingToolRequestorList();
                // Add what's current to the requestor list
                rl.add(currentEditable.getGraphic(), requestor);
                // then add the current editable to the eomgl
                eomgl.add(currentEditable);
                currentEditable.removeEOMGListener(this);

                // tell selectionlisteners to disregard the current
                // thing.
                setCurrentEditable(null);
                // reset the requestor to the requestor list
                requestor = rl;
                // now reactivate with the eomgl
                setCurrentEditable(eomgl);

                if (DEBUG) {
                    EditableOMGraphic ce = getCurrentEditable();
                    Debug.output("OMDrawingTool: current editable is: "
                            + (ce == null ? "null" : ce.getClass().getName()));
                }
                // Activate the list to make sure the listeners are
                // set up
                // so the map gets repainted with the new EOMG in
                // selected mode
                activate(false);

                // Don't need to repaint if we call activate()
                repaintCanvas = false;
            } else {
                // We already have an EditableOMGraphicList, just add
                // the new stuff to it.

                if (DEBUG) {
                    Debug.output("OMDrawingTool.select:  already working on EditableOMGraphicList");
                }
            }

            // OK, even if we've just created the new EOMGL and added
            // a previous OMG to it, we still need to deal with the
            // OMG that has just been added into the method.
            ((EditableOMGraphicList) currentEditable).add(omg, this);

            if (requestor instanceof DrawingToolRequestorList) {
                ((DrawingToolRequestorList) requestor).add(omg, req);
            } else {
                Debug.error("OHHHH, THE HORRORS!");
                Thread.dumpStack();
            }

            // OK, make the EditableOMGraphic list react to the new
            // mouse event, which will also set the state machine,
            // which will flow to the new EditableOMGraphic.
            ((EditableOMGraphicList) currentEditable).handleInitialMouseEvent(e);

            // Make sure the list is returned.
            ret = currentEditable.getGraphic();

            // Only need to call canvas.repaint() if activate isn't
            // called, and this is where that will happen. This makes
            // the grab points show up on the new OMGraphic.
            if (repaintCanvas && canvas != null) {
                canvas.repaint();
            }
        } else {
            if (DEBUG) {
                Debug.output("OMDrawingTool.select:  activating for: "
                        + omg.getClass().getName());
            }

            // Since this is the first OMG in the tool at this point,
            // it's standard editing behavior....
            ret = edit(omg, req, e);
        }

        return ret != null;
    }

    /**
     * Given a classname, check the EditToolLoaders and create the OMGraphic it
     * represents wrapped in an EditableOMGraphic.
     *
     * @param classname the classname of an OMGraphic to create.
     * @param ga GraphicAttributes needed to initialize the OMGraphic.
     * @return EdtiableOMGraphic, or null if none of the loaders can figure out
     *         what to make.
     */
    public EditableOMGraphic getEditableGraphic(String classname,
                                                GraphicAttributes ga) {

        EditableOMGraphic eomg = null;
        EditToolLoader loader = (EditToolLoader) loaders.get(classname);
        if (loader == null) {

            if (DEBUG) {
                Debug.output("OMDrawingTool.getEditableGraphic(" + classname
                        + ") - rechecking loaders");
            }

            // The loaders may be able to instantiate objects they
            // don't want in the GUI - check to see if they can..
            for (Iterator things = loaders.values().iterator(); things.hasNext();) {
                EditToolLoader ldr = (EditToolLoader) things.next();
                eomg = ldr.getEditableGraphic(classname, ga);
                if (eomg != null) {
                    break;
                }
            }

        } else {
            eomg = loader.getEditableGraphic(classname, ga);
        }

        if (eomg instanceof EditableOMGraphicList) {
            ((EditableOMGraphicList) eomg).init(this);
        }

        return eomg;
    }

    /**
     * Given an OMGraphic, check the EditToolLoaders and wrap it in an
     * EditableOMGraphic.
     *
     * @param g the OMGraphic being wrapped.
     * @return EdtiableOMGraphic, or null if none of the loaders can figure out
     *         what to make.
     */
    public EditableOMGraphic getEditableGraphic(OMGraphic g) {

        // This is what we need to do to handle an OMGraphicList
        // handled for editing, but we still need to come up with a
        // way to handle DrawingToolRequestors.

        // if (g instanceof OMGraphicList) {
        // EditableOMGraphicList eomgl =
        // new EditableOMGraphicList((OMGraphicList)g);
        // eomgl.init(this);
        // return eomgl;
        // }

        Set keys = loaders.keySet();
        Iterator iterator = keys.iterator();
        while (iterator.hasNext()) {
            String key = (String) iterator.next();
            if (DEBUG)
                Debug.output("OMDrawingTool.getEditableGraphic("
                        + g.getClass().getName() + "): looking at (" + key
                        + ") loader.");

            try {
                Class kc = Class.forName(key);
                Class gc = g.getClass();
                if (kc == gc || kc.isAssignableFrom(gc)) {
                    EditToolLoader loader = (EditToolLoader) loaders.get(key);

                    if (loader == null) {
                        return null;
                    }

                    // There is a reason why the generation of the
                    // graphic is done here. I think it has to do
                    // with something with the creation of the
                    // EditableOMGraphic and its display with the
                    // GrabPoints.
                    generateOMGraphic(g);

                    EditableOMGraphic eomg = loader.getEditableGraphic(g);

                    if (DEBUG)
                        Debug.output("OMDrawingTool.getEditableGraphic("
                                + g.getClass().getName() + "): found one.");

                    return eomg;
                }
            } catch (ClassNotFoundException cnfe) {
                if (DEBUG) {
                    Debug.output("OMDrawingTool.getEditableGraphic("
                            + g.getClass().getName()
                            + ") comparision couldn't find class for " + key);
                }
            }
        }
        return null;
    }

    /**
     * Return true if the OMDrawingTool can edit the OMGraphic. Meant to be a
     * low-cost check, with a minimal allocation of memory.
     */
    public boolean canEdit(Class omgc) {
        Iterator iterator;
        if (possibleEditableClasses == null) {
            Set keys = loaders.keySet();
            possibleEditableClasses = new Vector(keys.size());
            iterator = keys.iterator();
            while (iterator.hasNext()) {
                String key = (String) iterator.next();
                try {
                    possibleEditableClasses.add(Class.forName(key));
                } catch (ClassNotFoundException cnfe) {
                    // Don't worry about this now...
                }
            }
        }

        iterator = possibleEditableClasses.iterator();
        while (iterator.hasNext()) {
            Class kc = (Class) iterator.next();
            if (kc == omgc || kc.isAssignableFrom(omgc)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Set the EditableOMGraphic being used, if it hasn't already been set. You
     * can set it to null all the time. This method triggers the selection
     * listeners.
     */
    public synchronized boolean setCurrentEditable(EditableOMGraphic eomg) {

        if (currentEditable == null || eomg == null) {
            // Moved here so that currentEditable is set when the
            // events are fired, in case someone want's to know when
            // an OMGraphic has been selected.
            currentEditable = eomg;

            if (selectionSupport != null) {
                if (eomg == null && currentEditable != null) {
                    // No longer being edited.
                    selectionSupport.fireSelection(currentEditable.getGraphic(),
                            requestor,
                            false);
                } else if (eomg != null) {
                    // Starting to be edited.
                    selectionSupport.fireSelection(eomg.getGraphic(),
                            requestor,
                            true);
                } // else all is null, ignore...
            }

            if (currentEditable != null) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get the current EditableOMGraphic being used by the drawing tool. Could
     * be null if nothing valid is happening, i.e. if the OMDrawingTool isn't
     * actively editing something.
     */
    public EditableOMGraphic getCurrentEditable() {
        return currentEditable;
    }

    /**
     * If you need your OMDrawingToolMouseMode to do something a little
     * different, you can substitude your subclass here. Don't set this to null.
     */
    public void setMouseMode(OMDrawingToolMouseMode adtmm) {
        dtmm = adtmm;
    }

    /**
     * If you want to run the drawing tool in passive mode, you'll need a handle
     * on the mouseMode to feed events to.
     */
    public OMDrawingToolMouseMode getMouseMode() {
        return dtmm;
    }

    /**
     * Add an EditToolLoader to the Hashtable of loaders that the OMDrawingTool
     * can use to create/modify OMGraphics.
     */
    public void addLoader(EditToolLoader loader) {
        String[] classnames = loader.getEditableClasses();
        rawLoaders.add(loader);
        // Add the loader to the hashtable, with the classnames as
        // keys. Then, when we get a request for a classname, we do
        // a lookup and get the proper loader for the key.
        if (classnames != null) {
            for (int i = 0; i < classnames.length; i++) {
                loaders.put(classnames[i].intern(), loader);
            }
            possibleEditableClasses = null;
        }
        firePropertyChange(LoadersProperty, null, rawLoaders);
    }

    /**
     * Make sure that new property change listeners receive a current list of
     * edit tool loaders.
     */
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        if (listener != null) {
            super.addPropertyChangeListener(listener);
            listener.propertyChange(new PropertyChangeEvent(this, LoadersProperty, null, rawLoaders));
        }
    }

    /**
     * Remove an EditToolLoader from the Hashtable of loaders that the
     * OMDrawingTool can use to create/modify OMGraphics.
     */
    public void removeLoader(EditToolLoader loader) {
        String[] classnames = loader.getEditableClasses();

        // Remove the loader to the hashtable, with the classnames as
        // keys.
        if (classnames != null) {
            for (int i = 0; i < classnames.length; i++) {
                EditToolLoader etl = (EditToolLoader) loaders.get(classnames[i].intern());
                if (etl == loader) {
                    loaders.remove(classnames[i]);
                } else {
                    if (DEBUG) {
                        Debug.output("DrawingTool.removeLoader: loader to be removed isn't the current loader for "
                                + classnames[i] + ", ignored.");
                    }
                }
            }
            rawLoaders.remove(loader);
            firePropertyChange(LoadersProperty, null, rawLoaders);
            possibleEditableClasses = null;
        }
    }

    /**
     * Get all the loaders the OMDrawingTool has access to.
     */
    public EditToolLoader[] getLoaders() {

        Set keys = loaders.keySet();
        EditToolLoader[] etls = new EditToolLoader[keys.size()];

        Iterator iterator = keys.iterator();
        int count = 0;
        while (iterator.hasNext()) {
            etls[count++] = (EditToolLoader) loaders.get(iterator.next());
        }
        return etls;
    }

    /**
     * Set the loaders that the OMDrawingTool has access to.
     */
    public void setLoaders(EditToolLoader[] etls) {
        loaders.clear();
        rawLoaders.clear();
        for (int i = 0; i < etls.length; i++) {
            addLoader(etls[i]);
        }
    }

    public void resetGUIWhenDeactivated(boolean value) {
        resetGUIWhenDeactivated = value;
    }

    /**
     * Get the GUI that dictates what the OMDrawingTool has control over. This
     * should include a section on controlling the GraphicAttributes, a section
     * for controls provided by the current EditableOMGraphic for parameters
     * unique to the EOMG, and any other controls that the tool may need. This
     * method now returns this OMDrawingTool, but also serves as a reset method
     * for the GUI to configure itself for the current EditableOMGraphic.
     * <P>
     *
     * To create different types of graphics, the OMDrawingToolMouseMode can be
     * used, to attach to a layer to make it a drawing layer. The Loaders can be
     * queried to get their trigger graphics so you can load the drawing tool
     * with a particular loader to create a particular graphic. But here, we
     * just deal with the actual controls over the particular graphic loaded and
     * being modified.
     * <P>
     *
     * @return this.
     */
    public Component getGUI() {
        if (!resetGUIWhenDeactivated)
            return this;

        removeAll();
        Component eomgc = null;

        graphicAttributes.setLineMenuAdditions(null);
//        graphicAttributes.setOrientation(getOrientation());
        if (currentEditable != null) {
            // GUI specific to a particular EditableOMGraphic type.
            eomgc = currentEditable.getGUI(graphicAttributes);
            if (eomgc != null) {
                add(eomgc);
            }
        }

        // Basic, generic GUI if eomgc wasn't set.
        if (eomgc == null) {
            add(graphicAttributes.getGUI());
        }

        revalidate();
        return this;
    }

    public void setInformationDelegator(InformationDelegator id) {
        informationDelegator = id;
    }

    public InformationDelegator getInformationDelegator() {
        return informationDelegator;
    }

    /**
     * Put the message in a display line that the OMDrawingTool is using.
     */
    public void setRemarks(String message) {
        if (informationDelegator != null) {
            informationDelegator.displayInfoLine(message,
                    InformationDelegator.MAP_OBJECT_INFO_LINE);
        }
    }

    /**
     * Convenience function to tell if the OMDrawingTool is currently working on
     * an OMGraphic.
     */
    public boolean isActivated() {
        return activated;
    }

    /**
     * Turn the OMDrawingTool on, attaching it to the MouseDelegator or the
     * canvas component it is assigned to. Also brings up the drawing palette.
     * Called automatically from the create/edit methods.
     */
    protected synchronized void activate() {
        activate(true);
    }

    /**
     * Turn the OMDrawingTool on with the caveaut that the OMDrawingTool may be
     * active already, and that a complete hookup may not be needed. If a
     * complete hookup is needed, this methid will attach the OMDrawingTool to
     * the MouseDelegator or the canvas component it is assigned to and display
     * the drawing palette. Called automatically from the create/edit methods
     * for complete hookup. Partial hookup is called from select() methods.
     */
    protected synchronized void activate(boolean completeHookup) {
        activated = true;

        if (DEBUG)
            Debug.output("OMDrawingTool: activate()");
        if (currentEditable != null && graphicAttributes != null) {
            // For partial hookups, for select() we don't need this.
            if (completeHookup) {
                graphicAttributes.setTo(currentEditable.getGraphic());
                currentEditable.getGraphic().setVisible(false);
            }
            currentEditable.addEOMGListener(this);
        }

        if (!isMask(PASSIVE_MOUSE_EVENT_BEHAVIOR_MASK) && completeHookup) {
            if (mouseDelegator != null) {
                if (Debug.debugging("drawingtooldetail")) {
                    Debug.output("OMDrawingTool.activate() mousemode connecting to MouseDelegator");
                }
                formerMouseMode = mouseDelegator.getActiveMouseMode();
                mouseDelegator.setActiveMouseMode(dtmm);

            } else if (canvas != null) {
                // If a MouseDelegator is not being used, go directly
                // to
                // the MapBean.
                if (Debug.debugging("drawingtooldetail")) {
                    Debug.output("OMDrawingTool.activate() mousemode connecting directly to canvas");
                }
                canvas.addMouseListener(dtmm);
                canvas.addMouseMotionListener(dtmm);
            } else {
                Debug.error("Drawing Tool can't find a map to work with");
            }
        }

        // The Drawing tool is added as a projection listener so that
        // it can properly update the current graphic if the map
        // projection changes during graphic creation/edit.

        if (canvas != null) {
            if (canvas instanceof MapBean && completeHookup) {
                ((MapBean) canvas).addPaintListener(this);
                ((MapBean) canvas).addProjectionListener(this);
            }
            // Gets the graphic highlighted on the map, if needed.
            canvas.repaint();
        }

        if (completeHookup) {
            // Show the gui.
            showPalette();
        }
    }

    /**
     * Turn the drawing tool off, disconnecting it from the MouseDelegator or
     * canvas component, and removing the palette. Called automatically from the
     * mouse mode an GUI when appropriate, although you can force a cleanup if
     * needed by calling this method. Calling this version of deactivate() just
     * uses the action mask stored in the EditableOMGraphic, which knows if the
     * graphic is being updated or created.
     */
    public void deactivate() {
        int actionMask = 0;
        if (currentEditable != null) {
            actionMask = currentEditable.getActionMask();
        }
        deactivate(actionMask);
    }

    /**
     * Turn the drawing tool off, disconnecting it from the MouseDelegator or
     * canvas component, and removing the palette. This version can called when
     * you want to control what action is taken by the receiver.
     *
     * @param actionToDoWithOMGraphic a masked int from OMGraphicConstants that
     *        describes an OMAction to take on the current editable.
     * @see com.bbn.openmap.omGraphics.OMGraphicConstants
     */
    public synchronized void deactivate(int actionToDoWithOMGraphic) {
        if (DEBUG) {
            Debug.output("OMDrawingTool: deactivate("
                    + (activated ? "while active" : "while inactive") + ")");
        }

        // Don't waste effort;
        if (!activated)
            return;

        if (!isMask(PASSIVE_MOUSE_EVENT_BEHAVIOR_MASK)) {
            if (mouseDelegator != null) {
                mouseDelegator.setActiveMouseMode(formerMouseMode);
                mouseDelegator.removeMouseMode(dtmm);
            } else if (canvas != null) {
                // If a MouseDelegator is not being used, go directly
                // to
                // the canvas.
                canvas.removeMouseListener(dtmm);
                canvas.removeMouseMotionListener(dtmm);
            }
        }

        if (canvas != null) {
            if (canvas instanceof MapBean) {
                ((MapBean) canvas).removeProjectionListener(this);
                ((MapBean) canvas).removePaintListener(this);
            }
        }

        OMGraphic g = null;

        if (currentEditable != null) {
            if (!(currentEditable.getStateMachine().getState() instanceof com.bbn.openmap.omGraphics.editable.GraphicUndefinedState)) {
                g = currentEditable.getGraphic();
            }
            currentEditable.removeEOMGListener(this);
        }

        // ////////////////////////////////
        // Clean up, then notify listener

        setCurrentEditable(null);
        // hide the gui while currentEditable is null, so it resets to
        // the default.
        hidePalette();
        unsetMask(DEACTIVATE_ASAP_BEHAVIOR_MASK);
        popup = null;
        activated = false;

        // End cleanup
        // ////////////////////////////////

        if (g != null && requestor != null) {
            g.setVisible(true);
            OMAction action = new OMAction();
            action.setMask(actionToDoWithOMGraphic);
            generateOMGraphic(g);
            notifyListener(g, action);
        }

        // By putting this here, it gives the listener the slight
        // opportunity to not have the gui reset right away. This
        // opportunity gives an editor tool a smoother runtime when
        // duplicate objects are being created one after another, and
        // you don't want all the GUI reconfiguring to happen when it
        // will just go back to the same thing in a second.
        getGUI();

    }

    /**
     * If the projection is not null, generate the OMGraphic.
     */
    protected void generateOMGraphic(OMGraphic g) {
        if (g != null && g.getNeedToRegenerate()) {
            Projection proj = getProjection();
            if (proj != null) {
                g.generate(proj);
            } else if (DEBUG) {
                Debug.output("OMDrawingTool: graphic needs generation: "
                        + g.getNeedToRegenerate());
            }
        }
    }

    /**
     * Notify the listener of an action to a graphic.
     *
     * @param graphic the graphic being created/modified
     * @param action the OMAction telling the listener what to do with the
     *        graphic.
     */
    public void notifyListener(OMGraphic graphic, OMAction action) {
        if (requestor != null) {
            if (DEBUG)
                Debug.output("OMDrawingTool: notifying requestor, graphic with action");
            requestor.drawingComplete(graphic, action);
        }

        // in case the requestor is a layer that is not visible
        if (canvas != null) {
            canvas.repaint();
        }
    }

    /**
     * ProjectionListener method. Helps if the currentEditable is set.
     */
    public void projectionChanged(ProjectionEvent e) {
        setProjection((Projection) e.getProjection().makeClone());
    }

    /**
     * Set the current projection. Tells the currentEditable what it is too.
     */
    public void setProjection(Projection proj) {
        projection = proj;
        if (currentEditable != null) {
            currentEditable.setProjection(projection);
        }
    }

    /**
     * Get the current projection, if one has been provided. If one has not been
     * provided, then the canvas is checked to see if it is a MapBean. If it is,
     * then that projection is returned. If that doesn't work, it will finally
     * return null.
     */
    public Projection getProjection() {
        if (projection == null && canvas instanceof MapBean) {
            projection = ((MapBean) canvas).getProjection();
        }
        return projection;
    }

    /**
     * Set the GraphicAttributes object used to fill the OMGraphic
     * java.awt.Graphics parameters.
     */
    public void setAttributes(GraphicAttributes da) {
        if (graphicAttributes != null) {
            graphicAttributes.getPropertyChangeSupport()
                    .removePropertyChangeListener(this);
        }

        if (da == null) {
            graphicAttributes = GraphicAttributes.DEFAULT;
        } else {
            graphicAttributes = da;
        }

        graphicAttributes.getPropertyChangeSupport()
                .addPropertyChangeListener(this);

        if (currentEditable != null) {
            graphicAttributes.setTo(currentEditable.getGraphic());
        }
    }

    /**
     * Get the DrawingAttributes driving the parameters of the current graphic.
     */
    public GraphicAttributes getAttributes() {
        return graphicAttributes;
    }

    /**
     * PaintListener interface. We want to know when the canvas is repainted.
     *
     * @param g the Graphics to draw into.
     */
    public void listenerPaint(Graphics g) {
        // Call repaintRender here because if the graphic is in the
        // middle of being moved, we'll draw it in the mouse event
        // thread. Otherwise, it gets set in the image for the
        // background, which looks bad.
        if (currentEditable != null) {
            // do g.create() to prevent Stroke remnants from affecting
            // the Border of the canvas.
            currentEditable.repaintRender(g.create());
        }
    }

    /**
     * Set the MouseDelegator used to receive mouse events.
     */
    public void setMouseDelegator(MouseDelegator md) {
        mouseDelegator = md;
    }

    /**
     * Get the MouseDelegator used to receive mouse events.
     */
    public MouseDelegator getMouseDelegator() {
        return mouseDelegator;
    }

    public void setCursor(java.awt.Cursor cursor) {
        if (canvas != null) {
            canvas.setCursor(cursor);
        }
    }

    public java.awt.Cursor getCursor() {
        if (canvas != null) {
            return canvas.getCursor();
        } else {
            return null;
        }
    }

    /**
     * Set the JComponent this thing is directing events for. If the
     * MouseDelegator is not set, the Canvas is contacted to get MouseEvents
     * from. Within the BeanContext, the OMDrawingTool looks for MapBeans to use
     * as canvases.
     */
    public void setCanvas(JComponent can) {
        canvas = can;
    }

    /**
     * Get the JComponent this thing is directing events for.
     */
    public JComponent getCanvas() {
        return canvas;
    }

    /**
     * Set whether the Tool's face should be used. The subclasses to this class
     * should either remove all components from its face, or make its face
     * invisible if this is set to false.
     */
    public void setUseAsTool(boolean value) {
        super.setUseAsTool(value);
    }

    /**
     * Called from the findAndInit(Iterator) method, when objects are added to
     * the MapHandler. so the OMDrawingTool can hook up with what it needs. An
     * InformationDelegator is used to provide map coordinates of the mouse
     * movements. The MouseDelegator is used to intercept MouseEvents when the
     * OMDrawingTool is activated. The MapBean is used to get mouse events if
     * the MouseDelegator isn't loaded, and is also used to help out with smooth
     * repaints() in general. EditToolLoaders are looked for to load into the
     * OMDrawingTool to handler different graphic requests.
     */
    public void findAndInit(Object someObj) {

        if (someObj instanceof InformationDelegator) {
            if (DEBUG)
                Debug.output("DrawingTool: found InformationDelegator");
            if (dtmm != null) {
                dtmm.setInfoDelegator((InformationDelegator) someObj);
            }
            setInformationDelegator((InformationDelegator) someObj);
        }
        if (someObj instanceof MouseDelegator) {
            if (DEBUG)
                Debug.output("DrawingTool: found MouseDelegator.");
            setMouseDelegator((MouseDelegator) someObj);
        }
        if (someObj instanceof MapBean) {
            if (DEBUG)
                Debug.output("DrawingTool: found MapBean.");
            setCanvas((JComponent) someObj);
        }
        if (someObj instanceof EditToolLoader) {
            if (DEBUG)
                Debug.output("DrawingTool: found EditToolLoader: "
                        + someObj.getClass().getName());
            addLoader((EditToolLoader) someObj);
        }
    }

    /**
     * Called by childrenRemoved, it provides a good method for handling any
     * object you may want to take away from the OMDrawingTool. The
     * OMDrawingTool figures out if it should disconnect itseld from the object.
     */
    public void findAndUndo(Object someObj) {
        if (someObj == getInformationDelegator()) {
            if (dtmm != null
                    && dtmm.getInfoDelegator() == (InformationDelegator) someObj) {
                dtmm.setInfoDelegator(null);
            }
            setInformationDelegator(null);
        }
        if (someObj == getMouseDelegator()) {
            setMouseDelegator(null);
        }
        if (someObj == getCanvas()) {
            setCanvas(null);
        }
        if (someObj instanceof EditToolLoader) {
            removeLoader((EditToolLoader) someObj);
        }
    }

    // ////////////// end BeanContext stuff

    /**
     * Display the palette.
     */
    public void showPalette() {
        Debug.message("drawingtool", "OMDrawingTool.showPalette()");
        resetGUIWhenDeactivated = true;
        getGUI(); // resets the gui.

        /*
         * This repaint needs to be here because sometimes the GUI doesn't
         * update on the revalidate being called in the GUI.
         */
        repaint();

        // Should only be visible if the tool isn't being used as a
        // tool, which means that it's being held by something else,
        // or if it is a tool and the SHOW_GUI flag is set.
        boolean shouldBeVisible = !getUseAsTool()
                || (isMask(SHOW_GUI_BEHAVIOR_MASK) && getUseAsTool());

        setVisible(shouldBeVisible);
    }

    /**
     * Hide the OMDrawingTool palette.
     */
    public void hidePalette() {
        Debug.message("drawingtool", "OMDrawingTool.hidePalette()");

        setVisible(visibleWhenInactive);

        WindowSupport ws = getWindowSupport();
        if (ws != null) {
            ws.killWindow();
        }
    }

    public void showInWindow() {
        if (!getUseAsTool() && getWindowSupport() == null) {
            setWindowSupport(new WindowSupport(getGUI(), i18n.get(OMDrawingTool.class,
                    "drawingtool",
                    "Drawing Tool")));
        }

        WindowSupport ws = getWindowSupport();

        if (ws != null && !getUseAsTool()) {
            MapHandler mh = (MapHandler) getBeanContext();
            Frame frame = null;
            int xoffset = 0;
            int yoffset = 0;
            if (mh != null) {
                frame = (Frame) mh.get(java.awt.Frame.class);
                if (frame != null) {
                    xoffset = frame.getX();
                    yoffset = frame.getY();
                }
            }

            ws.displayInWindow(frame, WindowSupport.Dlg.class,
                    windowx + xoffset,
                    windowy + yoffset,
                    -1,
                    -1);
        } else {
            Debug.output("OMDrawingTool.showPalette(): NOT showing palette, ws == null:"
                    + (ws == null) + ", used as tool:" + getUseAsTool());
        }
    }

    /**
     * A integer that is looked at internally, bitwise, to determine different
     * behaviors. If you care about specific behavior of the DrawingTool, you
     * should set this to what you want to make sure the tool acts the way you
     * want.
     */
    public void setBehaviorMask(int mask) {
        behaviorMask = mask;
    }

    /**
     * A integer that is looked at internally, bitwise, to determine different
     * behaviors.
     */
    public int getBehaviorMask() {
        return behaviorMask;
    }

    /**
     * Set the behavior mask to the default.
     */
    public void resetBehaviorMask() {
        behaviorMask = DEFAULT_BEHAVIOR_MASK;
    }

    /**
     * Set a particular mask bit in the internal value.
     *
     * @param mask an OMDrawingTool behavior mask.
     * @return the changed integer value.
     */
    public int setMask(int mask) {
        behaviorMask = OMAction.setMask(behaviorMask, mask);
        return behaviorMask;
    }

    /**
     * Unset a particular mask bit in the internal value.
     *
     * @param mask an OMDrawingTool behavior mask.
     * @return the changed integer value.
     */
    public int unsetMask(int mask) {
        behaviorMask = OMAction.unsetMask(behaviorMask, mask);
        return behaviorMask;
    }

    /**
     * Return whether a mask value is set in the internal value.
     *
     * @param mask an OMDrawingTool behavior mask.
     * @return whether the value bit is set on the internal value.
     */
    public boolean isMask(int mask) {
        return OMAction.isMask(behaviorMask, mask);
    }

    /**
     * PropertyChangeListener method. If DrawingAttribute parameters change,
     * this method is called, and we update the OMGraphic parameters.
     */
    public void propertyChange(PropertyChangeEvent pce) {
        Object source = pce.getSource();
        if (source instanceof DrawingAttributes && currentEditable != null) {
            graphicAttributes.setTo(currentEditable.getGraphic());

            if (projection != null) {
                currentEditable.regenerate(projection);
            }

            if (canvas != null) {
                canvas.repaint();
            }
        }
    }

    /**
     * Used to hold the last thing displayed to the remarks window.
     */
    String lastRemarks = "";
    JPopupMenu popup = null;
    int windowx, windowy;

    /**
     * This is a EOMGListener method, and gets called by the EditableOMGraphic
     * when something changes.
     */
    public void eomgChanged(EOMGEvent event) {
        if (Debug.debugging("drawingtooldetail")) {
            Debug.output("OMDrawingTool.eomgChanged()");
        }

        Cursor cursor = event.getCursor();
        if (cursor != null) {
            setCursor(cursor);
        }

        // We might have used the InformationDelgator to put the
        // comments
        // in the info line, but that can't work, because we are
        // already putting the lat/lon info on the info line.

        // Updated, 4.6 - now that the InformationDelegator has new
        // places for coordinate information and map object
        // information, we can sent the info there, and it looks OK.

        String message = event.getMessage();
        if (message != null && !message.equals(lastRemarks)) {
            lastRemarks = message;
            setRemarks(message);
        }

        if (event.shouldShowGUI() && isMask(ALT_POPUP_BEHAVIOR_MASK)) {
            if (DEBUG)
                Debug.output("OMDrawingTool.eomgChanged(): try for menu.");
            MouseEvent me = event.getMouseEvent();

            // While we're here, get a good place for the window in
            // case we need to put it up later.
            if (currentEditable != null) {

                currentEditable.getStateMachine().setSelected();
                currentEditable.redraw(me, true);

                Shape ces = currentEditable.getGraphic().getShape();
                if (ces != null) {
                    Rectangle rect = ces.getBounds();
                    windowx = (int) rect.getX();
                    windowy = (int) rect.getY() - 50;
                }
            }

            /**
             * Let's see if we should bring up pop-up menu with all sorts of
             * lovely options - if the right mouse key was pressed, or if the
             * ctrl key was pressed with the mouse button being released,
             * display the option menu. Otherwise, just get ready to end.
             */
            doPopup(me.getX(), me.getY(), null);
        } else if (event.shouldDeactivate()) {
            if (DEBUG) {
                Debug.output("OMDrawingTool.eomgChanged(): omdt being told to deactivate");
            }

            if (isMask(USE_POPUP_BEHAVIOR_MASK) && !getUseAsTool()) {
                EditableOMGraphic eomg = getCurrentEditable();
                if (eomg != null) {
                    java.awt.Shape shape = eomg.getGraphic().getShape();
                    Rectangle rect = shape.getBounds();

                    Vector vec = new Vector();
                    vec.add(new JSeparator());

                    JMenuItem done = new JMenuItem("Done");
                    done.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent ae) {
                            deactivate();
                        }
                    });
                    vec.add(done);

                    if (!doPopup((int) (rect.getX() + rect.getWidth()),
                            (int) (rect.getY() + rect.getHeight()),
                            vec)) {
                        deactivate();
                    }
                }
            } else {
                deactivate();
            }
        }
    }

    protected boolean doPopup(int x, int y, java.util.List additionalOptions) {
        if (additionalOptions != null && additionalOptions.size() > 0) {
            popup = null;
        }

        boolean showPopup = (popup != null);

        if (popup == null && !getUseAsTool()) {
            popup = createPopupMenu();

            if (additionalOptions != null && additionalOptions.size() > 0
                    && popup != null) {
                for (Iterator it = additionalOptions.iterator(); it.hasNext();) {
                    Object obj = it.next();
                    if (obj instanceof JMenuItem) {
                        popup.add((JMenuItem) obj);
                    }
                }
            }

            showPopup = (popup != null);
        }

        if (showPopup) {

            JComponent map = null;
            if (mouseDelegator != null) {
                map = mouseDelegator.getMap();
            } else if (canvas != null) {
                // If a MouseDelegator is not being used, go
                // directly to the MapBean.
                map = canvas;
            }

            if (map != null && x >= 0 && y >= 0) {
                popup.show(map, x, y);
            } else {
                Debug.error("OMDrawingTool: no "
                        + (map == null ? "/component" : "/")
                        + ((x < 0 || y < 0) ? "location/" : "/")
                        + " to show popup on!");
            }
            return true;
        }

        return false;
    }

    public JPopupMenu createPopupMenu() {

        OMGraphic g = getCurrentEditable().getGraphic();
        JPopupMenu pum = new JPopupMenu();

        if ((g.getAttribute(OMGraphicConstants.CHANGE_APPEARANCE)) == null
                || ((Boolean) g.getAttribute(OMGraphicConstants.CHANGE_APPEARANCE)).booleanValue()) {

            JMenuItem gui = new JMenuItem(i18n.get(OMDrawingTool.class,
                    "popupMenuChangeAppearance",
                    "Change Appearance"));
            gui.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ae) {
                    EditableOMGraphic eomg = getCurrentEditable();
                    if (eomg != null) {
                        boolean previous = eomg.getShowGUI();
                        eomg.setShowGUI(true);
                        setVisible(true);
                        if (!getUseAsTool()) {
                            showInWindow();
                        }
                        eomg.setShowGUI(previous);
                        eomg.getStateMachine().setSelected();
                    }
                }
            });
            if (isMask(SHOW_GUI_BEHAVIOR_MASK | GUI_VIA_POPUP_BEHAVIOR_MASK)
                    && !getUseAsTool()) {
                pum.add(gui);
            } else {
                Debug.output("Not adding Change Appearance to popup: guiViaPopup("
                        + isMask(SHOW_GUI_BEHAVIOR_MASK)
                        + ") isTool("
                        + getUseAsTool() + ")");
            }
        }

        if ((g.getAttribute(OMGraphicConstants.REMOVABLE)) == null
                || ((Boolean) g.getAttribute(OMGraphicConstants.REMOVABLE)).booleanValue()) {

            JMenuItem delete = new JMenuItem(i18n.get(OMDrawingTool.class,
                    "popupMenuDelete",
                    "Delete"));
            delete.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ae) {
                    OMAction action = new OMAction();
                    action.setMask(OMGraphic.DELETE_GRAPHIC_MASK);
                    EditableOMGraphic eomg = getCurrentEditable();
                    if (eomg != null) {
                        OMGraphic g = eomg.getGraphic();
                        if (g != null) {
                            notifyListener(g, action);
                        }
                    }
                    setCurrentEditable(null);
                    deactivate();
                }
            });
            pum.add(delete);
        }

        // JMenuItem reset = new JMenuItem("Undo Changes");
        // reset.setEnabled(false);
        // reset.addActionListener(new ActionListener() {
        // public void actionPerformed(ActionEvent ae) {
        // if (currentEditable != null) {
        // currentEditable.reset();
        // }
        // }
        // });

        // pum.add(reset);

        return pum.getComponentCount() > 0 ? pum : null;
    }

    // ////////// SelectionListener support
    public void addSelectionListener(SelectionListener list) {
        if (selectionSupport != null)
            selectionSupport.addSelectionListener(list);
    }

    public void removeSelectionListener(SelectionListener list) {
        if (selectionSupport != null)
            selectionSupport.removeSelectionListener(list);
    }

    public void clearSelectionListeners() {
        if (selectionSupport != null)
            selectionSupport.clearSelectionListeners();
    }

    // ////////// SelectionListener support ends

    public static void main(String[] args) {
        OMDrawingTool omdt = new OMDrawingTool();
        omdt.showPalette();
    }

    public void setProperties(String prefix, Properties props) {
        super.setProperties(prefix, props);

        prefix = PropUtils.getScopedPropertyPrefix(prefix);
        visibleWhenInactive = PropUtils.booleanFromProperties(props, prefix
                + VisibleWhenInactiveProperty, visibleWhenInactive);
        getGUI();
        setVisible(visibleWhenInactive);

        String behaviorList = props.getProperty(prefix + BehaviorProperty);

        if (behaviorList != null) {
            Vector behaviorStrings = PropUtils.parseSpacedMarkers(behaviorList);
            int behavior = 0;
            for (Iterator it = behaviorStrings.iterator(); it.hasNext();) {
                String behaviorString = (String) it.next();
                try {
                    int val = OMDrawingTool.class.getField(behaviorString)
                            .getInt(null);
                    behavior |= val;
                } catch (NoSuchFieldException nsfe) {
                } catch (IllegalAccessException iae) {
                }

            }

            setMask(behavior);
        }

    }
   
    // TODO need to override getProperties to include Behavior mask settings.
}
TOP

Related Classes of com.bbn.openmap.tools.drawing.OMDrawingTool

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.