| /******************************************************************************* |
| * Copyright (c) 2000, 2015 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.internal.editors.text; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.BusyIndicator; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Tree; |
| |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.core.runtime.SafeRunner; |
| |
| import org.eclipse.core.resources.IContainer; |
| |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.util.SafeRunnable; |
| import org.eclipse.jface.viewers.CheckStateChangedEvent; |
| import org.eclipse.jface.viewers.CheckboxTableViewer; |
| import org.eclipse.jface.viewers.CheckboxTreeViewer; |
| import org.eclipse.jface.viewers.ICheckStateListener; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredContentProvider; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.ITreeViewerListener; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.TreeExpansionEvent; |
| import org.eclipse.jface.viewers.ViewerComparator; |
| |
| /* |
| * XXX: This is an copy of the internal ResourceTreeAndListGroup class, see http://bugs.eclipse.org/147027 |
| */ |
| |
| /** |
| * Workbench-level composite that combines a CheckboxTreeViewer and |
| * CheckboxListViewer. All viewer selection-driven interactions are handled |
| * within this object |
| */ |
| class SelectResourcesBlock implements ICheckStateListener, ISelectionChangedListener, ITreeViewerListener { |
| |
| |
| /** |
| * The IElementFilter is a interface that defines |
| * the API for filtering the current selection of |
| * a ResourceTreeAndListGroup in order to find a |
| * subset to update as the result of a type filtering. |
| * This is meant as an internal class and is used exclusively |
| * by the import dialog. |
| */ |
| interface IElementFilter { |
| |
| void filterElements(Collection<Object> elements) throws InterruptedException; |
| |
| void filterElements(Object[] elements) throws InterruptedException; |
| } |
| |
| |
| private Object root; |
| |
| private Object currentTreeSelection; |
| |
| private Collection<Object> expandedTreeNodes= new HashSet<>(); |
| |
| private Map<Object, List<Object>> checkedStateStore= new HashMap<>(9); |
| |
| private Collection<Object> whiteCheckedTreeItems= new HashSet<>(); |
| |
| private ListenerList listeners= new ListenerList(ListenerList.IDENTITY); |
| |
| private ITreeContentProvider treeContentProvider; |
| |
| private IStructuredContentProvider listContentProvider; |
| |
| private ILabelProvider treeLabelProvider; |
| |
| private ILabelProvider listLabelProvider; |
| |
| // widgets |
| private CheckboxTreeViewer treeViewer; |
| |
| private CheckboxTableViewer listViewer; |
| |
| //height hint for viewers |
| private static int PREFERRED_HEIGHT= 150; |
| |
| /** |
| * Create an instance of this class. Use this constructor if you wish to specify the width |
| * and/or height of the combined widget (to only hard code one of the sizing dimensions, specify |
| * the other dimension's value as -1) |
| * |
| * @param parent the parent composite |
| * @param rootObject the root object |
| * @param treeContentProvider the tree content provider |
| * @param treeLabelProvider the tree label provider |
| * @param listContentProvider the list content provider |
| * @param listLabelProvider the list label provider |
| * @param style the style flags for the new Composite |
| * @param useHeightHint If true then use the height hint to make this group big enough |
| */ |
| public SelectResourcesBlock(Composite parent, Object rootObject, ITreeContentProvider treeContentProvider, ILabelProvider treeLabelProvider, IStructuredContentProvider listContentProvider, ILabelProvider listLabelProvider, int style, boolean useHeightHint) { |
| |
| root= rootObject; |
| this.treeContentProvider= treeContentProvider; |
| this.listContentProvider= listContentProvider; |
| this.treeLabelProvider= treeLabelProvider; |
| this.listLabelProvider= listLabelProvider; |
| createContents(parent, style, useHeightHint); |
| } |
| |
| /** |
| * Add the passed listener to self's collection of clients that listen for |
| * changes to element checked states |
| * |
| * @param listener ICheckStateListener |
| */ |
| public void addCheckStateListener(ICheckStateListener listener) { |
| listeners.add(listener); |
| } |
| |
| /** |
| * Iterates over the passed elements which are being realized for the |
| * first time and check each one in the tree viewer as appropriate. |
| * |
| * @param elements the elements |
| */ |
| private void checkNewTreeElements(Object[] elements) { |
| for (int i= 0; i < elements.length; ++i) { |
| Object currentElement= elements[i]; |
| boolean checked= checkedStateStore.containsKey(currentElement); |
| treeViewer.setChecked(currentElement, checked); |
| treeViewer.setGrayed(currentElement, checked && !whiteCheckedTreeItems.contains(currentElement)); |
| } |
| } |
| |
| /** |
| * An item was checked in one of self's two views. Determine which view this |
| * occurred in and delegate appropriately |
| * |
| * @param event the check state event |
| * @see org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged(org.eclipse.jface.viewers.CheckStateChangedEvent) |
| */ |
| @Override |
| public void checkStateChanged(final CheckStateChangedEvent event) { |
| |
| //Potentially long operation - show a busy cursor |
| BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), new Runnable() { |
| @Override |
| public void run() { |
| if (event.getCheckable().equals(treeViewer)) |
| treeItemChecked(event.getElement(), event.getChecked()); |
| else |
| listItemChecked(event.getElement(), event.getChecked(), true); |
| |
| notifyCheckStateChangeListeners(event); |
| } |
| }); |
| } |
| |
| /** |
| * Lay out and initialize self's visual components. |
| * |
| * @param parent org.eclipse.swt.widgets.Composite |
| * @param style the style flags for the new Composite |
| * @param useHeightHint If true use the preferredHeight. |
| */ |
| private void createContents(Composite parent, int style, boolean useHeightHint) { |
| // group pane |
| Composite composite= new Composite(parent, style); |
| composite.setFont(parent.getFont()); |
| GridLayout layout= new GridLayout(); |
| layout.numColumns= 2; |
| layout.makeColumnsEqualWidth= true; |
| layout.marginHeight= 0; |
| layout.marginWidth= 0; |
| composite.setLayout(layout); |
| composite.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| |
| createTreeViewer(composite, useHeightHint); |
| createListViewer(composite, useHeightHint); |
| |
| initialize(); |
| } |
| |
| /** |
| * Creates this block's list viewer. |
| * |
| * @param parent the parent control |
| * @param useHeightHint if <code>true</code> use the height hints |
| */ |
| private void createListViewer(Composite parent, boolean useHeightHint) { |
| listViewer= CheckboxTableViewer.newCheckList(parent, SWT.BORDER); |
| GridData data= new GridData(GridData.FILL_BOTH); |
| if (useHeightHint) |
| data.heightHint= PREFERRED_HEIGHT; |
| data.widthHint= getViewerWidthHint(parent); |
| listViewer.getTable().setLayoutData(data); |
| listViewer.getTable().setFont(parent.getFont()); |
| listViewer.setContentProvider(listContentProvider); |
| listViewer.setLabelProvider(listLabelProvider); |
| listViewer.addCheckStateListener(this); |
| } |
| |
| /** |
| * Create this block's tree viewer. |
| * |
| * @param parent the parent control |
| * @param useHeightHint if <code>true</code> use the height hints |
| */ |
| private void createTreeViewer(Composite parent, boolean useHeightHint) { |
| Tree tree= new Tree(parent, SWT.CHECK | SWT.BORDER); |
| GridData data= new GridData(GridData.FILL_BOTH); |
| if (useHeightHint) |
| data.heightHint= PREFERRED_HEIGHT; |
| data.widthHint= getViewerWidthHint(parent); |
| tree.setLayoutData(data); |
| tree.setFont(parent.getFont()); |
| |
| treeViewer= new CheckboxTreeViewer(tree); |
| treeViewer.setUseHashlookup(true); |
| treeViewer.setContentProvider(treeContentProvider); |
| treeViewer.setLabelProvider(treeLabelProvider); |
| treeViewer.addTreeListener(this); |
| treeViewer.addCheckStateListener(this); |
| treeViewer.addSelectionChangedListener(this); |
| } |
| |
| private int getViewerWidthHint(Control control) { |
| GC gc= new GC(control); |
| int width= Dialog.convertWidthInCharsToPixels(gc.getFontMetrics(), 50); |
| gc.dispose(); |
| return width; |
| } |
| |
| /** |
| * Returns a boolean indicating whether the passed tree element should be at |
| * LEAST gray-checked. Note that this method does not consider whether it |
| * should be white-checked, so a specified tree item which should be |
| * white-checked will result in a <code>true</code> answer from this |
| * method. |
| * |
| * @param treeElement java.lang.Object |
| * @return boolean |
| */ |
| private boolean determineShouldBeAtLeastGrayChecked(Object treeElement) { |
| // if any list items associated with treeElement are checked then it |
| // retains its gray-checked status regardless of its children |
| List<Object> checked= checkedStateStore.get(treeElement); |
| if (checked != null && (!checked.isEmpty())) |
| return true; |
| |
| // if any children of treeElement are still gray-checked then |
| // treeElement |
| // must remain gray-checked as well. Only ask expanded nodes |
| if (expandedTreeNodes.contains(treeElement)) { |
| Object[] children= treeContentProvider.getChildren(treeElement); |
| for (int i= 0; i < children.length; ++i) { |
| if (checkedStateStore.containsKey(children[i])) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Expands an element in a tree viewer. |
| * |
| * @param element the element to be expanded |
| */ |
| private void expandTreeElement(final Object element) { |
| BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), new Runnable() { |
| @Override |
| public void run() { |
| |
| // First see if the children need to be given their checked |
| // state at all. If they've |
| // already been realized then this won't be necessary |
| if (expandedTreeNodes.contains(element)) |
| checkNewTreeElements(treeContentProvider.getChildren(element)); |
| else { |
| |
| expandedTreeNodes.add(element); |
| if (whiteCheckedTreeItems.contains(element)) { |
| //If this is the first expansion and this is a white |
| // checked node then check the children |
| Object[] children= treeContentProvider.getChildren(element); |
| for (int i= 0; i < children.length; ++i) { |
| if (!whiteCheckedTreeItems.contains(children[i])) { |
| Object child= children[i]; |
| setWhiteChecked(child, true); |
| treeViewer.setChecked(child, true); |
| checkedStateStore.put(child, new ArrayList<>()); |
| } |
| } |
| |
| //Now be sure to select the list of items too |
| setListForWhiteSelection(element); |
| } |
| } |
| |
| } |
| }); |
| } |
| |
| /** |
| * Adds all of the selected children of <code>treeElement</code> to result recursively. This |
| * does not set any values in the checked state. |
| * |
| * @param treeElement the tree element being queried |
| * @param parentLabel the parent label |
| * @param addAll a boolean to indicate if the checked state store needs to be queried |
| * @param filter the filter being used on the data |
| * @throws InterruptedException in case of interruption |
| */ |
| private void findAllSelectedListElements(Object treeElement, String parentLabel, boolean addAll, IElementFilter filter) throws InterruptedException { |
| |
| String fullLabel= null; |
| |
| if (addAll) |
| filter.filterElements(listContentProvider.getElements(treeElement)); |
| else { //Add what we have stored |
| if (checkedStateStore.containsKey(treeElement)) |
| filter.filterElements(checkedStateStore.get(treeElement)); |
| } |
| |
| Object[] treeChildren= treeContentProvider.getChildren(treeElement); |
| for (int i= 0; i < treeChildren.length; i++) { |
| Object child= treeChildren[i]; |
| if (addAll) |
| findAllSelectedListElements(child, fullLabel, true, filter); |
| else { //Only continue for those with checked state |
| if (checkedStateStore.containsKey(child)) |
| findAllSelectedListElements(child, fullLabel, whiteCheckedTreeItems.contains(child), filter); |
| } |
| |
| } |
| } |
| |
| /** |
| * Find all of the white checked children of the treeElement and add them to |
| * the collection. If the element itself is white select add it. If not then |
| * add any selected list elements and recurse down to the children. |
| * |
| * @param treeElement java.lang.Object |
| * @param result java.util.Collection |
| */ |
| private void findAllWhiteCheckedItems(Object treeElement, Collection<Object> result) { |
| |
| if (whiteCheckedTreeItems.contains(treeElement)) |
| result.add(treeElement); |
| else { |
| Collection<Object> listChildren= checkedStateStore.get(treeElement); |
| //if it is not in the store then it and its children are not interesting |
| if (listChildren == null) |
| return; |
| result.addAll(listChildren); |
| Object[] children= treeContentProvider.getChildren(treeElement); |
| for (int i= 0; i < children.length; ++i) { |
| findAllWhiteCheckedItems(children[i], result); |
| } |
| } |
| } |
| |
| /** |
| * Returns a flat list of all of the leaf elements which are checked. Filter |
| * then based on the supplied ElementFilter. If monitor is canceled then |
| * return null |
| * |
| * @param filter - the filter for the data |
| * @throws InterruptedException in case of interruption |
| */ |
| private void getAllCheckedListItems(IElementFilter filter) throws InterruptedException { |
| //Iterate through the children of the root as the root is not in the store |
| Object[] children= treeContentProvider.getChildren(root); |
| for (int i= 0; i < children.length; ++i) { |
| findAllSelectedListElements(children[i], null, whiteCheckedTreeItems.contains(children[i]), filter); |
| } |
| } |
| |
| /** |
| * Returns a flat list of all of the leaf elements which are checked. |
| * |
| * @return all of the leaf elements which are checked. This API does not |
| * return null in order to keep backwards compatibility. |
| */ |
| public List<Object> getAllCheckedListItems() { |
| |
| final ArrayList<Object> returnValue= new ArrayList<>(); |
| |
| IElementFilter passThroughFilter= new IElementFilter() { |
| |
| @Override |
| public void filterElements(Collection<Object> elements) throws InterruptedException { |
| returnValue.addAll(elements); |
| } |
| |
| @Override |
| public void filterElements(Object[] elements) throws InterruptedException { |
| for (int i= 0; i < elements.length; i++) { |
| returnValue.add(elements[i]); |
| } |
| } |
| }; |
| |
| try { |
| getAllCheckedListItems(passThroughFilter); |
| } catch (InterruptedException exception) { |
| return new ArrayList<>(); |
| } |
| return returnValue; |
| |
| } |
| |
| /** |
| * Returns a list of all of the items that are white checked. Any folders |
| * that are white checked are added and then any files from white checked |
| * folders are added. |
| * |
| * @return the list of all of the items that are white checked |
| */ |
| public List<Object> getAllWhiteCheckedItems() { |
| |
| List<Object> result= new ArrayList<>(); |
| |
| //Iterate through the children of the root as the root is not in the |
| // store |
| Object[] children= treeContentProvider.getChildren(root); |
| for (int i= 0; i < children.length; ++i) { |
| findAllWhiteCheckedItems(children[i], result); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Logically gray-check all ancestors of <code>treeElement</code> by ensuring that they |
| * appear in the checked table. |
| * |
| * @param treeElement the tree element |
| */ |
| private void grayCheckHierarchy(Object treeElement) { |
| |
| //expand the element first to make sure we have populated for it |
| expandTreeElement(treeElement); |
| |
| // if this tree element is already gray then its ancestors all are as |
| // well |
| if (checkedStateStore.containsKey(treeElement)) |
| return; // no need to proceed upwards from here |
| |
| checkedStateStore.put(treeElement, new ArrayList<>()); |
| Object parent= treeContentProvider.getParent(treeElement); |
| if (parent != null) |
| grayCheckHierarchy(parent); |
| } |
| |
| /** |
| * Set the checked state of self and all ancestors appropriately. Do not |
| * white check anyone - this is only done down a hierarchy. |
| * |
| * @param treeElement the tree element |
| */ |
| private void grayUpdateHierarchy(Object treeElement) { |
| |
| boolean shouldBeAtLeastGray= determineShouldBeAtLeastGrayChecked(treeElement); |
| |
| treeViewer.setGrayChecked(treeElement, shouldBeAtLeastGray); |
| |
| if (whiteCheckedTreeItems.contains(treeElement)) |
| whiteCheckedTreeItems.remove(treeElement); |
| |
| // proceed up the tree element hierarchy |
| Object parent= treeContentProvider.getParent(treeElement); |
| if (parent != null) { |
| grayUpdateHierarchy(parent); |
| } |
| } |
| |
| public void selectAndReveal(Object treeElement) { |
| treeViewer.reveal(treeElement); |
| IStructuredSelection selection= new StructuredSelection(treeElement); |
| treeViewer.setSelection(selection); |
| } |
| |
| /** |
| * Initialize this group's viewers after they have been laid out. |
| */ |
| private void initialize() { |
| treeViewer.setInput(root); |
| this.expandedTreeNodes= new ArrayList<>(); |
| this.expandedTreeNodes.add(root); |
| Object[] topElements= treeContentProvider.getElements(root); |
| if (topElements.length == 1) |
| treeViewer.setExpandedState(topElements[0], true); |
| } |
| |
| /** |
| * Callback that's invoked when the checked status of an item in the list is |
| * changed by the user. Do not try and update the hierarchy if we are |
| * building the initial list. |
| * |
| * @param listElement the list element |
| * @param state the checked state |
| * @param updatingFromSelection <code>true</code> if we are inside an |
| * update caused by selection |
| */ |
| private void listItemChecked(Object listElement, boolean state, boolean updatingFromSelection) { |
| List<Object> checkedListItems= checkedStateStore.get(currentTreeSelection); |
| //If it has not been expanded do so as the selection of list items will |
| // affect gray state |
| if (!expandedTreeNodes.contains(currentTreeSelection)) |
| expandTreeElement(currentTreeSelection); |
| |
| if (state) { |
| if (checkedListItems == null) { |
| // since the associated tree item has gone from 0 -> 1 checked |
| // list items, tree checking may need to be updated |
| grayCheckHierarchy(currentTreeSelection); |
| checkedListItems= checkedStateStore.get(currentTreeSelection); |
| } |
| checkedListItems.add(listElement); |
| } else { |
| checkedListItems.remove(listElement); |
| if (checkedListItems.isEmpty()) { |
| // since the associated tree item has gone from 1 -> 0 checked |
| // list items, tree checking may need to be updated |
| ungrayCheckHierarchy(currentTreeSelection); |
| } |
| } |
| |
| //Update the list with the selections if there are any |
| if (checkedListItems.size() > 0) |
| checkedStateStore.put(currentTreeSelection, checkedListItems); |
| if (updatingFromSelection) |
| grayUpdateHierarchy(currentTreeSelection); |
| } |
| |
| /** |
| * Notify all checked state listeners with the given event. |
| * |
| * @param event the event |
| */ |
| private void notifyCheckStateChangeListeners(final CheckStateChangedEvent event) { |
| Object[] array= listeners.getListeners(); |
| for (int i= 0; i < array.length; i++) { |
| final ICheckStateListener l= (ICheckStateListener) array[i]; |
| SafeRunner.run(new SafeRunnable() { |
| @Override |
| public void run() { |
| l.checkStateChanged(event); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Set the contents of the list viewer based upon the specified selected |
| * tree element. This also includes checking the appropriate list items. |
| * |
| * @param treeElement java.lang.Object |
| */ |
| private void populateListViewer(final Object treeElement) { |
| listViewer.setInput(treeElement); |
| |
| //If the element is white checked but not expanded we have not set up |
| // all of the children yet |
| if (!(expandedTreeNodes.contains(treeElement)) && whiteCheckedTreeItems.contains(treeElement)) { |
| |
| //Potentially long operation - show a busy cursor |
| BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), new Runnable() { |
| @Override |
| public void run() { |
| setListForWhiteSelection(treeElement); |
| listViewer.setAllChecked(true); |
| } |
| }); |
| |
| } else { |
| List<Object> listItemsToCheck= checkedStateStore.get(treeElement); |
| |
| if (listItemsToCheck != null) { |
| Iterator<Object> listItemsEnum= listItemsToCheck.iterator(); |
| while (listItemsEnum.hasNext()) |
| listViewer.setChecked(listItemsEnum.next(), true); |
| } |
| } |
| } |
| |
| /** |
| * Logically gray-check all ancestors of <code>item</code> by ensuring |
| * that they appear in the checked table. Add any elements to the |
| * <code>selectedNodes</code> so we can track that has been done. |
| * |
| * @param item the tree item |
| * @param selectedNodes the set of selected nodes |
| */ |
| private void primeHierarchyForSelection(Object item, Set<Object> selectedNodes) { |
| |
| //Only prime it if we haven't visited yet |
| if (selectedNodes.contains(item)) |
| return; |
| |
| checkedStateStore.put(item, new ArrayList<>()); |
| |
| //mark as expanded as we are going to populate it after this |
| expandedTreeNodes.add(item); |
| selectedNodes.add(item); |
| |
| Object parent= treeContentProvider.getParent(item); |
| if (parent != null) |
| primeHierarchyForSelection(parent, selectedNodes); |
| } |
| |
| /** |
| * Remove the passed listener from self's collection of clients that listen |
| * for changes to element checked states |
| * |
| * @param listener ICheckStateListener |
| */ |
| public void removeCheckStateListener(ICheckStateListener listener) { |
| listeners.remove(listener); |
| } |
| |
| /** |
| * Handle the selection of an item in the tree viewer |
| * |
| * @param event SelectionChangedEvent |
| */ |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| IStructuredSelection selection= (IStructuredSelection) event.getSelection(); |
| Object selectedElement= selection.getFirstElement(); |
| if (selectedElement == null) { |
| currentTreeSelection= null; |
| listViewer.setInput(null); |
| return; |
| } |
| |
| // i.e.- if not an item deselection |
| if (selectedElement != currentTreeSelection) |
| populateListViewer(selectedElement); |
| |
| currentTreeSelection= selectedElement; |
| } |
| |
| /** |
| * Select or deselect all of the elements in the tree depending on the value |
| * of <code>selection</code>. Be sure to update the displayed files as |
| * well. |
| * |
| * @param selection <code>true</code> if selection should be set, |
| * <code>false</code> if it should be removed |
| */ |
| public void setAllSelections(final boolean selection) { |
| |
| //If there is no root there is nothing to select |
| if (root == null) |
| return; |
| |
| //Potentially long operation - show a busy cursor |
| BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), new Runnable() { |
| @Override |
| public void run() { |
| setTreeChecked(root, selection); |
| listViewer.setAllChecked(selection); |
| } |
| }); |
| } |
| |
| public void refresh() { |
| //Potentially long operation - show a busy cursor |
| BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), new Runnable() { |
| @Override |
| public void run() { |
| treeViewer.refresh(); |
| populateListViewer(currentTreeSelection); |
| } |
| }); |
| } |
| |
| /** |
| * The treeElement has been white selected. Get the list for the element and |
| * set it in the checked state store. |
| * |
| * @param treeElement the element being updated |
| */ |
| private void setListForWhiteSelection(Object treeElement) { |
| |
| Object[] listItems= listContentProvider.getElements(treeElement); |
| List<Object> listItemsChecked= new ArrayList<>(); |
| for (int i= 0; i < listItems.length; ++i) { |
| listItemsChecked.add(listItems[i]); |
| } |
| |
| checkedStateStore.put(treeElement, listItemsChecked); |
| } |
| |
| /** |
| * Set the <code>comparator</code> that is to be applied to self's list viewer |
| * |
| * @param comparator the comparator to be set |
| */ |
| public void setListComparator(ViewerComparator comparator) { |
| listViewer.setComparator(comparator); |
| } |
| |
| /** |
| * Set the checked state of the passed <code>treeElement</code> |
| * appropriately, and do so recursively to all of its child tree elements as |
| * well. |
| * |
| * @param treeElement the root of the subtree |
| * @param state <code>true</code> if checked, <code>false</code> otherwise |
| */ |
| private void setTreeChecked(Object treeElement, boolean state) { |
| |
| if (treeElement.equals(currentTreeSelection)) { |
| listViewer.setAllChecked(state); |
| } |
| |
| if (state) { |
| setListForWhiteSelection(treeElement); |
| } else |
| checkedStateStore.remove(treeElement); |
| |
| setWhiteChecked(treeElement, state); |
| treeViewer.setChecked(treeElement, state); |
| treeViewer.setGrayed(treeElement, false); |
| |
| // now logically check/uncheck all children as well if it has been |
| // expanded |
| if (expandedTreeNodes.contains(treeElement)) { |
| Object[] children= treeContentProvider.getChildren(treeElement); |
| for (int i= 0; i < children.length; ++i) { |
| setTreeChecked(children[i], state); |
| } |
| } |
| } |
| |
| /** |
| * Set the comparator that is to be applied to self's tree viewer. |
| * |
| * @param comparator the comparator to be set |
| */ |
| public void setTreeComparator(ViewerComparator comparator) { |
| treeViewer.setComparator(comparator); |
| } |
| |
| /** |
| * Adjust the collection of references to white-checked tree elements |
| * appropriately. |
| * |
| * @param treeElement the root of the subtree |
| * @param isWhiteChecked <code>true</code> if white checked, |
| * <code>false</code> otherwise |
| */ |
| private void setWhiteChecked(Object treeElement, boolean isWhiteChecked) { |
| if (isWhiteChecked) { |
| if (!whiteCheckedTreeItems.contains(treeElement)) |
| whiteCheckedTreeItems.add(treeElement); |
| } else |
| whiteCheckedTreeItems.remove(treeElement); |
| } |
| |
| /** |
| * Handle the collapsing of an element in a tree viewer. |
| * |
| * @param event the collapse event |
| */ |
| @Override |
| public void treeCollapsed(TreeExpansionEvent event) { |
| // We don't need to do anything with this |
| } |
| |
| /** |
| * Handle the expansion of an element in a tree viewer. |
| * |
| * @param event the expansion event |
| */ |
| @Override |
| public void treeExpanded(TreeExpansionEvent event) { |
| expandTreeElement(event.getElement()); |
| } |
| |
| /** |
| * Callback that's invoked when the checked status of an item in the tree is |
| * changed by the user. |
| * |
| * @param treeElement the tree element that has been checked/unchecked |
| * @param state <code>true</code> if checked, <code>false</code> if |
| * unchecked |
| */ |
| private void treeItemChecked(Object treeElement, boolean state) { |
| |
| // recursively adjust all child tree elements appropriately |
| setTreeChecked(treeElement, state); |
| |
| Object parent= treeContentProvider.getParent(treeElement); |
| if (parent == null) |
| return; |
| |
| // now update upwards in the tree hierarchy |
| if (state) |
| grayCheckHierarchy(parent); |
| else |
| ungrayCheckHierarchy(parent); |
| |
| //Update the hierarchy but do not white select the parent |
| grayUpdateHierarchy(parent); |
| } |
| |
| /** |
| * Logically un-gray-check all ancestors of <code>treeElement</code> if |
| * appropriate. |
| * |
| * @param treeElement the root of the subtree |
| */ |
| private void ungrayCheckHierarchy(Object treeElement) { |
| if (!determineShouldBeAtLeastGrayChecked(treeElement)) |
| checkedStateStore.remove(treeElement); |
| |
| Object parent= treeContentProvider.getParent(treeElement); |
| if (parent != null) |
| ungrayCheckHierarchy(parent); |
| } |
| |
| /** |
| * Update the selections of the tree elements in items to reflect the new |
| * selections provided. |
| * |
| * @param items Map with keys of Object (the tree element) and values of |
| * List (the selected list elements). NOTE: This method does not |
| * special case keys with no values (i.e., a tree element with an |
| * empty list). If a tree element does not have any selected |
| * items, do not include the element in the Map. |
| */ |
| public void updateSelections(Map<IContainer, List<Object>> items) { |
| // We are replacing all selected items with the given selected items, |
| // so reinitialize everything. |
| this.listViewer.setAllChecked(false); |
| this.treeViewer.setCheckedElements(new Object[0]); |
| this.whiteCheckedTreeItems= new HashSet<>(); |
| Set<Object> selectedNodes= new HashSet<>(); |
| checkedStateStore= new HashMap<>(); |
| |
| //Update the store before the hierarchy to prevent updating parents |
| // before all of the children are done |
| Iterator<IContainer> keyIterator= items.keySet().iterator(); |
| while (keyIterator.hasNext()) { |
| Object key= keyIterator.next(); |
| primeHierarchyForSelection(key, selectedNodes); |
| checkedStateStore.put(key, items.get(key)); |
| } |
| |
| // Update the checked tree items. Since each tree item has a selected |
| // item, all the tree items will be gray checked. |
| treeViewer.setCheckedElements(checkedStateStore.keySet().toArray()); |
| treeViewer.setGrayedElements(checkedStateStore.keySet().toArray()); |
| |
| // Update the listView of the currently selected tree item. |
| if (currentTreeSelection != null) { |
| Object displayItems= items.get(currentTreeSelection); |
| if (displayItems != null) |
| listViewer.setCheckedElements(((List<?>) displayItems).toArray()); |
| } |
| } |
| } |