4 package com
.intellij
.ide
.projectView
.impl
;
6 import com
.intellij
.ide
.DataManager
;
7 import com
.intellij
.ide
.PsiCopyPasteManager
;
8 import com
.intellij
.ide
.SelectInTarget
;
9 import com
.intellij
.ide
.dnd
.aware
.DnDAwareTree
;
10 import com
.intellij
.ide
.favoritesTreeView
.FavoritesTreeViewPanel
;
11 import com
.intellij
.ide
.projectView
.BaseProjectTreeBuilder
;
12 import com
.intellij
.ide
.projectView
.ProjectView
;
13 import com
.intellij
.ide
.projectView
.impl
.nodes
.AbstractModuleNode
;
14 import com
.intellij
.ide
.projectView
.impl
.nodes
.AbstractProjectNode
;
15 import com
.intellij
.ide
.projectView
.impl
.nodes
.ModuleGroupNode
;
16 import com
.intellij
.ide
.util
.treeView
.*;
17 import com
.intellij
.openapi
.Disposable
;
18 import com
.intellij
.openapi
.actionSystem
.DataConstants
;
19 import com
.intellij
.openapi
.actionSystem
.DataContext
;
20 import com
.intellij
.openapi
.actionSystem
.DataProvider
;
21 import com
.intellij
.openapi
.actionSystem
.DefaultActionGroup
;
22 import com
.intellij
.openapi
.application
.ApplicationManager
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.extensions
.ExtensionPointName
;
25 import com
.intellij
.openapi
.module
.Module
;
26 import com
.intellij
.openapi
.project
.Project
;
27 import com
.intellij
.openapi
.roots
.ModuleRootManager
;
28 import com
.intellij
.openapi
.util
.*;
29 import com
.intellij
.openapi
.vfs
.VirtualFile
;
30 import com
.intellij
.openapi
.wm
.ToolWindowId
;
31 import com
.intellij
.openapi
.wm
.ToolWindowManager
;
32 import com
.intellij
.pom
.Navigatable
;
33 import com
.intellij
.psi
.*;
34 import com
.intellij
.util
.ArrayUtil
;
35 import com
.intellij
.util
.ReflectionCache
;
36 import com
.intellij
.util
.containers
.HashMap
;
37 import com
.intellij
.util
.ui
.tree
.TreeUtil
;
38 import org
.jdom
.Element
;
39 import org
.jetbrains
.annotations
.NonNls
;
40 import org
.jetbrains
.annotations
.NotNull
;
41 import org
.jetbrains
.annotations
.Nullable
;
44 import javax
.swing
.tree
.DefaultMutableTreeNode
;
45 import javax
.swing
.tree
.TreeNode
;
46 import javax
.swing
.tree
.TreePath
;
47 import java
.awt
.datatransfer
.DataFlavor
;
48 import java
.awt
.datatransfer
.Transferable
;
49 import java
.awt
.dnd
.*;
50 import java
.util
.ArrayList
;
51 import java
.util
.Collections
;
52 import java
.util
.List
;
56 public abstract class AbstractProjectViewPane
implements DataProvider
, Disposable
{
57 public static ExtensionPointName
<AbstractProjectViewPane
> EP_NAME
= ExtensionPointName
.create("com.intellij.projectViewPane");
59 protected final Project myProject
;
60 private Runnable myTreeChangeListener
;
61 protected DnDAwareTree myTree
;
62 protected AbstractTreeStructure myTreeStructure
;
63 private AbstractTreeBuilder myTreeBuilder
;
64 // subId->Tree state; key may be null
65 private final Map
<String
,TreeState
> myReadTreeState
= new HashMap
<String
, TreeState
>();
66 private String mySubId
;
67 @NonNls private static final String ELEMENT_SUBPANE
= "subPane";
68 @NonNls private static final String ATTRIBUTE_SUBID
= "subId";
70 protected AbstractProjectViewPane(Project project
) {
74 protected final void fireTreeChangeListener() {
75 if (myTreeChangeListener
!= null) myTreeChangeListener
.run();
78 public final void setTreeChangeListener(Runnable listener
) {
79 myTreeChangeListener
= listener
;
82 public final void removeTreeChangeListener() {
83 myTreeChangeListener
= null;
86 public abstract String
getTitle();
87 public abstract Icon
getIcon();
88 @NotNull public abstract String
getId();
89 @Nullable public final String
getSubId(){
93 public final void setSubId(@Nullable String subId
) {
98 public boolean isInitiallyVisible() {
103 * @return all supported sub views IDs.
104 * should return empty array if there is no subViews as in Project/Packages view.
106 @NotNull public String
[] getSubIds(){
107 return ArrayUtil
.EMPTY_STRING_ARRAY
;
110 @NotNull public String
getPresentableSubIdName(@NotNull final String subId
) {
111 throw new IllegalStateException("should not call");
113 public abstract JComponent
createComponent();
114 public JComponent
getComponentToFocus() {
117 public void expand(@Nullable final Object
[] path
, final boolean requestFocus
){
118 if (getTreeBuilder() == null || path
== null) return;
119 getTreeBuilder().buildNodeForPath(path
);
121 DefaultMutableTreeNode node
= getTreeBuilder().getNodeForPath(path
);
125 TreePath treePath
= new TreePath(node
.getPath());
126 myTree
.expandPath(treePath
);
128 myTree
.requestFocus();
130 TreeUtil
.selectPath(myTree
, treePath
);
133 public void dispose() {
134 setTreeBuilder(null);
136 myTreeStructure
= null;
139 public abstract ActionCallback
updateFromRoot(boolean restoreExpandedPaths
);
141 public abstract void select(Object element
, VirtualFile file
, boolean requestFocus
);
142 public void selectModule(final Module module
, final boolean requestFocus
) {
143 doSelectModuleOrGroup(module
, requestFocus
);
146 private void doSelectModuleOrGroup(final Object toSelect
, final boolean requestFocus
) {
147 ToolWindowManager windowManager
=ToolWindowManager
.getInstance(myProject
);
148 final Runnable runnable
= new Runnable() {
150 ProjectView projectView
= ProjectView
.getInstance(myProject
);
152 projectView
.changeView(getId(), getSubId());
154 ((BaseProjectTreeBuilder
)getTreeBuilder()).selectInWidth(toSelect
, requestFocus
, new Condition
<AbstractTreeNode
>(){
155 public boolean value(final AbstractTreeNode node
) {
156 return node
instanceof AbstractModuleNode
|| node
instanceof ModuleGroupNode
|| node
instanceof AbstractProjectNode
;
162 windowManager
.getToolWindow(ToolWindowId
.PROJECT_VIEW
).activate(runnable
);
169 public void selectModuleGroup(ModuleGroup moduleGroup
, boolean requestFocus
) {
170 doSelectModuleOrGroup(moduleGroup
, requestFocus
);
173 public TreePath
[] getSelectionPaths() {
174 return myTree
== null ?
null : myTree
.getSelectionPaths();
177 public void addToolbarActions(DefaultActionGroup actionGroup
) {
181 protected <T
extends NodeDescriptor
> List
<T
> getSelectedNodes(final Class
<T
> nodeClass
){
182 TreePath
[] paths
= getSelectionPaths();
183 if (paths
== null) return Collections
.emptyList();
184 final ArrayList
<T
> result
= new ArrayList
<T
>();
185 for (TreePath path
: paths
) {
186 Object lastPathComponent
= path
.getLastPathComponent();
187 if (lastPathComponent
instanceof DefaultMutableTreeNode
) {
188 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)lastPathComponent
;
189 Object userObject
= node
.getUserObject();
190 if (userObject
!= null && ReflectionCache
.isAssignable(nodeClass
, userObject
.getClass())) {
191 result
.add((T
)userObject
);
198 public Object
getData(String dataId
) {
199 if (DataConstants
.NAVIGATABLE_ARRAY
.equals(dataId
)) {
200 TreePath
[] paths
= getSelectionPaths();
201 if (paths
== null) return null;
202 final ArrayList
<Navigatable
> navigatables
= new ArrayList
<Navigatable
>();
203 for (TreePath path
: paths
) {
204 Object lastPathComponent
= path
.getLastPathComponent();
205 if (lastPathComponent
instanceof DefaultMutableTreeNode
) {
206 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)lastPathComponent
;
207 Object userObject
= node
.getUserObject();
208 if (userObject
instanceof Navigatable
) {
209 navigatables
.add((Navigatable
)userObject
);
211 else if (node
instanceof Navigatable
) {
212 navigatables
.add((Navigatable
)node
);
216 if (navigatables
.isEmpty()) {
220 return navigatables
.toArray(new Navigatable
[navigatables
.size()]);
223 if (myTreeStructure
instanceof AbstractTreeStructureBase
) {
224 return ((AbstractTreeStructureBase
) myTreeStructure
).getDataFromProviders(getSelectedNodes(AbstractTreeNode
.class), dataId
);
229 // used for sorting tabs in the tabbed pane
230 public abstract int getWeight();
232 public abstract SelectInTarget
createSelectInTarget();
234 public final TreePath
getSelectedPath() {
235 final TreePath
[] paths
= getSelectionPaths();
236 if (paths
!= null && paths
.length
== 1) return paths
[0];
240 public final NodeDescriptor
getSelectedDescriptor() {
241 final DefaultMutableTreeNode node
= getSelectedNode();
242 if (node
== null) return null;
243 Object userObject
= node
.getUserObject();
244 if (userObject
instanceof NodeDescriptor
) {
245 return (NodeDescriptor
)userObject
;
250 public final DefaultMutableTreeNode
getSelectedNode() {
251 TreePath path
= getSelectedPath();
255 Object lastPathComponent
= path
.getLastPathComponent();
256 if (!(lastPathComponent
instanceof DefaultMutableTreeNode
)) {
259 return (DefaultMutableTreeNode
)lastPathComponent
;
262 public final Object
getSelectedElement() {
263 final Object
[] elements
= getSelectedElements();
264 return elements
.length
== 1 ? elements
[0] : null;
268 public final PsiElement
[] getSelectedPSIElements() {
269 List
<PsiElement
> psiElements
= new ArrayList
<PsiElement
>();
270 for (Object element
: getSelectedElements()) {
271 final PsiElement psiElement
= getPSIElement(element
);
272 if (psiElement
!= null) {
273 psiElements
.add(psiElement
);
276 return psiElements
.toArray(new PsiElement
[psiElements
.size()]);
280 protected PsiElement
getPSIElement(@Nullable final Object element
) {
281 if (element
instanceof PsiElement
) {
282 PsiElement psiElement
= (PsiElement
)element
;
283 if (psiElement
.isValid()) {
291 public final Object
[] getSelectedElements() {
292 TreePath
[] paths
= getSelectionPaths();
293 if (paths
== null) return PsiElement
.EMPTY_ARRAY
;
294 ArrayList
<Object
> list
= new ArrayList
<Object
>(paths
.length
);
295 for (TreePath path
: paths
) {
296 Object lastPathComponent
= path
.getLastPathComponent();
297 if (lastPathComponent
instanceof TreeNode
) {
298 Object element
= getElement((TreeNode
)lastPathComponent
);
299 if (element
!= null) {
304 return ArrayUtil
.toObjectArray(list
);
308 private Object
getElement(@Nullable final TreeNode treeNode
) {
309 if (treeNode
instanceof DefaultMutableTreeNode
) {
310 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)treeNode
;
311 return exhumeElementFromNode(node
);
316 private TreeNode
[] getSelectedTreeNodes(){
317 TreePath
[] paths
= getSelectionPaths();
318 if (paths
== null) return null;
319 final List
<TreeNode
> result
= new ArrayList
<TreeNode
>();
320 for (TreePath path
: paths
) {
321 Object lastPathComponent
= path
.getLastPathComponent();
322 if (lastPathComponent
instanceof DefaultMutableTreeNode
) {
323 result
.add ( (TreeNode
) lastPathComponent
);
326 return result
.toArray(new TreeNode
[result
.size()]);
330 protected Object
exhumeElementFromNode(final DefaultMutableTreeNode node
) {
331 return extractUserObject(node
);
334 public static Object
extractUserObject(DefaultMutableTreeNode node
) {
335 Object userObject
= node
.getUserObject();
336 Object element
= null;
337 if (userObject
instanceof AbstractTreeNode
) {
338 AbstractTreeNode descriptor
= (AbstractTreeNode
)userObject
;
339 element
= descriptor
.getValue();
341 else if (userObject
instanceof NodeDescriptor
) {
342 NodeDescriptor descriptor
= (NodeDescriptor
)userObject
;
343 element
= descriptor
.getElement();
344 if (element
instanceof AbstractTreeNode
) {
345 element
= ((AbstractTreeNode
)element
).getValue();
348 else if (userObject
!= null) {
349 element
= userObject
;
354 public AbstractTreeBuilder
getTreeBuilder() {
355 return myTreeBuilder
;
358 public void readExternal(Element element
) throws InvalidDataException
{
359 List
<Element
> subPanes
= element
.getChildren(ELEMENT_SUBPANE
);
360 for (Element subPane
: subPanes
) {
361 String subId
= subPane
.getAttributeValue(ATTRIBUTE_SUBID
);
362 TreeState treeState
= new TreeState();
363 treeState
.readExternal(subPane
);
364 myReadTreeState
.put(subId
, treeState
);
368 public void writeExternal(Element element
) throws WriteExternalException
{
370 for (String subId
: myReadTreeState
.keySet()) {
371 TreeState treeState
= myReadTreeState
.get(subId
);
372 Element subPane
= new Element(ELEMENT_SUBPANE
);
374 subPane
.setAttribute(ATTRIBUTE_SUBID
, subId
);
376 treeState
.writeExternal(subPane
);
377 element
.addContent(subPane
);
381 void saveExpandedPaths() {
382 if (myTree
!= null) {
383 TreeState treeState
= TreeState
.createOn(myTree
);
384 myReadTreeState
.put(getSubId(), treeState
);
388 public final void restoreExpandedPaths(){
389 TreeState treeState
= myReadTreeState
.get(getSubId());
390 if (treeState
!= null) {
391 treeState
.applyTo(myTree
);
395 public void installComparator() {
396 final ProjectView projectView
= ProjectView
.getInstance(myProject
);
397 getTreeBuilder().setNodeDescriptorComparator(new GroupByTypeComparator(projectView
, getId()));
400 public JTree
getTree() {
404 public PsiDirectory
[] getSelectedDirectories() {
405 final PsiElement
[] elements
= getSelectedPSIElements();
406 if (elements
.length
== 1) {
407 final PsiElement element
= elements
[0];
408 if (element
instanceof PsiDirectory
) {
409 return new PsiDirectory
[]{(PsiDirectory
)element
};
411 else if (element
instanceof PsiDirectoryContainer
) {
412 return ((PsiDirectoryContainer
)element
).getDirectories();
415 final PsiFile containingFile
= element
.getContainingFile();
416 if (containingFile
!= null) {
417 final PsiDirectory psiDirectory
= containingFile
.getContainingDirectory();
418 return psiDirectory
!= null ?
new PsiDirectory
[]{psiDirectory
} : PsiDirectory
.EMPTY_ARRAY
;
423 final DefaultMutableTreeNode selectedNode
= getSelectedNode();
424 if (selectedNode
!= null) {
425 return getSelectedDirectoriesInAmbiguousCase(selectedNode
);
428 return PsiDirectory
.EMPTY_ARRAY
;
431 protected PsiDirectory
[] getSelectedDirectoriesInAmbiguousCase(@NotNull final DefaultMutableTreeNode node
) {
432 final Object userObject
= node
.getUserObject();
433 if (userObject
instanceof AbstractModuleNode
) {
434 final ModuleRootManager moduleRootManager
= ModuleRootManager
.getInstance(((AbstractModuleNode
)userObject
).getValue());
435 final VirtualFile
[] sourceRoots
= moduleRootManager
.getSourceRoots();
436 List
<PsiDirectory
> dirs
= new ArrayList
<PsiDirectory
>(sourceRoots
.length
);
437 final PsiManager psiManager
= PsiManager
.getInstance(myProject
);
438 for (final VirtualFile sourceRoot
: sourceRoots
) {
439 final PsiDirectory directory
= psiManager
.findDirectory(sourceRoot
);
440 if (directory
!= null) {
444 return dirs
.toArray(new PsiDirectory
[dirs
.size()]);
446 return PsiDirectory
.EMPTY_ARRAY
;
451 public static final DataFlavor
[] FLAVORS
;
452 private static final Logger LOG
= Logger
.getInstance("com.intellij.ide.projectView.ProjectViewImpl");
453 private final MyDragSourceListener myDragSourceListener
= new MyDragSourceListener();
456 DataFlavor
[] flavors
;
458 final Class aClass
= MyTransferable
.class;
459 flavors
= new DataFlavor
[]{new DataFlavor(
460 DataFlavor
.javaJVMLocalObjectMimeType
+ ";class=" + aClass
.getName(),
461 FavoritesTreeViewPanel
.ABSTRACT_TREE_NODE_TRANSFERABLE
,
462 aClass
.getClassLoader()
465 catch (ClassNotFoundException e
) {
466 LOG
.error(e
); // should not happen
467 flavors
= new DataFlavor
[0];
472 protected void enableDnD() {
473 if (!ApplicationManager
.getApplication().isHeadlessEnvironment()) {
474 DragSource
.getDefaultDragSource().createDefaultDragGestureRecognizer(myTree
, DnDConstants
.ACTION_COPY_OR_MOVE
, new MyDragGestureListener());
475 new DropTarget(myTree
, new MoveDropTargetListener(new PsiRetriever() {
477 public PsiElement
getPsiElement(@Nullable final TreeNode node
) {
478 return getPSIElement(getElement(node
));
480 }, myTree
, myProject
, FLAVORS
[0]));
482 myTree
.enableDnd(this);
486 public void setTreeBuilder(final AbstractTreeBuilder treeBuilder
) {
487 if (treeBuilder
!= null) {
488 Disposer
.register(this, treeBuilder
);
489 // needs refactoring for project view first
490 // treeBuilder.setCanYieldUpdate(true);
492 myTreeBuilder
= treeBuilder
;
495 private static class MyTransferable
implements Transferable
{
496 private final Object myTransferable
;
498 public MyTransferable(Object transferable
) {
499 myTransferable
= transferable
;
502 public DataFlavor
[] getTransferDataFlavors() {
503 DataFlavor
[] flavors
= new DataFlavor
[2];
504 flavors
[0] = FLAVORS
[0];
505 flavors
[1] = DataFlavor
.javaFileListFlavor
;
509 public boolean isDataFlavorSupported(DataFlavor flavor
) {
510 DataFlavor
[] flavors
= getTransferDataFlavors();
511 return ArrayUtil
.find(flavors
, flavor
) != -1;
514 public Object
getTransferData(DataFlavor flavor
) {
515 if (flavor
== DataFlavor
.javaFileListFlavor
) {
516 TransferableWrapper wrapper
= (TransferableWrapper
) myTransferable
;
517 return PsiCopyPasteManager
.asFileList(wrapper
.getPsiElements());
519 return myTransferable
;
523 public interface TransferableWrapper
{
524 TreeNode
[] getTreeNodes();
525 @Nullable PsiElement
[] getPsiElements();
528 private class MyDragGestureListener
implements DragGestureListener
{
529 public void dragGestureRecognized(DragGestureEvent dge
) {
530 if ((dge
.getDragAction() & DnDConstants
.ACTION_COPY_OR_MOVE
) == 0) return;
531 DataContext dataContext
= DataManager
.getInstance().getDataContext();
532 ProjectView projectView
= (ProjectView
)dataContext
.getData(ProjectViewImpl
.PROJECT_VIEW_DATA_CONSTANT
);
533 if (projectView
== null) return;
535 final AbstractProjectViewPane currentPane
= projectView
.getCurrentProjectViewPane();
536 final TreeNode
[] nodes
= currentPane
.getSelectedTreeNodes();
538 final Object
[] elements
= currentPane
.getSelectedElements();
539 final PsiElement
[] psiElements
= currentPane
.getSelectedPSIElements();
541 Object transferableWrapper
= new TransferableWrapper() {
542 public TreeNode
[] getTreeNodes() {
545 public PsiElement
[] getPsiElements() {
550 //FavoritesManager.getInstance(myProject).getCurrentTreeViewPanel().setDraggableObject(draggableObject.getClass(), draggableObject.getValue());
551 if ((psiElements
!= null && psiElements
.length
> 0) || canDragElements(elements
)) {
552 dge
.startDrag(DragSource
.DefaultMoveNoDrop
, new MyTransferable(transferableWrapper
), myDragSourceListener
);
555 catch (InvalidDnDOperationException idoe
) {
561 private boolean canDragElements(Object
[] elements
) {
562 for (Object element
: elements
) {
563 if (element
instanceof Module
) {
571 private static class MyDragSourceListener
implements DragSourceListener
{
573 public void dragEnter(DragSourceDragEvent dsde
) {
574 dsde
.getDragSourceContext().setCursor(null);
577 public void dragOver(DragSourceDragEvent dsde
) {}
579 public void dropActionChanged(DragSourceDragEvent dsde
) {
580 dsde
.getDragSourceContext().setCursor(null);
583 public void dragDropEnd(DragSourceDropEvent dsde
) { }
585 public void dragExit(DragSourceEvent dse
) { }