alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

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; } }
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.