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.
18 * class PsiViewerDialog
19 * created Aug 25, 2001
22 package com
.intellij
.internal
.psiView
;
24 import com
.intellij
.lang
.ASTNode
;
25 import com
.intellij
.lang
.Language
;
26 import com
.intellij
.lang
.LanguageUtil
;
27 import com
.intellij
.openapi
.editor
.Document
;
28 import com
.intellij
.openapi
.editor
.Editor
;
29 import com
.intellij
.openapi
.editor
.EditorFactory
;
30 import com
.intellij
.openapi
.editor
.markup
.HighlighterLayer
;
31 import com
.intellij
.openapi
.editor
.markup
.HighlighterTargetArea
;
32 import com
.intellij
.openapi
.editor
.markup
.RangeHighlighter
;
33 import com
.intellij
.openapi
.editor
.markup
.TextAttributes
;
34 import com
.intellij
.openapi
.extensions
.Extensions
;
35 import com
.intellij
.openapi
.fileTypes
.*;
36 import com
.intellij
.openapi
.fileTypes
.impl
.AbstractFileType
;
37 import com
.intellij
.openapi
.project
.Project
;
38 import com
.intellij
.openapi
.ui
.DialogWrapper
;
39 import com
.intellij
.openapi
.ui
.Messages
;
40 import com
.intellij
.openapi
.util
.Disposer
;
41 import com
.intellij
.openapi
.util
.TextRange
;
42 import com
.intellij
.psi
.PsiElement
;
43 import com
.intellij
.psi
.PsiFile
;
44 import com
.intellij
.psi
.PsiFileFactory
;
45 import com
.intellij
.psi
.PsiReference
;
46 import com
.intellij
.psi
.search
.FilenameIndex
;
47 import com
.intellij
.psi
.search
.GlobalSearchScope
;
48 import com
.intellij
.ui
.SortedComboBoxModel
;
49 import com
.intellij
.ui
.TreeSpeedSearch
;
50 import com
.intellij
.ui
.treeStructure
.Tree
;
51 import com
.intellij
.util
.IncorrectOperationException
;
52 import com
.intellij
.util
.ui
.UIUtil
;
53 import com
.intellij
.util
.ui
.tree
.TreeUtil
;
56 import javax
.swing
.event
.TreeSelectionEvent
;
57 import javax
.swing
.event
.TreeSelectionListener
;
58 import javax
.swing
.tree
.DefaultMutableTreeNode
;
59 import javax
.swing
.tree
.DefaultTreeModel
;
60 import javax
.swing
.tree
.TreePath
;
62 import java
.awt
.event
.*;
64 import java
.util
.List
;
66 public class PsiViewerDialog
extends DialogWrapper
{
67 private static final Object EXTENSION_KEY
= new Object();
69 private final Project myProject
;
71 private final Tree myTree
;
72 private final ViewerTreeBuilder myTreeBuilder
;
74 private final JList myRefs
;
76 private Editor myEditor
;
77 private String myLastParsedText
= null;
79 private List
<JRadioButton
> myExtensionButtons
= new ArrayList
<JRadioButton
>();
80 private JRadioButton
[] myFileTypeButtons
;
81 private FileType
[] myFileTypes
;
83 private JCheckBox myShowWhiteSpacesBox
;
84 private JPanel myStructureTreePanel
;
85 private JPanel myTextPanel
;
86 private JPanel myPanel
;
87 private JPanel myChoicesPanel
;
88 private JCheckBox myShowTreeNodesCheckBox
;
89 private JComboBox myDialectsComboBox
;
90 private JPanel myReferencesPanel
;
92 public PsiViewerDialog(Project project
,boolean modal
) {
94 setTitle("PSI Viewer");
96 myTree
= new Tree(new DefaultTreeModel(new DefaultMutableTreeNode()));
97 UIUtil
.setLineStyleAngled(myTree
);
98 myTree
.setRootVisible(false);
99 myTree
.setShowsRootHandles(true);
101 ToolTipManager
.sharedInstance().registerComponent(myTree
);
102 TreeUtil
.installActions(myTree
);
103 new TreeSpeedSearch(myTree
);
104 myTreeBuilder
= new ViewerTreeBuilder(project
, myTree
);
106 myTree
.addTreeSelectionListener(new MyTreeSelectionListener());
108 JScrollPane scrollPane
= new JScrollPane(myTree
);
109 JPanel panel
= new JPanel(new BorderLayout());
110 panel
.add(scrollPane
, BorderLayout
.CENTER
);
111 myStructureTreePanel
.setLayout(new BorderLayout());
112 myStructureTreePanel
.add(panel
, BorderLayout
.CENTER
);
114 myRefs
= new JList(new DefaultListModel());
115 JScrollPane refScrollPane
= new JScrollPane(myRefs
);
116 JPanel refPanel
= new JPanel(new BorderLayout());
117 refPanel
.add(refScrollPane
, BorderLayout
.CENTER
);
118 myReferencesPanel
.setLayout(new BorderLayout());
119 myReferencesPanel
.add(refPanel
, BorderLayout
.CENTER
);
120 final GoToListener listener
= new GoToListener(myRefs
, project
);
121 myRefs
.addKeyListener(listener
);
122 myRefs
.addMouseListener(listener
);
125 setOKButtonText("&Build PSI Tree");
129 protected String
getDimensionServiceKey(){
130 return "#com.intellij.internal.psiView.PsiViewerDialog";
133 public JComponent
getPreferredFocusedComponent() {
134 return myEditor
.getContentComponent();
137 protected void init() {
138 EditorFactory editorFactory
= EditorFactory
.getInstance();
139 Document document
= editorFactory
.createDocument("");
140 myEditor
= editorFactory
.createEditor(document
, myProject
);
141 myEditor
.getSettings().setFoldingOutlineShown(false);
143 for(PsiViewerExtension extension
: Extensions
.getExtensions(PsiViewerExtension
.EP_NAME
)) {
144 JRadioButton button
= new JRadioButton(extension
.getName());
145 button
.putClientProperty(EXTENSION_KEY
, extension
);
146 myExtensionButtons
.add(button
);
150 FileType
[] fileTypes
= FileTypeManager
.getInstance().getRegisteredFileTypes();
151 Arrays
.sort(fileTypes
,new Comparator
<FileType
>() {
152 public int compare(final FileType o1
, final FileType o2
) {
153 return o1
.getName().compareTo(o2
.getName());
157 List
<FileType
> customFileTypes
= new ArrayList
<FileType
>();
159 for (FileType fileType
: fileTypes
) {
160 if (fileType
!= StdFileTypes
.GUI_DESIGNER_FORM
&& fileType
!= StdFileTypes
.IDEA_MODULE
&& fileType
!= StdFileTypes
.IDEA_PROJECT
&&
161 fileType
!= StdFileTypes
.IDEA_WORKSPACE
&& fileType
!= FileTypes
.ARCHIVE
&& fileType
!= FileTypes
.UNKNOWN
&&
162 fileType
!= FileTypes
.PLAIN_TEXT
&& !(fileType
instanceof AbstractFileType
) && !fileType
.isBinary() && !fileType
.isReadOnly()) {
163 customFileTypes
.add(fileType
);
167 myFileTypes
= customFileTypes
.toArray(new FileType
[customFileTypes
.size()]);
168 myFileTypeButtons
= new JRadioButton
[myFileTypes
.length
];
170 ButtonGroup bg
= new ButtonGroup();
171 for (JRadioButton button
: myExtensionButtons
) {
175 final int rows
= 1 + myFileTypes
.length
/ 7;
176 JPanel choicesBox
= new JPanel(new GridLayout(rows
, 7));
178 for (JRadioButton extensionButton
: myExtensionButtons
) {
179 choicesBox
.add(extensionButton
);
182 for (int i
= 0; i
< myFileTypes
.length
; i
++) {
183 FileType fileType
= myFileTypes
[i
];
184 JRadioButton button
= new JRadioButton(fileType
.getName()+" file");
186 choicesBox
.add(button
);
187 myFileTypeButtons
[i
] = button
;
190 final ActionListener updateDialectsListener
= new ActionListener() {
191 public void actionPerformed(final ActionEvent e
) {
192 updateDialectsCombo();
195 final Enumeration
<AbstractButton
> buttonEnum
= bg
.getElements();
196 while (buttonEnum
.hasMoreElements()) {
197 buttonEnum
.nextElement().addActionListener(updateDialectsListener
);
199 updateDialectsCombo();
200 myDialectsComboBox
.setRenderer(new DefaultListCellRenderer() {
202 public Component
getListCellRendererComponent(final JList list
,
205 final boolean isSelected
,
206 final boolean cellHasFocus
) {
207 final Component result
= super.getListCellRendererComponent(list
, value
, index
, isSelected
, cellHasFocus
);
208 if (value
== null) setText("<no dialect>");
213 if (myExtensionButtons
.size() > 0) {
214 myExtensionButtons
.get(0).setSelected(true);
217 myFileTypeButtons
[0].setSelected(true);
220 myChoicesPanel
.setLayout(new BorderLayout());
221 myChoicesPanel
.add(choicesBox
, BorderLayout
.CENTER
);
223 final ViewerTreeStructure treeStructure
= (ViewerTreeStructure
)myTreeBuilder
.getTreeStructure();
224 myShowWhiteSpacesBox
.addActionListener(new ActionListener() {
225 public void actionPerformed(ActionEvent e
) {
226 treeStructure
.setShowWhiteSpaces(myShowWhiteSpacesBox
.isSelected());
227 myTreeBuilder
.updateFromRoot();
230 myShowTreeNodesCheckBox
.addActionListener(new ActionListener() {
231 public void actionPerformed(ActionEvent e
) {
232 treeStructure
.setShowTreeNodes(myShowTreeNodesCheckBox
.isSelected());
233 myTreeBuilder
.updateFromRoot();
236 myTextPanel
.setLayout(new BorderLayout());
237 myTextPanel
.add(myEditor
.getComponent(), BorderLayout
.CENTER
);
242 private void updateDialectsCombo() {
243 final SortedComboBoxModel
<Language
> model
= new SortedComboBoxModel
<Language
>(new Comparator
<Language
>() {
244 public int compare(final Language o1
, final Language o2
) {
245 if (o1
== null) return o2
== null?
0 : -1;
246 if (o2
== null) return 1;
247 return o1
.getID().compareTo(o2
.getID());
250 for (int i
= 0; i
< myFileTypeButtons
.length
; i
++) {
251 JRadioButton fileTypeButton
= myFileTypeButtons
[i
];
252 if (fileTypeButton
.isSelected()) {
253 final FileType type
= myFileTypes
[i
];
254 if (type
instanceof LanguageFileType
) {
255 final Language baseLang
= ((LanguageFileType
)type
).getLanguage();
256 model
.setAll(LanguageUtil
.getLanguageDialects(baseLang
));
262 myDialectsComboBox
.setModel(model
);
263 myDialectsComboBox
.setVisible(model
.getSize() > 1);
266 protected JComponent
createCenterPanel() {
270 protected void doOKAction() {
271 final String text
= myEditor
.getDocument().getText();
272 if ("".equals(text
.trim())) {
275 myLastParsedText
= text
;
276 PsiElement rootElement
= null;
278 for (JRadioButton button
: myExtensionButtons
) {
279 if (button
.isSelected()) {
280 PsiViewerExtension ext
= (PsiViewerExtension
) button
.getClientProperty(EXTENSION_KEY
);
281 rootElement
= ext
.createElement(myProject
, text
);
284 if (rootElement
== null) {
285 for (int i
= 0; i
< myFileTypeButtons
.length
; i
++) {
286 JRadioButton fileTypeButton
= myFileTypeButtons
[i
];
288 if (fileTypeButton
.isSelected()) {
289 final FileType type
= myFileTypes
[i
];
290 if (type
instanceof LanguageFileType
) {
291 final Language language
= ((LanguageFileType
)type
).getLanguage();
292 final Language dialect
= (Language
)myDialectsComboBox
.getSelectedItem();
293 rootElement
= PsiFileFactory
.getInstance(myProject
).createFileFromText("Dummy." + type
.getDefaultExtension(), dialect
== null? language
: dialect
, text
);
296 rootElement
= PsiFileFactory
.getInstance(myProject
).createFileFromText("Dummy." + type
.getDefaultExtension(), text
);
302 catch (IncorrectOperationException e1
) {
304 Messages
.showMessageDialog(
308 Messages
.getErrorIcon()
311 ViewerTreeStructure structure
= (ViewerTreeStructure
)myTreeBuilder
.getTreeStructure();
312 structure
.setRootPsiElement(rootElement
);
314 myTreeBuilder
.updateFromRoot();
315 myTree
.setRootVisible(true);
317 myTree
.setRootVisible(false);
320 private class MyTreeSelectionListener
implements TreeSelectionListener
{
321 private final TextAttributes myAttributes
;
322 private RangeHighlighter myHighlighter
;
324 public MyTreeSelectionListener() {
325 myAttributes
= new TextAttributes();
326 myAttributes
.setBackgroundColor(new Color(0, 0, 128));
327 myAttributes
.setForegroundColor(Color
.white
);
330 public void valueChanged(TreeSelectionEvent e
) {
331 if (!myEditor
.getDocument().getText().equals(myLastParsedText
)) return;
332 TreePath path
= myTree
.getSelectionPath();
338 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
339 if (!(node
.getUserObject() instanceof ViewerNodeDescriptor
)) return;
340 ViewerNodeDescriptor descriptor
= (ViewerNodeDescriptor
)node
.getUserObject();
341 Object elementObject
= descriptor
.getElement();
342 final PsiElement element
= elementObject
instanceof PsiElement?
(PsiElement
)elementObject
:
343 elementObject
instanceof ASTNode ?
((ASTNode
)elementObject
).getPsi() : null;
344 if (element
!= null) {
345 TextRange range
= element
.getTextRange();
346 int start
= range
.getStartOffset();
347 int end
= range
.getEndOffset();
348 final ViewerTreeStructure treeStructure
= (ViewerTreeStructure
)myTreeBuilder
.getTreeStructure();
349 PsiElement rootPsiElement
= treeStructure
.getRootPsiElement();
350 if (rootPsiElement
!= null) {
351 int baseOffset
= rootPsiElement
.getTextRange().getStartOffset();
356 final int textLength
= myEditor
.getDocument().getTextLength();
357 if (end
<= textLength
) {
358 myHighlighter
= myEditor
.getMarkupModel().addRangeHighlighter(start
, end
, HighlighterLayer
.FIRST
+ 1, myAttributes
, HighlighterTargetArea
.EXACT_RANGE
);
360 updateReferences(element
);
365 public void updateReferences(PsiElement element
) {
366 final DefaultListModel model
= (DefaultListModel
)myRefs
.getModel();
368 if (element
!= null) {
369 for (PsiReference reference
: element
.getReferences()) {
370 model
.addElement(reference
.getClass().getName());
375 private void clearSelection() {
376 if (myHighlighter
!= null) {
377 myEditor
.getMarkupModel().removeHighlighter(myHighlighter
);
378 myHighlighter
= null;
383 public void dispose() {
384 Disposer
.dispose(myTreeBuilder
);
385 EditorFactory
.getInstance().releaseEditor(myEditor
);
390 private static class GoToListener
implements KeyListener
, MouseListener
{
391 private final JList myList
;
392 private final Project myProject
;
394 public GoToListener(JList list
, Project project
) {
399 private void navigate() {
400 final Object value
= myList
.getSelectedValue();
401 if (value
instanceof String
) {
402 final String fqn
= (String
)value
;
403 String filename
= fqn
;
404 if (fqn
.contains(".")) {
405 filename
= fqn
.substring(fqn
.lastIndexOf('.') + 1);
407 if (filename
.contains("$")) {
408 filename
= filename
.substring(0, filename
.indexOf('$'));
411 final PsiFile
[] files
= FilenameIndex
.getFilesByName(myProject
, filename
, GlobalSearchScope
.allScope(myProject
));
412 if (files
!= null && files
.length
> 0) {
413 files
[0].navigate(true);
418 public void keyPressed(KeyEvent e
) {
419 if (e
.getKeyCode() == KeyEvent
.VK_ENTER
) {
424 public void mouseClicked(MouseEvent e
) {
425 if (e
.getClickCount() > 1) {
430 public void keyTyped(KeyEvent e
) {}
431 public void keyReleased(KeyEvent e
) {}
432 public void mousePressed(MouseEvent e
) {}
433 public void mouseReleased(MouseEvent e
) {}
434 public void mouseEntered(MouseEvent e
) {}
435 public void mouseExited(MouseEvent e
) {}