| /****************************************************************************** |
| * Copyright (c) 2002, 2008 IBM Corporation and others. |
| * 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: |
| * IBM Corporation - initial API and implementation |
| ****************************************************************************/ |
| |
| package org.eclipse.gmf.runtime.diagram.ui.providers.internal; |
| |
| import java.security.InvalidParameterException; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.draw2d.Connection; |
| import org.eclipse.draw2d.geometry.Dimension; |
| import org.eclipse.draw2d.geometry.Point; |
| import org.eclipse.draw2d.geometry.PointList; |
| import org.eclipse.draw2d.geometry.Rectangle; |
| import org.eclipse.emf.transaction.TransactionalEditingDomain; |
| import org.eclipse.gef.EditPart; |
| import org.eclipse.gef.GraphicalEditPart; |
| import org.eclipse.gef.Request; |
| import org.eclipse.gef.commands.Command; |
| import org.eclipse.gef.commands.CompoundCommand; |
| import org.eclipse.gmf.runtime.common.core.command.CommandResult; |
| import org.eclipse.gmf.runtime.common.core.service.IOperation; |
| import org.eclipse.gmf.runtime.common.core.util.Trace; |
| import org.eclipse.gmf.runtime.diagram.core.internal.commands.IPropertyValueDeferred; |
| import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; |
| import org.eclipse.gmf.runtime.diagram.ui.actions.internal.DiagramActionsDebugOptions; |
| import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionNodeEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.internal.properties.Properties; |
| import org.eclipse.gmf.runtime.diagram.ui.internal.requests.ChangeBoundsDeferredRequest; |
| import org.eclipse.gmf.runtime.diagram.ui.internal.services.layout.LayoutNodesOperation; |
| import org.eclipse.gmf.runtime.diagram.ui.providers.internal.l10n.DiagramUIProvidersMessages; |
| import org.eclipse.gmf.runtime.diagram.ui.requests.ArrangeRequest; |
| import org.eclipse.gmf.runtime.diagram.ui.requests.ChangePropertyValueRequest; |
| import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants; |
| import org.eclipse.gmf.runtime.diagram.ui.requests.SetAllBendpointRequest; |
| import org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider; |
| import org.eclipse.gmf.runtime.diagram.ui.services.layout.LayoutType; |
| import org.eclipse.gmf.runtime.draw2d.ui.mapmode.IMapMode; |
| import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; |
| import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; |
| import org.eclipse.gmf.runtime.notation.FontStyle; |
| import org.eclipse.gmf.runtime.notation.NotationPackage; |
| import org.eclipse.gmf.runtime.notation.View; |
| |
| /** |
| * @author sshaw |
| * @canBeSeenBy org.eclipse.gmf.runtime.diagram.ui.providers.* |
| * |
| * RadialProvider class that provides for LayoutType.RADIAL. |
| */ |
| public class RadialProvider |
| extends AbstractLayoutEditPartProvider { |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.common.core.service.IProvider#provides(org.eclipse.gmf.runtime.common.core.service.IOperation) |
| */ |
| public boolean provides(IOperation operation) { |
| Assert.isNotNull(operation); |
| |
| View cview = getContainer(operation); |
| if (cview == null) |
| return false; |
| |
| IAdaptable layoutHint = ((LayoutNodesOperation) operation).getLayoutHint(); |
| String layoutType = (String) layoutHint.getAdapter(String.class); |
| return LayoutType.RADIAL.equals(layoutType); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider#layoutEditParts(org.eclipse.gef.GraphicalEditPart, org.eclipse.core.runtime.IAdaptable) |
| */ |
| public Command layoutEditParts( |
| GraphicalEditPart containerEP, |
| IAdaptable layoutHint) { |
| |
| List children = containerEP.getChildren(); |
| return layout(containerEP, children, findRootView(children), layoutHint); |
| } |
| |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider#layoutEditParts(java.util.List, org.eclipse.core.runtime.IAdaptable) |
| */ |
| public Command layoutEditParts( |
| List selectedObjects, |
| IAdaptable layoutHint) { |
| |
| if (selectedObjects.size()== 0){ |
| return null; |
| } |
| |
| GraphicalEditPart editPart = (GraphicalEditPart) selectedObjects.get(0); |
| GraphicalEditPart containerEditPart = (GraphicalEditPart) editPart.getParent(); |
| |
| return layout(containerEditPart, selectedObjects, findRootView(selectedObjects), layoutHint); |
| } |
| |
| /** |
| * Method layout. |
| * |
| * @param layoutType |
| * @param containerEP |
| * @param selectedObjects |
| * @param rootEP |
| * @return Command |
| * @throws InvalidParameterException |
| * if either parameter is null. |
| */ |
| public Command layout( |
| GraphicalEditPart containerEP, |
| List selectedObjects, |
| ShapeEditPart rootEditPart, |
| IAdaptable layoutHint) { |
| |
| if (containerEP == null || selectedObjects == null) { |
| InvalidParameterException ipe = new InvalidParameterException(); |
| Trace.throwing(DiagramProvidersPlugin.getInstance(), DiagramActionsDebugOptions.EXCEPTIONS_THROWING, getClass(), "layout()", //$NON-NLS-1$ |
| ipe); |
| throw ipe; |
| } |
| |
| if (rootEditPart == null) |
| rootEditPart = findRootView(selectedObjects); |
| |
| List parts = new ArrayList(selectedObjects.size()); |
| |
| // Only add IShapeView to the master list |
| ListIterator li = selectedObjects.listIterator(); |
| while (li.hasNext()) { |
| EditPart ep = (EditPart) li.next(); |
| if (!ep.equals(rootEditPart) |
| && (ep instanceof ShapeEditPart |
| || ep instanceof ConnectionNodeEditPart)) { |
| parts.add(ep); |
| } |
| } |
| |
| Command cmd = null; |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| RadialLayout radialLayout = |
| new RadialLayout(rootEditPart, parts, 0, Math.PI * 2, false); |
| |
| cmd = radialLayout.getPrelayoutCommand(); |
| if (cmd != null) |
| cc.add(cmd); |
| |
| try { |
| cmd = radialLayout.getCommand(); |
| parts.add(rootEditPart); |
| } catch (LayoutEstheticsException e) { |
| // since the Layout esthetics have been violated, use the default layout |
| // instead. |
| parts.add(rootEditPart); |
| |
| ArrangeRequest request = new ArrangeRequest( |
| RequestConstants.REQ_ARRANGE_DEFERRED); |
| request.setViewAdaptersToArrange(parts); |
| cmd = containerEP.getCommand(request); |
| } |
| |
| if (cmd != null) |
| cc.add(cmd); |
| |
| Request req = new Request(RequestConstants.REQ_REFRESH); |
| cmd = rootEditPart.getParent().getCommand(req); |
| if (cmd != null) |
| cc.add(cmd); |
| |
| // position the entire radial circle |
| OffsetRadialPartsCommand orpc = new OffsetRadialPartsCommand( |
| rootEditPart.getEditingDomain(), parts); |
| cmd = new ICommandProxy(orpc); |
| if (cmd != null) |
| cc.add(cmd); |
| |
| return cc; |
| } |
| |
| /** |
| * @author sshaw |
| * |
| * Command to update the entire position of the Radial circle. |
| */ |
| static protected class OffsetRadialPartsCommand extends AbstractTransactionalCommand { |
| private List editParts; |
| private Rectangle origRect; |
| |
| /** |
| * @param editParts |
| * @param ptRoot |
| */ |
| public OffsetRadialPartsCommand(TransactionalEditingDomain editingDomain, List editParts) { |
| super(editingDomain, "", null); //$NON-NLS-1$ |
| this.editParts = editParts; |
| origRect = calcBoundBox(); |
| } |
| |
| protected CommandResult doExecuteWithResult( |
| IProgressMonitor progressMonitor, IAdaptable info) |
| throws ExecutionException { |
| |
| if (null == editParts) |
| return CommandResult.newCancelledCommandResult(); |
| |
| Rectangle radialRect = calcBoundBox(); |
| |
| IGraphicalEditPart firstEP = (IGraphicalEditPart)editParts.get(0); |
| IMapMode mm = MapModeUtil.getMapMode(firstEP.getFigure()); |
| |
| // consider ideal location |
| Rectangle newRadialRect = new Rectangle(radialRect); |
| newRadialRect.translate( -radialRect.getTopLeft().x + mm.DPtoLP(50), |
| -radialRect.getTopLeft().y + mm.DPtoLP(50)); |
| |
| if (origRect.x > newRadialRect.x && origRect.y > newRadialRect.y) { |
| newRadialRect = new Rectangle( Math.max(newRadialRect.x, origRect.x - (radialRect.width / 2)), |
| Math.max(newRadialRect.y, origRect.y - (radialRect.height / 2)), |
| radialRect.width, radialRect.height); |
| } |
| |
| final Point translate = new Point(newRadialRect.getTopLeft().x - radialRect.getTopLeft().x, |
| newRadialRect.getTopLeft().y - radialRect.getTopLeft().y); |
| |
| ListIterator li = editParts.listIterator(); |
| while (li.hasNext()) { |
| IGraphicalEditPart gep = (IGraphicalEditPart)li.next(); |
| View view = gep.getNotationView(); |
| if (view!=null){ |
| Integer pos = (Integer)ViewUtil.getStructuralFeatureValue(view,NotationPackage.eINSTANCE.getLocation_X()); |
| ViewUtil.setStructuralFeatureValue(view,NotationPackage.eINSTANCE.getLocation_X(), new Integer(pos.intValue() + translate.x)); |
| pos = (Integer)ViewUtil.getStructuralFeatureValue(view,NotationPackage.eINSTANCE.getLocation_Y()); |
| ViewUtil.setStructuralFeatureValue(view,NotationPackage.eINSTANCE.getLocation_Y(), new Integer(pos.intValue() + translate.y)); |
| } |
| } |
| |
| // clear for garbage collection; |
| editParts = null; |
| return CommandResult.newOKCommandResult(); |
| } |
| |
| /** |
| * @return |
| */ |
| private Rectangle calcBoundBox() { |
| Rectangle radialRect = null; |
| ListIterator li = editParts.listIterator(); |
| while (li.hasNext()) { |
| IGraphicalEditPart gep = (IGraphicalEditPart)li.next(); |
| if (null == radialRect) { |
| radialRect = new Rectangle(gep.getFigure().getBounds()); |
| } |
| else { |
| radialRect.union(gep.getFigure().getBounds()); |
| } |
| } |
| return null == radialRect ? new Rectangle() : radialRect; |
| } |
| } |
| |
| /** |
| * Method findRootView. Given a list of views, calculate the root view that |
| * all other views are ultimately related to. |
| * |
| * @param views |
| * List of editparts to determine the root view from. |
| * @return ShapeEditPart shape editpart object that represents the root |
| * view. |
| */ |
| protected ShapeEditPart findRootView(List editparts) { |
| if (editparts == null) |
| throw new InvalidParameterException(); |
| |
| // TodoKit: I am sure we must find better ways to dig up the root of a |
| // tree, for now I assume it to be |
| // the first in the collection as I know it was the first view created. |
| int count = editparts.size(); |
| if (count > 0) { |
| EditPart ep = (EditPart) editparts.get(0); |
| if (ep instanceof ShapeEditPart) { |
| return (ShapeEditPart) editparts.remove(0); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @author sshaw |
| * |
| * Nested RuntimeException class thrown when the esthetics of the RadialLayout |
| * are violated. i.e. when certain conditions are met that ensure that the RadialLayout |
| * will not look good. |
| */ |
| static protected class LayoutEstheticsException extends RuntimeException { |
| |
| /** |
| * |
| */ |
| private static final long serialVersionUID = 3084395663087786098L; |
| |
| /** |
| * @param message |
| */ |
| public LayoutEstheticsException(String message) { |
| super(message); |
| } |
| } |
| |
| /** |
| * @author sshaw |
| * |
| * Helper class to build the radial layout based on a root editpart. |
| */ |
| static protected class RadialLayout { |
| |
| private ShapeEditPart rootEP; |
| private List allEditparts = new ArrayList(); |
| private double startTheta; |
| private double totalTheta; |
| private boolean rootPositionLocked; |
| |
| public RadialLayout( |
| ShapeEditPart rootEP, |
| List shapeViews, |
| double startTheta, |
| double totalTheta, |
| boolean rootPositionLocked) { |
| this.rootEP = rootEP; |
| this.allEditparts.addAll(shapeViews); |
| this.startTheta = startTheta; |
| this.totalTheta = totalTheta; |
| this.rootPositionLocked = rootPositionLocked; |
| } |
| |
| /** |
| * Method getRootEditPart. |
| * |
| * @return ShapeEditPart |
| */ |
| public ShapeEditPart getRootEditPart() { |
| return rootEP; |
| } |
| |
| /** |
| * Method getCommand. |
| * |
| * @return Command |
| */ |
| public Command getCommand() throws LayoutEstheticsException { |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| Command cmd = null; |
| List restViews = new ArrayList(); |
| |
| List firstCircleParts = new ArrayList(); |
| |
| int innerRingCount = |
| findChildViews(rootEP, allEditparts, firstCircleParts, restViews); |
| |
| double theta = Math.PI; |
| if (innerRingCount > 1) { |
| theta = totalTheta / innerRingCount; |
| } |
| |
| // initialize the childAndSectionViewMap structure |
| Map childAndSectionMap = new Hashtable(firstCircleParts.size()); |
| List circleSectionParts = null; |
| List firstCircleShapes = new ArrayList(childAndSectionMap.keySet().size()); |
| |
| for (int i = 0; i < firstCircleParts.size(); i++) { |
| EditPart ep = (EditPart) firstCircleParts.get(i); |
| if (ep instanceof ShapeEditPart) { |
| ShapeEditPart shapeEP = (ShapeEditPart) ep; |
| circleSectionParts = |
| new ArrayList(firstCircleParts.size()); |
| |
| List restRestViews = new ArrayList(); |
| findChildViews( |
| shapeEP, |
| restViews, |
| circleSectionParts, |
| restRestViews); |
| |
| firstCircleShapes.add(ep); |
| childAndSectionMap.put(ep, circleSectionParts); |
| childAndSectionMap.put(circleSectionParts, restRestViews); |
| } |
| } |
| |
| CalculateRadialInfoCommand radialInfoCmd = |
| new CalculateRadialInfoCommand(rootEP, firstCircleShapes, theta); |
| cc.add(radialInfoCmd); |
| |
| cmd = positionRings(firstCircleParts, childAndSectionMap, theta, radialInfoCmd); |
| if (cmd != null) |
| cc.add(cmd); |
| |
| // route any extra connection, restViews should only contain |
| // connections by now, all other views has |
| // better be placed already. |
| cmd = routeConnection(firstCircleParts); |
| if (cmd != null) |
| cc.add(cmd); |
| |
| double increaseTheta = theta; |
| cmd = positionNextRings(firstCircleParts, childAndSectionMap, increaseTheta ); |
| if (cmd != null) |
| cc.add(cmd); |
| |
| return cc; |
| } |
| |
| /** |
| * getPrelayoutCommand |
| * Initializes the set of shapes for the layout operation. |
| * @return Cpmmand that will initialize the shapes for the layout operation. |
| */ |
| private Command getPrelayoutCommand() { |
| List restViews = new ArrayList(); |
| List firstCircleParts = new ArrayList(); |
| |
| findChildViews(rootEP, allEditparts, firstCircleParts, restViews); |
| Command cmd = diminishCircle(firstCircleParts); |
| |
| int size = getFontSize(rootEP); |
| int fontAdjust = size / 8; |
| |
| size -= fontAdjust; |
| |
| Command c2 = diminishCircle(restViews, size); |
| if (c2 != null) { |
| if (cmd != null) |
| cmd.chain(c2); |
| else |
| cmd = c2; |
| } |
| |
| Request req = new Request(RequestConstants.REQ_REFRESH); |
| Command c3 = rootEP.getParent().getCommand(req); |
| if (c3 != null) { |
| if (cmd != null) |
| cmd.chain(c3); |
| else |
| cmd = c3; |
| } |
| |
| return cmd; |
| } |
| |
| /** |
| * positionNextRings |
| * Method to handle the recursion of the RadialLayout. |
| * |
| * @param firstCircleParts |
| * @param childAndSectionMap |
| * @param theta |
| * @return |
| */ |
| private Command positionNextRings(List firstCircleParts, Map childAndSectionMap, double theta ) { |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| int n = 0; |
| |
| for (int i = 0; i < firstCircleParts.size(); i++) { |
| double totalThetaPrim = theta; |
| EditPart part = (EditPart) firstCircleParts.get(i); |
| if (part instanceof ShapeEditPart) { |
| List parts = (List)childAndSectionMap.get(part); |
| |
| if (parts != null |
| && !parts.isEmpty()) { |
| int posViewCount = 0; |
| |
| n = i; |
| // check next in list to see if we can increase theta |
| while (i + 1 < firstCircleParts.size()) { |
| Object key = firstCircleParts.get(i+1); |
| List nextViews = (List)childAndSectionMap.get(key); |
| if (null != nextViews && nextViews.size() == 0) { |
| totalThetaPrim = Math.min(Math.PI, totalThetaPrim + theta); |
| i++; |
| } |
| else |
| break; |
| } |
| |
| // count the ShapeEditParts |
| ListIterator li = parts.listIterator(); |
| while (li.hasNext()) { |
| if (li.next() instanceof ShapeEditPart) |
| posViewCount++; |
| } |
| |
| double dTheta = startTheta + (n * theta); |
| double thetaPrim = totalThetaPrim / posViewCount; |
| double startThetaPrim; |
| if (posViewCount < 2) { |
| startThetaPrim = dTheta; |
| } else { |
| startThetaPrim = dTheta - totalThetaPrim / 2 + thetaPrim / 2; |
| } |
| |
| List restRestViews = (List)childAndSectionMap.get(parts); |
| parts.addAll(restRestViews); |
| |
| RadialLayout radialLayout = |
| new RadialLayout((ShapeEditPart)part, parts, startThetaPrim, totalThetaPrim, true); |
| Command cmd = radialLayout.getCommand(); |
| if (cmd != null) |
| cc.add(cmd); |
| } |
| } |
| } |
| |
| if (!cc.isEmpty()) |
| return cc; |
| |
| return null; |
| } |
| |
| /** |
| * @author sshaw |
| * |
| * This class implements IAdaptable so that a deferred point can be |
| * calculated for an edit part. |
| */ |
| static protected class RadialPosition implements IAdaptable { |
| |
| private ShapeEditPart sep; |
| private CalculateRadialInfoCommand radialInfo; |
| private double theta; |
| private boolean useDelta; |
| private Point ptLocation = null; |
| |
| /** |
| * Method RadialPosition. Constructor for the inner ring elements. |
| * |
| * @param sep |
| * @param innerIndex |
| */ |
| public RadialPosition(ShapeEditPart sep, double theta, CalculateRadialInfoCommand radialInfo, boolean useDelta) { |
| this.sep = sep; |
| this.theta = theta; |
| this.radialInfo = radialInfo; |
| this.useDelta = useDelta; |
| } |
| |
| /** |
| * Method getAdapter. |
| * |
| * @param adapterType |
| * @return Object |
| */ |
| public Object getAdapter(Class adapterType) { |
| if (adapterType == IPropertyValueDeferred.class) { |
| return getPosition(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Method getPosition. Calculates the point based on stored |
| * information about the radius and index of the edit part. |
| * |
| * @return Point |
| */ |
| public Point getPosition() { |
| if (null == ptLocation) { |
| ptLocation = new Point(0, 0); |
| |
| if (null != radialInfo) { |
| ptLocation.x = |
| Math.round( |
| radialInfo.getRadius() * ((float) Math.cos(theta)) ); |
| ptLocation.y = |
| Math.round( |
| radialInfo.getRadius() * ((float) Math.sin(theta)) ); |
| |
| if (useDelta) |
| ptLocation.translate(radialInfo.getDelta()); |
| } |
| |
| ptLocation.translate( |
| -sep.getSize().width / 2, |
| -sep.getSize().height / 2); |
| |
| sep = null; |
| radialInfo = null; |
| } |
| |
| return ptLocation; |
| } |
| } |
| |
| /** |
| * Method findChildViews. This method finds all of the child views |
| * relative to a given root view and a set of views. |
| * |
| * @param rootEditPart |
| * ShapeEditPart to be compared against. If a connection ends |
| * on this root editpart the end must be related. |
| * @param editparts |
| * List of a editparts that are used to compare against the |
| * root editpart. |
| * @param childEPs |
| * List that is passed in and populated by the method. It |
| * will be populated the editparts that are related to the |
| * rootView. |
| * @param restEPs |
| * List that is passed in and populated by the method. It |
| * will be populated with the editparts are not related to |
| * the rootView. |
| * @return int number of related shape editparts in the childViews |
| * list. |
| */ |
| protected int findChildViews( |
| ShapeEditPart rootEditPart, |
| List editparts, |
| List childEPs, |
| List restEPs) { |
| if (rootEditPart == null) |
| throw new InvalidParameterException(); |
| |
| if (childEPs == null) |
| throw new InvalidParameterException(); |
| |
| if (restEPs == null) |
| throw new InvalidParameterException(); |
| |
| Set allSet = new HashSet(editparts.size()); |
| int posViewCount = 0; |
| int count = editparts.size(); |
| for (int i = 0; i < count; i++) { |
| EditPart ep = (EditPart) editparts.get(i); |
| allSet.add(ep); |
| } |
| |
| //get a list of the selected connections and the selected |
| //shapes connections |
| List connectionEPs = new ArrayList(); |
| for (int i = 0; i < count; i++) { |
| EditPart ep = (EditPart) editparts.get(i); |
| if (ep instanceof ShapeEditPart) { |
| ShapeEditPart shapeEP = (ShapeEditPart) ep; |
| connectionEPs.addAll(shapeEP.getSourceConnections()); |
| connectionEPs.addAll(shapeEP.getTargetConnections()); |
| } else if (ep instanceof ConnectionNodeEditPart) { |
| connectionEPs.add(ep); |
| } |
| } |
| |
| for (int i = 0; i < connectionEPs.size(); i++) { |
| EditPart ep = (EditPart) connectionEPs.get(i); |
| if (ep instanceof ConnectionNodeEditPart) { |
| ConnectionNodeEditPart connectionEP = |
| (ConnectionNodeEditPart) ep; |
| EditPart fromEP = connectionEP.getSource(); |
| EditPart toEP = connectionEP.getTarget(); |
| |
| EditPart el = null; |
| if (fromEP.equals(rootEditPart)) { |
| el = toEP; |
| } else if (toEP.equals(rootEditPart)) { |
| el = fromEP; |
| } |
| |
| if (el != null && allSet.contains(el)) { |
| childEPs.add(el); |
| posViewCount++; |
| childEPs.add(connectionEP); |
| |
| allSet.remove(el); |
| } |
| } |
| |
| } |
| |
| // If the rest collection was requested, pick out all views that |
| // was not in the child collection. |
| restEPs.addAll(allSet); |
| |
| return posViewCount; |
| } |
| |
| /** |
| * Method getFontSize. Returns the size of the associated font with the |
| * viewEl, |
| * |
| * @param sep |
| * ShapeEditPart element to retrieve the fontdata from. |
| * @return int value of the font size (height). |
| */ |
| protected int getFontSize(ShapeEditPart sep) { |
| if (sep == null) |
| throw new InvalidParameterException(); |
| View view = sep.getNotationView(); |
| if (view!=null){ |
| FontStyle style = (FontStyle) view.getStyle(NotationPackage.eINSTANCE.getFontStyle()); |
| if (style != null) |
| return style.getFontHeight(); |
| } |
| return 9; |
| } |
| |
| /** |
| * Method setFontSize. Sets the new font size for a given view element. |
| * This is a convenience wrapper. The same functionality can be |
| * achieved by using the setPropertyValue api. |
| * |
| * @param viewEl |
| * IView element to retrieve and set the fontdata from. |
| * @param size |
| * value of the new font size (height). |
| * @return Command |
| */ |
| protected Command setFontSize(ShapeEditPart sep, int size) { |
| if (sep == null) |
| throw new InvalidParameterException(); |
| |
| ChangePropertyValueRequest cpvr = new ChangePropertyValueRequest( |
| DiagramUIProvidersMessages.RadialProvider_changeFontRequest_label, |
| Properties.ID_FONTSIZE, new Integer(size)); |
| |
| return getCommand(sep, cpvr, true); |
| } |
| |
| /** |
| * Method diminishInnerCircle. Given a list of views this method will |
| * parse through them and diminish their size by setting the font. |
| * |
| * @param editparts |
| * List of editparts to diminish the size of. |
| * @param fontSize |
| * int value of the new font size. |
| * @return Command |
| * |
| */ |
| protected Command diminishCircle(List editparts) { |
| if (editparts == null) |
| throw new InvalidParameterException(); |
| |
| int count = editparts.size(); |
| |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| |
| //diminish by collapsing all compartments and hiding |
| // connection labels. |
| for (int i = 0; i < count; i++) { |
| EditPart editpart = (EditPart) editparts.get(i); |
| ChangePropertyValueRequest request = null; |
| |
| if (editpart instanceof ShapeEditPart) { |
| request = new ChangePropertyValueRequest( |
| DiagramUIProvidersMessages.RadialProvider_changeVisibilityRequest_label, |
| Properties.ID_ISVISIBLE, Boolean.FALSE); |
| |
| ShapeEditPart shapeEditPart = (ShapeEditPart) editpart; |
| |
| Iterator compartments = shapeEditPart |
| .getResizableCompartments().iterator(); |
| while (compartments.hasNext()) { |
| cc.add(((EditPart) compartments.next()) |
| .getCommand(request)); |
| } |
| |
| |
| } |
| } |
| |
| if (!cc.isEmpty()) |
| return cc; |
| |
| return null; |
| } |
| |
| /** |
| * Method diminishCircle. Given a list of views this method will |
| * parse through them and diminish their size by setting the font and |
| * also by hiding all compartments. |
| * |
| * @param editparts |
| * List of editparts to diminish the size of. |
| * @param fontSize |
| * int value of the new font size. |
| * @return Command |
| * |
| */ |
| protected Command diminishCircle(List editparts, int fontSize) { |
| if (editparts == null) |
| throw new InvalidParameterException(); |
| |
| long count = editparts.size(); |
| |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| |
| // diminish the same as the inner circle first. |
| Command cmd = diminishCircle(editparts); |
| if (cmd != null) |
| cc.add(cmd); |
| |
| //diminish font for outer circle |
| for (int i = 0; i < count; i++) { |
| EditPart ep = (EditPart) editparts.get(i); |
| if (ep instanceof ShapeEditPart) { |
| cmd = setFontSize((ShapeEditPart) ep, fontSize); |
| if (cmd != null) |
| cc.add(cmd); |
| } |
| } |
| |
| if (!cc.isEmpty()) |
| return cc; |
| |
| return null; |
| } |
| |
| /** |
| * sortFirstCircleParts |
| * Sort the circle views in a pattern such that the next iteration of radial |
| * views will be positioned evenly w.r.t. each other. |
| * |
| * @param firstCircleViews List of circle views |
| * @param childAndSectionViewMap Map of next level views. |
| */ |
| protected void sortFirstCircleParts(List firstCircleParts, Map childAndSectionMap) { |
| List firstCircleShapeParts = new ArrayList(firstCircleParts.size()); |
| List rest = new ArrayList(firstCircleParts.size()); |
| ListIterator li = firstCircleParts.listIterator(); |
| while (li.hasNext()) { |
| Object obj = li.next(); |
| if (obj instanceof ShapeEditPart) { |
| firstCircleShapeParts.add(obj); |
| } |
| else |
| rest.add(obj); |
| } |
| |
| // figure out how many empty 2nd line children there are |
| List emptyNextCircleList = new ArrayList(firstCircleParts.size()); |
| List nextCircleList = new ArrayList(firstCircleParts.size()); |
| for (int i = 0; i < firstCircleShapeParts.size(); i++) { |
| List circleList = (List)childAndSectionMap.get(firstCircleShapeParts.get(i)); |
| if (null != circleList && circleList.size() == 0) |
| emptyNextCircleList.add(firstCircleShapeParts.get(i)); |
| else |
| nextCircleList.add(firstCircleShapeParts.get(i)); |
| } |
| |
| firstCircleParts.clear(); |
| if (nextCircleList.size() > 1) { |
| int addInc = firstCircleShapeParts.size() / nextCircleList.size(); |
| int i = 0; |
| while (nextCircleList.size() > 0 || emptyNextCircleList.size() > 0) { |
| if (i % addInc == 0 && nextCircleList.size() > 0) { |
| firstCircleParts.add(nextCircleList.remove(0)); |
| } |
| else { |
| if (emptyNextCircleList.size() > 0) |
| firstCircleParts.add(emptyNextCircleList.remove(0)); |
| } |
| i++; |
| } |
| } |
| else |
| firstCircleParts.addAll(firstCircleShapeParts); |
| |
| firstCircleParts.addAll(rest); |
| } |
| |
| /** |
| * Method positionRings. This method positions the view rings around |
| * the root view. |
| * |
| * @return Command |
| */ |
| protected Command positionRings(List firstCircleParts, Map childAndSectionMap, |
| double theta, CalculateRadialInfoCommand radialInfo) { |
| int n = 0; |
| |
| if (theta < Math.PI / 32 && isRootPositionLocked()) |
| throw new LayoutEstheticsException("Angle is too small to resulting in very large radius");//$NON-NLS-1$ |
| |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| List editParts = new ArrayList(); |
| editParts.add(rootEP); |
| |
| sortFirstCircleParts(firstCircleParts, childAndSectionMap); |
| |
| ListIterator li = firstCircleParts.listIterator(); |
| while (li.hasNext()) { |
| EditPart editpart = (EditPart) li.next(); |
| |
| if (editpart instanceof ShapeEditPart) { |
| ShapeEditPart sep = (ShapeEditPart) editpart; |
| editParts.add(sep); |
| |
| IAdaptable deferredPos = new RadialPosition(sep, startTheta + n * theta, radialInfo, isRootPositionLocked()); |
| |
| ChangeBoundsDeferredRequest request = |
| new ChangeBoundsDeferredRequest(deferredPos); |
| |
| Command cmd = sep.getCommand(request); |
| |
| if (cmd != null) |
| cc.add(cmd); |
| |
| n++; |
| } |
| } |
| |
| if (!isRootPositionLocked()) { |
| IAdaptable deferredRootPos = new RadialPosition(getRootEditPart(), 0, null, isRootPositionLocked()); |
| ChangeBoundsDeferredRequest request = new ChangeBoundsDeferredRequest(deferredRootPos); |
| Command cmd = rootEP.getCommand(request); |
| if (cmd != null) |
| cc.add(cmd); |
| } |
| |
| if (!cc.isEmpty()) |
| return cc; |
| |
| return null; |
| } |
| |
| /** |
| * Method routeConnections. |
| * |
| * @param connections |
| * List of connections that need to be routed. |
| * @return Command |
| */ |
| protected Command routeConnection(List connections) { |
| |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| ListIterator li = connections.listIterator(); |
| |
| while (li.hasNext()) { |
| EditPart editpart = (EditPart) li.next(); |
| if (editpart instanceof ConnectionNodeEditPart) { |
| Command cmd = |
| routeConnection((ConnectionNodeEditPart) editpart); |
| if (cmd != null) |
| cc.add(cmd); |
| } |
| } |
| |
| if (!cc.isEmpty()) |
| return cc; |
| |
| return null; |
| } |
| |
| /** |
| * Method routeConnection. Route the given connection accordingly to the |
| * layout algorithm. TBD utilize the "avoid obstructions" routing. |
| * |
| * @param connectionEP |
| * ConnectionNodeEditPart connection to be routed. |
| * @return Command |
| */ |
| protected Command routeConnection(ConnectionNodeEditPart connectionEP) { |
| if (connectionEP == null) |
| throw new InvalidParameterException(); |
| |
| // reset connections |
| |
| Connection connection = connectionEP.getConnectionFigure(); |
| PointList newPoints = new PointList(2); |
| newPoints.addPoint(connection.getPoints().getFirstPoint()); |
| newPoints.addPoint(connection.getPoints().getLastPoint()); |
| SetAllBendpointRequest request = |
| new SetAllBendpointRequest( |
| RequestConstants.REQ_SET_ALL_BENDPOINT, |
| newPoints); |
| |
| // recurse through the children to get the compound command |
| return connectionEP.getCommand(request); |
| } |
| |
| /** |
| * Method getCommand. Utility function to optionally recurse through |
| * all child edit parts to send the request to. |
| * |
| * @param editpart |
| * EditPart at the Top level to send the command request to. |
| * @param request |
| * Request that is sent to the EditPart and it's children. |
| * @param bRecursive |
| * boolean true if the method is to send the request to all |
| * the children of the editpart as well, false otherwise. |
| * @return Command that is the result of the request to be executed. |
| */ |
| protected Command getCommand( |
| EditPart editpart, |
| Request request, |
| boolean bRecursive) { |
| List children = editpart.getChildren(); |
| ListIterator li = children.listIterator(); |
| |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| Command cmd = editpart.getCommand(request); |
| |
| if (cmd != null) |
| cc.add(cmd); |
| |
| if (bRecursive) { |
| while (li.hasNext()) { |
| IGraphicalEditPart childEP = (IGraphicalEditPart) li.next(); |
| cmd = getCommand(childEP, request, bRecursive); |
| if (cmd != null) |
| cc.add(cmd); |
| } |
| } |
| |
| if (!cc.isEmpty()) |
| return cc; |
| |
| return null; |
| } |
| |
| /** |
| * @author sshaw |
| * |
| * This will perform some interim calculation that depends on the |
| * previous command execution for setting the proper sizes for the view |
| * elements. |
| */ |
| static protected class CalculateRadialInfoCommand extends Command { |
| |
| // deferred calculation values |
| private int radius; |
| private double theta; |
| private ShapeEditPart rootEP; |
| private List firstCircleViews; |
| |
| public CalculateRadialInfoCommand(ShapeEditPart rootEP, List firstCircleViews, double theta) { |
| this.rootEP = rootEP; |
| this.firstCircleViews = firstCircleViews; |
| this.theta = theta; |
| } |
| |
| public void execute() { |
| radius = |
| calculateNeededRadius(firstCircleViews, firstCircleViews.size() * theta); |
| |
| // if innerradius is less than the 2 times the diagonal of the |
| // RootView extend it some. |
| double rootDiagonal = getViewWorstExtent(rootEP); |
| if (2 * rootDiagonal > radius) { |
| radius += rootDiagonal; |
| } |
| |
| radius = Math.max(MapModeUtil.getMapMode(rootEP.getFigure()).DPtoLP(180), radius); |
| } |
| |
| /** |
| * Method calculateNeededRadius. This method calculates the minimum |
| * radius needed to fully extent the given views away from a center |
| * point. |
| * |
| * @param circleEPs |
| * List of editparts that the radius will be calculated |
| * from. |
| * @param sectionAngle |
| * This is the angle in radians that the views will |
| * extent around. |
| * @return int value of the calculated radius. |
| */ |
| protected int calculateNeededRadius( |
| List circleEPs, |
| double sectionAngle) { |
| if (circleEPs == null) |
| throw new InvalidParameterException(); |
| |
| double neededDiameter = 0; |
| int count = circleEPs.size(); |
| double maxDiagonal = 0; |
| |
| for (int i = 0; i < count; i++) { |
| |
| EditPart ep = (EditPart) circleEPs.get(i); |
| if (ep instanceof ShapeEditPart) { |
| ShapeEditPart sep = (ShapeEditPart) ep; |
| |
| double diagonal = getViewWorstExtent(sep); |
| |
| neededDiameter += diagonal; |
| |
| if (diagonal > maxDiagonal) |
| maxDiagonal = diagonal; |
| } |
| |
| } |
| |
| double rad; |
| |
| rad = neededDiameter / sectionAngle; |
| |
| return (int) Math.round(rad); |
| } |
| |
| /** |
| * Method getViewWorstExtent. Determines the worst case extent of a |
| * given view to ensure no intersection occurs. The diagonal of the |
| * view extent is used to for this value. |
| * |
| * @param sep |
| * ShapeEditPart to calcualte to the worst case extent |
| * from. |
| * @return double value of the biggest extent where no intersection |
| * will occur with the view. |
| */ |
| protected double getViewWorstExtent(ShapeEditPart sep) { |
| if (sep == null) |
| throw new InvalidParameterException(); |
| |
| Dimension ext = sep.getSize(); |
| return Math.sqrt( |
| (ext.width * ext.width) + (ext.height * ext.height)) * 0.80; |
| } |
| |
| /** |
| * @return Returns the radius. |
| */ |
| public int getRadius() { |
| return radius; |
| } |
| |
| /** |
| * @return Returns the delta. |
| */ |
| public Point getDelta() { |
| View view = rootEP.getNotationView(); |
| if (view!=null){ |
| Integer posX = (Integer)ViewUtil.getStructuralFeatureValue(view,NotationPackage.eINSTANCE.getLocation_X()); |
| Integer posY = (Integer)ViewUtil.getStructuralFeatureValue(view,NotationPackage.eINSTANCE.getLocation_Y()); |
| return new Point(posX.intValue(), posY.intValue()); |
| } |
| return new Point(0,0); |
| } |
| } |
| |
| /** |
| * @return Returns the rootPositionLocked. |
| */ |
| public boolean isRootPositionLocked() { |
| return rootPositionLocked; |
| } |
| } |
| } |