core/com.nokia.carbide.cpp.sdk.ui/src/com/nokia/carbide/cpp/sdk/ui/shared/BuildTargetsPage.java
author timkelly
Tue, 31 Aug 2010 17:03:48 -0500
changeset 1931 d7c1f804f316
parent 1670 f1324e4c3588
permissions -rw-r--r--
using IRunnableContext for retrieving BuildTargetTreeNode input so progress dialog can be displayed for wizards or in the status bar when on the UI thread (e.g. ManageConfigurationsDialog) Removes API in BuildTargetTreeNode that presumed all nodes were SBSv1 (didn't appear to be used anywhere)

/*
* Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/

package com.nokia.carbide.cpp.sdk.ui.shared;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.dialogs.IDialogPage;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.IFilter;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeNode;
import org.eclipse.jface.viewers.TreeNodeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
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.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ContainerCheckedTreeViewer;
import org.eclipse.ui.dialogs.PreferencesUtil;

import com.nokia.carbide.cpp.internal.api.sdk.ui.TemplateUtils;
import com.nokia.carbide.cpp.internal.sdk.ui.Messages;
import com.nokia.carbide.cpp.internal.sdk.ui.SDKUIHelpIds;
import com.nokia.carbide.cpp.sdk.core.ISymbianBuildContext;
import com.nokia.carbide.cpp.sdk.core.ISymbianSDK;
import com.nokia.carbide.internal.api.templatewizard.ui.IWizardDataPage;
import com.nokia.carbide.template.engine.ITemplate;
import com.nokia.cpp.internal.api.utils.core.Check;
import com.nokia.cpp.internal.api.utils.core.HostOS;
import com.nokia.cpp.internal.api.utils.ui.WorkbenchUtils;

/**
 * Wizard page used to select a list of Carbide.c++ build configurations.  Currently used
 * by the new project and import bld.inf wizards, but can be used by any wizard that needs
 * to get a list of build configs.
 */
public class BuildTargetsPage extends WizardPage implements IWizardDataPage {
	
	protected static final String UID = ".uid"; //$NON-NLS-1$
	protected static final String SELECTED_BUILD_CONFIGS_VALUE_KEY = "selectedBuildConfigs"; //$NON-NLS-1$
	
	/**
	 * @since 1.4
	 */
	public static final String SBSV2BUILDER = "useSBSv2Builder"; //$NON-NLS-1$

	protected static class FilteringContentProviderWrapper implements ITreeContentProvider {

		protected ITreeContentProvider wrappedProvider;
		protected IFilter filter;
		protected IFilter configFilter;
		
		public FilteringContentProviderWrapper(ITreeContentProvider wrappedProvider) {
			super();
			Check.checkArg(wrappedProvider);
			this.wrappedProvider = wrappedProvider;
		}

		public Object[] getElements(Object inputElement) {
			List<Object> filteredElements = new ArrayList<Object>();
			Object[] elements = wrappedProvider.getElements(inputElement);
			for (int i = 0; i < elements.length; i++) {
				Object object = elements[i];
				if (filter == null || filter.select(object))
					filteredElements.add(object);
			}
			
			return filteredElements.toArray();
		}

		public void dispose() {
			wrappedProvider.dispose();
		}

		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
			wrappedProvider.inputChanged(viewer, oldInput, newInput);
		}

		public Object[] getChildren(Object parentElement) {
			List<Object> filteredElements = new ArrayList<Object>();
			Object[] elements = wrappedProvider.getChildren(parentElement);
			if (elements != null) {
				for (int i = 0; i < elements.length; i++) {
					Object object = elements[i];
					if (configFilter == null || configFilter.select(object))
						filteredElements.add(object);
				}
			}
			
			return filteredElements.toArray();
		}

		public Object getParent(Object element) {
			return wrappedProvider.getParent(element);
		}

		public boolean hasChildren(Object element) {
			return wrappedProvider.hasChildren(element);
		}

		public void setFilter(IFilter filter) {
			this.filter = filter;
		}
		
		public void setConfigFilter(IFilter filter) {
			this.configFilter = filter;
		}
	}
	
	static class TemplateSDKFilter implements IFilter {
		
		private ITemplate template;

		public void setTemplate(ITemplate template) {
			this.template = template;
		}
		
		public boolean select(Object toTest) {
			if (template == null)
				return true;
			
			Check.checkArg(toTest instanceof BuildTargetTreeNode);
			ISymbianSDK symbianSDK = ((BuildTargetTreeNode) toTest).getSymbianSDK();
			return TemplateUtils.sdkMatchesTemplate(symbianSDK, template);
		}
	}
	
	private Button filterCheckbox;
	protected ContainerCheckedTreeViewer viewer;
	private Label configLabel;
	private boolean hideFilterCheckbox;
	protected FilteringContentProviderWrapper filteringContentProviderWrapper;
	private TemplateSDKFilter templateFilter;
	private boolean inited;
	private boolean useSBSv2Builder;

	/**
	 * Default constructor
	 */
	public BuildTargetsPage() {
		super(Messages.getString("BuildTargetsPage.BuildTargetsTitle")); //$NON-NLS-1$
		setTitle(Messages.getString("BuildTargetsPage.SymbianSDKS")); //$NON-NLS-1$
		setDescription(Messages.getString("BuildTargetsPage.BuildTargetsDescription")); //$NON-NLS-1$
		templateFilter = new TemplateSDKFilter();
	}

	/**
	 * see {@link IDialogPage#createControl(Composite)}
	 */
	public void createControl(Composite parent) {
		setPageComplete(false);
		setErrorMessage(null);
		setMessage(null);

		initializeDialogUnits(parent);
        
        Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayout(new GridLayout());
        composite.setLayoutData(new GridData(GridData.FILL_BOTH));

		createControls(composite);
		
		setControl(composite);
 
		getControl().setData(UID, "BuildTargetsPage"); //$NON-NLS-1$
		getControl().setData("WizardPage", this); //$NON-NLS-1$
	}

	private void createControls(Composite parent) {
		Font font = parent.getFont();
		
		configLabel = new Label(parent, SWT.LEFT);
		configLabel.setFont(font);
		configLabel.setText(Messages.getString("BuildTargetsPage.BuildConfigsLabel")); //$NON-NLS-1$
		configLabel.setToolTipText(Messages.getString("BuildTargetsPage.BuildConfigsToolTip")); //$NON-NLS-1$
		
        drawSDKConfigView(parent);
		
        filterCheckbox = new Button(parent, SWT.CHECK);
        filterCheckbox.setText(Messages.getString("BuildTargetsPage.FilterText")); //$NON-NLS-1$
        filterCheckbox.setSelection(true); // default to checked
        if (hideFilterCheckbox)
        	filterCheckbox.setVisible(false);
        else
        	filteringContentProviderWrapper.setFilter(templateFilter);
		filterCheckbox.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				TemplateSDKFilter filter = filterCheckbox.getSelection() ? templateFilter : null;
				filteringContentProviderWrapper.setFilter(filter);
				viewer.refresh();
				setPageComplete(validatePage());
			}
        });
		filterCheckbox.setData(UID, "filterCheckbox"); //$NON-NLS-1$

		addOtherControls(parent);
		
		Link fConfigPrefsLink = new Link(parent, SWT.WRAP);
		fConfigPrefsLink.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		fConfigPrefsLink.setText(Messages.getString("BuildTargetsPage.Select_Filtering_Prefs_Link")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		fConfigPrefsLink.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
		fConfigPrefsLink.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				// I don't see a way to open it to a specific tab, only the page
				if (Window.OK == PreferencesUtil.createPreferenceDialogOn(getShell(), "com.nokia.carbide.cpp.sdk.ui.preferences.BuildPlatformFilterPage", null, null, 0).open()){ //$NON-NLS-1$
					inited = false;
					setVisible(true);
					TemplateSDKFilter filter = filterCheckbox.getSelection() ? templateFilter : null;
					filteringContentProviderWrapper.setFilter(filter);
					viewer.getTree().clearAll(true);
					viewer.refresh();
					setPageComplete(validatePage());
				}
			}

		});
		
		Link fSDKLink = new Link(parent, SWT.WRAP);
		fSDKLink.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		fSDKLink.setText(Messages.getString("BuildTargetsPage.Select_SymbianSDKs_Prefs_Link")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		fSDKLink.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
		fSDKLink.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				// I don't see a way to open it to a specific tab, only the page
				if (Window.OK == PreferencesUtil.createPreferenceDialogOn(getShell(), "com.nokia.carbide.cpp.sdk.ui.preferences.SDKPreferencePage", null, null, 0).open()){ //$NON-NLS-1$
					inited = false;
					setVisible(true);
					TemplateSDKFilter filter = filterCheckbox.getSelection() ? templateFilter : null;
					filteringContentProviderWrapper.setFilter(filter);
					viewer.getTree().clearAll(true);
					viewer.refresh();
					setPageComplete(validatePage());
				}
			}

		});
		
		setPageComplete(validatePage());
	}
	
	private void drawSDKConfigView(Composite parent) {
		
		viewer = new ContainerCheckedTreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
		viewer.getTree().setLayoutData(new GridData(GridData.FILL_BOTH));
		class SDKNodeLabelProvider extends LabelProvider implements IColorProvider {

			public Color getForeground(Object element) {
				if (element instanceof BuildTargetTreeNode){
					BuildTargetTreeNode treeNode = (BuildTargetTreeNode)element;
					if (treeNode.getValue() instanceof ISymbianSDK){
						if (treeNode.toString().contains(BuildTargetTreeNode.SDK_NODE_ERROR_EPOCROOT_INVALID)){
							return WorkbenchUtils.getSafeShell().getDisplay().getSystemColor(SWT.COLOR_RED);
						}
					}
				}
				
				return null;
			}

			public Color getBackground(Object element) {
				return null;
			}
		}
		
		viewer.setLabelProvider(new SDKNodeLabelProvider());
		
		TreeNodeContentProvider treeNodeContentProvider = new TreeNodeContentProvider();
		filteringContentProviderWrapper = 
			new FilteringContentProviderWrapper(treeNodeContentProvider);
		viewer.setContentProvider(filteringContentProviderWrapper);
		viewer.addCheckStateListener(new ICheckStateListener() {
			public void checkStateChanged(CheckStateChangedEvent event) {
				setPageComplete(validatePage());
			}
		});
		viewer.getControl().setData(UID, "buildConfigsTree"); //$NON-NLS-1$
		viewer.getControl().setData("viewer", viewer); //$NON-NLS-1$
		
	}

	/**
	 * Add more controls after the tree and filter checkbox.  Allows subclasses to add
	 * more controls.
	 * @since 1.4
	 */
	protected void addOtherControls(Composite parent) {
	}
	
	/**
	 * If using the template filter checkbox, this allows you to set the
	 * template to filter on.
	 * @param template the template to filter on
	 * @since 1.4
	 */
	public void setSelectedTemplate(ITemplate template) {
		this.templateFilter.setTemplate(template);
		
		// need to init again if they select another template
		inited = false;
	}
	
	/**
	 * Hides the "Filter SDKs based on selected template" checkbox
	 */
	public void setHideFilterCheckbox() {
		hideFilterCheckbox = true;
	}
	
	protected boolean validatePage() {
		boolean isValid = false;
		if (viewer.getTree().getItemCount() == 0) {
			if (filterCheckbox.getSelection())
				setErrorMessage(Messages.getString("BuildTargetsPage.NoMatchingSDKsError")); //$NON-NLS-1$
			else
				setErrorMessage(Messages.getString("BuildTargetsPage.NoSDKsError")); //$NON-NLS-1$
		} else if ((viewer.getCheckedElements()).length > 1) {
			setErrorMessage(null);
			isValid = true;
		} else {
			setErrorMessage(Messages.getString("BuildTargetsPage.NoBuildConfigsSelectedError")); //$NON-NLS-1$
		}
		return isValid;
	}
	
	protected void checkPathWithSDKs(IPath path) {
		setMessage(null);
		
		// drives are relevant in Windows only
		if (!HostOS.IS_WIN32)
			return;
		
		for (ISymbianBuildContext context : getSelectedBuildConfigs()) {
			Path sdkPath = new Path(context.getSDK().getEPOCROOT());
			if (sdkPath.getDevice() != null) {
				if (!useSBSv2Builder) {
					if (!sdkPath.getDevice().equalsIgnoreCase(path.getDevice())) {
						// there are no warnings so set the error message but return true so
						// the user can continue if they choose to ignore our warning.
						String warning = MessageFormat.format(
								Messages.getString("BuildTargetsPage.DifferentDrivesError"),  //$NON-NLS-1$
								new Object[] { context.getSDK().getUniqueId() });
						setMessage(warning, DialogPage.WARNING);
					}
				}
			} else {
				// else devices.xml 'epocroot' has no drive spec, so it will use the implicit epocroot
				String warning = MessageFormat.format(
						Messages.getString("BuildTargetsPage.NoDriveSpecWarning"),  //$NON-NLS-1$
						new Object[] { context.getSDK().getUniqueId() });
				setMessage(warning, DialogPage.WARNING);
			}
			
		}
	}

	/**
	 * Returns the list of build configs selected in this page to template wizards.
	 * @return the map of values for this page - 
	 * a singleton map containing the list of selected build configs mapped to the key "selectedBuildConfigs". 
	 */
	public Map<String, Object> getPageValues() {
		return Collections.singletonMap(SELECTED_BUILD_CONFIGS_VALUE_KEY, (Object)getSelectedBuildConfigs());
	}

	/**
	 * Gets the list of build configs selected in this page
	 * @return the list of build configs checked in the tree viewer
	 */
	public List<ISymbianBuildContext> getSelectedBuildConfigs() {
		List<ISymbianBuildContext> selectedConfigs = new ArrayList<ISymbianBuildContext>();
		Object[] items = viewer.getCheckedElements();
		for (int i=0; i<items.length; i++) {
			TreeNode node = (TreeNode)items[i];
			if (node.getValue() instanceof ISymbianBuildContext) {
				selectedConfigs.add((ISymbianBuildContext)node.getValue());
			}
		}
		return selectedConfigs;
	}

	/**
	 * See {@link IDialogPage#setVisible(boolean)}
	 */
	@Override
	public void setVisible(boolean visible) {
		super.setVisible(visible);

		if (visible) {
			// set the viewer input when the page becomes visible, but only do it once
			// unless the builder has changed.
			boolean setInput = true;
			
			Object value = getShell().getData(SBSV2BUILDER);
			if (value != null && value instanceof Boolean) {
				boolean sbsv2 = ((Boolean)value).booleanValue();
				if (sbsv2 == useSBSv2Builder) {
					if (inited) {
						setInput = false;
					}
				} else {
					useSBSv2Builder = sbsv2;
				}
			}

			if (setInput) {
		        viewer.setInput(BuildTargetTreeNode.getTreeViewerInput(useSBSv2Builder, getContainer()));
			}

			if (!inited) {
				inited = true;
				TreeItem[] items = viewer.getTree().getItems();
				if (items != null && items.length == 1) {
					viewer.setChecked(items[0].getData(), true);
				}
			}
			
			setPageComplete(validatePage());
		}
	}

	@Override
	public void performHelp() {
        PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl().getShell(), SDKUIHelpIds.BUILD_TARGETS_PAGE);
	}
}