| /****************************************************************************** |
| * 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; |
| } |
| } |