]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/forms/DataboardForm.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / forms / DataboardForm.java
index c73f76eab60ab3e302dfaffdc623e4f6d05a0ba3..b86726c19a5117f2c7bde92fd31f7348661e81c1 100644 (file)
-package org.simantics.databoard.forms;\r
-\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-\r
-import org.eclipse.jface.window.Window;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.custom.CTabFolder;\r
-import org.eclipse.swt.custom.CTabItem;\r
-import org.eclipse.swt.events.SelectionAdapter;\r
-import org.eclipse.swt.events.SelectionEvent;\r
-import org.eclipse.swt.graphics.Color;\r
-import org.eclipse.swt.layout.GridData;\r
-import org.eclipse.swt.layout.GridLayout;\r
-import org.eclipse.swt.widgets.Button;\r
-import org.eclipse.swt.widgets.Combo;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Control;\r
-import org.eclipse.swt.widgets.DirectoryDialog;\r
-import org.eclipse.swt.widgets.Event;\r
-import org.eclipse.swt.widgets.FileDialog;\r
-import org.eclipse.swt.widgets.Group;\r
-import org.eclipse.swt.widgets.Label;\r
-import org.eclipse.swt.widgets.Listener;\r
-import org.eclipse.swt.widgets.Shell;\r
-import org.eclipse.swt.widgets.Text;\r
-import org.simantics.databoard.Accessors;\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.accessor.Accessor;\r
-import org.simantics.databoard.accessor.BooleanAccessor;\r
-import org.simantics.databoard.accessor.RecordAccessor;\r
-import org.simantics.databoard.accessor.StringAccessor;\r
-import org.simantics.databoard.accessor.UnionAccessor;\r
-import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
-import org.simantics.databoard.accessor.error.AccessorException;\r
-import org.simantics.databoard.accessor.error.ReferenceException;\r
-import org.simantics.databoard.accessor.reference.ChildReference;\r
-import org.simantics.databoard.accessor.reference.LabelReference;\r
-import org.simantics.databoard.binding.Binding;\r
-import org.simantics.databoard.binding.NumberBinding;\r
-import org.simantics.databoard.binding.RecordBinding;\r
-import org.simantics.databoard.binding.StringBinding;\r
-import org.simantics.databoard.binding.UnionBinding;\r
-import org.simantics.databoard.binding.error.BindingException;\r
-import org.simantics.databoard.binding.mutable.TaggedObject;\r
-import org.simantics.databoard.parser.repository.DataTypeSyntaxError;\r
-import org.simantics.databoard.type.BooleanType;\r
-import org.simantics.databoard.type.Component;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.databoard.type.NumberType;\r
-import org.simantics.databoard.type.RecordType;\r
-import org.simantics.databoard.type.StringType;\r
-import org.simantics.databoard.type.UnionType;\r
-import org.simantics.databoard.util.Bean;\r
-import org.simantics.databoard.util.StringUtil;\r
-import org.simantics.databoard.util.URIUtil;\r
-\r
-/**\r
- * Databoard form creates SWT user interface from databoard record type.\r
- * \r
- * See DataboardFormExample for example usage.\r
- *\r
- * @author toni.kalajainen@semantum.fi\r
- */\r
-public class DataboardForm {   \r
-\r
-       public static final Datatype TEXTBOX;\r
-       public static final Datatype PASSWORD;\r
-       \r
-       public int column1Width = -1;\r
-       \r
-       private RecordType allfields = new RecordType();\r
-       \r
-       /**\r
-        * Make file type\r
-        * \r
-        * @param namesAndExts array of names and extensions\r
-        * @param exts\r
-        * @return\r
-        */\r
-       public static StringType fileSaveDialog(String...namesAndExts)\r
-       {               \r
-           StringType result = new StringType();\r
-           result.metadata.put("style", "filesave");\r
-                   \r
-           StringBuilder sb1 = new StringBuilder();\r
-           StringBuilder sb2 = new StringBuilder();\r
-           \r
-           for (int i=0; i<namesAndExts.length; i++) {\r
-               StringBuilder sb = (i%2==0?sb1:sb2);\r
-               int j = i/2;\r
-               if (j>0) sb.append(','); \r
-               sb.append(namesAndExts[i]);\r
-           }\r
-           \r
-               result.metadata.put("filetypes", sb1.toString());\r
-           result.metadata.put("fileexts", sb2.toString());\r
-           return result;\r
-       }\r
-\r
-       /**\r
-        * Make file type\r
-        * \r
-        * @param namesAndExts array of names and extensions\r
-        * @param exts\r
-        * @return\r
-        */\r
-       public static StringType fileOpenDialog(String...namesAndExts)\r
-       {               \r
-           StringType result = new StringType();\r
-           result.metadata.put("style", "fileopen");\r
-                   \r
-           StringBuilder sb1 = new StringBuilder();\r
-           StringBuilder sb2 = new StringBuilder();\r
-           \r
-           for (int i=0; i<namesAndExts.length; i++) {\r
-               StringBuilder sb = (i%2==0?sb1:sb2);\r
-               int j = i/2;\r
-               if (j>0) sb.append(','); \r
-               sb.append(namesAndExts[i]);\r
-           }\r
-           \r
-               result.metadata.put("filetypes", sb1.toString());\r
-           result.metadata.put("fileexts", sb2.toString());\r
-           return result;\r
-       }\r
-       \r
-       public static StringType directoryDialog()\r
-       {               \r
-           StringType result = new StringType();\r
-           result.metadata.put("style", "directory");\r
-           return result;\r
-       }\r
-       \r
-       public DataboardForm() {\r
-       }\r
-       \r
-       public void setFirstColumnWidth(int width) {\r
-               column1Width = width;\r
-       }\r
-       \r
-       \r
-       /**\r
-        * Validate the fields for valid values.\r
-        * StringTypes can be restricted with regular expressions.\r
-        * NumberTypes with value ranges.\r
-        * \r
-        * @param composite\r
-        * @return list of problems\r
-        */\r
-       public List<Problem> validate( Composite composite ) {\r
-               List<Problem> result = new ArrayList<Problem>();\r
-               _validateFields(composite, result);\r
-               return result;\r
-       }\r
-       \r
-       public static List<String> toStrings(List<Problem> problems) {\r
-               List<String> result = new ArrayList<String>( problems.size() );\r
-               for (Problem p : problems) result.add(p.error);\r
-               return result;\r
-       }\r
-       \r
-       public static class Problem extends Bean {\r
-               /** Reference to the field with problem */\r
-               public String fieldReference;\r
-               /** The field with problem */\r
-               public transient Control control;\r
-               /** The validation error */\r
-               public String error;\r
-       }\r
-\r
-       protected void _validateFields( Control control, List<Problem> result )\r
-       {\r
-               // Read this control\r
-               Object data = control.getData();\r
-               if ( data != null && data instanceof String ) {\r
-                       String refStr = (String) data;\r
-                       ChildReference ref = ChildReference.parsePath( refStr);\r
-                       if ( ref != null ) {\r
-                               try {\r
-                                       Datatype fieldType = allfields.getChildType( ref );\r
-                                       \r
-                                       // Read Combo Union\r
-                                       if ( fieldType instanceof UnionType && control instanceof Combo ) {\r
-                                               // Nothing to validate\r
-                                       } else\r
-\r
-                                       // Read Radio Union\r
-                                       if ( fieldType instanceof UnionType && control instanceof Composite ) {\r
-                                               // Nothing to validate\r
-                                       } else\r
-                                       \r
-                                       // Read Boolean\r
-                                       if ( fieldType instanceof BooleanType && control instanceof Button ) {\r
-                                               // Nothing to validate\r
-                                       } else\r
-\r
-                                       // Text + Dialog button\r
-                                       if ( fieldType instanceof RecordType && control instanceof Text ) {\r
-                                               try {\r
-                                                       Text text = (Text) control;\r
-                                                       RecordBinding fieldBinding = Bindings.getMutableBinding(fieldType);\r
-                                                       Object value = parse(fieldBinding, text.getText());\r
-                                                       fieldBinding.assertInstaceIsValid( value );\r
-                                               } catch (BindingException er) {\r
-                                                       Problem problem = new Problem();\r
-                                                       if ( er.getCause()!=null ) {\r
-                                                               problem.error = er.getCause().getMessage();\r
-                                                       } else {\r
-                                                               problem.error = er.getMessage();\r
-                                                       }\r
-                                                       problem.fieldReference = refStr;\r
-                                                       problem.control = control;\r
-                                                       result.add( problem );\r
-                                               }                                               \r
-                                               \r
-                                       } else\r
-                                               \r
-                                       // Read Text\r
-                                       if ( fieldType instanceof StringAccessor && control instanceof Text ) {\r
-                                               try {\r
-                                                       Text text = (Text) control;\r
-                                                       StringBinding binding = Bindings.getBinding(fieldType);\r
-                                                       Object value = binding.create(text.getText());\r
-                                                       binding.assertInstaceIsValid( value );\r
-                                               } catch (BindingException er) {\r
-                                                       Problem problem = new Problem();\r
-                                                       if ( er.getCause()!=null ) {\r
-                                                               problem.error = er.getCause().getMessage();\r
-                                                       } else {\r
-                                                               problem.error = er.getMessage();\r
-                                                       }\r
-                                                       problem.fieldReference = refStr;\r
-                                                       problem.control = control;\r
-                                                       result.add( problem );\r
-                                               }                                               \r
-                                       } else\r
-                                       \r
-                                       // Read Numbers\r
-                                       if ( fieldType instanceof NumberType && control instanceof Text ) {\r
-                                               try {\r
-                                                       Text text = (Text) control;\r
-                                                       NumberBinding binding = Bindings.getBinding(fieldType);\r
-                                                       Object value = binding.create(text.getText());\r
-                                                       binding.assertInstaceIsValid( value );\r
-                                               } catch (BindingException er) {\r
-                                                       Problem problem = new Problem();\r
-                                                       if ( er.getCause()!=null && er.getCause() instanceof NumberFormatException) {\r
-                                                               NumberFormatException nfe = (NumberFormatException) er.getCause();\r
-                                                               problem.error = nfe.getMessage();\r
-                                                       } else {\r
-                                                               problem.error = er.getMessage();\r
-                                                       }\r
-                                                       problem.fieldReference = refStr;\r
-                                                       problem.control = control;\r
-                                                       result.add( problem );\r
-                                               }                                               \r
-                                       }\r
-                                       \r
-                               } catch (AccessorConstructionException e) {\r
-                                       Problem problem = new Problem();\r
-                                       problem.error = e.getMessage();\r
-                                       problem.fieldReference = refStr;\r
-                                       problem.control = control;\r
-                                       result.add( problem );\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               // Recursion\r
-               if ( control instanceof Composite ) {\r
-                       Composite composite = (Composite) control;\r
-                       for (Control child : composite.getChildren()) \r
-                       {\r
-                               _validateFields(child, result);\r
-                       }\r
-               }\r
-               \r
-       }\r
-       \r
-       public RecordType type() {\r
-               return allfields;\r
-       }\r
-\r
-       /**\r
-        * Find control by reference \r
-        * @param control\r
-        * @param ref\r
-        * @return control or null\r
-        */\r
-       public Control getControl( Control control, ChildReference ref )\r
-       {\r
-               return getControl( control, ref.toPath() );\r
-       }\r
-       \r
-       /**\r
-        * Find control by reference. \r
-        * \r
-        * @param composite\r
-        * @param ref\r
-        * @return control or null\r
-        */\r
-       public Control getControl( Control control, String ref )\r
-       {\r
-               Object data = control.getData();\r
-               if ( data != null && data instanceof String && data.equals(ref)) return control;\r
-                       \r
-               // Recursion\r
-               if ( control instanceof Composite ) {\r
-                       Composite composite = (Composite) control;\r
-                       for (Control child : composite.getChildren()) \r
-                       {\r
-                               Control result = getControl(child, ref);\r
-                               if ( result != null ) return result;\r
-                       }\r
-               }\r
-               return null;\r
-       }\r
-       \r
-       /**\r
-        * Reads values of fields into a record\r
-        * \r
-        * @param composite the composite that holds the widgets\r
-        * @param binding record binding\r
-        * @param dst the record where values are read to\r
-        * @throws BindingException\r
-        * @throws AccessorConstructionException \r
-        * @throws AccessorException \r
-        */\r
-       public void readFields( Composite composite, RecordBinding binding, Object dst)\r
-       throws BindingException, AccessorConstructionException, AccessorException\r
-       {\r
-               RecordAccessor ra = Accessors.getAccessor(binding, dst);\r
-               _readFields(composite, ra);\r
-       }\r
-       \r
-       protected void _readFields( Control control, RecordAccessor ra ) \r
-       throws AccessorException, BindingException\r
-       {\r
-               // Read this control\r
-               Object data = control.getData();\r
-               if ( data != null && data instanceof String ) {\r
-                       ChildReference ref = ChildReference.parsePath( (String) data);\r
-                       if ( ref != null ) {\r
-                               try {\r
-                                       Accessor fieldAccessor = ra.getComponent( ref );\r
-                                       \r
-                                       // Read Combo Union\r
-                                       if ( fieldAccessor instanceof UnionAccessor && control instanceof Combo ) {\r
-                                               UnionAccessor sa = (UnionAccessor) fieldAccessor;\r
-                                               Combo combo = (Combo) control;\r
-                                               String text = combo.getText();\r
-                                               int tag = sa.type().getComponentIndex2(text);\r
-                                               Datatype tagType = sa.type().components[tag].type;\r
-                                               Binding tagBinding = Bindings.getBinding(tagType);\r
-                                               Object defaultValue = tagBinding.createDefault();                                                                       \r
-                                               sa.setComponentValue(tag, tagBinding, defaultValue);\r
-                                       } else\r
-\r
-                                       // Read Radio Union\r
-                                       if ( fieldAccessor instanceof UnionAccessor && control instanceof Composite ) {\r
-                                               Composite radioComposite = (Composite) control;\r
-                                               for (Control c : radioComposite.getChildren()) {\r
-                                                       if ( c instanceof Button && ((Button)c).getSelection() ) {\r
-                                                               Object data2 = c.getData();\r
-                                                               if (data2==null) continue;\r
-                                                               String name = getName( data2.toString() );\r
-                                                               if ( name==null ) continue;\r
-                                                               \r
-                                                               UnionAccessor sa = (UnionAccessor) fieldAccessor;\r
-                                                               int tag = sa.type().getComponentIndex2( name );\r
-                                                               if ( tag>=0 ) {\r
-                                                                       Datatype tagType = sa.type().components[tag].type;\r
-                                                                       Binding tagBinding = Bindings.getBinding(tagType);\r
-                                                                       Object defaultValue = tagBinding.createDefault();                                                                       \r
-                                                                       sa.setComponentValue(tag, tagBinding, defaultValue);\r
-                                                                       break;\r
-                                                               }\r
-                                                       }\r
-                                               }\r
-                                       } else\r
-                                       \r
-                                       // Read Boolean\r
-                                       if ( fieldAccessor instanceof BooleanAccessor && control instanceof Button ) {\r
-                                               BooleanAccessor sa = (BooleanAccessor) fieldAccessor;\r
-                                               sa.setValue(((Button)control).getSelection());\r
-                                       } else\r
-\r
-                                       // Read Text + Dialog Button\r
-                                       if ( fieldAccessor instanceof RecordAccessor && control instanceof Text ) {\r
-                                               RecordAccessor raa = (RecordAccessor) fieldAccessor;\r
-                                               Text text = (Text) control;\r
-                                               RecordBinding fieldBinding = Bindings.getMutableBinding( raa.type() );\r
-                                               Object value = parse( fieldBinding, text.getText() );\r
-                                               raa.setValue(fieldBinding, value);\r
-                                       } else\r
-                                               \r
-                                       // Read Text\r
-                                       if ( fieldAccessor instanceof StringAccessor && control instanceof Text ) {\r
-                                               StringAccessor sa = (StringAccessor) fieldAccessor;\r
-                                               sa.setValue(((Text)control).getText());\r
-                                       } else\r
-                                       \r
-                                       // Read Numbers\r
-                                       if ( fieldAccessor.type() instanceof NumberType && control instanceof Text ) {\r
-                                               NumberBinding binding = Bindings.getBinding( fieldAccessor.type() );\r
-                                               Object value = binding.create( ((Text)control).getText() );\r
-                                               fieldAccessor.setValue(binding, value);\r
-                                       }\r
-                                       \r
-                               } catch (AccessorConstructionException e) {\r
-                                       //e.printStackTrace();\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               // Recursion\r
-               if ( control instanceof Composite ) {\r
-                       Composite composite = (Composite) control;\r
-                       for (Control child : composite.getChildren()) \r
-                       {\r
-                               _readFields(child, ra);\r
-                       }\r
-               }\r
-       }\r
-\r
-       public void writeFields( Composite composite, RecordBinding binding, Object src)\r
-                       throws AccessorException, BindingException, AccessorConstructionException\r
-       {\r
-               RecordAccessor ra = Accessors.getAccessor(binding, src);\r
-               _writeFields(composite, ra);\r
-       }\r
-       \r
-       void _writeFields( Control control, RecordAccessor ra )\r
-                       throws AccessorException, BindingException, AccessorConstructionException\r
-       {\r
-               // Read this control\r
-               Object data = control.getData();\r
-               if ( data != null && data instanceof String ) {\r
-                       ChildReference ref = ChildReference.parsePath( (String) data);\r
-                       if ( ref != null ) {\r
-                               try {\r
-                                       Accessor fieldAccessor = ra.getComponent( ref );\r
-                                       \r
-                                       // Read Combo Union\r
-                                       if ( fieldAccessor instanceof UnionAccessor && control instanceof Combo ) {\r
-                                               UnionAccessor sa = (UnionAccessor) fieldAccessor;\r
-                                               Combo combo = (Combo) control;\r
-                                               int tag = sa.getTag();                                          \r
-                                               combo.setText( sa.type().getComponent(tag).name );\r
-                                       } else\r
-\r
-                                       // Read Radio Union\r
-                                       if ( fieldAccessor instanceof UnionAccessor && control instanceof Composite) {\r
-                                               Composite radioComposite = (Composite) control;\r
-                                               UnionAccessor sa = (UnionAccessor) fieldAccessor;\r
-                                               int tag = sa.getTag();\r
-                                               for (int i=0; i<sa.count(); i++) {\r
-                                                       Button button = (Button) radioComposite.getChildren()[i*2];\r
-                                                       button.setSelection(i==tag);\r
-                                               }\r
-                                       } else\r
-                                       \r
-                                       // Read Boolean\r
-                                       if ( fieldAccessor instanceof BooleanAccessor && control instanceof Button ) {\r
-                                               BooleanAccessor sa = (BooleanAccessor) fieldAccessor;\r
-                                               ((Button)control).setSelection(sa.getValue());\r
-                                       } else\r
-\r
-                                       // Write Text + Dialog Selection\r
-                                       if ( fieldAccessor instanceof RecordAccessor && control instanceof Text ) {\r
-                                               RecordAccessor raa = (RecordAccessor) fieldAccessor;\r
-                                               Text text = (Text) control;\r
-                                               RecordBinding fieldBinding = Bindings.getMutableBinding( raa.type() );\r
-                                               String str = print( fieldBinding, raa.getValue(fieldBinding) );                                 \r
-                                               text.setText( str );\r
-                                       } else\r
-                                               \r
-                                       // Read Text\r
-                                       if ( fieldAccessor instanceof StringAccessor && control instanceof Text ) {\r
-                                               StringAccessor sa = (StringAccessor) fieldAccessor;\r
-                                               ((Text)control).setText( sa.getValue() );\r
-                                       } else\r
-                                       \r
-                                       // Read Numbers\r
-                                       if ( fieldAccessor.type() instanceof NumberType && control instanceof Text ) {\r
-                                               NumberBinding binding = Bindings.getBinding( fieldAccessor.type() );\r
-                                               Object value = fieldAccessor.getValue(binding);\r
-                                               ((Text)control).setText( binding.toString(value, true) );\r
-                                       }\r
-                                       \r
-                               } catch (AccessorConstructionException e) {\r
-                                       //e.printStackTrace();\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               // Recursion\r
-               if ( control instanceof Composite ) {\r
-                       Composite composite = (Composite) control;\r
-                       for (Control child : composite.getChildren()) \r
-                       {\r
-                               _writeFields(child, ra);\r
-                       }\r
-               }\r
-               \r
-       }\r
-       \r
-       public void clear( Control control ) \r
-       {               \r
-               _clearFields( control );\r
-               allfields.clear();\r
-       }\r
-\r
-       protected void _clearFields( Control control ) \r
-       {               \r
-               // Recursion\r
-               if ( control instanceof Composite ) {\r
-                       Composite composite = (Composite) control;\r
-                       for (Control child : composite.getChildren()) \r
-                       {\r
-                               _clearFields(child);\r
-                               child.dispose();\r
-                       }\r
-               }\r
-\r
-       }\r
-       \r
-       /**\r
-        * Add a listener to all the controls that represent the data type.\r
-        * \r
-        * @param composite the composite that holds the widgets\r
-        * @param binding record binding\r
-        * @param dst the record where values are read to\r
-        * @throws BindingException\r
-        * @throws AccessorConstructionException \r
-        * @throws AccessorException \r
-        */\r
-       public void addListener( Composite composite, RecordType type, Listener listener )\r
-       throws BindingException, AccessorConstructionException, AccessorException\r
-       {\r
-               _addListener(composite, type, listener );\r
-       }\r
-       \r
-       protected void _addListener( Control control, Datatype type, Listener listener ) \r
-       throws AccessorException, BindingException\r
-       {\r
-               // Read this control\r
-               Object data = control.getData();\r
-               ChildReference ref = null;\r
-               if ( data != null && data instanceof String ) {\r
-                       ref = ChildReference.parsePath( (String) data);\r
-               }\r
-                       \r
-                               try {                                   \r
-                                       Datatype fieldType = ref==null ? type : type.getChildType( ref );\r
-\r
-                                       // Read Combo Union\r
-                                       if ( fieldType instanceof UnionType && control instanceof Combo ) {\r
-                                               control.addListener(SWT.Selection, listener);\r
-                                       } else\r
-                                       // Read Radio Union\r
-                                       if ( fieldType instanceof UnionType && control instanceof Composite ) {\r
-                                               Composite radioComposite = (Composite) control;\r
-                                               for (Control c : radioComposite.getChildren()) {\r
-                                                       if ( c instanceof Button ) {\r
-                                                               c.addListener(SWT.Selection, listener);\r
-                                                       }\r
-                                               }\r
-                                       } else\r
-                                       \r
-                                       // Read Boolean\r
-                                       if ( fieldType instanceof BooleanType && control instanceof Button ) {\r
-                                               control.addListener(SWT.Selection, listener);\r
-                                       } else\r
-                                       \r
-                                       // Read Text\r
-                                       if ( fieldType instanceof StringType && control instanceof Text ) {\r
-                                               control.addListener(SWT.Modify, listener);\r
-                                       } else\r
-                                       \r
-                                       // Read Numbers\r
-                                       if ( fieldType instanceof NumberType && control instanceof Text ) {\r
-                                               control.addListener(SWT.Modify, listener);\r
-                                       }\r
-                               \r
-                               } catch (ReferenceException re) {\r
-                               }\r
-               \r
-               // Recursion\r
-               if ( control instanceof Composite ) {\r
-                       Composite composite = (Composite) control;\r
-                       for (Control child : composite.getChildren()) \r
-                       {\r
-                               _addListener(child, type, listener);\r
-                       }\r
-               }\r
-       }\r
-       \r
-       public void addField( Composite parent, String name, Datatype fieldType )\r
-       {\r
-               addField(parent, fieldType, URIUtil.encodeURI(name), null);\r
-               allfields.addComponent(name, fieldType);\r
-       }\r
-       \r
-       public void addField( Composite parent, String name, Datatype fieldType, String ref )\r
-       {\r
-               addField(parent, fieldType, appendName(ref, name), null);\r
-               \r
-               RecordType root = allfields;\r
-               if ( ref != null && !ref.isEmpty() ) {\r
-                       // find deeper root\r
-                       ChildReference path = ChildReference.parsePath(ref);\r
-                       while ( path!=null ) {\r
-                               if ( path instanceof LabelReference == false ) {\r
-                                       throw new RuntimeException( "blaah" );                                          \r
-                               }\r
-                               LabelReference lr = (LabelReference) path;\r
-                               String label = lr.label;\r
-                               \r
-                               if ( root.hasComponent(label) ) {\r
-                                       root = (RecordType) root.getComponent(label).type;\r
-                               } else {\r
-                                       RecordType rt = new RecordType();\r
-                                       root.addComponent(label, rt);\r
-                                       root = rt;\r
-                               }\r
-                               path = path.childReference;\r
-                       }\r
-               }\r
-               if ( !root.hasComponent(name) ) root.addComponent(name, fieldType);             \r
-       }\r
-       \r
-       \r
-       public void addFields( Composite parent, RecordType source ) \r
-       {\r
-               for (Component component : source.getComponents()) \r
-                       addField( parent, component.name, component.type );\r
-       }\r
-       \r
-       public void addFields( Composite parent, RecordType source, String ref ) \r
-       {\r
-               for (Component component : source.getComponents()) \r
-                       addField( parent, component.name, component.type, ref );\r
-       }\r
-       \r
-       \r
-       public void addField( Composite parent, Datatype fieldType, String ref, GridData lblLayout )\r
-       {\r
-               if (lblLayout==null) {\r
-                       lblLayout = new GridData(SWT.LEFT, SWT.BEGINNING, false, false, 1, 1);\r
-                       if ( getDepth(ref) < 3 ) lblLayout.widthHint = column1Width;\r
-               }               \r
-               String fieldName = getName(ref);\r
-               \r
-               if (fieldType instanceof StringType) {\r
-                       Label label = new Label(parent, 0);\r
-                       label.setText(fieldName + ":");\r
-                       label.setToolTipText(fieldName);\r
-                       label.setLayoutData( lblLayout );\r
-\r
-                       addString(parent, (StringType) fieldType, ref);\r
-                       \r
-               } else if (fieldType instanceof BooleanType) {\r
-\r
-                       Label label = new Label(parent, 0);\r
-                       label.setLayoutData( lblLayout );\r
-                       \r
-                       addBoolean(parent, (BooleanType) fieldType, ref);\r
-\r
-               } else if (fieldType instanceof NumberType) {\r
-\r
-                       Label label = new Label(parent, 0);\r
-                       label.setText(fieldName + ":");\r
-                       label.setLayoutData( lblLayout );\r
-\r
-                       addNumber(parent, (NumberType) fieldType, ref);\r
-\r
-               } else if (fieldType instanceof RecordType) {\r
-                       RecordType record = (RecordType) fieldType;\r
-                       String options = record.metadata.get("style");\r
-                       boolean dialog = options==null?false:options.contains("dialog");\r
-                       boolean tabbed = options==null?false:options.contains("tabbed");\r
-                       \r
-                       // Method 0: Tabbed, each field is a tab page\r
-                       if ( tabbed ) {\r
-                               Label label = new Label(parent, 0);\r
-                               label.setText(fieldName + ":");\r
-                               label.setLayoutData( lblLayout );\r
-                               \r
-                               CTabFolder folder = new CTabFolder(parent, SWT.TOP | SWT.BORDER);\r
-                               //folder.setUnselectedCloseVisible(false);\r
-                               folder.setSimple(false);\r
-                               folder.setLayout( new GridLayout(3, false) );                   \r
-                               folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));\r
-                               \r
-                               for (int i=0; i<record.getComponentCount(); i++) {\r
-                                       String childName = record.getComponent(i).name;\r
-                                       Datatype childType = record.getComponentType(i);\r
-                                       String childRef = appendName(ref, childName);\r
-                                       if (i>0) new Label(parent, 0);\r
-                                       \r
-                                       CTabItem item = new CTabItem(folder, SWT.NONE);\r
-                                       item.setText(childName);\r
-                                       Composite composite = new Composite(folder, 0);\r
-                                       composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));\r
-                                       composite.setLayout( new GridLayout(3, false) );\r
-                                       composite.setData(ref);\r
-                                       \r
-                                       if ( childType instanceof RecordType ) {\r
-                                               addRecord( composite, (RecordType)childType, childRef);                                         \r
-                                       } else {\r
-                                               addWidget( composite, childType, childRef );\r
-                                       }\r
-                                       \r
-                                       item.setControl( composite );\r
-                               }       \r
-                               folder.setSelection(0);\r
-\r
-                       } else\r
-                               \r
-                       // Method 1: Dialog = Label + Text + [Button]\r
-                       if ( dialog ) {\r
-                               Label label = new Label(parent, 0);\r
-                               label.setText(fieldName + ":");\r
-                               label.setLayoutData( lblLayout );\r
-                               \r
-                               final String title = fieldName;\r
-                               final Shell shell = parent.getShell();\r
-                               final RecordBinding fieldBinding = Bindings.getMutableBinding( fieldType );\r
-                               final Object fieldValue = fieldBinding.createDefaultUnchecked();\r
-                               final Text text = new Text(parent, SWT.BORDER);\r
-                               final Button select = new Button(parent, SWT.PUSH);\r
-                               text.setLayoutData( new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1 ) );\r
-                               text.setText( print(fieldBinding, fieldValue) );\r
-                               text.setData(ref);                      \r
-                               text.addListener(SWT.Verify, new Listener() {\r
-                                       public void handleEvent(Event e) {\r
-                                               try {\r
-                                                       String newText = applyEventToString( text.getText(), e );\r
-                                                       Object value = parse(fieldBinding, newText);\r
-                                                       fieldBinding.assertInstaceIsValid( value );\r
-                                                       text.setBackground(null);\r
-                                               } catch (BindingException er) {\r
-                                                       Color error = new Color(text.getDisplay(), 255, 222, 222); \r
-                                                       text.setBackground(error);                                      \r
-                                                       error.dispose();\r
-                                               }\r
-                                       }\r
-                               });             \r
-                               \r
-                               //text.setEditable( false );\r
-                               select.setText("Select");\r
-                               select.setLayoutData(new GridData(SWT.RIGHT, SWT.BEGINNING, false, false));\r
-                               select.addSelectionListener(new SelectionAdapter() {\r
-                                       public void widgetSelected(SelectionEvent e) {\r
-                                               Object initialValue;\r
-                                               try {\r
-                                                       initialValue = parse(fieldBinding, text.getText());\r
-                                               } catch (BindingException e1) {\r
-                                                       initialValue = fieldBinding.createDefaultUnchecked();\r
-                                               }\r
-                                               DataboardDialog dialog = new DataboardDialog(\r
-                                                               shell,\r
-                                                               title, \r
-                                                               fieldBinding, \r
-                                                               initialValue);\r
-                                                                                                       \r
-                                               int code = dialog.open();\r
-                                               if ( code == Window.OK ) {\r
-                                                       Object result = dialog.getResult();\r
-                                                       String str = print(fieldBinding, result);\r
-                                                       text.setText( str );\r
-                                               }\r
-                                       }\r
-                               });\r
-                               \r
-                       } else \r
-\r
-                       // Method 2: Label + composite\r
-                       if ( allBooleans(record) ) {                    \r
-                               Label label = new Label(parent, 0);\r
-                               label.setText(fieldName + ":");\r
-                               label.setLayoutData( lblLayout );\r
-                               \r
-                               for (int i=0; i<record.getComponentCount(); i++) {\r
-                                       String childName = record.getComponent(i).name;\r
-                                       Datatype childType = record.getComponentType(i);\r
-                                       String childRef = appendName(ref, childName);\r
-                                       if (i>0) new Label(parent, 0);\r
-                                       addWidget( parent, childType, childRef );\r
-                               }               \r
-                               \r
-                       } \r
-                       else\r
-                       \r
-                       // Method 3: Groups\r
-                       {\r
-                               Group group = new Group(parent, 0);\r
-                               group.setText(fieldName);\r
-                               group.setLayout( new GridLayout(3, false) );                    \r
-                               group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));\r
-                               group.setData(ref);\r
-                               addRecord(group, record, ref);\r
-                       }\r
-                       \r
-               } else if (fieldType instanceof UnionType) {\r
-                       Label label = new Label(parent, 0);\r
-                       label.setText(fieldName + ":");\r
-                       label.setLayoutData( lblLayout );\r
-                       addUnion( parent, (UnionType) fieldType, ref );\r
-               }\r
-               \r
-       }\r
-\r
-       protected void addWidget(Composite parent, Datatype fieldType, String ref) {\r
-               if (fieldType instanceof StringType) {\r
-                       addString(parent, (StringType) fieldType, ref);\r
-               } else if (fieldType instanceof BooleanType) {\r
-                       addBoolean(parent, (BooleanType) fieldType, ref);\r
-               } else if (fieldType instanceof NumberType) {\r
-                       addNumber(parent, (NumberType) fieldType, ref);\r
-               } else if (fieldType instanceof RecordType) {\r
-                       RecordType rt = (RecordType) fieldType;\r
-                       Group group = new Group(parent, 0);\r
-                       String name = getName(ref);\r
-                       group.setText(name);\r
-                       group.setLayout( new GridLayout(3, false) );                    \r
-                       group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));\r
-                       group.setData(ref);\r
-                       addRecord(group, rt, ref);\r
-               } else if (fieldType instanceof UnionType) {\r
-                       addUnion( parent, (UnionType) fieldType, ref );\r
-               }\r
-       }\r
-       \r
-       public CTabFolder addTabFolder( Composite parent, RecordType record, String ref )\r
-       {\r
-               CTabFolder folder = new CTabFolder(parent, SWT.TOP | SWT.BORDER);\r
-               folder.setSimple(false);\r
-               folder.setLayout( new GridLayout(3, false) );                   \r
-               folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));\r
-               \r
-               for (int i=0; i<record.getComponentCount(); i++) {\r
-                       String childName = record.getComponent(i).name;\r
-                       Datatype childType = record.getComponentType(i);\r
-                       String childRef = appendName(ref, childName);\r
-                       if (i>0) new Label(parent, 0);\r
-                       \r
-                       CTabItem item = new CTabItem(folder, SWT.NONE);\r
-                       item.setText(childName);\r
-                       Composite composite = new Composite(folder, 0);\r
-                       composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));\r
-                       composite.setLayout( new GridLayout(3, false) );\r
-                       composite.setData(ref);\r
-                       \r
-                       if ( childType instanceof RecordType ) {\r
-                               addRecord( composite, (RecordType)childType, childRef);                                         \r
-                       } else {\r
-                               addWidget( composite, childType, childRef );\r
-                       }\r
-                       \r
-                       item.setControl( composite );\r
-                       allfields.addComponent(childName, childType);\r
-               }       \r
-               folder.setSelection(0);\r
-               return folder;\r
-               \r
-       }\r
-       \r
-       protected void addRecord( Composite parent, RecordType record, String ref )\r
-       {\r
-               String options = record.metadata.get("style");\r
-               boolean dialog = options==null?false:options.contains("dialog");\r
-               boolean tabbed = options==null?false:options.contains("tabbed");\r
-               \r
-               // Method 0: Tabbed, each field is a tab page\r
-               if ( tabbed ) {\r
-                       addTabFolder( parent, record, ref );\r
-                       return;\r
-               }\r
-               \r
-               // Normal Record\r
-               GridData lblLayout = new GridData(SWT.LEFT, SWT.BEGINNING, false, false, 1, 1);\r
-               if ( getDepth(ref) < 3 ) lblLayout.widthHint = column1Width;\r
-                       \r
-               for (int i=0; i<record.getComponentCount(); i++) {\r
-                       String fieldName = record.getComponent(i).name;\r
-                       Datatype fieldType = record.getComponentType(i);\r
-                       String fieldRef = appendName(ref, fieldName);\r
-                       addField( parent, fieldType, fieldRef, lblLayout);\r
-               }               \r
-               return;\r
-       }\r
-               \r
-       protected void addUnion( Composite parent, UnionType union, String ref )\r
-       {\r
-               if ( union.isEnumeration() ) {\r
-                       addEnum( parent, union, ref );\r
-               } else {\r
-                       addRadio( parent, union, ref );\r
-               }               \r
-       }\r
-       \r
-       protected void addEnum( Composite parent, UnionType union, String ref )\r
-       {\r
-               Combo combo = new Combo(parent, SWT.READ_ONLY);\r
-               String[] items = new String[union.getComponentCount()];\r
-               for (int i = 0; i < items.length; i++) {\r
-                       items[i] = union.getComponent(i).name;\r
-               }\r
-               combo.setItems(items);\r
-               combo.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));\r
-               combo.setData(ref);\r
-               \r
-               // Set default value\r
-               UnionBinding binding = Bindings.getBinding(union);\r
-               try {\r
-                       TaggedObject defaultOption = (TaggedObject) binding.createDefault();\r
-                       combo.setText( items[ defaultOption.tag ] );\r
-               } catch (BindingException e) {\r
-               }                               \r
-       }\r
-\r
-       protected void addRadio( Composite parent, UnionType union, String ref )\r
-       {\r
-               Composite composite = new Composite(parent, 0);\r
-               composite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));\r
-               composite.setLayout( new GridLayout(3, false) );\r
-               composite.setData(ref);\r
-               final int count = union.getComponentCount();\r
-               final Composite panels[] = new Composite[count];\r
-               final Button buttons[] = new Button[count];\r
-               for ( int i=0; i<count; i++ ) {\r
-                       Component component = union.getComponent(i);\r
-                       String childRef = ref+"/"+URIUtil.encodeURI(component.name);\r
-                       Button b = buttons[i] = new Button(composite, SWT.RADIO);\r
-                       b.setLayoutData(new GridData(SWT.LEFT, SWT.BEGINNING, false, false, 1, 1));\r
-                       b.setText(component.name);\r
-                       b.setData(childRef);\r
-                       \r
-                       String options = union.metadata.get("style");\r
-                       boolean border = options==null?true:!options.contains("no-border");\r
-                       int style = 0;                  \r
-                       if (border) style |= SWT.BORDER;\r
-                       \r
-                       Composite panel = panels[i] = new Composite(composite, style);\r
-                       panel.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));\r
-                       panel.setLayout( new GridLayout(3, false) );\r
-                       panel.setData(childRef);\r
-                       \r
-                       Datatype componentType = union.getComponentType(i);\r
-                       if ( componentType instanceof RecordType ) {\r
-                               addRecord(panel, (RecordType) componentType, childRef);\r
-                       } else {\r
-                               addWidget(panel, componentType, childRef);\r
-                       }                       \r
-               }\r
-               \r
-               // Set default value\r
-               UnionBinding binding = Bindings.getBinding(union);\r
-               try {\r
-                       TaggedObject defaultOption = (TaggedObject) binding.createDefault();\r
-                       buttons[defaultOption.tag].setSelection(true);\r
-               } catch (BindingException e) {\r
-               }                               \r
-               \r
-       }\r
-       \r
-       protected void addNumber( Composite parent, NumberType number, String ref )\r
-       {\r
-               String unit = number.getUnit();\r
-               String options = number.metadata.get("style");\r
-               boolean border = options==null?true:!options.contains("no-border");\r
-               int style = 0;                  \r
-               if (border) style |= SWT.BORDER;\r
-               final Text text = new Text(parent, style);\r
-               text.setData(ref);\r
-               text.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, unit==null?2:1, 1));\r
-               \r
-               // Set default value\r
-               final NumberBinding binding = Bindings.getBinding( number );\r
-               try {\r
-                       text.setText( binding.createDefault().toString() );\r
-               } catch (BindingException e) {\r
-               }\r
-               \r
-               // Add validator\r
-               text.addListener(SWT.Verify, new Listener() {\r
-                       public void handleEvent(Event e) {\r
-                               try {\r
-                                       String newText = applyEventToString( text.getText(), e );\r
-                                       Object value = binding.create( newText );\r
-                                       binding.assertInstaceIsValid( value );\r
-                                       text.setBackground(null);                                       \r
-                               } catch (BindingException er) {\r
-                                       Color error = new Color(text.getDisplay(), 255, 222, 222); \r
-                                       text.setBackground(error);\r
-                                       error.dispose();                                        \r
-                               }\r
-                       }});\r
-               \r
-               if ( unit!=null ) {\r
-                       Label unitLabel = new Label(parent, 0);\r
-                       unitLabel.setText(unit);\r
-               }\r
-       }\r
-       \r
-       static String applyEventToString(String orig, Event e) {\r
-               if (e.character==8) {\r
-                       return orig.substring(0, e.start) + orig.substring(e.end, orig.length());\r
-               }\r
-               return orig.substring(0, e.start) + e.text + orig.substring(e.end, orig.length());              \r
-       }\r
-       \r
-       protected void addBoolean( Composite parent, BooleanType booleanType, String ref )\r
-       {\r
-               Button button = new Button(parent, SWT.CHECK);\r
-               button.setText( getName(ref) );\r
-               button.setData(ref);\r
-               button.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));\r
-       }\r
-       \r
-       protected void addString( Composite parent, StringType stringType, String ref )\r
-       {               \r
-               String options = stringType.metadata.get("style");\r
-               boolean password = options==null?false:options.contains("password");\r
-               boolean filesave = options==null?false:options.contains("filesave");\r
-               boolean fileopen = options==null?false:options.contains("fileopen");\r
-               boolean directory = options==null?false:options.contains("directory");\r
-               boolean multi = options==null?false:options.contains("multi");\r
-               boolean border = options==null?true:!options.contains("no-border");\r
-\r
-               int style = 0;\r
-               if (password) style |= SWT.PASSWORD;\r
-               if (multi) style |= SWT.MULTI;\r
-               if (border) style |= SWT.BORDER;\r
-               final Text text = new Text(parent, style);\r
-               text.setLayoutData( new GridData(SWT.FILL, multi?SWT.FILL:SWT.BEGINNING, true, false, filesave|fileopen|directory?1:2, 1 ) );\r
-               text.setData(ref);\r
-\r
-               // Set default value\r
-               final StringBinding binding = Bindings.getBinding( stringType );\r
-               try {\r
-                       text.setText( binding.createDefault().toString() );\r
-               } catch (BindingException e) {\r
-               }\r
-               \r
-               text.addListener(SWT.Verify, new Listener() {\r
-                       public void handleEvent(Event e) {\r
-                               try {\r
-                                       String newText = applyEventToString( text.getText(), e );\r
-                                       Object value = binding.create( newText );\r
-                                       binding.assertInstaceIsValid( value );\r
-                                       text.setBackground(null);\r
-                               } catch (BindingException er) {\r
-                                       Color error = new Color(text.getDisplay(), 255, 222, 222); \r
-                                       text.setBackground(error);                                      \r
-                                       error.dispose();\r
-                               }\r
-                       }\r
-               });             \r
-               if (filesave|fileopen) {\r
-                       final FileDialog fd = new FileDialog(parent.getShell(), filesave?SWT.SAVE:SWT.OPEN);\r
-                                       \r
-                       String filetypesStr = stringType.metadata.get("filetypes");\r
-                       String fileextsStr = stringType.metadata.get("fileexts");\r
-                       String name = getName(ref);\r
-                       String[] filetypes = filetypesStr==null?null:(String[]) filetypesStr.split(","); \r
-                       String[] fileexts = fileextsStr==null?null:(String[]) fileextsStr.split(",");                                   \r
-                       fd.setFilterNames(filetypes);\r
-                       fd.setFilterExtensions(fileexts);\r
-                       \r
-                       boolean nameMatchesExt = false;\r
-                       if ( name!=null && !name.isEmpty() ) {\r
-                               for (String fileext : fileexts) {\r
-                                       nameMatchesExt |= StringUtil.simplePatternMatch(name, fileext) && !name.contains(" ");\r
-                               }\r
-                       }\r
-                       final String initialName = nameMatchesExt?name:null;\r
-                       \r
-                       Button chooseFileButton = new Button(parent, SWT.PUSH);\r
-                       chooseFileButton.setText("Select");\r
-                       chooseFileButton.setLayoutData(new GridData(SWT.RIGHT, SWT.BEGINNING, false, false));\r
-                       chooseFileButton.addSelectionListener(new SelectionAdapter() {\r
-                               public void widgetSelected(SelectionEvent e) {\r
-                                       String name = text.getText();\r
-                                       if ( name == null || name.isEmpty() ) name = initialName;\r
-                                       if ( name!=null ) fd.setFileName( name );\r
-                                       String result = fd.open();\r
-                                       if (result != null)\r
-                                               text.setText(result);\r
-                               }\r
-                       });\r
-               }\r
-\r
-               if (directory) {\r
-                       final DirectoryDialog fd = new DirectoryDialog(parent.getShell(), 0);\r
-                       String name = getName(ref);\r
-                       fd.setMessage( name );\r
-                       Button chooseDirButton = new Button(parent, SWT.PUSH);\r
-                       chooseDirButton.setText("Select");\r
-                       chooseDirButton.setLayoutData(new GridData(SWT.RIGHT, SWT.BEGINNING, false, false));\r
-                       chooseDirButton.addSelectionListener(new SelectionAdapter() {\r
-                               public void widgetSelected(SelectionEvent e) {\r
-                                       String dir = text.getText();                                    \r
-                                       fd.setFilterPath(dir);\r
-                                       String result = fd.open();\r
-                                       if (result != null)\r
-                                               text.setText(result);\r
-                               }\r
-                       });\r
-               }\r
-       }\r
-\r
-       static String appendName(String ref, String name)\r
-       {\r
-               return ref+"/"+URIUtil.encodeURI(name);\r
-       }\r
-       \r
-       static String getName(String ref)\r
-       {\r
-               if ( ref==null ) return null;\r
-               int i = ref.lastIndexOf('/');\r
-               if (i<0) return URIUtil.decodeURI(ref);\r
-               String namePart = ref.substring(i+1);\r
-               return URIUtil.decodeURI(namePart);\r
-       }\r
-       \r
-       static int getDepth(String ref)\r
-       {\r
-               int depth = 0;\r
-               for (int i=0; i<ref.length(); i++) if ( ref.charAt(i)=='/' ) depth++;\r
-               return depth;\r
-       }\r
-       \r
-       static boolean allBooleans(RecordType rt) {\r
-               for (Component c : rt.getComponents())\r
-                       if ( c.type instanceof BooleanType == false ) return false;\r
-               return true;\r
-       }\r
-       \r
-       static String print(RecordBinding recordBinding, Object value) {                \r
-               if ( allBooleans(recordBinding.type() ) ) {\r
-                       StringBuilder sb = new StringBuilder();\r
-                       int j = 0;\r
-                       for ( int i = 0; i<recordBinding.getComponentCount(); i++ ) {\r
-                               try {\r
-                                       boolean b = recordBinding.getBoolean(value, i);\r
-                                       if ( !b ) continue;\r
-                                       if ( j>0 ) sb.append(", ");\r
-                                       sb.append(recordBinding.type().getComponent(i).name);\r
-                                       j++;\r
-                               } catch (BindingException e) {\r
-                                       continue;\r
-                               }                               \r
-                       }\r
-                       return sb.toString();\r
-               } else \r
-               try {\r
-                       return recordBinding.toString(value, true);\r
-               } catch (BindingException e) {\r
-                       return e.toString();\r
-               }\r
-       }\r
-       \r
-       static Object parse(RecordBinding recordBinding, String txt) throws BindingException {\r
-               if ( allBooleans(recordBinding.type() ) ) {\r
-                       Object result = recordBinding.createDefaultUnchecked();\r
-                       String[] tokens = txt.split(", ");\r
-                       for ( String token : tokens ) {\r
-                               if ( token.isEmpty() ) continue;\r
-                               int i = recordBinding.type().getComponentIndex2(token);\r
-                               if ( i>=0 ) {\r
-                                       try {\r
-                                               recordBinding.setBoolean(result, i, true);\r
-                                       } catch (BindingException e) {\r
-                                               throw e;\r
-                                       }\r
-                               } else {\r
-                                       throw new BindingException("There is no field \""+token+"\"");\r
-                               }\r
-                       }\r
-                       return result;\r
-               } else {\r
-                       try {\r
-                               return recordBinding.parseValueDefinition(txt);\r
-                       } catch (DataTypeSyntaxError e) {\r
-                               throw new BindingException(e);\r
-                       }\r
-                       \r
-               }\r
-       }\r
-       \r
-       static {\r
-           PASSWORD = new StringType();\r
-           PASSWORD.metadata.put("style", "password");\r
-           \r
-           TEXTBOX = new StringType();\r
-           TEXTBOX.metadata.put("style", "multi");\r
-       }\r
-               \r
-}\r
+package org.simantics.databoard.forms;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.simantics.databoard.Accessors;
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.accessor.Accessor;
+import org.simantics.databoard.accessor.BooleanAccessor;
+import org.simantics.databoard.accessor.RecordAccessor;
+import org.simantics.databoard.accessor.StringAccessor;
+import org.simantics.databoard.accessor.UnionAccessor;
+import org.simantics.databoard.accessor.error.AccessorConstructionException;
+import org.simantics.databoard.accessor.error.AccessorException;
+import org.simantics.databoard.accessor.error.ReferenceException;
+import org.simantics.databoard.accessor.reference.ChildReference;
+import org.simantics.databoard.accessor.reference.LabelReference;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.NumberBinding;
+import org.simantics.databoard.binding.RecordBinding;
+import org.simantics.databoard.binding.StringBinding;
+import org.simantics.databoard.binding.UnionBinding;
+import org.simantics.databoard.binding.error.BindingException;
+import org.simantics.databoard.binding.mutable.TaggedObject;
+import org.simantics.databoard.parser.repository.DataTypeSyntaxError;
+import org.simantics.databoard.type.BooleanType;
+import org.simantics.databoard.type.Component;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.type.NumberType;
+import org.simantics.databoard.type.RecordType;
+import org.simantics.databoard.type.StringType;
+import org.simantics.databoard.type.UnionType;
+import org.simantics.databoard.util.Bean;
+import org.simantics.databoard.util.StringUtil;
+import org.simantics.databoard.util.URIUtil;
+
+/**
+ * Databoard form creates SWT user interface from databoard record type.
+ * 
+ * See DataboardFormExample for example usage.
+ *
+ * @author toni.kalajainen@semantum.fi
+ */
+public class DataboardForm {   
+
+       public static final Datatype TEXTBOX;
+       public static final Datatype PASSWORD;
+       
+       public int column1Width = -1;
+       
+       private RecordType allfields = new RecordType();
+       
+       /**
+        * Make file type
+        * 
+        * @param namesAndExts array of names and extensions
+        * @param exts
+        * @return
+        */
+       public static StringType fileSaveDialog(String...namesAndExts)
+       {               
+           StringType result = new StringType();
+           result.metadata.put("style", "filesave");
+                   
+           StringBuilder sb1 = new StringBuilder();
+           StringBuilder sb2 = new StringBuilder();
+           
+           for (int i=0; i<namesAndExts.length; i++) {
+               StringBuilder sb = (i%2==0?sb1:sb2);
+               int j = i/2;
+               if (j>0) sb.append(','); 
+               sb.append(namesAndExts[i]);
+           }
+           
+               result.metadata.put("filetypes", sb1.toString());
+           result.metadata.put("fileexts", sb2.toString());
+           return result;
+       }
+
+       /**
+        * Make file type
+        * 
+        * @param namesAndExts array of names and extensions
+        * @param exts
+        * @return
+        */
+       public static StringType fileOpenDialog(String...namesAndExts)
+       {               
+           StringType result = new StringType();
+           result.metadata.put("style", "fileopen");
+                   
+           StringBuilder sb1 = new StringBuilder();
+           StringBuilder sb2 = new StringBuilder();
+           
+           for (int i=0; i<namesAndExts.length; i++) {
+               StringBuilder sb = (i%2==0?sb1:sb2);
+               int j = i/2;
+               if (j>0) sb.append(','); 
+               sb.append(namesAndExts[i]);
+           }
+           
+               result.metadata.put("filetypes", sb1.toString());
+           result.metadata.put("fileexts", sb2.toString());
+           return result;
+       }
+       
+       public static StringType directoryDialog()
+       {               
+           StringType result = new StringType();
+           result.metadata.put("style", "directory");
+           return result;
+       }
+       
+       public DataboardForm() {
+       }
+       
+       public void setFirstColumnWidth(int width) {
+               column1Width = width;
+       }
+       
+       
+       /**
+        * Validate the fields for valid values.
+        * StringTypes can be restricted with regular expressions.
+        * NumberTypes with value ranges.
+        * 
+        * @param composite
+        * @return list of problems
+        */
+       public List<Problem> validate( Composite composite ) {
+               List<Problem> result = new ArrayList<Problem>();
+               _validateFields(composite, result);
+               return result;
+       }
+       
+       public static List<String> toStrings(List<Problem> problems) {
+               List<String> result = new ArrayList<String>( problems.size() );
+               for (Problem p : problems) result.add(p.error);
+               return result;
+       }
+       
+       public static class Problem extends Bean {
+               /** Reference to the field with problem */
+               public String fieldReference;
+               /** The field with problem */
+               public transient Control control;
+               /** The validation error */
+               public String error;
+       }
+
+       protected void _validateFields( Control control, List<Problem> result )
+       {
+               // Read this control
+               Object data = control.getData();
+               if ( data != null && data instanceof String ) {
+                       String refStr = (String) data;
+                       ChildReference ref = ChildReference.parsePath( refStr);
+                       if ( ref != null ) {
+                               try {
+                                       Datatype fieldType = allfields.getChildType( ref );
+                                       
+                                       // Read Combo Union
+                                       if ( fieldType instanceof UnionType && control instanceof Combo ) {
+                                               // Nothing to validate
+                                       } else
+
+                                       // Read Radio Union
+                                       if ( fieldType instanceof UnionType && control instanceof Composite ) {
+                                               // Nothing to validate
+                                       } else
+                                       
+                                       // Read Boolean
+                                       if ( fieldType instanceof BooleanType && control instanceof Button ) {
+                                               // Nothing to validate
+                                       } else
+
+                                       // Text + Dialog button
+                                       if ( fieldType instanceof RecordType && control instanceof Text ) {
+                                               try {
+                                                       Text text = (Text) control;
+                                                       RecordBinding fieldBinding = Bindings.getMutableBinding(fieldType);
+                                                       Object value = parse(fieldBinding, text.getText());
+                                                       fieldBinding.assertInstaceIsValid( value );
+                                               } catch (BindingException er) {
+                                                       Problem problem = new Problem();
+                                                       if ( er.getCause()!=null ) {
+                                                               problem.error = er.getCause().getMessage();
+                                                       } else {
+                                                               problem.error = er.getMessage();
+                                                       }
+                                                       problem.fieldReference = refStr;
+                                                       problem.control = control;
+                                                       result.add( problem );
+                                               }                                               
+                                               
+                                       } else
+                                               
+                                       // Read Text
+                                       if ( fieldType instanceof StringAccessor && control instanceof Text ) {
+                                               try {
+                                                       Text text = (Text) control;
+                                                       StringBinding binding = Bindings.getBinding(fieldType);
+                                                       Object value = binding.create(text.getText());
+                                                       binding.assertInstaceIsValid( value );
+                                               } catch (BindingException er) {
+                                                       Problem problem = new Problem();
+                                                       if ( er.getCause()!=null ) {
+                                                               problem.error = er.getCause().getMessage();
+                                                       } else {
+                                                               problem.error = er.getMessage();
+                                                       }
+                                                       problem.fieldReference = refStr;
+                                                       problem.control = control;
+                                                       result.add( problem );
+                                               }                                               
+                                       } else
+                                       
+                                       // Read Numbers
+                                       if ( fieldType instanceof NumberType && control instanceof Text ) {
+                                               try {
+                                                       Text text = (Text) control;
+                                                       NumberBinding binding = Bindings.getBinding(fieldType);
+                                                       Object value = binding.create(text.getText());
+                                                       binding.assertInstaceIsValid( value );
+                                               } catch (BindingException er) {
+                                                       Problem problem = new Problem();
+                                                       if ( er.getCause()!=null && er.getCause() instanceof NumberFormatException) {
+                                                               NumberFormatException nfe = (NumberFormatException) er.getCause();
+                                                               problem.error = nfe.getMessage();
+                                                       } else {
+                                                               problem.error = er.getMessage();
+                                                       }
+                                                       problem.fieldReference = refStr;
+                                                       problem.control = control;
+                                                       result.add( problem );
+                                               }                                               
+                                       }
+                                       
+                               } catch (AccessorConstructionException e) {
+                                       Problem problem = new Problem();
+                                       problem.error = e.getMessage();
+                                       problem.fieldReference = refStr;
+                                       problem.control = control;
+                                       result.add( problem );
+                               }
+                       }
+               }
+               
+               // Recursion
+               if ( control instanceof Composite ) {
+                       Composite composite = (Composite) control;
+                       for (Control child : composite.getChildren()) 
+                       {
+                               _validateFields(child, result);
+                       }
+               }
+               
+       }
+       
+       public RecordType type() {
+               return allfields;
+       }
+
+       /**
+        * Find control by reference 
+        * @param control
+        * @param ref
+        * @return control or null
+        */
+       public Control getControl( Control control, ChildReference ref )
+       {
+               return getControl( control, ref.toPath() );
+       }
+       
+       /**
+        * Find control by reference. 
+        * 
+        * @param composite
+        * @param ref
+        * @return control or null
+        */
+       public Control getControl( Control control, String ref )
+       {
+               Object data = control.getData();
+               if ( data != null && data instanceof String && data.equals(ref)) return control;
+                       
+               // Recursion
+               if ( control instanceof Composite ) {
+                       Composite composite = (Composite) control;
+                       for (Control child : composite.getChildren()) 
+                       {
+                               Control result = getControl(child, ref);
+                               if ( result != null ) return result;
+                       }
+               }
+               return null;
+       }
+       
+       /**
+        * Reads values of fields into a record
+        * 
+        * @param composite the composite that holds the widgets
+        * @param binding record binding
+        * @param dst the record where values are read to
+        * @throws BindingException
+        * @throws AccessorConstructionException 
+        * @throws AccessorException 
+        */
+       public void readFields( Composite composite, RecordBinding binding, Object dst)
+       throws BindingException, AccessorConstructionException, AccessorException
+       {
+               RecordAccessor ra = Accessors.getAccessor(binding, dst);
+               _readFields(composite, ra);
+       }
+       
+       protected void _readFields( Control control, RecordAccessor ra ) 
+       throws AccessorException, BindingException
+       {
+               // Read this control
+               Object data = control.getData();
+               if ( data != null && data instanceof String ) {
+                       ChildReference ref = ChildReference.parsePath( (String) data);
+                       if ( ref != null ) {
+                               try {
+                                       Accessor fieldAccessor = ra.getComponent( ref );
+                                       
+                                       // Read Combo Union
+                                       if ( fieldAccessor instanceof UnionAccessor && control instanceof Combo ) {
+                                               UnionAccessor sa = (UnionAccessor) fieldAccessor;
+                                               Combo combo = (Combo) control;
+                                               String text = combo.getText();
+                                               int tag = sa.type().getComponentIndex2(text);
+                                               Datatype tagType = sa.type().components[tag].type;
+                                               Binding tagBinding = Bindings.getBinding(tagType);
+                                               Object defaultValue = tagBinding.createDefault();                                                                       
+                                               sa.setComponentValue(tag, tagBinding, defaultValue);
+                                       } else
+
+                                       // Read Radio Union
+                                       if ( fieldAccessor instanceof UnionAccessor && control instanceof Composite ) {
+                                               Composite radioComposite = (Composite) control;
+                                               for (Control c : radioComposite.getChildren()) {
+                                                       if ( c instanceof Button && ((Button)c).getSelection() ) {
+                                                               Object data2 = c.getData();
+                                                               if (data2==null) continue;
+                                                               String name = getName( data2.toString() );
+                                                               if ( name==null ) continue;
+                                                               
+                                                               UnionAccessor sa = (UnionAccessor) fieldAccessor;
+                                                               int tag = sa.type().getComponentIndex2( name );
+                                                               if ( tag>=0 ) {
+                                                                       Datatype tagType = sa.type().components[tag].type;
+                                                                       Binding tagBinding = Bindings.getBinding(tagType);
+                                                                       Object defaultValue = tagBinding.createDefault();                                                                       
+                                                                       sa.setComponentValue(tag, tagBinding, defaultValue);
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                       } else
+                                       
+                                       // Read Boolean
+                                       if ( fieldAccessor instanceof BooleanAccessor && control instanceof Button ) {
+                                               BooleanAccessor sa = (BooleanAccessor) fieldAccessor;
+                                               sa.setValue(((Button)control).getSelection());
+                                       } else
+
+                                       // Read Text + Dialog Button
+                                       if ( fieldAccessor instanceof RecordAccessor && control instanceof Text ) {
+                                               RecordAccessor raa = (RecordAccessor) fieldAccessor;
+                                               Text text = (Text) control;
+                                               RecordBinding fieldBinding = Bindings.getMutableBinding( raa.type() );
+                                               Object value = parse( fieldBinding, text.getText() );
+                                               raa.setValue(fieldBinding, value);
+                                       } else
+                                               
+                                       // Read Text
+                                       if ( fieldAccessor instanceof StringAccessor && control instanceof Text ) {
+                                               StringAccessor sa = (StringAccessor) fieldAccessor;
+                                               sa.setValue(((Text)control).getText());
+                                       } else
+                                       
+                                       // Read Numbers
+                                       if ( fieldAccessor.type() instanceof NumberType && control instanceof Text ) {
+                                               NumberBinding binding = Bindings.getBinding( fieldAccessor.type() );
+                                               Object value = binding.create( ((Text)control).getText() );
+                                               fieldAccessor.setValue(binding, value);
+                                       }
+                                       
+                               } catch (AccessorConstructionException e) {
+                                       //e.printStackTrace();
+                               }
+                       }
+               }
+               
+               // Recursion
+               if ( control instanceof Composite ) {
+                       Composite composite = (Composite) control;
+                       for (Control child : composite.getChildren()) 
+                       {
+                               _readFields(child, ra);
+                       }
+               }
+       }
+
+       public void writeFields( Composite composite, RecordBinding binding, Object src)
+                       throws AccessorException, BindingException, AccessorConstructionException
+       {
+               RecordAccessor ra = Accessors.getAccessor(binding, src);
+               _writeFields(composite, ra);
+       }
+       
+       void _writeFields( Control control, RecordAccessor ra )
+                       throws AccessorException, BindingException, AccessorConstructionException
+       {
+               // Read this control
+               Object data = control.getData();
+               if ( data != null && data instanceof String ) {
+                       ChildReference ref = ChildReference.parsePath( (String) data);
+                       if ( ref != null ) {
+                               try {
+                                       Accessor fieldAccessor = ra.getComponent( ref );
+                                       
+                                       // Read Combo Union
+                                       if ( fieldAccessor instanceof UnionAccessor && control instanceof Combo ) {
+                                               UnionAccessor sa = (UnionAccessor) fieldAccessor;
+                                               Combo combo = (Combo) control;
+                                               int tag = sa.getTag();                                          
+                                               combo.setText( sa.type().getComponent(tag).name );
+                                       } else
+
+                                       // Read Radio Union
+                                       if ( fieldAccessor instanceof UnionAccessor && control instanceof Composite) {
+                                               Composite radioComposite = (Composite) control;
+                                               UnionAccessor sa = (UnionAccessor) fieldAccessor;
+                                               int tag = sa.getTag();
+                                               for (int i=0; i<sa.count(); i++) {
+                                                       Button button = (Button) radioComposite.getChildren()[i*2];
+                                                       button.setSelection(i==tag);
+                                               }
+                                       } else
+                                       
+                                       // Read Boolean
+                                       if ( fieldAccessor instanceof BooleanAccessor && control instanceof Button ) {
+                                               BooleanAccessor sa = (BooleanAccessor) fieldAccessor;
+                                               ((Button)control).setSelection(sa.getValue());
+                                       } else
+
+                                       // Write Text + Dialog Selection
+                                       if ( fieldAccessor instanceof RecordAccessor && control instanceof Text ) {
+                                               RecordAccessor raa = (RecordAccessor) fieldAccessor;
+                                               Text text = (Text) control;
+                                               RecordBinding fieldBinding = Bindings.getMutableBinding( raa.type() );
+                                               String str = print( fieldBinding, raa.getValue(fieldBinding) );                                 
+                                               text.setText( str );
+                                       } else
+                                               
+                                       // Read Text
+                                       if ( fieldAccessor instanceof StringAccessor && control instanceof Text ) {
+                                               StringAccessor sa = (StringAccessor) fieldAccessor;
+                                               ((Text)control).setText( sa.getValue() );
+                                       } else
+                                       
+                                       // Read Numbers
+                                       if ( fieldAccessor.type() instanceof NumberType && control instanceof Text ) {
+                                               NumberBinding binding = Bindings.getBinding( fieldAccessor.type() );
+                                               Object value = fieldAccessor.getValue(binding);
+                                               ((Text)control).setText( binding.toString(value, true) );
+                                       }
+                                       
+                               } catch (AccessorConstructionException e) {
+                                       //e.printStackTrace();
+                               }
+                       }
+               }
+               
+               // Recursion
+               if ( control instanceof Composite ) {
+                       Composite composite = (Composite) control;
+                       for (Control child : composite.getChildren()) 
+                       {
+                               _writeFields(child, ra);
+                       }
+               }
+               
+       }
+       
+       public void clear( Control control ) 
+       {               
+               _clearFields( control );
+               allfields.clear();
+       }
+
+       protected void _clearFields( Control control ) 
+       {               
+               // Recursion
+               if ( control instanceof Composite ) {
+                       Composite composite = (Composite) control;
+                       for (Control child : composite.getChildren()) 
+                       {
+                               _clearFields(child);
+                               child.dispose();
+                       }
+               }
+
+       }
+       
+       /**
+        * Add a listener to all the controls that represent the data type.
+        * 
+        * @param composite the composite that holds the widgets
+        * @param binding record binding
+        * @param dst the record where values are read to
+        * @throws BindingException
+        * @throws AccessorConstructionException 
+        * @throws AccessorException 
+        */
+       public void addListener( Composite composite, RecordType type, Listener listener )
+       throws BindingException, AccessorConstructionException, AccessorException
+       {
+               _addListener(composite, type, listener );
+       }
+       
+       protected void _addListener( Control control, Datatype type, Listener listener ) 
+       throws AccessorException, BindingException
+       {
+               // Read this control
+               Object data = control.getData();
+               ChildReference ref = null;
+               if ( data != null && data instanceof String ) {
+                       ref = ChildReference.parsePath( (String) data);
+               }
+                       
+                               try {                                   
+                                       Datatype fieldType = ref==null ? type : type.getChildType( ref );
+
+                                       // Read Combo Union
+                                       if ( fieldType instanceof UnionType && control instanceof Combo ) {
+                                               control.addListener(SWT.Selection, listener);
+                                       } else
+                                       // Read Radio Union
+                                       if ( fieldType instanceof UnionType && control instanceof Composite ) {
+                                               Composite radioComposite = (Composite) control;
+                                               for (Control c : radioComposite.getChildren()) {
+                                                       if ( c instanceof Button ) {
+                                                               c.addListener(SWT.Selection, listener);
+                                                       }
+                                               }
+                                       } else
+                                       
+                                       // Read Boolean
+                                       if ( fieldType instanceof BooleanType && control instanceof Button ) {
+                                               control.addListener(SWT.Selection, listener);
+                                       } else
+                                       
+                                       // Read Text
+                                       if ( fieldType instanceof StringType && control instanceof Text ) {
+                                               control.addListener(SWT.Modify, listener);
+                                       } else
+                                       
+                                       // Read Numbers
+                                       if ( fieldType instanceof NumberType && control instanceof Text ) {
+                                               control.addListener(SWT.Modify, listener);
+                                       }
+                               
+                               } catch (ReferenceException re) {
+                               }
+               
+               // Recursion
+               if ( control instanceof Composite ) {
+                       Composite composite = (Composite) control;
+                       for (Control child : composite.getChildren()) 
+                       {
+                               _addListener(child, type, listener);
+                       }
+               }
+       }
+       
+       public void addField( Composite parent, String name, Datatype fieldType )
+       {
+               addField(parent, fieldType, URIUtil.encodeURI(name), null);
+               allfields.addComponent(name, fieldType);
+       }
+       
+       public void addField( Composite parent, String name, Datatype fieldType, String ref )
+       {
+               addField(parent, fieldType, appendName(ref, name), null);
+               
+               RecordType root = allfields;
+               if ( ref != null && !ref.isEmpty() ) {
+                       // find deeper root
+                       ChildReference path = ChildReference.parsePath(ref);
+                       while ( path!=null ) {
+                               if ( path instanceof LabelReference == false ) {
+                                       throw new RuntimeException( "blaah" );                                          
+                               }
+                               LabelReference lr = (LabelReference) path;
+                               String label = lr.label;
+                               
+                               if ( root.hasComponent(label) ) {
+                                       root = (RecordType) root.getComponent(label).type;
+                               } else {
+                                       RecordType rt = new RecordType();
+                                       root.addComponent(label, rt);
+                                       root = rt;
+                               }
+                               path = path.childReference;
+                       }
+               }
+               if ( !root.hasComponent(name) ) root.addComponent(name, fieldType);             
+       }
+       
+       
+       public void addFields( Composite parent, RecordType source ) 
+       {
+               for (Component component : source.getComponents()) 
+                       addField( parent, component.name, component.type );
+       }
+       
+       public void addFields( Composite parent, RecordType source, String ref ) 
+       {
+               for (Component component : source.getComponents()) 
+                       addField( parent, component.name, component.type, ref );
+       }
+       
+       
+       public void addField( Composite parent, Datatype fieldType, String ref, GridData lblLayout )
+       {
+               if (lblLayout==null) {
+                       lblLayout = new GridData(SWT.LEFT, SWT.BEGINNING, false, false, 1, 1);
+                       if ( getDepth(ref) < 3 ) lblLayout.widthHint = column1Width;
+               }               
+               String fieldName = getName(ref);
+               
+               if (fieldType instanceof StringType) {
+                       Label label = new Label(parent, 0);
+                       label.setText(fieldName + ":");
+                       label.setToolTipText(fieldName);
+                       label.setLayoutData( lblLayout );
+
+                       addString(parent, (StringType) fieldType, ref);
+                       
+               } else if (fieldType instanceof BooleanType) {
+
+                       Label label = new Label(parent, 0);
+                       label.setLayoutData( lblLayout );
+                       
+                       addBoolean(parent, (BooleanType) fieldType, ref);
+
+               } else if (fieldType instanceof NumberType) {
+
+                       Label label = new Label(parent, 0);
+                       label.setText(fieldName + ":");
+                       label.setLayoutData( lblLayout );
+
+                       addNumber(parent, (NumberType) fieldType, ref);
+
+               } else if (fieldType instanceof RecordType) {
+                       RecordType record = (RecordType) fieldType;
+                       String options = record.metadata.get("style");
+                       boolean dialog = options==null?false:options.contains("dialog");
+                       boolean tabbed = options==null?false:options.contains("tabbed");
+                       
+                       // Method 0: Tabbed, each field is a tab page
+                       if ( tabbed ) {
+                               Label label = new Label(parent, 0);
+                               label.setText(fieldName + ":");
+                               label.setLayoutData( lblLayout );
+                               
+                               CTabFolder folder = new CTabFolder(parent, SWT.TOP | SWT.BORDER);
+                               //folder.setUnselectedCloseVisible(false);
+                               folder.setSimple(false);
+                               folder.setLayout( new GridLayout(3, false) );                   
+                               folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+                               
+                               for (int i=0; i<record.getComponentCount(); i++) {
+                                       String childName = record.getComponent(i).name;
+                                       Datatype childType = record.getComponentType(i);
+                                       String childRef = appendName(ref, childName);
+                                       if (i>0) new Label(parent, 0);
+                                       
+                                       CTabItem item = new CTabItem(folder, SWT.NONE);
+                                       item.setText(childName);
+                                       Composite composite = new Composite(folder, 0);
+                                       composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+                                       composite.setLayout( new GridLayout(3, false) );
+                                       composite.setData(ref);
+                                       
+                                       if ( childType instanceof RecordType ) {
+                                               addRecord( composite, (RecordType)childType, childRef);                                         
+                                       } else {
+                                               addWidget( composite, childType, childRef );
+                                       }
+                                       
+                                       item.setControl( composite );
+                               }       
+                               folder.setSelection(0);
+
+                       } else
+                               
+                       // Method 1: Dialog = Label + Text + [Button]
+                       if ( dialog ) {
+                               Label label = new Label(parent, 0);
+                               label.setText(fieldName + ":");
+                               label.setLayoutData( lblLayout );
+                               
+                               final String title = fieldName;
+                               final Shell shell = parent.getShell();
+                               final RecordBinding fieldBinding = Bindings.getMutableBinding( fieldType );
+                               final Object fieldValue = fieldBinding.createDefaultUnchecked();
+                               final Text text = new Text(parent, SWT.BORDER);
+                               final Button select = new Button(parent, SWT.PUSH);
+                               text.setLayoutData( new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1 ) );
+                               text.setText( print(fieldBinding, fieldValue) );
+                               text.setData(ref);                      
+                               text.addListener(SWT.Verify, new Listener() {
+                                       public void handleEvent(Event e) {
+                                               try {
+                                                       String newText = applyEventToString( text.getText(), e );
+                                                       Object value = parse(fieldBinding, newText);
+                                                       fieldBinding.assertInstaceIsValid( value );
+                                                       text.setBackground(null);
+                                               } catch (BindingException er) {
+                                                       Color error = new Color(text.getDisplay(), 255, 222, 222); 
+                                                       text.setBackground(error);                                      
+                                                       error.dispose();
+                                               }
+                                       }
+                               });             
+                               
+                               //text.setEditable( false );
+                               select.setText("Select");
+                               select.setLayoutData(new GridData(SWT.RIGHT, SWT.BEGINNING, false, false));
+                               select.addSelectionListener(new SelectionAdapter() {
+                                       public void widgetSelected(SelectionEvent e) {
+                                               Object initialValue;
+                                               try {
+                                                       initialValue = parse(fieldBinding, text.getText());
+                                               } catch (BindingException e1) {
+                                                       initialValue = fieldBinding.createDefaultUnchecked();
+                                               }
+                                               DataboardDialog dialog = new DataboardDialog(
+                                                               shell,
+                                                               title, 
+                                                               fieldBinding, 
+                                                               initialValue);
+                                                                                                       
+                                               int code = dialog.open();
+                                               if ( code == Window.OK ) {
+                                                       Object result = dialog.getResult();
+                                                       String str = print(fieldBinding, result);
+                                                       text.setText( str );
+                                               }
+                                       }
+                               });
+                               
+                       } else 
+
+                       // Method 2: Label + composite
+                       if ( allBooleans(record) ) {                    
+                               Label label = new Label(parent, 0);
+                               label.setText(fieldName + ":");
+                               label.setLayoutData( lblLayout );
+                               
+                               for (int i=0; i<record.getComponentCount(); i++) {
+                                       String childName = record.getComponent(i).name;
+                                       Datatype childType = record.getComponentType(i);
+                                       String childRef = appendName(ref, childName);
+                                       if (i>0) new Label(parent, 0);
+                                       addWidget( parent, childType, childRef );
+                               }               
+                               
+                       } 
+                       else
+                       
+                       // Method 3: Groups
+                       {
+                               Group group = new Group(parent, 0);
+                               group.setText(fieldName);
+                               group.setLayout( new GridLayout(3, false) );                    
+                               group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
+                               group.setData(ref);
+                               addRecord(group, record, ref);
+                       }
+                       
+               } else if (fieldType instanceof UnionType) {
+                       Label label = new Label(parent, 0);
+                       label.setText(fieldName + ":");
+                       label.setLayoutData( lblLayout );
+                       addUnion( parent, (UnionType) fieldType, ref );
+               }
+               
+       }
+
+       protected void addWidget(Composite parent, Datatype fieldType, String ref) {
+               if (fieldType instanceof StringType) {
+                       addString(parent, (StringType) fieldType, ref);
+               } else if (fieldType instanceof BooleanType) {
+                       addBoolean(parent, (BooleanType) fieldType, ref);
+               } else if (fieldType instanceof NumberType) {
+                       addNumber(parent, (NumberType) fieldType, ref);
+               } else if (fieldType instanceof RecordType) {
+                       RecordType rt = (RecordType) fieldType;
+                       Group group = new Group(parent, 0);
+                       String name = getName(ref);
+                       group.setText(name);
+                       group.setLayout( new GridLayout(3, false) );                    
+                       group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
+                       group.setData(ref);
+                       addRecord(group, rt, ref);
+               } else if (fieldType instanceof UnionType) {
+                       addUnion( parent, (UnionType) fieldType, ref );
+               }
+       }
+       
+       public CTabFolder addTabFolder( Composite parent, RecordType record, String ref )
+       {
+               CTabFolder folder = new CTabFolder(parent, SWT.TOP | SWT.BORDER);
+               folder.setSimple(false);
+               folder.setLayout( new GridLayout(3, false) );                   
+               folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
+               
+               for (int i=0; i<record.getComponentCount(); i++) {
+                       String childName = record.getComponent(i).name;
+                       Datatype childType = record.getComponentType(i);
+                       String childRef = appendName(ref, childName);
+                       if (i>0) new Label(parent, 0);
+                       
+                       CTabItem item = new CTabItem(folder, SWT.NONE);
+                       item.setText(childName);
+                       Composite composite = new Composite(folder, 0);
+                       composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+                       composite.setLayout( new GridLayout(3, false) );
+                       composite.setData(ref);
+                       
+                       if ( childType instanceof RecordType ) {
+                               addRecord( composite, (RecordType)childType, childRef);                                         
+                       } else {
+                               addWidget( composite, childType, childRef );
+                       }
+                       
+                       item.setControl( composite );
+                       allfields.addComponent(childName, childType);
+               }       
+               folder.setSelection(0);
+               return folder;
+               
+       }
+       
+       protected void addRecord( Composite parent, RecordType record, String ref )
+       {
+               String options = record.metadata.get("style");
+               boolean dialog = options==null?false:options.contains("dialog");
+               boolean tabbed = options==null?false:options.contains("tabbed");
+               
+               // Method 0: Tabbed, each field is a tab page
+               if ( tabbed ) {
+                       addTabFolder( parent, record, ref );
+                       return;
+               }
+               
+               // Normal Record
+               GridData lblLayout = new GridData(SWT.LEFT, SWT.BEGINNING, false, false, 1, 1);
+               if ( getDepth(ref) < 3 ) lblLayout.widthHint = column1Width;
+                       
+               for (int i=0; i<record.getComponentCount(); i++) {
+                       String fieldName = record.getComponent(i).name;
+                       Datatype fieldType = record.getComponentType(i);
+                       String fieldRef = appendName(ref, fieldName);
+                       addField( parent, fieldType, fieldRef, lblLayout);
+               }               
+               return;
+       }
+               
+       protected void addUnion( Composite parent, UnionType union, String ref )
+       {
+               if ( union.isEnumeration() ) {
+                       addEnum( parent, union, ref );
+               } else {
+                       addRadio( parent, union, ref );
+               }               
+       }
+       
+       protected void addEnum( Composite parent, UnionType union, String ref )
+       {
+               Combo combo = new Combo(parent, SWT.READ_ONLY);
+               String[] items = new String[union.getComponentCount()];
+               for (int i = 0; i < items.length; i++) {
+                       items[i] = union.getComponent(i).name;
+               }
+               combo.setItems(items);
+               combo.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
+               combo.setData(ref);
+               
+               // Set default value
+               UnionBinding binding = Bindings.getBinding(union);
+               try {
+                       TaggedObject defaultOption = (TaggedObject) binding.createDefault();
+                       combo.setText( items[ defaultOption.tag ] );
+               } catch (BindingException e) {
+               }                               
+       }
+
+       protected void addRadio( Composite parent, UnionType union, String ref )
+       {
+               Composite composite = new Composite(parent, 0);
+               composite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
+               composite.setLayout( new GridLayout(3, false) );
+               composite.setData(ref);
+               final int count = union.getComponentCount();
+               final Composite panels[] = new Composite[count];
+               final Button buttons[] = new Button[count];
+               for ( int i=0; i<count; i++ ) {
+                       Component component = union.getComponent(i);
+                       String childRef = ref+"/"+URIUtil.encodeURI(component.name);
+                       Button b = buttons[i] = new Button(composite, SWT.RADIO);
+                       b.setLayoutData(new GridData(SWT.LEFT, SWT.BEGINNING, false, false, 1, 1));
+                       b.setText(component.name);
+                       b.setData(childRef);
+                       
+                       String options = union.metadata.get("style");
+                       boolean border = options==null?true:!options.contains("no-border");
+                       int style = 0;                  
+                       if (border) style |= SWT.BORDER;
+                       
+                       Composite panel = panels[i] = new Composite(composite, style);
+                       panel.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
+                       panel.setLayout( new GridLayout(3, false) );
+                       panel.setData(childRef);
+                       
+                       Datatype componentType = union.getComponentType(i);
+                       if ( componentType instanceof RecordType ) {
+                               addRecord(panel, (RecordType) componentType, childRef);
+                       } else {
+                               addWidget(panel, componentType, childRef);
+                       }                       
+               }
+               
+               // Set default value
+               UnionBinding binding = Bindings.getBinding(union);
+               try {
+                       TaggedObject defaultOption = (TaggedObject) binding.createDefault();
+                       buttons[defaultOption.tag].setSelection(true);
+               } catch (BindingException e) {
+               }                               
+               
+       }
+       
+       protected void addNumber( Composite parent, NumberType number, String ref )
+       {
+               String unit = number.getUnit();
+               String options = number.metadata.get("style");
+               boolean border = options==null?true:!options.contains("no-border");
+               int style = 0;                  
+               if (border) style |= SWT.BORDER;
+               final Text text = new Text(parent, style);
+               text.setData(ref);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, unit==null?2:1, 1));
+               
+               // Set default value
+               final NumberBinding binding = Bindings.getBinding( number );
+               try {
+                       text.setText( binding.createDefault().toString() );
+               } catch (BindingException e) {
+               }
+               
+               // Add validator
+               text.addListener(SWT.Verify, new Listener() {
+                       public void handleEvent(Event e) {
+                               try {
+                                       String newText = applyEventToString( text.getText(), e );
+                                       Object value = binding.create( newText );
+                                       binding.assertInstaceIsValid( value );
+                                       text.setBackground(null);                                       
+                               } catch (BindingException er) {
+                                       Color error = new Color(text.getDisplay(), 255, 222, 222); 
+                                       text.setBackground(error);
+                                       error.dispose();                                        
+                               }
+                       }});
+               
+               if ( unit!=null ) {
+                       Label unitLabel = new Label(parent, 0);
+                       unitLabel.setText(unit);
+               }
+       }
+       
+       static String applyEventToString(String orig, Event e) {
+               if (e.character==8) {
+                       return orig.substring(0, e.start) + orig.substring(e.end, orig.length());
+               }
+               return orig.substring(0, e.start) + e.text + orig.substring(e.end, orig.length());              
+       }
+       
+       protected void addBoolean( Composite parent, BooleanType booleanType, String ref )
+       {
+               Button button = new Button(parent, SWT.CHECK);
+               button.setText( getName(ref) );
+               button.setData(ref);
+               button.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
+       }
+       
+       protected void addString( Composite parent, StringType stringType, String ref )
+       {               
+               String options = stringType.metadata.get("style");
+               boolean password = options==null?false:options.contains("password");
+               boolean filesave = options==null?false:options.contains("filesave");
+               boolean fileopen = options==null?false:options.contains("fileopen");
+               boolean directory = options==null?false:options.contains("directory");
+               boolean multi = options==null?false:options.contains("multi");
+               boolean border = options==null?true:!options.contains("no-border");
+
+               int style = 0;
+               if (password) style |= SWT.PASSWORD;
+               if (multi) style |= SWT.MULTI;
+               if (border) style |= SWT.BORDER;
+               final Text text = new Text(parent, style);
+               text.setLayoutData( new GridData(SWT.FILL, multi?SWT.FILL:SWT.BEGINNING, true, false, filesave|fileopen|directory?1:2, 1 ) );
+               text.setData(ref);
+
+               // Set default value
+               final StringBinding binding = Bindings.getBinding( stringType );
+               try {
+                       text.setText( binding.createDefault().toString() );
+               } catch (BindingException e) {
+               }
+               
+               text.addListener(SWT.Verify, new Listener() {
+                       public void handleEvent(Event e) {
+                               try {
+                                       String newText = applyEventToString( text.getText(), e );
+                                       Object value = binding.create( newText );
+                                       binding.assertInstaceIsValid( value );
+                                       text.setBackground(null);
+                               } catch (BindingException er) {
+                                       Color error = new Color(text.getDisplay(), 255, 222, 222); 
+                                       text.setBackground(error);                                      
+                                       error.dispose();
+                               }
+                       }
+               });             
+               if (filesave|fileopen) {
+                       final FileDialog fd = new FileDialog(parent.getShell(), filesave?SWT.SAVE:SWT.OPEN);
+                                       
+                       String filetypesStr = stringType.metadata.get("filetypes");
+                       String fileextsStr = stringType.metadata.get("fileexts");
+                       String name = getName(ref);
+                       String[] filetypes = filetypesStr==null?null:(String[]) filetypesStr.split(","); 
+                       String[] fileexts = fileextsStr==null?null:(String[]) fileextsStr.split(",");                                   
+                       fd.setFilterNames(filetypes);
+                       fd.setFilterExtensions(fileexts);
+                       
+                       boolean nameMatchesExt = false;
+                       if ( name!=null && !name.isEmpty() ) {
+                               for (String fileext : fileexts) {
+                                       nameMatchesExt |= StringUtil.simplePatternMatch(name, fileext) && !name.contains(" ");
+                               }
+                       }
+                       final String initialName = nameMatchesExt?name:null;
+                       
+                       Button chooseFileButton = new Button(parent, SWT.PUSH);
+                       chooseFileButton.setText("Select");
+                       chooseFileButton.setLayoutData(new GridData(SWT.RIGHT, SWT.BEGINNING, false, false));
+                       chooseFileButton.addSelectionListener(new SelectionAdapter() {
+                               public void widgetSelected(SelectionEvent e) {
+                                       String name = text.getText();
+                                       if ( name == null || name.isEmpty() ) name = initialName;
+                                       if ( name!=null ) fd.setFileName( name );
+                                       String result = fd.open();
+                                       if (result != null)
+                                               text.setText(result);
+                               }
+                       });
+               }
+
+               if (directory) {
+                       final DirectoryDialog fd = new DirectoryDialog(parent.getShell(), 0);
+                       String name = getName(ref);
+                       fd.setMessage( name );
+                       Button chooseDirButton = new Button(parent, SWT.PUSH);
+                       chooseDirButton.setText("Select");
+                       chooseDirButton.setLayoutData(new GridData(SWT.RIGHT, SWT.BEGINNING, false, false));
+                       chooseDirButton.addSelectionListener(new SelectionAdapter() {
+                               public void widgetSelected(SelectionEvent e) {
+                                       String dir = text.getText();                                    
+                                       fd.setFilterPath(dir);
+                                       String result = fd.open();
+                                       if (result != null)
+                                               text.setText(result);
+                               }
+                       });
+               }
+       }
+
+       static String appendName(String ref, String name)
+       {
+               return ref+"/"+URIUtil.encodeURI(name);
+       }
+       
+       static String getName(String ref)
+       {
+               if ( ref==null ) return null;
+               int i = ref.lastIndexOf('/');
+               if (i<0) return URIUtil.decodeURI(ref);
+               String namePart = ref.substring(i+1);
+               return URIUtil.decodeURI(namePart);
+       }
+       
+       static int getDepth(String ref)
+       {
+               int depth = 0;
+               for (int i=0; i<ref.length(); i++) if ( ref.charAt(i)=='/' ) depth++;
+               return depth;
+       }
+       
+       static boolean allBooleans(RecordType rt) {
+               for (Component c : rt.getComponents())
+                       if ( c.type instanceof BooleanType == false ) return false;
+               return true;
+       }
+       
+       static String print(RecordBinding recordBinding, Object value) {                
+               if ( allBooleans(recordBinding.type() ) ) {
+                       StringBuilder sb = new StringBuilder();
+                       int j = 0;
+                       for ( int i = 0; i<recordBinding.getComponentCount(); i++ ) {
+                               try {
+                                       boolean b = recordBinding.getBoolean(value, i);
+                                       if ( !b ) continue;
+                                       if ( j>0 ) sb.append(", ");
+                                       sb.append(recordBinding.type().getComponent(i).name);
+                                       j++;
+                               } catch (BindingException e) {
+                                       continue;
+                               }                               
+                       }
+                       return sb.toString();
+               } else 
+               try {
+                       return recordBinding.toString(value, true);
+               } catch (BindingException e) {
+                       return e.toString();
+               }
+       }
+       
+       static Object parse(RecordBinding recordBinding, String txt) throws BindingException {
+               if ( allBooleans(recordBinding.type() ) ) {
+                       Object result = recordBinding.createDefaultUnchecked();
+                       String[] tokens = txt.split(", ");
+                       for ( String token : tokens ) {
+                               if ( token.isEmpty() ) continue;
+                               int i = recordBinding.type().getComponentIndex2(token);
+                               if ( i>=0 ) {
+                                       try {
+                                               recordBinding.setBoolean(result, i, true);
+                                       } catch (BindingException e) {
+                                               throw e;
+                                       }
+                               } else {
+                                       throw new BindingException("There is no field \""+token+"\"");
+                               }
+                       }
+                       return result;
+               } else {
+                       try {
+                               return recordBinding.parseValueDefinition(txt);
+                       } catch (DataTypeSyntaxError e) {
+                               throw new BindingException(e);
+                       }
+                       
+               }
+       }
+       
+       static {
+           PASSWORD = new StringType();
+           PASSWORD.metadata.put("style", "password");
+           
+           TEXTBOX = new StringType();
+           TEXTBOX.metadata.put("style", "multi");
+       }
+               
+}