/******************************************************************************* * Copyright (c) 2014 Inria * * 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: * Generoso Pagano, Inria - Initial API and implementation *******************************************************************************/ package org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs; import java.util.HashSet; import java.util.Set; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ICheckable; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.dialogs.FilteredTree; import org.eclipse.ui.dialogs.PatternFilter; import org.eclipse.ui.progress.WorkbenchJob; /** * A <code>FilteredTree</code> wrapping a <code>CheckboxTreeViewer</code>. * * This tree keeps the check state of the nodes in sync, regardless of the fact * that a node is filtered or not. This way, even if an node is filtered (not * visible), the caller can get and set the check state. * * Note that all the "uncheck" operations act only on what is not filtered and * what is child of something not filtered (even if such a child is filtered). * On the contrary, all the "check" operations act only on what is not filtered. * * @author "Generoso Pagano <generoso.pagano@inria.fr>" */ public class FilteredCheckboxTree extends FilteredTree implements ICheckable { /** * Set containing only the tree items that are checked */ private Set<Object> fObjects = new HashSet<>(); /** * Handle to the tree viewer */ private CheckboxTreeViewer fCheckboxTreeViewer; /** * Create a new instance of the receiver. * * @param parent * the parent <code>Composite</code> * @param treeStyle * the style bits for the <code>Tree</code> * @param filter * the filter to be used * @param useNewLook * <code>true</code> if the new <code>FilteredTree</code> look * should be used */ public FilteredCheckboxTree(Composite parent, int treeStyle, PatternFilter filter, boolean useNewLook) { super(parent, treeStyle, filter, useNewLook); } @Override protected TreeViewer doCreateTreeViewer(Composite parentComposite, int style) { fCheckboxTreeViewer = new CheckboxTreeViewer(parentComposite, style); fCheckboxTreeViewer.setUseHashlookup(true); fCheckboxTreeViewer.addCheckStateListener(new ICheckStateListener() { @Override public void checkStateChanged(CheckStateChangedEvent event) { if (event.getChecked()) { fObjects.add(event.getElement()); } else { fObjects.remove(event.getElement()); } } }); return fCheckboxTreeViewer; } @Override protected WorkbenchJob doCreateRefreshJob() { WorkbenchJob job = super.doCreateRefreshJob(); job.addJobChangeListener(new JobChangeAdapter() { @Override public void done(IJobChangeEvent event) { if (fCheckboxTreeViewer.getTree().isDisposed()) { return; } fCheckboxTreeViewer.expandAll(); fCheckboxTreeViewer.setCheckedElements(getCheckedElements()); } }); return job; } @Override public boolean getChecked(Object element) { return fObjects.contains(element); } @Override public boolean setChecked(Object element, boolean state) { boolean checkable = fCheckboxTreeViewer.setChecked(element, state); if (!state) { fObjects.remove(element); } else if (checkable) { fObjects.add(element); } return checkable; } @Override public void addCheckStateListener(ICheckStateListener listener) { fCheckboxTreeViewer.addCheckStateListener(listener); } @Override public void removeCheckStateListener(ICheckStateListener listener) { fCheckboxTreeViewer.addCheckStateListener(listener); } /** * Returns all the checked elements of this tree, either visible or not. * * @return an array containing all the checked elements */ public Object[] getCheckedElements() { return fObjects.toArray(); } /** * Checks all the passed elements and unchecks all the other. * * @param elements * the elements to check */ public void setCheckedElements(Object[] elements) { fObjects = new HashSet<>(); for (Object element : elements) { fObjects.add(element); } fCheckboxTreeViewer.setCheckedElements(elements); } /** * Sets the check state for the given element and its children in this * viewer. The unchecked state is always set, while the checked state is set * only on visible elements. * * @param element * the element * @param state * the check state to set * @return <code>true</code> if the check state could be set, and * <code>false</code> otherwise */ public boolean setSubtreeChecked(Object element, boolean state) { checkSubtree(element, state); return fCheckboxTreeViewer.setSubtreeChecked(element, state); } /** * Recursively sets the check state on an element and its children, using * the politic specified in {@link #setSubtreeChecked(Object, boolean)} * documentation. * * @param element * the element * @param state * the check state to set */ private void checkSubtree(Object element, boolean state) { if (!state || (fCheckboxTreeViewer.testFindItem(element) != null)) { if (state) { fObjects.add(element); } else { fObjects.remove(element); } for (Object o : ((ITreeContentProvider) fCheckboxTreeViewer.getContentProvider()).getChildren(element)) { checkSubtree(o, state); } } } }