| /****************************************************************************** |
| * 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 API and implementation |
| ****************************************************************************/ |
| |
| package org.eclipse.gmf.runtime.diagram.ui.commands; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.gef.EditPart; |
| import org.eclipse.gef.Request; |
| import org.eclipse.gef.commands.Command; |
| import org.eclipse.gef.requests.CreateConnectionRequest; |
| import org.eclipse.gmf.runtime.common.core.command.CommandResult; |
| import org.eclipse.gmf.runtime.common.core.util.ObjectAdapter; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.internal.commands.ElementTypeLabelProvider; |
| import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages; |
| import org.eclipse.gmf.runtime.diagram.ui.menus.PopupMenu; |
| import org.eclipse.gmf.runtime.diagram.ui.requests.CreateConnectionViewAndElementRequest; |
| import org.eclipse.gmf.runtime.diagram.ui.requests.CreateConnectionViewRequest; |
| import org.eclipse.gmf.runtime.diagram.ui.requests.CreateUnspecifiedTypeConnectionRequest; |
| import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequestFactory; |
| import org.eclipse.gmf.runtime.emf.type.core.IElementType; |
| import org.eclipse.gmf.runtime.emf.type.core.requests.CreateRelationshipRequest; |
| import org.eclipse.gmf.runtime.emf.ui.services.modelingassistant.ModelingAssistantService; |
| import org.eclipse.gmf.runtime.emf.ui.services.modelingassistant.SelectExistingElementForSourceOperation; |
| import org.eclipse.gmf.runtime.emf.ui.services.modelingassistant.SelectExistingElementForTargetOperation; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.widgets.Display; |
| |
| /** |
| * <p> |
| * A command that pops up a menu which can allow the user to select the type of |
| * connection to be created and whether they want to create a new type or select |
| * an existing element for the other end of the connection. |
| * </p> |
| * |
| * <p> |
| * The <code>getRelationshipTypeAdapter()</code> method returns an adaptable |
| * to the relationship type result. |
| * </p> |
| * |
| * <p> |
| * The <code>getEndAdapter()</code> method returns an adaptable to the end |
| * type result. |
| * </p> |
| * |
| * @author cmahoney |
| */ |
| public class PromptForConnectionAndEndCommand |
| extends PopupMenuCommand { |
| |
| /** |
| * Label provider of the first popup menu with the relationship types. |
| */ |
| protected class ConnectionLabelProvider |
| extends ElementTypeLabelProvider { |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object) |
| */ |
| public String getText(Object element) { |
| String theInputStr = null; |
| if (isDirectionReversed()) |
| theInputStr = DiagramUIMessages.ConnectionHandle_Popup_CreateXFrom; |
| else |
| theInputStr = DiagramUIMessages.ConnectionHandle_Popup_CreateXTo; |
| |
| String text = NLS.bind(theInputStr, super |
| .getText(element)); |
| |
| return text; |
| |
| } |
| } |
| |
| /** |
| * Label provider of the second popup (submenus) for the type of the other |
| * end. |
| */ |
| protected class EndLabelProvider |
| extends ElementTypeLabelProvider { |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object) |
| */ |
| public String getText(Object element) { |
| if (element instanceof IElementType) { |
| String theInputStr = DiagramUIMessages.ConnectionHandle_Popup_NewX; |
| String text = NLS.bind(theInputStr, |
| super.getText(element)); |
| return text; |
| } else { |
| return element.toString(); |
| } |
| } |
| } |
| |
| /** |
| * Label provider of the first and only popup for the type of the other end |
| * when there is only one connection type (e.g. a single relationship type |
| * palette tool is used). |
| */ |
| protected class ConnectionAndEndLabelProvider |
| extends ElementTypeLabelProvider { |
| |
| /** the known connection item */ |
| private Object connectionItem; |
| |
| /** |
| * Creates a new <code>ConnectionAndEndLabelProvider</code>. |
| * |
| * @param connectionType |
| * the single known connection type |
| */ |
| protected ConnectionAndEndLabelProvider(Object connectionItem) { |
| this.connectionItem = connectionItem; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object) |
| */ |
| public String getText(Object element) { |
| String theInputStr = null; |
| if (element instanceof IElementType) { |
| if (isDirectionReversed()) |
| theInputStr = DiagramUIMessages.ConnectionHandle_Popup_CreateXFromNewY; |
| else |
| theInputStr = DiagramUIMessages.ConnectionHandle_Popup_CreateXToNewY; |
| String text = NLS.bind(theInputStr, new Object[] { |
| super.getText(connectionItem), super.getText(element)}); |
| return text; |
| } else { |
| if (isDirectionReversed()) |
| theInputStr = DiagramUIMessages.ConnectionHandle_Popup_CreateXFromY; |
| else |
| theInputStr = DiagramUIMessages.ConnectionHandle_Popup_CreateXToY; |
| String text = NLS.bind(theInputStr, new Object[] { |
| super.getText(connectionItem), super.getText(element)}); |
| return text; |
| } |
| } |
| |
| /** |
| * Gets the connection item. |
| * |
| * @return the connection item |
| */ |
| protected Object getConnectionItem() { |
| return connectionItem; |
| } |
| |
| } |
| |
| /** |
| * This can be added to the content list to add a 'select existing' option. |
| */ |
| private static String EXISTING_ELEMENT = DiagramUIMessages.ConnectionHandle_Popup_ExistingElement; |
| |
| /** Label provider of the popup menu for the connection types. */ |
| private ConnectionLabelProvider connectionLabelProvider; |
| |
| /** Label provider of the submenus for the other end element. */ |
| private static EndLabelProvider endLabelProvider; |
| |
| /** Adapts to the connection type result. */ |
| private ObjectAdapter connectionAdapter = new ObjectAdapter(); |
| |
| /** Adapts to the other end type result. */ |
| private ObjectAdapter endAdapter = new ObjectAdapter(); |
| |
| /** |
| * The request to create a connection. It may contain the connection type or |
| * it may be a <code>CreateUnspecifiedTypeConnectionRequest</code>. |
| */ |
| private CreateConnectionRequest request; |
| |
| /** The container editpart to send the view request to. */ |
| private IGraphicalEditPart containerEP; |
| |
| /** |
| * Creates a new <code>PromptForConnectionAndEndCommand</code>. |
| * |
| * @param request |
| * The request to create a connection. It may contain the |
| * connection type or it may be a |
| * <code>CreateUnspecifiedTypeConnectionRequest</code>. |
| * @param containerEP |
| * The container edit part, where the view and element request to |
| * create the other end is sent. This is used only for testing |
| * that a type is valid for the other end. |
| */ |
| public PromptForConnectionAndEndCommand(CreateConnectionRequest request, |
| IGraphicalEditPart containerEP) { |
| |
| super(DiagramUIMessages.Command_GetRelationshipTypeAndEndFromUser_Label, |
| Display.getCurrent().getActiveShell()); |
| this.request = request; |
| this.containerEP = containerEP; |
| } |
| |
| /** |
| * Gets a list of all the connection items that will represent the |
| * connection choices and will appear in the first part of the popup menu. |
| * |
| * <p> |
| * If the objects in this are not <code>IElementTypes</code> or they |
| * require a special label provider, then |
| * {@link #getConnectionLabelProvider()} should be overridden to provide |
| * this. |
| * </p> |
| * <p> |
| * When this command has executed, the connection adapter result ({@link #getConnectionAdapter()}) |
| * will be populated with the connection item chosen. |
| * </p> |
| * |
| * @return the list of connection items to appear in the popup menu |
| */ |
| protected List getConnectionMenuContent() { |
| List validRelTypes = new ArrayList(); |
| if (request instanceof CreateUnspecifiedTypeConnectionRequest) { |
| List allRelTypes = null; |
| if (((CreateUnspecifiedTypeConnectionRequest) request) |
| .useModelingAssistantService()) { |
| allRelTypes = isDirectionReversed() ? ModelingAssistantService |
| .getInstance().getRelTypesOnTarget(getKnownEnd()) |
| : ModelingAssistantService.getInstance() |
| .getRelTypesOnSource(getKnownEnd()); |
| } else { |
| allRelTypes = ((CreateUnspecifiedTypeConnectionRequest) request) |
| .getElementTypes(); |
| } |
| |
| if (isDirectionReversed()) { |
| validRelTypes = allRelTypes; |
| } else { |
| // Cycle through and make sure each connection type is |
| // supported |
| // for starting a connection on the source. |
| for (Iterator iter = allRelTypes.iterator(); iter.hasNext();) { |
| IElementType rType = (IElementType) iter.next(); |
| if (((CreateConnectionRequest) ((CreateUnspecifiedTypeConnectionRequest) request) |
| .getRequestForType(rType)).getStartCommand() != null) { |
| validRelTypes.add(rType); |
| } |
| } |
| } |
| |
| } else if (request instanceof CreateConnectionViewAndElementRequest) { |
| if (((CreateConnectionViewAndElementRequest) request) |
| .getStartCommand() != null) { |
| validRelTypes |
| .add(((CreateRelationshipRequest) ((CreateConnectionViewAndElementRequest) request) |
| .getConnectionViewAndElementDescriptor() |
| .getCreateElementRequestAdapter().getAdapter( |
| CreateRelationshipRequest.class)).getElementType()); |
| } |
| } else if (request instanceof CreateConnectionViewRequest) { |
| if (((CreateConnectionViewRequest) request).getStartCommand() != null) { |
| Object type = ((CreateConnectionViewRequest) request) |
| .getConnectionViewDescriptor().getElementAdapter() |
| .getAdapter(IElementType.class); |
| if (type != null) { |
| validRelTypes.add(type); |
| } |
| } |
| } |
| return validRelTypes; |
| } |
| |
| /** |
| * Gets a list of all the end items that will represent the other end |
| * choices and will appear in the submenu popup of the given connection |
| * item. |
| * |
| * <p> |
| * If the objects in this are not <code>IElementTypes</code> or they |
| * require a special label provider, then {@link #getEndLabelProvider()} |
| * should be overridden to provide this. |
| * </p> |
| * <p> |
| * When this command has executed, the end adapter result ({@link #getEndAdapter()}) |
| * will be populated with the end item chosen. |
| * </p> |
| * |
| * @param connectionItem |
| * the connection item for which this will be a submenu |
| * @return the list of end items to appear in the popup menu |
| */ |
| protected List getEndMenuContent(Object connectionItem) { |
| if (connectionItem instanceof IElementType) { |
| IElementType connectionType = (IElementType) connectionItem; |
| List menuContent = isDirectionReversed() ? ModelingAssistantService |
| .getInstance().getTypesForSource(getKnownEnd(), connectionType) |
| : ModelingAssistantService.getInstance().getTypesForTarget( |
| getKnownEnd(), connectionType); |
| |
| menuContent = filterUnsupportedNodeTypes(menuContent); |
| |
| if (!menuContent.isEmpty() |
| && supportsExistingElement(connectionType)) { |
| menuContent.add(EXISTING_ELEMENT); |
| } |
| |
| return menuContent; |
| } |
| return Collections.EMPTY_LIST; |
| } |
| |
| /** |
| * Gets the content to be used in the popup menu from the Modeling Assistant |
| * Service and creates the popup menu. |
| * |
| * @return the top-level popup menu |
| */ |
| protected PopupMenu createPopupMenu() { |
| |
| final List connectionMenuContent = getConnectionMenuContent(); |
| |
| if (connectionMenuContent == null || connectionMenuContent.isEmpty()) { |
| return null; |
| } else if (connectionMenuContent.size() == 1) { |
| List menuContent = getEndMenuContent(connectionMenuContent.get(0)); |
| if (menuContent == null || menuContent.isEmpty()) { |
| return null; |
| } |
| |
| ILabelProvider labelProvider = getConnectionAndEndLabelProvider(connectionMenuContent |
| .get(0)); |
| return new PopupMenu(menuContent, labelProvider) { |
| |
| /** |
| * @see org.eclipse.gmf.runtime.diagram.ui.menus.PopupMenu#getResult() |
| */ |
| public Object getResult() { |
| Object endResult = super.getResult(); |
| if (endResult == null) { |
| return null; |
| } else { |
| List resultList = new ArrayList(2); |
| resultList.add(connectionMenuContent.get(0)); |
| resultList.add(endResult); |
| return resultList; |
| } |
| } |
| }; |
| } else { |
| List menuContent = new ArrayList(); |
| for (Iterator iter = connectionMenuContent.iterator(); iter |
| .hasNext();) { |
| Object connectionItem = iter.next(); |
| |
| List subMenuContent = getEndMenuContent(connectionItem); |
| |
| if (subMenuContent.isEmpty()) { |
| continue; |
| } |
| |
| PopupMenu subMenu = new PopupMenu(subMenuContent, |
| getEndLabelProvider()); |
| |
| menuContent.add(new PopupMenu.CascadingMenu(connectionItem, |
| subMenu)); |
| } |
| if (!menuContent.isEmpty()) { |
| return new PopupMenu(menuContent, getConnectionLabelProvider()); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns a new list with all the types from the list given that can be |
| * created. |
| * |
| * @param allTypes |
| * a list of <code>IElementTypes</code>. |
| */ |
| private List filterUnsupportedNodeTypes(List allTypes) { |
| List validTypes = new ArrayList(); |
| for (Iterator iter = allTypes.iterator(); iter.hasNext();) { |
| IElementType type = (IElementType) iter.next(); |
| Request createRequest = CreateViewRequestFactory |
| .getCreateShapeRequest(type, containerEP |
| .getDiagramPreferencesHint()); |
| |
| EditPart target = containerEP.getTargetEditPart(createRequest); |
| if (target != null) { |
| Command cmd = target.getCommand(createRequest); |
| if (cmd != null && cmd.canExecute()) { |
| validTypes.add(type); |
| } |
| } |
| } |
| return validTypes; |
| } |
| |
| public boolean canExecute() { |
| return createPopupMenu() != null; |
| } |
| |
| /** |
| * Pops up the dialog with the content provided. If the user selects 'select |
| * existing', then the select elements dialog also appears. |
| */ |
| protected CommandResult doExecuteWithResult(IProgressMonitor progressMonitor, |
| IAdaptable info) |
| throws ExecutionException { |
| |
| PopupMenu popup = createPopupMenu(); |
| |
| if (popup == null) { |
| return CommandResult.newErrorCommandResult(getLabel()); |
| } |
| |
| setPopupMenu(popup); |
| |
| CommandResult cmdResult = super.doExecuteWithResult(progressMonitor, info); |
| if (!cmdResult.getStatus().isOK()) { |
| return cmdResult; |
| } |
| |
| Object result = cmdResult.getReturnValue(); |
| if (result instanceof List) { |
| List resultList = (List) result; |
| if (resultList.size() == 2) { |
| connectionAdapter.setObject(resultList.get(0)); |
| |
| Object targetResult = resultList.get(1); |
| |
| if (targetResult.equals(EXISTING_ELEMENT)) { |
| targetResult = isDirectionReversed() ? ModelingAssistantService |
| .getInstance().selectExistingElementForSource( |
| getKnownEnd(), (IElementType) resultList.get(0)) |
| : ModelingAssistantService.getInstance() |
| .selectExistingElementForTarget(getKnownEnd(), |
| (IElementType) resultList.get(0)); |
| if (targetResult == null) { |
| return CommandResult.newCancelledCommandResult(); |
| } |
| } |
| endAdapter.setObject(targetResult); |
| return CommandResult.newOKCommandResult(); |
| } |
| } |
| return CommandResult.newErrorCommandResult(getLabel()); |
| } |
| |
| /** |
| * Checks if the <code>ModelingAssistantService</code> supports the |
| * ability to open a dialog for the user to select an existing element |
| * |
| * @param connectionType |
| * @return true if the supported by the modeling assistant service; false |
| * otherwise |
| */ |
| private boolean supportsExistingElement(IElementType connectionType) { |
| if (isDirectionReversed()) { |
| if (ModelingAssistantService.getInstance().provides( |
| new SelectExistingElementForSourceOperation(getKnownEnd(), |
| connectionType))) { |
| return true; |
| } |
| } else if (ModelingAssistantService.getInstance().provides( |
| new SelectExistingElementForTargetOperation(getKnownEnd(), |
| connectionType))) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Gets the connectionAdapter. |
| * |
| * @return Returns the connectionAdapter. |
| */ |
| public ObjectAdapter getConnectionAdapter() { |
| return connectionAdapter; |
| } |
| |
| /** |
| * Gets the endAdapter. |
| * |
| * @return Returns the endAdapter. |
| */ |
| public IAdaptable getEndAdapter() { |
| return endAdapter; |
| } |
| |
| /** |
| * Returns true if the request is a reversed |
| * <code>CreateUnspecifiedTypeConnectionRequest</code>. |
| * |
| * @return Returns true if the request is a reversed |
| * <code>CreateUnspecifiedTypeConnectionRequest</code>; false |
| * otherwise |
| */ |
| protected boolean isDirectionReversed() { |
| return (request instanceof CreateUnspecifiedTypeConnectionRequest && ((CreateUnspecifiedTypeConnectionRequest) request) |
| .isDirectionReversed()); |
| } |
| |
| /** |
| * Gets the known end, which even in the case of a reversed |
| * <code>CreateUnspecifiedTypeConnectionRequest</code>, is the source |
| * editpart. |
| * |
| * @return the known end |
| */ |
| private EditPart getKnownEnd() { |
| return request.getSourceEditPart(); |
| } |
| |
| /** |
| * Gets the label provider that is to be used in the first menu of the popup |
| * where the user is to choose the connection to be created. |
| * |
| * @return the connection label provider |
| */ |
| protected ILabelProvider getConnectionLabelProvider() { |
| if (connectionLabelProvider == null) { |
| connectionLabelProvider = new ConnectionLabelProvider(); |
| } |
| return connectionLabelProvider; |
| } |
| |
| /** |
| * Gets the label provider that is to be used in the second menu of the |
| * popup where the user is to choose the end (could be source or target) to |
| * be created. |
| * |
| * @return the end label provider |
| */ |
| protected ILabelProvider getEndLabelProvider() { |
| if (endLabelProvider == null) { |
| endLabelProvider = new EndLabelProvider(); |
| } |
| return endLabelProvider; |
| } |
| |
| /** |
| * Gets the label provider that is to be used when there is only one option |
| * for the connection type so the popup menu consists of a single menu |
| * identifying the connection type to be created and options for the other |
| * end of which the user must choose |
| * |
| * @param connectionItem |
| * the single known connection item |
| * @return the label provider |
| */ |
| protected ILabelProvider getConnectionAndEndLabelProvider( |
| Object connectionItem) { |
| return new ConnectionAndEndLabelProvider(connectionItem); |
| } |
| |
| } |