/*
* Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
*
* THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
* WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
* IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
* CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
* NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
* DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
* THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
* USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
* PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
* AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
*/
package org.csstudio.ui.util.composites;
import org.csstudio.ui.util.internal.localization.Messages;
import org.eclipse.core.resources.IContainer;
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.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.fieldassist.FieldAssistColors;
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.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
/**
* Workbench-level composite for resource and container specification by the
* user. Services such as field validation are performed by the group. The group
* can be configured to accept existing resources, or only new resources.
*
* <p>
* <b>Code is based upon
* <code>org.eclipse.ui.internal.ide.misc.ResourceAndContainerGroup</code> in
* plugin <code>org.eclipse.ui.ide</code>.</b>
* </p>
*
* @author Alexander Will
* @version $Revision$
*/
//TODO: Copied from org.csstudio.platform.ui. Review is needed.
public final class ResourceAndContainerGroup implements Listener {
/**
* Problem identifier: No problem.
*/
public static final int PROBLEM_NONE = 0;
/**
* Problem identifier: Empty resource.
*/
public static final int PROBLEM_RESOURCE_EMPTY = 1;
/**
* Problem identifier: Resource already exists.
*/
public static final int PROBLEM_RESOURCE_EXIST = 2;
/**
* Problem identifier: Path is invalid.
*/
public static final int PROBLEM_PATH_INVALID = 4;
/**
* Problem identifier: Container is empty.
*/
public static final int PROBLEM_CONTAINER_EMPTY = 5;
/**
* Problem identifier: Project does not exist.
*/
public static final int PROBLEM_PROJECT_DOES_NOT_EXIST = 6;
/**
* Problem identifier: Invalid resource name.
*/
public static final int PROBLEM_NAME_INVALID = 7;
/**
* Problem identifier: Path os occupied.
*/
public static final int PROBLEM_PATH_OCCUPIED = 8;
/**
* The client to notify of changes.
*/
private Listener _client;
/**
* Whether to allow existing resources.
*/
private boolean _allowExistingResources = false;
/**
* Resource type (file, folder, project).
*/
private String _resourceType = "resource"; //$NON-NLS-1$
/**
* Show closed projects in the tree, by default.
*/
private boolean _showClosedProjects = true;
/**
* Problem indicator.
*/
private String _problemMessage = "";//$NON-NLS-1$
/**
* The default file extension.
*/
private String _fileExtension = ""; //$NON-NLS-1$
/**
* Problem type.
*/
private int _problemType = PROBLEM_NONE;
/**
* The container selection group.
*/
private ResourceSelectionGroup _containerGroup;
/**
* The name of the resource.
*/
private Text _resourceNameField;
/**
* The full path to the resource.
*/
private Label _fullPathLabel;
/**
* Sizing constand for the text field width.
*/
private static final int SIZING_TEXT_FIELD_WIDTH = 250;
/**
* Create an instance of the group to allow the user to enter/select a
* container and specify a resource name.
*
* @param parent
* composite widget to parent the group
* @param client
* object interested in changes to the group's fields value
* @param resourceFieldLabel
* label to use in front of the resource name field
* @param resourceType
* one word, in lowercase, to describe the resource to the user
* (file, folder, project) height hint for the container
* selection widget group
*/
public ResourceAndContainerGroup(final Composite parent,
final Listener client, final String resourceFieldLabel,
final String resourceType) {
this(parent, client, resourceFieldLabel, resourceType, true);
}
/**
* Create an instance of the group to allow the user to enter/select a
* container and specify a resource name.
*
* @param parent
* composite widget to parent the group
* @param client
* object interested in changes to the group's fields value
* @param resourceFieldLabel
* label to use in front of the resource name field
* @param resourceType
* one word, in lowercase, to describe the resource to the user
* (file, folder, project)
* @param showClosedProjects
* whether or not to show closed projects height hint for the
* container selection widget group
*/
public ResourceAndContainerGroup(final Composite parent,
final Listener client, final String resourceFieldLabel,
final String resourceType, final boolean showClosedProjects) {
this(parent, client, resourceFieldLabel, resourceType,
showClosedProjects, SWT.DEFAULT);
}
/**
* Create an instance of the group to allow the user to enter/select a
* container and specify a resource name.
*
* @param parent
* composite widget to parent the group
* @param client
* object interested in changes to the group's fields value
* @param resourceFieldLabel
* label to use in front of the resource name field
* @param resourceType
* one word, in lowercase, to describe the resource to the user
* (file, folder, project)
* @param showClosedProjects
* whether or not to show closed projects
* @param heightHint
* height hint for the container selection widget group
*/
public ResourceAndContainerGroup(final Composite parent,
final Listener client, final String resourceFieldLabel,
final String resourceType, final boolean showClosedProjects,
final int heightHint) {
super();
_resourceType = resourceType;
_showClosedProjects = showClosedProjects;
createContents(parent, resourceFieldLabel, heightHint);
_client = client;
}
/**
* Returns a boolean indicating whether all controls in this group contain
* valid values.
*
* @return boolean
*/
public boolean areAllValuesValid() {
return _problemType == PROBLEM_NONE;
}
/**
* Creates this object's visual components.
*
* @param parent
* org.eclipse.swt.widgets.Composite
* @param heightHint
* height hint for the container selection widget group
* @param resourceLabelString
* resource label text.
*/
private void createContents(final Composite parent,
final String resourceLabelString, final int heightHint) {
// Font font = parent.getFont();
// server name group
Composite composite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
layout.marginWidth = 0;
layout.marginHeight = 0;
composite.setLayout(layout);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
// composite.setFont(font);
// container group
if (heightHint == SWT.DEFAULT) {
_containerGroup = new ResourceSelectionGroup(composite, this,
null, null, _showClosedProjects);
} else {
_containerGroup = new ResourceSelectionGroup(composite, this,
null, null, _showClosedProjects, true, heightHint,
SIZING_TEXT_FIELD_WIDTH);
}
// resource name group
Composite nameGroup = new Composite(composite, SWT.NONE);
layout = new GridLayout();
layout.numColumns = 2;
layout.marginWidth = 0;
nameGroup.setLayout(layout);
nameGroup.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL
| GridData.GRAB_HORIZONTAL));
// nameGroup.setFont(font);
Label label = new Label(nameGroup, SWT.NONE);
label.setText(resourceLabelString);
// label.setFont(font);
// resource name entry field
_resourceNameField = new Text(nameGroup, SWT.BORDER);
_resourceNameField.addListener(SWT.Modify, this);
GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL
| GridData.GRAB_HORIZONTAL);
data.widthHint = SIZING_TEXT_FIELD_WIDTH;
_resourceNameField.setLayoutData(data);
// _resourceNameField.setFont(font);
_resourceNameField.setBackground(FieldAssistColors
.getRequiredFieldBackgroundColor(_resourceNameField));
// full path
label = new Label(nameGroup, SWT.NONE);
label.setText("Full path:");
_fullPathLabel = new Label(nameGroup, SWT.NONE | SWT.WRAP);
data = new GridData(GridData.HORIZONTAL_ALIGN_FILL
| GridData.GRAB_HORIZONTAL);
data.widthHint = SIZING_TEXT_FIELD_WIDTH;
_fullPathLabel.setLayoutData(data);
this.refreshFullPath();
validateControls();
}
/**
* Returns the path of the currently selected container or null if no
* container has been selected. Note that the container may not exist yet if
* the user entered a new container name in the field.
*
* @return The full path of the selected container.
*/
public IPath getContainerFullPath() {
return _containerGroup.getFullPath();
}
/**
* Returns an error message indicating the current problem with the value of
* a control in the group, or an empty message if all controls in the group
* contain valid values.
*
* @return java.lang.String
*/
public String getProblemMessage() {
return _problemMessage;
}
/**
* Returns the type of problem with the value of a control in the group.
*
* @return one of the PROBLEM_* constants
*/
public int getProblemType() {
return _problemType;
}
/**
* Returns a string that is the path of the currently selected container.
* Returns an empty string if no container has been selected.
*
* @return The entered resource name.
*/
public String getResource() {
return getResourceNameWithExtension();
}
/**
* Handles events for all controls in the group.
*
* @param e
* org.eclipse.swt.widgets.Event
*/
@Override
public void handleEvent(final Event e) {
validateControls();
this.refreshFullPath();
if (_client != null) {
_client.handleEvent(e);
}
}
/**
* Sets the flag indicating whether existing resources are permitted.
*
* @param value
* Flag that signals of it is allows to enter the names of
* already existing resources.
*/
public void setAllowExistingResources(final boolean value) {
_allowExistingResources = value;
}
/**
* Sets the value of this page's container.
*
* @param path
* Full path to the container.
*/
public void setContainerFullPath(final IPath path) {
IResource initial = ResourcesPlugin.getWorkspace().getRoot()
.findMember(path);
if (initial != null) {
if (!(initial instanceof IContainer)) {
initial = initial.getParent();
}
_containerGroup.setSelectedResource(initial);
}
validateControls();
}
/**
* Gives focus to the resource name field and selects its contents.
*/
public void setFocus() {
// select the whole resource name.
_resourceNameField.setSelection(0, _resourceNameField.getText()
.length());
_resourceNameField.setFocus();
}
/**
* Sets the value of this page's resource name.
*
* @param value
* new value
*/
public void setResource(final String value) {
_resourceNameField.setText(value);
validateControls();
}
/**
* Returns a <code>boolean</code> indicating whether a container name
* represents a valid container resource in the workbench. An error message
* is stored for future reference if the name does not represent a valid
* container.
*
* @return <code>boolean</code> indicating validity of the container name
*/
private boolean validateContainer() {
IPath path = _containerGroup.getFullPath();
if (path == null) {
_problemType = PROBLEM_CONTAINER_EMPTY;
_problemMessage = Messages.ResourceAndContainerGroup_PROBLEM_EMPTY;
return false;
}
IWorkspace workspace = ResourcesPlugin.getWorkspace();
String projectName = path.segment(0);
if (projectName == null
|| !workspace.getRoot().getProject(projectName).exists()) {
_problemType = PROBLEM_PROJECT_DOES_NOT_EXIST;
_problemMessage = Messages.ResourceAndContainerGroup_PROBLEM_DOES_NOT_EXIST;
return false;
}
// path is invalid if any prefix is occupied by a file
IWorkspaceRoot root = workspace.getRoot();
while (path.segmentCount() > 1) {
if (root.getFile(path).exists()) {
_problemType = PROBLEM_PATH_OCCUPIED;
_problemMessage = NLS
.bind(
Messages.ResourceAndContainerGroup_PROBLEM_FILE_ALREADY_EXISTS_AT_LOCATION,
path.makeRelative());
return false;
}
path = path.removeLastSegments(1);
}
return true;
}
/**
* Validates the values for each of the group's controls. If an invalid
* value is found then a descriptive error message is stored for later
* reference. Returns a boolean indicating the validity of all of the
* controls in the group.
*
* @return True, if all values are valid.
*/
private boolean validateControls() {
// don't attempt to validate controls until they have been created
if (_containerGroup == null) {
return false;
}
_problemType = PROBLEM_NONE;
_problemMessage = "";//$NON-NLS-1$
if (!validateContainer() || !validateResourceName()) {
return false;
}
IPath path = _containerGroup.getFullPath().append(
getResourceNameWithExtension());
return validateFullResourcePath(path);
}
/**
* Returns a <code>boolean</code> indicating whether the specified
* resource path represents a valid new resource in the workbench. An error
* message is stored for future reference if the path does not represent a
* valid new resource path.
*
* @param resourcePath
* the path to validate
* @return <code>boolean</code> indicating validity of the resource path
*/
private boolean validateFullResourcePath(final IPath resourcePath) {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IStatus result = workspace.validatePath(resourcePath.toString(),
IResource.FOLDER);
if (!result.isOK()) {
_problemType = PROBLEM_PATH_INVALID;
_problemMessage = result.getMessage();
return false;
}
if (!_allowExistingResources
&& (workspace.getRoot().getFolder(resourcePath).exists() || workspace
.getRoot().getFile(resourcePath).exists())) {
_problemType = PROBLEM_RESOURCE_EXIST;
_problemMessage = Messages.ResourceAndContainerGroup_PROBLEM_FILE_ALREADY_EXISTS;
return false;
}
return true;
}
/**
* Returns a <code>boolean</code> indicating whether the resource name
* rep- resents a valid resource name in the workbench. An error message is
* stored for future reference if the name does not represent a valid
* resource name.
*
* @return <code>boolean</code> indicating validity of the resource name
*/
private boolean validateResourceName() {
String resourceName = getResourceNameWithExtension();
if (resourceName.equals("")) {//$NON-NLS-1$
_problemType = PROBLEM_RESOURCE_EMPTY;
_problemMessage = NLS.bind(
Messages.ResourceAndContainerGroup_PROBLEM_EMPTY_NAME,
_resourceType);
return false;
}
if (!(new Path("")).isValidPath(resourceName)) { //$NON-NLS-1$
_problemType = PROBLEM_NAME_INVALID;
_problemMessage = NLS
.bind(
Messages.ResourceAndContainerGroup_PROBLEM_INVALID_FILE_NAME,
resourceName);
return false;
}
return true;
}
/**
* Refreshes the displayed full path of the resource.
*/
private void refreshFullPath() {
StringBuffer buffer = new StringBuffer();
if (_containerGroup.getFullPath() != null) {
buffer.append(ResourcesPlugin.getWorkspace().getRoot()
.getLocation());
buffer.append(_containerGroup.getFullPath());
buffer.append("/");
if (_resourceNameField.getText() != null
&& _resourceNameField.getText().trim().length() > 0) {
buffer.append(this.getResourceNameWithExtension());
}
}
_fullPathLabel.setText(buffer.toString());
Composite comp = _fullPathLabel.getParent().getParent();
if (comp != null) {
comp.layout();
}
}
/**
* Return the resource name including its eventually set file extension.
*
* @return The resource name including its eventually set file extension.
*/
private String getResourceNameWithExtension() {
String result = _resourceNameField.getText();
if ((_fileExtension != null) && (_fileExtension.length() > 0)) {
result = result + "." + _fileExtension; //$NON-NLS-1$
}
return result;
}
/**
* Return the file extension.
*
* @return The file extension
*/
public String getFileExtension() {
return _fileExtension;
}
/**
* Set the file extension.
*
* @param fileExtension
* The file extension to set
*/
public void setFileExtension(final String fileExtension) {
_fileExtension = fileExtension;
}
/**
* Set the embedded container selection group to enabled/disabled.
*
* @param enabled
* true for enabled, false for diabled.
*/
public void setContainerSelectionGroupEnabled(boolean enabled) {
if (_containerGroup != null) {
_containerGroup.setEnabled(enabled);
}
}
}