//------------------------------------------------------------------------------ // Copyright (c) 2005, 2006 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 implementation //------------------------------------------------------------------------------ package org.eclipse.epf.diagram.core.actions; import java.util.Iterator; import java.util.List; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.transaction.impl.InternalTransactionalEditingDomain; import org.eclipse.epf.diagram.core.DiagramCoreResources; import org.eclipse.epf.diagram.core.commands.ChangeBoundsCommand; import org.eclipse.epf.diagram.core.commands.ReconnectLinkCommand; import org.eclipse.epf.diagram.core.util.DiagramCoreUtil; import org.eclipse.gef.DefaultEditDomain; import org.eclipse.gef.EditPart; import org.eclipse.gef.GraphicalViewer; import org.eclipse.gef.commands.CommandStack; import org.eclipse.gef.editparts.AbstractGraphicalEditPart; import org.eclipse.gef.ui.actions.ActionRegistry; import org.eclipse.gef.ui.actions.GEFActionConstants; import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; import org.eclipse.gmf.runtime.diagram.ui.actions.ActionIds; import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionNodeEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeNodeEditPart; import org.eclipse.gmf.runtime.notation.Edge; import org.eclipse.gmf.runtime.notation.Node; import org.eclipse.gmf.runtime.notation.NotationPackage; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; /** * Service class to allow create and register common actions to the action * register. Code is migrate from old method of diagramming. * * @author Shashidhar Kannoori * @since 1.2 */ public class DiagramActionsService { public static final String ALIGN_HORZ_AVERAGE = "align_horizontal_to_average"; //$NON-NLS-1$ public static final String ALIGN_HORZ_FIRST_SELECTED = "align_horizontal_to_first_selection"; //$NON-NLS-1$ public static final String ALIGN_VERT_AVERAGE = "align_vertical_to_average"; //$NON-NLS-1$ public static final String ALIGN_VERT_FIRST_SELECTED = "align_vertical_to_first_selection"; //$NON-NLS-1$ private DefaultEditDomain editDomain; private GraphicalViewer graphicalViewer; private ActionRegistry actionRegistry; private InternalTransactionalEditingDomain transactionDomain; /** * Constructor * @param graphicalViewer {@link GraphicalViewer} * @param editDomain {@link DefaultEditDomain} * @param actionRegistry {@link ActionRegistry} */ public DiagramActionsService(InternalTransactionalEditingDomain transactionDomain, GraphicalViewer graphicalViewer, DefaultEditDomain editDomain, ActionRegistry actionRegistry) { this.transactionDomain = transactionDomain; this.graphicalViewer = graphicalViewer; this.editDomain = editDomain; this.actionRegistry = actionRegistry; } /** * Sets the GraphicalViewer. */ public void setGraphicalViewer(GraphicalViewer graphicalViewer) { this.graphicalViewer = graphicalViewer; } /** * returns GraphicalViewer */ private GraphicalViewer getGraphicalViewer() { return graphicalViewer; } /** * Returns the command stack. * * @return the command stack */ protected CommandStack getCommandStack() { return getEditDomain().getCommandStack(); } /** * Returns the edit domain. * * @return the edit domain */ protected DefaultEditDomain getEditDomain() { return editDomain; } protected ActionRegistry getActionRegistry() { return actionRegistry; } /** * Register HorizontalAlignAverageAction action. * HorizontalAignAverage Action aligns selected editparts to horizontal average. */ public IAction registerHorizontalAlignAverageAction() { // align horizontally to average y-value of all nodes IAction hAlignAverageAction = new Action( DiagramCoreResources.AbstractDiagramEditor_hAlignAverageAction_text) { //$NON-NLS-1$ public void run() { horizAlignToAverageSelected(); } public String getId() { return ALIGN_HORZ_AVERAGE; } }; getActionRegistry().registerAction(hAlignAverageAction); return hAlignAverageAction; } /** * Register HorizontalAlignFirstSelectedAction action. * HorizontalAlignFirstSelectedAction aligns selected editparts with * respect to first selected editpart. */ public IAction registerHorizontalAlignFirstSelectedAction() { // align horizontally to y-value of first selected node IAction hAlignFirstSelectedAction = new Action( DiagramCoreResources.AbstractDiagramEditor_hAlignFirstSelectedAction_text) { //$NON-NLS-1$ public void run() { horzAlignToFirstSelected(); } public String getId() { return ALIGN_HORZ_FIRST_SELECTED; } }; getActionRegistry().registerAction(hAlignFirstSelectedAction); return hAlignFirstSelectedAction; } /** * Register vertialAlignAverageAction action. * vertialAlignAverageAction aligns selected editparts vertically w.r.t average. * */ public IAction registerVerticalAlignAverageAction() { // align vertically to average x-value of all selected nodes IAction vAlignAverageAction = new Action( DiagramCoreResources.AbstractDiagramEditor_vAlignAverageAction_text) { //$NON-NLS-1$ public void run() { verticalAlignToAverageSelected(); } public String getId() { return ALIGN_VERT_AVERAGE; } }; getActionRegistry().registerAction(vAlignAverageAction); return vAlignAverageAction; } /** * Register vertialAlignFirstSelectedAction action. * vertialAlignFirstSelectedAction aligns selected editparts vertically w.r.t first selected. * */ public IAction registerVerticalAlignFirstSelectedAction() { // align vertically to x-value of first selected node IAction vAlignFirstSelectedAction = new Action( DiagramCoreResources.AbstractDiagramEditor_vAlignFirstSelectedAction_text) { //$NON-NLS-1$ public void run() { verticalAlignToFirstSelected(); } public String getId() { return ALIGN_VERT_FIRST_SELECTED; } }; getActionRegistry().registerAction(vAlignFirstSelectedAction); return vAlignFirstSelectedAction; } // ///////////////////////////////////////////////////////////////////////////////////////////////// // action methods // ///////////////////////////////////////////////////////////////////////////////////////////////// /** * Action implementation method for horizontalAlignFirsteSElectedAction. * */ public void horzAlignToFirstSelected() { List editParts = getGraphicalViewer().getSelectedEditParts(); AbstractGraphicalEditPart firstPart = null; // loop through all selected items, remember the y-value of the // first node, // set the following nodes to the same y-value for (int a = 0; a < editParts.size(); a++) { if (editParts.get(a) instanceof ShapeNodeEditPart) { AbstractGraphicalEditPart ep = (AbstractGraphicalEditPart) editParts .get(a); // remember the first part we move if (firstPart == null) { firstPart = ep; } else { // calculate the offset for centerline alignment Rectangle bounds = ep.getFigure().getBounds(); int offset = (int) ((double) (bounds.height - firstPart .getFigure().getBounds().height) / 2.0); // is this node in a container? if (!(ep.getParent() instanceof DiagramEditPart)) { // need to move the container, adjust the offset ep = (AbstractGraphicalEditPart) ep.getParent(); Rectangle parentBounds = ep.getFigure().getBounds(); offset += bounds.y - parentBounds.y; bounds = parentBounds; } // send the command to move the node org.eclipse.gef.commands.Command c = new ChangeBoundsCommand(transactionDomain, (Node) ep.getModel(), new Point(bounds.x, firstPart .getFigure().getBounds().y - offset), bounds.width, -1); getCommandStack().execute(c); } } } } /** * Action implementation method for horizontalAlignAverageAction. * */ public void horizAlignToAverageSelected() { List editParts = getGraphicalViewer().getSelectedEditParts(); AbstractGraphicalEditPart firstPart = null; int total = 0; int ysum = 0; for (int a = 0; a < editParts.size(); a++) { if (editParts.get(a) instanceof ShapeNodeEditPart) { AbstractGraphicalEditPart ep = (AbstractGraphicalEditPart) editParts .get(a); // remember the first part we move if (firstPart == null) { firstPart = ep; } ysum += ep.getFigure().getBounds().y; total++; } } // calculate the average y-value for all nodes int yave = (int) ((double) ysum / (double) total); // loop through all the nodes again and set them for (int a = 0; a < editParts.size(); a++) { if (editParts.get(a) instanceof ShapeNodeEditPart) { AbstractGraphicalEditPart ep = (AbstractGraphicalEditPart) editParts .get(a); // calculate the height difference, so that elements are aligned // through the centerline Rectangle bounds = ep.getFigure().getBounds(); int offset = (int) ((double) (bounds.height - firstPart .getFigure().getBounds().height) / 2.0); // is this node inside a container? if (!(ep.getParent() instanceof DiagramEditPart)) { // we need to move the container, adjust the offset ep = (AbstractGraphicalEditPart) ep.getParent(); Rectangle parentBounds = ep.getFigure().getBounds(); offset += bounds.y - parentBounds.y; bounds = parentBounds; } // send the command to move the node org.eclipse.gef.commands.Command c = new ChangeBoundsCommand(transactionDomain, (Node) ep.getModel(), new Point(bounds.x, yave - offset), bounds.width, -1); getCommandStack().execute(c); } } } /** * Action implementation method for verticalAlignToAverageAction. * */ public void verticalAlignToAverageSelected() { List editParts = getGraphicalViewer().getSelectedEditParts(); AbstractGraphicalEditPart firstPart = null; int total = 0; int xsum = 0; // loop through the selected parts for (int a = 0; a < editParts.size(); a++) { // we are only interested in nodes and containers if (editParts.get(a) instanceof ShapeNodeEditPart) { AbstractGraphicalEditPart ep = (AbstractGraphicalEditPart) editParts .get(a); if (firstPart == null) { firstPart = ep; } // sum the x coordinates of each element xsum += ep.getFigure().getBounds().x; // remember how many selected elements applied to this operation total++; } } // calculate the average x-value for all nodes int xave = (int) ((double) xsum / (double) total); // loop through all the nodes again and set them for (int a = 0; a < editParts.size(); a++) { if (editParts.get(a) instanceof ShapeNodeEditPart) { AbstractGraphicalEditPart ep = (AbstractGraphicalEditPart) editParts .get(a); // calculate the offset for centerline alignment Rectangle bounds = ep.getFigure().getBounds(); int offset = (int) ((double) (bounds.width - firstPart .getFigure().getBounds().width) / 2.0); // are we inside a container? if (!(ep.getParent() instanceof DiagramEditPart)) { // adjust the offset since we have to move the container ep = (AbstractGraphicalEditPart) ep.getParent(); Rectangle parentBounds = ep.getFigure().getBounds(); offset += bounds.x - parentBounds.x; bounds = parentBounds; } // send the command to move the element org.eclipse.gef.commands.Command c = new ChangeBoundsCommand(transactionDomain, (Node) ep.getModel(), new Point(xave - offset, bounds.y), bounds.width, -1); getCommandStack().execute(c); } } } /** * Action implementation method for verticalAlignToFirstSelectedAction. * */ public void verticalAlignToFirstSelected() { // get the selected items List editParts = getGraphicalViewer().getSelectedEditParts(); AbstractGraphicalEditPart alignToPart = null; // find the first diagram part we can align to for (int a = 0; a < editParts.size(); a++) { if (editParts.get(a) instanceof ShapeNodeEditPart) { AbstractGraphicalEditPart ep = (AbstractGraphicalEditPart) editParts .get(a); if (alignToPart == null) { // remember it and stop alignToPart = ep; break; } } } // loop through the elements and set their new position for (int a = 0; a < editParts.size(); a++) { if (editParts.get(a) instanceof ShapeNodeEditPart) { AbstractGraphicalEditPart ep = (AbstractGraphicalEditPart) editParts .get(a); // we can skip the first one since this is an align-to if (ep != alignToPart && !(DiagramCoreUtil.isSyncBar(alignToPart))) { // calculate the offset for centerline alignment if (!(DiagramCoreUtil.isSyncBar(ep))) { Rectangle bounds = ep.getFigure().getBounds(); int offset = (int) ((double) (bounds.width - alignToPart .getFigure().getBounds().width) / 2.0); // is this inside a container? if (!(ep.getParent() instanceof DiagramEditPart)) { // adjust the offset since we are moving the // container ep = (AbstractGraphicalEditPart) ep.getParent(); Rectangle parentBounds = ep.getFigure().getBounds(); offset += bounds.x - parentBounds.x; bounds = parentBounds; } // send the command to move the element org.eclipse.gef.commands.Command c = new ChangeBoundsCommand(transactionDomain, (Node) ep.getModel(), new Point(alignToPart .getFigure().getBounds().x - offset, bounds.y), bounds.width, -1); getCommandStack().execute(c); ep.getFigure().setLocation( new Point(alignToPart.getFigure().getBounds().x - offset, bounds.y)); } else { Point tp = ep.getFigure().getBounds().getCenter(); boolean connected = false; boolean source = false; Point endPoint = null; ConnectionNodeEditPart lp = null; // check target connections List list = ((ShapeNodeEditPart) ep).getTargetConnections(); for (Iterator itor = list.iterator(); itor.hasNext();) { lp = (ConnectionNodeEditPart) itor.next(); if (lp.getSource().equals(alignToPart)) { connected = true; break; } } // check source connections if not found in target connections if (!connected) { list = ((ShapeNodeEditPart) ep).getSourceConnections(); for (Iterator itor = list.iterator(); itor .hasNext();) { lp = (ConnectionNodeEditPart) itor.next(); if (lp.getTarget().equals(alignToPart)) { connected = true; source = true; break; } } } if (connected && (alignToPart instanceof ShapeNodeEditPart)) { Point centerPoint = alignToPart.getFigure() .getBounds().getCenter(); int delta = centerPoint.x - tp.x; endPoint.x += delta; ReconnectLinkCommand c = new ReconnectLinkCommand(transactionDomain, (Edge) lp.getModel(), (Node) ep.getModel(), source); c.setEndPoint(endPoint); getCommandStack().execute(c); } } } // If first selected edit part is syncbarnodeeditpart, need to // handle differently if (ep != alignToPart && !(DiagramCoreUtil.isSyncBar(alignToPart))) { Point sp = alignToPart.getFigure().getBounds() .getCenter(); boolean connected = false; // check if the part is connected via source connections List list = ((ShapeNodeEditPart) alignToPart) .getSourceConnections(); for (Iterator itor = list.iterator(); itor.hasNext();) { ConnectionNodeEditPart lp = (ConnectionNodeEditPart) itor.next(); if (lp.getTarget().equals(ep)) { connected = true; break; } } // if not found above, check if it is connected via target connections if (!connected) { list = ((ShapeNodeEditPart) alignToPart) .getTargetConnections(); for (Iterator itor = list.iterator(); itor.hasNext();) { ConnectionNodeEditPart lp = (ConnectionNodeEditPart) itor.next(); if (lp.getSource().equals(ep)) { connected = true; break; } } } if (DiagramCoreUtil.isSyncBar(ep)) { Point p = new Point(); p.x = ((Integer) ViewUtil.getStructuralFeatureValue((View)ep.getModel(), NotationPackage.eINSTANCE.getLocation_X())).intValue(); p.y = ((Integer) ViewUtil.getStructuralFeatureValue((View)ep.getModel(), NotationPackage.eINSTANCE.getLocation_Y())).intValue(); Point tp = p.getCopy(); Point alignToCenter = alignToPart.getFigure().getBounds().getCenter(); Point centerPoint = ep.getFigure().getBounds().getCenter(); int delta = alignToCenter.x - centerPoint.x; tp.x = tp.x + delta; org.eclipse.gef.commands.Command c = new ChangeBoundsCommand(transactionDomain, (Node) ep.getModel(), tp, ep.getFigure() .getBounds().width, -1); getCommandStack().execute(c); ep.getFigure().setLocation(new Point(tp.x, tp.y)); } else if (ep instanceof ShapeNodeEditPart) { Point p = new Point(); p.x = ((Integer) ViewUtil.getStructuralFeatureValue((View)ep.getModel(), NotationPackage.eINSTANCE.getLocation_X())).intValue(); p.y = ((Integer) ViewUtil.getStructuralFeatureValue((View)ep.getModel(), NotationPackage.eINSTANCE.getLocation_Y())).intValue(); Point tp = p.getCopy(); Point centerPoint = ep.getFigure().getBounds() .getCenter(); int delta = sp.x - centerPoint.x; tp.x = tp.x + delta; org.eclipse.gef.commands.Command c = new ChangeBoundsCommand(transactionDomain, (Node) ep.getModel(), tp, ep.getFigure() .getBounds().width, -1); getCommandStack().execute(c); ep.getFigure().setLocation(new Point(tp.x, tp.y)); } } } } } public void createAlignMenu(IMenuManager menu, boolean canModify) { // create a new menu manager for the cascading menu IContributionItem formatMenu = menu.find(ActionIds.MENU_FORMAT); MenuManager alignMenuManager = null; if (alignMenuManager == null) { alignMenuManager = new MenuManager( DiagramCoreResources.AbstractDiagramEditor_alignMenu_text) { //$NON-NLS-1$ public boolean isEnabled() { int total = 0; // enable the menu only if 2 or more nodes or node // containers // are selected List editParts = graphicalViewer.getSelectedEditParts(); // need at least two things selected to align if (editParts.size() > 1) { for (int a = 0; a < editParts.size(); a++) { EditPart editPart = (EditPart) editParts.get(a); // we can align nodes and containers if (editPart instanceof ShapeNodeEditPart) { // add up the elements we need, there may be // more // elements selected (links, etc.) total++; if (total > 1) { // we only need to know there is more than // 1, so // we can stop here break; } } } } return total > 1; } }; } if (formatMenu != null && formatMenu instanceof IMenuManager && alignMenuManager.isEnabled() && canModify) { IContributionItem item = ((IMenuManager)formatMenu).find(ActionIds.MENU_ALIGN); if(item != null && item instanceof IMenuManager){ IMenuManager alignMenu = (IMenuManager)item; alignMenu.add(getActionRegistry().getAction( DiagramActionsService.ALIGN_HORZ_AVERAGE)); alignMenu.add(getActionRegistry().getAction( DiagramActionsService.ALIGN_HORZ_FIRST_SELECTED)); alignMenu.add(getActionRegistry().getAction( DiagramActionsService.ALIGN_VERT_AVERAGE)); alignMenu.add(getActionRegistry().getAction( DiagramActionsService.ALIGN_VERT_FIRST_SELECTED)); } }else{ alignMenuManager.add(getActionRegistry().getAction( DiagramActionsService.ALIGN_HORZ_AVERAGE)); alignMenuManager.add(getActionRegistry().getAction( DiagramActionsService.ALIGN_HORZ_FIRST_SELECTED)); alignMenuManager.add(getActionRegistry().getAction( DiagramActionsService.ALIGN_VERT_AVERAGE)); alignMenuManager.add(getActionRegistry().getAction( DiagramActionsService.ALIGN_VERT_FIRST_SELECTED)); if (alignMenuManager.isEnabled() && canModify) { menu.appendToGroup(GEFActionConstants.MB_ADDITIONS, new Separator()); menu.appendToGroup(GEFActionConstants.MB_ADDITIONS, alignMenuManager); menu.appendToGroup(GEFActionConstants.MB_ADDITIONS, new Separator()); } } } }