sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.wizards/src/com/nokia/carbide/cpp/internal/pi/wizards/ui/util/PkgListTreeViewer.java
/*
* Copyright (c) 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.internal.pi.wizards.ui.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.IElementComparer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.TreeItem;
import com.nokia.carbide.cdt.builder.CarbideBuilderPlugin;
import com.nokia.carbide.cdt.builder.project.ICarbideBuildConfiguration;
import com.nokia.carbide.cdt.builder.project.ICarbideProjectInfo;
import com.nokia.carbide.cdt.builder.project.ISISBuilderInfo;
import com.nokia.carbide.cpp.pi.util.GeneralMessages;
public class PkgListTreeViewer extends TreeViewer {
PkgListTreeContentProvider contentProvider;
private class PkgListProjectItem extends PkgListBaseCustomItem {
PkgListProjectItem(PkgListTreeViewer myViewer, Object myElement,
TreeItem myTreeItem) {
super(myViewer, myElement, myTreeItem);
}
}
private class PkgListBuildConfigurationItem extends PkgListBaseCustomItem {
PkgListBuildConfigurationItem(PkgListTreeViewer myViewer,
Object myElement, TreeItem myTreeItem) {
super(myViewer, myElement, myTreeItem);
}
}
private class PkgListFileItem extends PkgListBaseCustomItem {
PkgListFileItem(PkgListTreeViewer myViewer, Object myElement,
TreeItem myTreeItem) {
super(myViewer, myElement, myTreeItem);
}
}
private class PkgListTreeViewerComparer implements IElementComparer {
public boolean equals(Object a, Object b) {
// if they're both build configuration items, check that their packages and configurations match
if ( PkgListTree.isBuildConfigurationItem(a)
&& PkgListTree.isBuildConfigurationItem(b)) {
ICarbideBuildConfiguration configA = (ICarbideBuildConfiguration)a;
ICarbideBuildConfiguration configB = (ICarbideBuildConfiguration)b;
return ( configA.getCarbideProject().getProject() == configB.getCarbideProject().getProject()
&& configA.getDisplayString().equals(configB.getDisplayString()));
}
else if (PkgListTree.isFileItem(a) &&
PkgListTree.isFileItem(b)) {
IPkgEntry fileA = (IPkgEntry)a;
IPkgEntry fileB = (IPkgEntry)b;
if (fileA.getPkgFile().equals(fileB.getPkgFile()) &&
fileA.getSdk().equals(fileB.getSdk())) {
return true;
}
return false;
}
return a.equals(b);
}
public int hashCode(Object element) {
return element.hashCode();
}
}
public PkgListTreeViewer(Composite arg0, int arg1) {
// Must use NO_RADIO_GROUP, otherwise SWT automatically handle unselect for only
// one item, I haven't find a way to create new composite for each project node.
// Plus we want want to handle that the not so normal interaction among
// project checkbox/config radio button manually
super(arg0, arg1 | SWT.NO_RADIO_GROUP | SWT.V_SCROLL | SWT.MULTI);
super.setComparer(new PkgListTreeViewerComparer());
}
@Override
public void setContentProvider(IContentProvider provider) {
super.setContentProvider(provider);
if (provider instanceof PkgListTreeContentProvider) {
contentProvider = (PkgListTreeContentProvider)provider;
} else {
GeneralMessages.showErrorMessage(Messages.getString("PkgListTreeViewer.invalid.content.provider")); //$NON-NLS-1$
}
}
private Map<Object, PkgListBaseCustomItem> itemMap = new HashMap<Object, PkgListBaseCustomItem>();
private int style = 0;
@Override
protected void inputChanged(Object input, Object oldInput) {
// collapseAll(); // just highlight the selected on, setSelectedItems() takes care of expand
super.inputChanged(input, oldInput);
}
public void initializeSelectedItems(TreePath[] paths) {
for (TreePath path : paths) {
reveal(path);
}
TreeSelection elementSelection = new TreeSelection(paths);
setSelection(elementSelection, true);
}
public IPkgEntry[] getSelectedPkgs() {
List<Object> selectionList = getSelectionFromWidget();
ArrayList<IPkgEntry> selectedPkgItems = new ArrayList<IPkgEntry>();
for (Object item : selectionList) {
if (item instanceof IPkgEntry) {
selectedPkgItems.add((IPkgEntry) item);
}
}
return selectedPkgItems.toArray(new IPkgEntry[selectedPkgItems.size()]);
}
public IProject[] getSelectedProjects() {
List<Object> selectionList = getSelectionFromWidget();
ArrayList<IProject> selectedProjects = new ArrayList<IProject>();
for (Object item : selectionList) {
if (item instanceof IProject) {
selectedProjects.add((IProject) item);
}
}
return selectedProjects.toArray(new IProject[selectedProjects.size()]);
}
public ICarbideBuildConfiguration[] getSelectedConfigs() {
List<Object> selectionList = getSelectionFromWidget();
ArrayList<ICarbideBuildConfiguration> selectedConfigs = new ArrayList<ICarbideBuildConfiguration>();
for (Object item : selectionList) {
if (item instanceof ICarbideBuildConfiguration) {
selectedConfigs.add((ICarbideBuildConfiguration) item);
}
}
return selectedConfigs.toArray(new ICarbideBuildConfiguration[selectedConfigs.size()]);
}
/**
* Set additional style bits for radio button appearance.
* Most likely style added may be FLAT when in a non-3D view.
* @param style int
* @see SWT#FLAT
*/
public void setStyle(int style) {
this.style = style;
}
/**
* We override associate to maintain each items optional
* radio button information.
*/
@Override
protected void associate(final Object element, Item item) {
super.associate(element, item);
if (!(item instanceof TreeItem)) {
return;
}
TreeItem treeItem = (TreeItem)item;
PkgListBaseCustomItem baseItem = itemMap.get(getItemMapKey(element));
if (baseItem == null) {
int buttonStyle = style;
SelectionAdapter selectionAdapter;
boolean enabled = true;
if (PkgListTree.isBuildConfigurationItem(element)) {
baseItem = new PkgListBuildConfigurationItem(this, element, treeItem);
buttonStyle = buttonStyle | SWT.RADIO;
selectionAdapter = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
toggleSelectionAndUpdateSelectionToViewer(element);
}
};
// check for bad PKG, which may happen on regular Symbian stationary
List<ISISBuilderInfo> sisBuilderInfoList = ((ICarbideBuildConfiguration)element).getSISBuilderInfoList();
boolean allPkgGood = true && sisBuilderInfoList.size() > 0;
for (ISISBuilderInfo sisBuilderInfo : sisBuilderInfoList)
{
if (sisBuilderInfo == null) {
allPkgGood = false;
} else if (sisBuilderInfo.getPKGFullPath().toFile().exists() == false) {
allPkgGood = false;
}
}
enabled = allPkgGood;
} else if (PkgListTree.isProjectItem(element)) {
baseItem = new PkgListProjectItem(this, element, treeItem);
buttonStyle = buttonStyle | SWT.CHECK;
selectionAdapter = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
toggleSelectionAndUpdateSelectionToViewer(element);
}
};
ICarbideProjectInfo cpi = CarbideBuilderPlugin.getBuildManager().getProjectInfo((IProject)element);
if (cpi != null) {
enabled = false;
List<ICarbideBuildConfiguration> bc = cpi.getBuildConfigurations();
for (ICarbideBuildConfiguration config : bc) {
// exclude configuration with (none) PKG
List<ISISBuilderInfo> sisBuilderInfoList = ((ICarbideBuildConfiguration)config).getSISBuilderInfoList();
for (ISISBuilderInfo sisBuilderInfo : sisBuilderInfoList)
{
if (sisBuilderInfo == null) {
continue;
} else if (sisBuilderInfo.getPKGFullPath().toFile().exists()) {
enabled = true;
break;
}
}
}
}
} else if (PkgListTree.isFileItem(element)) {
baseItem = new PkgListFileItem(this, element, treeItem);
buttonStyle = buttonStyle | SWT.CHECK;
selectionAdapter = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
toggleSelectionAndUpdateSelectionToViewer(element);
}
};
} else {
return; // not a custom item
}
baseItem.createButton(buttonStyle, selectionAdapter);
baseItem.setEnabled(enabled);
itemMap.put(getItemMapKey(element), baseItem);
}
}
/**
* We override disassociate to clean up any
* allocated radio button information and to
* help implement force selection.
*/
@Override
protected void disassociate(Item item) {
Object element = item.getData();
if(PkgListTree.isBuildConfigurationItem(element) ||
PkgListTree.isFileItem(element) ||
PkgListTree.isProjectItem(element))
{
PkgListBaseCustomItem baseItem = itemMap.get(getItemMapKey(element));
if (baseItem != null) {
if (baseItem instanceof PkgListBuildConfigurationItem ||
baseItem instanceof PkgListProjectItem ||
baseItem instanceof PkgListFileItem )
{
baseItem.dispose();
itemMap.remove(getItemMapKey(element));
}
else {
// shouldn't get here
}
}
}
super.disassociate(item);
}
// We override this underlying selection to insert UI handling of our custom
// UI associated, can't use Listeners from TreeViewer to propagate because
// we are not SWT.CHECKBOX
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.AbstractTreeViewer#setSelectionToWidget(java.util.List, boolean)
*/
@Override
protected void setSelectionToWidget(List l, boolean reveal) {
super.setSelectionToWidget(l, reveal);
// handle UI
refreshSelectionOnUI();
}
public void refreshSelectionOnUI () {
for (Iterator<Entry<Object, PkgListBaseCustomItem>> iter = itemMap.entrySet().iterator(); iter.hasNext();) {
Entry<Object, PkgListBaseCustomItem> entry = iter.next();
entry.getValue().setSelection(false);
}
List selectedList = getSelectionFromWidget();
for (Object o : selectedList) {
reveal(o); // only expanded/visible items are available for selecting programatically
// hidden items that never been expanded cannot
PkgListBaseCustomItem plbci = itemMap.get(getItemMapKey(o));
if(plbci != null) {
plbci.setSelection(true);
}
}
}
private ArrayList<TreePath> buildArrayWithoutElement(TreePath[] selectedPaths, Object element) {
ArrayList<TreePath> returnPath = new ArrayList<TreePath> ();
for (TreePath path : selectedPaths) {
boolean skip = false;
for (int i = 0; i < path.getSegmentCount(); i++) {
if (path.getSegment(i).equals(element)) {
skip = true;
break;
}
}
if (skip == false) {
returnPath.add(path);
}
}
return returnPath;
}
public TreePath getTreePathForElement(Object element) {
// go upward from element to root, then flip the list
ArrayList<Object> segments = new ArrayList<Object>();
Object current = element;
do {
segments.add(current);
current = contentProvider.getParent(current);
} while (current != null);
Object[] reverse = segments.toArray(new Object[segments.size()]);
Object[] inOrder = new Object[segments.size()];
for (int i = 0; i < segments.size(); i++) {
inOrder[i] = reverse[segments.size() - 1 - i ];
}
return new TreePath(inOrder);
}
/*
* Build and update the tree selection reflecting the element you want to toggle
*/
private void toggleSelectionAndUpdateSelectionToViewer(final Object element)
{
ArrayList<TreePath> myNewTreePath;
ISelection iSelection = this.getSelection();
ITreeSelection iTreeSelection;
if (iSelection instanceof TreeSelection) {
iTreeSelection = (TreeSelection)iSelection;
} else {
return;
}
TreePath[] selectedPaths = iTreeSelection.getPaths();
if (PkgListTree.isFileItem(element)) {
if (iTreeSelection.getPathsFor(element).length > 0) {
// file already selected, clear self
myNewTreePath = buildArrayWithoutElement(selectedPaths, element);
} else {
// file not yet selected, select self
myNewTreePath = new ArrayList<TreePath>();
for (TreePath path: selectedPaths) {
myNewTreePath.add(path);
}
TreePath self = getTreePathForElement(element);
if (self != null) {
myNewTreePath.add(self);
}
}
} else if (PkgListTree.isBuildConfigurationItem(element)) {
Object projectSelection = contentProvider.getParent(element);
myNewTreePath = buildArrayWithoutElement(selectedPaths, projectSelection);
if (iTreeSelection.getPathsFor(element).length > 0) {
// configuration already selected, clear self and parent project
// do nothing, myNewTreePath already copied without parent project and self
} else {
// configuration not yet selected, clear siblings, select self
TreePath self = getTreePathForElement(element);
// myNewTreePath already copied without parent project and self
if (self != null) {
myNewTreePath.add(self); // add self back
}
TreePath projectPath = getTreePathForElement(projectSelection);
if (projectPath != null) {
myNewTreePath.add(projectPath); // add project back
}
}
} else if (PkgListTree.isProjectItem(element)) {
myNewTreePath = buildArrayWithoutElement(selectedPaths, element);
if (iTreeSelection.getPathsFor(element).length > 0) {
// project already selected, clear self and all children configurations
// do nothing, myNewTreePath already copied without parent project and self
} else {
// project not yet selected, select self and a child configuration(default or next valid)
TreePath self = getTreePathForElement(element);
// so children are associated
expandToLevel(self, 1);
// I don't know why it didn't repaint proper without this
collapseToLevel(self, 1);
IProject project = (IProject) element;
Object[] children = this.getRawChildren(project);
ICarbideProjectInfo cpi = CarbideBuilderPlugin.getBuildManager().getProjectInfo(project);
ICarbideBuildConfiguration configDefault = cpi.getDefaultConfiguration();
ICarbideBuildConfiguration configDefaultOrFirstGood = null;
// enabled default or first enabled child
for (Object child : children) {
if (itemMap.get(getItemMapKey(child)) == null) {
continue;
}
if (itemMap.get(getItemMapKey(child)).getEnabled()) {
if (configDefaultOrFirstGood == null ||
((ICarbideBuildConfiguration)child).getDisplayString().equals(configDefault.getDisplayString())) {
configDefaultOrFirstGood = (ICarbideBuildConfiguration)child;
}
}
}
if (configDefaultOrFirstGood != null) {
// myNewTreePath already copied without self and children
if (self != null) {
myNewTreePath.add(self); // add self back
}
TreePath child = getTreePathForElement(configDefaultOrFirstGood);
// myNewTreePath already copied without self and children
if (child != null) {
myNewTreePath.add(child); // add child back
}
}
}
} else {
myNewTreePath = new ArrayList<TreePath> ();
for (TreePath path : selectedPaths) {
myNewTreePath.add(path);
}
}
// fire event
//updateSelection(getSelection());
// select from viewer, viewer will fire event and eventually change UI with our
// overrode routine
ITreeSelection treeSelection = new TreeSelection(myNewTreePath.toArray(new TreePath[myNewTreePath.size()]));
setSelection(treeSelection, true);
}
public boolean getElementEnabled(Object element) {
PkgListBaseCustomItem item = itemMap.get(getItemMapKey(element));
if (item != null) {
return item.getEnabled();
}
return false;
}
private Object getItemMapKey(Object element) {
// because ICarbideBuildConfiguration says keys are equal even when their projects
// do not match, use the project concatenated with configuration as the key for ICarbideBuildConfiguration
if (element instanceof ICarbideBuildConfiguration) {
ICarbideBuildConfiguration config = (ICarbideBuildConfiguration) element;
return config.getCarbideProject().getProject() + "/" + config.getDisplayString();
} else {
return element;
}
}
}