Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GWT - Add and remove nodes in celltree

Tags:

add

gwt

Here I have an complete and very easy example to dynamically add/remove nodes to an celltree. My example is not working very well. It seems there is an refresh problem. Only closing/expanding the nodes will show the correct result. I also did not found any answer in this forum which fits to this problem. Maybe somebody can try my example and tell me where the problem is. Any other hint is also very appreciated.

Greetings, Marco

import java.util.ArrayList;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.CellTree;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SingleSelectionModel;
import com.google.gwt.view.client.TreeViewModel;

public class MyCelltreeTest implements EntryPoint {
  private AbsolutePanel absolutePanel;
  private CellTree cellTree;
  private Button btnAdd;
  private Button btnRemove;
  private MyTreeModel myTreeModel;
  private SingleSelectionModel<MyNode> selectionModelCellTree = null;

  @Override
  public void onModuleLoad() {
    RootPanel rootPanel = RootPanel.get();
    rootPanel.add(getAbsolutePanel(), 0, 0);
  }

  private AbsolutePanel getAbsolutePanel() {
    if (absolutePanel == null) {
      absolutePanel = new AbsolutePanel();
      absolutePanel.setSize("612px", "482px");
      absolutePanel.add(getCellTree(), 0, 0);
      absolutePanel.add(getBtnAdd(), 265, 428);
      absolutePanel.add(getBtnRemove(), 336, 428);
    }
    return absolutePanel;
  }

  private CellTree getCellTree() {
    if (cellTree == null) {
      myTreeModel = new MyTreeModel();
      cellTree = new CellTree(myTreeModel, null);
      cellTree.setSize("285px", "401px");
    }
    return cellTree;
  }

  private Button getBtnAdd() {
    if (btnAdd == null) {
      btnAdd = new Button("Add");
      btnAdd.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {

          MyNode node =   selectionModelCellTree.getSelectedObject();
          if(node != null)
            myTreeModel.addNew(node, "Bla");
        }
      });
    }
    return btnAdd;
  }

  private Button getBtnRemove() {
    if (btnRemove == null) {
      btnRemove = new Button("Remove");
      btnRemove.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
          MyNode node = selectionModelCellTree.getSelectedObject();
          if(node != null)
            myTreeModel.remove(node);
        }
      });
    }
    return btnRemove;
  }

  public class MyNode {
    private String name;
    private ArrayList<MyNode> childs; //nodes childrens
    private MyNode parent; //track internal parent
    private MyCell cell; //for refresh - reference to visual component

    public MyNode(String name) {
      super();
      parent = null;
      this.name = name;
      childs = new ArrayList<MyNode>();
    }

    public void addSubMenu(MyNode m) {
      m.parent = this;
      childs.add(m);
    }

    public void removeMenu(MyNode m) {

      m.getParent().childs.remove(m);
    }

    public boolean hasChildrens() {
      return childs.size()>0;
    }

    public ArrayList<MyNode> getList() {
      return childs;
    }

    public MyNode getParent() {
      return parent;
    }

    public void setCell(MyCell cell) {
      this.cell = cell;
    }

    public void refresh() {
      if(parent!=null) {
        parent.refresh();
      }
      if (cell!=null) {
        cell.refresh(); //refresh tree
      }
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }
  }

  public class MyTreeModel implements TreeViewModel {
    private MyNode officialRoot; //default not dynamic
    private MyNode studentRoot; //default not dynamic
    private MyNode testRoot; //default not dynamic
    private MyNode root;

    public MyNode getRoot() { // to set CellTree root
      return root;
    }

    public MyTreeModel() {
      selectionModelCellTree = new SingleSelectionModel<MyNode>();
      root = new MyNode("root");
      // Default items
      officialRoot = new MyNode("Cat"); //some basic static data
      studentRoot = new MyNode("Dog");
      testRoot = new MyNode("Fish");
      root.addSubMenu(officialRoot);
      root.addSubMenu(studentRoot);
      root.addSubMenu(testRoot);
    }

    //example of add add logic
    public void addNew(MyNode myparent, String name) {
      myparent.addSubMenu(new MyNode(name));
      myparent.refresh(); //HERE refresh tree
    }
    public void remove(MyNode objToRemove) {

      objToRemove.removeMenu(objToRemove);
      objToRemove.refresh();
    }

    @Override
    public <T> NodeInfo<?> getNodeInfo(T value) {
      ListDataProvider<MyNode> dataProvider;
      MyNode myValue = null;
      if (value == null) { // root is not set
        dataProvider = new ListDataProvider<MyNode>(root.getList());
      } else {
        myValue = (MyNode) value;
        dataProvider = new ListDataProvider<MyNode>(myValue.getList());
      }
      MyCell cell = new MyCell(dataProvider); //HERE Add reference
      if (myValue != null)
        myValue.setCell(cell);
      return new DefaultNodeInfo<MyNode>(dataProvider, cell, selectionModelCellTree, null);
    }

    @Override
    public boolean isLeaf(Object value) {
      if (value instanceof MyNode) {
        MyNode t = (MyNode) value;
        if (!t.hasChildrens())
          return true;
        return false;
      }
      return false;
    }
  }

  public class MyCell extends AbstractCell<MyNode> {
    ListDataProvider<MyNode> dataProvider; //for refresh

    public MyCell(ListDataProvider<MyNode> dataProvider) {
      super();
      this.dataProvider = dataProvider;
    }
    public void refresh() {
      dataProvider.refresh();
    }

    @Override
    public void render(Context context, MyNode value, SafeHtmlBuilder sb) {
      if (value == null) {
        return;
      }
      sb.appendEscaped(value.getName());
    }
  }
}

