/******************************************************************************* * Copyright (c) 2011, 2013 Formal Mind GmbH and University of Dusseldorf. * 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: * Michael Jastram - initial API and implementation ******************************************************************************/ package org.eclipse.rmf.reqif10.pror.editor.presentation; import java.util.Collection; import java.util.EventObject; import java.util.Iterator; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.common.command.BasicCommandStack; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.command.CommandStack; import org.eclipse.emf.common.command.CommandStackListener; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.emf.edit.provider.ItemProviderAdapter; import org.eclipse.emf.edit.ui.action.EditingDomainActionBarContributor; import org.eclipse.emf.edit.ui.provider.UnwrappingSelectionProvider; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.rmf.reqif10.ReqIF; import org.eclipse.rmf.reqif10.ReqIF10Package; import org.eclipse.rmf.reqif10.SpecHierarchy; import org.eclipse.rmf.reqif10.SpecRelation; import org.eclipse.rmf.reqif10.Specification; import org.eclipse.rmf.reqif10.pror.editor.ISpecificationEditor; import org.eclipse.rmf.reqif10.pror.editor.actions.SpecificationWebPrintAction; import org.eclipse.rmf.reqif10.pror.editor.agilegrid.ProrAgileGrid; import org.eclipse.rmf.reqif10.pror.editor.agilegrid.ProrAgileGridViewer; import org.eclipse.rmf.reqif10.pror.filter.ReqifFilter; import org.eclipse.rmf.reqif10.pror.util.ProrUtil; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.part.EditorPart; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.eclipse.ui.views.properties.IPropertySheetPage; /** * @author Lukas Ladenberger * @author Michael Jastram */ public class SpecificationEditor extends EditorPart implements ISpecificationEditor, IMenuListener { public static final String EDITOR_ID = "org.eclipse.rmf.reqif10.pror.SpecificationEditor"; /** * The {@link Specification} associated with this Editor. */ private Specification specification; /** * The {@link ProrAgileGridViewer} for this Editor. */ private ProrAgileGridViewer prorAgileGridViewer; private Reqif10ActionBarContributor reqifActionBarContributor; /** * The {@link Reqif10Editor} of the owning {@link ReqIf} object. We keep a * reference, as we reuse a number of elements from that editor (Property * View, Outline View, EditingDomain). We keep the save status of the * Editors synchronized via the EditingDomain's Command Stack. */ private Reqif10Editor reqifEditor; // A number of Listeners private ISelectionChangedListener selectionChangedListener; // A listener to the content outline private ISelectionListener contentOutlineSelectionListener; private CommandStackListener commandStackListener; private AdapterImpl changeNameListener; private AdapterImpl deleteSpecListener; /** * Initializes the Editor. */ @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { // Sanity Check if (!(input instanceof ReqifSpecificationEditorInput)) { throw new IllegalArgumentException("Wrong input type: " + input); } // Extracting Info from the input reqifEditor = (Reqif10Editor) ((ReqifSpecificationEditorInput) input).getReqifEditor(); specification = ((ReqifSpecificationEditorInput) input).getSpec(); reqifActionBarContributor = (Reqif10ActionBarContributor) site .getActionBarContributor(); // Part Setup setSite(site); setInputWithNotify(input); setPartName(input.getName()); site.getActionBars().setGlobalActionHandler( ActionFactory.PRINT.getId(), new SpecificationWebPrintAction(reqifEditor.getEditingDomain(), reqifEditor.getAdapterFactory())); } /** * Builds the Part, which is an {@link ProrAgileGridViewer} and registers a * number of Listeners. */ @Override public void createPartControl(final Composite parent) { createSpecificationPart(parent); // Order matters! registerChangeNameListener(); registerDeleteListener(); registerSelectionChangedListener(); registerCommandStackListener(parent); } /** * Builds the actual {@link ProrAgileGridViewer} * * @param containter */ private void createSpecificationPart(Composite containter) { prorAgileGridViewer = new ProrAgileGridViewer(containter, reqifEditor.getAdapterFactory(), getEditingDomain(), reqifActionBarContributor.getAgileCellEditorActionHandler()); prorAgileGridViewer.setInput(specification); getSite().setSelectionProvider(prorAgileGridViewer); if (false == specification.getChildren().isEmpty()) { prorAgileGridViewer.setSelection(new StructuredSelection( specification.getChildren().get(0))); // We call this manually because the method selection changed of the // IPropertySheetPage isn't called any more in E4 (I think its a // bug) reqifEditor.getPropertySheetPage().selectionChanged(this, prorAgileGridViewer.getSelection()); } buildContextMenu(); } public Specification getSpecification() { return specification; } /** * Registers a command stack listener that updates the save state and * updates the selection. */ private void registerCommandStackListener(final Composite parent) { commandStackListener = new CommandStackListener() { public void commandStackChanged(final EventObject event) { parent.getDisplay().asyncExec(new Runnable() { public void run() { firePropertyChange(IEditorPart.PROP_DIRTY); // Try to select the affected objects. Command mostRecentCommand = ((CommandStack) event .getSource()).getMostRecentCommand(); if (mostRecentCommand != null) { Collection<?> affectedObjects = mostRecentCommand .getAffectedObjects(); setSelectionToViewer(affectedObjects); } } }); } }; getEditingDomain().getCommandStack().addCommandStackListener( commandStackListener); } /** * Upon a change of the name of the Specification, the Part must be renamed. */ private void registerChangeNameListener() { changeNameListener = new AdapterImpl() { @Override public void notifyChanged(Notification notification) { if (notification.getFeature() == ReqIF10Package.Literals.SPEC_ELEMENT_WITH_ATTRIBUTES__VALUES) { ItemProviderAdapter ip = ProrUtil.getItemProvider( reqifEditor.getAdapterFactory(), specification); setPartName(ip.getText(specification)); } } }; specification.eAdapters().add(changeNameListener); } /** * If the Specification is deleted, we must close the editor. */ private void registerDeleteListener() { final EObject container = specification.eContainer(); deleteSpecListener = new AdapterImpl() { @Override public void notifyChanged(Notification msg) { if (msg.getFeature() == ReqIF10Package.Literals.SPECIFICATION__CHILDREN || msg.getEventType() == Notification.REMOVE && msg.getOldValue() == specification) { getEditorSite().getPage().closeEditor( SpecificationEditor.this, false); } } }; container.eAdapters().add(deleteSpecListener); } /** * This sets the selection into whichever viewer is active. Code taken from * the generated {@link Reqif10Editor#setSelectionToViewer(Collection)} */ private void setSelectionToViewer(Collection<?> collection) { final Collection<?> theSelection = collection; // Make sure it's okay. // if (theSelection != null && !theSelection.isEmpty()) { Runnable runnable = new Runnable() { public void run() { // Try to select the items in the current content viewer of // the editor. // if (prorAgileGridViewer != null) { prorAgileGridViewer .setSelection(new StructuredSelection( theSelection.toArray()), true); } } }; getSite().getShell().getDisplay().syncExec(runnable); } } private void registerSelectionChangedListener() { selectionChangedListener = new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { reqifEditor.setStatusLineManager(event.getSelection()); } }; prorAgileGridViewer .addSelectionChangedListener(selectionChangedListener); contentOutlineSelectionListener = new ISelectionListener() { public void selectionChanged(IWorkbenchPart part, ISelection selection) { // Only apply selection if it contains at least one // SpecHierarchy if (selection instanceof IStructuredSelection) { for (Iterator<?> i = ((IStructuredSelection) selection) .iterator(); i.hasNext();) { Object item = i.next(); if (item instanceof SpecHierarchy) { prorAgileGridViewer.setSelection(selection); ((ProrAgileGrid) prorAgileGridViewer.getControl()) .scrollToFocus(); return; } } } } }; getSite().getPage().addSelectionListener( contentOutlineSelectionListener); } /** * Delegate populating the context menu to EMF. */ private MenuManager buildContextMenu() { MenuManager contextMenu = new MenuManager("#PopUp"); contextMenu.add(new Separator("additions")); contextMenu.setRemoveAllWhenShown(true); contextMenu.addMenuListener(this); Menu menu = contextMenu.createContextMenu(prorAgileGridViewer .getControl()); prorAgileGridViewer.getControl().setMenu(menu); getSite().registerContextMenu(contextMenu, new UnwrappingSelectionProvider(prorAgileGridViewer)); return contextMenu; } /** * We use the outline and property view from {@link Reqif10Editor}. */ @SuppressWarnings("rawtypes") @Override public Object getAdapter(Class key) { if (key.equals(IContentOutlinePage.class)) { return reqifEditor.getContentOutlinePage(); } else if (key.equals(IPropertySheetPage.class)) { return reqifEditor.getPropertySheetPage(); } else { return super.getAdapter(key); } } /** * This implements {@link org.eclipse.jface.action.IMenuListener} to help * fill the context menus with contributions from the Edit menu. */ public void menuAboutToShow(IMenuManager menuManager) { ((IMenuListener) getEditorSite().getActionBarContributor()) .menuAboutToShow(menuManager); } /** * The {@link EditingDomain} from the {@link Reqif10Editor}. */ public EditingDomain getEditingDomain() { return reqifEditor.getEditingDomain(); } /** * Synchronized with {@link Reqif10Editor} */ @Override public void doSave(IProgressMonitor monitor) { reqifEditor.doSave(monitor); firePropertyChange(IEditorPart.PROP_DIRTY); } /** * Synchronized with {@link Reqif10Editor} */ @Override public void doSaveAs() { reqifEditor.doSaveAs(); firePropertyChange(IEditorPart.PROP_DIRTY); } /** * Synchronized with the {@link EditingDomain} from the * {@link Reqif10Editor} */ @Override public boolean isDirty() { return ((BasicCommandStack) getEditingDomain().getCommandStack()) .isSaveNeeded(); } /** * Only allowed from main editor. */ @Override public boolean isSaveAsAllowed() { return false; } /** * Delegated to {@link #prorAgileGridViewer}. */ @Override public void setFocus() { prorAgileGridViewer.getControl().setFocus(); } /** * Detach all listeners. */ @Override public void dispose() { prorAgileGridViewer.dispose(); if (selectionChangedListener != null) { prorAgileGridViewer .removeSelectionChangedListener(selectionChangedListener); selectionChangedListener = null; } if (contentOutlineSelectionListener != null) { getSite().getPage().removeSelectionListener( contentOutlineSelectionListener); contentOutlineSelectionListener = null; } if (commandStackListener != null) { getEditingDomain().getCommandStack().removeCommandStackListener( commandStackListener); commandStackListener = null; } if (changeNameListener != null) { specification.eAdapters().remove(changeNameListener); changeNameListener = null; } if (deleteSpecListener != null) { specification.eAdapters().remove(deleteSpecListener); deleteSpecListener = null; } if (reqifEditor.getActionBarContributor().getActiveEditor() == this) { reqifEditor.getActionBarContributor().setActiveEditor(null); } super.dispose(); } // TODO I would like to remove this. public Reqif10Editor getReqifEditor() { return reqifEditor; } /** * Forward requests to show or hide SpecRelations. */ public void setShowSpecRelations(boolean checked) { ISelection sel = prorAgileGridViewer.getSelection(); prorAgileGridViewer.setShowSpecRelations(checked); // Set the correct selection after showing/hiding SpecRelations if (sel instanceof IStructuredSelection) { IStructuredSelection selection = (IStructuredSelection) sel; Object firstElement = selection.getFirstElement(); // If a SpecRelation was selected, select after hiding the // SpecRealtions the first SpecHierarchy of the Specification if (firstElement instanceof SpecRelation) { selection = new StructuredSelection(specification.getChildren() .get(0)); } prorAgileGridViewer.setSelection(selection); } } /** * Only the "Main" Editor ({@link Reqif10Editor}) requires saving. */ @Override public boolean isSaveOnCloseNeeded() { return false; } public void setFilter(ReqifFilter filter) { prorAgileGridViewer.setFilter(filter); } public ReqIF getReqif() { return reqifEditor.getReqif(); } public AdapterFactory getAdapterFactory() { return reqifEditor.getAdapterFactory(); } public EditingDomainActionBarContributor getActionBarContributor() { return reqifEditor.getActionBarContributor(); } public void addSelectionChangedListener(ISelectionChangedListener listener) { reqifEditor.addSelectionChangedListener(listener); } public ISelection getSelection() { return reqifEditor.getSelection(); } public void removeSelectionChangedListener( ISelectionChangedListener listener) { reqifEditor.removeSelectionChangedListener(listener); } public void setSelection(ISelection selection) { reqifEditor.setSelection(selection); } public ISpecificationEditor openSpecEditor(Specification spec) { return reqifEditor.openSpecEditor(spec); } }