VCS: quick search for shelf
[fedora-idea.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / shelf / ShelvedChangesViewManager.java
blobd76b9e8aff674deafa4304067b0648aff11c6e49
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Created by IntelliJ IDEA.
19 * User: yole
20 * Date: 23.11.2006
21 * Time: 15:11:11
23 package com.intellij.openapi.vcs.changes.shelf;
25 import com.intellij.ide.DataManager;
26 import com.intellij.ide.DeleteProvider;
27 import com.intellij.ide.util.treeView.TreeState;
28 import com.intellij.openapi.actionSystem.*;
29 import com.intellij.openapi.application.ApplicationManager;
30 import com.intellij.openapi.application.ModalityState;
31 import com.intellij.openapi.components.ProjectComponent;
32 import com.intellij.openapi.diff.impl.patch.FilePatch;
33 import com.intellij.openapi.diff.impl.patch.PatchSyntaxException;
34 import com.intellij.openapi.fileTypes.FileTypeManager;
35 import com.intellij.openapi.fileTypes.StdFileTypes;
36 import com.intellij.openapi.project.Project;
37 import com.intellij.openapi.ui.Messages;
38 import com.intellij.openapi.util.Pair;
39 import com.intellij.openapi.vcs.*;
40 import com.intellij.openapi.vcs.changes.Change;
41 import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer;
42 import com.intellij.openapi.vcs.changes.issueLinks.TreeLinkMouseListener;
43 import com.intellij.openapi.vcs.changes.patch.RelativePathCalculator;
44 import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager;
45 import com.intellij.openapi.wm.ToolWindow;
46 import com.intellij.openapi.wm.ToolWindowManager;
47 import com.intellij.ui.ColoredTreeCellRenderer;
48 import com.intellij.ui.PopupHandler;
49 import com.intellij.ui.SimpleTextAttributes;
50 import com.intellij.ui.TreeSpeedSearch;
51 import com.intellij.ui.content.Content;
52 import com.intellij.ui.content.ContentFactory;
53 import com.intellij.ui.treeStructure.Tree;
54 import com.intellij.util.containers.Convertor;
55 import com.intellij.util.messages.MessageBus;
56 import com.intellij.util.ui.tree.TreeUtil;
57 import org.jetbrains.annotations.NonNls;
58 import org.jetbrains.annotations.NotNull;
59 import org.jetbrains.annotations.Nullable;
61 import javax.swing.*;
62 import javax.swing.event.ChangeEvent;
63 import javax.swing.event.ChangeListener;
64 import javax.swing.tree.DefaultMutableTreeNode;
65 import javax.swing.tree.DefaultTreeModel;
66 import javax.swing.tree.TreeModel;
67 import javax.swing.tree.TreePath;
68 import java.awt.event.MouseAdapter;
69 import java.awt.event.MouseEvent;
70 import java.io.File;
71 import java.io.IOException;
72 import java.text.SimpleDateFormat;
73 import java.util.*;
75 public class ShelvedChangesViewManager implements ProjectComponent {
76 private final ChangesViewContentManager myContentManager;
77 private final ShelveChangesManager myShelveChangesManager;
78 private final Project myProject;
79 private final Tree myTree = new ShelfTree();
80 private Content myContent = null;
81 private final ShelvedChangeDeleteProvider myDeleteProvider = new ShelvedChangeDeleteProvider();
82 private boolean myUpdatePending = false;
83 private Runnable myPostUpdateRunnable = null;
85 public static DataKey<ShelvedChangeList[]> SHELVED_CHANGELIST_KEY = DataKey.create("ShelveChangesManager.ShelvedChangeListData");
86 public static DataKey<ShelvedChangeList[]> SHELVED_RECYCLED_CHANGELIST_KEY = DataKey.create("ShelveChangesManager.ShelvedRecycledChangeListData");
87 public static DataKey<List<ShelvedChange>> SHELVED_CHANGE_KEY = DataKey.create("ShelveChangesManager.ShelvedChange");
88 public static DataKey<List<ShelvedBinaryFile>> SHELVED_BINARY_FILE_KEY = DataKey.create("ShelveChangesManager.ShelvedBinaryFile");
89 private static final Object ROOT_NODE_VALUE = new Object();
90 private DefaultMutableTreeNode myRoot;
91 private final Map<Pair<String, String>, String> myMoveRenameInfo;
93 public static ShelvedChangesViewManager getInstance(Project project) {
94 return project.getComponent(ShelvedChangesViewManager.class);
97 public ShelvedChangesViewManager(Project project, ChangesViewContentManager contentManager, ShelveChangesManager shelveChangesManager,
98 final MessageBus bus) {
99 myProject = project;
100 myContentManager = contentManager;
101 myShelveChangesManager = shelveChangesManager;
102 bus.connect().subscribe(ShelveChangesManager.SHELF_TOPIC, new ChangeListener() {
103 public void stateChanged(ChangeEvent e) {
104 myUpdatePending = true;
105 ApplicationManager.getApplication().invokeLater(new Runnable() {
106 public void run() {
107 updateChangesContent();
109 }, ModalityState.NON_MODAL);
112 myMoveRenameInfo = new HashMap<Pair<String, String>, String>();
114 myTree.setRootVisible(false);
115 myTree.setShowsRootHandles(true);
116 myTree.setCellRenderer(new ShelfTreeCellRenderer(project, myMoveRenameInfo));
117 new TreeLinkMouseListener(new ShelfTreeCellRenderer(project, myMoveRenameInfo)).install(myTree);
119 final AnAction showDiffAction = ActionManager.getInstance().getAction("ShelvedChanges.Diff");
120 showDiffAction.registerCustomShortcutSet(CommonShortcuts.getDiff(), myTree);
122 PopupHandler.installPopupHandler(myTree, "ShelvedChangesPopupMenu", ActionPlaces.UNKNOWN);
124 myTree.addMouseListener(new MouseAdapter() {
125 public void mouseClicked(final MouseEvent e) {
126 if (e.getClickCount() != 2) return;
128 DiffShelvedChangesAction.showShelvedChangesDiff(DataManager.getInstance().getDataContext(myTree));
131 new TreeSpeedSearch(myTree, new Convertor<TreePath, String>() {
132 public String convert(TreePath o) {
133 final Object lc = o.getLastPathComponent();
134 final Object lastComponent = lc == null ? null : ((DefaultMutableTreeNode) lc).getUserObject();
135 if (lastComponent instanceof ShelvedChangeList) {
136 return ((ShelvedChangeList) lastComponent).DESCRIPTION;
137 } else if (lastComponent instanceof ShelvedChange) {
138 final ShelvedChange shelvedChange = (ShelvedChange)lastComponent;
139 return shelvedChange.getBeforeFileName() == null ? shelvedChange.getAfterFileName() : shelvedChange.getBeforeFileName();
140 } else if (lastComponent instanceof ShelvedBinaryFile) {
141 final ShelvedBinaryFile sbf = (ShelvedBinaryFile) lastComponent;
142 final String value = sbf.BEFORE_PATH == null ? sbf.AFTER_PATH : sbf.BEFORE_PATH;
143 int idx = value.lastIndexOf("/");
144 idx = (idx == -1) ? value.lastIndexOf("\\") : idx;
145 return idx > 0 ? value.substring(idx + 1) : value;
147 return null;
149 }, true);
152 public void projectOpened() {
153 updateChangesContent();
156 public void projectClosed() {
159 @NonNls @NotNull
160 public String getComponentName() {
161 return "ShelvedChangesViewManager";
164 public void initComponent() {
167 public void disposeComponent() {
170 private void updateChangesContent() {
171 myUpdatePending = false;
172 final List<ShelvedChangeList> changeLists = new ArrayList<ShelvedChangeList>(myShelveChangesManager.getShelvedChangeLists());
173 changeLists.addAll(myShelveChangesManager.getRecycledShelvedChangeLists());
174 if (changeLists.size() == 0) {
175 if (myContent != null) {
176 myContentManager.removeContent(myContent);
177 myContentManager.selectContent("Local");
179 myContent = null;
181 else {
182 if (myContent == null) {
183 myContent = ContentFactory.SERVICE.getInstance().createContent(new JScrollPane(myTree), VcsBundle.message("shelf.tab"), false);
184 myContent.setCloseable(false);
185 myContentManager.addContent(myContent);
187 TreeState state = TreeState.createOn(myTree);
188 myTree.setModel(buildChangesModel());
189 state.applyTo(myTree);
190 if (myPostUpdateRunnable != null) {
191 myPostUpdateRunnable.run();
194 myPostUpdateRunnable = null;
197 private TreeModel buildChangesModel() {
198 myRoot = new DefaultMutableTreeNode(ROOT_NODE_VALUE); // not null for TreeState matching to work
199 DefaultTreeModel model = new DefaultTreeModel(myRoot);
200 final List<ShelvedChangeList> changeLists = new ArrayList<ShelvedChangeList>(myShelveChangesManager.getShelvedChangeLists());
201 if (myShelveChangesManager.isShowRecycled()) {
202 changeLists.addAll(myShelveChangesManager.getRecycledShelvedChangeLists());
204 myMoveRenameInfo.clear();
206 for(ShelvedChangeList changeList: changeLists) {
207 DefaultMutableTreeNode node = new DefaultMutableTreeNode(changeList);
208 model.insertNodeInto(node, myRoot, myRoot.getChildCount());
210 final List<Object> shelvedFilesNodes = new ArrayList<Object>();
211 List<ShelvedChange> changes = changeList.getChanges();
212 for(ShelvedChange change: changes) {
213 putMovedMessage(change.getBeforePath(), change.getAfterPath());
214 shelvedFilesNodes.add(change);
216 List<ShelvedBinaryFile> binaryFiles = changeList.getBinaryFiles();
217 for(ShelvedBinaryFile file: binaryFiles) {
218 putMovedMessage(file.BEFORE_PATH, file.AFTER_PATH);
219 shelvedFilesNodes.add(file);
221 Collections.sort(shelvedFilesNodes, ShelvedFilePatchComparator.getInstance());
222 for (int i = 0; i < shelvedFilesNodes.size(); i++) {
223 final Object filesNode = shelvedFilesNodes.get(i);
224 final DefaultMutableTreeNode pathNode = new DefaultMutableTreeNode(filesNode);
225 model.insertNodeInto(pathNode, node, i);
228 return model;
231 private void putMovedMessage(final String beforeName, final String afterName) {
232 final String movedMessage = RelativePathCalculator.getMovedString(beforeName, afterName);
233 if (movedMessage != null) {
234 myMoveRenameInfo.put(new Pair<String, String>(beforeName, afterName), movedMessage);
238 public void activateView(final ShelvedChangeList list) {
239 Runnable runnable = new Runnable() {
240 public void run() {
241 if (list != null) {
242 TreeUtil.selectNode(myTree, TreeUtil.findNodeWithObject(myRoot, list));
244 myContentManager.setSelectedContent(myContent);
245 ToolWindow window = ToolWindowManager.getInstance(myProject).getToolWindow(ChangesViewContentManager.TOOLWINDOW_ID);
246 if (!window.isVisible()) {
247 window.activate(null);
251 if (myUpdatePending) {
252 myPostUpdateRunnable = runnable;
254 else {
255 runnable.run();
259 private class ShelfTree extends Tree implements TypeSafeDataProvider {
260 public void calcData(DataKey key, DataSink sink) {
261 if (key == SHELVED_CHANGELIST_KEY) {
262 final Set<ShelvedChangeList> changeLists = getSelectedLists(false);
264 if (changeLists.size() > 0) {
265 sink.put(SHELVED_CHANGELIST_KEY, changeLists.toArray(new ShelvedChangeList[changeLists.size()]));
268 else if (key == SHELVED_RECYCLED_CHANGELIST_KEY) {
269 final Set<ShelvedChangeList> changeLists = getSelectedLists(true);
271 if (changeLists.size() > 0) {
272 sink.put(SHELVED_RECYCLED_CHANGELIST_KEY, changeLists.toArray(new ShelvedChangeList[changeLists.size()]));
275 else if (key == SHELVED_CHANGE_KEY) {
276 sink.put(SHELVED_CHANGE_KEY, TreeUtil.collectSelectedObjectsOfType(this, ShelvedChange.class));
278 else if (key == SHELVED_BINARY_FILE_KEY) {
279 sink.put(SHELVED_BINARY_FILE_KEY, TreeUtil.collectSelectedObjectsOfType(this, ShelvedBinaryFile.class));
281 else if (key == VcsDataKeys.CHANGES) {
282 List<ShelvedChange> shelvedChanges = TreeUtil.collectSelectedObjectsOfType(this, ShelvedChange.class);
283 if (shelvedChanges.size() > 0) {
284 Change[] changes = new Change[shelvedChanges.size()];
285 for(int i=0; i<shelvedChanges.size(); i++) {
286 changes [i] = shelvedChanges.get(i).getChange(myProject);
288 sink.put(VcsDataKeys.CHANGES, changes);
290 else {
291 final List<ShelvedChangeList> changeLists = TreeUtil.collectSelectedObjectsOfType(this, ShelvedChangeList.class);
292 if (changeLists.size() > 0) {
293 List<Change> changes = new ArrayList<Change>();
294 for(ShelvedChangeList changeList: changeLists) {
295 shelvedChanges = changeList.getChanges();
296 for(ShelvedChange shelvedChange: shelvedChanges) {
297 changes.add(shelvedChange.getChange(myProject));
300 sink.put(VcsDataKeys.CHANGES, changes.toArray(new Change[changes.size()]));
304 else if (key == PlatformDataKeys.DELETE_ELEMENT_PROVIDER) {
305 sink.put(PlatformDataKeys.DELETE_ELEMENT_PROVIDER, myDeleteProvider);
309 private Set<ShelvedChangeList> getSelectedLists(final boolean recycled) {
310 final TreePath[] selections = getSelectionPaths();
311 final Set<ShelvedChangeList> changeLists = new HashSet<ShelvedChangeList>();
312 if (selections != null) {
313 for(TreePath path: selections) {
314 if (path.getPathCount() >= 2) {
315 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getPathComponent(1);
316 if (node.getUserObject() instanceof ShelvedChangeList) {
317 final ShelvedChangeList list = (ShelvedChangeList)node.getUserObject();
318 if (((! recycled) && (! list.isRecycled())) ||
319 (recycled && list.isRecycled())) {
320 changeLists.add(list);
326 return changeLists;
330 private final static class ShelvedFilePatchComparator implements Comparator<Object> {
331 private final static ShelvedFilePatchComparator ourInstance = new ShelvedFilePatchComparator();
333 public static ShelvedFilePatchComparator getInstance() {
334 return ourInstance;
337 public int compare(final Object o1, final Object o2) {
338 final String path1 = getPath(o1);
339 final String path2 = getPath(o2);
340 // case-insensitive; as in local changes
341 if (path1 == null) return -1;
342 if (path2 == null) return 1;
343 return path1.compareToIgnoreCase(path2);
346 private static String getPath(final Object patch) {
347 String path = null;
348 if (patch instanceof ShelvedBinaryFile) {
349 final ShelvedBinaryFile binaryFile = (ShelvedBinaryFile) patch;
350 path = binaryFile.BEFORE_PATH;
351 path = (path == null) ? binaryFile.AFTER_PATH : path;
352 } else if (patch instanceof ShelvedChange) {
353 final ShelvedChange shelvedChange = (ShelvedChange)patch;
354 path = shelvedChange.getBeforePath().replace('/', File.separatorChar);
356 if (path == null) {
357 return null;
359 final int pos = path.lastIndexOf(File.separatorChar);
360 return (pos >= 0) ? path.substring(pos + 1) : path;
364 private static class ShelfTreeCellRenderer extends ColoredTreeCellRenderer {
365 private final IssueLinkRenderer myIssueLinkRenderer;
366 private final Map<Pair<String, String>, String> myMoveRenameInfo;
368 public ShelfTreeCellRenderer(Project project, final Map<Pair<String, String>, String> moveRenameInfo) {
369 myMoveRenameInfo = moveRenameInfo;
370 myIssueLinkRenderer = new IssueLinkRenderer(project, this);
373 public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
374 DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
375 Object nodeValue = node.getUserObject();
376 if (nodeValue instanceof ShelvedChangeList) {
377 ShelvedChangeList changeListData = (ShelvedChangeList) nodeValue;
378 if (changeListData.isRecycled()) {
379 myIssueLinkRenderer.appendTextWithLinks(changeListData.DESCRIPTION, SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
380 } else {
381 myIssueLinkRenderer.appendTextWithLinks(changeListData.DESCRIPTION);
383 final int count = node.getChildCount();
384 final String numFilesText = " (" + count + ((count == 1) ? " file) " : " files) ");
385 append(numFilesText, SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES);
387 final String date = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT).format(changeListData.DATE);
388 append(" (" + date + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
389 setIcon(StdFileTypes.PATCH.getIcon());
391 else if (nodeValue instanceof ShelvedChange) {
392 ShelvedChange change = (ShelvedChange) nodeValue;
393 final String movedMessage = myMoveRenameInfo.get(new Pair<String, String>(change.getBeforePath(), change.getAfterPath()));
394 renderFileName(change.getBeforePath(), change.getFileStatus(), movedMessage);
396 else if (nodeValue instanceof ShelvedBinaryFile) {
397 ShelvedBinaryFile binaryFile = (ShelvedBinaryFile) nodeValue;
398 String path = binaryFile.BEFORE_PATH;
399 if (path == null) {
400 path = binaryFile.AFTER_PATH;
402 final String movedMessage = myMoveRenameInfo.get(new Pair<String, String>(binaryFile.BEFORE_PATH, binaryFile.AFTER_PATH));
403 renderFileName(path, binaryFile.getFileStatus(), movedMessage);
407 private void renderFileName(String path, final FileStatus fileStatus, final String movedMessage) {
408 path = path.replace('/', File.separatorChar);
409 int pos = path.lastIndexOf(File.separatorChar);
410 String fileName;
411 String directory;
412 if (pos >= 0) {
413 directory = path.substring(0, pos).replace(File.separatorChar, File.separatorChar);
414 fileName = path.substring(pos+1);
416 else {
417 directory = "<project root>";
418 fileName = path;
420 append(fileName, new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, fileStatus.getColor()));
421 if (movedMessage != null) {
422 append(movedMessage, SimpleTextAttributes.REGULAR_ATTRIBUTES);
424 append(" ("+ directory + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
425 setIcon(FileTypeManager.getInstance().getFileTypeByFileName(fileName).getIcon());
429 private class MyChangeListDeleteProvider implements DeleteProvider {
430 public void deleteElement(DataContext dataContext) {
431 //noinspection unchecked
432 final List<ShelvedChangeList> shelvedChangeLists = getLists(dataContext);
433 if (shelvedChangeLists.isEmpty()) return;
434 String message = (shelvedChangeLists.size() == 1)
435 ? VcsBundle.message("shelve.changes.delete.confirm", shelvedChangeLists.get(0).DESCRIPTION)
436 : VcsBundle.message("shelve.changes.delete.multiple.confirm", shelvedChangeLists.size());
437 int rc = Messages.showOkCancelDialog(myProject, message, VcsBundle.message("shelvedChanges.delete.title"), Messages.getWarningIcon());
438 if (rc != 0) return;
439 for(ShelvedChangeList changeList: shelvedChangeLists) {
440 ShelveChangesManager.getInstance(myProject).deleteChangeList(changeList);
444 public boolean canDeleteElement(DataContext dataContext) {
445 //noinspection unchecked
446 return ! getLists(dataContext).isEmpty();
449 private List<ShelvedChangeList> getLists(final DataContext dataContext) {
450 final ShelvedChangeList[] shelved = SHELVED_CHANGELIST_KEY.getData(dataContext);
451 final ShelvedChangeList[] recycled = SHELVED_RECYCLED_CHANGELIST_KEY.getData(dataContext);
453 final List<ShelvedChangeList> shelvedChangeLists = (shelved == null && recycled == null) ?
454 Collections.<ShelvedChangeList>emptyList() : new ArrayList<ShelvedChangeList>();
455 if (shelved != null) {
456 shelvedChangeLists.addAll(Arrays.asList(shelved));
458 if (recycled != null) {
459 shelvedChangeLists.addAll(Arrays.asList(recycled));
461 return shelvedChangeLists;
465 private class MyChangesDeleteProvider implements DeleteProvider {
466 public void deleteElement(DataContext dataContext) {
467 final ShelvedChangeList[] shelved = SHELVED_CHANGELIST_KEY.getData(dataContext);
468 if (shelved == null || (shelved.length != 1)) return;
469 final List<ShelvedChange> changes = SHELVED_CHANGE_KEY.getData(dataContext);
470 final List<ShelvedBinaryFile> binaryFiles = SHELVED_BINARY_FILE_KEY.getData(dataContext);
472 final ShelvedChangeList list = shelved[0];
474 final String message = VcsBundle.message("shelve.changes.delete.files.from.list", (changes == null ? 0 : changes.size()) +
475 (binaryFiles == null ? 0 : binaryFiles.size()));
476 int rc = Messages.showOkCancelDialog(myProject, message, VcsBundle.message("shelve.changes.delete.files.from.list.title"), Messages.getWarningIcon());
477 if (rc != 0) return;
479 final ArrayList<ShelvedBinaryFile> oldBinaries = new ArrayList<ShelvedBinaryFile>(list.getBinaryFiles());
480 final ArrayList<ShelvedChange> oldChanges = new ArrayList<ShelvedChange>(list.getChanges());
482 oldBinaries.removeAll(binaryFiles);
483 oldChanges.removeAll(changes);
485 final List<FilePatch> patches = new ArrayList<FilePatch>();
486 final List<VcsException> exceptions = new ArrayList<VcsException>();
487 for (ShelvedChange change : oldChanges) {
488 try {
489 patches.add(change.loadFilePatch());
491 catch (IOException e) {
492 //noinspection ThrowableInstanceNeverThrown
493 exceptions.add(new VcsException(e));
495 catch (PatchSyntaxException e) {
496 //noinspection ThrowableInstanceNeverThrown
497 exceptions.add(new VcsException(e));
501 myShelveChangesManager.saveRemainingPatches(list, patches, oldBinaries);
503 if (! exceptions.isEmpty()) {
504 String title = list.DESCRIPTION == null ? "" : list.DESCRIPTION;
505 title = title.substring(0, Math.min(10, list.DESCRIPTION.length()));
506 AbstractVcsHelper.getInstance(myProject).showErrors(exceptions, "Deleting files from '" + title + "'");
510 public boolean canDeleteElement(DataContext dataContext) {
511 final ShelvedChangeList[] shelved = SHELVED_CHANGELIST_KEY.getData(dataContext);
512 if (shelved == null || (shelved.length != 1)) return false;
513 final List<ShelvedChange> changes = SHELVED_CHANGE_KEY.getData(dataContext);
514 if (changes != null && (! changes.isEmpty())) return true;
515 final List<ShelvedBinaryFile> binaryFiles = SHELVED_BINARY_FILE_KEY.getData(dataContext);
516 return (binaryFiles != null && (! binaryFiles.isEmpty()));
520 private class ShelvedChangeDeleteProvider implements DeleteProvider {
521 private final List<DeleteProvider> myProviders;
523 private ShelvedChangeDeleteProvider() {
524 myProviders = Arrays.asList(new MyChangesDeleteProvider(), new MyChangeListDeleteProvider());
527 @Nullable
528 private DeleteProvider selectDelegate(final DataContext dataContext) {
529 for (DeleteProvider provider : myProviders) {
530 if (provider.canDeleteElement(dataContext)) {
531 return provider;
534 return null;
537 public void deleteElement(DataContext dataContext) {
538 final DeleteProvider delegate = selectDelegate(dataContext);
539 if (delegate != null) {
540 delegate.deleteElement(dataContext);
544 public boolean canDeleteElement(DataContext dataContext) {
545 return selectDelegate(dataContext) != null;