ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / platform / lang-impl / src / com / intellij / ide / favoritesTreeView / FavoritesTreeViewPanel.java
blob3e0914bbaa04b4070fbb9bd3f921f164a4f0a8cc
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.
17 package com.intellij.ide.favoritesTreeView;
19 import com.intellij.history.LocalHistory;
20 import com.intellij.history.LocalHistoryAction;
21 import com.intellij.ide.CopyPasteDelegator;
22 import com.intellij.ide.DeleteProvider;
23 import com.intellij.ide.IdeBundle;
24 import com.intellij.ide.IdeView;
25 import com.intellij.ide.dnd.aware.DnDAwareTree;
26 import com.intellij.ide.projectView.impl.ModuleGroup;
27 import com.intellij.ide.projectView.impl.nodes.LibraryGroupElement;
28 import com.intellij.ide.projectView.impl.nodes.NamedLibraryElement;
29 import com.intellij.ide.ui.customization.CustomActionsSchema;
30 import com.intellij.ide.ui.customization.CustomizationUtil;
31 import com.intellij.ide.util.DeleteHandler;
32 import com.intellij.ide.util.DirectoryChooserUtil;
33 import com.intellij.ide.util.EditorHelper;
34 import com.intellij.ide.util.treeView.AbstractTreeNode;
35 import com.intellij.ide.util.treeView.NodeDescriptor;
36 import com.intellij.ide.util.treeView.NodeRenderer;
37 import com.intellij.navigation.ItemPresentation;
38 import com.intellij.openapi.actionSystem.*;
39 import com.intellij.openapi.editor.Editor;
40 import com.intellij.openapi.extensions.Extensions;
41 import com.intellij.openapi.module.Module;
42 import com.intellij.openapi.project.Project;
43 import com.intellij.openapi.roots.ui.configuration.actions.ModuleDeleteProvider;
44 import com.intellij.openapi.util.Disposer;
45 import com.intellij.openapi.vfs.JarFileSystem;
46 import com.intellij.openapi.vfs.LocalFileSystem;
47 import com.intellij.openapi.vfs.VirtualFile;
48 import com.intellij.openapi.wm.ToolWindowManager;
49 import com.intellij.pom.Navigatable;
50 import com.intellij.psi.*;
51 import com.intellij.ui.ScrollPaneFactory;
52 import com.intellij.ui.SimpleTextAttributes;
53 import com.intellij.ui.TreeSpeedSearch;
54 import com.intellij.ui.TreeToolTipHandler;
55 import com.intellij.util.ArrayUtil;
56 import com.intellij.util.EditSourceOnDoubleClickHandler;
57 import com.intellij.util.EditSourceOnEnterKeyHandler;
58 import com.intellij.util.ui.UIUtil;
59 import com.intellij.util.ui.tree.TreeUtil;
60 import org.jetbrains.annotations.NonNls;
61 import org.jetbrains.annotations.NotNull;
63 import javax.swing.*;
64 import javax.swing.tree.DefaultMutableTreeNode;
65 import javax.swing.tree.DefaultTreeModel;
66 import javax.swing.tree.TreePath;
67 import java.awt.*;
68 import java.awt.event.MouseListener;
69 import java.util.ArrayList;
70 import java.util.Arrays;
71 import java.util.List;
73 public class FavoritesTreeViewPanel extends JPanel implements DataProvider {
74 @NonNls public static final String ABSTRACT_TREE_NODE_TRANSFERABLE = "AbstractTransferable";
76 private final FavoritesTreeStructure myFavoritesTreeStructure;
77 private FavoritesViewTreeBuilder myBuilder;
78 private final CopyPasteDelegator myCopyPasteDelegator;
79 private MouseListener myTreePopupHandler;
81 public static final DataKey<FavoritesTreeNodeDescriptor[]> CONTEXT_FAVORITES_ROOTS_DATA_KEY = DataKey.create("FavoritesRoot");
82 @Deprecated public static final String CONTEXT_FAVORITES_ROOTS = CONTEXT_FAVORITES_ROOTS_DATA_KEY.getName();
84 public static final DataKey<String> FAVORITES_LIST_NAME_DATA_KEY = DataKey.create("FavoritesListName");
85 @Deprecated public static final String FAVORITES_LIST_NAME = FAVORITES_LIST_NAME_DATA_KEY.getName();
87 protected Project myProject;
88 private final String myHelpId;
89 protected DnDAwareTree myTree;
91 private final MyDeletePSIElementProvider myDeletePSIElementProvider = new MyDeletePSIElementProvider();
92 private final ModuleDeleteProvider myDeleteModuleProvider = new ModuleDeleteProvider();
94 private String myListName;
95 private final IdeView myIdeView = new MyIdeView();
97 public FavoritesTreeViewPanel(Project project, String helpId, String name) {
98 super(new BorderLayout());
99 myProject = project;
100 myHelpId = helpId;
101 myListName = name;
103 myFavoritesTreeStructure = new FavoritesTreeStructure(project, myListName);
104 DefaultMutableTreeNode root = new DefaultMutableTreeNode();
105 root.setUserObject(myFavoritesTreeStructure.getRootElement());
106 final DefaultTreeModel treeModel = new DefaultTreeModel(root);
107 myTree = new DnDAwareTree(treeModel);
108 myBuilder = new FavoritesViewTreeBuilder(myProject, myTree, treeModel, myFavoritesTreeStructure, myListName);
110 TreeUtil.installActions(myTree);
111 UIUtil.setLineStyleAngled(myTree);
112 myTree.setRootVisible(false);
113 myTree.setShowsRootHandles(true);
114 myTree.setLargeModel(true);
115 new TreeSpeedSearch(myTree);
116 ToolTipManager.sharedInstance().registerComponent(myTree);
117 TreeToolTipHandler.install(myTree);
118 myTree.setCellRenderer(new NodeRenderer() {
119 public void customizeCellRenderer(JTree tree,
120 Object value,
121 boolean selected,
122 boolean expanded,
123 boolean leaf,
124 int row,
125 boolean hasFocus) {
126 super.customizeCellRenderer(tree, value, selected, expanded, leaf, row, hasFocus);
127 if (value instanceof DefaultMutableTreeNode) {
128 final DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
129 //only favorites roots to explain
130 final Object userObject = node.getUserObject();
131 if (userObject instanceof FavoritesTreeNodeDescriptor) {
132 final FavoritesTreeNodeDescriptor favoritesTreeNodeDescriptor = (FavoritesTreeNodeDescriptor)userObject;
133 AbstractTreeNode treeNode = favoritesTreeNodeDescriptor.getElement();
134 final ItemPresentation presentation = treeNode.getPresentation();
135 String locationString = presentation.getLocationString();
136 if (locationString != null && locationString.length() > 0) {
137 append(" (" + locationString + ")", SimpleTextAttributes.GRAY_ATTRIBUTES);
139 else if (node.getParent() != null && node.getParent().getParent() == null) {
140 final String location = favoritesTreeNodeDescriptor.getLocation();
141 if (location != null && location.length() > 0) {
142 append(" (" + location + ")", SimpleTextAttributes.GRAY_ATTRIBUTES);
149 JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTree);
150 myTreePopupHandler = CustomizationUtil.installPopupHandler(myTree, IdeActions.GROUP_FAVORITES_VIEW_POPUP, ActionPlaces.FAVORITES_VIEW_POPUP);
151 add(scrollPane, BorderLayout.CENTER);
152 //add(createActionsToolbar(), BorderLayout.NORTH);
154 EditSourceOnDoubleClickHandler.install(myTree);
155 EditSourceOnEnterKeyHandler.install(myTree);
156 myCopyPasteDelegator = new CopyPasteDelegator(myProject, this) {
157 @NotNull
158 protected PsiElement[] getSelectedElements() {
159 return getSelectedPsiElements();
164 public void selectElement(final Object selector, final VirtualFile file, final boolean requestFocus) {
165 myBuilder.select(selector, file, requestFocus);
168 public void dispose() {
169 Disposer.dispose(myBuilder);
170 myBuilder = null;
173 public DnDAwareTree getTree() {
174 return myTree;
177 public String getName() {
178 return myListName;
181 public void setName(String name) {
182 myListName = name;
185 @NotNull
186 private PsiElement[] getSelectedPsiElements() {
187 final Object[] elements = getSelectedNodeElements();
188 if (elements == null) {
189 return PsiElement.EMPTY_ARRAY;
191 ArrayList<PsiElement> result = new ArrayList<PsiElement>();
192 for (Object element : elements) {
193 if (element instanceof PsiElement) {
194 result.add((PsiElement)element);
196 else if (element instanceof SmartPsiElementPointer) {
197 PsiElement psiElement = ((SmartPsiElementPointer)element).getElement();
198 if (psiElement != null) {
199 result.add(psiElement);
201 } else {
202 for (FavoriteNodeProvider provider : Extensions.getExtensions(FavoriteNodeProvider.EP_NAME, myProject)) {
203 final PsiElement psiElement = provider.getPsiElement(element);
204 if (psiElement != null) {
205 result.add(psiElement);
206 break;
211 return result.toArray(new PsiElement[result.size()]);
214 public FavoritesTreeStructure getFavoritesTreeStructure() {
215 return myFavoritesTreeStructure;
218 public Object getData(String dataId) {
219 if (PlatformDataKeys.PROJECT.is(dataId)) {
220 return myProject;
222 if (PlatformDataKeys.NAVIGATABLE.is(dataId)) {
223 final FavoritesTreeNodeDescriptor[] selectedNodeDescriptors = getSelectedNodeDescriptors();
224 return selectedNodeDescriptors.length == 1 ? selectedNodeDescriptors[0].getElement() : null;
226 if (PlatformDataKeys.NAVIGATABLE_ARRAY.is(dataId)) {
227 final List<Navigatable> selectedElements = getSelectedElements(Navigatable.class);
228 return selectedElements.toArray(new Navigatable[selectedElements.size()]);
231 if (PlatformDataKeys.CUT_PROVIDER.is(dataId)) {
232 return myCopyPasteDelegator.getCutProvider();
234 if (PlatformDataKeys.COPY_PROVIDER.is(dataId)) {
235 return myCopyPasteDelegator.getCopyProvider();
237 if (PlatformDataKeys.PASTE_PROVIDER.is(dataId)) {
238 return myCopyPasteDelegator.getPasteProvider();
240 if (PlatformDataKeys.HELP_ID.is(dataId)) {
241 return myHelpId;
243 if (LangDataKeys.PSI_ELEMENT.is(dataId)) {
244 PsiElement[] elements = getSelectedPsiElements();
245 if (elements.length != 1) {
246 return null;
248 return elements[0] != null && elements[0].isValid() ? elements[0] : null;
250 if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
251 final PsiElement[] elements = getSelectedPsiElements();
252 ArrayList<PsiElement> result = new ArrayList<PsiElement>();
253 for (PsiElement element : elements) {
254 if (element.isValid()) {
255 result.add(element);
258 return result.isEmpty() ? null : result.toArray(new PsiElement[result.size()]);
261 if (LangDataKeys.IDE_VIEW.is(dataId)) {
262 return myIdeView;
265 if (LangDataKeys.TARGET_PSI_ELEMENT.is(dataId)) {
266 return null;
269 if (LangDataKeys.MODULE_CONTEXT.is(dataId)) {
270 Module[] selected = getSelectedModules();
271 return selected != null && selected.length == 1 ? selected[0] : null;
273 if (LangDataKeys.MODULE_CONTEXT_ARRAY.is(dataId)) {
274 return getSelectedModules();
277 if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId)) {
278 final Object[] elements = getSelectedNodeElements();
279 return elements != null && elements.length >= 1 && elements[0] instanceof Module
280 ? myDeleteModuleProvider
281 : myDeletePSIElementProvider;
284 if (ModuleGroup.ARRAY_DATA_KEY.is(dataId)) {
285 final List<ModuleGroup> selectedElements = getSelectedElements(ModuleGroup.class);
286 return selectedElements.isEmpty() ? null : selectedElements.toArray(new ModuleGroup[selectedElements.size()]);
288 if (LibraryGroupElement.ARRAY_DATA_KEY.is(dataId)) {
289 final List<LibraryGroupElement> selectedElements = getSelectedElements(LibraryGroupElement.class);
290 return selectedElements.isEmpty() ? null : selectedElements.toArray(new LibraryGroupElement[selectedElements.size()]);
292 if (NamedLibraryElement.ARRAY_DATA_KEY.is(dataId)) {
293 final List<NamedLibraryElement> selectedElements = getSelectedElements(NamedLibraryElement.class);
294 return selectedElements.isEmpty() ? null : selectedElements.toArray(new NamedLibraryElement[selectedElements.size()]);
296 if (CONTEXT_FAVORITES_ROOTS_DATA_KEY.is(dataId)) {
297 List<FavoritesTreeNodeDescriptor> result = new ArrayList<FavoritesTreeNodeDescriptor>();
298 FavoritesTreeNodeDescriptor[] selectedNodeDescriptors = getSelectedNodeDescriptors();
299 for (FavoritesTreeNodeDescriptor selectedNodeDescriptor : selectedNodeDescriptors) {
300 FavoritesTreeNodeDescriptor root = selectedNodeDescriptor.getFavoritesRoot();
301 if (root != null && !(root.getElement().getValue() instanceof String)) {
302 result.add(root);
305 return result.toArray(new FavoritesTreeNodeDescriptor[result.size()]);
307 if (FAVORITES_LIST_NAME_DATA_KEY.is(dataId)) {
308 return myListName;
310 FavoritesTreeNodeDescriptor[] descriptors = getSelectedNodeDescriptors();
311 if (descriptors.length > 0) {
312 List<AbstractTreeNode> nodes = new ArrayList<AbstractTreeNode>();
313 for (FavoritesTreeNodeDescriptor descriptor : descriptors) {
314 nodes.add(descriptor.getElement());
316 return myFavoritesTreeStructure.getDataFromProviders(nodes, dataId);
318 return null;
321 private <T> List<T> getSelectedElements(Class<T> klass) {
322 final Object[] elements = getSelectedNodeElements();
323 ArrayList<T> result = new ArrayList<T>();
324 if (elements == null) {
325 return result;
327 for (Object element : elements) {
328 if (element == null) continue;
329 if (klass.isAssignableFrom(element.getClass())) {
330 result.add((T)element);
333 return result;
336 private Module[] getSelectedModules() {
337 final Object[] elements = getSelectedNodeElements();
338 if (elements == null) {
339 return null;
341 ArrayList<Module> result = new ArrayList<Module>();
342 for (Object element : elements) {
343 if (element instanceof Module) {
344 result.add((Module)element);
346 else if (element instanceof ModuleGroup) {
347 result.addAll(((ModuleGroup)element).modulesInGroup(myProject, true));
351 return result.isEmpty() ? null : result.toArray(new Module[result.size()]);
354 private Object[] getSelectedNodeElements() {
355 final FavoritesTreeNodeDescriptor[] selectedNodeDescriptors = getSelectedNodeDescriptors();
356 ArrayList<Object> result = new ArrayList<Object>();
357 for (FavoritesTreeNodeDescriptor selectedNodeDescriptor : selectedNodeDescriptors) {
358 if (selectedNodeDescriptor != null) {
359 Object value = selectedNodeDescriptor.getElement().getValue();
360 if (value instanceof SmartPsiElementPointer) {
361 value = ((SmartPsiElementPointer)value).getElement();
363 result.add(value);
366 return ArrayUtil.toObjectArray(result);
369 @NotNull
370 public FavoritesTreeNodeDescriptor[] getSelectedNodeDescriptors() {
371 TreePath[] path = myTree.getSelectionPaths();
372 if (path == null) {
373 return FavoritesTreeNodeDescriptor.EMPTY_ARRAY;
375 ArrayList<FavoritesTreeNodeDescriptor> result = new ArrayList<FavoritesTreeNodeDescriptor>();
376 for (TreePath treePath : path) {
377 DefaultMutableTreeNode lastPathNode = (DefaultMutableTreeNode)treePath.getLastPathComponent();
378 Object userObject = lastPathNode.getUserObject();
379 if (!(userObject instanceof FavoritesTreeNodeDescriptor)) {
380 continue;
382 FavoritesTreeNodeDescriptor treeNodeDescriptor = (FavoritesTreeNodeDescriptor)userObject;
383 result.add(treeNodeDescriptor);
385 return result.toArray(new FavoritesTreeNodeDescriptor[result.size()]);
388 public static String getQualifiedName(final VirtualFile file) {
389 return file.getPresentableUrl();
392 public FavoritesViewTreeBuilder getBuilder() {
393 return myBuilder;
396 private final class MyDeletePSIElementProvider implements DeleteProvider {
397 public boolean canDeleteElement(DataContext dataContext) {
398 final PsiElement[] elements = getElementsToDelete();
399 return DeleteHandler.shouldEnableDeleteAction(elements);
402 public void deleteElement(DataContext dataContext) {
403 List<PsiElement> allElements = Arrays.asList(getElementsToDelete());
404 List<PsiElement> validElements = new ArrayList<PsiElement>();
405 for (PsiElement psiElement : allElements) {
406 if (psiElement != null && psiElement.isValid()) validElements.add(psiElement);
408 final PsiElement[] elements = validElements.toArray(new PsiElement[validElements.size()]);
410 LocalHistoryAction a = LocalHistory.startAction(myProject, IdeBundle.message("progress.deleting"));
411 try {
412 DeleteHandler.deletePsiElement(elements, myProject);
414 finally {
415 a.finish();
419 private PsiElement[] getElementsToDelete() {
420 ArrayList<PsiElement> result = new ArrayList<PsiElement>();
421 Object[] elements = getSelectedNodeElements();
422 for (int idx = 0; elements != null && idx < elements.length; idx++) {
423 if (elements[idx] instanceof PsiElement) {
424 final PsiElement element = (PsiElement)elements[idx];
425 result.add(element);
426 if (element instanceof PsiDirectory) {
427 final VirtualFile virtualFile = ((PsiDirectory)element).getVirtualFile();
428 final String path = virtualFile.getPath();
429 if (path.endsWith(JarFileSystem.JAR_SEPARATOR)) { // if is jar-file root
430 final VirtualFile vFile =
431 LocalFileSystem.getInstance().findFileByPath(path.substring(0, path.length() - JarFileSystem.JAR_SEPARATOR.length()));
432 if (vFile != null) {
433 final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(vFile);
434 if (psiFile != null) {
435 elements[idx] = psiFile;
443 return result.toArray(new PsiElement[result.size()]);
447 private final class MyIdeView implements IdeView {
448 public void selectElement(final PsiElement element) {
449 if (element != null) {
450 final boolean isDirectory = element instanceof PsiDirectory;
451 if (!isDirectory) {
452 Editor editor = EditorHelper.openInEditor(element);
453 if (editor != null) {
454 ToolWindowManager.getInstance(myProject).activateEditorComponent();
460 private PsiDirectory getDirectory() {
461 if (myBuilder == null) return null;
462 final FavoritesTreeNodeDescriptor[] selectedNodeDescriptors = getSelectedNodeDescriptors();
463 if (selectedNodeDescriptors.length != 1) return null;
464 final FavoritesTreeNodeDescriptor currentDescriptor = selectedNodeDescriptors[0];
465 if (currentDescriptor != null) {
466 if (currentDescriptor.getElement() != null) {
467 final AbstractTreeNode currentNode = currentDescriptor.getElement();
468 if (currentNode.getValue() instanceof PsiDirectory) {
469 return (PsiDirectory)currentNode.getValue();
472 final NodeDescriptor parentDescriptor = currentDescriptor.getParentDescriptor();
473 if (parentDescriptor != null) {
474 final Object parentElement = parentDescriptor.getElement();
475 if (parentElement instanceof AbstractTreeNode) {
476 final AbstractTreeNode parentNode = (AbstractTreeNode)parentElement;
477 final Object directory = parentNode.getValue();
478 if (directory instanceof PsiDirectory) {
479 return (PsiDirectory)directory;
485 return null;
488 public PsiDirectory[] getDirectories() {
489 PsiDirectory directory = getDirectory();
490 return directory == null ? PsiDirectory.EMPTY_ARRAY : new PsiDirectory[]{directory};
493 public PsiDirectory getOrChooseDirectory() {
494 return DirectoryChooserUtil.getOrChooseDirectory(this);