blob: 5d7a11dbb0515bb0d145bb3d218c91d0513f02e4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 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 org.eclipse.ui.internal.ide.dialogs;
import java.net.URI;
import java.net.URISyntaxException;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IPathVariableManager;
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.Status;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
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.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
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.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.ide.dialogs.PathVariableSelectionDialog;
import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.internal.ide.filesystem.FileSystemConfiguration;
import org.eclipse.ui.internal.ide.filesystem.FileSystemSupportRegistry;
/**
* Widget group for specifying a linked file or folder target.
*
* @since 2.1
*/
public class CreateLinkedResourceGroup {
private Listener listener;
private String linkTarget = ""; //$NON-NLS-1$
private int type;
private boolean createLink = false;
// used to compute layout sizes
private FontMetrics fontMetrics;
// widgets
private Composite groupComposite;
private Text linkTargetField;
private Button browseButton;
private Button variablesButton;
private Label resolvedPathLabelText;
private Label resolvedPathLabelData;
private final IStringValue updatableResourceName;
/**
* Helper interface intended for updating a string value based on the
* currently selected link target.
*
* @since 3.2
*/
public static interface IStringValue {
/**
* Sets the String value.
*
* @param string
* a non-null String
*/
void setValue(String string);
/**
* Gets the String value.
*
* @return the current value, or <code>null</code>
*/
String getValue();
}
private String lastUpdatedValue;
private FileSystemSelectionArea fileSystemSelectionArea;
/**
* Creates a link target group
*
* @param type
* specifies the type of resource to link to.
* <code>IResource.FILE</code> or <code>IResource.FOLDER</code>
* @param listener
* listener to notify when one of the widgets' value is changed.
* @param updatableResourceName
* an updatable string value that will be updated to reflect the
* link target's last segment, or <code>null</code>. Updating
* will only happen if the current value of that string is null
* or the empty string, or if it has not been changed since the
* last time it was updated.
*/
public CreateLinkedResourceGroup(int type, Listener listener,
IStringValue updatableResourceName) {
this.type = type;
this.listener = listener;
this.updatableResourceName = updatableResourceName;
if (updatableResourceName != null) {
lastUpdatedValue = updatableResourceName.getValue();
}
}
/**
* Creates the widgets
*
* @param parent
* parent composite of the widget group
* @return the widget group
*/
public Composite createContents(Composite parent) {
Font font = parent.getFont();
initializeDialogUnits(parent);
// top level group
groupComposite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
groupComposite.setLayout(layout);
groupComposite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
| GridData.FILL_HORIZONTAL));
groupComposite.setFont(font);
final Button createLinkButton = new Button(groupComposite, SWT.CHECK);
if (type == IResource.FILE) {
createLinkButton
.setText(IDEWorkbenchMessages.CreateLinkedResourceGroup_linkFileButton);
} else {
createLinkButton
.setText(IDEWorkbenchMessages.CreateLinkedResourceGroup_linkFolderButton);
}
createLinkButton.setSelection(createLink);
createLinkButton.setFont(font);
SelectionListener selectionListener = new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
createLink = createLinkButton.getSelection();
browseButton.setEnabled(createLink);
variablesButton.setEnabled(createLink);
// Set the required field color if the field is enabled
linkTargetField.setEnabled(createLink);
if (fileSystemSelectionArea != null)
fileSystemSelectionArea.setEnabled(createLink);
if (listener != null) {
listener.handleEvent(new Event());
}
}
};
createLinkButton.addSelectionListener(selectionListener);
createLinkLocationGroup(groupComposite, createLink);
return groupComposite;
}
/**
* Creates the link target location widgets.
*
* @param locationGroup
* the parent composite
* @param enabled
* sets the initial enabled state of the widgets
*/
private void createLinkLocationGroup(Composite locationGroup,
boolean enabled) {
Button button = new Button(locationGroup, SWT.CHECK);
int indent = button.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
button.dispose();
// linkTargetGroup is necessary to decouple layout from
// resolvedPathGroup layout
Composite linkTargetGroup = new Composite(locationGroup, SWT.NONE);
GridLayout layout = new GridLayout();
layout.numColumns = 4;
layout.marginHeight = 0;
layout.marginWidth = 0;
linkTargetGroup.setLayout(layout);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalIndent = indent;
linkTargetGroup.setLayoutData(data);
// link target location entry field
linkTargetField = new Text(linkTargetGroup, SWT.BORDER);
data = new GridData(GridData.FILL_HORIZONTAL);
data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
data.horizontalSpan = 2;
linkTargetField.setLayoutData(data);
linkTargetField.setEnabled(enabled);
linkTargetField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
linkTarget = linkTargetField.getText();
resolveVariable();
if (updatableResourceName != null) {
String value = updatableResourceName.getValue();
if (value == null
|| value.equals("") || value.equals(lastUpdatedValue)) { //$NON-NLS-1$
IPath linkTargetPath = new Path(linkTarget);
String lastSegment = linkTargetPath.lastSegment();
if (lastSegment != null) {
lastUpdatedValue = lastSegment;
updatableResourceName.setValue(lastSegment);
}
}
}
if (listener != null) {
listener.handleEvent(new Event());
}
}
});
// browse button
browseButton = new Button(linkTargetGroup, SWT.PUSH);
browseButton
.setText(IDEWorkbenchMessages.CreateLinkedResourceGroup_browseButton);
browseButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
handleLinkTargetBrowseButtonPressed();
}
});
browseButton.setEnabled(enabled);
setButtonLayoutData(browseButton);
// variables button
variablesButton = new Button(linkTargetGroup, SWT.PUSH);
variablesButton
.setText(IDEWorkbenchMessages.CreateLinkedResourceGroup_variablesButton);
variablesButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
handleVariablesButtonPressed();
}
});
variablesButton.setEnabled(enabled);
setButtonLayoutData(variablesButton);
createFileSystemSelection(linkTargetGroup, enabled);
createResolvedPathGroup(locationGroup, indent);
if (linkTarget != null) {
linkTargetField.setText(linkTarget);
}
}
/**
* Create the file system selection area.
*
* @param composite
* @param enabled
* the initial enablement state.
*/
private void createFileSystemSelection(Composite composite, boolean enabled) {
// Always use the default if that is all there is.
if (FileSystemSupportRegistry.getInstance().hasOneFileSystem()) {
return;
}
fileSystemSelectionArea = new FileSystemSelectionArea();
fileSystemSelectionArea.createContents(composite);
fileSystemSelectionArea.setEnabled(enabled);
}
/**
* Create the composite for the resolved path.
*
* @param locationGroup
* @param indent
*/
private void createResolvedPathGroup(Composite locationGroup, int indent) {
GridLayout layout;
GridData data;
Composite resolvedPathGroup = new Composite(locationGroup, SWT.NONE);
layout = new GridLayout();
layout.numColumns = 2;
layout.marginHeight = 0;
layout.marginWidth = 0;
resolvedPathGroup.setLayout(layout);
data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalIndent = indent;
resolvedPathGroup.setLayoutData(data);
resolvedPathLabelText = new Label(resolvedPathGroup, SWT.SINGLE);
resolvedPathLabelText
.setText(IDEWorkbenchMessages.CreateLinkedResourceGroup_resolvedPathLabel);
resolvedPathLabelText.setVisible(false);
resolvedPathLabelData = new Label(resolvedPathGroup, SWT.SINGLE);
data = new GridData(GridData.FILL_HORIZONTAL);
resolvedPathLabelData.setLayoutData(data);
resolvedPathLabelData.setVisible(false);
}
/**
* Returns a new status object with the given severity and message.
*
* @return a new status object with the given severity and message.
*/
private IStatus createStatus(int severity, String message) {
return new Status(severity, IDEWorkbenchPlugin.getDefault().getBundle()
.getSymbolicName(), severity, message, null);
}
/**
* Disposes the group's widgets.
*/
public void dispose() {
if (groupComposite != null && groupComposite.isDisposed() == false) {
groupComposite.dispose();
}
}
/**
* Returns the link target location entered by the user.
*
* @return the link target location entered by the user. null if the user
* chose not to create a link.
*/
public URI getLinkTargetURI() {
if (!createLink)
return null;
// resolve path variable if we have a relative path
if (!linkTarget.startsWith("/")) { //$NON-NLS-1$
IPathVariableManager pathVariableManager = ResourcesPlugin
.getWorkspace().getPathVariableManager();
try {
URI path = new URI(linkTarget.replace(java.io.File.separatorChar, '/'));
URI resolved = pathVariableManager.resolveURI(path);
if (path != resolved) {
// we know this is a path variable, but return unresolved
// path so resource will be created with variable intact
return path;
}
} catch (URISyntaxException e) {
// link target is not a valid URI. Fall through to handle this
// below
}
}
FileSystemConfiguration configuration = getSelectedConfiguration();
if (configuration == null) {
return URIUtil.toURI(linkTarget);
}
// validate non-local file system location
return configuration.getContributor().getURI(linkTarget);
}
/**
* Opens a file or directory browser depending on the link type.
*/
private void handleLinkTargetBrowseButtonPressed() {
IFileStore store = null;
String selection = null;
FileSystemConfiguration config = getSelectedConfiguration();
boolean isDefault = config == null
|| (FileSystemSupportRegistry.getInstance()
.getDefaultConfiguration()).equals(config);
if (linkTarget.length() > 0) {
store = IDEResourceInfoUtils.getFileStore(linkTarget);
if (!store.fetchInfo().exists()) {
store = null;
}
}
if (type == IResource.FILE) {
if (isDefault) {
FileDialog dialog = new FileDialog(linkTargetField.getShell());
dialog.setText(IDEWorkbenchMessages.CreateLinkedResourceGroup_targetSelectionTitle);
if (store != null) {
if (store.fetchInfo().isDirectory()) {
dialog.setFilterPath(linkTarget);
} else {
dialog.setFileName(linkTarget);
}
}
selection = dialog.open();
} else {
URI uri = config.getContributor().browseFileSystem(linkTarget,
linkTargetField.getShell());
if (uri != null)
selection = uri.toString();
}
} else {
String filterPath = null;
if (store != null) {
IFileStore path = store;
if (!store.fetchInfo().isDirectory()) {
path = store.getParent();
}
if (path != null) {
filterPath = store.toString();
}
}
if (isDefault) {
DirectoryDialog dialog = new DirectoryDialog(linkTargetField
.getShell());
dialog
.setMessage(IDEWorkbenchMessages.CreateLinkedResourceGroup_targetSelectionLabel);
if (filterPath != null)
dialog.setFilterPath(filterPath);
selection = dialog.open();
} else {
String initialPath = IDEResourceInfoUtils.EMPTY_STRING;
if (filterPath != null)
initialPath = filterPath;
URI uri = config.getContributor().browseFileSystem(initialPath,
linkTargetField.getShell());
if (uri != null)
selection = uri.toString();
}
}
if (selection != null) {
linkTargetField.setText(selection);
}
}
/**
* Return the selected configuration or <code>null</code> if there is not
* one selected.
*
* @return FileSystemConfiguration or <code>null</code>
*/
private FileSystemConfiguration getSelectedConfiguration() {
if (fileSystemSelectionArea == null)
return null;
return fileSystemSelectionArea.getSelectedConfiguration();
}
/**
* 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(
linkTargetField.getShell(), variableTypes);
if (dialog.open() == IDialogConstants.OK_ID) {
String[] variableNames = (String[]) dialog.getResult();
if (variableNames != null && variableNames.length == 1) {
linkTargetField.setText(variableNames[0]);
}
}
}
/**
* Initializes the computation of horizontal and vertical dialog units based
* on the size of current font.
* <p>
* This method must be called before <code>setButtonLayoutData</code> is
* called.
* </p>
*
* @param control
* a control from which to obtain the current font
*/
protected void initializeDialogUnits(Control control) {
// Compute and store a font metric
GC gc = new GC(control);
gc.setFont(control.getFont());
fontMetrics = gc.getFontMetrics();
gc.dispose();
}
/**
* 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 void resolveVariable() {
IPathVariableManager pathVariableManager = ResourcesPlugin
.getWorkspace().getPathVariableManager();
IPath path = new Path(linkTarget);
IPath resolvedPath = pathVariableManager.resolvePath(path);
if (path.equals(resolvedPath)) {
resolvedPathLabelText.setVisible(false);
resolvedPathLabelData.setVisible(false);
} else {
resolvedPathLabelText.setVisible(true);
resolvedPathLabelData.setVisible(true);
}
resolvedPathLabelData.setText(resolvedPath.toOSString());
}
/**
* Sets the <code>GridData</code> on the specified button to be one that
* is spaced for the current dialog page units. The method
* <code>initializeDialogUnits</code> must be called once before calling
* this method for the first time.
*
* @param button
* the button to set the <code>GridData</code>
* @return the <code>GridData</code> set on the specified button
*/
private GridData setButtonLayoutData(Button button) {
GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
int widthHint = Dialog.convertHorizontalDLUsToPixels(fontMetrics,
IDialogConstants.BUTTON_WIDTH);
data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT,
SWT.DEFAULT, true).x);
button.setLayoutData(data);
return data;
}
/**
* Sets the value of the link target field
*
* @param target
* the value of the link target field
*/
public void setLinkTarget(String target) {
linkTarget = target;
if (linkTargetField != null && linkTargetField.isDisposed() == false) {
linkTargetField.setText(target);
}
}
/**
* 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(IFileInfo linkTargetFile) {
if (type == IResource.FILE && linkTargetFile.isDirectory()) {
return createStatus(
IStatus.ERROR,
IDEWorkbenchMessages.CreateLinkedResourceGroup_linkTargetNotFile);
} else if (type == IResource.FOLDER && !linkTargetFile.isDirectory()) {
return createStatus(
IStatus.ERROR,
IDEWorkbenchMessages.CreateLinkedResourceGroup_linkTargetNotFolder);
}
return Status.OK_STATUS;
}
/**
* Validates this page's controls.
*
* @param linkHandle
* The target to check
*
* @return IStatus indicating the validation result. IStatus.OK if the
* specified link target is valid given the linkHandle.
*/
public IStatus validateLinkLocation(IResource linkHandle) {
if (linkTargetField == null || linkTargetField.isDisposed()
|| !createLink) {
return Status.OK_STATUS;
}
IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();
FileSystemConfiguration configuration = getSelectedConfiguration();
if (configuration == null
|| EFS.SCHEME_FILE.equals(configuration.getScheme())) {
// Special handling for UNC paths. See bug 90825
IPath location = new Path(linkTarget);
if (location.isUNC()) {
return createStatus(
IStatus.WARNING,
IDEWorkbenchMessages.CreateLinkedResourceGroup_unableToValidateLinkTarget);
}
}
URI locationURI = getLinkTargetURI();
IStatus locationStatus = workspace.validateLinkLocationURI(linkHandle,
locationURI);
if (locationStatus.getSeverity() == IStatus.ERROR) {
return locationStatus;
}
// use the resolved link target name
URI resolved = workspace.getPathVariableManager().resolveURI(
locationURI);
IFileInfo linkTargetFile = IDEResourceInfoUtils.getFileInfo(resolved);
if (linkTargetFile != null && linkTargetFile.exists()) {
IStatus fileTypeStatus = validateFileType(linkTargetFile);
if (!fileTypeStatus.isOK()) {
return fileTypeStatus;
}
} else if (locationStatus.isOK()) {
// locationStatus takes precedence over missing location warning.
return createStatus(
IStatus.WARNING,
IDEWorkbenchMessages.CreateLinkedResourceGroup_linkTargetNonExistent);
}
return locationStatus;
}
}