blob: 44f22d2d5fbb2139df91593cfe9581baa058c4d1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2010 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
* Matt McCutchen <hashproduct+eclipse@gmail.com> - Bug 94808 [Change Sets] "&" not showing up in dropdown menu
*******************************************************************************/
package org.eclipse.team.internal.ccvs.ui.mappings;
import java.util.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.mapping.ResourceTraversal;
import org.eclipse.jface.action.*;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.widgets.Control;
import org.eclipse.team.core.diff.*;
import org.eclipse.team.core.mapping.IResourceDiffTree;
import org.eclipse.team.core.mapping.provider.ResourceDiffTree;
import org.eclipse.team.internal.ccvs.core.mapping.ChangeSetModelProvider;
import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
import org.eclipse.team.internal.core.subscribers.*;
import org.eclipse.team.internal.ui.*;
import org.eclipse.team.internal.ui.mapping.ResourceModelActionProvider;
import org.eclipse.team.internal.ui.mapping.ResourceModelTraversalCalculator;
import org.eclipse.team.internal.ui.synchronize.*;
import org.eclipse.team.ui.synchronize.*;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.actions.BaseSelectionListenerAction;
import org.eclipse.ui.navigator.INavigatorContentExtension;
import org.eclipse.ui.navigator.INavigatorContentService;
public class ChangeSetActionProvider extends ResourceModelActionProvider {
/**
* Menu group that can be added to the context menu
*/
public final static String CHANGE_SET_GROUP = "changeSetActions"; //$NON-NLS-1$
// Constants for persisting sorting options
private static final String P_LAST_COMMENTSORT = TeamUIPlugin.ID + ".P_LAST_COMMENT_SORT"; //$NON-NLS-1$
private MenuManager sortByComment;
private MenuManager addToChangeSet;
private CreateChangeSetAction createChangeSet;
private EditChangeSetAction editChangeSet;
private RemoveChangeSetAction removeChangeSet;
private MakeDefaultChangeSetAction makeDefault;
private OpenChangeSetAction openCommitSet;
private class CreateChangeSetAction extends ModelParticipantAction {
public CreateChangeSetAction(ISynchronizePageConfiguration configuration) {
super(TeamUIMessages.ChangeLogModelProvider_0, configuration);
}
@Override
public void run() {
final IDiff[] diffs = getLocalChanges(getStructuredSelection());
syncExec(() -> createChangeSet(diffs));
}
/* package */void createChangeSet(IDiff[] diffs) {
ActiveChangeSet set = getChangeSetCapability().createChangeSet(getConfiguration(), diffs);
if (set != null) {
getActiveChangeSetManager().add(set);
}
}
@Override
protected boolean isEnabledForSelection(IStructuredSelection selection) {
return isContentProviderEnabled()
&& containsOnlyLocalChanges(selection);
}
}
/**
* Escape a string so it can be used as an action text without '&'
* being interpreted as a mnemonic. Specifically, turn each '&' into '&&'.
*/
/* package */static String escapeActionText(String x) {
// Loosely based on org.eclipse.jface.action.LegacyActionTools#removeMnemonics
int ampersandIndex = x.indexOf('&');
if (ampersandIndex == -1)
return x;
int len = x.length();
StringBuffer sb = new StringBuffer(2 * len + 1);
int doneIndex = 0;
while (ampersandIndex != -1) {
sb.append(x.substring(doneIndex, ampersandIndex));
sb.append("&&"); //$NON-NLS-1$
doneIndex = ampersandIndex + 1;
ampersandIndex = x.indexOf('&', doneIndex);
}
if (doneIndex < len)
sb.append(x.substring(doneIndex, len));
return sb.toString();
}
private class AddToChangeSetAction extends ModelParticipantAction {
private final ActiveChangeSet set;
public AddToChangeSetAction(ISynchronizePageConfiguration configuration, ActiveChangeSet set, ISelection selection) {
super(set == null ? TeamUIMessages.ChangeSetActionGroup_2 : escapeActionText(set.getTitle()), configuration);
this.set = set;
selectionChanged(selection);
}
@Override
public void run() {
IDiff[] diffArray = getLocalChanges(getStructuredSelection());
if (set != null) {
set.add(diffArray);
} else {
ChangeSet[] sets = getActiveChangeSetManager().getSets();
IResource[] resources = getResources(diffArray);
for (int i = 0; i < sets.length; i++) {
ActiveChangeSet activeSet = (ActiveChangeSet) sets[i];
activeSet.remove(resources);
}
}
}
@Override
protected boolean isEnabledForSelection(IStructuredSelection selection) {
return isContentProviderEnabled()
&& containsOnlyLocalChanges(selection);
}
}
private abstract class ChangeSetAction extends BaseSelectionListenerAction {
public ChangeSetAction(String title, ISynchronizePageConfiguration configuration) {
super(title);
}
@Override
protected boolean updateSelection(IStructuredSelection selection) {
return getSelectedSet() != null;
}
protected ActiveChangeSet getSelectedSet() {
IStructuredSelection selection = getStructuredSelection();
if (selection.size() == 1) {
Object first = selection.getFirstElement();
if (first instanceof ActiveChangeSet) {
ActiveChangeSet activeChangeSet = (ActiveChangeSet) first;
if (activeChangeSet.isUserCreated())
return activeChangeSet;
}
}
return null;
}
}
private class EditChangeSetAction extends ChangeSetAction {
public EditChangeSetAction(ISynchronizePageConfiguration configuration) {
super(TeamUIMessages.ChangeLogModelProvider_6, configuration);
}
@Override
public void run() {
ActiveChangeSet set = getSelectedSet();
if (set == null) return;
getChangeSetCapability().editChangeSet(internalGetSynchronizePageConfiguration(), set);
}
}
private class RemoveChangeSetAction extends ModelParticipantAction {
public RemoveChangeSetAction(ISynchronizePageConfiguration configuration) {
super(TeamUIMessages.ChangeLogModelProvider_7, configuration);
}
@Override
public void run() {
IDiff[] diffArray = getLocalChanges(getStructuredSelection());
ChangeSet[] sets = getActiveChangeSetManager().getSets();
IResource[] resources = getResources(diffArray);
for (int i = 0; i < sets.length; i++) {
ActiveChangeSet activeSet = (ActiveChangeSet) sets[i];
activeSet.remove(resources);
}
}
@Override
protected boolean isEnabledForSelection(IStructuredSelection selection) {
return isContentProviderEnabled()
&& containsOnlyLocalChanges(selection);
}
}
private class MakeDefaultChangeSetAction extends ChangeSetAction {
public MakeDefaultChangeSetAction(
ISynchronizePageConfiguration configuration) {
super(TeamUIMessages.ChangeLogModelProvider_9, configuration);
}
@Override
protected boolean updateSelection(IStructuredSelection selection) {
if (getSelectedSet() != null) {
setText(TeamUIMessages.ChangeLogModelProvider_9);
setChecked(getSelectedSet().equals(
getActiveChangeSetManager().getDefaultSet()));
} else {
setText(TeamUIMessages.ChangeLogModelProvider_10);
setChecked(false);
}
return true;
}
@Override
public void run() {
getActiveChangeSetManager().makeDefault(
isChecked() ? getSelectedSet() : null);
if (getSelectedSet() == null) {
setChecked(false); // keep unchecked
}
}
}
/* *****************************************************************************
* Action that allows changing the model providers sort order.
*/
private class ToggleSortOrderAction extends Action {
private int criteria;
protected ToggleSortOrderAction(String name, int criteria) {
super(name, IAction.AS_RADIO_BUTTON);
this.criteria = criteria;
update();
}
@Override
public void run() {
int sortCriteria = getSortCriteria(internalGetSynchronizePageConfiguration());
if (isChecked() && sortCriteria != criteria) {
setSortCriteria(internalGetSynchronizePageConfiguration(), criteria);
update();
((SynchronizePageConfiguration)internalGetSynchronizePageConfiguration()).getPage().getViewer().refresh();
}
}
public void update() {
setChecked(criteria == getSortCriteria(internalGetSynchronizePageConfiguration()));
}
}
public static int getSortCriteria(ISynchronizePageConfiguration configuration) {
int sortCriteria = ChangeSetSorter.DATE;
if (configuration != null) {
Object o = configuration.getProperty(P_LAST_COMMENTSORT);
if (o instanceof Integer) {
Integer wrapper = (Integer) o;
sortCriteria = wrapper.intValue();
} else {
try {
IDialogSettings pageSettings = configuration.getSite().getPageSettings();
if (pageSettings != null) {
sortCriteria = pageSettings.getInt(P_LAST_COMMENTSORT);
}
} catch (NumberFormatException e) {
// ignore and use the defaults.
}
}
}
switch (sortCriteria) {
case ChangeSetSorter.COMMENT:
case ChangeSetSorter.DATE:
case ChangeSetSorter.USER:
break;
default:
sortCriteria = ChangeSetSorter.DATE;
break;
}
return sortCriteria;
}
public static void setSortCriteria(ISynchronizePageConfiguration configuration, int criteria) {
configuration.setProperty(P_LAST_COMMENTSORT, Integer.valueOf(criteria));
IDialogSettings pageSettings = configuration.getSite().getPageSettings();
if (pageSettings != null) {
pageSettings.put(P_LAST_COMMENTSORT, criteria);
}
}
public ChangeSetActionProvider() {
super();
}
@Override
protected void initialize() {
super.initialize();
if (getChangeSetCapability().supportsCheckedInChangeSets()) {
sortByComment = new MenuManager(TeamUIMessages.ChangeLogModelProvider_0a);
sortByComment.add(new ToggleSortOrderAction(TeamUIMessages.ChangeLogModelProvider_1a, ChangeSetSorter.COMMENT));
sortByComment.add(new ToggleSortOrderAction(TeamUIMessages.ChangeLogModelProvider_2a, ChangeSetSorter.DATE));
sortByComment.add(new ToggleSortOrderAction(TeamUIMessages.ChangeLogModelProvider_3a, ChangeSetSorter.USER));
openCommitSet = new OpenChangeSetAction(getSynchronizePageConfiguration());
}
if (getChangeSetCapability().supportsActiveChangeSets()) {
createChangeSet = new CreateChangeSetAction(
getSynchronizePageConfiguration());
editChangeSet = new EditChangeSetAction(
getSynchronizePageConfiguration());
makeDefault = new MakeDefaultChangeSetAction(
getSynchronizePageConfiguration());
removeChangeSet = new RemoveChangeSetAction(
getSynchronizePageConfiguration());
}
}
protected ActiveChangeSet getSelectedActiveChangeSet(
IStructuredSelection selection) {
if (selection.size() == 1) {
Object first = selection.getFirstElement();
if (first instanceof ActiveChangeSet) {
ActiveChangeSet activeChangeSet = (ActiveChangeSet) first;
if (activeChangeSet.isUserCreated())
return activeChangeSet;
}
}
return null;
}
private IResource[] getResources(IDiff[] diffArray) {
List<IResource> result = new ArrayList<>();
for (int i = 0; i < diffArray.length; i++) {
IDiff diff = diffArray[i];
IResource resource = ResourceDiffTree.getResourceFor(diff);
if (resource != null) {
result.add(resource);
}
}
return result.toArray(new IResource[result.size()]);
}
@Override
public void fillContextMenu(IMenuManager menu) {
if (isContentProviderEnabled()) {
super.fillContextMenu(menu);
if (getChangeSetCapability().enableCheckedInChangeSetsFor(
getSynchronizePageConfiguration())) {
appendToGroup(menu, "file-bottom", //$NON-NLS-1$
openCommitSet);
appendToGroup(menu, ISynchronizePageConfiguration.SORT_GROUP,
sortByComment);
}
IStructuredSelection selection = (IStructuredSelection) getContext()
.getSelection();
if (getChangeSetCapability().enableActiveChangeSetsFor(
getSynchronizePageConfiguration())
&& containsOnlyLocalChanges(selection)) {
if (containsOnlyUnassignedChanges(selection)) {
// only local unassigned changes
addToChangeSet = new MenuManager(
TeamUIMessages.ChangeLogModelProvider_13);
appendToGroup(menu, CHANGE_SET_GROUP, addToChangeSet);
} else {
addToChangeSet = new MenuManager(
TeamUIMessages.ChangeLogModelProvider_12);
appendToGroup(menu, CHANGE_SET_GROUP, addToChangeSet);
appendToGroup(menu, CHANGE_SET_GROUP, removeChangeSet);
}
addChangeSets(addToChangeSet);
if (getSelectedActiveChangeSet(selection) != null) {
appendToGroup(menu, CHANGE_SET_GROUP, editChangeSet);
}
appendToGroup(menu, CHANGE_SET_GROUP, makeDefault);
}
}
}
protected void addChangeSets(IMenuManager manager) {
ChangeSet[] sets = getActiveChangeSetManager().getSets();
Arrays.sort(sets, new ChangeSetComparator());
ISelection selection = getContext().getSelection();
createChangeSet.selectionChanged(selection);
manager.add(createChangeSet);
manager.add(new Separator());
for (int i = 0; i < sets.length; i++) {
ActiveChangeSet set = (ActiveChangeSet) sets[i];
AddToChangeSetAction action = new AddToChangeSetAction(
getSynchronizePageConfiguration(), set, selection);
manager.add(action);
}
manager.add(new Separator());
}
@Override
public void dispose() {
if (addToChangeSet != null) {
addToChangeSet.dispose();
addToChangeSet.removeAll();
}
if (sortByComment != null) {
sortByComment.dispose();
sortByComment.removeAll();
}
super.dispose();
}
private boolean appendToGroup(IContributionManager manager, String groupId, IContributionItem item) {
if (manager == null || item == null) return false;
IContributionItem group = manager.find(groupId);
if (group != null) {
manager.appendToGroup(group.getId(), item);
return true;
}
return false;
}
private boolean appendToGroup(IContributionManager manager, String groupId, IAction action) {
if (manager == null || action == null) return false;
IContributionItem group = manager.find(groupId);
if (group != null) {
manager.appendToGroup(group.getId(), action);
// registerActionWithWorkbench(action);
return true;
}
return false;
}
public ChangeSetCapability getChangeSetCapability() {
ISynchronizeParticipant participant = getSynchronizePageConfiguration().getParticipant();
if (participant instanceof IChangeSetProvider) {
IChangeSetProvider provider = (IChangeSetProvider) participant;
return provider.getChangeSetCapability();
}
return null;
}
/* package */void syncExec(final Runnable runnable) {
final Control ctrl = getSynchronizePageConfiguration().getPage().getViewer().getControl();
Utils.syncExec(runnable, ctrl);
}
/* package */ActiveChangeSetManager getActiveChangeSetManager() {
return CVSUIPlugin.getPlugin().getChangeSetManager();
}
public IDiff[] getLocalChanges(IStructuredSelection selection) {
if (selection instanceof ITreeSelection) {
ITreeSelection ts = (ITreeSelection) selection;
TreePath[] paths = ts.getPaths();
List<IDiff> result = new ArrayList<>();
for (int i = 0; i < paths.length; i++) {
TreePath path = paths[i];
IDiff[] diffs = getLocalChanges(path);
for (int j = 0; j < diffs.length; j++) {
IDiff diff = diffs[j];
result.add(diff);
}
}
return result.toArray(new IDiff[result.size()]);
}
return new IDiff[0];
}
private IDiff[] getLocalChanges(TreePath path) {
IResourceDiffTree tree = getDiffTree(path);
if (path.getSegmentCount() == 1 && path.getLastSegment() instanceof IDiffTree) {
return ((ResourceDiffTree) tree).getDiffs();
}
ResourceTraversal[] traversals = getTraversals(path.getLastSegment());
return tree.getDiffs(traversals);
}
private IResourceDiffTree getDiffTree(TreePath path) {
return getContentProvider().getDiffTree(path);
}
private boolean containsOnlyUnassignedChanges(IStructuredSelection selection) {
IDiff[] diffArray = getLocalChanges(selection);
ChangeSet[] activeChangeSets = getActiveChangeSetManager().getSets();
IResource[] resources = getResources(diffArray);
for (int i = 0; i < activeChangeSets.length; i++) {
for (int j = 0; j < resources.length; j++) {
if (activeChangeSets[i].contains(resources[j]))
return false;
}
}
return true;
}
public boolean containsOnlyLocalChanges(IStructuredSelection selection) {
if (selection instanceof ITreeSelection) {
ITreeSelection ts = (ITreeSelection) selection;
TreePath[] paths = ts.getPaths();
for (int i = 0; i < paths.length; i++) {
TreePath path = paths[i];
if (!containsOnlyLocalChanges(path)) {
return false;
}
}
}
return true;
}
private boolean containsOnlyLocalChanges(TreePath path) {
IResourceDiffTree tree = getDiffTree(path);
ResourceTraversal[] traversals = getTraversals(path.getLastSegment());
return !tree.hasMatchingDiffs(traversals, getNonLocalChangesFilter());
}
private ResourceTraversal[] getTraversals(Object element) {
if (element instanceof ChangeSet) {
ChangeSet set = (ChangeSet) element;
return new ResourceTraversal[] { new ResourceTraversal(set.getResources(), IResource.DEPTH_ZERO, IResource.NONE) };
}
if (element instanceof IProject) {
IProject project = (IProject) element;
return new ResourceTraversal[] { new ResourceTraversal(new IResource[] { project }, IResource.DEPTH_INFINITE, IResource.NONE) };
}
if (element instanceof IFile) {
IFile file = (IFile) element;
return new ResourceTraversal[] { new ResourceTraversal(new IResource[] { file }, IResource.DEPTH_ZERO, IResource.NONE) };
}
if (element instanceof IFolder) {
IFolder folder = (IFolder) element;
if (getLayout().equals(IPreferenceIds.COMPRESSED_LAYOUT)) {
return new ResourceTraversal[] { new ResourceTraversal(new IResource[] { folder }, IResource.DEPTH_ONE, IResource.NONE) };
} else if (getLayout().equals(IPreferenceIds.TREE_LAYOUT)) {
return new ResourceTraversal[] { new ResourceTraversal(new IResource[] { folder }, IResource.DEPTH_INFINITE, IResource.NONE) };
} else if (getLayout().equals(IPreferenceIds.FLAT_LAYOUT)) {
return new ResourceTraversal[] { new ResourceTraversal(new IResource[] { folder }, IResource.DEPTH_ZERO, IResource.NONE) };
}
}
return new ResourceTraversal[0];
}
private FastDiffFilter getNonLocalChangesFilter() {
return new FastDiffFilter() {
@Override
public boolean select(IDiff diff) {
if (diff instanceof IThreeWayDiff && isVisible(diff)) {
IThreeWayDiff twd = (IThreeWayDiff) diff;
if (twd.getDirection() == IThreeWayDiff.OUTGOING
|| twd.getDirection() == IThreeWayDiff.CONFLICTING) {
return false;
}
}
return true;
}
};
}
/* package */boolean isVisible(IDiff diff) {
return ((SynchronizePageConfiguration)getSynchronizePageConfiguration()).isVisible(diff);
}
protected ResourceModelTraversalCalculator getTraversalCalculator() {
return ResourceModelTraversalCalculator.getTraversalCalculator(getSynchronizePageConfiguration());
}
private ChangeSetContentProvider getContentProvider() {
INavigatorContentExtension extension = getExtension();
if (extension != null) {
ITreeContentProvider provider = extension.getContentProvider();
if (provider instanceof ChangeSetContentProvider) {
return (ChangeSetContentProvider) provider;
}
}
return null;
}
private INavigatorContentExtension getExtension() {
INavigatorContentService service = getActionSite().getContentService();
Set set = service.findContentExtensionsByTriggerPoint(getModelProvider());
for (Iterator iter = set.iterator(); iter.hasNext();) {
INavigatorContentExtension extension = (INavigatorContentExtension) iter.next();
return extension;
}
return null;
}
private Object getModelProvider() {
return ChangeSetModelProvider.getProvider();
}
private String getLayout() {
return TeamUIPlugin.getPlugin().getPreferenceStore().getString(IPreferenceIds.SYNCVIEW_DEFAULT_LAYOUT);
}
@Override
public void setContext(ActionContext context) {
super.setContext(context);
if (context != null) {
if (editChangeSet != null)
editChangeSet.selectionChanged((IStructuredSelection)getContext().getSelection());
if (removeChangeSet != null)
removeChangeSet.selectionChanged((IStructuredSelection)getContext().getSelection());
if (makeDefault != null)
makeDefault.selectionChanged((IStructuredSelection)getContext().getSelection());
}
}
protected boolean isContentProviderEnabled() {
ChangeSetContentProvider provider = getContentProvider();
if (provider != null) {
return provider.isEnabled();
}
return false;
}
/* package */ISynchronizePageConfiguration internalGetSynchronizePageConfiguration() {
return getSynchronizePageConfiguration();
}
}