blob: 988c7aee66bd3ea6d38c6e61f4efdc2386118e00 [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.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.emf.common.ui.viewer.IViewerProvider;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.domain.IEditingDomainProvider;
import org.eclipse.emf.edit.ui.action.DeleteAction;
import org.eclipse.emf.edit.ui.action.EditingDomainActionBarContributor;
import org.eclipse.emf.edit.ui.action.ValidateAction;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.AbstractTreeViewer;
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.Viewer;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.handlers.CollapseAllHandler;
import org.eclipse.ui.handlers.ExpandAllHandler;
import org.polarsys.esf.core.common.ui.CommonUIActivator;
import org.polarsys.esf.core.common.ui.CommonUIActivator.Implementation;
import org.polarsys.esf.core.common.ui.actions.ActionBarContributorUtils;
import org.polarsys.esf.core.common.ui.provider.IToolBarManagerProvider;
/**
* This is the abstract action bar contributor used for all the multi pages editors.
*
* It extends {@link EditingDomainActionBarContributor} which provide the standard mechanisms
* to provide the actions on an editing domain.
*
* It implements {@link ISelectionChangedListener} to be able to react when the selection changed in the
* active editor, to update the action linked to the current selection.
*
* This contributor will populate the editor tool bar with different kind of actions :
* <ul>
* <li>Actions not linked to the current context (ie: expand / collapse tree)</li>
* <li>Actions linked to the current selection when one is set (ie: create children elements)</li>
* </ul>
*
* @author $Author: jdumont $
* @version $Revision: 83 $
*/
public abstract class AbstractActionBarContributor
extends EditingDomainActionBarContributor
implements ISelectionChangedListener {
/** Instance of utility class used by the action contributors. */
private ActionBarContributorUtils mActionBarContributorUtils = ActionBarContributorUtils.INSTANCE;
/** This keeps track of the active editor on which the actions are applied. */
private IEditorPart mActiveEditorPart = null;
/** This keeps track of the current selection provider. */
private ISelectionProvider mSelectionProvider = null;
/** This action opens the Properties view. */
private IAction mShowPropertiesViewAction = new Action(ActionBarContributorUtils.SHOW_PROPERTIES_ACTION_LABEL) {
/**
* {@inheritDoc}
*/
@Override
public void run() {
try {
getPage().showView(IPageLayout.ID_PROP_SHEET);
} catch (final PartInitException pException) {
CommonUIActivator.logError(
"Error during the opening of the Properties view", //$NON-NLS-1$
pException);
}
}
};
/** This action expands the active editor content, if possible. */
private IAction mExpandAllAction = createExpandAllAction();
/** This action collapse the active editor content, if possible. */
private IAction mCollapseAllAction = createCollapseAllAction();
/**
* This action refreshes the viewer of the current editor if the editor
* implements {@link org.eclipse.emf.common.ui.viewer.IViewerProvider}.
*/
private IAction mRefreshViewerAction = new Action(ActionBarContributorUtils.REFRESH_ACTION_LABEL) {
/**
* {@inheritDoc}
*
* The action is enabled only if the editor implements {@link IViewerProvider}
* to provide its owned viewer.
*/
@Override
public boolean isEnabled() {
return mActiveEditorPart instanceof IViewerProvider;
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
if (mActiveEditorPart instanceof IViewerProvider) {
final Viewer vViewer = ((IViewerProvider) mActiveEditorPart).getViewer();
if (vViewer != null) {
vViewer.refresh();
}
}
}
};
/**
* This will contain one {@link org.eclipse.emf.edit.ui.action.CreateChildAction} corresponding to each descriptor
* generated for the current selection by the item provider.
*/
private Collection<IAction> mCreateSelectChildActionsColl = Collections.emptyList();
/**
* This will contain a map of {@link org.eclipse.emf.edit.ui.action.CreateChildAction}s, keyed by sub-menu text
* for all the create child action relative to the current selection.
*/
private Map<String, Collection<IAction>> mCreateSelectChildMenuActionsMap = null;
/** This is the menu manager into which menu contribution items should be added for CreateChild actions. */
private IMenuManager mCreateSelectChildMenuManager = null;
/**
* Default constructor.
* This creates an instance of the contributor with the default style
* {@link EditingDomainActionBarContributor#ADDITIONS_LAST_STYLE}.
*/
public AbstractActionBarContributor() {
// Call the parent constructor and ask to add an 'additions' anchor at the end of the menu
super(ADDITIONS_LAST_STYLE);
// Create the parent action specific to the editing domain context
// NB : The 'load resource' and 'control' actions are not needed
validateAction = new ValidateAction();
}
/**
* {@inheritDoc}
*
* This implementation also call the method to contribute to any toolbar
* potentially provided by the active editor.
*/
@Override
public void init(final IActionBars pActionBars) {
// Call the parent method
super.init(pActionBars);
}
/**
* Contribute to the editor toolbar, if the active editor provide a tool bar manager.
* This method may be called only once as it will create all the actions, even the global
* actions which doesn't need to change or be updated after their creation.
*/
public void contributeToEditorToolBar() {
// Check if the editor provide a custom toolbar manager
if (mActiveEditorPart instanceof IToolBarManagerProvider) {
// Get the tool bar manager from the active editor
ToolBarManager vEditorToolBarManager = ((IToolBarManagerProvider) mActiveEditorPart).getToolBarManager();
// Use a flag to known if the tool bar content have been modified
boolean vNeedToUpdate = false;
// Add the expand all action if it's not already in it
if (vEditorToolBarManager.find(ActionBarContributorUtils.EXPAND_ALL_ACTION_ID) == null) {
vEditorToolBarManager.add(mExpandAllAction);
vNeedToUpdate = true;
}
// Add the collapse all action if it's not already in it
if (vEditorToolBarManager.find(ActionBarContributorUtils.COLLAPSE_ALL_ACTION_ID) == null) {
vEditorToolBarManager.add(mCollapseAllAction);
vNeedToUpdate = true;
}
// Add the separator for the actions on the selected element, if it's not already in it
if (vEditorToolBarManager.find(ActionBarContributorUtils.TOOLBAR_SEP_SELECTION_ACTIONS) == null) {
vEditorToolBarManager.add(new Separator(ActionBarContributorUtils.TOOLBAR_SEP_SELECTION_ACTIONS));
vNeedToUpdate = true;
}
// Add the delete action to the toolbar if it's not already in it
// This action will react to the selection change automatically
if (vEditorToolBarManager.find(ActionFactory.DELETE.getId()) == null) {
vEditorToolBarManager.add(new Separator(ActionBarContributorUtils.TOOLBAR_SEP_DELETE_ACTION));
vEditorToolBarManager.appendToGroup(ActionBarContributorUtils.TOOLBAR_SEP_DELETE_ACTION, deleteAction);
vNeedToUpdate = true;
}
// Finally update the toolbar if needed
if (vNeedToUpdate) {
vEditorToolBarManager.update(true);
vEditorToolBarManager.getControl().pack(true);
}
}
}
/**
* {@inheritDoc}
*
* By default, no menu is added.
*/
@Override
public void contributeToMenu(final IMenuManager pMenuManager) {
// Nothing to do
}
/**
* {@inheritDoc}
*
* When the active editor changes, this remembers the change and registers
* this instance with it as a selection provider.
*/
@Override
public void setActiveEditor(final IEditorPart pPart) {
// Call the parent method
super.setActiveEditor(pPart);
// Remember the active editor
mActiveEditorPart = pPart;
// If any selection provider was already registered, remove it
if (mSelectionProvider != null) {
mSelectionProvider.removeSelectionChangedListener(this);
}
// Then switch to the new selection provider if possible
if (mActiveEditorPart != null) {
// Get the selection provider from the new active editor
mSelectionProvider = mActiveEditorPart.getSite().getSelectionProvider();
// Register this instance to listen to the change on this selection provider
mSelectionProvider.addSelectionChangedListener(this);
// Fake a selection changed event to update the menus
if (mSelectionProvider.getSelection() != null) {
selectionChanged(new SelectionChangedEvent(mSelectionProvider, mSelectionProvider.getSelection()));
}
} else {
// Reset the selection provider
mSelectionProvider = null;
}
// Try to contribute to the tool bar that the active editor may provide
contributeToEditorToolBar();
}
/**
* {@inheritDoc}
*
* This implements {@link ISelectionChangedListener}, handling {@link SelectionChangedEvent}s
* by querying for the children that can be created under the selected object and updating the menus accordingly.
*/
@Override
public void selectionChanged(final SelectionChangedEvent pEvent) {
// Recreate the actions linked to the current selection in the contextual menus and in the editor toolbar
addCreateSelectChildActions(pEvent.getSelection());
}
/**
* Create the new child actions for the current selection, in the context menu
* and the editor toolbar.
*
* @param pSelection The current selection for which the actions must be created
*/
protected void addCreateSelectChildActions(final ISelection pSelection) {
// Force to update the action from the current selection,
// and recreate the actions in the menu
addCreateSelectChildActionsToMenu(pSelection, true);
// The recreate the actions in the tool bar, but without updating the
// actions as the selection has not changed
addCreateSelectChildActionsToEditorToolbar(pSelection, false);
}
/**
* Create the new child actions for the current selection in the editor context menu.
* The actions may be updated according to the given flag value. If no, the list of action
* is not updated according to the current selection, but the menu content is refresh even so.
*
* @param pSelection The current selection for which the actions must be created
* @param pUpdateActions If <code>true</code> the list of actions is updated according to the given selection
*/
protected void addCreateSelectChildActionsToMenu(final ISelection pSelection, final boolean pUpdateActions) {
// Remove any menu items created for the old selection
if (mCreateSelectChildMenuManager != null) {
mActionBarContributorUtils.depopulateManager(
mCreateSelectChildMenuManager,
mCreateSelectChildMenuActionsMap);
mActionBarContributorUtils.depopulateManager(
mCreateSelectChildMenuManager,
mCreateSelectChildActionsColl);
}
// Update the list of actions used to create child for the
// given selection only if needed
if (pUpdateActions) {
updateCreateSelectChildActionsCollection(pSelection);
}
// Finally populate and redraw the menus
if (mCreateSelectChildMenuManager != null) {
mActionBarContributorUtils.populateManager(
mCreateSelectChildMenuManager,
mCreateSelectChildMenuActionsMap,
null);
mActionBarContributorUtils.populateManager(
mCreateSelectChildMenuManager,
mCreateSelectChildActionsColl,
null);
mCreateSelectChildMenuManager.update(true);
}
}
/**
* Create the new child actions for the current selection in the editor toolbar.
* The actions may be updated according to the given flag value. If no, the list of action
* is not updated according to the current selection, but the toolbar content is refresh even so.
*
* @param pSelection The current selection for which the actions must be created
* @param pUpdateActions If <code>true</code> the list of actions is updated according to the given selection
*/
protected void addCreateSelectChildActionsToEditorToolbar(
final ISelection pSelection,
final boolean pUpdateActions) {
// Check if the editor provide a custom toolbar manager
if (mActiveEditorPart instanceof IToolBarManagerProvider) {
// Use a flag to known if the tool bar content have been modified
boolean vNeedToUpdate = false;
// Get the tool bar manager from the active editor
ToolBarManager vEditorToolBarManager = ((IToolBarManagerProvider) mActiveEditorPart).getToolBarManager();
// Depopulate the tool bar with the potential existing create child actions
for (IContributionItem vContributionItem : vEditorToolBarManager.getItems()) {
// The actions to remove are identified with their id prefix, thus check it
if (StringUtils.startsWith(
vContributionItem.getId(),
ActionBarContributorUtils.CREATECHILD_ACTION_ID_PREFIX)) {
// Remove the action
vEditorToolBarManager.remove(vContributionItem);
vNeedToUpdate = true;
}
}
// Update the list of actions used to create child for the
// given selection only if needed
if (pUpdateActions) {
updateCreateSelectChildActionsCollection(pSelection);
}
// Then populate the tool bar with the new actions
for (IAction vAction : mCreateSelectChildActionsColl) {
// Check if the action is not already in the tool bar
if (vEditorToolBarManager.find(vAction.getId()) == null) {
// Insert the action
vEditorToolBarManager.appendToGroup(
ActionBarContributorUtils.TOOLBAR_SEP_SELECTION_ACTIONS,
vAction);
// Remember that the toolbar must be updated
vNeedToUpdate = true;
}
}
// Finally, update the toolbar if needed
if (vNeedToUpdate) {
vEditorToolBarManager.update(true);
}
}
}
/**
* Generate the list of create child actions corresponding to the selection given in parameter,
* and update this instance property {@link #mCreateSelectChildActionsColl} with it.
*
* The map of menus linked to the created action {@link #mCreateSelectChildMenuActionsMap}, is
* also updated.
*
* @param pSelection The current selection in the active editor
*/
protected void updateCreateSelectChildActionsCollection(final ISelection pSelection) {
Collection<?> vNewChildDescrCollection = null;
// Ensure that the selection contains only one element
if (pSelection instanceof IStructuredSelection && ((IStructuredSelection) pSelection).size() == 1) {
// Get the selected object
Object vSelectedObject = ((IStructuredSelection) pSelection).getFirstElement();
// Get the editing domain from the active editor, and then
// query the new selection to find the appropriate new child descriptors
EditingDomain vEditingDomain = null;
if (mActiveEditorPart instanceof IEditingDomainProvider) {
vEditingDomain = ((IEditingDomainProvider) mActiveEditorPart).getEditingDomain();
// Find the new child actions corresponding to the selected objects
// and the editing domain used in the active editor
vNewChildDescrCollection = vEditingDomain.getNewChildDescriptors(vSelectedObject, null);
}
}
// Generate the actions corresponding to the current selection
mCreateSelectChildActionsColl = mActionBarContributorUtils.generateCreateChildActionsCollection(
mActiveEditorPart,
vNewChildDescrCollection,
pSelection);
// Then update the map menus with the created actions
mCreateSelectChildMenuActionsMap =
mActionBarContributorUtils.extractSubmenuActions(mCreateSelectChildActionsColl);
}
/**
* {@inheritDoc}
*
* This populates the pop-up menu before it appears with the child creation actions.
*/
@Override
public void menuAboutToShow(final IMenuManager pMenuManager) {
// Call the parent method
super.menuAboutToShow(pMenuManager);
// Create the create child submenu
MenuManager vCreateChildSubmenuManager = new MenuManager(ActionBarContributorUtils.CREATECHILD_MENU_LABEL);
// Populate the submenu and insert it in the parent menu manager
mActionBarContributorUtils.populateManager(
vCreateChildSubmenuManager,
mCreateSelectChildMenuActionsMap,
null);
mActionBarContributorUtils.populateManager(
vCreateChildSubmenuManager,
mCreateSelectChildActionsColl,
null);
pMenuManager.insertBefore(ActionBarContributorUtils.EDIT_SEPARATOR_ID, vCreateChildSubmenuManager);
}
/**
* {@inheritDoc}
*/
@Override
protected void addGlobalActions(final IMenuManager pMenuManager) {
// Insert the show properties action after a custom separator for the ui actions
pMenuManager.insertAfter(
ActionBarContributorUtils.ADDITIONS_END_SEPARATOR_ID,
new Separator(ActionBarContributorUtils.UI_ACTIONS_SEPARATOR_ID));
pMenuManager.insertAfter(
ActionBarContributorUtils.UI_ACTIONS_SEPARATOR_ID,
mShowPropertiesViewAction);
// Insert the refresh action, and update its state, after the the ui actions separator
mRefreshViewerAction.setEnabled(mRefreshViewerAction.isEnabled());
pMenuManager.insertAfter(ActionBarContributorUtils.UI_ACTIONS_SEPARATOR_ID, mRefreshViewerAction);
// Call the parent method to add the others global actions (Validate, etc.)
super.addGlobalActions(pMenuManager);
}
/**
* {@inheritDoc}
*
* This implementation ensures that a delete action will clean up all references to deleted objects.
*/
@Override
protected boolean removeAllReferencesOnDelete() {
return true;
}
/**
* {@inheritDoc}
*
* Overridden to ensure that the delete action id is set.
*/
@Override
protected DeleteAction createDeleteAction() {
// Create the custom action
DeleteAction vDeleteAction = new DeleteAction(removeAllReferencesOnDelete());
// Update the action to set its id
vDeleteAction.setId(ActionFactory.DELETE.getId());
return vDeleteAction;
}
/**
* Create the expand all action to apply on the active editor,
* if this one is a viewer provider.
*
* @return The expand all action created
*/
protected IAction createExpandAllAction() {
// Get the action image
final ImageDescriptor vImageDescriptor =
CommonUIActivator.getPlugin().getImageRegistry().getDescriptor(
Implementation.ICON_EXPAND_ALL_KEY);
// Create the action
final IAction vExpandAllAction = new Action(
ActionBarContributorUtils.EXPAND_ALL_ACTION_LABEL,
vImageDescriptor) {
/**
* {@inheritDoc}
*/
@Override
public void run() {
// Ensure that the active editor is a viewer provider
if (mActiveEditorPart instanceof IViewerProvider) {
// Get the viewer from the active editor
final Viewer vActiveViewer = ((IViewerProvider) mActiveEditorPart).getViewer();
// Check if the viewer is eligible for an expand action
if (vActiveViewer instanceof AbstractTreeViewer) {
// Prepare the expand action asynchronously, on the UI thread
mActiveEditorPart.getSite().getShell().getDisplay().asyncExec(new Runnable() {
/**
* {@inheritDoc}
*/
@Override
public void run() {
// Create the action handler on the viewer
final ExpandAllHandler vExpandAllHandler =
new ExpandAllHandler((AbstractTreeViewer) vActiveViewer);
// And finally execute it
vExpandAllHandler.execute(null);
}
});
}
}
}
};
// Set the unique id for this action
vExpandAllAction.setId(ActionBarContributorUtils.EXPAND_ALL_ACTION_ID);
return vExpandAllAction;
}
/**
* Create the collapse all action to apply on the active editor,
* if this one is a viewer provider.
*
* @return The collapse all action created
*/
protected IAction createCollapseAllAction() {
// Get the action image
final ImageDescriptor vImageDescriptor =
CommonUIActivator.getPlugin().getImageRegistry().getDescriptor(
Implementation.ICON_COLLAPSE_ALL_KEY);
// Create the action
final IAction vCollapseAllAction = new Action(
ActionBarContributorUtils.COLLAPSE_ALL_ACTION_LABEL,
vImageDescriptor) {
/**
* {@inheritDoc}
*/
@Override
public void run() {
// Ensure that the active editor is a viewer provider
if (mActiveEditorPart instanceof IViewerProvider) {
// Get the viewer from the active editor
final Viewer vActiveViewer = ((IViewerProvider) mActiveEditorPart).getViewer();
// Check if the viewer is eligible for an expand action
if (vActiveViewer instanceof AbstractTreeViewer) {
// Prepare the expand action asynchronously, on the UI thread
mActiveEditorPart.getSite().getShell().getDisplay().asyncExec(new Runnable() {
/**
* {@inheritDoc}
*/
@Override
public void run() {
// Create the action handler on the viewer
final CollapseAllHandler vCollapseAllHandler =
new CollapseAllHandler((AbstractTreeViewer) vActiveViewer);
// And finally execute it
vCollapseAllHandler.execute(null);
}
});
}
}
}
};
// Set the unique id for this action
vCollapseAllAction.setId(ActionBarContributorUtils.COLLAPSE_ALL_ACTION_ID);
return vCollapseAllAction;
}
}