TreeUi: initial selection (on maybeReady handler)
[fedora-idea.git] / platform / lang-impl / src / com / intellij / ide / projectView / impl / AbstractProjectViewPane.java
blobf4a4e617e2285547c441079e24b56a54a63f764d
1 /**
2 * @author cdr
3 */
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;
43 import javax.swing.*;
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;
53 import java.util.Map;
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) {
71 myProject = 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(){
90 return mySubId;
93 public final void setSubId(@Nullable String subId) {
94 saveExpandedPaths();
95 mySubId = subId;
98 public boolean isInitiallyVisible() {
99 return true;
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() {
115 return myTree;
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);
122 if (node == null) {
123 return;
125 TreePath treePath = new TreePath(node.getPath());
126 myTree.expandPath(treePath);
127 if (requestFocus) {
128 myTree.requestFocus();
130 TreeUtil.selectPath(myTree, treePath);
133 public void dispose() {
134 setTreeBuilder(null);
135 myTree = 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() {
149 public void run() {
150 ProjectView projectView = ProjectView.getInstance(myProject);
151 if (requestFocus) {
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;
161 if (requestFocus) {
162 windowManager.getToolWindow(ToolWindowId.PROJECT_VIEW).activate(runnable);
164 else {
165 runnable.run();
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) {
180 @NotNull
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);
195 return result;
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()) {
217 return null;
219 else {
220 return navigatables.toArray(new Navigatable[navigatables.size()]);
223 if (myTreeStructure instanceof AbstractTreeStructureBase) {
224 return ((AbstractTreeStructureBase) myTreeStructure).getDataFromProviders(getSelectedNodes(AbstractTreeNode.class), dataId);
226 return null;
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];
237 return null;
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;
247 return null;
250 public final DefaultMutableTreeNode getSelectedNode() {
251 TreePath path = getSelectedPath();
252 if (path == null) {
253 return null;
255 Object lastPathComponent = path.getLastPathComponent();
256 if (!(lastPathComponent instanceof DefaultMutableTreeNode)) {
257 return null;
259 return (DefaultMutableTreeNode)lastPathComponent;
262 public final Object getSelectedElement() {
263 final Object[] elements = getSelectedElements();
264 return elements.length == 1 ? elements[0] : null;
267 @NotNull
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()]);
279 @Nullable
280 protected PsiElement getPSIElement(@Nullable final Object element) {
281 if (element instanceof PsiElement) {
282 PsiElement psiElement = (PsiElement)element;
283 if (psiElement.isValid()) {
284 return psiElement;
287 return null;
290 @NotNull
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) {
300 list.add(element);
304 return ArrayUtil.toObjectArray(list);
307 @Nullable
308 private Object getElement(@Nullable final TreeNode treeNode) {
309 if (treeNode instanceof DefaultMutableTreeNode) {
310 DefaultMutableTreeNode node = (DefaultMutableTreeNode)treeNode;
311 return exhumeElementFromNode(node);
313 return null;
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;
351 return element;
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 {
369 saveExpandedPaths();
370 for (String subId : myReadTreeState.keySet()) {
371 TreeState treeState = myReadTreeState.get(subId);
372 Element subPane = new Element(ELEMENT_SUBPANE);
373 if (subId != null) {
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() {
401 return myTree;
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();
414 else {
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;
422 else {
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) {
441 dirs.add(directory);
444 return dirs.toArray(new PsiDirectory[dirs.size()]);
446 return PsiDirectory.EMPTY_ARRAY;
449 // Drag'n'Drop stuff
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();
455 static {
456 DataFlavor[] flavors;
457 try {
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];
469 FLAVORS = flavors;
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() {
476 @Nullable
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;
506 return flavors;
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();
537 if (nodes != null) {
538 final Object[] elements = currentPane.getSelectedElements();
539 final PsiElement[] psiElements = currentPane.getSelectedPSIElements();
540 try {
541 Object transferableWrapper = new TransferableWrapper() {
542 public TreeNode[] getTreeNodes() {
543 return nodes;
545 public PsiElement[] getPsiElements() {
546 return psiElements;
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) {
556 // ignore
561 private boolean canDragElements(Object[] elements) {
562 for (Object element : elements) {
563 if (element instanceof Module) {
564 return true;
567 return false;
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) { }