From 9166eedb0d0ede1c878b0b76b1c77d8b94d13a74 Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 16 Nov 2009 22:29:04 +0300 Subject: [PATCH] VCS: apply patch allow dirs striping for selection --- platform/icons/src/vcs/resetStrip.png | Bin 0 -> 491 bytes platform/icons/src/vcs/stripDown.png | Bin 0 -> 491 bytes platform/icons/src/vcs/stripNull.png | Bin 0 -> 485 bytes platform/icons/src/vcs/stripUp.png | Bin 0 -> 500 bytes .../patch/ApplyPatchDifferentiatedDialog.java | 123 ++++++++- .../changes/patch/FilePatchInProgress.java | 239 +++++++++++++++++- .../openapi/vcs/changes/patch/Strippable.java | 32 +++ .../vcs/changes/ui/ChangesTreeList.java | 61 ++++- 8 files changed, 436 insertions(+), 19 deletions(-) create mode 100644 platform/icons/src/vcs/resetStrip.png create mode 100644 platform/icons/src/vcs/stripDown.png create mode 100644 platform/icons/src/vcs/stripNull.png create mode 100644 platform/icons/src/vcs/stripUp.png create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/Strippable.java diff --git a/platform/icons/src/vcs/resetStrip.png b/platform/icons/src/vcs/resetStrip.png new file mode 100644 index 0000000000000000000000000000000000000000..b28c04eb4303642d725ce451af0a605571fa5488 GIT binary patch literal 491 zcmVV@c++u2396UhATJz-eP1#==cY8*{@&UrZ;wPn*bAI z0Vz7lGfs%YE{$N44?=K`2fwun#+Q#tvE06=! z`0oc$><8FejEo<#8UXd>Z=j9efVKl|{QC>2@grO#1JHF0e}M)8!w4O)z=Id)M20_5 zTY-xI|9%hG`2Qa$4E|zGWGsyT|1p05^q%4SiFP8=qy{vlAQ`~S%C&dV=9lcmW-4w@ h&RHa9CVT)OzyKK@4{LV|4!QsU002ovPDHLkV1nol-Jk#f literal 0 HcmV?d00001 diff --git a/platform/icons/src/vcs/stripDown.png b/platform/icons/src/vcs/stripDown.png new file mode 100644 index 0000000000000000000000000000000000000000..02e5c67f31b979446132b616beaf6e9e7a369938 GIT binary patch literal 491 zcmV37NpT=1h0by@{_MaV{?n4p17P={B7`QhaMEt7bOXDn7XU zV0Zqt-CvlPiMet`GUeXB>Eq>+ST7M<8nK~KC=?9Ew^wsQv=l9L7M&~|<9OJpDe}U| zP#=ZIcS!jB^+#9+qCpE}4TBL%6_l$~Eu?7w=z7yd!1jCvX=JL&dLoNpW>W>34zHl3 zQQ`d_ANyT%P+I+&^(jJ~p+{segEI}QLW)(oS2_$q!CWtVj zzos;I5-pNptKu^2`6iGpIRmrpawvi=p%wC^r4t?O(x|;D4uY=kp4+plPk*QD7z_kX hoVj3T`j7nzFaX*A3aik!b7}wp002ovPDHLkV1fqR(cu69 literal 0 HcmV?d00001 diff --git a/platform/icons/src/vcs/stripNull.png b/platform/icons/src/vcs/stripNull.png new file mode 100644 index 0000000000000000000000000000000000000000..c9a2726a40bed068aa289566d895f0d92107005b GIT binary patch literal 485 zcmV-q!>_+q!DIdITHhgfD{*W!kmrl5*D|Bgg_^quPux~4ha7T z~^$8$OV|9w4zg8S=z?t)?n9qT(Mpzy(7 zxcTTa!_zzK82j;U-(NtDAK@AqfUaZs3p5BAMi}V;o)dshWcUNM6{z_C@Aq(x z|Nnu);4k)s#Q6UoI?9SZ0_h#qD$2q6Jd}cA4`X!(CkvE$NFRf;q6Nv9|jC&;zHULoC0w-}yiaQgn zcrk$N!VG*=TrN)&%rij?i3v742uD`trv{5VA`;F`=akHL#y`K=i70x!N;pVtuxYW4 zg(o?)#*N~g!}qplU)&}?JRNn!k|4>we$o5$8F95l+)#*X3WY+!P;7HK#~NB>ptHzG z=@8d_J12>PzMd``8`>h~^Vc3>XgD>RBcm6LP^utZrE1}Ub`LJr9R&6a87ZX8H?=?{ z!OV3TWGcLZltP8KTYT(x3|4CNr`M(kbq+OBiwFQT%E*d~ q&p-TLw=oz995u)PT4gu;5nuon>k7f$vjLp|0000 patchesToSelect = changes2patches(myChangesTreeList.getSelectedChanges()); final List changes = getAllChanges(); final Collection included = getIncluded(doInitCheck, changes); + myChangesTreeList.setChangesToDisplay(changes); myChangesTreeList.setIncludedChanges(included); myChangesTreeList.repaint(); + if ((! doInitCheck) && patchesToSelect != null) { + final List toSelect = new ArrayList(patchesToSelect.size()); + for (FilePatchInProgress.PatchChange change : changes) { + if (patchesToSelect.contains(change.getPatchInProgress())) { + toSelect.add(change); + } + } + myChangesTreeList.select(toSelect); + } myContainBasedChanges = false; for (FilePatchInProgress patch : myPatches) { @@ -540,16 +556,15 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { } else { // todo maybe written pretty final Collection includedNow = myChangesTreeList.getIncludedChanges(); - final Set> toBeIncluded = new HashSet>(); + final Set toBeIncluded = new HashSet(); for (FilePatchInProgress.PatchChange change : includedNow) { final FilePatchInProgress patch = change.getPatchInProgress(); - toBeIncluded.add(new Pair(patch.getPatch().getBeforeName(), patch.getPatch().getAfterName())); + toBeIncluded.add(patch); } for (FilePatchInProgress.PatchChange change : changes) { final FilePatchInProgress patch = change.getPatchInProgress(); - final Pair pair = new Pair(patch.getPatch().getBeforeName(), patch.getPatch().getAfterName()); acceptChange(totalTrinity, change); - if (toBeIncluded.contains(pair) && patch.baseExistsOrAdded()) { + if (toBeIncluded.contains(patch) && patch.baseExistsOrAdded()) { acceptChange(includedTrinity, change); included.add(change); } @@ -581,6 +596,14 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { } } + private List changes2patches(final List selectedChanges) { + return ObjectsConvertor.convert(selectedChanges, new Convertor() { + public FilePatchInProgress convert(FilePatchInProgress.PatchChange o) { + return o.getPatchInProgress(); + } + }); + } + private class MapPopup extends BaseListPopupStep { private final Runnable myNewBaseSelector; @@ -759,6 +782,96 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { myCallback.consume(this); } + private class ZeroStrip extends AnAction { + private ZeroStrip() { + super("Remove Directories", "Remove Directories", IconLoader.getIcon("/vcs/stripNull.png")); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final List selectedChanges = myChangesTreeList.getSelectedChanges(); + for (FilePatchInProgress.PatchChange change : selectedChanges) { + change.getPatchInProgress().setZero(); + } + updateTree(false); + } + } + + private class StripDown extends AnAction { + private StripDown() { + super("Restore Directory", "Restore Directory", IconLoader.getIcon("/vcs/stripDown.png")); + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(isEnabled()); + } + + @Override + public void actionPerformed(AnActionEvent e) { + if (! isEnabled()) return; + final List selectedChanges = myChangesTreeList.getSelectedChanges(); + for (FilePatchInProgress.PatchChange change : selectedChanges) { + change.getPatchInProgress().down(); + } + updateTree(false); + } + + private boolean isEnabled() { + final List selectedChanges = myChangesTreeList.getSelectedChanges(); + if (selectedChanges.isEmpty()) return false; + for (FilePatchInProgress.PatchChange change : selectedChanges) { + if (! change.getPatchInProgress().canDown()) return false; + } + return true; + } + } + + private class StripUp extends AnAction { + private StripUp() { + super("Strip Directory", "Strip Directory", IconLoader.getIcon("/vcs/stripUp.png")); + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(isEnabled()); + } + + @Override + public void actionPerformed(AnActionEvent e) { + if (! isEnabled()) return; + final List selectedChanges = myChangesTreeList.getSelectedChanges(); + for (FilePatchInProgress.PatchChange change : selectedChanges) { + change.getPatchInProgress().up(); + } + updateTree(false); + } + + private boolean isEnabled() { + final List selectedChanges = myChangesTreeList.getSelectedChanges(); + if (selectedChanges.isEmpty()) return false; + for (FilePatchInProgress.PatchChange change : selectedChanges) { + if (! change.getPatchInProgress().canUp()) return false; + } + return true; + } + } + + private class ResetStrip extends AnAction { + private ResetStrip() { + super("Reset Directories", "Reset Directories", IconLoader.getIcon("/vcs/resetStrip.png")); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final List selectedChanges = myChangesTreeList.getSelectedChanges(); + for (FilePatchInProgress.PatchChange change : selectedChanges) { + change.getPatchInProgress().reset(); + } + updateTree(false); + } + } + private class MyShowDiff extends AnAction { private MyShowDiff() { super("Show Diff", "Show Diff", IconLoader.getIcon("/actions/diff.png")); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/FilePatchInProgress.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/FilePatchInProgress.java index 70f7f195131f..4f0ac8341799 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/FilePatchInProgress.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/FilePatchInProgress.java @@ -19,9 +19,13 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diff.DiffRequestFactory; import com.intellij.openapi.diff.MergeRequest; +import com.intellij.openapi.diff.impl.patch.ApplyPatchException; +import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; +import com.intellij.openapi.diff.impl.patch.FilePatch; import com.intellij.openapi.diff.impl.patch.TextFilePatch; import com.intellij.openapi.diff.impl.patch.formove.PathMerger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.Change; @@ -37,13 +41,12 @@ import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.Nullable; import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.io.IOException; +import java.util.*; -public class FilePatchInProgress { +public class FilePatchInProgress implements Strippable { private final TextFilePatch myPatch; + private final PatchStrippable myStrippable; private final FilePatchStatus myStatus; private VirtualFile myBase; @@ -59,6 +62,7 @@ public class FilePatchInProgress { public FilePatchInProgress(final TextFilePatch patch, final Collection autoBases, final VirtualFile baseDir) { myPatch = patch; + myStrippable = new PatchStrippable(patch); if (autoBases != null) { myAutoBases = new ArrayList(); final String path = myPatch.getBeforeName() == null ? myPatch.getAfterName() : myPatch.getBeforeName(); @@ -270,4 +274,229 @@ public class FilePatchInProgress { public Pair getKey() { return new Pair(myPatch.getBeforeName(), myPatch.getAfterName()); } + + private void refresh() { + myStrippable.applyBackToPatch(myPatch); + setNewBase(myBase); + } + + public void reset() { + myStrippable.reset(); + refresh(); + } + + public boolean canDown() { + return myStrippable.canDown(); + } + + public boolean canUp() { + return myStrippable.canUp(); + } + + public void up() { + myStrippable.up(); + refresh(); + } + + public void down() { + myStrippable.down(); + refresh(); + } + + public void setZero() { + myStrippable.setZero(); + refresh(); + } + + public String getCurrentPath() { + return myStrippable.getCurrentPath(); + } + + private static class StripCapablePath implements Strippable { + private final int myStripMax; + private int myCurrentStrip; + private final StringBuilder mySourcePath; + private final int[] myParts; + + private StripCapablePath(final String path) { + final String corrected = path.trim().replace('\\', '/'); + mySourcePath = new StringBuilder(corrected); + final String[] steps = corrected.split("/"); + myStripMax = steps.length - 1; + myParts = new int[steps.length]; + int pos = 0; + for (int i = 0; i < steps.length; i++) { + final String step = steps[i]; + myParts[i] = pos; + pos += step.length() + 1; // plus 1 for separator + } + myCurrentStrip = 0; + } + + public void reset() { + myCurrentStrip = 0; + } + + // down - restore dirs... + public boolean canDown() { + return myCurrentStrip > 0; + } + + public boolean canUp() { + return myCurrentStrip < myStripMax; + } + + public void up() { + if (canUp()) { + ++ myCurrentStrip; + } + } + + public void down() { + if (canDown()) { + -- myCurrentStrip; + } + } + + public void setZero() { + myCurrentStrip = myStripMax; + } + + public String getCurrentPath() { + return mySourcePath.substring(myParts[myCurrentStrip]); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + StripCapablePath that = (StripCapablePath)o; + + if (!mySourcePath.equals(that.mySourcePath)) return false; + + return true; + } + + @Override + public int hashCode() { + return mySourcePath.hashCode(); + } + } + + private static class PatchStrippable implements Strippable { + private final Strippable[] myParts; + private final int myBeforeIdx; + private final int myAfterIdx; + + private PatchStrippable(final FilePatch patch) { + final boolean onePath = patch.isDeletedFile() || patch.isNewFile() || Comparing.equal(patch.getAfterName(), patch.getBeforeName()); + final int size = onePath ? 1 : 2; + myParts = new Strippable[size]; + + int cnt = 0; + if (patch.getAfterName() != null) { + myAfterIdx = 0; + myParts[cnt] = new StripCapablePath(patch.getAfterName()); + ++ cnt; + } else { + myAfterIdx = -1; + } + if (cnt < size) { + myParts[cnt] = new StripCapablePath(patch.getBeforeName()); + myBeforeIdx = cnt; + } else { + myBeforeIdx = 0; + } + } + + public void reset() { + for (Strippable part : myParts) { + part.reset(); + } + } + + public boolean canDown() { + boolean result = true; + for (Strippable part : myParts) { + result &= part.canDown(); + } + return result; + } + + public boolean canUp() { + boolean result = true; + for (Strippable part : myParts) { + result &= part.canUp(); + } + return result; + } + + public void up() { + for (Strippable part : myParts) { + part.up(); + } + } + + public void down() { + for (Strippable part : myParts) { + part.down(); + } + } + + public void setZero() { + for (Strippable part : myParts) { + part.setZero(); + } + } + + public String getCurrentPath() { + return myParts[0].getCurrentPath(); + } + + public void applyBackToPatch(final FilePatch patch) { + final String beforeName = patch.getBeforeName(); + if (beforeName != null) { + patch.setBeforeName(myParts[myBeforeIdx].getCurrentPath()); + } + final String afterName = patch.getAfterName(); + if (afterName != null) { + patch.setAfterName(myParts[myAfterIdx].getCurrentPath()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PatchStrippable that = (PatchStrippable)o; + + if (!Arrays.equals(myParts, that.myParts)) return false; + + return true; + } + + @Override + public int hashCode() { + return Arrays.hashCode(myParts); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FilePatchInProgress that = (FilePatchInProgress)o; + + if (!myStrippable.equals(that.myStrippable)) return false; + + return true; + } + + @Override + public int hashCode() { + return myStrippable.hashCode(); + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/Strippable.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/Strippable.java new file mode 100644 index 000000000000..9a8ef5895ca8 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/Strippable.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2009 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. + */ +package com.intellij.openapi.vcs.changes.patch; + +public interface Strippable { + void reset();// down - restore dirs... + + boolean canDown(); + + boolean canUp(); + + void up(); + + void down(); + + void setZero(); + + String getCurrentPath(); +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java index 369838c98818..de27d74fb1fc 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java @@ -17,6 +17,7 @@ package com.intellij.openapi.vcs.changes.ui; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.EmptyRunnable; @@ -41,9 +42,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeCellRenderer; -import javax.swing.tree.TreePath; +import javax.swing.tree.*; import java.awt.*; import java.awt.event.*; import java.io.File; @@ -205,8 +204,10 @@ public abstract class ChangesTreeList extends JPanel { } public void setShowFlatten(final boolean showFlatten) { + final List wasSelected = getSelectedChanges(); myShowFlatten = showFlatten; myCards.show(this, myShowFlatten ? LIST_CARD : TREE_CARD); + select(wasSelected); if (myList.hasFocus() || myTree.hasFocus()) { SwingUtilities.invokeLater(new Runnable() { public void run() { @@ -243,7 +244,7 @@ public abstract class ChangesTreeList extends JPanel { final DefaultTreeModel model = buildTreeModel(changes, myChangeDecorator); myTree.setModel(model); - SwingUtilities.invokeLater(new Runnable() { + final Runnable runnable = new Runnable() { public void run() { if (myProject.isDisposed()) return; TreeUtil.expandAll(myTree); @@ -256,7 +257,7 @@ public abstract class ChangesTreeList extends JPanel { listSelection = count; break; } - count ++; + count++; } ChangesBrowserNode root = (ChangesBrowserNode)model.getRoot(); @@ -290,7 +291,12 @@ public abstract class ChangesTreeList extends JPanel { } } } - }); + }; + if (ApplicationManager.getApplication().isDispatchThread()) { + runnable.run(); + } else { + SwingUtilities.invokeLater(runnable); + } } protected abstract DefaultTreeModel buildTreeModel(final List changes, final ChangeNodeDecorator changeNodeDecorator); @@ -658,12 +664,49 @@ public abstract class ChangesTreeList extends JPanel { } } else { - final int count = myTree.getRowCount(); - if (count > 0) { - myTree.setSelectionInterval(0, count-1); + final int countTree = myTree.getRowCount(); + if (countTree > 0) { + myTree.setSelectionInterval(0, countTree-1); } } } } + public void select(final List changes) { + final DefaultTreeModel treeModel = (DefaultTreeModel) myTree.getModel(); + final TreeNode root = (TreeNode) treeModel.getRoot(); + final List treeSelection = new ArrayList(changes.size()); + TreeUtil.traverse(root, new TreeUtil.Traverse() { + public boolean accept(Object node) { + final T change = (T) ((DefaultMutableTreeNode) node).getUserObject(); + if (changes.contains(change)) { + treeSelection.add(new TreePath(((DefaultMutableTreeNode) node).getPath())); + } + return true; + } + }); + myTree.setSelectionPaths(treeSelection.toArray(new TreePath[treeSelection.size()])); + + // list + final ListModel model = myList.getModel(); + final int size = model.getSize(); + final List listSelection = new ArrayList(changes.size()); + for (int i = 0; i < size; i++) { + final T el = (T) model.getElementAt(i); + if (changes.contains(el)) { + listSelection.add(i); + } + } + myList.setSelectedIndices(int2int(listSelection)); + } + + private static int[] int2int(List treeSelection) { + final int[] toPass = new int[treeSelection.size()]; + int i = 0; + for (Integer integer : treeSelection) { + toPass[i] = integer; + ++ i; + } + return toPass; + } }