Merge remote-tracking branch 'origin/master'
[idea/community.git] / plugins / svn4idea / src / org / jetbrains / idea / svn / dialogs / SelectLocationDialog.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 package org.jetbrains.idea.svn.dialogs;
17
18 import com.intellij.openapi.actionSystem.ActionManager;
19 import com.intellij.openapi.actionSystem.DefaultActionGroup;
20 import com.intellij.openapi.help.HelpManager;
21 import com.intellij.openapi.progress.ProgressManager;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.ui.DialogWrapper;
24 import com.intellij.openapi.ui.Messages;
25 import com.intellij.openapi.util.Disposer;
26 import com.intellij.openapi.util.Pair;
27 import com.intellij.openapi.util.Ref;
28 import org.jetbrains.annotations.NonNls;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31 import org.jetbrains.idea.svn.SvnBundle;
32 import org.jetbrains.idea.svn.SvnUtil;
33 import org.jetbrains.idea.svn.SvnVcs;
34 import org.jetbrains.idea.svn.commandLine.SvnBindException;
35 import org.jetbrains.idea.svn.dialogs.browser.UrlOpeningExpander;
36 import org.tmatesoft.svn.core.SVNURL;
37 import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
38 import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
39
40 import javax.swing.*;
41 import javax.swing.event.DocumentEvent;
42 import javax.swing.event.DocumentListener;
43 import javax.swing.event.TreeSelectionEvent;
44 import javax.swing.event.TreeSelectionListener;
45 import java.awt.*;
46
47 import static com.intellij.openapi.util.Pair.create;
48
49 /**
50  * @author alex
51  */
52 public class SelectLocationDialog extends DialogWrapper {
53   private final Project myProject;
54   private RepositoryBrowserComponent myRepositoryBrowser;
55   private final SVNURL myURL;
56   private final String myDstName;
57   private final String myDstLabel;
58   private JTextField myDstText;
59   private final boolean myIsShowFiles;
60   private final boolean myAllowActions;
61
62   @NonNls private static final String HELP_ID = "vcs.subversion.common";
63
64   // todo check that works when authenticated
65   @Nullable
66   public static SVNURL selectLocation(Project project, String url) {
67     SelectLocationDialog dialog = openDialog(project, url, null, null, true, false, null);
68
69     return dialog == null || !dialog.isOK() ? null : dialog.getSelectedURL();
70   }
71
72   @Nullable
73   public static Pair<SVNURL, SVNURL> selectLocation(Project project, @NotNull SVNURL url) {
74     SelectLocationDialog dialog = new SelectLocationDialog(project, url, null, null, true, true);
75     return dialog.showAndGet() ? create(dialog.getSelectedURL(), dialog.getRootUrl()) : null;
76   }
77
78   @Nullable
79   public static String selectCopyDestination(Project project, String url, String dstLabel, String dstName, boolean showFiles) {
80     SelectLocationDialog dialog =
81       openDialog(project, url, dstLabel, dstName, showFiles, false, SvnBundle.message("select.location.invalid.url.message", url));
82
83     return dialog == null || !dialog.isOK() ? null : SVNPathUtil.append(dialog.getSelectedURL().toString(), dialog.getDestinationName());
84   }
85
86   @Nullable
87   private static SelectLocationDialog openDialog(Project project,
88                                                  String url,
89                                                  String dstLabel,
90                                                  String dstName,
91                                                  boolean showFiles,
92                                                  boolean allowActions,
93                                                  String errorMessage) {
94     try {
95       SVNURL svnUrl = SvnUtil.createUrl(url);
96       final SVNURL repositoryUrl = initRoot(project, svnUrl);
97       if (repositoryUrl == null) {
98         Messages.showErrorDialog(project, "Can not detect repository root for URL: " + url,
99                                  SvnBundle.message("dialog.title.select.repository.location"));
100         return null;
101       }
102       SelectLocationDialog dialog = new SelectLocationDialog(project, repositoryUrl, dstLabel, dstName, showFiles, allowActions);
103       dialog.show();
104       return dialog;
105     }
106     catch (SvnBindException e) {
107       Messages.showErrorDialog(project, errorMessage != null ? errorMessage : e.getMessage(),
108                                SvnBundle.message("dialog.title.select.repository.location"));
109       return null;
110     }
111   }
112
113   private SelectLocationDialog(Project project, SVNURL url, String dstLabel, String dstName, boolean showFiles, boolean allowActions) {
114     super(project, true);
115     myProject = project;
116     myDstLabel = dstLabel;
117     myDstName = dstName;
118     myURL = url;
119     myIsShowFiles = showFiles;
120     myAllowActions = allowActions;
121     setTitle(SvnBundle.message("dialog.title.select.repository.location"));
122     getHelpAction().setEnabled(true);
123     init();
124   }
125
126   protected void doHelpAction() {
127     HelpManager.getInstance().invokeHelp(HELP_ID);
128   }
129
130   @NotNull
131   protected Action[] createActions() {
132     return new Action[]{getOKAction(), getCancelAction(), getHelpAction()};
133   }
134
135   protected String getDimensionServiceKey() {
136     return "svn.repositoryBrowser";
137   }
138
139   @Nullable
140   private static SVNURL initRoot(final Project project, final SVNURL url) throws SvnBindException {
141     final Ref<SVNURL> result = new Ref<SVNURL>();
142     final Ref<SvnBindException> excRef = new Ref<SvnBindException>();
143
144     ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
145       public void run() {
146         try {
147           result.set(SvnUtil.getRepositoryRoot(SvnVcs.getInstance(project), url));
148         } catch (SvnBindException e) {
149           excRef.set(e);
150         }
151       }
152     }, "Detecting repository root", true, project);
153     if (! excRef.isNull()) {
154       throw excRef.get();
155     }
156     return result.get();
157   }
158
159   protected void init() {
160     super.init();
161     final String urlString = myURL.toString();
162     if (myAllowActions) {
163       // initialize repo browser this way - to make actions work correctly
164       myRepositoryBrowser.setRepositoryURLs(new SVNURL[]{myURL}, myIsShowFiles, new UrlOpeningExpander.Factory(urlString, urlString), true);
165     }
166     else {
167       myRepositoryBrowser.setRepositoryURL(myURL, myIsShowFiles, new UrlOpeningExpander.Factory(urlString, urlString));
168     }
169     myRepositoryBrowser.addChangeListener(new TreeSelectionListener() {
170       public void valueChanged(TreeSelectionEvent e) {
171         getOKAction().setEnabled(isOKActionEnabled());
172       }
173     });
174   }
175
176   @Override
177   protected void dispose() {
178     super.dispose();
179     Disposer.dispose(myRepositoryBrowser);
180   }
181
182   protected JComponent createCenterPanel() {
183     JPanel panel = new JPanel();
184     panel.setLayout(new BorderLayout());
185
186     JPanel browserPanel = new JPanel();
187     browserPanel.setLayout(new GridBagLayout());
188
189     GridBagConstraints gc = new GridBagConstraints();
190     gc.insets = new Insets(2, 2, 2, 2);
191     gc.gridwidth = 2;
192     gc.gridheight = 1;
193     gc.gridx = 0;
194     gc.gridy = 0;
195     gc.anchor = GridBagConstraints.WEST;
196     gc.fill = GridBagConstraints.BOTH;
197     gc.weightx = 1;
198     gc.weighty = 1;
199
200
201     myRepositoryBrowser = new RepositoryBrowserComponent(SvnVcs.getInstance(myProject));
202     browserPanel.add(myRepositoryBrowser, gc);
203     if (myDstName != null) {
204       gc.gridy += 1;
205       gc.gridwidth = 1;
206       gc.gridx = 0;
207       gc.fill = GridBagConstraints.NONE;
208       gc.weightx = 0;
209       gc.weighty = 0;
210
211       JLabel dstLabel = new JLabel(myDstLabel);
212       browserPanel.add(dstLabel, gc);
213
214       gc.gridx += 1;
215       gc.weightx = 1;
216       gc.fill = GridBagConstraints.HORIZONTAL;
217
218       myDstText = new JTextField();
219       myDstText.setText(myDstName);
220       myDstText.selectAll();
221       browserPanel.add(myDstText, gc);
222
223       myDstText.getDocument().addDocumentListener(new DocumentListener() {
224         public void insertUpdate(DocumentEvent e) {
225           getOKAction().setEnabled(isOKActionEnabled());
226         }
227
228         public void removeUpdate(DocumentEvent e) {
229           getOKAction().setEnabled(isOKActionEnabled());
230         }
231
232         public void changedUpdate(DocumentEvent e) {
233           getOKAction().setEnabled(isOKActionEnabled());
234         }
235       });
236
237       dstLabel.setLabelFor(myDstText);
238       gc.gridx = 0;
239       gc.gridy += 1;
240       gc.gridwidth = 2;
241
242       browserPanel.add(new JSeparator(), gc);
243     }
244
245     if (myAllowActions) {
246       panel.add(createToolbar(), BorderLayout.NORTH);
247     }
248     panel.add(browserPanel, BorderLayout.CENTER);
249
250     return panel;
251   }
252
253   @NotNull
254   private JComponent createToolbar() {
255     DefaultActionGroup group = new DefaultActionGroup();
256     group.add(new RepositoryBrowserDialog.EditLocationAction(myRepositoryBrowser));
257
258     return ActionManager.getInstance().createActionToolbar(RepositoryBrowserDialog.PLACE_TOOLBAR, group, true).getComponent();
259   }
260
261   public JComponent getPreferredFocusedComponent() {
262     return (JComponent)myRepositoryBrowser.getPreferredFocusedComponent();
263   }
264
265   public boolean shouldCloseOnCross() {
266     return true;
267   }
268
269   public boolean isOKActionEnabled() {
270     boolean ok = myRepositoryBrowser.getSelectedURL() != null;
271     if (ok && myDstText != null) {
272       return myDstText.getText().trim().length() > 0;
273     }
274     return ok;
275   }
276
277   public String getDestinationName() {
278     return SVNEncodingUtil.uriEncode(myDstText.getText().trim());
279   }
280
281   @Nullable
282   public SVNURL getSelectedURL() {
283     return myRepositoryBrowser.getSelectedSVNURL();
284   }
285
286   @Nullable
287   public SVNURL getRootUrl() {
288     RepositoryTreeNode node = myRepositoryBrowser.getSelectedNode();
289
290     // find the most top parent of type RepositoryTreeNode
291     while (node != null && node.getParent() instanceof RepositoryTreeNode) {
292       node = (RepositoryTreeNode)node.getParent();
293     }
294
295     return node != null ? node.getURL() : null;
296   }
297 }