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.
16 package com
.intellij
.internal
.psiView
;
18 import com
.intellij
.lang
.ASTNode
;
19 import com
.intellij
.lang
.Language
;
20 import com
.intellij
.lang
.LanguageUtil
;
21 import com
.intellij
.openapi
.actionSystem
.AnAction
;
22 import com
.intellij
.openapi
.actionSystem
.AnActionEvent
;
23 import com
.intellij
.openapi
.actionSystem
.DefaultActionGroup
;
24 import com
.intellij
.openapi
.actionSystem
.Presentation
;
25 import com
.intellij
.openapi
.actionSystem
.ex
.ComboBoxAction
;
26 import com
.intellij
.openapi
.application
.ApplicationManager
;
27 import com
.intellij
.openapi
.editor
.*;
28 import com
.intellij
.openapi
.editor
.event
.*;
29 import com
.intellij
.openapi
.editor
.markup
.*;
30 import com
.intellij
.openapi
.extensions
.Extensions
;
31 import com
.intellij
.openapi
.fileTypes
.*;
32 import com
.intellij
.openapi
.fileTypes
.impl
.AbstractFileType
;
33 import com
.intellij
.openapi
.project
.DumbAware
;
34 import com
.intellij
.openapi
.project
.Project
;
35 import com
.intellij
.openapi
.ui
.DialogWrapper
;
36 import com
.intellij
.openapi
.ui
.Messages
;
37 import com
.intellij
.openapi
.util
.DimensionService
;
38 import com
.intellij
.openapi
.util
.Disposer
;
39 import com
.intellij
.openapi
.util
.SystemInfo
;
40 import com
.intellij
.openapi
.util
.TextRange
;
41 import com
.intellij
.openapi
.wm
.IdeFocusManager
;
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
.psi
.util
.PsiUtilBase
;
49 import com
.intellij
.ui
.SortedComboBoxModel
;
50 import com
.intellij
.ui
.TitledBorderWithMnemonic
;
51 import com
.intellij
.ui
.TreeSpeedSearch
;
52 import com
.intellij
.ui
.treeStructure
.Tree
;
53 import com
.intellij
.util
.IncorrectOperationException
;
54 import com
.intellij
.util
.ui
.UIUtil
;
55 import com
.intellij
.util
.ui
.tree
.TreeUtil
;
56 import org
.jetbrains
.annotations
.NotNull
;
57 import org
.jetbrains
.annotations
.Nullable
;
60 import javax
.swing
.event
.ListSelectionEvent
;
61 import javax
.swing
.event
.ListSelectionListener
;
62 import javax
.swing
.event
.TreeSelectionEvent
;
63 import javax
.swing
.event
.TreeSelectionListener
;
64 import javax
.swing
.tree
.*;
66 import java
.awt
.event
.*;
68 import java
.util
.List
;
71 * @author Konstantin Bulenkov
73 public class PsiViewerDialog
extends DialogWrapper
{
74 private final Project myProject
;
76 private final Tree myTree
;
77 private final ViewerTreeBuilder myTreeBuilder
;
79 private final JList myRefs
;
80 private static String REFS_CACHE
= "References Resolve Cache";
82 private Editor myEditor
;
83 private String myLastParsedText
= null;
85 private JCheckBox myShowWhiteSpacesBox
;
86 private JPanel myStructureTreePanel
;
87 private JPanel myTextPanel
;
88 private JPanel myPanel
;
89 private JCheckBox myShowTreeNodesCheckBox
;
90 private JComboBox myDialectsComboBox
;
91 private JPanel myReferencesPanel
;
92 private JPanel myButtonPanel
;
93 private JSplitPane myTextSplit
;
94 private JSplitPane myTreeSplit
;
95 private Presentation myPresentation
= new Presentation();
96 private Map
<String
, Object
> handlers
= new HashMap
<String
, Object
>();
97 private DefaultActionGroup myGroup
;
98 private Language
[] myLanguageDialects
;
99 private final Color SELECTION_BG_COLOR
= new Color(0, 51, 51);
100 private static final Comparator
<Language
> DIALECTS_COMPARATOR
= new Comparator
<Language
>() {
101 public int compare(final Language o1
, final Language o2
) {
102 if (o1
== null) return o2
== null ?
0 : -1;
103 if (o2
== null) return 1;
104 return o1
.getID().compareTo(o2
.getID());
107 private EditorListener myEditorListener
= new EditorListener();
108 private int myLastParsedTextHashCode
= 17;
109 private int myNewDocumentHashCode
= 11;
112 private static PsiElement
findCommonParent(PsiElement start
, PsiElement end
) {
113 final TextRange range
= end
.getTextRange();
114 while (start
!= null && !start
.getTextRange().contains(range
)) {
115 start
= start
.getParent();
120 public PsiViewerDialog(Project project
, boolean modal
) {
121 super(project
, true);
122 setTitle("PSI Viewer");
124 myTree
= new Tree(new DefaultTreeModel(new DefaultMutableTreeNode()));
125 UIUtil
.setLineStyleAngled(myTree
);
126 myTree
.setRootVisible(false);
127 myTree
.setShowsRootHandles(true);
129 ToolTipManager
.sharedInstance().registerComponent(myTree
);
130 TreeUtil
.installActions(myTree
);
131 new TreeSpeedSearch(myTree
);
132 myTreeBuilder
= new ViewerTreeBuilder(project
, myTree
);
134 myTree
.addTreeSelectionListener(new MyTreeSelectionListener());
136 JScrollPane scrollPane
= new JScrollPane(myTree
);
137 JPanel panel
= new JPanel(new BorderLayout());
138 panel
.add(scrollPane
, BorderLayout
.CENTER
);
139 myStructureTreePanel
.setLayout(new BorderLayout());
140 myStructureTreePanel
.add(panel
, BorderLayout
.CENTER
);
142 myRefs
= new JList(new DefaultListModel());
143 JScrollPane refScrollPane
= new JScrollPane(myRefs
);
144 JPanel refPanel
= new JPanel(new BorderLayout());
145 refPanel
.add(refScrollPane
, BorderLayout
.CENTER
);
146 myReferencesPanel
.setLayout(new BorderLayout());
147 myReferencesPanel
.add(refPanel
, BorderLayout
.CENTER
);
148 final GoToListener listener
= new GoToListener();
149 myRefs
.addKeyListener(listener
);
150 myRefs
.addMouseListener(listener
);
151 myRefs
.getSelectionModel().addListSelectionListener(listener
);
152 myRefs
.setCellRenderer(new DefaultListCellRenderer() {
153 public Component
getListCellRendererComponent(JList list
, Object value
, int index
, boolean isSelected
, boolean cellHasFocus
) {
154 final Component comp
= super.getListCellRendererComponent(list
, value
, index
, isSelected
, cellHasFocus
);
155 if (resolve(index
) == null) {
156 comp
.setForeground(Color
.red
);
163 setOKButtonText("&Build PSI Tree");
167 protected String
getDimensionServiceKey() {
168 return "#com.intellij.internal.psiView.PsiViewerDialog";
171 public JComponent
getPreferredFocusedComponent() {
172 return myEditor
.getContentComponent();
175 private void updatePresentation(Presentation p
) {
176 myPresentation
.setText(p
.getText());
177 myPresentation
.setIcon(p
.getIcon());
180 protected void init() {
182 final List
<Presentation
> items
= new ArrayList
<Presentation
>();
183 final EditorFactory editorFactory
= EditorFactory
.getInstance();
184 final Document document
= editorFactory
.createDocument("");
185 myEditor
= editorFactory
.createEditor(document
, myProject
);
186 myEditor
.getSettings().setFoldingOutlineShown(false);
187 document
.addDocumentListener(myEditorListener
);
188 myEditor
.getSelectionModel().addSelectionListener(myEditorListener
);
189 myEditor
.getCaretModel().addCaretListener(myEditorListener
);
191 for (PsiViewerExtension extension
: Extensions
.getExtensions(PsiViewerExtension
.EP_NAME
)) {
192 final Presentation p
= new Presentation(extension
.getName());
193 p
.setIcon(extension
.getIcon());
194 handlers
.put(p
.getText(), extension
);
198 for (FileType fileType
: FileTypeManager
.getInstance().getRegisteredFileTypes()) {
199 if (fileType
!= StdFileTypes
.GUI_DESIGNER_FORM
&&
200 fileType
!= StdFileTypes
.IDEA_MODULE
&&
201 fileType
!= StdFileTypes
.IDEA_PROJECT
&&
202 fileType
!= StdFileTypes
.IDEA_WORKSPACE
&&
203 fileType
!= FileTypes
.ARCHIVE
&&
204 fileType
!= FileTypes
.UNKNOWN
&&
205 fileType
!= FileTypes
.PLAIN_TEXT
&&
206 !(fileType
instanceof AbstractFileType
) &&
207 !fileType
.isBinary() &&
208 !fileType
.isReadOnly()) {
209 final Presentation p
= new Presentation(fileType
.getName() + " file");
210 p
.setIcon(fileType
.getIcon());
211 handlers
.put(p
.getText(), fileType
);
216 final Presentation
[] popupItems
= items
.toArray(new Presentation
[items
.size()]);
217 Arrays
.sort(popupItems
, new Comparator
<Presentation
>() {
218 public int compare(Presentation p1
, Presentation p2
) {
219 return p1
.getText().toUpperCase().compareTo(p2
.getText().toUpperCase());
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
.queueUpdate();
230 myShowTreeNodesCheckBox
.addActionListener(new ActionListener() {
231 public void actionPerformed(ActionEvent e
) {
232 treeStructure
.setShowTreeNodes(myShowTreeNodesCheckBox
.isSelected());
233 myTreeBuilder
.queueUpdate();
236 myTextPanel
.setLayout(new BorderLayout());
237 myTextPanel
.add(myEditor
.getComponent(), BorderLayout
.CENTER
);
239 myGroup
= new DefaultActionGroup();
240 for (final Presentation popupItem
: popupItems
) {
241 myGroup
.add(new PopupItemAction(popupItem
));
244 final PsiViewerSettings settings
= PsiViewerSettings
.getSettings();
245 final String type
= settings
.type
;
246 for (Presentation popupItem
: popupItems
) {
247 if (popupItem
.getText().equals(type
)) {
248 updatePresentation(popupItem
);
253 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
255 myEditor
.getDocument().setText(settings
.text
);
256 myEditor
.getSelectionModel().setSelection(0, settings
.text
.length());
260 myShowWhiteSpacesBox
.setSelected(settings
.showWhiteSpaces
);
261 myShowTreeNodesCheckBox
.setSelected(settings
.showTreeNodes
);
263 final ChoosePsiTypeButton typeButton
= new ChoosePsiTypeButton();
264 myButtonPanel
.add(typeButton
.createCustomComponent(myPresentation
), BorderLayout
.CENTER
);
266 updateDialectsCombo();
267 myDialectsComboBox
.setRenderer(new DefaultListCellRenderer() {
269 public Component
getListCellRendererComponent(JList list
, Object value
, int index
,
270 boolean isSelected
, boolean cellHasFocus
) {
271 final Component result
= super.getListCellRendererComponent(list
, value
, index
, isSelected
, cellHasFocus
);
272 if (value
== null) setText("<no dialect>");
277 if (myDialectsComboBox
.isVisible()) {
278 for (int i
= 0; i
< myLanguageDialects
.length
; i
++) {
279 if (settings
.dialect
.equals(myLanguageDialects
[i
].toString())) {
280 myDialectsComboBox
.setSelectedIndex(i
+1);
286 registerCustomKeyboardActions();
287 final Dimension size
= DimensionService
.getInstance().getSize(getDimensionServiceKey(), myProject
);
289 DimensionService
.getInstance().setSize(getDimensionServiceKey(), new Dimension(600, 600));
291 myTextSplit
.setDividerLocation(settings
.textDividerLocation
);
292 myTreeSplit
.setDividerLocation(settings
.treeDividerLocation
);
296 private void registerCustomKeyboardActions() {
297 final Component component
= myButtonPanel
.getComponents()[0];
298 if (component
instanceof JComponent
) {
299 final Component button
= ((JComponent
)component
).getComponents()[0];
300 if (button
instanceof JButton
) {
301 final JButton jButton
= (JButton
)button
;
302 final int mask
= SystemInfo
.isMac ? KeyEvent
.META_DOWN_MASK
: KeyEvent
.ALT_DOWN_MASK
;
303 registerKeyboardAction(new ActionListener() {
304 public void actionPerformed(ActionEvent e
) {
307 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_P
, mask
));
309 registerKeyboardAction(new ActionListener() {
310 public void actionPerformed(ActionEvent e
) {
313 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_T
, mask
));
315 registerKeyboardAction(new ActionListener() {
316 public void actionPerformed(ActionEvent e
) {
319 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_S
, mask
));
321 registerKeyboardAction(new ActionListener() {
322 public void actionPerformed(ActionEvent e
) {
325 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_S
, mask
));
328 registerKeyboardAction(new ActionListener() {
329 public void actionPerformed(ActionEvent e
) {
332 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_S
, mask
));
334 registerKeyboardAction(new ActionListener() {
335 public void actionPerformed(ActionEvent e
) {
338 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_R
, mask
));
340 registerKeyboardAction(new ActionListener() {
341 public void actionPerformed(ActionEvent e
) {
342 if (myRefs
.isFocusOwner()) {
345 else if (myTree
.isFocusOwner()) {
349 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_TAB
, 0));
354 private void registerKeyboardAction(ActionListener actionListener
, KeyStroke keyStroke
) {
355 getRootPane().registerKeyboardAction(actionListener
, keyStroke
, JComponent
.WHEN_IN_FOCUSED_WINDOW
);
358 private void focusEditor() {
359 IdeFocusManager
.getInstance(myProject
).requestFocus(myEditor
.getContentComponent(), true);
362 private void focusTree() {
363 IdeFocusManager
.getInstance(myProject
).requestFocus(myTree
, true);
366 private void focusRefs() {
367 IdeFocusManager
.getInstance(myProject
).requestFocus(myRefs
, true);
368 if (myRefs
.getModel().getSize() > 0) {
369 if (myRefs
.getSelectedIndex() == -1) {
370 myRefs
.setSelectedIndex(0);
375 private void initBorders() {
376 myTextPanel
.setBorder(new TitledBorderWithMnemonic("&Text"));
377 myStructureTreePanel
.setBorder(new TitledBorderWithMnemonic("PSI &Structure"));
378 myReferencesPanel
.setBorder(new TitledBorderWithMnemonic("&References"));
382 private PsiElement
getPsiElement() {
383 final TreePath path
= myTree
.getSelectionPath();
384 return path
== null ?
null : getPsiElement((DefaultMutableTreeNode
)path
.getLastPathComponent());
388 private PsiElement
getPsiElement(DefaultMutableTreeNode node
) {
389 if (node
.getUserObject() instanceof ViewerNodeDescriptor
) {
390 ViewerNodeDescriptor descriptor
= (ViewerNodeDescriptor
)node
.getUserObject();
391 Object elementObject
= descriptor
.getElement();
392 return elementObject
instanceof PsiElement
393 ?
(PsiElement
)elementObject
394 : elementObject
instanceof ASTNode ?
((ASTNode
)elementObject
).getPsi() : null;
399 private void updateDialectsCombo() {
400 final SortedComboBoxModel
<Language
> model
= new SortedComboBoxModel
<Language
>(DIALECTS_COMPARATOR
);
401 final Object handler
= getHandler();
402 if (handler
instanceof LanguageFileType
) {
403 final Language baseLang
= ((LanguageFileType
)handler
).getLanguage();
404 myLanguageDialects
= LanguageUtil
.getLanguageDialects(baseLang
);
405 Arrays
.sort(myLanguageDialects
, DIALECTS_COMPARATOR
);
406 model
.setAll(myLanguageDialects
);
409 myDialectsComboBox
.setModel(model
);
410 myDialectsComboBox
.setVisible(model
.getSize() > 1);
411 if (!myDialectsComboBox
.isVisible()) {
412 myLanguageDialects
= new Language
[0];
416 protected JComponent
createCenterPanel() {
420 private Object
getHandler() {
421 return handlers
.get(myPresentation
.getText());
424 protected void doOKAction() {
425 final String text
= myEditor
.getDocument().getText();
426 if (text
.trim().length() == 0) return;
428 myLastParsedText
= text
;
429 myLastParsedTextHashCode
= text
.hashCode();
430 myNewDocumentHashCode
= myLastParsedTextHashCode
;
431 PsiElement rootElement
= null;
432 final Object handler
= getHandler();
435 if (handler
instanceof PsiViewerExtension
) {
436 final PsiViewerExtension ext
= (PsiViewerExtension
)handler
;
437 rootElement
= ext
.createElement(myProject
, text
);
439 else if (handler
instanceof FileType
) {
440 final FileType type
= (FileType
)handler
;
441 if (type
instanceof LanguageFileType
) {
442 final Language language
= ((LanguageFileType
)type
).getLanguage();
443 final Language dialect
= (Language
)myDialectsComboBox
.getSelectedItem();
444 rootElement
= PsiFileFactory
.getInstance(myProject
)
445 .createFileFromText("Dummy." + type
.getDefaultExtension(), dialect
== null ? language
: dialect
, text
);
448 rootElement
= PsiFileFactory
.getInstance(myProject
).createFileFromText("Dummy." + type
.getDefaultExtension(), text
);
453 catch (IncorrectOperationException e1
) {
455 Messages
.showMessageDialog(myProject
, e1
.getMessage(), "Error", Messages
.getErrorIcon());
457 ViewerTreeStructure structure
= (ViewerTreeStructure
)myTreeBuilder
.getTreeStructure();
458 structure
.setRootPsiElement(rootElement
);
460 myTreeBuilder
.queueUpdate();
461 myTree
.setRootVisible(true);
463 myTree
.setRootVisible(false);
466 private class MyTreeSelectionListener
implements TreeSelectionListener
{
467 private final TextAttributes myAttributes
;
469 public MyTreeSelectionListener() {
470 myAttributes
= new TextAttributes();
471 myAttributes
.setBackgroundColor(SELECTION_BG_COLOR
);
472 myAttributes
.setForegroundColor(Color
.white
);
475 public void valueChanged(TreeSelectionEvent e
) {
476 if (!myEditor
.getDocument().getText().equals(myLastParsedText
)) return;
477 TreePath path
= myTree
.getSelectionPath();
483 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
484 if (!(node
.getUserObject() instanceof ViewerNodeDescriptor
)) return;
485 ViewerNodeDescriptor descriptor
= (ViewerNodeDescriptor
)node
.getUserObject();
486 Object elementObject
= descriptor
.getElement();
487 final PsiElement element
= elementObject
instanceof PsiElement
488 ?
(PsiElement
)elementObject
489 : elementObject
instanceof ASTNode ?
((ASTNode
)elementObject
).getPsi() : null;
490 if (element
!= null) {
491 TextRange range
= element
.getTextRange();
492 int start
= range
.getStartOffset();
493 int end
= range
.getEndOffset();
494 final ViewerTreeStructure treeStructure
= (ViewerTreeStructure
)myTreeBuilder
.getTreeStructure();
495 PsiElement rootPsiElement
= treeStructure
.getRootPsiElement();
496 if (rootPsiElement
!= null) {
497 int baseOffset
= rootPsiElement
.getTextRange().getStartOffset();
502 final int textLength
= myEditor
.getDocument().getTextLength();
503 if (end
<= textLength
) {
504 myEditor
.getMarkupModel()
505 .addRangeHighlighter(start
, end
, HighlighterLayer
.FIRST
+ 1, myAttributes
, HighlighterTargetArea
.EXACT_RANGE
);
506 if (myTree
.hasFocus()) {
507 myEditor
.getCaretModel().moveToOffset(start
);
508 myEditor
.getScrollingModel().scrollToCaret(ScrollType
.MAKE_VISIBLE
);
510 myEditor
.getScrollingModel().scrollTo(myEditor
.offsetToLogicalPosition(start
), ScrollType
.MAKE_VISIBLE
);
513 updateReferences(element
);
518 public void updateReferences(PsiElement element
) {
519 final DefaultListModel model
= (DefaultListModel
)myRefs
.getModel();
521 final Object cache
= myRefs
.getClientProperty(REFS_CACHE
);
522 if (cache
instanceof Map
) {
523 ((Map
)cache
).clear();
525 myRefs
.putClientProperty(REFS_CACHE
, new HashMap());
527 if (element
!= null) {
528 for (PsiReference reference
: element
.getReferences()) {
529 model
.addElement(reference
.getClass().getName());
534 private void clearSelection() {
535 myEditor
.getMarkupModel().removeAllHighlighters();
539 public void doCancelAction() {
540 final PsiViewerSettings settings
= PsiViewerSettings
.getSettings();
541 settings
.type
= myPresentation
.getText();
542 settings
.text
= myEditor
.getDocument().getText();
543 settings
.showTreeNodes
= myShowTreeNodesCheckBox
.isSelected();
544 settings
.showWhiteSpaces
= myShowWhiteSpacesBox
.isSelected();
545 final Object selectedDialect
= myDialectsComboBox
.getSelectedItem();
546 settings
.dialect
= myDialectsComboBox
.isVisible() && selectedDialect
!= null ? selectedDialect
.toString() : "";
547 settings
.textDividerLocation
= myTextSplit
.getDividerLocation();
548 settings
.treeDividerLocation
= myTreeSplit
.getDividerLocation();
549 super.doCancelAction();
552 public void dispose() {
553 Disposer
.dispose(myTreeBuilder
);
554 EditorFactory
.getInstance().releaseEditor(myEditor
);
560 private PsiElement
resolve(int index
) {
561 final PsiElement element
= getPsiElement();
562 if (element
== null) return null;
563 Object o
= myRefs
.getClientProperty(REFS_CACHE
);
565 myRefs
.putClientProperty(REFS_CACHE
, o
= new HashMap());
567 HashMap map
= (HashMap
)o
;
568 Object cache
= map
.get(element
);
570 final PsiReference
[] references
= element
.getReferences();
571 cache
= new PsiElement
[references
.length
];
572 for (int i
= 0; i
< references
.length
; i
++) {
573 ((PsiElement
[])cache
)[i
] = references
[i
].resolve();
575 map
.put(element
, cache
);
577 PsiElement
[] elements
= (PsiElement
[])cache
;
578 return index
>= elements
.length ?
null : elements
[index
];
582 public static TreeNode
findNodeWithObject(final Object object
, final TreeModel model
, final Object parent
) {
583 for (int i
= 0; i
< model
.getChildCount(parent
); i
++) {
584 final DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
) model
.getChild(parent
, i
);
585 if (childNode
.getUserObject().equals(object
)) {
588 final TreeNode node
= findNodeWithObject(object
, model
, childNode
);
589 if (node
!= null) return node
;
595 private class ChoosePsiTypeButton
extends ComboBoxAction
{
596 protected int getMaxRows() {
600 protected int getMinWidth() {
604 protected int getMinHeight() {
609 protected DefaultActionGroup
createPopupActionGroup(JComponent button
) {
614 private class GoToListener
implements KeyListener
, MouseListener
, ListSelectionListener
{
615 private RangeHighlighter myHighlighter
;
616 private final TextAttributes myAttributes
=
617 new TextAttributes(Color
.white
, SELECTION_BG_COLOR
, Color
.red
, EffectType
.BOXED
, Font
.PLAIN
);
619 private void navigate() {
621 final Object value
= myRefs
.getSelectedValue();
622 if (value
instanceof String
) {
623 final String fqn
= (String
)value
;
624 String filename
= fqn
;
625 if (fqn
.contains(".")) {
626 filename
= fqn
.substring(fqn
.lastIndexOf('.') + 1);
628 if (filename
.contains("$")) {
629 filename
= filename
.substring(0, filename
.indexOf('$'));
632 final PsiFile
[] files
= FilenameIndex
.getFilesByName(myProject
, filename
, GlobalSearchScope
.allScope(myProject
));
633 if (files
!= null && files
.length
> 0) {
634 files
[0].navigate(true);
639 public void keyPressed(KeyEvent e
) {
640 if (e
.getKeyCode() == KeyEvent
.VK_ENTER
) {
645 public void mouseClicked(MouseEvent e
) {
646 if (e
.getClickCount() > 1) {
651 public void valueChanged(ListSelectionEvent e
) {
653 updateDialectsCombo();
654 final int ind
= myRefs
.getSelectedIndex();
655 final PsiElement element
= getPsiElement();
656 if (ind
> -1 && element
!= null) {
657 final PsiReference
[] references
= element
.getReferences();
658 if (ind
< references
.length
) {
659 final TextRange textRange
= references
[ind
].getRangeInElement();
660 TextRange range
= element
.getTextRange();
661 int start
= range
.getStartOffset();
662 int end
= range
.getEndOffset();
663 final ViewerTreeStructure treeStructure
= (ViewerTreeStructure
)myTreeBuilder
.getTreeStructure();
664 PsiElement rootPsiElement
= treeStructure
.getRootPsiElement();
665 if (rootPsiElement
!= null) {
666 int baseOffset
= rootPsiElement
.getTextRange().getStartOffset();
671 start
+= textRange
.getStartOffset();
672 end
= start
+ textRange
.getLength();
673 myHighlighter
= myEditor
.getMarkupModel()
674 .addRangeHighlighter(start
, end
, HighlighterLayer
.FIRST
+ 1, myAttributes
, HighlighterTargetArea
.EXACT_RANGE
);
679 public void clearSelection() {
680 if (myHighlighter
!= null && Arrays
.asList(myEditor
.getMarkupModel().getAllHighlighters()).contains(myHighlighter
)) {
681 myEditor
.getMarkupModel().removeHighlighter(myHighlighter
);
682 myHighlighter
= null;
686 public void keyTyped(KeyEvent e
) {}
687 public void keyReleased(KeyEvent e
) {}
688 public void mousePressed(MouseEvent e
) {}
689 public void mouseReleased(MouseEvent e
) {}
690 public void mouseEntered(MouseEvent e
) {}
691 public void mouseExited(MouseEvent e
) {}
694 private class PopupItemAction
extends AnAction
implements DumbAware
{
695 public PopupItemAction(Presentation p
) {
696 super(p
.getText(), p
.getText(), p
.getIcon());
699 public void actionPerformed(AnActionEvent e
) {
700 updatePresentation(e
.getPresentation());
701 updateDialectsCombo();
705 private class EditorListener
implements CaretListener
, SelectionListener
, DocumentListener
{
706 public void caretPositionChanged(CaretEvent e
) {
707 if (!available() || myEditor
.getSelectionModel().hasSelection()) return;
708 final PsiFile psiFile
= getPsiFile();
709 if (psiFile
== null) return;
710 final int offset
= myEditor
.getCaretModel().getOffset();
711 final PsiElement element
= PsiUtilBase
.getElementAtOffset(psiFile
, offset
);
712 myTreeBuilder
.select(element
);
715 public void selectionChanged(SelectionEvent e
) {
716 if (!available() || !myEditor
.getSelectionModel().hasSelection()) return;
717 final PsiFile psiFile
= getPsiFile();
718 if (psiFile
== null) return;
719 final SelectionModel selection
= myEditor
.getSelectionModel();
720 final int start
= selection
.getSelectionStart();
721 final int end
= selection
.getSelectionEnd();
722 final PsiElement element
= findCommonParent(PsiUtilBase
.getElementAtOffset(psiFile
, start
), PsiUtilBase
.getElementAtOffset(psiFile
, end
));
723 myTreeBuilder
.select(element
);
726 private boolean available() {
727 return myLastParsedTextHashCode
== myNewDocumentHashCode
&& myEditor
.getContentComponent().hasFocus();
731 private PsiFile
getPsiFile() {
732 final PsiElement root
= ((ViewerTreeStructure
)myTreeBuilder
.getTreeStructure()).getRootPsiElement();
733 return root
instanceof PsiFile ?
(PsiFile
)root
: null;
736 public void beforeDocumentChange(DocumentEvent event
) {
739 public void documentChanged(DocumentEvent event
) {
740 myNewDocumentHashCode
= event
.getDocument().getText().hashCode();