/** * 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.Collection; import java.util.HashSet; 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.ExclusionSearch; import org.eclipse.draw2d.FigureCanvas; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.LightweightSystem; import org.eclipse.draw2d.TreeSearch; import org.eclipse.draw2d.geometry.Point; import org.eclipse.e4.core.contexts.EclipseContextFactory; 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.commands.CommandStack; import org.eclipse.gef.editparts.ZoomManager; import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; 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.editparts.IScalableRootEditPart; import org.whole.lang.e4.ui.editparts.RootEditPart; import org.whole.lang.e4.ui.handler.CopyHandler; import org.whole.lang.e4.ui.handler.CutHandler; import org.whole.lang.e4.ui.handler.PasteHandler; import org.whole.lang.e4.ui.handler.SelectAllHandler; 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.ReflectionFactory; import org.whole.lang.ui.dialogs.IImportAsModelDialogFactory; import org.whole.lang.ui.dnd.EditPartTransferDragSourceListener; import org.whole.lang.ui.dnd.EditPartTransferDropTargetListener; import org.whole.lang.ui.dnd.FileTransferDropTargetListener; import org.whole.lang.ui.dnd.TextTransferDragSourceListener; import org.whole.lang.ui.dnd.TextTransferDropTargetListener; import org.whole.lang.ui.dnd.XmlBuilderFileTransferDragSourceListener; import org.whole.lang.ui.editparts.EntityPartListener; import org.whole.lang.ui.editparts.IEntityPart; import org.whole.lang.ui.editparts.IGraphicalEntityPart; import org.whole.lang.ui.editparts.IPartFocusListener; import org.whole.lang.ui.editparts.ModelObserver; import org.whole.lang.ui.editparts.WholeEditPartFactory; import org.whole.lang.ui.figures.IEntityFigure; 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.resources.ColorRegistry; import org.whole.lang.ui.resources.FontRegistry; import org.whole.lang.ui.resources.IColorRegistry; import org.whole.lang.ui.resources.IFontRegistry; import org.whole.lang.ui.resources.IResourceManager; import org.whole.lang.ui.tools.Tools; import org.whole.lang.ui.treesearch.DelegatingInteractiveTreeSearch; import org.whole.lang.ui.treesearch.ITreeSearch; import org.whole.lang.ui.treesearch.NonInteractiveConditional; import org.whole.lang.ui.viewers.EntityEditDomain; import org.whole.lang.ui.viewers.IEntityPartViewer; import org.whole.lang.ui.viewers.ZoomGestureListener; import org.whole.lang.util.EntityUtils; import org.whole.langs.core.CoreMetaModelsDeployer; /** * @author Enrico Persiani */ public class E4GraphicalViewer extends ScrollingGraphicalViewer implements IResourceManager, IEntityPartViewer { @Inject IEclipseContext context; @Inject @Named("parent") Composite parent; @Inject @Optional EntityEditDomain domain; @Inject IImportAsModelDialogFactory factory; private ModelObserver modelObserver; private List<IPartFocusListener> partFocusListeners; private List<IModelInputListener> modelInputListeners; private Set<String> referencedResources; @PostConstruct protected void initialize() { if (domain == null) domain = new EntityEditDomain(); partFocusListeners = new ArrayList<IPartFocusListener>(); modelInputListeners = new ArrayList<IModelInputListener>(); referencedResources = new HashSet<String>(); createControl2(parent); domain.addViewer(this); setEditPartFactory(new WholeEditPartFactory()); setRootEditPart(createRootEditPart()); configureViewer(domain); } protected void configureViewer(EntityEditDomain domain) { addDragSourceListener(new EditPartTransferDragSourceListener(this)); addDropTargetListener(new EditPartTransferDropTargetListener(this)); addDragSourceListener(new XmlBuilderFileTransferDragSourceListener(this)); addDropTargetListener(new FileTransferDropTargetListener(this, factory)); addDragSourceListener(new TextTransferDragSourceListener(this)); addDropTargetListener(new TextTransferDropTargetListener(this, factory)); getControl().addGestureListener(new ZoomGestureListener(getScalableRootEditPart().getZoomManager())); Tools.PANNING.ensureActive(domain); } protected RootEditPart createRootEditPart() { RootEditPart rootEditPart = new RootEditPart(); List<String> levels = new ArrayList<String>(3); levels.add(ZoomManager.FIT_ALL); levels.add(ZoomManager.FIT_WIDTH); levels.add(ZoomManager.FIT_HEIGHT); ZoomManager zoomManager = rootEditPart.getZoomManager(); zoomManager.setZoomLevelContributions(levels); zoomManager.setZoomAnimationStyle(ZoomManager.ANIMATE_ZOOM_IN_OUT); return rootEditPart; } public IScalableRootEditPart getScalableRootEditPart() { return (IScalableRootEditPart) super.getRootEditPart(); } // Begin Block Shared With E4TreeViewer 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; super.setContents(wrapContents(root)); updateModelObserver(root); invalidateTree(); } protected void invalidateTree() { RootEditPart rootPart = (RootEditPart) super.getRootEditPart(); rootPart.getFigure().invalidateTree(); rootPart.getFigure().validate(); getFigureCanvas().getViewport().invalidateTree(); getFigureCanvas().getViewport().validate(); } 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(); rebuildNotation(rootFragment); } // End Block Shared With E4TreeViewer public void selectAndReveal(IEntity entity, boolean propagate) { IEntityPart entityPart = ModelObserver.getObserver(entity, getEditPartRegistry()); if (entityPart != null) { getControl().getDisplay().syncExec(() -> reveal(entityPart)); select(entityPart, propagate); } } public void selectAndReveal(List<? extends IEntity> entities, boolean propagate) { if (!entities.isEmpty()) getControl().getDisplay().syncExec(() -> { reveal(entities.get(0)); entities.subList(1, entities.size()).stream().forEach((entity) -> { IEntityPart entityPart = ModelObserver.getObserver(entity, getEditPartRegistry()); if (!((IGraphicalEntityPart) entityPart).getFigure().isShowing()) reveal(entityPart); }); }); 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(); invalidateTree(); } public void refreshNotation(IEntity entity) { IEntityPart entityPart = ModelObserver.getObserver(entity, getEditPartRegistry()); if (entityPart == null) { if (EntityUtils.isAncestorOrSelf(entity, getEntityContents())) entityPart = ModelObserver.getObserver(getEntityContents(), getEditPartRegistry()); else return; } refreshNotation(((IGraphicalEntityPart) entityPart).getFigure()); } public void refreshNotation() { if (getFigureCanvas() != null) refreshNotation(getFigureCanvas().getViewport()); } protected void refreshNotation(IFigure figure) { figure.invalidateTree(); figure.revalidate(); getFigureCanvas().redraw(); } @SuppressWarnings("unchecked") public class E4FigureCanvas extends FigureCanvas { public E4FigureCanvas(Composite parent, LightweightSystem lws) { super(parent, lws); } public void cut() { IBindingManager bm = E4Utils.createSelectionBindings(getSelectedEditParts(), E4GraphicalViewer.this, EclipseContextFactory.create()); CutHandler handler = new CutHandler(); if (handler.canExecute(bm)) handler.execute(bm); } public void copy() { IBindingManager bm = E4Utils.createSelectionBindings(getSelectedEditParts(), E4GraphicalViewer.this, EclipseContextFactory.create()); CopyHandler handler = new CopyHandler(); if (handler.canExecute(bm)) handler.execute(bm); } public void paste() { IBindingManager bm = E4Utils.createSelectionBindings(getSelectedEditParts(), E4GraphicalViewer.this, EclipseContextFactory.create()); PasteHandler handler = new PasteHandler(); if (handler.canExecute(bm)) handler.execute(bm); } public void selectAll() { IBindingManager bm = E4Utils.createSelectionBindings(getSelectedEditParts(), E4GraphicalViewer.this, EclipseContextFactory.create()); SelectAllHandler handler = new SelectAllHandler(); if (handler.canExecute(bm)) handler.execute(bm); } } public Control createControl2(Composite parent) { setControl(new E4FigureCanvas(parent, getLightweightSystem())); hookRootFigure(); return getControl(); } public void setInteractive(IEntity entity, boolean edit, boolean browse, boolean inherited) { Map<?, ?> mapping = getEditPartRegistry(); IGraphicalEntityPart entityPart = (IGraphicalEntityPart) mapping.get(entity); if (entityPart != null) { IEntityFigure entityFigure = (IEntityFigure) entityPart.getFigure(); entityFigure.setInteractiveEdit(edit); entityFigure.setInteractiveBrowse(browse); entityFigure.setInteractiveInherited(inherited); } } @SuppressWarnings("rawtypes") @Override public EditPart findObjectAtExcluding(Point pt, Collection exclude, final Conditional condition) { class ConditionalTreeSearch extends ExclusionSearch implements ITreeSearch { ConditionalTreeSearch(Collection coll) { super(coll); } public boolean accept(IFigure figure) { EditPart editpart = null; while (editpart == null && figure != null) { editpart = (EditPart) getVisualPartMap().get(figure); figure = figure.getParent(); } return editpart != null && (condition == null || condition.evaluate(editpart)); } } TreeSearch search = new ConditionalTreeSearch(exclude); IFigure figure = getLightweightSystem().getRootFigure().findFigureAt(pt.x, pt.y, condition instanceof NonInteractiveConditional ? search : new DelegatingInteractiveTreeSearch(search)); EditPart part = null; while (part == null && figure != null) { part = (EditPart) getVisualPartMap().get(figure); figure = figure.getParent(); } if (part == null) return getContents(); return part; } private EditPart focusEditPart; @Override protected void handleFocusGained(FocusEvent fe) { if (focusEditPart != null) setFocus(focusEditPart); focusEditPart = null; } @Override protected void handleFocusLost(FocusEvent fe) { focusEditPart = getFocusEditPart(); focusEditPart = focusEditPart.hasFocus() ? focusEditPart : null; setFocus(null); } @Override public void setFocus(EditPart part) { IEntityPart oldPart = getFocusEntityPart(); super.setFocus(part); if (oldPart != part) firePartFocusChanged(oldPart, (IEntityPart) part); } @Override protected void handleDispose(DisposeEvent e) { colorRegistry = null; fontRegistry = null; super.handleDispose(e); } private IColorRegistry colorRegistry; public IColorRegistry getColorRegistry() { if (colorRegistry == null) colorRegistry = new ColorRegistry(this); return colorRegistry; } private IFontRegistry fontRegistry; public IFontRegistry getFontRegistry() { if (fontRegistry == null) fontRegistry = new FontRegistry(this); return fontRegistry; } }