1 package org.simantics.browsing.ui.nattable;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashSet;
10 import org.eclipse.core.runtime.IAdaptable;
11 import org.eclipse.jface.resource.ColorDescriptor;
12 import org.eclipse.jface.resource.FontDescriptor;
13 import org.eclipse.jface.resource.ImageDescriptor;
14 import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
15 import org.eclipse.nebula.widgets.nattable.style.Style;
16 import org.eclipse.swt.graphics.Color;
17 import org.eclipse.swt.graphics.Font;
18 import org.eclipse.swt.graphics.Image;
19 import org.simantics.browsing.ui.BuiltinKeys;
20 import org.simantics.browsing.ui.NodeContext;
21 import org.simantics.browsing.ui.common.internal.GENodeQueryManager;
22 import org.simantics.browsing.ui.content.ImageDecorator;
23 import org.simantics.browsing.ui.content.Imager;
24 import org.simantics.browsing.ui.content.LabelDecorator;
25 import org.simantics.browsing.ui.content.Labeler;
26 import org.simantics.browsing.ui.nattable.NatTableGraphExplorer.GECache2;
27 import org.simantics.browsing.ui.nattable.NatTableGraphExplorer.GeViewerContext;
28 import org.simantics.browsing.ui.swt.ViewerRowReference;
29 import org.simantics.utils.datastructures.BijectionMap;
31 public class TreeNode implements IAdaptable {
32 private static boolean DEBUG = false;
34 private NodeContext context;
35 GENodeQueryManager manager;
36 GeViewerContext explorerContext;
39 List<TreeNode> children = new ArrayList<TreeNode>();
41 boolean autoExpanded = false;
43 public TreeNode(NodeContext context, GeViewerContext explorerContext) {
44 this.context = context;
45 this.explorerContext = explorerContext;
46 this.expanded = false;
47 manager = new GENodeQueryManager(explorerContext, null, null, ViewerRowReference.create(this));
48 explorerContext.getContextToNodeMap().add(context, this);
51 public int getDepth() {
54 return parent.getDepth() + 1;
59 public int getListIndex() {
63 public void setListIndex(int listIndex) {
64 this.listIndex = listIndex;
67 List<TreeNode> getChildren() {
71 public TreeNode getParent() {
75 public void setExpanded(boolean expanded) {
76 this.expanded = expanded;
79 public boolean isExpanded() {
83 public boolean isHidden() {
93 public TreeNode getCollapsedAncestor() {
94 TreeNode collapsed = null;
104 public NodeContext getContext() {
108 private Labeler labeler;
109 private Imager imager;
110 Collection<LabelDecorator> labelDecorators;
111 Collection<ImageDecorator> imageDecorators;
113 Map<String, String> labels;
114 Map<String, String> runtimeLabels;
116 public String getValueString(int column) {
119 if (labeler != null) {
120 String key = explorerContext.getGe().getColumns()[column].getKey();
121 return getValue(key);
126 private String getValue(String key) {
128 if (runtimeLabels != null)
129 s = runtimeLabels.get(key);
132 if (labelDecorators != null && !labelDecorators.isEmpty()) {
134 for (LabelDecorator ld : labelDecorators) {
135 String ds = ld.decorateLabel(s, key, index);
143 public String getValueString(String key) {
146 if (labeler != null) {
147 return getValue(key);
152 public Image getImage(int column) {
153 String key = explorerContext.getGe().getColumns()[column].getKey();
154 if (imager != null) {
155 Object descOrImage = null;
156 boolean hasUncachedImages = false;
158 ImageDescriptor desc = imager.getImage(key);
161 // Attempt to decorate the label
162 if (!imageDecorators.isEmpty()) {
163 for (ImageDecorator id : imageDecorators) {
164 ImageDescriptor ds = id.decorateImage(desc, key, index);
170 // Try resolving only cached images here and now
171 Object img = explorerContext.getGe().localResourceManager.find(desc);
173 img = explorerContext.getGe().resourceManager.find(desc);
175 descOrImage = img != null ? img : desc;
176 hasUncachedImages |= img == null;
179 if (!hasUncachedImages) {
180 return (Image) descOrImage;
182 // Schedule loading to another thread to refrain from
184 // the UI with database operations.
185 explorerContext.getGe().queueImageTask(this, new ImageTask(this, descOrImage));
193 public void getStyle(int column, Style style) {
194 String key = explorerContext.getGe().getColumns()[column].getKey();
195 FontDescriptor font = explorerContext.getGe().originalFont;
196 ColorDescriptor bg = explorerContext.getGe().originalBackground;
197 ColorDescriptor fg = explorerContext.getGe().originalForeground;
199 // Attempt to decorate the label
200 if (labelDecorators != null && !labelDecorators.isEmpty()) {
202 for (LabelDecorator ld : labelDecorators) {
204 FontDescriptor dfont = ld.decorateFont(font, key, index);
208 ColorDescriptor dbg = ld.decorateBackground(bg, key, index);
212 ColorDescriptor dfg = ld.decorateForeground(fg, key, index);
218 if (font != explorerContext.getGe().originalFont) {
219 // System.out.println("set font: " + index + ": " +
221 style.setAttributeValue(CellStyleAttributes.FONT,(Font) explorerContext.getGe().localResourceManager.get(font));
223 style.setAttributeValue(CellStyleAttributes.FONT,(Font) (explorerContext.getGe().originalFont != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalFont) : null));
225 if (bg != explorerContext.getGe().originalBackground)
226 style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(bg));
228 style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) (explorerContext.getGe().originalBackground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalBackground) : null));
229 if (fg != explorerContext.getGe().originalForeground)
230 style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(fg));
232 style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) (explorerContext.getGe().originalForeground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalForeground) : null));
236 public void initData() {
237 labeler = manager.query(context, BuiltinKeys.SELECTED_LABELER);
238 imager = manager.query(context, BuiltinKeys.SELECTED_IMAGER);
239 labelDecorators = manager.query(context, BuiltinKeys.LABEL_DECORATORS);
240 imageDecorators = manager.query(context, BuiltinKeys.IMAGE_DECORATORS);
242 if (labeler != null) {
243 labels = labeler.getLabels();
244 runtimeLabels = labeler.getRuntimeLabels();
247 runtimeLabels = null;
251 public TreeNode addChild(NodeContext context, GeViewerContext explorerContext) {
252 TreeNode child = new TreeNode(context, explorerContext);
255 if (DEBUG) System.out.println("Add " + this + " -> " + child);
259 public TreeNode addChild(int index, NodeContext context,GeViewerContext explorerContext) {
261 TreeNode child = new TreeNode(context, explorerContext);
263 children.add(index,child);
264 if (DEBUG) System.out.println("Add " + this + " -> " + child + " at " + index);
268 public TreeNode setChild(int index, NodeContext context, GeViewerContext explorerContext) {
270 TreeNode child = new TreeNode(context, explorerContext);
272 children.set(index,child);
273 if (DEBUG) System.out.println("Set " + this + " -> " + child + " at " + index);
277 public void dispose() {
279 parent.children.remove(this);
283 public void dispose2() {
284 if (DEBUG) System.out.println("dispose " + this);
286 for (TreeNode n : children) {
291 explorerContext.getContextToNodeMap().remove(context, this);
293 explorerContext = null;
299 * Fast dispose is used to wipe the whole tree.
301 * ContextToNodeMap is cleared with one command, so we do not need to remove nodes one by one from the map.
303 public void fastDispose() {
304 if (DEBUG) System.out.println("dispose " + this);
306 for (TreeNode n : children) {
312 explorerContext = null;
317 private void clearCache() {
318 if (explorerContext != null) {
319 GECache2 cache = explorerContext.cache;
322 cache.dispose(context);
327 public boolean updateChildren() {
329 throw new IllegalStateException("Node is disposed.");
331 NodeContext[] childContexts = manager.query(context, BuiltinKeys.FINAL_CHILDREN);
333 if (DEBUG) System.out.println("updateChildren " + childContexts.length + " " + this);
336 boolean modified = false;
337 synchronized (children) {
339 int oldCount = children.size();
340 BijectionMap<Integer, Integer> indexes = new BijectionMap<Integer, Integer>();
341 Set<Integer> mapped = new HashSet<Integer>();
342 boolean reorder = false;
343 // locate matching pairs form old and new children
344 for (int i = 0; i < oldCount; i++) {
345 NodeContext oldCtx = children.get(i).context;
346 for (int j = 0; j <childContexts.length; j++) {
347 if (mapped.contains(j))
349 if (oldCtx.equals(childContexts[j])) {
358 // update children if required
359 if (childContexts.length != oldCount || reorder || childContexts.length != indexes.size()) {
361 List<TreeNode> oldChildren = new ArrayList<TreeNode>(oldCount);
362 oldChildren.addAll(children);
363 if (childContexts.length >= oldCount) {
364 for (int i = 0; i < oldCount; i++) {
365 Integer oldIndex = indexes.getLeft(i);
366 if (oldIndex == null) {
367 setChild(i, childContexts[i], explorerContext);
369 TreeNode n = oldChildren.get(oldIndex);
374 for (int i = oldCount; i < childContexts.length; i++) {
375 Integer oldIndex = indexes.getLeft(i);
376 if (oldIndex == null) {
377 addChild(childContexts[i], explorerContext);
379 TreeNode n = oldChildren.get(oldIndex);
384 for (int i = 0; i < childContexts.length; i++) {
385 Integer oldIndex = indexes.getLeft(i);
386 if (oldIndex == null) {
387 setChild(i, childContexts[i], explorerContext);
389 TreeNode n = oldChildren.get(oldIndex);
393 for (int i = oldCount -1; i >= childContexts.length; i--) {
397 for (int i = 0; i < oldChildren.size(); i++) {
398 if (!indexes.containsLeft(i)) {
399 oldChildren.get(i).dispose2();
409 public boolean isDisposed() {
410 return context == null;
413 public GENodeQueryManager getManager() {
417 @SuppressWarnings("rawtypes")
419 public Object getAdapter(Class adapter) {
420 if (adapter == NodeContext.class)
423 return context.getAdapter(adapter);
427 public String toString() {
428 return "TreeNode: " + listIndex + " " + (expanded ? "(+)" : "(-)") + " " + context ;