/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jkiss.dbeaver.ui.navigator.database; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ViewerFilter; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.core.DBeaverUI; import org.jkiss.dbeaver.model.navigator.DBNDataSource; import org.jkiss.dbeaver.model.navigator.DBNDatabaseNode; import org.jkiss.dbeaver.model.navigator.DBNNode; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress; import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor; import org.jkiss.utils.ArrayUtils; import java.lang.reflect.InvocationTargetException; import java.util.*; public class CheckboxTreeManager implements ICheckStateListener { private static final Log log = Log.getLog(CheckboxTreeManager.class); private final CheckboxTreeViewer viewer; private final Class<?>[] targetTypes; private Object[] checkedElements; private final ViewerFilter[] filters; public CheckboxTreeManager(CheckboxTreeViewer viewer, Class<?>[] targetTypes) { this.viewer = viewer; this.targetTypes = targetTypes; this.filters = viewer.getFilters(); viewer.addCheckStateListener(this); } @Override public void checkStateChanged(final CheckStateChangedEvent event) { updateElementsCheck( new Object[] {event.getElement()}, event.getChecked(), true); } private void updateElementsCheck(final Object[] elements, final boolean checked, final boolean change) { checkedElements = viewer.getCheckedElements(); try { DBeaverUI.runInProgressService(new DBRRunnableWithProgress() { @Override public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException { monitor.beginTask("Load sources tree", 100 * elements.length); try { for (Object element : elements) { updateElementHierarchy(monitor, element, checked, change); if (change) { // Update parent state if (element instanceof DBNDatabaseNode) { for (DBNNode node = ((DBNDatabaseNode) element).getParentNode(); node != null; node = node.getParentNode()) { if (node instanceof DBNDatabaseNode) { updateElementHierarchy(monitor, node, checked, false); } if (node instanceof DBNDataSource) { break; } } } } } } catch (DBException e) { throw new InvocationTargetException(e); } finally { monitor.done(); } } }); } catch (InvocationTargetException e) { log.error("Error updating checkbox state", e.getTargetException()); //UIUtils.showErrorDialog(viewer.getControl().getShell(), "Error", "Can't collect child nodes", e.getTargetException()); } catch (InterruptedException e) { // ignore } } private void updateElementHierarchy(final DBRProgressMonitor monitor, final Object element, final boolean checked, final boolean change) throws DBException { final List<DBNDatabaseNode> targetChildren = new ArrayList<>(); final List<DBNDatabaseNode> targetContainers = new ArrayList<>(); collectChildren(monitor, element, targetChildren, targetContainers, !change); // Run ui DBeaverUI.syncExec(new Runnable() { @Override public void run() { if (change) { for (DBNDatabaseNode child : targetChildren) { viewer.setChecked(child, checked); } } for (DBNDatabaseNode container : change ? targetContainers : Collections.singletonList((DBNDatabaseNode) element)) { try { DBNDatabaseNode[] directChildren = container.getChildren(new VoidProgressMonitor()); if (directChildren != null) { boolean missingOne = false, missingAll = true; for (DBNDatabaseNode node : directChildren) { if (!viewer.getChecked(node)) { missingOne = true; } else { missingAll = false; } } viewer.setChecked(container, change ? checked : !missingAll); viewer.setGrayed(container, missingOne); } } catch (DBException e) { // shouldn't be here } } } }); } private boolean collectChildren(DBRProgressMonitor monitor, final Object element, List<DBNDatabaseNode> targetChildren, List<DBNDatabaseNode> targetContainers, boolean onlyChecked) throws DBException { if (element instanceof DBNDatabaseNode) { for (ViewerFilter filter : filters) { if (!filter.select(viewer, ((DBNDatabaseNode) element).getParentNode(), element)) { return false; } } boolean isChecked = ArrayUtils.contains(checkedElements, element); for (Class<?> type : targetTypes) { if (type.isInstance(((DBNDatabaseNode) element).getObject())) { if (!onlyChecked || isChecked) { targetChildren.add((DBNDatabaseNode) element); } return true; } } ((DBNDatabaseNode) element).initializeNode(monitor, null); DBNDatabaseNode[] children = ((DBNDatabaseNode) element).getChildren(monitor); if (!ArrayUtils.isEmpty(children)) { boolean foundChild = false; for (DBNDatabaseNode child : children) { if (collectChildren(monitor, child, targetChildren, targetContainers, onlyChecked)) { foundChild = true; } } if (foundChild) { if (!onlyChecked || isChecked) { targetContainers.add((DBNDatabaseNode) element); } } return foundChild; } } return false; } public void updateCheckStates() { Set<DBNDatabaseNode> parentList = new LinkedHashSet<>(); for (Object element : viewer.getCheckedElements()) { for (DBNNode node = ((DBNDatabaseNode)element).getParentNode(); node != null; node = node.getParentNode()) { if (node instanceof DBNDatabaseNode) { parentList.add((DBNDatabaseNode) node); viewer.setChecked(node, true); } } } updateElementsCheck(parentList.toArray(), true, false); } }