Package com.bbn.openmap.tools.dnd

Source Code of com.bbn.openmap.tools.dnd.DefaultDnDCatcher

//**********************************************************************
//
//<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/dnd/DefaultDnDCatcher.java,v $
//$RCSfile: DefaultDnDCatcher.java,v $
//$Revision: 1.4.2.2 $
//$Date: 2005/08/09 21:17:57 $
//$Author: dietrick $
//
//**********************************************************************

package com.bbn.openmap.tools.dnd;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.beancontext.BeanContext;
import java.beans.beancontext.BeanContextChild;
import java.beans.beancontext.BeanContextChildSupport;
import java.beans.beancontext.BeanContextMembershipEvent;
import java.beans.beancontext.BeanContextMembershipListener;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import javax.swing.BorderFactory;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;

import com.bbn.openmap.Layer;
import com.bbn.openmap.LayerHandler;
import com.bbn.openmap.MapBean;
import com.bbn.openmap.MouseDelegator;
import com.bbn.openmap.event.LayerEvent;
import com.bbn.openmap.event.LayerListener;
import com.bbn.openmap.event.ProjectionEvent;
import com.bbn.openmap.event.ProjectionListener;
import com.bbn.openmap.event.SelectMouseMode;
import com.bbn.openmap.layer.OMGraphicHandlerLayer;
import com.bbn.openmap.layer.location.Location;
import com.bbn.openmap.omGraphics.OMAction;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.SinkGraphic;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;

/**
* DefaultDnDCatcher manages Drag and Drop events on the map.
*
* Drag: When a mouseDragged event occurs, DropListenerSupport
* forwards it to the DefaultDnDCatcher (consume() method). If it's
* the first mouseDragged event, dragGestureRecognized is fired and
* drag starts.
*
* Drop: Each layer in the LayerHandler listens to the drop events.
* When a drop occurs, a list of potential targets (layers) is shown
* in the popup menu.
*
* DefaultDnDCatcher recognizes Location as the droppable object.
*
* DefaultDnDCatcher recognizes OMGraphicHandlerLayer layers as
* potential drop targets.
*/

