/*******************************************************************************
* Copyright (c) 2005, 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
* Intel corporation - cloned to CDT UI, to avoid discouraged access
*******************************************************************************/
package org.eclipse.cdt.ui.newui;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.osgi.util.TextProcessor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.cdt.internal.ui.newui.Messages;
/**
* @noextend This class is not intended to be subclassed by clients.
*/
public class ProjectContentsArea {
private static final String ERROR_INVALID_PATH = Messages.ProjectContentsArea_3;
private static final String ERROR_PATH_EMPTY = Messages.ProjectContentsArea_4;
private static final String ERROR_NOT_ABSOLUTE = Messages.ProjectContentsArea_6;
private static final String ERROR_NOT_VALID = Messages.ProjectContentsArea_7;
private static final String ERROR_CANNOT_CREATE = Messages.ProjectContentsArea_8;
private static final String ERROR_FILE_EXISTS = Messages.ProjectContentsArea_9;
private static final String BROWSE_LABEL = Messages.ProjectContentsArea_0;
private static final int SIZING_TEXT_FIELD_WIDTH = 250;
private static final String FILE_SCHEME = "file"; //$NON-NLS-1$
private Label locationLabel;
private Text locationPathField;
private Button browseButton;
private IErrorMessageReporter errorReporter;
private String projectName = AbstractPage.EMPTY_STR;
private String userPath = AbstractPage.EMPTY_STR;
private Button useDefaultsButton;
private IProject existingProject;
/**
* Create a new instance of a ProjectContentsLocationArea.
*
* @param composite
*/
public ProjectContentsArea(IErrorMessageReporter er, Composite composite) {
errorReporter = er;
createContents(composite, true);
}
/**
* Create the contents of the receiver.
*
* @param composite
* @param defaultEnabled
*/
private void createContents(Composite composite, boolean defaultEnabled) {
// project specification group
Composite projectGroup = new Composite(composite, SWT.NONE);
GridLayout layout = new GridLayout();
layout.numColumns = 4;
projectGroup.setLayout(layout);
projectGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
useDefaultsButton = new Button(projectGroup, SWT.CHECK | SWT.RIGHT);
useDefaultsButton.setText(Messages.ProjectContentsArea_1);
useDefaultsButton.setSelection(defaultEnabled);
GridData buttonData = new GridData();
buttonData.horizontalSpan = 4;
useDefaultsButton.setLayoutData(buttonData);
createUserEntryArea(projectGroup, defaultEnabled);
useDefaultsButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
boolean useDefaults = useDefaultsButton.getSelection();
if (useDefaults) {
userPath = locationPathField.getText();
locationPathField.setText(TextProcessor
.process(getDefaultPathDisplayString()));
} else {
locationPathField.setText(TextProcessor.process(userPath));
}
setUserAreaEnabled(!useDefaults);
}
});
setUserAreaEnabled(!defaultEnabled);
}
/**
* Return whether or not we are currently showing the default location for
* the project.
*
* @return boolean
*/
public boolean isDefault() {
return useDefaultsButton.getSelection();
}
/**
* Create the area for user entry.
*
* @param composite
* @param defaultEnabled
*/
private void createUserEntryArea(Composite composite, boolean defaultEnabled) {
// location label
locationLabel = new Label(composite, SWT.NONE);
locationLabel.setText(Messages.ProjectContentsArea_2);
// project location entry field
locationPathField = new Text(composite, SWT.BORDER);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.widthHint = SIZING_TEXT_FIELD_WIDTH;
data.horizontalSpan = 2;
locationPathField.setLayoutData(data);
// browse button
browseButton = new Button(composite, SWT.PUSH);
browseButton.setText(BROWSE_LABEL);
browseButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
handleLocationBrowseButtonPressed();
}
});
if (defaultEnabled) {
locationPathField.setText(TextProcessor
.process(getDefaultPathDisplayString()));
} else {
if (existingProject == null) {
locationPathField.setText(AbstractPage.EMPTY_STR);
} else {
locationPathField.setText(TextProcessor.process(existingProject
.getLocation().toString()));
}
}
locationPathField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
errorReporter.reportError(checkValidLocation());
}
});
}
/**
* Return the path we are going to display. If it is a file URI then remove
* the file prefix.
*
* @return String
*/
private String getDefaultPathDisplayString() {
URI defaultURI = null;
if (existingProject != null) {
defaultURI = existingProject.getLocationURI();
}
// Handle files specially. Assume a file if there is no project to query
if (defaultURI == null || defaultURI.getScheme().equals(FILE_SCHEME)) {
return Platform.getLocation().append(projectName).toString();
}
return defaultURI.toString();
}
/**
* Set the enablement state of the receiver.
*
* @param enabled
*/
private void setUserAreaEnabled(boolean enabled) {
locationLabel.setEnabled(enabled);
locationPathField.setEnabled(enabled);
browseButton.setEnabled(enabled);
}
/**
* Return the browse button. Usually referenced in order to set the layout
* data for a dialog.
*
* @return Button
*/
public Button getBrowseButton() {
return browseButton;
}
/**
* Open an appropriate directory browser
*/
private void handleLocationBrowseButtonPressed() {
String selectedDirectory = null;
String dirName = getPathFromLocationField();
if (!dirName.equals(AbstractPage.EMPTY_STR)) {
File f = new Path(dirName).toFile();
if (!f.exists()) dirName = AbstractPage.EMPTY_STR;
}
DirectoryDialog dialog = new DirectoryDialog(locationPathField.getShell());
dialog.setMessage(Messages.ProjectContentsArea_5);
dialog.setFilterPath(dirName);
selectedDirectory = dialog.open();
if (selectedDirectory != null)
updateLocationField(selectedDirectory);
}
/**
* Update the location field based on the selected path.
*
* @param selectedPath
*/
private void updateLocationField(String selectedPath) {
locationPathField.setText(TextProcessor.process(selectedPath));
}
/**
* Return the path on the location field.
*
* @return String
*/
private String getPathFromLocationField() {
URI fieldURI;
try {
fieldURI = new URI(locationPathField.getText());
} catch (URISyntaxException e) {
return locationPathField.getText();
}
return fieldURI.getPath();
}
/**
* Check if the entry in the widget location is valid. If it is valid return
* null. Otherwise return a string that indicates the problem.
*
* @return String
*/
private String checkValidLocation() {
if (isDefault()) return null;
String locationFieldContents = locationPathField.getText();
if (locationFieldContents.length() == 0)
return ERROR_PATH_EMPTY;
URI newPath = getProjectLocationURI();
if (newPath == null)
return ERROR_INVALID_PATH;
if (!Path.EMPTY.isValidPath(locationFieldContents))
return ERROR_NOT_VALID;
Path p = new Path(locationFieldContents);
if (!p.isAbsolute())
return ERROR_NOT_ABSOLUTE;
// try to create dummy file
File f = p.toFile();
if (!f.exists()) {
boolean result = false;
try {
result = f.createNewFile();
} catch (IOException e) {}
if (result)
f.delete();
else
return ERROR_CANNOT_CREATE;
} else {
if (f.isFile())
return ERROR_FILE_EXISTS;
}
//create a dummy project for the purpose of validation if necessary
IProject project = existingProject;
if (project == null) {
String name = new Path(locationFieldContents).lastSegment();
if (name != null && Path.EMPTY.isValidSegment(name))
project = ResourcesPlugin.getWorkspace().getRoot().getProject(name);
else
return ERROR_INVALID_PATH;
}
IStatus locationStatus = project.getWorkspace().validateProjectLocationURI(project, newPath);
if (!locationStatus.isOK()) {
return locationStatus.getMessage();
}
if (existingProject != null) {
URI projectPath = existingProject.getLocationURI();
if (projectPath != null && URIUtil.equals(projectPath, newPath))
return ERROR_INVALID_PATH;
}
return null;
}
/**
* Get the URI for the location field if possible.
* @return URI or <code>null</code> if it is not valid.
*/
public URI getProjectLocationURI() {
return URIUtil.toURI(locationPathField.getText());
}
/**
* Set the text to the default or clear it if not using the defaults.
* @param newName
* the name of the project to use. If <code>null</code> use the
* existing project name.
*/
public void updateProjectName(String newName) {
projectName = newName;
if (isDefault())
locationPathField.setText(TextProcessor.process(getDefaultPathDisplayString()));
}
/**
* Return the location for the project. If we are using defaults then return
* the workspace root so that core creates it with default values.
*
* @return String
*/
public String getProjectLocation() {
return isDefault() ?
Platform.getLocation().toString():
locationPathField.getText();
}
/**
* IErrorMessageReporter is an interface for type that allow message
* reporting. Null means "clear error messages area".
*/
public interface IErrorMessageReporter {
public void reportError(String errorMessage);
}
}