blob: 661484491d5b9a2af4ab83ca5a93d6775c30ab28 [file] [log] [blame]
/*******************************************************************************
* 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.internal.ui.mapping;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.mapping.IModelProviderDescriptor;
import org.eclipse.core.resources.mapping.ModelProvider;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.DecoratingLabelProvider;
import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DecorationContext;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.IFontProvider;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.mapping.ISynchronizationContext;
import org.eclipse.team.core.mapping.ISynchronizationScope;
import org.eclipse.team.internal.ui.TeamUIPlugin;
import org.eclipse.team.internal.ui.Utils;
import org.eclipse.team.internal.ui.synchronize.AbstractTreeViewerAdvisor;
import org.eclipse.team.internal.ui.synchronize.SynchronizePageConfiguration;
import org.eclipse.team.ui.TeamUI;
import org.eclipse.team.ui.mapping.ITeamContentProviderDescriptor;
import org.eclipse.team.ui.mapping.ITeamContentProviderManager;
import org.eclipse.team.ui.mapping.SynchronizationStateTester;
import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
import org.eclipse.team.ui.synchronize.ModelSynchronizeParticipant;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.SubActionBars;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.navigator.CommonViewer;
import org.eclipse.ui.navigator.CommonViewerSiteFactory;
import org.eclipse.ui.navigator.CommonViewerSorter;
import org.eclipse.ui.navigator.ICommonViewerSite;
import org.eclipse.ui.navigator.INavigatorContentExtension;
import org.eclipse.ui.navigator.INavigatorContentServiceListener;
import org.eclipse.ui.navigator.NavigatorActionService;
import org.eclipse.ui.part.IPageSite;
/**
* Provides a Common Navigator based viewer for use by a {@link ModelSynchronizePage}.
*/
public class CommonViewerAdvisor extends AbstractTreeViewerAdvisor implements INavigatorContentServiceListener, IEmptyTreeListener, IPropertyChangeListener {
public static final class NavigableCommonViewer extends CommonViewer implements ITreeViewerAccessor {
private final IEmptyTreeListener listener;
private boolean empty;
private NavigableCommonViewer(String id, Composite parent, int style, IEmptyTreeListener listener) {
super(id, parent, style);
this.listener = listener;
}
@Override
public void createChildren(TreeItem item) {
super.createChildren(item);
}
@Override
public void openSelection() {
fireOpen(new OpenEvent(this, getSelection()));
}
@Override
protected void internalRefresh(Object element, boolean updateLabels) {
TreePath[] expanded = getVisibleExpandedPaths();
super.internalRefresh(element, updateLabels);
setExpandedTreePaths(expanded);
checkForEmptyViewer();
}
@Override
protected void internalRemove(Object parent, Object[] elements) {
super.internalRemove(parent, elements);
if (parent == getInput())
checkForEmptyViewer();
}
@Override
protected void internalRemove(Object[] elements) {
super.internalRemove(elements);
checkForEmptyViewer();
}
@Override
protected void internalAdd(Widget widget, Object parentElement, Object[] childElements) {
super.internalAdd(widget, parentElement, childElements);
if (empty) {
empty = false;
listener.notEmpty(this);
}
}
@Override
protected void inputChanged(Object input, Object oldInput) {
super.inputChanged(input, oldInput);
checkForEmptyViewer();
}
private void checkForEmptyViewer() {
Object input = getInput();
if (input != null) {
Widget w = findItem(input);
Item[] children = getChildren(w);
if (children.length == 0) {
if (!empty) {
empty = true;
listener.treeEmpty(this);
}
return;
}
}
empty = false;
if (listener != null)
listener.notEmpty(this);
}
public boolean isEmpty() {
return empty;
}
@Override
protected void initDragAndDrop() {
getNavigatorContentService().getDnDService().bindDragAssistant(new ResourceDragAdapterAssistant());
super.initDragAndDrop();
}
/**
* Gets the expanded elements that are visible to the user. An expanded
* element is only visible if the parent is expanded.
*
* @return the visible expanded elements
* @since 2.0
*/
public TreePath[] getVisibleExpandedPaths() {
ArrayList<TreePath> v = new ArrayList<>();
internalCollectVisibleExpanded(v, getControl());
return v.toArray(new TreePath[v.size()]);
}
private void internalCollectVisibleExpanded(ArrayList<TreePath> result, Widget widget) {
Item[] items = getChildren(widget);
for (int i = 0; i < items.length; i++) {
Item item = items[i];
if (getExpanded(item)) {
TreePath path = getTreePathFromItem(item);
if (path != null) {
result.add(path);
}
//Only recurse if it is expanded - if
//not then the children aren't visible
internalCollectVisibleExpanded(result, item);
}
}
}
}
/**
* Subclass of SubActionBars that manages the contributions from the common action service
*/
private class CommonSubActionBars extends SubActionBars {
public CommonSubActionBars(IActionBars parent) {
super(parent);
}
@Override
public void setGlobalActionHandler(String actionID, IAction handler) {
if (handler == null) {
// Only remove the handler if it was set
if (getGlobalActionHandler(actionID) != null) {
getParent().setGlobalActionHandler(actionID, null);
super.setGlobalActionHandler(actionID, null);
}
} else {
// Only set the action handler if the parent doesn't
if (getParent().getGlobalActionHandler(actionID) != null) {
TeamUIPlugin.log(new TeamException(NLS.bind("Conflicting attempt to set action id {0} detected", actionID))); //$NON-NLS-1$
return;
}
super.setGlobalActionHandler(actionID, handler);
}
}
@Override
public void clearGlobalActionHandlers() {
// When cleared, also remove the ids from the parent
Map handlers = getGlobalActionHandlers();
if (handlers != null) {
Set keys = handlers.keySet();
Iterator iter = keys.iterator();
while (iter.hasNext()) {
String actionId = (String) iter.next();
getParent().setGlobalActionHandler(actionId,
null);
}
}
super.clearGlobalActionHandlers();
}
@Override
public void updateActionBars() {
// On update, push all or action handlers into our parent
Map newActionHandlers = getGlobalActionHandlers();
if (newActionHandlers != null) {
Set keys = newActionHandlers.entrySet();
Iterator iter = keys.iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
getParent().setGlobalActionHandler((String) entry.getKey(),
(IAction) entry.getValue());
}
}
super.updateActionBars();
}
}
public static final String TEAM_NAVIGATOR_CONTENT = "org.eclipse.team.ui.navigatorViewer"; //$NON-NLS-1$
private static final String PROP_ACTION_SERVICE_ACTION_BARS = "org.eclipse.team.ui.actionServiceActionBars"; //$NON-NLS-1$
private Set<INavigatorContentExtension> extensions = new HashSet<>();
private NavigatorActionService actionService;
private IEmptyTreeListener emptyTreeListener;
/**
* Create a common viewer
* @param parent the parent composite of the common viewer
* @param configuration the configuration for the viewer
* @return a newly created common viewer
*/
private static CommonViewer createViewer(Composite parent, final ISynchronizePageConfiguration configuration, IEmptyTreeListener listener) {
final CommonViewer v = new NavigableCommonViewer(configuration.getViewerId(), parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, listener);
v.setSorter(new CommonViewerSorter());
v.setSorter(new TeamViewerSorter((CommonViewerSorter)v.getSorter()));
ISynchronizationScope scope = getScope(configuration);
bindTeamContentProviders(v);
scope.addScopeChangeListener((scope1, newMappings, newTraversals) -> {
enableContentProviders(v, configuration);
Utils.asyncExec((Runnable) () -> v.refresh(), v);
});
enableContentProviders(v, configuration);
configuration.getSite().setSelectionProvider(v);
return v;
}
private static void enableContentProviders(CommonViewer v, ISynchronizePageConfiguration configuration) {
v.getNavigatorContentService().getActivationService().activateExtensions(getEnabledContentProviders(configuration), true);
}
private static String[] getEnabledContentProviders(ISynchronizePageConfiguration configuration) {
String visibleModel = (String)configuration.getProperty(ModelSynchronizeParticipant.P_VISIBLE_MODEL_PROVIDER);
if (visibleModel != null && !visibleModel.equals(ModelSynchronizeParticipant.ALL_MODEL_PROVIDERS_VISIBLE)) {
ITeamContentProviderDescriptor desc = TeamUI.getTeamContentProviderManager().getDescriptor(visibleModel);
if (desc != null && desc.isEnabled())
return new String[] { desc.getContentExtensionId() };
}
configuration.setProperty(ModelSynchronizeParticipant.P_VISIBLE_MODEL_PROVIDER, ModelSynchronizeParticipant.ALL_MODEL_PROVIDERS_VISIBLE);
ModelSynchronizeParticipant participant = (ModelSynchronizeParticipant)configuration.getParticipant();
ModelProvider[] providers = participant.getEnabledModelProviders();
Set<String> result = new HashSet<>();
Object property = configuration.getProperty(ITeamContentProviderManager.PROP_PAGE_LAYOUT);
boolean isFlatLayout = property != null && property.equals(ITeamContentProviderManager.FLAT_LAYOUT);
for (int i = 0; i < providers.length; i++) {
ModelProvider provider = providers[i];
ITeamContentProviderDescriptor desc = TeamUI.getTeamContentProviderManager().getDescriptor(provider.getId());
if (desc != null && desc.isEnabled() && (!isFlatLayout || desc.isFlatLayoutSupported()))
result.add(desc.getContentExtensionId());
}
return result.toArray(new String[result.size()]);
}
private static void bindTeamContentProviders(CommonViewer v) {
ITeamContentProviderManager teamContentProviderManager = TeamUI.getTeamContentProviderManager();
ITeamContentProviderDescriptor[] descriptors = teamContentProviderManager.getDescriptors();
Set<String> toBind = new HashSet<>();
for (int i = 0; i < descriptors.length; i++) {
ITeamContentProviderDescriptor descriptor = descriptors[i];
toBind.add(descriptor.getContentExtensionId());
}
v.getNavigatorContentService().bindExtensions(toBind.toArray(new String[toBind.size()]), true);
}
private static ISynchronizationScope getScope(ISynchronizePageConfiguration configuration) {
return (ISynchronizationScope)configuration.getProperty(ITeamContentProviderManager.P_SYNCHRONIZATION_SCOPE);
}
/**
* Create the advisor using the given configuration
* @param parent the parent
* @param configuration the configuration
*/
public CommonViewerAdvisor(Composite parent, ISynchronizePageConfiguration configuration) {
super(configuration);
final CommonViewer viewer = CommonViewerAdvisor.createViewer(parent, configuration, this);
TeamUI.getTeamContentProviderManager().addPropertyChangeListener(this);
configuration.addPropertyChangeListener(this);
GridData data = new GridData(GridData.FILL_BOTH);
viewer.getControl().setLayoutData(data);
viewer.getNavigatorContentService().addListener(this);
initializeViewer(viewer);
IBaseLabelProvider provider = viewer.getLabelProvider();
if (provider instanceof DecoratingLabelProvider) {
DecoratingLabelProvider dlp = (DecoratingLabelProvider) provider;
ILabelDecorator decorator = ((SynchronizePageConfiguration)configuration).getLabelDecorator();
if (decorator != null) {
ILabelProvider lp = dlp.getLabelProvider();
dlp = new DecoratingLabelProvider(
new DecoratingLabelProvider(lp, decorator),
PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator());
viewer.setLabelProvider(dlp);
}
DecorationContext decorationContext = new DecorationContext();
decorationContext.putProperty(SynchronizationStateTester.PROP_TESTER, new SynchronizationStateTester() {
@Override
public boolean isStateDecorationEnabled() {
return false;
}
});
dlp.setDecorationContext(decorationContext);
} else if (provider instanceof DecoratingStyledCellLabelProvider) {
DecoratingStyledCellLabelProvider dsclp = (DecoratingStyledCellLabelProvider) provider;
ILabelDecorator decorator = ((SynchronizePageConfiguration) configuration)
.getLabelDecorator();
if (decorator != null) {
IStyledLabelProvider slp = dsclp.getStyledStringProvider();
dsclp = new DecoratingStyledCellLabelProvider(
new MyDecoratingStyledCellLabelProvider(slp, decorator),
PlatformUI.getWorkbench().getDecoratorManager()
.getLabelDecorator(), null);
viewer.setLabelProvider(dsclp);
}
DecorationContext decorationContext = new DecorationContext();
decorationContext.putProperty(SynchronizationStateTester.PROP_TESTER, new SynchronizationStateTester() {
@Override
public boolean isStateDecorationEnabled() {
return false;
}
});
dsclp.setDecorationContext(decorationContext);
}
}
private class MyDecoratingStyledCellLabelProvider extends
DecoratingStyledCellLabelProvider implements IStyledLabelProvider,
IFontProvider {
private IStyledLabelProvider slp;
public MyDecoratingStyledCellLabelProvider(IStyledLabelProvider slp,
ILabelDecorator decorator) {
super(slp, decorator, null);
this.slp = slp;
}
@Override
public StyledString getStyledText(Object element) {
return slp.getStyledText(element);
}
@Override
public Font getFont(Object element) {
// DelegatingStyledCellLabelProvider does not implement
// IFontProvider
return super.getFont(element);
}
}
@Override
public void setInitialInput() {
CommonViewer viewer = (CommonViewer)getViewer();
viewer.setInput(getInitialInput());
viewer.expandToLevel(2);
}
@Override
public void initializeViewer(StructuredViewer viewer) {
createActionService((CommonViewer)viewer, getConfiguration());
super.initializeViewer(viewer);
}
private void createActionService(CommonViewer viewer, ISynchronizePageConfiguration configuration) {
ICommonViewerSite commonSite = createCommonViewerSite(viewer, configuration);
actionService = new NavigatorActionService(commonSite, viewer, viewer.getNavigatorContentService());
}
private ICommonViewerSite createCommonViewerSite(CommonViewer viewer, ISynchronizePageConfiguration configuration) {
IWorkbenchSite site = configuration.getSite().getWorkbenchSite();
if (site instanceof IEditorSite) {
IEditorSite es = (IEditorSite) site;
return CommonViewerSiteFactory.createCommonViewerSite(es);
}
if (site instanceof IViewSite) {
IViewSite vs = (IViewSite) site;
return CommonViewerSiteFactory.createCommonViewerSite(vs);
}
if (site instanceof IPageSite) {
IPageSite ps = (IPageSite) site;
return CommonViewerSiteFactory.createCommonViewerSite(configuration.getViewerId(), ps);
}
return CommonViewerSiteFactory.createCommonViewerSite(configuration.getViewerId(), viewer, configuration.getSite().getShell());
}
private Object getInitialInput() {
String visible = (String)getConfiguration().getProperty(ModelSynchronizeParticipant.P_VISIBLE_MODEL_PROVIDER);
if (visible != null && !visible.equals(ModelSynchronizeParticipant.ALL_MODEL_PROVIDERS_VISIBLE)) {
try {
IModelProviderDescriptor desc = ModelProvider.getModelProviderDescriptor(visible);
if (desc != null)
return desc.getModelProvider();
} catch (CoreException e) {
TeamUIPlugin.log(e);
}
}
return getConfiguration().getProperty(ITeamContentProviderManager.P_SYNCHRONIZATION_CONTEXT);
}
@Override
public void onLoad(INavigatorContentExtension anExtension) {
extensions.add(anExtension);
ISynchronizationContext context = getParticipant().getContext();
anExtension.getStateModel().setProperty(ITeamContentProviderManager.P_SYNCHRONIZATION_SCOPE, context.getScope());
anExtension.getStateModel().setProperty(ITeamContentProviderManager.P_SYNCHRONIZATION_PAGE_CONFIGURATION, getConfiguration());
anExtension.getStateModel().setProperty(ITeamContentProviderManager.P_SYNCHRONIZATION_CONTEXT, context);
}
private ModelSynchronizeParticipant getParticipant() {
return (ModelSynchronizeParticipant)getConfiguration().getParticipant();
}
@Override
protected String getContextMenuId(StructuredViewer viewer) {
return ((CommonViewer)viewer).getNavigatorContentService().getViewerDescriptor().getPopupMenuId();
}
@Override
protected void registerContextMenu(StructuredViewer viewer, MenuManager menuMgr) {
actionService.prepareMenuForPlatformContributions(menuMgr,
viewer, false);
}
@Override
protected void fillContextMenu(StructuredViewer viewer, IMenuManager manager) {
// Clear any handlers from the menu
if (manager instanceof CommonMenuManager) {
CommonMenuManager cmm = (CommonMenuManager) manager;
cmm.clearHandlers();
}
// Add the actions from the service (which willal so add the groups)
ISelection selection = getViewer().getSelection();
actionService.setContext(new ActionContext(selection));
actionService.fillContextMenu(manager);
// Add any programmatic menu items
super.fillContextMenu(viewer, manager);
}
@Override
public void dispose() {
TeamUI.getTeamContentProviderManager().removePropertyChangeListener(this);
getConfiguration().removePropertyChangeListener(this);
actionService.dispose();
super.dispose();
}
@Override
protected void updateActionBars(IStructuredSelection selection) {
super.updateActionBars(selection);
if (!getConfiguration().getSite().isModal()) {
actionService.setContext(new ActionContext(selection));
// This is non-standard behavior that is required by the common navigator framework (see bug 122808)
SubActionBars subActionBars = (SubActionBars)getConfiguration().getProperty(PROP_ACTION_SERVICE_ACTION_BARS);
if (subActionBars == null) {
subActionBars = new CommonSubActionBars(getConfiguration().getSite().getActionBars());
getConfiguration().setProperty(PROP_ACTION_SERVICE_ACTION_BARS, subActionBars);
}
actionService.fillActionBars(subActionBars);
}
}
@Override
protected MenuManager createContextMenuManager(String targetID) {
return new CommonMenuManager(targetID);
}
@Override
protected void addContextMenuGroups(IMenuManager manager) {
// Don't do anything. The groups will be added by the action service
}
public void addEmptyTreeListener(IEmptyTreeListener emptyTreeListener) {
this.emptyTreeListener = emptyTreeListener;
}
@Override
public void treeEmpty(TreeViewer viewer) {
if (emptyTreeListener != null)
emptyTreeListener.treeEmpty(viewer);
}
@Override
public void notEmpty(TreeViewer viewer) {
if (emptyTreeListener != null)
emptyTreeListener.notEmpty(viewer);
}
@Override
public void propertyChange(final PropertyChangeEvent event) {
if (event.getProperty().equals(ITeamContentProviderManager.PROP_ENABLED_MODEL_PROVIDERS)) {
enableContentProviders((CommonViewer)getViewer(), getConfiguration());
} else if (event.getProperty().equals(ModelSynchronizeParticipant.P_VISIBLE_MODEL_PROVIDER)) {
enableContentProviders((CommonViewer)getViewer(), getConfiguration());
final Viewer viewer = getViewer();
Utils.syncExec((Runnable) () -> {
Object viewerInput = ModelSynchronizePage.getViewerInput(getConfiguration(), (String)event.getNewValue());
if (viewer != null && viewerInput != null) {
viewer.setInput(viewerInput);
}
}, (StructuredViewer)viewer);
} else if (event.getProperty().equals(ITeamContentProviderManager.PROP_PAGE_LAYOUT)) {
// TODO
enableContentProviders((CommonViewer)getViewer(), getConfiguration());
}
}
@Override
protected boolean handleDoubleClick(StructuredViewer viewer, DoubleClickEvent event) {
if (isOpenable(event.getSelection())) {
return true;
}
return super.handleDoubleClick(viewer, event);
}
private boolean isOpenable(ISelection selection) {
IStructuredSelection ss = (IStructuredSelection) selection;
Object object = ss.getFirstElement();
if (object == null)
return false;
return getParticipant().hasCompareInputFor(object);
}
@Override
protected void expandToNextDiff(Object element) {
((TreeViewer)getViewer()).expandToLevel(element, AbstractTreeViewer.ALL_LEVELS);
}
}