Ask to link library to model when dropping symbol from unlinked library 99/2499/4
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Tue, 27 Nov 2018 12:28:48 +0000 (14:28 +0200)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Tue, 27 Nov 2018 20:12:08 +0000 (22:12 +0200)
This simplifies workflow for users that want to add symbols from a user
library but have not yet linked the library to the model.

gitlab #215

Change-Id: I1eb4c0680a14dc21fca8dc8961baf1f059ecccd9

bundles/org.simantics.modeling.ui/META-INF/MANIFEST.MF
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/Messages.java [new file with mode: 0644]
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/PopulateElementDropParticipant.java
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/DropSuggestion.java [new file with mode: 0644]
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/DropSuggestions.java [new file with mode: 0644]
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/MarkupDialog.java [new file with mode: 0644]
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/Messages.java [new file with mode: 0644]
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/messages.properties [new file with mode: 0644]
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/messages.properties [new file with mode: 0644]
features/org.simantics.wiki.ui.feature/feature.xml
features/pom.xml

index 340e36b7ce3ba0df101fb748e2ade2bc82439ff1..b8a6da23c63815c57db0cd2b217047c60fe71822 100644 (file)
@@ -62,7 +62,10 @@ Require-Bundle: org.simantics.project;bundle-version="1.0.0",
  org.simantics.export.core;bundle-version="1.0.0",
  org.slf4j.api,
  org.simantics.graphfile.ontology,
- org.eclipse.e4.core.services
+ org.eclipse.e4.core.services,
+ org.eclipse.mylyn.wikitext;bundle-version="3.0.6",
+ org.eclipse.mylyn.wikitext.markdown;bundle-version="3.0.6",
+ org.eclipse.mylyn.wikitext.ui;bundle-version="3.0.6"
 Export-Package: org.simantics.modeling.ui,
  org.simantics.modeling.ui.actions,
  org.simantics.modeling.ui.chart.property,
diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/Messages.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/Messages.java
new file mode 100644 (file)
index 0000000..6a58dac
--- /dev/null
@@ -0,0 +1,16 @@
+package org.simantics.modeling.ui.diagramEditor;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+    private static final String BUNDLE_NAME = "org.simantics.modeling.ui.diagramEditor.messages"; //$NON-NLS-1$
+    public static String        PopulateElementDropParticipant_PreDropFixesFailed;
+    public static String        PopulateElementDropParticipant_PreDropFixesFailed_Title;
+    static {
+        // initialize resource bundle
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
index 4f27406025f752cd713b5dcc77cab0a5a2a2a6e0..49adcb65b48155ebe6f2082975551f030c1fba1c 100644 (file)
@@ -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.<Resource> getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE));
+                                        Object errorOrSymbolResource = validateDrag(synchronizer.getSession(), r,
+                                                diagram.<Resource> 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<Object>() {
-            @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<Object>) graph -> {
+            List<DropSuggestion> 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<IElement>         picks                   = new ArrayList<IElement>();
+        List<IElement>         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<WorkbenchSelectionElement> wses = new ArrayList<WorkbenchSelectionElement>();
+    private void validateDrop(IDiagram diagram, IDnDContext dp, Runnable dropFunction)
+            throws DatabaseException {
+        List<DropSuggestion> 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<WorkbenchSelectionElement> 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;
diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/DropSuggestion.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/DropSuggestion.java
new file mode 100644 (file)
index 0000000..52d239c
--- /dev/null
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.modeling.ui.diagramEditor.dnd;
+
+import org.simantics.db.WriteGraph;
+import org.simantics.db.exception.DatabaseException;
+
+/**
+ * Represents a suggestion of a fix/change that must be made to the target model
+ * before actually performing the element to diagram drop database changes.
+ * 
+ * The {@link #toString()} method should return a clear description of the
+ * suggested fix/change.
+ * 
+ * @author Tuukka Lehtonen
+ * @since 1.37.0
+ */
+public interface DropSuggestion {
+
+    /**
+     * Perform the suggested database fix/change encapsulated by this instance.
+     * 
+     * @param graph
+     * @throws DatabaseException
+     */
+    void fix(WriteGraph graph) throws DatabaseException;
+
+    /**
+     * @return a clear end-user readable description of the suggested fix/change
+     */
+    @Override
+    String toString();
+
+}
diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/DropSuggestions.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/DropSuggestions.java
new file mode 100644 (file)
index 0000000..0ff4415
--- /dev/null
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.modeling.ui.diagramEditor.dnd;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+import org.simantics.NameLabelMode;
+import org.simantics.NameLabelUtil;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.ValidationException;
+import org.simantics.db.layer0.request.IsLinkedTo;
+import org.simantics.db.request.Write;
+import org.simantics.layer0.Layer0;
+
+/**
+ * @author Tuukka Lehtonen
+ * @since 1.37.0
+ */
+public class DropSuggestions {
+
+    public static boolean askSuggestions(Shell parent, List<DropSuggestion> suggestions) {
+        return Dialog.OK == MarkupDialog.open(parent,
+                "diagram.dropSuggestionDialog", //$NON-NLS-1$
+                Messages.DropSuggestions_ApplySuggestions_DialogTitle,
+                Messages.DropSuggestions_ApplySuggestions_DialogMsg,
+                formatSuggestions(suggestions),
+                SWT.ICON_QUESTION,
+                SWT.NONE,
+                SWT.BORDER | SWT.WRAP | SWT.V_SCROLL);
+    }
+
+    private static String formatSuggestions(List<DropSuggestion> suggestions) {
+        return suggestions.stream()
+                .map(DropSuggestion::toString)
+                .collect(Collectors.joining("\n", "- ", "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+
+    public static Write performSuggestionsRequest(List<DropSuggestion> suggestions) {
+        return (Write) graph -> {
+            for (DropSuggestion s : suggestions) {
+                s.fix(graph);
+            }
+        };
+    }
+
+    public static DropSuggestion linkToLibrary(ReadGraph graph, Resource linkSource, Resource linkTarget) throws DatabaseException {
+        return new LinkToLibrary(
+                NLS.bind(Messages.DropSuggestions_Description_LinkToLibrary,
+                        NameLabelUtil.modalName(graph, linkSource, NameLabelMode.NAME_AND_LABEL),
+                        NameLabelUtil.modalName(graph, linkTarget, NameLabelMode.NAME_AND_LABEL))
+                , linkSource, linkTarget);
+    }
+
+    public static class LinkToLibrary implements DropSuggestion {
+
+        private final String desc;
+        private final Resource linkFrom;
+        private final Resource linkTo;
+
+        public LinkToLibrary(String desc, Resource linkFrom, Resource linkTo) {
+            this.desc = desc;
+            this.linkFrom = linkFrom;
+            this.linkTo = linkTo;
+        }
+
+        @Override
+        public void fix(WriteGraph graph) throws DatabaseException {
+            // Ensure that there is no reverse link between the namespaces
+            if (graph.syncRequest(new IsLinkedTo(linkFrom, linkTo))) {
+                throw new ValidationException(
+                        NLS.bind(Messages.DropSuggestions_Problem_CyclicDependency
+                                , NameUtils.getURIOrSafeNameInternal(graph, linkTo)
+                                , NameUtils.getURIOrSafeNameInternal(graph, linkFrom)));
+            }
+            Layer0 L0 = Layer0.getInstance(graph);
+            graph.claim(linkFrom, L0.IsLinkedTo, L0.IsLinkedTo_Inverse, linkTo);
+        }
+
+        @Override
+        public String toString() {
+            return desc;
+        }
+
+    }
+
+}
diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/MarkupDialog.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/MarkupDialog.java
new file mode 100644 (file)
index 0000000..bf99b58
--- /dev/null
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.modeling.ui.diagramEditor.dnd;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.text.Document;
+import org.eclipse.mylyn.wikitext.markdown.MarkdownLanguage;
+import org.eclipse.mylyn.wikitext.ui.viewer.MarkupViewer;
+import org.eclipse.mylyn.wikitext.ui.viewer.MarkupViewerConfiguration;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.simantics.modeling.ui.Activator;
+
+/**
+ * @author Tuukka Lehtonen
+ * @since 1.37.0
+ */
+class MarkupDialog extends Dialog {
+
+    private String title;
+    private String topText;
+    private String message;
+    private int dialogIconType;
+    private int viewerStyle;
+    private MarkupViewer viewer;
+
+    private IDialogSettings dialogBoundsSettings;
+
+    protected MarkupDialog(Shell parentShell, String dialogId, String title, String topText, String message, int dialogIconType, int viewerStyle) {
+        super(parentShell);
+        this.title = title;
+        this.topText = topText;
+        this.message = message;
+        this.dialogIconType = dialogIconType;
+        this.viewerStyle = viewerStyle;
+        setShellStyle(getShellStyle() | SWT.RESIZE);
+
+        IDialogSettings settings = Activator.getDefault().getDialogSettings();
+        dialogBoundsSettings = settings.getSection(dialogId);
+        if (dialogBoundsSettings == null)
+            dialogBoundsSettings = settings.addNewSection(dialogId);
+    }
+
+    @Override
+    protected IDialogSettings getDialogBoundsSettings() {
+        return dialogBoundsSettings;
+    }
+
+    @Override
+    protected void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+        newShell.setText(title);
+        newShell.setImage(newShell.getDisplay().getSystemImage(dialogIconType));
+    }
+
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        Composite composite = (Composite) super.createDialogArea(parent);
+
+        Text text = new Text(composite, SWT.READ_ONLY | SWT.MULTI | SWT.WRAP);
+        text.setEditable(false);
+        text.setText(topText);
+        GridDataFactory.fillDefaults()
+        .grab(true, false)
+        .hint(convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH), SWT.DEFAULT)
+        .applyTo(text);
+
+        viewer = new MarkupViewer(composite, null, viewerStyle);
+        viewer.setMarkupLanguage(new MarkdownLanguage());
+        viewer.setEditable(false);
+        MarkupViewerConfiguration configuration = new MarkupViewerConfiguration(viewer, Activator.getDefault().getPreferenceStore());
+        viewer.configure(configuration);
+        viewer.setDocument(new Document(message));
+        GridDataFactory.fillDefaults()
+        .grab(true, true)
+        .hint(convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH), SWT.DEFAULT)
+        .applyTo(viewer.getControl());
+
+        return composite;
+    }
+
+    protected void createButtonsForButtonBar(Composite parent) {
+        createButton(parent, IDialogConstants.PROCEED_ID, IDialogConstants.PROCEED_LABEL, true);
+        createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
+    }
+
+    @Override
+    protected void buttonPressed(int buttonId) {
+        if (IDialogConstants.PROCEED_ID == buttonId) {
+            okPressed();
+        } else if (IDialogConstants.CANCEL_ID == buttonId) {
+            cancelPressed();
+        }
+    }
+
+    public static int open(Shell parent, String dialogId, String title, String topText, String message, int dialogIconType, int dialogStyle, int sourceViewerStyle) {
+        MarkupDialog dialog = new MarkupDialog(parent, dialogId, title, topText, message, dialogIconType, sourceViewerStyle);
+        dialogStyle &= SWT.SHEET;
+        dialog.setShellStyle(dialog.getShellStyle() | dialogStyle);
+        return dialog.open();
+    }
+
+}
\ No newline at end of file
diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/Messages.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/Messages.java
new file mode 100644 (file)
index 0000000..b8c611b
--- /dev/null
@@ -0,0 +1,18 @@
+package org.simantics.modeling.ui.diagramEditor.dnd;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+    private static final String BUNDLE_NAME = "org.simantics.modeling.ui.diagramEditor.dnd.messages"; //$NON-NLS-1$
+    public static String        DropSuggestions_ApplySuggestions_DialogMsg;
+    public static String        DropSuggestions_ApplySuggestions_DialogTitle;
+    public static String        DropSuggestions_Description_LinkToLibrary;
+    public static String        DropSuggestions_Problem_CyclicDependency;
+    static {
+        // initialize resource bundle
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/messages.properties b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/dnd/messages.properties
new file mode 100644 (file)
index 0000000..b606334
--- /dev/null
@@ -0,0 +1,4 @@
+DropSuggestions_ApplySuggestions_DialogMsg=In order to perform the requested drop operation, the changes listed below need to be performed.\\n\\nAre you sure you want to proceed?
+DropSuggestions_ApplySuggestions_DialogTitle=Changes Required
+DropSuggestions_Description_LinkToLibrary=Link target model **{0}** \\u2192 library **{1}**
+DropSuggestions_Problem_CyclicDependency=Source namespace {0} is already linked to target namespace {1}. Creating a target \\u2192 source link would create a cyclic dependency.
diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/messages.properties b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/messages.properties
new file mode 100644 (file)
index 0000000..7ed685f
--- /dev/null
@@ -0,0 +1,2 @@
+PopulateElementDropParticipant_PreDropFixesFailed=Drop failed because the following pre-drop fixes could not be performed:\n\n{0}
+PopulateElementDropParticipant_PreDropFixesFailed_Title=Drop Failed
index d1dc4dd43fde676d4c24206b077ac7b40af60723..1a1ce696834737bf0bb83252ebc14ab3018a35d2 100644 (file)
@@ -2,7 +2,7 @@
 <feature
       id="org.simantics.wiki.ui.feature"
       label="Wiki Documentation Feature"
-      version="1.26.0.qualifier"
+      version="1.37.0.qualifier"
       provider-name="Semantum Oy">
 
    <description url="http://www.example.com/description">
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="org.eclipse.mylyn.wikitext.markdown"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
    <plugin
          id="org.jsoup"
          download-size="0"
index 2503989cf1060996167302c7d5f198bc952d1e2e..a2a39bac49055722fbc90ddc0b757d377ab1c686 100644 (file)
@@ -58,6 +58,7 @@
                   <plugin id="org.eclipse.mylyn.wikitext.core"/>
                   <plugin id="org.eclipse.mylyn.wikitext.core.ant"/>
                   <plugin id="org.eclipse.mylyn.wikitext.core.osgi"/>
+                  <plugin id="org.eclipse.mylyn.wikitext.markdown"/>
                   <plugin id="org.eclipse.mylyn.wikitext.mediawiki.core"/>
                   <plugin id="org.eclipse.mylyn.wikitext.mediawiki.ui"/>
                   <plugin id="org.eclipse.mylyn.wikitext.ui"/>