blob: 60be8b0cf983d013d1a2016d3fd9751113c50e40 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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 org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
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;
/**
* 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;
/**
* 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);
linkTargetField.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) {
Font font = locationGroup.getFont();
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 = 3;
layout.marginHeight = 0;
layout.marginWidth = 0;
linkTargetGroup.setLayout(layout);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalIndent = indent;
linkTargetGroup.setLayoutData(data);
linkTargetGroup.setFont(font);
// link target location entry field
linkTargetField = new Text(linkTargetGroup, SWT.BORDER);
data = new GridData(GridData.FILL_HORIZONTAL);
data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
linkTargetField.setLayoutData(data);
linkTargetField.setFont(font);
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.setFont(font);
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.setFont(font);
variablesButton.setText(IDEWorkbenchMessages.CreateLinkedResourceGroup_variablesButton);
variablesButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
handleVariablesButtonPressed();
}
});
variablesButton.setEnabled(enabled);
setButtonLayoutData(variablesButton);
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);
resolvedPathGroup.setFont(font);
resolvedPathLabelText = new Label(resolvedPathGroup, SWT.SINGLE);
resolvedPathLabelText.setText(IDEWorkbenchMessages.CreateLinkedResourceGroup_resolvedPathLabel);
resolvedPathLabelText.setVisible(false);
resolvedPathLabelText.setFont(font);
resolvedPathLabelData = new Label(resolvedPathGroup, SWT.SINGLE);
data = new GridData(GridData.FILL_HORIZONTAL);
resolvedPathLabelData.setLayoutData(data);
resolvedPathLabelData.setVisible(false);
resolvedPathLabelData.setFont(font);
if (linkTarget != null)
linkTargetField.setText(linkTarget);
}
/**
* 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 String getLinkTarget() {
if (createLink)
return linkTarget;
return null;
}
/**
* Opens a file or directory browser depending on the link type.
*/
private void handleLinkTargetBrowseButtonPressed() {
IFileStore store = null;
String selection = null;
if (linkTarget.length() > 0) {
store = IDEResourceInfoUtils.getFileStore(linkTarget);
if (!store.fetchInfo().exists())
store = null;
}
if (type == IResource.FILE) {
FileDialog dialog = new FileDialog(linkTargetField.getShell());
if (store != null) {
if (store.fetchInfo().isDirectory())
dialog.setFilterPath(linkTarget);
else
dialog.setFileName(linkTarget);
}
selection = dialog.open();
} else {
DirectoryDialog dialog = new DirectoryDialog(linkTargetField
.getShell());
if (store != null) {
IFileStore path = store;
if (!store.fetchInfo().isDirectory())
path = store.getParent();
if (path != null)
dialog.setFilterPath(store.toString());
}
dialog
.setMessage(IDEWorkbenchMessages.CreateLinkedResourceGroup_targetSelectionLabel);
selection = dialog.open();
}
if (selection != null)
linkTargetField.setText(selection);
}
/**
* 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())
return createStatus(IStatus.OK, ""); //$NON-NLS-1$
IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();
IPath path = new Path(linkTarget);
if (createLink == false)
return createStatus(IStatus.OK, ""); //$NON-NLS-1$
if (path.isUNC()) {
// print that path is not valid, but don't do core validation. See bug 90825.
return createStatus(IStatus.WARNING, IDEWorkbenchMessages.CreateLinkedResourceGroup_unableToValidateLinkTarget);
}
IStatus locationStatus = workspace.validateLinkLocation(linkHandle,
path);
if (locationStatus.getSeverity() == IStatus.ERROR)
return locationStatus;
// use the resolved link target name
String resolvedLinkTarget = resolvedPathLabelData.getText();
path = new Path(resolvedLinkTarget);
IFileInfo linkTargetFile = IDEResourceInfoUtils.getFileInfo(resolvedLinkTarget);
if (linkTargetFile != null && linkTargetFile.exists()) {
IStatus fileTypeStatus = validateFileType(linkTargetFile);
if (fileTypeStatus.isOK() == false)
return fileTypeStatus;
} else if (locationStatus.getSeverity() == IStatus.OK) {
// locationStatus takes precedence over missing location warning.
return createStatus(
IStatus.WARNING,
IDEWorkbenchMessages.CreateLinkedResourceGroup_linkTargetNonExistent);
}
return locationStatus;
}
}