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