1 package org.simantics.databoard.forms;
3 import java.util.ArrayList;
6 import org.eclipse.jface.window.Window;
7 import org.eclipse.swt.SWT;
8 import org.eclipse.swt.custom.CTabFolder;
9 import org.eclipse.swt.custom.CTabItem;
10 import org.eclipse.swt.events.SelectionAdapter;
11 import org.eclipse.swt.events.SelectionEvent;
12 import org.eclipse.swt.graphics.Color;
13 import org.eclipse.swt.layout.GridData;
14 import org.eclipse.swt.layout.GridLayout;
15 import org.eclipse.swt.widgets.Button;
16 import org.eclipse.swt.widgets.Combo;
17 import org.eclipse.swt.widgets.Composite;
18 import org.eclipse.swt.widgets.Control;
19 import org.eclipse.swt.widgets.DirectoryDialog;
20 import org.eclipse.swt.widgets.Event;
21 import org.eclipse.swt.widgets.FileDialog;
22 import org.eclipse.swt.widgets.Group;
23 import org.eclipse.swt.widgets.Label;
24 import org.eclipse.swt.widgets.Listener;
25 import org.eclipse.swt.widgets.Shell;
26 import org.eclipse.swt.widgets.Text;
27 import org.simantics.databoard.Accessors;
28 import org.simantics.databoard.Bindings;
29 import org.simantics.databoard.accessor.Accessor;
30 import org.simantics.databoard.accessor.BooleanAccessor;
31 import org.simantics.databoard.accessor.RecordAccessor;
32 import org.simantics.databoard.accessor.StringAccessor;
33 import org.simantics.databoard.accessor.UnionAccessor;
34 import org.simantics.databoard.accessor.error.AccessorConstructionException;
35 import org.simantics.databoard.accessor.error.AccessorException;
36 import org.simantics.databoard.accessor.error.ReferenceException;
37 import org.simantics.databoard.accessor.reference.ChildReference;
38 import org.simantics.databoard.accessor.reference.LabelReference;
39 import org.simantics.databoard.binding.Binding;
40 import org.simantics.databoard.binding.NumberBinding;
41 import org.simantics.databoard.binding.RecordBinding;
42 import org.simantics.databoard.binding.StringBinding;
43 import org.simantics.databoard.binding.UnionBinding;
44 import org.simantics.databoard.binding.error.BindingException;
45 import org.simantics.databoard.binding.mutable.TaggedObject;
46 import org.simantics.databoard.parser.repository.DataTypeSyntaxError;
47 import org.simantics.databoard.type.BooleanType;
48 import org.simantics.databoard.type.Component;
49 import org.simantics.databoard.type.Datatype;
50 import org.simantics.databoard.type.NumberType;
51 import org.simantics.databoard.type.RecordType;
52 import org.simantics.databoard.type.StringType;
53 import org.simantics.databoard.type.UnionType;
54 import org.simantics.databoard.util.Bean;
55 import org.simantics.databoard.util.StringUtil;
56 import org.simantics.databoard.util.URIUtil;
59 * Databoard form creates SWT user interface from databoard record type.
61 * See DataboardFormExample for example usage.
63 * @author toni.kalajainen@semantum.fi
65 public class DataboardForm {
67 public static final Datatype TEXTBOX;
68 public static final Datatype PASSWORD;
70 public int column1Width = -1;
72 private RecordType allfields = new RecordType();
77 * @param namesAndExts array of names and extensions
81 public static StringType fileSaveDialog(String...namesAndExts)
83 StringType result = new StringType();
84 result.metadata.put("style", "filesave");
86 StringBuilder sb1 = new StringBuilder();
87 StringBuilder sb2 = new StringBuilder();
89 for (int i=0; i<namesAndExts.length; i++) {
90 StringBuilder sb = (i%2==0?sb1:sb2);
92 if (j>0) sb.append(',');
93 sb.append(namesAndExts[i]);
96 result.metadata.put("filetypes", sb1.toString());
97 result.metadata.put("fileexts", sb2.toString());
104 * @param namesAndExts array of names and extensions
108 public static StringType fileOpenDialog(String...namesAndExts)
110 StringType result = new StringType();
111 result.metadata.put("style", "fileopen");
113 StringBuilder sb1 = new StringBuilder();
114 StringBuilder sb2 = new StringBuilder();
116 for (int i=0; i<namesAndExts.length; i++) {
117 StringBuilder sb = (i%2==0?sb1:sb2);
119 if (j>0) sb.append(',');
120 sb.append(namesAndExts[i]);
123 result.metadata.put("filetypes", sb1.toString());
124 result.metadata.put("fileexts", sb2.toString());
128 public static StringType directoryDialog()
130 StringType result = new StringType();
131 result.metadata.put("style", "directory");
135 public DataboardForm() {
138 public void setFirstColumnWidth(int width) {
139 column1Width = width;
144 * Validate the fields for valid values.
145 * StringTypes can be restricted with regular expressions.
146 * NumberTypes with value ranges.
149 * @return list of problems
151 public List<Problem> validate( Composite composite ) {
152 List<Problem> result = new ArrayList<Problem>();
153 _validateFields(composite, result);
157 public static List<String> toStrings(List<Problem> problems) {
158 List<String> result = new ArrayList<String>( problems.size() );
159 for (Problem p : problems) result.add(p.error);
163 public static class Problem extends Bean {
164 /** Reference to the field with problem */
165 public String fieldReference;
166 /** The field with problem */
167 public transient Control control;
168 /** The validation error */
172 protected void _validateFields( Control control, List<Problem> result )
175 Object data = control.getData();
176 if ( data != null && data instanceof String ) {
177 String refStr = (String) data;
178 ChildReference ref = ChildReference.parsePath( refStr);
181 Datatype fieldType = allfields.getChildType( ref );
184 if ( fieldType instanceof UnionType && control instanceof Combo ) {
185 // Nothing to validate
189 if ( fieldType instanceof UnionType && control instanceof Composite ) {
190 // Nothing to validate
194 if ( fieldType instanceof BooleanType && control instanceof Button ) {
195 // Nothing to validate
198 // Text + Dialog button
199 if ( fieldType instanceof RecordType && control instanceof Text ) {
201 Text text = (Text) control;
202 RecordBinding fieldBinding = Bindings.getMutableBinding(fieldType);
203 Object value = parse(fieldBinding, text.getText());
204 fieldBinding.assertInstaceIsValid( value );
205 } catch (BindingException er) {
206 Problem problem = new Problem();
207 if ( er.getCause()!=null ) {
208 problem.error = er.getCause().getMessage();
210 problem.error = er.getMessage();
212 problem.fieldReference = refStr;
213 problem.control = control;
214 result.add( problem );
220 if ( fieldType instanceof StringAccessor && control instanceof Text ) {
222 Text text = (Text) control;
223 StringBinding binding = Bindings.getBinding(fieldType);
224 Object value = binding.create(text.getText());
225 binding.assertInstaceIsValid( value );
226 } catch (BindingException er) {
227 Problem problem = new Problem();
228 if ( er.getCause()!=null ) {
229 problem.error = er.getCause().getMessage();
231 problem.error = er.getMessage();
233 problem.fieldReference = refStr;
234 problem.control = control;
235 result.add( problem );
240 if ( fieldType instanceof NumberType && control instanceof Text ) {
242 Text text = (Text) control;
243 NumberBinding binding = Bindings.getBinding(fieldType);
244 Object value = binding.create(text.getText());
245 binding.assertInstaceIsValid( value );
246 } catch (BindingException er) {
247 Problem problem = new Problem();
248 if ( er.getCause()!=null && er.getCause() instanceof NumberFormatException) {
249 NumberFormatException nfe = (NumberFormatException) er.getCause();
250 problem.error = nfe.getMessage();
252 problem.error = er.getMessage();
254 problem.fieldReference = refStr;
255 problem.control = control;
256 result.add( problem );
260 } catch (AccessorConstructionException e) {
261 Problem problem = new Problem();
262 problem.error = e.getMessage();
263 problem.fieldReference = refStr;
264 problem.control = control;
265 result.add( problem );
271 if ( control instanceof Composite ) {
272 Composite composite = (Composite) control;
273 for (Control child : composite.getChildren())
275 _validateFields(child, result);
281 public RecordType type() {
286 * Find control by reference
289 * @return control or null
291 public Control getControl( Control control, ChildReference ref )
293 return getControl( control, ref.toPath() );
297 * Find control by reference.
301 * @return control or null
303 public Control getControl( Control control, String ref )
305 Object data = control.getData();
306 if ( data != null && data instanceof String && data.equals(ref)) return control;
309 if ( control instanceof Composite ) {
310 Composite composite = (Composite) control;
311 for (Control child : composite.getChildren())
313 Control result = getControl(child, ref);
314 if ( result != null ) return result;
321 * Reads values of fields into a record
323 * @param composite the composite that holds the widgets
324 * @param binding record binding
325 * @param dst the record where values are read to
326 * @throws BindingException
327 * @throws AccessorConstructionException
328 * @throws AccessorException
330 public void readFields( Composite composite, RecordBinding binding, Object dst)
331 throws BindingException, AccessorConstructionException, AccessorException
333 RecordAccessor ra = Accessors.getAccessor(binding, dst);
334 _readFields(composite, ra);
337 protected void _readFields( Control control, RecordAccessor ra )
338 throws AccessorException, BindingException
341 Object data = control.getData();
342 if ( data != null && data instanceof String ) {
343 ChildReference ref = ChildReference.parsePath( (String) data);
346 Accessor fieldAccessor = ra.getComponent( ref );
349 if ( fieldAccessor instanceof UnionAccessor && control instanceof Combo ) {
350 UnionAccessor sa = (UnionAccessor) fieldAccessor;
351 Combo combo = (Combo) control;
352 String text = combo.getText();
353 int tag = sa.type().getComponentIndex2(text);
354 Datatype tagType = sa.type().components[tag].type;
355 Binding tagBinding = Bindings.getBinding(tagType);
356 Object defaultValue = tagBinding.createDefault();
357 sa.setComponentValue(tag, tagBinding, defaultValue);
361 if ( fieldAccessor instanceof UnionAccessor && control instanceof Composite ) {
362 Composite radioComposite = (Composite) control;
363 for (Control c : radioComposite.getChildren()) {
364 if ( c instanceof Button && ((Button)c).getSelection() ) {
365 Object data2 = c.getData();
366 if (data2==null) continue;
367 String name = getName( data2.toString() );
368 if ( name==null ) continue;
370 UnionAccessor sa = (UnionAccessor) fieldAccessor;
371 int tag = sa.type().getComponentIndex2( name );
373 Datatype tagType = sa.type().components[tag].type;
374 Binding tagBinding = Bindings.getBinding(tagType);
375 Object defaultValue = tagBinding.createDefault();
376 sa.setComponentValue(tag, tagBinding, defaultValue);
384 if ( fieldAccessor instanceof BooleanAccessor && control instanceof Button ) {
385 BooleanAccessor sa = (BooleanAccessor) fieldAccessor;
386 sa.setValue(((Button)control).getSelection());
389 // Read Text + Dialog Button
390 if ( fieldAccessor instanceof RecordAccessor && control instanceof Text ) {
391 RecordAccessor raa = (RecordAccessor) fieldAccessor;
392 Text text = (Text) control;
393 RecordBinding fieldBinding = Bindings.getMutableBinding( raa.type() );
394 Object value = parse( fieldBinding, text.getText() );
395 raa.setValue(fieldBinding, value);
399 if ( fieldAccessor instanceof StringAccessor && control instanceof Text ) {
400 StringAccessor sa = (StringAccessor) fieldAccessor;
401 sa.setValue(((Text)control).getText());
405 if ( fieldAccessor.type() instanceof NumberType && control instanceof Text ) {
406 NumberBinding binding = Bindings.getBinding( fieldAccessor.type() );
407 Object value = binding.create( ((Text)control).getText() );
408 fieldAccessor.setValue(binding, value);
411 } catch (AccessorConstructionException e) {
412 //e.printStackTrace();
418 if ( control instanceof Composite ) {
419 Composite composite = (Composite) control;
420 for (Control child : composite.getChildren())
422 _readFields(child, ra);
427 public void writeFields( Composite composite, RecordBinding binding, Object src)
428 throws AccessorException, BindingException, AccessorConstructionException
430 RecordAccessor ra = Accessors.getAccessor(binding, src);
431 _writeFields(composite, ra);
434 void _writeFields( Control control, RecordAccessor ra )
435 throws AccessorException, BindingException, AccessorConstructionException
438 Object data = control.getData();
439 if ( data != null && data instanceof String ) {
440 ChildReference ref = ChildReference.parsePath( (String) data);
443 Accessor fieldAccessor = ra.getComponent( ref );
446 if ( fieldAccessor instanceof UnionAccessor && control instanceof Combo ) {
447 UnionAccessor sa = (UnionAccessor) fieldAccessor;
448 Combo combo = (Combo) control;
449 int tag = sa.getTag();
450 combo.setText( sa.type().getComponent(tag).name );
454 if ( fieldAccessor instanceof UnionAccessor && control instanceof Composite) {
455 Composite radioComposite = (Composite) control;
456 UnionAccessor sa = (UnionAccessor) fieldAccessor;
457 int tag = sa.getTag();
458 for (int i=0; i<sa.count(); i++) {
459 Button button = (Button) radioComposite.getChildren()[i*2];
460 button.setSelection(i==tag);
465 if ( fieldAccessor instanceof BooleanAccessor && control instanceof Button ) {
466 BooleanAccessor sa = (BooleanAccessor) fieldAccessor;
467 ((Button)control).setSelection(sa.getValue());
470 // Write Text + Dialog Selection
471 if ( fieldAccessor instanceof RecordAccessor && control instanceof Text ) {
472 RecordAccessor raa = (RecordAccessor) fieldAccessor;
473 Text text = (Text) control;
474 RecordBinding fieldBinding = Bindings.getMutableBinding( raa.type() );
475 String str = print( fieldBinding, raa.getValue(fieldBinding) );
480 if ( fieldAccessor instanceof StringAccessor && control instanceof Text ) {
481 StringAccessor sa = (StringAccessor) fieldAccessor;
482 ((Text)control).setText( sa.getValue() );
486 if ( fieldAccessor.type() instanceof NumberType && control instanceof Text ) {
487 NumberBinding binding = Bindings.getBinding( fieldAccessor.type() );
488 Object value = fieldAccessor.getValue(binding);
489 ((Text)control).setText( binding.toString(value, true) );
492 } catch (AccessorConstructionException e) {
493 //e.printStackTrace();
499 if ( control instanceof Composite ) {
500 Composite composite = (Composite) control;
501 for (Control child : composite.getChildren())
503 _writeFields(child, ra);
509 public void clear( Control control )
511 _clearFields( control );
515 protected void _clearFields( Control control )
518 if ( control instanceof Composite ) {
519 Composite composite = (Composite) control;
520 for (Control child : composite.getChildren())
530 * Add a listener to all the controls that represent the data type.
532 * @param composite the composite that holds the widgets
533 * @param binding record binding
534 * @param dst the record where values are read to
535 * @throws BindingException
536 * @throws AccessorConstructionException
537 * @throws AccessorException
539 public void addListener( Composite composite, RecordType type, Listener listener )
540 throws BindingException, AccessorConstructionException, AccessorException
542 _addListener(composite, type, listener );
545 protected void _addListener( Control control, Datatype type, Listener listener )
546 throws AccessorException, BindingException
549 Object data = control.getData();
550 ChildReference ref = null;
551 if ( data != null && data instanceof String ) {
552 ref = ChildReference.parsePath( (String) data);
556 Datatype fieldType = ref==null ? type : type.getChildType( ref );
559 if ( fieldType instanceof UnionType && control instanceof Combo ) {
560 control.addListener(SWT.Selection, listener);
563 if ( fieldType instanceof UnionType && control instanceof Composite ) {
564 Composite radioComposite = (Composite) control;
565 for (Control c : radioComposite.getChildren()) {
566 if ( c instanceof Button ) {
567 c.addListener(SWT.Selection, listener);
573 if ( fieldType instanceof BooleanType && control instanceof Button ) {
574 control.addListener(SWT.Selection, listener);
578 if ( fieldType instanceof StringType && control instanceof Text ) {
579 control.addListener(SWT.Modify, listener);
583 if ( fieldType instanceof NumberType && control instanceof Text ) {
584 control.addListener(SWT.Modify, listener);
587 } catch (ReferenceException re) {
591 if ( control instanceof Composite ) {
592 Composite composite = (Composite) control;
593 for (Control child : composite.getChildren())
595 _addListener(child, type, listener);
600 public void addField( Composite parent, String name, Datatype fieldType )
602 addField(parent, fieldType, URIUtil.encodeURI(name), null);
603 allfields.addComponent(name, fieldType);
606 public void addField( Composite parent, String name, Datatype fieldType, String ref )
608 addField(parent, fieldType, appendName(ref, name), null);
610 RecordType root = allfields;
611 if ( ref != null && !ref.isEmpty() ) {
613 ChildReference path = ChildReference.parsePath(ref);
614 while ( path!=null ) {
615 if ( path instanceof LabelReference == false ) {
616 throw new RuntimeException( "blaah" );
618 LabelReference lr = (LabelReference) path;
619 String label = lr.label;
621 if ( root.hasComponent(label) ) {
622 root = (RecordType) root.getComponent(label).type;
624 RecordType rt = new RecordType();
625 root.addComponent(label, rt);
628 path = path.childReference;
631 if ( !root.hasComponent(name) ) root.addComponent(name, fieldType);
635 public void addFields( Composite parent, RecordType source )
637 for (Component component : source.getComponents())
638 addField( parent, component.name, component.type );
641 public void addFields( Composite parent, RecordType source, String ref )
643 for (Component component : source.getComponents())
644 addField( parent, component.name, component.type, ref );
648 public void addField( Composite parent, Datatype fieldType, String ref, GridData lblLayout )
650 if (lblLayout==null) {
651 lblLayout = new GridData(SWT.LEFT, SWT.BEGINNING, false, false, 1, 1);
652 if ( getDepth(ref) < 3 ) lblLayout.widthHint = column1Width;
654 String fieldName = getName(ref);
656 if (fieldType instanceof StringType) {
657 Label label = new Label(parent, 0);
658 label.setText(fieldName + ":");
659 label.setToolTipText(fieldName);
660 label.setLayoutData( lblLayout );
662 addString(parent, (StringType) fieldType, ref);
664 } else if (fieldType instanceof BooleanType) {
666 Label label = new Label(parent, 0);
667 label.setLayoutData( lblLayout );
669 addBoolean(parent, (BooleanType) fieldType, ref);
671 } else if (fieldType instanceof NumberType) {
673 Label label = new Label(parent, 0);
674 label.setText(fieldName + ":");
675 label.setLayoutData( lblLayout );
677 addNumber(parent, (NumberType) fieldType, ref);
679 } else if (fieldType instanceof RecordType) {
680 RecordType record = (RecordType) fieldType;
681 String options = record.metadata.get("style");
682 boolean dialog = options==null?false:options.contains("dialog");
683 boolean tabbed = options==null?false:options.contains("tabbed");
685 // Method 0: Tabbed, each field is a tab page
687 Label label = new Label(parent, 0);
688 label.setText(fieldName + ":");
689 label.setLayoutData( lblLayout );
691 CTabFolder folder = new CTabFolder(parent, SWT.TOP | SWT.BORDER);
692 //folder.setUnselectedCloseVisible(false);
693 folder.setSimple(false);
694 folder.setLayout( new GridLayout(3, false) );
695 folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
697 for (int i=0; i<record.getComponentCount(); i++) {
698 String childName = record.getComponent(i).name;
699 Datatype childType = record.getComponentType(i);
700 String childRef = appendName(ref, childName);
701 if (i>0) new Label(parent, 0);
703 CTabItem item = new CTabItem(folder, SWT.NONE);
704 item.setText(childName);
705 Composite composite = new Composite(folder, 0);
706 composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
707 composite.setLayout( new GridLayout(3, false) );
708 composite.setData(ref);
710 if ( childType instanceof RecordType ) {
711 addRecord( composite, (RecordType)childType, childRef);
713 addWidget( composite, childType, childRef );
716 item.setControl( composite );
718 folder.setSelection(0);
722 // Method 1: Dialog = Label + Text + [Button]
724 Label label = new Label(parent, 0);
725 label.setText(fieldName + ":");
726 label.setLayoutData( lblLayout );
728 final String title = fieldName;
729 final Shell shell = parent.getShell();
730 final RecordBinding fieldBinding = Bindings.getMutableBinding( fieldType );
731 final Object fieldValue = fieldBinding.createDefaultUnchecked();
732 final Text text = new Text(parent, SWT.BORDER);
733 final Button select = new Button(parent, SWT.PUSH);
734 text.setLayoutData( new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1 ) );
735 text.setText( print(fieldBinding, fieldValue) );
737 text.addListener(SWT.Verify, new Listener() {
738 public void handleEvent(Event e) {
740 String newText = applyEventToString( text.getText(), e );
741 Object value = parse(fieldBinding, newText);
742 fieldBinding.assertInstaceIsValid( value );
743 text.setBackground(null);
744 } catch (BindingException er) {
745 Color error = new Color(text.getDisplay(), 255, 222, 222);
746 text.setBackground(error);
752 //text.setEditable( false );
753 select.setText("Select");
754 select.setLayoutData(new GridData(SWT.RIGHT, SWT.BEGINNING, false, false));
755 select.addSelectionListener(new SelectionAdapter() {
756 public void widgetSelected(SelectionEvent e) {
759 initialValue = parse(fieldBinding, text.getText());
760 } catch (BindingException e1) {
761 initialValue = fieldBinding.createDefaultUnchecked();
763 DataboardDialog dialog = new DataboardDialog(
769 int code = dialog.open();
770 if ( code == Window.OK ) {
771 Object result = dialog.getResult();
772 String str = print(fieldBinding, result);
780 // Method 2: Label + composite
781 if ( allBooleans(record) ) {
782 Label label = new Label(parent, 0);
783 label.setText(fieldName + ":");
784 label.setLayoutData( lblLayout );
786 for (int i=0; i<record.getComponentCount(); i++) {
787 String childName = record.getComponent(i).name;
788 Datatype childType = record.getComponentType(i);
789 String childRef = appendName(ref, childName);
790 if (i>0) new Label(parent, 0);
791 addWidget( parent, childType, childRef );
799 Group group = new Group(parent, 0);
800 group.setText(fieldName);
801 group.setLayout( new GridLayout(3, false) );
802 group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
804 addRecord(group, record, ref);
807 } else if (fieldType instanceof UnionType) {
808 Label label = new Label(parent, 0);
809 label.setText(fieldName + ":");
810 label.setLayoutData( lblLayout );
811 addUnion( parent, (UnionType) fieldType, ref );
816 protected void addWidget(Composite parent, Datatype fieldType, String ref) {
817 if (fieldType instanceof StringType) {
818 addString(parent, (StringType) fieldType, ref);
819 } else if (fieldType instanceof BooleanType) {
820 addBoolean(parent, (BooleanType) fieldType, ref);
821 } else if (fieldType instanceof NumberType) {
822 addNumber(parent, (NumberType) fieldType, ref);
823 } else if (fieldType instanceof RecordType) {
824 RecordType rt = (RecordType) fieldType;
825 Group group = new Group(parent, 0);
826 String name = getName(ref);
828 group.setLayout( new GridLayout(3, false) );
829 group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
831 addRecord(group, rt, ref);
832 } else if (fieldType instanceof UnionType) {
833 addUnion( parent, (UnionType) fieldType, ref );
837 public CTabFolder addTabFolder( Composite parent, RecordType record, String ref )
839 CTabFolder folder = new CTabFolder(parent, SWT.TOP | SWT.BORDER);
840 folder.setSimple(false);
841 folder.setLayout( new GridLayout(3, false) );
842 folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
844 for (int i=0; i<record.getComponentCount(); i++) {
845 String childName = record.getComponent(i).name;
846 Datatype childType = record.getComponentType(i);
847 String childRef = appendName(ref, childName);
848 if (i>0) new Label(parent, 0);
850 CTabItem item = new CTabItem(folder, SWT.NONE);
851 item.setText(childName);
852 Composite composite = new Composite(folder, 0);
853 composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
854 composite.setLayout( new GridLayout(3, false) );
855 composite.setData(ref);
857 if ( childType instanceof RecordType ) {
858 addRecord( composite, (RecordType)childType, childRef);
860 addWidget( composite, childType, childRef );
863 item.setControl( composite );
864 allfields.addComponent(childName, childType);
866 folder.setSelection(0);
871 protected void addRecord( Composite parent, RecordType record, String ref )
873 String options = record.metadata.get("style");
874 boolean dialog = options==null?false:options.contains("dialog");
875 boolean tabbed = options==null?false:options.contains("tabbed");
877 // Method 0: Tabbed, each field is a tab page
879 addTabFolder( parent, record, ref );
884 GridData lblLayout = new GridData(SWT.LEFT, SWT.BEGINNING, false, false, 1, 1);
885 if ( getDepth(ref) < 3 ) lblLayout.widthHint = column1Width;
887 for (int i=0; i<record.getComponentCount(); i++) {
888 String fieldName = record.getComponent(i).name;
889 Datatype fieldType = record.getComponentType(i);
890 String fieldRef = appendName(ref, fieldName);
891 addField( parent, fieldType, fieldRef, lblLayout);
896 protected void addUnion( Composite parent, UnionType union, String ref )
898 if ( union.isEnumeration() ) {
899 addEnum( parent, union, ref );
901 addRadio( parent, union, ref );
905 protected void addEnum( Composite parent, UnionType union, String ref )
907 Combo combo = new Combo(parent, SWT.READ_ONLY);
908 String[] items = new String[union.getComponentCount()];
909 for (int i = 0; i < items.length; i++) {
910 items[i] = union.getComponent(i).name;
912 combo.setItems(items);
913 combo.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
917 UnionBinding binding = Bindings.getBinding(union);
919 TaggedObject defaultOption = (TaggedObject) binding.createDefault();
920 combo.setText( items[ defaultOption.tag ] );
921 } catch (BindingException e) {
925 protected void addRadio( Composite parent, UnionType union, String ref )
927 Composite composite = new Composite(parent, 0);
928 composite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
929 composite.setLayout( new GridLayout(3, false) );
930 composite.setData(ref);
931 final int count = union.getComponentCount();
932 final Composite panels[] = new Composite[count];
933 final Button buttons[] = new Button[count];
934 for ( int i=0; i<count; i++ ) {
935 Component component = union.getComponent(i);
936 String childRef = ref+"/"+URIUtil.encodeURI(component.name);
937 Button b = buttons[i] = new Button(composite, SWT.RADIO);
938 b.setLayoutData(new GridData(SWT.LEFT, SWT.BEGINNING, false, false, 1, 1));
939 b.setText(component.name);
942 String options = union.metadata.get("style");
943 boolean border = options==null?true:!options.contains("no-border");
945 if (border) style |= SWT.BORDER;
947 Composite panel = panels[i] = new Composite(composite, style);
948 panel.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
949 panel.setLayout( new GridLayout(3, false) );
950 panel.setData(childRef);
952 Datatype componentType = union.getComponentType(i);
953 if ( componentType instanceof RecordType ) {
954 addRecord(panel, (RecordType) componentType, childRef);
956 addWidget(panel, componentType, childRef);
961 UnionBinding binding = Bindings.getBinding(union);
963 TaggedObject defaultOption = (TaggedObject) binding.createDefault();
964 buttons[defaultOption.tag].setSelection(true);
965 } catch (BindingException e) {
970 protected void addNumber( Composite parent, NumberType number, String ref )
972 String unit = number.getUnit();
973 String options = number.metadata.get("style");
974 boolean border = options==null?true:!options.contains("no-border");
976 if (border) style |= SWT.BORDER;
977 final Text text = new Text(parent, style);
979 text.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, unit==null?2:1, 1));
982 final NumberBinding binding = Bindings.getBinding( number );
984 text.setText( binding.createDefault().toString() );
985 } catch (BindingException e) {
989 text.addListener(SWT.Verify, new Listener() {
990 public void handleEvent(Event e) {
992 String newText = applyEventToString( text.getText(), e );
993 Object value = binding.create( newText );
994 binding.assertInstaceIsValid( value );
995 text.setBackground(null);
996 } catch (BindingException er) {
997 Color error = new Color(text.getDisplay(), 255, 222, 222);
998 text.setBackground(error);
1004 Label unitLabel = new Label(parent, 0);
1005 unitLabel.setText(unit);
1009 static String applyEventToString(String orig, Event e) {
1010 if (e.character==8) {
1011 return orig.substring(0, e.start) + orig.substring(e.end, orig.length());
1013 return orig.substring(0, e.start) + e.text + orig.substring(e.end, orig.length());
1016 protected void addBoolean( Composite parent, BooleanType booleanType, String ref )
1018 Button button = new Button(parent, SWT.CHECK);
1019 button.setText( getName(ref) );
1020 button.setData(ref);
1021 button.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
1024 protected void addString( Composite parent, StringType stringType, String ref )
1026 String options = stringType.metadata.get("style");
1027 boolean password = options==null?false:options.contains("password");
1028 boolean filesave = options==null?false:options.contains("filesave");
1029 boolean fileopen = options==null?false:options.contains("fileopen");
1030 boolean directory = options==null?false:options.contains("directory");
1031 boolean multi = options==null?false:options.contains("multi");
1032 boolean border = options==null?true:!options.contains("no-border");
1035 if (password) style |= SWT.PASSWORD;
1036 if (multi) style |= SWT.MULTI;
1037 if (border) style |= SWT.BORDER;
1038 final Text text = new Text(parent, style);
1039 text.setLayoutData( new GridData(SWT.FILL, multi?SWT.FILL:SWT.BEGINNING, true, false, filesave|fileopen|directory?1:2, 1 ) );
1042 // Set default value
1043 final StringBinding binding = Bindings.getBinding( stringType );
1045 text.setText( binding.createDefault().toString() );
1046 } catch (BindingException e) {
1049 text.addListener(SWT.Verify, new Listener() {
1050 public void handleEvent(Event e) {
1052 String newText = applyEventToString( text.getText(), e );
1053 Object value = binding.create( newText );
1054 binding.assertInstaceIsValid( value );
1055 text.setBackground(null);
1056 } catch (BindingException er) {
1057 Color error = new Color(text.getDisplay(), 255, 222, 222);
1058 text.setBackground(error);
1063 if (filesave|fileopen) {
1064 final FileDialog fd = new FileDialog(parent.getShell(), filesave?SWT.SAVE:SWT.OPEN);
1066 String filetypesStr = stringType.metadata.get("filetypes");
1067 String fileextsStr = stringType.metadata.get("fileexts");
1068 String name = getName(ref);
1069 String[] filetypes = filetypesStr==null?null:(String[]) filetypesStr.split(",");
1070 String[] fileexts = fileextsStr==null?null:(String[]) fileextsStr.split(",");
1071 fd.setFilterNames(filetypes);
1072 fd.setFilterExtensions(fileexts);
1074 boolean nameMatchesExt = false;
1075 if ( name!=null && !name.isEmpty() ) {
1076 for (String fileext : fileexts) {
1077 nameMatchesExt |= StringUtil.simplePatternMatch(name, fileext) && !name.contains(" ");
1080 final String initialName = nameMatchesExt?name:null;
1082 Button chooseFileButton = new Button(parent, SWT.PUSH);
1083 chooseFileButton.setText("Select");
1084 chooseFileButton.setLayoutData(new GridData(SWT.RIGHT, SWT.BEGINNING, false, false));
1085 chooseFileButton.addSelectionListener(new SelectionAdapter() {
1086 public void widgetSelected(SelectionEvent e) {
1087 String name = text.getText();
1088 if ( name == null || name.isEmpty() ) name = initialName;
1089 if ( name!=null ) fd.setFileName( name );
1090 String result = fd.open();
1092 text.setText(result);
1098 final DirectoryDialog fd = new DirectoryDialog(parent.getShell(), 0);
1099 String name = getName(ref);
1100 fd.setMessage( name );
1101 Button chooseDirButton = new Button(parent, SWT.PUSH);
1102 chooseDirButton.setText("Select");
1103 chooseDirButton.setLayoutData(new GridData(SWT.RIGHT, SWT.BEGINNING, false, false));
1104 chooseDirButton.addSelectionListener(new SelectionAdapter() {
1105 public void widgetSelected(SelectionEvent e) {
1106 String dir = text.getText();
1107 fd.setFilterPath(dir);
1108 String result = fd.open();
1110 text.setText(result);
1116 static String appendName(String ref, String name)
1118 return ref+"/"+URIUtil.encodeURI(name);
1121 static String getName(String ref)
1123 if ( ref==null ) return null;
1124 int i = ref.lastIndexOf('/');
1125 if (i<0) return URIUtil.decodeURI(ref);
1126 String namePart = ref.substring(i+1);
1127 return URIUtil.decodeURI(namePart);
1130 static int getDepth(String ref)
1133 for (int i=0; i<ref.length(); i++) if ( ref.charAt(i)=='/' ) depth++;
1137 static boolean allBooleans(RecordType rt) {
1138 for (Component c : rt.getComponents())
1139 if ( c.type instanceof BooleanType == false ) return false;
1143 static String print(RecordBinding recordBinding, Object value) {
1144 if ( allBooleans(recordBinding.type() ) ) {
1145 StringBuilder sb = new StringBuilder();
1147 for ( int i = 0; i<recordBinding.getComponentCount(); i++ ) {
1149 boolean b = recordBinding.getBoolean(value, i);
1151 if ( j>0 ) sb.append(", ");
1152 sb.append(recordBinding.type().getComponent(i).name);
1154 } catch (BindingException e) {
1158 return sb.toString();
1161 return recordBinding.toString(value, true);
1162 } catch (BindingException e) {
1163 return e.toString();
1167 static Object parse(RecordBinding recordBinding, String txt) throws BindingException {
1168 if ( allBooleans(recordBinding.type() ) ) {
1169 Object result = recordBinding.createDefaultUnchecked();
1170 String[] tokens = txt.split(", ");
1171 for ( String token : tokens ) {
1172 if ( token.isEmpty() ) continue;
1173 int i = recordBinding.type().getComponentIndex2(token);
1176 recordBinding.setBoolean(result, i, true);
1177 } catch (BindingException e) {
1181 throw new BindingException("There is no field \""+token+"\"");
1187 return recordBinding.parseValueDefinition(txt);
1188 } catch (DataTypeSyntaxError e) {
1189 throw new BindingException(e);
1196 PASSWORD = new StringType();
1197 PASSWORD.metadata.put("style", "password");
1199 TEXTBOX = new StringType();
1200 TEXTBOX.metadata.put("style", "multi");