X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fhandler%2FDeleteHandler.java;h=5fa94ebc7a2787d1c9b3a51db9fe508ef04106dc;hp=d26e0cdcdaf27788286c16bb255119df70c33503;hb=fe216008faa34a8db02d0c53fc97e71cb70f64c0;hpb=05e9ccefa08e1407b49940c32b1239b171d0008e diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/handler/DeleteHandler.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/handler/DeleteHandler.java index d26e0cdcd..5fa94ebc7 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/handler/DeleteHandler.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/handler/DeleteHandler.java @@ -1,440 +1,452 @@ -/******************************************************************************* - * Copyright (c) 2007, 2010 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: - * VTT Technical Research Centre of Finland - initial API and implementation - *******************************************************************************/ -package org.simantics.diagram.handler; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.jface.action.IStatusLineManager; -import org.eclipse.swt.widgets.Display; -import org.simantics.DatabaseJob; -import org.simantics.Simantics; -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.WriteGraph; -import org.simantics.db.common.request.WriteRequest; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.layer0.adapter.Remover; -import org.simantics.db.layer0.exception.CannotRemoveException; -import org.simantics.db.layer0.util.RemoverUtil; -import org.simantics.diagram.adapter.ElementFactoryUtil; -import org.simantics.diagram.content.ConnectionUtil; -import org.simantics.diagram.content.EdgeResource; -import org.simantics.diagram.internal.Activator; -import org.simantics.diagram.synchronization.ISynchronizationContext; -import org.simantics.diagram.synchronization.graph.RemoveBranchpoint; -import org.simantics.diagram.synchronization.graph.RemoveElement; -import org.simantics.diagram.ui.DiagramModelHints; -import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; -import org.simantics.g2d.connection.ConnectionEntity; -import org.simantics.g2d.connection.handler.ConnectionHandler; -import org.simantics.g2d.diagram.IDiagram; -import org.simantics.g2d.diagram.handler.PickRequest.PickFilter; -import org.simantics.g2d.diagram.handler.Relationship; -import org.simantics.g2d.diagram.handler.RelationshipHandler; -import org.simantics.g2d.diagram.handler.RelationshipHandler.Relation; -import org.simantics.g2d.diagram.handler.Topology; -import org.simantics.g2d.diagram.handler.Topology.Connection; -import org.simantics.g2d.diagram.handler.Topology.Terminal; -import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant; -import org.simantics.g2d.diagram.participant.Selection; -import org.simantics.g2d.element.ElementClass; -import org.simantics.g2d.element.ElementHints; -import org.simantics.g2d.element.ElementUtils; -import org.simantics.g2d.element.IElement; -import org.simantics.g2d.element.handler.BendsHandler; -import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd; -import org.simantics.g2d.element.handler.TerminalTopology; -import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler; -import org.simantics.scenegraph.g2d.events.command.CommandEvent; -import org.simantics.scenegraph.g2d.events.command.Commands; -import org.simantics.utils.logging.TimeLogger; -import org.simantics.utils.strings.EString; -import org.simantics.utils.threads.SWTThread; -import org.simantics.utils.threads.ThreadUtils; -import org.simantics.utils.ui.dialogs.ShowMessage; - -/** - * DeleteHandler is a canvas handler for Commands.DELETE commands for an - * IDiagram. - * - *

- * The handler attempts to delete the current selection for pointer 0, meaning - * {@link Selection#SELECTION0}. - *

- * - *

- * The handler logic goes as follows: - *

- *
    - *
  1. Separate nodes and edges form the the removed selection
  2. - *
  3. Find all edges attached to the removed nodes and remove them too
  4. - *
  5. Delete connections that contain less than 2 terminal connections
  6. - *
- * - * @see Selection for the current diagram selection source - * - * @author Tuukka Lehtonen - * - * TODO: start using WorkbenchStatusLine participant - */ -public class DeleteHandler extends AbstractDiagramParticipant { - - public static final boolean DEBUG_DELETE = false; - - @Dependency Selection sel; - - private final IStatusLineManager statusLine; - - public DeleteHandler(IStatusLineManager statusLine) { - this.statusLine = statusLine; - } - - @EventHandler(priority = 0) - public boolean handleCommand(CommandEvent e) { - if (Commands.DELETE.equals( e.command )) { - IDiagram d = diagram; - if (d == null) - return true; - - Set ss = sel.getSelection(0); - if (ss.isEmpty()) - return true; - - if (delete(d, ss)) { - sel.clear(0); - } - - return true; - } - return false; - } - - public boolean delete(final IDiagram d, Collection ss) { - TimeLogger.resetTimeAndLog(getClass(), "delete"); - - Topology topology = d.getDiagramClass().getAtMostOneItemOfClass(Topology.class); - RelationshipHandler erh = d.getDiagramClass().getAtMostOneItemOfClass(RelationshipHandler.class); - - if (DEBUG_DELETE) { - System.out.println("diagram: " + d); - for (IElement e : d.getSnapshot()) { - ElementClass ec = e.getElementClass(); - System.out.println("\t-element " + e); - System.out.println("\t -class " + e.getElementClass()); - if (ec.containsClass(ConnectionHandler.class)) { - ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY); - for (IElement child : ce.getBranchPoints(null)) { - System.out.println("\t\t-branch " + child); - System.out.println("\t\t -class " + child.getElementClass()); - } - for (IElement child : ce.getSegments(null)) { - System.out.println("\t\t-segment " + child); - System.out.println("\t\t -class " + child.getElementClass()); - } - } - } - System.out.println("delete requested for elements:"); - for (IElement e : ss) - System.out.println("\t-element " + e); - } - - // Analyze removals: - // - separate elements and connections - // - find all connections attached to the elements and remove them too - Deque elementsToProcess = new ArrayDeque(ss); - Set processedElements = new HashSet(); - Set relationshipsProcessedForElement = new HashSet(); - - final Collection elements = new ArrayList(); - final Set edges = new HashSet(); - Collection connections = new ArrayList(); - Collection terminals = new ArrayList(); - Collection relations = new ArrayList(); - while (!elementsToProcess.isEmpty()) { - IElement el = elementsToProcess.pollFirst(); - - if (relationshipsProcessedForElement.add(el)) { - // Check for relationships to other elements and mark child - // elements to be removed before the parent element. - relations.clear(); - erh.getRelations(d, el, relations); - if (!relations.isEmpty()) { - boolean restart = false; - for (Relation r : relations) { - //System.out.println("FOUND RELATION: " + r); - if (r.getRelationship() == Relationship.PARENT_OF) { - if ((r.getObject() instanceof IElement)) { - IElement ee = (IElement) r.getObject(); - if (d.containsElement(ee)) { - //System.out.println("DIAGRAM CONTAINS OBJECT: " + r.getObject()); - - // Mark the object also to be processed for removal. - elementsToProcess.addFirst(ee); - restart = true; - } - } - } - } - if (restart) { - // Only process this element after we're sure that - // all its children have been processed. - elementsToProcess.addLast(el); - continue; - } - } - } - - if (!processedElements.add(el)) - continue; - - TerminalTopology tt = el.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class); - BendsHandler bh = el.getElementClass().getAtMostOneItemOfClass(BendsHandler.class); - - if (bh != null) { - // Verify that the edge is NOT between two branch points. - // If it is, do not allow deletion because it is the only case - // which can break a connection tree into a connection forest. - // We do not want that to happen. - Connection begin = topology.getConnection(el, EdgeEnd.Begin); - Connection end = topology.getConnection(el, EdgeEnd.End); - - // Try to work with cases where the model is somewhat corrupt. - if (begin != null && end != null) { - if (PickFilter.FILTER_BRANCH_POINT.accept(begin.node) && PickFilter.FILTER_BRANCH_POINT.accept(end.node)) { - error("Deletion of branch point connecting edges is not allowed. Must be connected to a node terminal."); - return false; - } - } - - if (DEBUG_DELETE) - System.out.println("ADDED EDGE FOR REMOVAL: " + el); - edges.add(el); - } else { - if (DEBUG_DELETE) - System.out.println("ADDED ELEMENT FOR REMOVAL: " + el); - elements.add(el); - - if (tt != null) { - terminals.clear(); - tt.getTerminals(el, terminals); - connections.clear(); - for (Terminal terminal : terminals) - topology.getConnections(el, terminal, connections); - for (Connection c : connections) { - if (c.edge != null) { - if (c.edge.getElementClass().containsClass(BendsHandler.class)) - edges.add(c.edge); - if (DEBUG_DELETE) - System.out.println("TERMINAL CONNECTION WILL BE DISCONNECTED: " + c); - } - } - } - } - } - - if (elements.isEmpty() && edges.isEmpty()) - return false; - - if (DEBUG_DELETE) { - System.out.println("gathered elements to delete:"); - System.out.println("\telements:"); - if (!elements.isEmpty()) - for (IElement e : elements) - System.out.println("\t\t" + e); - System.out.println("\tedges:"); - if (!edges.isEmpty()) - for (IElement e : edges) - System.out.println("\t\t" + e); - } - - final IDiagram diagram = this.diagram; - final ISynchronizationContext syncContext = ElementFactoryUtil.getContextChecked(diagram); - - new DatabaseJob("Delete selection") { - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - delete(monitor); - return Status.OK_STATUS; - } catch (CannotRemoveException e) { - ShowMessage.showInformation("Delete Selection Was Denied", e.getLocalizedMessage()); - return new Status(IStatus.CANCEL, Activator.PLUGIN_ID, e.getLocalizedMessage(), e); - } catch (DatabaseException e) { - return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unexpected error in delete.", e); - } finally { - error(null); - monitor.done(); - } - } - - private void delete(IProgressMonitor monitor) throws DatabaseException { - Simantics.getSession().syncRequest(new WriteRequest() { - Set connectionsToRemove = new HashSet(); - Set touchedConnections = new HashSet(); - - @Override - public void perform(WriteGraph graph) throws DatabaseException { - validateRemoval(graph); - graph.markUndoPoint(); - - ConnectionUtil cu = new ConnectionUtil(graph); - - // Remove edges - for (IElement edge : edges) { - ConnectionEntity ce = edge.getHint(ElementHints.KEY_CONNECTION_ENTITY); - touchConnection( ce.getConnection() ); - - if (DEBUG_DELETE) - System.out.println("REMOVING EDGE: " + edge); - Object obj = ElementUtils.getObject(edge); - if (obj instanceof EdgeResource) { - cu.remove((EdgeResource) obj); - } - } - - // Remove elements - for (IElement element : elements) { - ConnectionHandler ch = element.getElementClass().getAtMostOneItemOfClass(ConnectionHandler.class); - if (ch != null) { - if (DEBUG_DELETE) - System.out.println("MARKING CONNECTION TO BE REMOVED: " + element); - connectionsToRemove.add( (Resource) ElementUtils.getObject(element) ); - } else { - ConnectionEntity ce = element.getHint(ElementHints.KEY_CONNECTION_ENTITY); - if(ce != null) { - if (DEBUG_DELETE) - System.out.println("REMOVING BRANCH POINT: " + element); - new RemoveBranchpoint(element).perform(graph); - touchConnection( ce.getConnection() ); - } else { - if (DEBUG_DELETE) - System.out.println("REMOVING ELEMENT: " + element); - - Object obj = ElementUtils.getObject(element); - if (obj instanceof Resource) { - // Get terminal connections for element - Collection connectors = cu.getTerminalConnectors((Resource) obj, null); - for (Resource connector : connectors) { - Resource connection = ConnectionUtil.tryGetConnection(graph, connector); - if (connection != null) - touchConnection( connection ); - cu.disconnectFromAllRouteNodes(connector); - } - - new RemoveElement((Resource)d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), (Resource)element.getHint(ElementHints.KEY_OBJECT)).perform(graph); - } - } - } - } - - // Check all touched connections to see if they are empty. - for (Resource connection : touchedConnections) { - int removedConnectors = cu.removeUnusedConnectors(connection); - if (DEBUG_DELETE) - System.out.println("PRUNED " + removedConnectors + " CONNECTORS FROM TOUCHED CONNECTION " + connection); - while (true) { - int removedInteriorRouteNodes = cu.removeExtraInteriorRouteNodes(connection); - if (DEBUG_DELETE) - System.out.println("PRUNED " + removedInteriorRouteNodes + " INTERIOR ROUTE NODES FROM TOUCHED CONNECTION " + connection); - if (removedInteriorRouteNodes == 0) - break; - } - int connectors = cu.getConnectedConnectors(connection, null).size(); - if (DEBUG_DELETE) - System.out.println("\t" + connectors + " CONNECTORS LEFT"); - if (connectors < 2) { - connectionsToRemove.add(connection); - } - } - - // Remove selected/left-over empty connections - for (Resource connection : connectionsToRemove) { - if (DEBUG_DELETE) - System.out.println("REMOVING CONNECTION: " + connection); - RemoveElement.removeElement(graph, (Resource)d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), connection); - } - } - - private void validateRemoval(ReadGraph graph) throws DatabaseException, CannotRemoveException { - ArrayList problems = new ArrayList(elements.size()); - for (IElement element : elements) { - Object obj = ElementUtils.getObject(element); - if (obj instanceof Resource) { - Remover remover = RemoverUtil.getPossibleRemover(graph, (Resource) obj); - if (remover != null) { - String problem = remover.canRemove(graph, new HashMap(4)); - if (problem != null) { - problems.add(problem); - } - } - } - } - if (!problems.isEmpty()) { - throw new CannotRemoveException(EString.implode(problems)); - } - } - - void touchConnection(Object c) { - if (DEBUG_DELETE) - System.out.println("TOUCHED CONNECTION: " + c); - if (c instanceof IElement) { - Object obj = ElementUtils.getObject((IElement) c); - if (obj instanceof Resource) - touchedConnections.add((Resource) obj); - } else if (c instanceof Resource) { - touchedConnections.add((Resource) c); - } - } - }); - } - }.schedule(); - - return true; - } - - void message(final String message) { - if (statusLine == null) - return; - swtExec(new Runnable() { - @Override - public void run() { - statusLine.setMessage(message); - statusLine.setErrorMessage(null); - } - }); - } - - void error(final String message) { - if (statusLine == null) - return; - swtExec(new Runnable() { - @Override - public void run() { - statusLine.setErrorMessage(message); - } - }); - } - - void swtExec(Runnable r) { - ThreadUtils.asyncExec(SWTThread.getThreadAccess(Display.getDefault()), r); - } - -} +/******************************************************************************* + * Copyright (c) 2007, 2010 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: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.diagram.handler; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.swt.widgets.Display; +import org.simantics.DatabaseJob; +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.adapter.Remover; +import org.simantics.db.layer0.exception.CannotRemoveException; +import org.simantics.db.layer0.util.RemoverUtil; +import org.simantics.diagram.adapter.ElementFactoryUtil; +import org.simantics.diagram.content.ConnectionUtil; +import org.simantics.diagram.content.EdgeResource; +import org.simantics.diagram.internal.Activator; +import org.simantics.diagram.synchronization.ISynchronizationContext; +import org.simantics.diagram.synchronization.graph.RemoveBranchpoint; +import org.simantics.diagram.synchronization.graph.RemoveElement; +import org.simantics.diagram.ui.DiagramModelHints; +import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; +import org.simantics.g2d.connection.ConnectionEntity; +import org.simantics.g2d.connection.handler.ConnectionHandler; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.diagram.handler.PickRequest.PickFilter; +import org.simantics.g2d.diagram.handler.Relationship; +import org.simantics.g2d.diagram.handler.RelationshipHandler; +import org.simantics.g2d.diagram.handler.RelationshipHandler.Relation; +import org.simantics.g2d.diagram.handler.Topology; +import org.simantics.g2d.diagram.handler.Topology.Connection; +import org.simantics.g2d.diagram.handler.Topology.Terminal; +import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant; +import org.simantics.g2d.diagram.participant.Selection; +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.ElementHints; +import org.simantics.g2d.element.ElementUtils; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.handler.BendsHandler; +import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd; +import org.simantics.g2d.element.handler.TerminalTopology; +import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler; +import org.simantics.scenegraph.g2d.events.command.CommandEvent; +import org.simantics.scenegraph.g2d.events.command.Commands; +import org.simantics.utils.logging.TimeLogger; +import org.simantics.utils.strings.EString; +import org.simantics.utils.threads.SWTThread; +import org.simantics.utils.threads.ThreadUtils; +import org.simantics.utils.ui.dialogs.ShowMessage; + +/** + * DeleteHandler is a canvas handler for Commands.DELETE commands for an + * IDiagram. + * + *

+ * The handler attempts to delete the current selection for pointer 0, meaning + * {@link Selection#SELECTION0}. + *

+ * + *

+ * The handler logic goes as follows: + *

+ *
    + *
  1. Separate nodes and edges form the the removed selection
  2. + *
  3. Find all edges attached to the removed nodes and remove them too
  4. + *
  5. Delete connections that contain less than 2 terminal connections
  6. + *
+ * + * @see Selection for the current diagram selection source + * + * @author Tuukka Lehtonen + * + * TODO: start using WorkbenchStatusLine participant + */ +public class DeleteHandler extends AbstractDiagramParticipant { + + public static final boolean DEBUG_DELETE = false; + + @Dependency Selection sel; + + private final IStatusLineManager statusLine; + + public DeleteHandler(IStatusLineManager statusLine) { + this.statusLine = statusLine; + } + + @EventHandler(priority = 0) + public boolean handleCommand(CommandEvent e) { + if (Commands.DELETE.equals( e.command )) { + IDiagram d = diagram; + if (d == null) + return true; + + Set ss = sel.getSelection(0); + if (ss.isEmpty()) + return true; + + if (delete(d, ss)) { + sel.clear(0); + } + + return true; + } + return false; + } + + public boolean delete(final IDiagram d, Collection ss) { + TimeLogger.resetTimeAndLog(getClass(), "delete"); + + Topology topology = d.getDiagramClass().getAtMostOneItemOfClass(Topology.class); + RelationshipHandler erh = d.getDiagramClass().getAtMostOneItemOfClass(RelationshipHandler.class); + + if (DEBUG_DELETE) { + System.out.println("diagram: " + d); + for (IElement e : d.getSnapshot()) { + ElementClass ec = e.getElementClass(); + System.out.println("\t-element " + e); + System.out.println("\t -class " + e.getElementClass()); + if (ec.containsClass(ConnectionHandler.class)) { + ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY); + for (IElement child : ce.getBranchPoints(null)) { + System.out.println("\t\t-branch " + child); + System.out.println("\t\t -class " + child.getElementClass()); + } + for (IElement child : ce.getSegments(null)) { + System.out.println("\t\t-segment " + child); + System.out.println("\t\t -class " + child.getElementClass()); + } + } + } + System.out.println("delete requested for elements:"); + for (IElement e : ss) + System.out.println("\t-element " + e); + } + + // Analyze removals: + // - separate elements and connections + // - find all connections attached to the elements and remove them too + Deque elementsToProcess = new ArrayDeque(ss); + Set processedElements = new HashSet(); + Set relationshipsProcessedForElement = new HashSet(); + + final Collection elements = new ArrayList(); + final Set edges = new HashSet(); + Collection connections = new ArrayList(); + Collection terminals = new ArrayList(); + Collection relations = new ArrayList(); + while (!elementsToProcess.isEmpty()) { + IElement el = elementsToProcess.pollFirst(); + + if (relationshipsProcessedForElement.add(el)) { + // Check for relationships to other elements and mark child + // elements to be removed before the parent element. + relations.clear(); + erh.getRelations(d, el, relations); + if (!relations.isEmpty()) { + boolean restart = false; + for (Relation r : relations) { + //System.out.println("FOUND RELATION: " + r); + if (r.getRelationship() == Relationship.PARENT_OF) { + if ((r.getObject() instanceof IElement)) { + IElement ee = (IElement) r.getObject(); + if (d.containsElement(ee)) { + //System.out.println("DIAGRAM CONTAINS OBJECT: " + r.getObject()); + + // Mark the object also to be processed for removal. + elementsToProcess.addFirst(ee); + restart = true; + } + } + } + } + if (restart) { + // Only process this element after we're sure that + // all its children have been processed. + elementsToProcess.addLast(el); + continue; + } + } + } + + if (!processedElements.add(el)) + continue; + + TerminalTopology tt = el.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class); + BendsHandler bh = el.getElementClass().getAtMostOneItemOfClass(BendsHandler.class); + + if (bh != null) { + // Verify that the edge is NOT between two branch points. + // If it is, do not allow deletion because it is the only case + // which can break a connection tree into a connection forest. + // We do not want that to happen. + Connection begin = topology.getConnection(el, EdgeEnd.Begin); + Connection end = topology.getConnection(el, EdgeEnd.End); + + // Try to work with cases where the model is somewhat corrupt. + if (begin != null && end != null) { + if (PickFilter.FILTER_BRANCH_POINT.accept(begin.node) && PickFilter.FILTER_BRANCH_POINT.accept(end.node)) { + error("Deletion of branch point connecting edges is not allowed. Must be connected to a node terminal."); + return false; + } + } + + if (DEBUG_DELETE) + System.out.println("ADDED EDGE FOR REMOVAL: " + el); + edges.add(el); + } else { + if (DEBUG_DELETE) + System.out.println("ADDED ELEMENT FOR REMOVAL: " + el); + elements.add(el); + + if (tt != null) { + terminals.clear(); + tt.getTerminals(el, terminals); + connections.clear(); + for (Terminal terminal : terminals) + topology.getConnections(el, terminal, connections); + for (Connection c : connections) { + if (c.edge != null) { + if (c.edge.getElementClass().containsClass(BendsHandler.class)) + edges.add(c.edge); + if (DEBUG_DELETE) + System.out.println("TERMINAL CONNECTION WILL BE DISCONNECTED: " + c); + } + } + } + } + } + + if (elements.isEmpty() && edges.isEmpty()) + return false; + + if (DEBUG_DELETE) { + System.out.println("gathered elements to delete:"); + System.out.println("\telements:"); + if (!elements.isEmpty()) + for (IElement e : elements) + System.out.println("\t\t" + e); + System.out.println("\tedges:"); + if (!edges.isEmpty()) + for (IElement e : edges) + System.out.println("\t\t" + e); + } + + final IDiagram diagram = this.diagram; + final ISynchronizationContext syncContext = ElementFactoryUtil.getContextChecked(diagram); + + new DatabaseJob("Delete selection") { + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + delete(monitor); + return Status.OK_STATUS; + } catch (CannotRemoveException e) { + ShowMessage.showInformation("Delete Selection Was Denied", e.getLocalizedMessage()); + return new Status(IStatus.CANCEL, Activator.PLUGIN_ID, e.getLocalizedMessage(), e); + } catch (DatabaseException e) { + return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unexpected error in delete.", e); + } finally { + error(null); + monitor.done(); + } + } + + private void delete(IProgressMonitor monitor) throws DatabaseException { + SubMonitor subMonitor = SubMonitor.convert(monitor, 4); + Simantics.getSession().syncRequest(new WriteRequest() { + Set connectionsToRemove = new HashSet(); + Set touchedConnections = new HashSet(); + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + validateRemoval(graph); + graph.markUndoPoint(); + + ConnectionUtil cu = new ConnectionUtil(graph); + SubMonitor edgeElementMonitor = subMonitor.split(2); // Consume 50 % of the monitor + edgeElementMonitor.setWorkRemaining(edges.size() + elements.size()); // set work remaining to size of edges and elements + edgeElementMonitor.setTaskName("Removing edges"); + // Remove edges + for (IElement edge : edges) { + ConnectionEntity ce = edge.getHint(ElementHints.KEY_CONNECTION_ENTITY); + touchConnection( ce.getConnection() ); + + if (DEBUG_DELETE) + System.out.println("REMOVING EDGE: " + edge); + Object obj = ElementUtils.getObject(edge); + if (obj instanceof EdgeResource) { + cu.remove((EdgeResource) obj); + } + edgeElementMonitor.worked(1); + } + edgeElementMonitor.setTaskName("Removing elements"); + // Remove elements + for (IElement element : elements) { + ConnectionHandler ch = element.getElementClass().getAtMostOneItemOfClass(ConnectionHandler.class); + if (ch != null) { + if (DEBUG_DELETE) + System.out.println("MARKING CONNECTION TO BE REMOVED: " + element); + connectionsToRemove.add( (Resource) ElementUtils.getObject(element) ); + } else { + ConnectionEntity ce = element.getHint(ElementHints.KEY_CONNECTION_ENTITY); + if(ce != null) { + if (DEBUG_DELETE) + System.out.println("REMOVING BRANCH POINT: " + element); + new RemoveBranchpoint(element).perform(graph); + touchConnection( ce.getConnection() ); + } else { + if (DEBUG_DELETE) + System.out.println("REMOVING ELEMENT: " + element); + + Object obj = ElementUtils.getObject(element); + if (obj instanceof Resource) { + // Get terminal connections for element + Collection connectors = cu.getTerminalConnectors((Resource) obj, null); + for (Resource connector : connectors) { + Resource connection = ConnectionUtil.tryGetConnection(graph, connector); + if (connection != null) + touchConnection( connection ); + cu.disconnectFromAllRouteNodes(connector); + } + + new RemoveElement((Resource)d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), (Resource)element.getHint(ElementHints.KEY_OBJECT)).perform(graph); + } + } + } + edgeElementMonitor.worked(1); + } + SubMonitor connectionsMonitor = subMonitor.split(1); // Consume 50 % of the monitor + connectionsMonitor.setWorkRemaining(touchedConnections.size()); // set work remaining to size of edges and elements + connectionsMonitor.setTaskName("Pruning connectors"); + // Check all touched connections to see if they are empty. + for (Resource connection : touchedConnections) { + int removedConnectors = cu.removeUnusedConnectors(connection); + if (DEBUG_DELETE) + System.out.println("PRUNED " + removedConnectors + " CONNECTORS FROM TOUCHED CONNECTION " + connection); + while (true) { + int removedInteriorRouteNodes = cu.removeExtraInteriorRouteNodes(connection); + if (DEBUG_DELETE) + System.out.println("PRUNED " + removedInteriorRouteNodes + " INTERIOR ROUTE NODES FROM TOUCHED CONNECTION " + connection); + if (removedInteriorRouteNodes == 0) + break; + } + int connectors = cu.getConnectedConnectors(connection, null).size(); + if (DEBUG_DELETE) + System.out.println("\t" + connectors + " CONNECTORS LEFT"); + if (connectors < 2) { + connectionsToRemove.add(connection); + } + connectionsMonitor.worked(1); + } + SubMonitor connectionsMonitor2 = subMonitor.split(1); // Consume 50 % of the monitor + connectionsMonitor2.setWorkRemaining(connectionsToRemove.size()); // set work remaini + connectionsMonitor2.setTaskName("Removing leftover connections"); + // Remove selected/left-over empty connections + for (Resource connection : connectionsToRemove) { + if (DEBUG_DELETE) + System.out.println("REMOVING CONNECTION: " + connection); + RemoveElement.removeElement(graph, (Resource)d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), connection); + connectionsMonitor2.worked(1); + } + } + + private void validateRemoval(ReadGraph graph) throws DatabaseException, CannotRemoveException { + ArrayList problems = new ArrayList(elements.size()); + for (IElement element : elements) { + Object obj = ElementUtils.getObject(element); + if (obj instanceof Resource) { + Remover remover = RemoverUtil.getPossibleRemover(graph, (Resource) obj); + if (remover != null) { + String problem = remover.canRemove(graph, new HashMap(4)); + if (problem != null) { + problems.add(problem); + } + } + } + } + if (!problems.isEmpty()) { + throw new CannotRemoveException(EString.implode(problems)); + } + } + + void touchConnection(Object c) { + if (DEBUG_DELETE) + System.out.println("TOUCHED CONNECTION: " + c); + if (c instanceof IElement) { + Object obj = ElementUtils.getObject((IElement) c); + if (obj instanceof Resource) + touchedConnections.add((Resource) obj); + } else if (c instanceof Resource) { + touchedConnections.add((Resource) c); + } + } + }); + } + }.schedule(); + + return true; + } + + void message(final String message) { + if (statusLine == null) + return; + swtExec(new Runnable() { + @Override + public void run() { + statusLine.setMessage(message); + statusLine.setErrorMessage(null); + } + }); + } + + void error(final String message) { + if (statusLine == null) + return; + swtExec(new Runnable() { + @Override + public void run() { + statusLine.setErrorMessage(message); + } + }); + } + + void swtExec(Runnable r) { + ThreadUtils.asyncExec(SWTThread.getThreadAccess(Display.getDefault()), r); + } + +}