| /******************************************************************************* |
| * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com> |
| * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> |
| * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> |
| * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com> |
| * Copyright (c) 2010, Benjamin Muskalla <bmuskalla@eclipsesource.com> |
| * Copyright (c) 2012, IBM Corporation |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| *******************************************************************************/ |
| package org.eclipse.egit.ui.internal.clone; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.jobs.IJobChangeEvent; |
| import org.eclipse.core.runtime.jobs.JobChangeAdapter; |
| import org.eclipse.egit.core.op.ListRemoteOperation; |
| import org.eclipse.egit.core.securestorage.UserPasswordCredentials; |
| import org.eclipse.egit.core.settings.GitSettings; |
| import org.eclipse.egit.ui.Activator; |
| import org.eclipse.egit.ui.UIPreferences; |
| import org.eclipse.egit.ui.UIUtils; |
| import org.eclipse.egit.ui.internal.CommonUtils; |
| import org.eclipse.egit.ui.internal.UIIcons; |
| import org.eclipse.egit.ui.internal.UIText; |
| import org.eclipse.egit.ui.internal.components.CachedCheckboxTreeViewer; |
| import org.eclipse.egit.ui.internal.components.FilteredCheckboxTree; |
| import org.eclipse.egit.ui.internal.components.RepositorySelection; |
| import org.eclipse.egit.ui.internal.credentials.EGitCredentialsProvider; |
| import org.eclipse.egit.ui.internal.dialogs.SourceBranchFailureDialog; |
| import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNodeType; |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.dialogs.IMessageProvider; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.jface.viewers.CheckStateChangedEvent; |
| import org.eclipse.jface.viewers.ICheckStateListener; |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.LabelProvider; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.wizard.WizardPage; |
| import org.eclipse.jgit.annotations.NonNull; |
| import org.eclipse.jgit.api.errors.TransportException; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.Ref; |
| import org.eclipse.jgit.transport.TagOpt; |
| import org.eclipse.jgit.transport.URIish; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.layout.RowLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Group; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.dialogs.PatternFilter; |
| import org.eclipse.ui.progress.WorkbenchJob; |
| |
| class SourceBranchPage extends WizardPage { |
| |
| private RepositorySelection validatedRepoSelection; |
| |
| private Ref head; |
| |
| private final List<Ref> availableRefs = new ArrayList<>(); |
| |
| private Label label; |
| |
| private String transportError; |
| |
| private Button selectB; |
| |
| private Button unselectB; |
| |
| private TagOpt tagOption = TagOpt.AUTO_FOLLOW; |
| |
| private CachedCheckboxTreeViewer refsViewer; |
| |
| private UserPasswordCredentials credentials; |
| |
| private String helpContext = null; |
| |
| SourceBranchPage() { |
| super(SourceBranchPage.class.getName()); |
| setTitle(UIText.SourceBranchPage_title); |
| setDescription(UIText.SourceBranchPage_description); |
| } |
| |
| List<Ref> getSelectedBranches() { |
| Object[] checkedElements = refsViewer.getCheckedElements(); |
| Ref[] checkedRefs = new Ref[checkedElements.length]; |
| System.arraycopy(checkedElements, 0, checkedRefs, 0, checkedElements.length); |
| return Arrays.asList(checkedRefs); |
| } |
| |
| TagOpt getTagOption() { |
| return tagOption; |
| } |
| |
| List<Ref> getAvailableBranches() { |
| return availableRefs; |
| } |
| |
| Ref getHEAD() { |
| return head; |
| } |
| |
| boolean isSourceRepoEmpty() { |
| return availableRefs.isEmpty(); |
| } |
| |
| boolean isAllSelected() { |
| return availableRefs.size() == refsViewer.getCheckedElements().length; |
| } |
| |
| @Override |
| public void createControl(final Composite parent) { |
| final Composite panel = new Composite(parent, SWT.NULL); |
| final GridLayout layout = new GridLayout(); |
| layout.numColumns = 1; |
| panel.setLayout(layout); |
| |
| label = new Label(panel, SWT.NONE); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); |
| |
| FilteredCheckboxTree fTree = new FilteredCheckboxTree(panel, null, |
| SWT.NONE, new PatternFilter()) { |
| /* |
| * Overridden to check page when refreshing is done. |
| */ |
| @Override |
| protected WorkbenchJob doCreateRefreshJob() { |
| WorkbenchJob refreshJob = super.doCreateRefreshJob(); |
| refreshJob.addJobChangeListener(new JobChangeAdapter() { |
| @Override |
| public void done(IJobChangeEvent event) { |
| if (event.getResult().isOK()) { |
| getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| checkPage(); |
| } |
| }); |
| } |
| } |
| }); |
| return refreshJob; |
| } |
| }; |
| UIUtils.associateLabel(fTree.getFilterControl(), label); |
| refsViewer = fTree.getCheckboxTreeViewer(); |
| |
| ITreeContentProvider provider = new ITreeContentProvider() { |
| |
| @Override |
| public void inputChanged(Viewer arg0, Object arg1, Object arg2) { |
| // nothing |
| } |
| |
| @Override |
| public void dispose() { |
| // nothing |
| } |
| |
| @Override |
| public Object[] getElements(Object input) { |
| return ((List) input).toArray(); |
| } |
| |
| @Override |
| public boolean hasChildren(Object element) { |
| return false; |
| } |
| |
| @Override |
| public Object getParent(Object element) { |
| return null; |
| } |
| |
| @Override |
| public Object[] getChildren(Object parentElement) { |
| return null; |
| } |
| }; |
| refsViewer.setContentProvider(provider); |
| refsViewer.setLabelProvider(new LabelProvider() { |
| @Override |
| public String getText(Object element) { |
| if (((Ref)element).getName().startsWith(Constants.R_HEADS)) |
| return ((Ref)element).getName().substring(Constants.R_HEADS.length()); |
| return ((Ref)element).getName(); |
| } |
| |
| @Override |
| public Image getImage(Object element) { |
| return UIIcons.getImage( |
| Activator.getDefault().getResourceManager(), |
| RepositoryTreeNodeType.REF.getIcon()); |
| } |
| }); |
| |
| refsViewer.addCheckStateListener(new ICheckStateListener() { |
| @Override |
| public void checkStateChanged(CheckStateChangedEvent event) { |
| checkPage(); |
| } |
| }); |
| final Composite bPanel = new Composite(panel, SWT.NONE); |
| bPanel.setLayout(new RowLayout()); |
| selectB = new Button(bPanel, SWT.PUSH); |
| selectB.setText(UIText.SourceBranchPage_selectAll); |
| selectB.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| refsViewer.setAllChecked(true); |
| checkPage(); |
| } |
| }); |
| unselectB = new Button(bPanel, SWT.PUSH); |
| unselectB.setText(UIText.SourceBranchPage_selectNone); |
| unselectB.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| refsViewer.setAllChecked(false); |
| checkPage(); |
| } |
| }); |
| createTagOptionGroup(panel); |
| bPanel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); |
| |
| Dialog.applyDialogFont(panel); |
| setControl(panel); |
| checkPage(); |
| } |
| |
| private void createTagOptionGroup(Composite parent) { |
| final Group tagsGroup = new Group(parent, SWT.NULL); |
| tagsGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); |
| tagsGroup.setText(UIText.TagOptions_groupName); |
| tagsGroup.setLayout(new GridLayout()); |
| createTagOptionButton(tagsGroup, TagOpt.AUTO_FOLLOW, |
| UIText.TagOptions_autoFollow); |
| createTagOptionButton(tagsGroup, TagOpt.FETCH_TAGS, |
| UIText.TagOptions_fetchTags); |
| createTagOptionButton(tagsGroup, TagOpt.NO_TAGS, |
| UIText.TagOptions_noTags); |
| } |
| |
| private void createTagOptionButton(Group tagsGroup, final TagOpt option, |
| String text) { |
| Button button = new Button(tagsGroup, SWT.RADIO); |
| button.setText(text); |
| button.setSelection(option == tagOption); |
| button.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| tagOption = option; |
| } |
| }); |
| } |
| |
| public void setSelection(@NonNull RepositorySelection selection) { |
| revalidate(selection); |
| } |
| |
| public void setCredentials(UserPasswordCredentials credentials) { |
| this.credentials = credentials; |
| } |
| |
| /** |
| * Set the ID for context sensitive help |
| * |
| * @param id |
| * help context |
| */ |
| public void setHelpContext(String id) { |
| helpContext = id; |
| } |
| |
| @Override |
| public void performHelp() { |
| PlatformUI.getWorkbench().getHelpSystem().displayHelp(helpContext); |
| } |
| |
| /** |
| * Check internal state for page completion status. This method should be |
| * called only when all necessary data from previous form is available. |
| */ |
| private void checkPage() { |
| setMessage(null); |
| int checkedElementCount = refsViewer.getCheckedElements().length; |
| selectB.setEnabled(checkedElementCount != availableRefs.size()); |
| unselectB.setEnabled(checkedElementCount != 0); |
| if (transportError != null) { |
| setErrorMessage(transportError); |
| setPageComplete(false); |
| return; |
| } |
| |
| if (getSelectedBranches().isEmpty()) { |
| setErrorMessage(UIText.SourceBranchPage_errorBranchRequired); |
| setPageComplete(false); |
| return; |
| } |
| setErrorMessage(null); |
| setPageComplete(true); |
| } |
| |
| private void checkForEmptyRepo() { |
| if (isSourceRepoEmpty()) { |
| setErrorMessage(null); |
| setMessage(UIText.SourceBranchPage_repoEmpty, IMessageProvider.WARNING); |
| setPageComplete(true); |
| } |
| } |
| |
| private void revalidate(@NonNull final RepositorySelection newRepoSelection) { |
| if (newRepoSelection.equals(validatedRepoSelection)) { |
| // URI hasn't changed, no need to refill the page with new data |
| checkPage(); |
| return; |
| } |
| |
| label.setText(NLS.bind(UIText.SourceBranchPage_branchList, |
| newRepoSelection.getURI().toString())); |
| label.getParent().layout(); |
| |
| validatedRepoSelection = null; |
| transportError = null; |
| head = null; |
| availableRefs.clear(); |
| refsViewer.setInput(null); |
| setPageComplete(false); |
| setErrorMessage(null); |
| setMessage(null); |
| label.getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| revalidateImpl(newRepoSelection); |
| } |
| }); |
| } |
| |
| private void revalidateImpl(final RepositorySelection newRepoSelection) { |
| if (label.isDisposed() || !isCurrentPage()) |
| return; |
| |
| final ListRemoteOperation listRemoteOp; |
| final URIish uri = newRepoSelection.getURI(); |
| try { |
| listRemoteOp = new ListRemoteOperation(uri, |
| GitSettings.getRemoteConnectionTimeout()); |
| if (credentials != null) |
| listRemoteOp |
| .setCredentialsProvider(new EGitCredentialsProvider( |
| credentials.getUser(), credentials |
| .getPassword())); |
| getContainer().run(true, true, new IRunnableWithProgress() { |
| @Override |
| public void run(IProgressMonitor monitor) |
| throws InvocationTargetException, InterruptedException { |
| listRemoteOp.run(monitor); |
| } |
| }); |
| } catch (InvocationTargetException e) { |
| Throwable why = e.getCause(); |
| transportError(why); |
| if (showDetailedFailureDialog()) |
| SourceBranchFailureDialog.show(getShell(), uri); |
| return; |
| } catch (InterruptedException e) { |
| transportError(UIText.SourceBranchPage_remoteListingCancelled); |
| return; |
| } |
| |
| final Ref idHEAD = listRemoteOp.getRemoteRef(Constants.HEAD); |
| head = null; |
| boolean headIsMaster = false; |
| final String masterBranchRef = Constants.R_HEADS + Constants.MASTER; |
| for (final Ref r : listRemoteOp.getRemoteRefs()) { |
| final String n = r.getName(); |
| if (!n.startsWith(Constants.R_HEADS)) { |
| continue; |
| } |
| availableRefs.add(r); |
| if (idHEAD == null || headIsMaster) { |
| continue; |
| } |
| ObjectId objectId = r.getObjectId(); |
| if (objectId == null) { |
| continue; |
| } |
| if (objectId.equals(idHEAD.getObjectId())) { |
| headIsMaster = masterBranchRef.equals(r.getName()); |
| if (head == null || headIsMaster) { |
| head = r; |
| } |
| } |
| } |
| Collections.sort(availableRefs, CommonUtils.REF_ASCENDING_COMPARATOR); |
| if (idHEAD != null && head == null) { |
| head = idHEAD; |
| availableRefs.add(0, idHEAD); |
| } |
| |
| validatedRepoSelection = newRepoSelection; |
| refsViewer.setInput(availableRefs); |
| refsViewer.setAllChecked(true); |
| checkPage(); |
| checkForEmptyRepo(); |
| } |
| |
| private void transportError(final Throwable why) { |
| Activator.logError(why.getMessage(), why); |
| Throwable cause = why.getCause(); |
| if (why instanceof TransportException && cause != null) |
| transportError(NLS.bind(getMessage(why), why.getMessage(), |
| cause.getMessage())); |
| else |
| transportError(why.getMessage()); |
| } |
| |
| private String getMessage(final Throwable why) { |
| if (why.getMessage().endsWith("Auth fail")) //$NON-NLS-1$ |
| return UIText.SourceBranchPage_AuthFailMessage; |
| else |
| return UIText.SourceBranchPage_CompositeTransportErrorMessage; |
| } |
| |
| private void transportError(final String msg) { |
| transportError = msg; |
| checkPage(); |
| } |
| |
| private boolean showDetailedFailureDialog() { |
| return Activator |
| .getDefault() |
| .getPreferenceStore() |
| .getBoolean( |
| UIPreferences.CLONE_WIZARD_SHOW_DETAILED_FAILURE_DIALOG); |
| } |
| |
| } |