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.
16 package com.intellij.openapi.vcs.changes.patch;
18 import com.intellij.ide.util.PropertiesComponent;
19 import com.intellij.openapi.actionSystem.*;
20 import com.intellij.openapi.diff.impl.patch.*;
21 import com.intellij.openapi.fileChooser.FileChooser;
22 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
23 import com.intellij.openapi.fileChooser.FileChooserDialog;
24 import com.intellij.openapi.fileChooser.FileChooserFactory;
25 import com.intellij.openapi.fileTypes.FileTypes;
26 import com.intellij.openapi.fileTypes.StdFileTypes;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.openapi.ui.DialogWrapper;
29 import com.intellij.openapi.ui.TextFieldWithBrowseButton;
30 import com.intellij.openapi.ui.popup.JBPopupFactory;
31 import com.intellij.openapi.ui.popup.PopupStep;
32 import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
33 import com.intellij.openapi.util.IconLoader;
34 import com.intellij.openapi.util.Pair;
35 import com.intellij.openapi.util.text.StringUtil;
36 import com.intellij.openapi.vcs.ObjectsConvertor;
37 import com.intellij.openapi.vcs.VcsBundle;
38 import com.intellij.openapi.vcs.ZipperUpdater;
39 import com.intellij.openapi.vcs.changes.Change;
40 import com.intellij.openapi.vcs.changes.ChangeListManager;
41 import com.intellij.openapi.vcs.changes.LocalChangeList;
42 import com.intellij.openapi.vcs.changes.actions.DiffRequestPresentable;
43 import com.intellij.openapi.vcs.changes.actions.ShowDiffAction;
44 import com.intellij.openapi.vcs.changes.actions.ShowDiffUIContext;
45 import com.intellij.openapi.vcs.changes.ui.*;
46 import com.intellij.openapi.vfs.LocalFileSystem;
47 import com.intellij.openapi.vfs.VirtualFile;
48 import com.intellij.ui.DocumentAdapter;
49 import com.intellij.ui.SimpleColoredComponent;
50 import com.intellij.ui.SimpleTextAttributes;
51 import com.intellij.util.Consumer;
52 import com.intellij.util.containers.Convertor;
53 import org.jetbrains.annotations.NonNls;
54 import org.jetbrains.annotations.NotNull;
55 import org.jetbrains.annotations.Nullable;
58 import javax.swing.event.DocumentEvent;
59 import javax.swing.tree.DefaultTreeModel;
62 import java.io.IOException;
64 import java.util.List;
65 import java.util.concurrent.atomic.AtomicReference;
67 public class ApplyPatchDifferentiatedDialog extends DialogWrapper {
68 private final ZipperUpdater myLoadQueue;
69 private final TextFieldWithBrowseButton myPatchFile;
71 private final List<FilePatchInProgress> myPatches;
72 private final MyChangeTreeList myChangesTreeList;
74 private JComponent myCenterPanel;
75 private JComponent mySouthPanel;
76 private final Project myProject;
78 private final AtomicReference<FilePresentation> myRecentPathFileChange;
79 private final ApplyPatchDifferentiatedDialog.MyUpdater myUpdater;
80 private final Runnable myReset;
81 private final ChangeListChooserPanel myChangeListChooser;
82 private final ChangesLegendCalculator myInfoCalculator;
83 private final CommitLegendPanel myCommitLegendPanel;
84 private final Consumer<ApplyPatchDifferentiatedDialog> myCallback;
86 private boolean myContainBasedChanges;
88 public ApplyPatchDifferentiatedDialog(final Project project, final Consumer<ApplyPatchDifferentiatedDialog> callback,
89 @Nullable final VirtualFile patchFile) {
91 myCallback = callback;
93 setTitle(VcsBundle.message("patch.apply.dialog.title"));
95 final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) {
97 public boolean isFileSelectable(VirtualFile file) {
98 return file.getFileType() == StdFileTypes.PATCH || file.getFileType() == FileTypes.PLAIN_TEXT;
101 descriptor.setTitle(VcsBundle.message("patch.apply.select.title"));
102 myUpdater = new MyUpdater();
103 myPatchFile = new TextFieldWithBrowseButton();
104 myPatchFile.addBrowseFolderListener(VcsBundle.message("patch.apply.select.title"), "", project, descriptor);
105 myPatchFile.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
106 protected void textChanged(DocumentEvent e) {
107 setPathFileChangeDefault();
108 myLoadQueue.queue(myUpdater);
113 myLoadQueue = new ZipperUpdater(500);
114 myPatches = new LinkedList<FilePatchInProgress>();
115 myRecentPathFileChange = new AtomicReference<FilePresentation>();
116 myChangesTreeList = new MyChangeTreeList(project, Collections.<FilePatchInProgress.PatchChange>emptyList(),
119 final NamedTrinity includedTrinity = new NamedTrinity();
120 final Collection<FilePatchInProgress.PatchChange> includedChanges = myChangesTreeList.getIncludedChanges();
121 final Set<Pair<String, String>> set = new HashSet<Pair<String, String>>();
122 for (FilePatchInProgress.PatchChange change : includedChanges) {
123 final TextFilePatch patch = change.getPatchInProgress().getPatch();
124 final Pair<String, String> pair = new Pair<String, String>(patch.getBeforeName(), patch.getAfterName());
125 if (set.contains(pair)) continue;
127 acceptChange(includedTrinity, change);
129 myInfoCalculator.setIncluded(includedTrinity);
130 myCommitLegendPanel.update();
132 }, new MyChangeNodeDecorator());
133 myReset = new Runnable() {
139 myChangeListChooser = new ChangeListChooserPanel(project, new Consumer<String>() {
140 public void consume(final String errorMessage) {
141 setOKActionEnabled(errorMessage == null);
142 setErrorText(errorMessage);
145 ChangeListManager changeListManager = ChangeListManager.getInstance(project);
146 myChangeListChooser.setChangeLists(changeListManager.getChangeListsCopy());
147 myChangeListChooser.setDefaultSelection(changeListManager.getDefaultChangeList());
148 myChangeListChooser.init();
150 myInfoCalculator = new ChangesLegendCalculator();
151 myCommitLegendPanel = new CommitLegendPanel(myInfoCalculator);
155 if (patchFile != null && patchFile.isValid()) {
158 final FileChooserDialog fileChooserDialog = FileChooserFactory.getInstance().createFileChooser(descriptor, project);
159 final VirtualFile[] files = fileChooserDialog.choose(null, project);
160 if (files != null && files.length > 0) {
168 protected String getDimensionServiceKey() {
169 return "vcs.ApplyPatchDifferentiatedDialog";
173 protected String getHelpId() {
174 return "reference.dialogs.vcs.patch.apply";
177 private void setPathFileChangeDefault() {
178 myRecentPathFileChange.set(new FilePresentation(myPatchFile.getText()));
181 public void init(final VirtualFile patchFile) {
182 myPatchFile.setText(patchFile.getPath());
183 myRecentPathFileChange.set(new FilePresentation(patchFile));
184 myLoadQueue.queue(myUpdater);
187 private class MyUpdater implements Runnable {
189 final FilePresentation filePresentation = myRecentPathFileChange.get();
190 if ((filePresentation == null) || (filePresentation.getVf() == null)) {
191 SwingUtilities.invokeLater(myReset);
194 final VirtualFile file = filePresentation.getVf();
196 final List<TextFilePatch> patches = loadPatches(file);
197 final AutoMatchIterator autoMatchIterator = new AutoMatchIterator(myProject);
198 final List<FilePatchInProgress> matchedPathes = autoMatchIterator.execute(patches);
200 SwingUtilities.invokeLater(new Runnable() {
202 myChangeListChooser.setDefaultName(file.getNameWithoutExtension().replace('_', ' ').trim());
204 myPatches.addAll(matchedPathes);
212 private List<TextFilePatch> loadPatches(final VirtualFile patchFile) {
213 if (! patchFile.isValid()) {
215 //queueUpdateStatus("Cannot find patch file");
216 return Collections.emptyList();
220 reader = PatchVirtualFileReader.create(patchFile);
222 catch (IOException e) {
224 //queueUpdateStatus(VcsBundle.message("patch.apply.open.error", e.getMessage()));
225 return Collections.emptyList();
227 final List<TextFilePatch> result = new LinkedList<TextFilePatch>();
231 patch = reader.readNextPatch();
233 catch (PatchSyntaxException e) {
235 if (e.getLine() >= 0) {
236 //queueUpdateStatus(VcsBundle.message("patch.apply.load.error.line", e.getMessage(), e.getLine()));
239 //queueUpdateStatus(VcsBundle.message("patch.apply.load.error", e.getMessage()));
241 return Collections.emptyList();
247 result.add((TextFilePatch) patch);
249 if (myPatches.isEmpty()) {
251 //queueUpdateStatus(VcsBundle.message("patch.apply.no.patches.found"));
256 private static class FilePresentation {
257 private final VirtualFile myVf;
258 private final String myPath;
260 private FilePresentation(VirtualFile vf) {
265 private FilePresentation(String path) {
271 public VirtualFile getVf() {
275 final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(myPath);
276 return (file != null) && (! file.isDirectory()) ? file : null;
280 public void reset() {
282 myChangesTreeList.setChangesToDisplay(Collections.<FilePatchInProgress.PatchChange>emptyList());
283 myChangesTreeList.repaint();
284 myContainBasedChanges = false;
288 protected JComponent createCenterPanel() {
289 if (myCenterPanel == null) {
290 myCenterPanel = new JPanel(new GridBagLayout());
291 final GridBagConstraints gb =
292 new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(1, 1, 1, 1), 0, 0);
294 final JLabel label = new JLabel(VcsBundle.message("create.patch.file.name.field"));
295 label.setLabelFor(myPatchFile);
296 myCenterPanel.add(label, gb);
299 gb.fill = GridBagConstraints.HORIZONTAL;
301 myCenterPanel.add(myPatchFile, gb);
307 gb.fill = GridBagConstraints.HORIZONTAL;
310 final DefaultActionGroup group = new DefaultActionGroup();
311 final AnAction[] treeActions = myChangesTreeList.getTreeActions();
312 group.addAll(treeActions);
313 group.add(new MapDirectory());
315 final MyShowDiff diffAction = new MyShowDiff();
316 diffAction.registerCustomShortcutSet(CommonShortcuts.getDiff(), getRootPane());
317 group.add(diffAction);
319 group.add(new StripUp());
320 group.add(new StripDown());
321 group.add(new ResetStrip());
322 group.add(new ZeroStrip());
324 final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("APPLY_PATCH", group, true);
325 myCenterPanel.add(toolbar.getComponent(), gb);
331 gb.fill = GridBagConstraints.BOTH;
332 myCenterPanel.add(myChangesTreeList, gb);
334 final JPanel wrapper = new JPanel(new GridBagLayout());
335 final GridBagConstraints gb1 =
336 new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(1, 1, 1, 1), 0, 0);
337 wrapper.add(myChangeListChooser, gb1);
339 gb1.fill = GridBagConstraints.NONE;
341 gb1.insets.left = 10;
342 wrapper.add(myCommitLegendPanel.getComponent(), gb1);
348 gb.fill = GridBagConstraints.HORIZONTAL;
349 myCenterPanel.add(wrapper, gb);
351 return myCenterPanel;
354 private static class MyChangeTreeList extends ChangesTreeList<FilePatchInProgress.PatchChange> {
355 private MyChangeTreeList(Project project,
356 Collection<FilePatchInProgress.PatchChange> initiallyIncluded,
357 @Nullable Runnable inclusionListener,
358 @Nullable ChangeNodeDecorator decorator) {
359 super(project, initiallyIncluded, true, false, inclusionListener, decorator);
363 protected DefaultTreeModel buildTreeModel(List<FilePatchInProgress.PatchChange> changes, ChangeNodeDecorator changeNodeDecorator) {
364 TreeModelBuilder builder = new TreeModelBuilder(myProject, false);
365 return builder.buildModel(ObjectsConvertor.convert(changes,
366 new Convertor<FilePatchInProgress.PatchChange, Change>() {
367 public Change convert(FilePatchInProgress.PatchChange o) {
370 }), changeNodeDecorator);
374 protected List<FilePatchInProgress.PatchChange> getSelectedObjects(ChangesBrowserNode<FilePatchInProgress.PatchChange> node) {
375 final List<Change> under = node.getAllChangesUnder();
376 return ObjectsConvertor.convert(under, new Convertor<Change, FilePatchInProgress.PatchChange>() {
377 public FilePatchInProgress.PatchChange convert(Change o) {
378 return (FilePatchInProgress.PatchChange) o;
384 protected FilePatchInProgress.PatchChange getLeadSelectedObject(ChangesBrowserNode node) {
385 final Object o = node.getUserObject();
386 if (o instanceof FilePatchInProgress.PatchChange) {
387 return (FilePatchInProgress.PatchChange) o;
393 private class MapDirectory extends AnAction {
394 private final NewBaseSelector myNewBaseSelector;
396 private MapDirectory() {
397 super("Map base directory", "Map base directory", IconLoader.getIcon("/vcs/mapBase.png"));
398 myNewBaseSelector = new NewBaseSelector();
402 public void actionPerformed(AnActionEvent e) {
403 final List<FilePatchInProgress.PatchChange> selectedChanges = myChangesTreeList.getSelectedChanges();
404 if ((selectedChanges.size() >= 1) && (sameBase(selectedChanges))) {
405 final FilePatchInProgress.PatchChange patchChange = selectedChanges.get(0);
406 final FilePatchInProgress patch = patchChange.getPatchInProgress();
407 final List<VirtualFile> autoBases = patch.getAutoBasesCopy();
408 if (autoBases.isEmpty() || (autoBases.size() == 1 && autoBases.get(0).equals(patch.getBase()))) {
409 myNewBaseSelector.run();
412 final MapPopup step = new MapPopup(autoBases, myNewBaseSelector);
413 JBPopupFactory.getInstance().createListPopup(step).showCenteredInCurrentWindow(myProject);
419 public void update(AnActionEvent e) {
420 final List<FilePatchInProgress.PatchChange> selectedChanges = myChangesTreeList.getSelectedChanges();
421 e.getPresentation().setEnabled((selectedChanges.size() >= 1) && (sameBase(selectedChanges)));
425 private boolean sameBase(final List<FilePatchInProgress.PatchChange> selectedChanges) {
426 VirtualFile base = null;
427 for (FilePatchInProgress.PatchChange change : selectedChanges) {
428 final VirtualFile changeBase = change.getPatchInProgress().getBase();
431 } else if (! base.equals(changeBase)) {
438 private void updateTree(boolean doInitCheck) {
439 final List<FilePatchInProgress> patchesToSelect = changes2patches(myChangesTreeList.getSelectedChanges());
440 final List<FilePatchInProgress.PatchChange> changes = getAllChanges();
441 final Collection<FilePatchInProgress.PatchChange> included = getIncluded(doInitCheck, changes);
443 myChangesTreeList.setChangesToDisplay(changes);
444 myChangesTreeList.setIncludedChanges(included);
445 myChangesTreeList.repaint();
446 if ((! doInitCheck) && patchesToSelect != null) {
447 final List<FilePatchInProgress.PatchChange> toSelect = new ArrayList<FilePatchInProgress.PatchChange>(patchesToSelect.size());
448 for (FilePatchInProgress.PatchChange change : changes) {
449 if (patchesToSelect.contains(change.getPatchInProgress())) {
450 toSelect.add(change);
453 myChangesTreeList.select(toSelect);
456 myContainBasedChanges = false;
457 for (FilePatchInProgress patch : myPatches) {
458 if (patch.baseExistsOrAdded()) {
459 myContainBasedChanges = true;
465 private List<FilePatchInProgress.PatchChange> getAllChanges() {
466 return ObjectsConvertor.convert(myPatches,
467 new Convertor<FilePatchInProgress, FilePatchInProgress.PatchChange>() {
468 public FilePatchInProgress.PatchChange convert(FilePatchInProgress o) {
469 return o.getChange();
474 private void acceptChange(final NamedTrinity trinity, final FilePatchInProgress.PatchChange change) {
475 final FilePatchInProgress patchInProgress = change.getPatchInProgress();
476 if (FilePatchStatus.ADDED.equals(patchInProgress.getStatus())) {
478 } else if (FilePatchStatus.DELETED.equals(patchInProgress.getStatus())) {
479 trinity.plusDeleted();
481 trinity.plusModified();
485 private Collection<FilePatchInProgress.PatchChange> getIncluded(boolean doInitCheck, List<FilePatchInProgress.PatchChange> changes) {
486 final NamedTrinity totalTrinity = new NamedTrinity();
487 final NamedTrinity includedTrinity = new NamedTrinity();
489 final Collection<FilePatchInProgress.PatchChange> included = new LinkedList<FilePatchInProgress.PatchChange>();
491 for (FilePatchInProgress.PatchChange change : changes) {
492 acceptChange(totalTrinity, change);
493 final FilePatchInProgress filePatchInProgress = change.getPatchInProgress();
494 if (filePatchInProgress.baseExistsOrAdded()) {
495 acceptChange(includedTrinity, change);
496 included.add(change);
500 // todo maybe written pretty
501 final Collection<FilePatchInProgress.PatchChange> includedNow = myChangesTreeList.getIncludedChanges();
502 final Set<FilePatchInProgress> toBeIncluded = new HashSet<FilePatchInProgress>();
503 for (FilePatchInProgress.PatchChange change : includedNow) {
504 final FilePatchInProgress patch = change.getPatchInProgress();
505 toBeIncluded.add(patch);
507 for (FilePatchInProgress.PatchChange change : changes) {
508 final FilePatchInProgress patch = change.getPatchInProgress();
509 acceptChange(totalTrinity, change);
510 if (toBeIncluded.contains(patch) && patch.baseExistsOrAdded()) {
511 acceptChange(includedTrinity, change);
512 included.add(change);
516 myInfoCalculator.setTotal(totalTrinity);
517 myInfoCalculator.setIncluded(includedTrinity);
518 myCommitLegendPanel.update();
522 private class NewBaseSelector implements Runnable {
524 final FileChooserDescriptor descriptor = new FileChooserDescriptor(false, true, false, false, false, false);
525 VirtualFile[] selectedFiles = FileChooser.chooseFiles(myProject, descriptor);
526 if (selectedFiles.length != 1 || selectedFiles[0] == null) {
529 final VirtualFile selectedValue = selectedFiles[0];
531 final List<FilePatchInProgress.PatchChange> selectedChanges = myChangesTreeList.getSelectedChanges();
532 if (selectedChanges.size() >= 1) {
533 for (FilePatchInProgress.PatchChange patchChange : selectedChanges) {
534 final FilePatchInProgress patch = patchChange.getPatchInProgress();
535 patch.setNewBase(selectedValue);
542 private List<FilePatchInProgress> changes2patches(final List<FilePatchInProgress.PatchChange> selectedChanges) {
543 return ObjectsConvertor.convert(selectedChanges, new Convertor<FilePatchInProgress.PatchChange, FilePatchInProgress>() {
544 public FilePatchInProgress convert(FilePatchInProgress.PatchChange o) {
545 return o.getPatchInProgress();
550 private class MapPopup extends BaseListPopupStep<VirtualFile> {
551 private final Runnable myNewBaseSelector;
553 private MapPopup(final @NotNull List<? extends VirtualFile> aValues, Runnable newBaseSelector) {
554 super("Select base directory for a path", aValues);
555 myNewBaseSelector = newBaseSelector;
559 public boolean isSpeedSearchEnabled() {
564 public PopupStep onChosen(final VirtualFile selectedValue, boolean finalChoice) {
565 if (selectedValue == null) {
566 myNewBaseSelector.run();
569 final List<FilePatchInProgress.PatchChange> selectedChanges = myChangesTreeList.getSelectedChanges();
570 if (selectedChanges.size() >= 1) {
571 for (FilePatchInProgress.PatchChange patchChange : selectedChanges) {
572 final FilePatchInProgress patch = patchChange.getPatchInProgress();
573 patch.setNewBase(selectedValue);
582 public String getTextFor(VirtualFile value) {
583 return value == null ? "Select base for a path" : value.getPath();
587 private static class NamedTrinity {
589 private int myModified;
590 private int myDeleted;
592 public NamedTrinity() {
598 public NamedTrinity(int added, int modified, int deleted) {
600 myModified = modified;
604 public void plusAdded() {
608 public void plusModified() {
612 public void plusDeleted() {
616 public int getAdded() {
620 public int getModified() {
624 public int getDeleted() {
629 private static class ChangesLegendCalculator implements CommitLegendPanel.InfoCalculator {
630 private NamedTrinity myTotal;
631 private NamedTrinity myIncluded;
633 private ChangesLegendCalculator() {
634 myTotal = new NamedTrinity();
635 myIncluded = new NamedTrinity();
638 public void setTotal(final NamedTrinity trinity) {
642 public void setIncluded(final NamedTrinity trinity) {
643 myIncluded = trinity;
646 public int getNew() {
647 return myTotal.getAdded();
650 public int getModified() {
651 return myTotal.getModified();
654 public int getDeleted() {
655 return myTotal.getDeleted();
658 public int getIncludedNew() {
659 return myIncluded.getAdded();
662 public int getIncludedModified() {
663 return myIncluded.getModified();
666 public int getIncludedDeleted() {
667 return myIncluded.getDeleted();
671 private static class MyChangeNodeDecorator implements ChangeNodeDecorator {
672 public void decorate(Change change, SimpleColoredComponent component, boolean isShowFlatten) {
673 if (change instanceof FilePatchInProgress.PatchChange) {
674 final FilePatchInProgress.PatchChange patchChange = (FilePatchInProgress.PatchChange) change;
675 if (! isShowFlatten) {
676 // add change subpath
677 final TextFilePatch filePatch = patchChange.getPatchInProgress().getPatch();
678 final String patchPath = filePatch.getAfterName() == null ? filePatch.getBeforeName() : filePatch.getAfterName();
679 component.append(" ");
680 component.append("["+ patchPath + "]", SimpleTextAttributes.GRAY_ATTRIBUTES);
682 if (patchChange.getPatchInProgress().getCurrentStrip() > 0) {
683 component.append(" stripped " + patchChange.getPatchInProgress().getCurrentStrip(), SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES);
686 if (FilePatchStatus.ADDED.equals(patchChange.getPatchInProgress().getStatus())) {
688 } else if (FilePatchStatus.DELETED.equals(patchChange.getPatchInProgress().getStatus())) {
693 component.append(" ");
694 component.append(text, SimpleTextAttributes.GRAY_ATTRIBUTES);
698 public List<Pair<String, Stress>> stressPartsOfFileName(final Change change, final String parentPath) {
699 if (change instanceof FilePatchInProgress.PatchChange) {
700 final FilePatchInProgress.PatchChange patchChange = (FilePatchInProgress.PatchChange) change;
701 final String basePath = patchChange.getPatchInProgress().getBase().getPath();
702 final String basePathCorrected = basePath.trim().replace('/', File.separatorChar);
703 if (parentPath.startsWith(basePathCorrected)) {
704 return Arrays.asList(new Pair<String, Stress>(basePathCorrected, Stress.BOLD),
705 new Pair<String, Stress>(StringUtil.tail(parentPath, basePathCorrected.length()), Stress.PLAIN));
711 public void preDecorate(Change change, ChangesBrowserNodeRenderer renderer, boolean showFlatten) {
715 public Collection<FilePatchInProgress> getIncluded() {
716 return ObjectsConvertor.convert(myChangesTreeList.getIncludedChanges(),
717 new Convertor<FilePatchInProgress.PatchChange, FilePatchInProgress>() {
718 public FilePatchInProgress convert(FilePatchInProgress.PatchChange o) {
719 return o.getPatchInProgress();
724 public LocalChangeList getSelectedChangeList() {
725 return myChangeListChooser.getSelectedList(myProject);
729 protected void doOKAction() {
731 myCallback.consume(this);
734 private class ZeroStrip extends AnAction {
735 private ZeroStrip() {
736 super("Remove Directories", "Remove Directories", IconLoader.getIcon("/vcs/stripNull.png"));
740 public void actionPerformed(AnActionEvent e) {
741 final List<FilePatchInProgress.PatchChange> selectedChanges = myChangesTreeList.getSelectedChanges();
742 for (FilePatchInProgress.PatchChange change : selectedChanges) {
743 change.getPatchInProgress().setZero();
749 private class StripDown extends AnAction {
750 private StripDown() {
751 super("Restore Directory", "Restore Directory", IconLoader.getIcon("/vcs/stripDown.png"));
755 public void update(AnActionEvent e) {
756 e.getPresentation().setEnabled(isEnabled());
760 public void actionPerformed(AnActionEvent e) {
761 if (! isEnabled()) return;
762 final List<FilePatchInProgress.PatchChange> selectedChanges = myChangesTreeList.getSelectedChanges();
763 for (FilePatchInProgress.PatchChange change : selectedChanges) {
764 change.getPatchInProgress().down();
769 private boolean isEnabled() {
770 final List<FilePatchInProgress.PatchChange> selectedChanges = myChangesTreeList.getSelectedChanges();
771 if (selectedChanges.isEmpty()) return false;
772 for (FilePatchInProgress.PatchChange change : selectedChanges) {
773 if (! change.getPatchInProgress().canDown()) return false;
779 private class StripUp extends AnAction {
781 super("Strip Directory", "Strip Directory", IconLoader.getIcon("/vcs/stripUp.png"));
785 public void update(AnActionEvent e) {
786 e.getPresentation().setEnabled(isEnabled());
790 public void actionPerformed(AnActionEvent e) {
791 if (! isEnabled()) return;
792 final List<FilePatchInProgress.PatchChange> selectedChanges = myChangesTreeList.getSelectedChanges();
793 for (FilePatchInProgress.PatchChange change : selectedChanges) {
794 change.getPatchInProgress().up();
799 private boolean isEnabled() {
800 final List<FilePatchInProgress.PatchChange> selectedChanges = myChangesTreeList.getSelectedChanges();
801 if (selectedChanges.isEmpty()) return false;
802 for (FilePatchInProgress.PatchChange change : selectedChanges) {
803 if (! change.getPatchInProgress().canUp()) return false;
809 private class ResetStrip extends AnAction {
810 private ResetStrip() {
811 super("Reset Directories", "Reset Directories", IconLoader.getIcon("/vcs/resetStrip.png"));
815 public void actionPerformed(AnActionEvent e) {
816 final List<FilePatchInProgress.PatchChange> selectedChanges = myChangesTreeList.getSelectedChanges();
817 for (FilePatchInProgress.PatchChange change : selectedChanges) {
818 change.getPatchInProgress().reset();
824 private class MyShowDiff extends AnAction {
825 private final MyChangeComparator myMyChangeComparator;
826 private MyShowDiff() {
827 super("Show Diff", "Show Diff", IconLoader.getIcon("/actions/diff.png"));
828 myMyChangeComparator = new MyChangeComparator();
831 public void update(AnActionEvent e) {
832 e.getPresentation().setEnabled((! myPatches.isEmpty()) && myContainBasedChanges);
835 public void actionPerformed(AnActionEvent e) {
836 if (myPatches.isEmpty() || (! myContainBasedChanges)) return;
837 final List<FilePatchInProgress.PatchChange> changes = getAllChanges();
838 Collections.sort(changes, myMyChangeComparator);
839 final List<FilePatchInProgress.PatchChange> selectedChanges = myChangesTreeList.getSelectedChanges();
842 final ArrayList<DiffRequestPresentable> diffRequestPresentables = new ArrayList<DiffRequestPresentable>(changes.size());
843 if (selectedChanges.isEmpty()) {
844 selectedChanges.addAll(changes);
846 if (! selectedChanges.isEmpty()) {
847 final FilePatchInProgress.PatchChange c = selectedChanges.get(0);
848 for (FilePatchInProgress.PatchChange change : changes) {
849 final FilePatchInProgress patchInProgress = change.getPatchInProgress();
850 if (! patchInProgress.baseExistsOrAdded()) continue;
851 final DiffRequestPresentable diffRequestPresentable = change.createDiffRequestPresentable(myProject);
852 if (diffRequestPresentable != null) {
853 diffRequestPresentables.add(diffRequestPresentable);
855 if (change.equals(c)) {
856 selectedIdx = diffRequestPresentables.size() - 1;
860 if (diffRequestPresentables.isEmpty()) return;
861 ShowDiffAction.showDiffImpl(myProject, diffRequestPresentables, selectedIdx, new ShowDiffUIContext(false));
865 private class MyChangeComparator implements Comparator<FilePatchInProgress.PatchChange> {
866 public int compare(FilePatchInProgress.PatchChange o1, FilePatchInProgress.PatchChange o2) {
867 if (PropertiesComponent.getInstance(myProject).isTrueValue("ChangesBrowser.SHOW_FLATTEN")) {
868 return o1.getPatchInProgress().getIoCurrentBase().getName().compareTo(o2.getPatchInProgress().getIoCurrentBase().getName());
870 return o1.getPatchInProgress().getIoCurrentBase().compareTo(o2.getPatchInProgress().getIoCurrentBase());