blob: 87f9b713aa9fde9699e2d3632aa91f46cb1edfdc [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2008-2016 See4sys, itemis 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:
* See4sys - Initial API and implementation
* itemis - [393479] Enable BasicTabbedPropertySheetTitleProvider to retrieve same AdapterFactory as underlying IWorkbenchPart is using
* itemis - [418005] Add support for model files with multiple root elements
* itemis - [420505] Editor shows no content when editor input object is added lately
* itemis - [421585] Form Editor silently closes if model is not loaded via Sphinx
* itemis - [425173] Form editor closes when the input resource are changed externally
* itemis - [426798] BasicTransactionalFormsEditor uses wrong drag and drop transfer type
* itemis - [430218] Sphinx-integrated form editors should not prompt user for saving when being closed
* itemis - [434809] The BasicTransactionalFormEditor does not report resource errors & warnings
* itemis - [434842] BasicTransactionalFormEditor does not close loadingEditorInputPage for empty resources
* itemis - [458862] Navigation from problem markers in Check Validation view to model editors and Model Explorer view broken
* itemis - [459054] Rework the implementation of BasicTransactionalFormsEditor to reuse common functionalities provided the Sphinx Common Editor Logic plugin
* itemis - [460260] Expanded paths are collapsed on resource reload
* itemis - [501112] Temporary model loading form editor page sometimes happens to remain in place forever even when underlying model has been loaded
*
* </copyright>
*/
package org.eclipse.sphinx.emf.editors.forms;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.ui.URIEditorInput;
import org.eclipse.emf.common.ui.viewer.IViewerProvider;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EValidator;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.domain.IEditingDomainProvider;
import org.eclipse.emf.edit.provider.AdapterFactoryItemDelegator;
import org.eclipse.emf.edit.provider.ItemProviderAdapter;
import org.eclipse.emf.edit.ui.action.EditingDomainActionBarContributor;
import org.eclipse.emf.edit.ui.dnd.EditingDomainViewerDropAdapter;
import org.eclipse.emf.edit.ui.dnd.LocalTransfer;
import org.eclipse.emf.edit.ui.dnd.ViewerDragAdapter;
import org.eclipse.emf.edit.ui.provider.ExtendedImageRegistry;
import org.eclipse.emf.edit.ui.provider.UnwrappingSelectionProvider;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.ui.provider.TransactionalAdapterFactoryContentProvider;
import org.eclipse.emf.transaction.ui.provider.TransactionalAdapterFactoryLabelProvider;
import org.eclipse.emf.workspace.IWorkspaceCommandStack;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IPageChangedListener;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.PageChangedEvent;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.sphinx.emf.editors.IModelEditorInputChangeAnalyzer;
import org.eclipse.sphinx.emf.editors.IModelEditorInputChangeHandler;
import org.eclipse.sphinx.emf.editors.ModelEditorInputSynchronizer;
import org.eclipse.sphinx.emf.editors.ModelEditorUndoContextManager;
import org.eclipse.sphinx.emf.editors.forms.internal.Activator;
import org.eclipse.sphinx.emf.editors.forms.internal.DefaultSaveable;
import org.eclipse.sphinx.emf.editors.forms.internal.messages.Messages;
import org.eclipse.sphinx.emf.editors.forms.pages.GenericContentsTreePage;
import org.eclipse.sphinx.emf.editors.forms.pages.MessagePage;
import org.eclipse.sphinx.emf.metamodel.MetaModelDescriptorRegistry;
import org.eclipse.sphinx.emf.model.IModelDescriptor;
import org.eclipse.sphinx.emf.model.ModelDescriptorRegistry;
import org.eclipse.sphinx.emf.ui.util.EcoreUIUtil;
import org.eclipse.sphinx.emf.util.EcorePlatformUtil;
import org.eclipse.sphinx.emf.util.EcoreResourceUtil;
import org.eclipse.sphinx.emf.util.WorkspaceEditingDomainUtil;
import org.eclipse.sphinx.emf.workspace.domain.WorkspaceEditingDomainManager;
import org.eclipse.sphinx.emf.workspace.loading.ModelLoadManager;
import org.eclipse.sphinx.emf.workspace.saving.ModelSaveManager;
import org.eclipse.sphinx.emf.workspace.ui.saving.BasicModelSaveablesProvider;
import org.eclipse.sphinx.emf.workspace.ui.saving.BasicModelSaveablesProvider.SiteNotifyingSaveablesLifecycleListener;
import org.eclipse.sphinx.platform.ui.util.SelectionUtil;
import org.eclipse.sphinx.platform.util.PlatformLogUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IPersistableEditor;
import org.eclipse.ui.ISaveablePart2;
import org.eclipse.ui.ISaveablesLifecycleListener;
import org.eclipse.ui.ISaveablesSource;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartConstants;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.Saveable;
import org.eclipse.ui.SaveablesLifecycleEvent;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.editor.IFormPage;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.navigator.SaveablesProvider;
import org.eclipse.ui.views.contentoutline.ContentOutline;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.PropertySheet;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
/**
* A basic Eclipse Forms-based model editor.
*/
public class BasicTransactionalFormEditor extends FormEditor implements IModelEditorInputChangeAnalyzer, IEditingDomainProvider, ISelectionProvider,
IMenuListener, IViewerProvider, IGotoMarker, IPersistableEditor, ITabbedPropertySheetPageContributor, ISaveablesSource, ISaveablePart2 {
private static final String MEMENTO_KEY_EDITOR_DIRTY_ON_WORKBENCH_CLOSE = "editorDirtyOnWorkbenchClose"; //$NON-NLS-1$
/**
* The editor input object that is currently being edited.
*/
private Object editorInputObject = null;
/**
* The editor input object that has been edited before.
*/
private Object oldEditorInputObject = null;
/**
* This is the content outline page.
*/
protected IContentOutlinePage contentOutlinePage;
/**
* This is a kludge...
*/
protected IStatusLineManager contentOutlineStatusLineManager;
/**
* This is the content outline page's viewer.
*/
protected TreeViewer contentOutlineViewer;
/**
* This is collection of the property sheet pages.
*/
protected Set<IPropertySheetPage> propertySheetPages = new HashSet<IPropertySheetPage>();
/**
* This is the selection provider that shadows the selection in the content outline. The parent relation must be
* correctly defined for this to work.
*/
protected ISelectionProvider selectionProvider;
/**
* This keeps track of the active selection provider, which may be either one of the viewers in the pages or the
* content outline viewer.
*/
protected ISelectionProvider currentSelectionProvider;
/**
* This listens to which ever viewer is active.
*/
protected ISelectionChangedListener selectionChangedListener;
/**
* This keeps track of all the {@link org.eclipse.jface.viewers.ISelectionChangedListener}s that are listening to
* this editor.
*/
protected Collection<ISelectionChangedListener> selectionChangedListeners = new ArrayList<ISelectionChangedListener>();
/**
* This keeps track of the selection of the editor as a whole.
*/
protected ISelection editorSelection = StructuredSelection.EMPTY;
/**
* Indicated if the page creation of this editor needs finished upon activation due to editor input object change
* while editor has been inactive.
*/
protected boolean finishCreatePagesOnActivation = false;
protected IFormPage messagePage;
protected SaveablesProvider modelSaveablesProvider;
protected AdapterFactoryItemDelegator itemDelegator;
/**
* The model editor undo context manager.
*/
protected ModelEditorUndoContextManager undoContextManager;
/**
* The model editor input synchronizer.
*/
protected ModelEditorInputSynchronizer editorInputSynchronizer;
/**
* This listens for when Outline and Properties view become active/inactive
*/
protected IPartListener partListener = new IPartListener() {
@Override
public void partActivated(IWorkbenchPart part) {
if (part instanceof ContentOutline) {
if (((ContentOutline) part).getCurrentPage() == contentOutlinePage) {
getActionBarContributor().setActiveEditor(BasicTransactionalFormEditor.this);
setCurrentSelectionProvider(contentOutlineViewer);
}
} else if (part instanceof PropertySheet) {
if (propertySheetPages.contains(((PropertySheet) part).getCurrentPage())) {
getActionBarContributor().setActiveEditor(BasicTransactionalFormEditor.this);
handleActivate();
}
} else if (part == BasicTransactionalFormEditor.this) {
setCurrentSelectionProvider(selectionProvider);
handleActivate();
}
}
@Override
public void partBroughtToTop(IWorkbenchPart part) {
}
@Override
public void partClosed(IWorkbenchPart part) {
if (part instanceof PropertySheet) {
((PropertySheet) part).getCurrentPage().dispose();
propertySheetPages.remove(((PropertySheet) part).getCurrentPage());
}
}
@Override
public void partDeactivated(IWorkbenchPart part) {
}
@Override
public void partOpened(IWorkbenchPart part) {
}
};
/**
* This creates a model editor.
*/
public BasicTransactionalFormEditor() {
// Ensures that this editor will only display the page's tab area if there is more than one page
addPageChangedListener(new IPageChangedListener() {
@Override
public void pageChanged(PageChangedEvent event) {
if (getPageCount() <= 1) {
hideTabs();
} else {
showTabs();
}
}
});
}
/**
* This is called during startup.
*/
@Override
public void init(IEditorSite site, IEditorInput editorInput) {
setSite(site);
setInputWithNotify(editorInput);
setPartName(getEditorInputName());
setTitleImage(getEditorInputImage());
site.setSelectionProvider(this);
site.getPage().addPartListener(partListener);
if (getEditingDomain() != null) {
undoContextManager = getModelEditorUndoContextManager();
editorInputSynchronizer = new ModelEditorInputSynchronizer(getEditorInput(), (TransactionalEditingDomain) getEditingDomain(), this,
new BasicTransactionalFormEditorInputChangeHandler());
}
modelSaveablesProvider = createModelSaveablesProvider();
modelSaveablesProvider.init(createModelSaveablesLifecycleListener());
}
/**
* This sets the selection into whichever viewer is active.
*/
public void setSelectionToViewer(Collection<?> collection) {
final Collection<?> theSelection = collection;
// Make sure it's okay.
if (theSelection != null && !theSelection.isEmpty()) {
// I don't know if this should be run this deferred because we might have to give the editor a chance to
// process the viewer update events and hence to update the views first
Runnable runnable = new Runnable() {
@Override
public void run() {
// Try to select the items in the current content viewer of the editor
try {
if (currentSelectionProvider != null) {
if (currentSelectionProvider instanceof Viewer) {
((Viewer) currentSelectionProvider).setSelection(new StructuredSelection(theSelection.toArray()), true);
} else {
currentSelectionProvider.setSelection(new StructuredSelection(theSelection.toArray()));
}
}
} catch (RuntimeException ex) {
// Ignore exception
}
}
};
runnable.run();
}
}
protected Map<?, ?> getLoadOptions() {
return EcoreResourceUtil.getDefaultLoadOptions();
}
protected Map<?, ?> getSaveOptions() {
return EcoreResourceUtil.getDefaultSaveOptions();
}
/**
* Handles activation of the editor or it's associated views.
*/
protected void handleActivate() {
// Recompute the read only state
EditingDomain editingDomain = getEditingDomain();
if (editingDomain instanceof AdapterFactoryEditingDomain) {
if (((AdapterFactoryEditingDomain) editingDomain).getResourceToReadOnlyMap() != null) {
((AdapterFactoryEditingDomain) editingDomain).getResourceToReadOnlyMap().clear();
}
}
if (finishCreatePagesOnActivation) {
finishCreatePages();
finishCreatePagesOnActivation = false;
}
// Refresh any actions that may become enabled or disabled
setSelection(getSelection());
}
/**
* This is to make sure that one of the selection providers in one of the pages can shadows the selection in the
* content outline. The parent relation must be correctly defined for this to work.
*/
public void setSelectionProvider(ISelectionProvider selectionProvider) {
this.selectionProvider = selectionProvider;
setCurrentSelectionProvider(selectionProvider);
}
/**
* This makes sure that one selection provider, either for the current page or the outline view, if it has focus, is
* the current one.
*/
protected void setCurrentSelectionProvider(ISelectionProvider selectionProvider) {
// If it is changing...
if (currentSelectionProvider != selectionProvider) {
if (selectionChangedListener == null) {
// Create the listener on demand
selectionChangedListener = new ISelectionChangedListener() {
// This just notifies those things that are affected by the section
@Override
public void selectionChanged(SelectionChangedEvent selectionChangedEvent) {
setSelection(selectionChangedEvent.getSelection());
}
};
}
// Stop listening to the old one
if (currentSelectionProvider != null) {
currentSelectionProvider.removeSelectionChangedListener(selectionChangedListener);
}
// Start listening to the new one
if (selectionProvider != null) {
selectionProvider.addSelectionChangedListener(selectionChangedListener);
}
// Remember it
currentSelectionProvider = selectionProvider;
// Set the editors selection based on the current viewer's selection
setSelection(currentSelectionProvider != null ? currentSelectionProvider.getSelection() : StructuredSelection.EMPTY);
}
}
/**
* This returns the viewer as required by the {@link IViewerProvider} interface.
*/
@Override
public Viewer getViewer() {
if (currentSelectionProvider instanceof Viewer) {
return (Viewer) currentSelectionProvider;
}
return null;
}
/**
* This creates a context menu for the viewer and adds a listener as well registering the menu for extension.
*/
public void createContextMenuFor(StructuredViewer viewer) {
MenuManager contextMenu = new MenuManager("#PopUp"); //$NON-NLS-1$
contextMenu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
contextMenu.setRemoveAllWhenShown(true);
contextMenu.addMenuListener(this);
Menu menu = contextMenu.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
getSite().registerContextMenu(contextMenu, new UnwrappingSelectionProvider(viewer));
int dndOperations = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK;
Transfer[] transfers = new Transfer[] { LocalTransfer.getInstance(), LocalSelectionTransfer.getTransfer(), FileTransfer.getInstance() };
viewer.addDragSupport(dndOperations, transfers, new ViewerDragAdapter(viewer));
EditingDomain editingDomain = getEditingDomain();
if (editingDomain != null) {
viewer.addDropSupport(dndOperations, transfers, new EditingDomainViewerDropAdapter(editingDomain, viewer));
}
}
protected void updateEditorInput(URI newURI) {
Assert.isNotNull(newURI);
URI newInputURI = newURI;
URI oldInputURI = EcoreUIUtil.getURIFromEditorInput(getEditorInput());
if (!newURI.hasFragment() && oldInputURI != null && oldInputURI.hasFragment()) {
newInputURI = newURI.appendFragment(oldInputURI.fragment());
}
if (!newInputURI.equals(oldInputURI)) {
IEditorInput newInput = new URIEditorInput(newInputURI);
// Set new editor input
// FIXME Let ModelEditorInputSynchronizer become a IPropertyListener listening for PROP_INPUT and updating
// the ModelEditorInputSynchronizer's editorInput field
setInputWithNotify(newInput);
// Update editor part title
setTitleToolTip(getTitleToolTip());
}
}
/**
* @deprecated Use {@link #getEditorInputObject()} instead.
*/
@Deprecated
public Object getModelRoot() {
Object editorInputObject = getEditorInputObject();
if (editorInputObject instanceof EObject) {
return editorInputObject;
} else if (editorInputObject instanceof Resource) {
return EcoreResourceUtil.getModelRoot((Resource) editorInputObject);
}
return null;
}
/**
* @deprecated Use {@link #getOldEditorInputObject()} instead.
*/
@Deprecated
public Object getOldModelRoot() {
Object oldEditorInputObject = getOldEditorInputObject();
if (oldEditorInputObject instanceof EObject) {
return oldEditorInputObject;
} else if (oldEditorInputObject instanceof Resource) {
return EcoreResourceUtil.getModelRoot((Resource) oldEditorInputObject);
}
return null;
}
/**
* @deprecated Use {@link #getEditorInputResource()} instead.
*/
@Deprecated
public Resource getModelRootResource() {
return getEditorInputResource();
}
/**
* Returns whether the object behind the {@link IEditorInput editor input} is something that likely represents a
* stale object, e.g., an {@link EObject} that has become a {@link EObject#eIsProxy() proxy object} or a
* {@link Resource} that has been {@link Resource#unload() unloaded}.
*/
protected boolean isEditorInputObjectStale() {
if (editorInputObject instanceof EObject) {
EObject editorInputEObject = (EObject) editorInputObject;
if (editorInputEObject.eIsProxy() || editorInputEObject.eResource() == null || !editorInputEObject.eResource().isLoaded()) {
return true;
}
}
if (editorInputObject instanceof Resource) {
Resource editorInputResource = (Resource) editorInputObject;
if (!editorInputResource.isLoaded() || editorInputResource.getResourceSet() == null) {
return true;
}
}
return false;
}
/**
* @return The object behind the {@link IEditorInput editor input} that is currently being edited in this editor or
* <code>null</code> if no such is available.
* @see #getOldEditorInputObject()
*/
public Object getEditorInputObject() {
if (editorInputObject == null || isEditorInputObjectStale()) {
URI editorInputURI = EcoreUIUtil.getURIFromEditorInput(getEditorInput());
if (editorInputURI != null) {
// Don't loose track of old editor input object as long as no new editor input object is available
if (editorInputObject != null) {
oldEditorInputObject = editorInputObject;
}
if (editorInputURI.hasFragment()) {
editorInputObject = EcorePlatformUtil.getEObject(editorInputURI);
} else {
editorInputObject = EcorePlatformUtil.getResource(editorInputURI);
}
// Discard old editor input object as soon as (but not earlier than) a new editor input object is
// available
if (editorInputObject != null) {
oldEditorInputObject = null;
}
}
}
return editorInputObject;
}
/**
* @return The object behind the {@link IEditorInput editor input} that has been edited before in case that actual
* editor input object has become unavailable, or <code>null</code> if actual editor input object is still
* available.
* @see #getEditorInputObject()
*/
public Object getOldEditorInputObject() {
return oldEditorInputObject;
}
public Resource getEditorInputResource() {
Object editorInputObject = getEditorInputObject();
if (editorInputObject instanceof EObject) {
return ((EObject) editorInputObject).eResource();
} else if (editorInputObject instanceof Resource) {
return (Resource) editorInputObject;
}
return null;
}
protected String getEditorInputName() {
IEditorInput editorInput = getEditorInput();
if (editorInput instanceof IFileEditorInput) {
return editorInput.getName();
}
Object editorInputObject = getEditorInputObject();
if (editorInputObject != null) {
AdapterFactoryItemDelegator itemDelegator = getItemDelegator();
if (itemDelegator != null) {
// Return label of editor input object on which editor has been opened
return itemDelegator.getText(editorInputObject);
}
}
return editorInput.getName();
}
protected Image getEditorInputImage() {
IEditorInput editorInput = getEditorInput();
if (editorInput instanceof IFileEditorInput) {
ImageDescriptor imageDescriptor = editorInput.getImageDescriptor();
return ExtendedImageRegistry.getInstance().getImage(imageDescriptor);
}
Object editorInputObject = getEditorInputObject();
if (editorInputObject != null) {
AdapterFactoryItemDelegator itemDelegator = getItemDelegator();
if (itemDelegator != null) {
// Return icon of editor input object on which editor has been opened
Object imageURL = itemDelegator.getImage(editorInputObject);
return ExtendedImageRegistry.getInstance().getImage(imageURL);
}
}
ImageDescriptor imageDescriptor = editorInput.getImageDescriptor();
return ExtendedImageRegistry.getInstance().getImage(imageDescriptor);
}
/**
* If there is just one page in the multi-page editor part, this hides the single tab at the bottom.
*/
protected void hideTabs() {
if (getPageCount() <= 1) {
if (getContainer() instanceof CTabFolder) {
((CTabFolder) getContainer()).setTabHeight(0);
((CTabFolder) getContainer()).layout();
}
}
}
/**
* If there is more than one page in the multi-page editor part, this shows the tabs at the bottom.
*/
protected void showTabs() {
if (getPageCount() > 1) {
if (getContainer() instanceof CTabFolder) {
((CTabFolder) getContainer()).setTabHeight(SWT.DEFAULT);
((CTabFolder) getContainer()).layout();
}
}
}
/**
* This is how the framework determines which interfaces we implement.
*/
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(Class key) {
if (key.equals(AdapterFactory.class)) {
return getAdapterFactory();
} else if (key.equals(IContentOutlinePage.class)) {
return showOutlineView() ? getContentOutlinePage() : null;
} else if (key.equals(IPropertySheetPage.class)) {
return getPropertySheetPage();
} else if (key.equals(IGotoMarker.class)) {
return this;
} else if (key.equals(IUndoContext.class)) {
// used by undo/redo actions to get their undo context
return getModelEditorUndoContextManager().getUndoContext();
} else {
return super.getAdapter(key);
}
}
/**
* This accesses a cached version of the content outliner.
*/
public IContentOutlinePage getContentOutlinePage() {
if (contentOutlinePage == null) {
// The content outline is just a tree.
//
class MyContentOutlinePage extends ContentOutlinePage {
@Override
public void createControl(Composite parent) {
super.createControl(parent);
contentOutlineViewer = getTreeViewer();
contentOutlineViewer.addSelectionChangedListener(this);
// Set up the tree viewer
Object editorInputObject = getEditorInputObject();
EditingDomain editingDomain = getEditingDomain();
AdapterFactory adapterFactory = getAdapterFactory();
if (editingDomain != null && adapterFactory != null) {
contentOutlineViewer.setContentProvider(
new TransactionalAdapterFactoryContentProvider((TransactionalEditingDomain) editingDomain, adapterFactory));
contentOutlineViewer.setLabelProvider(
new TransactionalAdapterFactoryLabelProvider((TransactionalEditingDomain) editingDomain, adapterFactory));
contentOutlineViewer.setInput(editorInputObject);
} else {
if (contentOutlineViewer.getContentProvider() != null) {
contentOutlineViewer.setInput(null);
}
}
// Make sure our popups work
createContextMenuFor(contentOutlineViewer);
if (editorInputObject != null) {
// Select the root object in the view
contentOutlineViewer.setSelection(new StructuredSelection(editorInputObject), true);
}
}
@Override
public void makeContributions(IMenuManager menuManager, IToolBarManager toolBarManager, IStatusLineManager statusLineManager) {
super.makeContributions(menuManager, toolBarManager, statusLineManager);
contentOutlineStatusLineManager = statusLineManager;
}
@Override
public void setActionBars(IActionBars actionBars) {
super.setActionBars(actionBars);
getActionBarContributor().shareGlobalActions(this, actionBars);
}
}
contentOutlinePage = new MyContentOutlinePage();
// Listen to selection so that we can handle it is a special way.
//
contentOutlinePage.addSelectionChangedListener(new ISelectionChangedListener() {
// This ensures that we handle selections correctly.
//
@Override
public void selectionChanged(SelectionChangedEvent event) {
handleContentOutlineSelection(event.getSelection());
}
});
}
return contentOutlinePage;
}
/**
* This creates a new property sheet page instance and manages it in the cache.
*/
public IPropertySheetPage getPropertySheetPage() {
IPropertySheetPage propertySheetPage = new TabbedPropertySheetPage(this);
propertySheetPages.add(propertySheetPage);
return propertySheetPage;
}
@Override
public String getContributorId() {
return getSite().getId();
}
/**
* This deals with how we want selection in the outliner to affect the other views.
*/
public void handleContentOutlineSelection(ISelection selection) {
if (selectionProvider != null) {
if (!selection.isEmpty() && selection instanceof IStructuredSelection) {
Iterator<?> selectedElements = ((IStructuredSelection) selection).iterator();
if (selectedElements.hasNext()) {
// Get the first selected element
Object selectedElement = selectedElements.next();
ArrayList<Object> selectionList = new ArrayList<Object>();
selectionList.add(selectedElement);
while (selectedElements.hasNext()) {
selectionList.add(selectedElements.next());
}
// Set the selection to the widget
selectionProvider.setSelection(new StructuredSelection(selectionList));
}
}
}
}
@Override
public boolean isDirty() {
// For resources outside the workspace
if (getEditorInput() instanceof FileStoreEditorInput && ((FileStoreEditorInput) getEditorInput()).exists()) {
return ((BasicCommandStack) getEditingDomain().getCommandStack()).isSaveNeeded();
}
// Check dirty state of model which the editor input object belongs to
Object editorInputObject = getEditorInputObject();
if (editorInputObject instanceof EObject) {
return ModelSaveManager.INSTANCE.isDirty(((EObject) editorInputObject).eResource());
} else if (editorInputObject instanceof Resource) {
return ModelSaveManager.INSTANCE.isDirty((Resource) editorInputObject);
}
return false;
}
@Override
public boolean isSaveOnCloseNeeded() {
// In this case the opened editor own the file
if (getEditorInput() instanceof FileStoreEditorInput && ((FileStoreEditorInput) getEditorInput()).exists()) {
return isDirty();
}
// Model-based editors don't need to be saved when being closed even if the model is dirty, because they don't
// own the model. The model is loaded, managed, and saved globally, i.e. it is not destroyed but stays there
// when editors are being closed.
return false;
}
@Override
public int promptToSaveOnClose() {
// Model-based editors don't need to be saved when being closed even if the model is dirty, because they don't
// own the model. The model is loaded, managed, and saved globally, i.e. it is not destroyed but stays there
// when editors are being closed.
return ISaveablePart2.NO;
}
/**
* This is for implementing {@link IEditorPart} and simply saves the model file.
*/
@Override
public void doSave(IProgressMonitor monitor) {
if (getEditorInput() instanceof FileStoreEditorInput) {
saveResource();
} else {
try {
// Save the all dirty resources of the model the editor input object belongs to
Object editorInputObject = getEditorInputObject();
if (editorInputObject instanceof EObject) {
ModelSaveManager.INSTANCE.saveModel(((EObject) editorInputObject).eResource(), getSaveOptions(), false, monitor);
} else if (editorInputObject instanceof Resource) {
ModelSaveManager.INSTANCE.saveModel((Resource) editorInputObject, getSaveOptions(), false, monitor);
}
} finally {
/*
* !! Important Note !! Normally we shouldn't need to close down the progress monitor at this point.
* However, it looks like that the progress monitor is not handled appropriately by whoever call us here
* because we have observed the progress bar stays frozen at 100% after completion of the save
* operation. In order to avoid that we notify the progress monitor that the save work is done right
* here.
*/
if (monitor != null) {
monitor.done();
}
}
}
}
/**
* Saves the resource on which the editor is opened.
*/
protected void saveResource() {
final EditingDomain editingDomain = getEditingDomain();
// Save only if resource changed.
//
final Map<Object, Object> saveOptions = new HashMap<Object, Object>();
saveOptions.put(Resource.OPTION_SAVE_ONLY_IF_CHANGED, Resource.OPTION_SAVE_ONLY_IF_CHANGED_MEMORY_BUFFER);
// Do the work within an operation because this is a long running activity that modifies the workbench.
//
WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
// This is the method that gets invoked when the operation runs.
//
@Override
public void execute(IProgressMonitor monitor) {
// Save the resources to the file system.
//
Resource editorInputResource = getEditorInputResource();
if (editorInputResource != null && (!editorInputResource.getContents().isEmpty() || isPersisted(editorInputResource))
&& !editingDomain.isReadOnly(editorInputResource)) {
try {
editorInputResource.save(saveOptions);
} catch (Exception exception) {
PlatformLogUtil.logAsError(Activator.getPlugin(), exception);
}
}
}
};
try {
// This runs the operation, and shows progress.
//
new ProgressMonitorDialog(getSite().getShell()).run(true, false, operation);
// Refresh the necessary state.
//
((BasicCommandStack) editingDomain.getCommandStack()).saveIsDone();
firePropertyChange(IEditorPart.PROP_DIRTY);
} catch (Exception exception) {
// Something went wrong that shouldn't.
//
PlatformLogUtil.logAsError(Activator.getPlugin(), exception);
}
}
/**
* This returns whether something has been persisted to the URI of the specified resource. The implementation uses
* the URI converter from the editor's resource set to try to open an input stream.
*/
protected boolean isPersisted(Resource resource) {
boolean result = false;
try {
InputStream stream = getEditingDomain().getResourceSet().getURIConverter().createInputStream(resource.getURI());
if (stream != null) {
result = true;
stream.close();
}
} catch (IOException e) {
// Ignore
}
return result;
}
@Override
public boolean isSaveAsAllowed() {
return true;
}
/**
* This also changes the editor's input.
*/
@Override
public void doSaveAs() {
SaveAsDialog saveAsDialog = new SaveAsDialog(getSite().getShell());
saveAsDialog.open();
IPath path = saveAsDialog.getResult();
if (path != null) {
// Create URI of new editor input resource
final URI newResourceURI = EcorePlatformUtil.createURI(path);
// Changing the URI is, conceptually, a write operation. However, it does not affect the abstract state
// of the model, so we only need exclusive (read) access
try {
EditingDomain editingDomain = getEditingDomain();
if (editingDomain != null) {
((TransactionalEditingDomain) editingDomain).runExclusive(new Runnable() {
@Override
public void run() {
// Change saved resource's URI
Resource editorInputResource = getEditorInputResource();
if (editorInputResource != null) {
editorInputResource.setURI(newResourceURI);
}
// Update editor input
updateEditorInput(newResourceURI);
}
});
}
} catch (InterruptedException ex) {
PlatformLogUtil.logAsError(Activator.getPlugin(), ex);
// Don't follow through with the save because we were interrupted while trying to start the
// transaction, so our URI is not actually changed
return;
}
IStatusLineManager statusLineManager = getActionBars() != null ? getActionBars().getStatusLineManager() : null;
IProgressMonitor monitor = statusLineManager != null ? statusLineManager.getProgressMonitor() : new NullProgressMonitor();
doSave(monitor);
}
}
@Override
public void gotoMarker(IMarker marker) {
try {
if (marker.isSubtypeOf(EValidator.MARKER)) {
final String uriAttribute = marker.getAttribute(EValidator.URI_ATTRIBUTE, null);
if (uriAttribute != null) {
EditingDomain editingDomain = getEditingDomain();
if (editingDomain != null) {
EObject object = EcorePlatformUtil.getEObject((TransactionalEditingDomain) editingDomain, URI.createURI(uriAttribute, true));
// FIXME This way of retrieving the wrapped object is not appropriate in case that the editor,
// its pages or sections use custom adapter factories instead of the standard adapter factory of
// the underlying editing domain
Object wrappedObject = ((AdapterFactoryEditingDomain) editingDomain).getWrapper(object);
setSelectionToViewer(Collections.singleton(wrappedObject));
}
}
}
} catch (CoreException ex) {
PlatformLogUtil.logAsError(Activator.getPlugin(), ex);
}
}
protected void reset() {
if (!isDisposed()) {
// Discard undo context and reset dirty state
IOperationHistory operationHistory = getOperationHistory();
if (operationHistory != null) {
operationHistory.dispose(getModelEditorUndoContextManager().getUndoContext(), true, true, true);
}
// Update editor part name
setPartName(getEditorInputName());
setTitleImage(getEditorInputImage());
// Update this editor's input state.
/*
* !! Important Note !! Doing so will trigger IPropertyListener implementations that listen for
* IWorkbenchPartConstants.PROP_INPUT events (such as e.g., the AbstractFormPage#inputChangeListener). This
* kind or listeners are useful to update the input object of affected JFace viewers and/or the titles of
* affected form pages and sections.
*/
firePropertyChange(IWorkbenchPartConstants.PROP_INPUT);
// Update this editor's dirty state
firePropertyChange(IEditorPart.PROP_DIRTY);
}
}
@Override
public void setFocus() {
int pageIndex = getActivePage();
if (pageIndex != -1) {
Control control = getControl(pageIndex);
if (control != null && !control.isDisposed()) {
control.setFocus();
}
}
}
/**
* This implements {@link org.eclipse.jface.viewers.ISelectionProvider}.
*/
@Override
public void addSelectionChangedListener(ISelectionChangedListener listener) {
selectionChangedListeners.add(listener);
}
/**
* This implements {@link org.eclipse.jface.viewers.ISelectionProvider}.
*/
@Override
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
selectionChangedListeners.remove(listener);
}
/**
* This implements {@link org.eclipse.jface.viewers.ISelectionProvider} to return this editor's overall selection.
*/
@Override
public ISelection getSelection() {
return editorSelection;
}
/**
* This implements {@link org.eclipse.jface.viewers.ISelectionProvider} to set this editor's overall selection.
* Calling this result will notify the listeners.
*/
@Override
public void setSelection(ISelection selection) {
editorSelection = !SelectionUtil.getStructuredSelection(selection).isEmpty() ? selection : getDefaultSelection();
for (ISelectionChangedListener listener : new ArrayList<ISelectionChangedListener>(selectionChangedListeners)) {
listener.selectionChanged(new SelectionChangedEvent(this, editorSelection));
}
setStatusLineManager(editorSelection);
}
public ISelection getDefaultSelection() {
// Try to return editor input object as default selection
Object editorInputObject = getEditorInputObject();
if (editorInputObject != null) {
return new StructuredSelection(editorInputObject);
}
return StructuredSelection.EMPTY;
}
public void setStatusLineManager(ISelection selection) {
IStatusLineManager statusLineManager = currentSelectionProvider != null && currentSelectionProvider == contentOutlineViewer
? contentOutlineStatusLineManager : getActionBars() != null ? getActionBars().getStatusLineManager() : null;
if (statusLineManager != null) {
if (selection instanceof IStructuredSelection) {
Collection<?> collection = ((IStructuredSelection) selection).toList();
switch (collection.size()) {
case 0: {
statusLineManager.setMessage(getString("_UI_NoObjectSelected")); //$NON-NLS-1$
break;
}
case 1: {
Object object = collection.iterator().next();
String text = getItemDelegator() != null ? getItemDelegator().getText(object) : ""; //$NON-NLS-1$
Object image = getItemDelegator() != null ? getItemDelegator().getImage(object) : null;
statusLineManager.setMessage(ExtendedImageRegistry.getInstance().getImage(image), text);
break;
}
default: {
statusLineManager.setMessage(getString("_UI_MultiObjectSelected", Integer.toString(collection.size()))); //$NON-NLS-1$
break;
}
}
} else {
statusLineManager.setMessage(""); //$NON-NLS-1$
}
}
}
/**
* This looks up a string in the plugin's plugin.properties file.
*/
private static String getString(String key) {
return Activator.INSTANCE.getString(key);
}
/**
* This looks up a string in plugin.properties, making a substitution.
*/
private static String getString(String key, Object s1) {
return Activator.INSTANCE.getString(key, new Object[] { s1 });
}
/**
* This implements {@link org.eclipse.jface.action.IMenuListener} to help fill the context menus with contributions
* from the Edit menu.
*/
@Override
public void menuAboutToShow(IMenuManager menuManager) {
((IMenuListener) getEditorSite().getActionBarContributor()).menuAboutToShow(menuManager);
}
public EditingDomainActionBarContributor getActionBarContributor() {
return (EditingDomainActionBarContributor) getEditorSite().getActionBarContributor();
}
public IActionBars getActionBars() {
EditingDomainActionBarContributor actionBarContributor = getActionBarContributor();
return actionBarContributor != null ? actionBarContributor.getActionBars() : null;
}
public IOperationHistory getOperationHistory() {
EditingDomain editingDomain = getEditingDomain();
if (editingDomain != null) {
IWorkspaceCommandStack commandStack = (IWorkspaceCommandStack) editingDomain.getCommandStack();
if (commandStack != null) {
return commandStack.getOperationHistory();
}
}
return null;
}
@Override
public void dispose() {
if (undoContextManager != null) {
undoContextManager.dispose();
}
getSite().getPage().removePartListener(partListener);
if (getActionBarContributor() != null && getActionBarContributor().getActiveEditor() == this) {
getActionBarContributor().deactivate();
getActionBarContributor().setActiveEditor(null);
}
for (IPropertySheetPage propertySheetPage : propertySheetPages) {
propertySheetPage.dispose();
}
propertySheetPages.clear();
if (contentOutlinePage != null) {
contentOutlinePage.dispose();
}
if (modelSaveablesProvider != null) {
modelSaveablesProvider.dispose();
}
// Unload the resource when disposing the editor if resource is outside the workspace
if (getEditorInput() instanceof FileStoreEditorInput) {
EcoreResourceUtil.unloadResource(getEditorInputResource(), true);
}
super.dispose();
}
/**
* Tests if this editor has already been disposed.
* <p>
* This implementation determines the disposed state of the editor by checking if the {@link FormEditor#pages} field
* is <code>null</code> or not. When the editor has been disposed, it is an error to invoke any other method using
* the editor.
* </p>
*
* @return <code>true</code> when the editor is disposed and <code>false</code> otherwise.
*/
protected boolean isDisposed() {
return pages == null;
}
/**
* Returns whether the outline view should be presented to the user.
*/
protected boolean showOutlineView() {
return true;
}
protected ModelEditorUndoContextManager getModelEditorUndoContextManager() {
if (undoContextManager == null) {
undoContextManager = createModelEditorUndoContextManager();
}
return undoContextManager;
}
protected ModelEditorUndoContextManager createModelEditorUndoContextManager() {
return new ModelEditorUndoContextManager(getSite(), this, (TransactionalEditingDomain) getEditingDomain());
}
protected boolean isActivePart() {
return this == getSite().getWorkbenchWindow().getPartService().getActivePart();
}
protected boolean isActivePropertySheetPage() {
IWorkbenchPart activePart = getSite().getWorkbenchWindow().getPartService().getActivePart();
if (activePart instanceof PropertySheet) {
return propertySheetPages.contains(((PropertySheet) activePart).getCurrentPage());
}
return false;
}
protected SaveablesProvider createModelSaveablesProvider() {
return new BasicModelSaveablesProvider();
}
/**
* Creates an {@linkplain ISaveablesLifecycleListener}
*
* @return
*/
protected ISaveablesLifecycleListener createModelSaveablesLifecycleListener() {
return new SiteNotifyingSaveablesLifecycleListener(this) {
@Override
public void handleLifecycleEvent(SaveablesLifecycleEvent event) {
super.handleLifecycleEvent(event);
if (event.getEventType() == SaveablesLifecycleEvent.DIRTY_CHANGED) {
firePropertyChange(IEditorPart.PROP_DIRTY);
}
}
};
}
/**
* This returns the editing domain as required by the {@link IEditingDomainProvider} interface. This is important
* for implementing the static methods of {@link AdapterFactoryEditingDomain} and for supporting
* {@link org.eclipse.emf.edit.ui.action.CommandAction}.
*/
@Override
public EditingDomain getEditingDomain() {
URI uri = EcoreUIUtil.getURIFromEditorInput(getEditorInput());
return getEditingDomain(uri);
}
protected TransactionalEditingDomain getEditingDomain(final URI uri) {
TransactionalEditingDomain editingDomain = WorkspaceEditingDomainUtil.getEditingDomain(uri);
if (editingDomain == null && getEditorInput() instanceof FileStoreEditorInput) {
// If the file has been deleted
if (((FileStoreEditorInput) getEditorInput()).exists()) {
String modelNamespace = EcoreResourceUtil.readModelNamespace(null, EcoreUIUtil.getURIFromEditorInput(getEditorInput()));
editingDomain = WorkspaceEditingDomainManager.INSTANCE.getEditingDomainMapping().getEditingDomain(null,
MetaModelDescriptorRegistry.INSTANCE.getDescriptor(java.net.URI.create(modelNamespace)));
}
}
return editingDomain;
}
/**
* Returns the {@link AdapterFactory adapter factory} to be used by this {@link BasicTransactionalFormEditor form
* editor} for creating {@link ItemProviderAdapter item provider}s which control the way how {@link EObject model
* element}s from given <code>editingDomain</code> are displayed and can be edited.
* <p>
* This implementation returns the {@link AdapterFactory adapter factory} which is embedded in the given
* <code>editingDomain</code> by default. Clients which want to use an alternative {@link AdapterFactory adapter
* factory} (e.g., an {@link AdapterFactory adapter factory} that creates {@link ItemProviderAdapter item provider}s
* which are specifically designed for the {@link IEditorPart editor} in which this
* {@link BasicTransactionalFormEditor form editor} is used) may override {@link #getCustomAdapterFactory()} and
* return any {@link AdapterFactory adapter factory} of their choice. This custom {@link AdapterFactory adapter
* factory} will then be returned as result by this method.
* </p>
*
* @param editingDomain
* The {@link TransactionalEditingDomain editing domain} whose embedded {@link AdapterFactory adapter
* factory} is to be returned as default. May be left <code>null</code> if
* {@link #getCustomAdapterFactory()} has been overridden and returns a non-<code>null</code> result.
* @return The {@link AdapterFactory adapter factory} that will be used by this {@link BasicTransactionalFormEditor
* form editor}. <code>null</code> if no custom {@link AdapterFactory adapter factory} is provided through
* {@link #getCustomAdapterFactory()} and no <code>editingDomain</code> has been specified.
* @see #getCustomAdapterFactory()
*/
public AdapterFactory getAdapterFactory() {
EditingDomain editingDomain = getEditingDomain();
AdapterFactory customAdapterFactory = getCustomAdapterFactory();
if (customAdapterFactory != null) {
return customAdapterFactory;
} else if (editingDomain != null) {
return ((AdapterFactoryEditingDomain) editingDomain).getAdapterFactory();
}
return null;
}
/**
* Returns a custom {@link AdapterFactory adapter factory} to be used by this {@link BasicTransactionalFormEditor
* form editor} for creating {@link ItemProviderAdapter item provider}s which control the way how {@link EObject
* model element}s from given <code>editingDomain</code> are displayed and can be edited.
* <p>
* This implementation returns <code>null</code> as default. Clients which want to use their own
* {@link AdapterFactory adapter factory} (e.g., an {@link AdapterFactory adapter factory} that creates
* {@link ItemProviderAdapter item provider}s which are specifically designed for the {@link IEditorPart editor} in
* which this {@link BasicTransactionalFormEditor form editor} is used) may override this method and return any
* {@link AdapterFactory adapter factory} of their choice. This custom {@link AdapterFactory adapter factory} will
* then be returned as result by {@link #getAdapterFactory(TransactionalEditingDomain)}.
* </p>
*
* @return The custom {@link AdapterFactory adapter factory} that is to be used by this
* {@link BasicTransactionalFormEditor form editor}. <code>null</code> the default {@link AdapterFactory
* adapter factory} returned by {@link #getAdapterFactory(TransactionalEditingDomain)} should be used
* instead.
* @see #getAdapterFactory(TransactionalEditingDomain)
*/
protected AdapterFactory getCustomAdapterFactory() {
return null;
}
public AdapterFactoryItemDelegator getItemDelegator() {
if (itemDelegator == null) {
itemDelegator = createItemDelegator();
}
return itemDelegator;
}
protected AdapterFactoryItemDelegator createItemDelegator() {
AdapterFactory adapterFactory = getAdapterFactory();
if (adapterFactory != null) {
return new AdapterFactoryItemDelegator(adapterFactory);
}
return null;
}
/**
* Creates the common toolkit for this editor and adds pages to the editor.
*/
@Override
protected void createPages() {
// Editor input not yet available?
if (getEditorInputObject() == null) {
// Close editor if file behind editor input is out of scope
IFile file = EcoreUIUtil.getFileFromEditorInput(getEditorInput());
IModelDescriptor modelDescriptor = ModelDescriptorRegistry.INSTANCE.getModel(file);
if (modelDescriptor == null) {
MessageDialog.openError(getSite().getShell(), Messages.error_editorInitialization_title,
NLS.bind(Messages.error_editorInitialization_modelNotLoaded,
file != null ? file.getFullPath().toString() : getEditorInput().getName()));
showProblemsView();
close(false);
return;
}
// Request asynchronous loading of model behind editor input
ModelLoadManager.INSTANCE.loadModel(modelDescriptor, true, null);
// Create temporary page indicating that editor input is being loaded
setMessagePage(createLoadingEditorInputPage());
return;
}
// Show Problems view in case that underlying resource has errors or warnings
Resource editorInputResource = getEditorInputResource();
if (editorInputResource != null && (!editorInputResource.getErrors().isEmpty() || !editorInputResource.getWarnings().isEmpty())) {
showProblemsView();
}
// Create editor pages normally
super.createPages();
}
protected IFormPage getMessagePage() {
return messagePage;
}
protected void setMessagePage(IFormPage messagePage) {
if (!isDisposed()) {
// Remove old message page if present
if (this.messagePage != null) {
int pageIndex = this.messagePage.getIndex();
/*
* !! Important Note !! Before removing the page, get it's selectable user interface and set page
* control to null. The page control will not be disposed in MultiPageEditorPart#removePage(int).
*/
if (pageIndex >= 0 && pageIndex < pages.size()) {
CTabItem item = ((CTabFolder) getContainer()).getItem(pageIndex);
if (item != null) {
item.setControl(null);
}
}
// Call remove page
removePage(pageIndex);
}
// Add new message page if not null
if (messagePage != null) {
try {
addPage(messagePage);
if (getActivePage() == -1) {
setActivePage(0);
}
} catch (PartInitException ex) {
PlatformLogUtil.logAsError(Activator.getPlugin(), ex);
}
}
}
this.messagePage = messagePage;
}
protected void showProblemsView() {
try {
getEditorSite().getPage().showView("org.eclipse.ui.views.ProblemView"); //$NON-NLS-1$
} catch (PartInitException exception) {
PlatformLogUtil.logAsError(Activator.getPlugin(), exception);
}
}
protected IFormPage createLoadingEditorInputPage() {
return new MessagePage(this, NLS.bind(Messages.msg_waitingForModelObjectToBeLoaded, getEditorInputName()));
}
protected IFormPage createNoEditorInputPage() {
URI editorInputURI = EcoreUIUtil.getURIFromEditorInput(getEditorInput());
if (editorInputURI != null) {
String msg = editorInputURI.hasFragment() ? Messages.msg_modelObjectNotFound : Messages.msg_modelResourceIsEmpty;
return new MessagePage(this, NLS.bind(msg, getEditorInputName()));
}
return new MessagePage(this, ""); //$NON-NLS-1$
}
protected synchronized void finishCreatePages() {
if (!isDisposed() && getMessagePage() != null) {
// Remove previously displayed message page
setMessagePage(null);
// Try to create actual pages
createPages();
// Activate first page if no other page is already active
if (getActivePage() == -1) {
setActivePage(0);
}
}
}
/**
* This is the method used by the framework to install your own pages.
* <p>
* This implementation add a single {@link GenericContentsTreePage page} that displays the editor input object and
* its contents in a tree viewer.
* </p>
*/
@Override
protected void addPages() {
try {
addPage(new GenericContentsTreePage(this));
} catch (PartInitException ex) {
PlatformLogUtil.logAsError(Activator.getPlugin(), ex);
}
}
@Override
public IFormPage getActivePageInstance() {
// FIXME File bug to Eclipse Platform: Calling getActivePageInstance() causes NullPointerException if this
// editor has already been disposed
if (!isDisposed()) {
return super.getActivePageInstance();
}
return null;
}
@Override
protected void setActivePage(int pageIndex) {
// FIXME File bug to Eclipse Platform: Calling setActivePage(0) causes AssertionFailedException if for some
// reason no pages have been added yet or all pages have already been removed
if (pageIndex >= 0 && pageIndex < getPageCount()) {
super.setActivePage(pageIndex);
}
}
@Override
protected IEditorPart getEditor(int pageIndex) {
// FIXME File bug to Eclipse Platform: Calling setEditor(0) causes SWT error if for some reason no pages have
// been added yet or all pages have already been removed
if (pageIndex >= 0 && pageIndex < ((CTabFolder) getContainer()).getItemCount()) {
return super.getEditor(pageIndex);
}
return null;
}
@Override
public void saveState(IMemento memento) {
// Save editor dirty state; required upon editor restoration
memento.putBoolean(MEMENTO_KEY_EDITOR_DIRTY_ON_WORKBENCH_CLOSE, isDirty());
}
@Override
public void restoreState(IMemento memento) {
// Close editor if it has been left dirty upon last workbench close; in this case the editor input URI might be
// pointing at some model element that hasn't been saved and therefore doesn't exist upon editor restoration
if (memento.getBoolean(MEMENTO_KEY_EDITOR_DIRTY_ON_WORKBENCH_CLOSE)) {
close(false);
}
}
@Override
public Saveable[] getActiveSaveables() {
return getSaveables();
}
@Override
public Saveable[] getSaveables() {
// As Saveables management is based on ModelDescriptors & no ModelDescriptor for files outside the workspace, we
// return here a default Saveable
if (getEditorInput() instanceof FileStoreEditorInput) {
return new Saveable[] { new DefaultSaveable(this) };
}
if (modelSaveablesProvider != null) {
Saveable saveable = modelSaveablesProvider.getSaveable(getEditorInputResource());
if (saveable != null) {
return new Saveable[] { saveable };
}
}
return new Saveable[] {};
}
@Override
public String toString() {
Object editorInputObject = getEditorInputObject();
if (editorInputObject instanceof EObject) {
URI uri = EcoreUtil.getURI((EObject) editorInputObject);
if (uri != null) {
return uri.toString();
}
} else if (editorInputObject instanceof Resource) {
URI uri = ((Resource) editorInputObject).getURI();
if (uri != null) {
return uri.toString();
}
}
return super.toString();
}
@Override
public boolean containsEditorInputObject(IEditorInput editorInput, Set<EObject> objects) {
// Determine if editor input object or one of its containers is part of the specified set of objects
Object editorInputObject = getEditorInputObject();
if (editorInputObject == null) {
editorInputObject = getOldEditorInputObject();
}
if (editorInputObject != null) {
if (objects.contains(editorInputObject)) {
return true;
} else {
if (editorInputObject instanceof EObject) {
for (EObject parent = ((EObject) editorInputObject).eContainer(); parent != null; parent = parent.eContainer()) {
if (objects.contains(parent)) {
return true;
}
}
}
}
}
return false;
}
@Override
public boolean containsEditorInputResourceURI(IEditorInput editorInput, Set<URI> resourceURIs) {
URI editorInputURI = EcoreUIUtil.getURIFromEditorInput(editorInput);
if (editorInputURI != null) {
URI editorInputResourceURI = editorInputURI.trimFragment();
for (URI resourceURI : resourceURIs) {
if (resourceURI.equals(editorInputResourceURI)) {
return true;
}
}
}
return false;
}
public class BasicTransactionalFormEditorInputChangeHandler implements IModelEditorInputChangeHandler {
/**
* {@inheritDoc}
*/
@Override
public void handleEditorInputObjectAdded(IEditorInput editorInput, final Set<EObject> addedObjects) {
IWorkbenchPartSite site = getSite();
if (site != null && site.getShell() != null && !site.getShell().isDisposed()) {
site.getShell().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
// Reset the editor state
reset();
// Finish page creation if editor is currently visible; otherwise mark that as to be
// done when editor gets activated the next time
if (getSite().getPage().isPartVisible(BasicTransactionalFormEditor.this)) {
finishCreatePages();
} else {
finishCreatePagesOnActivation = true;
}
}
});
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleEditorInputObjectRemoved(IEditorInput editorInput, Set<EObject> removedObjects) {
// Close editor
close(false);
}
/**
* {@inheritDoc}
*/
@Override
public void handleEditorInputObjectMoved(IEditorInput editorInput, Set<EObject> movedObjects) {
handleEditorInputObjectChanged(editorInput, movedObjects);
}
/**
* {@inheritDoc}
*/
@Override
public void handleEditorInputObjectChanged(IEditorInput editorInput, final Set<EObject> changedObjects) {
// Handle affected objects
IWorkbenchPartSite site = getSite();
if (site != null && site.getShell() != null && !site.getShell().isDisposed()) {
site.getShell().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (isActivePart() || isActivePropertySheetPage()) {
// Try to select the affected objects
setSelectionToViewer(changedObjects);
}
// Update editor input if necessary (e.g., when editor input object is a model object and
// the latter has been renamed)
Object editorInputObject = getEditorInputObject();
if (editorInputObject instanceof EObject) {
URI newEditorInputObjectURI = EcoreUtil.getURI((EObject) editorInputObject);
updateEditorInput(newEditorInputObjectURI);
}
// Update editor part name
setPartName(getEditorInputName());
setTitleImage(getEditorInputImage());
}
});
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleEditorInputResourceLoaded(IEditorInput editorInput) {
// Invoked when editor input object has been added.
final IWorkbenchPartSite site = getSite();
if (site != null && site.getShell() != null && !site.getShell().isDisposed()) {
site.getShell().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
// Reset the editor state
reset();
// Finish page creation if editor is currently visible; otherwise mark that as to be done when
// editor gets activated the next time
if (site.getPage().isPartVisible(BasicTransactionalFormEditor.this)) {
finishCreatePages();
} else {
finishCreatePagesOnActivation = true;
}
}
});
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleEditorInputResourceUnloaded(IEditorInput editorInput) {
IWorkbenchPartSite site = getSite();
if (site != null && site.getShell() != null && !site.getShell().isDisposed()) {
site.getShell().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
// Reset the editor state
reset();
// Add message page indicating that no editor input is present (even if the editor is not
// visible)
setMessagePage(createNoEditorInputPage());
}
});
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleEditorInputResourceMoved(IEditorInput editorInput, URI oldURI, final URI newURI) {
IWorkbenchPartSite site = getSite();
if (site != null && site.getShell() != null && !site.getShell().isDisposed()) {
site.getShell().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
// Discard undo context
IOperationHistory operationHistory = getOperationHistory();
if (operationHistory != null) {
operationHistory.dispose(getModelEditorUndoContextManager().getUndoContext(), true, true, true);
}
// Update editor input
updateEditorInput(newURI);
// Update this editor's dirty state
firePropertyChange(IEditorPart.PROP_DIRTY);
}
});
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleEditorInputResourceRemoved(IEditorInput editorInput) {
// Close editor
close(false);
}
}
}