Merge remote-tracking branch 'origin/master'
[idea/community.git] / plugins / svn4idea / src / org / jetbrains / idea / svn / branchConfig / BranchConfigurationDialog.java
1 /*
2  * Copyright 2000-2014 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
17 package org.jetbrains.idea.svn.branchConfig;
18
19 import com.intellij.openapi.actionSystem.ActionToolbarPosition;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.openapi.ui.DialogWrapper;
22 import com.intellij.openapi.ui.MultiLineLabelUI;
23 import com.intellij.openapi.ui.TextFieldWithBrowseButton;
24 import com.intellij.openapi.util.Pair;
25 import com.intellij.openapi.vfs.VfsUtilCore;
26 import com.intellij.openapi.vfs.VirtualFile;
27 import com.intellij.ui.*;
28 import com.intellij.ui.components.JBList;
29 import com.intellij.util.ObjectUtils;
30 import org.jetbrains.annotations.NonNls;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33 import org.jetbrains.idea.svn.RootUrlInfo;
34 import org.jetbrains.idea.svn.SvnBundle;
35 import org.jetbrains.idea.svn.SvnUtil;
36 import org.jetbrains.idea.svn.SvnVcs;
37 import org.jetbrains.idea.svn.commandLine.SvnBindException;
38 import org.jetbrains.idea.svn.dialogs.SelectLocationDialog;
39 import org.tmatesoft.svn.core.SVNURL;
40 import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
41
42 import javax.swing.*;
43 import javax.swing.event.DocumentEvent;
44 import java.awt.*;
45 import java.awt.event.ActionEvent;
46 import java.awt.event.ActionListener;
47 import java.util.ArrayList;
48 import java.util.List;
49
50 /**
51  * @author yole
52  */
53 public class BranchConfigurationDialog extends DialogWrapper {
54   private JPanel myTopPanel;
55   private TextFieldWithBrowseButton myTrunkLocationTextField;
56   private JList myLocationList;
57   private JPanel myListPanel;
58   private JLabel myErrorPrompt;
59   private final NewRootBunch mySvnBranchConfigManager;
60   private final VirtualFile myRoot;
61
62   public BranchConfigurationDialog(@NotNull final Project project,
63                                    @NotNull final SvnBranchConfigurationNew configuration,
64                                    final @NotNull SVNURL rootUrl,
65                                    @NotNull final VirtualFile root,
66                                    @NotNull String url) {
67     super(project, true);
68     myRoot = root;
69     init();
70     setTitle(SvnBundle.message("configure.branches.title"));
71
72     final String trunkUrl = configuration.getTrunkUrl();
73     if (trunkUrl == null || trunkUrl.trim().length() == 0) {
74       configuration.setTrunkUrl(url);
75     }
76
77     mySvnBranchConfigManager = SvnBranchConfigurationManager.getInstance(project).getSvnBranchConfigManager();
78
79     myTrunkLocationTextField.setText(configuration.getTrunkUrl());
80     myTrunkLocationTextField.addActionListener(new ActionListener() {
81       public void actionPerformed(final ActionEvent e) {
82         Pair<SVNURL, SVNURL> selectionData = SelectLocationDialog.selectLocation(project, rootUrl);
83
84         if (selectionData != null && selectionData.getFirst() != null) {
85           myTrunkLocationTextField.setText(selectionData.getFirst().toString());
86         }
87       }
88     });
89
90     final TrunkUrlValidator trunkUrlValidator = new TrunkUrlValidator(rootUrl, configuration);
91     myTrunkLocationTextField.getTextField().getDocument().addDocumentListener(trunkUrlValidator);
92     trunkUrlValidator.textChanged(null);
93
94     myErrorPrompt.setUI(new MultiLineLabelUI());
95     myErrorPrompt.setForeground(SimpleTextAttributes.ERROR_ATTRIBUTES.getFgColor());
96
97     final MyListModel listModel = new MyListModel(configuration);
98     myLocationList = new JBList(listModel);
99
100     myListPanel.add(
101       ToolbarDecorator.createDecorator(myLocationList)
102         .setAddAction(new AnActionButtonRunnable() {
103
104           @Nullable private SVNURL usedRootUrl;
105
106           @Override
107           public void run(AnActionButton button) {
108             Pair<SVNURL, SVNURL> result = SelectLocationDialog.selectLocation(project, ObjectUtils.notNull(usedRootUrl, rootUrl));
109             if (result != null) {
110               String selectedUrl = result.getFirst().toString();
111               usedRootUrl = result.getSecond();
112               if (selectedUrl != null) {
113                 if (!configuration.getBranchUrls().contains(selectedUrl)) {
114                   configuration
115                     .addBranches(selectedUrl, new InfoStorage<List<SvnBranchItem>>(new ArrayList<SvnBranchItem>(), InfoReliability.empty));
116                   mySvnBranchConfigManager.reloadBranchesAsync(myRoot, selectedUrl, InfoReliability.setByUser);
117                   listModel.fireItemAdded();
118                   myLocationList.setSelectedIndex(listModel.getSize() - 1);
119                 }
120               }
121             }
122           }
123         }).setRemoveAction(new AnActionButtonRunnable() {
124         @Override
125         public void run(AnActionButton button) {
126           int selIndex = myLocationList.getSelectedIndex();
127           Object[] selection = myLocationList.getSelectedValues();
128           for (Object urlObj : selection) {
129             String url = (String)urlObj;
130             int index = configuration.getBranchUrls().indexOf(url);
131             configuration.removeBranch(url);
132             listModel.fireItemRemoved(index);
133           }
134           if (listModel.getSize() > 0) {
135             if (selIndex >= listModel.getSize())
136               selIndex = listModel.getSize() - 1;
137             myLocationList.setSelectedIndex(selIndex);
138           }
139         }
140       }).disableUpDownActions().setToolbarPosition(ActionToolbarPosition.BOTTOM).createPanel(), BorderLayout.CENTER);
141   }
142
143   private class TrunkUrlValidator extends DocumentAdapter {
144     private final SVNURL myRootUrl;
145     private final SvnBranchConfigurationNew myConfiguration;
146
147     private TrunkUrlValidator(final SVNURL rootUrl, final SvnBranchConfigurationNew configuration) {
148       myRootUrl = rootUrl;
149       myConfiguration = configuration;
150     }
151
152     protected void textChanged(final DocumentEvent e) {
153       SVNURL url = parseUrl(myTrunkLocationTextField.getText());
154
155       if (url != null) {
156         boolean isAncestor = SVNURLUtil.isAncestor(myRootUrl, url);
157         boolean areNotSame = isAncestor && !url.equals(myRootUrl);
158
159         if (areNotSame) {
160           myConfiguration.setTrunkUrl(url.toDecodedString());
161         }
162         myErrorPrompt.setText(areNotSame ? "" : SvnBundle.message("configure.branches.error.wrong.url", myRootUrl));
163       }
164     }
165
166     @Nullable
167     private SVNURL parseUrl(@NotNull String url) {
168       SVNURL result = null;
169
170       try {
171         result = SvnUtil.createUrl(url);
172       }
173       catch (SvnBindException e) {
174         myErrorPrompt.setText(e.getMessage());
175       }
176
177       return result;
178     }
179   }
180
181   @Nullable
182   protected JComponent createCenterPanel() {
183     return myTopPanel;
184   }
185
186   @Override
187   @NonNls
188   protected String getDimensionServiceKey() {
189     return "Subversion.BranchConfigurationDialog";
190   }
191
192   public static void configureBranches(final Project project, @Nullable VirtualFile file) {
193     if (file == null) {
194       return;
195     }
196
197     final RootUrlInfo wcRoot = SvnVcs.getInstance(project).getSvnFileUrlMapping().getWcRootForFilePath(VfsUtilCore.virtualToIoFile(file));
198     if (wcRoot == null) {
199       return;
200     }
201
202     SvnBranchConfigurationNew configuration = SvnBranchConfigurationManager.getInstance(project).get(file);
203     SvnBranchConfigurationNew clonedConfiguration = configuration.copy();
204     BranchConfigurationDialog dlg =
205       new BranchConfigurationDialog(project, clonedConfiguration, wcRoot.getRepositoryUrlUrl(), file, wcRoot.getUrl());
206     if (dlg.showAndGet()) {
207       SvnBranchConfigurationManager.getInstance(project).setConfiguration(file, clonedConfiguration);
208     }
209   }
210
211   private static class MyListModel extends AbstractListModel {
212     private final SvnBranchConfigurationNew myConfiguration;
213     private List<String> myBranchUrls;
214
215     public MyListModel(final SvnBranchConfigurationNew configuration) {
216       myConfiguration = configuration;
217       myBranchUrls = myConfiguration.getBranchUrls();
218     }
219
220     public int getSize() {
221       return myBranchUrls.size();
222     }
223
224     public Object getElementAt(final int index) {
225       return myBranchUrls.get(index);
226     }
227
228     public void fireItemAdded() {
229       final int index = myConfiguration.getBranchUrls().size() - 1;
230       myBranchUrls = myConfiguration.getBranchUrls();
231       super.fireIntervalAdded(this, index, index);
232     }
233
234     public void fireItemRemoved(final int index) {
235       myBranchUrls = myConfiguration.getBranchUrls();
236       super.fireIntervalRemoved(this, index, index);
237     }
238   }
239 }