/** * Copyright (c) 2006, 2007 Borland Software Corporation * * 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: * bblajer - initial API and implementation */ package org.eclipse.gmf.runtime.lite.parts; import java.util.Collection; import java.util.Collections; import java.util.EventObject; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.IFigure; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.emf.transaction.util.TransactionUtil; import org.eclipse.emf.workspace.WorkspaceEditingDomainFactory; import org.eclipse.gef.DefaultEditDomain; import org.eclipse.gef.EditDomain; 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.editparts.ZoomManager; import org.eclipse.gef.palette.PaletteRoot; import org.eclipse.gef.ui.actions.ActionRegistry; import org.eclipse.gef.ui.actions.SelectionAction; import org.eclipse.gef.ui.actions.StackAction; import org.eclipse.gef.ui.actions.WorkbenchPartAction; import org.eclipse.gef.ui.views.palette.PalettePage; import org.eclipse.gmf.runtime.lite.services.DefaultDiagramLayouter; import org.eclipse.gmf.runtime.lite.services.IDiagramLayouter; import org.eclipse.jface.action.IAction; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorMatchingStrategy; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IEditorRegistry; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.EditorPart; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.eclipse.ui.views.properties.IPropertySheetPage; /** * This class serves as the base class for the generated diagram editors. * @author bblajer */ public abstract class DiagramEditor extends EditorPart implements IDiagramManager { private DiagramDisplayer myDiagramDisplayer; private boolean myIsDirty = false; private CommandStackListener commandStackListener = new CommandStackListener() { public void commandStackChanged(EventObject event) { setDirty(((CommandStack) event.getSource()).isDirty()); } }; private CommandStackEventListener mySaveListener = new CommandStackEventListener() { public void stackChanged(CommandStackEvent event) { if (event.isPostChangeEvent() && isSaved()) { getCommandStack().markSaveLocation(); } } private boolean isSaved() { for(Resource next : getEditingDomain().getResourceSet().getResources()) { if (!next.isLoaded()) { continue; } if (!next.isTrackingModification() || next.isModified()) { return false; } } return true; } }; protected void save(IProgressMonitor monitor) throws CoreException { myDiagramDisplayer.save(getSaveOptions(), monitor); } /** * Returns the options with which the resources will be saved. Subclasses should override. */ protected Map<?, ?> getSaveOptions() { return Collections.emptyMap(); } public final TransactionalEditingDomain getEditingDomain() { return myDiagramDisplayer.getEditingDomain(); } protected final EditDomain getEditDomain() { return myDiagramDisplayer.getEditDomain(); } protected final CommandStack getCommandStack() { return getEditDomain().getCommandStack(); } protected final ZoomManager getZoomManager() { return myDiagramDisplayer.getZoomManager(); } protected final ActionRegistry getActionRegistry() { return myDiagramDisplayer.getActionRegistry(); } protected final GraphicalViewer getGraphicalViewer() { return myDiagramDisplayer.getGraphicalViewer(); } protected IPropertySheetPage getPropertySheetPage() { return myDiagramDisplayer.getPropertySheetPage(); } protected IContentOutlinePage getOutlinePage() { return new DiagramContentOutlinePage(myDiagramDisplayer, getDefaultOutlineViewMode()); } protected IDiagramLayouter getDiagramLayouter() { return new DefaultDiagramLayouter(); } /** * Returns the initial display mode for the outline page to be shown. Possible values are <code>DiagramContentOutlinePage.ID_OUTLINE</code> and * <code>DiagramContentOutlinePage.ID_OVERVIEW</code>. * By default, the outline page starts in the overview mode. Subclasses may reimplement. */ protected int getDefaultOutlineViewMode() { return DiagramContentOutlinePage.ID_OVERVIEW; } @Override public boolean isSaveAsAllowed() { // TODO: should be allowed. return false; } @Override public void doSaveAs() { // TODO: Implement. } @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { setSite(site); TransactionalEditingDomain editingDomain = getEditingDomain(input); if (editingDomain == null) { editingDomain = reuseEditingDomain(input); } if (editingDomain == null) { editingDomain = createEditingDomain(); } ForceTrackingModificationAdapter adapter = (ForceTrackingModificationAdapter) EcoreUtil.getExistingAdapter(editingDomain.getResourceSet(), ForceTrackingModificationAdapter.class); if (adapter == null) { adapter = new ForceTrackingModificationAdapter(); editingDomain.getResourceSet().eAdapters().add(adapter); } adapter.acquire(); myDiagramDisplayer = new DiagramDisplayer(this, createEditDomain(), editingDomain); getCommandStack().addCommandStackListener(commandStackListener); getCommandStack().addCommandStackEventListener(mySaveListener); setInput(input); } @Override public void dispose() { if (myDiagramDisplayer != null) { getCommandStack().removeCommandStackEventListener(mySaveListener); getCommandStack().removeCommandStackListener(commandStackListener); ForceTrackingModificationAdapter adapter = (ForceTrackingModificationAdapter) EcoreUtil.getExistingAdapter(getEditingDomain().getResourceSet(), ForceTrackingModificationAdapter.class); if (adapter != null) { adapter.release(); if (adapter.isReleased()) { getEditingDomain().getResourceSet().eAdapters().remove(adapter); } } myDiagramDisplayer.dispose(); myDiagramDisplayer = null; } super.dispose(); } @Override public Object getAdapter(Class type) { if (type == IPropertySheetPage.class) { return getPropertySheetPage(); } else if (type == IContentOutlinePage.class) { return getOutlinePage(); } else if (type == ZoomManager.class) { return getZoomManager(); } else if (type == IDiagramLayouter.class) { return getDiagramLayouter(); } else if (type == PalettePage.class) { return myDiagramDisplayer.getPalettePage(); } else if (type == GraphicalViewer.class) { return getGraphicalViewer(); } else if (type == CommandStack.class) { return getCommandStack(); } else if (type == ActionRegistry.class) { return getActionRegistry(); } else if (type == EditPart.class && getGraphicalViewer() != null) { return getGraphicalViewer().getRootEditPart(); } else if (type == IFigure.class && getGraphicalViewer() != null) { return ((GraphicalEditPart)getGraphicalViewer().getRootEditPart()).getFigure(); } return super.getAdapter(type); } private void setDirty(boolean isDirty) { if (isDirty != myIsDirty) { myIsDirty = isDirty; firePropertyChange(PROP_DIRTY); } } @Override public boolean isDirty() { return myIsDirty; } /** * Adds an action to this editor's <code>ActionRegistry</code>. (This is * a helper method.) * * @param action * the action to add. */ protected void addAction(IAction action) { myDiagramDisplayer.addAction(action); } /** * Adds an editor action to this editor. * * <p> * Editor actions are actions that depend and work on the editor. * * @param action * the editor action */ protected void addEditorAction(WorkbenchPartAction action) { myDiagramDisplayer.addEditorAction(action); } /** * Adds an <code>EditPart</code> action to this editor. * * <p> * <code>EditPart</code> actions are actions that depend and work on the * selected <code>EditPart</code>s. * * @param action * the <code>EditPart</code> action */ protected void addEditPartAction(SelectionAction action) { myDiagramDisplayer.addEditPartAction(action); } /** * Adds an <code>CommandStack</code> action to this editor. * * <p> * <code>CommandStack</code> actions are actions that depend and work on * the <code>CommandStack</code>. * * @param action * the <code>CommandStack</code> action */ protected void addStackAction(StackAction action) { myDiagramDisplayer.addStackAction(action); } @Override public void createPartControl(Composite parent) { myDiagramDisplayer.createViewer(parent); createActions(); } @Override public void setFocus() { myDiagramDisplayer.setFocus(); } /** * Returns the editing domain instance to be used for the specified input. If this method returns <code>null</code>, * a {@link #createEditDomain() default instance} will be created and used. * By default, return <code>null</code>. Subclasses may reimplement. */ protected TransactionalEditingDomain getEditingDomain(IEditorInput input) { if (input instanceof DiagramEditorInput) { TransactionalEditingDomain result = TransactionUtil.getEditingDomain(((DiagramEditorInput) input).getDiagram()); return result; } return null; } /** * Returns the editing domain that is used by other editors with the same input. This is done to correctly support * "New Editor" operation that is available in the context menu of the editor tab. * @return */ protected TransactionalEditingDomain reuseEditingDomain(IEditorInput input) { IEditorRegistry editorRegistry = PlatformUI.getWorkbench().getEditorRegistry(); IEditorDescriptor editorDesc = editorRegistry.findEditor(getSite().getId()); IEditorMatchingStrategy matchingStrategy = editorDesc.getEditorMatchingStrategy(); IEditorReference[] editorRefs = getEditorSite().getPage().getEditorReferences(); for (int i = 0; i < editorRefs.length; i++) { if (matches(matchingStrategy, editorRefs[i], input)) { DiagramEditor anotherEditor = (DiagramEditor) editorRefs[i].getEditor(false); if (anotherEditor != null) { return anotherEditor.getEditingDomain(); } } } return null; } private boolean matches(IEditorMatchingStrategy strategy, IEditorReference editorRef, IEditorInput input) { if (strategy == null) { if (getSite().getId().equals(editorRef.getId())) { try { return input.equals(editorRef.getEditorInput()); } catch (PartInitException e) { return false; } } return false; } else { return strategy.matches(editorRef, input); } } /** * Returns the editing domain instance to be used for the diagram if none may be {@link #getEditingDomain(IEditorInput) inferred} from the input. * Subclasses may extend or reimplement. */ protected TransactionalEditingDomain createEditingDomain() { TransactionalEditingDomain editingDomain = WorkspaceEditingDomainFactory.INSTANCE.createEditingDomain(); editingDomain.getResourceSet().eAdapters().add(new AdapterFactoryEditingDomain.EditingDomainProvider(editingDomain)); return editingDomain; } public void configureGraphicalViewer() { getGraphicalViewer().getControl().setBackground(ColorConstants.listBackground); } protected double[] getZoomLevels() { double[] result = {.05, .1, .25, .5, .75, 1, 1.25, 1.5, 1.75, 2, 4}; return result; } public abstract void configurePalette(PaletteRoot paletteRoot); /** * Creates edit domain that will be used for the editor. * Subclasses may extend. */ protected EditDomain createEditDomain() { DefaultEditDomain domain = new DefaultEditDomain(this); return domain; } public abstract void initializeGraphicalViewer(); public abstract AdapterFactory getDomainAdapterFactory(); public abstract boolean isFlyoutPalette(); protected abstract void createActions(); private static class ForceTrackingModificationAdapter extends AdapterImpl { @Override public void setTarget(Notifier newTarget) { super.setTarget(newTarget); if (newTarget instanceof ResourceSet) { ResourceSet resourceSet = (ResourceSet) newTarget; for(Resource next : resourceSet.getResources()) { next.setTrackingModification(true); } } } @Override public void notifyChanged(Notification msg) { if (msg.getNotifier() == getTarget() && msg.getFeatureID(ResourceSet.class) == ResourceSet.RESOURCE_SET__RESOURCES) { switch (msg.getEventType()) { case Notification.ADD: { Resource resource = (Resource) msg.getNewValue(); resource.setTrackingModification(true); } break; case Notification.ADD_MANY: { @SuppressWarnings("unchecked") Collection<Resource> resources = (Collection<Resource>) msg.getNewValue(); for(Resource next : resources) { next.setTrackingModification(true); } } } } } @Override public boolean isAdapterForType(Object type) { return ForceTrackingModificationAdapter.class.equals(type); } public void acquire() { myRefCount++; } public void release() { if (myRefCount == 0) { throw new IllegalStateException(); } myRefCount--; } public boolean isReleased() { return myRefCount == 0; } private int myRefCount; } }