/******************************************************************************* * Copyright (c) 2005 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 *******************************************************************************/ package org.eclipse.ui.dialogs; import java.util.ArrayList; 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.Item; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.Widget; /** * CheckboxTreeViewer with special behaviour of the checked / gray state on * container (non-leaf) nodes: * The grayed state is used to visualize the checked state of its children. * Containers are checked and non-gray if all contained leafs are checked. The * container is grayed if some but not all leafs are checked. * @since 3.1 */ public class ContainerCheckedTreeViewer extends CheckboxTreeViewer { /** * Constructor for ContainerCheckedTreeViewer. * @see CheckboxTreeViewer#CheckboxTreeViewer(Composite) */ public ContainerCheckedTreeViewer(Composite parent) { super(parent); initViewer(); } /** * Constructor for ContainerCheckedTreeViewer. * @see CheckboxTreeViewer#CheckboxTreeViewer(Composite,int) */ public ContainerCheckedTreeViewer(Composite parent, int style) { super(parent, style); initViewer(); } /** * Constructor for ContainerCheckedTreeViewer. * @see CheckboxTreeViewer#CheckboxTreeViewer(Tree) */ public ContainerCheckedTreeViewer(Tree tree) { super(tree); initViewer(); } private void initViewer() { setUseHashlookup(true); addCheckStateListener(new ICheckStateListener() { public void checkStateChanged(CheckStateChangedEvent event) { doCheckStateChanged(event.getElement()); } }); 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); } } }); } /** * Update element after a checkstate change. * @param element */ protected void doCheckStateChanged(Object element) { Widget item = findItem(element); if (item instanceof TreeItem) { TreeItem treeItem = (TreeItem) item; treeItem.setGrayed(false); updateChildrenItems(treeItem); updateParentItems(treeItem.getParentItem()); } } /** * The item has expanded. Updates the checked state of its children. */ private void initializeItem(TreeItem item) { if (item.getChecked() && !item.getGrayed()) { updateChildrenItems(item); } } /** * Updates the check state of all created children */ private 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); updateChildrenItems(curr); } } } /** * Updates the check / gray state of all parent items */ private 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); updateParentItems(item.getParentItem()); } } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ICheckable#setChecked(java.lang.Object, boolean) */ public boolean setChecked(Object element, boolean state) { if (super.setChecked(element, state)) { doCheckStateChanged(element); return true; } return false; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.CheckboxTreeViewer#setCheckedElements(java.lang.Object[]) */ public void setCheckedElements(Object[] elements) { super.setCheckedElements(elements); for (int i = 0; i < elements.length; i++) { doCheckStateChanged(elements[i]); } } /* (non-Javadoc) * @see org.eclipse.jface.viewers.AbstractTreeViewer#setExpanded(org.eclipse.swt.widgets.Item, boolean) */ protected void setExpanded(Item item, boolean expand) { super.setExpanded(item, expand); if (expand && item instanceof TreeItem) { initializeItem((TreeItem) item); } } /* (non-Javadoc) * @see org.eclipse.jface.viewers.CheckboxTreeViewer#getCheckedElements() */ public Object[] getCheckedElements() { Object[] checked = super.getCheckedElements(); // add all items that are children of a checked node but not created yet ArrayList result = new ArrayList(); for (int i = 0; i < checked.length; i++) { Object curr = checked[i]; result.add(curr); Widget item = findItem(curr); if (item != null) { Item[] children = getChildren(item); // check if contains the dummy node if (children.length == 1 && children[0].getData() == null) { // not yet created collectChildren(curr, result); } } } return result.toArray(); } /** * Recursively add the filtered children of element to the result. * @param element * @param result */ private void collectChildren(Object element, ArrayList result) { Object[] filteredChildren = getFilteredChildren(element); for (int i = 0; i < filteredChildren.length; i++) { Object curr = filteredChildren[i]; result.add(curr); collectChildren(curr, result); } } }