--- /dev/null
+package org.simantics.views.swt.client.base;\r
+\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.lang.reflect.Method;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import org.eclipse.jface.resource.ColorDescriptor;\r
+import org.eclipse.jface.resource.FontDescriptor;\r
+import org.eclipse.jface.resource.ResourceManager;\r
+import org.eclipse.swt.widgets.Control;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.binding.ArrayBinding;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.reflection.BindingRequest;\r
+import org.simantics.datatypes.literal.Font;\r
+import org.simantics.datatypes.literal.RGB;\r
+import org.simantics.scenegraph.LoaderNode;\r
+import org.simantics.scenegraph.loader.ScenegraphLoaderUtils;\r
+import org.simantics.scl.runtime.function.Function1;\r
+import org.simantics.scl.runtime.function.Function2;\r
+import org.simantics.scl.runtime.function.FunctionImpl1;\r
+import org.simantics.ui.colors.Colors;\r
+import org.simantics.ui.fonts.Fonts;\r
+import org.simantics.utils.threads.IThreadWorkQueue;\r
+import org.simantics.utils.threads.SWTThread;\r
+import org.simantics.views.ViewUtils.GridDataBean;\r
+\r
+abstract public class SingleSWTViewNode<T extends Control> extends SWTParentNode implements LoaderNode {\r
+ \r
+ private static final long serialVersionUID = -5810308021930769003L;\r
+ \r
+ private Map<String, Object> genericProperties = new HashMap<String, Object>();\r
+ \r
+ public Function2<String, Object, Boolean> propertyCallback;\r
+ public GridDataBean layoutData;\r
+ public int style;\r
+ public String text;\r
+ public RGB.Integer background;\r
+ public RGB.Integer foreground;\r
+ public Font font;\r
+ protected transient ColorDescriptor backgroundDesc;\r
+ protected transient ColorDescriptor foregroundDesc;\r
+ protected transient FontDescriptor fontDesc;\r
+\r
+ protected T control;\r
+ \r
+ @Override\r
+ public Control getControl() {\r
+ return control;\r
+ }\r
+ \r
+ @Override\r
+ public void reset() {\r
+ control = null;\r
+ }\r
+\r
+ final protected boolean isDisposed() {\r
+ Control c = getControl();\r
+ return c == null ? false : c.isDisposed();\r
+ }\r
+\r
+ final protected void dispatch(final Runnable runnable) {\r
+ if(isNodeDisposed()) return;\r
+ IThreadWorkQueue thread = SWTThread.getThreadAccess();\r
+ if(thread.currentThreadAccess()) runnable.run();\r
+ else thread.asyncExec(new Runnable() {\r
+\r
+ @Override\r
+ public void run() {\r
+ if(isDisposed()) return;\r
+ Control c = control;\r
+ if(c == null || c.isDisposed()) return;\r
+ runnable.run();\r
+ }\r
+ \r
+ });\r
+ }\r
+ \r
+ private Field getPropertyField(String propertyName) {\r
+ assert(!isNodeDisposed());\r
+ return ScenegraphLoaderUtils.getPropertyField(this, propertyName);\r
+ }\r
+\r
+ private Class<?> getPropertyType(Field field) {\r
+ return field.getType();\r
+ }\r
+ \r
+ private Binding getPropertyBinding(Field field) {\r
+ try {\r
+ Binding b = Bindings.getBinding( BindingRequest.create( field ) );\r
+\r
+ // Safety checks for unusable bindings.\r
+ if (b instanceof ArrayBinding && ((ArrayBinding) b).type().componentType() == null)\r
+ return null;\r
+\r
+ return b;\r
+ } catch (BindingConstructionException e) {\r
+ return null;\r
+ } catch (Throwable t) {\r
+ return null;\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Convert binding to generic binding \r
+ * @param binding\r
+ * @return\r
+ */\r
+ private Binding getGenericPropertyBinding(Binding binding) {\r
+ if (binding == null) return null;\r
+ return Bindings.getBinding(binding.type());\r
+ }\r
+\r
+ @Override\r
+ public Function1<Object, Boolean> getPropertyFunction(final String propertyName) {\r
+\r
+ if(isNodeDisposed()) return null;\r
+ \r
+// assert(!isNodeDisposed());\r
+\r
+ final Field field = getPropertyField(propertyName);\r
+ if(field != null) {\r
+\r
+ return new FunctionImpl1<Object, Boolean>() {\r
+\r
+ final Method method = ScenegraphLoaderUtils.getSynchronizeMethod(SingleSWTViewNode.this, propertyName);\r
+ final Class<?> type = getPropertyType(field);\r
+ final Binding binding = getPropertyBinding(field);\r
+ final Binding genericBinding = getGenericPropertyBinding(binding);\r
+\r
+ private Object setField(Object value) {\r
+\r
+ try {\r
+ if(type.isPrimitive() || (value == null) || type.isInstance(value) || type.isArray()) {\r
+ field.set(SingleSWTViewNode.this, value);\r
+ return value;\r
+ } else {\r
+ Object instance = binding.createDefaultUnchecked();\r
+ binding.readFrom(genericBinding, value, instance);\r
+ field.set(SingleSWTViewNode.this, instance);\r
+ return instance;\r
+ }\r
+ } catch (IllegalArgumentException e1) {\r
+ e1.printStackTrace();\r
+ } catch (IllegalAccessException e1) {\r
+ e1.printStackTrace();\r
+ } catch (BindingException e) {\r
+ e.printStackTrace();\r
+ } catch (Throwable t) {\r
+ t.printStackTrace();\r
+ }\r
+\r
+ return null;\r
+\r
+ }\r
+\r
+ @Override\r
+ public Boolean apply(Object value) {\r
+\r
+ if(isNodeDisposed()) return true;\r
+\r
+ final Object translated = setField(value);\r
+\r
+ if(method != null) {\r
+ dispatch(new Runnable() {\r
+ \r
+ @Override\r
+ public void run() {\r
+ \r
+ try {\r
+ method.invoke(SingleSWTViewNode.this, translated);\r
+ } catch (IllegalArgumentException e) {\r
+ e.printStackTrace();\r
+ } catch (IllegalAccessException e) {\r
+ e.printStackTrace();\r
+ } catch (InvocationTargetException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ \r
+ });\r
+ }\r
+ return isDisposed();\r
+ }\r
+\r
+ };\r
+\r
+ } else {\r
+\r
+ return new FunctionImpl1<Object, Boolean>() {\r
+\r
+ @Override\r
+ public Boolean apply(Object p0) {\r
+ assert(!isNodeDisposed());\r
+ genericProperties.put(propertyName, p0);\r
+ return isDisposed();\r
+ }\r
+\r
+ };\r
+\r
+ }\r
+\r
+ }\r
+\r
+ @Override\r
+ public void setPropertyCallback(Function2<String, Object, Boolean> callback) {\r
+ this.propertyCallback = callback;\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T2> T2 getProperty(String propertyName) {\r
+\r
+ Method m = ScenegraphLoaderUtils.getReadMethod(this, propertyName);\r
+ if(m != null) {\r
+\r
+ try {\r
+ return (T2)m.invoke(this);\r
+ } catch (IllegalArgumentException e) {\r
+ throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e);\r
+ } catch (IllegalAccessException e) {\r
+ throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e);\r
+ } catch (InvocationTargetException e) {\r
+ throw new RuntimeException("Failed to get property '" + propertyName + "' for " + getClass().getName(), e);\r
+ }\r
+ \r
+ } else {\r
+ return (T2)genericProperties.get(propertyName);\r
+ }\r
+ \r
+ }\r
+ \r
+ protected void setProperties() {\r
+ for(Method m : getClass().getMethods()) {\r
+ if(m.getName().startsWith("synchronize")) {\r
+ String upperFieldName = m.getName().substring("synchronize".length());\r
+ String fieldName = upperFieldName.substring(0,1).toLowerCase() + upperFieldName.substring(1);\r
+ Field f = ScenegraphLoaderUtils.getPropertyField(this, fieldName);\r
+ try {\r
+ m.invoke(this, f.get(this));\r
+ } catch (IllegalArgumentException e) {\r
+ throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e);\r
+ } catch (IllegalAccessException e) {\r
+ throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e);\r
+ } catch (InvocationTargetException e) {\r
+ throw new RuntimeException("Failed to set property '" + fieldName + "' for " + getClass().getName(), e);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ public void synchronizeText(String text) {\r
+ }\r
+\r
+ final public void synchronizeFont(Font font) {\r
+ if (font == null && fontDesc == null)\r
+ return;\r
+ ResourceManager rm = getResourceManager();\r
+ FontDescriptor oldDesc = fontDesc;\r
+ if (font != null)\r
+ control.setFont( getResourceManager().createFont( fontDesc = Fonts.swt(font) ) );\r
+ if (oldDesc != null)\r
+ rm.destroy(oldDesc);\r
+ }\r
+ \r
+ final public void synchronizeForeground(RGB.Integer foreground) {\r
+ if (foreground == null && foregroundDesc == null)\r
+ return;\r
+ ResourceManager rm = getResourceManager();\r
+ ColorDescriptor oldDesc = foregroundDesc;\r
+ if (foreground != null)\r
+ control.setForeground( getResourceManager().createColor( foregroundDesc = Colors.swt(foreground) ) );\r
+ if (oldDesc != null)\r
+ rm.destroy(oldDesc);\r
+ }\r
+\r
+ final public void synchronizeBackground(RGB.Integer background) {\r
+ if (background == null && backgroundDesc == null)\r
+ return;\r
+ ResourceManager rm = getResourceManager();\r
+ ColorDescriptor oldDesc = backgroundDesc;\r
+ if (background != null)\r
+ control.setBackground( getResourceManager().createColor( backgroundDesc = Colors.swt(background) ) );\r
+ if (oldDesc != null)\r
+ rm.destroy(oldDesc);\r
+ }\r
+\r
+ final public void synchronizeStyle(int style) {\r
+ }\r
+\r
+ final public void synchronizeLayoutData(GridDataBean layoutData) {\r
+ if(layoutData != null) control.setLayoutData(SWTViewUtils.toGridData(layoutData));\r
+ }\r
+ \r
+}\r