Java tutorial
/******************************************************************************* * Copyright (c) 2006-2013 The RCP Company and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * The RCP Company - initial API and implementation *******************************************************************************/ package com.rcpcompany.uibindings.internal.utils; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.core.commands.ParameterizedCommand; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.WritableValue; import org.eclipse.core.runtime.Assert; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EEnumLiteral; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.jface.databinding.swt.SWTObservables; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Spinner; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.commands.ICommandService; import org.eclipse.ui.forms.widgets.ColumnLayout; import org.eclipse.ui.forms.widgets.ExpandableComposite; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.forms.widgets.Section; import org.eclipse.ui.forms.widgets.TableWrapData; import org.eclipse.ui.forms.widgets.TableWrapLayout; import org.eclipse.ui.handlers.IHandlerService; import com.rcpcompany.uibindings.Constants; import com.rcpcompany.uibindings.IBindingContext; import com.rcpcompany.uibindings.IBindingContext.FinishOption; import com.rcpcompany.uibindings.IBindingContextFinalizer; import com.rcpcompany.uibindings.IDisposable; import com.rcpcompany.uibindings.IManager; import com.rcpcompany.uibindings.IUIBindingsPackage; import com.rcpcompany.uibindings.IValueBinding; import com.rcpcompany.uibindings.UIBindingsEMFObservables; import com.rcpcompany.uibindings.UIBindingsUtils; import com.rcpcompany.uibindings.observables.EListKeyedElementObservableValue; import com.rcpcompany.uibindings.uiAttributes.VirtualUIAttribute; import com.rcpcompany.uibindings.utils.IBindingSpec; import com.rcpcompany.uibindings.utils.IBindingSpec.SpecContext; import com.rcpcompany.uibindings.utils.IFormChooser; import com.rcpcompany.uibindings.utils.IFormCreator; import com.rcpcompany.uibindings.utils.ITableCreator; import com.rcpcompany.uibindings.validators.EValidatorAdapter; import com.rcpcompany.uibindings.validators.IValidatorAdapterManager; import com.rcpcompany.utils.basic.ui.TSSWTUtils; import com.rcpcompany.utils.logging.LogUtils; /** * Implementation of {@link IFormCreator}. * * @author Tonny Madsen, The RCP Company */ public class FormCreator implements IFormCreator { /** * The used context. */ private final IBindingContext myContext; /** * The top level {@link Composite}. Has a {@link TableWrapLayout} with 1 column and contains a * number of sections. */ private final Composite myTop; /** * The toolkit used to create all widgets. */ private final FormToolkit myToolkit; /** * The current value of this form. */ private final IObservableValue myObservableValue; /** * For a hierarchy of forms, the top form. */ private final FormCreator myTopForm; // TODO move to the top form? private Map<IObservableValue, Map<EStructuralFeature, IObservableValue>> myObservables; /** * Whether this form - and all sub-forms - are readonly. */ private boolean myReadOnly; /** * My {@link ScrolledForm} UI object - can be <code>null</code>. */ private ScrolledForm myScrolledForm = null; /** * My {@link Section} UI object - can be <code>null</code>. */ private Section mySection = null; /** * List of all sub forms - including sections. */ private List<FormCreator> mySubForms = null; @Override public boolean isTopForm() { return myTopForm == this; } /** * List with all bindings of this form that have not yet been finalized - see * {@link #createFieldReally(BindingDescription)}. */ private final List<BindingDescription> myBindings = new ArrayList<BindingDescription>(); /** * Adapter used to ensure the form is finished when the context is... * <p> * Only used in the top form. */ protected Adapter myContextListener = null; /** * Listener used to track the current focus widget. * <p> * Only used in the top form. */ protected Listener myFocusListener; /** * The control that last held focus in this form. * <p> * Only used in the top form. */ protected Control myLastFocusControl; /** * Constructs and returns a new form creator. * * @param context the context * @param obj the main object * @param toolkit the used Forms UI Toolkit * @param top the top level Composite * @param formHeader the header text used for the form */ public FormCreator(IBindingContext context, EObject obj, FormToolkit toolkit, Composite top, String formHeader) { this(context, createIOV(top, obj), toolkit, top, formHeader); } /** * Constructs and returns a new form creator. * * @param context the context * @param value the observable value * @param toolkit the used Forms UI Toolkit * @param top the top level Composite * @param formHeader the header text used for the form */ public FormCreator(IBindingContext context, IObservableValue value, FormToolkit toolkit, Composite top, String formHeader) { this(null, context, value, toolkit, top, formHeader); } /** * Constructs and returns a new form creator. * * @param topForm the top-level creator for this form creator - <code>null</code> for a * top-level creator * @param context the context * @param value the observable value * @param toolkit the used Forms UI Toolkit * @param top the top level Composite * @param formHeader the header text used for the form */ public FormCreator(FormCreator topForm, IBindingContext context, IObservableValue value, FormToolkit toolkit, Composite top, String formHeader) { if (toolkit == null) { if (context != null) { toolkit = context.getService(FormToolkit.class); } } if (toolkit == null) { toolkit = IManager.Factory.getManager().getFormToolkit(top); } myToolkit = toolkit; if (topForm == null) { myTopForm = this; } else { myTopForm = topForm; setReadOnly(myTopForm.isReadOnly()); } if (top instanceof Section) { mySection = (Section) top; } if (context == null) { if (formHeader != null) { myScrolledForm = createScrolledForm(top, formHeader); top = myScrolledForm.getBody(); context = IBindingContext.Factory.createContext(myScrolledForm); } else { top = createTopComposite(top); context = IBindingContext.Factory.createContext(top); } context.registerService(myToolkit); } else { if (formHeader != null) { myScrolledForm = createScrolledForm(top, formHeader); top = myScrolledForm.getBody(); } else { top = createTopComposite(top); } } myContext = context; myObservableValue = value; myTop = top; } public FormCreator(final EObject obj, WizardPage page, Composite parent) { this(obj, page, IManager.Factory.getManager().getFormToolkit(parent), parent); } public FormCreator(final EObject obj, WizardPage page, FormToolkit toolkit, Composite parent) { myToolkit = toolkit; myTopForm = this; myContext = IBindingContext.Factory.createContext(page); myObservableValue = createIOV(parent, obj); myTop = createTopComposite(parent); final IValidatorAdapterManager vam = IValidatorAdapterManager.Factory.getManager(); vam.addRoot(obj, new EValidatorAdapter()); /* * Make sure the validation adapter is removed again when the page is disposed... * * Page is disposed ==> top composite is disposed => context is disposed ==> dispose is * called on all services */ final IDisposable adapterDisposer = new IDisposable() { @Override public void dispose() { vam.removeRoot(obj); } }; getContext().registerService(adapterDisposer); } @Override public void dispose() { } private static IObservableValue createIOV(Composite top, EObject obj) { Assert.isNotNull(obj); return new WritableValue(SWTObservables.getRealm(top.getDisplay()), obj, obj.eClass()); } @Override public void setObject(EObject main) { myObservableValue.setValue(main); } @Override public boolean isReadOnly() { return myReadOnly; } @Override public void setReadOnly(boolean readonly) { myReadOnly = readonly; } @Override public EObject getObject() { return (EObject) myObservableValue.getValue(); } @Override public IObservableValue getObservableValue() { return myObservableValue; } @Override public void setHeading(String heading) { if (myScrolledForm != null) { myScrolledForm.setText(heading); } if (mySection != null) { mySection.setText(heading); } } @Override public IFormCreator subForm(Composite parent, IObservableValue obj) { return subForm(parent, obj, 0); } /** * Creates a sub form for the specified parent and new base object. * * @param parent the parent composite * @param obj the new base object * @param indent whether the new form is indented * @return the new sub form */ protected IFormCreator subForm(Composite parent, IObservableValue obj, int indent) { final FormCreator form = new FormCreator(myTopForm, myContext, obj, myToolkit, parent, (String) null); form.setFieldsAligned(areFieldsAligned()); if (mySubForms == null) { mySubForms = new ArrayList<FormCreator>(); } mySubForms.add(form); final TableWrapLayout layout = (TableWrapLayout) form.getTop().getLayout(); layout.leftMargin = indent; layout.rightMargin = indent; layout.topMargin = 3; layout.bottomMargin = 3; return form; } @Override public IFormCreator subForm(Composite parent) { return subForm(parent, myObservableValue, 0); } private void adapt(Control c) { if (myTop == null) return; if (c instanceof Text) return; if (c instanceof StyledText) return; if (c instanceof Spinner) return; c.setBackground(myTop.getBackground()); } @Override public void addLabel(String labelText) { final Label label = myToolkit.createLabel(myTop, labelText, SWT.NONE); adapt(label); final TableWrapData ld = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP); label.setLayoutData(ld); } @Override public IValueBinding addField(EStructuralFeature feature) { return addField(myObservableValue, feature, SWT.NONE); } @Override public IValueBinding addField(EStructuralFeature feature, int style) { return addField(myObservableValue, feature, style); } @Override public IValueBinding addField(EObject object, EStructuralFeature feature, int style) { /* * We create the binding first here! * * This allows us to access all the needed arguments of the binding :-) */ if (myContext == null) { LogUtils.throwException(this, "No context specified for IFormCreator", null); } final IValueBinding binding = myContext.addBinding().model(object, feature); createField(binding, style); return binding; }; @Override public IValueBinding addField(IObservableValue value, EStructuralFeature feature, int style) { /* * We create the binding first here! * * This allows us to access all the needed arguments of the binding :-) */ if (myContext == null) { LogUtils.throwException(this, "No context specified for IFormCreator", null); } final IValueBinding binding = myContext.addBinding().model(value, feature); createField(binding, style); return binding; }; @Override public IValueBinding addField(IObservableValue value, int style) { /* * We create the binding first here! * * This allows use to access all the needed arguments of the binding :-) */ if (myContext == null) { LogUtils.throwException(this, "No context specified for IFormCreator", null); } final IValueBinding binding = myContext.addBinding().model(value); createField(binding, style); return binding; } private static final Object FIELDS_COMPOSITE_MARKER = new Object(); /** * Creates the field based on the label and the binding... * * @param binding the binding * @param style additional styles to use for the value Control */ private void createField(final IValueBinding binding, int style) { final Composite fieldComp = getFieldsComposite(); final Label labelControl = myToolkit.createLabel(fieldComp, ""); adapt(labelControl); labelControl.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); final Label placeholderControl = new Label(fieldComp, SWT.NONE); myBindings.add(new BindingDescription(binding, labelControl, placeholderControl, style)); delayContextFinish(); } /** * */ void delayContextFinish() { if (!isTopForm()) { myTopForm.delayContextFinish(); return; } if (myContextListener == null && myContext != null) { myContextListener = new AdapterImpl() { @Override public void notifyChanged(Notification msg) { if (msg.isTouch()) return; if (msg.getFeature() != IUIBindingsPackage.Literals.BINDING_CONTEXT__STATE) return; finish(); } }; myContext.eAdapters().add(myContextListener); } } /** * Check if the last control of {@link #myTop} is a {@link Composite} with the data * {@link #FIELDS_COMPOSITE_MARKER} . * * @return a suitable Composite for the fields */ @Override public Composite getFieldsComposite() { final Control[] children = myTop.getChildren(); if (children.length == 0) return createFieldsComposite(); final Control last = children[children.length - 1]; if (!(last instanceof Composite)) return createFieldsComposite(); final Composite fc = (Composite) last; if (fc.getData() != FIELDS_COMPOSITE_MARKER) return createFieldsComposite(); return fc; } /** * Creates a new suitable fields {@link Composite}. * * @return the a new fields composite */ private Composite createFieldsComposite() { final Composite fc = myToolkit.createComposite(myTop, SWT.NONE); adapt(fc); fc.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP)); final GridLayout layout = new GridLayout(2, false); layout.marginHeight = 0; layout.marginWidth = 0; layout.horizontalSpacing = 10; fc.setLayout(layout); fc.setData(FIELDS_COMPOSITE_MARKER); myToolkit.paintBordersFor(fc); return fc; } /** * Fixes the binding based on the binding description. * * @param description the description of the wanted binding */ private void createFieldReally(BindingDescription description) { String label = description.binding.getLabel(); if (label.length() > 0 && label.charAt(label.length() - 1) != ':') { label += ":"; } description.labelControl.setText(label); description.labelControl .setToolTipText(description.binding.getArgument(Constants.ARG_TOOL_TIP_TEXT, String.class, null)); int style = description.placeholderStyle; if (myReadOnly || !description.binding.isChangeable()) { style |= SWT.READ_ONLY; } final EStructuralFeature sf = description.binding.getModelFeature(); if (sf != null && (!sf.isChangeable() || EcoreUtil.isSuppressedVisibility(sf, EcoreUtil.SET))) { style |= SWT.READ_ONLY; } if (description.binding.getArgument(Constants.ARG_PASSWORD, Boolean.class, false)) { style |= SWT.PASSWORD; } /* * Create the real control/controls */ final Composite parent = description.placeholderControl.getParent(); final Control c = description.binding.createPreferredControl(parent, style | myToolkit.getBorderStyle(), false); adapt(c); /* * Figure out the layout to use */ if (c instanceof Button) { // No layout for buttons } else { final boolean b = (style & SWT.V_SCROLL) == SWT.V_SCROLL; final GridData ld = new GridData(SWT.LEFT, b ? SWT.FILL : SWT.TOP, false, b); ld.widthHint = description.binding.getArgument(Constants.ARG_WIDTH, Integer.class, 200); if (c instanceof StyledText) { ld.heightHint = description.binding.getArgument(Constants.ARG_HEIGHT, Integer.class, 80); ld.widthHint += 7; // missing trim in StyledText compared to Text } if ((style & SWT.MULTI) == SWT.MULTI) { ld.heightHint = description.binding.getArgument(Constants.ARG_HEIGHT, Integer.class, 80); } c.setLayoutData(ld); } myTopForm.decorateControl(c); /* * Assign it as the ui of the binding */ description.binding.ui(c); /* * Replace the old place holder */ c.moveAbove(description.placeholderControl); description.placeholderControl.dispose(); description.placeholderControl = c; } private Set<Control> myDecoratedControls = null; private Listener myDisposeListener; /** * Adds any extra needed decoration to the specified control. * * @param c the control to decorate */ protected void decorateControl(Control c) { if (myDecoratedControls == null) { myDecoratedControls = new HashSet<Control>(); } if (myDecoratedControls.contains(c)) return; if (myDisposeListener == null) { myDisposeListener = new Listener() { @Override public void handleEvent(Event event) { myDecoratedControls.remove(event.widget); } }; } c.addListener(SWT.Dispose, myDisposeListener); if (myFocusListener == null) { myFocusListener = new Listener() { @Override public void handleEvent(Event event) { if (myLastFocusControl == (Control) event.widget) return; myLastFocusControl = (Control) event.widget; // LogUtils.debug(FormCreator.this, ToStringUtils.toPath(myLastFocusControl)); } }; } c.addListener(SWT.FocusIn, myFocusListener); if (c != getContext().getTop()) { decorateControl(c.getParent()); } } @Override public void addConstantField(String label, Object value, int style) { IObservableValue ov; if (value instanceof EEnumLiteral) { ov = WritableValue.withValueType(((EEnumLiteral) value).getEEnum()); } else if (value instanceof EObject) { ov = WritableValue.withValueType(((EObject) value).eClass()); } else { ov = WritableValue.withValueType(String.class); value = value == null ? "<null>" : value.toString(); } ov.setValue(value); addField(ov, style).label(label); } @Override public void setFocus() { if (!isTopForm()) { myTopForm.setFocus(); return; } /* * Set the focus to the first focusable widget of the top composite */ final Control focusControl = Display.getCurrent().getFocusControl(); // Problem! final String ws = TSSWTUtils.toPath(focusControl); final String is = TSSWTUtils.toPath(myLastFocusControl); // LogUtils.debug(this, "FOCUS\nwas: " + ws + "\nlast: " + is); if (myLastFocusControl != null && !myLastFocusControl.isDisposed() && myLastFocusControl != focusControl) { myLastFocusControl.forceFocus(); } else { getTop().setFocus(); } } @Override public Composite addComposite() { return addComposite(true, false); } @Override public Composite addComposite(boolean grabHorizontal, boolean grabVertical) { final Composite c = myToolkit.createComposite(myTop); adapt(c); setLayoutData(c, grabHorizontal, grabVertical); c.setLayout(new FillLayout()); return c; } public void addControl(Control c) { } /** * @deprecated use {@link #addTableCreator(EReference, boolean, int)} */ @Override @Deprecated public ITableCreator addTableCreator(boolean grabHorizontal, int style) { final Composite parent = addComposite(grabHorizontal, false); final ITableCreator table = ITableCreator.Factory.create(getContext(), parent, style); if (isReadOnly()) { table.getBinding().readonly(); } return table; } @Override public ITableCreator addTableCreator(EReference ref, boolean grabHorizontal, int style) { final Composite parent = addComposite(grabHorizontal, false); final ITableCreator table = ITableCreator.Factory.create(getContext(), parent, style, myObservableValue, ref); if (isReadOnly()) { table.getBinding().readonly(); } return table; } @Override public IFormCreator addSection(String label, EObject obj) { return addSection(label, createIOV(myTop, obj)); } @Override public IFormCreator addSection(String label, IObservableValue ov) { return addSection(label, ov, false); } @Override public IFormCreator addSection(String label, boolean grabVertical) { return addSection(label, myObservableValue, grabVertical); } @Override public IFormCreator addSection(String label, IObservableValue ov, boolean grabVertical) { final Section section = myToolkit.createSection(myTop, label != null ? ExpandableComposite.TITLE_BAR | ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED : ExpandableComposite.NO_TITLE); adapt(section); section.clientVerticalSpacing = 6; if (label != null) { section.setText(label); } setLayoutData(section, true, grabVertical); final IFormCreator subForm = subForm(section, ov, 5); setLayoutData(subForm.getTop(), true, grabVertical); section.setClient(subForm.getTop()); return subForm; } @Override public IFormCreator addSection(String label) { return addSection(label, myObservableValue); } @Override public void addSeparator() { final Label label = myToolkit.createLabel(myTop, "", SWT.SEPARATOR | SWT.HORIZONTAL); adapt(label); final TableWrapData ld = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP); label.setLayoutData(ld); } @Override public void addSeparator(Separator type) { int height = SWT.DEFAULT; int style = SWT.NONE; switch (type) { case LINE: style = SWT.SEPARATOR | SWT.HORIZONTAL; break; case MICRO: height = 1; break; case TINY: height = 4; break; case SMALL: height = 8; break; case BIG: height = 16; break; } final Label sep = myToolkit.createLabel(myTop, "", style); adapt(sep); final TableWrapData ld = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP); ld.heightHint = height; sep.setLayoutData(ld); } /** * Creates and returns a new form composite. * * @param parent the parent composite * @param formHeader the header text used for the form * @return the new composite */ protected ScrolledForm createScrolledForm(final Composite parent, String formHeader) { final ScrolledForm form = myToolkit.createScrolledForm(parent); myToolkit.decorateFormHeading(form.getForm()); form.setText(formHeader); adapt(form); /* * See FormUtil.processKey(int keyCode, Control c) * * Without this, the form will scroll as well when a Combo or CCombo is scrolled. */ form.setData("novarrows", Boolean.TRUE); final Composite c = form.getBody(); myToolkit.paintBordersFor(c); if (parent.getLayout() instanceof GridLayout) { final GridLayout g = (GridLayout) parent.getLayout(); final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true, g.numColumns, 1); form.setLayoutData(gd); } else if (parent.getLayout() instanceof TableWrapLayout) { final TableWrapLayout g = (TableWrapLayout) parent.getLayout(); final TableWrapData gd = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB, 1, g.numColumns); form.setLayoutData(gd); } else if (parent.getLayout() instanceof ColumnLayout) { // Nothing! } else { // Nothing? } setTopLayout(c); return form; } /** * Sets the layout of a top composite. * * @param c the composite */ protected void setTopLayout(final Composite c) { final TableWrapLayout l = new TableWrapLayout(); l.verticalSpacing = 10; l.leftMargin = 5; l.rightMargin = 5; l.topMargin = 5; l.bottomMargin = 5; c.setLayout(l); } /** * Creates and returns a new form composite. * * @param parent the parent composite * @return the new composite */ protected Composite createTopComposite(final Composite parent) { final Composite c = myToolkit.createComposite(parent, SWT.NONE); c.setBackground(parent.getBackground()); final Layout l = parent.getLayout(); if (l == null) { parent.setLayout(new FillLayout()); } else if (l instanceof GridLayout) { final GridLayout g = (GridLayout) l; final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true, g.numColumns, 1); c.setLayoutData(gd); } else if (l instanceof TableWrapLayout) { final TableWrapLayout g = (TableWrapLayout) l; final TableWrapData gd = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB, 1, g.numColumns); c.setLayoutData(gd); } else if (l instanceof ColumnLayout) { // Nothing! } else { // Nothing? } myToolkit.paintBordersFor(c); setTopLayout(c); return c; } @Override public void setLayoutData(Control ctl, boolean grabHorizontal, boolean grabVertical) { final TableWrapData ld = new TableWrapData(grabHorizontal ? TableWrapData.FILL_GRAB : TableWrapData.LEFT, grabVertical ? TableWrapData.FILL_GRAB : TableWrapData.TOP); ctl.setLayoutData(ld); } @Override public void finish() { /* * Delegate to the top-level form */ if (!isTopForm()) { myTopForm.finish(); return; } if (myContext == null) return; if (myContextListener != null) { myContext.eAdapters().remove(myContextListener); myContextListener = null; } myTop.setLayoutDeferred(true); final List<BindingDescription> fields = new ArrayList<BindingDescription>(); finishAllfields(fields); myTop.setLayoutDeferred(false); /* * Now left align all the fields */ myTop.layout(true); final Map<Label, Rectangle> labels = new HashMap<Label, Rectangle>(); int right = 0; for (final BindingDescription bd : fields) { final Label label = bd.labelControl; if (label == null) { continue; } final Rectangle rect = label.getDisplay().map(label, getTop(), label.getBounds()); final int myright = rect.x + rect.width; labels.put(label, rect); if (right < myright) { right = myright; } } myTop.setLayoutDeferred(true); for (final Entry<Label, Rectangle> entry : labels.entrySet()) { final Rectangle rect = entry.getValue(); final int wantedWidth = right - rect.x; if (wantedWidth <= rect.width) { continue; } final Label label = entry.getKey(); final Object ld = label.getLayoutData(); if (!(ld instanceof GridData)) { continue; } final GridData gd = (GridData) ld; gd.widthHint = wantedWidth; label.getParent().layout(true); } myTop.setLayoutDeferred(false); myContext.finish(FinishOption.LAZY); // myTop.layout(); // dumpControl(myTop, ""); } private void dumpControl(Control c, String prefix) { System.out.println(prefix + c + ": " + c.getBounds() + " " + c.getLayoutData()); // $codepro.audit.disable // debuggingCode if (c instanceof Composite) { final Composite comp = (Composite) c; System.out.println(prefix + " " + comp.getLayout()); // $codepro.audit.disable // debuggingCode for (final Control ch : comp.getChildren()) { dumpControl(ch, prefix + "| "); } } } /** * Creates all fields in this form and all sub forms. * * @param fields list to which all fields must be added if this form is aligned */ public void finishAllfields(List<BindingDescription> fields) { for (final BindingDescription bd : myBindings) { createFieldReally(bd); if (areFieldsAligned()) { fields.add(bd); } } myBindings.clear(); if (mySubForms != null) { for (final FormCreator c : mySubForms) { c.finishAllfields(fields); } } } @Override public IBindingContext getContext() { return myContext; } @Override public Composite getTop() { return myTop; } @Override public FormToolkit getToolkit() { return myToolkit; } @Override public ScrolledForm getScrolledForm() { return myScrolledForm; } @Override public IValueBinding addField(String spec) { return addField(myObservableValue, spec); } @Override public IValueBinding addField(IObservableValue ov, String spec) { final IObservableValue currentValue = getObservableValue(ov, spec); final Object valueType = ov.getValueType(); EClass valueEClass = null; if (valueType instanceof EClass) { valueEClass = (EClass) valueType; } else if (valueType instanceof EReference) { valueEClass = ((EReference) valueType).getEReferenceType(); } else { LogUtils.throwException(this, "The current value type must be a class or a reference to one: '" + valueType + "'", null); } final List<IBindingSpec> specList = IBindingSpec.Factory.parseSingleSpec(valueEClass, spec, SpecContext.FORM_FIELD); if (specList == null) return null; final Map<String, Object> arguments = specList.get(specList.size() - 1).getArguments(); /* * Figure out the style to use */ int style = SWT.NONE; final String a = (String) arguments.get(Constants.ARG_ALIGNMENT); if (a != null) { if (a.equals("l")) { style |= SWT.LEAD; } else if (a.equals("c")) { style |= SWT.CENTER; } else if (a.equals("r")) { style |= SWT.TRAIL; } else { LogUtils.throwException(this, "Alignment must be one of 'l', 'c' or 'r', got '" + a + "'", null); } } else { style |= UIBindingsUtils.defaultAlignment(currentValue.getValueType()); } /* * Figure out the scrollbars to use */ final String sb = (String) arguments.get(IBindingSpec.SCROLLBARS); if (sb != null) { if (sb.equals("h")) { style |= SWT.H_SCROLL; } else if (sb.equals("v")) { style |= SWT.V_SCROLL; } else if (sb.equals("b")) { style |= SWT.H_SCROLL | SWT.V_SCROLL; } else { LogUtils.throwException(this, "Scrollbars must be one of 'h', 'v' or 'b', got '" + sb + "'", null); } } /* * Fish out read-only */ final Boolean ro = (Boolean) arguments.get(Constants.ARG_READONLY); if (ro != null && ro) { style |= SWT.READ_ONLY; } /* * Fish out multi-line */ final Boolean multi = (Boolean) arguments.get(IBindingSpec.MULTI); if (multi != null && multi) { style |= SWT.MULTI; } /* * Create the field */ final IValueBinding binding = addField(currentValue, style); /* * Add any arguments */ // final Control control = binding.getControl(); for (final Entry<String, Object> entry : arguments.entrySet()) { if (Constants.ARG_ALIGNMENT.equals(entry.getKey())) { // Done above } else { binding.arg(entry.getKey(), entry.getValue()); } } return binding; } /** * The description of a single binding of this form. */ private static class BindingDescription { private BindingDescription(IValueBinding binding, Label labelControl, Control placeholderControl, int placeholderStyle) { super(); this.binding = binding; this.labelControl = labelControl; this.placeholderControl = placeholderControl; this.placeholderStyle = placeholderStyle; } public final IValueBinding binding; public final Label labelControl; public Control placeholderControl; public final int placeholderStyle; } @Override public IFormCreator[] addColumns(boolean... grab) { final List<GridData> data = new ArrayList<GridData>(); for (final boolean e : grab) { data.add(new GridData(SWT.FILL, SWT.FILL, e, false)); } return addColumns(data.toArray(new GridData[data.size()])); } @Override public IFormCreator[] addColumns(GridData... layoutData) { final List<IFormCreator> forms = new ArrayList<IFormCreator>(); boolean grabVertical = false; for (final GridData gd : layoutData) { if (gd.grabExcessVerticalSpace) { grabVertical = true; } } final Composite parent = addComposite(true, grabVertical); final GridLayout l = new GridLayout(layoutData.length, false); l.marginHeight = 0; l.marginWidth = 0; parent.setLayout(l); for (final GridData gd : layoutData) { final Composite child = new Composite(parent, SWT.NONE); child.setBackground(parent.getBackground()); child.setMenu(parent.getMenu()); child.setLayoutData(gd); final IFormCreator s = subForm(child); s.setFieldsAligned(false); forms.add(s); } return forms.toArray(new IFormCreator[forms.size()]); } @Override public IFormChooser addFormChooser(IValueBinding discriminant) { final IObservableValue ov = discriminant.getModelObservableValue(); discriminant.assertTrue(ov != null, "Discriminant not single valued"); return IFormChooser.Factory.create(getContext(), ov, addComposite()); } @Override public IFormChooser addFormChooser(IObservableValue discriminant) { return IFormChooser.Factory.create(getContext(), discriminant, addComposite()); } @Override public IObservableValue getObservableValue(String spec) { return getObservableValue(myObservableValue, spec); } @Override public IObservableValue getObservableValue(IObservableValue ov, String spec) { final Object valueType = ov.getValueType(); EClass valueEClass = null; if (valueType instanceof EClass) { valueEClass = (EClass) valueType; } else if (valueType instanceof EReference) { valueEClass = ((EReference) valueType).getEReferenceType(); } else { LogUtils.throwException(this, "The current value type must be a class or a reference to one: '" + valueType + "'", null); } final List<IBindingSpec> specList = IBindingSpec.Factory.parseSingleSpec(valueEClass, spec, SpecContext.FORM_FIELD); if (specList == null) return null; if (myObservables == null) { myObservables = new HashMap<IObservableValue, Map<EStructuralFeature, IObservableValue>>(); } IObservableValue currentValue = ov; for (final IBindingSpec s : specList) { Map<EStructuralFeature, IObservableValue> features = myObservables.get(currentValue); if (features == null) { features = new HashMap<EStructuralFeature, IObservableValue>(); myObservables.put(currentValue, features); } switch (s.getType()) { case NONE: case ROW_NO: case ROW_ELEMENT: LogUtils.throwException(this, "Spec element type not supported in form: '" + s.getType() + "'", null); return null; case FEATURE: case KEY_VALUE: break; } final EStructuralFeature feature = s.getFeature(); IObservableValue value = features.get(feature); if (value == null) { switch (s.getType()) { default: break; case FEATURE: value = UIBindingsEMFObservables.observeDetailValue(getContext().getEditingDomain(), currentValue, feature); break; case KEY_VALUE: value = new EListKeyedElementObservableValue<EObject>(getContext().getEditingDomain(), currentValue, (EReference) feature, s.getKeyFeature(), s.getKeyValue(), s.getValueFeature()); break; } features.put(feature, value); } currentValue = value; } return currentValue; } private boolean myFieldsAligned = true; @Override public boolean areFieldsAligned() { return myFieldsAligned; } @Override public void setFieldsAligned(boolean align) { myFieldsAligned = align; } @Override public Section getSection() { return mySection; } @Override public void addObjectMessages() { addObjectMessages(getObservableValue()); } @Override public void addObjectMessages(String spec) { addObjectMessages(getObservableValue(spec)); } private Set<IObservableValue> myObjectMessageObjects = null; @Override public void addObjectMessages(final IObservableValue value) { if (!isTopForm()) { myTopForm.addObjectMessages(value); return; } if (myObjectMessageObjects == null) { myObjectMessageObjects = new HashSet<IObservableValue>(); } if (myObjectMessageObjects.contains(value)) return; final IValueBinding binding = myContext.addBinding(); binding.model(value).ui(new VirtualUIAttribute(String.class)).arg(Constants.ARG_VALUE_OBJECT_MESSAGES, true); myObjectMessageObjects.add(value); } @Override public void addFinalizer(final Runnable runnable) { myTopForm.myContext.getFinalizers().add(new IBindingContextFinalizer() { @Override public void run(IBindingContext context) { try { runnable.run(); } finally { myTopForm.myContext.getFinalizers().remove(this); } } }); } @Override public void addCommandLink(String linkText) { final Link link = new Link(getFieldsComposite(), SWT.WRAP); link.setText(linkText); link.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { widgetDefaultSelected(e); } @Override public void widgetDefaultSelected(SelectionEvent e) { final IWorkbench workbench = PlatformUI.getWorkbench(); final ICommandService cs = (ICommandService) workbench.getService(ICommandService.class); final IHandlerService hs = (IHandlerService) workbench.getService(IHandlerService.class); try { final ParameterizedCommand command = cs.deserialize(e.text); hs.executeCommand(command, null); } catch (final Exception ex) { LogUtils.error(this, ex); } } }); } }