blob: 9d3f34c71430b94b4d215c0af7666c44062bf2f9 [file] [log] [blame]
/*
* Copyright 2000-2012 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.tree;
import com.intellij.ide.util.treeView.TreeState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.MultiValuesMap;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.util.ui.tree.TreeUtil;
import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroup;
import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointItem;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.util.*;
/**
* @author nik, zajac
*/
public class BreakpointItemsTreeController implements BreakpointsCheckboxTree.Delegate {
private final TreeNodeComparator myComparator = new TreeNodeComparator();
private final CheckedTreeNode myRoot;
private final Map<BreakpointItem, BreakpointItemNode> myNodes = new HashMap<BreakpointItem, BreakpointItemNode>();
private List<XBreakpointGroupingRule> myGroupingRules;
private final MultiValuesMap<XBreakpointGroupingRule, XBreakpointGroup> myGroups = new MultiValuesMap<XBreakpointGroupingRule, XBreakpointGroup>();
private JTree myTreeView;
protected boolean myInBuild;
public BreakpointItemsTreeController(Collection<XBreakpointGroupingRule> groupingRules) {
myRoot = new CheckedTreeNode("root");
setGroupingRulesInternal(groupingRules);
}
public JTree getTreeView() {
return myTreeView;
}
public void setTreeView(JTree treeView) {
myTreeView = treeView;
myTreeView.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent event) {
selectionChanged();
}
});
if (treeView instanceof BreakpointsCheckboxTree) {
((BreakpointsCheckboxTree)treeView).setDelegate(this);
}
myTreeView.setShowsRootHandles(!myGroupingRules.isEmpty());
}
protected void selectionChanged() {
if (myInBuild) return;
selectionChangedImpl();
}
protected void selectionChangedImpl() {
}
@Override
public void nodeStateDidChange(CheckedTreeNode node) {
if (myInBuild) return;
nodeStateDidChangeImpl(node);
}
protected void nodeStateDidChangeImpl(CheckedTreeNode node) {
if (node instanceof BreakpointItemNode) {
((BreakpointItemNode)node).getBreakpointItem().setEnabled(node.isChecked());
}
}
@Override
public void nodeStateWillChange(CheckedTreeNode node) {
if (myInBuild) return;
nodeStateWillChangeImpl(node);
}
protected void nodeStateWillChangeImpl(CheckedTreeNode node) {
}
private void setGroupingRulesInternal(final Collection<XBreakpointGroupingRule> groupingRules) {
myGroupingRules = new ArrayList<XBreakpointGroupingRule>(groupingRules);
}
public void buildTree(@NotNull Collection<? extends BreakpointItem> breakpoints) {
final TreeState state = TreeState.createOn(myTreeView, myRoot);
myRoot.removeAllChildren();
myNodes.clear();
myGroups.clear();
for (BreakpointItem breakpoint : breakpoints) {
BreakpointItemNode node = new BreakpointItemNode(breakpoint);
CheckedTreeNode parent = getParentNode(breakpoint);
parent.add(node);
myNodes.put(breakpoint, node);
}
TreeUtil.sort(myRoot, myComparator);
myInBuild = true;
((DefaultTreeModel)(myTreeView.getModel())).nodeStructureChanged(myRoot);
state.applyTo(myTreeView, myRoot);
TreeUtil.expandAll(myTreeView);
myInBuild = false;
}
@NotNull
private CheckedTreeNode getParentNode(final BreakpointItem breakpoint) {
CheckedTreeNode parent = myRoot;
for (int i = 0; i < myGroupingRules.size(); i++) {
XBreakpointGroup group = myGroupingRules.get(i).getGroup(breakpoint.getBreakpoint(), Collections.emptyList());
if (group != null) {
parent = getOrCreateGroupNode(parent, group, i);
if (breakpoint.isEnabled()) {
parent.setChecked(true);
}
}
}
return parent;
}
private static Collection<XBreakpointGroup> getGroupNodes(CheckedTreeNode parent) {
Collection<XBreakpointGroup> nodes = new ArrayList<XBreakpointGroup>();
Enumeration children = parent.children();
while (children.hasMoreElements()) {
Object element = children.nextElement();
if (element instanceof BreakpointsGroupNode) {
nodes.add(((BreakpointsGroupNode)element).getGroup());
}
}
return nodes;
}
private static BreakpointsGroupNode getOrCreateGroupNode(CheckedTreeNode parent, final XBreakpointGroup group,
final int level) {
Enumeration children = parent.children();
while (children.hasMoreElements()) {
Object element = children.nextElement();
if (element instanceof BreakpointsGroupNode) {
XBreakpointGroup groupFound = ((BreakpointsGroupNode)element).getGroup();
if (groupFound.equals(group)) {
return (BreakpointsGroupNode)element;
}
}
}
BreakpointsGroupNode groupNode = new BreakpointsGroupNode<XBreakpointGroup>(group, level);
parent.add(groupNode);
return groupNode;
}
public void setGroupingRules(Collection<XBreakpointGroupingRule> groupingRules) {
setGroupingRulesInternal(groupingRules);
rebuildTree(new ArrayList<BreakpointItem>(myNodes.keySet()));
}
public void rebuildTree(Collection<BreakpointItem> items) {
TreePath path = myTreeView.getSelectionPath();
buildTree(items);
selectBreakpointItem(null, path);
}
public List<BreakpointItem> getSelectedBreakpoints() {
TreePath[] selectionPaths = myTreeView.getSelectionPaths();
if (selectionPaths == null || selectionPaths.length == 0) return Collections.emptyList();
final ArrayList<BreakpointItem> list = new ArrayList<BreakpointItem>();
for (TreePath selectionPath : selectionPaths) {
TreeUtil.traverseDepth((TreeNode)selectionPath.getLastPathComponent(), new TreeUtil.Traverse() {
public boolean accept(final Object node) {
if (node instanceof BreakpointItemNode) {
list.add(((BreakpointItemNode)node).getBreakpointItem());
}
return true;
}
});
}
return list;
}
public void selectBreakpointItem(@Nullable final BreakpointItem breakpoint, TreePath path) {
BreakpointItemNode node = myNodes.get(breakpoint);
if (node != null) {
TreeUtil.selectNode(myTreeView, node);
}
else {
TreeUtil.selectPath(myTreeView, path);
}
}
public CheckedTreeNode getRoot() {
return myRoot;
}
public void selectFirstBreakpointItem() {
TreeUtil.selectPath(myTreeView, TreeUtil.getFirstLeafNodePath(myTreeView));
}
public void removeSelectedBreakpoints(Project project) {
final TreePath[] paths = myTreeView.getSelectionPaths();
if (paths == null) return;
final List<BreakpointItem> breakpoints = getSelectedBreakpoints();
for (TreePath path : paths) {
final Object node = path.getLastPathComponent();
if (node instanceof BreakpointItemNode) {
final BreakpointItem item = ((BreakpointItemNode)node).getBreakpointItem();
if (!item.allowedToRemove()) {
TreeUtil.unselect(myTreeView, (DefaultMutableTreeNode)node);
breakpoints.remove(item);
}
}
}
if (breakpoints.isEmpty()) return;
TreeUtil.removeSelected(myTreeView);
for (BreakpointItem breakpoint : breakpoints) {
breakpoint.removed(project);
}
}
private static class TreeNodeComparator implements Comparator<TreeNode> {
public int compare(final TreeNode o1, final TreeNode o2) {
if (o1 instanceof BreakpointItemNode && o2 instanceof BreakpointItemNode) {
//noinspection unchecked
BreakpointItem b1 = ((BreakpointItemNode)o1).getBreakpointItem();
//noinspection unchecked
BreakpointItem b2 = ((BreakpointItemNode)o2).getBreakpointItem();
boolean default1 = b1.isDefaultBreakpoint();
boolean default2 = b2.isDefaultBreakpoint();
if (default1 && !default2) return -1;
if (!default1 && default2) return 1;
return b1.compareTo(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;
}
}
}