X

Geertjan's Blog

  • March 19, 2009

Drag a Node from a Palette onto a Node

Geertjan Wielenga
Product Manager
The original question asked by John on the mailing list was about dragging an item from a palette and dropping it in a BeanTreeView. So, after following the previous two blog entries on this topic (here and here), I reimplemented the scenario to use a Palette instead. Note that a Palette only works if it is created for a TopComponent that is in the "editor" position (read about that in German here), as is the case below:

I drag the palette item and then I hold down the Ctrl key, causing the icon over the node where I'm going to drop it to indicate that I am allowed to drop it:

I then drop it, and the node is added right after the node on which I dropped it:

In the constructor of my editor TopComponent, this is how I define the Palette:

Node node = new AbstractNode(Children.create(new PaletteChildFactory(), true));
associateLookup(Lookups.fixed(PaletteFactory.createPalette(node, new PaletteActions() {
@Override
public Action[] getImportActions() {
return null;
}
@Override
public Action[] getCustomPaletteActions() {
return null;
}
@Override
public Action[] getCustomCategoryActions(Lookup arg0) {
return null;
}
@Override
public Action[] getCustomItemActions(Lookup arg0) {
return null;
}
@Override
public Action getPreferredAction(Lookup arg0) {
return null;
}
})));

And this is how the "PaletteChildFactory", referred to above, is defined:

import java.awt.datatransfer.Transferable;
import java.io.IOException;
import java.util.List;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
public class PaletteChildFactory extends ChildFactory<String> {
@Override
protected boolean createKeys(List<String> list) {
list.add("Names");
return true;
}
@Override
protected Node createNodeForKey(String name) {
Node node = new AbstractNode(Children.create(new PaletteItemChildFactory(), true));
node.setDisplayName(name);
return node;
}
private class PaletteItemChildFactory extends ChildFactory<Customer> {
@Override
protected boolean createKeys(List<Customer> list) {
String[] names = {"Tom", "Dick", "Harry"};
for (String name : names) {
Customer c = new Customer();
c.setName(name);
list.add(c);
}
return true;
}
@Override
protected Node createNodeForKey(final Customer c) {
Node node = new AbstractNode(Children.LEAF) {
@Override
public Transferable drag() throws IOException {
return c;
}
};
node.setDisplayName(c.getName());
return node;
}
}
}

This is the same as shown previously. For the above to be possible, the Customer class needs to implement Transferable, as described in one of the two earlier blog entries on this topic.

So, we now have a Palette. Next, our editor TopComponent needs to have a BeanTreeView (or any other explorer view, of course). Implement ExplorerManager.Provider and then create the nodes in the BeanTreeView in the constructor of the editor TopComponent like this:

em.setRootContext(new AbstractNode(Children.create(new EditorChildFactory(), true)));

Finally, the "EditorChildFactory" is defined as follows, pay particular attention to the getDropType override:

import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.datatransfer.PasteType;
class EditorChildFactory extends ChildFactory<String> {
ArrayList<String> names = new ArrayList<String>();
public EditorChildFactory() {
names.add("Jane");
names.add("Judy");
names.add("Paula");
}
@Override
protected boolean createKeys(List<String> list) {
for (String name : names) {
list.add(name);
}
return true;
}
@Override
protected Node createNodeForKey(final String name) {
Node node = new AbstractNode(Children.LEAF) {
@Override
public PasteType getDropType(Transferable t, final int action, final int index) {
try {//We get the current customer from the transferable:
final Customer customer = (Customer) t.getTransferData(Customer.DATA_FLAVOR);//We use the current name to get the current index
//in the ArrayList:

final int dropIndex = names.indexOf(name);
return new PasteType() {
@Override
public Transferable paste() throws IOException {//We add a new node after the current index,
//setting its name to the name of the current customer:

names.add(dropIndex + 1, customer.getName());//We refresh the node:
refresh(true);//We put nothing in the clipboard:
return null;
}
};
} catch (UnsupportedFlavorException ex) {
Exceptions.printStackTrace(ex);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
return null;
}
};
node.setDisplayName(name);
return node;
}
}

Thanks again to Stan Aubrecht for helping with this.


Join the discussion

Comments ( 2 )
  • Miguel Garcia-Lopez Friday, March 20, 2009

    Dear Geertjan, I'll be just great if you could complete this series on nodes dragging with an example of: dragging one node, not onto another node, but between nodes.

    Sample scenario: I have a list of nodes whose keys I'm holding in a sorted list. I would like to be able to change their order by simply dragging the node up or down to the position I want it to be. That'll also fire the code to change the property of my node'd item which I'm using in the Comparator (simply int index; in my case).

    You already mentioned that dragging between nodes (as opposed to onto them) makes the Children get the event and not the node. What I have not been able to find out is how to know the position (index in BeanTreeView, in my case) it has been dropped to.

    Just a little selfish suggestion :)


  • John Vasilopoulos Friday, April 10, 2009

    Dear Geertjan,

    Thank you very much for this tutorial.

    As you have mentioned before, if a drop from the palette between nodes or onto the rootNode is to be supported, one would have to add drop functionality, namely override getDropType() ,to a custom node that extends AbstractNode and then use that node as the root of the children nodes.

    My question is how the getDropType()method of the rootNode should be overridden, in order to add a new node to its children, when it accepts a drop from the palette.

    Thanks in advance…


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.