|
What this is
This file is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Other links
The source code
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.explorer.view;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragSource;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.TreeSet;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.NotifyDescriptor;
import org.openide.NotifyDescriptor.Message;
import org.openide.awt.JPopupMenuPlus;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.UserCancelException;
import org.openide.util.Utilities;
import org.openide.util.datatransfer.ExClipboard;
import org.openide.util.datatransfer.ExTransferable;
import org.openide.util.datatransfer.MultiTransferObject;
import org.openide.util.datatransfer.PasteType;
import org.openide.util.datatransfer.ExTransferable.Multi;
/** Class that provides methods for common tasks needed during
* drag and drop when working with explorer views.
*
* @author Dafe Simonek
*/
final class DragDropUtilities extends Object {
static final boolean dragAndDropEnabled = isDragAndDropEnabled();
static final int NODE_UP = -1;
static final int NODE_CENTRAL = 0;
static final int NODE_DOWN = 1;
static final Point CURSOR_CENTRAL_POINT = new Point (10, 10);
static Runnable postDropRun = null;
/** No need to instantiate this class */
private DragDropUtilities() {
}
// helper constants
static final int NoDrag = 0;
static final int NoDrop = 1;
//static final int Modifiers4Move =
/**
* Checks system property netbeans.dnd.enabled. If it is not
* present return true.
*/
private static boolean isDragAndDropEnabled() {
if (System.getProperty("netbeans.dnd.enabled") != null) { // NOI18N
return Boolean.getBoolean("netbeans.dnd.enabled"); // NOI18N
} else {
return true;
}
}
/** Utility method - chooses and returns right cursor
* for given user drag action.
*/
static Cursor chooseCursor (int dragAction, boolean canDrop) {
//System.out.print("------> chooseCursor(action: "+dragAction+", can? "+canDrop+")");
// if the node does not provide icon use system default
Image image;
String name;
try {
switch (dragAction) {
case DnDConstants.ACTION_COPY:
case DnDConstants.ACTION_COPY_OR_MOVE:
if (canDrop) {
image = Utilities.loadImage(
"org/openide/resources/cursorscopysingle.gif"); // NOI18N
name = "ACTION_COPY"; // NOI18N
} else {
image = Utilities.loadImage(
"org/openide/resources/cursorsnone.gif"); // NOI18N
name = "NO_ACTION_COPY"; // NOI18N
}
break;
case DnDConstants.ACTION_MOVE:
if (canDrop) {
image = Utilities.loadImage(
"org/openide/resources/cursorsmovesingle.gif"); // NOI18N
name = "ACTION_MOVE"; // NOI18N
} else {
image = Utilities.loadImage(
"org/openide/resources/cursorsnone.gif"); // NOI18N
name = "NO_ACTION_MOVE"; // NOI18N
}
break;
case DnDConstants.ACTION_LINK:
if (canDrop) {
image = Utilities.loadImage(
"org/openide/resources/cursorsunknownsingle.gif"); // NOI18N
name = "ACTION_LINK"; // NOI18N
} else {
image = Utilities.loadImage(
"org/openide/resources/cursorsnone.gif"); // NOI18N
name = "NO_ACTION_LINK"; // NOI18N
}
break;
default:
image = Utilities.loadImage(
"org/openide/resources/cursorsnone.gif"); // NOI18N
name = "ACTION_NONE"; // NOI18N
break;
}
//System.out.println("--> "+image.getSource());
return createCustomCursor(image, name);
} catch (Exception ex) {
ErrorManager.getDefault ().notify (ex);
}
return DragSource.DefaultMoveNoDrop;
}
/**
* Returns cursor created from given icon.
*/
private static Cursor createCustomCursor(Image icon, String name) {
Toolkit t = Toolkit.getDefaultToolkit();
Dimension d = t.getBestCursorSize(16, 16);
Image i = icon;
if (d.width != icon.getWidth(null)) {
// need to resize the icon
Image empty = createBufferedImage(d.width, d.height);
i = Utilities.mergeImages(icon, empty, 0, 0);
}
return t.createCustomCursor(i, new Point(1,1), name);
}
/**
* Creates BufferedImage and Transparency.BITMASK
* Note: this method is copied from org.openide.util.IconManager. Should
* it be exposed in Utilities? I don't know (dstrupl).
*/
private static final BufferedImage createBufferedImage(int width, int height) {
ColorModel model = GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().getDefaultConfiguration().getColorModel(Transparency.BITMASK);
BufferedImage buffImage = new BufferedImage(model,
model.createCompatibleWritableRaster(width, height), model.isAlphaPremultiplied(), null);
return buffImage;
}
/** Utility method.
* @return true if given node supports given action,
* false otherwise.
*/
static boolean checkNodeForAction (Node node, int dragAction) {
if (node.canCut() &&
((dragAction == DnDConstants.ACTION_MOVE) ||
(dragAction == DnDConstants.ACTION_COPY_OR_MOVE)))
return true;
if (node.canCopy() &&
((dragAction == DnDConstants.ACTION_COPY) ||
(dragAction == DnDConstants.ACTION_COPY_OR_MOVE) ||
(dragAction == DnDConstants.ACTION_LINK) ||
(dragAction == DnDConstants.ACTION_REFERENCE)))
return true;
// hmmm, conditions not satisfied..
return false;
}
/** Gets right transferable of given nodes (according to given
* drag action) and also converts the transferable.
* Can be called only with correct action constant.
* @return The transferable.
*/
static Transferable getNodeTransferable (Node[] nodes, int dragAction)
throws IOException {
Transferable[] tArray = new Transferable[nodes.length];
//System.out.println("Sel count: " + nodes.length); // NOI18N
for (int i = 0; i < nodes.length; i++) {
Clipboard c = getClipboard();
if (c instanceof ExClipboard) {
ExClipboard cb = (ExClipboard)c;
if (dragAction == DnDConstants.ACTION_MOVE) {
tArray[i] = cb.convert (nodes[i].clipboardCut());
//System.out.println("Clipboard CUT for node: "+nodes[0]);
} else {
tArray[i] = cb.convert (nodes[i].clipboardCopy());
//System.out.println("Clipboard COPY for node: "+nodes[0]);
}
} else {
// In case of standalone library we cannot do
// conversion here. Is this ok?
if (dragAction == DnDConstants.ACTION_MOVE) {
tArray[i] = nodes[i].clipboardCut();
//System.out.println("Clipboard CUT for node: "+nodes[0]);
} else {
tArray[i] = nodes[i].clipboardCopy();
//System.out.println("Clipboard COPY for node: "+nodes[0]);
}
}
}
if (tArray.length == 1)
// only one node, so return regular single transferable
return tArray[0];
// enclose the transferables into multi transferable
return new Multi(tArray);
}
/** Returns transferable of given node
* @return The transferable.
*/
static Transferable getNodeTransferable (Node node, int dragAction)
throws IOException {
return getNodeTransferable(new Node[] { node }, dragAction);
}
/** Sets a runnable it will be executed after drop action is performed.
* @param run a runnable for execution */
static void setPostDropRun (Runnable run) {
postDropRun = run;
}
/* Invokes the stored runnable if it is there and than set to null.
*/
static private void invokePostDropRun () {
if (postDropRun!=null) {
SwingUtilities.invokeLater (postDropRun);
postDropRun = null;
}
}
/**
* Performs the drop. Performs paste on given paste type.
* (part of bugfix #37279, performPaste returns array of new nodes in target folder)
* @param type paste type
* @param targetFolder target folder for given paste type, can be null
* @return array of new added nodes in target folder
*/
static Node[] performPaste (PasteType type, Node targetFolder) {
//System.out.println("performing drop...."+type); // NOI18N
try {
if (targetFolder == null) {
// call paste action
type.paste();
return new Node[] {};
}
Node[] preNodes = targetFolder.getChildren ().getNodes (true);
// call paste action
type.paste();
Node[] postNodes = targetFolder.getChildren().getNodes(true);
// calculate new nodes
List pre = Arrays.asList (preNodes);
List post = Arrays.asList (postNodes);
Iterator it = post.iterator ();
List diff = new ArrayList();
while (it.hasNext ()) {
Node n = (Node)it.next ();
if (!pre.contains (n)) {
diff.add(n);
}
}
return (Node[])diff.toArray(new Node[diff.size()]);
/*Clipboard clipboard = T opManager.getDefault().getClipboard();
if (trans != null) {
ClipboardOwner owner = trans instanceof ClipboardOwner ?
(ClipboardOwner)trans
:
new StringSelection ("");
clipboard.setContents(trans, owner);
}*/
} catch (UserCancelException exc) {
// ignore - user just pressed cancel in some dialog....
return new Node[] {};
} catch (IOException e) {
ErrorManager.getDefault ().notify(e);
return new Node[] {};
}
}
/** Returns array of paste types for given transferable.
* If given transferable contains multiple transferables,
* multi paste type which encloses pate types of all contained
* transferables is returned.
* Returns empty array if given node did not accepted the transferable
* (or some sub-transferables in multi transferable)
*
* @param node given node to ask fro paste types
* @param trans transferable to discover
*/
static PasteType[] getPasteTypes (Node node, Transferable trans) {
// find out if given transferable is multi
boolean isMulti = false;
try {
isMulti = trans.isDataFlavorSupported(ExTransferable.multiFlavor);
} catch (Exception e) {
// patch to get the Netbeans start under Solaris
// [PENDINGworkaround]
}
if (!isMulti) {
// only single, so return paste types
PasteType [] pt = null;
try {
pt = node.getPasteTypes(trans);
} catch (NullPointerException npe) {
ErrorManager.getDefault ().notify (npe);
// there are not paste types
}
return pt;
} else {
// multi transferable, we must do extra work
try {
MultiTransferObject obj = (MultiTransferObject)
trans.getTransferData(ExTransferable.multiFlavor);
int count = obj.getCount();
Transferable[] t = new Transferable[count];
PasteType[] p = new PasteType[count];
PasteType[] curTypes = null;
// extract default paste types of transferables
for (int i = 0; i < count; i++) {
t[i] = obj.getTransferableAt(i);
curTypes = node.getPasteTypes(t[i]);
// return if not accepted
if (curTypes.length == 0)
return curTypes;
p[i] = curTypes[0];
}
// return new multi paste type
return new PasteType[] { new MultiPasteType(t, p) };
} catch (UnsupportedFlavorException e) {
// ignore and return empty array
}
catch (IOException e) {
// ignore and return empty array
}
}
return new PasteType[0];
}
/** Notifies user that the drop was not succesfull. */
static void dropNotSuccesfull () {
DialogDisplayer.getDefault().notify(
new Message(
NbBundle.getBundle(TreeViewDropSupport.class).
getString("MSG_NoPasteTypes"),
NotifyDescriptor.WARNING_MESSAGE)
);
}
/** If our clipboard is not found return the default system clipboard. */
private static Clipboard getClipboard() {
Clipboard c = (Clipboard)
Lookup.getDefault().lookup(Clipboard.class);
if (c == null) {
c = Toolkit.getDefaultToolkit().getSystemClipboard();
}
return c;
}
/** Paste type used when in clipbopard is MultiTransferable */
static final class MultiPasteType extends PasteType {
// Attributes
/** Array of transferables */
Transferable[] t;
/** Array of paste types */
PasteType[] p;
// Operations
/** Constructs new MultiPasteType for the given
* transferables and paste types.*/
MultiPasteType(Transferable[] t, PasteType[] p) {
this.t = t;
this.p = p;
}
/** Performs the paste action.
* @return Transferable which should be inserted into the
* clipboard after paste action. It can be null, which means
* that clipboard content should be cleared.
*/
public Transferable paste() throws IOException {
int size = p.length;
Transferable[] arr = new Transferable[size];
// perform paste for all source transferables
for (int i = 0; i < size; i++) {
//System.out.println("Pasting #" + i); // NOI18N
arr[i] = p[i].paste();
}
return new Multi(arr);
}
} // end of MultiPasteType
/** Utility method created by Enno Sandner. Is it needed?
* I don't know (dstrupl).
*/
static Node secureFindNode(Object o) {
try {
return Visualizer.findNode(o);
}
catch (ClassCastException e) {
e.printStackTrace();
return null;
}
}
/**
* Creates and populates popup as a result of
* dropping an item.
* @author Enno Sandner
*/
static JPopupMenu createDropFinishPopup(final TreeSet pasteTypes) {
JPopupMenu menu = new JPopupMenuPlus();
//System.arraycopy(pasteTypes, 0, pasteTypes_, 0, pasteTypes.length);
final JMenuItem[] items_ = new JMenuItem[pasteTypes.size ()];
ActionListener aListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuItem source = (JMenuItem)e.getSource();
final Iterator it = pasteTypes.iterator ();
for (int i=0; it.hasNext(); i++) {
PasteType action = (PasteType)it.next();
if (items_[i].equals(source)) {
DragDropUtilities.performPaste (action, null);
invokePostDropRun ();
break;
}
}
}
};
Iterator it = pasteTypes.iterator ();
for (int i=0; it.hasNext(); i++) {
items_[i]=new JMenuItem(((PasteType)it.next()).getName ());
items_[i].addActionListener(aListener);
menu.add(items_[i]);
}
menu.addSeparator();
JMenuItem abortItem=new JMenuItem(NbBundle.getBundle(DragDropUtilities.class).getString("MSG_ABORT"));
menu.add(abortItem);
return menu;
}
}
|