| /******************************************************************************* |
| * <copyright> |
| * |
| * Copyright (c) 2005, 2015 SAP AG. |
| * 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: |
| * SAP AG - initial API, implementation and documentation |
| * mwenz - Enabled sub classes of Graphiti metamodel objects to trigger a refresh |
| * (for Bug 330035 - Notational metamodel extension) |
| * Bug 336488 - DiagramEditor API |
| * Benjamin Schmeling - mwenz - Bug 367483 - Support composite connections |
| * pjpaulin - Bug 352120 - Now uses IDiagramContainerUI interface |
| * mwenz - Bug 481936 - Fix NPE in DiagramChangeListener line 261 on move in Chess editor |
| * |
| * </copyright> |
| * |
| *******************************************************************************/ |
| package org.eclipse.graphiti.ui.internal.editor; |
| |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.emf.common.command.Command; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.transaction.NotificationFilter; |
| import org.eclipse.emf.transaction.ResourceSetChangeEvent; |
| import org.eclipse.emf.transaction.ResourceSetListener; |
| import org.eclipse.emf.transaction.RollbackException; |
| import org.eclipse.gef.EditPart; |
| import org.eclipse.gef.GraphicalViewer; |
| import org.eclipse.graphiti.internal.services.GraphitiInternal; |
| import org.eclipse.graphiti.mm.GraphicsAlgorithmContainer; |
| import org.eclipse.graphiti.mm.MmPackage; |
| import org.eclipse.graphiti.mm.Property; |
| import org.eclipse.graphiti.mm.algorithms.AlgorithmsPackage; |
| import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm; |
| import org.eclipse.graphiti.mm.algorithms.styles.StylesPackage; |
| import org.eclipse.graphiti.mm.pictograms.Anchor; |
| import org.eclipse.graphiti.mm.pictograms.AnchorContainer; |
| import org.eclipse.graphiti.mm.pictograms.Connection; |
| import org.eclipse.graphiti.mm.pictograms.ConnectionDecorator; |
| import org.eclipse.graphiti.mm.pictograms.Diagram; |
| import org.eclipse.graphiti.mm.pictograms.PictogramElement; |
| import org.eclipse.graphiti.mm.pictograms.PictogramsPackage; |
| import org.eclipse.graphiti.services.Graphiti; |
| import org.eclipse.graphiti.ui.editor.DiagramBehavior; |
| import org.eclipse.graphiti.ui.internal.Messages; |
| import org.eclipse.graphiti.ui.internal.T; |
| import org.eclipse.graphiti.ui.internal.parts.ConnectionEditPart; |
| import org.eclipse.swt.widgets.Display; |
| |
| /** |
| * Listener for model changes in the notational model. Is attached to the |
| * <code>ResourceSet</code> provided by the <code>DiagramEditorInternal</code>. |
| * |
| * @noinstantiate This class is not intended to be instantiated by clients. |
| * @noextend This class is not intended to be subclassed by clients. |
| */ |
| public class DiagramChangeListener implements ResourceSetListener { |
| |
| private DiagramRefreshJob diagramRefreshJob; |
| private DiagramBehavior diagramBehavior; |
| |
| public DiagramChangeListener(DiagramBehavior diagramBehavior) { |
| this.diagramBehavior = diagramBehavior; |
| } |
| |
| public NotificationFilter getFilter() { |
| return NotificationFilter.NOT_TOUCH; |
| } |
| |
| public boolean isAggregatePrecommitListener() { |
| return false; |
| } |
| |
| public boolean isPostcommitOnly() { |
| return true; |
| } |
| |
| public boolean isPrecommitOnly() { |
| return false; |
| } |
| |
| public void resourceSetChanged(ResourceSetChangeEvent event) { |
| if (!diagramBehavior.getRefreshBehavior().isAutoRefresh()) { |
| return; |
| } |
| DiagramRefreshJob refreshDiagramJob = getRefreshDiagramJob(); |
| |
| if (!refreshDiagramJob.isRefreshAll()) { |
| GraphicalViewer graphicalViewer = diagramBehavior.getDiagramContainer().getGraphicalViewer(); |
| if (graphicalViewer == null) { |
| return; |
| } |
| // Compute editparts to refresh. |
| List<Notification> notifications = event.getNotifications(); |
| if (notifications.size() > 5000) { |
| refreshDiagramJob.setRefreshAll(); |
| } else { |
| for (Notification notification : notifications) { |
| Object notifier = notification.getNotifier(); |
| if (!(notifier instanceof EObject)) |
| continue; |
| EObject eo = (EObject) notifier; |
| // Do not work with dangling objects |
| if (!GraphitiInternal.getEmfService().isObjectAlive(eo)) |
| continue; |
| // If the diagrameditpart was added to the job, it is in refresh all mode, |
| // and further processing makes no sense. |
| if (refreshDiagramJob.isRefreshAll()) |
| break; |
| // Filter non-pictogram model element changes, these are handled by the framework |
| EClass eClass = eo.eClass(); |
| long time = System.currentTimeMillis(); |
| boolean isRelevant = isGraphitiMmObject(eClass) || superClassIsGraphitiMmObject(eClass); |
| if (T.racer().info()) { |
| time = System.currentTimeMillis() - time; |
| T.racer().info(getClass().getName(), "resourceSetChanged", "Relevance check took " + time + "ms."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| if (!(isRelevant)) { |
| continue; |
| } |
| // Compute editpart for eo and add it to job's editpart list. |
| PictogramElement activeContainerPe = calculateActiveContainerPe(eo); |
| if (activeContainerPe != null) { |
| Map<?, ?> editPartRegistry = graphicalViewer.getEditPartRegistry(); |
| Object o = editPartRegistry.get(activeContainerPe); |
| if (o instanceof EditPart) { |
| EditPart affectedEditPart = (EditPart) o; |
| refreshDiagramJob.addEditPart(affectedEditPart); |
| addRelevantChildEditPartsToRefreshJob(activeContainerPe); |
| } |
| } |
| } |
| } |
| } |
| // Avoid unnecessary scheduling. |
| if (refreshDiagramJob.shouldBeRun()) { |
| // If we are in the UI thread we update immediately to avoid flickering. |
| if (Display.getCurrent() != null) { |
| refreshDiagramJob.runInUIThread(new NullProgressMonitor()); |
| } else { |
| refreshDiagramJob.schedule(); |
| } |
| } |
| } |
| |
| /** |
| * Checks if the given {@link EClass} belongs to one of the metamodel |
| * packages of the Graphiti metamodel |
| * |
| * @param eClass |
| * @return |
| */ |
| private boolean isGraphitiMmObject(EClass eClass) { |
| EPackage p = eClass.getEPackage(); |
| return p instanceof PictogramsPackage || p instanceof AlgorithmsPackage || p instanceof StylesPackage || p instanceof MmPackage; |
| } |
| |
| /** |
| * Checks if one of the super classes of the given {@link EClass} belongs to |
| * one of the metamodel packages of the Graphiti metamodel |
| * |
| * @param eClass |
| * @return |
| */ |
| private boolean superClassIsGraphitiMmObject(EClass eClass) { |
| EList<EClass> superTypes = eClass.getESuperTypes(); |
| for (Iterator<EClass> iterator = superTypes.iterator(); iterator.hasNext();) { |
| EClass eSuperClass = iterator.next(); |
| if (isGraphitiMmObject(eSuperClass)) { |
| return true; |
| } |
| } |
| for (Iterator<EClass> iterator = superTypes.iterator(); iterator.hasNext();) { |
| EClass eSuperClass = iterator.next(); |
| if (superClassIsGraphitiMmObject(eSuperClass)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private void addRelevantChildEditPartsToRefreshJob(PictogramElement pe) { |
| Map<?, ?> editPartRegistry = diagramBehavior.getDiagramContainer().getGraphicalViewer().getEditPartRegistry(); |
| DiagramRefreshJob refreshJob = getRefreshDiagramJob(); |
| |
| if (pe instanceof AnchorContainer) { |
| AnchorContainer anchorContainer = (AnchorContainer) pe; |
| Collection<Anchor> anchors = anchorContainer.getAnchors(); |
| for (Iterator<Anchor> iter = anchors.iterator(); iter.hasNext();) { |
| Anchor anchor = iter.next(); |
| Collection<Connection> incomingConnections = anchor.getIncomingConnections(); |
| for (Iterator<Connection> iterator = incomingConnections.iterator(); iterator.hasNext();) { |
| Connection connection = iterator.next(); |
| refreshJob.addEditPart((EditPart) editPartRegistry.get(connection)); |
| addRelevantChildEditPartsToRefreshJob(connection); |
| } |
| Collection<Connection> outgoingConnections = anchor.getOutgoingConnections(); |
| for (Iterator<Connection> iterator = outgoingConnections.iterator(); iterator.hasNext();) { |
| Connection connection = iterator.next(); |
| refreshJob.addEditPart((EditPart) editPartRegistry.get(connection)); |
| addRelevantChildEditPartsToRefreshJob(connection); |
| } |
| } |
| } |
| |
| if (pe instanceof Connection) { |
| Connection connection = (Connection) pe; |
| for (Iterator<ConnectionDecorator> iter = connection.getConnectionDecorators().iterator(); iter.hasNext();) { |
| ConnectionDecorator connectionDecorator = iter.next(); |
| if (connectionDecorator.isActive()) { |
| Object object = editPartRegistry.get(connectionDecorator); |
| if (object instanceof EditPart) { |
| refreshJob.addEditPart((EditPart) object); |
| } else if (object == null) { |
| refreshJob.setRefreshAll(); |
| return; |
| } |
| } |
| } |
| // Compute if a target or source editpart changed and refresh accordingly. |
| AnchorContainer end = ((Connection) pe).getEnd() != null ? ((Connection) pe).getEnd().getParent() : null; |
| AnchorContainer start = ((Connection) pe).getStart() != null ? ((Connection) pe).getStart().getParent() : null; |
| Object endPart = editPartRegistry.get(end); |
| Object startPart = editPartRegistry.get(start); |
| EditPart connPart = (EditPart) editPartRegistry.get(connection); |
| // Only refresh the editpart that changed. |
| if (connPart instanceof ConnectionEditPart) { |
| ConnectionEditPart cep = (ConnectionEditPart) connPart; |
| if (cep.getSource() != null && !cep.getSource().equals(startPart) && startPart != null) { |
| refreshJob.addEditPart((EditPart) startPart); |
| } else if (cep.getTarget() != null && !cep.getTarget().equals(endPart) && endPart != null) { |
| refreshJob.addEditPart((EditPart) endPart); |
| } |
| |
| } |
| } |
| } |
| |
| private PictogramElement calculateActiveContainerPe(EObject affectedElement) { |
| if (affectedElement instanceof PictogramElement) { |
| PictogramElement pe = (PictogramElement) affectedElement; |
| if (pe instanceof org.eclipse.graphiti.mm.pictograms.ChopboxAnchor) { |
| pe = ((Anchor) pe).getParent(); |
| } |
| if (pe instanceof Connection){ |
| Diagram diagram = ((Connection)pe).getParent(); |
| return diagram; |
| } |
| if (pe != null && pe.isActive()) { |
| return pe; |
| } |
| } |
| |
| GraphicsAlgorithmContainer gac = null; |
| if (affectedElement instanceof GraphicsAlgorithmContainer) { |
| gac = (GraphicsAlgorithmContainer) affectedElement; |
| } else if (affectedElement instanceof org.eclipse.graphiti.mm.algorithms.styles.Point) { |
| org.eclipse.graphiti.mm.algorithms.styles.Point p = (org.eclipse.graphiti.mm.algorithms.styles.Point) affectedElement; |
| EObject eContainer = p.eContainer(); |
| if (eContainer instanceof GraphicsAlgorithmContainer) { |
| gac = (GraphicsAlgorithmContainer) eContainer; |
| } |
| } else if (affectedElement instanceof Property) { |
| // Bugzilla 326733: a property has been changed -> update its parent |
| // GraphicsAlgorithm |
| Property property = (Property) affectedElement; |
| EObject container = property.eContainer(); |
| if (container instanceof GraphicsAlgorithmContainer) { |
| gac = (GraphicsAlgorithmContainer) container; |
| } |
| } |
| |
| PictogramElement ret = null; |
| if (gac != null) { |
| if (gac instanceof PictogramElement) { |
| ret = (PictogramElement) gac; |
| } else if (gac instanceof GraphicsAlgorithm) { |
| ret = Graphiti.getPeService().getActiveContainerPe((GraphicsAlgorithm) gac); |
| } |
| } |
| return ret; |
| } |
| |
| public Command transactionAboutToCommit(ResourceSetChangeEvent event) throws RollbackException { |
| return null; |
| } |
| |
| private DiagramRefreshJob getRefreshDiagramJob() { |
| if (diagramRefreshJob == null) { |
| diagramRefreshJob = new DiagramRefreshJob(Messages.DiagramEditor_0_xmsg, diagramBehavior); |
| } |
| return diagramRefreshJob; |
| } |
| |
| public boolean stopListening() { |
| return getRefreshDiagramJob().cancel(); |
| } |
| |
| } |
| |
| //----------------------alternative listener-------------- |
| // listening as EContentAdapter yields tons of single events. |
| // the above solution works batched, which should result in better performance. |
| |
| //package org.eclipse.graphiti.ui.internal.editor; |
| // |
| //import java.util.Collection; |
| //import java.util.Iterator; |
| //import java.util.Map; |
| // |
| //import org.eclipse.emf.common.notify.Notification; |
| //import org.eclipse.emf.ecore.EObject; |
| //import org.eclipse.emf.ecore.util.EContentAdapter; |
| //import org.eclipse.gef.EditPart; |
| //import org.eclipse.gef.GraphicalViewer; |
| // |
| //import org.eclipse.graphiti.mm.pictograms.Anchor; |
| //import org.eclipse.graphiti.mm.pictograms.AnchorContainer; |
| //import org.eclipse.graphiti.mm.pictograms.Connection; |
| //import org.eclipse.graphiti.mm.pictograms.ConnectionDecorator; |
| //import org.eclipse.graphiti.mm.pictograms.GraphicsAlgorithm; |
| //import org.eclipse.graphiti.mm.pictograms.GraphicsAlgorithmContainer; |
| //import org.eclipse.graphiti.mm.pictograms.PictogramElement; |
| //import org.eclipse.graphiti.ui.Messages; |
| //import org.eclipse.graphiti.util.PeUtil; |
| //import org.eclipse.graphiti.util.T; |
| // |
| ///** |
| // * Package-private listener used to update the DiagramEditorInternal. See |
| // * DiagramChangeListener2 for a more efficient version. |
| // * |
| // */ |
| //class DiagramChangeListener extends EContentAdapter { |
| // private DiagramRefreshJob diagramRefreshJob; |
| // private DiagramEditorInternal diagramBehavior; |
| // |
| // DiagramChangeListener(DiagramEditorInternal diagramBehavior) { |
| // super(); |
| // this.ed = diagramBehavior; |
| // } |
| // |
| // @Override |
| // public void notifyChanged(Notification notification) { |
| // super.notifyChanged(notification); |
| // |
| // if (!diagramBehavior.isAutoRefresh()) { |
| // return; |
| // } |
| // if (GFTestConfiguration.isCPUProfilingTraceActive()) { |
| // if (T.racer().info()) { |
| // T.racer().info("DiagramChangeListener.notifyChanged(Notification notification)"); //$NON-NLS-1$ |
| // } |
| // } |
| // notify(notification); |
| // } |
| // |
| // private void addRelevantChildEditPartsToRefreshJob(PictogramElement pe) { |
| // Map editPartRegistry = diagramBehavior.getGraphicalViewer().getEditPartRegistry(); |
| // DiagramRefreshJob refreshJob = getRefreshDiagramJob(); |
| // |
| // if (pe instanceof AnchorContainer) { |
| // AnchorContainer anchorContainer = (AnchorContainer) pe; |
| // Collection<Anchor> anchors = anchorContainer.getAnchors(); |
| // for (Iterator<Anchor> iter = anchors.iterator(); iter.hasNext();) { |
| // Anchor anchor = iter.next(); |
| // Collection<Connection> incomingConnections = anchor.getIncomingConnections(); |
| // for (Iterator<Connection> iterator = incomingConnections.iterator(); iterator.hasNext();) { |
| // Connection connection = iterator.next(); |
| // refreshJob.addEditPart((EditPart) editPartRegistry.get(connection)); |
| // addRelevantChildEditPartsToRefreshJob(connection); |
| // } |
| // Collection<Connection> outgoingConnections = anchor.getOutgoingConnections(); |
| // for (Iterator<Connection> iterator = outgoingConnections.iterator(); iterator.hasNext();) { |
| // Connection connection = iterator.next(); |
| // refreshJob.addEditPart((EditPart) editPartRegistry.get(connection)); |
| // addRelevantChildEditPartsToRefreshJob(connection); |
| // } |
| // } |
| // } |
| // |
| // if (pe instanceof Connection) { |
| // Connection connection = (Connection) pe; |
| // for (Iterator<ConnectionDecorator> iter = connection.getConnectionDecorators().iterator(); iter.hasNext();) { |
| // ConnectionDecorator connectionDecorator = iter.next(); |
| // if (connectionDecorator.isActive()) { |
| // Object object = editPartRegistry.get(connectionDecorator); |
| // if (object instanceof EditPart) { |
| // refreshJob.addEditPart((EditPart) object); |
| // } |
| // } |
| // } |
| // } |
| // } |
| // |
| // private PictogramElement calculateActiveContainerPe(EObject affectedElement) { |
| // if (affectedElement instanceof PictogramElement) { |
| // PictogramElement pe = (PictogramElement) affectedElement; |
| // if (pe.isActive()) { |
| // return pe; |
| // } |
| // } |
| // |
| // GraphicsAlgorithmContainer gac = null; |
| // if (affectedElement instanceof GraphicsAlgorithmContainer) { |
| // gac = (GraphicsAlgorithmContainer) affectedElement; |
| // } else if (affectedElement instanceof org.eclipse.graphiti.mm.datatypes.Point) { |
| // // org.eclipse.graphiti.mm.datatypes.Point p = |
| // // (org.eclipse.graphiti.mm.datatypes.Point) affectedElement; |
| // // RefFeatured refImmediateComposite = |
| // // p.refImmediateComposite(); |
| // // if (refImmediateComposite instanceof |
| // // GraphicsAlgorithmContainer) { |
| // // gac = (GraphicsAlgorithmContainer) refImmediateComposite; |
| // // } |
| // } |
| // |
| // PictogramElement ret = null; |
| // if (gac != null) { |
| // if (gac instanceof PictogramElement) { |
| // ret = (PictogramElement) gac; |
| // } else if (gac instanceof GraphicsAlgorithm) { |
| // ret = PeUtil.getActiveContainerPe((GraphicsAlgorithm) gac); |
| // } |
| // } |
| // return ret; |
| // } |
| // |
| // private DiagramRefreshJob getRefreshDiagramJob() { |
| // if (diagramRefreshJob == null) { |
| // diagramRefreshJob = new DiagramRefreshJob(Messages.DiagramEditor_0_xmsg, diagramBehavior); |
| // } |
| // return diagramRefreshJob; |
| // } |
| // |
| // private void notify(Notification notification) { |
| // boolean singleEditPart = false; |
| // DiagramRefreshJob refreshDiagramJob = getRefreshDiagramJob(); |
| // if (!refreshDiagramJob.isRefreshAll()) { |
| // GraphicalViewer graphicalViewer = diagramBehavior.getGraphicalViewer(); |
| // if (graphicalViewer == null) { |
| // return; |
| // } |
| // Object notifier = notification.getNotifier(); |
| // if (notifier instanceof EObject) { |
| // EObject eo = (EObject) notifier; |
| // PictogramElement activeContainerPe = calculateActiveContainerPe(eo); |
| // if (activeContainerPe != null) { |
| // Map editPartRegistry = graphicalViewer.getEditPartRegistry(); |
| // Object o = editPartRegistry.get(activeContainerPe); |
| // if (o instanceof EditPart) { |
| // EditPart affectedEditPart = (EditPart) o; |
| // refreshDiagramJob.addEditPart(affectedEditPart); |
| // singleEditPart = true; |
| // addRelevantChildEditPartsToRefreshJob(activeContainerPe); |
| // } |
| // } |
| // |
| // } |
| // |
| // if (!singleEditPart) { |
| // refreshDiagramJob.setRefreshAll(); |
| // } |
| // } |
| // |
| // refreshDiagramJob.schedule(1); |
| // } |
| // |
| // boolean stopListening() { |
| // return getRefreshDiagramJob().cancel(); |
| // } |
| //} |