/*******************************************************************************
* Copyright (c) 2000, 2009 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
*******************************************************************************/
package net.sf.eclipsefp.haskell.ui.internal.wizards;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Observable;
import java.util.Observer;
import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin;
import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin;
import net.sf.eclipsefp.haskell.ui.dialog.Validator;
import net.sf.eclipsefp.haskell.ui.dialog.ValidatorManager;
import net.sf.eclipsefp.haskell.ui.dialog.dialogfields.DialogField;
import net.sf.eclipsefp.haskell.ui.dialog.dialogfields.IDialogFieldListener;
import net.sf.eclipsefp.haskell.ui.dialog.dialogfields.IStringButtonAdapter;
import net.sf.eclipsefp.haskell.ui.dialog.dialogfields.LayoutUtil;
import net.sf.eclipsefp.haskell.ui.dialog.dialogfields.SelectionButtonDialogField;
import net.sf.eclipsefp.haskell.ui.dialog.dialogfields.StringButtonDialogField;
import net.sf.eclipsefp.haskell.ui.dialog.dialogfields.StringDialogField;
import net.sf.eclipsefp.haskell.ui.internal.util.UITexts;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.osgi.util.NLS;
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.Group;
/**
* Largely a copy of org.eclipse.jdt.ui.wizards.NewJavaProjectWizardPageOne.
*
* It would make sense to extend {@link WizardNewProjectCreationPage}. However,
* that class does not allow setting the project name, so then we couldn't set
* the project name based on the directory name of the existing source.
*
* @author Thomas ten Cate
*/
public class NewProjectWizardPage extends WizardPage {
/**
* Request a project name. Fires an event whenever the text field is
* changed, regardless of its content.
*/
private final class NameGroup extends Observable implements IDialogFieldListener {
protected final StringDialogField fNameField;
public NameGroup() {
// text field for project name
fNameField= new StringDialogField();
fNameField.setLabelText(UITexts.newProjectWizardPage_NameGroup_label_text);
fNameField.setDialogFieldListener(this);
}
public Control createControl(final Composite composite) {
Composite nameComposite= new Composite(composite, SWT.NONE);
nameComposite.setFont(composite.getFont());
nameComposite.setLayout(initGridLayout(new GridLayout(2, false), false));
fNameField.doFillIntoGrid(nameComposite, 2);
LayoutUtil.setHorizontalGrabbing(fNameField.getTextControl(null));
return nameComposite;
}
protected void fireEvent() {
setChanged();
notifyObservers();
}
public String getName() {
return fNameField.getText().trim();
}
public void postSetFocus() {
fNameField.postSetFocusOnDialogField(getShell().getDisplay());
}
public void setName(final String name) {
fNameField.setText(name);
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.ui.wizards.dialogfields.IDialogFieldListener#dialogFieldChanged(org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField)
*/
@Override
public void dialogFieldChanged(final DialogField field) {
fireEvent();
}
}
/**
* Request a location. Fires an event whenever the checkbox or the location
* field is changed, regardless of whether the change originates from the
* user or has been invoked programmatically.
*/
private final class LocationGroup extends Observable implements Observer, IStringButtonAdapter, IDialogFieldListener {
protected final SelectionButtonDialogField fWorkspaceRadio;
protected final SelectionButtonDialogField fExternalRadio;
protected final StringButtonDialogField fLocation;
private String fPreviousExternalLocation;
private final String DIALOGSTORE_LAST_EXTERNAL_LOC= HaskellUIPlugin.getPluginId() + ".last.external.project"; //$NON-NLS-1$
public LocationGroup() {
fWorkspaceRadio= new SelectionButtonDialogField(SWT.RADIO);
fWorkspaceRadio.setDialogFieldListener(this);
fWorkspaceRadio.setLabelText(UITexts.newProjectWizardPage_LocationGroup_workspace_desc);
fExternalRadio= new SelectionButtonDialogField(SWT.RADIO);
fExternalRadio.setLabelText(UITexts.newProjectWizardPage_LocationGroup_external_desc);
fLocation= new StringButtonDialogField(this);
fLocation.setDialogFieldListener(this);
fLocation.setLabelText(UITexts.newProjectWizardPage_LocationGroup_locationLabel_desc);
fLocation.setButtonLabel(UITexts.newProjectWizardPage_LocationGroup_browseButton_desc);
fExternalRadio.attachDialogField(fLocation);
fWorkspaceRadio.setSelection(true);
fExternalRadio.setSelection(false);
fPreviousExternalLocation= ""; //$NON-NLS-1$
}
public Control createControl(final Composite composite) {
final int numColumns= 3;
final Group group= new Group(composite, SWT.NONE);
group.setLayout(initGridLayout(new GridLayout(numColumns, false), true));
group.setText(UITexts.newProjectWizardPage_LocationGroup_title);
fWorkspaceRadio.doFillIntoGrid(group, numColumns);
fExternalRadio.doFillIntoGrid(group, numColumns);
fLocation.doFillIntoGrid(group, numColumns);
LayoutUtil.setHorizontalGrabbing(fLocation.getTextControl(null));
return group;
}
protected void fireEvent() {
setChanged();
notifyObservers();
}
protected String getDefaultPath(final String name) {
final IPath path= Platform.getLocation().append(name);
return path.toOSString();
}
/* (non-Javadoc)
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
@Override
public void update(final Observable o, final Object arg) {
if (isWorkspaceRadioSelected()) {
fLocation.setText(getDefaultPath(getProjectName()));
}
fireEvent();
}
public IPath getLocation() {
if (isWorkspaceRadioSelected()) {
return Platform.getLocation();
}
return Path.fromOSString(fLocation.getText().trim());
}
public boolean isWorkspaceRadioSelected() {
return fWorkspaceRadio.isSelected();
}
/**
* Returns <code>true</code> if the location is in the workspace
*
* @return <code>true</code> if the location is in the workspace
*/
public boolean isLocationInWorkspace() {
final String location= fLocationGroup.getLocation().toOSString();
IPath projectPath= Path.fromOSString(location);
return Platform.getLocation().isPrefixOf(projectPath);
}
public void setLocation(final IPath path) {
fWorkspaceRadio.setSelection(path == null);
if (path != null) {
fLocation.setText(path.toOSString());
} else {
fLocation.setText(getDefaultPath(getProjectName()));
}
fireEvent();
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.ui.wizards.dialogfields.IStringButtonAdapter#changeControlPressed(org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField)
*/
@Override
public void changeControlPressed(final DialogField field) {
final DirectoryDialog dialog= new DirectoryDialog(getShell());
dialog.setMessage(UITexts.newProjectWizardPage_directory_message);
String directoryName = fLocation.getText().trim();
if (directoryName.length() == 0) {
String prevLocation= HaskellUIPlugin.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) {
String oldDirectory= new Path(fLocation.getText().trim()).lastSegment();
fLocation.setText(selectedDirectory);
String lastSegment= new Path(selectedDirectory).lastSegment();
if (lastSegment != null && (getProjectName().length() == 0 || getProjectName().equals(oldDirectory))) {
setProjectName(lastSegment);
}
HaskellUIPlugin.getDefault().getDialogSettings().put(DIALOGSTORE_LAST_EXTERNAL_LOC, selectedDirectory);
}
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.ui.wizards.dialogfields.IDialogFieldListener#dialogFieldChanged(org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField)
*/
@Override
public void dialogFieldChanged(final DialogField field) {
if (field == fWorkspaceRadio) {
final boolean checked= fWorkspaceRadio.isSelected();
if (checked) {
fPreviousExternalLocation= fLocation.getText();
fLocation.setText(getDefaultPath(fNameGroup.getName()));
} else {
fLocation.setText(fPreviousExternalLocation);
}
}
fireEvent();
}
}
private final class ComponentGroup extends Observable implements IDialogFieldListener{
protected final SelectionButtonDialogField fExecutable;
protected final SelectionButtonDialogField fLibrary;
public ComponentGroup() {
fExecutable= new SelectionButtonDialogField(SWT.CHECK);
fExecutable.setDialogFieldListener(this);
fExecutable.setLabelText(UITexts.newProjectWizardPage_ComponentGroup_executable);
fExecutable.setSelection( true );
fLibrary= new SelectionButtonDialogField(SWT.CHECK);
fLibrary.setDialogFieldListener(this);
fLibrary.setLabelText(UITexts.newProjectWizardPage_ComponentGroup_library);
}
public Composite createControl(final Composite composite) {
final int numColumns= 1;
final Group group= new Group(composite, SWT.NONE);
group.setLayout(initGridLayout(new GridLayout(numColumns, false), true));
group.setText(UITexts.newProjectWizardPage_ComponentGroup_title);
fExecutable.doFillIntoGrid(group, numColumns);
fLibrary.doFillIntoGrid(group, numColumns);
return group;
}
protected void fireEvent() {
setChanged();
notifyObservers();
}
@Override
public void dialogFieldChanged( final DialogField field ) {
fireEvent();
}
public boolean isLibrary(){
return fLibrary.isSelected();
}
public boolean isExecutable(){
return fExecutable.isSelected();
}
}
/**
* Validate this page and show appropriate warnings and error NewWizardMessages.
*/
private final class PageValidator extends Validator {
public PageValidator( final ValidatorManager manager ) {
super( manager );
}
@Override
protected void doUpdate() {
final IWorkspace workspace= ResourcesPlugin.getWorkspace();
final String name= fNameGroup.getName();
// check whether the project name field is empty
if (name.length() == 0) {
setIncomplete(UITexts.newProjectWizardPage_Message_enterProjectName,IMessageProvider.INFORMATION);
return;
}
// check whether the project name is valid
final IStatus nameStatus= workspace.validateName(name, IResource.PROJECT);
if (!nameStatus.isOK()) {
setErrorMessage(nameStatus.getMessage());
setPageComplete(false);
return;
}
for (int a=0;a<name.length();a++){
char c=name.charAt( a );
if (!(Character.isDigit( c ) || Character.isLetter( c ) || c=='-')){
setIncomplete(UITexts.newProjectWizardPage_Message_projectInvalidName);
return;
}
}
CharsetEncoder asciiEncoder =
Charset.forName("US-ASCII").newEncoder();
if (!asciiEncoder.canEncode( name )){
setIncomplete(UITexts.newProjectWizardPage_Message_projectNonAsciiName);
return;
}
// check whether project already exists
final IProject handle= workspace.getRoot().getProject(name);
if (handle.exists()) {
setIncomplete(UITexts.newProjectWizardPage_Message_projectAlreadyExists);
return;
}
if (fLocationGroup.isWorkspaceRadioSelected()){
IPath projectLocation= ResourcesPlugin.getWorkspace().getRoot().getLocation().append(name);
if (projectLocation.toFile().exists()) {
try {
//correct casing
String canonicalPath= projectLocation.toFile().getCanonicalPath();
projectLocation= new Path(canonicalPath);
} catch (IOException e) {
HaskellUIPlugin.log(e);
}
String existingName= projectLocation.lastSegment();
if (!existingName.equals(fNameGroup.getName())) {
setIncomplete(NLS.bind(UITexts.newProjectWizardPageMessage_invalidProjectNameForWorkspaceRoot, existingName));
return;
}
}
}
final String location= fLocationGroup.getLocation().toOSString();
// check whether location is empty
if (location.length() == 0) {
setErrorMessage(null);
setIncomplete(UITexts.newProjectWizardPage_Message_enterLocation);
return;
}
// check whether the location is a syntactically correct path
if (!Path.EMPTY.isValidPath(location)) {
setIncomplete(UITexts.newProjectWizardPage_Message_invalidDirectory);
return;
}
IPath projectPath= Path.fromOSString(location);
if (fLocationGroup.isWorkspaceRadioSelected()) {
projectPath= projectPath.append(fNameGroup.getName());
}
if (projectPath.toFile().exists()) {//create from existing source
if (Platform.getLocation().isPrefixOf(projectPath)) { //create from existing source in workspace
// why?
// if (!Platform.getLocation().equals(projectPath.removeLastSegments(1))) {
// setErrorMessage(UITexts.newProjectWizardPage_Message_notOnWorkspaceRoot);
// setPageComplete(false);
// return;
// }
if (!projectPath.toFile().exists()) {
setIncomplete(UITexts.newProjectWizardPage_Message_notExistingProjectOnWorkspaceRoot);
return;
}
}
IPath cabalFile = BuildWrapperPlugin.getCabalFile( projectPath, name );
//projectPath.append(name).addFileExtension( FileUtil.EXTENSION_CABAL );
// cabal file exists: disable component choice
enableComponentControl(cabalFile==null );
setWarningMessage( UITexts.newProjectWizardPage_Message_alreadyExists);
setPageComplete(true);
return;
} else if (!fLocationGroup.isWorkspaceRadioSelected()) {//create at non existing external location
if (!canCreate(projectPath.toFile())) {
setIncomplete(UITexts.newProjectWizardPage_Message_cannotCreateAtExternalLocation);
return;
}
// If we do not place the contents in the workspace validate the
// location.
final IStatus locationStatus= workspace.validateProjectLocation(handle, projectPath);
if (!locationStatus.isOK()) {
setIncomplete(locationStatus.getMessage());
return;
}
}
enableComponentControl( true );
setPageComplete(true);
setMessage(null);
}
private boolean canCreate(final File file) {
File parent = file;
while (!parent.exists()) {
parent= parent.getParentFile();
if (parent == null) {
return false;
}
}
return parent.canWrite();
}
}
private static final String PAGE_NAME= "NewProjectWizardPage"; //$NON-NLS-1$
private final ValidatorManager fValidatorManager;
private PageValidator fValidator;
private final NameGroup fNameGroup;
private final LocationGroup fLocationGroup;
private final ComponentGroup fComponentGroup;
private Composite componentControl;
public NewProjectWizardPage() {
this(PAGE_NAME);
}
public NewProjectWizardPage( final String pageName ) {
super( pageName );
fNameGroup= new NameGroup();
fLocationGroup= new LocationGroup();
fComponentGroup=new ComponentGroup();
// establish connections
fNameGroup.addObserver(fLocationGroup);
// initialize all elements
fNameGroup.notifyObservers();
// create and connect validator
fValidatorManager= new ValidatorManager(this);
createValidators( fValidatorManager );
fNameGroup.addObserver(fValidator);
fLocationGroup.addObserver(fValidator);
// initialize defaults
setProjectName(""); //$NON-NLS-1$
setProjectLocationURI(null);
}
/**
* Subclasses may override to add additional validators,
* but must call the superclass method.
*/
protected void createValidators(final ValidatorManager manager) {
fValidator = new PageValidator(manager);
}
protected ValidatorManager getValidatorManager() {
return fValidatorManager;
}
/**
* @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
*
* Subclasses should not override; rather, override {@link #createControls}.
*/
@Override
public void createControl(final Composite parent) {
initializeDialogUnits(parent);
final Composite composite= new Composite(parent, SWT.NULL);
composite.setFont(parent.getFont());
composite.setLayout(initGridLayout(new GridLayout(1, false), true));
composite.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
createControls(composite);
setControl(composite);
fValidatorManager.fullUpdate();
}
/**
* Creates the controls within this page.
* Subclasses may override.
*/
protected void createControls(final Composite composite) {
createNameControl(composite);
createLocationControl(composite);
componentControl=createComponentControl(composite);
}
/**
* Creates the controls for the name field.
*
* @param composite the parent composite
* @return the created control
*/
protected Control createNameControl(final Composite composite) {
Control nameControl = fNameGroup.createControl(composite);
nameControl.setLayoutData(horizontalFillGridData());
return nameControl;
}
/**
* Creates the controls for the location field.
*
* @param composite the parent composite
* @return the created control
*/
protected Control createLocationControl(final Composite composite) {
Control locationControl = fLocationGroup.createControl(composite);
locationControl.setLayoutData(horizontalFillGridData());
return locationControl;
}
/**
* Creates the controls for the name field.
*
* @param composite the parent composite
* @return the created control
*/
protected Composite createComponentControl(final Composite composite) {
Composite componentControl = fComponentGroup.createControl(composite);
componentControl.setLayoutData(horizontalFillGridData());
return componentControl;
}
/**
* enable or disable the component group
* @param enabled
*/
protected void enableComponentControl(final boolean enabled){
if (componentControl!=null){
componentControl.setEnabled( enabled );
fComponentGroup.fExecutable.setEnabled( enabled );
fComponentGroup.fLibrary.setEnabled( enabled );
if (!enabled){
fComponentGroup.fExecutable.setSelection( false );
fComponentGroup.fLibrary.setSelection( false );
}
}
}
public boolean isLibrary(){
return fComponentGroup.isLibrary();
}
public boolean isExecutable(){
return fComponentGroup.isExecutable();
}
/**
* Gets a project name for the new project.
*
* @return the new project resource handle
*/
public String getProjectName() {
return fNameGroup.getName();
}
/**
* Sets the name of the new project
*
* @param name the new name
*/
public void setProjectName(final String name) {
if (name == null) {
throw new IllegalArgumentException();
}
fNameGroup.setName(name);
}
/**
* Returns the current project location path as entered by the user, or <code>null</code>
* if the project should be created in the workspace.
* @return the project location path or its anticipated initial value.
*/
public URI getProjectLocationURI() {
if (fLocationGroup.isLocationInWorkspace()) {
return null;
}
return URIUtil.toURI(fLocationGroup.getLocation());
}
/**
* Sets the project location of the new project or <code>null</code> if the project
* should be created in the workspace
*
* @param uri the new project location
*/
public void setProjectLocationURI(final URI uri) {
IPath path= uri != null ? URIUtil.toPath(uri) : null;
fLocationGroup.setLocation(path);
}
/**
* Creates a project resource handle for the current project name field
* value. The project handle is created relative to the workspace root.
* <p>
* This method does not create the project resource; this is the
* responsibility of <code>IProject::create</code> invoked by the new
* project resource wizard.
* </p>
*
* @return the new project resource handle
*/
public IProject getProjectHandle() {
return ResourcesPlugin.getWorkspace().getRoot().getProject(
getProjectName());
}
/**
* Returns the current project location path as entered by
* the user, or its anticipated initial value.
* Note that if the default has been returned the path
* in a project description used to create a project
* should not be set.
*
* @return the project location path or its anticipated initial value.
*/
public IPath getProjectLocationPath() {
return fLocationGroup.getLocation();
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.DialogPage#setVisible(boolean)
*/
@Override
public void setVisible(final boolean visible) {
super.setVisible(visible);
if (visible) {
fNameGroup.postSetFocus();
}
}
protected GridLayout initGridLayout(final GridLayout layout, final boolean margins) {
layout.horizontalSpacing= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
layout.verticalSpacing= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
if (margins) {
layout.marginWidth= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
layout.marginHeight= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
} else {
layout.marginWidth= 0;
layout.marginHeight= 0;
}
return layout;
}
protected GridData horizontalFillGridData() {
return new GridData(GridData.FILL_HORIZONTAL);
}
}