/*******************************************************************************
* 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);
}
}
});
}
}