IDEA-50121
[fedora-idea.git] / java / java-impl / src / com / intellij / slicer / SlicePanel.java
blob5abdc99d33d1d95f889fb2a01601f8fc1c15e6cc
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.slicer;
18 import com.intellij.ide.IdeBundle;
19 import com.intellij.ide.actions.CloseTabToolbarAction;
20 import com.intellij.ide.util.treeView.AbstractTreeNode;
21 import com.intellij.openapi.Disposable;
22 import com.intellij.openapi.actionSystem.*;
23 import com.intellij.openapi.application.ApplicationManager;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.ui.Splitter;
26 import com.intellij.openapi.util.Disposer;
27 import com.intellij.openapi.util.IconLoader;
28 import com.intellij.pom.Navigatable;
29 import com.intellij.ui.*;
30 import com.intellij.ui.treeStructure.Tree;
31 import com.intellij.usageView.UsageInfo;
32 import com.intellij.usageView.UsageViewBundle;
33 import com.intellij.usages.Usage;
34 import com.intellij.usages.UsageViewSettings;
35 import com.intellij.usages.impl.UsagePreviewPanel;
36 import com.intellij.util.EditSourceOnDoubleClickHandler;
37 import com.intellij.util.ui.UIUtil;
38 import com.intellij.util.ui.tree.TreeUtil;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.TestOnly;
42 import javax.swing.*;
43 import javax.swing.event.TreeSelectionEvent;
44 import javax.swing.event.TreeSelectionListener;
45 import javax.swing.tree.DefaultMutableTreeNode;
46 import javax.swing.tree.DefaultTreeModel;
47 import javax.swing.tree.TreePath;
48 import javax.swing.tree.TreeSelectionModel;
49 import java.awt.*;
50 import java.awt.event.KeyAdapter;
51 import java.awt.event.KeyEvent;
52 import java.util.ArrayList;
53 import java.util.Collections;
54 import java.util.List;
56 /**
57 * @author cdr
59 public abstract class SlicePanel extends JPanel implements TypeSafeDataProvider, Disposable {
60 private final SliceTreeBuilder myBuilder;
61 private final JTree myTree;
63 private final AutoScrollToSourceHandler myAutoScrollToSourceHandler = new AutoScrollToSourceHandler() {
64 protected boolean isAutoScrollMode() {
65 return isAutoScroll();
68 protected void setAutoScrollMode(final boolean state) {
69 setAutoScroll(state);
72 private UsagePreviewPanel myUsagePreviewPanel;
73 private final Project myProject;
74 private boolean isDisposed;
76 public SlicePanel(Project project, boolean dataFlowToThis, SliceNode rootNode, boolean splitByLeafExpressions) {
77 super(new BorderLayout());
78 myProject = project;
79 myTree = createTree();
81 myBuilder = new SliceTreeBuilder(myTree, project, dataFlowToThis, rootNode, splitByLeafExpressions);
82 myBuilder.setCanYieldUpdate(!ApplicationManager.getApplication().isUnitTestMode());
84 Disposer.register(this, myBuilder);
86 myBuilder.addSubtreeToUpdate((DefaultMutableTreeNode)myTree.getModel().getRoot(), new Runnable() {
87 public void run() {
88 if (isDisposed || myBuilder.isDisposed() || myProject.isDisposed()) return;
89 final SliceNode rootNode = myBuilder.getRootSliceNode();
90 myBuilder.expand(rootNode, new Runnable() {
91 public void run() {
92 if (isDisposed || myBuilder.isDisposed() || myProject.isDisposed()) return;
93 myBuilder.select(rootNode.myCachedChildren.get(0)); //first there is ony one child
95 });
96 treeSelectionChanged();
98 });
100 layoutPanel();
103 private void layoutPanel() {
104 if (myUsagePreviewPanel != null) {
105 Disposer.dispose(myUsagePreviewPanel);
107 removeAll();
108 if (isPreview()) {
109 Splitter splitter = new Splitter(false, UsageViewSettings.getInstance().PREVIEW_USAGES_SPLITTER_PROPORTIONS);
110 splitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree));
111 myUsagePreviewPanel = new UsagePreviewPanel(myProject);
112 Disposer.register(this, myUsagePreviewPanel);
113 splitter.setSecondComponent(myUsagePreviewPanel);
114 add(splitter, BorderLayout.CENTER);
116 else {
117 add(ScrollPaneFactory.createScrollPane(myTree), BorderLayout.CENTER);
120 add(createToolbar().getComponent(), BorderLayout.WEST);
122 revalidate();
125 public void dispose() {
126 if (myUsagePreviewPanel != null) {
127 UsageViewSettings.getInstance().PREVIEW_USAGES_SPLITTER_PROPORTIONS = ((Splitter)myUsagePreviewPanel.getParent()).getProportion();
128 myUsagePreviewPanel = null;
131 isDisposed = true;
132 ToolTipManager.sharedInstance().unregisterComponent(myTree);
135 private JTree createTree() {
136 DefaultMutableTreeNode root = new DefaultMutableTreeNode();
137 final JTree tree = new Tree(new DefaultTreeModel(root)){
138 protected void paintComponent(Graphics g) {
139 DuplicateNodeRenderer.paintDuplicateNodesBackground(g, this);
140 super.paintComponent(g);
143 tree.setOpaque(false);
145 tree.setToggleClickCount(-1);
146 SliceUsageCellRenderer renderer = new SliceUsageCellRenderer();
147 renderer.setOpaque(false);
148 tree.setCellRenderer(renderer);
149 UIUtil.setLineStyleAngled(tree);
150 tree.setRootVisible(false);
152 tree.setShowsRootHandles(true);
153 tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
154 tree.setSelectionPath(new TreePath(root.getPath()));
155 //ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(IdeActions.GROUP_METHOD_HIERARCHY_POPUP);
156 //PopupHandler.installPopupHandler(tree, group, ActionPlaces.METHOD_HIERARCHY_VIEW_POPUP, ActionManager.getInstance());
157 EditSourceOnDoubleClickHandler.install(tree);
159 new TreeSpeedSearch(tree);
160 TreeUtil.installActions(tree);
161 TreeToolTipHandler.install(tree);
162 ToolTipManager.sharedInstance().registerComponent(tree);
164 myAutoScrollToSourceHandler.install(tree);
166 tree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
167 public void valueChanged(final TreeSelectionEvent e) {
168 treeSelectionChanged();
172 tree.addKeyListener(new KeyAdapter() {
173 public void keyPressed(KeyEvent e) {
174 if (KeyEvent.VK_ENTER == e.getKeyCode()) {
175 List<Navigatable> navigatables = getNavigatables();
176 if (navigatables.isEmpty()) return;
177 for (Navigatable navigatable : navigatables) {
178 if (navigatable instanceof AbstractTreeNode && ((AbstractTreeNode)navigatable).getValue() instanceof Usage) {
179 navigatable = (Usage)((AbstractTreeNode)navigatable).getValue();
181 if (navigatable.canNavigateToSource()) {
182 navigatable.navigate(false);
183 if (navigatable instanceof Usage) {
184 ((Usage)navigatable).highlightInEditor();
188 e.consume();
193 return tree;
196 private void treeSelectionChanged() {
197 SwingUtilities.invokeLater(new Runnable() {
198 public void run() {
199 if (isDisposed) return;
200 List<UsageInfo> infos = getSelectedUsageInfos();
201 if (infos != null && myUsagePreviewPanel != null) {
202 myUsagePreviewPanel.updateLayout(infos);
208 private List<UsageInfo> getSelectedUsageInfos() {
209 TreePath[] paths = myTree.getSelectionPaths();
210 if (paths == null) return null;
211 final ArrayList<UsageInfo> result = new ArrayList<UsageInfo>();
212 for (TreePath path : paths) {
213 Object lastPathComponent = path.getLastPathComponent();
214 if (lastPathComponent instanceof DefaultMutableTreeNode) {
215 DefaultMutableTreeNode node = (DefaultMutableTreeNode)lastPathComponent;
216 Object userObject = node.getUserObject();
217 if (userObject instanceof SliceNode) {
218 result.add(((SliceNode)userObject).getValue().getUsageInfo());
222 if (result.isEmpty()) return null;
223 return result;
226 public void calcData(DataKey key, DataSink sink) {
227 if (key == PlatformDataKeys.NAVIGATABLE_ARRAY) {
228 List<Navigatable> navigatables = getNavigatables();
229 if (!navigatables.isEmpty()) {
230 sink.put(PlatformDataKeys.NAVIGATABLE_ARRAY, navigatables.toArray(new Navigatable[navigatables.size()]));
235 @NotNull
236 private List<Navigatable> getNavigatables() {
237 TreePath[] paths = myTree.getSelectionPaths();
238 if (paths == null) return Collections.emptyList();
239 final ArrayList<Navigatable> navigatables = new ArrayList<Navigatable>();
240 for (TreePath path : paths) {
241 Object lastPathComponent = path.getLastPathComponent();
242 if (lastPathComponent instanceof DefaultMutableTreeNode) {
243 DefaultMutableTreeNode node = (DefaultMutableTreeNode)lastPathComponent;
244 Object userObject = node.getUserObject();
245 if (userObject instanceof Navigatable) {
246 navigatables.add((Navigatable)userObject);
248 else if (node instanceof Navigatable) {
249 navigatables.add((Navigatable)node);
253 return navigatables;
256 private ActionToolbar createToolbar() {
257 final DefaultActionGroup actionGroup = new DefaultActionGroup();
258 actionGroup.add(new RefreshAction(myTree));
259 actionGroup.add(myAutoScrollToSourceHandler.createToggleAction());
260 actionGroup.add(new CloseAction());
261 actionGroup.add(new ToggleAction(UsageViewBundle.message("preview.usages.action.text"), "preview", IconLoader.getIcon("/actions/preview.png")) {
262 public boolean isSelected(AnActionEvent e) {
263 return isPreview();
266 public void setSelected(AnActionEvent e, boolean state) {
267 setPreview(state);
268 layoutPanel();
272 if (myBuilder.dataFlowToThis) {
273 actionGroup.add(new AnalyzeLeavesAction(myBuilder));
274 actionGroup.add(new CanItBeNullAction(myBuilder));
277 //actionGroup.add(new ContextHelpAction(HELP_ID));
279 return ActionManager.getInstance().createActionToolbar(ActionPlaces.TYPE_HIERARCHY_VIEW_TOOLBAR, actionGroup, false);
282 public abstract boolean isAutoScroll();
284 public abstract void setAutoScroll(boolean autoScroll);
286 public abstract boolean isPreview();
288 public abstract void setPreview(boolean preview);
290 private class CloseAction extends CloseTabToolbarAction {
291 public final void actionPerformed(final AnActionEvent e) {
292 close();
296 protected abstract void close();
298 private final class RefreshAction extends com.intellij.ide.actions.RefreshAction {
299 private RefreshAction(JComponent tree) {
300 super(IdeBundle.message("action.refresh"), IdeBundle.message("action.refresh"), IconLoader.getIcon("/actions/sync.png"));
301 registerShortcutOn(tree);
304 public final void actionPerformed(final AnActionEvent e) {
305 SliceNode rootNode = (SliceNode)myBuilder.getRootNode().getUserObject();
306 rootNode.setChanged();
307 myBuilder.addSubtreeToUpdate(myBuilder.getRootNode());
310 public final void update(final AnActionEvent event) {
311 final Presentation presentation = event.getPresentation();
312 presentation.setEnabled(true);
316 @TestOnly
317 public SliceTreeBuilder getBuilder() {
318 return myBuilder;