blob: 76bf366bcc25a5f8c8ee9f426b679038a46cfb79 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 487943
* Martin Karpisek <martin.karpisek@gmail.com> - Bug 526283
*******************************************************************************/
package org.eclipse.pde.internal.ui.wizards.imports;
import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.*;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.plugin.TargetPlatform;
import org.eclipse.pde.internal.core.ICoreConstants;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.feature.ExternalFeatureModel;
import org.eclipse.pde.internal.core.ifeature.IFeatureModel;
import org.eclipse.pde.internal.ui.*;
import org.eclipse.pde.internal.ui.shared.CachedCheckboxTreeViewer;
import org.eclipse.pde.internal.ui.shared.FilteredCheckboxTree;
import org.eclipse.pde.internal.ui.shared.target.Messages;
import org.eclipse.pde.internal.ui.util.SWTUtil;
import org.eclipse.pde.internal.ui.wizards.ListUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.progress.UIJob;
/**
* Wizard page used when importing features. Allows the user to choose a location (Default or a folder) to load features from
* then select one or more features to import into the workspace.
*
* @see FeatureImportWizard
*/
public class FeatureImportWizardPage extends WizardPage {
private static final String SETTINGS_DROPLOCATION = "droplocation"; //$NON-NLS-1$
private static final String SETTINGS_DOOTHER = "doother"; //$NON-NLS-1$
private static final String SETTINGS_NOT_BINARY = "notbinary"; //$NON-NLS-1$
/**
* How long to wait before validating the directory
*/
protected static final int TYPING_DELAY = 300;
private Label fOtherLocationLabel;
private Button fRuntimeLocationButton;
private Button fBrowseButton;
private Combo fDropLocation;
private boolean fSelfSetLocation;
private String fCurrDropLocation;
private Job fTextChangedJob;
private CachedCheckboxTreeViewer fFeatureViewer;
private Label fCounterLabel;
private Button fSelectAllButton;
private Button fDeselectAllButton;
private IFeatureModel[] fModels;
private Button fBinaryButton;
class ContentProvider implements IStructuredContentProvider {
@Override
public Object[] getElements(Object parent) {
if (fModels != null)
return fModels;
return new Object[0];
}
}
private class LocationChangedJob extends UIJob {
public LocationChangedJob(Display jobDisplay, String name) {
super(jobDisplay, name);
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
validateDropLocation();
if (!fRuntimeLocationButton.getSelection()) {
String newLoc = fDropLocation.getText();
if (getMessageType() != WARNING && !newLoc.equals(fCurrDropLocation) && !fSelfSetLocation) {
handleReload();
}
fCurrDropLocation = newLoc;
}
return Status.OK_STATUS;
}
}
public FeatureImportWizardPage() {
super("FeatureImportWizardPage"); //$NON-NLS-1$
setTitle(PDEUIMessages.FeatureImportWizard_FirstPage_title);
setDescription(PDEUIMessages.FeatureImportWizard_FirstPage_desc);
PDEPlugin.getDefault().getLabelProvider().connect(this);
}
/*
* @see IDialogPage#createControl(Composite)
*/
@Override
public void createControl(Composite parent) {
initializeDialogUnits(parent);
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout(1, false));
fRuntimeLocationButton = new Button(composite, SWT.CHECK);
fillHorizontal(fRuntimeLocationButton, 1, false);
fRuntimeLocationButton.setText(PDEUIMessages.FeatureImportWizard_FirstPage_runtimeLocation);
Composite otherLocationComposite = SWTFactory.createComposite(composite, 3, 1, GridData.FILL_HORIZONTAL, 0, 0);
fOtherLocationLabel = new Label(otherLocationComposite, SWT.NULL);
fOtherLocationLabel.setText(PDEUIMessages.FeatureImportWizard_FirstPage_otherFolder);
fDropLocation = new Combo(otherLocationComposite, SWT.DROP_DOWN);
fillHorizontal(fDropLocation, 1, true);
fBrowseButton = new Button(otherLocationComposite, SWT.PUSH);
fBrowseButton.setText(PDEUIMessages.FeatureImportWizard_FirstPage_browse);
fBrowseButton.setLayoutData(new GridData());
SWTUtil.setButtonDimensionHint(fBrowseButton);
createFeatureTable(composite);
Composite underTableComp = SWTFactory.createComposite(composite, 2, 1, GridData.FILL_HORIZONTAL, 0, 0);
fCounterLabel = SWTFactory.createLabel(underTableComp, "", 1); //$NON-NLS-1$
fCounterLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, false));
Composite buttonComp = SWTFactory.createComposite(underTableComp, 2, 1, SWT.NONE, 0, 0);
buttonComp.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
fSelectAllButton = SWTFactory.createPushButton(buttonComp, PDEUIMessages.WizardCheckboxTablePart_selectAll, null);
fSelectAllButton.addSelectionListener(widgetSelectedAdapter(e -> {
fFeatureViewer.setAllChecked(true);
updateCounter();
dialogChanged();
}));
fDeselectAllButton = SWTFactory.createPushButton(buttonComp, PDEUIMessages.WizardCheckboxTablePart_deselectAll, null);
fDeselectAllButton.addSelectionListener(widgetSelectedAdapter(e -> {
fFeatureViewer.setAllChecked(false);
updateCounter();
dialogChanged();
}));
fBinaryButton = SWTFactory.createCheckButton(composite, PDEUIMessages.FeatureImportWizard_FirstPage_binaryImport, null, true, 1);
initializeFields(getDialogSettings());
hookListeners();
setControl(composite);
updateCounter();
dialogChanged();
Dialog.applyDialogFont(composite);
PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, IHelpContextIds.FEATURE_IMPORT_FIRST_PAGE);
}
private void hookListeners() {
fRuntimeLocationButton.addSelectionListener(widgetSelectedAdapter(e -> {
setOtherEnabled(!fRuntimeLocationButton.getSelection());
setLocation(fRuntimeLocationButton.getSelection() ? TargetPlatform.getLocation() : fCurrDropLocation);
handleReload();
}));
fDropLocation.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
// If the text is a combo item, immediately try to resolve, otherwise wait in case they type more
boolean isItem = false;
String[] items = fDropLocation.getItems();
for (String item : items) {
if (fDropLocation.getText().equals(item)) {
isItem = true;
break;
}
}
locationChanged(isItem ? 0 : TYPING_DELAY);
}
});
fBrowseButton.addSelectionListener(widgetSelectedAdapter(e -> {
IPath chosen = chooseDropLocation();
if (chosen != null) {
setLocation(chosen.toOSString());
handleReload();
}
}));
}
/**
* Called whenever the location or another aspect of the container has changed
* in the UI. Will schedule a UIJob to verify and resolve the container
* reporting any problems to the user. If a previous job is running or sleeping
* it will be cancelled.
*
* @param delay a delay to add to the job scheduling
*/
protected void locationChanged(long delay) {
if (fTextChangedJob == null) {
fTextChangedJob = new LocationChangedJob(getShell().getDisplay(), Messages.EditDirectoryContainerPage_3);
} else {
fTextChangedJob.cancel();
}
fTextChangedJob.schedule(delay);
}
private GridData fillHorizontal(Control control, int span, boolean grab) {
GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gd.horizontalSpan = span;
gd.grabExcessHorizontalSpace = grab;
control.setLayoutData(gd);
return gd;
}
private void initializeFields(IDialogSettings initialSettings) {
String[] dropItems = new String[0];
boolean doOther = false;
boolean binary = true;
if (initialSettings != null) {
doOther = initialSettings.getBoolean(SETTINGS_DOOTHER);
binary = !initialSettings.getBoolean(SETTINGS_NOT_BINARY);
ArrayList<String> items = new ArrayList<>();
for (int i = 0; i < 6; i++) {
String curr = initialSettings.get(SETTINGS_DROPLOCATION + String.valueOf(i));
if (curr != null && !items.contains(curr))
items.add(curr);
}
if (items.isEmpty())
items.add(""); //$NON-NLS-1$
dropItems = items.toArray(new String[items.size()]);
}
fDropLocation.setItems(dropItems);
fRuntimeLocationButton.setSelection(!doOther);
setOtherEnabled(doOther);
fCurrDropLocation = doOther ? dropItems[0] : TargetPlatform.getLocation();
setLocation(fCurrDropLocation);
fBinaryButton.setSelection(binary);
validateDropLocation();
handleReload();
}
private void setOtherEnabled(boolean enabled) {
fOtherLocationLabel.setEnabled(enabled);
fDropLocation.setEnabled(enabled);
fBrowseButton.setEnabled(enabled);
}
public void storeSettings(boolean finishPressed) {
IDialogSettings settings = getDialogSettings();
boolean other = !fRuntimeLocationButton.getSelection();
boolean binary = fBinaryButton.getSelection();
if (finishPressed || fDropLocation.getText().length() > 0 && other) {
settings.put(SETTINGS_DROPLOCATION + String.valueOf(0), fDropLocation.getText());
String[] items = fDropLocation.getItems();
int nEntries = Math.min(items.length, 5);
for (int i = 0; i < nEntries; i++)
settings.put(SETTINGS_DROPLOCATION + String.valueOf(i + 1), items[i]);
}
if (finishPressed) {
settings.put(SETTINGS_DOOTHER, other);
settings.put(SETTINGS_NOT_BINARY, !binary);
}
}
/**
* Browses for a drop location.
*/
private IPath chooseDropLocation() {
DirectoryDialog dialog = new DirectoryDialog(getShell());
dialog.setFilterPath(fDropLocation.getText());
dialog.setText(PDEUIMessages.FeatureImportWizard_messages_folder_title);
dialog.setMessage(PDEUIMessages.FeatureImportWizard_messages_folder_message);
String res = dialog.open();
return res != null ? new Path(res) : null;
}
private void validateDropLocation() {
String errorMessage = null;
if (isOtherLocation()) {
IPath curr = getDropLocation();
if (curr.segmentCount() == 0)
errorMessage = PDEUIMessages.FeatureImportWizard_errors_locationMissing;
else if (!Path.ROOT.isValidPath(fDropLocation.getText()))
errorMessage = PDEUIMessages.FeatureImportWizard_errors_buildFolderInvalid;
else {
File file = curr.toFile();
if (!file.exists() || !file.isDirectory())
errorMessage = PDEUIMessages.FeatureImportWizard_errors_buildFolderMissing;
}
}
setErrorMessage(errorMessage);
setPageComplete(errorMessage == null);
}
public boolean isBinary() {
return fBinaryButton.getSelection();
}
/**
* Returns the drop location.
*/
public IPath getDropLocation() {
return new Path(fDropLocation.getText().trim());
}
public boolean isOtherLocation() {
return !fRuntimeLocationButton.getSelection();
}
private void handleReload() {
IFeatureModel[] models = getModels();
fFeatureViewer.setInput(models);
if (models != null) {
if (!fRuntimeLocationButton.getSelection()) {
String currItem = fDropLocation.getText();
if (models.length > 0 && fDropLocation.indexOf(currItem) == -1) {
fDropLocation.add(currItem, 0);
if (fDropLocation.getItemCount() > 6)
fDropLocation.remove(6);
storeSettings(false);
}
// When the models are loaded, the entire contents of the combo get selected. This can easily lead to overwriting the contents so we instead select the end.
fDropLocation.setSelection(new Point(fDropLocation.getText().length(), fDropLocation.getText().length()));
}
fFeatureViewer.setCheckedElements(models);
}
updateCounter();
dialogChanged();
}
public void createFeatureTable(Composite parent) {
Composite container = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
layout.marginHeight = layout.marginWidth = 0;
container.setLayout(layout);
GridData gd = new GridData(GridData.FILL_BOTH);
gd.verticalIndent = 5;
gd.heightHint = gd.widthHint = 300;
container.setLayoutData(gd);
PatternFilter filter = new PatternFilter();
filter.setIncludeLeadingWildcard(true);
FilteredCheckboxTree tree = new FilteredCheckboxTree(container, null, SWT.NONE, filter);
fFeatureViewer = tree.getCheckboxTreeViewer();
fFeatureViewer.setContentProvider(new ITreeContentProvider() {
@Override
public Object[] getElements(Object inputElement) {
if (inputElement instanceof Object[]) {
return (Object[]) inputElement;
}
return new Object[0];
}
@Override
public boolean hasChildren(Object element) {
return false;
}
@Override
public Object getParent(Object element) {
return null;
}
@Override
public Object[] getChildren(Object parentElement) {
return new Object[0];
}
});
fFeatureViewer.setLabelProvider(PDEPlugin.getDefault().getLabelProvider());
fFeatureViewer.setComparator(ListUtil.FEATURE_COMPARATOR);
fFeatureViewer.addCheckStateListener(new ICheckStateListener() {
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
updateCounter();
dialogChanged();
}
});
fFeatureViewer.addDoubleClickListener(new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
IStructuredSelection selection = fFeatureViewer.getStructuredSelection();
if (!selection.isEmpty()) {
Object selected = selection.getFirstElement();
fFeatureViewer.setChecked(selected, !fFeatureViewer.getChecked(selected));
updateCounter();
dialogChanged();
}
}
});
}
private void updateCounter() {
int total = 0;
if (fModels != null) {
total = fModels.length;
}
int checked = fFeatureViewer.getCheckedLeafCount();
fCounterLabel.setText(NLS.bind(PDEUIMessages.WizardCheckboxTablePart_counter, Integer.toString(checked),
Integer.toString(total)));
}
@Override
public void dispose() {
super.dispose();
PDEPlugin.getDefault().getLabelProvider().disconnect(this);
}
public IFeatureModel[] getModels() {
final IPath home = getDropLocation();
final boolean useRuntimeLocation = fRuntimeLocationButton.getSelection() || TargetPlatform.getLocation().equals(fDropLocation.getText().trim());
IRunnableWithProgress runnable = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
monitor.beginTask(PDEUIMessages.FeatureImportWizard_messages_updating, IProgressMonitor.UNKNOWN);
ArrayList<IFeatureModel> result = new ArrayList<>();
if (useRuntimeLocation) {
IFeatureModel[] allModels = PDECore.getDefault().getFeatureModelManager().getModels();
for (IFeatureModel model : allModels)
if (model.getUnderlyingResource() == null)
result.add(model);
} else {
MultiStatus errors = doLoadFeatures(result, createPath(home));
if (errors != null && errors.getChildren().length > 0)
PDEPlugin.log(errors);
}
fModels = result.toArray(new IFeatureModel[result.size()]);
monitor.done();
}
};
IProgressService pservice = PlatformUI.getWorkbench().getProgressService();
try {
pservice.busyCursorWhile(runnable);
} catch (InvocationTargetException e) {
} catch (InterruptedException e) {
}
return fModels;
}
private File createPath(IPath dropLocation) {
File featuresDir = new File(dropLocation.toFile(), ICoreConstants.FEATURE_FOLDER_NAME);
if (featuresDir.exists())
return featuresDir;
return null;
}
private MultiStatus doLoadFeatures(ArrayList<IFeatureModel> result, File path) {
if (path == null)
return null;
File[] dirs = path.listFiles();
if (dirs == null)
return null;
ArrayList<IStatus> resultStatus = new ArrayList<>();
for (File dir : dirs) {
if (dir.isDirectory()) {
File manifest = new File(dir, ICoreConstants.FEATURE_FILENAME_DESCRIPTOR);
if (manifest.exists()) {
IStatus status = doLoadFeature(dir, manifest, result);
if (status != null)
resultStatus.add(status);
}
}
}
return new MultiStatus(IPDEUIConstants.PLUGIN_ID, IStatus.OK, resultStatus.toArray(new IStatus[resultStatus.size()]), PDEUIMessages.FeatureImportWizard_DetailedPage_problemsLoading, null);
}
private IStatus doLoadFeature(File dir, File manifest, ArrayList<IFeatureModel> result) {
ExternalFeatureModel model = new ExternalFeatureModel();
model.setInstallLocation(dir.getAbsolutePath());
IStatus status = null;
InputStream stream = null;
try {
stream = new BufferedInputStream(new FileInputStream(manifest));
model.load(stream, false);
if (!model.isValid()) {
status = new Status(IStatus.WARNING, IPDEUIConstants.PLUGIN_ID, IStatus.OK, NLS.bind(PDEUIMessages.FeatureImportWizardPage_importHasInvalid, dir), null);
}
} catch (Exception e) {
// Errors in the file
status = new Status(IStatus.ERROR, IPDEUIConstants.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
}
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
if (status == null)
result.add(model);
return status;
}
public IFeatureModel[] getSelectedModels() {
Object[] selected = fFeatureViewer.getCheckedElements();
IFeatureModel[] result = new IFeatureModel[selected.length];
System.arraycopy(selected, 0, result, 0, selected.length);
return result;
}
private void dialogChanged() {
String message = null;
if (fFeatureViewer != null && (fModels == null || fModels.length == 0)) {
message = PDEUIMessages.FeatureImportWizard_messages_noFeatures;
}
setMessage(message, IMessageProvider.INFORMATION);
setPageComplete(fFeatureViewer.getCheckedLeafCount() > 0);
}
@Override
public boolean isPageComplete() {
return fFeatureViewer.getCheckedLeafCount() > 0;
}
private void setLocation(String location) {
fSelfSetLocation = true;
fDropLocation.setText(location);
fSelfSetLocation = false;
}
}