Add descriptive message to directory browsers
[egit/eclipse.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / components / RepositorySelectionPage.java
blob8c3cb0e79c976709182063c6f274861b9ea6c844
1 /*******************************************************************************
2 * Copyright (C) 2007, 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>
4 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
6 * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
7 * Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
9 * All rights reserved. This program and the accompanying materials
10 * are made available under the terms of the Eclipse Public License v1.0
11 * which accompanies this distribution, and is available at
12 * http://www.eclipse.org/legal/epl-v10.html
13 *******************************************************************************/
14 package org.eclipse.egit.ui.internal.components;
16 import java.io.File;
17 import java.net.URISyntaxException;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.TreeMap;
21 import java.util.regex.Pattern;
23 import org.eclipse.egit.core.RepositoryUtil;
24 import org.eclipse.egit.core.securestorage.UserPasswordCredentials;
25 import org.eclipse.egit.ui.Activator;
26 import org.eclipse.egit.ui.UIPreferences;
27 import org.eclipse.egit.ui.UIUtils;
28 import org.eclipse.egit.ui.UIUtils.IPreviousValueProposalHandler;
29 import org.eclipse.egit.ui.internal.SecureStoreUtils;
30 import org.eclipse.egit.ui.internal.UIText;
31 import org.eclipse.egit.ui.internal.clone.GitUrlChecker;
32 import org.eclipse.egit.ui.internal.components.RemoteSelectionCombo.IRemoteSelectionListener;
33 import org.eclipse.egit.ui.internal.components.RemoteSelectionCombo.SelectionType;
34 import org.eclipse.egit.ui.internal.provisional.wizards.GitRepositoryInfo;
35 import org.eclipse.egit.ui.internal.provisional.wizards.IRepositorySearchResult;
36 import org.eclipse.jface.dialogs.Dialog;
37 import org.eclipse.jface.layout.GridDataFactory;
38 import org.eclipse.jface.preference.IPreferenceStore;
39 import org.eclipse.jface.wizard.WizardPage;
40 import org.eclipse.jgit.lib.Constants;
41 import org.eclipse.jgit.transport.RemoteConfig;
42 import org.eclipse.jgit.transport.URIish;
43 import org.eclipse.jgit.util.FS;
44 import org.eclipse.osgi.util.NLS;
45 import org.eclipse.swt.SWT;
46 import org.eclipse.swt.dnd.Clipboard;
47 import org.eclipse.swt.dnd.TextTransfer;
48 import org.eclipse.swt.events.ModifyEvent;
49 import org.eclipse.swt.events.ModifyListener;
50 import org.eclipse.swt.events.SelectionAdapter;
51 import org.eclipse.swt.events.SelectionEvent;
52 import org.eclipse.swt.events.SelectionListener;
53 import org.eclipse.swt.events.VerifyEvent;
54 import org.eclipse.swt.events.VerifyListener;
55 import org.eclipse.swt.layout.GridData;
56 import org.eclipse.swt.layout.GridLayout;
57 import org.eclipse.swt.widgets.Button;
58 import org.eclipse.swt.widgets.Combo;
59 import org.eclipse.swt.widgets.Composite;
60 import org.eclipse.swt.widgets.DirectoryDialog;
61 import org.eclipse.swt.widgets.Display;
62 import org.eclipse.swt.widgets.Event;
63 import org.eclipse.swt.widgets.Group;
64 import org.eclipse.swt.widgets.Label;
65 import org.eclipse.swt.widgets.Text;
66 import org.eclipse.ui.PlatformUI;
68 /**
69 * Wizard page that allows the user entering the location of a remote repository
70 * by specifying URL manually or selecting a preconfigured remote repository.
72 public class RepositorySelectionPage extends WizardPage implements IRepositorySearchResult {
74 private static final String EMPTY_STRING = ""; //$NON-NLS-1$
76 private final static String USED_URIS_PREF = "RepositorySelectionPage.UsedUris"; //$NON-NLS-1$
78 private final List<RemoteConfig> configuredRemotes;
80 private final boolean sourceSelection;
82 private final String presetUri;
84 private Group authGroup;
86 private Text uriText;
88 private Text hostText;
90 private Text pathText;
92 private Text userText;
94 private Text passText;
96 private Button storeCheckbox;
98 private Combo scheme;
100 private Text portText;
102 private int eventDepth;
104 private URIish uri;
106 private RemoteConfig remoteConfig;
108 private RepositorySelection selection;
110 private Composite remotePanel;
112 private Button remoteButton;
114 private RemoteSelectionCombo remoteCombo;
116 private Composite uriPanel;
118 private Button uriButton;
120 private IPreviousValueProposalHandler uriProposalHandler;
122 private String user = EMPTY_STRING;
124 private String password = EMPTY_STRING;
126 private boolean storeInSecureStore;
128 private String helpContext = null;
131 * Transport protocol abstraction
133 * TODO rework this to become part of JGit API
135 public static class Protocol {
136 /** Ordered list of all protocols **/
137 private static final TreeMap<String, Protocol> protocols = new TreeMap<>();
139 /** Git native transfer */
140 public static final Protocol GIT = new Protocol("git", //$NON-NLS-1$
141 UIText.RepositorySelectionPage_tip_git, true, true, false);
143 /** Git over SSH */
144 public static final Protocol SSH = new Protocol("ssh", //$NON-NLS-1$
145 UIText.RepositorySelectionPage_tip_ssh, true, true, true) {
146 @Override
147 public boolean handles(URIish uri) {
148 if (!uri.isRemote())
149 return false;
150 final String scheme = uri.getScheme();
151 if (getDefaultScheme().equals(scheme))
152 return true;
153 if ("ssh+git".equals(scheme)) //$NON-NLS-1$
154 return true;
155 if ("git+ssh".equals(scheme)) //$NON-NLS-1$
156 return true;
157 if (scheme == null && uri.getHost() != null
158 && uri.getPath() != null)
159 return true;
160 return false;
164 /** Secure FTP */
165 public static final Protocol SFTP = new Protocol("sftp", //$NON-NLS-1$
166 UIText.RepositorySelectionPage_tip_sftp, true, true, true);
168 /** HTTP */
169 public static final Protocol HTTP = new Protocol("http", //$NON-NLS-1$
170 UIText.RepositorySelectionPage_tip_http, true, true, true);
172 /** Secure HTTP */
173 public static final Protocol HTTPS = new Protocol("https", //$NON-NLS-1$
174 UIText.RepositorySelectionPage_tip_https, true, true, true);
176 /** FTP */
177 public static final Protocol FTP = new Protocol("ftp", //$NON-NLS-1$
178 UIText.RepositorySelectionPage_tip_ftp, true, true, true);
180 /** Local repository */
181 public static final Protocol FILE = new Protocol("file", //$NON-NLS-1$
182 UIText.RepositorySelectionPage_tip_file, false, false, false) {
183 @Override
184 public boolean handles(URIish uri) {
185 if (getDefaultScheme().equals(uri.getScheme()))
186 return true;
187 if (uri.getHost() != null || uri.getPort() > 0
188 || uri.getUser() != null || uri.getPass() != null
189 || uri.getPath() == null)
190 return false;
191 if (uri.getScheme() == null)
192 return FS.DETECTED
193 .resolve(new File("."), uri.getPath()).isDirectory(); //$NON-NLS-1$
194 return false;
198 private final String defaultScheme;
200 private final String tooltip;
202 private final boolean hasHost;
204 private final boolean hasPort;
206 private final boolean canAuthenticate;
208 private Protocol(String defaultScheme, String tooltip,
209 boolean hasHost, boolean hasPort, boolean canAuthenticate) {
210 this.defaultScheme = defaultScheme;
211 this.tooltip = tooltip;
212 this.hasHost = hasHost;
213 this.hasPort = hasPort;
214 this.canAuthenticate = canAuthenticate;
215 protocols.put(defaultScheme, this);
219 * @param uri
220 * URI to match against this protocol
221 * @return {@code true} if the uri is handled by this protocol
223 public boolean handles(URIish uri) {
224 return getDefaultScheme().equals(uri.getScheme());
228 * @return the default protocol scheme
230 public String getDefaultScheme() {
231 return defaultScheme;
235 * @return the tooltip text describing the protocol
237 public String getTooltip() {
238 return tooltip;
242 * @return true if protocol has host segment
244 public boolean hasHost() {
245 return hasHost;
249 * @return true if protocol has port
251 public boolean hasPort() {
252 return hasPort;
256 * @return true if protocol can authenticate
258 public boolean canAuthenticate() {
259 return canAuthenticate;
263 * @return all protocols
265 public static Protocol[] values() {
266 return protocols.values().toArray(new Protocol[protocols.size()]);
270 * Lookup protocol supporting given default URL scheme
272 * @param scheme
273 * default scheme to lookup protocol for
274 * @return protocol matching scheme or null
276 public static Protocol fromDefaultScheme(String scheme) {
277 return protocols.get(scheme);
281 * Lookup protocol handling given URI
283 * @param uri URI to lookup protocol for
284 * @return protocol handling this URI
286 public static Protocol fromUri(URIish uri) {
287 for (Protocol p : protocols.values())
288 if (p.handles(uri))
289 return p;
290 return null;
295 * Create repository selection page, allowing user specifying URI or
296 * (optionally) choosing from preconfigured remotes list.
297 * <p>
298 * Wizard page is created without image, just with text description.
300 * @param sourceSelection
301 * true if dialog is used for source selection; false otherwise
302 * (destination selection). This indicates appropriate text
303 * messages.
304 * @param configuredRemotes
305 * list of configured remotes that user may select as an
306 * alternative to manual URI specification. Remotes appear in
307 * given order in GUI, with
308 * {@value Constants#DEFAULT_REMOTE_NAME} as the default choice.
309 * List may be null or empty - no remotes configurations appear
310 * in this case. Note that the provided list may be changed by
311 * this constructor.
312 * @param presetUri
313 * the pre-set URI, may be null
315 public RepositorySelectionPage(final boolean sourceSelection,
316 final List<RemoteConfig> configuredRemotes, String presetUri) {
318 super(RepositorySelectionPage.class.getName());
320 this.uri = new URIish();
321 this.sourceSelection = sourceSelection;
323 String preset = presetUri;
324 if (presetUri == null) {
325 Clipboard clipboard = new Clipboard(Display.getCurrent());
326 String text = (String) clipboard
327 .getContents(TextTransfer.getInstance());
328 if (text != null) {
329 text = GitUrlChecker.sanitizeAsGitUrl(text);
330 if (GitUrlChecker.isValidGitUrl(text)) {
331 preset = text;
334 clipboard.dispose();
336 this.presetUri = preset;
338 this.configuredRemotes = getUsableConfigs(configuredRemotes);
340 selection = RepositorySelection.INVALID_SELECTION;
342 if (sourceSelection) {
343 setTitle(UIText.RepositorySelectionPage_sourceSelectionTitle);
344 setDescription(UIText.RepositorySelectionPage_sourceSelectionDescription);
345 } else {
346 setTitle(UIText.RepositorySelectionPage_destinationSelectionTitle);
347 setDescription(UIText.RepositorySelectionPage_destinationSelectionDescription);
350 storeInSecureStore = getPreferenceStore().getBoolean(
351 UIPreferences.CLONE_WIZARD_STORE_SECURESTORE);
355 * Create repository selection page, allowing user specifying URI, with no
356 * preconfigured remotes selection.
358 * @param sourceSelection
359 * true if dialog is used for source selection; false otherwise
360 * (destination selection). This indicates appropriate text
361 * messages.
362 * @param presetUri
363 * the pre-set URI, may be null
365 public RepositorySelectionPage(final boolean sourceSelection,
366 String presetUri) {
367 this(sourceSelection, null, presetUri);
371 * No args constructor; needed because the page is provided by the extension
372 * point {@code org.eclipse.egit.ui.cloneSourceProvider}
374 public RepositorySelectionPage() {
375 this(true, null);
379 * @return repository selection representing current page state.
381 public RepositorySelection getSelection() {
382 return selection;
386 * Compare current repository selection set by user to provided one.
388 * @param s
389 * repository selection to compare.
390 * @return true if provided selection is equal to current page selection,
391 * false otherwise.
393 public boolean selectionEquals(final RepositorySelection s) {
394 return selection.equals(s);
397 @Override
398 public void createControl(final Composite parent) {
399 final Composite panel = new Composite(parent, SWT.NULL);
400 panel.setLayout(new GridLayout());
402 if (configuredRemotes != null)
403 createRemotePanel(panel);
404 else
405 createRemoteNamePanel(panel);
407 createUriPanel(panel);
409 if (presetUri != null)
410 updateFields(presetUri);
412 updateRemoteAndURIPanels();
413 Dialog.applyDialogFont(panel);
414 setControl(panel);
416 checkPage();
419 private void createRemotePanel(final Composite parent) {
420 remoteButton = new Button(parent, SWT.RADIO);
421 remoteButton
422 .setText(UIText.RepositorySelectionPage_configuredRemoteChoice
423 + ":"); //$NON-NLS-1$
424 remoteButton.setSelection(true);
426 remotePanel = new Composite(parent, SWT.NULL);
427 remotePanel.setLayout(new GridLayout());
428 final GridData gd = new GridData();
429 gd.grabExcessHorizontalSpace = true;
430 gd.horizontalAlignment = SWT.FILL;
431 remotePanel.setLayoutData(gd);
433 SelectionType selectionType = sourceSelection ? SelectionType.FETCH : SelectionType.PUSH;
434 remoteCombo = new RemoteSelectionCombo(remotePanel, SWT.NULL, selectionType);
435 remoteConfig = remoteCombo.setItems(configuredRemotes);
436 remoteCombo.addRemoteSelectionListener(new IRemoteSelectionListener() {
437 @Override
438 public void remoteSelected(RemoteConfig rc) {
439 remoteConfig = rc;
440 checkPage();
447 * @param panel
449 protected void createRemoteNamePanel(Composite panel) {
450 // Only used by subclass
453 private void createUriPanel(final Composite parent) {
454 if (configuredRemotes != null) {
455 uriButton = new Button(parent, SWT.RADIO);
456 uriButton.setText(UIText.RepositorySelectionPage_uriChoice + ":"); //$NON-NLS-1$
457 uriButton.addSelectionListener(new SelectionAdapter() {
458 @Override
459 public void widgetSelected(SelectionEvent e) {
460 // occurs either on selection or unselection event
461 updateRemoteAndURIPanels();
462 checkPage();
467 uriPanel = new Composite(parent, SWT.NULL);
468 uriPanel.setLayout(new GridLayout());
469 final GridData gd = new GridData();
470 gd.grabExcessHorizontalSpace = true;
471 gd.horizontalAlignment = SWT.FILL;
472 uriPanel.setLayoutData(gd);
474 createLocationGroup(uriPanel);
475 createConnectionGroup(uriPanel);
476 authGroup = createAuthenticationGroup(uriPanel);
479 private void createLocationGroup(final Composite parent) {
480 final Group g = createGroup(parent,
481 UIText.RepositorySelectionPage_groupLocation);
483 g.setLayout(new GridLayout(3, false));
485 newLabel(g, UIText.RepositorySelectionPage_promptURI + ":"); //$NON-NLS-1$
486 uriText = new Text(g, SWT.BORDER);
488 if (presetUri != null) {
489 uriText.setText(presetUri);
490 uriText.selectAll();
493 uriText.setLayoutData(createFieldGridData());
494 uriText.addModifyListener(new ModifyListener() {
495 @Override
496 public void modifyText(final ModifyEvent e) {
497 updateFields(uriText.getText());
501 uriProposalHandler = UIUtils.addPreviousValuesContentProposalToText(
502 uriText, USED_URIS_PREF);
504 Button browseButton = new Button(g, SWT.NULL);
505 browseButton.setText(UIText.RepositorySelectionPage_BrowseLocalFile);
506 browseButton.addSelectionListener(new SelectionAdapter() {
508 @Override
509 public void widgetSelected(SelectionEvent evt) {
510 DirectoryDialog dialog = new DirectoryDialog(getShell());
511 if (sourceSelection) {
512 dialog.setMessage(
513 UIText.RepositorySelectionPage_sourceSelectionTitle);
514 } else {
515 dialog.setMessage(
516 UIText.RepositorySelectionPage_destinationSelectionTitle);
518 // if a file was selected before, let's try to open
519 // the directory dialog on the same directory
520 if (!uriText.getText().isEmpty()) {
521 try {
522 // first we try if this is a simple file name
523 File testFile = new File(uriText.getText());
524 if (testFile.exists()) {
525 dialog.setFilterPath(testFile.getPath());
526 } else {
527 // this could still be a file URIish
528 URIish testUri = new URIish(uriText.getText());
529 if (Protocol.FILE.defaultScheme
530 .equals(testUri.getScheme())) {
531 testFile = new File(testUri.getPath());
532 if (testFile.exists()) {
533 dialog.setFilterPath(testFile.getPath());
537 } catch (IllegalArgumentException | URISyntaxException e) {
538 // ignore here, we just' don't set the directory in the
539 // browser
542 // if nothing else, we start the search from the default folder for repositories
543 String filterPath = dialog.getFilterPath();
544 if (filterPath == null || filterPath.isEmpty()) {
545 dialog.setFilterPath(
546 RepositoryUtil.getDefaultRepositoryDir());
548 String result = dialog.open();
549 if (result != null) {
550 uriText.setText("file:///" + result); //$NON-NLS-1$
556 newLabel(g, UIText.RepositorySelectionPage_promptHost + ":"); //$NON-NLS-1$
557 hostText = new Text(g, SWT.BORDER);
558 GridDataFactory.fillDefaults().span(2, 1).applyTo(hostText);
559 hostText.addModifyListener(new ModifyListener() {
560 @Override
561 public void modifyText(final ModifyEvent e) {
562 setURI(uri.setHost(nullString(hostText.getText())));
566 newLabel(g, UIText.RepositorySelectionPage_promptPath + ":"); //$NON-NLS-1$
567 pathText = new Text(g, SWT.BORDER);
568 GridDataFactory.fillDefaults().span(2, 1).applyTo(pathText);
569 pathText.addModifyListener(new ModifyListener() {
570 @Override
571 public void modifyText(final ModifyEvent e) {
572 setURI(uri.setPath(nullString(pathText.getText())));
578 private Group createAuthenticationGroup(final Composite parent) {
579 final Group g = createGroup(parent,
580 UIText.RepositorySelectionPage_groupAuthentication);
582 newLabel(g, UIText.RepositorySelectionPage_promptUser + ":"); //$NON-NLS-1$
583 userText = new Text(g, SWT.BORDER);
584 userText.setLayoutData(createFieldGridData());
585 userText.addModifyListener(new ModifyListener() {
586 @Override
587 public void modifyText(final ModifyEvent e) {
588 Protocol protocol = getProtocol();
589 if (protocol != Protocol.HTTP && protocol != Protocol.HTTPS)
590 setURI(uri.setUser(nullString(userText.getText())));
591 user = userText.getText();
595 newLabel(g, UIText.RepositorySelectionPage_promptPassword + ":"); //$NON-NLS-1$
596 passText = new Text(g, SWT.BORDER | SWT.PASSWORD);
597 passText.setLayoutData(createFieldGridData());
598 passText.addModifyListener(new ModifyListener() {
599 @Override
600 public void modifyText(final ModifyEvent e) {
601 setURI(uri.setPass(null));
602 password = passText.getText();
606 storeCheckbox = new Button(g, SWT.CHECK);
607 storeCheckbox
608 .setText(UIText.RepositorySelectionPage_storeInSecureStore);
609 storeCheckbox.setSelection(storeInSecureStore);
610 storeCheckbox.addSelectionListener(new SelectionListener() {
611 @Override
612 public void widgetSelected(SelectionEvent e) {
613 storeInSecureStore = storeCheckbox.getSelection();
616 @Override
617 public void widgetDefaultSelected(SelectionEvent e) {
618 storeInSecureStore = storeCheckbox.getSelection();
622 return g;
625 private void createConnectionGroup(final Composite parent) {
626 final Group g = createGroup(parent,
627 UIText.RepositorySelectionPage_groupConnection);
629 newLabel(g, UIText.RepositorySelectionPage_promptScheme + ":"); //$NON-NLS-1$
630 scheme = new Combo(g, SWT.DROP_DOWN | SWT.READ_ONLY);
631 for (Protocol p : Protocol.values())
632 scheme.add(p.getDefaultScheme());
633 scheme.addSelectionListener(new SelectionAdapter() {
634 @Override
635 public void widgetSelected(final SelectionEvent e) {
636 final int idx = scheme.getSelectionIndex();
637 if (idx < 0) {
638 setURI(uri.setScheme(null));
639 scheme.setToolTipText(EMPTY_STRING);
640 } else {
641 setURI(uri.setScheme(nullString(scheme.getItem(idx))));
642 scheme.setToolTipText(Protocol.values()[idx].getTooltip());
644 updateGroups();
648 newLabel(g, UIText.RepositorySelectionPage_promptPort + ":"); //$NON-NLS-1$
649 portText = new Text(g, SWT.BORDER);
650 portText.addVerifyListener(new VerifyListener() {
651 final Pattern p = Pattern.compile("^(?:[1-9][0-9]*)?$"); //$NON-NLS-1$
653 @Override
654 public void verifyText(final VerifyEvent e) {
655 final String v = portText.getText();
656 e.doit = p.matcher(
657 v.substring(0, e.start) + e.text + v.substring(e.end))
658 .matches();
661 portText.addModifyListener(new ModifyListener() {
662 @Override
663 public void modifyText(final ModifyEvent e) {
664 final String val = nullString(portText.getText());
665 if (val == null)
666 setURI(uri.setPort(-1));
667 else
668 try {
669 setURI(uri.setPort(Integer.parseInt(val)));
670 } catch (NumberFormatException err) {
671 // Ignore it for now.
677 private Group createGroup(final Composite parent, final String text) {
678 final Group g = new Group(parent, SWT.NONE);
679 final GridLayout layout = new GridLayout();
680 layout.numColumns = 2;
681 g.setLayout(layout);
682 g.setText(text);
683 final GridData gd = new GridData();
684 gd.grabExcessHorizontalSpace = true;
685 gd.horizontalAlignment = SWT.FILL;
686 g.setLayoutData(gd);
687 return g;
690 private void newLabel(final Group g, final String text) {
691 new Label(g, SWT.NULL).setText(text);
694 private GridData createFieldGridData() {
695 return new GridData(SWT.FILL, SWT.DEFAULT, true, false);
698 private String nullString(final String value) {
699 if (value == null)
700 return null;
701 final String v = value.trim();
702 return v.length() == 0 ? null : v;
705 private void safeSet(final Text text, final String value) {
706 text.setText(value != null ? value : EMPTY_STRING);
709 private boolean isURISelected() {
710 return uriButton == null || uriButton.getSelection();
713 private void setURI(final URIish u) {
714 try {
715 eventDepth++;
716 if (eventDepth == 1) {
717 uri = u;
718 String oldUriText = uriText.getText();
719 String newUriText = uri.toString();
720 // avoid moving the cursor to the first position if there are no
721 // changes by this automatic update
722 if (!oldUriText.equals(newUriText)) {
723 uriText.setText(newUriText);
725 checkPage();
727 } finally {
728 eventDepth--;
732 private List<RemoteConfig> getUsableConfigs(final List<RemoteConfig> remotes) {
734 if (remotes == null)
735 return null;
737 List<RemoteConfig> result = new ArrayList<>();
739 for (RemoteConfig config : remotes)
740 if ((sourceSelection && !config.getURIs().isEmpty() || !sourceSelection
741 && (!config.getPushURIs().isEmpty() || !config.getURIs()
742 .isEmpty())))
743 result.add(config);
745 if (!result.isEmpty())
746 return result;
748 return null;
752 * Check the user input and set messages in case of invalid input.
754 protected void checkPage() {
755 if (isURISelected()) {
756 assert uri != null;
757 if (uriText.getText().length() == 0) {
758 selectionIncomplete(null);
759 return;
760 } else if (uriText.getText().endsWith(" ")) { //$NON-NLS-1$
761 selectionIncomplete(UIText.RepositorySelectionPage_UriMustNotHaveTrailingSpacesMessage);
762 return;
765 try {
766 final URIish finalURI = new URIish(
767 GitUrlChecker.sanitizeAsGitUrl(uriText.getText()));
768 String proto = finalURI.getScheme();
769 if (proto == null && scheme.getSelectionIndex() >= 0)
770 proto = scheme.getItem(scheme.getSelectionIndex());
772 if (uri.getPath() == null) {
773 selectionIncomplete(NLS.bind(
774 UIText.RepositorySelectionPage_fieldRequired,
775 unamp(UIText.RepositorySelectionPage_promptPath),
776 proto));
777 return;
780 if (Protocol.FILE.handles(finalURI)) {
781 String badField = null;
782 if (uri.getHost() != null)
783 badField = UIText.RepositorySelectionPage_promptHost;
784 else if (uri.getUser() != null)
785 badField = UIText.RepositorySelectionPage_promptUser;
786 else if (uri.getPass() != null)
787 badField = UIText.RepositorySelectionPage_promptPassword;
788 if (badField != null) {
789 selectionIncomplete(NLS
790 .bind(
791 UIText.RepositorySelectionPage_fieldNotSupported,
792 unamp(badField), proto));
793 return;
796 final File d = FS.DETECTED.resolve(
797 new File("."), uri.getPath()); //$NON-NLS-1$
798 if (!d.exists()) {
799 selectionIncomplete(NLS.bind(
800 UIText.RepositorySelectionPage_fileNotFound,
801 d.getAbsolutePath()));
802 return;
805 selectionComplete(finalURI, null);
806 return;
809 if (uri.getHost() == null) {
810 selectionIncomplete(NLS.bind(
811 UIText.RepositorySelectionPage_fieldRequired,
812 unamp(UIText.RepositorySelectionPage_promptHost),
813 proto));
814 return;
817 if (Protocol.GIT.handles(finalURI)) {
818 String badField = null;
819 if (uri.getUser() != null)
820 badField = UIText.RepositorySelectionPage_promptUser;
821 else if (uri.getPass() != null)
822 badField = UIText.RepositorySelectionPage_promptPassword;
823 if (badField != null) {
824 selectionIncomplete(NLS
825 .bind(
826 UIText.RepositorySelectionPage_fieldNotSupported,
827 unamp(badField), proto));
828 return;
832 if (Protocol.HTTP.handles(finalURI)
833 || Protocol.HTTPS.handles(finalURI)) {
834 UserPasswordCredentials credentials = SecureStoreUtils
835 .getCredentials(finalURI);
836 if (credentials != null) {
837 String u = credentials.getUser();
838 String p = credentials.getPassword();
839 String uriUser = finalURI.getUser();
840 if (uriUser == null) {
841 if (setSafeUser(u) && setSafePassword(p))
842 setStoreInSecureStore(true);
843 } else if (uriUser.length() != 0 && uriUser.equals(u)) {
844 if (setSafePassword(p))
845 setStoreInSecureStore(true);
850 selectionComplete(finalURI, null);
851 return;
852 } catch (URISyntaxException e) {
853 selectionIncomplete(e.getReason());
854 return;
855 } catch (Exception e) {
856 Activator.logError(NLS.bind(
857 UIText.RepositorySelectionPage_errorValidating,
858 getClass().getName()), e);
859 selectionIncomplete(UIText.RepositorySelectionPage_internalError);
860 return;
862 } else {
863 assert remoteButton.getSelection();
864 selectionComplete(null, remoteConfig);
865 return;
869 private boolean setSafePassword(String p) {
870 if ((password == null || password.length() == 0) && p != null
871 && p.length() != 0) {
872 password = p;
873 passText.setText(p);
874 return true;
876 return false;
879 private boolean setSafeUser(String u) {
880 if ((user == null || user.length() == 0) && u != null
881 && u.length() != 0) {
882 user = u;
883 userText.setText(u);
884 return true;
886 return false;
889 private void setStoreInSecureStore(boolean store) {
890 storeInSecureStore = store;
891 storeCheckbox.setSelection(store);
894 private String unamp(String s) {
895 return s.replace("&", EMPTY_STRING); //$NON-NLS-1$
898 private void selectionIncomplete(final String errorMessage) {
899 setExposedSelection(null, null);
900 setErrorMessage(errorMessage);
901 setPageComplete(false);
904 private void selectionComplete(final URIish u, final RemoteConfig rc) {
905 setExposedSelection(u, rc);
906 setErrorMessage(null);
907 setPageComplete(true);
910 private void setExposedSelection(final URIish u, final RemoteConfig rc) {
911 final RepositorySelection newSelection = new RepositorySelection(u, rc);
912 if (newSelection.equals(selection))
913 return;
915 selection = newSelection;
918 private void updateRemoteAndURIPanels() {
919 UIUtils.setEnabledRecursively(uriPanel, isURISelected());
920 if (uriPanel.getEnabled())
921 updateGroups();
922 if (configuredRemotes != null)
923 UIUtils.setEnabledRecursively(remotePanel, !isURISelected());
926 private void updateGroups() {
927 Protocol p = getProtocol();
928 if (p != null) {
929 hostText.setEnabled(p.hasHost());
930 if (!p.hasHost())
931 hostText.setText(EMPTY_STRING);
932 portText.setEnabled(p.hasPort());
933 if (!p.hasPort())
934 portText.setText(EMPTY_STRING);
936 UIUtils.setEnabledRecursively(authGroup, p.canAuthenticate());
937 if (!p.canAuthenticate()) {
938 userText.setText(EMPTY_STRING);
939 passText.setText(EMPTY_STRING);
940 storeCheckbox.setSelection(false);
945 private Protocol getProtocol() {
946 int idx = scheme.getSelectionIndex();
947 if (idx >= 0)
948 return Protocol.values()[idx];
949 return null;
952 @Override
953 public void setVisible(boolean visible) {
954 super.setVisible(visible);
955 if (visible)
956 uriText.setFocus();
960 * Updates the proposal list for the URI field
962 public void saveUriInPrefs() {
963 uriProposalHandler.updateProposals();
967 * @return credentials
969 public UserPasswordCredentials getCredentials() {
970 if ((user == null || user.length() == 0)
971 && (password == null || password.length() == 0))
972 return null;
973 return new UserPasswordCredentials(user, password);
977 * @return true if credentials should be stored
979 public boolean getStoreInSecureStore() {
980 return this.storeInSecureStore;
984 * Set the ID for context sensitive help
986 * @param id
987 * help context
989 public void setHelpContext(String id) {
990 helpContext = id;
993 @Override
994 public void performHelp() {
995 PlatformUI.getWorkbench().getHelpSystem().displayHelp(helpContext);
998 private void updateFields(final String text) {
999 try {
1000 eventDepth++;
1001 if (eventDepth != 1)
1002 return;
1004 String strippedText = GitUrlChecker.sanitizeAsGitUrl(text);
1005 final URIish u = new URIish(strippedText);
1006 if (!text.equals(strippedText)) {
1007 uriText.setText(strippedText);
1009 safeSet(hostText, u.getHost());
1010 safeSet(pathText, u.getPath());
1011 safeSet(userText, u.getUser());
1012 safeSet(passText, u.getPass());
1014 if (u.getPort() > 0)
1015 portText.setText(Integer.toString(u.getPort()));
1016 else
1017 portText.setText(EMPTY_STRING);
1019 if (u.getScheme() != null) {
1020 scheme.select(scheme.indexOf(u.getScheme()));
1021 scheme.notifyListeners(SWT.Selection, new Event());
1024 updateGroups();
1025 uri = u;
1026 } catch (URISyntaxException err) {
1027 // leave uriText as it is, but clean up underlying uri and
1028 // decomposed fields
1029 uri = new URIish();
1030 hostText.setText(EMPTY_STRING);
1031 pathText.setText(EMPTY_STRING);
1032 userText.setText(EMPTY_STRING);
1033 passText.setText(EMPTY_STRING);
1034 portText.setText(EMPTY_STRING);
1035 scheme.select(-1);
1036 } finally {
1037 eventDepth--;
1039 checkPage();
1042 private IPreferenceStore getPreferenceStore() {
1043 return Activator.getDefault().getPreferenceStore();
1046 @Override
1047 public GitRepositoryInfo getGitRepositoryInfo() {
1048 GitRepositoryInfo info = new GitRepositoryInfo(uri.toString());
1049 info.setCredentials(user, password);
1050 info.setShouldSaveCredentialsInSecureStore(storeInSecureStore);
1051 uriProposalHandler.updateProposals();
1052 return info;