public class DefaultDnDCatcher extends DnDListener implements BeanContextChild,
        BeanContextMembershipListener, PropertyChangeListener, Serializable,
        ProjectionListener, LayerListener, ActionListener {

    /**
     * PropertyChangeSupport for handling listeners.
     */
    protected PropertyChangeSupport pcSupport = new PropertyChangeSupport(this);

    /**
     * BeanContextChildSupport object provides helper functions for
     * BeanContextChild interface.
     */
    protected BeanContextChildSupport beanContextChildSupport = new BeanContextChildSupport(this);

    /**
     * Hashtable for keeping references to potential drop targets
     */
    protected Hashtable layers = new Hashtable();

    protected DragSource dragSource;

    // a reference to the MouseDelegator object in the MapHandler
    protected transient MouseDelegator md;

    // a copy of current projection
    protected transient Projection proj;

    // object that is being passed in transferable
    protected Object transferData;

    protected Point dropLocation;

    /**
     * Constructs a new DefaultDnDCatcher.
     */
    public DefaultDnDCatcher() {
        this(new DragSource());
    }

    /**
     * Constructs a new DefaultDnDCatcher given the DragSource for the
     * Component.
     *
     * @param ds the DragSource for the Component
     */
    public DefaultDnDCatcher(DragSource ds) {
        this(ds, null);
    }

    /**
     * Construct a new DefaultDnDCatcher given the DragSource for the
     * Component c, and the Component to observe.
     *
     * @param ds the DragSource for the Component c
     * @param c the Component to observe
     */
    public DefaultDnDCatcher(DragSource ds, Component c) {
        this(ds, c, DnDConstants.ACTION_NONE);
    }

    public DefaultDnDCatcher(DragSource ds, Component c, int act) {
        this(ds, c, act, null);
    }

    public DefaultDnDCatcher(DragSource ds, Component c, int act,
            DragGestureListener dgl) {
        super(ds, c, act, dgl);
        dragSource = getDragSource();
        dragGestureListener = new ComponentDragGestureListener(this, this);
        setSourceActions(DnDConstants.ACTION_MOVE);
    }

    /**
     * Invoked when an action from the popup menu occurs.
     */
    public void actionPerformed(java.awt.event.ActionEvent e) {
        Object source = e.getSource();
        if (!(source instanceof JMenuItem))
            return;

        JMenuItem mi = (JMenuItem) source;
        String name = mi.getText();
        OMGraphicHandlerLayer targetLayer = (OMGraphicHandlerLayer) layers.get(name);

        if (targetLayer == null) {
            Debug.message("defaultdndcatcher",
                    "ERROR> DefaultDnDCatcher::actionPerformed: "
                            + "no layer found with name " + name);
            return;
        }

        targetLayer.doAction((OMGraphic) transferData,
                new OMAction(OMAction.UPDATE_GRAPHIC_MASK));
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(listener);
    }

    /** Method for BeanContextChild interface. */
    public void addPropertyChangeListener(String propertyName,
                                          PropertyChangeListener in_pcl) {
        pcSupport.addPropertyChangeListener(propertyName, in_pcl);
    }

    /** Method for BeanContextChild interface. */
    public void addVetoableChangeListener(String propertyName,
                                          VetoableChangeListener in_vcl) {
        beanContextChildSupport.addVetoableChangeListener(propertyName, in_vcl);
    }

    /**
     * BeanContextMembershipListener method. Called when new objects
     * are added to the parent BeanContext.
     *
     * @param bcme event that contains an iterator that can be used to
     *        go through the new objects.
     */
    public void childrenAdded(BeanContextMembershipEvent bcme) {
        findAndInit(bcme.iterator());
    }

    /**
     * BeanContextMembershipListener method. Called when objects have
     * been removed from the parent BeanContext. The DefaultDnDCatcher
     * looks for the MapBean it is managing DnD and MouseEvents for,
     * and any layers that may be removed.
     *
     * @param bcme event that contains an iterator that can be used to
     *        go through the removed objects.
     */
    public void childrenRemoved(BeanContextMembershipEvent bcme) {
        Iterator it = bcme.iterator();
        while (it.hasNext()) {
            findAndUndo(it.next());
        }
    }

    /**
     * The method is invoked on mousePressed, mouseReleased, and
     * mouseDragged events that come from the MapBean through
     * DropListenerSupport.
     *
     * @return boolean
     * @param e java.awt.event.MouseEvent
     */
    public boolean consume(MouseEvent e) {
        if (e.getID() == MouseEvent.MOUSE_PRESSED) {
            mousePressed(e);
        } else if (e.getID() == MouseEvent.MOUSE_RELEASED) {
            mouseReleased(e);
        } else if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
            mouseDragged(e);
        }

        return false;
    }

    /**
     * The drag operation has terminated with a drop on this
     * <code>DropTarget</code>. This method is responsible for
     * undertaking the transfer of the data associated with the
     * gesture. The <code>DropTargetDropEvent</code> provides a
     * means to obtain a <code>Transferable</code> object that
     * represents the data object(s) to be transfered.
     * <P>
     * From this method, the <code>DropTargetListener</code> shall
     * accept or reject the drop via the acceptDrop(int dropAction) or
     * rejectDrop() methods of the <code>DropTargetDropEvent</code>
     * parameter.
     * <P>
     * Subsequent to acceptDrop(), but not before,
     * <code>DropTargetDropEvent</code>'s getTransferable() method
     * may be invoked, and data transfer may be performed via the
     * returned <code>Transferable</code>'s getTransferData()
     * method.
     * <P>
     * At the completion of a drop, an implementation of this method
     * is required to signal the success/failure of the drop by
     * passing an appropriate <code>boolean</code> to the
     * <code>DropTargetDropEvent</code>'s dropComplete(boolean
     * success) method.
     * <P>
     * Note: The actual processing of the data transfer is not
     * required to finish before this method returns. It may be
     * deferred until later.
     * <P>
     *
     * @param dtde the <code>DropTargetDropEvent</code>
     */
    public void drop(java.awt.dnd.DropTargetDropEvent dtde) {

        //
        // Accept the drop and get transferable object.
        //
        dtde.acceptDrop(DnDConstants.ACTION_MOVE);
        transferData = extractTransferData(dtde);
        dropLocation = extractDropLocation(dtde);
        dtde.dropComplete(true);

        if (transferData == null || dropLocation == null)
            return;

        JPopupMenu popup = new JPopupMenu();
        TitledBorder titledBorder = BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(),
                "Available Drop Targets:");

        titledBorder.setTitleColor(Color.gray);
        popup.setBorder(titledBorder);

        Border compoundborder = BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(),
                BorderFactory.createEmptyBorder(2, 2, 2, 2));

        //
        // Check whether the dropped object is of type Location
        // (has exact x and y coordinates).
        //
        if (transferData instanceof Location) {
            ((Location) transferData).setLocation(dropLocation.x,
                    dropLocation.y,
                    proj);

            OMGraphicHandlerLayer omlayer = null;
            String layer_name;
            Enumeration keys = layers.keys();

            while (keys.hasMoreElements()) {
                layer_name = keys.nextElement().toString();
                omlayer = (OMGraphicHandlerLayer) layers.get(layer_name);

                if (omlayer.isVisible()) {
                    JMenuItem menuItem = new JMenuItem(layer_name);
                    menuItem.setHorizontalTextPosition(SwingConstants.CENTER);
                    menuItem.setBorder(compoundborder);
                    menuItem.addActionListener(this);
                    popup.add(menuItem);
                }
            }

            popup.addSeparator();
        }

        JMenuItem menuItem = new JMenuItem("CANCEL");
        menuItem.setForeground(Color.red);
        menuItem.setHorizontalTextPosition(SwingConstants.CENTER);
        menuItem.setBorder(compoundborder);

        popup.add(menuItem);

        popup.setPreferredSize(new Dimension(150, (popup.getComponentCount() + 1) * 25));

        //
        // Show a popup menu of available drop targets.
        //
        popup.show(((DropTarget) dtde.getSource()).getComponent(),
                dropLocation.x,
                dropLocation.y);

    }

    /**
     * Gets the location where the drop action occured.
     */
    private Point extractDropLocation(DropTargetDropEvent dtde) {
        if (dtde == null) {
            Debug.message("defaultdndcatcher",
                    "ERROR> BDnDC::getTransferData(): dropEvent is null");
            return null;
        }
        return dtde.getLocation();
    }

    /**
     * Gets the object that is passed in transferable in
     * DropTargetDropEvent.
     */

    private Object extractTransferData(DropTargetDropEvent dtde) {
        if (dtde == null) {
            Debug.message("defaultdndcatcher",
                    "ERROR> DefaultDnDCatcher::getTransferData(): dropEvent is null");
            return null;
        }

        Transferable tr = dtde.getTransferable();
        try {
            return tr.getTransferData(DefaultTransferableObject.OBJECT_FLAVOR);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Called when an object should be evaluated by the
     * DefaultDnDCatcher to see if it is needed.
     */
    public void findAndInit(Object someObj) {

        if (someObj instanceof MouseDelegator)
            md = (MouseDelegator) someObj;

        if (someObj instanceof MapBean) {
            ((MapBean) someObj).addProjectionListener(this);
            setProjection(((MapBean) someObj).getProjection().makeClone());
        }

        if (someObj instanceof LayerHandler) {
            LayerHandler lh = (LayerHandler) someObj;
            lh.addLayerListener(this);
            setLayers(lh.getLayers());
        }
    }

    /**
     * Eventually gets called when the DefaultDnDCatcher is added to
     * the BeanContext, and when other objects are added to the
     * BeanContext anytime after that. The DefaultDnDCatcher looks for
     * LayerHandler to get OMGraphicHandlerLayer layers to manage Drag
     * and Drop events for. If a MapBean is added to the BeanContext
     * while another already is in use, the second MapBean will take
     * the place of the first.
     *
     * @param it iterator to use to go through the new objects in the
     *        BeanContext.
     */
    public void findAndInit(Iterator it) {
        while (it.hasNext()) {
            findAndInit(it.next());
        }
    }

    /**
     * Called by childrenRemoved.
     */
    public void findAndUndo(Object someObj) {}

    public void firePropertyChange(String property, Object oldObj, Object newObj) {

        pcSupport.firePropertyChange(property, oldObj, newObj);
    }

    /**
     * Report a vetoable property update to any registered listeners.
     * If anyone vetos the change, then fire a new event reverting
     * everyone to the old value and then rethrow the
     * PropertyVetoException.
     * <P>
     *
     * No event is fired if old and new are equal and non-null.
     * <P>
     *
     * @param name The programmatic name of the property that is about
     *        to change
     *
     * @param oldValue The old value of the property
     * @param newValue - The new value of the property
     *
     * @throws PropertyVetoException if the recipient wishes the
     *         property change to be rolled back.
     */
    public void fireVetoableChange(String name, Object oldValue, Object newValue)
            throws PropertyVetoException {
        beanContextChildSupport.fireVetoableChange(name, oldValue, newValue);
    }

    /**
     * @return the current BeanContext associated with the JavaBean
     */
    public java.beans.beancontext.BeanContext getBeanContext() {
        return beanContextChildSupport.getBeanContext();
    }

    /**
     * Gets current projection.
     */

    public Projection getProjection() {
        return proj;
    }

    /**
     * The mouseDragged event gets interpreted as
     * DragGestureRecognized when startDrag boolean is true. After the
     * first mouseDragged event, set startDrag to false.
     * 
     */
    public void mouseDragged(MouseEvent e) {
        Debug.message("defaultdndcatcher", "mouseDragged, startDrag="
                + startDrag);
        if (startDrag) {
            startDrag = false;
            if (md.getActiveMouseMode() instanceof SelectMouseMode) {
                appendEvent(e);
                setComponent((Component) e.getSource());
                fireDragGestureRecognized(DnDConstants.ACTION_MOVE,
                        ((MouseEvent) getTriggerEvent()).getPoint());
            }
        }
    }

    /**
     * On mouseReleased, set startDrag to true in order to enable
     * dragging.
     */

    public void mouseReleased(MouseEvent e) {
        startDrag = true;
    }

    /**
     * Invoked when there has been a fundamental change to the Map.
     * <p>
     * Layers are expected to recompute their graphics (if this makes
     * sense), and then <code>repaint()</code> themselves.
     *
     * @param e ProjectionEvent
     */
    public void projectionChanged(ProjectionEvent e) {
        setProjection(e);
    }

    /**
     * This method gets called when a bound property is changed.
     *
     * @param evt A PropertyChangeEvent object describing the event
     *        source and the property that has changed.
     */
    public void propertyChange(java.beans.PropertyChangeEvent evt) {}

    /**
     * remove a property change listener to this bean child
     */
    public void removePropertyChangeListener(
                                             String name,
                                             java.beans.PropertyChangeListener pcl) {}

    /**
     * remove a vetoable change listener to this child
     */
    public void removeVetoableChangeListener(
                                             String name,
                                             java.beans.VetoableChangeListener vcl) {}

    /**
     * A change in the value of the nesting BeanContext property of
     * this BeanContextChild may be vetoed by throwing the appropriate
     * exception.
     *
     * @param in_bc the new BeanContext for this object
     */
    public void setBeanContext(BeanContext in_bc) throws PropertyVetoException {
        if (in_bc != null) {
            in_bc.addBeanContextMembershipListener(this);
            beanContextChildSupport.setBeanContext(in_bc);
            findAndInit(in_bc.iterator());
        }
    }

    /**
     * DefaultDnDCatcher adds itself to each layer as the
     * DropTargetListener. This is needed in order to capture drop
     * events from any layer on the map, and then apply the events to
     * the applicable layers.
     */
    public void setLayers(Layer[] allLayers) {
        // remove old layers list
        layers.clear();
        for (int i = 0; i < allLayers.length; i++) {
            // create a new drop target
            /* dropTarget = */new DropTarget(allLayers[i], DnDConstants.ACTION_MOVE, this);
            if (allLayers[i] instanceof OMGraphicHandlerLayer) {
                Debug.message("DnDCatcher", "Layers changed");
                // keep a reference to potential drop target
                layers.put(allLayers[i].getName(), allLayers[i]);
            }
        }
    }

    /**
     * The method is invoked when there is a change in layers property
     * in the LayerHandler.
     */

    public void setLayers(LayerEvent evt) {
        if (evt.getType() == LayerEvent.ALL) {
            setLayers(evt.getLayers());
        }
    }

    /**
     * This method lets you take the ProjectionEvent received from the
     * MapBean, and lets you know if you should do something with it.
     * MUST to be called in the projectionChanged() method of your
     * layer, if you want to refer to the projection later. If this
     * methods returns null, you probably just want to call repaint()
     * if your layer.paint() method is ready to paint what it should.
     *
     * @param projEvent the ProjectionEvent from the
     *        ProjectionListener method.
     * @return The new Projection if it is different from the one we
     *         already have, null if is the same as the current one.
     */
    public Projection setProjection(ProjectionEvent projEvent) {
        Projection newProjection = projEvent.getProjection();

        if (!newProjection.equals(getProjection())) {
            Projection clone = newProjection.makeClone();
            setProjection(clone);
            return clone;
        } else {
            return null;
        }
    }

    /**
     * Sets the current projection.
     */

    public void setProjection(Projection projection) {
        proj = projection;
    }

    /**
     * Invoked on dragGestureRecognized in the
     * ComponentDragGestureListener class.
     * 
     */
    public void startDragAction(DragGestureEvent dge, DragSourceListener dsl) {

        // create a Transferable object here.

        // Create a location object that can be dropped on a layer.

        // dragSource.startDrag(dge,
        // getCursor(DragSource.DefaultMoveDrop), new
        // DefaultTransferableObject(new BasicLocation()), dsl);

        // SinkGraphic is a singleton object used as sample. No action
        // on a layer will be done at drop.
        dragSource.startDrag(dge,
                getCursor(DragSource.DefaultMoveDrop),
                new DefaultTransferableObject(SinkGraphic.getSharedInstance()),
                dsl);
    }
}
TOP

Related Classes of com.bbn.openmap.tools.dnd.DefaultDnDCatcher

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.