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