ExcludedFileIndex -> FileIndexFacade; use it in PsiClassImplUtil
[idea/community.git] / plugins / svn4idea / src / org / jetbrains / idea / svn / integrate / IntegratedSelectedOptionsDialog.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package org.jetbrains.idea.svn.integrate;
17
18 import com.intellij.lifecycle.PeriodicalTasksCloser;
19 import com.intellij.openapi.actionSystem.*;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.fileChooser.FileChooser;
22 import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.ui.DialogWrapper;
25 import com.intellij.openapi.ui.Messages;
26 import com.intellij.openapi.util.Pair;
27 import com.intellij.openapi.roots.FileIndexFacade;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.util.PlatformIcons;
30 import com.intellij.util.ui.UIUtil;
31 import org.jetbrains.annotations.Nullable;
32 import org.jetbrains.idea.svn.*;
33 import org.tmatesoft.svn.core.SVNException;
34 import org.tmatesoft.svn.core.SVNURL;
35 import org.tmatesoft.svn.core.wc.SVNInfo;
36 import org.tmatesoft.svn.core.wc.SVNRevision;
37 import org.tmatesoft.svn.core.wc.SVNWCClient;
38
39 import javax.swing.*;
40 import javax.swing.event.ListSelectionEvent;
41 import javax.swing.event.ListSelectionListener;
42 import java.io.File;
43 import java.util.*;
44
45 public class IntegratedSelectedOptionsDialog extends DialogWrapper {
46   private JPanel contentPane;
47   private JCheckBox myDryRunCheckbox;
48   private JList myWorkingCopiesList;
49   private JComponent myToolbar;
50   private JLabel mySourceInfoLabel;
51   private JLabel myTargetInfoLabel;
52   private JLabel myWcListTitleLabel;
53   private JCheckBox myIgnoreWhitespacesCheckBox;
54
55   private final Project myProject;
56   private final String mySelectedBranchUrl;
57   private final SvnVcs myVcs;
58   private final String mySelectedRepositoryUUID;
59
60   private DefaultActionGroup myGroup;
61
62   private boolean myMustSelectBeforeOk;
63
64   public IntegratedSelectedOptionsDialog(final Project project, final SVNURL currentBranch, final String selectedBranchUrl) {
65     super(project, true);
66     myMustSelectBeforeOk = true;
67     myProject = project;
68     mySelectedBranchUrl = selectedBranchUrl;
69     myVcs = SvnVcs.getInstance(myProject);
70
71     mySelectedRepositoryUUID = SvnUtil.getRepositoryUUID(myVcs, currentBranch);
72
73     setTitle(SvnBundle.message("action.Subversion.integrate.changes.dialog.title"));
74     init();
75
76     myWorkingCopiesList.setModel(new DefaultListModel());
77     myWorkingCopiesList.addListSelectionListener(new ListSelectionListener() {
78       public void valueChanged(final ListSelectionEvent e) {
79         setOKActionEnabled((! myMustSelectBeforeOk) || (myWorkingCopiesList.getSelectedIndex() != -1));
80       }
81     });
82     setOKActionEnabled((! myMustSelectBeforeOk) || (myWorkingCopiesList.getSelectedIndex() != -1));
83
84     final List<WorkingCopyInfo> workingCopyInfoList = new ArrayList<WorkingCopyInfo>();
85     final Set<String> workingCopies = SvnBranchMapperManager.getInstance().get(mySelectedBranchUrl);
86     if (workingCopies != null) {
87       for (String workingCopy : workingCopies) {
88         workingCopyInfoList.add(new WorkingCopyInfo(workingCopy, underProject(new File(workingCopy))));
89       }
90     }
91     Collections.sort(workingCopyInfoList, WorkingCopyInfoComparator.getInstance());
92
93     for (WorkingCopyInfo info : workingCopyInfoList) {
94       ((DefaultListModel)myWorkingCopiesList.getModel()).addElement(info);
95     }
96     if (!workingCopyInfoList.isEmpty()) {
97       myWorkingCopiesList.setSelectedIndex(0);
98     }
99
100     SvnConfiguration svnConfig = SvnConfiguration.getInstance(myVcs.getProject());
101     myDryRunCheckbox.setSelected(svnConfig.MERGE_DRY_RUN);
102     myIgnoreWhitespacesCheckBox.setSelected(svnConfig.IGNORE_SPACES_IN_MERGE);
103
104     mySourceInfoLabel.setText(SvnBundle.message("action.Subversion.integrate.changes.branch.info.source.label.text", currentBranch));
105     myTargetInfoLabel.setText(SvnBundle.message("action.Subversion.integrate.changes.branch.info.target.label.text", selectedBranchUrl));
106
107     final String addText = SvnBundle.message("action.Subversion.integrate.changes.dialog.add.wc.text");
108     final AnAction addAction = new AnAction(addText, addText, PlatformIcons.ADD_ICON) {
109       {
110         registerCustomShortcutSet(CommonShortcuts.INSERT, myWorkingCopiesList);
111       }
112
113       public void actionPerformed(final AnActionEvent e) {
114         final VirtualFile vFile = FileChooser.chooseFile(myProject, FileChooserDescriptorFactory.createSingleFolderDescriptor());
115         if (vFile != null) {
116           final File file = new File(vFile.getPath());
117           if (hasDuplicate(file)) {
118             return; // silently do not add duplicate
119           }
120
121           final String repositoryUUID = SvnUtil.getRepositoryUUID(myVcs, file);
122
123           // local not consistent copy can not prevent us from integration: only remote local copy is really involved
124           if ((mySelectedRepositoryUUID != null) && (! mySelectedRepositoryUUID.equals(repositoryUUID))) {
125             if (OK_EXIT_CODE == Messages.showOkCancelDialog((repositoryUUID == null) ? SvnBundle.message("action.Subversion.integrate.changes.message.not.under.control.text")
126                                                             : SvnBundle.message("action.Subversion.integrate.changes.message.another.wc.text"),
127                                                             getTitle(), UIUtil.getWarningIcon())) {
128               onOkToAdd(file);
129             }
130           }
131           else {
132             onOkToAdd(file);
133           }
134         }
135       }
136     };
137
138     myGroup.add(addAction);
139
140     final String removeText = SvnBundle.message("action.Subversion.integrate.changes.dialog.remove.wc.text");
141     myGroup.add(new AnAction(removeText, removeText, PlatformIcons.DELETE_ICON) {
142       {
143         registerCustomShortcutSet(CommonShortcuts.DELETE, myWorkingCopiesList);
144       }
145
146       public void update(final AnActionEvent e) {
147         final Presentation presentation = e.getPresentation();
148         final int idx = (myWorkingCopiesList == null) ? -1 : myWorkingCopiesList.getSelectedIndex();
149         presentation.setEnabled(idx != -1);
150       }
151
152       public void actionPerformed(final AnActionEvent e) {
153         final int idx = myWorkingCopiesList.getSelectedIndex();
154         if (idx != -1) {
155           final DefaultListModel model = (DefaultListModel)myWorkingCopiesList.getModel();
156           final WorkingCopyInfo info = (WorkingCopyInfo)model.get(idx);
157           model.removeElementAt(idx);
158           SvnBranchMapperManager.getInstance().remove(mySelectedBranchUrl, new File(info.getLocalPath()));
159         }
160       }
161     });
162   }
163
164   public void setSelectedWcPath(final String path) {
165     final ListModel model = myWorkingCopiesList.getModel();
166     final int size = model.getSize();
167     for (int i = 0; i < size; i++) {
168       final WorkingCopyInfo info = (WorkingCopyInfo) model.getElementAt(i);
169       if (info.getLocalPath().equals(path)) {
170         myWorkingCopiesList.setSelectedValue(info, true);
171         return;
172       }
173     }
174   }
175
176   public void selectWcopyRootOnly() {
177     myMustSelectBeforeOk = false;
178     setTitle(SvnBundle.message("dialog.Subversion.select.working.copy.title"));
179     myIgnoreWhitespacesCheckBox.setVisible(false);
180     myDryRunCheckbox.setVisible(false);
181     myWcListTitleLabel.setText(SvnBundle.message("dialog.Subversion.select.working.copy.wcopy.list.title"));
182   }
183
184   private void createUIComponents() {
185     myGroup = new DefaultActionGroup();
186     final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, myGroup, false);
187     myToolbar = actionToolbar.getComponent();
188   }
189
190   private boolean hasDuplicate(final File file) {
191     final DefaultListModel model = (DefaultListModel)myWorkingCopiesList.getModel();
192     final String path = file.getAbsolutePath();
193
194     for (int i = 0; i < model.getSize(); i++) {
195       final WorkingCopyInfo info = (WorkingCopyInfo)model.getElementAt(i);
196       if (path.equals(info.getLocalPath())) {
197         return true;
198       }
199     }
200     return false;
201   }
202
203   private void onOkToAdd(final File file) {
204     final WorkingCopyInfo info = new WorkingCopyInfo(file.getAbsolutePath(), underProject(file));
205     final DefaultListModel model = (DefaultListModel) myWorkingCopiesList.getModel();
206     model.addElement(info);
207     myWorkingCopiesList.setSelectedValue(info, true);
208     SvnBranchMapperManager.getInstance().put(mySelectedBranchUrl, file.getAbsolutePath());
209   }
210
211   private boolean underProject(final File file) {
212     final VirtualFile vf = SvnUtil.getVirtualFile(file.getAbsolutePath());
213     return (vf == null) || PeriodicalTasksCloser.getInstance().safeGetService(myProject, FileIndexFacade.class).isInContent(vf);
214   }
215
216   public WorkingCopyInfo getSelectedWc() {
217     return (WorkingCopyInfo)myWorkingCopiesList.getSelectedValue();
218   }
219
220   public void saveOptions() {
221     SvnConfiguration svnConfig = SvnConfiguration.getInstance(myVcs.getProject());
222     svnConfig.MERGE_DRY_RUN = myDryRunCheckbox.isSelected();
223     svnConfig.IGNORE_SPACES_IN_MERGE = myIgnoreWhitespacesCheckBox.isSelected();
224   }
225
226   protected JComponent createCenterPanel() {
227     return contentPane;
228   }
229
230   public static class WorkingCopyInfoComparator implements Comparator<WorkingCopyInfo> {
231     private static final WorkingCopyInfoComparator instance = new WorkingCopyInfoComparator();
232
233     public static WorkingCopyInfoComparator getInstance() {
234       return instance;
235     }
236
237     private WorkingCopyInfoComparator() {
238     }
239
240     public int compare(final WorkingCopyInfo o1, final WorkingCopyInfo o2) {
241       return o1.getLocalPath().compareTo(o2.getLocalPath());
242     }
243   }
244
245   @Nullable
246   private static SVNURL realTargetUrl(final SvnVcs vcs, final WorkingCopyInfo info, final String targetBranchUrl) {
247     final SVNWCClient client = vcs.createWCClient();
248     try {
249       final SVNInfo svnInfo = client.doInfo(new File(info.getLocalPath()), SVNRevision.WORKING);
250       final SVNURL svnurl = svnInfo.getURL();
251
252       if ((svnurl != null) && (svnurl.toString().startsWith(targetBranchUrl))) {
253         return svnurl;
254       }
255     }
256     catch (SVNException e) {
257       // tracked by return value
258     }
259     return null;
260   }
261
262   @Nullable
263   public static Pair<WorkingCopyInfo, SVNURL> selectWorkingCopy(final Project project, final SVNURL currentBranch, final String targetBranch,
264                                                                 final boolean showIntegrationParameters,
265                                                                 final String selectedLocalBranchPath, final String dialogTitle) {
266     final IntegratedSelectedOptionsDialog dialog = new IntegratedSelectedOptionsDialog(project, currentBranch, targetBranch);
267     if (! showIntegrationParameters) {
268       dialog.selectWcopyRootOnly();
269     }
270     if (selectedLocalBranchPath != null) {
271       dialog.setSelectedWcPath(selectedLocalBranchPath);
272     }
273     if (dialogTitle != null) {
274       dialog.setTitle(dialogTitle);
275     }
276     dialog.show();
277
278     if (dialog.isOK()) {
279       ApplicationManager.getApplication().saveAll();
280       dialog.saveOptions();
281
282       final WorkingCopyInfo info = dialog.getSelectedWc();
283       if (info != null) {
284         final File file = new File(info.getLocalPath());
285         if ((! file.exists()) || (! file.isDirectory())) {
286           Messages.showErrorDialog(SvnBundle.message("action.Subversion.integrate.changes.error.target.not.dir.text"),
287                                    SvnBundle.message("action.Subversion.integrate.changes.messages.title"));
288           return null;
289         }
290
291         final SVNURL targetUrl = realTargetUrl(SvnVcs.getInstance(project), info, targetBranch);
292
293         if (targetUrl == null) {
294           Messages.showErrorDialog(SvnBundle.message("action.Subversion.integrate.changes.error.not.versioned.text"),
295                                    SvnBundle.message("action.Subversion.integrate.changes.messages.title"));
296           return null;
297         }
298         return new Pair<WorkingCopyInfo, SVNURL>(info, targetUrl);
299       }
300     }
301     return null;
302   }
303 }