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
;
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
;
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
;
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
) {
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());
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() {
88 if (isDisposed
|| myBuilder
.isDisposed() || myProject
.isDisposed()) return;
89 final SliceNode rootNode
= myBuilder
.getRootSliceNode();
90 myBuilder
.expand(rootNode
, new Runnable() {
92 if (isDisposed
|| myBuilder
.isDisposed() || myProject
.isDisposed()) return;
93 myBuilder
.select(rootNode
.myCachedChildren
.get(0)); //first there is ony one child
96 treeSelectionChanged();
103 private void layoutPanel() {
104 if (myUsagePreviewPanel
!= null) {
105 Disposer
.dispose(myUsagePreviewPanel
);
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
);
117 add(ScrollPaneFactory
.createScrollPane(myTree
), BorderLayout
.CENTER
);
120 add(createToolbar().getComponent(), BorderLayout
.WEST
);
125 public void dispose() {
126 if (myUsagePreviewPanel
!= null) {
127 UsageViewSettings
.getInstance().PREVIEW_USAGES_SPLITTER_PROPORTIONS
= ((Splitter
)myUsagePreviewPanel
.getParent()).getProportion();
128 myUsagePreviewPanel
= null;
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();
196 private void treeSelectionChanged() {
197 SwingUtilities
.invokeLater(new Runnable() {
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;
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()]));
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
);
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
) {
266 public void setSelected(AnActionEvent e
, boolean state
) {
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
) {
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);
317 public SliceTreeBuilder
getBuilder() {