1 package org.simantics.views.swt.client.base;
3 import java.lang.reflect.Field;
4 import java.lang.reflect.InvocationTargetException;
5 import java.lang.reflect.Method;
6 import java.util.HashMap;
9 import org.eclipse.jface.resource.ColorDescriptor;
10 import org.eclipse.jface.resource.FontDescriptor;
11 import org.eclipse.jface.resource.ResourceManager;
12 import org.eclipse.swt.widgets.Control;
13 import org.simantics.databoard.Bindings;
14 import org.simantics.databoard.binding.ArrayBinding;
15 import org.simantics.databoard.binding.Binding;
16 import org.simantics.databoard.binding.error.BindingConstructionException;
17 import org.simantics.databoard.binding.error.BindingException;
18 import org.simantics.databoard.binding.reflection.BindingRequest;
19 import org.simantics.datatypes.literal.Font;
20 import org.simantics.datatypes.literal.RGB;
21 import org.simantics.scenegraph.LoaderNode;
22 import org.simantics.scenegraph.loader.ScenegraphLoaderUtils;
23 import org.simantics.scl.runtime.function.Function1;
24 import org.simantics.scl.runtime.function.Function2;
25 import org.simantics.scl.runtime.function.FunctionImpl1;
26 import org.simantics.ui.colors.Colors;
27 import org.simantics.ui.fonts.Fonts;
28 import org.simantics.utils.threads.IThreadWorkQueue;
29 import org.simantics.utils.threads.SWTThread;
30 import org.simantics.views.ViewUtils.LayoutDataBean;
32 abstract public class SingleSWTViewNode<T extends Control> extends SWTParentNode implements LoaderNode {
34 private static final long serialVersionUID = -5810308021930769003L;
36 private Map<String, Object> genericProperties = new HashMap<String, Object>();
38 public Function2<String, Object, Boolean> propertyCallback;
39 public LayoutDataBean layoutData;
42 public RGB.Integer background;
43 public RGB.Integer foreground;
45 protected transient ColorDescriptor backgroundDesc;
46 protected transient ColorDescriptor foregroundDesc;
47 protected transient FontDescriptor fontDesc;
52 public Control getControl() {
61 final protected boolean isDisposed() {
62 Control c = getControl();
63 return c == null ? false : c.isDisposed();
66 final protected void dispatch(final Runnable runnable) {
67 if(isNodeDisposed()) return;
68 IThreadWorkQueue thread = SWTThread.getThreadAccess();
69 if(thread.currentThreadAccess()) runnable.run();
70 else thread.asyncExec(new Runnable() {
74 if(isDisposed()) return;
76 if(c == null || c.isDisposed()) return;
83 private Field getPropertyField(String propertyName) {
84 assert(!isNodeDisposed());
85 return ScenegraphLoaderUtils.getPropertyField(this, propertyName);
88 private Class<?> getPropertyType(Field field) {
89 return field.getType();
92 private Binding getPropertyBinding(Field field) {
94 Binding b = Bindings.getBinding( BindingRequest.create( field ) );
96 // Safety checks for unusable bindings.
97 if (b instanceof ArrayBinding && ((ArrayBinding) b).type().componentType() == null)
101 } catch (BindingConstructionException e) {
103 } catch (Throwable t) {
109 * Convert binding to generic binding
113 private Binding getGenericPropertyBinding(Binding binding) {
114 if (binding == null) return null;
115 return Bindings.getBinding(binding.type());
119 public Function1<Object, Boolean> getPropertyFunction(final String propertyName) {
121 if(isNodeDisposed()) return null;
123 // assert(!isNodeDisposed());
125 final Field field = getPropertyField(propertyName);
128 return new FunctionImpl1<Object, Boolean>() {
130 final Method method = ScenegraphLoaderUtils.getSynchronizeMethod(SingleSWTViewNode.this, propertyName);
131 final Class<?> type = getPropertyType(field);
132 final Binding binding = getPropertyBinding(field);
133 final Binding genericBinding = getGenericPropertyBinding(binding);
135 private Object setField(Object value) {
138 if(type.isPrimitive() || (value == null) || type.isInstance(value) || type.isArray()) {
139 field.set(SingleSWTViewNode.this, value);
142 Object instance = binding.createDefaultUnchecked();
143 binding.readFrom(genericBinding, value, instance);
144 field.set(SingleSWTViewNode.this, instance);
147 } catch (IllegalArgumentException e1) {
148 e1.printStackTrace();
149 } catch (IllegalAccessException e1) {
150 e1.printStackTrace();
151 } catch (BindingException e) {
153 } catch (Throwable t) {
162 public Boolean apply(Object value) {
164 if(isNodeDisposed()) return true;
166 final Object translated = setField(value);
169 dispatch(new Runnable() {
175 method.invoke(SingleSWTViewNode.this, translated);
176 } catch (IllegalArgumentException e) {
178 } catch (IllegalAccessException e) {
180 } catch (InvocationTargetException e) {
181 e.getCause().printStackTrace();
194 return new FunctionImpl1<Object, Boolean>() {
197 public Boolean apply(Object p0) {
198 assert(!isNodeDisposed());
199 genericProperties.put(propertyName, p0);
210 public void setPropertyCallback(Function2<String, Object, Boolean> callback) {
211 this.propertyCallback = callback;
214 @SuppressWarnings("unchecked")
216 public <T2> T2 getProperty(String propertyName) {
218 Method m = ScenegraphLoaderUtils.getReadMethod(this, propertyName);
222 return (T2)m.invoke(this);
223 } catch (IllegalArgumentException e) {
224 throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e);
225 } catch (IllegalAccessException e) {
226 throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e);
227 } catch (InvocationTargetException e) {
228 throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e.getCause());
232 return (T2)genericProperties.get(propertyName);
237 protected void setProperties() {
238 for(Method m : getClass().getMethods()) {
239 if(m.getName().startsWith("synchronize")) {
240 String upperFieldName = m.getName().substring("synchronize".length());
241 String fieldName = upperFieldName.substring(0,1).toLowerCase() + upperFieldName.substring(1);
242 Field f = ScenegraphLoaderUtils.getPropertyField(this, fieldName);
244 m.invoke(this, f.get(this));
245 } catch (IllegalArgumentException e) {
246 throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e);
247 } catch (IllegalAccessException e) {
248 throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e);
249 } catch (InvocationTargetException e) {
250 throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e.getCause());
256 public void synchronizeText(String text) {
259 final public void synchronizeFont(Font font) {
260 if (font == null && fontDesc == null)
262 ResourceManager rm = getResourceManager();
263 FontDescriptor oldDesc = fontDesc;
265 control.setFont( getResourceManager().createFont( fontDesc = Fonts.swt(font) ) );
270 final public void synchronizeForeground(RGB.Integer foreground) {
271 if (foreground == null && foregroundDesc == null)
273 ResourceManager rm = getResourceManager();
274 ColorDescriptor oldDesc = foregroundDesc;
275 if (foreground != null)
276 control.setForeground( getResourceManager().createColor( foregroundDesc = Colors.swt(foreground) ) );
281 final public void synchronizeBackground(RGB.Integer background) {
282 if (background == null && backgroundDesc == null)
284 ResourceManager rm = getResourceManager();
285 ColorDescriptor oldDesc = backgroundDesc;
286 if (background != null)
287 control.setBackground( getResourceManager().createColor( backgroundDesc = Colors.swt(background) ) );
292 final public void synchronizeStyle(int style) {
295 final public void synchronizeLayoutData(LayoutDataBean layoutData) {
296 if(layoutData != null) control.setLayoutData(SWTViewUtils.toLayoutData(layoutData));