]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/TreeNode.java
Fix column width issues on HiDPI displays. KeyTiSelection fixes.
[simantics/platform.git] / bundles / org.simantics.browsing.ui.nattable / src / org / simantics / browsing / ui / nattable / TreeNode.java
index 941c6f319be56e491909cc35472e7173a5040ccf..4f9273d9120cfd48547986b1fcb630120fe98582 100644 (file)
-package org.simantics.browsing.ui.nattable;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import org.eclipse.core.runtime.IAdaptable;\r
-import org.eclipse.jface.resource.ColorDescriptor;\r
-import org.eclipse.jface.resource.FontDescriptor;\r
-import org.eclipse.jface.resource.ImageDescriptor;\r
-import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;\r
-import org.eclipse.nebula.widgets.nattable.style.Style;\r
-import org.eclipse.swt.graphics.Color;\r
-import org.eclipse.swt.graphics.Font;\r
-import org.eclipse.swt.graphics.Image;\r
-import org.simantics.browsing.ui.BuiltinKeys;\r
-import org.simantics.browsing.ui.NodeContext;\r
-import org.simantics.browsing.ui.common.internal.GENodeQueryManager;\r
-import org.simantics.browsing.ui.content.ImageDecorator;\r
-import org.simantics.browsing.ui.content.Imager;\r
-import org.simantics.browsing.ui.content.LabelDecorator;\r
-import org.simantics.browsing.ui.content.Labeler;\r
-import org.simantics.browsing.ui.nattable.NatTableGraphExplorer.GECache2;\r
-import org.simantics.browsing.ui.nattable.NatTableGraphExplorer.GeViewerContext;\r
-import org.simantics.browsing.ui.swt.ViewerRowReference;\r
-import org.simantics.utils.datastructures.BijectionMap;\r
-\r
-public class TreeNode implements IAdaptable {\r
-       private static boolean DEBUG = false;\r
-       \r
-       private NodeContext context;\r
-       GENodeQueryManager manager;\r
-       GeViewerContext explorerContext;\r
-       \r
-       TreeNode parent;\r
-       List<TreeNode> children = new ArrayList<TreeNode>();\r
-       boolean expanded;\r
-       \r
-       public TreeNode(NodeContext context, GeViewerContext explorerContext) {\r
-               this.context = context;\r
-               this.explorerContext = explorerContext;\r
-               this.expanded = false;\r
-               manager = new GENodeQueryManager(explorerContext, null, null, ViewerRowReference.create(this));\r
-               explorerContext.getContextToNodeMap().add(context, this);\r
-       }\r
-       \r
-       int getDepth() {\r
-               if (parent == null)\r
-                       return 0;\r
-               return parent.getDepth() + 1;\r
-       }\r
-       \r
-       int listIndex;\r
-       \r
-       public int getListIndex() {\r
-               return listIndex;\r
-       }\r
-       \r
-       public void setListIndex(int listIndex) {\r
-               this.listIndex = listIndex;\r
-       }\r
-       \r
-       List<TreeNode> getChildren() {\r
-               return children;\r
-       }\r
-       \r
-       public TreeNode getParent() {\r
-               return parent;\r
-       }\r
-       \r
-       public void setExpanded(boolean expanded) {\r
-               this.expanded = expanded;\r
-       }\r
-       \r
-       public boolean isExpanded() {\r
-               return expanded;\r
-       }\r
-       \r
-       public NodeContext getContext() {\r
-               return context;\r
-       }\r
-       \r
-       private Labeler labeler;\r
-       private Imager imager;\r
-       Collection<LabelDecorator> labelDecorators;\r
-       Collection<ImageDecorator> imageDecorators;\r
-        \r
-       Map<String, String> labels;\r
-       Map<String, String> runtimeLabels;\r
-       \r
-       public String getValueString(int column) {\r
-               if (column == 0) {\r
-                       initData();\r
-               }\r
-               if (labeler != null) {\r
-                       String key = explorerContext.getGe().getColumns()[column].getKey();\r
-                       String s = null;\r
-                       if (runtimeLabels != null)\r
-                               s = runtimeLabels.get(key);\r
-                       if (s == null)\r
-                               s = labels.get(key);\r
-                       if (labelDecorators != null && !labelDecorators.isEmpty()) {\r
-                               int index = 0;\r
-                               for (LabelDecorator ld : labelDecorators) {\r
-                                       String ds = ld.decorateLabel(s, key, index);\r
-                                       if (ds != null)\r
-                                               s = ds;\r
-                               }\r
-                       }\r
-                       return s;\r
-               }\r
-               return null;\r
-       }\r
-       \r
-       public Image getImage(int column) {\r
-               String key = explorerContext.getGe().getColumns()[column].getKey();\r
-               if (imager != null) {\r
-                       Object descOrImage = null;\r
-                       boolean hasUncachedImages = false;\r
-\r
-                       ImageDescriptor desc = imager.getImage(key);\r
-                       if (desc != null) {\r
-                               int index = 0;\r
-                               // Attempt to decorate the label\r
-                               if (!imageDecorators.isEmpty()) {\r
-                                       for (ImageDecorator id : imageDecorators) {\r
-                                               ImageDescriptor ds = id.decorateImage(desc, key, index);\r
-                                               if (ds != null)\r
-                                                       desc = ds;\r
-                                       }\r
-                               }\r
-\r
-                               // Try resolving only cached images here and now\r
-                               Object img = explorerContext.getGe().localResourceManager.find(desc);\r
-                               if (img == null)\r
-                                       img = explorerContext.getGe().resourceManager.find(desc);\r
-\r
-                               descOrImage = img != null ? img : desc;\r
-                               hasUncachedImages |= img == null;\r
-                       }\r
-\r
-                       if (!hasUncachedImages) {\r
-                               return (Image) descOrImage;\r
-                       } else {\r
-                               // Schedule loading to another thread to refrain from\r
-                               // blocking\r
-                               // the UI with database operations.\r
-                               explorerContext.getGe().queueImageTask(this, new ImageTask(this, descOrImage));\r
-                               return null;\r
-                       }\r
-               } else {\r
-                       return null;\r
-               }\r
-       }\r
-       \r
-       public void getStyle(int column, Style style) {\r
-               String key = explorerContext.getGe().getColumns()[column].getKey();\r
-               FontDescriptor font = explorerContext.getGe().originalFont;\r
-               ColorDescriptor bg = explorerContext.getGe().originalBackground;\r
-               ColorDescriptor fg = explorerContext.getGe().originalForeground;\r
-               \r
-               // Attempt to decorate the label\r
-               if (labelDecorators != null && !labelDecorators.isEmpty()) {\r
-                       int index = 0;\r
-                       for (LabelDecorator ld : labelDecorators) {\r
-\r
-                               FontDescriptor dfont = ld.decorateFont(font, key, index);\r
-                               if (dfont != null)\r
-                                       font = dfont;\r
-\r
-                               ColorDescriptor dbg = ld.decorateBackground(bg, key, index);\r
-                               if (dbg != null)\r
-                                       bg = dbg;\r
-\r
-                               ColorDescriptor dfg = ld.decorateForeground(fg, key, index);\r
-                               if (dfg != null)\r
-                                       fg = dfg;\r
-                       }\r
-               }\r
-\r
-               if (font != explorerContext.getGe().originalFont) {\r
-                       // System.out.println("set font: " + index + ": " +\r
-                       // font);\r
-                       style.setAttributeValue(CellStyleAttributes.FONT,(Font) explorerContext.getGe().localResourceManager.get(font));\r
-               } else {\r
-                       style.setAttributeValue(CellStyleAttributes.FONT,(Font) (explorerContext.getGe().originalFont != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalFont) : null));\r
-               }\r
-               if (bg != explorerContext.getGe().originalBackground)\r
-                       style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(bg));\r
-               else\r
-                       style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) (explorerContext.getGe().originalBackground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalBackground) : null));\r
-               if (fg != explorerContext.getGe().originalForeground)\r
-                       style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(fg));\r
-               else\r
-                       style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) (explorerContext.getGe().originalForeground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalForeground) : null));\r
-\r
-       }\r
-       \r
-       private void initData() {\r
-               labeler = manager.query(context, BuiltinKeys.SELECTED_LABELER);\r
-               imager = manager.query(context, BuiltinKeys.SELECTED_IMAGER);\r
-               labelDecorators = manager.query(context, BuiltinKeys.LABEL_DECORATORS);\r
-               imageDecorators = manager.query(context, BuiltinKeys.IMAGE_DECORATORS);\r
-               \r
-               if (labeler != null) {\r
-               labels = labeler.getLabels();\r
-                       runtimeLabels = labeler.getRuntimeLabels();\r
-       } else {\r
-               labels = null;\r
-               runtimeLabels = null;\r
-       }\r
-       }\r
-       \r
-       public TreeNode addChild(NodeContext context, GeViewerContext explorerContext) {\r
-               TreeNode child = new TreeNode(context, explorerContext);\r
-               child.parent = this;\r
-               children.add(child);\r
-               if (DEBUG) System.out.println("Add " + this  + " -> " + child);\r
-               return child;\r
-       }\r
-       \r
-       public TreeNode addChild(int index, NodeContext context,GeViewerContext explorerContext) {\r
-               \r
-               TreeNode child = new TreeNode(context, explorerContext);\r
-               child.parent = this;\r
-               children.add(index,child);\r
-               if (DEBUG) System.out.println("Add " + this  + " -> " + child + " at " + index);\r
-               return child;\r
-       }\r
-       \r
-       public TreeNode setChild(int index, NodeContext context, GeViewerContext explorerContext) {\r
-               \r
-               TreeNode child = new TreeNode(context, explorerContext);\r
-               child.parent = this;\r
-               children.set(index,child);\r
-               if (DEBUG) System.out.println("Set " + this  + " -> " + child + " at " + index);\r
-               return child;\r
-       }\r
-       \r
-       public void dispose() {\r
-               if (parent != null)\r
-                       parent.children.remove(this);\r
-               dispose2();\r
-       }\r
-       \r
-       public void dispose2() {\r
-               if (DEBUG)      System.out.println("dispose " + this);\r
-               parent = null;\r
-               for (TreeNode n : children) {\r
-                       n.dispose2();\r
-               }\r
-               clearCache();\r
-               children.clear();\r
-               explorerContext.getContextToNodeMap().remove(context, this);\r
-               context = null;\r
-               explorerContext = null;\r
-               manager.dispose();\r
-               manager = null; \r
-       }\r
-       \r
-       private void clearCache() {\r
-               if (explorerContext != null) {\r
-                       GECache2 cache = explorerContext.cache;\r
-                       \r
-                       if (cache != null) {\r
-                               cache.dispose(context);\r
-                       }\r
-               }\r
-       }\r
-       \r
-       public boolean updateChildren() {\r
-               if (context == null)\r
-                       throw new IllegalStateException("Node is disposed.");\r
-               \r
-               NodeContext[] childContexts = manager.query(context, BuiltinKeys.FINAL_CHILDREN);\r
-               \r
-               if (DEBUG) System.out.println("updateChildren " + childContexts.length + " " + this);\r
-               \r
-               \r
-               boolean modified = false;\r
-               synchronized (children) {\r
-                       \r
-                       int oldCount = children.size();\r
-                       BijectionMap<Integer, Integer> indexes = new BijectionMap<Integer, Integer>();\r
-                       Set<Integer> mapped = new HashSet<Integer>();\r
-                       boolean reorder = false;\r
-                       // locate matching pairs form old and new children\r
-                       for (int i = 0; i < oldCount; i++) {\r
-                               NodeContext oldCtx = children.get(i).context;\r
-                               for (int j = 0; j <childContexts.length; j++) {\r
-                                       if (mapped.contains(j))\r
-                                               continue;\r
-                                       if (oldCtx.equals(childContexts[j])) {\r
-                                               indexes.map(i, j);\r
-                                               mapped.add(j);\r
-                                               if (i != j)\r
-                                                       reorder = true;\r
-                                               break;\r
-                                       }\r
-                               }\r
-                       }\r
-                       // update children if required\r
-                       if (childContexts.length != oldCount || reorder || childContexts.length != indexes.size()) {\r
-                               modified = true;\r
-                               List<TreeNode> oldChildren = new ArrayList<TreeNode>(oldCount);\r
-                               oldChildren.addAll(children);\r
-                               if (childContexts.length >= oldCount) {\r
-                       for (int i = 0; i < oldCount; i++) {\r
-                               Integer oldIndex = indexes.getLeft(i);\r
-                               if (oldIndex == null) {\r
-                                       setChild(i, childContexts[i], explorerContext);\r
-                               } else {\r
-                                       TreeNode n = oldChildren.get(oldIndex);\r
-                                       children.set(i, n);\r
-                               }\r
-                               \r
-                       }\r
-                       for (int i = oldCount; i < childContexts.length; i++) {\r
-                               addChild(childContexts[i], explorerContext);\r
-                       }\r
-                       } else {\r
-                               for (int i = 0; i < childContexts.length; i++) {\r
-                                       Integer oldIndex = indexes.getLeft(i);\r
-                               if (oldIndex == null) {\r
-                                       setChild(i, childContexts[i], explorerContext);\r
-                               } else {\r
-                                       TreeNode n = oldChildren.get(oldIndex);\r
-                                       children.set(i, n);\r
-                               }\r
-                       }\r
-                               for (int i = oldCount -1; i >= childContexts.length; i--) {\r
-                                       children.remove(i);\r
-                               }\r
-                       }\r
-                               for (int i = 0; i < oldChildren.size(); i++) {\r
-                                       if (!indexes.containsLeft(i)) {\r
-                                               oldChildren.get(i).dispose2();\r
-                                       }\r
-                               }\r
-                               \r
-                       }\r
-               \r
-               }\r
-               return modified;\r
-       }\r
-       \r
-       public boolean isDisposed() {\r
-               return context == null;\r
-       }\r
-       \r
-       public GENodeQueryManager getManager() {\r
-               return manager;\r
-       }\r
-       \r
-       @SuppressWarnings("rawtypes")\r
-       @Override\r
-       public Object getAdapter(Class adapter) {\r
-               if (adapter == NodeContext.class)\r
-                       return context;\r
-               \r
-               return context.getAdapter(adapter);\r
-       }\r
-\r
-}\r
+package org.simantics.browsing.ui.nattable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.resource.ColorDescriptor;
+import org.eclipse.jface.resource.FontDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
+import org.eclipse.nebula.widgets.nattable.style.Style;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.simantics.browsing.ui.BuiltinKeys;
+import org.simantics.browsing.ui.NodeContext;
+import org.simantics.browsing.ui.common.internal.GENodeQueryManager;
+import org.simantics.browsing.ui.content.ImageDecorator;
+import org.simantics.browsing.ui.content.Imager;
+import org.simantics.browsing.ui.content.LabelDecorator;
+import org.simantics.browsing.ui.content.Labeler;
+import org.simantics.browsing.ui.nattable.NatTableGraphExplorer.GECache2;
+import org.simantics.browsing.ui.nattable.NatTableGraphExplorer.GeViewerContext;
+import org.simantics.browsing.ui.swt.ViewerRowReference;
+import org.simantics.utils.datastructures.BijectionMap;
+
+public class TreeNode implements IAdaptable {
+       private static boolean DEBUG = false;
+       
+       private NodeContext context;
+       GENodeQueryManager manager;
+       GeViewerContext explorerContext;
+       
+       TreeNode parent;
+       List<TreeNode> children = new ArrayList<TreeNode>();
+       boolean expanded;
+       boolean autoExpanded = false;
+       
+       public TreeNode(NodeContext context, GeViewerContext explorerContext) {
+               this.context = context;
+               this.explorerContext = explorerContext;
+               this.expanded = false;
+               manager = new GENodeQueryManager(explorerContext, null, null, ViewerRowReference.create(this));
+               explorerContext.getContextToNodeMap().add(context, this);
+       }
+       
+       public int getDepth() {
+               if (parent == null)
+                       return 0;
+               return parent.getDepth() + 1;
+       }
+       
+       int listIndex = -1;
+       
+       public int getListIndex() {
+               return listIndex;
+       }
+       
+       public void setListIndex(int listIndex) {
+               this.listIndex = listIndex;
+       }
+       
+       List<TreeNode> getChildren() {
+               return children;
+       }
+       
+       public TreeNode getParent() {
+               return parent;
+       }
+       
+       public void setExpanded(boolean expanded) {
+               this.expanded = expanded;
+       }
+       
+       public boolean isExpanded() {
+               return expanded;
+       }
+       
+       public boolean isHidden() {
+               TreeNode n = parent;
+               while (n != null) {
+                       if (!n.isExpanded())
+                               return true;
+                       n = n.getParent();
+               }
+               return false;
+       }
+       
+       public TreeNode getCollapsedAncestor() {
+               TreeNode collapsed = null;
+               TreeNode n = parent;
+               while (n != null) {
+                       if (!n.isExpanded())
+                               collapsed = n;
+                       n = n.getParent();
+               }
+               return collapsed;
+       }
+       
+       public NodeContext getContext() {
+               return context;
+       }
+       
+       private Labeler labeler;
+       private Imager imager;
+       Collection<LabelDecorator> labelDecorators;
+       Collection<ImageDecorator> imageDecorators;
+        
+       Map<String, String> labels;
+       Map<String, String> runtimeLabels;
+       
+       public String getValueString(int column) {
+               if (labels == null)
+                       initData();
+               if (labeler != null) {
+                       String key = explorerContext.getGe().getColumns()[column].getKey();
+                       return getValue(key);
+               }
+               return null;
+       }
+       
+       private String getValue(String key) {
+               String s = null;
+               if (runtimeLabels != null)
+                       s = runtimeLabels.get(key);
+               if (s == null)
+                       s = labels.get(key);
+               if (labelDecorators != null && !labelDecorators.isEmpty()) {
+                       int index = 0;
+                       for (LabelDecorator ld : labelDecorators) {
+                               String ds = ld.decorateLabel(s, key, index);
+                               if (ds != null)
+                                       s = ds;
+                       }
+               }
+               return s;
+       }
+       
+       public String getValueString(String key) {
+               if (labels == null)
+                       initData();
+               if (labeler != null) {
+                       return getValue(key);
+               }
+               return null;
+       }
+       
+       public Image getImage(int column) {
+               String key = explorerContext.getGe().getColumns()[column].getKey();
+               if (imager != null) {
+                       Object descOrImage = null;
+                       boolean hasUncachedImages = false;
+
+                       ImageDescriptor desc = imager.getImage(key);
+                       if (desc != null) {
+                               int index = 0;
+                               // Attempt to decorate the label
+                               if (!imageDecorators.isEmpty()) {
+                                       for (ImageDecorator id : imageDecorators) {
+                                               ImageDescriptor ds = id.decorateImage(desc, key, index);
+                                               if (ds != null)
+                                                       desc = ds;
+                                       }
+                               }
+
+                               // Try resolving only cached images here and now
+                               Object img = explorerContext.getGe().localResourceManager.find(desc);
+                               if (img == null)
+                                       img = explorerContext.getGe().resourceManager.find(desc);
+
+                               descOrImage = img != null ? img : desc;
+                               hasUncachedImages |= img == null;
+                       }
+
+                       if (!hasUncachedImages) {
+                               return (Image) descOrImage;
+                       } else {
+                               // Schedule loading to another thread to refrain from
+                               // blocking
+                               // the UI with database operations.
+                               explorerContext.getGe().queueImageTask(this, new ImageTask(this, descOrImage));
+                               return null;
+                       }
+               } else {
+                       return null;
+               }
+       }
+       
+       public void getStyle(int column, Style style) {
+               String key = explorerContext.getGe().getColumns()[column].getKey();
+               FontDescriptor font = explorerContext.getGe().originalFont;
+               ColorDescriptor bg = explorerContext.getGe().originalBackground;
+               ColorDescriptor fg = explorerContext.getGe().originalForeground;
+               
+               // Attempt to decorate the label
+               if (labelDecorators != null && !labelDecorators.isEmpty()) {
+                       int index = 0;
+                       for (LabelDecorator ld : labelDecorators) {
+
+                               FontDescriptor dfont = ld.decorateFont(font, key, index);
+                               if (dfont != null)
+                                       font = dfont;
+
+                               ColorDescriptor dbg = ld.decorateBackground(bg, key, index);
+                               if (dbg != null)
+                                       bg = dbg;
+
+                               ColorDescriptor dfg = ld.decorateForeground(fg, key, index);
+                               if (dfg != null)
+                                       fg = dfg;
+                       }
+               }
+
+               if (font != explorerContext.getGe().originalFont) {
+                       // System.out.println("set font: " + index + ": " +
+                       // font);
+                       style.setAttributeValue(CellStyleAttributes.FONT,(Font) explorerContext.getGe().localResourceManager.get(font));
+               } else {
+                       style.setAttributeValue(CellStyleAttributes.FONT,(Font) (explorerContext.getGe().originalFont != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalFont) : null));
+               }
+               if (bg != explorerContext.getGe().originalBackground)
+                       style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(bg));
+               else
+                       style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) (explorerContext.getGe().originalBackground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalBackground) : null));
+               if (fg != explorerContext.getGe().originalForeground)
+                       style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(fg));
+               else
+                       style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) (explorerContext.getGe().originalForeground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalForeground) : null));
+
+       }
+       
+       public void initData() {
+               labeler = manager.query(context, BuiltinKeys.SELECTED_LABELER);
+               imager = manager.query(context, BuiltinKeys.SELECTED_IMAGER);
+               labelDecorators = manager.query(context, BuiltinKeys.LABEL_DECORATORS);
+               imageDecorators = manager.query(context, BuiltinKeys.IMAGE_DECORATORS);
+               
+               if (labeler != null) {
+               labels = labeler.getLabels();
+                       runtimeLabels = labeler.getRuntimeLabels();
+       } else {
+               labels = null;
+               runtimeLabels = null;
+       }
+       }
+       
+       public TreeNode addChild(NodeContext context, GeViewerContext explorerContext) {
+               TreeNode child = new TreeNode(context, explorerContext);
+               child.parent = this;
+               children.add(child);
+               if (DEBUG) System.out.println("Add " + this  + " -> " + child);
+               return child;
+       }
+       
+       public TreeNode addChild(int index, NodeContext context,GeViewerContext explorerContext) {
+               
+               TreeNode child = new TreeNode(context, explorerContext);
+               child.parent = this;
+               children.add(index,child);
+               if (DEBUG) System.out.println("Add " + this  + " -> " + child + " at " + index);
+               return child;
+       }
+       
+       public TreeNode setChild(int index, NodeContext context, GeViewerContext explorerContext) {
+               
+               TreeNode child = new TreeNode(context, explorerContext);
+               child.parent = this;
+               children.set(index,child);
+               if (DEBUG) System.out.println("Set " + this  + " -> " + child + " at " + index);
+               return child;
+       }
+       
+       public void dispose() {
+               if (parent != null)
+                       parent.children.remove(this);
+               dispose2();
+       }
+       
+       public void dispose2() {
+               if (DEBUG)      System.out.println("dispose " + this);
+               parent = null;
+               for (TreeNode n : children) {
+                       n.dispose2();
+               }
+               clearCache();
+               children.clear();
+               explorerContext.getContextToNodeMap().remove(context, this);
+               context = null;
+               explorerContext = null;
+               manager.dispose();
+               manager = null; 
+       }
+       
+       private void clearCache() {
+               if (explorerContext != null) {
+                       GECache2 cache = explorerContext.cache;
+                       
+                       if (cache != null) {
+                               cache.dispose(context);
+                       }
+               }
+       }
+       
+       public boolean updateChildren() {
+               if (context == null)
+                       throw new IllegalStateException("Node is disposed.");
+               
+               NodeContext[] childContexts = manager.query(context, BuiltinKeys.FINAL_CHILDREN);
+               
+               if (DEBUG) System.out.println("updateChildren " + childContexts.length + " " + this);
+               
+               
+               boolean modified = false;
+               synchronized (children) {
+                       
+                       int oldCount = children.size();
+                       BijectionMap<Integer, Integer> indexes = new BijectionMap<Integer, Integer>();
+                       Set<Integer> mapped = new HashSet<Integer>();
+                       boolean reorder = false;
+                       // locate matching pairs form old and new children
+                       for (int i = 0; i < oldCount; i++) {
+                               NodeContext oldCtx = children.get(i).context;
+                               for (int j = 0; j <childContexts.length; j++) {
+                                       if (mapped.contains(j))
+                                               continue;
+                                       if (oldCtx.equals(childContexts[j])) {
+                                               indexes.map(i, j);
+                                               mapped.add(j);
+                                               if (i != j)
+                                                       reorder = true;
+                                               break;
+                                       }
+                               }
+                       }
+                       // update children if required
+                       if (childContexts.length != oldCount || reorder || childContexts.length != indexes.size()) {
+                               modified = true;
+                               List<TreeNode> oldChildren = new ArrayList<TreeNode>(oldCount);
+                               oldChildren.addAll(children);
+                               if (childContexts.length >= oldCount) {
+                       for (int i = 0; i < oldCount; i++) {
+                               Integer oldIndex = indexes.getLeft(i);
+                               if (oldIndex == null) {
+                                       setChild(i, childContexts[i], explorerContext);
+                               } else {
+                                       TreeNode n = oldChildren.get(oldIndex);
+                                       children.set(i, n);
+                               }
+                               
+                       }
+                       for (int i = oldCount; i < childContexts.length; i++) {
+                               Integer oldIndex = indexes.getLeft(i);
+                               if (oldIndex == null) {
+                                       addChild(childContexts[i], explorerContext);
+                               } else {
+                                       TreeNode n = oldChildren.get(oldIndex);
+                                       children.add(n);
+                               }
+                       }
+                       } else {
+                               for (int i = 0; i < childContexts.length; i++) {
+                                       Integer oldIndex = indexes.getLeft(i);
+                               if (oldIndex == null) {
+                                       setChild(i, childContexts[i], explorerContext);
+                               } else {
+                                       TreeNode n = oldChildren.get(oldIndex);
+                                       children.set(i, n);
+                               }
+                       }
+                               for (int i = oldCount -1; i >= childContexts.length; i--) {
+                                       children.remove(i);
+                               }
+                       }
+                               for (int i = 0; i < oldChildren.size(); i++) {
+                                       if (!indexes.containsLeft(i)) {
+                                               oldChildren.get(i).dispose2();
+                                       }
+                               }
+                               
+                       }
+               
+               }
+               return modified;
+       }
+       
+       public boolean isDisposed() {
+               return context == null;
+       }
+       
+       public GENodeQueryManager getManager() {
+               return manager;
+       }
+       
+       @SuppressWarnings("rawtypes")
+       @Override
+       public Object getAdapter(Class adapter) {
+               if (adapter == NodeContext.class)
+                       return context;
+               
+               return context.getAdapter(adapter);
+       }
+       
+       @Override
+       public String toString() {
+               return "TreeNode: " + listIndex + " " + (expanded ? "(+)" : "(-)") + " " + context ;
+       }
+
+}