/* ********************************************************************** * * Use, duplication, or disclosure by the Government is subject to * restricted rights as set forth in the DFARS. * * BBNT Solutions LLC * A Part of Verizon * 10 Moulton Street * Cambridge, MA 02138 * (617) 873-3000 * * Copyright (C) 2002 by BBNT Solutions, LLC * All Rights Reserved. * ********************************************************************** */ package com.bbn.openmap.tools.beanbox; import java.awt.Color; import java.awt.Component; import java.awt.Image; 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.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.beans.BeanInfo; import java.beans.PropertyChangeListener; import java.beans.beancontext.BeanContextChild; import java.beans.beancontext.BeanContextMembershipListener; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.URL; import java.util.Enumeration; import java.util.HashMap; import java.util.Vector; import javax.swing.BorderFactory; import javax.swing.ImageIcon; 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.MapBean; import com.bbn.openmap.SoloMapComponent; import com.bbn.openmap.event.LayerListener; import com.bbn.openmap.event.ProjectionListener; import com.bbn.openmap.tools.dnd.ComponentDragGestureListener; import com.bbn.openmap.tools.dnd.DefaultDnDCatcher; import com.bbn.openmap.tools.dnd.DefaultTransferableObject; import com.bbn.openmap.util.Debug; /** * The BeanBoxDnDCatcher class manages all Java Drag-and-Drop events associated * with openmap layers that implement the * {@link com.bbn.openmap.tools.beanbox.BeanBoxHandler}interface. */ public class BeanBoxDnDCatcher extends DefaultDnDCatcher implements SoloMapComponent, BeanContextChild, BeanContextMembershipListener, PropertyChangeListener, Serializable, ProjectionListener, LayerListener, ActionListener { static { setDefaultIcon(); } private Vector transferData; private Point dropLocation; /** holds the currently selected bean */ protected Object selectedBean = null; /** holds the serialized version of currently selected bean */ protected ByteArrayOutputStream serBean = null; /** holds the map location of the currently selected bean */ protected Point selectedBeanLocation = null; /** * holds the {@link com.bbn.openmap.tools.beanbox.BeanBox}that manages the * currently selected bean */ protected BeanBox selectedBeanBox = null; /** * holds the openmap layer that contains the currently selected bean */ protected Layer selectedBeanLayer = null; /** holds the currently cut bean, if any */ Object cutBean = null; /** * contains BeanInfo objects hashed by the class names of the associated * bean classes */ protected HashMap beanInfoMap = null; /** * Constructs a new {@link com.bbn.openmap.tools.dnd.DnDListener} object. */ public BeanBoxDnDCatcher() { this(new DragSource()); } /** * Constructs a new MouseDragGestureRecognizer given the DragSource for the * Component. * * @param ds the DragSource for the Component */ public BeanBoxDnDCatcher(DragSource ds) { this(ds, null); } /** * Construct a new MouseDragGestureRecognizer 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 BeanBoxDnDCatcher(DragSource ds, Component c) { this(ds, c, DnDConstants.ACTION_MOVE); } /** * Construct a new MouseDragGestureRecognizer given the DragSource for the * Component c, and the Component to observe and the drag-and-drop action. * * @param ds the DragSource for the Component c * @param c the Component to observe * @param act the drag-and-drop action */ public BeanBoxDnDCatcher(DragSource ds, Component c, int act) { this(ds, c, act, null); } /** * Construct a new MouseDragGestureRecognizer given the DragSource for the * Component c, and the Component to observe. the drag-and-drop action and a * DragGestureListener * * @param ds the DragSource for the Component c * @param c the Component to observe * @param act the drag-and-drop action * @param dgl the DragGestureListener */ public BeanBoxDnDCatcher(DragSource ds, Component c, int act, DragGestureListener dgl) { super(ds, c, act, dgl); dragSource = getDragSource(); dragGestureListener = new ComponentDragGestureListener(this, this); setSourceActions(DnDConstants.ACTION_MOVE); beanInfoMap = new HashMap(); } /** * Calls superclass method and then adds the KeyListener to someObj if * someObj is of type OpenMapFrame. */ public void findAndInit(Object someObj) { super.findAndInit(someObj); if (someObj instanceof MapBean) { ((MapBean) someObj).addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent evt) { if (evt.getModifiers() == InputEvent.CTRL_MASK && evt.getKeyCode() == KeyEvent.VK_C) copySelectedBean(); else if (evt.getModifiers() == InputEvent.CTRL_MASK && evt.getKeyCode() == KeyEvent.VK_V) pasteSelectedBean(); else if (evt.getModifiers() == InputEvent.CTRL_MASK && evt.getKeyCode() == KeyEvent.VK_X) cutSelectedBean(); else if (evt.getKeyCode() == KeyEvent.VK_ESCAPE) unCutSelectedBean(); else if (evt.getKeyCode() == KeyEvent.VK_DELETE) deleteSelectedBean(); } }); } } /** * This method is called when the user chooses to copy a bean by some means * such by by pressing Ctrl-C. This method tries to serialize the selected * bean. If no bean is selected or the bean is not serializable, this method * is a no-op. */ protected void copySelectedBean() { if (Debug.debugging("beanbox")) Debug.output("Enter> copySelectedBean"); if (selectedBean == null || selectedBeanLocation == null) { clearSelection(); if (Debug.debugging("beanbox")) Debug.output("selectedBean=" + selectedBean); if (Debug.debugging("beanbox")) Debug.output("selectedBeanLocation=" + selectedBeanLocation); return; } try { serBean = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(serBean); oos.writeObject(selectedBean); } catch (Exception e) { e.printStackTrace(); clearSelection(); if (Debug.debugging("beanbox")) Debug.output("Exit> copySelectedBean"); return; } cutBean = null; if (Debug.debugging("beanbox")) Debug.output("Exit> copySelectedBean"); } /** * This method is called when the user chooses to paste by some means (such * by pressing Ctrl-V) a previously copied or cut bean. This method tries to * deserialize the previously serialized bean. If the bean in question was * cut, this method also removes it from from the source beanbox. The paste * operation is treated the same as a drop operation. If no bean was * previously copied or cut or if an error occurs during deserialization, * this method is a no-op. */ protected void pasteSelectedBean() { if (Debug.debugging("beanbox")) Debug.output("Enter> pasteSelectedBean"); if (serBean == null) { clearSelection(); if (Debug.debugging("beanbox")) Debug.output("Exit> pasteSelectedBean"); return; } BeanInfo beanInfo = (BeanInfo) beanInfoMap.get(selectedBean.getClass() .getName()); if (beanInfo == null) { System.out.println("ERROR> BBDnDC::pasteSelectedBean: " + "no cached BeanInfo found for bean " + selectedBean); clearSelection(); return; } // if bean was cut, remove it from its present location if (cutBean != null) { selectedBeanBox.removeBean(selectedBean); } Object deserBean = null; try { ByteArrayInputStream bais = new ByteArrayInputStream(serBean.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); deserBean = ois.readObject(); } catch (Exception e) { e.printStackTrace(); clearSelection(); if (Debug.debugging("beanbox")) Debug.output("Exit> pasteSelectedBean"); return; } // construct a transferData object transferData = new Vector(); transferData.add(deserBean); transferData.add(beanInfo); transferData.add(new Boolean(false)); // bean not being moved // let dropLocation be selectedBeanLocation dropLocation = selectedBeanLocation; showPopUp(selectedBeanLayer); cutBean = null; if (Debug.debugging("beanbox")) Debug.output("Exit> pasteSelectedBean"); } /** * This method is called when the user chooses to cut a bean by some means * such by by pressing Ctrl-X. This method tries to serialize the selected * bean. If no bean is selected or the bean is not serializable, this method * is a no-op. */ protected void cutSelectedBean() { if (Debug.debugging("beanbox")) Debug.output("Enter> cutSelectedBean"); if (selectedBean == null || selectedBeanLocation == null) { if (Debug.debugging("beanbox")) Debug.output("selectedBean=" + selectedBean); if (Debug.debugging("beanbox")) Debug.output("selectedBeanLocation=" + selectedBeanLocation); clearSelection(); return; } try { serBean = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(serBean); oos.writeObject(selectedBean); } catch (Exception e) { e.printStackTrace(); clearSelection(); if (Debug.debugging("beanbox")) Debug.output("Exit> copySelectedBean"); return; } cutBean = selectedBean; selectedBeanBox.showCut(selectedBean); if (Debug.debugging("beanbox")) Debug.output("Exit> cutSelectedBean"); } /** * This method is called when the user chooses to cancel a cut operation on * a bean by some means such by by pressing ESC. If no bean was marked for * cutting this method is a no-op. */ protected void unCutSelectedBean() { if (Debug.debugging("beanbox")) Debug.output("Enter> unCutSelectedBean"); if (selectedBean == null || selectedBeanLocation == null) { if (Debug.debugging("beanbox")) Debug.output("selectedBean=" + selectedBean); if (Debug.debugging("beanbox")) Debug.output("selectedBeanLocation=" + selectedBeanLocation); clearSelection(); return; } selectedBeanBox.showUnCut(selectedBean); clearSelection(); if (Debug.debugging("beanbox")) Debug.output("Exit> unCutSelectedBean"); } private void clearSelection() { cutBean = null; selectedBean = null; selectedBeanLocation = null; selectedBeanBox = null; selectedBeanLayer = null; serBean = null; } /** * This method is called when the user chooses to delete a bean by some * means such by by pressing DEL. This method removes the selected bean from * its beanbox. If no bean is selected this method is a no-op. */ protected void deleteSelectedBean() { if (Debug.debugging("beanbox")) Debug.output("Enter> deleteSelectedBean"); if (selectedBean == null || selectedBeanLocation == null) { if (Debug.debugging("beanbox")) Debug.output("selectedBean=" + selectedBean); if (Debug.debugging("beanbox")) Debug.output("selectedBeanLocation=" + selectedBeanLocation); return; } selectedBeanBox.removeBean(selectedBean); cutBean = null; if (Debug.debugging("beanbox")) Debug.output("Exit> deleteSelectedBean"); } /** * 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> * <P> * * @param dtde the <code>DropTargetDropEvent</code> */ public void drop(DropTargetDropEvent dtde) { if (Debug.debugging("beanbox")) Debug.output("Enter> drop"); dtde.acceptDrop(DnDConstants.ACTION_MOVE); extractTransferData(dtde); extractDropLocation(dtde); if (transferData == null || dropLocation == null) return; Component parent = ((DropTarget) dtde.getSource()).getComponent(); dtde.dropComplete(true); showPopUp(parent); if (Debug.debugging("beanbox")) Debug.output("Exit> drop"); } private void showPopUp(Component parent) { if (Debug.debugging("beanbox")) Debug.output("Enter> showPopUp"); 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)); Enumeration keys = layers.keys(); while (keys.hasMoreElements()) { String layerName = keys.nextElement().toString(); Layer omlayer = (Layer) layers.get(layerName); if (omlayer.isVisible()) { JMenuItem menuItem = new JMenuItem(layerName); 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); if (Debug.debugging("beanbox")) Debug.output("showing popup"); popup.show(parent, dropLocation.x, dropLocation.y); if (Debug.debugging("beanbox")) Debug.output("Exit> showPopUp"); } /** * Displays a {@link com.bbn.openmap.tools.beanbox.GenericPropertySheet}if * mouse click is on a bean in some layer. In case of overlapping beans, * chooses the first bean found to be under the mouse, which is usually a * bean in the top most visible layer. */ public void mouseClicked(MouseEvent evt) { if (Debug.debugging("beanbox")) Debug.output("Enter> mouseClicked"); Point srcLocation = evt.getPoint(); Enumeration keys = layers.keys(); while (keys.hasMoreElements()) { String layerName = keys.nextElement().toString(); selectedBeanLayer = (Layer) layers.get(layerName); if (!selectedBeanLayer.isVisible()) continue; selectedBeanBox = ((BeanBoxHandler) selectedBeanLayer).getBeanBox(); if (selectedBeanBox == null) continue; selectedBean = selectedBeanBox.getBeanAtLocation(srcLocation); if (selectedBean != null) { break; } } if (selectedBean == null) { clearSelection(); return; } selectedBeanLocation = srcLocation; selectedBeanBox.showSelected(selectedBean); if (Debug.debugging("beanbox")) Debug.output("selectedBean=" + selectedBean); if (evt.getModifiers() != InputEvent.BUTTON1_MASK) { GenericPropertySheet propertySheet = new GenericPropertySheet(selectedBean, 575, 20, null, selectedBeanBox); propertySheet.setVisible(true); } if (Debug.debugging("beanbox")) Debug.output("Exit> mouseClicked"); } /** * This method is called whenever the user choose a layer to drop or move a * bean to. This method adds the bean to the layer or moves the beans to or * within the selected layer. */ public void actionPerformed(ActionEvent evt) { if (Debug.debugging("beanbox")) Debug.output("Enter> actionPerformed"); Object source = evt.getSource(); if (!(source instanceof JMenuItem)) return; JMenuItem mi = (JMenuItem) source; String name = mi.getText(); Layer targetLayer = (Layer) layers.get(name); if (targetLayer == null) { System.out.println("ERROR> BBDnDC::actionPerformed: " + "no layer found with name " + name); return; } BeanBox targetBeanBox = ((BeanBoxHandler) targetLayer).getBeanBox(); Object bean = transferData.get(0); BeanInfo beanInfo = (BeanInfo) transferData.get(1); Boolean wasBeanMoved = (Boolean) transferData.get(2); if (wasBeanMoved.booleanValue()) { String sourceLayerName = (String) transferData.get(3); if (sourceLayerName.equals(targetLayer.getName())) { targetBeanBox.relocateBean(bean, beanInfo, dropLocation); } else { Layer sourceLayer = (Layer) layers.get(sourceLayerName); BeanBox sourceBeanBox = ((BeanBoxHandler) sourceLayer).getBeanBox(); sourceBeanBox.removeBean(bean); Vector object = new Vector(); object.add(bean); object.add(beanInfo); object.add(dropLocation); targetBeanBox.addBean(object); } } else { Vector object = new Vector(); object.add(bean); object.add(beanInfo); object.add(dropLocation); targetBeanBox.addBean(object); } } /** * Asscoiates a DropTarget with each layer. Also caches all layers that * implement the BeanBoxHandler interface. */ public void setLayers(Layer[] allLayers) { layers.clear(); if (allLayers != null) { for (int i = 0; i < allLayers.length; i++) { new DropTarget(allLayers[i], DnDConstants.ACTION_MOVE, this); if (allLayers[i] instanceof BeanBoxHandler) { Debug.message("DnDCatcher", "Layers changed"); layers.put(allLayers[i].getName(), allLayers[i]); } } } } /** * Invoked on dragGestureRecognized */ public void startDragAction(DragGestureEvent dge, DragSourceListener dsl) { if (Debug.debugging("beanbox")) Debug.output("Enter> startDragAction"); Object selectedBean = null; BeanBox selectedBeanBox = null; Layer selectedLayer = null; Point srcLocation = dge.getDragOrigin(); Enumeration keys = layers.keys(); while (keys.hasMoreElements()) { String layerName = keys.nextElement().toString(); Layer omLayer = (Layer) layers.get(layerName); BeanBox beanBox = ((BeanBoxHandler) omLayer).getBeanBox(); selectedBean = beanBox.getBeanAtLocation(srcLocation); if (selectedBean != null) { selectedBeanBox = beanBox; selectedLayer = omLayer; break; } } if (Debug.debugging("beanbox")) Debug.output("selectedBean=" + selectedBean); if (selectedBean == null || selectedBeanBox == null || selectedLayer == null) { if (Debug.debugging("beanbox")) Debug.output("Exit> startDragAction, selected bean/beanbox/layer is null"); return; } Image dragImage = selectedBeanBox.getDragImage(selectedBean); super.setCursor(dragImage, DragSource.DefaultMoveDrop); BeanInfo beanInfo = selectedBeanBox.getBeanInfoForBean(selectedBean.getClass() .getName()); Vector beanTransferData = new Vector(); beanTransferData.add(selectedBean); beanTransferData.add(beanInfo); beanTransferData.add(new Boolean(true)); beanTransferData.add(selectedLayer.getName()); dragSource.startDrag(dge, super.getCursor(DragSource.DefaultMoveDrop), new DefaultTransferableObject(beanTransferData), dsl); if (Debug.debugging("beanbox")) Debug.output("Exit> startDragAction"); } private void extractTransferData(DropTargetDropEvent dtde) { if (dtde == null) { System.out.println("ERROR> BDnDC::getTransferData(): dropEvent is null"); return; } Transferable tr = dtde.getTransferable(); try { transferData = (Vector) tr.getTransferData(DefaultTransferableObject.OBJECT_FLAVOR); // cache beanInfos if (transferData.size() >= 2) { Object bean = transferData.get(0); BeanInfo beanInfo = (BeanInfo) transferData.get(1); beanInfoMap.put(bean.getClass().getName(), beanInfo); } } catch (Exception e) { e.printStackTrace(); } } private void extractDropLocation(DropTargetDropEvent dtde) { if (dtde == null) { System.out.println("ERROR> BDnDC::getTransferData(): dropEvent is null"); return; } dropLocation = dtde.getLocation(); } private static void setDefaultIcon() { if (BeanPanel.defaultBeanIcon == null) { URL url = BeanPanel.class.getResource("bluebean.gif"); if (url != null) BeanPanel.defaultBeanIcon = new ImageIcon(url); } } }