blob: fd8220c2870eec2627671e9a62ffcdb72da8a2c1 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}