/*******************************************************************************
 * Copyright (c) 2005-2011
 * University of Augsburg, Germany <www.ds-lab.org>
 *
 * 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:
 *    Christian Saad, Programming distributed Systems Lab, University of Augsburg 
 *    	- initial API and implementation
 *******************************************************************************/
package org.eclipse.jwt.we.editors.preferences.fieldeditors;

import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.StringConverter;
import org.eclipse.jwt.we.editors.preferences.wrappers.FontPreferenceWrapper;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.FontDialog;
import org.eclipse.swt.widgets.Label;


/**
 * This is a slightly modified version of the 'official' eclipse font field editor. It
 * uses FontPreferenceWrapper to handle the fonts, displays an obligatory preview label of
 * a fixed size and does not have a text label.
 */
public class CustomFontFieldEditor
		extends FieldEditor
{

	/**
	 * The change font button, or <code>null</code> if none (before creation and after
	 * disposal).
	 */
	private Button changeFontButton = null;

	/**
	 * The text for the change font button, or <code>null</code> if missing.
	 */
	private String changeButtonText;

	/**
	 * Font data for the chosen font button, or <code>null</code> if none.
	 */
	private FontData[] chosenFont;

	/**
	 * The label that displays the selected font, or <code>null</code> if none.
	 */
	private Label valueControl;

	/**
	 * The previewer, or <code>null</code> if none.
	 */
	private DefaultPreviewer previewer;

	/**
	 * The previewer, or <code>null</code> if none.
	 */
	private FontPreferenceWrapper fontWrapper;

	/**
	 * Internal font previewer implementation.
	 */
	private static class DefaultPreviewer
	{

		private Label text;

		private String string;

		private Font font;


		/**
		 * Constructor for the previewer.
		 * 
		 * @param s
		 * @param parent
		 */
		public DefaultPreviewer(String s, Composite parent)
		{
			string = s;
			text = new Label(parent, SWT.LEFT | SWT.BORDER);
			text.addDisposeListener(new DisposeListener()
			{

				public void widgetDisposed(DisposeEvent e)
				{
					if (font != null)
					{
						font.dispose();
					}
				}
			});
			if (string != null)
			{
				text.setText(string);
			}
		}


		/**
		 * @return the control the previewer is using
		 */
		public Control getControl()
		{
			return text;
		}


		/**
		 * Set the font to display with
		 * 
		 * @param fontData
		 */
		public void setFont(FontData[] fontData)
		{
			if (font != null)
			{
				font.dispose();
			}
			font = new Font(text.getDisplay(), fontData);
			text.setFont(font);
		}
	}


	/**
	 * Creates a new font field editor
	 */
	protected CustomFontFieldEditor()
	{
	}


	/**
	 * Creates a font field editor with an optional preview area.
	 * 
	 * @param name
	 *            the name of the preference this field editor works on
	 * @param labelText
	 *            the label text of the field editor
	 * @param previewAreaText
	 *            the text used for the preview window. If it is <code>null</code> there
	 *            will be no preview area,
	 * @param parent
	 *            the parent of the field editor's control
	 */
	public CustomFontFieldEditor(String name, Composite parent)
	{
		init(name, ""); //$NON-NLS-1$
		changeButtonText = JFaceResources.getString("openChange"); //$NON-NLS-1$
		createControl(parent);
		fontWrapper = new FontPreferenceWrapper(name);
	}


	/*
	 * (non-Javadoc) Method declared on FieldEditor.
	 */
	protected void adjustForNumColumns(int numColumns)
	{

		GridData data = new GridData();
		if (valueControl.getLayoutData() != null)
		{
			data = (GridData) valueControl.getLayoutData();
		}

		data.horizontalSpan = numColumns - getNumberOfControls() + 1;
		valueControl.setLayoutData(data);
	}


	/*
	 * (non-Javadoc) Method declared on FieldEditor.
	 */
	protected void applyFont()
	{
		if (chosenFont != null && previewer != null)
		{
			previewer.setFont(chosenFont);
		}
	}


	/*
	 * (non-Javadoc) Method declared on FieldEditor.
	 */
	protected void doFillIntoGrid(Composite parent, int numColumns)
	{
		// grid data
		GridData gd = new GridData();
		gd.horizontalSpan = numColumns - getNumberOfControls() + 1;

		gd = new GridData(GridData.FILL_HORIZONTAL);
		gd.heightHint = 30;
		gd.widthHint = 20;
		
		// previewer
		previewer = new DefaultPreviewer(
				"This is a preview text.", //$NON-NLS-1$
				parent);
		previewer.getControl().setLayoutData(gd);

		// value control
		valueControl = getValueControl(parent);
		valueControl.setLayoutData(gd);

		// change font
		changeFontButton = getChangeControl(parent);
		gd = new GridData();
		int widthHint = convertHorizontalDLUsToPixels(changeFontButton,
				IDialogConstants.BUTTON_WIDTH);
		gd.widthHint = Math.max(widthHint, changeFontButton.computeSize(SWT.DEFAULT,
				SWT.DEFAULT, true).x);
		changeFontButton.setLayoutData(gd);
	}


	/*
	 * (non-Javadoc) Method declared on FieldEditor.
	 */
	protected void doLoad()
	{
		if (changeFontButton == null)
		{
			return;
		}
		updateFont(fontWrapper.get().getFontData());
	}


	/*
	 * (non-Javadoc) Method declared on FieldEditor.
	 */
	protected void doLoadDefault()
	{
		if (changeFontButton == null)
		{
			return;
		}
		updateFont(fontWrapper.getDefault().getFontData());
	}


	/*
	 * (non-Javadoc) Method declared on FieldEditor.
	 */
	protected void doStore()
	{
		if (chosenFont != null)
		{
			Font newFont = new Font(null, chosenFont);
			fontWrapper.set(newFont);
		}
	}


	/**
	 * Returns the change button for this field editor.
	 * 
	 * @param parent
	 *            The Composite to create the button in if required.
	 * @return the change button
	 */
	protected Button getChangeControl(Composite parent)
	{
		if (changeFontButton == null)
		{
			changeFontButton = new Button(parent, SWT.PUSH);
			if (changeButtonText != null)
			{
				changeFontButton.setText(changeButtonText);
			}
			changeFontButton.addSelectionListener(new SelectionAdapter()
			{

				public void widgetSelected(SelectionEvent event)
				{
					FontDialog fontDialog = new FontDialog(changeFontButton.getShell());
					if (chosenFont != null)
					{
						fontDialog.setFontList(chosenFont);
					}
					FontData font = fontDialog.open();
					if (font != null)
					{
						FontData[] oldFont = chosenFont;
						if (oldFont == null)
						{
							oldFont = JFaceResources.getDefaultFont().getFontData();
						}
						setPresentsDefaultValue(false);
						FontData[] newData = new FontData[1];
						newData[0] = font;
						updateFont(newData);
						fireValueChanged(VALUE, oldFont[0], font);
					}

				}
			});
			changeFontButton.addDisposeListener(new DisposeListener()
			{

				public void widgetDisposed(DisposeEvent event)
				{
					changeFontButton = null;
				}
			});
			changeFontButton.setFont(parent.getFont());
			setButtonLayoutData(changeFontButton);
		}
		else
		{
			checkParent(changeFontButton, parent);
		}
		return changeFontButton;
	}


	/*
	 * (non-Javadoc) Method declared on FieldEditor.
	 */
	public int getNumberOfControls()
	{
		return 3;
	}


	/**
	 * Returns the preferred preview height.
	 * 
	 * @return the height, or <code>-1</code> if no previewer is installed
	 */
	public int getPreferredPreviewHeight()
	{
		if (previewer == null)
		{
			return -1;
		}
		return 20;
	}


	/**
	 * Returns the preview control for this field editor.
	 * 
	 * @return the preview control
	 */
	public Control getPreviewControl()
	{
		if (previewer == null)
		{
			return null;
		}

		return previewer.getControl();
	}


	/**
	 * Returns the value control for this field editor. The value control displays the
	 * currently selected font name.
	 * 
	 * @param parent
	 *            The Composite to create the viewer in if required
	 * @return the value control
	 */
	protected Label getValueControl(Composite parent)
	{
		if (valueControl == null)
		{
			valueControl = new Label(parent, SWT.LEFT);
			valueControl.setFont(parent.getFont());
			valueControl.addDisposeListener(new DisposeListener()
			{

				public void widgetDisposed(DisposeEvent event)
				{
					valueControl = null;
				}
			});
		}
		else
		{
			checkParent(valueControl, parent);
		}
		return valueControl;
	}


	/**
	 * Sets the text of the change button.
	 * 
	 * @param text
	 *            the new text
	 */
	public void setChangeButtonText(String text)
	{
		Assert.isNotNull(text);
		changeButtonText = text;
		if (changeFontButton != null)
		{
			changeFontButton.setText(text);
		}
	}


	/**
	 * Updates the change font button and the previewer to reflect the newly selected
	 * font.
	 * 
	 * @param font
	 *            The FontData[] to update with.
	 */
	private void updateFont(FontData font[])
	{
		FontData[] bestFont = JFaceResources.getFontRegistry().filterData(font,
				valueControl.getDisplay());

		// if we have nothing valid do as best we can
		if (bestFont == null)
		{
			bestFont = getDefaultFontData();
		}

		// Now cache this value in the receiver
		this.chosenFont = bestFont;

		if (valueControl != null)
		{
			valueControl.setText(StringConverter.asString(chosenFont[0]));
		}
		if (previewer != null)
		{
			previewer.setFont(bestFont);
		}
	}


	/**
	 * Store the default preference for the field being edited
	 */
	protected void setToDefault()
	{
		fontWrapper.setDefault();
	}


	/**
	 * Get the system default font data.
	 * 
	 * @return FontData[]
	 */
	private FontData[] getDefaultFontData()
	{
		return valueControl.getDisplay().getSystemFont().getFontData();
	}


	/*
	 * @see FieldEditor.setEnabled(boolean,Composite).
	 */
	public void setEnabled(boolean enabled, Composite parent)
	{
		getChangeControl(parent).setEnabled(enabled);
		getValueControl(parent).setEnabled(enabled);
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.preference.FieldEditor#dispose()
	 */
	@Override
	public void dispose()
	{
		super.dispose();

		fontWrapper = null;
	}

}
