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 children = new ArrayList(); boolean expanded; 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); } int getDepth() { if (parent == null) return 0; return parent.getDepth() + 1; } int listIndex; public int getListIndex() { return listIndex; } public void setListIndex(int listIndex) { this.listIndex = listIndex; } List getChildren() { return children; } public TreeNode getParent() { return parent; } public void setExpanded(boolean expanded) { this.expanded = expanded; } public boolean isExpanded() { return expanded; } public NodeContext getContext() { return context; } private Labeler labeler; private Imager imager; Collection labelDecorators; Collection imageDecorators; Map labels; Map runtimeLabels; public String getValueString(int column) { if (column == 0) { initData(); } if (labeler != null) { String key = explorerContext.getGe().getColumns()[column].getKey(); 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; } 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)); } private 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 indexes = new BijectionMap(); Set mapped = new HashSet(); 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 oldChildren = new ArrayList(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++) { addChild(childContexts[i], explorerContext); } } 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); } }