blob: 861043838d42f8bf489c557bd9579e6e055b19a2 [file] [log] [blame]
/*
* Copyright 2000-2009 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.xdebugger.impl.breakpoints.ui;
import com.intellij.ide.util.treeView.TreeState;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.MultiValuesMap;
import com.intellij.pom.Navigatable;
import com.intellij.ui.CheckboxTree;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.ui.tree.TreeUtil;
import com.intellij.xdebugger.breakpoints.XBreakpoint;
import com.intellij.xdebugger.breakpoints.XBreakpointManager;
import com.intellij.xdebugger.breakpoints.XBreakpointType;
import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroup;
import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
import com.intellij.xdebugger.impl.breakpoints.XBreakpointUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.util.*;
/**
* @author nik
*/
public class XBreakpointsTree<B extends XBreakpoint<?>> extends CheckboxTree {
private final TreeNodeComparator myComparator;
private final CheckedTreeNode myRoot;
private DialogWrapper myParentDialog;
private final Map<B, BreakpointNode<B>> myNodes = new HashMap<B, BreakpointNode<B>>();
private List<XBreakpointGroupingRule<B, ?>> myGroupingRules;
private final Map<XBreakpointGroup, BreakpointsGroupNode> myGroupNodes = new HashMap<XBreakpointGroup, BreakpointsGroupNode>();
private final MultiValuesMap<XBreakpointGroupingRule<B, ?>, XBreakpointGroup> myGroups = new MultiValuesMap<XBreakpointGroupingRule<B,?>, XBreakpointGroup>();
private XBreakpointsTree(final XBreakpointType<B, ?> type, final CheckedTreeNode root,
Collection<XBreakpointGroupingRule<B, ?>> groupingRules,
DialogWrapper parentDialog,
XBreakpointManager breakpointManager) {
super(new BreakpointsTreeCellRenderer(), root);
myRoot = root;
myParentDialog = parentDialog;
myComparator = new TreeNodeComparator<B>(type, breakpointManager);
setGroupingRulesInternal(groupingRules);
getEmptyText().setText("No " + type.getTitle());
}
private void setGroupingRulesInternal(final Collection<XBreakpointGroupingRule<B, ?>> groupingRules) {
myGroupingRules = new ArrayList<XBreakpointGroupingRule<B,?>>(groupingRules);
setShowsRootHandles(!groupingRules.isEmpty());
}
public static <B extends XBreakpoint<?>> XBreakpointsTree<B> createTree(final XBreakpointType<B, ?> type,
final Collection<XBreakpointGroupingRule<B, ?>> groupingRules,
DialogWrapper parentDialog, XBreakpointManager breakpointManager) {
return new XBreakpointsTree<B>(type, new CheckedTreeNode("root"), groupingRules, parentDialog, breakpointManager);
}
public void buildTree(@NotNull Collection<? extends B> breakpoints) {
final TreeState state = TreeState.createOn(this, myRoot);
myRoot.removeAllChildren();
myNodes.clear();
myGroupNodes.clear();
myGroups.clear();
for (B breakpoint : breakpoints) {
BreakpointNode<B> node = new BreakpointNode<B>(breakpoint);
CheckedTreeNode parent = getParentNode(breakpoint);
parent.add(node);
myNodes.put(breakpoint, node);
}
TreeUtil.sort(myRoot, myComparator);
((DefaultTreeModel)getModel()).nodeStructureChanged(myRoot);
expandPath(new TreePath(myRoot));
state.applyTo(this, myRoot);
}
@NotNull
private CheckedTreeNode getParentNode(final B breakpoint) {
CheckedTreeNode parent = myRoot;
for (int i = 0; i < myGroupingRules.size(); i++) {
XBreakpointGroup group = getGroup(breakpoint, myGroupingRules.get(i));
if (group != null) {
parent = getOrCreateGroupNode(parent, group, i);
}
}
return parent;
}
private <G extends XBreakpointGroup> BreakpointsGroupNode<G> getOrCreateGroupNode(CheckedTreeNode parent, final G group,
final int level) {
//noinspection unchecked
BreakpointsGroupNode<G> groupNode = (BreakpointsGroupNode<G>)myGroupNodes.get(group);
if (groupNode == null) {
groupNode = new BreakpointsGroupNode<G>(group, level);
myGroupNodes.put(group, groupNode);
parent.add(groupNode);
}
return groupNode;
}
@Override
protected void onDoubleClick(CheckedTreeNode node) {
if (node instanceof BreakpointNode<?>) {
final Navigatable navigatable = ((BreakpointNode)node).getBreakpoint().getNavigatable();
if (navigatable != null) {
navigatable.navigate(true);
myParentDialog.close(DialogWrapper.OK_EXIT_CODE);
}
}
}
@Nullable
private <G extends XBreakpointGroup> XBreakpointGroup getGroup(final B breakpoint, final XBreakpointGroupingRule<B, G> groupingRule) {
//noinspection unchecked
Collection<G> groups = (Collection<G>)myGroups.get(groupingRule);
if (groups == null) {
groups = Collections.emptyList();
}
G group = groupingRule.getGroup(breakpoint, groups);
if (group != null) {
myGroups.put(groupingRule, group);
}
return group;
}
@Override
protected void onNodeStateChanged(final CheckedTreeNode node) {
if (node instanceof BreakpointNode) {
((BreakpointNode)node).getBreakpoint().setEnabled(node.isChecked());
}
}
public void setGroupingRules(List<XBreakpointGroupingRule<B, ?>> groupingRules) {
List<B> selectedBreakpoints = getSelectedBreakpoints();
List<B> allBreakpoints = new ArrayList<B>(myNodes.keySet());
setGroupingRulesInternal(groupingRules);
buildTree(allBreakpoints);
if (selectedBreakpoints.size() > 0) {
selectBreakpoint(selectedBreakpoints.get(0));
}
}
public List<B> getSelectedBreakpoints() {
final ArrayList<B> list = new ArrayList<B>();
TreePath[] selectionPaths = getSelectionPaths();
if (selectionPaths == null || selectionPaths.length == 0) return list;
for (TreePath selectionPath : selectionPaths) {
TreeUtil.traverseDepth((TreeNode)selectionPath.getLastPathComponent(), new TreeUtil.Traverse() {
public boolean accept(final Object node) {
if (node instanceof BreakpointNode) {
//noinspection unchecked
list.add(((BreakpointNode<B>)node).getBreakpoint());
}
return true;
}
});
}
return list;
}
public void selectBreakpoint(final B breakpoint) {
BreakpointNode<B> node = myNodes.get(breakpoint);
if (node != null) {
TreeUtil.selectNode(this, node);
}
}
private static class BreakpointsTreeCellRenderer extends CheckboxTreeCellRenderer {
@Override
public void customizeRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
if (value instanceof BreakpointNode) {
BreakpointNode node = (BreakpointNode)value;
XBreakpoint breakpoint = node.getBreakpoint();
String text = XBreakpointUtil.getDisplayText(breakpoint);
getTextRenderer().setIcon(node.getIcon());
getTextRenderer().append(text, node.getTextAttributes());
}
else if (value instanceof BreakpointsGroupNode) {
XBreakpointGroup group = ((BreakpointsGroupNode)value).getGroup();
getTextRenderer().setIcon(group.getIcon(expanded));
getTextRenderer().append(group.getName(), SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES);
}
}
}
private static class BreakpointsGroupNode<G extends XBreakpointGroup> extends CheckedTreeNode {
private final G myGroup;
private final int myLevel;
private BreakpointsGroupNode(G group, int level) {
super(group);
myLevel = level;
setChecked(false);
myGroup = group;
}
public G getGroup() {
return myGroup;
}
public int getLevel() {
return myLevel;
}
}
private static class BreakpointNode<B extends XBreakpoint<?>> extends CheckedTreeNode {
private final B myBreakpoint;
private BreakpointNode(final B breakpoint) {
super(breakpoint);
myBreakpoint = breakpoint;
setChecked(breakpoint.isEnabled());
}
public B getBreakpoint() {
return myBreakpoint;
}
public Icon getIcon() {
XBreakpointType type = myBreakpoint.getType();
return isChecked() ? type.getEnabledIcon() : type.getDisabledIcon();
}
public SimpleTextAttributes getTextAttributes() {
return isChecked() ? SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES : SimpleTextAttributes.GRAYED_ATTRIBUTES;
}
}
private static class TreeNodeComparator<B extends XBreakpoint<?>> implements Comparator<TreeNode> {
private final Comparator<B> myBreakpointComparator;
private final XBreakpointManager myBreakpointManager;
public TreeNodeComparator(final XBreakpointType<B, ?> type, XBreakpointManager breakpointManager) {
myBreakpointManager = breakpointManager;
myBreakpointComparator = type.getBreakpointComparator();
}
public int compare(final TreeNode o1, final TreeNode o2) {
if (o1 instanceof BreakpointNode && o2 instanceof BreakpointNode) {
//noinspection unchecked
B b1 = (B)((BreakpointNode)o1).getBreakpoint();
//noinspection unchecked
B b2 = (B)((BreakpointNode)o2).getBreakpoint();
boolean default1 = myBreakpointManager.isDefaultBreakpoint(b1);
boolean default2 = myBreakpointManager.isDefaultBreakpoint(b2);
if (default1 && !default2) return -1;
if (!default1 && default2) return 1;
return myBreakpointComparator.compare(b1, b2);
}
if (o1 instanceof BreakpointsGroupNode && o2 instanceof BreakpointsGroupNode) {
final BreakpointsGroupNode group1 = (BreakpointsGroupNode)o1;
final BreakpointsGroupNode group2 = (BreakpointsGroupNode)o2;
if (group1.getLevel() != group2.getLevel()) {
return group1.getLevel() - group2.getLevel();
}
return group1.getGroup().compareTo(group2.getGroup());
}
return o1 instanceof BreakpointsGroupNode ? -1 : 1;
}
}
}