Geertjan's Blog

  • October 16, 2008

How Users Can Let Children Move Up & Move Down (Part 2)

Geertjan Wielenga
Product Manager
In How Users Can Let Children Move Up & Move Down (Part 1), the children class extended Index.ArrayChildren. That was extremely convenient, because (in order for the 'Move Up' and 'Move Down' menu items to be enabled), an implementation of the Index class MUST be included in the root node's Lookup. Since an implementation of Index.ArrayChildren meets this requirement, things look good so long as you're using that class. However, what if you're extending Children.Keys instead? That's the vexing question posed in this blog entry.

In the case where you're not extending Index.ArrayChildren, you have a problem. In this case, you need to create a new class that extends Index.Support, with content like this:

import javax.swing.JOptionPane;
import org.openide.nodes.Index;
import org.openide.nodes.Node;
public class IndexSupportImpl extends Index.Support {
private PropChildren prop;
public IndexSupportImpl(PropChildren prop) {
this.prop = prop;
public Node[] getNodes() {
return prop.getNodes();
public int getNodesCount() {
return prop.getNodesCount();
public void reorder(final int[] perm) {
JOptionPane.showMessageDialog(null, "reorder here!");

The "only" problem is that I haven't been able to figure out how to implement the 'reorder' method above. I've seen all the related sources but haven't been able to figure it out. Anyway, now you need to add the above implementation of Index to the root node's Lookup:

PropChildren prop = new PropChildren();
explorerManager.setRootContext(new AbstractNode(prop, Lookups.singleton(new IndexSupportImpl(prop))));

And, finally, your children object needs to implement Index and then delegate to the above Index.Support class. At least, then the menu items will be enabled, even though the 'reorder' will need to be figured out per implementation.

Join the discussion

Comments ( 6 )
  • Carsten Schmalhorst Friday, October 17, 2008

    Hi Geertjan,

    me too, I had a hard time figuring out how to implement Node ordering. Let's assume you have an ArrayList keyArrayList to store the keys for your Children.Keys object. You need to permute the order of the keyArrayList content. It seems that Children.Keys.createKeys(List keys) is invoked directly after Index.Support.reorder() is called. Here's my implementation:


    public class IndexImpl extends Index.Support


    //This is where my Children.Keys stores its keys in

    ArrayList keyArrayList;

    Node myNode;

    IndexImpl(ArrayList keyArrayList, Node myNode) {

    this.keyArrayList = keyArrayList;

    this.myNode = myNode;



    public Node[] getNodes()


    return myNode.getChildren().getNodes();



    public int getNodesCount()


    return myNode.getChildren().getNodesCount();



    public void reorder(int[] perm)


    ArrayList newOrder = new ArrayList();

    for(int i = 0; i < _manips.length; i++) {

    newOrder.add( keyArrayList.get(perm[i]));






  • Gunnar Reinseth Friday, October 17, 2008

    Note: I'm posting some code here that can be difficult to read without formatting. Anyways, the comments in the program flow may be useful.


    Strange that you should blog about this just as me and colleague was struggling with the very same problem. We have a node implementation that wraps an EMF Ecore model (which is cut loose from Eclipse) and we use EMF Commands to perform all manipulations on the model and our node implementation simply listen for changes and reflect them.

    So for our Children implementation, we extended Index.KeysChildren and had to copy/paste the default implementation of Index.KeysChildren#createIndex() to return the following class:


    protected Index createIndex() {

    // create support instance for delegation of common tasks

    return new Index.Support() {

    // Copied from Index.KeysChildren#createIndex()

    public Node[] getNodes() {

    List<Node> l = Arrays.asList(EmfChildren.this.getNodes());

    if (EmfChildren.this.nodes != null) {



    return l.toArray(new Node[l.size()]);


    public int getNodesCount() {

    return list.size();


    public void reorder(int[] perm) {

    EmfChildren.this.reorder(perm); // will invoke update() implicitly

    fireChangeEvent(new ChangeEvent(this));




    As you can see - reorder() doesn't call update() as in the default implementation asthis method is invoked implicitly after the underlying EMF model is updated. (

    Our reorder() method in our Children implementation is as follows:


    protected void reorder(int[] perm) {

    // Reordering of children. Should ultimately result in the execution of a MoveCommand.

    // Assumes that a single node has been moved among its siblings.

    // perm[] is an array whose indexes represent the original positions of siblings, and the values represent the updated positions.

    int toMoveIndex = -1;

    int movedToIndex = -1;

    // Detect between which positions the move has been performed.

    // Moving from last position is a special case treated after the for-loop.

    // When interchanging positions of neighbour nodes, we don't need to know which of the two nodes were actually moved.

    for (int i = 0; i < perm.length - 1; i++) {

    if (perm[i] == movedToIndex) {

    toMoveIndex = i;



    if (movedToIndex == -1 && perm[i] > i) {

    // Found point of change

    if (i > 0 && (perm[i] - perm[i - 1]) == 2) {

    // node has been moved forward.

    movedToIndex = i;

    } else if (i == 0 && perm[0] == 1) {

    // node has been moved into first position

    movedToIndex = 0;

    } else {

    // node has been moved backward

    movedToIndex = perm[i];

    toMoveIndex = i;





    if (movedToIndex != -1 && toMoveIndex == -1) {

    // Moved to end of list

    toMoveIndex = perm.length - 1;


    if (toMoveIndex >= 0 && movedToIndex >= 0) {

    // Execute EMF command corresponding to the detected move operation.

    EObject movedObject = getNodes()[toMoveIndex].getCookie(EmfCookie.class).getObject();

    EObject objectAtNewIndex = getNodes()[movedToIndex].getCookie(EmfCookie.class).getObject();

    // Translate index from visual representation, to actual position in unfiltered model.

    int emfIndex = ModelUtilities.getChildren(getParent()).indexOf(objectAtNewIndex);

    Command moveCmd = MoveCommand.create(getProject().getEditingDomain(), getParent(), null, movedObject, emfIndex);

    if (moveCmd.canExecute()) {





  • Jesse Glick Friday, October 17, 2008

    The section "Reordering Subnodes" in NB-TDG you pointed to before does explain how to properly implement reorder, not using Index.ArrayChildren. (As always, I do not recommend any kind of children other than Children.Keys.) When reorder is called, you simply permute your data model as requested. Really your whole example is not great here because it does not make much sense to reorder system properties. If you had a true data model with a definable and mutable order, then the implementation of reordering would follow naturally.

  • Geertjan Wielenga Friday, October 17, 2008

    @Jesse: But then why do I not have a problem when I use Index.ArrayChildren? With Index.ArrayChildren, I'm able to reorder without a problem, while with Children.Keys it seems nigh impossible. (And why do you recommend using Children.Keys? What's wrong with the others?)

  • Jesse Glick Friday, October 17, 2008

    Index.ArrayChildren does make it trivial to reorder subnodes, but the reordering does not do anything useful: you are just shuffling around GUI elements. When you have an actual data model you are displaying, then you want to use Children.Keys to reflect that in your view. Adding an Index support is not much work if you follow the example given in NB-TDG.

  • Arnaud GROSJEAN Sunday, July 18, 2010

    Hello, I wanted to insert a node inbetween two other nodes (the netbeans project view shows an horizontal bar between two nodes when dragging over).

    the problem was that the index in the getDropType methods always returned -1. So I tried, instead of using an AbstractNode, to use an IndexedNode. Now the problem is that the bar between nodes doesn't appear anymore when dragging over.

    wtf ?


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