4 Replies Latest reply on Mar 18, 2009 8:41 AM by fr.evrard

    tree + drag & drop : dropped node is not added to the tree w

      hi,
      I try to implement the sample of the drag & drop on a tree presented in the richfaces documentation page from : http://www.jboss.org/file-access/default/members/jbossrichfaces/freezone/docs/devguide/en/html/tree.html

      This sample doc refers to one method called "processDrop", I have tried with this implementation but this doesn't works very well, because the dragged object is well remove from the parent treeNode source but is not added to the dropNode (but in the model of the tree, it seems that the dropped object have been added, is there an bug in the refresh process of the tree after the drop event ? I don't knwo why).

      Could anyone send me any working sample for this processDrop method ?

      Here's my implementation from a compilation from RichFaces 3.3.0 sample code :

      import java.io.ByteArrayOutputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.util.ArrayList;
      import java.util.Collection;
      import java.util.HashMap;
      import java.util.HashSet;
      import java.util.Iterator;
      import java.util.List;
      import java.util.Map;
      import java.util.Set;
      import java.util.StringTokenizer;
      
      import javax.faces.context.FacesContext;
      import javax.swing.text.html.HTMLDocument.HTMLReader.IsindexAction;
      
      import org.ajax4jsf.context.AjaxContext;
      import org.richfaces.component.UIDragSupport;
      import org.richfaces.component.UITree;
      import org.richfaces.component.UITreeNode;
      import org.richfaces.component.html.HtmlTree;
      import org.richfaces.event.DropEvent;
      import org.richfaces.event.NodeSelectedEvent;
      import org.richfaces.model.TreeNode;
      import org.richfaces.model.TreeRowKey;
      
      public class Library implements TreeNode {
      
       /**
       *
       */
       private static final long serialVersionUID = -3530085227471752526L;
      
       private Map artists = null;
      
       private Object state1;
      
       private Object state2;
      
       private List<Song> songsList;
      
       private Set<Song> selectedSongsSet = new HashSet<Song>();
      
       private List<Song> selectedSongsList = new ArrayList<Song>();
      
       public Library() {
       songsList = new ArrayList<Song>();
       songsList.addAll(getLibraryAsList());
       }
      
       private Map getArtists() {
       if (this.artists == null) {
       initData();
       }
       return this.artists;
       }
      
       public void addArtist(Artist artist) {
       addChild(Long.toString(artist.getId()), artist);
       }
      
       public void addChild(Object identifier, TreeNode child) {
       getArtists().put(identifier, child);
       child.setParent(this);
       }
      
       public TreeNode getChild(Object id) {
       return (TreeNode) getArtists().get(id);
       }
      
       public Iterator getChildren() {
       return getArtists().entrySet().iterator();
       }
      
       public Object getData() {
       return this;
       }
      
       public TreeNode getParent() {
       return null;
       }
      
       public boolean isLeaf() {
       return getArtists().isEmpty();
       }
      
       public void removeChild(Object id) {
       getArtists().remove(id);
       }
      
       public void setData(Object data) {
       }
      
       public void setParent(TreeNode parent) {
       }
      
       public String getType() {
       return "library";
       }
      
       private long nextId = 0;
      
       private long getNextId() {
       return nextId++;
       }
      
       private Map albumCache = new HashMap();
      
       private Map artistCache = new HashMap();
      
       private Artist getArtistByName(String name, Library library) {
       Artist artist = (Artist) artistCache.get(name);
       if (artist == null) {
       artist = new Artist(getNextId());
       artist.setName(name);
       artistCache.put(name, artist);
       library.addArtist(artist);
       }
       return artist;
       }
      
       private Album getAlbumByTitle(String title, Artist artist) {
       Album album = (Album) albumCache.get(title);
       if (album == null) {
       album = new Album(getNextId());
       album.setTitle(title);
       albumCache.put(title, album);
       artist.addAlbum(album);
       }
       return album;
       }
      
       private void initData() {
       artists = new HashMap();
       InputStream is = this.getClass().getClassLoader().getResourceAsStream("data/data.txt");
       ByteArrayOutputStream os = new ByteArrayOutputStream();
       byte[] rb = new byte[1024];
       int read;
       try {
       do {
       read = is.read(rb);
       if (read > 0) {
       os.write(rb, 0, read);
       }
       } while (read > 0);
       String buf = os.toString();
       StringTokenizer toc1 = new StringTokenizer(buf, "\n");
       while (toc1.hasMoreTokens()) {
       String str = toc1.nextToken();
       StringTokenizer toc2 = new StringTokenizer(str, "\t");
       String songTitle = toc2.nextToken();
       String artistName = toc2.nextToken();
       String albumTitle = toc2.nextToken();
       toc2.nextToken();
       toc2.nextToken();
       String albumYear = toc2.nextToken();
       Artist artist = getArtistByName(artistName, this);
       Album album = getAlbumByTitle(albumTitle, artist);
       album.setYear(new Integer(albumYear));
       Song song = new Song(getNextId());
       song.setTitle(songTitle);
       album.addSong(song);
       }
       } catch (IOException e) {
       throw new RuntimeException(e);
       }
       }
      
       public Object getState1() {
       return state1;
       }
      
       public void setState1(Object state1) {
       this.state1 = state1;
       }
      
       public Object getState2() {
       return state2;
       }
      
       public void setState2(Object state2) {
       this.state2 = state2;
       }
      
       public void walk(TreeNode node, List<TreeNode> appendTo, Class<? extends TreeNode> type) {
       if (type.isInstance(node)) {
       appendTo.add(node);
       }
       Iterator<Map.Entry<Object, TreeNode>> iterator = node.getChildren();
       while (iterator.hasNext()) {
       walk(iterator.next().getValue(), appendTo, type);
       }
      
       }
      
       public ArrayList getLibraryAsList() {
       ArrayList appendTo = new ArrayList();
       walk(this, appendTo, Song.class);
       return appendTo;
       }
      
       public List<Song> getSongsList() {
       return songsList;
       }
      
       public void setSongsList(List<Song> songsList) {
       this.songsList = songsList;
       }
      
       public void takeSelection() {
       selectedSongsList.clear();
       selectedSongsList.addAll(selectedSongsSet);
       }
      
       public Set<Song> getSelectedSongsSet() {
       return selectedSongsSet;
       }
      
       public void setSelectedSongsSet(Set<Song> selectedSongsSet) {
       this.selectedSongsSet = selectedSongsSet;
       }
      
       public List<Song> getSelectedSongsList() {
       return selectedSongsList;
       }
      
       public void setSelectedSongsList(List<Song> selectedSongsList) {
       this.selectedSongsList = selectedSongsList;
       }
      
       // ++ FED 160309
       private List<String> complementInfo = new ArrayList<String>();
      
       /**
       * on souhaite en plus de l'arbre, afficher une description complémentaire de l'item selectionné dans une zone de texte à droite de l'arborescence
       *
       * @param event
       * @return void
       */
       public void processSelection(NodeSelectedEvent event) {
       HtmlTree tree = (HtmlTree) event.getComponent();
       Object rowData = tree.getRowData();
       complementInfo.clear();
       // obtient le type de l'objet qui est selectionné, est-ce celui-ci ?
       if (rowData instanceof Song) {
       long id = ((Song) rowData).getId();
       String title = ((Song) rowData).getTitle();
       String parentID = ((Song) rowData).getParent().toString();
       complementInfo.add("song id : " + id);
       complementInfo.add("title : " + title);
       complementInfo.add("parentID : " + parentID);
       }
       else if (rowData instanceof Album) {
       Album alb = (Album) rowData;
       complementInfo.add("id : " + alb.getId());
       complementInfo.add("data : " + alb.getData());
       complementInfo.add("title : " + alb.getTitle());
       complementInfo.add("artist : " + alb.getArtist());
       complementInfo.add("parentID : " + alb.getParent().toString());
       complementInfo.add("childs : " + alb.getChildren().toString());
       }
       }
      
       /**
       * @return the complementInfo
       */
       public List<String> getComplementInfo() {
       return this.complementInfo;
       }
      
       /**
       * @param complementInfo
       * the complementInfo to set
       */
       public void setComplementInfo(List<String> complementInfo) {
       this.complementInfo = complementInfo;
       }
      
       /**
       * gère le dépot d'un node dans sur un autre
       *
       * @param dropEvent
       * @return void
       */
       public void dropListener(DropEvent dropEvent) {
      
       // resolve drag destination attributes
       UITreeNode destNode = (dropEvent.getSource() instanceof UITreeNode) ? (UITreeNode) dropEvent.getSource() : null;
       UITree destTree = destNode != null ? destNode.getUITree() : null;
       TreeRowKey dropNodeKey = (dropEvent.getDropValue() instanceof TreeRowKey) ? (TreeRowKey) dropEvent.getDropValue() : null;
       TreeNode droppedInNode = dropNodeKey != null ? destTree.getTreeNode(dropNodeKey) : null;
      
       // resolve drag source attributes
       UITreeNode srcNode = (dropEvent.getDraggableSource() instanceof UITreeNode) ? (UITreeNode) dropEvent.getDraggableSource() : null;
       UITree srcTree = srcNode != null ? srcNode.getUITree() : null;
       TreeRowKey dragNodeKey = (dropEvent.getDragValue() instanceof TreeRowKey) ? (TreeRowKey) dropEvent.getDragValue() : null;
       TreeNode draggedNode = dragNodeKey != null ? srcTree.getTreeNode(dragNodeKey) : null;
       if (dropEvent.getDraggableSource() instanceof UIDragSupport && srcTree == null && draggedNode == null && dropEvent.getDragValue() instanceof TreeNode) {
       srcTree = destTree;
       draggedNode = (TreeNode) dropEvent.getDragValue();
       dragNodeKey = srcTree.getTreeNodeRowKey(draggedNode) instanceof TreeRowKey ? (TreeRowKey) srcTree.getTreeNodeRowKey(draggedNode) : null;
       }
      
       FacesContext context = FacesContext.getCurrentInstance();
      
       if (dropNodeKey != null) {
       // add destination node for rerender
       destTree.addRequestKey(dropNodeKey);
      
       Object state = null;
       if (dragNodeKey != null) {
       // Drag from this or other tree
       TreeNode parentNode = draggedNode.getParent();
      
       // 1. remove node from tree
       state = srcTree.removeNode(dragNodeKey);
      
       // ++ FED 170309 1811
       parentNode.removeChild(dragNodeKey);
      
       // 2. add parent for rerender
       Object rowKey = srcTree.getTreeNodeRowKey(parentNode);
       srcTree.addRequestKey(rowKey);
      
       // gère l'affichage de la zone d'affichage compélemtaire située à droite de l'arbre
       if (dropEvent.getDraggableSource() instanceof UIDragSupport) {
       // -- FED 160309 1233 selectedNodeChildren.remove(draggedNode);
       // if node was gragged in it's parent place dragged node to
       // the end of selected nodes in grid
       // -- FED 160309 1233 if (droppedInNode.equals(parentNode)) {
       // -- FED 160309 1233 selectedNodeChildren.add(draggedNode);
       // -- FED 160309 1233 }
       }
       } else if (dropEvent.getDragValue() != null) {
       System.out.print(dropEvent.getDragValue().toString());
       // Drag from some drag source (mais pas un noeud existant (autre objet mais contenant des données))
       // -- FED 160309 1233 draggedNode = new DemoTreeNodeImpl<String>();
       // -- FED 160309 1233 draggedNode.setData(dropEvent.getDragValue().toString());
       }
      
       // generate new node id
       Object id = getNewId(destTree.getTreeNode(dropNodeKey));
       destTree.addNode(dropNodeKey, draggedNode, id, state);
       }
      
       AjaxContext ac = AjaxContext.getCurrentInstance();
      
       // Add destination tree to reRender
       try {
       ac.addComponentToAjaxRender(destTree);
       } catch (Exception e) {
       System.err.print(e.getMessage());
       }
      
       // Add source tree to reRender
       try {
       ac.addComponentToAjaxRender(srcTree);
       } catch (Exception e) {
       System.err.print(e.getMessage());
       }
       }
      
       /**
       * @param parentNode
       * @return
       * @return Object
       */
       private Object getNewId(TreeNode parentNode) {
       Map<Object, TreeNode> childs = new HashMap<Object, TreeNode>();
       Iterator<Map.Entry<Object, TreeNode>> iter = parentNode.getChildren();
       while (iter != null && iter.hasNext()) {
       Map.Entry<Object, TreeNode> entry = iter.next();
       childs.put(entry.getKey(), entry.getValue());
       }
      
       Integer index = 1;
       while (childs.containsKey(index)) {
       index++;
       }
       return index;
       }
      }



      tks a lot,
      Franck.

        • 1. Re: tree + drag & drop : dropped node is not added to the tr

          I forgot to mention my xhtml page to display this tree.
          note: I've changed the name of the processDrop to dropListener.

          <ui:composition xmlns="http://www.w3.org/1999/xhtml"
           xmlns:s="http://jboss.com/products/seam/taglib"
           xmlns:ui="http://java.sun.com/jsf/facelets"
           xmlns:f="http://java.sun.com/jsf/core"
           xmlns:h="http://java.sun.com/jsf/html"
           xmlns:rich="http://richfaces.org/rich"
           template="/layout/template.xhtml">
          
          <ui:define name="body">
          
           <p>test drag and drop</p><br/>
           <rich:dragIndicator id="indicator3" />
          
           <h:form>
           <h:panelGrid columns="2" width="100%" columnClasses="col1,col2">
          
           <rich:tree ajaxKeys="#{null}" style="width:300px" nodeSelectListener="#{library.processSelection}"
           reRender="selectedNode" ajaxSubmitSelection="true" switchType="ajax" dragIndicator="indicator3"
           value="#{library.data}" id="tree" var="item" nodeFace="#{item.type}" treeNodeVar="treeNode"
           dropListener="#{library.dropListener}">
          
           <rich:treeNode type="artist" icon="/images/tree/singer.gif" iconLeaf="/images/tree/singer.gif" acceptedTypes="album">
           <h:outputText value="#{item.name} #{item.id}" />
           </rich:treeNode>
           <rich:treeNode type="album" icon="/images/tree/disc.gif" iconLeaf="/images/tree/disc.gif" dragType="album" acceptedTypes="song">
           <h:outputText value="#{item.title} #{item.id}" />
           <rich:dndParam name="label" type="drag" value="Album: #{item.title}" />
           </rich:treeNode>
           <rich:treeNode type="song" icon="/images/tree/song.gif" iconLeaf="/images/tree/song.gif" dragType="song">
           <h:outputText value="#{item.title} #{item.id}" />
           <rich:dndParam name="label" type="drag" value="Song: #{item.title}" />
           </rich:treeNode>
          
           </rich:tree>
          
           <rich:panel id="selectedNode">
           <rich:dataGrid id="selectedNodeGrid" style="display: #{!empty library.complementInfo ? '' : 'none'}" value="#{library.complementInfo}" var="item" columns="2" border="0">
           <h:outputText value="#{item}"/>
           </rich:dataGrid>
           </rich:panel>
           </h:panelGrid>
          
           </h:form>
          
          </ui:define>
          
          </ui:composition>
          


          • 2. Re: tree + drag & drop : dropped node is not added to the tr

            it's working now with this implementation in the dropListener method :

            ListRowKey srcKey = (ListRowKey) dropEvent.getDragValue();
             ListRowKey destKey = (ListRowKey) dropEvent.getDropValue();
             UITree tree = ((UITreeNode) dropEvent.getComponent()).getUITree();
             TreeNode destNode = tree.getTreeNode(destKey);
             TreeNode srcNode = tree.getTreeNode(srcKey);
             try {
             tree.queueNodeCollapse(srcKey);
             tree.queueNodeExpand(destKey);
             } catch (IOException e) {
             throw new FacesException(e.getMessage(), e);
             }
            
             // String srcNodeKey = srcTree.getTreeNode(dragNodeKey); // String.valueOf(((TreeNode) srcNode).getId());
             String srcNodeKey = String.valueOf(((IdentifierTreeNode) srcNode).getId());
             srcNode.getParent().removeChild(srcNodeKey);
             destNode.addChild(srcNodeKey, srcNode);
             List list = new ArrayList();
             // this is to get row key segments for the parent node of source node
             // candidate for inclusion to UITree API
             for (Iterator iterator = srcKey.iterator(); iterator.hasNext();) {
             Object object = (Object) iterator.next();
             if (iterator.hasNext()) {
             list.add(object);
             }
             }
             tree.addRequestKey(new ListRowKey(list));
             tree.addRequestKey(destKey);


            To Jboss richFace team, the sample code in the 3.3.0 GA version doesn't exactly refer to the documentation.

            • 3. Re: tree + drag & drop : dropped node is not added to the tr
              ilya_shaikovsky

              the sample with sources available at livedemo.

              • 4. Re: tree + drag & drop : dropped node is not added to the tr

                Sorry, but the source code for moving complex object (album, song, ...) with a drag & drop operation over a tree like it's described at http://livedemo.exadel.com/richfaces-demo/richfaces/tree.jsf?tab=dnd is not detailled in the "library tree" sample at http://livedemo.exadel.com/richfaces-demo/richfaces/tree.jsf?tab=usage and the first one can't be transposed directly to the second.

                The 'dropListener' method I've pasted in my previous post come from an old RichFaces sample for this library tree of 2007. So, the source code for the implementation that figure in the documentation at http://www.jboss.org/file-access/default/members/jbossrichfaces/freezone/docs/devguide/en/html/tree.html#tsebro is not available.