blob: fae8ea4dbecdebef36d98dff52d241f7e27e0a19 [file] [log] [blame]
/*******************************************************************************
* <copyright>
*
* Copyright (c) 2015-2018 SRC, SAP SE
* 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:
* pjpaulin - initial API, implementation and documentation
* jsivadier - Bug 467502 - Improve DiagramComposite implementation without IWorkbenchPart
* mwenz - Bug 531207 - Prepare removal of deprecated CommandStackListener GEF interface
* mwenz - Bug 541019 - NullPointerException in GraphicalComposite.selectionChanged
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.ui.editor;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.IFigure;
import org.eclipse.gef.ContextMenuProvider;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CommandStackEvent;
import org.eclipse.gef.commands.CommandStackEventListener;
import org.eclipse.gef.commands.CommandStackListener;
import org.eclipse.gef.ui.actions.ActionBarContributor;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.DeleteAction;
import org.eclipse.gef.ui.actions.PrintAction;
import org.eclipse.gef.ui.actions.RedoAction;
import org.eclipse.gef.ui.actions.SelectAllAction;
import org.eclipse.gef.ui.actions.UndoAction;
import org.eclipse.gef.ui.actions.UpdateAction;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
import org.eclipse.gef.ui.parts.SelectionSynchronizer;
import org.eclipse.gef.ui.properties.UndoablePropertySheetPage;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.actions.ActionFactory;
/**
* Based on the original GEF GraphicalEditor class, this is a composite that
* supports graphical editing.
*
* @since 0.10
* @experimental This API is in an experimental state and should be used by
* clients only with care, as it not final and can be removed or
* changed without prior notice!
*/
@SuppressWarnings("rawtypes")
public abstract class GraphicalComposite extends Composite
implements CommandStackListener, CommandStackEventListener,
ISelectionListener {
@SuppressWarnings("serial")
private static class ActionIDList extends ArrayList {
@SuppressWarnings("unchecked")
public boolean add(Object o) {
if (o instanceof IAction) {
try {
IAction action = (IAction) o;
o = action.getId();
throw new IllegalArgumentException(
"Action IDs should be added to lists, not the action: " + action); //$NON-NLS-1$
}
catch(IllegalArgumentException exc) {
exc.printStackTrace();
}
}
return super.add(o);
}
}
private DefaultEditDomain editDomain;
private GraphicalViewer graphicalViewer;
private ActionRegistry actionRegistry;
private SelectionSynchronizer synchronizer;
private List selectionActions = new ActionIDList();
private List stackActions = new ActionIDList();
private List propertyActions = new ActionIDList();
/**
* Constructs the editor part
*/
public GraphicalComposite(Composite parent, int style) {
super(parent, style);
setLayout(new FillLayout());
}
/**
* When the command stack changes, the actions interested in the command
* stack are updated.
*
* @param event
* the change event
* @since 0.15
*/
@Override
public void stackChanged(CommandStackEvent event) {
if ((event.getDetail() & CommandStack.POST_MASK) != 0) {
updateActions(stackActions);
}
}
/**
* When the command stack changes, the actions interested in the command
* stack are updated.
*
* @param event
* the change event
* @deprecated Replaced by {@link #stackChanged(CommandStackEvent)} because
* the class {@link CommandStackListener} is deprecated by GEF
*/
@Override
public void commandStackChanged(EventObject event) {
updateActions(stackActions);
}
/**
* Called to configure the graphical viewer before it receives its contents. This is where the
* root editpart should be configured. Subclasses should extend or override this method as
* needed.
*/
protected void configureGraphicalViewer() {
getGraphicalViewer().getControl().setBackground(ColorConstants.listBackground);
}
/**
* Creates actions for this editor. Subclasses should override this method to create and
* register actions with the {@link ActionRegistry}.
*/
@SuppressWarnings("unchecked")
protected void createActions() {
if (getWorkbenchPart() != null) {
IWorkbenchPart workbenchPart = getWorkbenchPart();
ActionRegistry registry = getActionRegistry();
IAction action;
action = new UndoAction(workbenchPart);
registry.registerAction(action);
getStackActions().add(action.getId());
action = new RedoAction(workbenchPart);
registry.registerAction(action);
getStackActions().add(action.getId());
action = new SelectAllAction(workbenchPart);
registry.registerAction(action);
action = new DeleteAction(workbenchPart);
registry.registerAction(action);
getSelectionActions().add(action.getId());
registry.registerAction(new PrintAction(workbenchPart));
}
}
/**
* Creates the GraphicalViewer on the specified <code>Composite</code>.
*
* @param parent
* the parent composite
*/
protected void createGraphicalViewer() {
GraphicalViewer viewer = new ScrollingGraphicalViewer();
viewer.createControl(this);
setGraphicalViewer(viewer);
configureGraphicalViewer();
hookGraphicalViewer();
initializeGraphicalViewer();
}
/**
* Realizes the Editor by creating it's Control.
* <P>
* WARNING: This method may or may not be called by the workbench prior to {@link #dispose()}.
*
* @param parent
* the parent composite
*/
public void createControl() {
createGraphicalViewer();
}
/**
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
public void dispose() {
getCommandStack().removeCommandStackEventListener(this);
if (getWorkbenchPart() != null)
this.getWorkbenchPart().getSite().getWorkbenchWindow().getSelectionService()
.removeSelectionListener(this);
getEditDomain().setActiveTool(null);
getActionRegistry().dispose();
super.dispose();
}
/**
* @see org.eclipse.ui.part.WorkbenchPart#firePropertyChange(int)
*/
protected void firePropertyChange(int property) {
/* TODO Need to communicate this to part somehow */
// super.firePropertyChange(property);
updateActions(propertyActions);
}
/**
* Lazily creates and returns the action registry.
*
* @return the action registry
*/
public ActionRegistry getActionRegistry() {
if (actionRegistry == null)
actionRegistry = new ActionRegistry();
return actionRegistry;
}
/**
* Returns the adapter for the specified key.
*
* <P>
* <EM>IMPORTANT</EM> certain requests, such as the property sheet, may be made before or after
* {@link #createPartControl(Composite)} is called. The order is unspecified by the Workbench.
*
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter(Class type) {
if (type == org.eclipse.ui.views.properties.IPropertySheetPage.class) {
return new UndoablePropertySheetPage(getCommandStack(), getActionRegistry().getAction(
ActionFactory.UNDO.getId()), getActionRegistry().getAction(
ActionFactory.REDO.getId()));
}
if (type == GraphicalViewer.class)
return getGraphicalViewer();
if (type == CommandStack.class)
return getCommandStack();
if (type == ActionRegistry.class)
return getActionRegistry();
if (type == EditPart.class && getGraphicalViewer() != null)
return getGraphicalViewer().getRootEditPart();
if (type == IFigure.class && getGraphicalViewer() != null)
return ((GraphicalEditPart) getGraphicalViewer().getRootEditPart()).getFigure();
return null;
}
/**
* Returns the command stack.
*
* @return the command stack
*/
protected CommandStack getCommandStack() {
return getEditDomain().getCommandStack();
}
/**
* Returns the edit domain.
*
* @return the edit domain
*/
public DefaultEditDomain getEditDomain() {
return editDomain;
}
/**
* Returns the graphical viewer.
*
* @return the graphical viewer
*/
public GraphicalViewer getGraphicalViewer() {
return graphicalViewer;
}
/**
* Returns the list of {@link IAction IActions} dependant on property changes in the Editor.
* These actions should implement the {@link UpdateAction} interface so that they can be updated
* in response to property changes. An example is the "Save" action.
*
* @return the list of property-dependant actions
*/
protected List getPropertyActions() {
return propertyActions;
}
/**
* Returns the list of <em>IDs</em> of Actions that are dependant on changes in the workbench's
* {@link ISelectionService}. The associated Actions can be found in the action registry. Such
* actions should implement the {@link UpdateAction} interface so that they can be updated in
* response to selection changes.
*
* @see #updateActions(List)
* @return the list of selection-dependant action IDs
*/
public List getSelectionActions() {
return selectionActions;
}
/**
* Returns the selection syncronizer object. The synchronizer can be used to sync the selection
* of 2 or more EditPartViewers.
*
* @return the syncrhonizer
*/
protected SelectionSynchronizer getSelectionSynchronizer() {
if (synchronizer == null)
synchronizer = new SelectionSynchronizer();
return synchronizer;
}
/**
* Returns the list of <em>IDs</em> of Actions that are dependant on the CommmandStack's state.
* The associated Actions can be found in the action registry. These actions should implement
* the {@link UpdateAction} interface so that they can be updated in response to command stack
* changes. An example is the "undo" action.
*
* @return the list of stack-dependant action IDs
*/
protected List getStackActions() {
return stackActions;
}
/**
* Hooks the GraphicalViewer to the rest of the Editor. By default, the viewer is added to the
* SelectionSynchronizer, which can be used to keep 2 or more EditPartViewers in sync. The
* viewer is also registered as the ISelectionProvider for the Editor's PartSite.
*/
public void hookGraphicalViewer() {
getSelectionSynchronizer().addViewer(getGraphicalViewer());
if (getWorkbenchPart() != null) {
this.getWorkbenchPart().getSite().setSelectionProvider(getGraphicalViewer());
}
}
protected void init() {
getCommandStack().addCommandStackEventListener(this);
if (getWorkbenchPart() != null) {
this.getWorkbenchPart().getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(this);
initializeActionRegistry();
}
this.createControl();
}
/**
* Initializes the ActionRegistry. This registry may be used by {@link ActionBarContributor
* ActionBarContributors} and/or {@link ContextMenuProvider ContextMenuProviders}.
* <P>
* This method may be called on Editor creation, or lazily the first time
* {@link #getActionRegistry()} is called.
*/
protected void initializeActionRegistry() {
createActions();
updateActions(propertyActions);
updateActions(stackActions);
}
/**
* Override to set the contents of the GraphicalViewer after it has been created.
*
* @see #createGraphicalViewer(Composite)
*/
protected void initializeGraphicalViewer() {
/* may be overridden */
}
/**
* Returns <code>true</code> if the command stack is dirty
*
* @see org.eclipse.ui.ISaveablePart#isDirty()
*/
public boolean isDirty() {
return getCommandStack().isDirty();
}
/**
* @see org.eclipse.ui.ISelectionListener#selectionChanged(IWorkbenchPart,
* ISelection)
*/
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
// If not the active part, ignore selection changed.
IWorkbenchPart workbenchPart = getWorkbenchPart();
if (workbenchPart != null) {
IWorkbenchPartSite site = workbenchPart.getSite();
if (site != null) {
IWorkbenchPage page = site.getPage();
if (page != null) {
IWorkbenchPart activePart = page.getActivePart();
if (workbenchPart.equals(activePart)) {
updateActions(selectionActions);
}
}
}
}
}
/**
* Sets the ActionRegistry for this EditorPart.
*
* @param registry
* the registry
*/
protected void setActionRegistry(ActionRegistry registry) {
actionRegistry = registry;
}
/**
* Sets the EditDomain for this EditorPart.
*
* @param ed
* the domain
*/
public void setEditDomain(DefaultEditDomain editDomain) {
this.editDomain = editDomain;
}
public boolean setFocus() {
return getGraphicalViewer().getControl().setFocus();
}
/**
* Sets the graphicalViewer for this EditorPart.
*
* @param viewer
* the graphical viewer
*/
public void setGraphicalViewer(GraphicalViewer viewer) {
getEditDomain().addViewer(viewer);
this.graphicalViewer = viewer;
}
/**
* A convenience method for updating a set of actions defined by the given List of action IDs.
* The actions are found by looking up the ID in the {@link #getActionRegistry() action
* registry}. If the corresponding action is an {@link UpdateAction}, it will have its
* <code>update()</code> method called.
*
* @param actionIds
* the list of IDs to update
*/
protected void updateActions(List actionIds) {
ActionRegistry registry = getActionRegistry();
Iterator iter = actionIds.iterator();
while (iter.hasNext()) {
IAction action = registry.getAction(iter.next());
if (action instanceof UpdateAction)
((UpdateAction) action).update();
}
}
protected abstract IWorkbenchPart getWorkbenchPart();
}