Remember to dispose the clipboard after use
[egit/spearce.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / components / RepositorySelectionPage.java
blob0294c7b0b176c774c576bbd9fd37f61c75d4a37e
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(text != null) {
175 text = text.trim();
176 if(Transport.canHandleProtocol(new URIish(text)))
177 preset = text;
179 } catch (URISyntaxException e) {
180 preset = null;
182 clippy.dispose();
184 this.presetUri = preset;
186 this.configuredRemotes = getUsableConfigs(configuredRemotes);
187 this.remoteConfig = selectDefaultRemoteConfig();
189 selection = RepositorySelection.INVALID_SELECTION;
191 if (sourceSelection) {
192 setTitle(UIText.RepositorySelectionPage_sourceSelectionTitle);
193 setDescription(UIText.RepositorySelectionPage_sourceSelectionDescription);
194 } else {
195 setTitle(UIText.RepositorySelectionPage_destinationSelectionTitle);
196 setDescription(UIText.RepositorySelectionPage_destinationSelectionDescription);
201 * Create repository selection page, allowing user specifying URI, with no
202 * preconfigured remotes selection.
204 * @param sourceSelection
205 * true if dialog is used for source selection; false otherwise
206 * (destination selection). This indicates appropriate text
207 * messages.
208 * @param presetUri
209 * the pre-set URI, may be null
211 public RepositorySelectionPage(final boolean sourceSelection,
212 String presetUri) {
213 this(sourceSelection, null, presetUri);
217 * @return repository selection representing current page state.
219 public RepositorySelection getSelection() {
220 return selection;
224 * Compare current repository selection set by user to provided one.
226 * @param s
227 * repository selection to compare.
228 * @return true if provided selection is equal to current page selection,
229 * false otherwise.
231 public boolean selectionEquals(final RepositorySelection s) {
232 return selection.equals(s);
235 public void createControl(final Composite parent) {
236 final Composite panel = new Composite(parent, SWT.NULL);
237 panel.setLayout(new GridLayout());
239 if (configuredRemotes != null)
240 createRemotePanel(panel);
242 createUriPanel(panel);
244 if(presetUri != null)
245 updateFields(presetUri);
247 updateRemoteAndURIPanels();
248 setControl(panel);
250 checkPage();
253 private void createRemotePanel(final Composite parent) {
254 remoteButton = new Button(parent, SWT.RADIO);
255 remoteButton
256 .setText(UIText.RepositorySelectionPage_configuredRemoteChoice
257 + ":"); //$NON-NLS-1$
258 remoteButton.setSelection(true);
260 remotePanel = new Composite(parent, SWT.NULL);
261 remotePanel.setLayout(new GridLayout());
262 final GridData gd = new GridData();
263 gd.grabExcessHorizontalSpace = true;
264 gd.horizontalAlignment = SWT.FILL;
265 remotePanel.setLayoutData(gd);
267 remoteCombo = new Combo(remotePanel, SWT.READ_ONLY | SWT.DROP_DOWN);
268 final String items[] = new String[configuredRemotes.size()];
269 int i = 0;
270 for (final RemoteConfig rc : configuredRemotes)
271 items[i++] = getTextForRemoteConfig(rc);
272 final int defaultIndex = configuredRemotes.indexOf(remoteConfig);
273 remoteCombo.setItems(items);
274 remoteCombo.select(defaultIndex);
275 remoteCombo.addSelectionListener(new SelectionAdapter() {
276 @Override
277 public void widgetSelected(SelectionEvent e) {
278 final int idx = remoteCombo.getSelectionIndex();
279 remoteConfig = configuredRemotes.get(idx);
280 checkPage();
285 private void createUriPanel(final Composite parent) {
286 if (configuredRemotes != null) {
287 uriButton = new Button(parent, SWT.RADIO);
288 uriButton.setText(UIText.RepositorySelectionPage_uriChoice + ":"); //$NON-NLS-1$
289 uriButton.addSelectionListener(new SelectionAdapter() {
290 public void widgetSelected(SelectionEvent e) {
291 // occurs either on selection or unselection event
292 updateRemoteAndURIPanels();
293 checkPage();
298 uriPanel = new Composite(parent, SWT.NULL);
299 uriPanel.setLayout(new GridLayout());
300 final GridData gd = new GridData();
301 gd.grabExcessHorizontalSpace = true;
302 gd.horizontalAlignment = SWT.FILL;
303 uriPanel.setLayoutData(gd);
305 createLocationGroup(uriPanel);
306 createConnectionGroup(uriPanel);
307 authGroup = createAuthenticationGroup(uriPanel);
310 private void createLocationGroup(final Composite parent) {
311 final Group g = createGroup(parent,
312 UIText.RepositorySelectionPage_groupLocation);
314 g.setLayout(new GridLayout(3, false));
316 newLabel(g, UIText.RepositorySelectionPage_promptURI + ":"); //$NON-NLS-1$
317 uriText = new Text(g, SWT.BORDER);
319 if (presetUri != null)
320 uriText.setText(presetUri);
322 uriText.setLayoutData(createFieldGridData());
323 uriText.addModifyListener(new ModifyListener() {
324 public void modifyText(final ModifyEvent e) {
325 updateFields(uriText.getText());
329 addContentProposalToUriText(uriText);
331 Button browseButton = new Button(g, SWT.NULL);
332 browseButton.setText(UIText.RepositorySelectionPage_BrowseLocalFile);
333 browseButton.addSelectionListener(new SelectionAdapter() {
335 @Override
336 public void widgetSelected(SelectionEvent e) {
337 DirectoryDialog dialog = new DirectoryDialog(getShell());
338 // if a file-uri was selected before, let's try to open
339 // the directory dialog on the same directory
340 if (!uriText.getText().equals("")) { //$NON-NLS-1$
341 try {
342 URI testUri = URI.create(uriText.getText().replace(
343 '\\', '/'));
344 if (testUri.getScheme().equals("file")) { //$NON-NLS-1$
345 String path = testUri.getPath();
346 if (path.length() > 1 && path.startsWith("/")) //$NON-NLS-1$
347 path = path.substring(1);
349 dialog.setFilterPath(path);
351 } catch (IllegalArgumentException e1) {
352 // ignore here, we just' don't set the directory in the
353 // browser
357 String result = dialog.open();
358 if (result != null)
359 uriText.setText("file:///" + result); //$NON-NLS-1$
364 newLabel(g, UIText.RepositorySelectionPage_promptHost + ":"); //$NON-NLS-1$
365 hostText = new Text(g, SWT.BORDER);
366 GridDataFactory.fillDefaults().span(2, 1).applyTo(hostText);
367 hostText.addModifyListener(new ModifyListener() {
368 public void modifyText(final ModifyEvent e) {
369 setURI(uri.setHost(nullString(hostText.getText())));
373 newLabel(g, UIText.RepositorySelectionPage_promptPath + ":"); //$NON-NLS-1$
374 pathText = new Text(g, SWT.BORDER);
375 GridDataFactory.fillDefaults().span(2, 1).applyTo(pathText);
376 pathText.addModifyListener(new ModifyListener() {
377 public void modifyText(final ModifyEvent e) {
378 setURI(uri.setPath(nullString(pathText.getText())));
384 private Group createAuthenticationGroup(final Composite parent) {
385 final Group g = createGroup(parent,
386 UIText.RepositorySelectionPage_groupAuthentication);
388 newLabel(g, UIText.RepositorySelectionPage_promptUser + ":"); //$NON-NLS-1$
389 userText = new Text(g, SWT.BORDER);
390 userText.setLayoutData(createFieldGridData());
391 userText.addModifyListener(new ModifyListener() {
392 public void modifyText(final ModifyEvent e) {
393 setURI(uri.setUser(nullString(userText.getText())));
397 newLabel(g, UIText.RepositorySelectionPage_promptPassword + ":"); //$NON-NLS-1$
398 passText = new Text(g, SWT.BORDER | SWT.PASSWORD);
399 passText.setLayoutData(createFieldGridData());
400 return g;
403 private void createConnectionGroup(final Composite parent) {
404 final Group g = createGroup(parent,
405 UIText.RepositorySelectionPage_groupConnection);
407 newLabel(g, UIText.RepositorySelectionPage_promptScheme + ":"); //$NON-NLS-1$
408 scheme = new Combo(g, SWT.DROP_DOWN | SWT.READ_ONLY);
409 scheme.setItems(DEFAULT_SCHEMES);
410 scheme.addSelectionListener(new SelectionAdapter() {
411 public void widgetSelected(final SelectionEvent e) {
412 final int idx = scheme.getSelectionIndex();
413 if (idx < 0)
414 setURI(uri.setScheme(null));
415 else
416 setURI(uri.setScheme(nullString(scheme.getItem(idx))));
417 updateAuthGroup();
421 newLabel(g, UIText.RepositorySelectionPage_promptPort + ":"); //$NON-NLS-1$
422 portText = new Text(g, SWT.BORDER);
423 portText.addVerifyListener(new VerifyListener() {
424 final Pattern p = Pattern.compile("^(?:[1-9][0-9]*)?$"); //$NON-NLS-1$
426 public void verifyText(final VerifyEvent e) {
427 final String v = portText.getText();
428 e.doit = p.matcher(
429 v.substring(0, e.start) + e.text + v.substring(e.end))
430 .matches();
433 portText.addModifyListener(new ModifyListener() {
434 public void modifyText(final ModifyEvent e) {
435 final String val = nullString(portText.getText());
436 if (val == null)
437 setURI(uri.setPort(-1));
438 else {
439 try {
440 setURI(uri.setPort(Integer.parseInt(val)));
441 } catch (NumberFormatException err) {
442 // Ignore it for now.
449 private Group createGroup(final Composite parent, final String text) {
450 final Group g = new Group(parent, SWT.NONE);
451 final GridLayout layout = new GridLayout();
452 layout.numColumns = 2;
453 g.setLayout(layout);
454 g.setText(text);
455 final GridData gd = new GridData();
456 gd.grabExcessHorizontalSpace = true;
457 gd.horizontalAlignment = SWT.FILL;
458 g.setLayoutData(gd);
459 return g;
462 private void newLabel(final Group g, final String text) {
463 new Label(g, SWT.NULL).setText(text);
466 private GridData createFieldGridData() {
467 return new GridData(SWT.FILL, SWT.DEFAULT, true, false);
470 private boolean isGIT(final URIish uri) {
471 return "git".equals(uri.getScheme()); //$NON-NLS-1$
474 private boolean isFile(final URIish uri) {
475 if ("file".equals(uri.getScheme()) || uri.getScheme() == null) //$NON-NLS-1$
476 return true;
477 if (uri.getHost() != null || uri.getPort() > 0 || uri.getUser() != null
478 || uri.getPass() != null || uri.getPath() == null)
479 return false;
480 if (uri.getScheme() == null)
481 return FS.resolve(new File("."), uri.getPath()).isDirectory(); //$NON-NLS-1$
482 return false;
485 private boolean isSSH(final URIish uri) {
486 if (!uri.isRemote())
487 return false;
488 final String scheme = uri.getScheme();
489 if ("ssh".equals(scheme)) //$NON-NLS-1$
490 return true;
491 if ("ssh+git".equals(scheme)) //$NON-NLS-1$
492 return true;
493 if ("git+ssh".equals(scheme)) //$NON-NLS-1$
494 return true;
495 if (scheme == null && uri.getHost() != null && uri.getPath() != null)
496 return true;
497 return false;
500 private String nullString(final String value) {
501 if (value == null)
502 return null;
503 final String v = value.trim();
504 return v.length() == 0 ? null : v;
507 private void safeSet(final Text text, final String value) {
508 text.setText(value != null ? value : ""); //$NON-NLS-1$
511 private boolean isURISelected() {
512 return configuredRemotes == null || presetUri != null
513 || uriButton.getSelection();
516 private void setURI(final URIish u) {
517 try {
518 eventDepth++;
519 if (eventDepth == 1) {
520 uri = u;
521 uriText.setText(uri.toString());
522 checkPage();
524 } finally {
525 eventDepth--;
529 private List<RemoteConfig> getUsableConfigs(final List<RemoteConfig> remotes) {
531 if (remotes == null)
532 return null;
534 List<RemoteConfig> result = new ArrayList<RemoteConfig>();
536 for (RemoteConfig config : remotes)
537 if ((sourceSelection && !config.getURIs().isEmpty() || !sourceSelection
538 && (!config.getPushURIs().isEmpty() || !config.getURIs()
539 .isEmpty())))
540 result.add(config);
542 if (!result.isEmpty())
543 return result;
545 return null;
548 private RemoteConfig selectDefaultRemoteConfig() {
549 if (configuredRemotes == null)
550 return null;
551 for (final RemoteConfig rc : configuredRemotes)
552 if (Constants.DEFAULT_REMOTE_NAME.equals(rc.getName()))
553 return rc;
554 return configuredRemotes.get(0);
557 private String getTextForRemoteConfig(final RemoteConfig rc) {
558 final StringBuilder sb = new StringBuilder(rc.getName());
559 sb.append(": "); //$NON-NLS-1$
560 boolean first = true;
561 List<URIish> uris;
562 if (sourceSelection) {
563 uris = rc.getURIs();
564 } else {
565 uris = rc.getPushURIs();
566 // if no push URIs are defined, use fetch URIs instead
567 if (uris.isEmpty()) {
568 uris = rc.getURIs();
572 for (final URIish u : uris) {
573 final String uString = u.toString();
574 if (first)
575 first = false;
576 else {
577 sb.append(", "); //$NON-NLS-1$
578 if (sb.length() + uString.length() > REMOTE_CONFIG_TEXT_MAX_LENGTH) {
579 sb.append("..."); //$NON-NLS-1$
580 break;
583 sb.append(uString);
585 return sb.toString();
588 private void checkPage() {
589 if (isURISelected()) {
590 assert uri != null;
591 if (uriText.getText().length() == 0) {
592 selectionIncomplete(null);
593 return;
596 try {
597 final URIish finalURI = new URIish(uriText.getText());
598 String proto = finalURI.getScheme();
599 if (proto == null && scheme.getSelectionIndex() >= 0)
600 proto = scheme.getItem(scheme.getSelectionIndex());
602 if (uri.getPath() == null) {
603 selectionIncomplete(NLS.bind(
604 UIText.RepositorySelectionPage_fieldRequired,
605 unamp(UIText.RepositorySelectionPage_promptPath),
606 proto));
607 return;
610 if (isFile(finalURI)) {
611 String badField = null;
612 if (uri.getHost() != null)
613 badField = UIText.RepositorySelectionPage_promptHost;
614 else if (uri.getUser() != null)
615 badField = UIText.RepositorySelectionPage_promptUser;
616 else if (uri.getPass() != null)
617 badField = UIText.RepositorySelectionPage_promptPassword;
618 if (badField != null) {
619 selectionIncomplete(NLS
620 .bind(
621 UIText.RepositorySelectionPage_fieldNotSupported,
622 unamp(badField), proto));
623 return;
626 final File d = FS.resolve(new File("."), uri.getPath()); //$NON-NLS-1$
627 if (!d.exists()) {
628 selectionIncomplete(NLS.bind(
629 UIText.RepositorySelectionPage_fileNotFound, d
630 .getAbsolutePath()));
631 return;
634 selectionComplete(finalURI, null);
635 return;
638 if (uri.getHost() == null) {
639 selectionIncomplete(NLS.bind(
640 UIText.RepositorySelectionPage_fieldRequired,
641 unamp(UIText.RepositorySelectionPage_promptHost),
642 proto));
643 return;
646 if (isGIT(finalURI)) {
647 String badField = null;
648 if (uri.getUser() != null)
649 badField = UIText.RepositorySelectionPage_promptUser;
650 else if (uri.getPass() != null)
651 badField = UIText.RepositorySelectionPage_promptPassword;
652 if (badField != null) {
653 selectionIncomplete(NLS
654 .bind(
655 UIText.RepositorySelectionPage_fieldNotSupported,
656 unamp(badField), proto));
657 return;
661 selectionComplete(finalURI, null);
662 return;
663 } catch (URISyntaxException e) {
664 selectionIncomplete(e.getReason());
665 return;
666 } catch (Exception e) {
667 Activator.logError(NLS.bind(
668 UIText.RepositorySelectionPage_errorValidating,
669 getClass().getName()),
671 selectionIncomplete(UIText.RepositorySelectionPage_internalError);
672 return;
674 } else {
675 assert remoteButton.getSelection();
676 selectionComplete(null, remoteConfig);
677 return;
681 private String unamp(String s) {
682 return s.replace("&", ""); //$NON-NLS-1$ //$NON-NLS-2$
685 private void selectionIncomplete(final String errorMessage) {
686 setExposedSelection(null, null);
687 setErrorMessage(errorMessage);
688 setPageComplete(false);
691 private void selectionComplete(final URIish u, final RemoteConfig rc) {
692 setExposedSelection(u, rc);
693 setErrorMessage(null);
694 setPageComplete(true);
695 notifySelectionChanged();
698 private void setExposedSelection(final URIish u, final RemoteConfig rc) {
699 final RepositorySelection newSelection = new RepositorySelection(u, rc);
700 if (newSelection.equals(selection))
701 return;
703 selection = newSelection;
704 notifySelectionChanged();
707 private void updateRemoteAndURIPanels() {
708 setEnabledRecursively(uriPanel, isURISelected());
709 if (uriPanel.getEnabled())
710 updateAuthGroup();
711 if (configuredRemotes != null)
712 setEnabledRecursively(remotePanel, !isURISelected());
715 private void updateAuthGroup() {
716 switch (scheme.getSelectionIndex()) {
717 case S_GIT:
718 hostText.setEnabled(true);
719 portText.setEnabled(true);
720 setEnabledRecursively(authGroup, false);
721 break;
722 case S_SSH:
723 case S_SFTP:
724 case S_HTTP:
725 case S_HTTPS:
726 case S_FTP:
727 hostText.setEnabled(true);
728 portText.setEnabled(true);
729 setEnabledRecursively(authGroup, true);
730 break;
731 case S_FILE:
732 hostText.setEnabled(false);
733 portText.setEnabled(false);
734 setEnabledRecursively(authGroup, false);
735 break;
739 @Override
740 public void setVisible(boolean visible) {
741 super.setVisible(visible);
742 if (visible)
743 uriText.setFocus();
747 * Adds a URI string to the list of previously added ones
749 * TODO move this to some proper preferences handling class instead of
750 * making it static
752 * @param stringToAdd
754 public static void saveUriInPrefs(String stringToAdd) {
756 List<String> uriStrings = getUrisFromPrefs();
758 if (uriStrings.indexOf(stringToAdd) == 0)
759 return;
760 uriStrings.add(0, stringToAdd);
762 IEclipsePreferences prefs = new InstanceScope().getNode(Activator
763 .getPluginId());
765 StringBuilder sb = new StringBuilder();
766 StringBuilder lb = new StringBuilder();
768 // there is no "good" separator for URIish, so we
769 // keep track of the URI lengths separately
770 for (String uriString : uriStrings) {
771 sb.append(uriString);
772 lb.append(uriString.length());
773 lb.append(" "); //$NON-NLS-1$
775 prefs.put(USED_URIS_PREF, sb.toString());
776 prefs.put(USED_URIS_LENGTH_PREF, lb.toString());
778 try {
779 prefs.flush();
780 } catch (BackingStoreException e) {
781 // we simply ignore this here
786 * Gets the previously added URIs from the preferences
788 * TODO move this to some proper preferences handling class instead of
789 * making it static
791 * @return a (possibly empty) list of URIs, never <code>null</code>
793 public static List<String> getUrisFromPrefs() {
795 // use a TreeSet to get the same sorting always
796 List<String> uriStrings = new ArrayList<String>();
798 IEclipsePreferences prefs = new InstanceScope().getNode(Activator
799 .getPluginId());
800 // since there is no "good" separator for URIish, so we
801 // keep track of the URI lengths separately
802 String uriLengths = prefs.get(USED_URIS_LENGTH_PREF, ""); //$NON-NLS-1$
803 String uris = prefs.get(USED_URIS_PREF, ""); //$NON-NLS-1$
805 StringTokenizer tok = new StringTokenizer(uriLengths, " "); //$NON-NLS-1$
806 int offset = 0;
807 while (tok.hasMoreTokens()) {
808 try {
809 int length = Integer.parseInt(tok.nextToken());
810 if (uris.length() >= (offset + length)) {
811 uriStrings.add(uris.substring(offset, offset + length));
812 offset += length;
814 } catch (NumberFormatException nfe) {
815 // ignore here
820 return uriStrings;
823 private void setEnabledRecursively(final Control control,
824 final boolean enable) {
825 control.setEnabled(enable);
826 if (control instanceof Composite)
827 for (final Control child : ((Composite) control).getChildren())
828 setEnabledRecursively(child, enable);
831 private void addContentProposalToUriText(Text uriTextField) {
833 UIUtils.addBulbDecorator(uriTextField, UIText.RepositorySelectionPage_ShowPreviousURIs_HoverText);
835 IContentProposalProvider cp = new IContentProposalProvider() {
837 public IContentProposal[] getProposals(String contents, int position) {
839 List<IContentProposal> resultList = new ArrayList<IContentProposal>();
841 String patternString = contents;
842 while (patternString.length() > 0
843 && patternString.charAt(0) == ' ')
844 patternString = patternString.substring(1);
845 // make the simplest possible pattern check: allow "*"
846 // for multiple characters
847 patternString = patternString.replaceAll("\\x2A", ".*"); //$NON-NLS-1$ //$NON-NLS-2$
848 // make sure we add a (logical) * at the end
849 if (!patternString.endsWith(".*")) { //$NON-NLS-1$
850 patternString = patternString + ".*"; //$NON-NLS-1$
852 // let's compile a case-insensitive pattern (assumes ASCII only)
853 Pattern pattern;
854 try {
855 pattern = Pattern.compile(patternString,
856 Pattern.CASE_INSENSITIVE);
857 } catch (PatternSyntaxException e) {
858 pattern = null;
861 List<String> uriStrings = getUrisFromPrefs();
862 for (final String uriString : uriStrings) {
864 if (pattern != null
865 && !pattern.matcher(uriString).matches())
866 continue;
868 IContentProposal propsal = new IContentProposal() {
870 public String getLabel() {
871 return null;
874 public String getDescription() {
875 return null;
878 public int getCursorPosition() {
879 return 0;
882 public String getContent() {
883 return uriString;
886 resultList.add(propsal);
889 return resultList.toArray(new IContentProposal[resultList
890 .size()]);
894 // set the acceptance style to always replace the complete content
895 new ContentProposalAdapter(uriTextField, new TextContentAdapter(), cp,
896 null, null)
897 .setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
901 private void updateFields(final String text) {
902 try {
903 eventDepth++;
904 if (eventDepth != 1)
905 return;
907 final URIish u = new URIish(text);
908 safeSet(hostText, u.getHost());
909 safeSet(pathText, u.getPath());
910 safeSet(userText, u.getUser());
911 safeSet(passText, u.getPass());
913 if (u.getPort() > 0)
914 portText.setText(Integer.toString(u.getPort()));
915 else
916 portText.setText(""); //$NON-NLS-1$
918 if (isFile(u))
919 scheme.select(S_FILE);
920 else if (isSSH(u))
921 scheme.select(S_SSH);
922 else {
923 for (int i = 0; i < DEFAULT_SCHEMES.length; i++) {
924 if (DEFAULT_SCHEMES[i].equals(u.getScheme())) {
925 scheme.select(i);
926 break;
931 updateAuthGroup();
932 uri = u;
933 } catch (URISyntaxException err) {
934 // leave uriText as it is, but clean up underlying uri and
935 // decomposed fields
936 uri = new URIish();
937 hostText.setText(""); //$NON-NLS-1$
938 pathText.setText(""); //$NON-NLS-1$
939 userText.setText(""); //$NON-NLS-1$
940 passText.setText(""); //$NON-NLS-1$
941 portText.setText(""); //$NON-NLS-1$
942 scheme.select(0);
943 } finally {
944 eventDepth--;
946 checkPage();