/******************************************************************************* * <copyright> * * Copyright (c) 2013 SRC * 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 * * </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.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.IWorkbenchPart; 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, 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 */ 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) { ActionRegistry registry = getActionRegistry(); IAction action; action = new UndoAction(this.getWorkbenchPart()); registry.registerAction(action); getStackActions().add(action.getId()); action = new RedoAction(this.getWorkbenchPart()); registry.registerAction(action); getStackActions().add(action.getId()); action = new SelectAllAction(this.getWorkbenchPart()); registry.registerAction(action); action = new DeleteAction(this.getWorkbenchPart()); registry.registerAction(action); getSelectionActions().add(action.getId()); registry.registerAction(new PrintAction(this.getWorkbenchPart())); } } /** * 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().removeCommandStackListener(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().addCommandStackListener(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. if (getWorkbenchPart() != null && getWorkbenchPart().equals(getWorkbenchPart().getSite().getPage().getActivePart())) 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(); }