//------------------------------------------------------------------------------ // 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.authoring.gef.edit; import java.util.Iterator; import java.util.List; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.epf.authoring.gef.commands.ChangeBoundsCommand; import org.eclipse.epf.authoring.gef.commands.ReconnectLinkCommand; import org.eclipse.epf.authoring.gef.util.DiagramUIResources; import org.eclipse.epf.diagram.model.Link; import org.eclipse.epf.diagram.model.Node; import org.eclipse.gef.DefaultEditDomain; 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.jface.action.Action; import org.eclipse.jface.action.IAction; /** * Service class to allow create and register common action to the action * register. Most of the code are moved from the Editor class. * * @author Jinhua Xi * @since 1.0 */ public class DiagramActionService { 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; /** * Constructor * @param graphicalViewer {@link GraphicalViewer} * @param editDomain {@link DefaultEditDomain} * @param actionRegistry {@link ActionRegistry} */ public DiagramActionService(GraphicalViewer graphicalViewer, DefaultEditDomain editDomain, ActionRegistry actionRegistry) { 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( DiagramUIResources.AbstractDiagramEditor_hAlignAverageAction_text) { 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( DiagramUIResources.AbstractDiagramEditor_hAlignFirstSelectedAction_text) { 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( DiagramUIResources.AbstractDiagramEditor_vAlignAverageAction_text) { 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( DiagramUIResources.AbstractDiagramEditor_vAlignFirstSelectedAction_text) { 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 NodeEditPart || editParts.get(a) instanceof NodeContainerEditPart) { 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( (Node) ep.getModel(), new Point(bounds.x, firstPart .getFigure().getBounds().y - offset), bounds.width); 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 NodeEditPart || editParts.get(a) instanceof NodeContainerEditPart) { 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 NodeEditPart || editParts.get(a) instanceof NodeContainerEditPart) { 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( (Node) ep.getModel(), new Point(bounds.x, yave - offset), bounds.width); 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 NodeEditPart || editParts.get(a) instanceof NodeContainerEditPart) { 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 NodeEditPart || editParts.get(a) instanceof NodeContainerEditPart) { 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( (Node) ep.getModel(), new Point(xave - offset, bounds.y), bounds.width); 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 NodeEditPart || editParts.get(a) instanceof NodeContainerEditPart) { 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 NodeEditPart || editParts.get(a) instanceof NodeContainerEditPart) { AbstractGraphicalEditPart ep = (AbstractGraphicalEditPart) editParts .get(a); // we can skip the first one since this is an align-to if (ep != alignToPart && !(alignToPart instanceof SynchBarNodeEditPart)) { // calculate the offset for centerline alignment if (!(ep instanceof SynchBarNodeEditPart)) { 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( (Node) ep.getModel(), new Point(alignToPart .getFigure().getBounds().x - offset, bounds.y), bounds.width); getCommandStack().execute(c); ep.getFigure().setLocation( new Point(alignToPart.getFigure().getBounds().x - offset, bounds.y)); } else { Point tp = ep.getFigure().getBounds().getLocation(); boolean connected = false; boolean source = false; Point endPoint = null; LinkEditPart lp = null; // check target connections List list = ((NodeEditPart) ep).getTargetConnections(); for (Iterator itor = list.iterator(); itor.hasNext();) { lp = (LinkEditPart) itor.next(); if (lp.getSource().equals(alignToPart)) { endPoint = ((Link) lp.getModel()) .getTargetEndPoint(); tp = tp.getTranslated(endPoint); connected = true; break; } } // check source connections if not found in target connections if (!connected) { list = ((NodeEditPart) ep).getSourceConnections(); for (Iterator itor = list.iterator(); itor .hasNext();) { lp = (LinkEditPart) itor.next(); if (lp.getTarget().equals(alignToPart)) { endPoint = ((Link) lp.getModel()) .getSourceEndPoint(); tp = tp.getTranslated(endPoint); connected = true; source = true; break; } } } if (connected && (alignToPart instanceof NodeEditPart)) { Point centerPoint = alignToPart.getFigure() .getBounds().getCenter(); int delta = centerPoint.x - tp.x; endPoint.x += delta; ReconnectLinkCommand c = new ReconnectLinkCommand( (Link) 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 && (alignToPart instanceof SynchBarNodeEditPart)) { Point sp = alignToPart.getFigure().getBounds() .getLocation(); boolean connected = false; // check if the part is connected via source connections List list = ((NodeEditPart) alignToPart) .getSourceConnections(); for (Iterator itor = list.iterator(); itor.hasNext();) { LinkEditPart lp = (LinkEditPart) itor.next(); if (lp.getTarget().equals(ep)) { Point sourceEndPoint = ((Link) lp.getModel()) .getSourceEndPoint(); sp = sp.getTranslated(sourceEndPoint); connected = true; break; } } // if not found above, check if it is connected via target connections if (!connected) { list = ((NodeEditPart) alignToPart) .getTargetConnections(); for (Iterator itor = list.iterator(); itor.hasNext();) { LinkEditPart lp = (LinkEditPart) itor.next(); if (lp.getSource().equals(ep)) { Point targetEndPoint = ((Link) lp.getModel()) .getTargetEndPoint(); sp = sp.getTranslated(targetEndPoint); connected = true; break; } } } if (ep instanceof SynchBarNodeEditPart) { Point tp = ((Node) ep.getModel()).getLocation() .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( (Node) ep.getModel(), tp, ep.getFigure() .getBounds().width); getCommandStack().execute(c); ep.getFigure().setLocation(new Point(tp.x, tp.y)); } else if (ep instanceof NodeEditPart) { Point tp = ((Node) ep.getModel()).getLocation() .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( (Node) ep.getModel(), tp, ep.getFigure() .getBounds().width); getCommandStack().execute(c); ep.getFigure().setLocation(new Point(tp.x, tp.y)); } } } } } }