Node Cut/Copy/Paste/Delete/Move/Reorder/Drag/Drop on the NetBeans Platform

Anyone using the Nodes API on the NetBeans Platform must have encountered the tricky difficulties in getting the cut/copy/paste/delete/move/reorder/drag/drop operations to work correctly.

The examples and tutorials in this area tend to focus on one or two of these, rather than all in the same context, which can be frustrating. That's why it is great that Toni Epple has contributed a complete example, here:

http://java.net/projects/nb-api-samples/sources/api-samples/show/versions/7.1/misc/DragDrop

When you've downloaded the sample and run it, you'll have the operations all working as expected:

And, when you go one level up, even more are available:


And drag/drop is also supported.

Here is part of the code, i.e., the Customer node that you see above:

public class CustomerNode extends AbstractNode {

    private final CustomerList model;

    public CustomerNode(Children children, Lookup lookup, CustomerList model) {
        super(children, lookup);
        this.model = model;
    }

    @Override
    public String getDisplayName() {
        return getLookup().lookup(Customer.class).getName();
    }

    @Override
    public Action[] getActions(boolean context) {
        return new Action[]{
                    CutAction.get(CutAction.class),
                    CopyAction.get(CopyAction.class),
                    DeleteAction.get(DeleteAction.class),
                    MoveUpAction.get(MoveUpAction.class),
                    MoveDownAction.get(MoveDownAction.class)
                };
    }

    @Override
    public boolean canCut() {
        return true;
    }

    @Override
    public boolean canCopy() {
        return true;
    }

    @Override
    public boolean canDestroy() {
        return true;
    }

    @Override
    public void destroy() throws IOException {
        model.remove(getLookup().lookup(Customer.class));
    }

    @Override
    public Transferable clipboardCut() throws IOException {
        Transferable deflt = super.clipboardCut();
        ExTransferable added = ExTransferable.create(deflt);
        added.put(new ExTransferable.Single(CustomerFlavor.CUSTOMER_FLAVOR) {
            @Override
            protected Customer getData() {
                return getLookup().lookup(Customer.class);
            }
        });
        return added;
    }

    @Override
    public Transferable clipboardCopy() throws IOException {
        Transferable deflt = super.clipboardCopy();
        ExTransferable added = ExTransferable.create(deflt);
        added.put(new ExTransferable.Single(CustomerFlavor.CUSTOMER_FLAVOR) {
            @Override
            protected Customer getData() {
                return getLookup().lookup(Customer.class);
            }
        });
        return added;
    }
    
}

And here is the parent of the Customer node:

public class DNDContainerNode extends AbstractNode {

    private final CustomerList model;

    public DNDContainerNode(final CustomerList model) {
        super(Children.create(new CustomerFactory(model), true));
        this.model = model;
        getCookieSet().add(new Index.Support() {
            @Override
            public Node[] getNodes() {
                return getChildren().getNodes(true);
            }
            @Override
            public int getNodesCount() {
                return getNodes().length;
            }
            @Override
            public void reorder(int[] perm) {
                model.reorder(perm);
            }
        });
    }

    @Override
    public Action[] getActions(boolean context) {
        return new Action[] {
            ReorderAction.get(ReorderAction.class),
            PasteAction.get(PasteAction.class),
        };
    }

