/*******************************************************************************
 * Copyright (c) 2004, 2008 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
 *     QNX Software Systems - adapted for use in CDT
 *     Markus Schorn (Wind River Systems)
 *     Anton Leherbauer (Wind River Systems)
 *******************************************************************************/
package org.eclipse.cdt.ui.browser.typeinfo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
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.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.FilteredList;
import org.eclipse.ui.dialogs.TwoPaneElementSelector;

import org.eclipse.cdt.core.browser.IQualifiedTypeName;
import org.eclipse.cdt.core.browser.ITypeInfo;
import org.eclipse.cdt.core.browser.ITypeReference;
import org.eclipse.cdt.core.browser.IndexTypeInfo;
import org.eclipse.cdt.core.browser.QualifiedTypeName;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.ui.CUIPlugin;

import org.eclipse.cdt.internal.ui.util.StringMatcher;


/**
 * A dialog to select a type from a list of types.
 */
public class TypeSelectionDialog extends TwoPaneElementSelector {

	private static class TypeFilterMatcher implements FilteredList.FilterMatcher {

		private static final char END_SYMBOL = '<';
		private static final char ANY_STRING = '*';
		
		private StringMatcher fNameMatcher = null;
		private StringMatcher[] fSegmentMatchers = null;
		private boolean fMatchGlobalNamespace = false;
		private Collection<Integer> fVisibleTypes = new HashSet<Integer>();
		private boolean fShowLowLevelTypes = false;
		
		/*
		 * @see FilteredList.FilterMatcher#setFilter(String, boolean)
		 */
		public void setFilter(String pattern, boolean ignoreCase, boolean ignoreWildCards) {
			// parse pattern into segments
			QualifiedTypeName qualifiedName = new QualifiedTypeName(pattern);
			String[] segments = qualifiedName.segments();
			int length = segments.length;

			// append wildcard to innermost segment
			segments[length-1] = adjustPattern(segments[length-1]);
			
			fMatchGlobalNamespace = false;
			fSegmentMatchers = new StringMatcher[length];
			int count = 0;
			for (int i = 0; i < length; ++i) {
				if (segments[i].length() > 0) {
					// create StringMatcher for this segment
					fSegmentMatchers[count++] = new StringMatcher(segments[i], ignoreCase, ignoreWildCards);
				} else if (i == 0) {
					// allow outermost segment to be blank (e.g. "::foo*")
					fMatchGlobalNamespace = true;
				} else {
					// skip over blank segments (e.g. treat "foo::::b*" as "foo::b*")
				}
			}
			if (count != length) {
				if (count > 0) {
					// resize array
					StringMatcher[] newMatchers = new StringMatcher[count];
					System.arraycopy(fSegmentMatchers, 0, newMatchers, 0, count);
					fSegmentMatchers = newMatchers;
				} else {
					// fallback to wildcard (should never get here)
					fSegmentMatchers = new StringMatcher[1];
					fSegmentMatchers[0] = new StringMatcher(String.valueOf(ANY_STRING), ignoreCase, ignoreWildCards);
				}
			}
			// match simple name with innermost segment
			fNameMatcher = fSegmentMatchers[fSegmentMatchers.length-1];
		}
		
		public void setVisibleTypes(Collection<Integer> visibleTypes) {
			fVisibleTypes.clear();
			fVisibleTypes.addAll(visibleTypes);
		}

		public Collection<Integer> getVisibleTypes() {
			return fVisibleTypes;
		}
		
		public void setShowLowLevelTypes(boolean show) {
			fShowLowLevelTypes = show;
		}

		public boolean getShowLowLevelTypes() {
			return fShowLowLevelTypes;
		}

		/*
		 * @see FilteredList.FilterMatcher#match(Object)
		 */
		public boolean match(Object element) {
			if (!(element instanceof ITypeInfo))
				return false;

			ITypeInfo info = (ITypeInfo) element;
			IQualifiedTypeName qualifiedName = info.getQualifiedTypeName();
			
			if (fVisibleTypes != null && !fVisibleTypes.contains(new Integer(info.getCElementType())))
				return false;

			if (!fShowLowLevelTypes && qualifiedName.isLowLevel())
				return false;
			
			if (fSegmentMatchers.length == 1 && !fMatchGlobalNamespace)
				return fNameMatcher.match(qualifiedName.getName());
			
			return matchQualifiedName(info);
		}

