/****************************************************************************** * Copyright (c) 2002, 2007 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.actions; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.draw2d.geometry.Point; import org.eclipse.gef.Disposable; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartViewer; import org.eclipse.gef.Request; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.CommandStack; import org.eclipse.gef.commands.CompoundCommand; import org.eclipse.gef.commands.UnexecutableCommand; import org.eclipse.gmf.runtime.common.core.util.StringStatics; import org.eclipse.gmf.runtime.common.ui.action.AbstractActionHandler; import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint; import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramCommandStack; import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramEditDomain; import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramGraphicalViewer; import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; /** * An abstract implementation of a diagram action that follows the * request-command architecture. * * Notice: 1) This action retargets to the active workbench part 2) This action * can either be contributed programatically or through the * <code>ControbutionItemService</code>. * * @author melaasar */ public abstract class DiagramAction extends AbstractActionHandler implements Disposable { /** the target request */ private Request targetRequest; /** the cached operation set */ private List _operationSet = Collections.EMPTY_LIST; /** * Constructs a new diagram action * * @param workbenchPage * The workbench page associated with this action */ public DiagramAction(IWorkbenchPage workbenchPage) { super(workbenchPage); } /** * Constructs a new diagram action. This constructor is provided just in * case a derived class needs to support both the construction of a diagram * action with a workbenchpart. Typically this is only when the diagram * declares its own action in additional to the one registered with the * action serivce. * * @param workbenchpart * The workbench part associated with this action */ protected DiagramAction(IWorkbenchPart workbenchpart) { super(workbenchpart); } /* * (non-Javadoc) * * @see org.eclipse.gmf.runtime.common.ui.action.IDisposableAction#dispose() */ public void dispose() { targetRequest = null; _operationSet = null; super.dispose(); } /* * (non-Javadoc) * * @see org.eclipse.gmf.runtime.common.ui.action.AbstractActionHandler#doRun(org.eclipse.core.runtime.IProgressMonitor) */ protected void doRun(IProgressMonitor progressMonitor) { execute(getCommand(), progressMonitor); } /* * (non-Javadoc) * * @see org.eclipse.gmf.runtime.common.ui.action.IRepeatableAction#refresh() */ public void refresh() { _operationSet = null; // invalidate the cached set updateTargetRequest(); setEnabled(calculateEnabled()); } /** * Calculates the enblement state of the action * * @return <code>true</code> if action should be enabled, * <code>false</code> otherwise */ protected boolean calculateEnabled() { Command command = getCommand(); return command != null && command.canExecute(); } /** * Executes the given {@link Command}. * * @param command * the command to execute * @param progressMonitor * the progress monitor to use during execution */ protected final void execute(Command command, IProgressMonitor progressMonitor) { if (command == null || !command.canExecute()) return; if (getDiagramCommandStack() != null) getDiagramCommandStack().execute(command, progressMonitor); } /** * gives access to the diagram command stack * * @return the diagram command stack */ protected DiagramCommandStack getDiagramCommandStack() { Object stack = getWorkbenchPart().getAdapter(CommandStack.class); return (stack instanceof DiagramCommandStack) ? (DiagramCommandStack) stack : null; } /** * Gets the associated Command with this action based on the target request * * @return a command */ protected Command getCommand() { return getCommand(getTargetRequest()); } /** * Gets a command to execute on the operation set based on a given request * * @param request * request to use to get the command * @return a command */ protected Command getCommand(Request request) { List operationSet = getOperationSet(); Iterator editParts = operationSet.iterator(); CompoundCommand command = new CompoundCommand(getCommandLabel()); while (editParts.hasNext()) { EditPart editPart = (EditPart) editParts.next(); Command curCommand = editPart.getCommand(request); if (curCommand != null) { command.add(curCommand); } } return command.isEmpty() || command.size() != operationSet.size() ? UnexecutableCommand.INSTANCE : (Command) command; } /** * Gets an optional label for the action's executed command * * @return An optional label for the action's executed command */ protected String getCommandLabel() { return StringStatics.BLANK; } /** * Gets a request to be addressed to the operation set * * @return a target request */ protected Request getTargetRequest() { if (targetRequest == null) targetRequest = createTargetRequest(); return targetRequest; } /** * Creates a new target request * * @return the new target request */ protected abstract Request createTargetRequest(); /** * updates the target request. Clients should call this method whenever the * request is expected to be changed */ protected void updateTargetRequest() { // no def impl } /** * Sets the target request to <tt>null</tt>. This will force the creation * of a new target request on the next {@link #getTargetRequest()} call. */ protected void clearTargetRequest() { targetRequest = null; } /** * A utility method to return a list of objects in the current structured * selection * * @return A list of objects in the current structure selection */ protected List getSelectedObjects() { return getStructuredSelection().toList(); } /** * Return the list of editparts considered the operation set after caching * them * * @return A list of editparts conidered the operation set */ protected final List getOperationSet() { if (_operationSet == null) { _operationSet = createOperationSet(); if (_operationSet == null) _operationSet = Collections.EMPTY_LIST; } return _operationSet; } /** * Filters the selected objects and returns only editparts that understands * the request * * @return a list of editparts selected. */ protected List createOperationSet() { List selection = getSelectedObjects(); if (selection.isEmpty() || !(selection.get(0) instanceof IGraphicalEditPart)) return Collections.EMPTY_LIST; Iterator selectedEPs = selection.iterator(); List targetedEPs = new ArrayList(); while (selectedEPs.hasNext()) { EditPart selectedEP = (EditPart) selectedEPs.next(); targetedEPs.addAll(getTargetEditParts(selectedEP)); } return targetedEPs.isEmpty() ? Collections.EMPTY_LIST : targetedEPs; } /** * Given an editpart, returns a list of target editparts to the current * request If no targets could be found, an empty list is returned * * @param editpart * The given editpart * @return a list of target editparts, or Empty list if none */ protected List getTargetEditParts(EditPart editpart) { EditPart targetEP = editpart.getTargetEditPart(getTargetRequest()); return (targetEP == null) ? Collections.EMPTY_LIST : Collections.singletonList(targetEP); } /** * A utility method to get the <code>IDiagramEditDomain</code> from the * current part if it adapts to it * * @return The diagram edit domain adapter if it exists; <code>null</code> * otherwise */ protected IDiagramEditDomain getDiagramEditDomain() { return (IDiagramEditDomain) getWorkbenchPart().getAdapter( IDiagramEditDomain.class); } /** * A utility method to return the active part if it implements * or adapts to the <code>IDiagramWorkbenchPart</code> interface * * @return The current part if it implements or adapts to * <code>IDiagramWorkbenchPart</code>; <code>null</code> * otherwise */ protected IDiagramWorkbenchPart getDiagramWorkbenchPart() { IDiagramWorkbenchPart diagramPart = null; IWorkbenchPart part = getWorkbenchPart(); if (part instanceof IDiagramWorkbenchPart) { diagramPart = (IDiagramWorkbenchPart) part; } else if (part!=null){ diagramPart = (IDiagramWorkbenchPart) part .getAdapter(IDiagramWorkbenchPart.class); } return diagramPart; } /** * A utility method to return the active <code>DiagramEditPart</code> if * the current part implements <code>IDiagramWorkbenchPart</code> * * @return The current diagram if the parts implements * <code>IDiagramWorkbenchPart</code>; <code>null</code> * otherwise */ protected DiagramEditPart getDiagramEditPart() { IDiagramWorkbenchPart part = getDiagramWorkbenchPart(); return part != null ? part.getDiagramEditPart() : null; } /** * A utility method to return the active <code>DiagramEditPart</code> if * the current part implements <code>IDiagramWorkbenchPart</code> * * @return The current diagram if the parts implements * <code>IDiagramWorkbenchPart</code>; <code>null</code> * otherwise */ protected IDiagramGraphicalViewer getDiagramGraphicalViewer() { IDiagramWorkbenchPart part = getDiagramWorkbenchPart(); return part != null ? part.getDiagramGraphicalViewer() : null; } /** * Filters the given list of EditParts so that the list only contains the * EditParts that matches the given condition. * * @param list * the list of edit parts to filter * @param condition * the condition * @return a modified list containing those editparts that matched the * condition */ protected List filterEditPartsMatching(List list, EditPartViewer.Conditional condition) { List matchList = new ArrayList(); Iterator iter = list.iterator(); while (iter.hasNext()) { EditPart ep = (EditPart) iter.next(); if (condition.evaluate(ep)) matchList.add(ep); } return matchList; } /** * Does this action need to listen to selection change events? If the * enablement state of the context menu or the operation set depends on what * is selected in a diagram, then this needs to return true. If this action * targets the diagram only, then it should return false. * <p> * Actions that are only contributed to the popup menu (and not toolbar or * main menus) will not receive selection events at all. The refresh() * method will be called when the context menu is about to show. * </p> */ protected abstract boolean isSelectionListener(); /** * @param targetRequest * The targetRequest to set. */ protected void setTargetRequest(Request targetRequest) { this.targetRequest = targetRequest; } /** * Gets the preferences hint that is to be used to find the appropriate * preference store from which to retrieve diagram preference values. The * preference hint is mapped to a preference store in the preference * registry <@link DiagramPreferencesRegistry>. * * @return the preferences hint */ protected PreferencesHint getPreferencesHint() { if (getDiagramEditPart() != null) { return getDiagramEditPart().getDiagramPreferencesHint(); } return PreferencesHint.USE_DEFAULTS; } /** * Gets the location of the mouse pointer relative to the viewer. * * @return the location of the mouse pointer or null if it cannot be * determined */ protected final Point getMouseLocation() { Display display = Display.getCurrent(); if (display != null) { IDiagramGraphicalViewer viewer = getDiagramGraphicalViewer(); if (viewer != null) { return new Point(viewer.getControl().toControl( display.getCursorLocation())); } } return null; } }