Thanks Ümit for your explanation. I tried the close-reopen version. I have replaced my refresh method with the methods below. Adding is working but removing not. Very strange the whole topic. I'm very suprised that these basic functions are not really supported by GWT. Can somebody give me more help to run a real working example.

    public void refresh() {

         closeReopenTreeNodes(cellTree.getRootTreeNode());
    }

    private void closeReopenTreeNodes(TreeNode node) {
        if(node == null) {
            return;
        }
        for(int i = 0; i < node.getChildCount(); i++) {

             if(node.getChildValue(i).equals(this)){

                 if(node.getParent() != null){

                     node.getParent().setChildOpen(i, false);
                     //node.getParent().setChildOpen(i, true);
                 }

                 node.setChildOpen(i, false);
                 node.setChildOpen(i, true);
             }               
             TreeNode child = node.setChildOpen(i, node.isChildOpen(i));
             closeReopenTreeNodes(child);
        }
    }

Here my third try: This way is recommended by gwt-commiter. Please see following issue: http://code.google.com/p/google-web-toolkit/issues/detail?id=7160

Current status: * Adding is possible * Removing is possible if not last child!

So, last open point, refresh the tree if last open child!

package com.test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.CellTree;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SingleSelectionModel;
import com.google.gwt.view.client.TreeViewModel;

public class MyCelltreeTest2 implements EntryPoint {
    private AbsolutePanel absolutePanel;
    private CellTree cellTree;
    private Button btnAdd;
    private Button btnRemove;
    private MyTreeModel myTreeModel;
    private SingleSelectionModel<MyNode> selectionModelCellTree = null;
    private Map<MyNode, ListDataProvider<MyNode>> mapDataProviders = null;
    private ListDataProvider<MyNode> rootDataProvider = null;

    public void onModuleLoad() {
            RootPanel rootPanel = RootPanel.get();
            rootPanel.add(getAbsolutePanel(), 0, 0);
    }

    private AbsolutePanel getAbsolutePanel() {
            if (absolutePanel == null) {
                    absolutePanel = new AbsolutePanel();
                    absolutePanel.setSize("612px", "482px");
                    absolutePanel.add(getCellTree(), 0, 0);
                    absolutePanel.add(getBtnAdd(), 265, 428);
                    absolutePanel.add(getBtnRemove(), 336, 428);
            }
            return absolutePanel;
    }
    private CellTree getCellTree() {
            if (cellTree == null) {
                    myTreeModel = new MyTreeModel();
                    cellTree = new CellTree(myTreeModel, null);
                    cellTree.setSize("285px", "401px");
            }
            return cellTree;
    }
    private Button getBtnAdd() {
            if (btnAdd == null) {
                    btnAdd = new Button("Add");
                    btnAdd.addClickHandler(new ClickHandler() {
                            public void onClick(ClickEvent event) {

                    MyNode node = selectionModelCellTree.getSelectedObject();

                                 myTreeModel.add(node, "Bla");
                            }
                    });
            }
            return btnAdd;
    }
    private Button getBtnRemove() {
            if (btnRemove == null) {
                    btnRemove = new Button("Remove");
                    btnRemove.addClickHandler(new ClickHandler() {
                            public void onClick(ClickEvent event) {

                             MyNode node = selectionModelCellTree.getSelectedObject();

                                     myTreeModel.remove(node);
                            }
                    });
            }
            return btnRemove;
    }


