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