1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.scenegraph.ui;
14 import java.lang.reflect.Field;
15 import java.lang.reflect.Modifier;
16 import java.util.ArrayList;
17 import java.util.List;
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;
63 * @author Tuukka Lehtonen
65 public class AttributeDialog extends Dialog implements ISelectionChangedListener {
67 private static final String DIALOG = "AttributeDialog"; //$NON-NLS-1$
69 private IDialogSettings dialogBoundsSettings;
71 private ResourceManager resourceManager;
73 private TableViewer viewer;
75 private final ISelectionProvider selectionProvider;
77 private final boolean showClass = true;
78 private boolean showTransient = false;
79 private boolean showStatic = false;
81 private final ColorDescriptor staticColor = ColorDescriptor.createFrom(new RGB(224, 224, 224));
82 private final ColorDescriptor transientColor = ColorDescriptor.createFrom(new RGB(192, 255, 255));
84 protected AttributeDialog(Shell parentShell, ISelectionProvider selectionProvider) {
86 this.selectionProvider = selectionProvider;
88 IDialogSettings settings = Activator.getDefault().getDialogSettings();
89 dialogBoundsSettings = settings.getSection(DIALOG);
90 if (dialogBoundsSettings == null)
91 dialogBoundsSettings = settings.addNewSection(DIALOG);
95 protected IDialogSettings getDialogBoundsSettings() {
96 return dialogBoundsSettings;
100 protected int getShellStyle() {
101 return SWT.RESIZE | SWT.MODELESS | SWT.TITLE | SWT.CLOSE | SWT.BORDER;
105 protected void configureShell(Shell newShell) {
106 super.configureShell(newShell);
107 newShell.setText("Scene Graph Node Attributes");
111 protected Control createButtonBar(Composite parent) {
116 protected void createButtonsForButtonBar(Composite parent) {
117 // No buttons, this is a non-modal property dialog.
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);
130 protected Control createDialogArea(Composite parent) {
131 Composite composite = (Composite) super.createDialogArea(parent);
133 this.resourceManager = new LocalResourceManager(JFaceResources.getResources());
134 composite.addListener(SWT.Dispose, new Listener() {
136 public void handleEvent(Event event) {
137 selectionProvider.removeSelectionChangedListener(AttributeDialog.this);
138 resourceManager.dispose();
139 resourceManager = null;
143 GridLayoutFactory.fillDefaults().margins(0, 0).spacing(0, 0).applyTo(composite);
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");
157 Composite tableComposite = new Composite(composite, SWT.NONE);
158 GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite);
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() {
165 public void keyPressed(KeyEvent e) {
166 if (e.keyCode == SWT.F5)
171 TableColumnLayout ad = new TableColumnLayout();
172 tableComposite.setLayout(ad);
174 viewer.getTable().setHeaderVisible(true);
175 viewer.getTable().setLinesVisible(true);
176 viewer.setUseHashlookup(true);
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());
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));
195 selectionProvider.addSelectionChangedListener(this);
197 showStaticItem.addSelectionListener(new SelectionAdapter() {
199 public void widgetSelected(SelectionEvent e) {
200 showStatic = showStaticItem.getSelection();
204 showTransientItem.addSelectionListener(new SelectionAdapter() {
206 public void widgetSelected(SelectionEvent e) {
207 showTransient = showTransientItem.getSelection();
211 refresh.addSelectionListener(new SelectionAdapter() {
213 public void widgetSelected(SelectionEvent e) {
221 applyDialogFont(composite);
225 static class Header {
227 public Header(String name) {
231 public String toString() {
236 static class ComputedAttr {
238 public Object object;
239 public String stringValue;
240 public ComputedAttr(String name, Object object) {
241 this(name, object, object != null ? object.toString() : "null");
243 public ComputedAttr(String name, Object object, String stringValue) {
245 this.object = object;
246 this.stringValue = stringValue;
249 public String toString() {
257 public Attr(Object obj, Field f) {
261 public Object getObject() {
264 public <T> T getObject(Class<T> clazz) {
265 return clazz.cast(object);
267 public Field getField() {
271 public String toString() {
272 return field.getName();
276 class FieldContentProvider implements ITreeContentProvider {
278 public Object[] getChildren(Object parentElement) {
279 return new Object[0];
283 public Object getParent(Object element) {
288 public boolean hasChildren(Object element) {
293 public Object[] getElements(Object inputElement) {
294 if (inputElement instanceof NodeProxy) {
295 NodeProxy np = (NodeProxy) inputElement;
296 INode node = np.getNode();
298 return new Object[0];
299 List<Object> result = new ArrayList<Object>();
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)));
310 Class<?> clazz = node.getClass();
311 while (clazz != null && clazz != Object.class) {
314 Field[] fields = clazz.getDeclaredFields();
315 for (int i = 0; i < fields.length; ++i) {
318 // Ignore transient properties, those are generally just caches.
319 if (!showTransient && Modifier.isTransient(f.getModifiers()))
322 // Ignore statics, those shouldn't affect data transfer.
323 if (!showStatic && Modifier.isStatic(f.getModifiers()))
326 result.add(new Attr(np, fields[i]));
328 clazz = clazz.getSuperclass();
330 return result.toArray();
332 return new Object[0];
336 public void dispose() {
340 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
344 abstract class NodeLabelProvider extends ColumnLabelProvider {
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;
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() + ")");
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));
373 class FieldType extends NodeLabelProvider {
375 public void update(ViewerCell cell, ComputedAttr attr) {
376 cell.setText(attr.object != null ? attr.object.getClass().getSimpleName() : "null");
379 public void update(ViewerCell cell, Attr attr, INode node) {
380 Field f = attr.getField();
381 cell.setText(f.getType().getSimpleName());
385 class FieldName extends NodeLabelProvider {
387 public void update(ViewerCell cell, ComputedAttr attr) {
388 cell.setText(attr.name);
391 public void update(ViewerCell cell, Attr attr, INode node) {
392 Field f = attr.getField();
393 cell.setText(f.getName());
396 public void updateHeader(ViewerCell cell, String header) {
397 super.updateHeader(cell, header);
398 cell.setText(header);
402 class FieldValue extends NodeLabelProvider {
404 public void update(ViewerCell cell, ComputedAttr attr) {
405 cell.setText(attr.stringValue);
408 public void update(ViewerCell cell, Attr attr, INode node) {
409 Field f = attr.getField();
410 boolean accessible = f.isAccessible();
413 f.setAccessible(true);
414 Object value = f.get(node);
415 String label = value == null ? "null" : ValueUtils.toString(value);
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));
423 } catch (IllegalArgumentException e) {
425 cell.setText(e.getMessage());
426 } catch (IllegalAccessException e) {
428 cell.setText(e.getMessage());
431 f.setAccessible(false);
437 public void selectionChanged(SelectionChangedEvent event) {
438 selectionChanged(event.getSelection());
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);
448 viewer.setInput(new Object());
452 public void refresh() {
453 selectionChanged(selectionProvider.getSelection());