/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.ui.common.widget; import java.util.Iterator; import java.util.List; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.swt.widgets.Tree; import org.teiid.designer.ui.common.tree.TreeViewerUtil; import org.teiid.designer.ui.common.viewsupport.UiBusyIndicator; /** * Extension to CheckboxTreeViewer in which: A checkmark at a node automatically places a checkmark at all descendant nodes. * Clearing a checkmark at a node automatically clears a checkmark at all ancestor nodes. The case where a node has no checkmark * but at least one descendant node has a checkmark is handled by the 'style' parameter. * * @since 8.0 */ public class InheritanceCheckboxTreeViewer extends CheckboxTreeViewer implements ICheckStateListener { // Values for uncheckedNodeWithCheckedDescendantsStyle: public final static int UNCHECKED_WHITE = 1; public final static int UNCHECKED_GRAYED = 2; public final static int CHECKED_WHITE_IF_ALL_DESCENDANTS_CHECKED_ELSE_UNCHECKED_GRAYED = 3; public final static int CHECKED_WHITE_IF_ALL_DESCENDANTS_CHECKED_ELSE_CHECKED_GRAYED_IF_ANY_CHECKED = 4; int uncheckedNodeWithCheckedDescendantsStyle; private boolean listenerEnabled = true; private INodeDescendantsDeselectionHandler deselectionHandler; private ICheckableController checkController; public InheritanceCheckboxTreeViewer( Tree tree, int style, INodeDescendantsDeselectionHandler deselectionHandler ) { super(tree); this.uncheckedNodeWithCheckedDescendantsStyle = style; this.deselectionHandler = deselectionHandler; this.addCheckStateListener(this); } @Override public Object getRoot() { return super.getRoot(); } public void setListenerEnabled( boolean state ) { this.listenerEnabled = state; } public void setCheckableController( ICheckableController theController ) { this.checkController = theController; } @Override public void checkStateChanged( CheckStateChangedEvent event ) { // undo the event if needed if ((this.checkController != null) && !this.checkController.isEditable(event.getElement())) { setChecked(event.getElement(), !event.getChecked()); } else if (this.listenerEnabled) { // just in case, don't re-enter this while working: listenerEnabled = false; // Have to take care of changing the checked state and/or shading of ancestor or // descendant nodes. Since this can take more than a second, we will use a // wait-cursor. final InheritanceCheckboxTreeViewer viewer = this; final ITreeContentProvider contentProvider = (ITreeContentProvider)viewer.getContentProvider(); final boolean isChecked = event.getChecked(); final Object node = event.getElement(); boolean deselecting = false; if (!isChecked) { if (contentProvider != null) { boolean hasDescendant = contentProvider.hasChildren(node); if (hasDescendant) { deselecting = deselectionHandler.deselectDescendants(node); } } } final boolean deselectingDescendants = deselecting; Runnable runnable = new Runnable() { @Override public void run() { if (isChecked) { viewer.setGrayed(node, false); // Set all descendant nodes to checked: List /*<Object>*/descendants = TreeViewerUtil.getDescendantsOfNode(viewer, node, false); Iterator it = descendants.iterator(); while (it.hasNext()) { Object curNode = it.next(); viewer.setChecked(curNode, true); viewer.setGrayed(curNode, false); } // the following is much faster than the above, but will not create // children for nodes that have not yet been expanded. Other code // would have to be more intelligent in processing the tree checked // status for this to work. if (viewer.uncheckedNodeWithCheckedDescendantsStyle != InheritanceCheckboxTreeViewer.UNCHECKED_WHITE) { // Change ancestor nodes to grayed or checked if (contentProvider != null) { Object parent = contentProvider.getParent(node); switch (viewer.uncheckedNodeWithCheckedDescendantsStyle) { case InheritanceCheckboxTreeViewer.UNCHECKED_GRAYED: while (parent != null) { viewer.setGrayed(parent, true); parent = contentProvider.getParent(parent); } break; case InheritanceCheckboxTreeViewer.CHECKED_WHITE_IF_ALL_DESCENDANTS_CHECKED_ELSE_UNCHECKED_GRAYED: boolean allDescendantsChecked = true; while (parent != null) { // if all descendents of last (lower) node were checked if (allDescendantsChecked) { // determine if all descendents of the current ('parent') node are checked allDescendantsChecked = TreeViewerUtil.allDescendantsChecked(viewer, parent); } // set the state of the current ('parent') node if (allDescendantsChecked) { viewer.setChecked(parent, true); viewer.setGrayed(parent, false); } else { viewer.setGrayed(parent, true); } // ascend to my parent and repeat the process parent = contentProvider.getParent(parent); } break; case InheritanceCheckboxTreeViewer.CHECKED_WHITE_IF_ALL_DESCENDANTS_CHECKED_ELSE_CHECKED_GRAYED_IF_ANY_CHECKED: boolean anyDescendantsChecked2 = false; boolean allDescendantsChecked2 = true; // loop backwards through ancestors while (parent != null) { anyDescendantsChecked2 = TreeViewerUtil.anyDescendantChecked(viewer, parent); // if all descendents of last (lower) node were checked if (allDescendantsChecked2) { // determine if all descendents of the current ('parent') node are checked allDescendantsChecked2 = TreeViewerUtil.allDescendantsChecked(viewer, parent); } // set the state of the current ('parent') node // All descendents selected if (allDescendantsChecked2) { viewer.setChecked(parent, true); viewer.setGrayed(parent, false); } else // Some descendents selected if (anyDescendantsChecked2) { viewer.setChecked(parent, true); viewer.setGrayed(parent, true); } // No descendents selected else { viewer.setGrayed(parent, true); } // ascend to my parent and repeat the process parent = contentProvider.getParent(parent); } break; default: break; } } } } else { // Unchecked if (contentProvider != null) { if (deselectingDescendants) { List /*<Object>*/descendants = TreeViewerUtil.getDescendantsOfNode(viewer, node, true); Iterator it = descendants.iterator(); while (it.hasNext()) { Object curNode = it.next(); viewer.setChecked(curNode, false); viewer.setGrayed(curNode, false); } // the following is much faster than the above, but will not create // children for nodes that have not yet been expanded. Other code // would have to be more intelligent in processing the tree checked // status for this to work. } // Set ancestor nodes to grayed as appropriate boolean anyDescendantChecked = false; // For implementation ease, start parent out as the node itself, will reset // to unchecked but this does not matter Object parent = node; while (parent != null) { viewer.setChecked(parent, false); if (viewer.uncheckedNodeWithCheckedDescendantsStyle != InheritanceCheckboxTreeViewer.UNCHECKED_WHITE) { if (!anyDescendantChecked) { anyDescendantChecked = TreeViewerUtil.anyDescendantChecked(viewer, parent); } switch (viewer.uncheckedNodeWithCheckedDescendantsStyle) { case InheritanceCheckboxTreeViewer.UNCHECKED_GRAYED: viewer.setGrayed(parent, anyDescendantChecked); viewer.setChecked(parent, false); break; case InheritanceCheckboxTreeViewer.CHECKED_WHITE_IF_ALL_DESCENDANTS_CHECKED_ELSE_UNCHECKED_GRAYED: viewer.setGrayed(parent, anyDescendantChecked); viewer.setChecked(parent, false); break; case InheritanceCheckboxTreeViewer.CHECKED_WHITE_IF_ALL_DESCENDANTS_CHECKED_ELSE_CHECKED_GRAYED_IF_ANY_CHECKED: viewer.setGrayed(parent, anyDescendantChecked); viewer.setChecked(parent, anyDescendantChecked); break; default: break; } } parent = contentProvider.getParent(parent); } } } } }; UiBusyIndicator.showWhile(null, runnable); // restore enabled state: listenerEnabled = true; } } }