blob: 385140a98f4c8388c1d2b0ebfca298cf397e6c8a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
*******************************************************************************/
package org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.Observable;
import java.util.Observer;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IPathVariableManager;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.dltk.internal.corext.util.Messages;
import org.eclipse.dltk.internal.ui.dialogs.StatusUtil;
import org.eclipse.dltk.internal.ui.wizards.NewWizardMessages;
import org.eclipse.dltk.internal.ui.wizards.dialogfields.DialogField;
import org.eclipse.dltk.internal.ui.wizards.dialogfields.IDialogFieldListener;
import org.eclipse.dltk.internal.ui.wizards.dialogfields.IStringButtonAdapter;
import org.eclipse.dltk.internal.ui.wizards.dialogfields.LayoutUtil;
import org.eclipse.dltk.internal.ui.wizards.dialogfields.SelectionButtonDialogField;
import org.eclipse.dltk.internal.ui.wizards.dialogfields.StringButtonDialogField;
import org.eclipse.dltk.internal.ui.wizards.dialogfields.StringDialogField;
import org.eclipse.dltk.ui.DLTKUIPlugin;
import org.eclipse.dltk.ui.dialogs.StatusInfo;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.dialogs.StatusDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.ide.dialogs.PathVariableSelectionDialog;
public class LinkFolderDialog extends StatusDialog {
private final class FolderNameField extends Observable implements IDialogFieldListener {
private StringDialogField fNameDialogField;
public FolderNameField(Composite parent, int numOfColumns) {
createControls(parent, numOfColumns);
}
private void createControls(Composite parent, int numColumns) {
fNameDialogField= new StringDialogField();
fNameDialogField.setLabelText(NewWizardMessages.LinkFolderDialog_folderNameGroup_label);
fNameDialogField.doFillIntoGrid(parent, 2);
LayoutUtil.setHorizontalGrabbing(fNameDialogField.getTextControl(null));
LayoutUtil.setHorizontalSpan(fNameDialogField.getLabelControl(null), numColumns);
DialogField.createEmptySpace(parent, numColumns - 1);
fNameDialogField.setDialogFieldListener(this);
}
public void setText(String text) {
fNameDialogField.setText(text);
fNameDialogField.setFocus();
}
public String getText() {
return fNameDialogField.getText();
}
protected void fireEvent() {
setChanged();
notifyObservers();
}
@Override
public void dialogFieldChanged(DialogField field) {
fireEvent();
}
}
private final class LinkFields extends Observable implements IStringButtonAdapter, IDialogFieldListener{
private StringButtonDialogField fLinkLocation;
private static final String DIALOGSTORE_LAST_EXTERNAL_LOC= DLTKUIPlugin.PLUGIN_ID + ".last.external.project"; //$NON-NLS-1$
public LinkFields(Composite parent, int numColumns) {
createControls(parent, numColumns);
}
private void createControls(Composite parent, int numColumns) {
fLinkLocation= new StringButtonDialogField(this);
fLinkLocation.setLabelText(NewWizardMessages.LinkFolderDialog_dependenciesGroup_locationLabel_desc);
fLinkLocation.setButtonLabel(NewWizardMessages.LinkFolderDialog_dependenciesGroup_browseButton_desc);
fLinkLocation.setDialogFieldListener(this);
SelectionButtonDialogField variables= new SelectionButtonDialogField(SWT.PUSH);
variables.setLabelText(NewWizardMessages.LinkFolderDialog_dependenciesGroup_variables_desc);
variables.setDialogFieldListener(field -> handleVariablesButtonPressed());
fLinkLocation.doFillIntoGrid(parent, numColumns);
LayoutUtil.setHorizontalSpan(fLinkLocation.getLabelControl(null), numColumns);
LayoutUtil.setHorizontalGrabbing(fLinkLocation.getTextControl(null));
variables.doFillIntoGrid(parent, 1);
}
public String getLinkTarget() {
return fLinkLocation.getText();
}
public void setLinkTarget(String text) {
fLinkLocation.setText(text);
}
@Override
public void changeControlPressed(DialogField field) {
final DirectoryDialog dialog= new DirectoryDialog(getShell());
dialog.setMessage(NewWizardMessages.ScriptProjectWizardFirstPage_directory_message);
String directoryName = getLinkTarget().trim();
if (directoryName.length() == 0) {
String prevLocation= DLTKUIPlugin.getDefault().getDialogSettings().get(DIALOGSTORE_LAST_EXTERNAL_LOC);
if (prevLocation != null) {
directoryName= prevLocation;
}
}
if (directoryName.length() > 0) {
final File path = new File(directoryName);
if (path.exists())
dialog.setFilterPath(directoryName);
}
final String selectedDirectory = dialog.open();
if (selectedDirectory != null) {
fLinkLocation.setText(selectedDirectory);
if (fName == null) {
fFolderNameField.setText(selectedDirectory.substring(selectedDirectory.lastIndexOf(File.separatorChar) + 1));
}
DLTKUIPlugin.getDefault().getDialogSettings().put(DIALOGSTORE_LAST_EXTERNAL_LOC, selectedDirectory);
}
}
/**
* Opens a path variable selection dialog
*/
private void handleVariablesButtonPressed() {
int variableTypes = IResource.FOLDER;
// allow selecting file and folder variables when creating a
// linked file
/*if (type == IResource.FILE)
variableTypes |= IResource.FILE;*/
PathVariableSelectionDialog dialog = new PathVariableSelectionDialog(getShell(), variableTypes);
if (dialog.open() == IDialogConstants.OK_ID) {
String[] variableNames = (String[]) dialog.getResult();
if (variableNames != null && variableNames.length == 1) {
fLinkLocation.setText(variableNames[0]);
if (fName == null) {
fFolderNameField.setText(variableNames[0]);
}
}
}
}
@Override
public void dialogFieldChanged(DialogField field) {
fireEvent();
}
private void fireEvent() {
setChanged();
notifyObservers();
}
}
/**
* Validate this page and show appropriate warnings and error NewWizardMessages.
*/
private final class Validator implements Observer {
@Override
public void update(Observable o, Object arg) {
String name= fFolderNameField.getText();
IStatus nameStatus= validateFolderName(name);
if (nameStatus.matches(IStatus.ERROR)) {
updateStatus(nameStatus);
} else {
IStatus dependencyStatus= validateLinkLocation(name);
updateStatus(StatusUtil.getMoreSevere(nameStatus, dependencyStatus));
}
}
/**
* Validates this page's controls.
*
* @return IStatus indicating the validation result. IStatus.OK if the
* specified link target is valid given the linkHandle.
*/
private IStatus validateLinkLocation(String name) {
IWorkspace workspace= DLTKUIPlugin.getWorkspace();
IPath path= Path.fromOSString(fDependenciesGroup.getLinkTarget());
IStatus locationStatus= workspace.validateLinkLocation(fContainer.getFolder(new Path(name)), path);
if (locationStatus.matches(IStatus.ERROR))
return locationStatus;
// use the resolved link target name
String resolvedLinkTarget= resolveVariable();
path= new Path(resolvedLinkTarget);
File linkTargetFile= new Path(resolvedLinkTarget).toFile();
if (linkTargetFile.exists()) {
IStatus fileTypeStatus= validateFileType(linkTargetFile);
if (!fileTypeStatus.isOK())
return fileTypeStatus;
} else
if (locationStatus.isOK()) {
// locationStatus takes precedence over missing location warning.
return new StatusInfo(IStatus.ERROR, NewWizardMessages.NewFolderDialog_linkTargetNonExistent);
}
if (locationStatus.isOK()) {
return new StatusInfo();
}
return new StatusInfo(locationStatus.getSeverity(), locationStatus.getMessage());
}
/**
* Validates the type of the given file against the link type specified
* in the constructor.
*
* @param linkTargetFile file to validate
* @return IStatus indicating the validation result. IStatus.OK if the
* given file is valid.
*/
private IStatus validateFileType(File linkTargetFile) {
if (!linkTargetFile.isDirectory())
return new StatusInfo(IStatus.ERROR, NewWizardMessages.NewFolderDialog_linkTargetNotFolder);
return new StatusInfo();
}
/**
* Tries to resolve the value entered in the link target field as
* a variable, if the value is a relative path.
* Displays the resolved value if the entered value is a variable.
*/
private String resolveVariable() {
IPathVariableManager pathVariableManager = ResourcesPlugin.getWorkspace().getPathVariableManager();
IPath path= Path.fromOSString(fDependenciesGroup.getLinkTarget());
IPath resolvedPath= pathVariableManager.resolvePath(path);
return resolvedPath.toOSString();
}
/**
* Checks if the folder name is valid.
*
* @return <code>true</code> if validation was
* correct, <code>false</code> otherwise
*/
private IStatus validateFolderName(String name) {
if (name.length() == 0) {
return new StatusInfo(IStatus.ERROR, NewWizardMessages.NewFolderDialog_folderNameEmpty);
}
IStatus nameStatus = fContainer.getWorkspace().validateName(name, IResource.FOLDER);
if (!nameStatus.matches(IStatus.ERROR)) {
return nameStatus;
}
IPath path = new Path(name);
if (fContainer.findMember(path) != null) {
return new StatusInfo(IStatus.ERROR, Messages.format(NewWizardMessages.NewFolderDialog_folderNameEmpty_alreadyExists, name));
}
return nameStatus;
}
}
private FolderNameField fFolderNameField;
private LinkFields fDependenciesGroup;
private IContainer fContainer;
private IFolder fCreatedFolder;
private boolean fCreateLink;
private String fName;
private String fTarget;
/**
* Creates a NewFolderDialog
*
* @param parentShell parent of the new dialog
* @param container parent of the new folder
*
* @see HintTextGroup
*/
public LinkFolderDialog(Shell parentShell, IContainer container) {
this(parentShell, container, true);
}
public LinkFolderDialog(Shell parentShell, IContainer container, boolean createLink) {
super(parentShell);
fContainer = container;
fCreateLink= createLink;
setTitle(NewWizardMessages.LinkFolderDialog_title);
setShellStyle(getShellStyle() | SWT.RESIZE);
setStatusLineAboveButtons(true);
}
@Override
protected void configureShell(Shell shell) {
super.configureShell(shell);
}
@Override
public void create() {
super.create();
// initially disable the ok button since we don't preset the
// folder name field
getButton(IDialogConstants.OK_ID).setEnabled(false);
}
public void setName(String name) {
if (fFolderNameField != null) {
fFolderNameField.setText(name);
}
fName= name;
}
public void setLinkTarget(String target) {
if (fDependenciesGroup != null) {
fDependenciesGroup.setLinkTarget(target);
}
fTarget= target;
}
@Override
protected Control createDialogArea(Composite parent) {
initializeDialogUnits(parent);
int numOfColumns= 3;
Composite composite = new Composite(parent, SWT.NONE);
composite.setFont(parent.getFont());
GridLayout layout = new GridLayout(numOfColumns, false);
layout.marginHeight= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
layout.marginWidth= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
composite.setLayout(layout);
GridData gridData= new GridData(SWT.FILL, SWT.FILL, true, true);
gridData.minimumWidth= convertWidthInCharsToPixels(80);
composite.setLayoutData(gridData);
Label label= new Label(composite, SWT.NONE);
label.setFont(composite.getFont());
label.setText(Messages.format(NewWizardMessages.LinkFolderDialog_createIn, fContainer.getFullPath().makeRelative().toString()));
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, numOfColumns, 1));
fDependenciesGroup= new LinkFields(composite, numOfColumns);
if (fTarget != null) {
fDependenciesGroup.setLinkTarget(fTarget);
}
fFolderNameField= new FolderNameField(composite, numOfColumns);
if (fName != null) {
fFolderNameField.setText(fName);
}
Validator validator= new Validator();
fDependenciesGroup.addObserver(validator);
fFolderNameField.addObserver(validator);
return composite;
}
/**
* Creates a folder resource handle for the folder with the given name.
* The folder handle is created relative to the container specified during
* object creation.
*
* @param folderName the name of the folder resource to create a handle for
* @return the new folder resource handle
*/
private IFolder createFolderHandle(String folderName) {
IWorkspaceRoot workspaceRoot = fContainer.getWorkspace().getRoot();
IPath folderPath = fContainer.getFullPath().append(folderName);
IFolder folderHandle = workspaceRoot.getFolder(folderPath);
return folderHandle;
}
/**
* Creates a new folder with the given name and optionally linking to
* the specified link target.
*
* @param folderName name of the new folder
* @param linkTargetName name of the link target folder. may be null.
* @return IFolder the new folder
*/
private IFolder createNewFolder(final String folderName, final String linkTargetName) {
final IFolder folderHandle = createFolderHandle(folderName);
WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
@Override
public void execute(IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask(NewWizardMessages.NewFolderDialog_progress, 2000);
if (monitor.isCanceled())
throw new OperationCanceledException();
// create link to folder
folderHandle.createLink(Path.fromOSString(fDependenciesGroup.getLinkTarget()), IResource.ALLOW_MISSING_LOCAL, monitor);
if (monitor.isCanceled())
throw new OperationCanceledException();
} catch (StringIndexOutOfBoundsException e) {
e.printStackTrace();
}
finally {
monitor.done();
}
}
};
try {
new ProgressMonitorDialog(getShell())
.run(true, true, operation);
} catch (InterruptedException exception) {
return null;
} catch (InvocationTargetException exception) {
if (exception.getTargetException() instanceof CoreException) {
ErrorDialog.openError(getShell(), NewWizardMessages.NewFolderDialog_errorTitle,
null, // no special message
((CoreException) exception.getTargetException())
.getStatus());
} else {
// CoreExceptions are handled above, but unexpected runtime exceptions and errors may still occur.
DLTKUIPlugin.log(new Exception(Messages.format(
"Exception in {0}.createNewFolder(): {1}", //$NON-NLS-1$
new Object[] { getClass().getName(),
exception.getTargetException() })));
MessageDialog.openError(getShell(), NewWizardMessages.NewFolderDialog_errorTitle,
Messages.format(
NewWizardMessages.NewFolderDialog_internalError,
new Object[] { exception.getTargetException()
.getMessage() }));
}
return null;
}
return folderHandle;
}
/**
* Update the dialog's status line to reflect the given status. It is safe to call
* this method before the dialog has been opened.
*/
@Override
protected void updateStatus(IStatus status) {
super.updateStatus(status);
}
@Override
protected void okPressed() {
if (fCreateLink) {
String linkTarget = fDependenciesGroup.getLinkTarget();
linkTarget= linkTarget.length() == 0 ? null : linkTarget;
fCreatedFolder = createNewFolder(fFolderNameField.getText(), linkTarget);
} else {
fCreatedFolder = createFolderHandle(fFolderNameField.getText());
}
super.okPressed();
}
/**
* Returns the created folder or <code>null</code>
* if there is none.
*
* @return created folder or <code>null</code>
*/
public IFolder getCreatedFolder() {
return fCreatedFolder;
}
public IPath getLinkTarget() {
return Path.fromOSString(fDependenciesGroup.getLinkTarget());
}
}