package org.openlca.app.components; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.forms.widgets.FormToolkit; import org.openlca.app.navigation.INavigationElement; import org.openlca.app.navigation.ModelElement; import org.openlca.app.navigation.NavigationComparator; import org.openlca.app.navigation.NavigationContentProvider; import org.openlca.app.navigation.NavigationLabelProvider; import org.openlca.app.navigation.Navigator; import org.openlca.app.navigation.filters.ModelTypeFilter; import org.openlca.app.util.UI; import org.openlca.core.model.ModelType; import org.openlca.core.model.descriptors.CategorizedDescriptor; public class ModelCheckBoxTree implements ICheckStateListener { private final ModelType[] types; private CheckboxTreeViewer tree; private Runnable onChange; public ModelCheckBoxTree(ModelType... types) { this.types = types; } public void drawOn(Composite comp) { drawOn(comp, null); } public void onSelectionChanged(Runnable fn) { this.onChange = fn; } public void drawOn(Composite comp, FormToolkit tk) { tree = new CheckboxTreeViewer(comp, SWT.VIRTUAL | SWT.MULTI | SWT.BORDER); tree.setUseHashlookup(true); tree.setContentProvider(new NavigationContentProvider()); tree.setLabelProvider(new NavigationLabelProvider(false)); tree.setComparator(new NavigationComparator()); tree.addFilter(new ModelTypeFilter(types)); tree.addCheckStateListener(this); ColumnViewerToolTipSupport.enableFor(tree); if (tk != null) { tk.adapt(tree.getTree()); } // compute a height hint GridData data = UI.gridData(tree.getTree(), true, true); data.minimumHeight = 120; Point p = comp.computeSize(SWT.DEFAULT, SWT.DEFAULT); data.heightHint = p.y < 120 ? 120 : p.y; if (types == null || types.length == 0) return; if (types.length == 1) { tree.setInput(Navigator.findElement(types[0])); } else { List<INavigationElement<?>> elems = Arrays.stream(types) .map(type -> Navigator.findElement(type)) .filter(elem -> !elem.getChildren().isEmpty()) .collect(Collectors.toList()); tree.setInput(elems); } tree.expandToLevel(2); } @Override public void checkStateChanged(CheckStateChangedEvent evt) { tree.getControl().setRedraw(false); INavigationElement<?> elem = (INavigationElement<?>) evt.getElement(); tree.setGrayed(elem, false); checkChildren(elem, evt.getChecked()); checkParent(elem); tree.getControl().setRedraw(true); if (onChange != null) { onChange.run(); } } private void checkChildren(INavigationElement<?> elem, boolean state) { for (INavigationElement<?> child : elem.getChildren()) { tree.setGrayed(child, false); tree.setChecked(child, state); if (!(child instanceof ModelElement)) { checkChildren(child, state); } } } private void checkParent(INavigationElement<?> elem) { INavigationElement<?> parent = elem.getParent(); if (parent == null) return; boolean checked = false; boolean all = true; for (INavigationElement<?> child : parent.getChildren()) { checked = tree.getChecked(child) || tree.getGrayed(child); if (!tree.getChecked(child) || tree.getGrayed(child)) all = false; } tree.setGrayed(parent, !all && checked); tree.setChecked(parent, checked); checkParent(parent); } public List<CategorizedDescriptor> getSelection() { Object[] elems = tree.getCheckedElements(); if (elems == null || elems.length == 0) return Collections.emptyList(); return Arrays.stream(elems) .filter(e -> e instanceof ModelElement) .map(e -> ((ModelElement) e).getContent()) .collect(Collectors.toList()); } }