/*******************************************************************************
* 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.contextAdapters;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.databinding.observable.list.IListChangeListener;
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
import org.eclipse.jface.dialogs.IMessageProvider;
import com.rcpcompany.uibindings.IBindingContext;
import com.rcpcompany.uibindings.IBindingMessage;
import com.rcpcompany.uibindings.internal.bindingMessages.IContextMessageProvider;
import com.rcpcompany.uibindings.internal.utils.AbstractContextMonitor;
import com.rcpcompany.uibindings.utils.IManagerRunnable;
/**
* Message decorator support for a single {@link IBindingContext}.
* <p>
* This class controls the message decorator of all bindings of the context as well as the
* form/wizard/whatever that hosts the binding context.
*
* @author Tonny Madsen, The RCP Company
*/
public class ContextMessageDecorator extends AbstractContextMonitor {
/**
* The {@link IContextMessageDecoratorAdapter adapter} for this decorator context.
*/
private IContextMessageDecoratorAdapter myAdapter;
/**
* Returns the adapter of this decorator.
*
* @return the adapter
*/
public IContextMessageDecoratorAdapter getAdapter() {
return myAdapter;
}
/**
* Returns a list of all messages for this decorator.
*
* @return the messages
*/
public List<IBindingMessage> getMessages() {
return myMessages;
}
/**
* Constructs and returns a new decorator.
*
* @param context the context
* @param adapter the adapter for the actual context form
*/
public ContextMessageDecorator(IBindingContext context, IContextMessageDecoratorAdapter adapter) {
super(context);
myAdapter = adapter;
getContext().registerService(this);
init();
}
@Override
public void dispose() {
getContext().unregisterService(this);
super.dispose();
if (myAdapter != null) {
myAdapter.dispose();
myAdapter = null;
}
}
// @Override
// protected void bindingAdded(IBinding binding) {
// super.bindingAdded(binding);
//
// /*
// * The following is a little complex, but...
// *
// * For each viewer binding a ViewerBindingMessageDecorator is created and associated with the
// binding as a
// * service.
// *
// * For each value binding, it is checked whether it has an ARG_CELL_KEY argument. This, if
// present, is the cell
// * that constructed the binding - it is handled in ColumnBindingCellInformationImpl.init(...).
// *
// * If the cell is found, one widget decoration creation factory is used that will create a
// sub-cell decoration
// * of the ViewerBindingMessageDecorator.
// *
// * If it is not found an ordinary ControlDecoration is used.
// */
// IContextMessageProvider p = null;
// if (binding instanceof IViewerBinding) {
// final IViewerBinding viewer = (IViewerBinding) binding;
// new ViewerBindingMessageDecorator(viewer);
// } else if (binding instanceof IValueBinding) {
// final IValueBinding vb = (IValueBinding) binding;
// final IColumnBindingCellInformation cell = vb.getCell();
// final Control control = vb.getControl();
// if (cell != null) {
// final IViewerBinding viewer = cell.getColumn().getViewerBinding();
// final ViewerBindingMessageDecorator decorator =
// viewer.getService(ViewerBindingMessageDecorator.class);
// final IWidgetDecorationFactory factory = new IWidgetDecorationFactory() {
// @Override
// public IWidgetDecoration create(int position) {
// return decorator.addCellDecoration(cell, position);
// }
// };
// p = new ValueBindingMessageImageDecorator(vb, factory, false);
// } else if (control != null) {
// final IWidgetDecorationFactory factory = new IWidgetDecorationFactory() {
// @Override
// public IWidgetDecoration create(int position) {
// return new ControlWidgetDecoration(control, position, getContext().getTop());
// }
// };
// p = new ValueBindingMessageImageDecorator(vb, factory, true);
// } else {
// /*
// * No need for decoration factory as we don't have a control....
// */
// p = new ValueBindingMessageImageDecorator(vb, null, false);
// }
//
// if (p != null) {
// addMessageProvider(p);
// }
// }
// }
/**
* Adds a new message provider to this context decorator.
* <p>
* Adds listener to monitor the changes in the messages for the provider
*
* @param provider the provider
*/
public void addMessageProvider(IContextMessageProvider provider) {
provider.getMessages().addListChangeListener(myProviderChangeListener);
}
/**
* Removes a message provider.
*
* @param provider the provider
*/
public void removeMessageProvider(IContextMessageProvider provider) {
provider.getMessages().removeListChangeListener(myProviderChangeListener);
}
// @Override
// protected void bindingRemoved(IBinding binding) {
// super.bindingRemoved(binding);
//
// final IContextMessageProvider p = binding.getService(IContextMessageProvider.class);
// if (p != null) {
// removeMessageProvider(p);
// }
// }
/**
* Listener that monitors the messages providers for any changes in the messages.
*/
private final IListChangeListener myProviderChangeListener = new IListChangeListener() {
private final ListDiffVisitor myVisitor = new ListDiffVisitor() {
@Override
public void handleRemove(int index, Object element) {
getMessages().remove(element);
}
@Override
public void handleAdd(int index, Object element) {
getMessages().add((IBindingMessage) element);
}
};
@Override
public void handleListChange(ListChangeEvent event) {
event.diff.accept(myVisitor);
updateMessages();
}
};
/**
* The list of current messages.
*/
private final List<IBindingMessage> myMessages = new ArrayList<IBindingMessage>();
/**
* {@link Runnable} for the next delayed update up messages in the adapter.
*/
private final Runnable myUpdateMessagesRunnable = new Runnable() {
@Override
public void run() {
if (myAdapter != null) {
/*
* Weed out any superseded messages.
*/
final List<IBindingMessage> ml = new ArrayList<IBindingMessage>(getMessages());
if (ml.size() > 0) {
final IBindingMessage[] ma = ml.toArray(new IBindingMessage[ml.size()]);
for (int i = 0; i < ma.length; i++) {
final IBindingMessage a = ma[i];
for (int j = i + 1; j < ma.length; j++) {
final IBindingMessage b = ma[j];
if (a.supersedes(b)) {
ml.remove(b);
continue;
}
if (b.supersedes(a)) {
ml.remove(a);
break;
}
}
}
}
final int type = ml.size() > 0 ? ml.get(0).getMessageType() : IMessageProvider.NONE;
myAdapter.update(ml, type == IMessageProvider.ERROR, type);
}
}
};
/**
* Updates the messages for the context.
*/
protected void updateMessages() {
IManagerRunnable.Factory.asyncExec("update", this, myUpdateMessagesRunnable);
}
}