    public class MyNode {

        private String name;
        private ArrayList<MyNode> childs; //nodes childrens
        private MyNode parent; //track internal parent


        public MyNode(String name) {
            super();
            parent = null;
            this.name = name;
            childs = new ArrayList<MyNode>();
        }
        public boolean hasChildrens() {
            return childs.size()>0;
        }
        public ArrayList<MyNode> getList() {
            return childs;
        }
        public MyNode getParent() {
            return parent;
        }

        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }      
    }

    public class MyTreeModel implements TreeViewModel {


        public MyTreeModel() {
            selectionModelCellTree = new SingleSelectionModel<MyNode>();
            mapDataProviders = new HashMap<MyCelltreeTest2.MyNode, ListDataProvider<MyNode>>();
        }

        public void add(MyNode myparent, String name) {

            MyNode child = new MyNode(name);

            //root-node
            if(myparent == null){
                    rootDataProvider.getList().add(child);
                    mapDataProviders.put(child, rootDataProvider);
            }
            else{

                    ListDataProvider<MyNode> dataprovider = mapDataProviders.get(myparent);
                    myparent.childs.add(child);
                    child.parent = myparent;
                    dataprovider.refresh();
            }
        }   
        public void remove(MyNode objToRemove) {

            ListDataProvider<MyNode> dataprovider = mapDataProviders.get(objToRemove);
                    dataprovider.getList().remove(objToRemove);
  //                 mapDataProviders.remove(objToRemove);
                    dataprovider.refresh();
                    dataprovider.flush();

                    if(objToRemove.parent != null){
                            ListDataProvider<MyNode> dataproviderParent = mapDataProviders.get(objToRemove.parent);
                            objToRemove.parent.childs.remove(objToRemove);
                            dataproviderParent.refresh();
                            dataproviderParent.flush();
                    }
                    else{
                            rootDataProvider.refresh();
                            rootDataProvider.flush();
                    }       
        } 


        @Override
        public <T> NodeInfo<?> getNodeInfo(T value) {

            if (value == null) { // root is not set
             rootDataProvider = new ListDataProvider<MyNode>(new ArrayList<MyNode>());
                    MyCell cell = new MyCell(); 
   return new DefaultNodeInfo<MyNode>(rootDataProvider, cell,   
  selectionModelCellTree, null);
            } else {
                    MyNode myValue = (MyNode) value;
                ListDataProvider<MyNode> dataProvider = 
                    new ListDataProvider<MyNode>(myValue.childs);
                    MyCell cell = new MyCell(); 
                    for(MyNode currentNode : myValue.childs){
                            mapDataProviders.put(currentNode, dataProvider);
                    }
                return new DefaultNodeInfo<MyNode>(dataProvider, cell,  
              selectionModelCellTree, null);
            }
        }

        @Override
        public boolean isLeaf(Object value) {
            if (value instanceof MyNode) {
                MyNode t = (MyNode) value;
                if (!t.hasChildrens())
                    return true;
                return false;
            }
            return false; 
        }

    }

    public class MyCell extends AbstractCell<MyNode> {
            public MyCell() {
              super();
            }
            @Override
            public void render(Context context, MyNode value, SafeHtmlBuilder sb) {
              if (value == null) {
                return;
              }
              sb.appendEscaped(value.getName());
            }
    }
}
like image 571
user1165474 Avatar asked Jan 28 '12 10:01

user1165474


1 Answers

This is somehow a known problem with CellTree.
The reason for the refresh problem is that in the getNodeInfo() function you create a new ListDataProvider instance for each CellTree level.
The CellTree only updates/refreshes itself if you update the items in that ListDataProvider. I believe that your removeMenu() and addSubMenu() functions add and remove items from the original list stored in your MyNode class but won't update the list in the corresponding ListDataProviders (you can try to check that in debug mode).
The reason why the CellTree is refreshed when you close and re -open the nodes is because when you re-open the nodes the getNodeInfo() is called again and the whole CellTree structure will be constructed again (including the new menu or without the removed one respectively).

There are two possible solutions:

  1. Keep a reference for each of the ListDataProviders somewhere and call remove/add on that list (although you do that I assume that the items are not really added/removed there).
  2. Programatically close all nodes and re-open it.

Both are somehow a PITA to implement. Unfortunately there is no easy way around it.

like image 141
Ümit Avatar answered Oct 30 '22 00:10

Ümit