| // Copyright 2016 The Bazel Authors. All rights reserved. |
| // |
| // 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 com.google.devtools.bazel.e4b.wizard; |
| |
| import java.io.File; |
| |
| import org.eclipse.jface.viewers.CheckboxTreeViewer; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.ILabelProviderListener; |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.widgets.Composite; |
| |
| import com.google.common.collect.ImmutableList; |
| |
| /** |
| * A tree content provider that enable selecting a list of sub-directories of a directory root (the |
| * Bazel workspace root). |
| */ |
| public class DirectoryTreeContentProvider implements ITreeContentProvider { |
| |
| private File root; |
| |
| public DirectoryTreeContentProvider(File root) { |
| this.root = root; |
| } |
| |
| @Override |
| public void dispose() {} |
| |
| @Override |
| public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {} |
| |
| @Override |
| public Object[] getElements(Object inputElement) { |
| if (root == null) { |
| return new Object[] {}; |
| } |
| // We only have one root |
| return new Object[] {root}; |
| } |
| |
| @Override |
| public Object[] getChildren(Object parentElement) { |
| return ((File) parentElement).listFiles((f) -> (f.isDirectory() && !f.getName().startsWith(".") |
| && !f.getName().startsWith("bazel-"))); |
| } |
| |
| @Override |
| public Object getParent(Object element) { |
| File file = (File) element; |
| if (file.equals(root)) { |
| return null; |
| } |
| return file.getParentFile(); |
| } |
| |
| @Override |
| public boolean hasChildren(Object element) { |
| Object[] childrens = getChildren(element); |
| return (childrens != null) && (childrens.length > 0); |
| } |
| |
| public File getRoot() { |
| return root; |
| } |
| |
| public void setRoot(File root) { |
| this.root = root; |
| } |
| |
| |
| /** |
| * Create a tree view that use a FileTreeContentProvider for its content. The created tree view |
| * can be used to select branches of a directory tree. |
| * |
| * @param container The parent composite for the created tree view. |
| * @return A checkbox tree view |
| */ |
| static CheckboxTreeViewer createTreeView(Composite container) { |
| final CheckboxTreeViewer tv = new CheckboxTreeViewer(container, SWT.BORDER); |
| tv.setContentProvider(new DirectoryTreeContentProvider(null)); |
| tv.setLabelProvider(new ILabelProvider() { |
| |
| @Override |
| public void removeListener(ILabelProviderListener listener) { |
| // we do not have event notifying listeners, ignore. |
| } |
| |
| @Override |
| public boolean isLabelProperty(Object element, String property) { |
| return false; |
| } |
| |
| |
| @Override |
| public void dispose() {} |
| |
| @Override |
| public void addListener(ILabelProviderListener listener) { |
| // we do not have event notifying listeners, ignore. |
| } |
| |
| @Override |
| public Image getImage(Object element) { |
| return null; |
| } |
| |
| @Override |
| public String getText(Object element) { |
| return ((File) element).getName(); |
| } |
| }); |
| tv.setInput("root"); // pass a non-null that will be ignored |
| |
| tv.addCheckStateListener(event -> setChecked(tv, event.getElement())); |
| |
| return tv; |
| } |
| |
| private static void setChecked(final CheckboxTreeViewer tv, Object element) { |
| // When user checks a checkbox in the tree, check all its children and mark parent as greyed |
| // When a user uncheck a checkbox, mark the subtree as unchecked and ungrayed and if unique |
| // sibling parent as grayed. |
| DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider(); |
| |
| boolean isChecked = tv.getChecked(element); |
| if (tv.getGrayed(element)) { |
| isChecked = !isChecked; |
| } |
| tv.setChecked(element, isChecked); |
| tv.setGrayed(element, false); |
| if (isChecked) { |
| tv.setSubtreeChecked(element, true); |
| } else { |
| tv.setSubtreeChecked(element, false); |
| } |
| setGrayed(tv, provider.getParent(element)); |
| } |
| |
| private static void setGrayed(CheckboxTreeViewer tv, Object element) { |
| if (element == null) { |
| return; |
| } |
| DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider(); |
| boolean checked = tv.getChecked(element); |
| boolean grayed = false; |
| for (Object object : provider.getChildren(element)) { |
| grayed = grayed || tv.getGrayed(object) || tv.getChecked(object); |
| checked = checked && tv.getChecked(object) && !tv.getGrayed(element); |
| } |
| if (checked) { |
| tv.setChecked(element, true); |
| tv.setGrayed(element, false); |
| } else if (grayed) { |
| tv.setGrayChecked(element, true); |
| } else { |
| tv.setChecked(element, false); |
| tv.setGrayed(element, false); |
| } |
| setGrayed(tv, provider.getParent(element)); |
| } |
| |
| /** |
| * Set the root of the directory tree view and refresh the view if appropriate |
| */ |
| static void setFileTreeRoot(CheckboxTreeViewer tv, File root) { |
| DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider(); |
| if ((root == null && provider.getRoot() != null) || !root.equals(provider.getRoot())) { |
| provider.setRoot(root); |
| tv.refresh(); |
| } |
| } |
| |
| /** |
| * Returns the list of path selected in <code>tv</code>. It returns the list of checked path |
| * without the children of the checked path. Each path is returned as a string giving the relative |
| * path from the root of the tree. |
| */ |
| static ImmutableList<String> getSelectPathsRelativeToRoot(CheckboxTreeViewer tv) { |
| DirectoryTreeContentProvider provider = (DirectoryTreeContentProvider) tv.getContentProvider(); |
| String root = provider.root.getAbsolutePath(); |
| ImmutableList.Builder<String> builder = ImmutableList.builder(); |
| for (Object element : tv.getCheckedElements()) { |
| if (!tv.getGrayed(element)) { |
| Object parent = provider.getParent(element); |
| if (parent == null || tv.getGrayed(parent)) { |
| // Only add this element if its parent is not selected (so it's the root). |
| String path = ((File) element).getAbsolutePath(); |
| // Strip root from path |
| if (path.startsWith(root)) { |
| path = path.substring(root.length()); |
| if (path.startsWith("/")) { |
| path = path.substring(1); |
| } |
| builder.add(path); |
| } |
| } |
| } |
| } |
| return builder.build(); |
| } |
| } |