001: /*******************************************************************************
002: * Copyright (c) 2005 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.dialogs;
011:
012: import java.util.ArrayList;
013:
014: import org.eclipse.jface.viewers.CheckStateChangedEvent;
015: import org.eclipse.jface.viewers.CheckboxTreeViewer;
016: import org.eclipse.jface.viewers.ICheckStateListener;
017: import org.eclipse.jface.viewers.ITreeViewerListener;
018: import org.eclipse.jface.viewers.TreeExpansionEvent;
019: import org.eclipse.swt.widgets.Composite;
020: import org.eclipse.swt.widgets.Item;
021: import org.eclipse.swt.widgets.Tree;
022: import org.eclipse.swt.widgets.TreeItem;
023: import org.eclipse.swt.widgets.Widget;
024:
025: /**
026: * CheckboxTreeViewer with special behaviour of the checked / gray state on
027: * container (non-leaf) nodes:
028: * The grayed state is used to visualize the checked state of its children.
029: * Containers are checked and non-gray if all contained leafs are checked. The
030: * container is grayed if some but not all leafs are checked.
031: * @since 3.1
032: */
033: public class ContainerCheckedTreeViewer extends CheckboxTreeViewer {
034:
035: /**
036: * Constructor for ContainerCheckedTreeViewer.
037: * @see CheckboxTreeViewer#CheckboxTreeViewer(Composite)
038: */
039: public ContainerCheckedTreeViewer(Composite parent) {
040: super (parent);
041: initViewer();
042: }
043:
044: /**
045: * Constructor for ContainerCheckedTreeViewer.
046: * @see CheckboxTreeViewer#CheckboxTreeViewer(Composite,int)
047: */
048: public ContainerCheckedTreeViewer(Composite parent, int style) {
049: super (parent, style);
050: initViewer();
051: }
052:
053: /**
054: * Constructor for ContainerCheckedTreeViewer.
055: * @see CheckboxTreeViewer#CheckboxTreeViewer(Tree)
056: */
057: public ContainerCheckedTreeViewer(Tree tree) {
058: super (tree);
059: initViewer();
060: }
061:
062: private void initViewer() {
063: setUseHashlookup(true);
064: addCheckStateListener(new ICheckStateListener() {
065: public void checkStateChanged(CheckStateChangedEvent event) {
066: doCheckStateChanged(event.getElement());
067: }
068: });
069: addTreeListener(new ITreeViewerListener() {
070: public void treeCollapsed(TreeExpansionEvent event) {
071: }
072:
073: public void treeExpanded(TreeExpansionEvent event) {
074: Widget item = findItem(event.getElement());
075: if (item instanceof TreeItem) {
076: initializeItem((TreeItem) item);
077: }
078: }
079: });
080: }
081:
082: /**
083: * Update element after a checkstate change.
084: * @param element
085: */
086: protected void doCheckStateChanged(Object element) {
087: Widget item = findItem(element);
088: if (item instanceof TreeItem) {
089: TreeItem treeItem = (TreeItem) item;
090: treeItem.setGrayed(false);
091: updateChildrenItems(treeItem);
092: updateParentItems(treeItem.getParentItem());
093: }
094: }
095:
096: /**
097: * The item has expanded. Updates the checked state of its children.
098: */
099: private void initializeItem(TreeItem item) {
100: if (item.getChecked() && !item.getGrayed()) {
101: updateChildrenItems(item);
102: }
103: }
104:
105: /**
106: * Updates the check state of all created children
107: */
108: private void updateChildrenItems(TreeItem parent) {
109: Item[] children = getChildren(parent);
110: boolean state = parent.getChecked();
111: for (int i = 0; i < children.length; i++) {
112: TreeItem curr = (TreeItem) children[i];
113: if (curr.getData() != null
114: && ((curr.getChecked() != state) || curr
115: .getGrayed())) {
116: curr.setChecked(state);
117: curr.setGrayed(false);
118: updateChildrenItems(curr);
119: }
120: }
121: }
122:
123: /**
124: * Updates the check / gray state of all parent items
125: */
126: private void updateParentItems(TreeItem item) {
127: if (item != null) {
128: Item[] children = getChildren(item);
129: boolean containsChecked = false;
130: boolean containsUnchecked = false;
131: for (int i = 0; i < children.length; i++) {
132: TreeItem curr = (TreeItem) children[i];
133: containsChecked |= curr.getChecked();
134: containsUnchecked |= (!curr.getChecked() || curr
135: .getGrayed());
136: }
137: item.setChecked(containsChecked);
138: item.setGrayed(containsChecked && containsUnchecked);
139: updateParentItems(item.getParentItem());
140: }
141: }
142:
143: /* (non-Javadoc)
144: * @see org.eclipse.jface.viewers.ICheckable#setChecked(java.lang.Object, boolean)
145: */
146: public boolean setChecked(Object element, boolean state) {
147: if (super .setChecked(element, state)) {
148: doCheckStateChanged(element);
149: return true;
150: }
151: return false;
152: }
153:
154: /* (non-Javadoc)
155: * @see org.eclipse.jface.viewers.CheckboxTreeViewer#setCheckedElements(java.lang.Object[])
156: */
157: public void setCheckedElements(Object[] elements) {
158: super .setCheckedElements(elements);
159: for (int i = 0; i < elements.length; i++) {
160: doCheckStateChanged(elements[i]);
161: }
162: }
163:
164: /* (non-Javadoc)
165: * @see org.eclipse.jface.viewers.AbstractTreeViewer#setExpanded(org.eclipse.swt.widgets.Item, boolean)
166: */
167: protected void setExpanded(Item item, boolean expand) {
168: super .setExpanded(item, expand);
169: if (expand && item instanceof TreeItem) {
170: initializeItem((TreeItem) item);
171: }
172: }
173:
174: /* (non-Javadoc)
175: * @see org.eclipse.jface.viewers.CheckboxTreeViewer#getCheckedElements()
176: */
177: public Object[] getCheckedElements() {
178: Object[] checked = super .getCheckedElements();
179: // add all items that are children of a checked node but not created yet
180: ArrayList result = new ArrayList();
181: for (int i = 0; i < checked.length; i++) {
182: Object curr = checked[i];
183: result.add(curr);
184: Widget item = findItem(curr);
185: if (item != null) {
186: Item[] children = getChildren(item);
187: // check if contains the dummy node
188: if (children.length == 1
189: && children[0].getData() == null) {
190: // not yet created
191: collectChildren(curr, result);
192: }
193: }
194: }
195: return result.toArray();
196: }
197:
198: /**
199: * Recursively add the filtered children of element to the result.
200: * @param element
201: * @param result
202: */
203: private void collectChildren(Object element, ArrayList result) {
204: Object[] filteredChildren = getFilteredChildren(element);
205: for (int i = 0; i < filteredChildren.length; i++) {
206: Object curr = filteredChildren[i];
207: result.add(curr);
208: collectChildren(curr, result);
209: }
210: }
211:
212: }
|