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
.openapi
.actionSystem
.*;
19 import com
.intellij
.openapi
.diff
.impl
.patch
.FilePatch
;
20 import com
.intellij
.openapi
.diff
.impl
.patch
.PatchReader
;
21 import com
.intellij
.openapi
.diff
.impl
.patch
.PatchSyntaxException
;
22 import com
.intellij
.openapi
.diff
.impl
.patch
.TextFilePatch
;
23 import com
.intellij
.openapi
.fileChooser
.FileChooser
;
24 import com
.intellij
.openapi
.fileChooser
.FileChooserDescriptor
;
25 import com
.intellij
.openapi
.fileChooser
.FileChooserDialog
;
26 import com
.intellij
.openapi
.fileChooser
.FileChooserFactory
;
27 import com
.intellij
.openapi
.fileTypes
.FileTypes
;
28 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
29 import com
.intellij
.openapi
.project
.Project
;
30 import com
.intellij
.openapi
.ui
.DialogWrapper
;
31 import com
.intellij
.openapi
.ui
.TextFieldWithBrowseButton
;
32 import com
.intellij
.openapi
.ui
.popup
.JBPopupFactory
;
33 import com
.intellij
.openapi
.ui
.popup
.PopupStep
;
34 import com
.intellij
.openapi
.ui
.popup
.util
.BaseListPopupStep
;
35 import com
.intellij
.openapi
.util
.IconLoader
;
36 import com
.intellij
.openapi
.util
.Pair
;
37 import com
.intellij
.openapi
.util
.text
.StringUtil
;
38 import com
.intellij
.openapi
.vcs
.ObjectsConvertor
;
39 import com
.intellij
.openapi
.vcs
.VcsBundle
;
40 import com
.intellij
.openapi
.vcs
.ZipperUpdater
;
41 import com
.intellij
.openapi
.vcs
.changes
.Change
;
42 import com
.intellij
.openapi
.vcs
.changes
.ChangeListManager
;
43 import com
.intellij
.openapi
.vcs
.changes
.LocalChangeList
;
44 import com
.intellij
.openapi
.vcs
.changes
.actions
.DiffRequestPresentable
;
45 import com
.intellij
.openapi
.vcs
.changes
.actions
.ShowDiffAction
;
46 import com
.intellij
.openapi
.vcs
.changes
.ui
.*;
47 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
48 import com
.intellij
.openapi
.vfs
.VirtualFile
;
49 import com
.intellij
.ui
.DocumentAdapter
;
50 import com
.intellij
.ui
.SimpleColoredComponent
;
51 import com
.intellij
.ui
.SimpleTextAttributes
;
52 import com
.intellij
.util
.Consumer
;
53 import com
.intellij
.util
.containers
.Convertor
;
54 import org
.jetbrains
.annotations
.NonNls
;
55 import org
.jetbrains
.annotations
.NotNull
;
56 import org
.jetbrains
.annotations
.Nullable
;
59 import javax
.swing
.event
.DocumentEvent
;
60 import javax
.swing
.tree
.DefaultTreeModel
;
63 import java
.io
.IOException
;
65 import java
.util
.List
;
66 import java
.util
.concurrent
.atomic
.AtomicReference
;
68 public class ApplyPatchDifferentiatedDialog
extends DialogWrapper
{
69 private final ZipperUpdater myLoadQueue
;
70 private TextFieldWithBrowseButton myPatchFile
;
72 private final List
<FilePatchInProgress
> myPatches
;
73 private final MyChangeTreeList myChangesTreeList
;
75 private JComponent myCenterPanel
;
76 private JComponent mySouthPanel
;
77 private final Project myProject
;
79 private AtomicReference
<FilePresentation
> myRecentPathFileChange
;
80 private ApplyPatchDifferentiatedDialog
.MyUpdater myUpdater
;
81 private Runnable myReset
;
82 private ChangeListChooserPanel myChangeListChooser
;
83 private ChangesLegendCalculator myInfoCalculator
;
84 private CommitLegendPanel myCommitLegendPanel
;
85 private final Consumer
<ApplyPatchDifferentiatedDialog
> myCallback
;
87 private boolean myContainBasedChanges
;
89 public ApplyPatchDifferentiatedDialog(final Project project
, final Consumer
<ApplyPatchDifferentiatedDialog
> callback
) {
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(null, 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(project
);
150 myInfoCalculator
= new ChangesLegendCalculator();
151 myCommitLegendPanel
= new CommitLegendPanel(myInfoCalculator
);
154 final FileChooserDialog fileChooserDialog
= FileChooserFactory
.getInstance().createFileChooser(descriptor
, project
);
155 final VirtualFile
[] files
= fileChooserDialog
.choose(null, project
);
156 if (files
!= null && files
.length
> 0) {
163 protected String
getDimensionServiceKey() {
164 return "vcs.ApplyPatchDifferentiatedDialog";
168 protected String
getHelpId() {
169 return "reference.dialogs.vcs.patch.apply";
172 private void setPathFileChangeDefault() {
173 myRecentPathFileChange
.set(new FilePresentation(myPatchFile
.getText()));
176 public void init(final VirtualFile patchFile
) {
177 myPatchFile
.setText(patchFile
.getPath());
178 myRecentPathFileChange
.set(new FilePresentation(patchFile
));
179 myLoadQueue
.queue(myUpdater
);
182 private class MyUpdater
implements Runnable
{
184 final FilePresentation filePresentation
= myRecentPathFileChange
.get();
185 if ((filePresentation
== null) || (filePresentation
.getVf() == null)) {
186 SwingUtilities
.invokeLater(myReset
);
189 final VirtualFile file
= filePresentation
.getVf();
191 final List
<TextFilePatch
> patches
= loadPatches(file
);
192 final AutoMatchIterator autoMatchIterator
= new AutoMatchIterator(myProject
);
193 final List
<FilePatchInProgress
> matchedPathes
= autoMatchIterator
.execute(patches
);
195 SwingUtilities
.invokeLater(new Runnable() {
197 myChangeListChooser
.setDefaultName(file
.getNameWithoutExtension().replace('_', ' ').trim());
199 myPatches
.addAll(matchedPathes
);
207 private List
<TextFilePatch
> loadPatches(final VirtualFile patchFile
) {
208 if (! patchFile
.isValid()) {
210 //queueUpdateStatus("Cannot find patch file");
211 return Collections
.emptyList();
215 reader
= new PatchReader(patchFile
);
217 catch (IOException e
) {
219 //queueUpdateStatus(VcsBundle.message("patch.apply.open.error", e.getMessage()));
220 return Collections
.emptyList();
222 final List
<TextFilePatch
> result
= new LinkedList
<TextFilePatch
>();
226 patch
= reader
.readNextPatch();
228 catch (PatchSyntaxException e
) {
230 if (e
.getLine() >= 0) {
231 //queueUpdateStatus(VcsBundle.message("patch.apply.load.error.line", e.getMessage(), e.getLine()));
234 //queueUpdateStatus(VcsBundle.message("patch.apply.load.error", e.getMessage()));
236 return Collections
.emptyList();
242 result
.add((TextFilePatch
) patch
);
244 if (myPatches
.isEmpty()) {
246 //queueUpdateStatus(VcsBundle.message("patch.apply.no.patches.found"));
251 private static class FilePresentation
{
252 private final VirtualFile myVf
;
253 private final String myPath
;
255 private FilePresentation(VirtualFile vf
) {
260 private FilePresentation(String path
) {
266 public VirtualFile
getVf() {
270 final VirtualFile file
= LocalFileSystem
.getInstance().refreshAndFindFileByPath(myPath
);
271 return (file
!= null) && (! file
.isDirectory()) ? file
: null;
275 public void reset() {
277 myChangesTreeList
.setChangesToDisplay(Collections
.<FilePatchInProgress
.PatchChange
>emptyList());
278 myChangesTreeList
.repaint();
279 myContainBasedChanges
= false;
283 protected JComponent
createCenterPanel() {
284 if (myCenterPanel
== null) {
285 myCenterPanel
= new JPanel(new GridBagLayout());
286 final GridBagConstraints gb
=
287 new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints
.NORTHWEST
, GridBagConstraints
.NONE
, new Insets(1, 1, 1, 1), 0, 0);
289 final JLabel label
= new JLabel(VcsBundle
.message("create.patch.file.name.field"));
290 label
.setLabelFor(myPatchFile
);
291 myCenterPanel
.add(label
, gb
);
294 gb
.fill
= GridBagConstraints
.HORIZONTAL
;
296 myCenterPanel
.add(myPatchFile
, gb
);
302 gb
.fill
= GridBagConstraints
.HORIZONTAL
;
305 final DefaultActionGroup group
= new DefaultActionGroup();
306 final AnAction
[] treeActions
= myChangesTreeList
.getTreeActions();
307 group
.addAll(treeActions
);
308 group
.add(new MapDirectory());
310 final MyShowDiff diffAction
= new MyShowDiff();
311 diffAction
.registerCustomShortcutSet(CommonShortcuts
.getDiff(), myCenterPanel
);
312 group
.add(diffAction
);
314 group
.add(new StripUp());
315 group
.add(new StripDown());
316 group
.add(new ResetStrip());
317 group
.add(new ZeroStrip());
319 final ActionToolbar toolbar
= ActionManager
.getInstance().createActionToolbar("APPLY_PATCH", group
, true);
320 myCenterPanel
.add(toolbar
.getComponent(), gb
);
326 gb
.fill
= GridBagConstraints
.BOTH
;
327 myCenterPanel
.add(myChangesTreeList
, gb
);
329 final JPanel wrapper
= new JPanel(new GridBagLayout());
330 final GridBagConstraints gb1
=
331 new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints
.NORTHWEST
, GridBagConstraints
.HORIZONTAL
, new Insets(1, 1, 1, 1), 0, 0);
332 wrapper
.add(myChangeListChooser
, gb1
);
334 gb1
.fill
= GridBagConstraints
.NONE
;
336 gb1
.insets
.left
= 10;
337 wrapper
.add(myCommitLegendPanel
.getComponent(), gb1
);
343 gb
.fill
= GridBagConstraints
.HORIZONTAL
;
344 myCenterPanel
.add(wrapper
, gb
);
346 return myCenterPanel
;
349 private static class MyChangeTreeList
extends ChangesTreeList
<FilePatchInProgress
.PatchChange
> {
350 private MyChangeTreeList(Project project
,
351 Collection
<FilePatchInProgress
.PatchChange
> initiallyIncluded
,
352 @Nullable Runnable inclusionListener
,
353 @Nullable ChangeNodeDecorator decorator
) {
354 super(project
, initiallyIncluded
, true, false, inclusionListener
, decorator
);
358 protected DefaultTreeModel
buildTreeModel(List
<FilePatchInProgress
.PatchChange
> changes
, ChangeNodeDecorator changeNodeDecorator
) {
359 TreeModelBuilder builder
= new TreeModelBuilder(myProject
, false);
360 return builder
.buildModel(ObjectsConvertor
.convert(changes
,
361 new Convertor
<FilePatchInProgress
.PatchChange
, Change
>() {
362 public Change
convert(FilePatchInProgress
.PatchChange o
) {
365 }), changeNodeDecorator
);
369 protected List
<FilePatchInProgress
.PatchChange
> getSelectedObjects(ChangesBrowserNode
<FilePatchInProgress
.PatchChange
> node
) {
370 final List
<Change
> under
= node
.getAllChangesUnder();
371 return ObjectsConvertor
.convert(under
, new Convertor
<Change
, FilePatchInProgress
.PatchChange
>() {
372 public FilePatchInProgress
.PatchChange
convert(Change o
) {
373 return (FilePatchInProgress
.PatchChange
) o
;
379 protected FilePatchInProgress
.PatchChange
getLeadSelectedObject(ChangesBrowserNode node
) {
380 final Object o
= node
.getUserObject();
381 if (o
instanceof FilePatchInProgress
.PatchChange
) {
382 return (FilePatchInProgress
.PatchChange
) o
;
388 private class MapDirectory
extends AnAction
{
389 private final NewBaseSelector myNewBaseSelector
;
391 private MapDirectory() {
392 super("Map base directory", "Map base directory", IconLoader
.getIcon("/vcs/mapBase.png"));
393 myNewBaseSelector
= new NewBaseSelector();
397 public void actionPerformed(AnActionEvent e
) {
398 final List
<FilePatchInProgress
.PatchChange
> selectedChanges
= myChangesTreeList
.getSelectedChanges();
399 if ((selectedChanges
.size() >= 1) && (sameBase(selectedChanges
))) {
400 final FilePatchInProgress
.PatchChange patchChange
= selectedChanges
.get(0);
401 final FilePatchInProgress patch
= patchChange
.getPatchInProgress();
402 final List
<VirtualFile
> autoBases
= patch
.getAutoBasesCopy();
403 if (autoBases
.isEmpty() || (autoBases
.size() == 1 && autoBases
.get(0).equals(patch
.getBase()))) {
404 myNewBaseSelector
.run();
407 final MapPopup step
= new MapPopup(autoBases
, myNewBaseSelector
);
408 JBPopupFactory
.getInstance().createListPopup(step
).showCenteredInCurrentWindow(myProject
);
414 public void update(AnActionEvent e
) {
415 final List
<FilePatchInProgress
.PatchChange
> selectedChanges
= myChangesTreeList
.getSelectedChanges();
416 e
.getPresentation().setEnabled((selectedChanges
.size() >= 1) && (sameBase(selectedChanges
)));
420 private boolean sameBase(final List
<FilePatchInProgress
.PatchChange
> selectedChanges
) {
421 VirtualFile base
= null;
422 for (FilePatchInProgress
.PatchChange change
: selectedChanges
) {
423 final VirtualFile changeBase
= change
.getPatchInProgress().getBase();
426 } else if (! base
.equals(changeBase
)) {
433 private void updateTree(boolean doInitCheck
) {
434 final List
<FilePatchInProgress
> patchesToSelect
= changes2patches(myChangesTreeList
.getSelectedChanges());
435 final List
<FilePatchInProgress
.PatchChange
> changes
= getAllChanges();
436 final Collection
<FilePatchInProgress
.PatchChange
> included
= getIncluded(doInitCheck
, changes
);
438 myChangesTreeList
.setChangesToDisplay(changes
);
439 myChangesTreeList
.setIncludedChanges(included
);
440 myChangesTreeList
.repaint();
441 if ((! doInitCheck
) && patchesToSelect
!= null) {
442 final List
<FilePatchInProgress
.PatchChange
> toSelect
= new ArrayList
<FilePatchInProgress
.PatchChange
>(patchesToSelect
.size());
443 for (FilePatchInProgress
.PatchChange change
: changes
) {
444 if (patchesToSelect
.contains(change
.getPatchInProgress())) {
445 toSelect
.add(change
);
448 myChangesTreeList
.select(toSelect
);
451 myContainBasedChanges
= false;
452 for (FilePatchInProgress patch
: myPatches
) {
453 if (patch
.baseExistsOrAdded()) {
454 myContainBasedChanges
= true;
460 private List
<FilePatchInProgress
.PatchChange
> getAllChanges() {
461 return ObjectsConvertor
.convert(myPatches
,
462 new Convertor
<FilePatchInProgress
, FilePatchInProgress
.PatchChange
>() {
463 public FilePatchInProgress
.PatchChange
convert(FilePatchInProgress o
) {
464 return o
.getChange();
469 private void acceptChange(final NamedTrinity trinity
, final FilePatchInProgress
.PatchChange change
) {
470 final FilePatchInProgress patchInProgress
= change
.getPatchInProgress();
471 if (FilePatchStatus
.ADDED
.equals(patchInProgress
.getStatus())) {
473 } else if (FilePatchStatus
.DELETED
.equals(patchInProgress
.getStatus())) {
474 trinity
.plusDeleted();
476 trinity
.plusModified();
480 private Collection
<FilePatchInProgress
.PatchChange
> getIncluded(boolean doInitCheck
, List
<FilePatchInProgress
.PatchChange
> changes
) {
481 final NamedTrinity totalTrinity
= new NamedTrinity();
482 final NamedTrinity includedTrinity
= new NamedTrinity();
484 final Collection
<FilePatchInProgress
.PatchChange
> included
= new LinkedList
<FilePatchInProgress
.PatchChange
>();
486 for (FilePatchInProgress
.PatchChange change
: changes
) {
487 acceptChange(totalTrinity
, change
);
488 final FilePatchInProgress filePatchInProgress
= change
.getPatchInProgress();
489 if (filePatchInProgress
.baseExistsOrAdded()) {
490 acceptChange(includedTrinity
, change
);
491 included
.add(change
);
495 // todo maybe written pretty
496 final Collection
<FilePatchInProgress
.PatchChange
> includedNow
= myChangesTreeList
.getIncludedChanges();
497 final Set
<FilePatchInProgress
> toBeIncluded
= new HashSet
<FilePatchInProgress
>();
498 for (FilePatchInProgress
.PatchChange change
: includedNow
) {
499 final FilePatchInProgress patch
= change
.getPatchInProgress();
500 toBeIncluded
.add(patch
);
502 for (FilePatchInProgress
.PatchChange change
: changes
) {
503 final FilePatchInProgress patch
= change
.getPatchInProgress();
504 acceptChange(totalTrinity
, change
);
505 if (toBeIncluded
.contains(patch
) && patch
.baseExistsOrAdded()) {
506 acceptChange(includedTrinity
, change
);
507 included
.add(change
);
511 myInfoCalculator
.setTotal(totalTrinity
);
512 myInfoCalculator
.setIncluded(includedTrinity
);
513 myCommitLegendPanel
.update();
517 private class NewBaseSelector
implements Runnable
{
519 final FileChooserDescriptor descriptor
= new FileChooserDescriptor(false, true, false, false, false, false);
520 VirtualFile
[] selectedFiles
= FileChooser
.chooseFiles(myProject
, descriptor
);
521 if (selectedFiles
.length
!= 1 || selectedFiles
[0] == null) {
524 final VirtualFile selectedValue
= selectedFiles
[0];
526 final List
<FilePatchInProgress
.PatchChange
> selectedChanges
= myChangesTreeList
.getSelectedChanges();
527 if (selectedChanges
.size() >= 1) {
528 for (FilePatchInProgress
.PatchChange patchChange
: selectedChanges
) {
529 final FilePatchInProgress patch
= patchChange
.getPatchInProgress();
530 patch
.setNewBase(selectedValue
);
537 private List
<FilePatchInProgress
> changes2patches(final List
<FilePatchInProgress
.PatchChange
> selectedChanges
) {
538 return ObjectsConvertor
.convert(selectedChanges
, new Convertor
<FilePatchInProgress
.PatchChange
, FilePatchInProgress
>() {
539 public FilePatchInProgress
convert(FilePatchInProgress
.PatchChange o
) {
540 return o
.getPatchInProgress();
545 private class MapPopup
extends BaseListPopupStep
<VirtualFile
> {
546 private final Runnable myNewBaseSelector
;
548 private MapPopup(final @NotNull List
<?
extends VirtualFile
> aValues
, Runnable newBaseSelector
) {
549 super("Select base directory for a path", aValues
);
550 myNewBaseSelector
= newBaseSelector
;
554 public boolean isSpeedSearchEnabled() {
559 public PopupStep
onChosen(final VirtualFile selectedValue
, boolean finalChoice
) {
560 if (selectedValue
== null) {
561 myNewBaseSelector
.run();
564 final List
<FilePatchInProgress
.PatchChange
> selectedChanges
= myChangesTreeList
.getSelectedChanges();
565 if (selectedChanges
.size() >= 1) {
566 for (FilePatchInProgress
.PatchChange patchChange
: selectedChanges
) {
567 final FilePatchInProgress patch
= patchChange
.getPatchInProgress();
568 patch
.setNewBase(selectedValue
);
577 public String
getTextFor(VirtualFile value
) {
578 return value
== null ?
"Select base for a path" : value
.getPath();
582 private static class NamedTrinity
{
584 private int myModified
;
585 private int myDeleted
;
587 public NamedTrinity() {
593 public NamedTrinity(int added
, int modified
, int deleted
) {
595 myModified
= modified
;
599 public void plusAdded() {
603 public void plusModified() {
607 public void plusDeleted() {
611 public int getAdded() {
615 public int getModified() {
619 public int getDeleted() {
624 private static class ChangesLegendCalculator
implements CommitLegendPanel
.InfoCalculator
{
625 private NamedTrinity myTotal
;
626 private NamedTrinity myIncluded
;
628 private ChangesLegendCalculator() {
629 myTotal
= new NamedTrinity();
630 myIncluded
= new NamedTrinity();
633 public void setTotal(final NamedTrinity trinity
) {
637 public void setIncluded(final NamedTrinity trinity
) {
638 myIncluded
= trinity
;
641 public int getNew() {
642 return myTotal
.getAdded();
645 public int getModified() {
646 return myTotal
.getModified();
649 public int getDeleted() {
650 return myTotal
.getDeleted();
653 public int getIncludedNew() {
654 return myIncluded
.getAdded();
657 public int getIncludedModified() {
658 return myIncluded
.getModified();
661 public int getIncludedDeleted() {
662 return myIncluded
.getDeleted();
666 private static class MyChangeNodeDecorator
implements ChangeNodeDecorator
{
667 public void decorate(Change change
, SimpleColoredComponent component
, boolean isShowFlatten
) {
668 if (change
instanceof FilePatchInProgress
.PatchChange
) {
669 final FilePatchInProgress
.PatchChange patchChange
= (FilePatchInProgress
.PatchChange
) change
;
670 if (! isShowFlatten
) {
671 // add change subpath
672 final TextFilePatch filePatch
= patchChange
.getPatchInProgress().getPatch();
673 final String patchPath
= filePatch
.getAfterName() == null ? filePatch
.getBeforeName() : filePatch
.getAfterName();
674 component
.append(" ");
675 component
.append("["+ patchPath
+ "]", SimpleTextAttributes
.GRAY_ATTRIBUTES
);
677 if (patchChange
.getPatchInProgress().getCurrentStrip() > 0) {
678 component
.append(" stripped " + patchChange
.getPatchInProgress().getCurrentStrip(), SimpleTextAttributes
.GRAY_ITALIC_ATTRIBUTES
);
681 if (FilePatchStatus
.ADDED
.equals(patchChange
.getPatchInProgress().getStatus())) {
683 } else if (FilePatchStatus
.DELETED
.equals(patchChange
.getPatchInProgress().getStatus())) {
688 component
.append(" ");
689 component
.append(text
, SimpleTextAttributes
.GRAY_ATTRIBUTES
);
693 public List
<Pair
<String
, Stress
>> stressPartsOfFileName(final Change change
, final String parentPath
) {
694 if (change
instanceof FilePatchInProgress
.PatchChange
) {
695 final FilePatchInProgress
.PatchChange patchChange
= (FilePatchInProgress
.PatchChange
) change
;
696 final String basePath
= patchChange
.getPatchInProgress().getBase().getPath();
697 final String basePathCorrected
= basePath
.trim().replace('/', File
.separatorChar
);
698 if (parentPath
.startsWith(basePathCorrected
)) {
699 return Arrays
.asList(new Pair
<String
, Stress
>(basePathCorrected
, Stress
.BOLD
),
700 new Pair
<String
, Stress
>(StringUtil
.tail(parentPath
, basePathCorrected
.length()), Stress
.PLAIN
));
706 public void preDecorate(Change change
, ChangesBrowserNodeRenderer renderer
, boolean showFlatten
) {
710 public Collection
<FilePatchInProgress
> getIncluded() {
711 return ObjectsConvertor
.convert(myChangesTreeList
.getIncludedChanges(),
712 new Convertor
<FilePatchInProgress
.PatchChange
, FilePatchInProgress
>() {
713 public FilePatchInProgress
convert(FilePatchInProgress
.PatchChange o
) {
714 return o
.getPatchInProgress();
719 public LocalChangeList
getSelectedChangeList() {
720 return myChangeListChooser
.getSelectedList(myProject
);
724 protected void doOKAction() {
726 myCallback
.consume(this);
729 private class ZeroStrip
extends AnAction
{
730 private ZeroStrip() {
731 super("Remove Directories", "Remove Directories", IconLoader
.getIcon("/vcs/stripNull.png"));
735 public void actionPerformed(AnActionEvent e
) {
736 final List
<FilePatchInProgress
.PatchChange
> selectedChanges
= myChangesTreeList
.getSelectedChanges();
737 for (FilePatchInProgress
.PatchChange change
: selectedChanges
) {
738 change
.getPatchInProgress().setZero();
744 private class StripDown
extends AnAction
{
745 private StripDown() {
746 super("Restore Directory", "Restore Directory", IconLoader
.getIcon("/vcs/stripDown.png"));
750 public void update(AnActionEvent e
) {
751 e
.getPresentation().setEnabled(isEnabled());
755 public void actionPerformed(AnActionEvent e
) {
756 if (! isEnabled()) return;
757 final List
<FilePatchInProgress
.PatchChange
> selectedChanges
= myChangesTreeList
.getSelectedChanges();
758 for (FilePatchInProgress
.PatchChange change
: selectedChanges
) {
759 change
.getPatchInProgress().down();
764 private boolean isEnabled() {
765 final List
<FilePatchInProgress
.PatchChange
> selectedChanges
= myChangesTreeList
.getSelectedChanges();
766 if (selectedChanges
.isEmpty()) return false;
767 for (FilePatchInProgress
.PatchChange change
: selectedChanges
) {
768 if (! change
.getPatchInProgress().canDown()) return false;
774 private class StripUp
extends AnAction
{
776 super("Strip Directory", "Strip Directory", IconLoader
.getIcon("/vcs/stripUp.png"));
780 public void update(AnActionEvent e
) {
781 e
.getPresentation().setEnabled(isEnabled());
785 public void actionPerformed(AnActionEvent e
) {
786 if (! isEnabled()) return;
787 final List
<FilePatchInProgress
.PatchChange
> selectedChanges
= myChangesTreeList
.getSelectedChanges();
788 for (FilePatchInProgress
.PatchChange change
: selectedChanges
) {
789 change
.getPatchInProgress().up();
794 private boolean isEnabled() {
795 final List
<FilePatchInProgress
.PatchChange
> selectedChanges
= myChangesTreeList
.getSelectedChanges();
796 if (selectedChanges
.isEmpty()) return false;
797 for (FilePatchInProgress
.PatchChange change
: selectedChanges
) {
798 if (! change
.getPatchInProgress().canUp()) return false;
804 private class ResetStrip
extends AnAction
{
805 private ResetStrip() {
806 super("Reset Directories", "Reset Directories", IconLoader
.getIcon("/vcs/resetStrip.png"));
810 public void actionPerformed(AnActionEvent e
) {
811 final List
<FilePatchInProgress
.PatchChange
> selectedChanges
= myChangesTreeList
.getSelectedChanges();
812 for (FilePatchInProgress
.PatchChange change
: selectedChanges
) {
813 change
.getPatchInProgress().reset();
819 private class MyShowDiff
extends AnAction
{
820 private MyShowDiff() {
821 super("Show Diff", "Show Diff", IconLoader
.getIcon("/actions/diff.png"));
824 public void update(AnActionEvent e
) {
825 e
.getPresentation().setEnabled((! myPatches
.isEmpty()) && myContainBasedChanges
);
828 public void actionPerformed(AnActionEvent e
) {
829 if (myPatches
.isEmpty() || (! myContainBasedChanges
)) return;
830 final List
<FilePatchInProgress
.PatchChange
> changes
= getAllChanges();
831 Collections
.sort(changes
, MyChangeComparator
.getInstance());
832 final List
<FilePatchInProgress
.PatchChange
> selectedChanges
= myChangesTreeList
.getSelectedChanges();
835 final ArrayList
<DiffRequestPresentable
> diffRequestPresentables
= new ArrayList
<DiffRequestPresentable
>(changes
.size());
836 if (! selectedChanges
.isEmpty()) {
837 final FilePatchInProgress
.PatchChange c
= selectedChanges
.get(0);
838 for (FilePatchInProgress
.PatchChange change
: changes
) {
839 final FilePatchInProgress patchInProgress
= change
.getPatchInProgress();
840 if (! patchInProgress
.baseExistsOrAdded()) continue;
841 final DiffRequestPresentable diffRequestPresentable
= change
.createDiffRequestPresentable(myProject
);
842 if (diffRequestPresentable
!= null) {
843 diffRequestPresentables
.add(diffRequestPresentable
);
845 if (change
.equals(c
)) {
846 selectedIdx
= diffRequestPresentables
.size() - 1;
850 if (diffRequestPresentables
.isEmpty()) return;
851 ShowDiffAction
.showDiffImpl(myProject
, diffRequestPresentables
, selectedIdx
, ShowDiffAction
.DiffExtendUIFactory
.NONE
, false);
855 private static class MyChangeComparator
implements Comparator
<FilePatchInProgress
.PatchChange
> {
856 private static final MyChangeComparator ourInstance
= new MyChangeComparator();
858 public static MyChangeComparator
getInstance() {
862 public int compare(FilePatchInProgress
.PatchChange o1
, FilePatchInProgress
.PatchChange o2
) {
863 return o1
.getPatchInProgress().getIoCurrentBase().compareTo(o2
.getPatchInProgress().getIoCurrentBase());