blob: 0112e382749bd13c72316cb55232e403c440c8b4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 Matthew Hall 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:
* Matthew Hall - initial API and implementation (bug 268472
******************************************************************************/
package org.eclipse.jface.internal.databinding.provisional.fieldassist;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.databinding.ValidationStatusProvider;
import org.eclipse.core.databinding.observable.DisposeEvent;
import org.eclipse.core.databinding.observable.IDecoratingObservable;
import org.eclipse.core.databinding.observable.IDisposeListener;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.IObserving;
import org.eclipse.core.databinding.observable.list.IListChangeListener;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.databinding.swt.ISWTObservable;
import org.eclipse.jface.databinding.viewers.IViewerObservable;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Widget;
/**
* <b>EXPERIMENTAL</b>: This class is not API. It is experimental and subject to
* arbitrary change, including removal. Please provide feedback if you would
* like to see this become API.
* <p>
* Decorates the underlying controls of the target observables of a
* {@link ValidationStatusProvider} with {@link ControlDecoration}s mirroring
* the current validation status. Only those target observables which implement
* {@link ISWTObservable} or {@link IViewerObservable} are decorated.
*
* @since 1.3
*/
public class ControlDecorationSupport {
/**
* <b>EXPERIMENTAL</b>: This method is not API. It is experimental and
* subject to arbitrary change, including removal. Please provide feedback
* if you would like to see this become API.
*
* @param validationStatusProvider
* @param position
* @return .
*/
public static ControlDecorationSupport create(
ValidationStatusProvider validationStatusProvider, int position) {
return create(validationStatusProvider, position, null,
new ControlDecorationUpdater());
}
/**
* <b>EXPERIMENTAL</b>: This method is not API. It is experimental and
* subject to arbitrary change, including removal. Please provide feedback
* if you would like to see this become API.
*
* @param validationStatusProvider
* @param position
* @param composite
* @return .
*/
public static ControlDecorationSupport create(
ValidationStatusProvider validationStatusProvider, int position,
Composite composite) {
return create(validationStatusProvider, position, composite,
new ControlDecorationUpdater());
}
/**
* <b>EXPERIMENTAL</b>: This method is not API. It is experimental and
* subject to arbitrary change, including removal. Please provide feedback
* if you would like to see this become API.
*
* @param validationStatusProvider
* @param position
* @param composite
* @param updater
* @return .
*/
public static ControlDecorationSupport create(
ValidationStatusProvider validationStatusProvider, int position,
Composite composite, ControlDecorationUpdater updater) {
return new ControlDecorationSupport(validationStatusProvider, position,
composite, updater);
}
private final int position;
private final Composite composite;
private final ControlDecorationUpdater updater;
private IObservableValue validationStatus;
private IObservableList targets;
private IDisposeListener disposeListener = new IDisposeListener() {
public void handleDispose(DisposeEvent staleEvent) {
dispose();
}
};
private IValueChangeListener statusChangeListener = new IValueChangeListener() {
public void handleValueChange(ValueChangeEvent event) {
statusChanged((IStatus) validationStatus.getValue());
}
};
private IListChangeListener targetsChangeListener = new IListChangeListener() {
public void handleListChange(ListChangeEvent event) {
event.diff.accept(new ListDiffVisitor() {
public void handleAdd(int index, Object element) {
targetAdded((IObservable) element);
}
public void handleRemove(int index, Object element) {
targetRemoved((IObservable) element);
}
});
statusChanged((IStatus) validationStatus.getValue());
}
};
private static class TargetDecoration {
public final IObservable target;
public final ControlDecoration decoration;
TargetDecoration(IObservable target, ControlDecoration decoration) {
this.target = target;
this.decoration = decoration;
}
}
private List targetDecorations;
private ControlDecorationSupport(
ValidationStatusProvider validationStatusProvider, int position,
Composite composite, ControlDecorationUpdater updater) {
this.position = position;
this.composite = composite;
this.updater = updater;
this.validationStatus = validationStatusProvider.getValidationStatus();
Assert.isTrue(!this.validationStatus.isDisposed());
this.targets = validationStatusProvider.getTargets();
Assert.isTrue(!this.targets.isDisposed());
this.targetDecorations = new ArrayList();
validationStatus.addDisposeListener(disposeListener);
validationStatus.addValueChangeListener(statusChangeListener);
targets.addDisposeListener(disposeListener);
targets.addListChangeListener(targetsChangeListener);
for (Iterator it = targets.iterator(); it.hasNext();)
targetAdded((IObservable) it.next());
statusChanged((IStatus) validationStatus.getValue());
}
private void targetAdded(IObservable target) {
Control control = findControl(target);
if (control != null)
targetDecorations.add(new TargetDecoration(target,
new ControlDecoration(control, position, composite)));
}
private void targetRemoved(IObservable target) {
for (Iterator it = targetDecorations.iterator(); it.hasNext();) {
TargetDecoration targetDecoration = (TargetDecoration) it.next();
if (targetDecoration.target == target) {
targetDecoration.decoration.dispose();
it.remove();
}
}
}
private Control findControl(IObservable target) {
if (target instanceof ISWTObservable) {
Widget widget = ((ISWTObservable) target).getWidget();
if (widget instanceof Control)
return (Control) widget;
}
if (target instanceof IViewerObservable) {
Viewer viewer = ((IViewerObservable) target).getViewer();
return viewer.getControl();
}
if (target instanceof IDecoratingObservable) {
IObservable decorated = ((IDecoratingObservable) target)
.getDecorated();
Control control = findControl(decorated);
if (control != null)
return control;
}
if (target instanceof IObserving) {
Object observed = ((IObserving) target).getObserved();
if (observed instanceof IObservable)
return findControl((IObservable) observed);
}
return null;
}
private void statusChanged(IStatus status) {
for (Iterator it = targetDecorations.iterator(); it.hasNext();) {
TargetDecoration targetDecoration = (TargetDecoration) it.next();
ControlDecoration decoration = targetDecoration.decoration;
updater.update(decoration, status);
}
}
/**
* <b>EXPERIMENTAL</b>: This method is not API. It is experimental and
* subject to arbitrary change, including removal. Please provide feedback
* if you would like to see this become API.
* <p>
* Disposes this ControlDecorationSupport, including all control decorations
* managed by it. A ControlDecorationSupport is automatically disposed when
* its target ValidationStatusProvider is disposed.
*/
public void dispose() {
if (validationStatus != null) {
validationStatus.removeDisposeListener(disposeListener);
validationStatus.removeValueChangeListener(statusChangeListener);
validationStatus = null;
}
if (targets != null) {
targets.removeDisposeListener(disposeListener);
targets.removeListChangeListener(targetsChangeListener);
targets = null;
}
disposeListener = null;
statusChangeListener = null;
targetsChangeListener = null;
if (targetDecorations != null) {
for (Iterator it = targetDecorations.iterator(); it.hasNext();) {
TargetDecoration targetDecoration = (TargetDecoration) it
.next();
targetDecoration.decoration.dispose();
}
targetDecorations.clear();
targetDecorations = null;
}
}
}