]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/TreeNode.java
Fix to NatTable graph explorer issue. (fixes #7786)
[simantics/platform.git] / bundles / org.simantics.browsing.ui.nattable / src / org / simantics / browsing / ui / nattable / TreeNode.java
1 package org.simantics.browsing.ui.nattable;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9
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;
30
31 public class TreeNode implements IAdaptable {
32         private static boolean DEBUG = false;
33         
34         private NodeContext context;
35         GENodeQueryManager manager;
36         GeViewerContext explorerContext;
37         
38         TreeNode parent;
39         List<TreeNode> children = new ArrayList<TreeNode>();
40         boolean expanded;
41         boolean autoExpanded = false;
42         
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);
49         }
50         
51         public int getDepth() {
52                 if (parent == null)
53                         return 0;
54                 return parent.getDepth() + 1;
55         }
56         
57         int listIndex = -1;
58         
59         public int getListIndex() {
60                 return listIndex;
61         }
62         
63         public void setListIndex(int listIndex) {
64                 this.listIndex = listIndex;
65         }
66         
67         List<TreeNode> getChildren() {
68                 return children;
69         }
70         
71         public TreeNode getParent() {
72                 return parent;
73         }
74         
75         public void setExpanded(boolean expanded) {
76                 this.expanded = expanded;
77         }
78         
79         public boolean isExpanded() {
80                 return expanded;
81         }
82         
83         public boolean isHidden() {
84                 TreeNode n = parent;
85                 while (n != null) {
86                         if (!n.isExpanded())
87                                 return true;
88                         n = n.getParent();
89                 }
90                 return false;
91         }
92         
93         public TreeNode getCollapsedAncestor() {
94                 TreeNode collapsed = null;
95                 TreeNode n = parent;
96                 while (n != null) {
97                         if (!n.isExpanded())
98                                 collapsed = n;
99                         n = n.getParent();
100                 }
101                 return collapsed;
102         }
103         
104         public NodeContext getContext() {
105                 return context;
106         }
107         
108         private Labeler labeler;
109         private Imager imager;
110         Collection<LabelDecorator> labelDecorators;
111         Collection<ImageDecorator> imageDecorators;
112          
113         Map<String, String> labels;
114         Map<String, String> runtimeLabels;
115         
116         public String getValueString(int column) {
117                 if (labels == null)
118                         initData();
119                 if (labeler != null) {
120                         String key = explorerContext.getGe().getColumns()[column].getKey();
121                         String s = null;
122                         if (runtimeLabels != null)
123                                 s = runtimeLabels.get(key);
124                         if (s == null)
125                                 s = labels.get(key);
126                         if (labelDecorators != null && !labelDecorators.isEmpty()) {
127                                 int index = 0;
128                                 for (LabelDecorator ld : labelDecorators) {
129                                         String ds = ld.decorateLabel(s, key, index);
130                                         if (ds != null)
131                                                 s = ds;
132                                 }
133                         }
134                         return s;
135                 }
136                 return null;
137         }
138         
139         public Image getImage(int column) {
140                 String key = explorerContext.getGe().getColumns()[column].getKey();
141                 if (imager != null) {
142                         Object descOrImage = null;
143                         boolean hasUncachedImages = false;
144
145                         ImageDescriptor desc = imager.getImage(key);
146                         if (desc != null) {
147                                 int index = 0;
148                                 // Attempt to decorate the label
149                                 if (!imageDecorators.isEmpty()) {
150                                         for (ImageDecorator id : imageDecorators) {
151                                                 ImageDescriptor ds = id.decorateImage(desc, key, index);
152                                                 if (ds != null)
153                                                         desc = ds;
154                                         }
155                                 }
156
157                                 // Try resolving only cached images here and now
158                                 Object img = explorerContext.getGe().localResourceManager.find(desc);
159                                 if (img == null)
160                                         img = explorerContext.getGe().resourceManager.find(desc);
161
162                                 descOrImage = img != null ? img : desc;
163                                 hasUncachedImages |= img == null;
164                         }
165
166                         if (!hasUncachedImages) {
167                                 return (Image) descOrImage;
168                         } else {
169                                 // Schedule loading to another thread to refrain from
170                                 // blocking
171                                 // the UI with database operations.
172                                 explorerContext.getGe().queueImageTask(this, new ImageTask(this, descOrImage));
173                                 return null;
174                         }
175                 } else {
176                         return null;
177                 }
178         }
179         
180         public void getStyle(int column, Style style) {
181                 String key = explorerContext.getGe().getColumns()[column].getKey();
182                 FontDescriptor font = explorerContext.getGe().originalFont;
183                 ColorDescriptor bg = explorerContext.getGe().originalBackground;
184                 ColorDescriptor fg = explorerContext.getGe().originalForeground;
185                 
186                 // Attempt to decorate the label
187                 if (labelDecorators != null && !labelDecorators.isEmpty()) {
188                         int index = 0;
189                         for (LabelDecorator ld : labelDecorators) {
190
191                                 FontDescriptor dfont = ld.decorateFont(font, key, index);
192                                 if (dfont != null)
193                                         font = dfont;
194
195                                 ColorDescriptor dbg = ld.decorateBackground(bg, key, index);
196                                 if (dbg != null)
197                                         bg = dbg;
198
199                                 ColorDescriptor dfg = ld.decorateForeground(fg, key, index);
200                                 if (dfg != null)
201                                         fg = dfg;
202                         }
203                 }
204
205                 if (font != explorerContext.getGe().originalFont) {
206                         // System.out.println("set font: " + index + ": " +
207                         // font);
208                         style.setAttributeValue(CellStyleAttributes.FONT,(Font) explorerContext.getGe().localResourceManager.get(font));
209                 } else {
210                         style.setAttributeValue(CellStyleAttributes.FONT,(Font) (explorerContext.getGe().originalFont != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalFont) : null));
211                 }
212                 if (bg != explorerContext.getGe().originalBackground)
213                         style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(bg));
214                 else
215                         style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,(Color) (explorerContext.getGe().originalBackground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalBackground) : null));
216                 if (fg != explorerContext.getGe().originalForeground)
217                         style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) explorerContext.getGe().localResourceManager.get(fg));
218                 else
219                         style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR,(Color) (explorerContext.getGe().originalForeground != null ? explorerContext.getGe().localResourceManager.get(explorerContext.getGe().originalForeground) : null));
220
221         }
222         
223         public void initData() {
224                 labeler = manager.query(context, BuiltinKeys.SELECTED_LABELER);
225                 imager = manager.query(context, BuiltinKeys.SELECTED_IMAGER);
226                 labelDecorators = manager.query(context, BuiltinKeys.LABEL_DECORATORS);
227                 imageDecorators = manager.query(context, BuiltinKeys.IMAGE_DECORATORS);
228                 
229                 if (labeler != null) {
230                 labels = labeler.getLabels();
231                         runtimeLabels = labeler.getRuntimeLabels();
232         } else {
233                 labels = null;
234                 runtimeLabels = null;
235         }
236         }
237         
238         public TreeNode addChild(NodeContext context, GeViewerContext explorerContext) {
239                 TreeNode child = new TreeNode(context, explorerContext);
240                 child.parent = this;
241                 children.add(child);
242                 if (DEBUG) System.out.println("Add " + this  + " -> " + child);
243                 return child;
244         }
245         
246         public TreeNode addChild(int index, NodeContext context,GeViewerContext explorerContext) {
247                 
248                 TreeNode child = new TreeNode(context, explorerContext);
249                 child.parent = this;
250                 children.add(index,child);
251                 if (DEBUG) System.out.println("Add " + this  + " -> " + child + " at " + index);
252                 return child;
253         }
254         
255         public TreeNode setChild(int index, NodeContext context, GeViewerContext explorerContext) {
256                 
257                 TreeNode child = new TreeNode(context, explorerContext);
258                 child.parent = this;
259                 children.set(index,child);
260                 if (DEBUG) System.out.println("Set " + this  + " -> " + child + " at " + index);
261                 return child;
262         }
263         
264         public void dispose() {
265                 if (parent != null)
266                         parent.children.remove(this);
267                 dispose2();
268         }
269         
270         public void dispose2() {
271                 if (DEBUG)      System.out.println("dispose " + this);
272                 parent = null;
273                 for (TreeNode n : children) {
274                         n.dispose2();
275                 }
276                 clearCache();
277                 children.clear();
278                 explorerContext.getContextToNodeMap().remove(context, this);
279                 context = null;
280                 explorerContext = null;
281                 manager.dispose();
282                 manager = null; 
283         }
284         
285         private void clearCache() {
286                 if (explorerContext != null) {
287                         GECache2 cache = explorerContext.cache;
288                         
289                         if (cache != null) {
290                                 cache.dispose(context);
291                         }
292                 }
293         }
294         
295         public boolean updateChildren() {
296                 if (context == null)
297                         throw new IllegalStateException("Node is disposed.");
298                 
299                 NodeContext[] childContexts = manager.query(context, BuiltinKeys.FINAL_CHILDREN);
300                 
301                 if (DEBUG) System.out.println("updateChildren " + childContexts.length + " " + this);
302                 
303                 
304                 boolean modified = false;
305                 synchronized (children) {
306                         
307                         int oldCount = children.size();
308                         BijectionMap<Integer, Integer> indexes = new BijectionMap<Integer, Integer>();
309                         Set<Integer> mapped = new HashSet<Integer>();
310                         boolean reorder = false;
311                         // locate matching pairs form old and new children
312                         for (int i = 0; i < oldCount; i++) {
313                                 NodeContext oldCtx = children.get(i).context;
314                                 for (int j = 0; j <childContexts.length; j++) {
315                                         if (mapped.contains(j))
316                                                 continue;
317                                         if (oldCtx.equals(childContexts[j])) {
318                                                 indexes.map(i, j);
319                                                 mapped.add(j);
320                                                 if (i != j)
321                                                         reorder = true;
322                                                 break;
323                                         }
324                                 }
325                         }
326                         // update children if required
327                         if (childContexts.length != oldCount || reorder || childContexts.length != indexes.size()) {
328                                 modified = true;
329                                 List<TreeNode> oldChildren = new ArrayList<TreeNode>(oldCount);
330                                 oldChildren.addAll(children);
331                                 if (childContexts.length >= oldCount) {
332                         for (int i = 0; i < oldCount; i++) {
333                                 Integer oldIndex = indexes.getLeft(i);
334                                 if (oldIndex == null) {
335                                         setChild(i, childContexts[i], explorerContext);
336                                 } else {
337                                         TreeNode n = oldChildren.get(oldIndex);
338                                         children.set(i, n);
339                                 }
340                                 
341                         }
342                         for (int i = oldCount; i < childContexts.length; i++) {
343                                 Integer oldIndex = indexes.getLeft(i);
344                                 if (oldIndex == null) {
345                                         addChild(childContexts[i], explorerContext);
346                                 } else {
347                                         TreeNode n = oldChildren.get(oldIndex);
348                                         children.add(n);
349                                 }
350                         }
351                         } else {
352                                 for (int i = 0; i < childContexts.length; i++) {
353                                         Integer oldIndex = indexes.getLeft(i);
354                                 if (oldIndex == null) {
355                                         setChild(i, childContexts[i], explorerContext);
356                                 } else {
357                                         TreeNode n = oldChildren.get(oldIndex);
358                                         children.set(i, n);
359                                 }
360                         }
361                                 for (int i = oldCount -1; i >= childContexts.length; i--) {
362                                         children.remove(i);
363                                 }
364                         }
365                                 for (int i = 0; i < oldChildren.size(); i++) {
366                                         if (!indexes.containsLeft(i)) {
367                                                 oldChildren.get(i).dispose2();
368                                         }
369                                 }
370                                 
371                         }
372                 
373                 }
374                 return modified;
375         }
376         
377         public boolean isDisposed() {
378                 return context == null;
379         }
380         
381         public GENodeQueryManager getManager() {
382                 return manager;
383         }
384         
385         @SuppressWarnings("rawtypes")
386         @Override
387         public Object getAdapter(Class adapter) {
388                 if (adapter == NodeContext.class)
389                         return context;
390                 
391                 return context.getAdapter(adapter);
392         }
393         
394         @Override
395         public String toString() {
396                 return "TreeNode: " + listIndex + " " + (expanded ? "(+)" : "(-)") + " " + context ;
397         }
398
399 }