0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2006 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.ui.internal.ide.dialogs;
0011:
0012: import java.util.ArrayList;
0013: import java.util.Collection;
0014: import java.util.HashMap;
0015: import java.util.HashSet;
0016: import java.util.Iterator;
0017: import java.util.List;
0018: import java.util.Map;
0019: import java.util.Set;
0020:
0021: import org.eclipse.core.commands.common.EventManager;
0022: import org.eclipse.core.runtime.IPath;
0023: import org.eclipse.core.runtime.IProgressMonitor;
0024: import org.eclipse.core.runtime.Path;
0025: import org.eclipse.core.runtime.SafeRunner;
0026: import org.eclipse.jface.util.SafeRunnable;
0027: import org.eclipse.jface.viewers.CheckStateChangedEvent;
0028: import org.eclipse.jface.viewers.CheckboxTableViewer;
0029: import org.eclipse.jface.viewers.CheckboxTreeViewer;
0030: import org.eclipse.jface.viewers.ICheckStateListener;
0031: import org.eclipse.jface.viewers.ILabelProvider;
0032: import org.eclipse.jface.viewers.ISelectionChangedListener;
0033: import org.eclipse.jface.viewers.IStructuredContentProvider;
0034: import org.eclipse.jface.viewers.IStructuredSelection;
0035: import org.eclipse.jface.viewers.ITreeContentProvider;
0036: import org.eclipse.jface.viewers.ITreeViewerListener;
0037: import org.eclipse.jface.viewers.SelectionChangedEvent;
0038: import org.eclipse.jface.viewers.StructuredSelection;
0039: import org.eclipse.jface.viewers.TreeExpansionEvent;
0040: import org.eclipse.jface.viewers.ViewerComparator;
0041: import org.eclipse.swt.SWT;
0042: import org.eclipse.swt.custom.BusyIndicator;
0043: import org.eclipse.swt.layout.GridData;
0044: import org.eclipse.swt.layout.GridLayout;
0045: import org.eclipse.swt.widgets.Composite;
0046: import org.eclipse.swt.widgets.Table;
0047: import org.eclipse.swt.widgets.Tree;
0048:
0049: /**
0050: * Workbench-level composite that combines a CheckboxTreeViewer and CheckboxListViewer.
0051: * All viewer selection-driven interactions are handled within this object
0052: */
0053: public class ResourceTreeAndListGroup extends EventManager implements
0054: ICheckStateListener, ISelectionChangedListener,
0055: ITreeViewerListener {
0056: private Object root;
0057:
0058: private Object currentTreeSelection;
0059:
0060: private Collection expandedTreeNodes = new HashSet();
0061:
0062: private Map checkedStateStore = new HashMap(9);
0063:
0064: private Collection whiteCheckedTreeItems = new HashSet();
0065:
0066: private ITreeContentProvider treeContentProvider;
0067:
0068: private IStructuredContentProvider listContentProvider;
0069:
0070: private ILabelProvider treeLabelProvider;
0071:
0072: private ILabelProvider listLabelProvider;
0073:
0074: // widgets
0075: private CheckboxTreeViewer treeViewer;
0076:
0077: private CheckboxTableViewer listViewer;
0078:
0079: //height hint for viewers
0080: private static int PREFERRED_HEIGHT = 150;
0081:
0082: /**
0083: * Create an instance of this class. Use this constructor if you wish to specify
0084: * the width and/or height of the combined widget (to only hardcode one of the
0085: * sizing dimensions, specify the other dimension's value as -1)
0086: *
0087: * @param parent
0088: * @param rootObject
0089: * @param treeContentProvider
0090: * @param treeLabelProvider
0091: * @param listContentProvider
0092: * @param listLabelProvider
0093: * @param style
0094: * @param useHeightHint If true then use the height hint
0095: * to make this group big enough
0096: *
0097: */
0098: public ResourceTreeAndListGroup(Composite parent,
0099: Object rootObject,
0100: ITreeContentProvider treeContentProvider,
0101: ILabelProvider treeLabelProvider,
0102: IStructuredContentProvider listContentProvider,
0103: ILabelProvider listLabelProvider, int style,
0104: boolean useHeightHint) {
0105:
0106: root = rootObject;
0107: this .treeContentProvider = treeContentProvider;
0108: this .listContentProvider = listContentProvider;
0109: this .treeLabelProvider = treeLabelProvider;
0110: this .listLabelProvider = listLabelProvider;
0111: createContents(parent, style, useHeightHint);
0112: }
0113:
0114: /**
0115: * This method must be called just before this window becomes visible.
0116: */
0117: public void aboutToOpen() {
0118: determineWhiteCheckedDescendents(root);
0119: checkNewTreeElements(treeContentProvider.getElements(root));
0120: currentTreeSelection = null;
0121:
0122: //select the first element in the list
0123: Object[] elements = treeContentProvider.getElements(root);
0124: Object primary = elements.length > 0 ? elements[0] : null;
0125: if (primary != null) {
0126: treeViewer.setSelection(new StructuredSelection(primary));
0127: }
0128: treeViewer.getControl().setFocus();
0129: }
0130:
0131: /**
0132: * Add the passed listener to self's collection of clients
0133: * that listen for changes to element checked states
0134: *
0135: * @param listener ICheckStateListener
0136: */
0137: public void addCheckStateListener(ICheckStateListener listener) {
0138: addListenerObject(listener);
0139: }
0140:
0141: /**
0142: * Return a boolean indicating whether all children of the passed tree element
0143: * are currently white-checked
0144: *
0145: * @return boolean
0146: * @param treeElement java.lang.Object
0147: */
0148: protected boolean areAllChildrenWhiteChecked(Object treeElement) {
0149: Object[] children = treeContentProvider
0150: .getChildren(treeElement);
0151: for (int i = 0; i < children.length; ++i) {
0152: if (!whiteCheckedTreeItems.contains(children[i])) {
0153: return false;
0154: }
0155: }
0156:
0157: return true;
0158: }
0159:
0160: /**
0161: * Return a boolean indicating whether all list elements associated with
0162: * the passed tree element are currently checked
0163: *
0164: * @return boolean
0165: * @param treeElement java.lang.Object
0166: */
0167: protected boolean areAllElementsChecked(Object treeElement) {
0168: List checkedElements = (List) checkedStateStore
0169: .get(treeElement);
0170: if (checkedElements == null) {
0171: return false;
0172: }
0173:
0174: return getListItemsSize(treeElement) == checkedElements.size();
0175: }
0176:
0177: /**
0178: * Iterate through the passed elements which are being realized for the first
0179: * time and check each one in the tree viewer as appropriate
0180: */
0181: protected void checkNewTreeElements(Object[] elements) {
0182: for (int i = 0; i < elements.length; ++i) {
0183: Object currentElement = elements[i];
0184: boolean checked = checkedStateStore
0185: .containsKey(currentElement);
0186: treeViewer.setChecked(currentElement, checked);
0187: treeViewer.setGrayed(currentElement, checked
0188: && !whiteCheckedTreeItems.contains(currentElement));
0189: }
0190: }
0191:
0192: /**
0193: * An item was checked in one of self's two views. Determine which
0194: * view this occurred in and delegate appropriately
0195: *
0196: * @param event CheckStateChangedEvent
0197: */
0198: public void checkStateChanged(final CheckStateChangedEvent event) {
0199:
0200: //Potentially long operation - show a busy cursor
0201: BusyIndicator.showWhile(treeViewer.getControl().getDisplay(),
0202: new Runnable() {
0203: public void run() {
0204: if (event.getCheckable().equals(treeViewer)) {
0205: treeItemChecked(event.getElement(), event
0206: .getChecked());
0207: } else {
0208: listItemChecked(event.getElement(), event
0209: .getChecked(), true);
0210: }
0211:
0212: notifyCheckStateChangeListeners(event);
0213: }
0214: });
0215: }
0216:
0217: /**
0218: * Lay out and initialize self's visual components.
0219: *
0220: * @param parent org.eclipse.swt.widgets.Composite
0221: * @param style the style flags for the new Composite
0222: * @param useHeightHint If true yse the preferredHeight.
0223: */
0224: protected void createContents(Composite parent, int style,
0225: boolean useHeightHint) {
0226: // group pane
0227: Composite composite = new Composite(parent, style);
0228: composite.setFont(parent.getFont());
0229: GridLayout layout = new GridLayout();
0230: layout.numColumns = 2;
0231: layout.makeColumnsEqualWidth = true;
0232: layout.marginHeight = 0;
0233: layout.marginWidth = 0;
0234: composite.setLayout(layout);
0235: composite.setLayoutData(new GridData(GridData.FILL_BOTH));
0236:
0237: createTreeViewer(composite, useHeightHint);
0238: createListViewer(composite, useHeightHint);
0239:
0240: initialize();
0241: }
0242:
0243: /**
0244: * Create this group's list viewer.
0245: */
0246: protected void createListViewer(Composite parent,
0247: boolean useHeightHint) {
0248: listViewer = CheckboxTableViewer.newCheckList(parent,
0249: SWT.BORDER);
0250: GridData data = new GridData(GridData.FILL_BOTH);
0251: if (useHeightHint) {
0252: data.heightHint = PREFERRED_HEIGHT;
0253: }
0254: listViewer.getTable().setLayoutData(data);
0255: listViewer.getTable().setFont(parent.getFont());
0256: listViewer.setContentProvider(listContentProvider);
0257: listViewer.setLabelProvider(listLabelProvider);
0258: listViewer.addCheckStateListener(this );
0259: }
0260:
0261: /**
0262: * Create this group's tree viewer.
0263: */
0264: protected void createTreeViewer(Composite parent,
0265: boolean useHeightHint) {
0266: Tree tree = new Tree(parent, SWT.CHECK | SWT.BORDER);
0267: GridData data = new GridData(GridData.FILL_BOTH);
0268: if (useHeightHint) {
0269: data.heightHint = PREFERRED_HEIGHT;
0270: }
0271: tree.setLayoutData(data);
0272: tree.setFont(parent.getFont());
0273:
0274: treeViewer = new CheckboxTreeViewer(tree);
0275: treeViewer.setContentProvider(treeContentProvider);
0276: treeViewer.setLabelProvider(treeLabelProvider);
0277: treeViewer.addTreeListener(this );
0278: treeViewer.addCheckStateListener(this );
0279: treeViewer.addSelectionChangedListener(this );
0280: }
0281:
0282: /**
0283: * Returns a boolean indicating whether the passed tree element should be
0284: * at LEAST gray-checked. Note that this method does not consider whether
0285: * it should be white-checked, so a specified tree item which should be
0286: * white-checked will result in a <code>true</code> answer from this method.
0287: * To determine whether a tree item should be white-checked use method
0288: * #determineShouldBeWhiteChecked(Object).
0289: *
0290: * @param treeElement java.lang.Object
0291: * @return boolean
0292: * @see #determineShouldBeWhiteChecked(Object)
0293: */
0294: protected boolean determineShouldBeAtLeastGrayChecked(
0295: Object treeElement) {
0296: // if any list items associated with treeElement are checked then it
0297: // retains its gray-checked status regardless of its children
0298: List checked = (List) checkedStateStore.get(treeElement);
0299: if (checked != null && (!checked.isEmpty())) {
0300: return true;
0301: }
0302:
0303: // if any children of treeElement are still gray-checked then treeElement
0304: // must remain gray-checked as well. Only ask expanded nodes
0305: if (expandedTreeNodes.contains(treeElement)) {
0306: Object[] children = treeContentProvider
0307: .getChildren(treeElement);
0308: for (int i = 0; i < children.length; ++i) {
0309: if (checkedStateStore.containsKey(children[i])) {
0310: return true;
0311: }
0312: }
0313: }
0314:
0315: return false;
0316: }
0317:
0318: /**
0319: * Returns a boolean indicating whether the passed tree item should be
0320: * white-checked.
0321: *
0322: * @return boolean
0323: * @param treeElement java.lang.Object
0324: */
0325: protected boolean determineShouldBeWhiteChecked(Object treeElement) {
0326: return areAllChildrenWhiteChecked(treeElement)
0327: && areAllElementsChecked(treeElement);
0328: }
0329:
0330: /**
0331: * Recursively add appropriate tree elements to the collection of
0332: * known white-checked tree elements.
0333: *
0334: * @param treeElement java.lang.Object
0335: */
0336: protected void determineWhiteCheckedDescendents(Object treeElement) {
0337: // always go through all children first since their white-checked
0338: // statuses will be needed to determine the white-checked status for
0339: // this tree element
0340: Object[] children = treeContentProvider
0341: .getElements(treeElement);
0342: for (int i = 0; i < children.length; ++i) {
0343: determineWhiteCheckedDescendents(children[i]);
0344: }
0345:
0346: // now determine the white-checked status for this tree element
0347: if (determineShouldBeWhiteChecked(treeElement)) {
0348: setWhiteChecked(treeElement, true);
0349: }
0350: }
0351:
0352: /**
0353: * Cause the tree viewer to expand all its items
0354: */
0355: public void expandAll() {
0356: treeViewer.expandAll();
0357: }
0358:
0359: /**
0360: * Expand an element in a tree viewer
0361: */
0362: private void expandTreeElement(final Object item) {
0363: BusyIndicator.showWhile(treeViewer.getControl().getDisplay(),
0364: new Runnable() {
0365: public void run() {
0366:
0367: // First see if the children need to be given their checked state at all. If they've
0368: // already been realized then this won't be necessary
0369: if (expandedTreeNodes.contains(item)) {
0370: checkNewTreeElements(treeContentProvider
0371: .getChildren(item));
0372: } else {
0373:
0374: expandedTreeNodes.add(item);
0375: if (whiteCheckedTreeItems.contains(item)) {
0376: //If this is the first expansion and this is a white checked node then check the children
0377: Object[] children = treeContentProvider
0378: .getChildren(item);
0379: for (int i = 0; i < children.length; ++i) {
0380: if (!whiteCheckedTreeItems
0381: .contains(children[i])) {
0382: Object child = children[i];
0383: setWhiteChecked(child, true);
0384: treeViewer.setChecked(child,
0385: true);
0386: checkedStateStore.put(child,
0387: new ArrayList());
0388: }
0389: }
0390:
0391: //Now be sure to select the list of items too
0392: setListForWhiteSelection(item);
0393: }
0394: }
0395:
0396: }
0397: });
0398: }
0399:
0400: /**
0401: * Add all of the selected children of nextEntry to result recursively.
0402: * This does not set any values in the checked state.
0403: * @param The treeElement being queried
0404: * @param addAll a boolean to indicate if the checked state store needs to be queried
0405: * @param filter IElementFilter - the filter being used on the data
0406: * @param monitor IProgressMonitor or null that the cancel is polled for
0407: */
0408: private void findAllSelectedListElements(Object treeElement,
0409: String parentLabel, boolean addAll, IElementFilter filter,
0410: IProgressMonitor monitor) throws InterruptedException {
0411:
0412: String fullLabel = null;
0413: if (monitor != null && monitor.isCanceled()) {
0414: return;
0415: }
0416: if (monitor != null) {
0417: fullLabel = getFullLabel(treeElement, parentLabel);
0418: monitor.subTask(fullLabel);
0419: }
0420:
0421: if (addAll) {
0422: filter.filterElements(listContentProvider
0423: .getElements(treeElement), monitor);
0424: } else { //Add what we have stored
0425: if (checkedStateStore.containsKey(treeElement)) {
0426: filter.filterElements((Collection) checkedStateStore
0427: .get(treeElement), monitor);
0428: }
0429: }
0430:
0431: Object[] treeChildren = treeContentProvider
0432: .getChildren(treeElement);
0433: for (int i = 0; i < treeChildren.length; i++) {
0434: Object child = treeChildren[i];
0435: if (addAll) {
0436: findAllSelectedListElements(child, fullLabel, true,
0437: filter, monitor);
0438: } else { //Only continue for those with checked state
0439: if (checkedStateStore.containsKey(child)) {
0440: findAllSelectedListElements(child, fullLabel,
0441: whiteCheckedTreeItems.contains(child),
0442: filter, monitor);
0443: }
0444: }
0445:
0446: }
0447: }
0448:
0449: /**
0450: * Find all of the white checked children of the treeElement and add them to the collection.
0451: * If the element itself is white select add it. If not then add any selected list elements
0452: * and recurse down to the children.
0453: * @param treeElement java.lang.Object
0454: * @param result java.util.Collection
0455: */
0456: private void findAllWhiteCheckedItems(Object treeElement,
0457: Collection result) {
0458:
0459: if (whiteCheckedTreeItems.contains(treeElement)) {
0460: result.add(treeElement);
0461: } else {
0462: Collection listChildren = (Collection) checkedStateStore
0463: .get(treeElement);
0464: //if it is not in the store then it and it's children are not interesting
0465: if (listChildren == null) {
0466: return;
0467: }
0468: result.addAll(listChildren);
0469: Object[] children = treeContentProvider
0470: .getChildren(treeElement);
0471: for (int i = 0; i < children.length; ++i) {
0472: findAllWhiteCheckedItems(children[i], result);
0473: }
0474: }
0475: }
0476:
0477: /**
0478: * Returns a flat list of all of the leaf elements which are checked. Filter
0479: * then based on the supplied ElementFilter. If monitor is cancelled then
0480: * return null
0481: *
0482: * @param filter -
0483: * the filter for the data
0484: * @param monitor
0485: * IProgressMonitor or null
0486: * @throws InterruptedException
0487: * If the find is interrupted.
0488: */
0489: public void getAllCheckedListItems(IElementFilter filter,
0490: IProgressMonitor monitor) throws InterruptedException {
0491:
0492: //Iterate through the children of the root as the root is not in the store
0493: Object[] children = treeContentProvider.getChildren(root);
0494: for (int i = 0; i < children.length; ++i) {
0495: findAllSelectedListElements(children[i], null,
0496: whiteCheckedTreeItems.contains(children[i]),
0497: filter, monitor);
0498: }
0499: }
0500:
0501: /**
0502: * Returns a flat list of all of the leaf elements which are checked.
0503: *
0504: * @return all of the leaf elements which are checked. This API does not
0505: * return null in order to keep backwards compatibility.
0506: */
0507: public List getAllCheckedListItems() {
0508:
0509: final ArrayList returnValue = new ArrayList();
0510:
0511: IElementFilter passThroughFilter = new IElementFilter() {
0512:
0513: public void filterElements(Collection elements,
0514: IProgressMonitor monitor) {
0515: returnValue.addAll(elements);
0516: }
0517:
0518: public void filterElements(Object[] elements,
0519: IProgressMonitor monitor) {
0520: for (int i = 0; i < elements.length; i++) {
0521: returnValue.add(elements[i]);
0522: }
0523: }
0524: };
0525:
0526: try {
0527: getAllCheckedListItems(passThroughFilter, null);
0528: } catch (InterruptedException exception) {
0529: return new ArrayList();
0530: }
0531: return returnValue;
0532:
0533: }
0534:
0535: /**
0536: * Returns a list of all of the items that are white checked.
0537: * Any folders that are white checked are added and then any files
0538: * from white checked folders are added.
0539: *
0540: * @return the list of all of the items that are white checked
0541: */
0542: public List getAllWhiteCheckedItems() {
0543:
0544: List result = new ArrayList();
0545:
0546: //Iterate through the children of the root as the root is not in the store
0547: Object[] children = treeContentProvider.getChildren(root);
0548: for (int i = 0; i < children.length; ++i) {
0549: findAllWhiteCheckedItems(children[i], result);
0550: }
0551:
0552: return result;
0553: }
0554:
0555: /**
0556: * Answer the number of elements that have been checked by the
0557: * user.
0558: *
0559: * @return int
0560: */
0561: public int getCheckedElementCount() {
0562: return checkedStateStore.size();
0563: }
0564:
0565: /**
0566: * Get the full label of the treeElement (its name and its parent's name).
0567: * @param treeElement - the element being exported
0568: * @param parentLabel - the label of the parent, can be null
0569: * @return String
0570: */
0571: protected String getFullLabel(Object treeElement, String parentLabel) {
0572: String label = parentLabel;
0573: if (parentLabel == null) {
0574: label = ""; //$NON-NLS-1$
0575: }
0576: IPath parentName = new Path(label);
0577:
0578: String elementText = treeLabelProvider.getText(treeElement);
0579: if (elementText == null) {
0580: return parentName.toString();
0581: }
0582: return parentName.append(elementText).toString();
0583: }
0584:
0585: /**
0586: * Return a count of the number of list items associated with a
0587: * given tree item.
0588: *
0589: * @return int
0590: * @param treeElement java.lang.Object
0591: */
0592: protected int getListItemsSize(Object treeElement) {
0593: Object[] elements = listContentProvider
0594: .getElements(treeElement);
0595: return elements.length;
0596: }
0597:
0598: /**
0599: * Get the table the list viewer uses.
0600: * @return org.eclipse.swt.widgets.Table
0601: */
0602: public Table getListTable() {
0603: return this .listViewer.getTable();
0604: }
0605:
0606: /**
0607: * Logically gray-check all ancestors of treeItem by ensuring that they
0608: * appear in the checked table
0609: */
0610: protected void grayCheckHierarchy(Object treeElement) {
0611:
0612: //expand the element first to make sure we have populated for it
0613: expandTreeElement(treeElement);
0614:
0615: // if this tree element is already gray then its ancestors all are as well
0616: if (checkedStateStore.containsKey(treeElement)) {
0617: return; // no need to proceed upwards from here
0618: }
0619:
0620: checkedStateStore.put(treeElement, new ArrayList());
0621: Object parent = treeContentProvider.getParent(treeElement);
0622: if (parent != null) {
0623: grayCheckHierarchy(parent);
0624: }
0625: }
0626:
0627: /**
0628: * Set the checked state of self and all ancestors appropriately. Do not white check anyone - this is
0629: * only done down a hierarchy.
0630: */
0631: private void grayUpdateHierarchy(Object treeElement) {
0632:
0633: boolean shouldBeAtLeastGray = determineShouldBeAtLeastGrayChecked(treeElement);
0634:
0635: treeViewer.setGrayChecked(treeElement, shouldBeAtLeastGray);
0636:
0637: if (whiteCheckedTreeItems.contains(treeElement)) {
0638: whiteCheckedTreeItems.remove(treeElement);
0639: }
0640:
0641: // proceed up the tree element hierarchy
0642: Object parent = treeContentProvider.getParent(treeElement);
0643: if (parent != null) {
0644: grayUpdateHierarchy(parent);
0645: }
0646: }
0647:
0648: /**
0649: * Set the initial checked state of the passed list element to true.
0650: * @param element
0651: */
0652: public void initialCheckListItem(Object element) {
0653: Object parent = treeContentProvider.getParent(element);
0654: selectAndReveal(parent);
0655: //Check the element in the viewer as if it had been manually checked
0656: listViewer.setChecked(element, true);
0657: //As this is not done from the UI then set the box for updating from the selection to false
0658: listItemChecked(element, true, false);
0659: grayUpdateHierarchy(parent);
0660: }
0661:
0662: /**
0663: * Set the initial checked state of the passed element to true,
0664: * as well as to all of its children and associated list elements
0665: * @param element
0666: */
0667: public void initialCheckTreeItem(Object element) {
0668: treeItemChecked(element, true);
0669: selectAndReveal(element);
0670: }
0671:
0672: private void selectAndReveal(Object treeElement) {
0673: treeViewer.reveal(treeElement);
0674: IStructuredSelection selection = new StructuredSelection(
0675: treeElement);
0676: treeViewer.setSelection(selection);
0677: }
0678:
0679: /**
0680: * Initialize this group's viewers after they have been laid out.
0681: */
0682: protected void initialize() {
0683: treeViewer.setInput(root);
0684: this .expandedTreeNodes = new ArrayList();
0685: this .expandedTreeNodes.add(root);
0686:
0687: }
0688:
0689: /**
0690: * Callback that's invoked when the checked status of an item in the list
0691: * is changed by the user. Do not try and update the hierarchy if we are building the
0692: * initial list.
0693: */
0694: protected void listItemChecked(Object listElement, boolean state,
0695: boolean updatingFromSelection) {
0696: List checkedListItems = (List) checkedStateStore
0697: .get(currentTreeSelection);
0698: //If it has not been expanded do so as the selection of list items will affect gray state
0699: if (!expandedTreeNodes.contains(currentTreeSelection)) {
0700: expandTreeElement(currentTreeSelection);
0701: }
0702:
0703: if (state) {
0704: if (checkedListItems == null) {
0705: // since the associated tree item has gone from 0 -> 1 checked
0706: // list items, tree checking may need to be updated
0707: grayCheckHierarchy(currentTreeSelection);
0708: checkedListItems = (List) checkedStateStore
0709: .get(currentTreeSelection);
0710: }
0711: checkedListItems.add(listElement);
0712: } else {
0713: checkedListItems.remove(listElement);
0714: if (checkedListItems.isEmpty()) {
0715: // since the associated tree item has gone from 1 -> 0 checked
0716: // list items, tree checking may need to be updated
0717: ungrayCheckHierarchy(currentTreeSelection);
0718: }
0719: }
0720:
0721: //Update the list with the selections if there are any
0722: if (checkedListItems.size() > 0) {
0723: checkedStateStore.put(currentTreeSelection,
0724: checkedListItems);
0725: }
0726: if (updatingFromSelection) {
0727: grayUpdateHierarchy(currentTreeSelection);
0728: }
0729: }
0730:
0731: /**
0732: * Notify all checked state listeners that the passed element has had
0733: * its checked state changed to the passed state
0734: */
0735: protected void notifyCheckStateChangeListeners(
0736: final CheckStateChangedEvent event) {
0737: Object[] array = getListeners();
0738: for (int i = 0; i < array.length; i++) {
0739: final ICheckStateListener l = (ICheckStateListener) array[i];
0740: SafeRunner.run(new SafeRunnable() {
0741: public void run() {
0742: l.checkStateChanged(event);
0743: }
0744: });
0745: }
0746: }
0747:
0748: /**
0749: *Set the contents of the list viewer based upon the specified selected
0750: *tree element. This also includes checking the appropriate list items.
0751: *
0752: *@param treeElement java.lang.Object
0753: */
0754: protected void populateListViewer(final Object treeElement) {
0755: listViewer.setInput(treeElement);
0756:
0757: //If the element is white checked but not expanded we have not set up all of the children yet
0758: if (!(expandedTreeNodes.contains(treeElement))
0759: && whiteCheckedTreeItems.contains(treeElement)) {
0760:
0761: //Potentially long operation - show a busy cursor
0762: BusyIndicator.showWhile(treeViewer.getControl()
0763: .getDisplay(), new Runnable() {
0764: public void run() {
0765: setListForWhiteSelection(treeElement);
0766: listViewer.setAllChecked(true);
0767: }
0768: });
0769:
0770: } else {
0771: List listItemsToCheck = (List) checkedStateStore
0772: .get(treeElement);
0773:
0774: if (listItemsToCheck != null) {
0775: Iterator listItemsEnum = listItemsToCheck.iterator();
0776: while (listItemsEnum.hasNext()) {
0777: listViewer.setChecked(listItemsEnum.next(), true);
0778: }
0779: }
0780: }
0781: }
0782:
0783: /**
0784: * Logically gray-check all ancestors of treeItem by ensuring that they
0785: * appear in the checked table. Add any elements to the selectedNodes
0786: * so we can track that has been done.
0787: */
0788: private void primeHierarchyForSelection(Object item,
0789: Set selectedNodes) {
0790:
0791: //Only prime it if we haven't visited yet
0792: if (selectedNodes.contains(item)) {
0793: return;
0794: }
0795:
0796: checkedStateStore.put(item, new ArrayList());
0797:
0798: //mark as expanded as we are going to populate it after this
0799: expandedTreeNodes.add(item);
0800: selectedNodes.add(item);
0801:
0802: Object parent = treeContentProvider.getParent(item);
0803: if (parent != null) {
0804: primeHierarchyForSelection(parent, selectedNodes);
0805: }
0806: }
0807:
0808: /**
0809: * Remove the passed listener from self's collection of clients
0810: * that listen for changes to element checked states
0811: *
0812: * @param listener ICheckStateListener
0813: */
0814: public void removeCheckStateListener(ICheckStateListener listener) {
0815: removeListenerObject(listener);
0816: }
0817:
0818: /**
0819: * Handle the selection of an item in the tree viewer
0820: *
0821: * @param event SelectionChangedEvent
0822: */
0823: public void selectionChanged(SelectionChangedEvent event) {
0824: IStructuredSelection selection = (IStructuredSelection) event
0825: .getSelection();
0826: Object selectedElement = selection.getFirstElement();
0827: if (selectedElement == null) {
0828: currentTreeSelection = null;
0829: listViewer.setInput(currentTreeSelection);
0830: return;
0831: }
0832:
0833: // ie.- if not an item deselection
0834: if (selectedElement != currentTreeSelection) {
0835: populateListViewer(selectedElement);
0836: }
0837:
0838: currentTreeSelection = selectedElement;
0839: }
0840:
0841: /**
0842: * Select or deselect all of the elements in the tree depending on the value of the selection
0843: * boolean. Be sure to update the displayed files as well.
0844: * @param selection
0845: */
0846: public void setAllSelections(final boolean selection) {
0847:
0848: //If there is no root there is nothing to select
0849: if (root == null) {
0850: return;
0851: }
0852:
0853: //Potentially long operation - show a busy cursor
0854: BusyIndicator.showWhile(treeViewer.getControl().getDisplay(),
0855: new Runnable() {
0856: public void run() {
0857: setTreeChecked(root, selection);
0858: listViewer.setAllChecked(selection);
0859: }
0860: });
0861: }
0862:
0863: /**
0864: * The treeElement has been white selected. Get the list for the element and
0865: * set it in the checked state store.
0866: * @param treeElement the element being updated
0867: */
0868: private void setListForWhiteSelection(Object treeElement) {
0869:
0870: Object[] listItems = listContentProvider
0871: .getElements(treeElement);
0872: List listItemsChecked = new ArrayList();
0873: for (int i = 0; i < listItems.length; ++i) {
0874: listItemsChecked.add(listItems[i]);
0875: }
0876:
0877: checkedStateStore.put(treeElement, listItemsChecked);
0878: }
0879:
0880: /**
0881: * Set the list viewer's providers to those passed
0882: *
0883: * @param contentProvider ITreeContentProvider
0884: * @param labelProvider ILabelProvider
0885: */
0886: public void setListProviders(
0887: IStructuredContentProvider contentProvider,
0888: ILabelProvider labelProvider) {
0889: listViewer.setContentProvider(contentProvider);
0890: listViewer.setLabelProvider(labelProvider);
0891: }
0892:
0893: /**
0894: * Set the comparator that is to be applied to self's list viewer
0895: *
0896: * @param comparator the sorter for the list
0897: */
0898: public void setListComparator(ViewerComparator comparator) {
0899: listViewer.setComparator(comparator);
0900: }
0901:
0902: /**
0903: * Set the root of the widget to be new Root. Regenerate all of the tables and lists from this
0904: * value.
0905: * @param newRoot
0906: */
0907: public void setRoot(Object newRoot) {
0908: this .root = newRoot;
0909: initialize();
0910: }
0911:
0912: /**
0913: * Set the checked state of the passed tree element appropriately, and
0914: * do so recursively to all of its child tree elements as well
0915: */
0916: protected void setTreeChecked(Object treeElement, boolean state) {
0917:
0918: if (treeElement.equals(currentTreeSelection)) {
0919: listViewer.setAllChecked(state);
0920: }
0921:
0922: if (state) {
0923: setListForWhiteSelection(treeElement);
0924: } else {
0925: checkedStateStore.remove(treeElement);
0926: }
0927:
0928: setWhiteChecked(treeElement, state);
0929: treeViewer.setChecked(treeElement, state);
0930: treeViewer.setGrayed(treeElement, false);
0931:
0932: // now logically check/uncheck all children as well if it has been expanded
0933: if (expandedTreeNodes.contains(treeElement)) {
0934: Object[] children = treeContentProvider
0935: .getChildren(treeElement);
0936: for (int i = 0; i < children.length; ++i) {
0937: setTreeChecked(children[i], state);
0938: }
0939: }
0940: }
0941:
0942: /**
0943: * Set the tree viewer's providers to those passed
0944: *
0945: * @param contentProvider ITreeContentProvider
0946: * @param labelProvider ILabelProvider
0947: */
0948: public void setTreeProviders(ITreeContentProvider contentProvider,
0949: ILabelProvider labelProvider) {
0950: treeViewer.setContentProvider(contentProvider);
0951: treeViewer.setLabelProvider(labelProvider);
0952: }
0953:
0954: /**
0955: * Set the comparator that is to be applied to self's tree viewer
0956: *
0957: * @param comparator the comparator for the tree
0958: */
0959: public void setTreeComparator(ViewerComparator comparator) {
0960: treeViewer.setComparator(comparator);
0961: }
0962:
0963: /**
0964: * Adjust the collection of references to white-checked tree elements appropriately.
0965: *
0966: * @param treeElement java.lang.Object
0967: * @param isWhiteChecked boolean
0968: */
0969: protected void setWhiteChecked(Object treeElement,
0970: boolean isWhiteChecked) {
0971: if (isWhiteChecked) {
0972: if (!whiteCheckedTreeItems.contains(treeElement)) {
0973: whiteCheckedTreeItems.add(treeElement);
0974: }
0975: } else {
0976: whiteCheckedTreeItems.remove(treeElement);
0977: }
0978: }
0979:
0980: /**
0981: * Handle the collapsing of an element in a tree viewer
0982: */
0983: public void treeCollapsed(TreeExpansionEvent event) {
0984: // We don't need to do anything with this
0985: }
0986:
0987: /**
0988: * Handle the expansionsion of an element in a tree viewer
0989: */
0990: public void treeExpanded(TreeExpansionEvent event) {
0991: expandTreeElement(event.getElement());
0992: }
0993:
0994: /**
0995: * Callback that's invoked when the checked status of an item in the tree
0996: * is changed by the user.
0997: */
0998: protected void treeItemChecked(Object treeElement, boolean state) {
0999:
1000: // recursively adjust all child tree elements appropriately
1001: setTreeChecked(treeElement, state);
1002:
1003: Object parent = treeContentProvider.getParent(treeElement);
1004: if (parent == null) {
1005: return;
1006: }
1007:
1008: // now update upwards in the tree hierarchy
1009: if (state) {
1010: grayCheckHierarchy(parent);
1011: } else {
1012: ungrayCheckHierarchy(parent);
1013: }
1014:
1015: //Update the hierarchy but do not white select the parent
1016: grayUpdateHierarchy(parent);
1017: }
1018:
1019: /**
1020: * Logically un-gray-check all ancestors of treeItem iff appropriate.
1021: */
1022: protected void ungrayCheckHierarchy(Object treeElement) {
1023: if (!determineShouldBeAtLeastGrayChecked(treeElement)) {
1024: checkedStateStore.remove(treeElement);
1025: }
1026:
1027: Object parent = treeContentProvider.getParent(treeElement);
1028: if (parent != null) {
1029: ungrayCheckHierarchy(parent);
1030: }
1031: }
1032:
1033: /**
1034: * Set the checked state of self and all ancestors appropriately
1035: */
1036: protected void updateHierarchy(Object treeElement) {
1037:
1038: boolean whiteChecked = determineShouldBeWhiteChecked(treeElement);
1039: boolean shouldBeAtLeastGray = determineShouldBeAtLeastGrayChecked(treeElement);
1040:
1041: treeViewer.setChecked(treeElement, shouldBeAtLeastGray);
1042: setWhiteChecked(treeElement, whiteChecked);
1043: if (whiteChecked) {
1044: treeViewer.setGrayed(treeElement, false);
1045: } else {
1046: treeViewer.setGrayed(treeElement, shouldBeAtLeastGray);
1047: }
1048:
1049: // proceed up the tree element hierarchy but gray select all of them
1050: Object parent = treeContentProvider.getParent(treeElement);
1051: if (parent != null) {
1052: grayUpdateHierarchy(parent);
1053: }
1054: }
1055:
1056: /**
1057: * Update the selections of the tree elements in items to reflect the new
1058: * selections provided.
1059: * @param items Map with keys of Object (the tree element) and values of List (the selected
1060: * list elements).
1061: * NOTE: This method does not special case keys with no values (i.e.,
1062: * a tree element with an empty list). If a tree element does not have any selected
1063: * items, do not include the element in the Map.
1064: */
1065: public void updateSelections(Map items) {
1066: // We are replacing all selected items with the given selected items,
1067: // so reinitialize everything.
1068: this .listViewer.setAllChecked(false);
1069: this .treeViewer.setCheckedElements(new Object[0]);
1070: this .whiteCheckedTreeItems = new HashSet();
1071: Set selectedNodes = new HashSet();
1072: checkedStateStore = new HashMap();
1073:
1074: //Update the store before the hierarchy to prevent updating parents before all of the children are done
1075: Iterator keyIterator = items.keySet().iterator();
1076: while (keyIterator.hasNext()) {
1077: Object key = keyIterator.next();
1078: List selections = (List) items.get(key);
1079: //Replace the items in the checked state store with those from the supplied items
1080: checkedStateStore.put(key, selections);
1081: selectedNodes.add(key);
1082: // proceed up the tree element hierarchy
1083: Object parent = treeContentProvider.getParent(key);
1084: if (parent != null) {
1085: // proceed up the tree element hierarchy and make sure everything is in the table
1086: primeHierarchyForSelection(parent, selectedNodes);
1087: }
1088: }
1089:
1090: // Update the checked tree items. Since each tree item has a selected
1091: // item, all the tree items will be gray checked.
1092: treeViewer.setCheckedElements(checkedStateStore.keySet()
1093: .toArray());
1094: treeViewer.setGrayedElements(checkedStateStore.keySet()
1095: .toArray());
1096:
1097: // Update the listView of the currently selected tree item.
1098: if (currentTreeSelection != null) {
1099: Object displayItems = items.get(currentTreeSelection);
1100: if (displayItems != null) {
1101: listViewer.setCheckedElements(((List) displayItems)
1102: .toArray());
1103: }
1104: }
1105: }
1106:
1107: /**
1108: * Set the focus on to the list widget.
1109: */
1110: public void setFocus() {
1111:
1112: this.treeViewer.getTree().setFocus();
1113: }
1114:
1115: }
|