diff --git a/openapi/src/com/intellij/ide/structureView/FileEditorPositionListener.java b/openapi/src/com/intellij/ide/structureView/FileEditorPositionListener.java index 861580d23e16..1f02fa6e861e 100644 --- a/openapi/src/com/intellij/ide/structureView/FileEditorPositionListener.java +++ b/openapi/src/com/intellij/ide/structureView/FileEditorPositionListener.java @@ -32,5 +32,5 @@ package com.intellij.ide.structureView; public interface FileEditorPositionListener { - void onCurrentElementChanged(Object currentElement); + void onCurrentElementChanged(); } diff --git a/openapi/src/com/intellij/ide/structureView/StructureViewModel.java b/openapi/src/com/intellij/ide/structureView/StructureViewModel.java index a3710a1fc033..d8de7080bf87 100644 --- a/openapi/src/com/intellij/ide/structureView/StructureViewModel.java +++ b/openapi/src/com/intellij/ide/structureView/StructureViewModel.java @@ -36,5 +36,11 @@ import com.intellij.ide.util.treeView.smartTree.TreeModel; public interface StructureViewModel extends TreeModel{ Object getCurrentEditorElement(); + void addEditorPositionListener(FileEditorPositionListener listener); + + void removeEditorPositionListener(FileEditorPositionListener listener); + StructureViewTreeElement getRoot(); + + void dispose(); } diff --git a/openapi/src/com/intellij/ide/structureView/TextEditorBasedStructureViewModel.java b/openapi/src/com/intellij/ide/structureView/TextEditorBasedStructureViewModel.java new file mode 100644 index 000000000000..bb3a693d417b --- /dev/null +++ b/openapi/src/com/intellij/ide/structureView/TextEditorBasedStructureViewModel.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2004 JetBrains s.r.o. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * -Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduct the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * Neither the name of JetBrains or IntelliJ IDEA + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING + * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. JETBRAINS AND ITS LICENSORS SHALL NOT + * BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT + * OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS + * DERIVATIVES. IN NO EVENT WILL JETBRAINS OR ITS LICENSORS BE LIABLE FOR ANY LOST + * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, + * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY + * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN + * IF JETBRAINS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ +package com.intellij.ide.structureView; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.event.CaretEvent; +import com.intellij.openapi.editor.event.CaretListener; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.TextEditor; +import com.intellij.openapi.util.Comparing; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; + +import java.util.ArrayList; +import java.util.List; + +public abstract class TextEditorBasedStructureViewModel implements StructureViewModel { + private final Editor myEditor; + private final CaretListener myCaretListener; + private final List myListeners = new ArrayList(); + + private static Logger LOG = Logger.getInstance("#com.intellij.ide.structureView.TextEditorBasedStructureViewModel"); + + protected TextEditorBasedStructureViewModel(PsiFile psiFile) { + this(getEditorForFile(psiFile)); + + } + + private static Editor getEditorForFile(final PsiFile psiFile) { + final FileEditor[] editors = FileEditorManager.getInstance(psiFile.getProject()).getEditors(psiFile.getVirtualFile()); + for (int i = 0; i < editors.length; i++) { + FileEditor editor = editors[i]; + if (editor instanceof TextEditor) { + return ((TextEditor)editor).getEditor(); + } + } + return null; + } + + protected TextEditorBasedStructureViewModel(final Editor editor) { + LOG.assertTrue(editor != null); + myEditor = editor; + myCaretListener = new CaretListener() { + public void caretPositionChanged(CaretEvent e) { + if (Comparing.equal(e.getEditor(), myEditor)) { + fireCaretPositionChanged(); + } + } + + private void fireCaretPositionChanged() { + final FileEditorPositionListener[] listeners = myListeners.toArray(new FileEditorPositionListener[myListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCurrentElementChanged(); + } + } + }; + + EditorFactory.getInstance().getEventMulticaster().addCaretListener(myCaretListener); + } + + public final void addEditorPositionListener(FileEditorPositionListener listener) { + myListeners.add(listener); + } + + public final void removeEditorPositionListener(FileEditorPositionListener listener) { + myListeners.remove(listener); + } + + public void dispose() { + EditorFactory.getInstance().getEventMulticaster().removeCaretListener(myCaretListener); + } + + public final Object getCurrentEditorElement() { + final int offset = myEditor.getCaretModel().getOffset(); + PsiElement element = getPsiFile().findElementAt(offset); + while (!isSutable(element)) { + if (element == null) return null; + element = element.getParent(); + } + return element; + } + + protected abstract PsiFile getPsiFile(); + + private boolean isSutable(final PsiElement element) { + if (element == null) return false; + final Class[] suitableClasses = getSuitableClasses(); + for (int i = 0; i < suitableClasses.length; i++) { + Class suitableClass = suitableClasses[i]; + if (suitableClass.isAssignableFrom(element.getClass())) return true; + } + return false; + } + + protected abstract Class[] getSuitableClasses(); +} diff --git a/source/com/intellij/ide/impl/StructureViewWrapper.java b/source/com/intellij/ide/impl/StructureViewWrapper.java index 81cd56300a8e..d1559de48deb 100644 --- a/source/com/intellij/ide/impl/StructureViewWrapper.java +++ b/source/com/intellij/ide/impl/StructureViewWrapper.java @@ -78,8 +78,7 @@ public class StructureViewWrapper implements StructureView { } setFileEditor(newEditor); if (myStructureViewFactory.AUTOSCROLL_FROM_SOURCE && myStructureView != null) { - FileEditor newEditor = event.getNewEditor(); - myStructureView.scrollToElementAtCaret(newEditor); + myStructureView.scrollToSelectedElement(); } } }, 400 diff --git a/source/com/intellij/ide/structureView/impl/java/JavaFileTreeModel.java b/source/com/intellij/ide/structureView/impl/java/JavaFileTreeModel.java index f18dd2c15e2e..c11d2cf76931 100644 --- a/source/com/intellij/ide/structureView/impl/java/JavaFileTreeModel.java +++ b/source/com/intellij/ide/structureView/impl/java/JavaFileTreeModel.java @@ -1,21 +1,17 @@ package com.intellij.ide.structureView.impl.java; -import com.intellij.ide.structureView.StructureViewModel; import com.intellij.ide.structureView.StructureViewTreeElement; +import com.intellij.ide.structureView.TextEditorBasedStructureViewModel; import com.intellij.ide.util.treeView.smartTree.Filter; import com.intellij.ide.util.treeView.smartTree.Grouper; import com.intellij.ide.util.treeView.smartTree.Sorter; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.util.Comparing; import com.intellij.psi.*; -public class JavaFileTreeModel implements StructureViewModel { +public class JavaFileTreeModel extends TextEditorBasedStructureViewModel { private final PsiJavaFile myFile; public JavaFileTreeModel(PsiJavaFile file) { + super(file); myFile = file; } @@ -37,21 +33,11 @@ public class JavaFileTreeModel implements StructureViewModel { return new Sorter[]{KindSorter.INSTANCE, Sorter.ALPHA_SORTER, VisibilitySorter.INSTANCE}; } - public Object getCurrentEditorElement() { - final Editor editor = FileEditorManager.getInstance(myFile.getProject()).getSelectedTextEditor(); - final Document document = FileDocumentManager.getInstance().getDocument(myFile.getVirtualFile()); - if (!Comparing.equal(editor.getDocument(), document)) return null; - - final int offset = editor.getCaretModel().getOffset(); - PsiElement element = myFile.findElementAt(offset); - while (!isSutable(element)) { - if (element == null) return null; - element = element.getParent(); - } - return element; + protected PsiFile getPsiFile() { + return myFile; } - private boolean isSutable(final PsiElement element) { - return element instanceof PsiClass || element instanceof PsiMethod || element instanceof PsiField; + protected Class[] getSuitableClasses() { + return new Class[]{PsiClass.class, PsiMethod.class, PsiField.class}; } } diff --git a/source/com/intellij/ide/structureView/impl/xml/XmlStructureViewTreeModel.java b/source/com/intellij/ide/structureView/impl/xml/XmlStructureViewTreeModel.java index a70fe05d595b..aa9d4aa44f6f 100644 --- a/source/com/intellij/ide/structureView/impl/xml/XmlStructureViewTreeModel.java +++ b/source/com/intellij/ide/structureView/impl/xml/XmlStructureViewTreeModel.java @@ -31,15 +31,20 @@ */ package com.intellij.ide.structureView.impl.xml; -import com.intellij.ide.util.treeView.smartTree.*; -import com.intellij.ide.structureView.StructureViewModel; import com.intellij.ide.structureView.StructureViewTreeElement; +import com.intellij.ide.structureView.TextEditorBasedStructureViewModel; +import com.intellij.ide.util.treeView.smartTree.Filter; +import com.intellij.ide.util.treeView.smartTree.Grouper; +import com.intellij.ide.util.treeView.smartTree.Sorter; +import com.intellij.psi.PsiFile; import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; -public class XmlStructureViewTreeModel implements StructureViewModel{ +public class XmlStructureViewTreeModel extends TextEditorBasedStructureViewModel{ private final XmlFile myFile; public XmlStructureViewTreeModel(XmlFile file) { + super(file); myFile = file; } @@ -59,7 +64,11 @@ public class XmlStructureViewTreeModel implements StructureViewModel{ return new Filter[0]; } - public Object getCurrentEditorElement() { - return null; + protected PsiFile getPsiFile() { + return myFile; + } + + protected Class[] getSuitableClasses() { + return new Class[]{XmlTag.class}; } } diff --git a/source/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java b/source/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java index 14ae1c58833c..28b6429d135d 100644 --- a/source/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java +++ b/source/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java @@ -2,6 +2,7 @@ package com.intellij.ide.structureView.newStructureView; import com.intellij.ide.CopyPasteManagerEx; import com.intellij.ide.DataManager; +import com.intellij.ide.structureView.FileEditorPositionListener; import com.intellij.ide.structureView.StructureViewFactory; import com.intellij.ide.structureView.StructureViewModel; import com.intellij.ide.structureView.StructureViewTreeElement; @@ -13,18 +14,12 @@ import com.intellij.ide.util.treeView.smartTree.*; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.DataConstantsEx; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.event.CaretEvent; -import com.intellij.openapi.editor.event.CaretListener; -import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.Navigatable; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; @@ -382,16 +377,13 @@ public class StructureViewComponent extends JPanel implements TreeActionsOwner, return null; } - public void scrollToElementAtCaret(final FileEditor editor) { + public void scrollToSelectedElement() { if (myAutoscrollFeedback) { myAutoscrollFeedback = false; return; } - if (myFileEditor == null || !Comparing.equal(myFileEditor, editor)) return; - - StructureViewFactoryImpl structureViewFactory - = (StructureViewFactoryImpl)StructureViewFactory.getInstance(myProject); + StructureViewFactoryImpl structureViewFactory = (StructureViewFactoryImpl)StructureViewFactory.getInstance(myProject); if (!structureViewFactory.AUTOSCROLL_FROM_SOURCE) return; @@ -512,7 +504,7 @@ public class StructureViewComponent extends JPanel implements TreeActionsOwner, } private class MyAutoScrollFromSourceHandler extends AutoScrollFromSourceHandler { - private CaretListener myEditorCaretListener; + private FileEditorPositionListener myFileEditorPositionListener; private MyAutoScrollFromSourceHandler(Project project) { super(project); @@ -523,24 +515,17 @@ public class StructureViewComponent extends JPanel implements TreeActionsOwner, } public void dispose() { - EditorFactory.getInstance().getEventMulticaster().removeCaretListener(myEditorCaretListener); + myTreeModel.removeEditorPositionListener(myFileEditorPositionListener); + myTreeModel.dispose(); } private void addEditorCaretListener() { - myEditorCaretListener = new CaretListener() { - public void caretPositionChanged(final CaretEvent e) { - Editor editor = e.getEditor(); - FileEditor fileEditor = getFileEditorForEditor(editor); - scrollToElementAtCaret(fileEditor); - } - - private FileEditor getFileEditorForEditor(Editor editor) { - VirtualFile file = FileDocumentManager.getInstance().getFile(editor.getDocument()); - if (file == null) return null; - return FileEditorManager.getInstance(myProject).getSelectedEditor(file); + myFileEditorPositionListener = new FileEditorPositionListener() { + public void onCurrentElementChanged() { + scrollToSelectedElement(); } }; - EditorFactory.getInstance().getEventMulticaster().addCaretListener(myEditorCaretListener); + myTreeModel.addEditorPositionListener(myFileEditorPositionListener); } protected boolean isAutoScrollMode() { @@ -553,7 +538,7 @@ public class StructureViewComponent extends JPanel implements TreeActionsOwner, structureViewFactory.AUTOSCROLL_FROM_SOURCE = state; final FileEditor[] selectedEditors = FileEditorManager.getInstance(myProject).getSelectedEditors(); if (selectedEditors != null && selectedEditors.length > 0) { - if (state) scrollToElementAtCaret(selectedEditors[0]); + if (state) scrollToSelectedElement(); } } } diff --git a/source/com/intellij/ide/structureView/newStructureView/TreeModelWrapper.java b/source/com/intellij/ide/structureView/newStructureView/TreeModelWrapper.java index 19cf87a97eb1..859ed20de0e0 100644 --- a/source/com/intellij/ide/structureView/newStructureView/TreeModelWrapper.java +++ b/source/com/intellij/ide/structureView/newStructureView/TreeModelWrapper.java @@ -3,6 +3,7 @@ package com.intellij.ide.structureView.newStructureView; import com.intellij.ide.util.treeView.smartTree.*; import com.intellij.ide.structureView.StructureViewModel; import com.intellij.ide.structureView.StructureViewTreeElement; +import com.intellij.ide.structureView.FileEditorPositionListener; import java.util.ArrayList; @@ -54,4 +55,16 @@ public class TreeModelWrapper implements StructureViewModel { public static boolean shouldRevert(final TreeAction action) { return action instanceof Filter && ((Filter)action).isReverted(); } + + public void addEditorPositionListener(FileEditorPositionListener listener) { + myModel.addEditorPositionListener(listener); + } + + public void removeEditorPositionListener(FileEditorPositionListener listener) { + myModel.removeEditorPositionListener(listener); + } + + public void dispose() { + myModel.dispose(); + } } diff --git a/source/com/intellij/ide/util/treeView/smartTree/CachingChildrenTreeNode.java b/source/com/intellij/ide/util/treeView/smartTree/CachingChildrenTreeNode.java index 8e769a37af3d..f8751d590640 100644 --- a/source/com/intellij/ide/util/treeView/smartTree/CachingChildrenTreeNode.java +++ b/source/com/intellij/ide/util/treeView/smartTree/CachingChildrenTreeNode.java @@ -8,7 +8,7 @@ import com.intellij.openapi.util.Comparing; import java.util.*; public abstract class CachingChildrenTreeNode extends AbstractTreeNode { - private List myChildren; + protected List myChildren; protected List myOldChildren = null; protected final TreeModel myTreeModel; @@ -173,7 +173,7 @@ public abstract class CachingChildrenTreeNode extends AbstractTreeNode iterator = myOldChildren.iterator(); iterator.hasNext();) { CachingChildrenTreeNode oldInstance = iterator.next(); final int newIndex = getIndexOfPointerToTheSameValue(oldInstance); diff --git a/source/com/intellij/ide/util/treeView/smartTree/SmartTreeStructure.java b/source/com/intellij/ide/util/treeView/smartTree/SmartTreeStructure.java index 33c72a682600..223309cea51f 100644 --- a/source/com/intellij/ide/util/treeView/smartTree/SmartTreeStructure.java +++ b/source/com/intellij/ide/util/treeView/smartTree/SmartTreeStructure.java @@ -54,6 +54,11 @@ public class SmartTreeStructure extends AbstractTreeStructure { } public void rebuildTree() { + if (myRootElementWrapper != null) { + final TreeElementWrapper newRoot = createTree(); + newRoot.myOldChildren = myRootElementWrapper.myChildren; + newRoot.synchronizeChildren(); + } ((CachingChildrenTreeNode)getRootElement()).rebuildChildren(); } }