/******************************************************************************* * Copyright (c) 2000, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.team.internal.ui.synchronize; import org.eclipse.core.resources.IResource; import org.eclipse.jface.action.*; import org.eclipse.jface.util.*; import org.eclipse.jface.viewers.*; import org.eclipse.swt.events.*; import org.eclipse.swt.widgets.Menu; import org.eclipse.team.core.synchronize.SyncInfoSet; import org.eclipse.team.internal.ui.IPreferenceIds; import org.eclipse.team.internal.ui.TeamUIPlugin; import org.eclipse.team.internal.ui.mapping.DiffTreeStatusLineContributionGroup; import org.eclipse.team.ui.synchronize.*; import org.eclipse.ui.*; import org.eclipse.ui.actions.ActionContext; import org.eclipse.ui.actions.ActionGroup; /** * A StructuredViewerAdvisor controls various UI * aspects of viewers that show {@link SyncInfoSet} like the context menu, toolbar, * content provider, label provider, navigation, and model provider. The * advisor allows decoupling viewer behavior from the viewers presentation. This * allows viewers that aren't in the same class hierarchy to re-use basic * behavior. *

* This advisor allows viewer contributions made in a plug-in manifest to * be scoped to a particular unique id. As a result the context menu for the * viewer can be configured to show object contributions for random id schemes. * To enable declarative action contributions for a configuration there are two * steps required: *

*

* Clients may subclass to add behavior for concrete structured viewers. *

* * @see TreeViewerAdvisor * @since 3.0 */ public abstract class StructuredViewerAdvisor extends AbstractViewerAdvisor { // Property change listener which responds to: // - working set selection by the user // - decorator format change selected by the user private IPropertyChangeListener propertyListener = event -> { // Change to showing of sync state in text labels preference if(event.getProperty().equals(IPreferenceIds.SYNCVIEW_VIEW_SYNCINFO_IN_LABEL)) { StructuredViewer viewer = getViewer(); if(viewer != null && !viewer.getControl().isDisposed()) { viewer.refresh(true /* update labels */); } } }; private DiffTreeStatusLineContributionGroup statusLine; /** * Create an advisor that will allow viewer contributions with the given targetID. This * advisor will provide a presentation model based on the given sync info set. The model is disposed * when the viewer is disposed. * * @param configuration */ public StructuredViewerAdvisor(ISynchronizePageConfiguration configuration) { super(configuration); } /** * Install a viewer to be configured with this advisor. An advisor can only be installed with * one viewer at a time. When this method completes the viewer is considered initialized and * can be shown to the user. * @param viewer the viewer being installed */ @Override public void initializeViewer(final StructuredViewer viewer) { super.initializeViewer(viewer); initializeListeners(viewer); hookContextMenu(viewer); } /** * Must be called when an advisor is no longer needed. */ public void dispose() { if (getActionGroup() != null) { getActionGroup().dispose(); } if (statusLine != null) statusLine.dispose(); TeamUIPlugin.getPlugin().getPreferenceStore().removePropertyChangeListener(propertyListener); } /** * Method invoked from initializeViewer(Composite, StructuredViewer) * in order to initialize any listeners for the viewer. * * @param viewer the viewer being initialize */ protected void initializeListeners(final StructuredViewer viewer) { viewer.getControl().addDisposeListener(e -> StructuredViewerAdvisor.this.dispose()); new OpenAndLinkWithEditorHelper(viewer) { @Override protected void activate(ISelection selection) { final int currentMode= OpenStrategy.getOpenMethod(); try { OpenStrategy.setOpenMethod(OpenStrategy.DOUBLE_CLICK); handleOpen(); } finally { OpenStrategy.setOpenMethod(currentMode); } } @Override protected void linkToEditor(ISelection selection) { // not supported by this part } @Override protected void open(ISelection selection, boolean activate) { handleOpen(); } }; viewer.addDoubleClickListener(event -> handleDoubleClick(viewer, event)); viewer.addSelectionChangedListener(event -> updateActionBars((IStructuredSelection)viewer.getSelection())); TeamUIPlugin.getPlugin().getPreferenceStore().addPropertyChangeListener(propertyListener); } /** * Handles a double-click event. If false is returned, * subclasses may handle the double click. */ protected boolean handleDoubleClick(StructuredViewer viewer, DoubleClickEvent event) { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); Object node = selection.getFirstElement(); if (node != null && node instanceof SyncInfoModelElement) { SyncInfoModelElement syncNode = (SyncInfoModelElement) node; IResource resource = syncNode.getResource(); if (syncNode != null && resource != null && resource.getType() == IResource.FILE) { // The open is handled by the open strategy but say we handled // it so that overriding methods will not do anything return true; } } return false; } private void handleOpen() { Object o = getConfiguration().getProperty(SynchronizePageConfiguration.P_OPEN_ACTION); if (o instanceof IAction) { IAction action = (IAction)o; action.run(); } } /** * Method invoked from the synchronize page when the action * bars are set. The advisor uses the configuration to determine * which groups appear in the action bar menus and allows all * action groups registered with the configuration to fill the action bars. * @param actionBars the Action bars for the page */ @Override public final void setActionBars(IActionBars actionBars) { if(actionBars != null) { IToolBarManager manager = actionBars.getToolBarManager(); // Populate the toolbar menu with the configured groups Object o = getConfiguration().getProperty(ISynchronizePageConfiguration.P_TOOLBAR_MENU); if (!(o instanceof String[])) { o = ISynchronizePageConfiguration.DEFAULT_TOOLBAR_MENU; } String[] groups = (String[])o; for (int i = 0; i < groups.length; i++) { String group = groups[i]; // The groupIds must be converted to be unique since the toolbar is shared manager.add(new Separator(getGroupId(group))); } // view menu IMenuManager menu = actionBars.getMenuManager(); if (menu != null) { // Populate the view drop-down menu with the configured groups o = getConfiguration().getProperty(ISynchronizePageConfiguration.P_VIEW_MENU); if (!(o instanceof String[])) { o = ISynchronizePageConfiguration.DEFAULT_VIEW_MENU; } groups = (String[]) o; initializeStatusLine(actionBars); for (int i = 0; i < groups.length; i++) { String group = groups[i]; // The groupIds must be converted to be unique since the // view menu is shared menu.add(new Separator(getGroupId(group))); } } fillActionBars(actionBars); } } /** * Initialize the status line * @param actionBars the action bars */ protected void initializeStatusLine(IActionBars actionBars) { statusLine = new DiffTreeStatusLineContributionGroup( getConfiguration().getSite().getShell(), getConfiguration()); IStatusLineManager statusLineMgr = actionBars.getStatusLineManager(); if (statusLineMgr != null && statusLine != null) { statusLine.fillActionBars(actionBars); } } /* * Method invoked from initializeViewer(StructuredViewer) * in order to configure the viewer to call fillContextMenu(StructuredViewer, IMenuManager) * when a context menu is being displayed in viewer. * * @param viewer the viewer being initialized * @see fillContextMenu(StructuredViewer, IMenuManager) */ private void hookContextMenu(final StructuredViewer viewer) { String targetID = getContextMenuId(viewer); final MenuManager menuMgr = createContextMenuManager(targetID); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(manager -> fillContextMenu(viewer, manager)); Menu menu = menuMgr.createContextMenu(viewer.getControl()); menu.addMenuListener(new MenuListener() { @Override public void menuHidden(MenuEvent e) { } // Hack to allow action contributions to update their // state before the menu is shown. This is required when // the state of the selection changes and the contributions // need to update enablement based on this. // TODO: Is this hack still needed @Override public void menuShown(MenuEvent e) { IContributionItem[] items = menuMgr.getItems(); for (int i = 0; i < items.length; i++) { IContributionItem item = items[i]; if (item instanceof ActionContributionItem) { IAction actionItem = ((ActionContributionItem) item).getAction(); if (actionItem instanceof SynchronizeModelAction) { ((SynchronizeModelAction) actionItem).selectionChanged(viewer.getSelection()); } } } } }); viewer.getControl().setMenu(menu); registerContextMenu(viewer, menuMgr); } /** * Create the menu manager to be used to manage the context * menu of the viewer. * @param targetID the context menu id * @return the menu manager to be used to manage the context * menu of the viewer */ protected MenuManager createContextMenuManager(String targetID) { return new MenuManager(targetID); } /** * Register the context menu with the platform if appropriate. * @param viewer the viewer * @param menuMgr the context menu manager */ protected void registerContextMenu(final StructuredViewer viewer, MenuManager menuMgr) { String targetID = getContextMenuId(viewer); if (targetID != null) { IWorkbenchSite workbenchSite = getConfiguration().getSite().getWorkbenchSite(); IWorkbenchPartSite ws = null; if (workbenchSite instanceof IWorkbenchPartSite) ws = (IWorkbenchPartSite)workbenchSite; if (ws != null) { ws.registerContextMenu(targetID, menuMgr, viewer); } } } /** * Return the context menu id. * @param viewer the viewer * @return the context menu id */ protected String getContextMenuId(StructuredViewer viewer) { String targetID; Object o = getConfiguration().getProperty(ISynchronizePageConfiguration.P_OBJECT_CONTRIBUTION_ID); if (o instanceof String) { targetID = (String)o; } else { targetID = null; } return targetID; } /** * Callback that is invoked when a context menu is about to be shown in the * viewer. Subclasses must implement to contribute menus. Also, menus can * contributed by creating a viewer contribution with a targetID * that groups sets of actions that are related. * * @param viewer the viewer in which the context menu is being shown. * @param manager the menu manager to which actions can be added. */ protected void fillContextMenu(StructuredViewer viewer, final IMenuManager manager) { addContextMenuGroups(manager); getActionGroup().setContext(new ActionContext(viewer.getSelection())); getActionGroup().fillContextMenu(manager); } /** * Add the context menu groups to the context menu. * @param manager the menu manager */ protected void addContextMenuGroups(final IMenuManager manager) { // Populate the menu with the configured groups Object o = getConfiguration().getProperty(ISynchronizePageConfiguration.P_CONTEXT_MENU); if (!(o instanceof String[])) { o = ISynchronizePageConfiguration.DEFAULT_CONTEXT_MENU; } String[] groups = (String[])o; for (int i = 0; i < groups.length; i++) { String group = groups[i]; // There is no need to adjust the group ids in a context menu (see setActionBars) manager.add(new Separator(group)); } } /** * Invoked once when the action bars are set. * @param actionBars the action bars */ protected void fillActionBars(IActionBars actionBars) { getActionGroup().fillActionBars(actionBars); updateActionBars((IStructuredSelection) getViewer().getSelection()); Object input = getViewer().getInput(); if (input instanceof ISynchronizeModelElement) { getActionGroup().modelChanged((ISynchronizeModelElement) input); } } /** * Invoked each time the selection in the view changes in order * to update the action bars. * @param selection the selection from the viewer */ protected void updateActionBars(IStructuredSelection selection) { ActionGroup group = getActionGroup(); if (group != null) { group.setContext(new ActionContext(selection)); group.updateActionBars(); } } protected SynchronizePageActionGroup getActionGroup() { return (SynchronizePageActionGroup)getConfiguration(); } private String getGroupId(String group) { return ((SynchronizePageConfiguration)getConfiguration()).getGroupId(group); } }