/**
* Copyright 2004-2016 Riccardo Solmi. All rights reserved.
* This file is part of the Whole Platform.
*
* The Whole Platform is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Whole Platform is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whole.lang.e4.ui.viewers;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.LightweightEditDomain;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.requests.LocationRequest;
import org.eclipse.gef.ui.parts.TreeViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.Widget;
import org.whole.lang.bindings.BindingManagerFactory;
import org.whole.lang.bindings.IBindingManager;
import org.whole.lang.commons.model.RootFragment;
import org.whole.lang.commons.model.impl.LazyContainmentRootFragmentImpl;
import org.whole.lang.e4.ui.util.E4Utils;
import org.whole.lang.model.ICompoundModel;
import org.whole.lang.model.IEntity;
import org.whole.lang.reflect.FeatureDescriptorEnum;
import org.whole.lang.reflect.ILanguageKit;
import org.whole.lang.reflect.ReflectionFactory;
import org.whole.lang.ui.editparts.EntityPartListener;
import org.whole.lang.ui.editparts.IEntityPart;
import org.whole.lang.ui.editparts.IPartFocusListener;
import org.whole.lang.ui.editparts.ITreeEntityPart;
import org.whole.lang.ui.editparts.ModelObserver;
import org.whole.lang.ui.editparts.OutlineViewEditPartFactory;
import org.whole.lang.ui.input.IModelInput;
import org.whole.lang.ui.input.IModelInputListener;
import org.whole.lang.ui.keys.AbstractKeyHandler;
import org.whole.lang.ui.viewers.EntityEditDomain;
import org.whole.lang.ui.viewers.IEntityPartViewer;
import org.whole.lang.util.EntityUtils;
import org.whole.langs.core.CoreMetaModelsDeployer;
/**
* @author Enrico Persiani
*/
@SuppressWarnings("unchecked")
public class E4TreeViewer extends TreeViewer implements IEntityPartViewer {
@Inject IEclipseContext context;
@Inject @Named("parent") Composite parent;
@Inject @Optional EntityEditDomain domain;
private ModelObserver modelObserver;
private List<IPartFocusListener> partFocusListeners;
private List<IModelInputListener> modelInputListeners;
private Set<String> referencedResources;
public E4TreeViewer() {
setEditPartFactory(new OutlineViewEditPartFactory());
}
@PostConstruct
public void initialize() {
if (domain == null)
domain = new EntityEditDomain();
partFocusListeners = new ArrayList<IPartFocusListener>();
modelInputListeners = new ArrayList<IModelInputListener>();
referencedResources = new HashSet<String>();
createControl(parent);
domain.addViewer(this);
}
@Override
public Control createControl(Composite parent) {
Tree tree = (Tree) super.createControl(parent);
tree.addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick(MouseEvent event) {
Point point = new Point(event.x, event.y);
EditPart editPart = findObjectAt(point);
if (editPart != null) {
LocationRequest request = new LocationRequest(RequestConstants.REQ_DIRECT_EDIT);
request.setLocation(point);
editPart.performRequest(request);
}
}
});
return tree;
}
@Override
protected void refreshDropTargetAdapter() {
if (getControl() == null)
return;
if (getDelegatingDropAdapter().isEmpty())
setDropTarget(null);
else {
if (getDropTarget() == null)
setDropTarget(new DropTarget(getControl(), DND.DROP_MOVE
| DND.DROP_COPY | DND.DROP_LINK));
//FIXME workaround for Eclipse bug
if (getDropTarget().getControl() == null)
return;
getDropTarget().setTransfer(
getDelegatingDropAdapter().getTransfers());
}
}
@Override
public void deselect(EditPart editpart) {
//FIXME workaround for Eclipse bug
Widget widget = ((ITreeEntityPart) editpart).getWidget();
if (widget != null && !widget.isDisposed())
super.deselect(editpart);
}
@Override
public void setSelection(ISelection newSelection, boolean propagate) {
// the underlying TreeViewer keeps an unordered selection
// at least, we have to preserve the old child ordering
List<IEntityPart> previousParts = new ArrayList<>(((IStructuredSelection) getSelection()).toList());
List<IEntityPart> newParts = new ArrayList<>(((IStructuredSelection) newSelection).toList());
previousParts.retainAll(newParts);
newParts.removeAll(previousParts);
List<IEntityPart> orderedParts = new LinkedList<>(newParts);
orderedParts.addAll(previousParts);
super.setSelection(new StructuredSelection(orderedParts), propagate);
}
@Override
public void setEditDomain(LightweightEditDomain editdomain) {
if (getControl() != null)
super.setEditDomain(editdomain);
}
// Begin Block Shared With E4GraphicalViewer
public IEclipseContext getContext() {
return context;
}
public IBindingManager getContextBindings() {
Object selection = getContext().get(ESelectionService.class).getSelection();
if (selection instanceof IBindingManager)
return (IBindingManager) selection;
else {
IBindingManager bm = BindingManagerFactory.instance.createBindingManager();
E4Utils.defineResourceBindings(bm, modelInput);
return bm;
}
}
public Set<String> getReferencedResources() {
return referencedResources;
}
protected boolean executable = true;
public boolean isOperationExecutable() {
return executable;
}
public void setOperationExecutable(boolean executable) {
this.executable = executable;
}
public CommandStack getCommandStack() {
return getEditDomain().getCommandStack();
}
public boolean isDirty() {
return getCommandStack().isDirty();
}
@Override
public EntityEditDomain getEditDomain() {
return (EntityEditDomain) super.getEditDomain();
}
public LightweightEditDomain linkEditDomain(IEntityPartViewer viewer) {
EntityEditDomain editDomain = getEditDomain();
if (editDomain != null)
editDomain.removeViewer(this);
editDomain = viewer.getEditDomain();
editDomain.addViewer(this);
return editDomain;
}
public IEntity getEntityContents() {
RootFragment modelEntity = ((IEntityPart) getContents()).getModelEntity();
return modelEntity.getRootEntity().wGetAdaptee(false);
}
public void setEntityContents(IEntity entity) {
setContents(entity);
flush();
ReflectionFactory.getHistoryManager(entity).setHistoryEnabled(true);
getCommandStack().flush();
}
protected IModelInput modelInput = null;
public void setContents(IModelInput modelInput, IEntity defaultContents) {
if (modelInput != null) {
IModelInput oldModelInput = this.modelInput;
this.modelInput = modelInput;
try {
setEntityContents(this.modelInput.readModel());
fireModelInputChanged(oldModelInput, this.modelInput);
} catch (Exception e) {
String errorMessage = String.format("Unable to open \"%s\" using \"%s\" persistence kit",
modelInput.getName(), modelInput.getPersistenceKit().getDescription());
setEntityContents(E4Utils.createErrorStatusContents(errorMessage, e.getLocalizedMessage()));
}
} else
setEntityContents(defaultContents);
}
public void reloadContents() {
setContents(modelInput, null);
}
protected RootFragment wrapContents(IEntity entity) {
return entity instanceof RootFragment ? (RootFragment) entity :
new LazyContainmentRootFragmentImpl(entity);
}
public void setContents(Object contents) {
IEntity root = (IEntity) contents;
//FIXME workaround since selection is not properly reset
if (getContents() != null)
setSelection(StructuredSelection.EMPTY);
super.setContents(wrapContents(root));
updateModelObserver(root);
}
public boolean hasContents() {
return !CoreMetaModelsDeployer.STATUS_URI.equals(getEntityContents().wGetLanguageKit().getURI());
}
@SuppressWarnings("unchecked")
public Map<IEntity, IEntityPart> getEditPartRegistry() {
return super.getEditPartRegistry();
}
public IEntityPart getFocusEntityPart() {
return (IEntityPart) getFocusEditPart();
}
public AbstractKeyHandler getKeyHandler() {
return (AbstractKeyHandler) super.getKeyHandler();
}
public void setKeyHandler(AbstractKeyHandler handler) {
super.setKeyHandler(handler);
}
public void select(IEntity entity) {
select(entity, true);
}
public void select(IEntity entity, boolean propagate) {
IEntityPart entityPart = ModelObserver.getObserver(entity, getEditPartRegistry());
if (entityPart != null && entityPart.isSelectable())
select(entityPart, propagate);
}
@Override
public void select(List<? extends IEntity> entities) {
select(entities, true);
}
@Override
public void select(List<? extends IEntity> entities, boolean propagate) {
List<IEntityPart> entityParts = new ArrayList<>();
for (int i = entities.size()-1; i >= 0; i--) {
IEntityPart entityPart = ModelObserver.getObserver(entities.get(i), getEditPartRegistry());
if (entityPart != null && entityPart.isSelectable())
entityParts.add(entityPart);
}
setSelection(new StructuredSelection(entityParts), propagate);
}
public void reveal(IEntity entity) {
IEntityPart entityPart = ModelObserver.getObserver(entity, getEditPartRegistry());
reveal(entityPart);
}
public void selectAndReveal(IEntity entity) {
selectAndReveal(entity, true);
}
public void selectAndReveal(List<? extends IEntity> entities) {
selectAndReveal(entities, true);
}
protected void updateModelObserver(IEntity entity) {
ICompoundModel model = entity.wGetModel().getCompoundModel();
if (modelObserver != null && modelObserver.getModel() != model) {
modelObserver.dispose();
modelObserver = null;
}
if (modelObserver == null)
modelObserver = new ModelObserver(model, this);
}
public void addEntityPartListener(EntityPartListener listener) {
modelObserver.addEntityPartListener(listener);
}
public void removeEntityPartListener(EntityPartListener listener) {
modelObserver.removeEntityPartListener(listener);
}
public void dispose() {
if (modelObserver != null)
modelObserver.dispose();
}
protected void firePartFocusChanged(IEntityPart oldPart, IEntityPart newPart) {
for (IPartFocusListener listener : partFocusListeners)
listener.focusChanged(oldPart, newPart);
}
public void addPartFocusListener(IPartFocusListener listener) {
partFocusListeners.add(listener);
}
public void removePartFocusListener(IPartFocusListener listener) {
partFocusListeners.remove(listener);
}
protected void fireModelInputChanged(IModelInput oldModelInput, IModelInput newModelInput) {
for (IModelInputListener listener : modelInputListeners)
listener.modelInputChanged(oldModelInput, newModelInput);
}
public void addModelInputListener(IModelInputListener listener) {
modelInputListeners.add(listener);
}
public void removeModelInputListener(IModelInputListener listener) {
modelInputListeners.remove(listener);
}
public void rebuildNotation() {
RootFragment rootFragment = (RootFragment) getContents().getModel();
//FIXME should be rebuildNotation(rootFragment)
rebuildNotation(rootFragment.getRootEntity().wGetAdaptee(true));
}
// End Block Shared With E4GraphicalViewer
public void selectAndReveal(IEntity entity, boolean propagate) {
IEntityPart entityPart = ModelObserver.getObserver(entity, getEditPartRegistry());
if (entityPart != null) {
reveal(entityPart);
select(entityPart, propagate);
}
}
public void selectAndReveal(List<? extends IEntity> entities, boolean propagate) {
if (!entities.isEmpty())
reveal(entities.get(entities.size()-1));
select(entities, propagate);
}
public void rebuildNotation(IEntity entity) {
IEntityPart entityPart = ModelObserver.getObserver(entity, getEditPartRegistry());
if (entityPart == null) {
if (EntityUtils.isAncestorOrSelf(entity, getEntityContents()))
entityPart = ModelObserver.getObserver(getEntityContents(), getEditPartRegistry());
else
return;
}
entityPart.rebuild();
}
public void refreshNotation() {
// do nothing
}
public void refreshNotation(IEntity entity) {
// do nothing
}
public void setInteractive(IEntity entity, boolean edit, boolean browse, boolean inherited) {
throw new UnsupportedOperationException();
}
}