blob: bf3d383aeebb7552ae52b3396718d0a88f0fabba [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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.intellij.openapi.roots.libraries.ui.impl;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBoxTableRenderer;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.TitlePanel;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.*;
import com.intellij.ui.treeStructure.treetable.TreeColumnInfo;
import com.intellij.util.PlatformIcons;
import com.intellij.util.containers.Convertor;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.ComboBoxCellEditor;
import com.intellij.util.ui.tree.TreeUtil;
import com.intellij.xml.util.XmlStringUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.io.File;
import java.util.*;
import java.util.List;
/**
* This dialog allows selecting paths inside selected archives or directories.
* The tree is three-level:
* <ul>
* <li>The root is a fake node that just holds child nodes.</li>
* <li>The second level is archives or directories selected on the previous selection step.</li>
* <li>The third level are detected roots inside previous selection.</li>
* </ul>
*
* @author max
* @author Constantine.Plotnikov
*/
public class DetectedRootsChooserDialog extends DialogWrapper {
private static final ColumnInfo ROOT_COLUMN = new TreeColumnInfo("");
private static final ColumnInfo<VirtualFileCheckedTreeNode, String> ROOT_TYPE_COLUMN = new ColumnInfo<VirtualFileCheckedTreeNode, String>("") {
@Override
public String valueOf(VirtualFileCheckedTreeNode node) {
final SuggestedChildRootInfo rootInfo = node.getRootInfo();
return rootInfo != null ? rootInfo.getRootTypeName(rootInfo.getSelectedRootType()) : "";
}
@Override
public TableCellRenderer getRenderer(VirtualFileCheckedTreeNode node) {
final SuggestedChildRootInfo rootInfo = node.getRootInfo();
if (rootInfo != null && isCellEditable(node)) {
return new ComboBoxTableRenderer<String>(rootInfo.getRootTypeNames());
}
return new DefaultTableCellRenderer();
}
@Override
public TableCellEditor getEditor(VirtualFileCheckedTreeNode o) {
final SuggestedChildRootInfo rootInfo = o.getRootInfo();
if (rootInfo == null) return null;
final ComboBoxCellEditor editor = new ComboBoxCellEditor() {
@Override
protected List<String> getComboBoxItems() {
return Arrays.asList(rootInfo.getRootTypeNames());
}
};
editor.setClickCountToStart(1);
return editor;
}
@Override
public boolean isCellEditable(VirtualFileCheckedTreeNode node) {
final SuggestedChildRootInfo rootInfo = node.getRootInfo();
return rootInfo != null && rootInfo.getDetectedRoot().getTypes().size() > 1;
}
@Override
public void setValue(VirtualFileCheckedTreeNode node, String value) {
final SuggestedChildRootInfo rootInfo = node.getRootInfo();
if (rootInfo != null) {
rootInfo.setSelectedRootType(value);
}
}
};
private CheckboxTreeTable myTreeTable;
private JScrollPane myPane;
private String myDescription;
public DetectedRootsChooserDialog(Component component, Collection<SuggestedChildRootInfo> suggestedRoots) {
super(component, true);
init(suggestedRoots);
}
public DetectedRootsChooserDialog(Project project, Collection<SuggestedChildRootInfo> suggestedRoots) {
super(project, true);
init(suggestedRoots);
}
private void init(Collection<SuggestedChildRootInfo> suggestedRoots) {
myDescription = XmlStringUtil.wrapInHtml(ApplicationNamesInfo.getInstance().getFullProductName() +
" just scanned files and detected the following " + StringUtil.pluralize("root", suggestedRoots.size()) + ".<br>" +
"Select items in the tree below or press Cancel to cancel operation.");
myTreeTable = createTreeTable(suggestedRoots);
myPane = ScrollPaneFactory.createScrollPane(myTreeTable);
setTitle("Detected Roots");
init();
}
private static CheckboxTreeTable createTreeTable(Collection<SuggestedChildRootInfo> suggestedRoots) {
final CheckedTreeNode root = createRoot(suggestedRoots);
CheckboxTreeTable treeTable = new CheckboxTreeTable(root, new CheckboxTree.CheckboxTreeCellRenderer(true) {
@Override
public void customizeRenderer(JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
if (!(value instanceof VirtualFileCheckedTreeNode)) return;
VirtualFileCheckedTreeNode node = (VirtualFileCheckedTreeNode)value;
VirtualFile file = node.getFile();
String text;
SimpleTextAttributes attributes;
Icon icon;
boolean isValid = true;
if (leaf) {
VirtualFile ancestor = ((VirtualFileCheckedTreeNode)node.getParent()).getFile();
if (ancestor != null) {
text = VfsUtilCore.getRelativePath(file, ancestor, File.separatorChar);
if (StringUtil.isEmpty(text)) {
text = File.separator;
}
}
else {
text = file.getPresentableUrl();
}
if (text == null) {
isValid = false;
text = file.getPresentableUrl();
}
attributes = SimpleTextAttributes.REGULAR_ATTRIBUTES;
icon = PlatformIcons.DIRECTORY_CLOSED_ICON;
}
else {
text = file.getPresentableUrl();
attributes = SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES;
icon = PlatformIcons.DIRECTORY_CLOSED_ICON;
}
final ColoredTreeCellRenderer textRenderer = getTextRenderer();
textRenderer.setIcon(icon);
if (!isValid) {
textRenderer.append("[INVALID] ", SimpleTextAttributes.ERROR_ATTRIBUTES);
}
textRenderer.append(text, attributes);
}
}, new ColumnInfo[]{ROOT_COLUMN, ROOT_TYPE_COLUMN});
int max = 0;
for (SuggestedChildRootInfo info : suggestedRoots) {
for (String s : info.getRootTypeNames()) {
max = Math.max(max, treeTable.getFontMetrics(treeTable.getFont()).stringWidth(s));
}
}
final TableColumn column = treeTable.getColumnModel().getColumn(1);
int width = max + 20;//add space for combobox button
column.setPreferredWidth(width);
column.setMaxWidth(width);
treeTable.setRootVisible(false);
new TreeTableSpeedSearch(treeTable, new Convertor<TreePath, String>() {
@Override
public String convert(TreePath o) {
Object node = o.getLastPathComponent();
if (!(node instanceof VirtualFileCheckedTreeNode)) return "";
return ((VirtualFileCheckedTreeNode)node).getFile().getPresentableUrl();
}
});
TreeUtil.expandAll(treeTable.getTree());
return treeTable;
}
private static CheckedTreeNode createRoot(Collection<SuggestedChildRootInfo> suggestedRoots) {
SuggestedChildRootInfo[] sortedRoots = suggestedRoots.toArray(new SuggestedChildRootInfo[suggestedRoots.size()]);
Arrays.sort(sortedRoots, new Comparator<SuggestedChildRootInfo>() {
@Override
public int compare(@NotNull SuggestedChildRootInfo o1, @NotNull SuggestedChildRootInfo o2) {
return o1.getDetectedRoot().getFile().getPresentableUrl().compareTo(o2.getDetectedRoot().getFile().getPresentableUrl());
}
});
CheckedTreeNode root = new CheckedTreeNode(null);
Map<VirtualFile, CheckedTreeNode> rootCandidateNodes = new HashMap<VirtualFile, CheckedTreeNode>();
for (SuggestedChildRootInfo rootInfo : sortedRoots) {
final VirtualFile rootCandidate = rootInfo.getRootCandidate();
CheckedTreeNode parent = rootCandidateNodes.get(rootCandidate);
if (parent == null) {
parent = new VirtualFileCheckedTreeNode(rootCandidate);
rootCandidateNodes.put(rootCandidate, parent);
root.add(parent);
}
parent.add(new VirtualFileCheckedTreeNode(rootInfo));
}
return root;
}
@Override
protected JComponent createTitlePane() {
return new TitlePanel("Choose Roots", myDescription);
}
@Nullable
@Override
protected JComponent createCenterPanel() {
return myPane;
}
public SuggestedChildRootInfo[] getChosenRoots() {
return myTreeTable.getCheckedNodes(SuggestedChildRootInfo.class);
}
@NonNls
@Override
protected String getDimensionServiceKey() {
return "DetectedRootsChooserDialog";
}
@Nullable
@Override
public JComponent getPreferredFocusedComponent() {
return myTreeTable;
}
private static class VirtualFileCheckedTreeNode extends CheckedTreeNode {
private final VirtualFile myFile;
private VirtualFileCheckedTreeNode(VirtualFile file) {
super(file);
myFile = file;
}
public VirtualFileCheckedTreeNode(SuggestedChildRootInfo rootInfo) {
super(rootInfo);
myFile = rootInfo.getDetectedRoot().getFile();
}
public VirtualFile getFile() {
return myFile;
}
@Nullable
private SuggestedChildRootInfo getRootInfo() {
return userObject instanceof SuggestedChildRootInfo ? (SuggestedChildRootInfo)userObject : null;
}
}
}