    @Override
    public PasteType getDropType(final Transferable t, int arg1, int arg2) {
        if (t.isDataFlavorSupported(CustomerFlavor.CUSTOMER_FLAVOR)) {
            return new PasteType() {
                @Override
                public Transferable paste() throws IOException {
                    try {
                        model.add((Customer) t.getTransferData(CustomerFlavor.CUSTOMER_FLAVOR));
                        final Node node = NodeTransfer.node(t, NodeTransfer.DND_MOVE + NodeTransfer.CLIPBOARD_CUT);
                        if (node != null) {
                            node.destroy();
                        }
                    } catch (UnsupportedFlavorException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                    return null;
                }
            };
        } else {
            return null;
        }
    }

    @Override
    protected void createPasteTypes(Transferable t, List<PasteType> s) {
        super.createPasteTypes(t, s);
        PasteType p = getDropType(t, 0, 0);
        if (p != null) {
            s.add(p);
        }
    }

}

Thanks Toni!

Comments:

Geertjan, Toni,
Thank you. I find these type of examples very helpful. One comment.

In the CustomerNode destroy method, the refresh should not be necessary as the model will fire a change event on model.remove(getLookup().lookup(Customer.class)) that the factory will pick up.

Posted by Chris Cannell on October 16, 2011 at 04:44 AM PDT #

Hi Chris, you're right. I updated the code snippet in this sample, as well as the code in the repo. Nice side effect is that the CustomerNode can now be a separate class, rather than an inner class of the ChildFactory. And I'm interested to know how your organization is using the NetBeans Platform! Maybe you can drop me an e-mail about it at geertjan dot wielenga at oracle dot com ?

Posted by Geertjan on October 16, 2011 at 07:17 AM PDT #

I'm just about finished with an example using the code in the repo, that also puts in a JXTable with Glazedlists with DnD between the CategoryNode's and the JXTable. Also puts in a dialog that asks you for what type of DnDConstants-action you want to perform. Let me know if it's something you want to take a gander at.

I'm somewhat puzzled by the DNDContainerNode as I think it inputs certain assumptions that the "root" nodes are different in some way. They can of course be different for a wide range of use cases, but I'm probably going to spend sometime modifying it to remove that "assumption" as I liked some parts of the code better than the one I'm currently using.

Learned a bit from this sample, so thanks for writing about it :-)

Posted by Christian Michelsen on October 16, 2011 at 08:14 AM PDT #

Cool! It works perfectly! I was able to implement all the functionality I need on the nodes. But there is still a problem that I can not fix. In this code, when you select multiple nodes (even of different types) and paste, each node is managed individually. In this way it's not possible, for example, implement a unique undo / redo action. It would be necessary to manage all data from the clipboard and delegate all to a single action... Is there a way to solve?

Posted by Giacomo on October 31, 2011 at 10:24 AM PDT #

Hi Sir,

I am a Java Developer working in TCS/India. Thanks for providing valuable information to all the users.

I have a requirement where. I need to developer functionality for cut/copy/paste operations on a network model(which is built on nodes and links connected together). And currently we are using Netbeans6.8 and Java 1.6 for our development. Node API is not available with this env.

Please guide me the better way to implement this requirement.

Thanks,
Suresh

Posted by guest on May 23, 2013 at 10:39 PM PDT #

Nodes API is not available?! Yes, it is available. It's always been available. It is one of the core APIs of the NetBeans Platform. Are you sure you're using the NetBeans Platform? Doesn't sound like it. Follow some tutorials:

https://netbeans.org/features/platform/all-docs.html

Posted by Geertjan on May 24, 2013 at 01:08 AM PDT #

Hi,

if I try to use this example with Netbeans 7.4 under OpenSUSE 13.1 with Oracle JRE 1.7 b45 64 bit, the copy function is not working. Has something changed since 7.1 or is it somehow a bug?

Andreas

Posted by Andreas on February 16, 2014 at 11:37 PM PST #

Probably you need to hold down the Ctrl key.

Posted by Geertjan on February 17, 2014 at 12:19 AM PST #

What does that mean? For example: If I right click on the "Tom" node and choose "Cut" or "Copy" and I want to paste it to the "D&D 2" node, the "Paste" action is allways disabled. There is no Transferable with the supported Flavor. Although the drag&drop is working. So when do I have to hold the Ctrl key?

Posted by Andreas on February 18, 2014 at 12:53 AM PST #

What does that mean? If I want to copy&paste or cut&paste the "Tom" node to the "D&D 2" node, I'm doing the following.

1. Right click on the "Tom" node
2. Left click on "Copy" or "Cut"
3. Right click on the "D&D 2" node
4. Left click on "Paste"

The last step is not possible because of an disabled "Paste" action. There is no Transferable with the supported Flavor. But there should be on. The Drag&Drop is working.

When do I have to press the ctrl key?

Andreas

Posted by Andreas on February 18, 2014 at 12:58 AM PST #

For me, it works. So, you're going to need to be clearer. Operating system, JDK, NetBeans version, as much real info as you can provide.

Posted by Geertjan on February 18, 2014 at 06:09 AM PST #

It works perfectly. I made and upload a movie to prove it: www.youtube.com/watch?v=HEnqO1kNtcw

Posted by Geertjan on February 18, 2014 at 06:46 AM PST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today