		private boolean matchQualifiedName(ITypeInfo info) {
			IQualifiedTypeName qualifiedName = info.getQualifiedTypeName();
			if (fSegmentMatchers.length != qualifiedName.segmentCount())
				return false;
			
			if (fMatchGlobalNamespace) {
				// must match global namespace (eg ::foo)
				if (info.getRootNamespace(false) != null)
					return false;
			}
			
			boolean matchFound = true;
			int max = Math.min(fSegmentMatchers.length, qualifiedName.segmentCount());
			for (int i = 0; i < max; ++i) {
				StringMatcher matcher = fSegmentMatchers[i];
				String name = qualifiedName.segment(i);
				if (name == null || !matcher.match(name)) {
					matchFound = false;
					break;
				}
			}
			return matchFound;
		}

		private static String adjustPattern(String pattern) {
			int length = pattern.length();
			if (length > 0) {
				switch (pattern.charAt(length - 1)) {
					case END_SYMBOL:
						return pattern.substring(0, length - 1);
					case ANY_STRING:
						return pattern;
				}
			}
			return pattern + ANY_STRING;
		}
	}
	
	private static class StringComparator implements Comparator<String> {
	    public int compare(String left, String right) {
	     	int result = left.compareToIgnoreCase(right);			
			if (result == 0)
				result = left.compareTo(right);

			return result;
	    }
	}

	private static final String DIALOG_SETTINGS = TypeSelectionDialog.class.getName();
	private static final String SETTINGS_X_POS = "x"; //$NON-NLS-1$
	private static final String SETTINGS_Y_POS = "y"; //$NON-NLS-1$
	private static final String SETTINGS_WIDTH = "width"; //$NON-NLS-1$
	private static final String SETTINGS_HEIGHT = "height"; //$NON-NLS-1$
	private static final String SETTINGS_SHOW_NAMESPACES = "show_namespaces"; //$NON-NLS-1$
	private static final String SETTINGS_SHOW_CLASSES = "show_classes"; //$NON-NLS-1$
	private static final String SETTINGS_SHOW_STRUCTS = "show_structs"; //$NON-NLS-1$
	private static final String SETTINGS_SHOW_TYPEDEFS = "show_typedefs"; //$NON-NLS-1$
	private static final String SETTINGS_SHOW_ENUMS = "show_enums"; //$NON-NLS-1$
	private static final String SETTINGS_SHOW_UNIONS = "show_unions"; //$NON-NLS-1$
	private static final String SETTINGS_SHOW_FUNCTIONS = "show_functions"; //$NON-NLS-1$
	private static final String SETTINGS_SHOW_VARIABLES = "show_variables"; //$NON-NLS-1$
	private static final String SETTINGS_SHOW_MACROS = "show_macros"; //$NON-NLS-1$
	private static final String SETTINGS_SHOW_LOWLEVEL = "show_lowlevel"; //$NON-NLS-1$

	private static final TypeInfoLabelProvider fElementRenderer = new TypeInfoLabelProvider(
			TypeInfoLabelProvider.SHOW_NAME_ONLY | TypeInfoLabelProvider.SHOW_PARAMETERS);
	private static final TypeInfoLabelProvider fQualifierRenderer = new TypeInfoLabelProvider(
			TypeInfoLabelProvider.SHOW_FULLY_QUALIFIED |
			TypeInfoLabelProvider.SHOW_PARAMETERS |
			TypeInfoLabelProvider.SHOW_PATH);
	
	private static final StringComparator fStringComparator = new StringComparator();

	private static final int[] ALL_TYPES = { ICElement.C_NAMESPACE, ICElement.C_CLASS,
			ICElement.C_STRUCT, ICElement.C_TYPEDEF, ICElement.C_ENUMERATION,
			ICElement.C_UNION, ICElement.C_FUNCTION, ICElement.C_VARIABLE, ICElement.C_MACRO };

	// the filter matcher contains state information, must not be static
	private final TypeFilterMatcher fFilterMatcher = new TypeFilterMatcher();
	private Set<Integer> fKnownTypes = new HashSet<Integer>(ALL_TYPES.length);
	private Text fTextWidget;
	private boolean fSelectFilterText = false;
	private FilteredList fNewFilteredList;
	private String fDialogSection;
	private Point fLocation;
	private Point fSize;
 
