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
;
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
.URIish
;
36 import org
.eclipse
.jgit
.util
.FS
;
37 import org
.eclipse
.osgi
.util
.NLS
;
38 import org
.eclipse
.swt
.SWT
;
39 import org
.eclipse
.swt
.events
.ModifyEvent
;
40 import org
.eclipse
.swt
.events
.ModifyListener
;
41 import org
.eclipse
.swt
.events
.SelectionAdapter
;
42 import org
.eclipse
.swt
.events
.SelectionEvent
;
43 import org
.eclipse
.swt
.events
.VerifyEvent
;
44 import org
.eclipse
.swt
.events
.VerifyListener
;
45 import org
.eclipse
.swt
.layout
.GridData
;
46 import org
.eclipse
.swt
.layout
.GridLayout
;
47 import org
.eclipse
.swt
.widgets
.Button
;
48 import org
.eclipse
.swt
.widgets
.Combo
;
49 import org
.eclipse
.swt
.widgets
.Composite
;
50 import org
.eclipse
.swt
.widgets
.Control
;
51 import org
.eclipse
.swt
.widgets
.DirectoryDialog
;
52 import org
.eclipse
.swt
.widgets
.Group
;
53 import org
.eclipse
.swt
.widgets
.Label
;
54 import org
.eclipse
.swt
.widgets
.Text
;
55 import org
.osgi
.service
.prefs
.BackingStoreException
;
58 * Wizard page that allows the user entering the location of a remote repository
59 * by specifying URL manually or selecting a preconfigured remote repository.
61 public class RepositorySelectionPage
extends BaseWizardPage
{
63 private final static String USED_URIS_PREF
= "RepositorySelectionPage.UsedUris"; //$NON-NLS-1$
65 private final static String USED_URIS_LENGTH_PREF
= "RepositorySelectionPage.UsedUrisLength"; //$NON-NLS-1$
67 private static final int REMOTE_CONFIG_TEXT_MAX_LENGTH
= 80;
69 private static final int S_GIT
= 0;
71 private static final int S_SSH
= 1;
73 private static final int S_SFTP
= 2;
75 private static final int S_HTTP
= 3;
77 private static final int S_HTTPS
= 4;
79 private static final int S_FTP
= 5;
81 private static final int S_FILE
= 6;
83 private static final String
[] DEFAULT_SCHEMES
;
86 DEFAULT_SCHEMES
= new String
[7];
87 DEFAULT_SCHEMES
[S_GIT
] = "git"; //$NON-NLS-1$
88 DEFAULT_SCHEMES
[S_SSH
] = "git+ssh"; //$NON-NLS-1$
89 DEFAULT_SCHEMES
[S_SFTP
] = "sftp"; //$NON-NLS-1$
90 DEFAULT_SCHEMES
[S_HTTP
] = "http"; //$NON-NLS-1$
91 DEFAULT_SCHEMES
[S_HTTPS
] = "https"; //$NON-NLS-1$
92 DEFAULT_SCHEMES
[S_FTP
] = "ftp"; //$NON-NLS-1$
93 DEFAULT_SCHEMES
[S_FILE
] = "file"; //$NON-NLS-1$
96 private final List
<RemoteConfig
> configuredRemotes
;
98 private final boolean sourceSelection
;
100 private final String presetUri
;
102 private Group authGroup
;
104 private Text uriText
;
106 private Text hostText
;
108 private Text pathText
;
110 private Text userText
;
112 private Text passText
;
114 private Combo scheme
;
116 private Text portText
;
118 private int eventDepth
;
122 private RemoteConfig remoteConfig
;
124 private RepositorySelection selection
;
126 private Composite remotePanel
;
128 private Button remoteButton
;
130 private Combo remoteCombo
;
132 private Composite uriPanel
;
134 private Button uriButton
;
137 * Create repository selection page, allowing user specifying URI or
138 * (optionally) choosing from preconfigured remotes list.
140 * Wizard page is created without image, just with text description.
142 * @param sourceSelection
143 * true if dialog is used for source selection; false otherwise
144 * (destination selection). This indicates appropriate text
146 * @param configuredRemotes
147 * list of configured remotes that user may select as an
148 * alternative to manual URI specification. Remotes appear in
149 * given order in GUI, with
150 * {@value Constants#DEFAULT_REMOTE_NAME} as the default choice.
151 * List may be null or empty - no remotes configurations appear
152 * in this case. Note that the provided list may be changed by
155 * the pre-set URI, may be null
157 public RepositorySelectionPage(final boolean sourceSelection
,
158 final List
<RemoteConfig
> configuredRemotes
, String presetUri
) {
160 super(RepositorySelectionPage
.class.getName());
162 this.uri
= new URIish();
163 this.sourceSelection
= sourceSelection
;
164 this.presetUri
= presetUri
;
166 this.configuredRemotes
= getUsableConfigs(configuredRemotes
);
167 this.remoteConfig
= selectDefaultRemoteConfig();
169 selection
= RepositorySelection
.INVALID_SELECTION
;
171 if (sourceSelection
) {
172 setTitle(UIText
.RepositorySelectionPage_sourceSelectionTitle
);
173 setDescription(UIText
.RepositorySelectionPage_sourceSelectionDescription
);
175 setTitle(UIText
.RepositorySelectionPage_destinationSelectionTitle
);
176 setDescription(UIText
.RepositorySelectionPage_destinationSelectionDescription
);
181 * Create repository selection page, allowing user specifying URI, with no
182 * preconfigured remotes selection.
184 * @param sourceSelection
185 * true if dialog is used for source selection; false otherwise
186 * (destination selection). This indicates appropriate text
189 * the pre-set URI, may be null
191 public RepositorySelectionPage(final boolean sourceSelection
,
193 this(sourceSelection
, null, presetUri
);
197 * @return repository selection representing current page state.
199 public RepositorySelection
getSelection() {
204 * Compare current repository selection set by user to provided one.
207 * repository selection to compare.
208 * @return true if provided selection is equal to current page selection,
211 public boolean selectionEquals(final RepositorySelection s
) {
212 return selection
.equals(s
);
215 public void createControl(final Composite parent
) {
216 final Composite panel
= new Composite(parent
, SWT
.NULL
);
217 panel
.setLayout(new GridLayout());
219 if (configuredRemotes
!= null)
220 createRemotePanel(panel
);
222 createUriPanel(panel
);
224 updateRemoteAndURIPanels();
230 private void createRemotePanel(final Composite parent
) {
231 remoteButton
= new Button(parent
, SWT
.RADIO
);
233 .setText(UIText
.RepositorySelectionPage_configuredRemoteChoice
234 + ":"); //$NON-NLS-1$
235 remoteButton
.setSelection(true);
237 remotePanel
= new Composite(parent
, SWT
.NULL
);
238 remotePanel
.setLayout(new GridLayout());
239 final GridData gd
= new GridData();
240 gd
.grabExcessHorizontalSpace
= true;
241 gd
.horizontalAlignment
= SWT
.FILL
;
242 remotePanel
.setLayoutData(gd
);
244 remoteCombo
= new Combo(remotePanel
, SWT
.READ_ONLY
| SWT
.DROP_DOWN
);
245 final String items
[] = new String
[configuredRemotes
.size()];
247 for (final RemoteConfig rc
: configuredRemotes
)
248 items
[i
++] = getTextForRemoteConfig(rc
);
249 final int defaultIndex
= configuredRemotes
.indexOf(remoteConfig
);
250 remoteCombo
.setItems(items
);
251 remoteCombo
.select(defaultIndex
);
252 remoteCombo
.addSelectionListener(new SelectionAdapter() {
254 public void widgetSelected(SelectionEvent e
) {
255 final int idx
= remoteCombo
.getSelectionIndex();
256 remoteConfig
= configuredRemotes
.get(idx
);
262 private void createUriPanel(final Composite parent
) {
263 if (configuredRemotes
!= null) {
264 uriButton
= new Button(parent
, SWT
.RADIO
);
265 uriButton
.setText(UIText
.RepositorySelectionPage_uriChoice
+ ":"); //$NON-NLS-1$
266 uriButton
.addSelectionListener(new SelectionAdapter() {
267 public void widgetSelected(SelectionEvent e
) {
268 // occurs either on selection or unselection event
269 updateRemoteAndURIPanels();
275 uriPanel
= new Composite(parent
, SWT
.NULL
);
276 uriPanel
.setLayout(new GridLayout());
277 final GridData gd
= new GridData();
278 gd
.grabExcessHorizontalSpace
= true;
279 gd
.horizontalAlignment
= SWT
.FILL
;
280 uriPanel
.setLayoutData(gd
);
282 createLocationGroup(uriPanel
);
283 createConnectionGroup(uriPanel
);
284 authGroup
= createAuthenticationGroup(uriPanel
);
287 private void createLocationGroup(final Composite parent
) {
288 final Group g
= createGroup(parent
,
289 UIText
.RepositorySelectionPage_groupLocation
);
291 g
.setLayout(new GridLayout(3, false));
293 newLabel(g
, UIText
.RepositorySelectionPage_promptURI
+ ":"); //$NON-NLS-1$
294 uriText
= new Text(g
, SWT
.BORDER
);
296 if (presetUri
!= null)
297 uriText
.setText(presetUri
);
299 uriText
.setLayoutData(createFieldGridData());
300 uriText
.addModifyListener(new ModifyListener() {
301 public void modifyText(final ModifyEvent e
) {
307 final URIish u
= new URIish(uriText
.getText());
308 safeSet(hostText
, u
.getHost());
309 safeSet(pathText
, u
.getPath());
310 safeSet(userText
, u
.getUser());
311 safeSet(passText
, u
.getPass());
314 portText
.setText(Integer
.toString(u
.getPort()));
316 portText
.setText(""); //$NON-NLS-1$
319 scheme
.select(S_FILE
);
321 scheme
.select(S_SSH
);
323 for (int i
= 0; i
< DEFAULT_SCHEMES
.length
; i
++) {
324 if (DEFAULT_SCHEMES
[i
].equals(u
.getScheme())) {
333 } catch (URISyntaxException err
) {
334 // leave uriText as it is, but clean up underlying uri and
337 hostText
.setText(""); //$NON-NLS-1$
338 pathText
.setText(""); //$NON-NLS-1$
339 userText
.setText(""); //$NON-NLS-1$
340 passText
.setText(""); //$NON-NLS-1$
341 portText
.setText(""); //$NON-NLS-1$
350 addContentProposalToUriText(uriText
);
352 Button browseButton
= new Button(g
, SWT
.NULL
);
353 browseButton
.setText(UIText
.RepositorySelectionPage_BrowseLocalFile
);
354 browseButton
.addSelectionListener(new SelectionAdapter() {
357 public void widgetSelected(SelectionEvent e
) {
358 DirectoryDialog dialog
= new DirectoryDialog(getShell());
359 // if a file-uri was selected before, let's try to open
360 // the directory dialog on the same directory
361 if (!uriText
.getText().equals("")) { //$NON-NLS-1$
363 URI testUri
= URI
.create(uriText
.getText().replace(
365 if (testUri
.getScheme().equals("file")) { //$NON-NLS-1$
366 String path
= testUri
.getPath();
367 if (path
.length() > 1 && path
.startsWith("/")) //$NON-NLS-1$
368 path
= path
.substring(1);
370 dialog
.setFilterPath(path
);
372 } catch (IllegalArgumentException e1
) {
373 // ignore here, we just' don't set the directory in the
378 String result
= dialog
.open();
380 uriText
.setText("file:///" + result
); //$NON-NLS-1$
385 newLabel(g
, UIText
.RepositorySelectionPage_promptHost
+ ":"); //$NON-NLS-1$
386 hostText
= new Text(g
, SWT
.BORDER
);
387 GridDataFactory
.fillDefaults().span(2, 1).applyTo(hostText
);
388 hostText
.addModifyListener(new ModifyListener() {
389 public void modifyText(final ModifyEvent e
) {
390 setURI(uri
.setHost(nullString(hostText
.getText())));
394 newLabel(g
, UIText
.RepositorySelectionPage_promptPath
+ ":"); //$NON-NLS-1$
395 pathText
= new Text(g
, SWT
.BORDER
);
396 GridDataFactory
.fillDefaults().span(2, 1).applyTo(pathText
);
397 pathText
.addModifyListener(new ModifyListener() {
398 public void modifyText(final ModifyEvent e
) {
399 setURI(uri
.setPath(nullString(pathText
.getText())));
404 private Group
createAuthenticationGroup(final Composite parent
) {
405 final Group g
= createGroup(parent
,
406 UIText
.RepositorySelectionPage_groupAuthentication
);
408 newLabel(g
, UIText
.RepositorySelectionPage_promptUser
+ ":"); //$NON-NLS-1$
409 userText
= new Text(g
, SWT
.BORDER
);
410 userText
.setLayoutData(createFieldGridData());
411 userText
.addModifyListener(new ModifyListener() {
412 public void modifyText(final ModifyEvent e
) {
413 setURI(uri
.setUser(nullString(userText
.getText())));
417 newLabel(g
, UIText
.RepositorySelectionPage_promptPassword
+ ":"); //$NON-NLS-1$
418 passText
= new Text(g
, SWT
.BORDER
| SWT
.PASSWORD
);
419 passText
.setLayoutData(createFieldGridData());
423 private void createConnectionGroup(final Composite parent
) {
424 final Group g
= createGroup(parent
,
425 UIText
.RepositorySelectionPage_groupConnection
);
427 newLabel(g
, UIText
.RepositorySelectionPage_promptScheme
+ ":"); //$NON-NLS-1$
428 scheme
= new Combo(g
, SWT
.DROP_DOWN
| SWT
.READ_ONLY
);
429 scheme
.setItems(DEFAULT_SCHEMES
);
430 scheme
.addSelectionListener(new SelectionAdapter() {
431 public void widgetSelected(final SelectionEvent e
) {
432 final int idx
= scheme
.getSelectionIndex();
434 setURI(uri
.setScheme(null));
436 setURI(uri
.setScheme(nullString(scheme
.getItem(idx
))));
441 newLabel(g
, UIText
.RepositorySelectionPage_promptPort
+ ":"); //$NON-NLS-1$
442 portText
= new Text(g
, SWT
.BORDER
);
443 portText
.addVerifyListener(new VerifyListener() {
444 final Pattern p
= Pattern
.compile("^(?:[1-9][0-9]*)?$"); //$NON-NLS-1$
446 public void verifyText(final VerifyEvent e
) {
447 final String v
= portText
.getText();
449 v
.substring(0, e
.start
) + e
.text
+ v
.substring(e
.end
))
453 portText
.addModifyListener(new ModifyListener() {
454 public void modifyText(final ModifyEvent e
) {
455 final String val
= nullString(portText
.getText());
457 setURI(uri
.setPort(-1));
460 setURI(uri
.setPort(Integer
.parseInt(val
)));
461 } catch (NumberFormatException err
) {
462 // Ignore it for now.
469 private Group
createGroup(final Composite parent
, final String text
) {
470 final Group g
= new Group(parent
, SWT
.NONE
);
471 final GridLayout layout
= new GridLayout();
472 layout
.numColumns
= 2;
475 final GridData gd
= new GridData();
476 gd
.grabExcessHorizontalSpace
= true;
477 gd
.horizontalAlignment
= SWT
.FILL
;
482 private void newLabel(final Group g
, final String text
) {
483 new Label(g
, SWT
.NULL
).setText(text
);
486 private GridData
createFieldGridData() {
487 return new GridData(SWT
.FILL
, SWT
.DEFAULT
, true, false);
490 private boolean isGIT(final URIish uri
) {
491 return "git".equals(uri
.getScheme()); //$NON-NLS-1$
494 private boolean isFile(final URIish uri
) {
495 if ("file".equals(uri
.getScheme()) || uri
.getScheme() == null) //$NON-NLS-1$
497 if (uri
.getHost() != null || uri
.getPort() > 0 || uri
.getUser() != null
498 || uri
.getPass() != null || uri
.getPath() == null)
500 if (uri
.getScheme() == null)
501 return FS
.resolve(new File("."), uri
.getPath()).isDirectory(); //$NON-NLS-1$
505 private boolean isSSH(final URIish uri
) {
508 final String scheme
= uri
.getScheme();
509 if ("ssh".equals(scheme
)) //$NON-NLS-1$
511 if ("ssh+git".equals(scheme
)) //$NON-NLS-1$
513 if ("git+ssh".equals(scheme
)) //$NON-NLS-1$
515 if (scheme
== null && uri
.getHost() != null && uri
.getPath() != null)
520 private String
nullString(final String value
) {
523 final String v
= value
.trim();
524 return v
.length() == 0 ?
null : v
;
527 private void safeSet(final Text text
, final String value
) {
528 text
.setText(value
!= null ? value
: ""); //$NON-NLS-1$
531 private boolean isURISelected() {
532 return configuredRemotes
== null || presetUri
!= null
533 || uriButton
.getSelection();
536 private void setURI(final URIish u
) {
539 if (eventDepth
== 1) {
541 uriText
.setText(uri
.toString());
549 private List
<RemoteConfig
> getUsableConfigs(final List
<RemoteConfig
> remotes
) {
554 List
<RemoteConfig
> result
= new ArrayList
<RemoteConfig
>();
556 for (RemoteConfig config
: remotes
)
557 if ((sourceSelection
&& !config
.getURIs().isEmpty() || !sourceSelection
558 && (!config
.getPushURIs().isEmpty() || !config
.getURIs()
562 if (!result
.isEmpty())
568 private RemoteConfig
selectDefaultRemoteConfig() {
569 if (configuredRemotes
== null)
571 for (final RemoteConfig rc
: configuredRemotes
)
572 if (Constants
.DEFAULT_REMOTE_NAME
.equals(rc
.getName()))
574 return configuredRemotes
.get(0);
577 private String
getTextForRemoteConfig(final RemoteConfig rc
) {
578 final StringBuilder sb
= new StringBuilder(rc
.getName());
579 sb
.append(": "); //$NON-NLS-1$
580 boolean first
= true;
582 if (sourceSelection
) {
585 uris
= rc
.getPushURIs();
586 // if no push URIs are defined, use fetch URIs instead
587 if (uris
.isEmpty()) {
592 for (final URIish u
: uris
) {
593 final String uString
= u
.toString();
597 sb
.append(", "); //$NON-NLS-1$
598 if (sb
.length() + uString
.length() > REMOTE_CONFIG_TEXT_MAX_LENGTH
) {
599 sb
.append("..."); //$NON-NLS-1$
605 return sb
.toString();
608 private void checkPage() {
609 if (isURISelected()) {
611 if (uriText
.getText().length() == 0) {
612 selectionIncomplete(null);
617 final URIish finalURI
= new URIish(uriText
.getText());
618 String proto
= finalURI
.getScheme();
619 if (proto
== null && scheme
.getSelectionIndex() >= 0)
620 proto
= scheme
.getItem(scheme
.getSelectionIndex());
622 if (uri
.getPath() == null) {
623 selectionIncomplete(NLS
.bind(
624 UIText
.RepositorySelectionPage_fieldRequired
,
625 unamp(UIText
.RepositorySelectionPage_promptPath
),
630 if (isFile(finalURI
)) {
631 String badField
= null;
632 if (uri
.getHost() != null)
633 badField
= UIText
.RepositorySelectionPage_promptHost
;
634 else if (uri
.getUser() != null)
635 badField
= UIText
.RepositorySelectionPage_promptUser
;
636 else if (uri
.getPass() != null)
637 badField
= UIText
.RepositorySelectionPage_promptPassword
;
638 if (badField
!= null) {
639 selectionIncomplete(NLS
641 UIText
.RepositorySelectionPage_fieldNotSupported
,
642 unamp(badField
), proto
));
646 final File d
= FS
.resolve(new File("."), uri
.getPath()); //$NON-NLS-1$
648 selectionIncomplete(NLS
.bind(
649 UIText
.RepositorySelectionPage_fileNotFound
, d
650 .getAbsolutePath()));
654 selectionComplete(finalURI
, null);
658 if (uri
.getHost() == null) {
659 selectionIncomplete(NLS
.bind(
660 UIText
.RepositorySelectionPage_fieldRequired
,
661 unamp(UIText
.RepositorySelectionPage_promptHost
),
666 if (isGIT(finalURI
)) {
667 String badField
= null;
668 if (uri
.getUser() != null)
669 badField
= UIText
.RepositorySelectionPage_promptUser
;
670 else if (uri
.getPass() != null)
671 badField
= UIText
.RepositorySelectionPage_promptPassword
;
672 if (badField
!= null) {
673 selectionIncomplete(NLS
675 UIText
.RepositorySelectionPage_fieldNotSupported
,
676 unamp(badField
), proto
));
681 selectionComplete(finalURI
, null);
683 } catch (URISyntaxException e
) {
684 selectionIncomplete(e
.getReason());
686 } catch (Exception e
) {
687 Activator
.logError(NLS
.bind(
688 UIText
.RepositorySelectionPage_errorValidating
,
689 getClass().getName()),
691 selectionIncomplete(UIText
.RepositorySelectionPage_internalError
);
695 assert remoteButton
.getSelection();
696 selectionComplete(null, remoteConfig
);
701 private String
unamp(String s
) {
702 return s
.replace("&", ""); //$NON-NLS-1$ //$NON-NLS-2$
705 private void selectionIncomplete(final String errorMessage
) {
706 setExposedSelection(null, null);
707 setErrorMessage(errorMessage
);
708 setPageComplete(false);
711 private void selectionComplete(final URIish u
, final RemoteConfig rc
) {
712 setExposedSelection(u
, rc
);
713 setErrorMessage(null);
714 setPageComplete(true);
715 notifySelectionChanged();
718 private void setExposedSelection(final URIish u
, final RemoteConfig rc
) {
719 final RepositorySelection newSelection
= new RepositorySelection(u
, rc
);
720 if (newSelection
.equals(selection
))
723 selection
= newSelection
;
724 notifySelectionChanged();
727 private void updateRemoteAndURIPanels() {
728 setEnabledRecursively(uriPanel
, isURISelected());
729 if (uriPanel
.getEnabled())
731 if (configuredRemotes
!= null)
732 setEnabledRecursively(remotePanel
, !isURISelected());
735 private void updateAuthGroup() {
736 switch (scheme
.getSelectionIndex()) {
738 hostText
.setEnabled(true);
739 portText
.setEnabled(true);
740 setEnabledRecursively(authGroup
, false);
747 hostText
.setEnabled(true);
748 portText
.setEnabled(true);
749 setEnabledRecursively(authGroup
, true);
752 hostText
.setEnabled(false);
753 portText
.setEnabled(false);
754 setEnabledRecursively(authGroup
, false);
760 public void setVisible(boolean visible
) {
761 super.setVisible(visible
);
767 * Adds a URI string to the list of previously added ones
769 * TODO move this to some proper preferences handling class instead of
774 public static void saveUriInPrefs(String stringToAdd
) {
776 List
<String
> uriStrings
= getUrisFromPrefs();
778 if (uriStrings
.indexOf(stringToAdd
) == 0)
780 uriStrings
.add(0, stringToAdd
);
782 IEclipsePreferences prefs
= new InstanceScope().getNode(Activator
785 StringBuilder sb
= new StringBuilder();
786 StringBuilder lb
= new StringBuilder();
788 // there is no "good" separator for URIish, so we
789 // keep track of the URI lengths separately
790 for (String uriString
: uriStrings
) {
791 sb
.append(uriString
);
792 lb
.append(uriString
.length());
793 lb
.append(" "); //$NON-NLS-1$
795 prefs
.put(USED_URIS_PREF
, sb
.toString());
796 prefs
.put(USED_URIS_LENGTH_PREF
, lb
.toString());
800 } catch (BackingStoreException e
) {
801 // we simply ignore this here
806 * Gets the previously added URIs from the preferences
808 * TODO move this to some proper preferences handling class instead of
811 * @return a (possibly empty) list of URIs, never <code>null</code>
813 public static List
<String
> getUrisFromPrefs() {
815 // use a TreeSet to get the same sorting always
816 List
<String
> uriStrings
= new ArrayList
<String
>();
818 IEclipsePreferences prefs
= new InstanceScope().getNode(Activator
820 // since there is no "good" separator for URIish, so we
821 // keep track of the URI lengths separately
822 String uriLengths
= prefs
.get(USED_URIS_LENGTH_PREF
, ""); //$NON-NLS-1$
823 String uris
= prefs
.get(USED_URIS_PREF
, ""); //$NON-NLS-1$
825 StringTokenizer tok
= new StringTokenizer(uriLengths
, " "); //$NON-NLS-1$
827 while (tok
.hasMoreTokens()) {
829 int length
= Integer
.parseInt(tok
.nextToken());
830 if (uris
.length() >= (offset
+ length
)) {
831 uriStrings
.add(uris
.substring(offset
, offset
+ length
));
834 } catch (NumberFormatException nfe
) {
843 private void setEnabledRecursively(final Control control
,
844 final boolean enable
) {
845 control
.setEnabled(enable
);
846 if (control
instanceof Composite
)
847 for (final Control child
: ((Composite
) control
).getChildren())
848 setEnabledRecursively(child
, enable
);
851 private void addContentProposalToUriText(Text uriTextField
) {
853 UIUtils
.addBulbDecorator(uriTextField
, UIText
.RepositorySelectionPage_ShowPreviousURIs_HoverText
);
855 IContentProposalProvider cp
= new IContentProposalProvider() {
857 public IContentProposal
[] getProposals(String contents
, int position
) {
859 List
<IContentProposal
> resultList
= new ArrayList
<IContentProposal
>();
861 String patternString
= contents
;
862 while (patternString
.length() > 0
863 && patternString
.charAt(0) == ' ')
864 patternString
= patternString
.substring(1);
865 // make the simplest possible pattern check: allow "*"
866 // for multiple characters
867 patternString
= patternString
.replaceAll("\\x2A", ".*"); //$NON-NLS-1$ //$NON-NLS-2$
868 // make sure we add a (logical) * at the end
869 if (!patternString
.endsWith(".*")) { //$NON-NLS-1$
870 patternString
= patternString
+ ".*"; //$NON-NLS-1$
872 // let's compile a case-insensitive pattern (assumes ASCII only)
875 pattern
= Pattern
.compile(patternString
,
876 Pattern
.CASE_INSENSITIVE
);
877 } catch (PatternSyntaxException e
) {
881 List
<String
> uriStrings
= getUrisFromPrefs();
882 for (final String uriString
: uriStrings
) {
885 && !pattern
.matcher(uriString
).matches())
888 IContentProposal propsal
= new IContentProposal() {
890 public String
getLabel() {
894 public String
getDescription() {
898 public int getCursorPosition() {
902 public String
getContent() {
906 resultList
.add(propsal
);
909 return resultList
.toArray(new IContentProposal
[resultList
914 // set the acceptance style to always replace the complete content
915 new ContentProposalAdapter(uriTextField
, new TextContentAdapter(), cp
,
917 .setProposalAcceptanceStyle(ContentProposalAdapter
.PROPOSAL_REPLACE
);