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