/*******************************************************************************
 * Copyright (c) 2000, 2006 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.pde.internal.ui.parts;
import org.eclipse.pde.internal.ui.editor.IEditorValidationProvider;
import org.eclipse.pde.internal.ui.editor.IEditorValidator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
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.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.FormColors;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.forms.widgets.TableWrapData;
import org.eclipse.ui.forms.widgets.TableWrapLayout;
/**
 * The helper class for creating entry fields with label and text. Optionally,
 * a button can be added after the text. The attached listener reacts to all
 * the events. Entring new text makes the entry 'dirty', but only when 'commit'
 * is called is 'valueChanged' method called (and only if 'dirty' flag is set).
 * This allows delayed commit.
 */
public class FormEntry implements IEditorValidationProvider {
	private Control fLabel;
	private Text fText;
	private Button fBrowse;
	private String fValue=""; //$NON-NLS-1$
	private boolean fDirty;
	boolean fIgnoreModify = false;
	private IFormEntryListener fListener;
	private boolean fSkipValidation;
	private IEditorValidator fValidator;
	/**
	 * The default constructor. Call 'createControl' to make it.
	 *  
	 */
	public FormEntry(Composite parent, FormToolkit toolkit, String labelText, int style) {
		createControl(parent, toolkit, labelText, style, null, false, 0, 0);
	}
	/**
	 * This constructor create all the controls right away.
	 * 
	 * @param parent
	 * @param toolkit
	 * @param labelText
	 * @param browseText
	 * @param linkLabel
	 */
	public FormEntry(Composite parent, FormToolkit toolkit, String labelText,
			String browseText, boolean linkLabel) {
		this(parent, toolkit, labelText, browseText, linkLabel, 0);
	}
	
	public FormEntry(Composite parent, FormToolkit toolkit, String labelText,
			String browseText, boolean linkLabel, int indent) {
		createControl(parent, toolkit, labelText, SWT.SINGLE, browseText, linkLabel, indent, 0);
	}
	
	public FormEntry(Composite parent, FormToolkit toolkit, String labelText,
			int indent, int tcolspan) {
		createControl(parent, toolkit, labelText, SWT.SINGLE, null, false, indent, tcolspan);
	}
	