	/**
	 * Constructs a type selection dialog.
	 * @param parent  the parent shell.
	 */
	public TypeSelectionDialog(Shell parent) {
		super(parent, fElementRenderer, fQualifierRenderer);
		setMatchEmptyString(false);
		setUpperListLabel(TypeInfoMessages.TypeSelectionDialog_upperLabel); 
		setLowerListLabel(TypeInfoMessages.TypeSelectionDialog_lowerLabel); 
		setVisibleTypes(ALL_TYPES);
		setDialogSettings(DIALOG_SETTINGS);
	}
	
	/**
	 * Sets the filter pattern.
	 * @param filter the filter pattern.
	 * @param selectText <code>true</code> if filter text should be initially selected
	 * @see org.eclipse.ui.dialogs.AbstractElementListSelectionDialog#setFilter(java.lang.String)
	 */
	public void setFilter(String filter, boolean selectText) {
		super.setFilter(filter);
		fSelectFilterText = selectText;
	}

	/**
	 * Sets which CElement types are visible in the dialog.
	 * 
	 * @param types Array of CElement types.
	 */
	public void setVisibleTypes(int[] types) {
		fKnownTypes.clear();
		for (int i = 0; i < types.length; ++i) {
			fKnownTypes.add(new Integer(types[i]));
		}
	}
	
	/**
	 * Answer whether the given type is visible in the dialog.
	 * 
	 * @param type
	 *            the type constant, see {@link ICElement}
	 * @return <code>true</code> if the given type is visible,
	 *         <code>false</code> otherwise
	 */
	protected boolean isVisibleType(int type) {
		return fKnownTypes.contains(new Integer(type));
	}

	/**
	 * Sets section name to use when storing the dialog settings.
	 * 
	 * @param section Name of section.
	 */
	public void setDialogSettings(String section) {
		fDialogSection = section + "Settings"; //$NON-NLS-1$
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.ui.dialogs.AbstractElementListSelectionDialog#createFilterText(org.eclipse.swt.widgets.Composite)
	 */
 	@Override
	protected Text createFilterText(Composite parent) {
 		fTextWidget = super.createFilterText(parent);

		// create type checkboxes below filter text
 		createTypeFilterArea(parent);
		
 		return fTextWidget;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.ui.dialogs.AbstractElementListSelectionDialog#createFilteredList(org.eclipse.swt.widgets.Composite)
	 */
 	@Override
	protected FilteredList createFilteredList(Composite parent) {
 		fNewFilteredList = super.createFilteredList(parent);
		fNewFilteredList.setFilterMatcher(fFilterMatcher);
		fNewFilteredList.setComparator(fStringComparator);
		//bug 189330 - adding label to element list for accessiblity
		if (fNewFilteredList != null) {
			fNewFilteredList.getAccessible().addAccessibleListener(
	            new AccessibleAdapter() {                       
	                @Override
					public void getName(AccessibleEvent e) {
	                        e.result = TypeInfoMessages.TypeSelectionDialog_upperLabel;
	                }
	            }
	        );
		}
		return fNewFilteredList;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.window.Window#create()
	 */
	@Override
	public void create() {
		super.create();
		if (fSelectFilterText)
			fTextWidget.selectAll();
	}

	/*
	 * @see Window#close()
	 */
	@Override
	public boolean close() {
		writeSettings(getDialogSettings());
		return super.close();
	}

	/*
	 * @see org.eclipse.jface.window.Window#createContents(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	protected Control createContents(Composite parent) {
		readSettings(getDialogSettings());
		return super.createContents(parent);
	}

	/**
	 * Creates a type filter checkbox.
	 */
	private void createTypeCheckbox(Composite parent, Integer typeObject) {
		String name;
		int type = typeObject.intValue();
		switch (type) {
			case ICElement.C_NAMESPACE:
				name = TypeInfoMessages.TypeSelectionDialog_filterNamespaces; 
			break;
			case ICElement.C_CLASS:
				name = TypeInfoMessages.TypeSelectionDialog_filterClasses; 
			break;
			case ICElement.C_STRUCT:
				name = TypeInfoMessages.TypeSelectionDialog_filterStructs; 
			break;
			case ICElement.C_TYPEDEF:
				name = TypeInfoMessages.TypeSelectionDialog_filterTypedefs; 
			break;
			case ICElement.C_ENUMERATION:
				name = TypeInfoMessages.TypeSelectionDialog_filterEnums; 
			break;
			case ICElement.C_UNION:
				name = TypeInfoMessages.TypeSelectionDialog_filterUnions; 
			break;
			case ICElement.C_FUNCTION:
				name = TypeInfoMessages.TypeSelectionDialog_filterFunctions; 
			break;
			case ICElement.C_VARIABLE:
				name = TypeInfoMessages.TypeSelectionDialog_filterVariables; 
			break;
			case ICElement.C_MACRO:
				name = TypeInfoMessages.TypeSelectionDialog_filterMacros; 
			break;
			default:
				return;
		}
		Image icon = TypeInfoLabelProvider.getTypeIcon(type);

		Composite composite = new Composite(parent, SWT.NONE);
		GridLayout layout= new GridLayout(2, false);
		layout.marginHeight = 0;
		composite.setLayout(layout);
		
		final Integer fTypeObject = typeObject;
		Button checkbox = new Button(composite, SWT.CHECK);
		checkbox.setFont(composite.getFont());
		checkbox.setImage(icon);
		checkbox.setSelection(fFilterMatcher.getVisibleTypes().contains(fTypeObject));
		checkbox.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				if (e.widget instanceof Button) {
					Button checkbox = (Button) e.widget;
					if (checkbox.getSelection())
						fFilterMatcher.getVisibleTypes().add(fTypeObject);
					else
						fFilterMatcher.getVisibleTypes().remove(fTypeObject);
					updateElements();
				}
			}
		});
		checkbox.setText(name);
	}

