X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.modeling.ui%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2Fui%2FdiagramEditor%2FPopulateElementDropParticipant.java;h=49adcb65b48155ebe6f2082975551f030c1fba1c;hp=4f27406025f752cd713b5dcc77cab0a5a2a2a6e0;hb=252076ed80eef446cc62c5b193d34b7bea3cdc80;hpb=1f09c7df2e394be485a82de0acef444ae68a4c9b diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/PopulateElementDropParticipant.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/PopulateElementDropParticipant.java index 4f2740602..49adcb65b 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/PopulateElementDropParticipant.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/PopulateElementDropParticipant.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * Copyright (c) 2007, 2018 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -8,6 +8,7 @@ * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation + * Semantum Oy - gitlab #215 *******************************************************************************/ package org.simantics.modeling.ui.diagramEditor; @@ -22,37 +23,39 @@ import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchPartSite; import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; import org.simantics.db.Session; -import org.simantics.db.WriteGraph; import org.simantics.db.common.request.PossibleIndexRoot; import org.simantics.db.common.request.ResourceRead2; -import org.simantics.db.common.request.UniqueRead; -import org.simantics.db.common.request.WriteRequest; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.IsLinkedTo; import org.simantics.db.layer0.util.Layer0Utils; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; +import org.simantics.db.request.Read; +import org.simantics.db.request.Write; import org.simantics.db.service.SerialisationSupport; import org.simantics.diagram.adapter.GraphToDiagramSynchronizer; import org.simantics.diagram.content.Change; import org.simantics.diagram.content.DiagramContentChanges; import org.simantics.diagram.content.DiagramContentTracker; import org.simantics.diagram.stubs.DiagramResource; -import org.simantics.diagram.symbollibrary.ISymbolItem; import org.simantics.diagram.synchronization.runtime.DiagramSelectionUpdater; import org.simantics.diagram.ui.DiagramModelHints; import org.simantics.diagram.ui.ElementClassTransferable; @@ -76,22 +79,40 @@ import org.simantics.g2d.element.IElement; import org.simantics.g2d.participant.TransformUtil; import org.simantics.modeling.ModelingResources; import org.simantics.modeling.ui.Activator; +import org.simantics.modeling.ui.diagramEditor.dnd.DropSuggestion; +import org.simantics.modeling.ui.diagramEditor.dnd.DropSuggestions; import org.simantics.scenegraph.g2d.snap.ISnapAdvisor; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.ui.dnd.LocalObjectTransfer; import org.simantics.ui.dnd.LocalObjectTransferable; import org.simantics.ui.selection.WorkbenchSelectionElement; +import org.simantics.utils.datastructures.hints.IHintContext; +import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.logging.TimeLogger; +import org.simantics.utils.strings.EString; +import org.simantics.utils.ui.SWTUtils; +import org.simantics.utils.ui.dialogs.ShowError; import org.simantics.utils.ui.workbench.WorkbenchUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This participant populates Elements from ElementClass-resources drops */ public class PopulateElementDropParticipant extends AbstractDiagramParticipant implements IDropTargetParticipant { + private static final Logger LOGGER = LoggerFactory.getLogger(PopulateElementDropParticipant.class); + + /** + * List of {@link DropSuggestion} instances that need to be applied to be model + * before the drop can commence. Used with the hint context from + * {@link IDnDContext#getHints()}. + */ + private static final Key KEY_SUGGESTIONS = new IHintContext.KeyOf(List.class); + @Dependency PickContext pickContext; @Dependency TransformUtil transformUtil; - + protected GraphToDiagramSynchronizer synchronizer; protected IWorkbenchPartSite partSite; @@ -111,19 +132,14 @@ public class PopulateElementDropParticipant extends AbstractDiagramParticipant i Transferable tr = dtde.getTransferable(); if (tr.isDataFlavorSupported(LocalObjectTransferable.FLAVOR)) { - // System.out.println("joo"); Object obj = null; // This must be done to have SWT transfer set the source data try { obj = tr.getTransferData(LocalObjectTransferable.FLAVOR); // System.out.println("GOT FROM AWT: " + obj); - } catch (UnsupportedFlavorException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + } catch (UnsupportedFlavorException | IOException e) { + LOGGER.error("Could not get AWT transferable data", e); //$NON-NLS-1$ } // Check SWT @@ -143,32 +159,20 @@ public class PopulateElementDropParticipant extends AbstractDiagramParticipant i } else { Resource r = (Resource) ((IAdaptable) elm).getAdapter(Resource.class); if (r != null) { - if (elm instanceof ISymbolItem) { - /* FIXME fix this check - ISymbolItem symbol = (ISymbolItem) elm; - Resource dia = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE); - try { - if (!DiagramTypeUtils.symbolAllowedOnDiagram(synchronizer.getSession(), symbol, dia)) { - // Deny dragging of this symbol - continue; - } - } catch (DatabaseException e) { - e.printStackTrace(); - continue; - }*/ - } - try { - Object errorOrSymbolResource = validateDrop(synchronizer.getSession(), r, - diagram. getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE)); + Object errorOrSymbolResource = validateDrag(synchronizer.getSession(), r, + diagram. getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), + dp.getHints()); if (errorOrSymbolResource instanceof Resource) { - ElementClassDragItem item = new ElementClassDragItem(synchronizer.getNodeClass((Resource)errorOrSymbolResource)); + Resource symbol = (Resource) errorOrSymbolResource; + ElementClassDragItem item = new ElementClassDragItem(synchronizer.getNodeClass(symbol)); item.getHintContext().setHint(ElementHints.KEY_TRANSFORM, AffineTransform.getScaleInstance(1, 1)); + item.getHintContext().setHint(ElementHints.KEY_OBJECT, symbol); dp.add(item); } } catch (DatabaseException e) { - // Ignore node-class retrieval failures. - //System.out.println("error: " + e.getMessage()); + // Ignore node-class retrieval failures, so only log as debug + LOGGER.debug("Could not retrieve node class for dropped symbol", e); //$NON-NLS-1$ } } } @@ -207,62 +211,69 @@ public class PopulateElementDropParticipant extends AbstractDiagramParticipant i } } - private Object validateDrop(RequestProcessor processor, final Resource draggedResource, final Resource dropTarget) throws DatabaseException { - return processor.syncRequest(new UniqueRead() { - @Override - public Object perform(ReadGraph graph) throws DatabaseException { -// System.out.println("dragged resource: " + draggedResource); -// System.out.println("drop target resource: " + dropTarget); - Resource sourceModel = graph.syncRequest(new PossibleIndexRoot(draggedResource)); - Resource targetModel = graph.syncRequest(new PossibleIndexRoot(dropTarget)); -// System.out.println("source model: " + sourceModel); -// System.out.println("target model: " + targetModel); - - // Prevent dragging data from one source model to another. - // If source is not part of any model, everything is okay. - if (sourceModel != null && !graph.syncRequest(new IsLinkedTo(targetModel, sourceModel))) { - // Prevent a symbol instantiating within its own configuration. - // NOTE: this doesn't handle transitive cycles. - return "Cannot instantiate " + NameUtils.getSafeName(graph, draggedResource) + " into model " - + NameUtils.getURIOrSafeNameInternal(graph, targetModel) + ". The source namespace (" - + NameUtils.getURIOrSafeNameInternal(graph, sourceModel) + ") is not linked to the target model."; - } - - // Prevent dragging to published components - ModelingResources MOD = ModelingResources.getInstance(graph); - StructuralResource2 STR = StructuralResource2.getInstance(graph); - Resource configuration = graph.getPossibleObject(dropTarget, MOD.DiagramToComposite); - if (configuration != null) { - Resource componentTypeFromDiagram = graph.getPossibleObject(configuration, STR.Defines); - if(componentTypeFromDiagram != null) { - if(Layer0Utils.isPublished(graph, componentTypeFromDiagram)) - return "Cannot create elements into a diagram that belongs to a published user component."; - } - } + private Object validateDrag(RequestProcessor processor, final Resource draggedResource, final Resource dropTarget, IHintContext dndHints) throws DatabaseException { + return processor.syncRequest((Read) graph -> { + List suggestions = dndHints.getHint(KEY_SUGGESTIONS); + if (suggestions == null) { + suggestions = new ArrayList<>(); + dndHints.setHint(KEY_SUGGESTIONS, suggestions); + } - // Check if dragged object is symbol or component type and determine other - Resource componentType; - Resource symbol = graph.getPossibleObject(draggedResource, MOD.ComponentTypeToSymbol); - - if(symbol != null) - componentType = draggedResource; - else { - componentType = graph.getPossibleObject(draggedResource, MOD.SymbolToComponentType); - symbol = draggedResource; - } - - // Prevent dragging a symbol of component type into its own configuration. - if (componentType != null) { - if (configuration != null) { - Resource componentTypeFromDiagram = graph.getPossibleObject(configuration, STR.Defines); - if (componentTypeFromDiagram != null && componentType.equals(componentTypeFromDiagram)) { - return "Cannot instantiate user component within its own configuration."; - } - } + //System.out.println("dragged resource: " + draggedResource); + //System.out.println("drop target resource: " + dropTarget); + Resource sourceRoot = graph.syncRequest(new PossibleIndexRoot(draggedResource)); + Resource targetRoot = graph.syncRequest(new PossibleIndexRoot(dropTarget)); + //System.out.println("source model: " + sourceRoot); + //System.out.println("target model: " + targetRoot); + + // Prevent dragging data from one source model to another. + // If source is not part of any model, everything is okay. + if (sourceRoot != null && !graph.syncRequest(new IsLinkedTo(targetRoot, sourceRoot))) { + // Prevent instantiation from source roots that are already dependent on the target root. + // This would form a dependency cycle. + if (graph.syncRequest(new IsLinkedTo(sourceRoot, targetRoot))) { + return NLS.bind("Cannot instantiate {0} into namespace {1}. The source namespace ({2}) is already linked to the target namespace. Linking the target to the source would form a dependency cycle.", //$NON-NLS-1$ + new Object[] { + NameUtils.getSafeName(graph, draggedResource), + NameUtils.getURIOrSafeNameInternal(graph, targetRoot), + NameUtils.getURIOrSafeNameInternal(graph, sourceRoot) + }); } - return symbol; + // It is OK to continue for now, even though the target root is not linked to the source root. + // The question of whether to link the target root to the source root will asked at drop time. + suggestions.add(DropSuggestions.linkToLibrary(graph, targetRoot, sourceRoot)); } + + ModelingResources MOD = ModelingResources.getInstance(graph); + StructuralResource2 STR = StructuralResource2.getInstance(graph); + + Resource configurationComposite = graph.getPossibleObject(dropTarget, MOD.DiagramToComposite); + Resource componentTypeFromDiagram = configurationComposite != null ? graph.getPossibleObject(configurationComposite, STR.Defines) : null; + + // Prevent dragging to published components + if (componentTypeFromDiagram != null && Layer0Utils.isPublished(graph, componentTypeFromDiagram)) + return "Cannot create elements into a diagram that belongs to a published user component."; //$NON-NLS-1$ + + // Check if dragged object is symbol or component type and determine other + Resource componentType; + Resource symbol = graph.getPossibleObject(draggedResource, MOD.ComponentTypeToSymbol); + if (symbol != null) + componentType = draggedResource; + else { + componentType = graph.getPossibleObject(draggedResource, MOD.SymbolToComponentType); + symbol = draggedResource; + } + + // Prevent dragging a symbol of component type into its own configuration. + if (componentType != null + && configurationComposite != null + && componentTypeFromDiagram != null + && componentType.equals(componentTypeFromDiagram)) { + return "Cannot instantiate user component within its own configuration."; //$NON-NLS-1$ + } + + return symbol; }); } @@ -277,95 +288,122 @@ public class PopulateElementDropParticipant extends AbstractDiagramParticipant i } private IElement tryPick(Point p) { + Point2D canvas = transformUtil.controlToCanvas(p, null); - Point2D canvas = transformUtil.controlToCanvas(p, null); - assertDependencies(); PickRequest req = new PickRequest(canvas); req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS; - List picks = new ArrayList(); + List picks = new ArrayList<>(); pickContext.pick(diagram, req, picks); if(picks.size() == 1) return picks.iterator().next(); - + return null; - } @Override public void drop(DropTargetDropEvent dtde, final IDnDContext dp) { - TimeLogger.resetTimeAndLog(getClass(), "drop"); + TimeLogger.resetTimeAndLog(getClass(), "drop"); //$NON-NLS-1$ - final IDiagram d = getHint(DiagramHints.KEY_DIAGRAM); + final Point loc = dtde.getLocation(); + final IDiagram d = diagram; if (d == null) return; - IElement pick = tryPick(dtde.getLocation()); - if(pick != null) { + try { + validateDrop(d, dp, () -> performDrop(d, loc, dp)); + } catch (DatabaseException e) { + LOGGER.error("Element drop validation failed", e); //$NON-NLS-1$ + } + } - final List wses = new ArrayList(); + private void validateDrop(IDiagram diagram, IDnDContext dp, Runnable dropFunction) + throws DatabaseException { + List reqs = dp.getHints().getHint(KEY_SUGGESTIONS); + if (reqs != null && !reqs.isEmpty()) { + // Ask user if suggestions should be ran before dropping. + // If not, cancel. + Shell parentShell = partSite.getWorkbenchWindow().getShell(); + SWTUtils.asyncExec(parentShell, () -> { + if (parentShell.isDisposed()) + return; + if (!DropSuggestions.askSuggestions(parentShell, reqs)) + return; - for(IDragItem i : dp.toArray()) - if(i instanceof WSEDragItem) - wses.add(((WSEDragItem)i).getObject()); + try { + Simantics.getSession().syncRequest(DropSuggestions.performSuggestionsRequest(reqs)); + + getThread().asyncExec(() -> { + if (isRemoved()) + return; + dropFunction.run(); + }); + } catch (DatabaseException e) { + String format = Messages.PopulateElementDropParticipant_PreDropFixesFailed; + String formattedSuggestions = EString.implode(reqs); + LOGGER.error(format, formattedSuggestions, e); + ShowError.showError(Messages.PopulateElementDropParticipant_PreDropFixesFailed_Title, NLS.bind(format, formattedSuggestions), e); + } + }); + } else { + dropFunction.run(); + } + } - final Resource element = (Resource)ElementUtils.getData(d, pick); - if(element != null && !wses.isEmpty()) { + private void performDrop(IDiagram d, Point loc, IDnDContext dp) { + IElement pick = tryPick(loc); + if (pick != null) { + List wses = Arrays.stream(dp.toArray()) + .filter(WSEDragItem.class::isInstance) + .map(di -> ((WSEDragItem) di).getObject()) + .collect(Collectors.toList()); + final Resource element = (Resource) ElementUtils.getData(d, pick); + if (element != null && !wses.isEmpty()) { try { Session db = Simantics.getSession(); DiagramResource DIA = DiagramResource.getInstance(db); Variable function = db.syncRequest(new PossibleVariableProperty(element, DIA.symbolDropHandler)); if (function != null) { - db.syncRequest(new WriteRequest() { - @Override - public void perform(WriteGraph graph) throws DatabaseException { - Simantics.invokeSCLWrite(graph, function, wses); - } - }); + db.syncRequest((Write) graph -> Simantics.invokeSCLWrite(graph, function, wses)); return; } } catch (DatabaseException e) { Activator.getDefault().getLog() .log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, - "Invocation to custom symbolDropHandler for element " - + element + " failed.", + "Invocation to custom symbolDropHandler for element " //$NON-NLS-1$ + + element + " failed.", //$NON-NLS-1$ e)); return; } - - } - - + } } - - Runnable creator = new Runnable() { - public void run() { - DiagramUtils.mutateDiagram(d, m -> { - IDragItem items[] = dp.toArray(); - for (IDragItem i : items) { - if (!(i instanceof ElementClassDragItem)) - continue; + Runnable creator = () -> { + DiagramUtils.mutateDiagram(d, m -> { + IDragItem items[] = dp.toArray(); - ElementClassDragItem res = (ElementClassDragItem) i; - ElementClass ec = res.getElementClass(); + for (IDragItem i : items) { + if (!(i instanceof ElementClassDragItem)) + continue; - Point2D pos = dp.getItemPosition(i); - // System.out.println(pos); - assert (pos != null); + ElementClassDragItem res = (ElementClassDragItem) i; + ElementClass ec = res.getElementClass(); - IElement element = m.newElement(ec); - element.setHints(res.getHintContext().getHints()); + Point2D pos = dp.getItemPosition(i); + // System.out.println(pos); + assert (pos != null); - setupDroppedElement(element, pos); + IElement element = m.newElement(ec); + element.setHints(res.getHintContext().getHints()); - // Remove only the drag items we've processed. - dp.remove(i); - } - }); - } + setupDroppedElement(element, pos); + + // Remove only the drag items we've processed. + dp.remove(i); + } + }); }; selectNewDiagramContentAfter(d, partSite, creator); @@ -408,7 +446,7 @@ public class PopulateElementDropParticipant extends AbstractDiagramParticipant i } } } catch (DatabaseException e) { - Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Diagram content change tracking failed.", e)); + Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Diagram content change tracking failed.", e)); //$NON-NLS-1$ } } @@ -437,7 +475,7 @@ public class PopulateElementDropParticipant extends AbstractDiagramParticipant i public int getAllowedOps() { return DnDConstants.ACTION_COPY; } - + @Override public double getPriority() { return 10.0;