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

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;
    }

    @Override
    public Node[] getNodes() {
        return prop.getNodes();
    }

    @Override
    public int getNodesCount() {
        return prop.getNodesCount();
    }

    @Override
    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.

Comments:

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:
<code>
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;
}

@Override
public Node[] getNodes()
{
return myNode.getChildren().getNodes();
}

@Override
public int getNodesCount()
{
return myNode.getChildren().getNodesCount();
}

@Override
public void reorder(int[] perm)
{
ArrayList newOrder = new ArrayList();
for(int i = 0; i < _manips.length; i++) {
newOrder.add( keyArrayList.get(perm[i]));
}
keyArrayList.clear();
keyArrayList.addAll(newOrder);
}
}</code>

Posted by Carsten Schmalhorst on October 16, 2008 at 06:05 PM PDT #

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:

@Override
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) {
l.removeAll(EmfChildren.this.nodes);
}

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:

@Override
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;
break;
}
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;
break;
}
}
}
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()) {
getProject().getEditingDomain().getCommandStack().execute(moveCmd);
}
}
}

Posted by Gunnar Reinseth on October 16, 2008 at 09:43 PM PDT #

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.

Posted by Jesse Glick on October 17, 2008 at 03:40 AM PDT #

@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?)

Posted by Geertjan Wielenga on October 17, 2008 at 06:26 AM PDT #

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.

Posted by Jesse Glick on October 17, 2008 at 06:48 AM PDT #

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 ?

Thx.

Posted by Arnaud GROSJEAN on July 18, 2010 at 06:20 AM PDT #

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
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today