Allow Git repository to be pre-filled from clipboard
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / components / RepositorySelectionPage.java
blobf5d89526eca1f6ee677496207257ef86d1414437
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>
7 * All rights reserved. This program and the accompanying materials
8 * are made available under the terms of the Eclipse Public License v1.0
9 * which accompanies this distribution, and is available at
10 * http://www.eclipse.org/legal/epl-v10.html
11 *******************************************************************************/
12 package org.eclipse.egit.ui.internal.components;
14 import java.io.File;
15 import java.net.URI;
16 import java.net.URISyntaxException;
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.StringTokenizer;
20 import java.util.regex.Pattern;
21 import java.util.regex.PatternSyntaxException;
23 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
24 import org.eclipse.core.runtime.preferences.InstanceScope;
25 import org.eclipse.egit.ui.Activator;
26 import org.eclipse.egit.ui.UIText;
27 import org.eclipse.egit.ui.UIUtils;
28 import org.eclipse.jface.fieldassist.ContentProposalAdapter;
29 import org.eclipse.jface.fieldassist.IContentProposal;
30 import org.eclipse.jface.fieldassist.IContentProposalProvider;
31 import org.eclipse.jface.fieldassist.TextContentAdapter;
32 import org.eclipse.jface.layout.GridDataFactory;
33 import org.eclipse.jgit.lib.Constants;
34 import org.eclipse.jgit.transport.RemoteConfig;
35 import org.eclipse.jgit.transport.Transport;
36 import org.eclipse.jgit.transport.URIish;
37 import org.eclipse.jgit.util.FS;
38 import org.eclipse.osgi.util.NLS;
39 import org.eclipse.swt.SWT;
40 import org.eclipse.swt.dnd.Clipboard;
41 import org.eclipse.swt.dnd.TextTransfer;
42 import org.eclipse.swt.events.ModifyEvent;
43 import org.eclipse.swt.events.ModifyListener;
44 import org.eclipse.swt.events.SelectionAdapter;
45 import org.eclipse.swt.events.SelectionEvent;
46 import org.eclipse.swt.events.VerifyEvent;
47 import org.eclipse.swt.events.VerifyListener;
48 import org.eclipse.swt.layout.GridData;
49 import org.eclipse.swt.layout.GridLayout;
50 import org.eclipse.swt.widgets.Button;
51 import org.eclipse.swt.widgets.Combo;
52 import org.eclipse.swt.widgets.Composite;
53 import org.eclipse.swt.widgets.Control;
54 import org.eclipse.swt.widgets.DirectoryDialog;
55 import org.eclipse.swt.widgets.Display;
56 import org.eclipse.swt.widgets.Group;
57 import org.eclipse.swt.widgets.Label;
58 import org.eclipse.swt.widgets.Text;
59 import org.osgi.service.prefs.BackingStoreException;
61 /**
62 * Wizard page that allows the user entering the location of a remote repository
63 * by specifying URL manually or selecting a preconfigured remote repository.
65 public class RepositorySelectionPage extends BaseWizardPage {
67 private final static String USED_URIS_PREF = "RepositorySelectionPage.UsedUris"; //$NON-NLS-1$
69 private final static String USED_URIS_LENGTH_PREF = "RepositorySelectionPage.UsedUrisLength"; //$NON-NLS-1$
71 private static final int REMOTE_CONFIG_TEXT_MAX_LENGTH = 80;
73 private static final int S_GIT = 0;
75 private static final int S_SSH = 1;
77 private static final int S_SFTP = 2;
79 private static final int S_HTTP = 3;
81 private static final int S_HTTPS = 4;
83 private static final int S_FTP = 5;
85 private static final int S_FILE = 6;
87 private static final String[] DEFAULT_SCHEMES;
89 static {
90 DEFAULT_SCHEMES = new String[7];
91 DEFAULT_SCHEMES[S_GIT] = "git"; //$NON-NLS-1$
92 DEFAULT_SCHEMES[S_SSH] = "git+ssh"; //$NON-NLS-1$
93 DEFAULT_SCHEMES[S_SFTP] = "sftp"; //$NON-NLS-1$
94 DEFAULT_SCHEMES[S_HTTP] = "http"; //$NON-NLS-1$
95 DEFAULT_SCHEMES[S_HTTPS] = "https"; //$NON-NLS-1$
96 DEFAULT_SCHEMES[S_FTP] = "ftp"; //$NON-NLS-1$
97 DEFAULT_SCHEMES[S_FILE] = "file"; //$NON-NLS-1$
100 private final List<RemoteConfig> configuredRemotes;
102 private final boolean sourceSelection;
104 private final String presetUri;
106 private Group authGroup;
108 private Text uriText;
110 private Text hostText;
112 private Text pathText;
114 private Text userText;
116 private Text passText;
118 private Combo scheme;
120 private Text portText;
122 private int eventDepth;
124 private URIish uri;
126 private RemoteConfig remoteConfig;
128 private RepositorySelection selection;
130 private Composite remotePanel;
132 private Button remoteButton;
134 private Combo remoteCombo;
136 private Composite uriPanel;
138 private Button uriButton;
141 * Create repository selection page, allowing user specifying URI or
142 * (optionally) choosing from preconfigured remotes list.
143 * <p>
144 * Wizard page is created without image, just with text description.
146 * @param sourceSelection
147 * true if dialog is used for source selection; false otherwise
148 * (destination selection). This indicates appropriate text
149 * messages.
150 * @param configuredRemotes
151 * list of configured remotes that user may select as an
152 * alternative to manual URI specification. Remotes appear in
153 * given order in GUI, with
154 * {@value Constants#DEFAULT_REMOTE_NAME} as the default choice.
155 * List may be null or empty - no remotes configurations appear
156 * in this case. Note that the provided list may be changed by
157 * this constructor.
158 * @param presetUri
159 * the pre-set URI, may be null
161 public RepositorySelectionPage(final boolean sourceSelection,
162 final List<RemoteConfig> configuredRemotes, String presetUri) {
164 super(RepositorySelectionPage.class.getName());
166 this.uri = new URIish();
167 this.sourceSelection = sourceSelection;
169 String preset = null;
170 if (presetUri == null) {
171 Clipboard clippy = new Clipboard(Display.getCurrent());
172 String text = (String) clippy.getContents(TextTransfer.getInstance());
173 try {
174 if(Transport.canHandleProtocol(new URIish(text))) {
175 preset = text;
177 } catch (URISyntaxException e) {
178 preset = null;
181 this.presetUri = preset;
183 this.configuredRemotes = getUsableConfigs(configuredRemotes);
184 this.remoteConfig = selectDefaultRemoteConfig();
186 selection = RepositorySelection.INVALID_SELECTION;
188 if (sourceSelection) {
189 setTitle(UIText.RepositorySelectionPage_sourceSelectionTitle);
190 setDescription(UIText.RepositorySelectionPage_sourceSelectionDescription);
191 } else {
192 setTitle(UIText.RepositorySelectionPage_destinationSelectionTitle);
193 setDescription(UIText.RepositorySelectionPage_destinationSelectionDescription);
198 * Create repository selection page, allowing user specifying URI, with no
199 * preconfigured remotes selection.
201 * @param sourceSelection
202 * true if dialog is used for source selection; false otherwise
203 * (destination selection). This indicates appropriate text
204 * messages.
205 * @param presetUri
206 * the pre-set URI, may be null
208 public RepositorySelectionPage(final boolean sourceSelection,
209 String presetUri) {
210 this(sourceSelection, null, presetUri);
214 * @return repository selection representing current page state.
216 public RepositorySelection getSelection() {
217 return selection;
221 * Compare current repository selection set by user to provided one.
223 * @param s
224 * repository selection to compare.
225 * @return true if provided selection is equal to current page selection,
226 * false otherwise.
228 public boolean selectionEquals(final RepositorySelection s) {
229 return selection.equals(s);
232 public void createControl(final Composite parent) {
233 final Composite panel = new Composite(parent, SWT.NULL);
234 panel.setLayout(new GridLayout());
236 if (configuredRemotes != null)
237 createRemotePanel(panel);
239 createUriPanel(panel);
241 if(presetUri != null)
242 updateFields(presetUri);
244 updateRemoteAndURIPanels();
245 setControl(panel);
247 checkPage();
250 private void createRemotePanel(final Composite parent) {
251 remoteButton = new Button(parent, SWT.RADIO);
252 remoteButton
253 .setText(UIText.RepositorySelectionPage_configuredRemoteChoice
254 + ":"); //$NON-NLS-1$
255 remoteButton.setSelection(true);
257 remotePanel = new Composite(parent, SWT.NULL);
258 remotePanel.setLayout(new GridLayout());
259 final GridData gd = new GridData();
260 gd.grabExcessHorizontalSpace = true;
261 gd.horizontalAlignment = SWT.FILL;
262 remotePanel.setLayoutData(gd);
264 remoteCombo = new Combo(remotePanel, SWT.READ_ONLY | SWT.DROP_DOWN);
265 final String items[] = new String[configuredRemotes.size()];
266 int i = 0;
267 for (final RemoteConfig rc : configuredRemotes)
268 items[i++] = getTextForRemoteConfig(rc);
269 final int defaultIndex = configuredRemotes.indexOf(remoteConfig);
270 remoteCombo.setItems(items);
271 remoteCombo.select(defaultIndex);
272 remoteCombo.addSelectionListener(new SelectionAdapter() {
273 @Override
274 public void widgetSelected(SelectionEvent e) {
275 final int idx = remoteCombo.getSelectionIndex();
276 remoteConfig = configuredRemotes.get(idx);
277 checkPage();
282 private void createUriPanel(final Composite parent) {
283 if (configuredRemotes != null) {
284 uriButton = new Button(parent, SWT.RADIO);
285 uriButton.setText(UIText.RepositorySelectionPage_uriChoice + ":"); //$NON-NLS-1$
286 uriButton.addSelectionListener(new SelectionAdapter() {
287 public void widgetSelected(SelectionEvent e) {
288 // occurs either on selection or unselection event
289 updateRemoteAndURIPanels();
290 checkPage();
295 uriPanel = new Composite(parent, SWT.NULL);
296 uriPanel.setLayout(new GridLayout());
297 final GridData gd = new GridData();
298 gd.grabExcessHorizontalSpace = true;
299 gd.horizontalAlignment = SWT.FILL;
300 uriPanel.setLayoutData(gd);
302 createLocationGroup(uriPanel);
303 createConnectionGroup(uriPanel);
304 authGroup = createAuthenticationGroup(uriPanel);
307 private void createLocationGroup(final Composite parent) {
308 final Group g = createGroup(parent,
309 UIText.RepositorySelectionPage_groupLocation);
311 g.setLayout(new GridLayout(3, false));
313 newLabel(g, UIText.RepositorySelectionPage_promptURI + ":"); //$NON-NLS-1$
314 uriText = new Text(g, SWT.BORDER);
316 if (presetUri != null)
317 uriText.setText(presetUri);
319 uriText.setLayoutData(createFieldGridData());
320 uriText.addModifyListener(new ModifyListener() {
321 public void modifyText(final ModifyEvent e) {
322 updateFields(uriText.getText());
326 addContentProposalToUriText(uriText);
328 Button browseButton = new Button(g, SWT.NULL);
329 browseButton.setText(UIText.RepositorySelectionPage_BrowseLocalFile);
330 browseButton.addSelectionListener(new SelectionAdapter() {
332 @Override
333 public void widgetSelected(SelectionEvent e) {
334 DirectoryDialog dialog = new DirectoryDialog(getShell());
335 // if a file-uri was selected before, let's try to open
336 // the directory dialog on the same directory
337 if (!uriText.getText().equals("")) { //$NON-NLS-1$
338 try {
339 URI testUri = URI.create(uriText.getText().replace(
340 '\\', '/'));
341 if (testUri.getScheme().equals("file")) { //$NON-NLS-1$
342 String path = testUri.getPath();
343 if (path.length() > 1 && path.startsWith("/")) //$NON-NLS-1$
344 path = path.substring(1);
346 dialog.setFilterPath(path);
348 } catch (IllegalArgumentException e1) {
349 // ignore here, we just' don't set the directory in the
350 // browser
354 String result = dialog.open();
355 if (result != null)
356 uriText.setText("file:///" + result); //$NON-NLS-1$
361 newLabel(g, UIText.RepositorySelectionPage_promptHost + ":"); //$NON-NLS-1$
362 hostText = new Text(g, SWT.BORDER);
363 GridDataFactory.fillDefaults().span(2, 1).applyTo(hostText);
364 hostText.addModifyListener(new ModifyListener() {
365 public void modifyText(final ModifyEvent e) {
366 setURI(uri.setHost(nullString(hostText.getText())));
370 newLabel(g, UIText.RepositorySelectionPage_promptPath + ":"); //$NON-NLS-1$
371 pathText = new Text(g, SWT.BORDER);
372 GridDataFactory.fillDefaults().span(2, 1).applyTo(pathText);
373 pathText.addModifyListener(new ModifyListener() {
374 public void modifyText(final ModifyEvent e) {
375 setURI(uri.setPath(nullString(pathText.getText())));
381 private Group createAuthenticationGroup(final Composite parent) {
382 final Group g = createGroup(parent,
383 UIText.RepositorySelectionPage_groupAuthentication);
385 newLabel(g, UIText.RepositorySelectionPage_promptUser + ":"); //$NON-NLS-1$
386 userText = new Text(g, SWT.BORDER);
387 userText.setLayoutData(createFieldGridData());
388 userText.addModifyListener(new ModifyListener() {
389 public void modifyText(final ModifyEvent e) {
390 setURI(uri.setUser(nullString(userText.getText())));
394 newLabel(g, UIText.RepositorySelectionPage_promptPassword + ":"); //$NON-NLS-1$
395 passText = new Text(g, SWT.BORDER | SWT.PASSWORD);
396 passText.setLayoutData(createFieldGridData());
397 return g;
400 private void createConnectionGroup(final Composite parent) {
401 final Group g = createGroup(parent,
402 UIText.RepositorySelectionPage_groupConnection);
404 newLabel(g, UIText.RepositorySelectionPage_promptScheme + ":"); //$NON-NLS-1$
405 scheme = new Combo(g, SWT.DROP_DOWN | SWT.READ_ONLY);
406 scheme.setItems(DEFAULT_SCHEMES);
407 scheme.addSelectionListener(new SelectionAdapter() {
408 public void widgetSelected(final SelectionEvent e) {
409 final int idx = scheme.getSelectionIndex();
410 if (idx < 0)
411 setURI(uri.setScheme(null));
412 else
413 setURI(uri.setScheme(nullString(scheme.getItem(idx))));
414 updateAuthGroup();
418 newLabel(g, UIText.RepositorySelectionPage_promptPort + ":"); //$NON-NLS-1$
419 portText = new Text(g, SWT.BORDER);
420 portText.addVerifyListener(new VerifyListener() {
421 final Pattern p = Pattern.compile("^(?:[1-9][0-9]*)?$"); //$NON-NLS-1$
423 public void verifyText(final VerifyEvent e) {
424 final String v = portText.getText();
425 e.doit = p.matcher(
426 v.substring(0, e.start) + e.text + v.substring(e.end))
427 .matches();
430 portText.addModifyListener(new ModifyListener() {
431 public void modifyText(final ModifyEvent e) {
432 final String val = nullString(portText.getText());
433 if (val == null)
434 setURI(uri.setPort(-1));
435 else {
436 try {
437 setURI(uri.setPort(Integer.parseInt(val)));
438 } catch (NumberFormatException err) {
439 // Ignore it for now.
446 private Group createGroup(final Composite parent, final String text) {
447 final Group g = new Group(parent, SWT.NONE);
448 final GridLayout layout = new GridLayout();
449 layout.numColumns = 2;
450 g.setLayout(layout);
451 g.setText(text);
452 final GridData gd = new GridData();
453 gd.grabExcessHorizontalSpace = true;
454 gd.horizontalAlignment = SWT.FILL;
455 g.setLayoutData(gd);
456 return g;
459 private void newLabel(final Group g, final String text) {
460 new Label(g, SWT.NULL).setText(text);
463 private GridData createFieldGridData() {
464 return new GridData(SWT.FILL, SWT.DEFAULT, true, false);
467 private boolean isGIT(final URIish uri) {
468 return "git".equals(uri.getScheme()); //$NON-NLS-1$
471 private boolean isFile(final URIish uri) {
472 if ("file".equals(uri.getScheme()) || uri.getScheme() == null) //$NON-NLS-1$
473 return true;
474 if (uri.getHost() != null || uri.getPort() > 0 || uri.getUser() != null
475 || uri.getPass() != null || uri.getPath() == null)
476 return false;
477 if (uri.getScheme() == null)
478 return FS.resolve(new File("."), uri.getPath()).isDirectory(); //$NON-NLS-1$
479 return false;
482 private boolean isSSH(final URIish uri) {
483 if (!uri.isRemote())
484 return false;
485 final String scheme = uri.getScheme();
486 if ("ssh".equals(scheme)) //$NON-NLS-1$
487 return true;
488 if ("ssh+git".equals(scheme)) //$NON-NLS-1$
489 return true;
490 if ("git+ssh".equals(scheme)) //$NON-NLS-1$
491 return true;
492 if (scheme == null && uri.getHost() != null && uri.getPath() != null)
493 return true;
494 return false;
497 private String nullString(final String value) {
498 if (value == null)
499 return null;
500 final String v = value.trim();
501 return v.length() == 0 ? null : v;
504 private void safeSet(final Text text, final String value) {
505 text.setText(value != null ? value : ""); //$NON-NLS-1$
508 private boolean isURISelected() {
509 return configuredRemotes == null || presetUri != null
510 || uriButton.getSelection();
513 private void setURI(final URIish u) {
514 try {
515 eventDepth++;
516 if (eventDepth == 1) {
517 uri = u;
518 uriText.setText(uri.toString());
519 checkPage();
521 } finally {
522 eventDepth--;
526 private List<RemoteConfig> getUsableConfigs(final List<RemoteConfig> remotes) {
528 if (remotes == null)
529 return null;
531 List<RemoteConfig> result = new ArrayList<RemoteConfig>();
533 for (RemoteConfig config : remotes)
534 if ((sourceSelection && !config.getURIs().isEmpty() || !sourceSelection
535 && (!config.getPushURIs().isEmpty() || !config.getURIs()
536 .isEmpty())))
537 result.add(config);
539 if (!result.isEmpty())
540 return result;
542 return null;
545 private RemoteConfig selectDefaultRemoteConfig() {
546 if (configuredRemotes == null)
547 return null;
548 for (final RemoteConfig rc : configuredRemotes)
549 if (Constants.DEFAULT_REMOTE_NAME.equals(rc.getName()))
550 return rc;
551 return configuredRemotes.get(0);
554 private String getTextForRemoteConfig(final RemoteConfig rc) {
555 final StringBuilder sb = new StringBuilder(rc.getName());
556 sb.append(": "); //$NON-NLS-1$
557 boolean first = true;
558 List<URIish> uris;
559 if (sourceSelection) {
560 uris = rc.getURIs();
561 } else {
562 uris = rc.getPushURIs();
563 // if no push URIs are defined, use fetch URIs instead
564 if (uris.isEmpty()) {
565 uris = rc.getURIs();
569 for (final URIish u : uris) {
570 final String uString = u.toString();
571 if (first)
572 first = false;
573 else {
574 sb.append(", "); //$NON-NLS-1$
575 if (sb.length() + uString.length() > REMOTE_CONFIG_TEXT_MAX_LENGTH) {
576 sb.append("..."); //$NON-NLS-1$
577 break;
580 sb.append(uString);
582 return sb.toString();
585 private void checkPage() {
586 if (isURISelected()) {
587 assert uri != null;
588 if (uriText.getText().length() == 0) {
589 selectionIncomplete(null);
590 return;
593 try {
594 final URIish finalURI = new URIish(uriText.getText());
595 String proto = finalURI.getScheme();
596 if (proto == null && scheme.getSelectionIndex() >= 0)
597 proto = scheme.getItem(scheme.getSelectionIndex());
599 if (uri.getPath() == null) {
600 selectionIncomplete(NLS.bind(
601 UIText.RepositorySelectionPage_fieldRequired,
602 unamp(UIText.RepositorySelectionPage_promptPath),
603 proto));
604 return;
607 if (isFile(finalURI)) {
608 String badField = null;
609 if (uri.getHost() != null)
610 badField = UIText.RepositorySelectionPage_promptHost;
611 else if (uri.getUser() != null)
612 badField = UIText.RepositorySelectionPage_promptUser;
613 else if (uri.getPass() != null)
614 badField = UIText.RepositorySelectionPage_promptPassword;
615 if (badField != null) {
616 selectionIncomplete(NLS
617 .bind(
618 UIText.RepositorySelectionPage_fieldNotSupported,
619 unamp(badField), proto));
620 return;
623 final File d = FS.resolve(new File("."), uri.getPath()); //$NON-NLS-1$
624 if (!d.exists()) {
625 selectionIncomplete(NLS.bind(
626 UIText.RepositorySelectionPage_fileNotFound, d
627 .getAbsolutePath()));
628 return;
631 selectionComplete(finalURI, null);
632 return;
635 if (uri.getHost() == null) {
636 selectionIncomplete(NLS.bind(
637 UIText.RepositorySelectionPage_fieldRequired,
638 unamp(UIText.RepositorySelectionPage_promptHost),
639 proto));
640 return;
643 if (isGIT(finalURI)) {
644 String badField = null;
645 if (uri.getUser() != null)
646 badField = UIText.RepositorySelectionPage_promptUser;
647 else if (uri.getPass() != null)
648 badField = UIText.RepositorySelectionPage_promptPassword;
649 if (badField != null) {
650 selectionIncomplete(NLS
651 .bind(
652 UIText.RepositorySelectionPage_fieldNotSupported,
653 unamp(badField), proto));
654 return;
658 selectionComplete(finalURI, null);
659 return;
660 } catch (URISyntaxException e) {
661 selectionIncomplete(e.getReason());
662 return;
663 } catch (Exception e) {
664 Activator.logError(NLS.bind(
665 UIText.RepositorySelectionPage_errorValidating,
666 getClass().getName()),
668 selectionIncomplete(UIText.RepositorySelectionPage_internalError);
669 return;
671 } else {
672 assert remoteButton.getSelection();
673 selectionComplete(null, remoteConfig);
674 return;
678 private String unamp(String s) {
679 return s.replace("&", ""); //$NON-NLS-1$ //$NON-NLS-2$
682 private void selectionIncomplete(final String errorMessage) {
683 setExposedSelection(null, null);
684 setErrorMessage(errorMessage);
685 setPageComplete(false);
688 private void selectionComplete(final URIish u, final RemoteConfig rc) {
689 setExposedSelection(u, rc);
690 setErrorMessage(null);
691 setPageComplete(true);
692 notifySelectionChanged();
695 private void setExposedSelection(final URIish u, final RemoteConfig rc) {
696 final RepositorySelection newSelection = new RepositorySelection(u, rc);
697 if (newSelection.equals(selection))
698 return;
700 selection = newSelection;
701 notifySelectionChanged();
704 private void updateRemoteAndURIPanels() {
705 setEnabledRecursively(uriPanel, isURISelected());
706 if (uriPanel.getEnabled())
707 updateAuthGroup();
708 if (configuredRemotes != null)
709 setEnabledRecursively(remotePanel, !isURISelected());
712 private void updateAuthGroup() {
713 switch (scheme.getSelectionIndex()) {
714 case S_GIT:
715 hostText.setEnabled(true);
716 portText.setEnabled(true);
717 setEnabledRecursively(authGroup, false);
718 break;
719 case S_SSH:
720 case S_SFTP:
721 case S_HTTP:
722 case S_HTTPS:
723 case S_FTP:
724 hostText.setEnabled(true);
725 portText.setEnabled(true);
726 setEnabledRecursively(authGroup, true);
727 break;
728 case S_FILE:
729 hostText.setEnabled(false);
730 portText.setEnabled(false);
731 setEnabledRecursively(authGroup, false);
732 break;
736 @Override
737 public void setVisible(boolean visible) {
738 super.setVisible(visible);
739 if (visible)
740 uriText.setFocus();
744 * Adds a URI string to the list of previously added ones
746 * TODO move this to some proper preferences handling class instead of
747 * making it static
749 * @param stringToAdd
751 public static void saveUriInPrefs(String stringToAdd) {
753 List<String> uriStrings = getUrisFromPrefs();
755 if (uriStrings.indexOf(stringToAdd) == 0)
756 return;
757 uriStrings.add(0, stringToAdd);
759 IEclipsePreferences prefs = new InstanceScope().getNode(Activator
760 .getPluginId());
762 StringBuilder sb = new StringBuilder();
763 StringBuilder lb = new StringBuilder();
765 // there is no "good" separator for URIish, so we
766 // keep track of the URI lengths separately
767 for (String uriString : uriStrings) {
768 sb.append(uriString);
769 lb.append(uriString.length());
770 lb.append(" "); //$NON-NLS-1$
772 prefs.put(USED_URIS_PREF, sb.toString());
773 prefs.put(USED_URIS_LENGTH_PREF, lb.toString());
775 try {
776 prefs.flush();
777 } catch (BackingStoreException e) {
778 // we simply ignore this here
783 * Gets the previously added URIs from the preferences
785 * TODO move this to some proper preferences handling class instead of
786 * making it static
788 * @return a (possibly empty) list of URIs, never <code>null</code>
790 public static List<String> getUrisFromPrefs() {
792 // use a TreeSet to get the same sorting always
793 List<String> uriStrings = new ArrayList<String>();
795 IEclipsePreferences prefs = new InstanceScope().getNode(Activator
796 .getPluginId());
797 // since there is no "good" separator for URIish, so we
798 // keep track of the URI lengths separately
799 String uriLengths = prefs.get(USED_URIS_LENGTH_PREF, ""); //$NON-NLS-1$
800 String uris = prefs.get(USED_URIS_PREF, ""); //$NON-NLS-1$
802 StringTokenizer tok = new StringTokenizer(uriLengths, " "); //$NON-NLS-1$
803 int offset = 0;
804 while (tok.hasMoreTokens()) {
805 try {
806 int length = Integer.parseInt(tok.nextToken());
807 if (uris.length() >= (offset + length)) {
808 uriStrings.add(uris.substring(offset, offset + length));
809 offset += length;
811 } catch (NumberFormatException nfe) {
812 // ignore here
817 return uriStrings;
820 private void setEnabledRecursively(final Control control,
821 final boolean enable) {
822 control.setEnabled(enable);
823 if (control instanceof Composite)
824 for (final Control child : ((Composite) control).getChildren())
825 setEnabledRecursively(child, enable);
828 private void addContentProposalToUriText(Text uriTextField) {
830 UIUtils.addBulbDecorator(uriTextField, UIText.RepositorySelectionPage_ShowPreviousURIs_HoverText);
832 IContentProposalProvider cp = new IContentProposalProvider() {
834 public IContentProposal[] getProposals(String contents, int position) {
836 List<IContentProposal> resultList = new ArrayList<IContentProposal>();
838 String patternString = contents;
839 while (patternString.length() > 0
840 && patternString.charAt(0) == ' ')
841 patternString = patternString.substring(1);
842 // make the simplest possible pattern check: allow "*"
843 // for multiple characters
844 patternString = patternString.replaceAll("\\x2A", ".*"); //$NON-NLS-1$ //$NON-NLS-2$
845 // make sure we add a (logical) * at the end
846 if (!patternString.endsWith(".*")) { //$NON-NLS-1$
847 patternString = patternString + ".*"; //$NON-NLS-1$
849 // let's compile a case-insensitive pattern (assumes ASCII only)
850 Pattern pattern;
851 try {
852 pattern = Pattern.compile(patternString,
853 Pattern.CASE_INSENSITIVE);
854 } catch (PatternSyntaxException e) {
855 pattern = null;
858 List<String> uriStrings = getUrisFromPrefs();
859 for (final String uriString : uriStrings) {
861 if (pattern != null
862 && !pattern.matcher(uriString).matches())
863 continue;
865 IContentProposal propsal = new IContentProposal() {
867 public String getLabel() {
868 return null;
871 public String getDescription() {
872 return null;
875 public int getCursorPosition() {
876 return 0;
879 public String getContent() {
880 return uriString;
883 resultList.add(propsal);
886 return resultList.toArray(new IContentProposal[resultList
887 .size()]);
891 // set the acceptance style to always replace the complete content
892 new ContentProposalAdapter(uriTextField, new TextContentAdapter(), cp,
893 null, null)
894 .setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
898 private void updateFields(final String text) {
899 try {
900 eventDepth++;
901 if (eventDepth != 1)
902 return;
904 final URIish u = new URIish(text);
905 safeSet(hostText, u.getHost());
906 safeSet(pathText, u.getPath());
907 safeSet(userText, u.getUser());
908 safeSet(passText, u.getPass());
910 if (u.getPort() > 0)
911 portText.setText(Integer.toString(u.getPort()));
912 else
913 portText.setText(""); //$NON-NLS-1$
915 if (isFile(u))
916 scheme.select(S_FILE);
917 else if (isSSH(u))
918 scheme.select(S_SSH);
919 else {
920 for (int i = 0; i < DEFAULT_SCHEMES.length; i++) {
921 if (DEFAULT_SCHEMES[i].equals(u.getScheme())) {
922 scheme.select(i);
923 break;
928 updateAuthGroup();
929 uri = u;
930 } catch (URISyntaxException err) {
931 // leave uriText as it is, but clean up underlying uri and
932 // decomposed fields
933 uri = new URIish();
934 hostText.setText(""); //$NON-NLS-1$
935 pathText.setText(""); //$NON-NLS-1$
936 userText.setText(""); //$NON-NLS-1$
937 passText.setText(""); //$NON-NLS-1$
938 portText.setText(""); //$NON-NLS-1$
939 scheme.select(0);
940 } finally {
941 eventDepth--;
943 checkPage();