+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