]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph.ui/src/org/simantics/scenegraph/ui/AttributeDialog.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scenegraph.ui / src / org / simantics / scenegraph / ui / AttributeDialog.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.scenegraph.ui;\r
13 \r
14 import java.lang.reflect.Field;\r
15 import java.lang.reflect.Modifier;\r
16 import java.util.ArrayList;\r
17 import java.util.List;\r
18 \r
19 import org.eclipse.jface.dialogs.Dialog;\r
20 import org.eclipse.jface.dialogs.IDialogSettings;\r
21 import org.eclipse.jface.layout.GridDataFactory;\r
22 import org.eclipse.jface.layout.GridLayoutFactory;\r
23 import org.eclipse.jface.layout.TableColumnLayout;\r
24 import org.eclipse.jface.resource.ColorDescriptor;\r
25 import org.eclipse.jface.resource.FontDescriptor;\r
26 import org.eclipse.jface.resource.JFaceResources;\r
27 import org.eclipse.jface.resource.LocalResourceManager;\r
28 import org.eclipse.jface.resource.ResourceManager;\r
29 import org.eclipse.jface.viewers.ColumnLabelProvider;\r
30 import org.eclipse.jface.viewers.ColumnWeightData;\r
31 import org.eclipse.jface.viewers.ISelection;\r
32 import org.eclipse.jface.viewers.ISelectionChangedListener;\r
33 import org.eclipse.jface.viewers.ISelectionProvider;\r
34 import org.eclipse.jface.viewers.IStructuredSelection;\r
35 import org.eclipse.jface.viewers.ITreeContentProvider;\r
36 import org.eclipse.jface.viewers.SelectionChangedEvent;\r
37 import org.eclipse.jface.viewers.TableViewer;\r
38 import org.eclipse.jface.viewers.TableViewerColumn;\r
39 import org.eclipse.jface.viewers.Viewer;\r
40 import org.eclipse.jface.viewers.ViewerCell;\r
41 import org.eclipse.swt.SWT;\r
42 import org.eclipse.swt.events.KeyAdapter;\r
43 import org.eclipse.swt.events.KeyEvent;\r
44 import org.eclipse.swt.events.SelectionAdapter;\r
45 import org.eclipse.swt.events.SelectionEvent;\r
46 import org.eclipse.swt.graphics.Color;\r
47 import org.eclipse.swt.graphics.Point;\r
48 import org.eclipse.swt.graphics.RGB;\r
49 import org.eclipse.swt.layout.GridData;\r
50 import org.eclipse.swt.widgets.Composite;\r
51 import org.eclipse.swt.widgets.Control;\r
52 import org.eclipse.swt.widgets.Event;\r
53 import org.eclipse.swt.widgets.Listener;\r
54 import org.eclipse.swt.widgets.Shell;\r
55 import org.eclipse.swt.widgets.ToolBar;\r
56 import org.eclipse.swt.widgets.ToolItem;\r
57 import org.simantics.scenegraph.INode;\r
58 import org.simantics.scenegraph.g2d.IG2DNode;\r
59 import org.simantics.scenegraph.utils.NodeUtil;\r
60 import org.simantics.utils.datastructures.ValueUtils;\r
61 \r
62 /**\r
63  * @author Tuukka Lehtonen\r
64  */\r
65 public class AttributeDialog extends Dialog implements ISelectionChangedListener {\r
66 \r
67     private static final String      DIALOG = "AttributeDialog"; //$NON-NLS-1$\r
68 \r
69     private IDialogSettings          dialogBoundsSettings;\r
70 \r
71     private ResourceManager          resourceManager;\r
72 \r
73     private TableViewer              viewer;\r
74 \r
75     private final ISelectionProvider selectionProvider;\r
76 \r
77     private final boolean            showClass      = true;\r
78     private boolean                  showTransient  = false;\r
79     private boolean                  showStatic     = false;\r
80 \r
81     private final ColorDescriptor    staticColor    = ColorDescriptor.createFrom(new RGB(224, 224, 224));\r
82     private final ColorDescriptor    transientColor = ColorDescriptor.createFrom(new RGB(192, 255, 255));\r
83     \r
84     protected AttributeDialog(Shell parentShell, ISelectionProvider selectionProvider) {\r
85         super(parentShell);\r
86         this.selectionProvider = selectionProvider;\r
87 \r
88         IDialogSettings settings = Activator.getDefault().getDialogSettings();\r
89         dialogBoundsSettings = settings.getSection(DIALOG);\r
90         if (dialogBoundsSettings == null)\r
91             dialogBoundsSettings = settings.addNewSection(DIALOG);\r
92     }\r
93 \r
94     @Override\r
95     protected IDialogSettings getDialogBoundsSettings() {\r
96         return dialogBoundsSettings;\r
97     }\r
98 \r
99     @Override\r
100     protected int getShellStyle() {\r
101         return SWT.RESIZE | SWT.MODELESS | SWT.TITLE | SWT.CLOSE | SWT.BORDER;\r
102     }\r
103 \r
104     @Override\r
105     protected void configureShell(Shell newShell) {\r
106         super.configureShell(newShell);\r
107         newShell.setText("Scene Graph Node Attributes");\r
108     }\r
109 \r
110     @Override\r
111     protected Control createButtonBar(Composite parent) {\r
112         return parent;\r
113     }\r
114 \r
115     @Override\r
116     protected void createButtonsForButtonBar(Composite parent) {\r
117         // No buttons, this is a non-modal property dialog.\r
118     }\r
119 \r
120     @Override\r
121     protected Point getInitialSize() {\r
122         Point defaultSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT, true);\r
123         Point result = super.getInitialSize();\r
124         if (defaultSize.equals(result))\r
125             return new Point(500, 300);\r
126         return result;\r
127     }\r
128 \r
129     @Override\r
130     protected Control createDialogArea(Composite parent) {\r
131         Composite composite = (Composite) super.createDialogArea(parent);\r
132 \r
133         this.resourceManager = new LocalResourceManager(JFaceResources.getResources());\r
134         composite.addListener(SWT.Dispose, new Listener() {\r
135             @Override\r
136             public void handleEvent(Event event) {\r
137                 selectionProvider.removeSelectionChangedListener(AttributeDialog.this);\r
138                 resourceManager.dispose();\r
139                 resourceManager = null;\r
140             }\r
141         });\r
142 \r
143         GridLayoutFactory.fillDefaults().margins(0, 0).spacing(0, 0).applyTo(composite);\r
144 \r
145         ToolBar toolbar = new ToolBar(composite, SWT.NONE);\r
146         GridDataFactory.fillDefaults().grab(true, false).applyTo(toolbar);\r
147         final ToolItem showStaticItem = new ToolItem(toolbar, SWT.CHECK);\r
148         showStaticItem.setText("Show &Static");\r
149         showStaticItem.setToolTipText("Show Static Fields of Selected Object");\r
150         final ToolItem showTransientItem = new ToolItem(toolbar, SWT.CHECK);\r
151         showTransientItem.setText("Show &Transient");\r
152         showTransientItem.setToolTipText("Show Transient Fields of Selected Object");\r
153         final ToolItem refresh = new ToolItem(toolbar, SWT.PUSH);\r
154         refresh.setText("&Refresh");\r
155         refresh.setToolTipText("Refresh Values");\r
156 \r
157         Composite tableComposite = new Composite(composite, SWT.NONE);\r
158         GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite);\r
159 \r
160         viewer = new TableViewer(tableComposite, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION);\r
161         viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\r
162         viewer.setContentProvider(new FieldContentProvider());\r
163         viewer.getTable().addKeyListener(new KeyAdapter() {\r
164             @Override\r
165             public void keyPressed(KeyEvent e) {\r
166                 if (e.keyCode == SWT.F5)\r
167                     refresh();\r
168             }\r
169         });\r
170 \r
171         TableColumnLayout ad = new TableColumnLayout();\r
172         tableComposite.setLayout(ad);\r
173 \r
174         viewer.getTable().setHeaderVisible(true);\r
175         viewer.getTable().setLinesVisible(true);\r
176         viewer.setUseHashlookup(true);\r
177 \r
178         TableViewerColumn nameColumn = new TableViewerColumn(viewer, SWT.LEFT);\r
179         nameColumn.setLabelProvider(new FieldName());\r
180         TableViewerColumn typeColumn = new TableViewerColumn(viewer, SWT.LEFT);\r
181         typeColumn.setLabelProvider(new FieldType());\r
182         TableViewerColumn valueColumn = new TableViewerColumn(viewer, SWT.LEFT);\r
183         valueColumn.setLabelProvider(new FieldValue());\r
184 \r
185         typeColumn.getColumn().setText("Type");\r
186         typeColumn.getColumn().setWidth(20);\r
187         ad.setColumnData(typeColumn.getColumn(), new ColumnWeightData(10, 140));\r
188         nameColumn.getColumn().setText("Name");\r
189         nameColumn.getColumn().setWidth(20);\r
190         ad.setColumnData(nameColumn.getColumn(), new ColumnWeightData(10, 140));\r
191         valueColumn.getColumn().setText("Value");\r
192         valueColumn.getColumn().setWidth(20);\r
193         ad.setColumnData(valueColumn.getColumn(), new ColumnWeightData(90, 200));\r
194 \r
195         selectionProvider.addSelectionChangedListener(this);\r
196 \r
197         showStaticItem.addSelectionListener(new SelectionAdapter() {\r
198             @Override\r
199             public void widgetSelected(SelectionEvent e) {\r
200                 showStatic = showStaticItem.getSelection();\r
201                 refresh();\r
202             }\r
203         });\r
204         showTransientItem.addSelectionListener(new SelectionAdapter() {\r
205             @Override\r
206             public void widgetSelected(SelectionEvent e) {\r
207                 showTransient = showTransientItem.getSelection();\r
208                 refresh();\r
209             }\r
210         });\r
211         refresh.addSelectionListener(new SelectionAdapter() {\r
212             @Override\r
213             public void widgetSelected(SelectionEvent e) {\r
214                 refresh();\r
215             }\r
216         });\r
217 \r
218         // Bootstrap dialog.\r
219         refresh();\r
220 \r
221         applyDialogFont(composite);\r
222         return composite;\r
223     }\r
224 \r
225     static class Header {\r
226         String name;\r
227         public Header(String name) {\r
228             this.name = name;\r
229         }\r
230         @Override\r
231         public String toString() {\r
232             return name;\r
233         }\r
234     }\r
235 \r
236     static class ComputedAttr {\r
237         public String name;\r
238         public Object object;\r
239         public String stringValue;\r
240         public ComputedAttr(String name, Object object) {\r
241             this(name, object, object != null ? object.toString() : "null");\r
242         }\r
243         public ComputedAttr(String name, Object object, String stringValue) {\r
244             this.name = name;\r
245             this.object = object;\r
246             this.stringValue = stringValue;\r
247         }\r
248         @Override\r
249         public String toString() {\r
250             return stringValue;\r
251         }\r
252     }\r
253 \r
254     static class Attr {\r
255         Object object;\r
256         Field field;\r
257         public Attr(Object obj, Field f) {\r
258             this.object = obj;\r
259             this.field = f;\r
260         }\r
261         public Object getObject() {\r
262             return object;\r
263         }\r
264         public <T> T getObject(Class<T> clazz) {\r
265             return clazz.cast(object);\r
266         }\r
267         public Field getField() {\r
268             return field;\r
269         }\r
270         @Override\r
271         public String toString() {\r
272             return field.getName();\r
273         }\r
274     }\r
275 \r
276     class FieldContentProvider implements ITreeContentProvider {\r
277         @Override\r
278         public Object[] getChildren(Object parentElement) {\r
279             return new Object[0];\r
280         }\r
281 \r
282         @Override\r
283         public Object getParent(Object element) {\r
284             return null;\r
285         }\r
286 \r
287         @Override\r
288         public boolean hasChildren(Object element) {\r
289             return false;\r
290         }\r
291 \r
292         @Override\r
293         public Object[] getElements(Object inputElement) {\r
294             if (inputElement instanceof NodeProxy) {\r
295                 NodeProxy np = (NodeProxy) inputElement;\r
296                 INode node = np.getNode();\r
297                 if (node == null)\r
298                     return new Object[0];\r
299                 List<Object> result = new ArrayList<Object>();\r
300 \r
301                 if (node instanceof IG2DNode) {\r
302                     IG2DNode g2dnode = (IG2DNode) node;\r
303                     // Calculate some useful computational properties\r
304                     result.add(new Header("Computational IG2DNode properties"));\r
305                     result.add(new ComputedAttr("local bounds", g2dnode.getBoundsInLocal()));\r
306                     result.add(new ComputedAttr("world bounds", g2dnode.getBounds()));\r
307                     result.add(new ComputedAttr("local to world transform", NodeUtil.getLocalToGlobalTransform(g2dnode)));\r
308                 }\r
309 \r
310                 Class<?> clazz = node.getClass();\r
311                 while (clazz != null && clazz != Object.class) {\r
312                     if (showClass)\r
313                         result.add(clazz);\r
314                     Field[] fields = clazz.getDeclaredFields();\r
315                     for (int i = 0; i < fields.length; ++i) {\r
316                         Field f = fields[i];\r
317 \r
318                         // Ignore transient properties, those are generally just caches.\r
319                         if (!showTransient && Modifier.isTransient(f.getModifiers()))\r
320                             continue;\r
321 \r
322                         // Ignore statics, those shouldn't affect data transfer.\r
323                         if (!showStatic && Modifier.isStatic(f.getModifiers()))\r
324                             continue;\r
325 \r
326                         result.add(new Attr(np, fields[i]));\r
327                     }\r
328                     clazz = clazz.getSuperclass();\r
329                 }\r
330                 return result.toArray();\r
331             }\r
332             return new Object[0];\r
333         }\r
334 \r
335         @Override\r
336         public void dispose() {\r
337         }\r
338 \r
339         @Override\r
340         public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
341         }\r
342     }\r
343 \r
344     abstract class NodeLabelProvider extends ColumnLabelProvider {\r
345         @Override\r
346         public final void update(ViewerCell cell) {\r
347             Object elem = cell.getElement();\r
348             if (elem instanceof Attr) {\r
349                 Attr attr = (Attr) elem;\r
350                 NodeProxy np = attr.getObject(NodeProxy.class);\r
351                 INode node = np.getNode();\r
352                 update(cell, attr, node);\r
353             } else if (elem instanceof ComputedAttr) {\r
354                 ComputedAttr attr = (ComputedAttr) elem;\r
355                 update(cell, attr);\r
356             } else if (elem instanceof Header) {\r
357                 updateHeader(cell, ((Header) elem).name);\r
358             } else if (elem instanceof Class<?>) {\r
359                 Class<?> clazz = (Class<?>) elem;\r
360                 updateHeader(cell, clazz.getSimpleName());\r
361             }\r
362         }\r
363 \r
364         public abstract void update(ViewerCell cell, ComputedAttr attr);\r
365         public abstract void update(ViewerCell cell, Attr attr, INode node);\r
366         public void updateHeader(ViewerCell cell, String header) {\r
367             cell.setFont(resourceManager.createFont(FontDescriptor.createFrom(cell.getFont()).withStyle(SWT.BOLD|SWT.ITALIC)));\r
368             cell.setForeground(cell.getControl().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));\r
369             cell.setBackground(cell.getControl().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));\r
370         }\r
371     }\r
372 \r
373     class FieldType extends NodeLabelProvider {\r
374         @Override\r
375         public void update(ViewerCell cell, ComputedAttr attr) {\r
376             cell.setText(attr.object != null ? attr.object.getClass().getSimpleName() : "null");\r
377         }\r
378         @Override\r
379         public void update(ViewerCell cell, Attr attr, INode node) {\r
380             Field f = attr.getField();\r
381             cell.setText(f.getType().getSimpleName());\r
382         }\r
383     }\r
384 \r
385     class FieldName extends NodeLabelProvider {\r
386         @Override\r
387         public void update(ViewerCell cell, ComputedAttr attr) {\r
388             cell.setText(attr.name);\r
389         }\r
390         @Override\r
391         public void update(ViewerCell cell, Attr attr, INode node) {\r
392             Field f = attr.getField();\r
393             cell.setText(f.getName());\r
394         }\r
395         @Override\r
396         public void updateHeader(ViewerCell cell, String header) {\r
397             super.updateHeader(cell, header);\r
398             cell.setText(header);\r
399         }\r
400     }\r
401 \r
402     class FieldValue extends NodeLabelProvider {\r
403         @Override\r
404         public void update(ViewerCell cell, ComputedAttr attr) {\r
405             cell.setText(attr.stringValue);\r
406         }\r
407         @Override\r
408         public void update(ViewerCell cell, Attr attr, INode node) {\r
409             Field f = attr.getField();\r
410             boolean accessible = f.isAccessible();\r
411             try {\r
412                 if (!accessible)\r
413                     f.setAccessible(true);\r
414                 Object value = f.get(node);\r
415                 String label = value == null ? "null" : ValueUtils.toString(value);\r
416                 cell.setText(label);\r
417 \r
418                 if (Modifier.isStatic(f.getModifiers())) {\r
419                     cell.setBackground((Color) resourceManager.get(staticColor));\r
420                 } else if (Modifier.isTransient(f.getModifiers())) {\r
421                     cell.setBackground((Color) resourceManager.get(transientColor));\r
422                 }\r
423             } catch (IllegalArgumentException e) {\r
424                 e.printStackTrace();\r
425                 cell.setText(e.getMessage());\r
426             } catch (IllegalAccessException e) {\r
427                 e.printStackTrace();\r
428                 cell.setText(e.getMessage());\r
429             } finally {\r
430                 if (!accessible)\r
431                     f.setAccessible(false);\r
432             }\r
433         }\r
434     }\r
435 \r
436     @Override\r
437     public void selectionChanged(SelectionChangedEvent event) {\r
438         selectionChanged(event.getSelection());\r
439     }\r
440 \r
441     public void selectionChanged(ISelection selection) {\r
442         //System.out.println("selection changed: " + event);\r
443         IStructuredSelection ss = (IStructuredSelection) selection;\r
444         Object obj = ss.getFirstElement();\r
445         if (ss.size() == 1 || (obj instanceof NodeProxy)) {\r
446             viewer.setInput(obj);\r
447         } else {\r
448             viewer.setInput(new Object());\r
449         }\r
450     }\r
451 \r
452     public void refresh() {\r
453         selectionChanged(selectionProvider.getSelection());\r
454     }\r
455 \r
456 }\r