/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.ui.internal.ide.dialogs;

import java.io.File;

import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
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;
 
/**
 * 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.
 */ 
public CreateLinkedResourceGroup(int type, Listener listener) {
	this.type = type;
	this.listener = listener;
}
/**
 * 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.getString("CreateLinkedResourceGroup.linkFileButton")); //$NON-NLS-1$
	else
		createLinkButton.setText(IDEWorkbenchMessages.getString("CreateLinkedResourceGroup.linkFolderButton")); //$NON-NLS-1$
	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 (listener != null)
				listener.handleEvent(new Event());
		}
	});
	// browse button
	browseButton = new Button(linkTargetGroup, SWT.PUSH);
	setButtonLayoutData(browseButton);
	browseButton.setFont(font);
	browseButton.setText(IDEWorkbenchMessages.getString("CreateLinkedResourceGroup.browseButton")); //$NON-NLS-1$
	browseButton.addSelectionListener(new SelectionAdapter() {
		public void widgetSelected(SelectionEvent event) {
			handleLinkTargetBrowseButtonPressed();
		}
	});
	browseButton.setEnabled(enabled);

	// variables button
	variablesButton = new Button(linkTargetGroup, SWT.PUSH);
	setButtonLayoutData(variablesButton);
	variablesButton.setFont(font);
	variablesButton.setText(IDEWorkbenchMessages.getString("CreateLinkedResourceGroup.variablesButton")); //$NON-NLS-1$
	variablesButton.addSelectionListener(new SelectionAdapter() {
		public void widgetSelected(SelectionEvent event) {
			handleVariablesButtonPressed();
		}
	});
	variablesButton.setEnabled(enabled);

	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.getString("CreateLinkedResourceGroup.resolvedPathLabel")); //$NON-NLS-1$
	resolvedPathLabelText.setVisible(false);

	resolvedPathLabelData = new Label(resolvedPathGroup, SWT.SINGLE);
	data = new GridData(GridData.FILL_HORIZONTAL);
	resolvedPathLabelData.setLayoutData(data);
	resolvedPathLabelData.setVisible(false);

	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().getDescriptor().getUniqueIdentifier(),
		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() {
	File file = null;
	String selection = null;
	
	if ("".equals(linkTarget) == false) {	//$NON-NLS-1$
		file = new File(linkTarget);
		if (file.exists() == false)
			file = null;
	}
	if (type == IResource.FILE) {
		FileDialog dialog = new FileDialog(linkTargetField.getShell());
		if (file != null) {
			if (file.isFile())
				dialog.setFileName(linkTarget);
			else
				dialog.setFilterPath(linkTarget);
		}
		selection = dialog.open();		
	}
	else {
		DirectoryDialog dialog = new DirectoryDialog(linkTargetField.getShell());
		if (file != null) {
			String path = linkTarget;
			if (file.isFile())
				path = file.getParent();
			if (path != null)
				dialog.setFilterPath(path);
		}
		dialog.setMessage(IDEWorkbenchMessages.getString("CreateLinkedResourceGroup.targetSelectionLabel")); //$NON-NLS-1$
		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);
	data.heightHint = Dialog.convertVerticalDLUsToPixels(fontMetrics, IDialogConstants.BUTTON_HEIGHT);
	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(File linkTargetFile) {
	if (type == IResource.FILE && linkTargetFile.isFile() == false) {
		return createStatus(
			IStatus.ERROR,
			IDEWorkbenchMessages.getString("CreateLinkedResourceGroup.linkTargetNotFile"));	//$NON-NLS-1$
	} else if (type == IResource.FOLDER && linkTargetFile.isDirectory() == false) {
		return createStatus(
			IStatus.ERROR,
			IDEWorkbenchMessages.getString("CreateLinkedResourceGroup.linkTargetNotFolder"));	//$NON-NLS-1$
	}
	return createStatus(IStatus.OK, ""); //$NON-NLS-1$
}
/**
 * Validates this page's controls.
 *
 * @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$

	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);
	File linkTargetFile = new Path(resolvedLinkTarget).toFile();
	if (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.getString("CreateLinkedResourceGroup.linkTargetNonExistent"));	//$NON-NLS-1$	
	}
	return locationStatus;
}
}
