| /******************************************************************************* |
| * Copyright (c) 2000, 2017 IBM Corporation and others. |
| * |
| * 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.team.ui.synchronize; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.core.runtime.SafeRunner; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.IContributionItem; |
| import org.eclipse.jface.action.IContributionManager; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.util.SafeRunnable; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.team.internal.ui.synchronize.SynchronizePageConfiguration; |
| import org.eclipse.ui.IActionBars; |
| import org.eclipse.ui.IKeyBindingService; |
| import org.eclipse.ui.actions.ActionGroup; |
| |
| /** |
| * Used to add one or more actions to the context menu, toolbar or view menu |
| * of an {@link ISynchronizePage}. An action group is added to a synchronize |
| * page by adding the group to the {@link ISynchronizePageConfiguration} after |
| * configuration has been created by the page but before the page is created. |
| * <p> |
| * The life cycle of an action group is: |
| * </p> |
| * <ul> |
| * <li>the <code>initialize(ISynchronizePageConfiguration}</code> method is |
| * invoked before the methods to populate menus. This is done to give clients |
| * a change to create and initialize the actions of the action group. |
| * <li>The <code>fillActionBars(IActionBars)</code> method is invoked |
| * to populate the page's action bars (view menu and toolbar). It is |
| * possible for the action bars to be missing one or more components |
| * so clients are expected to check for <code>null</code> when accessing |
| * the menus from the action bars. |
| * <li>The <code>fillContextMenu(IMenuManager)</code> method is invoked each time |
| * the context menu is shown. Before this method is called, the |
| * action group will be provided with an <code>ActionContext</code> |
| * containing the view selection. Clients can access the context using |
| * <code>getContext()</code>. |
| * <li>The <code>updateActionBars()</code> method is invoked whenever the |
| * page's selection changes. Before this method is called, the |
| * action group will be provided with an <code>ActionContext</code> |
| * containing the view selection. Clients can access the context using |
| * <code>getContext()</code>. |
| * <li>The <code>modelChanged(ISynchronizeModelElement)</code> method is |
| * invoked whenever the model being displayed is changed. This gives clients |
| * a chance to adjust the input to actions that operate on all visible elements. |
| * <li>The <code>dispose()</code> method is called when the page is disposed. |
| * </ul> |
| * @since 3.0 |
| */ |
| public abstract class SynchronizePageActionGroup extends ActionGroup { |
| |
| private ISynchronizePageConfiguration configuration; |
| |
| private Map<String, Map<String, List<Object>>> menuContributions = new HashMap<>(); |
| |
| private VisibleRootsSelectionProvider visibleRootSelectionProvider; |
| |
| /* |
| * A selection provider whose selection is the root elements visible in the |
| * page. Selection changed events are sent out when the model roots change |
| * or their visible children change |
| */ |
| private class VisibleRootsSelectionProvider extends SynchronizePageActionGroup implements ISelectionProvider { |
| |
| private ListenerList<ISelectionChangedListener> selectionChangedListeners = new ListenerList<>(ListenerList.IDENTITY); |
| private ISelection selection; |
| |
| protected VisibleRootsSelectionProvider(ISynchronizeModelElement element) { |
| modelChanged(element); |
| } |
| |
| @Override |
| public void modelChanged(ISynchronizeModelElement root) { |
| if (root == null) { |
| setSelection(StructuredSelection.EMPTY); |
| } else { |
| setSelection(new StructuredSelection(root)); |
| } |
| } |
| |
| @Override |
| public void addSelectionChangedListener(ISelectionChangedListener listener) { |
| selectionChangedListeners.add(listener); |
| } |
| |
| @Override |
| public void removeSelectionChangedListener(ISelectionChangedListener listener) { |
| selectionChangedListeners.remove(listener); |
| } |
| |
| @Override |
| public ISelection getSelection() { |
| return selection; |
| } |
| |
| @Override |
| public void setSelection(ISelection selection) { |
| this.selection = selection; |
| selectionChanged(new SelectionChangedEvent(this, getSelection())); |
| } |
| |
| private void selectionChanged(final SelectionChangedEvent event) { |
| // pass on the notification to listeners |
| for (final ISelectionChangedListener l: selectionChangedListeners) { |
| SafeRunner.run(new SafeRunnable() { |
| @Override |
| public void run() { |
| l.selectionChanged(event); |
| } |
| }); |
| } |
| } |
| } |
| |
| /** |
| * Initialize the actions of this contribution. This method will be invoked |
| * once before any calls are made to <code>filleContextMenu</code> or |
| * <code>setActionBars</code> but after the control for the page has been |
| * created. As a result of this, the site of the configuration can be |
| * accessed. Subclasses may override this method but must invoke the |
| * overridden method. |
| * |
| * @param configuration the configuration for the part to which the |
| * contribution is associated |
| */ |
| public void initialize(ISynchronizePageConfiguration configuration) { |
| this.configuration = configuration; |
| if (visibleRootSelectionProvider != null) { |
| configuration.addActionContribution(visibleRootSelectionProvider); |
| } |
| } |
| |
| /** |
| * This method is invoked whenever the model being displayed in the view |
| * changes. This includes when the input to the view changes and when the |
| * children of the input change. The default implementation of this method |
| * does nothing. Subclasses may override. |
| * |
| * @param root the root of the model being viewed |
| */ |
| public void modelChanged(ISynchronizeModelElement root) { |
| // Do nothing by default |
| } |
| |
| /** |
| * Dispose of the action group. Subclasses may override but must |
| * invoke the overridden method. |
| */ |
| @Override |
| public void dispose() { |
| super.dispose(); |
| if (configuration != null) { |
| configuration.removeActionContribution(this); |
| } |
| } |
| |
| /** |
| * Helper method to find the group of the given id for the page associated |
| * with the configuration of this action group. The id of the returned group |
| * will not match that of the provided id since the group must be modified |
| * to ensure that groups are unique across pages. |
| * |
| * @param menu the menu |
| * @param groupId the id of the group being searched for |
| * @return the group for the given id or <code>null</code> |
| */ |
| protected IContributionItem findGroup(IContributionManager menu, String groupId) { |
| if(menu == null) return null; |
| IContributionItem item = menu.find(((SynchronizePageConfiguration)configuration).getGroupId(groupId)); |
| if (item == null) { |
| // Context menus do not change the id |
| item = menu.find(groupId); |
| } |
| return item; |
| } |
| |
| /** |
| * Helper method to add an action to a group in a menu. The action is only |
| * added to the menu if the group exists in the menu. Calling this method |
| * also has no effect if either the menu or action are <code>null</code>. |
| * |
| * @param manager the menu manager |
| * @param groupId the group to append the action to |
| * @param action the action to add |
| * @return <code>true</code> if the group exists and the action was added |
| * and <code>false</code> if the action was not added |
| */ |
| protected boolean appendToGroup(IContributionManager manager, String groupId, IAction action) { |
| if (internalAppendToGroup(manager, groupId, action)) { |
| registerActionWithWorkbench(action); |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean internalAppendToGroup(IContributionManager manager, String groupId, IAction action) { |
| if (manager == null || action == null) return false; |
| IContributionItem group = findGroup(manager, groupId); |
| if (group != null) { |
| manager.appendToGroup(group.getId(), action); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Helper method to add a contribution item to a group in a menu. The item |
| * is only added to the menu if the group exists in the menu. Calling this |
| * method also has no effect if either the menu or item are |
| * <code>null</code>. |
| * |
| * @param manager the menu manager |
| * @param groupId the group to append the action to |
| * @param item the item to add |
| * @return <code>true</code> if the group exists and the action was added |
| * and <code>false</code> if the action was not added |
| */ |
| protected boolean appendToGroup(IContributionManager manager, String groupId, IContributionItem item) { |
| if (manager == null || item == null) return false; |
| IContributionItem group = findGroup(manager, groupId); |
| if (group != null) { |
| manager.appendToGroup(group.getId(), item); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Helper method that can be invoked during initialization to add an action |
| * to a particular menu (one of P_TOOLBAR_MENU, P_VIEW_MENU, P_CONTEXT_MENU |
| * from ISynchronizePageConfiguration). The action is added to the given |
| * group if it is present. Otherwise the action is not added to the menu. |
| * |
| * @param menuId the menu id (one of P_TOOLBAR_MENU, P_VIEW_MENU, |
| * P_CONTEXT_MENU from ISynchronizePageConfiguration) |
| * @param groupId the group id in the menu to which the action is to be |
| * added |
| * @param action the action to be added |
| */ |
| protected void appendToGroup(String menuId, String groupId, IAction action) { |
| registerActionWithWorkbench(action); |
| internalAppendToGroup(menuId, groupId, action); |
| } |
| |
| /** |
| * Register this action with the workbench so that it can participate in keybindings and |
| * retargetable actions. |
| * |
| * @param action the action to register |
| */ |
| private void registerActionWithWorkbench(IAction action) { |
| ISynchronizePageSite site = configuration.getSite(); |
| String id = action.getId(); |
| if (id != null) { |
| site.getActionBars().setGlobalActionHandler(id, action); |
| IKeyBindingService keyBindingService = site.getKeyBindingService(); |
| if(keyBindingService != null) |
| keyBindingService.registerAction(action); |
| } |
| } |
| |
| /** |
| * Helper method that can be invoked during initialization to add an item to |
| * a particular menu (one of P_TOOLBAR_MENU, P_VIEW_MENU, P_CONTEXT_MENU |
| * from ISynchronizePageConfiguration). The item is added to the given group |
| * if it is present. Otherwise the item is not added to the menu. |
| * |
| * @param menuId the menu id (one of P_TOOLBAR_MENU, P_VIEW_MENU, |
| * P_CONTEXT_MENU from ISynchronizePageConfiguration) |
| * @param groupId the group id in the menu to which the item is to be added |
| * @param item the item to be added |
| */ |
| protected void appendToGroup(String menuId, String groupId, IContributionItem item) { |
| internalAppendToGroup(menuId, groupId, item); |
| } |
| |
| /** |
| * Return a selection provider whose selection includes all roots of the |
| * elements visible in the page. Selection change events are fired when the |
| * elements visible in the view change. |
| * |
| * @return a selection provider whose selection is the roots of all |
| * elements visible in the page |
| */ |
| protected ISelectionProvider getVisibleRootsSelectionProvider() { |
| if (visibleRootSelectionProvider == null) { |
| ISynchronizeModelElement root = null; |
| if (configuration != null) { |
| root = (ISynchronizeModelElement)configuration.getProperty(SynchronizePageConfiguration.P_MODEL); |
| } |
| visibleRootSelectionProvider = new VisibleRootsSelectionProvider(root); |
| if (configuration != null) { |
| configuration.addActionContribution(visibleRootSelectionProvider); |
| } |
| } |
| return visibleRootSelectionProvider; |
| } |
| |
| @Override |
| public void fillContextMenu(IMenuManager menu) { |
| super.fillContextMenu(menu); |
| fillMenu(menu, ISynchronizePageConfiguration.P_CONTEXT_MENU); |
| } |
| |
| @Override |
| public void fillActionBars(IActionBars actionBars) { |
| super.fillActionBars(actionBars); |
| if (actionBars != null) { |
| fillMenu(actionBars.getMenuManager(), ISynchronizePageConfiguration.P_VIEW_MENU); |
| fillMenu(actionBars.getToolBarManager(), ISynchronizePageConfiguration.P_TOOLBAR_MENU); |
| } |
| } |
| |
| private void fillMenu(IContributionManager menu, String menuId) { |
| Map<String, List<Object>> groups = menuContributions.get(menuId); |
| if (menu != null && groups != null) { |
| for (Iterator<String> iter = groups.keySet().iterator(); iter.hasNext(); ) { |
| String groupId = iter.next(); |
| List actions = groups.get(groupId); |
| if (actions != null) { |
| for (Iterator iter2 = actions.iterator(); iter2.hasNext();) { |
| Object element = iter2.next(); |
| if (element instanceof IAction) { |
| // Call the internal method to avoid registering the action |
| // as a global handler since it would have been registered |
| // when the action was added to the menuContributions |
| internalAppendToGroup(menu, groupId, (IAction)element); |
| } else if (element instanceof IContributionItem) { |
| appendToGroup(menu, groupId, (IContributionItem)element); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private void internalAppendToGroup(String menuId, String groupId, Object action) { |
| Map<String, List<Object>> groups = menuContributions.get(menuId); |
| if (groups == null) { |
| groups = new HashMap<>(); |
| menuContributions.put(menuId, groups); |
| } |
| List<Object> actions = groups.get(groupId); |
| if (actions == null) { |
| actions = new ArrayList<>(); |
| groups.put(groupId, actions); |
| } |
| actions.add(action); |
| } |
| |
| /** |
| * Return the configuration for the page to which the action group |
| * is associated. |
| * @return the configuration for the page to which the action group |
| * is associated |
| * |
| * @since 3.1 |
| */ |
| public ISynchronizePageConfiguration getConfiguration() { |
| return configuration; |
| } |
| } |