/*******************************************************************************
* Copyright (c) 2010 Philipp Kursawe.
* 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:
* Philipp Kursawe (phil.kursawe@gmail.com) - initial API and implementation
******************************************************************************/
package eclipseutils.jface.databinding;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.beans.BeansObservables;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.databinding.dialog.TitleAreaDialogSupport;
import org.eclipse.jface.databinding.swt.ISWTObservable;
import org.eclipse.jface.databinding.viewers.IViewerObservable;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.internal.databinding.provisional.fieldassist.ControlDecorationSupport;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;
import eclipseutils.jface.databinding.FieldOptions.ControlCustomizer;
import eclipseutils.jface.databinding.editors.AbstractEditorCreator;
import eclipseutils.jface.databinding.editors.BooleanEditorCreator;
import eclipseutils.jface.databinding.editors.ComboEditorCreator;
import eclipseutils.jface.databinding.editors.TextEditorCreator;
/**
* An abstract implementation of the <code>Builder</code> interface.
*
* <p>
* Subclasses must provide layout methods.
*
* @author <a href="mailto:phil.kursawe@gmail.com">Philipp Kursawe</a>
*
*/
@SuppressWarnings("restriction")
public abstract class AbstractBuilder implements Builder {
static final Map<Object, EditorCreator> creators = new HashMap<Object, EditorCreator>();
static {
creators.put(String.class, new TextEditorCreator());
creators.put(Boolean.class, BooleanEditorCreator.getInstance());
creators.put(boolean.class, BooleanEditorCreator.getInstance());
creators.put(Collection.class, new ComboEditorCreator());
}
private final Composite parent;
private final Object bean;
private final int targetToModelPolicy;
private final ControlToolkit creator;
private int fields;
private int readOnlyFlag;
protected final DataBindingContext ctx;
private boolean isBean = true;
/**
* Creates the builder using the SWTControlCreator.
*
* @param parent
* @param bean
* @param targetToModelPolicy
*/
public AbstractBuilder(final Composite parent, final Object bean,
final int targetToModelPolicy) {
this(SWTControlToolkit.getInstance(), parent, bean, targetToModelPolicy);
}
/**
* @param creator
* @param parent
* @param bean
* @param targetToModelPolicy
*/
public AbstractBuilder(final ControlToolkit creator,
final Composite parent, final Object bean,
final int targetToModelPolicy) {
ctx = new DataBindingContext();
parent.addDisposeListener(new DisposeListener() {
public void widgetDisposed(final DisposeEvent e) {
ctx.dispose();
}
});
this.parent = parent;
this.bean = bean;
this.targetToModelPolicy = targetToModelPolicy;
this.creator = creator;
}
protected IObservableValue getPropertyObservable(String property) {
if (isBean ) {
return BeansObservables.observeValue(bean, property);
}
return PojoObservables.observeValue(bean, property);
}
public Builder field(final String property, final FieldOptions fieldOptions) {
final IObservableValue beanValueProperty = getPropertyObservable(property);
if (beanValueProperty == null) {
return this;
}
final Object type = fieldOptions!=null && fieldOptions.getType() != null ? fieldOptions
.getType() : beanValueProperty.getValueType();
final EditorCreator editorCreator = creators.get(type);
if (editorCreator != null) {
final Label label = editorCreator.hasLabel() ? creator.createLabel(
parent, LocalizationHelper.getLabel(bean, property) + ":", //$NON-NLS-1$
SWT.LEFT) : null;
if (label != null) {
AbstractEditorCreator.setToolTip(label, bean, property);
}
final IObservableValue control = editorCreator.create(creator,
parent, bean, property, getStyle(), fieldOptions);
if (control != null) {
++fields;
UpdateValueStrategy targetToModel = fieldOptions != null ? fieldOptions
.getTargetToModel()
: null;
if (targetToModel == null) {
targetToModel = new UpdateValueStrategy(targetToModelPolicy);
// Set also any validator
if (fieldOptions != null) {
targetToModel.setAfterConvertValidator(fieldOptions
.getValidator());
}
}
bind(control, beanValueProperty, targetToModel);
final Control controlWidget = getControl(control);
if (controlWidget != null && fieldOptions != null) {
addContentProposal(controlWidget, fieldOptions);
final ControlCustomizer controlCustomizer = fieldOptions
.getControlCustomizer();
if (controlCustomizer != null) {
controlCustomizer.customizeControl(controlWidget,
control, fieldOptions);
}
}
if (controlWidget != null) {
if (label == null) {
AbstractEditorCreator.setToolTip(controlWidget, bean, property);
}
applyLayout(label, controlWidget);
}
} else {
label.dispose();
}
}
return this;
}
private int getStyle() {
return readOnlyFlag;
}
public Builder control(ControlCreator creator) {
Control control = creator.create(parent, bean);
if (control != null) {
applyLayout(null, control);
}
return this;
}
protected abstract void applyLayout(Label label, Control controlWidget);
private Control getControl(final IObservableValue observableValue) {
if (observableValue instanceof ISWTObservable) {
final Widget widget = ((ISWTObservable) observableValue)
.getWidget();
if (widget instanceof Control) {
return (Control) widget;
}
}
if (observableValue instanceof IViewerObservable) {
return ((IViewerObservable)observableValue).getViewer().getControl();
}
return null;
}
private void addContentProposal(final Control control,
final FieldOptions fieldOptions) {
if (!(control instanceof Text)) {
return;
}
final IContentProposalProvider proposalProvider = fieldOptions
.getProposalProvider();
if (proposalProvider != null) {
final Text text = (Text) control;
final KeyStroke proposalKeyStroke = fieldOptions
.getProposalKeyStroke();
final ContentProposalAdapter adapter = new ContentProposalAdapter(
text, new TextContentAdapter(), proposalProvider,
proposalKeyStroke, fieldOptions
.getAutoActivationnCharacters());
adapter.setPropagateKeys(true);
adapter.setLabelProvider(fieldOptions.getProposalLabelProvider());
adapter.setProposalAcceptanceStyle(fieldOptions
.getProposalAcceptanceStyle());
final FieldDecoration decoration = FieldDecorationRegistry
.getDefault().getFieldDecoration(
FieldDecorationRegistry.DEC_CONTENT_PROPOSAL);
final ControlDecoration dec = new ControlDecoration(text,
SWT.BOTTOM | SWT.LEFT);
dec.setImage(decoration.getImage());
dec.setShowOnlyOnFocus(true);
dec.setDescriptionText(NLS.bind(fieldOptions
.getProposalDescription(), proposalKeyStroke));
}
}
public Builder field(final String property) {
return field(property, null);
}
public Builder newLine() {
return this;
}
protected Composite getParent() {
return parent;
}
protected Object getBean() {
return bean;
}
protected void bind(final IObservableValue target,
final IObservableValue model,
final UpdateValueStrategy targetToModel) {
final Binding binding = ctx.bindValue(target, model, targetToModel,
null);
ControlDecorationSupport.create(binding, SWT.TOP | SWT.LEFT);
}
public Builder updateModels() {
ctx.updateModels();
return this;
}
public Builder addDialogSupport(final TitleAreaDialog dialog,
final IObservableValue target) {
final AggregateValidationStatus status = new AggregateValidationStatus(
ctx, AggregateValidationStatus.MAX_SEVERITY);
if (target != null) {
status.addChangeListener(new IChangeListener() {
public void handleChange(final ChangeEvent event) {
final Object value = status.getValue();
if (value instanceof IStatus) {
target.setValue(value);
}
}
});
}
TitleAreaDialogSupport.create(dialog, ctx);
return this;
}
public Builder readOnly(boolean readOnly) {
readOnlyFlag = readOnly ? SWT.READ_ONLY : 0;
return this;
}
public Builder selection(String property, Collection<?> items) {
return field(property, new FieldOptions(items));
}
public Builder selection(String property, FieldOptions options) {
return field(property, options);
}
}