	/**
	 * Creates an area to filter types.
	 * 
	 * @param parent area to create controls in
	 */
	private void createTypeFilterArea(Composite parent) {
		createLabel(parent, TypeInfoMessages.TypeSelectionDialog_filterLabel); 
		
		Composite upperRow = new Composite(parent, SWT.NONE);
		int columns= fKnownTypes.size() > 6 ? 4 : 3;
		GridLayout upperLayout = new GridLayout(columns, true);
		upperLayout.verticalSpacing = 2;
		upperLayout.marginHeight = 0;
		upperLayout.marginWidth = 0;
		upperRow.setLayout(upperLayout);

		// the for loop is here to guarantee we always
		// create the checkboxes in the same order
		for (int i = 0; i < ALL_TYPES.length; ++i) {
			Integer typeObject = new Integer(ALL_TYPES[i]);
			if (fKnownTypes.contains(typeObject))
				createTypeCheckbox(upperRow, typeObject);
		}

		if (showLowLevelFilter()) {
			Composite lowerRow = new Composite(parent, SWT.NONE);
			GridLayout lowerLayout = new GridLayout(1, true);
			lowerLayout.verticalSpacing = 2;
			lowerLayout.marginHeight = 0;
			upperLayout.marginWidth = 0;
			lowerRow.setLayout(lowerLayout);

			Composite composite = new Composite(lowerRow, SWT.NONE);
			GridLayout layout= new GridLayout(2, false);
			layout.marginHeight = 0;
			layout.marginWidth = 0;
			composite.setLayout(layout);

			String name = TypeInfoMessages.TypeSelectionDialog_filterLowLevelTypes; 
			Button checkbox = new Button(composite, SWT.CHECK);
			checkbox.setFont(composite.getFont());
			checkbox.setText(name);
			checkbox.setSelection(fFilterMatcher.getShowLowLevelTypes());
			checkbox.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					if (e.widget instanceof Button) {
						Button button = (Button) e.widget;
						fFilterMatcher.setShowLowLevelTypes(button.getSelection());
						updateElements();
					}
				}
			});
		}
	}
	
	/**
	 * Forces redraw of elements list.
	 */
	void updateElements() {
		fNewFilteredList.setFilter(fTextWidget.getText());
		handleSelectionChanged();
	}
	
	/**
	 * Returns the dialog settings object used to save state
	 * for this dialog.
	 *
	 * @return the dialog settings to be used
	 */
	protected IDialogSettings getDialogSettings() {
		IDialogSettings allSettings = CUIPlugin.getDefault().getDialogSettings();
		IDialogSettings section = allSettings.getSection(fDialogSection);		
		if (section == null) {
			section = allSettings.addNewSection(fDialogSection);
			writeDefaultSettings(section);
		}
		return section;
	}
	
	/**
	 * Stores current configuration in the dialog store.
	 */
	protected void writeSettings(IDialogSettings section) {
		Point location = getShell().getLocation();
		section.put(SETTINGS_X_POS, location.x);
		section.put(SETTINGS_Y_POS, location.y);

		if (getTray() == null) {
			// only save size if help tray is not shown
			Point size = getShell().getSize();
			section.put(SETTINGS_WIDTH, size.x);
			section.put(SETTINGS_HEIGHT, size.y);
		}
		section.put(SETTINGS_SHOW_NAMESPACES, fFilterMatcher.getVisibleTypes().contains(new Integer(ICElement.C_NAMESPACE)));
		section.put(SETTINGS_SHOW_CLASSES, fFilterMatcher.getVisibleTypes().contains(new Integer(ICElement.C_CLASS)));
		section.put(SETTINGS_SHOW_STRUCTS, fFilterMatcher.getVisibleTypes().contains(new Integer(ICElement.C_STRUCT)));
		section.put(SETTINGS_SHOW_TYPEDEFS, fFilterMatcher.getVisibleTypes().contains(new Integer(ICElement.C_TYPEDEF)));
		section.put(SETTINGS_SHOW_ENUMS, fFilterMatcher.getVisibleTypes().contains(new Integer(ICElement.C_ENUMERATION)));
		section.put(SETTINGS_SHOW_UNIONS, fFilterMatcher.getVisibleTypes().contains(new Integer(ICElement.C_UNION)));
		section.put(SETTINGS_SHOW_FUNCTIONS, fFilterMatcher.getVisibleTypes().contains(new Integer(ICElement.C_FUNCTION)));
		section.put(SETTINGS_SHOW_VARIABLES, fFilterMatcher.getVisibleTypes().contains(new Integer(ICElement.C_VARIABLE)));
		section.put(SETTINGS_SHOW_MACROS, fFilterMatcher.getVisibleTypes().contains(new Integer(ICElement.C_MACRO)));
		section.put(SETTINGS_SHOW_LOWLEVEL, fFilterMatcher.getShowLowLevelTypes());
	}

	/**
	 * Stores default dialog settings.
	 */
	protected void writeDefaultSettings(IDialogSettings section) {
		section.put(SETTINGS_SHOW_NAMESPACES, true); 
		section.put(SETTINGS_SHOW_CLASSES, true);
		section.put(SETTINGS_SHOW_STRUCTS, true);
		section.put(SETTINGS_SHOW_TYPEDEFS, true);
		section.put(SETTINGS_SHOW_ENUMS, true);
		section.put(SETTINGS_SHOW_UNIONS, true);
		section.put(SETTINGS_SHOW_FUNCTIONS, true);
		section.put(SETTINGS_SHOW_VARIABLES, true);
		section.put(SETTINGS_SHOW_MACROS, true);
		section.put(SETTINGS_SHOW_LOWLEVEL, false);
	}

	/**
	 * Initializes itself from the dialog settings with the same state
	 * as at the previous invocation.
	 */
	protected void readSettings(IDialogSettings section) {
		try {
			int x = section.getInt(SETTINGS_X_POS);
			int y = section.getInt(SETTINGS_Y_POS);
			fLocation = new Point(x, y);
			int width = section.getInt(SETTINGS_WIDTH);
			int height = section.getInt(SETTINGS_HEIGHT);
			fSize = new Point(width, height);
		} catch (NumberFormatException e) {
			fLocation = null;
			fSize = null;
		}

		if (section.getBoolean(SETTINGS_SHOW_NAMESPACES)) {
			Integer typeObject = new Integer(ICElement.C_NAMESPACE);
			if (fKnownTypes.contains(typeObject))
				fFilterMatcher.getVisibleTypes().add(typeObject);
		}
		if (section.getBoolean(SETTINGS_SHOW_CLASSES)) {
			Integer typeObject = new Integer(ICElement.C_CLASS);
			if (fKnownTypes.contains(typeObject))
				fFilterMatcher.getVisibleTypes().add(typeObject);
		}
		if (section.getBoolean(SETTINGS_SHOW_STRUCTS)) {
			Integer typeObject = new Integer(ICElement.C_STRUCT);
			if (fKnownTypes.contains(typeObject))
				fFilterMatcher.getVisibleTypes().add(typeObject);
		}
		if (section.getBoolean(SETTINGS_SHOW_TYPEDEFS)) {
			Integer typeObject = new Integer(ICElement.C_TYPEDEF);
			if (fKnownTypes.contains(typeObject))
				fFilterMatcher.getVisibleTypes().add(typeObject);
		}
		if (section.getBoolean(SETTINGS_SHOW_ENUMS)) {
			Integer typeObject = new Integer(ICElement.C_ENUMERATION);
			if (fKnownTypes.contains(typeObject))
				fFilterMatcher.getVisibleTypes().add(typeObject);
		}
		if (section.getBoolean(SETTINGS_SHOW_UNIONS)) {
			Integer typeObject = new Integer(ICElement.C_UNION);
			if (fKnownTypes.contains(typeObject))
				fFilterMatcher.getVisibleTypes().add(typeObject);
		}
		if (section.getBoolean(SETTINGS_SHOW_FUNCTIONS)) {
			Integer typeObject = new Integer(ICElement.C_FUNCTION);
			if (fKnownTypes.contains(typeObject))
				fFilterMatcher.getVisibleTypes().add(typeObject);
		}
		if (section.getBoolean(SETTINGS_SHOW_VARIABLES)) {
			Integer typeObject = new Integer(ICElement.C_VARIABLE);
			if (fKnownTypes.contains(typeObject))
				fFilterMatcher.getVisibleTypes().add(typeObject);
		}
		if (section.getBoolean(SETTINGS_SHOW_MACROS)) {
			Integer typeObject = new Integer(ICElement.C_MACRO);
			if (fKnownTypes.contains(typeObject))
				fFilterMatcher.getVisibleTypes().add(typeObject);
		}
		if (showLowLevelFilter()) {
			fFilterMatcher.setShowLowLevelTypes(section.getBoolean(SETTINGS_SHOW_LOWLEVEL));
		} else {
			fFilterMatcher.setShowLowLevelTypes(true);
		}
	}
	
	/**
	 * @return whether the low level filter checkbox should be shown
	 */
	protected boolean showLowLevelFilter() {
		return true;
	}

	/* (non-Cdoc)
	 * @see org.eclipse.jface.window.Window#getInitialSize()
	 */
	@Override
	protected Point getInitialSize() {
		Point result = super.getInitialSize();
		if (fSize != null) {
			result.x = Math.max(result.x, fSize.x);
			result.y = Math.max(result.y, fSize.y);
			Rectangle display = getShell().getDisplay().getClientArea();
			result.x = Math.min(result.x, display.width);
			result.y = Math.min(result.y, display.height);
		}
		return result;
	}
	
	/* (non-Cdoc)
	 * @see org.eclipse.jface.window.Window#getInitialLocation(org.eclipse.swt.graphics.Point)
	 */
	@Override
	protected Point getInitialLocation(Point initialSize) {
		Point result = super.getInitialLocation(initialSize);
		if (fLocation != null) {
			result.x = fLocation.x;
			result.y = fLocation.y;
			Rectangle display = getShell().getDisplay().getClientArea();
			int xe = result.x + initialSize.x;
			if (xe > display.width) {
				result.x -= xe - display.width; 
			}
			int ye = result.y + initialSize.y;
			if (ye > display.height) {
				result.y -= ye - display.height; 
			}
		}
		return result;
	}	
	
	/*
	 * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
	 */
	@Override
	protected void computeResult() {
		ITypeInfo selection = (ITypeInfo) getLowerSelectedElement();
		if (selection == null)
			return;
			
		List<ITypeInfo> result = new ArrayList<ITypeInfo>(1);
		result.add(selection);
		setResult(result);
	}
	
    @Override
	public Object[] getFoldedElements(int index) {
    	ArrayList<IndexTypeInfo> result= new ArrayList<IndexTypeInfo>();
    	Object[] typeInfos= super.getFoldedElements(index);
    	if (typeInfos != null) {
    		for (Object typeInfo : typeInfos) {
    			if (typeInfo instanceof IndexTypeInfo) {
    				addFoldedElements((IndexTypeInfo) typeInfo, result);
    			}
    		}
    	}
    	return result.toArray();
    }

	private void addFoldedElements(IndexTypeInfo typeInfo, ArrayList<IndexTypeInfo> result) {
		ITypeReference[] refs= typeInfo.getReferences();
		for (ITypeReference ref : refs) {
			result.add(new IndexTypeInfo(typeInfo, ref));
		}
	}
}
