blob: 22efde10e5dc0af31799e0edefa795ac11c2d979 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created by IntelliJ IDEA.
* User: yole
* Date: 23.11.2006
* Time: 15:11:11
*/
package com.intellij.openapi.vcs.changes.shelf;
import com.intellij.CommonBundle;
import com.intellij.ide.DataManager;
import com.intellij.ide.DeleteProvider;
import com.intellij.ide.actions.EditSourceAction;
import com.intellij.ide.impl.TypeSafeDataProviderAdapter;
import com.intellij.ide.util.treeView.TreeState;
import com.intellij.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.diff.impl.patch.FilePatch;
import com.intellij.openapi.diff.impl.patch.PatchSyntaxException;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.CommitContext;
import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer;
import com.intellij.openapi.vcs.changes.issueLinks.TreeLinkMouseListener;
import com.intellij.openapi.vcs.changes.patch.RelativePathCalculator;
import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.pom.Navigatable;
import com.intellij.pom.NavigatableAdapter;
import com.intellij.ui.*;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Convertor;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
public class ShelvedChangesViewManager implements ProjectComponent {
private final ChangesViewContentManager myContentManager;
private final ShelveChangesManager myShelveChangesManager;
private final Project myProject;
private final ShelfTree myTree;
private Content myContent = null;
private final ShelvedChangeDeleteProvider myDeleteProvider = new ShelvedChangeDeleteProvider();
private boolean myUpdatePending = false;
private Runnable myPostUpdateRunnable = null;
public static DataKey<ShelvedChangeList[]> SHELVED_CHANGELIST_KEY = DataKey.create("ShelveChangesManager.ShelvedChangeListData");
public static DataKey<ShelvedChangeList[]> SHELVED_RECYCLED_CHANGELIST_KEY = DataKey.create("ShelveChangesManager.ShelvedRecycledChangeListData");
public static DataKey<List<ShelvedChange>> SHELVED_CHANGE_KEY = DataKey.create("ShelveChangesManager.ShelvedChange");
public static DataKey<List<ShelvedBinaryFile>> SHELVED_BINARY_FILE_KEY = DataKey.create("ShelveChangesManager.ShelvedBinaryFile");
private static final Object ROOT_NODE_VALUE = new Object();
private DefaultMutableTreeNode myRoot;
private final Map<Couple<String>, String> myMoveRenameInfo;
public static ShelvedChangesViewManager getInstance(Project project) {
return PeriodicalTasksCloser.getInstance().safeGetComponent(project, ShelvedChangesViewManager.class);
}
public ShelvedChangesViewManager(Project project, ChangesViewContentManager contentManager, ShelveChangesManager shelveChangesManager,
final MessageBus bus) {
myProject = project;
myContentManager = contentManager;
myShelveChangesManager = shelveChangesManager;
bus.connect().subscribe(ShelveChangesManager.SHELF_TOPIC, new ChangeListener() {
public void stateChanged(ChangeEvent e) {
myUpdatePending = true;
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
updateChangesContent();
}
}, ModalityState.NON_MODAL);
}
});
myMoveRenameInfo = new HashMap<Couple<String>, String>();
myTree = new ShelfTree();
myTree.setRootVisible(false);
myTree.setShowsRootHandles(true);
myTree.setCellRenderer(new ShelfTreeCellRenderer(project, myMoveRenameInfo));
new TreeLinkMouseListener(new ShelfTreeCellRenderer(project, myMoveRenameInfo)).installOn(myTree);
final AnAction showDiffAction = ActionManager.getInstance().getAction("ShelvedChanges.Diff");
showDiffAction.registerCustomShortcutSet(CommonShortcuts.getDiff(), myTree);
final EditSourceAction editSourceAction = new EditSourceAction();
editSourceAction.registerCustomShortcutSet(CommonShortcuts.getEditSource(), myTree);
PopupHandler.installPopupHandler(myTree, "ShelvedChangesPopupMenu", ActionPlaces.UNKNOWN);
new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent e) {
DiffShelvedChangesAction.showShelvedChangesDiff(DataManager.getInstance().getDataContext(myTree));
return true;
}
}.installOn(myTree);
new TreeSpeedSearch(myTree, new Convertor<TreePath, String>() {
public String convert(TreePath o) {
final Object lc = o.getLastPathComponent();
final Object lastComponent = lc == null ? null : ((DefaultMutableTreeNode) lc).getUserObject();
if (lastComponent instanceof ShelvedChangeList) {
return ((ShelvedChangeList) lastComponent).DESCRIPTION;
} else if (lastComponent instanceof ShelvedChange) {
final ShelvedChange shelvedChange = (ShelvedChange)lastComponent;
return shelvedChange.getBeforeFileName() == null ? shelvedChange.getAfterFileName() : shelvedChange.getBeforeFileName();
} else if (lastComponent instanceof ShelvedBinaryFile) {
final ShelvedBinaryFile sbf = (ShelvedBinaryFile) lastComponent;
final String value = sbf.BEFORE_PATH == null ? sbf.AFTER_PATH : sbf.BEFORE_PATH;
int idx = value.lastIndexOf("/");
idx = (idx == -1) ? value.lastIndexOf("\\") : idx;
return idx > 0 ? value.substring(idx + 1) : value;
}
return null;
}
}, true);
}
public void projectOpened() {
updateChangesContent();
}
public void projectClosed() {
}
@NonNls @NotNull
public String getComponentName() {
return "ShelvedChangesViewManager";
}
public void initComponent() {
}
public void disposeComponent() {
}
private void updateChangesContent() {
myUpdatePending = false;
final List<ShelvedChangeList> changeLists = new ArrayList<ShelvedChangeList>(myShelveChangesManager.getShelvedChangeLists());
changeLists.addAll(myShelveChangesManager.getRecycledShelvedChangeLists());
if (changeLists.size() == 0) {
if (myContent != null) {
myContentManager.removeContent(myContent);
myContentManager.selectContent("Local");
}
myContent = null;
}
else {
if (myContent == null) {
JPanel rootPanel = createRootPanel();
myContent = ContentFactory.SERVICE.getInstance().createContent(rootPanel, VcsBundle.message("shelf.tab"), false);
myContent.setCloseable(false);
myContentManager.addContent(myContent);
}
TreeState state = TreeState.createOn(myTree);
myTree.setModel(buildChangesModel());
state.applyTo(myTree);
if (myPostUpdateRunnable != null) {
myPostUpdateRunnable.run();
}
}
myPostUpdateRunnable = null;
}
@NotNull
private JPanel createRootPanel() {
JScrollPane pane = ScrollPaneFactory.createScrollPane(myTree);
pane.setBorder(null);
DefaultActionGroup actionGroup = new DefaultActionGroup();
actionGroup.addAll((ActionGroup)ActionManager.getInstance().getAction("ShelvedChangesToolbar"));
actionGroup.add(ActionManager.getInstance().getAction("ShelvedChangesToolbarGear"));
ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, actionGroup, false);
JPanel rootPanel = new JPanel(new BorderLayout());
rootPanel.add(toolbar.getComponent(), BorderLayout.WEST);
rootPanel.add(pane, BorderLayout.CENTER);
DataManager.registerDataProvider(rootPanel, new TypeSafeDataProviderAdapter(myTree));
return rootPanel;
}
private TreeModel buildChangesModel() {
myRoot = new DefaultMutableTreeNode(ROOT_NODE_VALUE); // not null for TreeState matching to work
DefaultTreeModel model = new DefaultTreeModel(myRoot);
final List<ShelvedChangeList> changeLists = new ArrayList<ShelvedChangeList>(myShelveChangesManager.getShelvedChangeLists());
Collections.sort(changeLists, ChangelistComparator.getInstance());
if (myShelveChangesManager.isShowRecycled()) {
ArrayList<ShelvedChangeList> recycled =
new ArrayList<ShelvedChangeList>(myShelveChangesManager.getRecycledShelvedChangeLists());
Collections.sort(recycled, ChangelistComparator.getInstance());
changeLists.addAll(recycled);
}
myMoveRenameInfo.clear();
for(ShelvedChangeList changeList: changeLists) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(changeList);
model.insertNodeInto(node, myRoot, myRoot.getChildCount());
final List<Object> shelvedFilesNodes = new ArrayList<Object>();
List<ShelvedChange> changes = changeList.getChanges(myProject);
for(ShelvedChange change: changes) {
putMovedMessage(change.getBeforePath(), change.getAfterPath());
shelvedFilesNodes.add(change);
}
List<ShelvedBinaryFile> binaryFiles = changeList.getBinaryFiles();
for(ShelvedBinaryFile file: binaryFiles) {
putMovedMessage(file.BEFORE_PATH, file.AFTER_PATH);
shelvedFilesNodes.add(file);
}
Collections.sort(shelvedFilesNodes, ShelvedFilePatchComparator.getInstance());
for (int i = 0; i < shelvedFilesNodes.size(); i++) {
final Object filesNode = shelvedFilesNodes.get(i);
final DefaultMutableTreeNode pathNode = new DefaultMutableTreeNode(filesNode);
model.insertNodeInto(pathNode, node, i);
}
}
return model;
}
private static class ChangelistComparator implements Comparator<ShelvedChangeList> {
private final static ChangelistComparator ourInstance = new ChangelistComparator();
public static ChangelistComparator getInstance() {
return ourInstance;
}
@Override
public int compare(ShelvedChangeList o1, ShelvedChangeList o2) {
return o2.DATE.compareTo(o1.DATE);
}
}
private void putMovedMessage(final String beforeName, final String afterName) {
final String movedMessage = RelativePathCalculator.getMovedString(beforeName, afterName);
if (movedMessage != null) {
myMoveRenameInfo.put(Couple.of(beforeName, afterName), movedMessage);
}
}
public void activateView(final ShelvedChangeList list) {
Runnable runnable = new Runnable() {
public void run() {
if (list != null) {
TreeUtil.selectNode(myTree, TreeUtil.findNodeWithObject(myRoot, list));
}
myContentManager.setSelectedContent(myContent);
ToolWindow window = ToolWindowManager.getInstance(myProject).getToolWindow(ChangesViewContentManager.TOOLWINDOW_ID);
if (!window.isVisible()) {
window.activate(null);
}
}
};
if (myUpdatePending) {
myPostUpdateRunnable = runnable;
}
else {
runnable.run();
}
}
private class ShelfTree extends Tree implements TypeSafeDataProvider {
public void calcData(DataKey key, DataSink sink) {
if (key == SHELVED_CHANGELIST_KEY) {
final Set<ShelvedChangeList> changeLists = getSelectedLists(false);
if (changeLists.size() > 0) {
sink.put(SHELVED_CHANGELIST_KEY, changeLists.toArray(new ShelvedChangeList[changeLists.size()]));
}
}
else if (key == SHELVED_RECYCLED_CHANGELIST_KEY) {
final Set<ShelvedChangeList> changeLists = getSelectedLists(true);
if (changeLists.size() > 0) {
sink.put(SHELVED_RECYCLED_CHANGELIST_KEY, changeLists.toArray(new ShelvedChangeList[changeLists.size()]));
}
}
else if (key == SHELVED_CHANGE_KEY) {
sink.put(SHELVED_CHANGE_KEY, TreeUtil.collectSelectedObjectsOfType(this, ShelvedChange.class));
}
else if (key == SHELVED_BINARY_FILE_KEY) {
sink.put(SHELVED_BINARY_FILE_KEY, TreeUtil.collectSelectedObjectsOfType(this, ShelvedBinaryFile.class));
}
else if (key == VcsDataKeys.HAVE_SELECTED_CHANGES) {
sink.put(VcsDataKeys.HAVE_SELECTED_CHANGES, getSelectionCount() > 0);
/*List<ShelvedChange> shelvedChanges = TreeUtil.collectSelectedObjectsOfType(this, ShelvedChange.class);
final List<ShelvedChangeList> changeLists = TreeUtil.collectSelectedObjectsOfType(this, ShelvedChangeList.class);*/
} else if (key == VcsDataKeys.CHANGES) {
List<ShelvedChange> shelvedChanges = TreeUtil.collectSelectedObjectsOfType(this, ShelvedChange.class);
final List<ShelvedBinaryFile> shelvedBinaryFiles = TreeUtil.collectSelectedObjectsOfType(this, ShelvedBinaryFile.class);
if (! shelvedChanges.isEmpty() || ! shelvedBinaryFiles.isEmpty()) {
final List<Change> changes = new ArrayList<Change>(shelvedChanges.size() + shelvedBinaryFiles.size());
for (ShelvedChange shelvedChange : shelvedChanges) {
changes.add(shelvedChange.getChange(myProject));
}
for (ShelvedBinaryFile binaryFile : shelvedBinaryFiles) {
changes.add(binaryFile.createChange(myProject));
}
sink.put(VcsDataKeys.CHANGES, changes.toArray(new Change[changes.size()]));
}
else {
final List<ShelvedChangeList> changeLists = TreeUtil.collectSelectedObjectsOfType(this, ShelvedChangeList.class);
final List<Change> changes = new ArrayList<Change>();
for(ShelvedChangeList changeList: changeLists) {
shelvedChanges = changeList.getChanges(myProject);
for(ShelvedChange shelvedChange: shelvedChanges) {
changes.add(shelvedChange.getChange(myProject));
}
final List<ShelvedBinaryFile> binaryFiles = changeList.getBinaryFiles();
for (ShelvedBinaryFile file : binaryFiles) {
changes.add(file.createChange(myProject));
}
}
sink.put(VcsDataKeys.CHANGES, changes.toArray(new Change[changes.size()]));
}
}
else if (key == PlatformDataKeys.DELETE_ELEMENT_PROVIDER) {
sink.put(PlatformDataKeys.DELETE_ELEMENT_PROVIDER, myDeleteProvider);
} else if (CommonDataKeys.NAVIGATABLE_ARRAY.equals(key)) {
List<ShelvedChange> shelvedChanges = new ArrayList<ShelvedChange>(TreeUtil.collectSelectedObjectsOfType(this, ShelvedChange.class));
final ArrayDeque<Navigatable> navigatables = new ArrayDeque<Navigatable>();
final List<ShelvedChangeList> changeLists = TreeUtil.collectSelectedObjectsOfType(this, ShelvedChangeList.class);
for (ShelvedChangeList changeList : changeLists) {
shelvedChanges.addAll(changeList.getChanges(myProject));
}
for (final ShelvedChange shelvedChange : shelvedChanges) {
if (shelvedChange.getBeforePath() != null && ! FileStatus.ADDED.equals(shelvedChange.getFileStatus())) {
final NavigatableAdapter navigatable = new NavigatableAdapter() {
@Override
public void navigate(boolean requestFocus) {
final VirtualFile vf = shelvedChange.getBeforeVFUnderProject(myProject);
if (vf != null) {
navigate(myProject, vf, true);
}
}
};
navigatables.add(navigatable);
}
}
sink.put(CommonDataKeys.NAVIGATABLE_ARRAY, navigatables.toArray(new Navigatable[navigatables.size()]));
}
}
private Set<ShelvedChangeList> getSelectedLists(final boolean recycled) {
final TreePath[] selections = getSelectionPaths();
final Set<ShelvedChangeList> changeLists = new HashSet<ShelvedChangeList>();
if (selections != null) {
for(TreePath path: selections) {
if (path.getPathCount() >= 2) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getPathComponent(1);
if (node.getUserObject() instanceof ShelvedChangeList) {
final ShelvedChangeList list = (ShelvedChangeList)node.getUserObject();
if (((! recycled) && (! list.isRecycled())) ||
(recycled && list.isRecycled())) {
changeLists.add(list);
}
}
}
}
}
return changeLists;
}
}
private final static class ShelvedFilePatchComparator implements Comparator<Object> {
private final static ShelvedFilePatchComparator ourInstance = new ShelvedFilePatchComparator();
public static ShelvedFilePatchComparator getInstance() {
return ourInstance;
}
public int compare(final Object o1, final Object o2) {
final String path1 = getPath(o1);
final String path2 = getPath(o2);
// case-insensitive; as in local changes
if (path1 == null) return -1;
if (path2 == null) return 1;
return path1.compareToIgnoreCase(path2);
}
private static String getPath(final Object patch) {
String path = null;
if (patch instanceof ShelvedBinaryFile) {
final ShelvedBinaryFile binaryFile = (ShelvedBinaryFile) patch;
path = binaryFile.BEFORE_PATH;
path = (path == null) ? binaryFile.AFTER_PATH : path;
} else if (patch instanceof ShelvedChange) {
final ShelvedChange shelvedChange = (ShelvedChange)patch;
path = shelvedChange.getBeforePath().replace('/', File.separatorChar);
}
if (path == null) {
return null;
}
final int pos = path.lastIndexOf(File.separatorChar);
return (pos >= 0) ? path.substring(pos + 1) : path;
}
}
private static class ShelfTreeCellRenderer extends ColoredTreeCellRenderer {
private final IssueLinkRenderer myIssueLinkRenderer;
private final Map<Couple<String>, String> myMoveRenameInfo;
public ShelfTreeCellRenderer(Project project, final Map<Couple<String>, String> moveRenameInfo) {
myMoveRenameInfo = moveRenameInfo;
myIssueLinkRenderer = new IssueLinkRenderer(project, this);
}
public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
Object nodeValue = node.getUserObject();
if (nodeValue instanceof ShelvedChangeList) {
ShelvedChangeList changeListData = (ShelvedChangeList) nodeValue;
if (changeListData.isRecycled()) {
myIssueLinkRenderer.appendTextWithLinks(changeListData.DESCRIPTION, SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
} else {
myIssueLinkRenderer.appendTextWithLinks(changeListData.DESCRIPTION);
}
final int count = node.getChildCount();
final String numFilesText = " (" + count + ((count == 1) ? " file) " : " files) ");
append(numFilesText, SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES);
final String date = DateFormatUtil.formatPrettyDateTime(changeListData.DATE);
append(" (" + date + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
setIcon(StdFileTypes.PATCH.getIcon());
}
else if (nodeValue instanceof ShelvedChange) {
ShelvedChange change = (ShelvedChange) nodeValue;
final String movedMessage = myMoveRenameInfo.get(Couple.of(change.getBeforePath(), change.getAfterPath()));
renderFileName(change.getBeforePath(), change.getFileStatus(), movedMessage);
}
else if (nodeValue instanceof ShelvedBinaryFile) {
ShelvedBinaryFile binaryFile = (ShelvedBinaryFile) nodeValue;
String path = binaryFile.BEFORE_PATH;
if (path == null) {
path = binaryFile.AFTER_PATH;
}
final String movedMessage = myMoveRenameInfo.get(Couple.of(binaryFile.BEFORE_PATH, binaryFile.AFTER_PATH));
renderFileName(path, binaryFile.getFileStatus(), movedMessage);
}
}
private void renderFileName(String path, final FileStatus fileStatus, final String movedMessage) {
path = path.replace('/', File.separatorChar);
int pos = path.lastIndexOf(File.separatorChar);
String fileName;
String directory;
if (pos >= 0) {
directory = path.substring(0, pos).replace(File.separatorChar, File.separatorChar);
fileName = path.substring(pos+1);
}
else {
directory = "<project root>";
fileName = path;
}
append(fileName, new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, fileStatus.getColor()));
if (movedMessage != null) {
append(movedMessage, SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
append(" ("+ directory + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
setIcon(FileTypeManager.getInstance().getFileTypeByFileName(fileName).getIcon());
}
}
private class MyChangeListDeleteProvider implements DeleteProvider {
public void deleteElement(@NotNull DataContext dataContext) {
//noinspection unchecked
final List<ShelvedChangeList> shelvedChangeLists = getLists(dataContext);
if (shelvedChangeLists.isEmpty()) return;
String message = (shelvedChangeLists.size() == 1)
? VcsBundle.message("shelve.changes.delete.confirm", shelvedChangeLists.get(0).DESCRIPTION)
: VcsBundle.message("shelve.changes.delete.multiple.confirm", shelvedChangeLists.size());
int rc = Messages.showOkCancelDialog(myProject, message, VcsBundle.message("shelvedChanges.delete.title"), CommonBundle.message("button.delete"), CommonBundle.getCancelButtonText(), Messages.getWarningIcon());
if (rc != Messages.OK) return;
for(ShelvedChangeList changeList: shelvedChangeLists) {
ShelveChangesManager.getInstance(myProject).deleteChangeList(changeList);
}
}
public boolean canDeleteElement(@NotNull DataContext dataContext) {
//noinspection unchecked
return ! getLists(dataContext).isEmpty();
}
private List<ShelvedChangeList> getLists(final DataContext dataContext) {
final ShelvedChangeList[] shelved = SHELVED_CHANGELIST_KEY.getData(dataContext);
final ShelvedChangeList[] recycled = SHELVED_RECYCLED_CHANGELIST_KEY.getData(dataContext);
final List<ShelvedChangeList> shelvedChangeLists = (shelved == null && recycled == null) ?
Collections.<ShelvedChangeList>emptyList() : new ArrayList<ShelvedChangeList>();
if (shelved != null) {
ContainerUtil.addAll(shelvedChangeLists, shelved);
}
if (recycled != null) {
ContainerUtil.addAll(shelvedChangeLists, recycled);
}
return shelvedChangeLists;
}
}
private class MyChangesDeleteProvider implements DeleteProvider {
public void deleteElement(@NotNull DataContext dataContext) {
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project == null) return;
final ShelvedChangeList[] shelved = SHELVED_CHANGELIST_KEY.getData(dataContext);
if (shelved == null || (shelved.length != 1)) return;
final List<ShelvedChange> changes = SHELVED_CHANGE_KEY.getData(dataContext);
final List<ShelvedBinaryFile> binaryFiles = SHELVED_BINARY_FILE_KEY.getData(dataContext);
final ShelvedChangeList list = shelved[0];
final String message = VcsBundle.message("shelve.changes.delete.files.from.list", (changes == null ? 0 : changes.size()) +
(binaryFiles == null ? 0 : binaryFiles.size()));
int rc = Messages.showOkCancelDialog(myProject, message, VcsBundle.message("shelve.changes.delete.files.from.list.title"), Messages.getWarningIcon());
if (rc != Messages.OK) return;
final ArrayList<ShelvedBinaryFile> oldBinaries = new ArrayList<ShelvedBinaryFile>(list.getBinaryFiles());
final ArrayList<ShelvedChange> oldChanges = new ArrayList<ShelvedChange>(list.getChanges(project));
oldBinaries.removeAll(binaryFiles);
oldChanges.removeAll(changes);
final CommitContext commitContext = new CommitContext();
final List<FilePatch> patches = new ArrayList<FilePatch>();
final List<VcsException> exceptions = new ArrayList<VcsException>();
for (ShelvedChange change : oldChanges) {
try {
patches.add(change.loadFilePatch(myProject, commitContext));
}
catch (IOException e) {
//noinspection ThrowableInstanceNeverThrown
exceptions.add(new VcsException(e));
}
catch (PatchSyntaxException e) {
//noinspection ThrowableInstanceNeverThrown
exceptions.add(new VcsException(e));
}
}
myShelveChangesManager.saveRemainingPatches(list, patches, oldBinaries, commitContext);
if (! exceptions.isEmpty()) {
String title = list.DESCRIPTION == null ? "" : list.DESCRIPTION;
title = title.substring(0, Math.min(10, list.DESCRIPTION.length()));
AbstractVcsHelper.getInstance(myProject).showErrors(exceptions, "Deleting files from '" + title + "'");
}
}
public boolean canDeleteElement(@NotNull DataContext dataContext) {
final ShelvedChangeList[] shelved = SHELVED_CHANGELIST_KEY.getData(dataContext);
if (shelved == null || (shelved.length != 1)) return false;
final List<ShelvedChange> changes = SHELVED_CHANGE_KEY.getData(dataContext);
if (changes != null && (! changes.isEmpty())) return true;
final List<ShelvedBinaryFile> binaryFiles = SHELVED_BINARY_FILE_KEY.getData(dataContext);
return (binaryFiles != null && (! binaryFiles.isEmpty()));
}
}
private class ShelvedChangeDeleteProvider implements DeleteProvider {
private final List<DeleteProvider> myProviders;
private ShelvedChangeDeleteProvider() {
myProviders = Arrays.asList(new MyChangesDeleteProvider(), new MyChangeListDeleteProvider());
}
@Nullable
private DeleteProvider selectDelegate(final DataContext dataContext) {
for (DeleteProvider provider : myProviders) {
if (provider.canDeleteElement(dataContext)) {
return provider;
}
}
return null;
}
public void deleteElement(@NotNull DataContext dataContext) {
final DeleteProvider delegate = selectDelegate(dataContext);
if (delegate != null) {
delegate.deleteElement(dataContext);
}
}
public boolean canDeleteElement(@NotNull DataContext dataContext) {
return selectDelegate(dataContext) != null;
}
}
}