Honor workbench setting for Dialog font
[egit/spearce.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / components / RepositorySelectionPage.java
blob7088cc45315a5140177d14176ca50ccc8c14ec2b
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.dialogs.Dialog;
29 import org.eclipse.jface.fieldassist.ContentProposalAdapter;
30 import org.eclipse.jface.fieldassist.IContentProposal;
31 import org.eclipse.jface.fieldassist.IContentProposalProvider;
32 import org.eclipse.jface.fieldassist.TextContentAdapter;
33 import org.eclipse.jface.layout.GridDataFactory;
34 import org.eclipse.jgit.lib.Constants;
35 import org.eclipse.jgit.transport.RemoteConfig;
36 import org.eclipse.jgit.transport.Transport;
37 import org.eclipse.jgit.transport.URIish;
38 import org.eclipse.jgit.util.FS;
39 import org.eclipse.osgi.util.NLS;
40 import org.eclipse.swt.SWT;
41 import org.eclipse.swt.dnd.Clipboard;
42 import org.eclipse.swt.dnd.TextTransfer;
43 import org.eclipse.swt.events.ModifyEvent;
44 import org.eclipse.swt.events.ModifyListener;
45 import org.eclipse.swt.events.SelectionAdapter;
46 import org.eclipse.swt.events.SelectionEvent;
47 import org.eclipse.swt.events.VerifyEvent;
48 import org.eclipse.swt.events.VerifyListener;
49 import org.eclipse.swt.layout.GridData;
50 import org.eclipse.swt.layout.GridLayout;
51 import org.eclipse.swt.widgets.Button;
52 import org.eclipse.swt.widgets.Combo;
53 import org.eclipse.swt.widgets.Composite;
54 import org.eclipse.swt.widgets.Control;
55 import org.eclipse.swt.widgets.DirectoryDialog;
56 import org.eclipse.swt.widgets.Display;
57 import org.eclipse.swt.widgets.Group;
58 import org.eclipse.swt.widgets.Label;
59 import org.eclipse.swt.widgets.Text;
60 import org.osgi.service.prefs.BackingStoreException;
62 /**
63 * Wizard page that allows the user entering the location of a remote repository
64 * by specifying URL manually or selecting a preconfigured remote repository.
66 public class RepositorySelectionPage extends BaseWizardPage {
68 private final static String USED_URIS_PREF = "RepositorySelectionPage.UsedUris"; //$NON-NLS-1$
70 private final static String USED_URIS_LENGTH_PREF = "RepositorySelectionPage.UsedUrisLength"; //$NON-NLS-1$
72 private static final int REMOTE_CONFIG_TEXT_MAX_LENGTH = 80;
74 private static final int S_GIT = 0;
76 private static final int S_SSH = 1;
78 private static final int S_SFTP = 2;
80 private static final int S_HTTP = 3;
82 private static final int S_HTTPS = 4;
84 private static final int S_FTP = 5;
86 private static final int S_FILE = 6;
88 private static final String[] DEFAULT_SCHEMES;
90 static {
91 DEFAULT_SCHEMES = new String[7];
92 DEFAULT_SCHEMES[S_GIT] = "git"; //$NON-NLS-1$
93 DEFAULT_SCHEMES[S_SSH] = "git+ssh"; //$NON-NLS-1$
94 DEFAULT_SCHEMES[S_SFTP] = "sftp"; //$NON-NLS-1$
95 DEFAULT_SCHEMES[S_HTTP] = "http"; //$NON-NLS-1$
96 DEFAULT_SCHEMES[S_HTTPS] = "https"; //$NON-NLS-1$
97 DEFAULT_SCHEMES[S_FTP] = "ftp"; //$NON-NLS-1$
98 DEFAULT_SCHEMES[S_FILE] = "file"; //$NON-NLS-1$
101 private final List<RemoteConfig> configuredRemotes;
103 private final boolean sourceSelection;
105 private final String presetUri;
107 private Group authGroup;
109 private Text uriText;
111 private Text hostText;
113 private Text pathText;
115 private Text userText;
117 private Text passText;
119 private Combo scheme;
121 private Text portText;
123 private int eventDepth;
125 private URIish uri;
127 private RemoteConfig remoteConfig;
129 private RepositorySelection selection;
131 private Composite remotePanel;
133 private Button remoteButton;
135 private Combo remoteCombo;
137 private Composite uriPanel;
139 private Button uriButton;
142 * Create repository selection page, allowing user specifying URI or
143 * (optionally) choosing from preconfigured remotes list.
144 * <p>
145 * Wizard page is created without image, just with text description.
147 * @param sourceSelection
148 * true if dialog is used for source selection; false otherwise
149 * (destination selection). This indicates appropriate text
150 * messages.
151 * @param configuredRemotes
152 * list of configured remotes that user may select as an
153 * alternative to manual URI specification. Remotes appear in
154 * given order in GUI, with
155 * {@value Constants#DEFAULT_REMOTE_NAME} as the default choice.
156 * List may be null or empty - no remotes configurations appear
157 * in this case. Note that the provided list may be changed by
158 * this constructor.
159 * @param presetUri
160 * the pre-set URI, may be null
162 public RepositorySelectionPage(final boolean sourceSelection,
163 final List<RemoteConfig> configuredRemotes, String presetUri) {
165 super(RepositorySelectionPage.class.getName());
167 this.uri = new URIish();
168 this.sourceSelection = sourceSelection;
170 String preset = null;
171 if (presetUri == null) {
172 Clipboard clippy = new Clipboard(Display.getCurrent());
173 String text = (String) clippy.getContents(TextTransfer.getInstance());
174 try {
175 if(text != null) {
176 text = text.trim();
177 if(Transport.canHandleProtocol(new URIish(text), FS.DETECTED))
178 preset = text;
180 } catch (URISyntaxException e) {
181 preset = null;
183 clippy.dispose();
185 this.presetUri = preset;
187 this.configuredRemotes = getUsableConfigs(configuredRemotes);
188 this.remoteConfig = selectDefaultRemoteConfig();
190 selection = RepositorySelection.INVALID_SELECTION;
192 if (sourceSelection) {
193 setTitle(UIText.RepositorySelectionPage_sourceSelectionTitle);
194 setDescription(UIText.RepositorySelectionPage_sourceSelectionDescription);
195 } else {
196 setTitle(UIText.RepositorySelectionPage_destinationSelectionTitle);
197 setDescription(UIText.RepositorySelectionPage_destinationSelectionDescription);
202 * Create repository selection page, allowing user specifying URI, with no
203 * preconfigured remotes selection.
205 * @param sourceSelection
206 * true if dialog is used for source selection; false otherwise
207 * (destination selection). This indicates appropriate text
208 * messages.
209 * @param presetUri
210 * the pre-set URI, may be null
212 public RepositorySelectionPage(final boolean sourceSelection,
213 String presetUri) {
214 this(sourceSelection, null, presetUri);
218 * @return repository selection representing current page state.
220 public RepositorySelection getSelection() {
221 return selection;
225 * Compare current repository selection set by user to provided one.
227 * @param s
228 * repository selection to compare.
229 * @return true if provided selection is equal to current page selection,
230 * false otherwise.
232 public boolean selectionEquals(final RepositorySelection s) {
233 return selection.equals(s);
236 public void createControl(final Composite parent) {
237 final Composite panel = new Composite(parent, SWT.NULL);
238 panel.setLayout(new GridLayout());
240 if (configuredRemotes != null)
241 createRemotePanel(panel);
243 createUriPanel(panel);
245 if(presetUri != null)
246 updateFields(presetUri);
248 updateRemoteAndURIPanels();
249 Dialog.applyDialogFont(panel);
250 setControl(panel);
252 checkPage();
255 private void createRemotePanel(final Composite parent) {
256 remoteButton = new Button(parent, SWT.RADIO);
257 remoteButton
258 .setText(UIText.RepositorySelectionPage_configuredRemoteChoice
259 + ":"); //$NON-NLS-1$
260 remoteButton.setSelection(true);
262 remotePanel = new Composite(parent, SWT.NULL);
263 remotePanel.setLayout(new GridLayout());
264 final GridData gd = new GridData();
265 gd.grabExcessHorizontalSpace = true;
266 gd.horizontalAlignment = SWT.FILL;
267 remotePanel.setLayoutData(gd);
269 remoteCombo = new Combo(remotePanel, SWT.READ_ONLY | SWT.DROP_DOWN);
270 final String items[] = new String[configuredRemotes.size()];
271 int i = 0;
272 for (final RemoteConfig rc : configuredRemotes)
273 items[i++] = getTextForRemoteConfig(rc);
274 final int defaultIndex = configuredRemotes.indexOf(remoteConfig);
275 remoteCombo.setItems(items);
276 remoteCombo.select(defaultIndex);
277 remoteCombo.addSelectionListener(new SelectionAdapter() {
278 @Override
279 public void widgetSelected(SelectionEvent e) {
280 final int idx = remoteCombo.getSelectionIndex();
281 remoteConfig = configuredRemotes.get(idx);
282 checkPage();
287 private void createUriPanel(final Composite parent) {
288 if (configuredRemotes != null) {
289 uriButton = new Button(parent, SWT.RADIO);
290 uriButton.setText(UIText.RepositorySelectionPage_uriChoice + ":"); //$NON-NLS-1$
291 uriButton.addSelectionListener(new SelectionAdapter() {
292 public void widgetSelected(SelectionEvent e) {
293 // occurs either on selection or unselection event
294 updateRemoteAndURIPanels();
295 checkPage();
300 uriPanel = new Composite(parent, SWT.NULL);
301 uriPanel.setLayout(new GridLayout());
302 final GridData gd = new GridData();
303 gd.grabExcessHorizontalSpace = true;
304 gd.horizontalAlignment = SWT.FILL;
305 uriPanel.setLayoutData(gd);
307 createLocationGroup(uriPanel);
308 createConnectionGroup(uriPanel);
309 authGroup = createAuthenticationGroup(uriPanel);
312 private void createLocationGroup(final Composite parent) {
313 final Group g = createGroup(parent,
314 UIText.RepositorySelectionPage_groupLocation);
316 g.setLayout(new GridLayout(3, false));
318 newLabel(g, UIText.RepositorySelectionPage_promptURI + ":"); //$NON-NLS-1$
319 uriText = new Text(g, SWT.BORDER);
321 if (presetUri != null)
322 uriText.setText(presetUri);
324 uriText.setLayoutData(createFieldGridData());
325 uriText.addModifyListener(new ModifyListener() {
326 public void modifyText(final ModifyEvent e) {
327 updateFields(uriText.getText());
331 addContentProposalToUriText(uriText);
333 Button browseButton = new Button(g, SWT.NULL);
334 browseButton.setText(UIText.RepositorySelectionPage_BrowseLocalFile);
335 browseButton.addSelectionListener(new SelectionAdapter() {
337 @Override
338 public void widgetSelected(SelectionEvent e) {
339 DirectoryDialog dialog = new DirectoryDialog(getShell());
340 // if a file-uri was selected before, let's try to open
341 // the directory dialog on the same directory
342 if (!uriText.getText().equals("")) { //$NON-NLS-1$
343 try {
344 URI testUri = URI.create(uriText.getText().replace(
345 '\\', '/'));
346 if (testUri.getScheme().equals("file")) { //$NON-NLS-1$
347 String path = testUri.getPath();
348 if (path.length() > 1 && path.startsWith("/")) //$NON-NLS-1$
349 path = path.substring(1);
351 dialog.setFilterPath(path);
353 } catch (IllegalArgumentException e1) {
354 // ignore here, we just' don't set the directory in the
355 // browser
359 String result = dialog.open();
360 if (result != null)
361 uriText.setText("file:///" + result); //$NON-NLS-1$
366 newLabel(g, UIText.RepositorySelectionPage_promptHost + ":"); //$NON-NLS-1$
367 hostText = new Text(g, SWT.BORDER);
368 GridDataFactory.fillDefaults().span(2, 1).applyTo(hostText);
369 hostText.addModifyListener(new ModifyListener() {
370 public void modifyText(final ModifyEvent e) {
371 setURI(uri.setHost(nullString(hostText.getText())));
375 newLabel(g, UIText.RepositorySelectionPage_promptPath + ":"); //$NON-NLS-1$
376 pathText = new Text(g, SWT.BORDER);
377 GridDataFactory.fillDefaults().span(2, 1).applyTo(pathText);
378 pathText.addModifyListener(new ModifyListener() {
379 public void modifyText(final ModifyEvent e) {
380 setURI(uri.setPath(nullString(pathText.getText())));
386 private Group createAuthenticationGroup(final Composite parent) {
387 final Group g = createGroup(parent,
388 UIText.RepositorySelectionPage_groupAuthentication);
390 newLabel(g, UIText.RepositorySelectionPage_promptUser + ":"); //$NON-NLS-1$
391 userText = new Text(g, SWT.BORDER);
392 userText.setLayoutData(createFieldGridData());
393 userText.addModifyListener(new ModifyListener() {
394 public void modifyText(final ModifyEvent e) {
395 setURI(uri.setUser(nullString(userText.getText())));
399 newLabel(g, UIText.RepositorySelectionPage_promptPassword + ":"); //$NON-NLS-1$
400 passText = new Text(g, SWT.BORDER | SWT.PASSWORD);
401 passText.setLayoutData(createFieldGridData());
402 return g;
405 private void createConnectionGroup(final Composite parent) {
406 final Group g = createGroup(parent,
407 UIText.RepositorySelectionPage_groupConnection);
409 newLabel(g, UIText.RepositorySelectionPage_promptScheme + ":"); //$NON-NLS-1$
410 scheme = new Combo(g, SWT.DROP_DOWN | SWT.READ_ONLY);
411 scheme.setItems(DEFAULT_SCHEMES);
412 scheme.addSelectionListener(new SelectionAdapter() {
413 public void widgetSelected(final SelectionEvent e) {
414 final int idx = scheme.getSelectionIndex();
415 if (idx < 0)
416 setURI(uri.setScheme(null));
417 else
418 setURI(uri.setScheme(nullString(scheme.getItem(idx))));
419 updateAuthGroup();
423 newLabel(g, UIText.RepositorySelectionPage_promptPort + ":"); //$NON-NLS-1$
424 portText = new Text(g, SWT.BORDER);
425 portText.addVerifyListener(new VerifyListener() {
426 final Pattern p = Pattern.compile("^(?:[1-9][0-9]*)?$"); //$NON-NLS-1$
428 public void verifyText(final VerifyEvent e) {
429 final String v = portText.getText();
430 e.doit = p.matcher(
431 v.substring(0, e.start) + e.text + v.substring(e.end))
432 .matches();
435 portText.addModifyListener(new ModifyListener() {
436 public void modifyText(final ModifyEvent e) {
437 final String val = nullString(portText.getText());
438 if (val == null)
439 setURI(uri.setPort(-1));
440 else {
441 try {
442 setURI(uri.setPort(Integer.parseInt(val)));
443 } catch (NumberFormatException err) {
444 // Ignore it for now.
451 private Group createGroup(final Composite parent, final String text) {
452 final Group g = new Group(parent, SWT.NONE);
453 final GridLayout layout = new GridLayout();
454 layout.numColumns = 2;
455 g.setLayout(layout);
456 g.setText(text);
457 final GridData gd = new GridData();
458 gd.grabExcessHorizontalSpace = true;
459 gd.horizontalAlignment = SWT.FILL;
460 g.setLayoutData(gd);
461 return g;
464 private void newLabel(final Group g, final String text) {
465 new Label(g, SWT.NULL).setText(text);
468 private GridData createFieldGridData() {
469 return new GridData(SWT.FILL, SWT.DEFAULT, true, false);
472 private boolean isGIT(final URIish uri) {
473 return "git".equals(uri.getScheme()); //$NON-NLS-1$
476 private boolean isFile(final URIish uri) {
477 if ("file".equals(uri.getScheme()) || uri.getScheme() == null) //$NON-NLS-1$
478 return true;
479 if (uri.getHost() != null || uri.getPort() > 0 || uri.getUser() != null
480 || uri.getPass() != null || uri.getPath() == null)
481 return false;
482 if (uri.getScheme() == null)
483 return FS.DETECTED.resolve(new File("."), uri.getPath()).isDirectory(); //$NON-NLS-1$
484 return false;
487 private boolean isSSH(final URIish uri) {
488 if (!uri.isRemote())
489 return false;
490 final String scheme = uri.getScheme();
491 if ("ssh".equals(scheme)) //$NON-NLS-1$
492 return true;
493 if ("ssh+git".equals(scheme)) //$NON-NLS-1$
494 return true;
495 if ("git+ssh".equals(scheme)) //$NON-NLS-1$
496 return true;
497 if (scheme == null && uri.getHost() != null && uri.getPath() != null)
498 return true;
499 return false;
502 private String nullString(final String value) {
503 if (value == null)
504 return null;
505 final String v = value.trim();
506 return v.length() == 0 ? null : v;
509 private void safeSet(final Text text, final String value) {
510 text.setText(value != null ? value : ""); //$NON-NLS-1$
513 private boolean isURISelected() {
514 return configuredRemotes == null || presetUri != null
515 || uriButton.getSelection();
518 private void setURI(final URIish u) {
519 try {
520 eventDepth++;
521 if (eventDepth == 1) {
522 uri = u;
523 uriText.setText(uri.toString());
524 checkPage();
526 } finally {
527 eventDepth--;
531 private List<RemoteConfig> getUsableConfigs(final List<RemoteConfig> remotes) {
533 if (remotes == null)
534 return null;
536 List<RemoteConfig> result = new ArrayList<RemoteConfig>();
538 for (RemoteConfig config : remotes)
539 if ((sourceSelection && !config.getURIs().isEmpty() || !sourceSelection
540 && (!config.getPushURIs().isEmpty() || !config.getURIs()
541 .isEmpty())))
542 result.add(config);
544 if (!result.isEmpty())
545 return result;
547 return null;
550 private RemoteConfig selectDefaultRemoteConfig() {
551 if (configuredRemotes == null)
552 return null;
553 for (final RemoteConfig rc : configuredRemotes)
554 if (Constants.DEFAULT_REMOTE_NAME.equals(rc.getName()))
555 return rc;
556 return configuredRemotes.get(0);
559 private String getTextForRemoteConfig(final RemoteConfig rc) {
560 final StringBuilder sb = new StringBuilder(rc.getName());
561 sb.append(": "); //$NON-NLS-1$
562 boolean first = true;
563 List<URIish> uris;
564 if (sourceSelection) {
565 uris = rc.getURIs();
566 } else {
567 uris = rc.getPushURIs();
568 // if no push URIs are defined, use fetch URIs instead
569 if (uris.isEmpty()) {
570 uris = rc.getURIs();
574 for (final URIish u : uris) {
575 final String uString = u.toString();
576 if (first)
577 first = false;
578 else {
579 sb.append(", "); //$NON-NLS-1$
580 if (sb.length() + uString.length() > REMOTE_CONFIG_TEXT_MAX_LENGTH) {
581 sb.append("..."); //$NON-NLS-1$
582 break;
585 sb.append(uString);
587 return sb.toString();
590 private void checkPage() {
591 if (isURISelected()) {
592 assert uri != null;
593 if (uriText.getText().length() == 0) {
594 selectionIncomplete(null);
595 return;
598 try {
599 final URIish finalURI = new URIish(uriText.getText());
600 String proto = finalURI.getScheme();
601 if (proto == null && scheme.getSelectionIndex() >= 0)
602 proto = scheme.getItem(scheme.getSelectionIndex());
604 if (uri.getPath() == null) {
605 selectionIncomplete(NLS.bind(
606 UIText.RepositorySelectionPage_fieldRequired,
607 unamp(UIText.RepositorySelectionPage_promptPath),
608 proto));
609 return;
612 if (isFile(finalURI)) {
613 String badField = null;
614 if (uri.getHost() != null)
615 badField = UIText.RepositorySelectionPage_promptHost;
616 else if (uri.getUser() != null)
617 badField = UIText.RepositorySelectionPage_promptUser;
618 else if (uri.getPass() != null)
619 badField = UIText.RepositorySelectionPage_promptPassword;
620 if (badField != null) {
621 selectionIncomplete(NLS
622 .bind(
623 UIText.RepositorySelectionPage_fieldNotSupported,
624 unamp(badField), proto));
625 return;
628 final File d = FS.DETECTED.resolve(new File("."), uri.getPath()); //$NON-NLS-1$
629 if (!d.exists()) {
630 selectionIncomplete(NLS.bind(
631 UIText.RepositorySelectionPage_fileNotFound, d
632 .getAbsolutePath()));
633 return;
636 selectionComplete(finalURI, null);
637 return;
640 if (uri.getHost() == null) {
641 selectionIncomplete(NLS.bind(
642 UIText.RepositorySelectionPage_fieldRequired,
643 unamp(UIText.RepositorySelectionPage_promptHost),
644 proto));
645 return;
648 if (isGIT(finalURI)) {
649 String badField = null;
650 if (uri.getUser() != null)
651 badField = UIText.RepositorySelectionPage_promptUser;
652 else if (uri.getPass() != null)
653 badField = UIText.RepositorySelectionPage_promptPassword;
654 if (badField != null) {
655 selectionIncomplete(NLS
656 .bind(
657 UIText.RepositorySelectionPage_fieldNotSupported,
658 unamp(badField), proto));
659 return;
663 selectionComplete(finalURI, null);
664 return;
665 } catch (URISyntaxException e) {
666 selectionIncomplete(e.getReason());
667 return;
668 } catch (Exception e) {
669 Activator.logError(NLS.bind(
670 UIText.RepositorySelectionPage_errorValidating,
671 getClass().getName()),
673 selectionIncomplete(UIText.RepositorySelectionPage_internalError);
674 return;
676 } else {
677 assert remoteButton.getSelection();
678 selectionComplete(null, remoteConfig);
679 return;
683 private String unamp(String s) {
684 return s.replace("&", ""); //$NON-NLS-1$ //$NON-NLS-2$
687 private void selectionIncomplete(final String errorMessage) {
688 setExposedSelection(null, null);
689 setErrorMessage(errorMessage);
690 setPageComplete(false);
693 private void selectionComplete(final URIish u, final RemoteConfig rc) {
694 setExposedSelection(u, rc);
695 setErrorMessage(null);
696 setPageComplete(true);
697 notifySelectionChanged();
700 private void setExposedSelection(final URIish u, final RemoteConfig rc) {
701 final RepositorySelection newSelection = new RepositorySelection(u, rc);
702 if (newSelection.equals(selection))
703 return;
705 selection = newSelection;
706 notifySelectionChanged();
709 private void updateRemoteAndURIPanels() {
710 setEnabledRecursively(uriPanel, isURISelected());
711 if (uriPanel.getEnabled())
712 updateAuthGroup();
713 if (configuredRemotes != null)
714 setEnabledRecursively(remotePanel, !isURISelected());
717 private void updateAuthGroup() {
718 switch (scheme.getSelectionIndex()) {
719 case S_GIT:
720 hostText.setEnabled(true);
721 portText.setEnabled(true);
722 setEnabledRecursively(authGroup, false);
723 break;
724 case S_SSH:
725 case S_SFTP:
726 case S_HTTP:
727 case S_HTTPS:
728 case S_FTP:
729 hostText.setEnabled(true);
730 portText.setEnabled(true);
731 setEnabledRecursively(authGroup, true);
732 break;
733 case S_FILE:
734 hostText.setEnabled(false);
735 portText.setEnabled(false);
736 setEnabledRecursively(authGroup, false);
737 break;
741 @Override
742 public void setVisible(boolean visible) {
743 super.setVisible(visible);
744 if (visible)
745 uriText.setFocus();
749 * Adds a URI string to the list of previously added ones
751 * TODO move this to some proper preferences handling class instead of
752 * making it static
754 * @param stringToAdd
756 public static void saveUriInPrefs(String stringToAdd) {
758 List<String> uriStrings = getUrisFromPrefs();
760 if (uriStrings.indexOf(stringToAdd) == 0)
761 return;
762 uriStrings.add(0, stringToAdd);
764 IEclipsePreferences prefs = new InstanceScope().getNode(Activator
765 .getPluginId());
767 StringBuilder sb = new StringBuilder();
768 StringBuilder lb = new StringBuilder();
770 // there is no "good" separator for URIish, so we
771 // keep track of the URI lengths separately
772 for (String uriString : uriStrings) {
773 sb.append(uriString);
774 lb.append(uriString.length());
775 lb.append(" "); //$NON-NLS-1$
777 prefs.put(USED_URIS_PREF, sb.toString());
778 prefs.put(USED_URIS_LENGTH_PREF, lb.toString());
780 try {
781 prefs.flush();
782 } catch (BackingStoreException e) {
783 // we simply ignore this here
788 * Gets the previously added URIs from the preferences
790 * TODO move this to some proper preferences handling class instead of
791 * making it static
793 * @return a (possibly empty) list of URIs, never <code>null</code>
795 public static List<String> getUrisFromPrefs() {
797 // use a TreeSet to get the same sorting always
798 List<String> uriStrings = new ArrayList<String>();
800 IEclipsePreferences prefs = new InstanceScope().getNode(Activator
801 .getPluginId());
802 // since there is no "good" separator for URIish, so we
803 // keep track of the URI lengths separately
804 String uriLengths = prefs.get(USED_URIS_LENGTH_PREF, ""); //$NON-NLS-1$
805 String uris = prefs.get(USED_URIS_PREF, ""); //$NON-NLS-1$
807 StringTokenizer tok = new StringTokenizer(uriLengths, " "); //$NON-NLS-1$
808 int offset = 0;
809 while (tok.hasMoreTokens()) {
810 try {
811 int length = Integer.parseInt(tok.nextToken());
812 if (uris.length() >= (offset + length)) {
813 uriStrings.add(uris.substring(offset, offset + length));
814 offset += length;
816 } catch (NumberFormatException nfe) {
817 // ignore here
822 return uriStrings;
825 private void setEnabledRecursively(final Control control,
826 final boolean enable) {
827 control.setEnabled(enable);
828 if (control instanceof Composite)
829 for (final Control child : ((Composite) control).getChildren())
830 setEnabledRecursively(child, enable);
833 private void addContentProposalToUriText(Text uriTextField) {
835 UIUtils.addBulbDecorator(uriTextField, UIText.RepositorySelectionPage_ShowPreviousURIs_HoverText);
837 IContentProposalProvider cp = new IContentProposalProvider() {
839 public IContentProposal[] getProposals(String contents, int position) {
841 List<IContentProposal> resultList = new ArrayList<IContentProposal>();
843 String patternString = contents;
844 while (patternString.length() > 0
845 && patternString.charAt(0) == ' ')
846 patternString = patternString.substring(1);
847 // make the simplest possible pattern check: allow "*"
848 // for multiple characters
849 patternString = patternString.replaceAll("\\x2A", ".*"); //$NON-NLS-1$ //$NON-NLS-2$
850 // make sure we add a (logical) * at the end
851 if (!patternString.endsWith(".*")) { //$NON-NLS-1$
852 patternString = patternString + ".*"; //$NON-NLS-1$
854 // let's compile a case-insensitive pattern (assumes ASCII only)
855 Pattern pattern;
856 try {
857 pattern = Pattern.compile(patternString,
858 Pattern.CASE_INSENSITIVE);
859 } catch (PatternSyntaxException e) {
860 pattern = null;
863 List<String> uriStrings = getUrisFromPrefs();
864 for (final String uriString : uriStrings) {
866 if (pattern != null
867 && !pattern.matcher(uriString).matches())
868 continue;
870 IContentProposal propsal = new IContentProposal() {
872 public String getLabel() {
873 return null;
876 public String getDescription() {
877 return null;
880 public int getCursorPosition() {
881 return 0;
884 public String getContent() {
885 return uriString;
888 resultList.add(propsal);
891 return resultList.toArray(new IContentProposal[resultList
892 .size()]);
896 // set the acceptance style to always replace the complete content
897 new ContentProposalAdapter(uriTextField, new TextContentAdapter(), cp,
898 null, null)
899 .setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
903 private void updateFields(final String text) {
904 try {
905 eventDepth++;
906 if (eventDepth != 1)
907 return;
909 final URIish u = new URIish(text);
910 safeSet(hostText, u.getHost());
911 safeSet(pathText, u.getPath());
912 safeSet(userText, u.getUser());
913 safeSet(passText, u.getPass());
915 if (u.getPort() > 0)
916 portText.setText(Integer.toString(u.getPort()));
917 else
918 portText.setText(""); //$NON-NLS-1$
920 if (isFile(u))
921 scheme.select(S_FILE);
922 else if (isSSH(u))
923 scheme.select(S_SSH);
924 else {
925 for (int i = 0; i < DEFAULT_SCHEMES.length; i++) {
926 if (DEFAULT_SCHEMES[i].equals(u.getScheme())) {
927 scheme.select(i);
928 break;
933 updateAuthGroup();
934 uri = u;
935 } catch (URISyntaxException err) {
936 // leave uriText as it is, but clean up underlying uri and
937 // decomposed fields
938 uri = new URIish();
939 hostText.setText(""); //$NON-NLS-1$
940 pathText.setText(""); //$NON-NLS-1$
941 userText.setText(""); //$NON-NLS-1$
942 passText.setText(""); //$NON-NLS-1$
943 portText.setText(""); //$NON-NLS-1$
944 scheme.select(0);
945 } finally {
946 eventDepth--;
948 checkPage();