/* * * This file is part of antro, the line-level profiler for ant build scripts. * * antro is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * antro is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with antro. If not, see <http://www.gnu.org/licenses/>. */ package ru.jkff.antro.ui; import org.jdesktop.swingx.JXTree; import ru.jkff.antro.OurLocation; import ru.jkff.antro.Report; import ru.jkff.antro.Trace; import javax.swing.*; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.Enumeration; import java.util.IdentityHashMap; /** * Created on 17:32:49 17.03.2008 * * @author jkff */ public class TraceView extends JPanel { private Report report; private JXTree tree; private LineView lineView; private IdentityHashMap<Trace, DefaultMutableTreeNode> nodes; private static final Color HIGHLIGHTED_FG_COLOR = new Color(51, 0, 102); private static final Color HIGHLIGHTED_BG_COLOR = new Color(204, 153, 255); private static final Color GRAYED_FG_COLOR = Color.GRAY; private Mode mode; private BarView barView; public TraceView() { } public void highlightLineUsages(OurLocation location) { highlightUsages(TracePredicates.byLocation(location)); } public void highlightUsages(Predicate<Trace> whichNodesToSelect) { setPredicateHighlighter(whichNodesToSelect); expandNodesAccordingToPredicate(whichNodesToSelect); repaint(); } private void expandNodesAccordingToPredicate(Predicate<Trace> whichNodesToSelect) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getModel().getRoot(); expandRecursively(node, whichNodesToSelect); } private void expandRecursively( DefaultMutableTreeNode node, Predicate<Trace> expandOrNot) { Trace trace = (Trace) node.getUserObject(); if(expandOrNot.fits(trace)) { tree.expandPath(new TreePath(node.getPath()).getParentPath()); } // No else branch! Don't collapse nodes that are already expanded Enumeration children = node.children(); while(children.hasMoreElements()) { DefaultMutableTreeNode child = (DefaultMutableTreeNode) children.nextElement(); expandRecursively(child, expandOrNot); } } private void setPredicateHighlighter(Predicate<Trace> whichNodesToSelect) { tree.setCellRenderer(createHighlightingCellRenderer( createPredicateHighlighter(whichNodesToSelect))); } public void load(Report report) { unload(); this.report = report; this.nodes = new IdentityHashMap<Trace, DefaultMutableTreeNode>(); tree = new JXTree(toNode(report.getTrace())); mode = Mode.PERCENT_PARENT; tree.setCellRenderer(createDefaultCellRenderer()); tree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { TreePath path = e.getNewLeadSelectionPath(); if(path == null) return; Trace trace = (Trace) (((DefaultMutableTreeNode)path.getLastPathComponent()).getUserObject()); barView.focusOn(trace); } }); JScrollPane scrollPane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); scrollPane.getViewport().add(tree); this.setLayout(new BorderLayout()); this.add(scrollPane, BorderLayout.CENTER); this.registerKeyboardAction( new JumpToSourceLineAction(), "Jump to source", KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0, true), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); this.registerKeyboardAction( new FindUsagesOfSameLineAction(), "Find usages of same line", KeyStroke.getKeyStroke(KeyEvent.VK_F7, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK, true), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); this.registerKeyboardAction( new ToDefaultModeAction(), "To default mode", KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); } private Trace getTrace(TreeNode node) { return (Trace) ((DefaultMutableTreeNode)node).getUserObject(); } private TreeCellRenderer createDefaultCellRenderer() { return new TraceCellRenderer(mode, report, new NodeHighlighter() { public NodeStyle getStyle(TreeNode node) { return new NodeStyle(Color.BLACK, Color.WHITE, TextAttributes.make()); } }); } private NodeHighlighter createPredicateHighlighter(final Predicate<Trace> predicate) { return new NodeHighlighter() { public NodeStyle getStyle(TreeNode node) { Trace trace = getTrace(node); if(predicate.fits(trace)) { return new NodeStyle(HIGHLIGHTED_FG_COLOR, HIGHLIGHTED_BG_COLOR, TextAttributes.make().bold()); } else { return new NodeStyle(GRAYED_FG_COLOR, Color.WHITE, TextAttributes.make()); } } }; } private TreeCellRenderer createHighlightingCellRenderer(NodeHighlighter highlighter) { return new TraceCellRenderer(mode, report, highlighter); } private OurLocation getSelectedLocation() { if(tree.getSelectionCount() == 0) return null; TreePath selPath = tree.getSelectionPaths()[0]; Trace trace = (Trace) ((DefaultMutableTreeNode)selPath.getLastPathComponent()).getUserObject(); return trace.getCall().location; } public void jumpToSourceLine() { OurLocation loc = getSelectedLocation(); if(loc == null) return; if(lineView != null) { lineView.jumpToLine(loc.fileName, loc.line); } } public void findUsagesOfSameLine() { highlightLineUsages(getSelectedLocation()); } public void toDefaultMode() { tree.setCellRenderer(createDefaultCellRenderer()); repaint(); } public void setLineView(LineView lineView) { this.lineView = lineView; } public void setBarView(BarView barView) { this.barView = barView; } public void select(Trace t) { DefaultMutableTreeNode node = nodes.get(t); TreePath path = new TreePath(node.getPath()); tree.expandPath(path.getParentPath()); tree.scrollPathToVisible(path); tree.setSelectionPath(path); } private class JumpToSourceLineAction implements ActionListener { public void actionPerformed(ActionEvent e) { jumpToSourceLine(); } } private class FindUsagesOfSameLineAction implements ActionListener { public void actionPerformed(ActionEvent e) { findUsagesOfSameLine(); } } private class ToDefaultModeAction implements ActionListener { public void actionPerformed(ActionEvent e) { toDefaultMode(); } } private MutableTreeNode toNode(Trace trace) { DefaultMutableTreeNode res = new DefaultMutableTreeNode(); res.setUserObject(trace); for (Trace child : trace.getChildren()) { res.add(toNode(child)); } nodes.put(trace, res); return res; } private void unload() { this.removeAll(); this.report = null; } }