	/**
	 * Create all the controls in the provided parent.
	 * 
	 * @param parent
	 * @param toolkit
	 * @param labelText
	 * @param span
	 * @param browseText
	 * @param linkLabel
	 */
	private void createControl(Composite parent, FormToolkit toolkit,
			String labelText, int style, String browseText, boolean linkLabel, int indent, int tcolspan) {
		if (linkLabel) {
			Hyperlink link = toolkit.createHyperlink(parent, labelText,
					SWT.NULL);
			fLabel = link;
		} else {
			if (labelText != null) {
				fLabel = toolkit.createLabel(parent, labelText);
				fLabel.setForeground(toolkit.getColors().getColor(FormColors.TITLE));
			}
		}
		fText = toolkit.createText(parent, "", style); //$NON-NLS-1$
		addListeners();
		if (browseText != null) {
			fBrowse = toolkit.createButton(parent, browseText, SWT.PUSH);
			fBrowse.addSelectionListener(new SelectionAdapter() {
				public void widgetSelected(SelectionEvent e) {
					if (fListener != null)
						fListener.browseButtonSelected(FormEntry.this);
				}
			});
		}
		fillIntoGrid(parent, indent, tcolspan);
	}
	public void setEditable(boolean editable) {
		if (fValidator != null)
			fValidator.setEnabled(editable);
		fText.setEditable(editable);
		if (fLabel instanceof Hyperlink)
			((Hyperlink)fLabel).setUnderlined(editable);
		
		if (fBrowse!=null) 
			fBrowse.setEnabled(editable);
	}
	private void fillIntoGrid(Composite parent, int indent, int tcolspan) {
		Layout layout = parent.getLayout();
		int tspan;
		if (layout instanceof GridLayout) {
			int span = ((GridLayout) layout).numColumns;
			if (tcolspan > 0)
				tspan = tcolspan;
			else
				tspan = fBrowse != null ? span - 2 : span - 1;
			GridData gd;
			if (fLabel != null) {
				gd = new GridData(GridData.VERTICAL_ALIGN_CENTER);
				gd.horizontalIndent = indent;
				fLabel.setLayoutData(gd);
			}
			gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
			gd.horizontalSpan = tspan;
			gd.grabExcessHorizontalSpace = (tspan == 1);
			gd.widthHint = 10;
			fText.setLayoutData(gd);
			if (fBrowse != null) {
				gd = new GridData(GridData.VERTICAL_ALIGN_CENTER);
				fBrowse.setLayoutData(gd);
			}
		} else if (layout instanceof TableWrapLayout) {
			int span = ((TableWrapLayout) layout).numColumns;
			if (tcolspan > 0)
				tspan = tcolspan;
			else
				tspan = fBrowse != null ? span - 2 : span - 1;
			TableWrapData td;
			if (fLabel != null) {
				td = new TableWrapData();
				td.valign = TableWrapData.MIDDLE;
				td.indent = indent;
				fLabel.setLayoutData(td);
			}
			td = new TableWrapData(TableWrapData.FILL);
			td.colspan = tspan;
			td.grabHorizontal = (tspan == 1);
			td.valign = TableWrapData.MIDDLE;
			fText.setLayoutData(td);
			if (fBrowse != null) {
				td = new TableWrapData(TableWrapData.FILL);
				td.valign = TableWrapData.MIDDLE;
				fBrowse.setLayoutData(td);
			}
		}
	}
	/**
	 * Attaches the listener for the entry.
	 * 
	 * @param listener
	 */
	public void setFormEntryListener(IFormEntryListener listener) {
		if (fLabel != null && fLabel instanceof Hyperlink) {
			if (this.fListener!=null)
				((Hyperlink)fLabel).removeHyperlinkListener(this.fListener);
			if (listener!=null)
				((Hyperlink)fLabel).addHyperlinkListener(listener);
		}
		this.fListener = listener;
	}
	private void addListeners() {
		fText.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent e) {
				keyReleaseOccured(e);
			}
		});
		fText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				editOccured(e);
			}
		});
		fText.addFocusListener(new FocusAdapter() {
			public void focusGained(FocusEvent e) {
				if (fListener != null)
					fListener.focusGained(FormEntry.this);
			}
			public void focusLost(FocusEvent e) {
				if (fDirty)
					commit();
			}
		});
	}
	/**
	 * If dirty, commits the text in the widget to the value and notifies the
	 * listener. This call clears the 'dirty' flag.
	 *  
	 */
	public void commit() {
		if (fDirty) {
			fValue = fText.getText();
			//if (value.length()==0)
				//value = null;
			//notify
			if (fListener != null)
				fListener.textValueChanged(this);
		}
		fDirty = false;
	}
	public void cancelEdit() {
		fDirty = false;
	}
	private void editOccured(ModifyEvent e) {
		if (!fSkipValidation)
			validate();
		if (fIgnoreModify)
			return;
		fDirty = true;
		if (fListener != null)
			fListener.textDirty(this);
	}
	/**
	 * Returns the text control.
	 * 
	 * @return
	 */
	public Text getText() {
		return fText;
	}
	
	public Control getLabel() {
		return fLabel;
	}
	
	/**
	 * Returns the browse button control.
	 * @return
	 */
	public Button getButton() {
		return fBrowse;
	}
	/**
	 * Returns the current entry value. If the entry is dirty and was not
	 * commited, the value may be different from the text in the widget.
	 * 
	 * @return
	 */
	public String getValue() {
		return fValue.trim();
	}
	/**
	 * Returns true if the text has been modified.
	 * 
	 * @return
	 */
	public boolean isDirty() {
		return fDirty;
	}
	private void keyReleaseOccured(KeyEvent e) {
		if (e.character == '\r') {
			// commit value
			if (fDirty)
				commit();
		} else if (e.character == '\u001b') { // Escape character
			if (!fValue.equals(fText.getText())) 
				fText.setText(fValue != null ? fValue : ""); // restore old //$NON-NLS-1$
			fDirty = false;
		}
		if (fListener != null)
			fListener.selectionChanged(FormEntry.this);
	}
	/**
	 * Sets the value of this entry.
	 * 
	 * @param value
	 */
	public void setValue(String value) {
		if (fText != null)
			fText.setText(value != null ? value : ""); //$NON-NLS-1$
		this.fValue = (value != null) ? value : ""; //$NON-NLS-1$
	}
	/**
	 * Sets the value of this entry with the possibility to turn the
	 * notification off.
	 * 
	 * @param value
	 * @param blockNotification
	 */
	public void setValue(String value, boolean blockNotification) {
		fIgnoreModify = blockNotification;
		setValue(value);
		fIgnoreModify = false;
	}
	
	/**
	 * Sets the value of this entry with the possibility to turn the
	 * notification and validation off.
	 * 
	 * @param value
	 * @param blockNotification
	 */
	public void setValue(String value, boolean blockNotification, boolean skipValidation) {
		fSkipValidation = skipValidation;
		setValue(value, blockNotification);
		fSkipValidation = false;
	}
	
	public void setVisible(boolean visible) {
		if (fLabel != null)
			fLabel.setVisible(visible);
		if (fText != null)
			fText.setVisible(visible);
		if (fBrowse != null)
			fBrowse.setVisible(visible);
	}
	
	public void setValidator(IEditorValidator validator) {
		fValidator = validator;
	}
	
	public IEditorValidator getValidator() {
		return fValidator;
	}
	
	public void validate() {
		if (fValidator != null)
			fValidator.validate(true);
	}
	
	public String getProviderValue() {
		return fText.getText();
	}
	
	public String getProviderDescription() {
		if (fLabel == null)
			return null;
		if (fLabel instanceof Label)
			return ((Label)fLabel).getText();
		else if (fLabel instanceof Hyperlink)
			return ((Hyperlink)fLabel).getText();
		return null;
	}
}
