| /******************************************************************************* |
| * Copyright (c) 2016 ALL4TEC & CEA LIST. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * ALL4TEC & CEA LIST - initial API and implementation |
| ******************************************************************************/ |
| package org.polarsys.esf.core.common.ui.editor; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.EventObject; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IResourceChangeEvent; |
| import org.eclipse.core.resources.IResourceChangeListener; |
| import org.eclipse.core.resources.IResourceDelta; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| 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.ui.MarkerHelper; |
| import org.eclipse.emf.common.ui.editor.ProblemEditorPart; |
| import org.eclipse.emf.common.ui.viewer.IViewerProvider; |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.common.util.WrappedException; |
| import org.eclipse.emf.ecore.resource.Resource; |
| 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.ComposedAdapterFactory; |
| import org.eclipse.emf.edit.provider.IDisposable; |
| 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.UnwrappingSelectionProvider; |
| import org.eclipse.emf.edit.ui.util.EditUIMarkerHelper; |
| import org.eclipse.emf.edit.ui.util.EditUIUtil; |
| import org.eclipse.emf.edit.ui.view.ExtendedPropertySheetPage; |
| import org.eclipse.jface.action.IMenuListener; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.IStatusLineManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.action.ToolBarManager; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.dialogs.ProgressMonitorDialog; |
| import org.eclipse.jface.viewers.IBaseLabelProvider; |
| import org.eclipse.jface.viewers.IContentProvider; |
| 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.TreeViewerColumn; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerFilter; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.CTabFolder; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.Transfer; |
| import org.eclipse.swt.events.ControlAdapter; |
| import org.eclipse.swt.events.ControlEvent; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.ui.IActionBars; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IPartListener; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.actions.WorkspaceModifyOperation; |
| import org.eclipse.ui.ide.IGotoMarker; |
| import org.eclipse.ui.part.MultiPageEditorPart; |
| import org.eclipse.ui.views.properties.IPropertySheetPage; |
| import org.eclipse.ui.views.properties.IPropertySourceProvider; |
| import org.eclipse.ui.views.properties.PropertyColumnLabelProvider; |
| import org.eclipse.ui.views.properties.PropertySheet; |
| import org.eclipse.ui.views.properties.PropertySheetPage; |
| import org.eclipse.uml2.uml.UMLPackage; |
| import org.polarsys.esf.core.common.adapter.ProblemIndicationAdapter; |
| import org.polarsys.esf.core.common.listener.IProblemIndicationListener; |
| import org.polarsys.esf.core.common.ui.CommonUIActivator; |
| import org.polarsys.esf.core.common.ui.adapter.PartListenerAdapter; |
| import org.polarsys.esf.core.common.ui.filter.AbstractFilter; |
| import org.polarsys.esf.core.common.ui.filter.IFilterPartListener; |
| import org.polarsys.esf.core.common.ui.filter.NamePropertyFilter; |
| import org.polarsys.esf.core.common.ui.provider.AdapterFactoryColumnLabelProvider; |
| import org.polarsys.esf.core.common.ui.provider.IToolBarManagerProvider; |
| import org.polarsys.esf.core.common.ui.view.properties.ArrayInputPropertySourceProvider; |
| import org.polarsys.esf.core.common.ui.view.properties.PropertySourceProvider; |
| import org.polarsys.esf.core.common.ui.widget.TreeViewerUtils; |
| import org.polarsys.esf.core.common.visitor.ResourceDeltaVisitor; |
| |
| /** |
| * This is the abstract base for all the multi page editor, used to create and edit a resource content. |
| * The default implementation will manage a main page with a tree viewer and a toolbar, and a optional page |
| * only displayed if an error is identified in the resource content. |
| * |
| * It extends {@link MultiPageEditorPart} by convenience, even if it actually contains only one page. |
| * |
| * It implements : |
| * <ul> |
| * <li>{@link IEditingDomainProvider} : To provide the editing domain that it edits</li> |
| * <li>{@link ISelectionProvider} : To provide its selection to potential listeners</li> |
| * <li>{@link IMenuListener} : To be able to react when its context menu is going to be opened</li> |
| * <li>{@link IViewerProvider} : To provide its viewer</li> |
| * <li>{@link IGotoMarker} : To be able to react to a 'Go to' action, pointing to an element in its viewer</li> |
| * <li>{@link IToolBarManagerProvider} : To be able to provide the manager of the custom toolbar in the main page</li> |
| * <li>{@link IProblemIndicationListener} : To be able to react to the resources analysis, and warn the user if any |
| * error is detected</li> |
| * <li>{@link IFilterPartListener} : To be able to filter its content according to a text value.</li> |
| * </ul> |
| * |
| * @author $Author: jdumont $ |
| * @version $Revision: 83 $ |
| */ |
| public abstract class AbstractMultiPageEditor |
| extends MultiPageEditorPart |
| implements IEditingDomainProvider, ISelectionProvider, IMenuListener, IViewerProvider, IGotoMarker, |
| IToolBarManagerProvider, IProblemIndicationListener, IFilterPartListener { |
| |
| /** Size of the first column. */ |
| private static final int FIRST_COLUMN_DEFAULT_SIZE = 400; |
| |
| /** This keeps track of the editing domain that is used to track all changes to the resource. */ |
| private AdapterFactoryEditingDomain mEditingDomain = null; |
| |
| /** This is the adapter factory used for providing views of the resource content. */ |
| private AdapterFactory mAdapterFactory = null; |
| |
| /** The property source provider used in the viewer. */ |
| private IPropertySourceProvider mPropertySourceProvider = null; |
| |
| /** |
| * The listener to plug to the command stack of the editing domain used, |
| * to be warned when any change append. |
| */ |
| private CommandStackListener mCommandStackListener = new CommandStackListener() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void commandStackChanged(final EventObject pEvent) { |
| // Use an asynchronous UI thread, to call the 'handleCommandStackChanged' method |
| // to react to the commands execution |
| AbstractMultiPageEditor.this.getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void run() { |
| handleCommandStackChanged(((CommandStack) pEvent.getSource()).getMostRecentCommand()); |
| } |
| }); |
| } |
| }; |
| |
| /** This is the property sheet page provided by the editor. */ |
| private PropertySheetPage mPropertySheetPage = null; |
| |
| /** This is the tool bar displayed in the main page of the editor. */ |
| private ToolBarManager mEditorToolBarManager = null; |
| |
| /** |
| * This is the tree viewer displayed in the editor main page, |
| * to edit the resource content. |
| */ |
| private TreeViewer mEditorTreeViewer = null; |
| |
| /** This listens to the selection in the active viewer. */ |
| private ISelectionChangedListener mSelectionChangedListener = null; |
| |
| /** This keeps track of all the ISelectionChangedListener that are listening to this editor. */ |
| private List<ISelectionChangedListener> mSelectionChangedListenersList = new ArrayList<ISelectionChangedListener>(); |
| |
| /** This keeps track of the selection of the editor as a whole. */ |
| private ISelection mEditorSelection = StructuredSelection.EMPTY; |
| |
| /** |
| * The MarkerHelper is responsible for creating workspace resource markers presented |
| * in Eclipse's Problems View. |
| */ |
| private MarkerHelper mMarkerHelper = new EditUIMarkerHelper(); |
| |
| /** |
| * This listens to the parts activation to react when |
| * the properties view or this editor instance becomes active for example. |
| */ |
| private IPartListener mPartListener = createPartListener(); |
| |
| /** Resources that have been removed since last activation. */ |
| private List<Resource> mRemovedResourcesList = new ArrayList<Resource>(); |
| |
| /** Resources that have been changed since last activation from other editors or views. */ |
| private List<Resource> mChangedResourcesList = new ArrayList<Resource>(); |
| |
| /** Resources that have been saved. */ |
| private List<Resource> mSavedResourcesList = new ArrayList<Resource>(); |
| |
| /** Controls whether the problem indication should be updated or not. */ |
| private boolean mUpdateProblemIndication = true; |
| |
| /** Adapter used to analyse the resources when they are going to be loaded. */ |
| private ProblemIndicationAdapter mProblemIndicationAdapter = new ProblemIndicationAdapter(); |
| |
| /** |
| * This listens for workspace changes to maintain the lists of changed or removed resources. |
| * This is mandatory to treat any edition conflict with other views or editors. |
| */ |
| private IResourceChangeListener mWorkspaceChangeListener = new IResourceChangeListener() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void resourceChanged(final IResourceChangeEvent pEvent) { |
| // Get the delta identifying all the changes |
| IResourceDelta vDelta = pEvent.getDelta(); |
| |
| try { |
| // Build the visitor instance and launch it |
| final ResourceDeltaVisitor vVisitor = new ResourceDeltaVisitor(mEditingDomain.getResourceSet()); |
| vDelta.accept(vVisitor); |
| |
| // Check if any resources have been removed |
| if (!vVisitor.getRemovedResources().isEmpty()) { |
| |
| // Remove from the saved resources those which have been removed |
| mSavedResourcesList.removeAll(vVisitor.getChangedResources()); |
| |
| // Remember of the resources which have been removed |
| mRemovedResourcesList.addAll(vVisitor.getRemovedResources()); |
| |
| // Use an asynchronous UI thread to close the editor if the |
| // resources used are deleted |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void run() { |
| if (!isDirty()) { |
| getSite().getPage().closeEditor(AbstractMultiPageEditor.this, false); |
| } |
| } |
| }); |
| } |
| |
| // Check if any resources have changed |
| if (!vVisitor.getChangedResources().isEmpty()) { |
| |
| // Loop on the changed resource found by the visitor to update the list |
| // of resources managed by this editor |
| for (Resource vResource : vVisitor.getChangedResources()) { |
| |
| // Check if the resource is considered as saved |
| if (mSavedResourcesList.contains(vResource)) { |
| // The resource is considered as saved by the editor, thus |
| // the change found by the visitor is linked to the save action. |
| // In other words, the resource is not changed from the editor point of view, |
| // but it can now be removed from the saved resources list to prepare its next change |
| mSavedResourcesList.remove(vResource); |
| } else { |
| // The resource is not considered as saved, the change |
| // found by the visitor was a real change from an other editor |
| // Thus add the resource in the corresponding list |
| mChangedResourcesList.add(vResource); |
| } |
| } |
| |
| // Use an asynchronous UI thread to refresh the editor if the |
| // resources used have changed |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void run() { |
| if (AbstractMultiPageEditor.this.equals(getSite().getPage().getActiveEditor())) { |
| handleActivate(); |
| } |
| } |
| }); |
| } |
| |
| } catch (final CoreException pException) { |
| CommonUIActivator.logError("Error due to a resource change", //$NON-NLS-1$ |
| pException); |
| } |
| } |
| }; |
| |
| /** |
| * Default constructor. |
| */ |
| public AbstractMultiPageEditor() { |
| // Call the parent constructor |
| super(); |
| |
| // Initialise the editing domain used by the editor |
| initializeEditingDomain(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void dispose() { |
| // Disable the problems indication update |
| mUpdateProblemIndication = false; |
| |
| // Remove the listeners from the different services |
| // ... The workspace listener |
| ResourcesPlugin.getWorkspace().removeResourceChangeListener(mWorkspaceChangeListener); |
| // ... The parts listener |
| if (mPartListener != null) { |
| getSite().getPage().removePartListener(mPartListener); |
| } |
| |
| // Dispose the adapter factory used in the editor |
| if (mAdapterFactory instanceof IDisposable) { |
| ((IDisposable) mAdapterFactory).dispose(); |
| } |
| |
| // Unregister the active editor from the action bar |
| if (this.equals(getActionBarContributor().getActiveEditor())) { |
| getActionBarContributor().setActiveEditor(null); |
| } |
| |
| // Dispose the views provided by this editor |
| // ... The property sheet page |
| if (mPropertySheetPage != null) { |
| mPropertySheetPage.dispose(); |
| } |
| |
| super.dispose(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * This implementation sets the editor site to the given site, its |
| * input to the given input, and the site's selection provider this editor instance. |
| */ |
| @Override |
| public void init(final IEditorSite pSite, final IEditorInput pEditorInput) { |
| // Set the editor site |
| setSite(pSite); |
| |
| // Set the editor input and launch an notification for this update |
| setInputWithNotify(pEditorInput); |
| |
| // Set the editor title, with the input name |
| setPartName(pEditorInput.getName()); |
| |
| // Set this instance as selection provider of the site |
| pSite.setSelectionProvider(this); |
| |
| // If a part listener is created, register it |
| if (mPartListener != null) { |
| pSite.getPage().addPartListener(mPartListener); |
| } |
| |
| // Register to listen to the problems indication |
| mProblemIndicationAdapter.addProblemIndicationListener(this); |
| |
| // Finally, add a listener to the workspace resources |
| ResourcesPlugin.getWorkspace().addResourceChangeListener( |
| mWorkspaceChangeListener, |
| IResourceChangeEvent.POST_CHANGE); |
| } |
| |
| /** |
| * This sets up the editing domain for the model editor. |
| */ |
| protected void initializeEditingDomain() { |
| // Create the adapter factory |
| mAdapterFactory = createAdapterFactory(); |
| |
| // Create the command stack used to notify this editor as commands are executed |
| CommandStack vCommandStack = createCommandStack(); |
| |
| // Create the editing domain with the created command stack |
| mEditingDomain = |
| new AdapterFactoryEditingDomain(mAdapterFactory, vCommandStack, new HashMap<Resource, Boolean>()); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * 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() { |
| return mEditingDomain; |
| } |
| |
| /** |
| * Create and return an instance of {@link AdapterFactory} and initialise it with the adapter factories |
| * linked to the manipulated resources. |
| * |
| * @return The created {@link ComposedAdapterFactory} |
| */ |
| protected abstract AdapterFactory createAdapterFactory(); |
| |
| /** |
| * Create and return a default command stack, which allow to react to its changes |
| * using the {@link AbstractMultiPageEditor#handleCommandStackChanged(Command)} method. |
| * |
| * @return The command stack created |
| */ |
| protected CommandStack createCommandStack() { |
| // Create the command stack that will notify this editor as commands are executed |
| BasicCommandStack vCommandStack = new BasicCommandStack(); |
| |
| // Add a listener to set the most recent command's affected objects to be the selection of the viewer with focus |
| vCommandStack.addCommandStackListener(mCommandStackListener); |
| |
| return vCommandStack; |
| } |
| |
| /** |
| * This method is called when the command stack is changed, to allow the |
| * page to react. By default, this will simply select the objects |
| * affected by the command in the viewer, and refresh the provided property |
| * sheet page. |
| * |
| * @param pMostRecentCommand The most recent command |
| */ |
| protected void handleCommandStackChanged(final Command pMostRecentCommand) { |
| |
| // Change the editor state to dirty |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| |
| if (pMostRecentCommand != null) { |
| |
| // Refresh the tree content |
| // NB : As a same object can be displayed several times in the tree, |
| // the standard refresh which uses the first affected object instance |
| // is not sufficient. A full refresh is mandatory, for example to display |
| // correctly a new child on each object representation, and not only on the first one |
| if (mEditorTreeViewer != null) { |
| mEditorTreeViewer.refresh(); |
| } |
| |
| // Try to select the objects affected by the last command |
| setSelectionToViewer(pMostRecentCommand.getAffectedObjects()); |
| } |
| |
| // Refresh the property sheet page content with the new selection |
| if (mPropertySheetPage != null && !mPropertySheetPage.getControl().isDisposed()) { |
| mPropertySheetPage.refresh(); |
| } |
| } |
| |
| /** |
| * Handles activation of the editor or it's associated views, like the properties view. |
| */ |
| protected void handleActivate() { |
| |
| // Recompute the read only state of the resources |
| if (mEditingDomain.getResourceToReadOnlyMap() != null) { |
| mEditingDomain.getResourceToReadOnlyMap().clear(); |
| |
| // Refresh any actions that may become enabled or disabled |
| setSelection(getSelection()); |
| } |
| |
| if (!mRemovedResourcesList.isEmpty()) { |
| // If some resources have been removed since |
| // the last activation, ask the user if the changes |
| // must be discarded |
| if (handleDirtyConflict()) { |
| // The user has chosen to close the editor and discard the changes |
| getSite().getPage().closeEditor(AbstractMultiPageEditor.this, false); |
| } else { |
| // The user wants to keep the changes, so clear the changes collections |
| mRemovedResourcesList.clear(); |
| mChangedResourcesList.clear(); |
| mSavedResourcesList.clear(); |
| } |
| |
| } else if (!mChangedResourcesList.isEmpty()) { |
| // If some resources have been changed since |
| // the last activation, handle it |
| |
| // ... First remove form the changes all what has been saved |
| mChangedResourcesList.removeAll(mSavedResourcesList); |
| |
| // ... Then handle the changes |
| handleChangedResources(); |
| |
| // ... And finally clear the changes collections |
| mChangedResourcesList.clear(); |
| mSavedResourcesList.clear(); |
| } |
| } |
| |
| /** |
| * Handles what to do with changed resources on activation. |
| */ |
| protected void handleChangedResources() { |
| // Check if some changes have been identified since last activation |
| if (!mChangedResourcesList.isEmpty()) { |
| |
| if (!isDirty() || handleDirtyConflict()) { |
| // If the editor is not dirty or if the user has chosen |
| // to discard the changes, the editor resources must be |
| // reloaded, and revalidated |
| |
| if (isDirty()) { |
| // If the editor is dirty, all the resources of the |
| // current resources set are considered as changed |
| mChangedResourcesList.addAll(mEditingDomain.getResourceSet().getResources()); |
| } |
| |
| // Dispose all the commands of the stack |
| mEditingDomain.getCommandStack().flush(); |
| |
| // Temporary disable the problems indication, while the |
| // diagnostics are updated |
| mUpdateProblemIndication = false; |
| |
| // Loop on the resources considered as changed since the last activation |
| // to try to load them and ensure that they are valid |
| for (Resource vResource : mChangedResourcesList) { |
| if (vResource.isLoaded()) { |
| // If the resource is already loaded, unload it |
| vResource.unload(); |
| |
| try { |
| // Then try to reload it |
| vResource.load(Collections.EMPTY_MAP); |
| } catch (final IOException pException) { |
| // An exception as been thrown during the resource load, |
| // remember of the resource problem |
| mProblemIndicationAdapter.analyzeResource(vResource, pException); |
| } |
| } |
| } |
| |
| // If the editor selection points to something which is unloaded, |
| // reset the selection |
| if (AdapterFactoryEditingDomain.isStale(mEditorSelection)) { |
| setSelection(StructuredSelection.EMPTY); |
| } |
| |
| // Finally, reactivate the problems indication |
| mUpdateProblemIndication = true; |
| |
| // Finally, get the diagnostic from the problem indication adapter |
| // and update its content |
| updateProblemIndication(mProblemIndicationAdapter.getDiagnostic()); |
| } |
| } |
| } |
| |
| /** |
| * Shows a dialog that asks if conflicting changes should be discarded. |
| * |
| * @return <code>true</code> if the changes in the editor must be discarded |
| */ |
| protected boolean handleDirtyConflict() { |
| return MessageDialog.openQuestion( |
| getSite().getShell(), |
| CommonUIActivator.getMessages().getString("AbstractMultiPageEditor.fileconflict.title"), //$NON-NLS-1$ |
| CommonUIActivator.getMessages().getString("AbstractMultiPageEditor.fileconflict.message")); //$NON-NLS-1$ |
| } |
| |
| /** |
| * This sets the selection into whichever viewer is active. |
| * It can be the viewer in the editor or in one of its linked view. |
| * |
| * @param pSelectionCollection The collection of elements to select |
| */ |
| protected void setSelectionToViewer(final Collection<?> pSelectionCollection) { |
| |
| // Ensure that the collection to select is valid |
| if (pSelectionCollection != null && !pSelectionCollection.isEmpty()) { |
| |
| // Use an asynchronous UI thread to update the selection |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void run() { |
| // Try to select the items in the editor content viewer |
| if (mEditorTreeViewer != null) { |
| mEditorTreeViewer.setSelection(new StructuredSelection(pSelectionCollection.toArray()), true); |
| } |
| } |
| }); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * This returns the viewer used to edit the resource content, |
| * as required by the {@link IViewerProvider} interface. |
| * |
| * Moreover, it's overridden to return a {@link TreeViewer} instead of a {@link Viewer}. |
| * |
| */ |
| @Override |
| public TreeViewer getViewer() { |
| return mEditorTreeViewer; |
| } |
| |
| /** |
| * This creates a context menu for the viewer and adds a listener as well registering the menu for extension. |
| * |
| * @param pViewer The viewer for which the context menu will be created |
| */ |
| protected void createContextMenuFor(final StructuredViewer pViewer) { |
| // Create the context menu manager |
| MenuManager vContextMenuManager = new MenuManager("#PopUp"); //$NON-NLS-1$ |
| |
| vContextMenuManager.add(new Separator("additions")); //$NON-NLS-1$ |
| vContextMenuManager.setRemoveAllWhenShown(true); |
| vContextMenuManager.addMenuListener(this); |
| |
| // Register the menu manager created as the viewer context menu |
| Menu vContextMenu = vContextMenuManager.createContextMenu(pViewer.getControl()); |
| pViewer.getControl().setMenu(vContextMenu); |
| getSite().registerContextMenu(vContextMenuManager, new UnwrappingSelectionProvider(pViewer)); |
| } |
| |
| /** |
| * This creates the drag and drop support mechanisms for the given viewer. |
| * |
| * @param pViewer The viewer for which the drag and drop supports will be created |
| */ |
| protected void createDragAndDropSupportFor(final StructuredViewer pViewer) { |
| // Get the drag and drop operations supported |
| int vDndOperations = getDragAndDropSupportedOperations(); |
| |
| Transfer[] vTransfersArray = new Transfer[] {LocalTransfer.getInstance()}; |
| |
| // Add the drag and drop support to the viewer |
| pViewer.addDragSupport(vDndOperations, vTransfersArray, new ViewerDragAdapter(pViewer)); |
| |
| pViewer.addDropSupport(vDndOperations, vTransfersArray, new EditingDomainViewerDropAdapter( |
| mEditingDomain, |
| pViewer)); |
| } |
| |
| /** |
| * Return an <code>int</code> corresponding to the operations |
| * supported by the drag and drop actions. |
| * |
| * By default, the drag and drop are allowed for a 'move' action only. |
| * |
| * @return The supported drag and drop operation flag |
| */ |
| protected int getDragAndDropSupportedOperations() { |
| return DND.DROP_MOVE; |
| } |
| |
| /** |
| * This is the method called to load the resource used as input |
| * from the editing domain's resource set. |
| * |
| * This will analyse the resource to find any problem. |
| */ |
| protected void initializeInputResource() { |
| // Get the URI of the resource used as input for the editor |
| URI vResourceURI = EditUIUtil.getURI(getEditorInput()); |
| |
| Exception vException = null; |
| Resource vResource = null; |
| |
| try { |
| // Try to load the resource through the editing domain |
| vResource = mEditingDomain.getResourceSet().getResource(vResourceURI, true); |
| |
| } catch (final WrappedException pException) { |
| // An exception is thrown during the load, get the resource without loading it |
| vException = pException; |
| vResource = mEditingDomain.getResourceSet().getResource(vResourceURI, false); |
| } |
| |
| // Analyse the resource and remember of the resulting diagnostic if there is any error |
| mProblemIndicationAdapter.analyzeResource(vResource, vException); |
| |
| // Finally, register the problem indication adapter to the resource set adapter |
| // to listen to the events on the resources |
| mEditingDomain.getResourceSet().eAdapters().add(mProblemIndicationAdapter); |
| } |
| |
| /** |
| * Find the initial input used in the viewer of this editor. |
| * |
| * @return The initial input found |
| */ |
| protected abstract Object getInitialInput(); |
| |
| /** |
| * {@inheritDoc} |
| * |
| * This is the method used by the framework to install your own pages |
| * in the editor. This default implementation will create and add a main page, |
| * and prepare the editor to be able to display tabs if other pages are added. |
| */ |
| @Override |
| public void createPages() { |
| // Creates the model from the editor input |
| initializeInputResource(); |
| |
| // Only creates the other pages if there is something that can be edited |
| if (!getEditingDomain().getResourceSet().getResources().isEmpty()) { |
| |
| // Create the main page, used to edit the resource content |
| Composite vMainPage = createMainPage(); |
| |
| // Then add the new page to the editor and set its title |
| final int vPageIndex = addPage(vMainPage); |
| setPageText(vPageIndex, getMainPageTitle()); |
| |
| // Add optionally others pages |
| createOptionalPages(); |
| |
| // Use and asynchronous UI thread to activate the new page |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void run() { |
| setActivePage(vPageIndex); |
| } |
| }); |
| |
| } |
| |
| // Ensures that this editor will only display the page's tab |
| // area if there are more than one page |
| getContainer().addControlListener(new ControlAdapter() { |
| |
| /** Flag used to prevent from untimely resized events. */ |
| private boolean mGuard = false; |
| |
| /** |
| * {@inheritDoc} |
| * |
| * Hide the tabs during the resizing. |
| */ |
| @Override |
| public void controlResized(final ControlEvent pEvent) { |
| // Use a guard to prevent from untimely events as the |
| // tabs hiding will launch resized events |
| if (!mGuard) { |
| mGuard = true; |
| hideTabs(); |
| mGuard = false; |
| } |
| } |
| }); |
| |
| // Finally, get the diagnostic from the problem indication adapter |
| // and update its content |
| updateProblemIndication(mProblemIndicationAdapter.getDiagnostic()); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setFocus() { |
| // Set the focus to the active page |
| getControl(getActivePage()).setFocus(); |
| } |
| |
| /** |
| * If there is just one page in the multi-page editor part, |
| * this hides the single tab at the bottom. |
| */ |
| private void hideTabs() { |
| // Ensure that there is just one page |
| if (getPageCount() <= 1) { |
| // Reset the first page text |
| setPageText(0, StringUtils.EMPTY); |
| |
| if (getContainer() instanceof CTabFolder) { |
| // Remove the tabs from the container |
| ((CTabFolder) getContainer()).setTabHeight(1); |
| Point vPoint = getContainer().getSize(); |
| getContainer().setSize(vPoint.x, vPoint.y + 6); |
| } |
| } |
| } |
| |
| /** |
| * If there is more than one page in the multi-page editor part, |
| * this shows the tabs at the bottom. |
| */ |
| private void showTabs() { |
| // Ensure that there is more than one page |
| if (getPageCount() > 1) { |
| // Update the page text |
| setPageText(0, getMainPageTitle()); |
| |
| if (getContainer() instanceof CTabFolder) { |
| // Display the tabs in the container |
| ((CTabFolder) getContainer()).setTabHeight(SWT.DEFAULT); |
| Point vPoint = getContainer().getSize(); |
| getContainer().setSize(vPoint.x, vPoint.y - 6); |
| } |
| } |
| } |
| |
| /** |
| * Create optional pages. By default, nothing is done. |
| */ |
| protected void createOptionalPages() { |
| // Nothing to do |
| } |
| |
| /** |
| * Create a composite corresponding to the main page of the editor, containing a |
| * custom toolbar and a tree viewer used to edit the resource content. |
| * |
| * @return The main composite corresponding to the page created |
| */ |
| protected Composite createMainPage() { |
| // Create the page main composite |
| Composite vMainComposite = new Composite(getContainer(), SWT.NONE); |
| |
| // Set the main composite layout |
| GridLayout vMainLayout = new GridLayout(); |
| vMainLayout.horizontalSpacing = 0; |
| vMainLayout.verticalSpacing = 0; |
| vMainLayout.marginHeight = 0; |
| vMainLayout.marginWidth = 0; |
| vMainComposite.setLayout(vMainLayout); |
| vMainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| |
| // Create the editor toolbar |
| createMainToolbar(vMainComposite); |
| |
| // Create and initialise the tree viewer |
| createMainTreeViewer(vMainComposite); |
| |
| // Create the context menu for the viewer, once the selection is updated |
| createContextMenuFor(mEditorTreeViewer); |
| |
| // Create the drag and drop support for the new viewer |
| createDragAndDropSupportFor(mEditorTreeViewer); |
| |
| return vMainComposite; |
| } |
| |
| /** |
| * Return the title for the main page created by the method {@link AbstractMultiPageEditor#createMainPage()}. |
| * |
| * @return The main page title |
| */ |
| protected abstract String getMainPageTitle(); |
| |
| /** |
| * Create the tool bar for the main page, in the parent composite |
| * given. This will also create the tool bar manager used to add |
| * the actions in the toolbar. |
| * |
| * @param pParent The parent composite where the toolbar must be created |
| */ |
| protected void createMainToolbar(final Composite pParent) { |
| // Create the toolbar at the top of the page main composite |
| // NB : Use the flat style, to allow the display of separator bars, and don't |
| // set any layout to avoid UI bugs (see ToolBar javadoc) |
| ToolBar vEditorToolBar = new ToolBar(pParent, SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL); |
| vEditorToolBar.setLayoutData(new GridData(SWT.END, SWT.CENTER, true, false)); |
| |
| // Create the toolbar manager, linked to the tool bar |
| // It will be this manager which will be used to add actions in the toolbar |
| mEditorToolBarManager = new ToolBarManager(vEditorToolBar); |
| } |
| |
| /** |
| * Create the tree viewer in the main page to edit the resource content. |
| * The tree viewer is created in the parent composite given. |
| * |
| * This will also create the selection change listener and attach it to the |
| * tree viewer created. |
| * |
| * Moreover, the tree viewer is directly initialised with the needed label and |
| * content provider, and with its input. |
| * |
| * @param pParent The parent composite where the toolbar must be created |
| */ |
| protected void createMainTreeViewer(final Composite pParent) { |
| // Create a tree viewer, allowing multiple selection |
| mEditorTreeViewer = new TreeViewer(pParent, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION); |
| |
| mEditorTreeViewer.getTree().setLayout(new GridLayout()); |
| mEditorTreeViewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| |
| mEditorTreeViewer.getTree().setLinesVisible(true); |
| |
| // Add all the known filters to the tree viewer |
| for (AbstractFilter vFilter : getFilters()) { |
| mEditorTreeViewer.addFilter(vFilter); |
| } |
| |
| // Create the selection changed listener on the new tree viewer |
| if (mSelectionChangedListener == null) { |
| mSelectionChangedListener = new ISelectionChangedListener() { |
| |
| /** |
| * {@inheritDoc} |
| * |
| * This just notifies those things that are affected by the section. |
| */ |
| @Override |
| public void selectionChanged(final SelectionChangedEvent pSelectionChangedEvent) { |
| setSelection(pSelectionChangedEvent.getSelection()); |
| } |
| }; |
| } |
| |
| // Start listening to the new tree viewer |
| mEditorTreeViewer.addSelectionChangedListener(mSelectionChangedListener); |
| |
| // Create the required column for the tree viewer |
| // NB : This column will use the label provider which will be set the |
| // the tree viewer hereafter |
| int vFirstColumnSize = FIRST_COLUMN_DEFAULT_SIZE; |
| if (1 == getFinalColumnNumber()) { |
| // There will be only one column, take a maximum size |
| vFirstColumnSize = FIRST_COLUMN_DEFAULT_SIZE * 2; |
| } |
| createMainTreeViewerRequiredColumn(vFirstColumnSize); |
| |
| // Set the behaviour of the tree viewer, to customise how the elements are displayed |
| // and what is the content of the tree |
| mEditorTreeViewer.setContentProvider(createContentProvider()); |
| mEditorTreeViewer.setLabelProvider(createLabelProvider()); |
| |
| // Create the optional columns |
| // NB : These columns will use their own label provider, |
| // defined from the property that they will display |
| createMainTreeViewerOptionalColumns(); |
| |
| // Once all the columns are added, ensure that the header will be displayed |
| mEditorTreeViewer.getTree().setHeaderVisible(true); |
| |
| // Set the viewer input |
| mEditorTreeViewer.setInput(getInitialInput()); |
| |
| // Then expand the first level if possible to don't display |
| // only the root element by default |
| mEditorTreeViewer.expandToLevel(2); |
| } |
| |
| /** |
| * Create the first required column in the tree viewer of the main page. |
| * This column will use the tree viewer label provider. |
| * |
| * In the default implementation, the required column is used to display |
| * the value of the 'name' property, for all the elements. |
| * |
| * @param pInitialSize The initial width of column |
| */ |
| protected void createMainTreeViewerRequiredColumn(final int pInitialSize) { |
| // Create the required column, to display the object name |
| createMainTreeViewerColumn( |
| CommonUIActivator.getMessages().getString("AbstractMultiPageEditor.requiredcolumn.header"), //$NON-NLS-1$ |
| SWT.LEFT, |
| pInitialSize, |
| UMLPackage.Literals.NAMED_ELEMENT__NAME.getName()); |
| } |
| |
| /** |
| * Create the others columns in the tree viewer of the main page. |
| * Theses columns will use their own label provider. |
| * |
| * In the default implementation, no column is added. |
| */ |
| protected void createMainTreeViewerOptionalColumns() { |
| // Nothing to do by default |
| } |
| |
| /** |
| * Return the property source provider to associate to the viewer. |
| * If no property source provider is known, create a default one. |
| * |
| * @return Return the property source provider to associate to the viewer |
| */ |
| protected IPropertySourceProvider getPropertySourceProvider() { |
| if (mPropertySourceProvider == null) { |
| mPropertySourceProvider = createPropertySourceProvider(); |
| } |
| return mPropertySourceProvider; |
| } |
| |
| /** |
| * Create and return a default property source provider based on the adapter factory. |
| * |
| * @return The created property source provider |
| */ |
| protected IPropertySourceProvider createPropertySourceProvider() { |
| return new PropertySourceProvider(getAdapterFactory()); |
| } |
| |
| /** |
| * Create a new column in the tree viewer of the main page. |
| * This column will be initialised with the given name, width and alignment. |
| * |
| * Moreover, if a property Id is given, the column content and behaviour will |
| * be adapted to work on this property. |
| * |
| * @param pColumnName The name of the column |
| * @param pAlignment The column alignment |
| * @param pColumnWidth The column width |
| * @param pPropertyID The ID of the property that this column will display |
| * @return The column created |
| */ |
| protected TreeViewerColumn createMainTreeViewerColumn( |
| final String pColumnName, |
| final int pAlignment, |
| final int pColumnWidth, |
| final String pPropertyID) { |
| |
| PropertyColumnLabelProvider vLabelProvider = null; |
| |
| if (pPropertyID != null) { |
| // Build the label provider, based on the property whose |
| // id is given in parameter |
| vLabelProvider = new PropertyColumnLabelProvider(getPropertySourceProvider(), pPropertyID); |
| } |
| |
| // Finally, create the new column |
| // NB : Doesn't set an editing support : the main column is voluntary never editable |
| final TreeViewerColumn vTreeViewerColumn = |
| TreeViewerUtils.createViewerColumn(getViewer(), pColumnName, pAlignment, pColumnWidth, vLabelProvider); |
| |
| return vTreeViewerColumn; |
| } |
| |
| /** |
| * Create and return a label provider based on the adapter factory |
| * to be able to find the texts and images to display in the viewer. |
| * |
| * @return The created label provider |
| */ |
| protected IBaseLabelProvider createLabelProvider() { |
| // Create a custom label provider which is able to |
| // manage the tree viewer with columns |
| IBaseLabelProvider vLabelProvider = new AdapterFactoryColumnLabelProvider(getAdapterFactory()); |
| |
| return vLabelProvider; |
| } |
| |
| /** |
| * Create and return a default content provider. |
| * This will manage directly different kinds of input, like a resource, or |
| * an array, to display their content directly in the viewer. |
| * |
| * @return The created content provider |
| */ |
| protected IContentProvider createContentProvider() { |
| return new ArrayInputPropertySourceProvider(getAdapterFactory()); |
| } |
| |
| /** |
| * Create a new part listener specific to the behaviour of this instance. |
| * |
| * The default implementation will react to the activation of this editor instance |
| * or to the activation of the property sheet page provided. |
| * |
| * @return The part listener created |
| */ |
| protected IPartListener createPartListener() { |
| return new PartListenerAdapter() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void partActivated(final IWorkbenchPart pPart) { |
| // Check what is the part activated ... |
| if (pPart instanceof PropertySheet) { |
| // ... The properties view is activated, check if the page |
| // activated is the one provided by this editor |
| if (((PropertySheet) pPart).getCurrentPage().equals(mPropertySheetPage)) { |
| // Activate the actions linked to this editor instance |
| getActionBarContributor().setActiveEditor(AbstractMultiPageEditor.this); |
| |
| // React to the associated page activation |
| handleActivate(); |
| } |
| |
| } else if (pPart.equals(AbstractMultiPageEditor.this)) { |
| // ... The current editor instance is activated |
| |
| // Activate the actions linked to this editor instance |
| getActionBarContributor().setActiveEditor(AbstractMultiPageEditor.this); |
| |
| // React to the editor instance activation |
| handleActivate(); |
| } |
| } |
| }; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * This is how the framework determines which interfaces we implement. |
| */ |
| @SuppressWarnings("rawtypes") |
| @Override |
| public Object getAdapter(final Class pKeyClass) { |
| Object vAdapter = null; |
| |
| if (pKeyClass.equals(IPropertySheetPage.class)) { |
| // If the expecting result is a properties page, provide |
| // the page corresponding |
| vAdapter = getPropertySheetPage(); |
| |
| } else if (pKeyClass.equals(IGotoMarker.class)) { |
| // If the expecting result is a marker provider |
| // return the editor instance |
| vAdapter = this; |
| |
| } else { |
| // For all the others cases, call the parent method to find an adapter |
| vAdapter = super.getAdapter(pKeyClass); |
| } |
| |
| return vAdapter; |
| } |
| |
| /** |
| * This accesses a cached version of the property sheet. |
| * If the page doesn't exist, it can be created. |
| * |
| * @return The property sheet page provided by the editor |
| */ |
| protected IPropertySheetPage getPropertySheetPage() { |
| // Check if the page already exists |
| if (mPropertySheetPage == null) { |
| |
| // Create a new property sheet page, which |
| // will use the editor editing domain |
| mPropertySheetPage = new ExtendedPropertySheetPage(mEditingDomain) { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setSelectionToViewer(final List<?> pSelection) { |
| // Set the selection to the editor viewer |
| AbstractMultiPageEditor.this.setSelectionToViewer(pSelection); |
| AbstractMultiPageEditor.this.setFocus(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setActionBars(final IActionBars pActionBars) { |
| // Call the parent method to prepare the standard actions |
| super.setActionBars(pActionBars); |
| |
| // Add the global actions (undo, redo, etc.) |
| getActionBarContributor().shareGlobalActions(this, pActionBars); |
| } |
| }; |
| |
| // Set the property source provider to the property sheet page, |
| // using the editor adapter factory |
| mPropertySheetPage.setPropertySourceProvider(new PropertySourceProvider(mAdapterFactory)); |
| } |
| |
| return mPropertySheetPage; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * This simply tests the command stack to known if there is unsaved changes. |
| */ |
| @Override |
| public boolean isDirty() { |
| return ((BasicCommandStack) mEditingDomain.getCommandStack()).isSaveNeeded(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * This simply saves the resources of the managed editing domain, which are not opened in read-only. |
| */ |
| @Override |
| public void doSave(final IProgressMonitor pProgressMonitor) { |
| // Save only resources that have actually changed |
| final Map<Object, Object> vSaveOptions = new HashMap<Object, Object>(); |
| vSaveOptions.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 vWorkspaceModifyOperation = new WorkspaceModifyOperation() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void execute(final IProgressMonitor pMonitor) { |
| // Loop on the resources to save them to the file system |
| boolean vIsFirstResource = true; |
| for (Resource vResource : mEditingDomain.getResourceSet().getResources()) { |
| // Check if the resource is not in read only mode, and if the resource content |
| // is not empty and is persisted. Moreover, the first resource not in read only |
| // is always saved |
| if (!mEditingDomain.isReadOnly(vResource) |
| && (vIsFirstResource || !vResource.getContents().isEmpty() || isPersisted(vResource))) { |
| |
| try { |
| // Get the time when the resource was previously saved or loaded |
| long vTimeStamp = vResource.getTimeStamp(); |
| |
| // Try to save the resource |
| vResource.save(vSaveOptions); |
| |
| // Check if the resource time stamp changed, this means that the save |
| // was really effective, and thus remember the resource in the |
| // saved resources collection |
| if (vResource.getTimeStamp() != vTimeStamp) { |
| mSavedResourcesList.add(vResource); |
| } |
| |
| } catch (final IOException pException) { |
| // An error occurred during the save, analyse the resource and add the |
| // result to the diagnostics map |
| mProblemIndicationAdapter.analyzeResource(vResource, pException); |
| } |
| |
| // Remember that a resource has been saved, the followings won't be the first one |
| vIsFirstResource = false; |
| } |
| } |
| } |
| }; |
| |
| // Temporary disable the problems update while the save will be performed |
| mUpdateProblemIndication = false; |
| |
| // Run the save action, and show progress |
| try { |
| new ProgressMonitorDialog(getSite().getShell()).run(true, false, vWorkspaceModifyOperation); |
| |
| // Refresh the dirty state once the save is done |
| ((BasicCommandStack) mEditingDomain.getCommandStack()).saveIsDone(); |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| |
| } catch (final InvocationTargetException pException) { |
| CommonUIActivator.logError("Error during the save of the resource", pException); //$NON-NLS-1$ |
| } catch (final InterruptedException pException) { |
| CommonUIActivator.logError("Save of the resource interrupted", pException); //$NON-NLS-1$ |
| } |
| |
| // Re-enable the problems update and do it |
| mUpdateProblemIndication = true; |
| |
| // Finally, get the diagnostic from the problem indication adapter |
| // and update its content |
| updateProblemIndication(mProblemIndicationAdapter.getDiagnostic()); |
| } |
| |
| /** |
| * 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. |
| * |
| * This means that if a file already exists at the resource URI location, the resource is considered as persisted. |
| * |
| * @param pResource The resource to check |
| * @return <code>true</code> if the resource is persisted, this means if a file exists at the resource URI location |
| */ |
| private boolean isPersisted(final Resource pResource) { |
| boolean vIsPersisted = false; |
| |
| InputStream vResourceStream = null; |
| try { |
| // Try to open a stream to the resource URI location |
| vResourceStream = mEditingDomain.getResourceSet().getURIConverter().createInputStream(pResource.getURI()); |
| if (vResourceStream != null) { |
| // The stream can be opened, the resource is thus rightly persisted to a file |
| vIsPersisted = true; |
| } |
| |
| } catch (final IOException pException) { |
| // A stream can't be opened to the resource URI location, just |
| // set the flag value but don't log anything |
| vIsPersisted = false; |
| |
| } finally { |
| // Close the stream |
| if (vResourceStream != null) { |
| try { |
| vResourceStream.close(); |
| } catch (final IOException pException) { |
| CommonUIActivator.logError("Error during the close of a stream", //$NON-NLS-1$ |
| pException); |
| } |
| } |
| } |
| |
| return vIsPersisted; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * This always returns false because it is not currently supported. |
| */ |
| @Override |
| public boolean isSaveAsAllowed() { |
| return false; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * Not implemented for this editor. |
| */ |
| @Override |
| public void doSaveAs() { |
| // Not implemented |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void gotoMarker(final IMarker pMarker) { |
| // Get the objects pointed by the given marker |
| List<?> vTargetObjects = mMarkerHelper.getTargetObjects(mEditingDomain, pMarker); |
| |
| if (!vTargetObjects.isEmpty()) { |
| // Update the selection to focus on the objects corresponding to the marker |
| setSelectionToViewer(vTargetObjects); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addSelectionChangedListener(final ISelectionChangedListener pListener) { |
| mSelectionChangedListenersList.add(pListener); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void removeSelectionChangedListener(final ISelectionChangedListener pListener) { |
| mSelectionChangedListenersList.remove(pListener); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public ISelection getSelection() { |
| return mEditorSelection; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * Calling this result will notify the listeners, and update the status line content. |
| */ |
| @Override |
| public void setSelection(final ISelection pSelection) { |
| // Update the selection |
| mEditorSelection = pSelection; |
| |
| // Warn all the registered listeners |
| for (ISelectionChangedListener vListener : mSelectionChangedListenersList) { |
| vListener.selectionChanged(new SelectionChangedEvent(this, pSelection)); |
| } |
| |
| // Update the status line content |
| updateStatusLineManager(pSelection); |
| } |
| |
| /** |
| * Update the status line manager according to the selection given in parameter. |
| * |
| * @param pSelection The selection used to build the status line message |
| */ |
| protected void updateStatusLineManager(final ISelection pSelection) { |
| |
| // Find the status line manager from the action bar |
| IStatusLineManager vStatusLineManager = getActionBars().getStatusLineManager(); |
| |
| if (vStatusLineManager != null) { |
| String vMessage = StringUtils.EMPTY; |
| |
| if (pSelection instanceof IStructuredSelection) { |
| // Cast the selection |
| IStructuredSelection vStructuredSelection = (IStructuredSelection) pSelection; |
| int vSelectionSize = vStructuredSelection.size(); |
| |
| // Adapt the status message according to the selection size |
| switch (vStructuredSelection.size()) { |
| case 0: |
| vMessage = |
| CommonUIActivator.getMessages().getString( |
| "AbstractMultiPageEditor.statusline.noobjectselected"); //$NON-NLS-1$ |
| break; |
| |
| case 1: |
| // Get the text corresponding to the selected object |
| String vText = |
| new AdapterFactoryItemDelegator(mAdapterFactory).getText(vStructuredSelection |
| .getFirstElement()); |
| |
| vMessage = |
| CommonUIActivator.getMessages().getString( |
| "AbstractMultiPageEditor.statusline.singleobjectselected", //$NON-NLS-1$ |
| new Object[] {vText}); |
| break; |
| |
| default: |
| vMessage = |
| CommonUIActivator.getMessages().getString( |
| "AbstractMultiPageEditor.statusline.multiobjectsselected", //$NON-NLS-1$ |
| new Object[] {vSelectionSize}); |
| break; |
| } |
| } |
| |
| // Update the status line message |
| vStatusLineManager.setMessage(vMessage); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * This implementation will notify the action bar contributor that the menu is about to be shown. |
| */ |
| @Override |
| public void menuAboutToShow(final IMenuManager pMenuManager) { |
| ((IMenuListener) getEditorSite().getActionBarContributor()).menuAboutToShow(pMenuManager); |
| } |
| |
| /** |
| * @return The action bar contributor managed by this editor site. |
| */ |
| protected EditingDomainActionBarContributor getActionBarContributor() { |
| return (EditingDomainActionBarContributor) getEditorSite().getActionBarContributor(); |
| } |
| |
| /** |
| * @return The action bar managed by the action bar contributor of this editor. |
| */ |
| protected IActionBars getActionBars() { |
| return getActionBarContributor().getActionBars(); |
| } |
| |
| /** |
| * @return The adapter factory used in this editor. |
| */ |
| protected AdapterFactory getAdapterFactory() { |
| return mAdapterFactory; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public ToolBarManager getToolBarManager() { |
| return mEditorToolBarManager; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * Updates the problems indication with the information described in the specified diagnostic. |
| */ |
| @Override |
| public void updateProblemIndication(final Diagnostic pDiagnostic) { |
| // Ensure that the problems indication can be updated |
| if (mUpdateProblemIndication) { |
| |
| // Use and asynchronous UI thread to update the problems indication if needed |
| // This can create a second page for the problems |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void run() { |
| |
| // Get the last editor page index |
| int vLastEditorPageIndex = getPageCount() - 1; |
| |
| // Check if the last editor page is a already 'Problems' page |
| if (vLastEditorPageIndex >= 0 && getEditor(vLastEditorPageIndex) instanceof ProblemEditorPart) { |
| // The problem page is ready, update its content with the root diagnostic, |
| // and if it corresponds to an error, activate the problem page |
| ((ProblemEditorPart) getEditor(vLastEditorPageIndex)).setDiagnostic(pDiagnostic); |
| if (pDiagnostic.getSeverity() != Diagnostic.OK) { |
| setActivePage(vLastEditorPageIndex); |
| } |
| |
| } else if (pDiagnostic.getSeverity() != Diagnostic.OK) { |
| // The problem page is not already created, so do it |
| ProblemEditorPart vProblemEditorPart = new ProblemEditorPart(); |
| |
| // Update the problem page content with the root diagnostic |
| vProblemEditorPart.setDiagnostic(pDiagnostic); |
| vProblemEditorPart.setMarkerHelper(mMarkerHelper); |
| |
| try { |
| // Once that the problem page has been created, add it to the editor, |
| // activate it and display the tabs to allow the user to navigate between |
| // the different pages |
| addPage(++vLastEditorPageIndex, vProblemEditorPart, getEditorInput()); |
| setPageText(vLastEditorPageIndex, vProblemEditorPart.getPartName()); |
| setActivePage(vLastEditorPageIndex); |
| showTabs(); |
| |
| } catch (final PartInitException pException) { |
| CommonUIActivator.logError("Error during the problem page creation", //$NON-NLS-1$ |
| pException); |
| } |
| } |
| |
| // Finally, manage the markers |
| if (mMarkerHelper.hasMarkers(mEditingDomain.getResourceSet())) { |
| mMarkerHelper.deleteMarkers(mEditingDomain.getResourceSet()); |
| if (pDiagnostic.getSeverity() != Diagnostic.OK) { |
| try { |
| mMarkerHelper.createMarkers(pDiagnostic); |
| } catch (final CoreException pException) { |
| CommonUIActivator.logError("Error during the markers creation", //$NON-NLS-1$ |
| pException); |
| } |
| } |
| } |
| |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Return the filters that will be associated to the viewer during its creation. |
| * By default, this returns a table of filters containing a new {@link NamePropertyFilter} filter. |
| * |
| * @return The table of filters to associate to the viewer |
| */ |
| protected AbstractFilter[] getFilters() { |
| return new AbstractFilter[] {new NamePropertyFilter()}; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * For a multi page editor, the filter is applied on the |
| * current viewer. |
| */ |
| @Override |
| public void filterTextChanged(final String pNewFilterText) { |
| TreeViewer vViewer = getViewer(); |
| |
| if (vViewer != null) { |
| // Loop on the registered filters to apply the new text |
| for (final ViewerFilter vViewerFilter : vViewer.getFilters()) { |
| if (vViewerFilter instanceof AbstractFilter) { |
| ((AbstractFilter) vViewerFilter).setFilter(pNewFilterText); |
| } |
| } |
| |
| // Finally refresh the viewer content |
| refreshViewer(); |
| } |
| } |
| |
| /** |
| * Refresh the viewer. |
| */ |
| protected void refreshViewer() { |
| // Use an asynchronous UI thread to refresh the viewer |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void run() { |
| TreeViewer vViewer = getViewer(); |
| |
| // Ensure that the viewer is still available |
| if (vViewer != null && !vViewer.getControl().isDisposed()) { |
| synchronized (vViewer) { |
| vViewer.refresh(); |
| } |
| } |
| } |
| }); |
| } |
| |
| /** |
| * @return The final number of column, including the first one. This is used to calculate a correct size even for |
| * first column |
| */ |
| protected int getFinalColumnNumber() { |
| return 1; |
| } |
| |
| } |