/*******************************************************************************
* Copyright (c) 2000, 2005 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.gef.ui.parts;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.views.properties.PropertySheetPage;
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.SaveAction;
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.properties.UndoablePropertySheetEntry;
/**
* This class serves as a quick starting point for clients who are new to GEF. It will
* create an Editor containing a single GraphicalViewer as its control.
* <P>
* <EM>IMPORTANT</EM>This class should only be used as a reference for creating your own
* EditorPart implementation. This class will not suit everyone's needs, and may change in
* the future. Clients may copy the implementation.
* @author hudsonr
*/
public abstract class GraphicalEditor
extends EditorPart
implements CommandStackListener, ISelectionListener
{
private static class ActionIDList extends ArrayList {
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 GraphicalEditor() { }
/**
* 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}.
*/
protected void createActions() {
ActionRegistry registry = getActionRegistry();
IAction action;
action = new UndoAction(this);
registry.registerAction(action);
getStackActions().add(action.getId());
action = new RedoAction(this);
registry.registerAction(action);
getStackActions().add(action.getId());
action = new SelectAllAction(this);
registry.registerAction(action);
action = new DeleteAction((IWorkbenchPart)this);
registry.registerAction(action);
getSelectionActions().add(action.getId());
action = new SaveAction(this);
registry.registerAction(action);
getPropertyActions().add(action.getId());
registry.registerAction(new PrintAction(this));
}
/**
* Creates the GraphicalViewer on the specified <code>Composite</code>.
* @param parent the parent composite
*/
protected void createGraphicalViewer(Composite parent) {
GraphicalViewer viewer = new ScrollingGraphicalViewer();
viewer.createControl(parent);
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 createPartControl(Composite parent) {
createGraphicalViewer(parent);
}
/**
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
public void dispose() {
getCommandStack().removeCommandStackListener(this);
getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(this);
getEditDomain().setActiveTool(null);
getActionRegistry().dispose();
super.dispose();
}
/**
* Does nothing be default. This method should be overridden if {@link #isSaveAsAllowed()}
* has been overridden to return <code>true</code>.
* @see org.eclipse.ui.ISaveablePart#doSaveAs()
*/
public void doSaveAs() {
throw new RuntimeException("doSaveAs must be overridden"); //$NON-NLS-1$
}
/**
* @see org.eclipse.ui.part.WorkbenchPart#firePropertyChange(int)
*/
protected void firePropertyChange(int property) {
super.firePropertyChange(property);
updateActions(propertyActions);
}
/**
* Lazily creates and returns the action registry.
* @return the action registry
*/
protected 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) {
PropertySheetPage page = new PropertySheetPage();
page.setRootEntry(new UndoablePropertySheetEntry(getCommandStack()));
return page;
}
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 super.getAdapter(type);
}
/**
* Returns the command stack.
* @return the command stack
*/
protected CommandStack getCommandStack() {
return getEditDomain().getCommandStack();
}
/**
* Returns the edit domain.
* @return the edit domain
*/
protected DefaultEditDomain getEditDomain() {
return editDomain;
}
/**
* Returns the graphical viewer.
* @return the graphical viewer
*/
protected 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
*/
protected 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.
*/
protected void hookGraphicalViewer() {
getSelectionSynchronizer().addViewer(getGraphicalViewer());
getSite().setSelectionProvider(getGraphicalViewer());
}
/**
* Sets the site and input for this editor then creates and initializes the actions.
* Subclasses may extend this method, but should always call <code>super.init(site, input)
* </code>.
* @see org.eclipse.ui.IEditorPart#init(IEditorSite, IEditorInput)
*/
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
setSite(site);
setInput(input);
getCommandStack().addCommandStackListener(this);
getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(this);
initializeActionRegistry();
}
/**
* 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 abstract void initializeGraphicalViewer();
/**
* Returns <code>true</code> if the command stack is dirty
* @see org.eclipse.ui.ISaveablePart#isDirty()
*/
public boolean isDirty() {
return getCommandStack().isDirty();
}
/**
* Returns <code>false</code> by default. Subclasses must return <code>true</code> to
* allow {@link #doSaveAs()} to be called.
* @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed()
*/
public boolean isSaveAsAllowed() {
return false;
}
/**
* @see org.eclipse.ui.ISelectionListener#selectionChanged(IWorkbenchPart, ISelection)
*/
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
// If not the active editor, ignore selection changed.
if (this.equals(getSite().getPage().getActiveEditor()))
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
*/
protected void setEditDomain(DefaultEditDomain ed) {
this.editDomain = ed;
}
/**
* @see org.eclipse.ui.IWorkbenchPart#setFocus()
*/
public void setFocus() {
getGraphicalViewer().getControl().setFocus();
}
/**
* Sets the graphicalViewer for this EditorPart.
* @param viewer the graphical viewer
*/
protected 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();
}
}
}