blob: 945a6279c9cbc0524f2ad6e191506790a5fee8c0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2009 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 implementation
*******************************************************************************/
package org.eclipse.epf.authoring.ui.views;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.epf.authoring.ui.providers.ConfigPackageContentProvider;
import org.eclipse.epf.library.configuration.ConfigurationData;
import org.eclipse.epf.library.edit.navigator.ConfigContentPackageItemProvider.LeafElementsItemProvider;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.uma.ContentPackage;
import org.eclipse.epf.uma.CustomCategory;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.MethodPackage;
import org.eclipse.epf.uma.MethodPlugin;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.dialogs.ContainerCheckedTreeViewer;
/**
* ContainerCheckedTreeViewer class that supports MethodElements.
* Specifically, has changes to support FeatureValueWrapperItemProviders.
*
* Also, expands the tree for checked items when they are set via
* setCheckedElements(..) iff expandWhenChecking is true.
* @author Jeff Hardy
* @author Phong Nguyen Le
*/
public class MethodContainerCheckedTreeViewer extends
ContainerCheckedTreeViewer {
protected boolean expandWhenChecking = false;
protected boolean initializingTreeCheckState = false;
private ConfigurationData configData;
private boolean disableHandleLeafCrayChecks = false;
protected ListenerList childrenCheckStateListeners = new ListenerList();
/**
* Constructor for MethodContainerCheckedTreeViewer.
* @see CheckboxTreeViewer#CheckboxTreeViewer(Composite)
*/
public MethodContainerCheckedTreeViewer(Composite parent) {
super(parent);
initViewer();
}
/**
* Constructor for MethodContainerCheckedTreeViewer.
* @see CheckboxTreeViewer#CheckboxTreeViewer(Composite,int)
*/
public MethodContainerCheckedTreeViewer(Composite parent, int style) {
super(parent, style);
initViewer();
}
/**
* Constructor for MethodContainerCheckedTreeViewer.
* @see CheckboxTreeViewer#CheckboxTreeViewer(Tree)
*/
public MethodContainerCheckedTreeViewer(Tree tree) {
super(tree);
initViewer();
}
protected void initViewer() {
setUseHashlookup(true);
addCheckStateListener(new ICheckStateListener() {
public void checkStateChanged(CheckStateChangedEvent event) {
updateWrappers(event.getElement());
doCheckStateChanged(event.getElement());
// not ready to call the following line yet
// handleLeafCrayChecks(element);
}
});
addTreeListener(new ITreeViewerListener() {
public void treeCollapsed(TreeExpansionEvent event) {
}
public void treeExpanded(TreeExpansionEvent event) {
Widget item = findItem(event.getElement());
if (item instanceof TreeItem) {
initializeItem((TreeItem) item);
}
}
});
}
/**
* The item has expanded. Updates the checked state of its children.
*/
protected void initializeItem(TreeItem item) {
if (item.getChecked() && !item.getGrayed()) {
updateChildrenItems(item);
}
}
/**
* Updates the check state of all created children
*/
protected void updateChildrenItems(TreeItem parent) {
Item[] children = getChildren(parent);
boolean state = parent.getChecked();
for (int i = 0; i < children.length; i++) {
TreeItem curr = (TreeItem) children[i];
if (curr.getData() != null
&& ((curr.getChecked() != state) || curr.getGrayed())) {
curr.setChecked(state);
curr.setGrayed(false);
notifyChildrenCheckStateListeners(new CheckStateChangedEvent(this, curr.getData(),
state));
updateChildrenItems(curr);
}
}
}
@Override
protected void doCheckStateChanged(Object element) {
if (initializingTreeCheckState) {
// when initializing, don't update children:
// parent may have been gray-checked and we don't
// want to white-check all children
// children will be checked because setCheckedElements(..)
// typically receives list of all checked elements
Widget item = findItem(element);
if (item instanceof TreeItem) {
TreeItem treeItem = (TreeItem) item;
treeItem.setGrayed(false);
updateParentItems(treeItem.getParentItem());
}
} else {
super.doCheckStateChanged(element);
}
}
private void handleLeafCrayChecks(Object element) {
if (getConfigData() == null || disableHandleLeafCrayChecks) {
return;
}
Widget item = findItem(element);
TreeItem treeItem = item instanceof TreeItem ? (TreeItem) item : null;
if (treeItem == null) {
return;
}
Set<LeafElementsItemProvider> uncheckedLeafs = new HashSet<LeafElementsItemProvider>();
collectUncheckedLeafs(treeItem, uncheckedLeafs);
handleLeafCrayChecks(uncheckedLeafs);
}
private void collectUncheckedLeafs(TreeItem treeItem, Set<LeafElementsItemProvider> uncheckedLeafs) {
if (treeItem.getData() instanceof LeafElementsItemProvider) {
if (! treeItem.getChecked()) {
uncheckedLeafs.add((LeafElementsItemProvider) treeItem.getData());
}
return;
}
Item[] children = getChildren(treeItem);
for (int i = 0; i < children.length; i++) {
TreeItem curr = (TreeItem) children[i];
collectUncheckedLeafs(curr, uncheckedLeafs);
}
}
public void handleLeafCrayChecks(
Collection<LeafElementsItemProvider> uncheckedLeafs) {
if (uncheckedLeafs == null || uncheckedLeafs.isEmpty()) {
return;
}
Set<TreeItem> grayCheckedSet = new HashSet<TreeItem>();
for (LeafElementsItemProvider leaf : uncheckedLeafs) {
MethodPackage leafParent = leaf.getParentPackage();
if (!getConfigData().hasAddedElements(leafParent)) {
continue;
}
Widget item = findItem(leaf);
if (item instanceof TreeItem) {
TreeItem treeItem = (TreeItem) item;
while (treeItem != null) {
if (!grayCheckedSet.contains(treeItem)) {
treeItem.setChecked(true);
treeItem.setGrayed(true);
grayCheckedSet.add(treeItem);
}
treeItem = treeItem.getParentItem();
}
}
}
}
public void updateParents(Object element) {
Widget item = findItem(element);
if (item instanceof TreeItem) {
TreeItem treeItem = (TreeItem) item;
updateParentItems(treeItem.getParentItem());
}
}
/**
* Updates the check / gray state of all parent items
*/
protected void updateParentItems(TreeItem item) {
if (item != null) {
Item[] children = getChildren(item);
boolean containsChecked = false;
boolean containsUnchecked = false;
for (int i = 0; i < children.length; i++) {
TreeItem curr = (TreeItem) children[i];
containsChecked |= curr.getChecked();
containsUnchecked |= (!curr.getChecked() || curr.getGrayed());
}
item.setChecked(containsChecked);
item.setGrayed(containsChecked && containsUnchecked);
if (expandWhenChecking && item.getChecked()) {
item.setExpanded(true);
}
updateParentItems(item.getParentItem());
}
}
private void updateWrappers(Object object) {
boolean state = getChecked(object);
Object element = TngUtil.unwrap(object);
if(object != element) {
setChecked(element, state);
}
AdapterFactory adapterFactory = null;
if (getContentProvider() instanceof AdapterFactoryContentProvider) {
adapterFactory = ((AdapterFactoryContentProvider) getContentProvider())
.getAdapterFactory();
}
if (adapterFactory != null) {
Collection<?> wrappers = TngUtil.getWrappers(adapterFactory,
element);
if (!wrappers.isEmpty()) {
for (Object wrapper : wrappers) {
setChecked(wrapper, state);
}
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.CheckboxTreeViewer#setCheckedElements(java.lang.Object[])
*/
public void setCheckedElements(Object[] elements) {
// fix elements list to use EPF's wrappers
List<Object> treeElements = new ArrayList<Object>();
AdapterFactory adapterFactory = null;
if (getContentProvider() instanceof AdapterFactoryContentProvider) {
adapterFactory = ((AdapterFactoryContentProvider)getContentProvider()).getAdapterFactory();
}
for (int i = 0; i < elements.length; i++) {
Object element = elements[i];
if (adapterFactory != null) {
treeElements.addAll(TngUtil.getWrappers(adapterFactory, element));
}
if(!(element instanceof CustomCategory)) {
treeElements.add(element);
}
}
initializingTreeCheckState = true;
super.setCheckedElements(treeElements.toArray());
initializingTreeCheckState = false;
}
/**
* Returns a list of top-most checked elements in this viewer's tree,
* including currently hidden ones that are marked as
* checked but are under a collapsed ancestor.
*
* Only returns white-checked items, not grayed items
*
* @return the array of checked elements
*
* @see #setCheckedElements
*/
public Object[] getTopCheckedElements() {
ArrayList v = new ArrayList();
Control tree = getControl();
internalCollectTopChecked(v, tree);
return v.toArray();
}
/**
* Gathers the top-most checked states of the given widget and its
* descendents, following a pre-order traversal of the tree.
*
* @param result a writable list of elements (element type: <code>Object</code>)
* @param widget the widget
*/
private void internalCollectTopChecked(List result, Widget widget) {
Item[] items = getChildren(widget);
for (int i = 0; i < items.length; i++) {
Item item = items[i];
if (item instanceof TreeItem && ((TreeItem) item).getChecked() && !((TreeItem) item).getGrayed()) {
Object data = item.getData();
if (data != null) {
result.add(data);
}
}
if (item instanceof TreeItem && (!((TreeItem) item).getChecked() || ((TreeItem) item).getGrayed())) {
internalCollectTopChecked(result, item);
}
}
}
public boolean isExpandWhenChecking() {
return expandWhenChecking;
}
public void setExpandWhenChecking(boolean expandWhenChecking) {
this.expandWhenChecking = expandWhenChecking;
}
/**
* notified when children checkstate is changed (by checking a parent)
* @param listener
*/
public void addChildrenCheckStateListener(ICheckStateListener listener) {
childrenCheckStateListeners.add(listener);
}
public void removeChildrenCheckStateListener(ICheckStateListener listener) {
childrenCheckStateListeners.remove(listener);
}
protected void notifyChildrenCheckStateListeners(final CheckStateChangedEvent event) {
for (final Object listener : childrenCheckStateListeners.getListeners()) {
SafeRunner.run(new SafeRunnable() {
public void run() {
((ICheckStateListener)listener).checkStateChanged(event);
}
});
}
}
public Set<MethodPackage> getElementsUnslectedPkgs(Collection<MethodPackage> pkgsInConfig, ConfigPackageContentProvider provider) {
Set<MethodPackage> elementsUnslectedPkgs = new HashSet<MethodPackage>();
for (MethodPackage pkg : pkgsInConfig) {
if (pkg instanceof ContentPackage) {
Object leaf = provider.getLeafElementsNode(pkg);
Widget item = findItem(leaf);
if (item instanceof TreeItem) {
TreeItem treeItem = (TreeItem) item;
if (! treeItem.getChecked() || treeItem.getGrayed()) {
elementsUnslectedPkgs.add((ContentPackage) pkg);
}
}
}
}
return elementsUnslectedPkgs;
}
public void debugDump(Collection items, String msg) {
System.out.println("LD> msg : " + msg); //$NON-NLS-1$
for (Object item : items) {
System.out.println("LD> item : " + item); //$NON-NLS-1$
System.out.println("LD> found: " + findItem(item)); //$NON-NLS-1$
}
System.out.println(""); //$NON-NLS-1$
}
public ConfigurationData getConfigData() {
return configData;
}
public void setConfigData(ConfigurationData configData) {
this.configData = configData;
}
@Override
public boolean setChecked(Object element, boolean state) {
try {
disableHandleLeafCrayChecks = true;
return super.setChecked(element, state);
} finally {
disableHandleLeafCrayChecks = false;
}
}
public void debugTree() {
Object root = getRoot();
Tree tree = (Tree) this.findItem(root);
debugItem(tree.getItem(0), "LD>"); //$NON-NLS-1$
}
private void debugItem(Object objItem, String indent) {
if (objItem instanceof TreeItem) {
TreeItem item = (TreeItem) objItem;
System.out.println(indent + "item : " + item + ", hash: " //$NON-NLS-1$ //$NON-NLS-2$
+ item.hashCode());
System.out.println(indent + "data : " + item.getData()); //$NON-NLS-1$
System.out.println(""); //$NON-NLS-1$
for (TreeItem cItem : item.getItems()) {
debugItem(cItem, indent + " "); //$NON-NLS-1$
}
} else {
System.out.println(indent + "objItem : " + objItem); //$NON-NLS-1$
System.out.println(""); //$NON-NLS-1$
}
}
}