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
;
64 import javax
.swing
.tree
.DefaultMutableTreeNode
;
65 import javax
.swing
.tree
.DefaultTreeModel
;
66 import javax
.swing
.tree
.TreePath
;
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());
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
,
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) {
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
);
173 public DnDAwareTree
getTree() {
177 public String
getName() {
181 public void setName(String name
) {
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
);
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
);
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
)) {
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
)) {
243 if (LangDataKeys
.PSI_ELEMENT
.is(dataId
)) {
244 PsiElement
[] elements
= getSelectedPsiElements();
245 if (elements
.length
!= 1) {
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()) {
258 return result
.isEmpty() ?
null : result
.toArray(new PsiElement
[result
.size()]);
261 if (LangDataKeys
.IDE_VIEW
.is(dataId
)) {
265 if (LangDataKeys
.TARGET_PSI_ELEMENT
.is(dataId
)) {
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
)) {
305 return result
.toArray(new FavoritesTreeNodeDescriptor
[result
.size()]);
307 if (FAVORITES_LIST_NAME_DATA_KEY
.is(dataId
)) {
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
);
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) {
327 for (Object element
: elements
) {
328 if (element
== null) continue;
329 if (klass
.isAssignableFrom(element
.getClass())) {
330 result
.add((T
)element
);
336 private Module
[] getSelectedModules() {
337 final Object
[] elements
= getSelectedNodeElements();
338 if (elements
== 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();
366 return ArrayUtil
.toObjectArray(result
);
370 public FavoritesTreeNodeDescriptor
[] getSelectedNodeDescriptors() {
371 TreePath
[] path
= myTree
.getSelectionPaths();
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
)) {
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() {
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"));
412 DeleteHandler
.deletePsiElement(elements
, myProject
);
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
];
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()));
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
;
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
;
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);