diff --git a/platform/platform-api/src/com/intellij/ui/tabs/TabsListener.java b/platform/platform-api/src/com/intellij/ui/tabs/TabsListener.java index 08888ee75160..ee3bc4a8f2af 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/TabsListener.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/TabsListener.java @@ -15,7 +15,10 @@ */ package com.intellij.ui.tabs; +import com.intellij.util.messages.Topic; + public interface TabsListener { + Topic TOPIC = Topic.create("TabsListener", TabsListener.class); void selectionChanged(TabInfo oldSelection, TabInfo newSelection); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index ef1716c324ca..12aaa3da4acd 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -243,7 +243,9 @@ public class JBTabsImpl extends JComponent new LazyUiDisposable(parent, this, this) { protected void initialize(@NotNull Disposable parent, @NotNull JBTabsImpl child, @Nullable Project project) { - myProject = project; + if (project != null) { + myProject = project; + } Disposer.register(child, myAnimator); Disposer.register(child, new Disposable() { @@ -793,6 +795,9 @@ public class JBTabsImpl extends JComponent private void fireSelectionChanged(TabInfo oldInfo, TabInfo newInfo) { if (oldInfo != newInfo) { + if (myProject != null) { + myProject.getMessageBus().syncPublisher(TabsListener.TOPIC).selectionChanged(oldInfo, newInfo); + } for (TabsListener eachListener : myTabListeners) { if (eachListener != null) { eachListener.selectionChanged(oldInfo, newInfo); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java index 3be56bce173b..ee52597d0427 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java @@ -22,13 +22,9 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.markup.EffectType; import com.intellij.openapi.editor.markup.TextAttributes; -import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; -import com.intellij.openapi.fileEditor.impl.EditorHistoryManager; -import com.intellij.openapi.fileEditor.impl.EditorWindow; -import com.intellij.openapi.fileEditor.impl.EditorsSplitters; -import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; +import com.intellij.openapi.fileEditor.impl.*; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; @@ -48,6 +44,7 @@ import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; +import com.intellij.ui.tabs.TabInfo; import com.intellij.util.ArrayUtil; import com.intellij.util.IconUtil; import com.intellij.util.Icons; @@ -158,7 +155,7 @@ public class Switcher extends AnAction implements DumbAware { final JLabel pathLabel = new JLabel(" "); final JPanel descriptions; final Project project; - final Map files2editors; + //final Map files2editors; final Map twShortcuts; @SuppressWarnings({"ManualArrayToCollectionCopy"}) @@ -250,38 +247,37 @@ public class Switcher extends AnAction implements DumbAware { return new Dimension(5, max.height); } }; - separator.setBackground(Color.WHITE); + separator.setBackground(Color.WHITE); - final FileEditorManager editorManager = FileEditorManager.getInstance(project); - final FileEditor[] allEditors = editorManager.getAllEditors(); - files2editors = new HashMap(); - for (FileEditor editor : allEditors) { - files2editors.put(((FileEditorManagerImpl)editorManager).getFile(editor), editor); + final FileEditorManagerImpl editorManager = (FileEditorManagerImpl)FileEditorManager.getInstance(project); + final ArrayList filesData = new ArrayList(); + final ArrayList editors = new ArrayList(); + for (EditorsSplitters splitters : editorManager.getAllSplitters()) { + for (EditorWindow window : splitters.getWindows()) { + for (VirtualFile file : window.getFiles()) { + editors.add(new FileEditorInfo(file, window)); + } + } } - final ArrayList openFiles = new ArrayList(); - final ArrayList editorFiles = new ArrayList(); - for (EditorsSplitters splitters : ((FileEditorManagerImpl)editorManager).getAllSplitters()) { - editorFiles.addAll(Arrays.asList(splitters.getOpenFiles())); - } - if (editorFiles.size() < 2) { + if (editors.size() < 2) { final VirtualFile[] recentFiles = ArrayUtil.reverseArray(EditorHistoryManager.getInstance(project).getFiles()); - final int len = Math.min(toolWindows.getModel().getSize(), Math.max(editorFiles.size(), recentFiles.length)); + final int len = Math.min(toolWindows.getModel().getSize(), Math.max(editors.size(), recentFiles.length)); for (int i = 0; i < len; i++) { - openFiles.add(recentFiles[i]); + filesData.add(new FileEditorInfo(recentFiles[i], null)); } } else { try { - ContainerUtil.sort(editorFiles, new RecentFilesComparator(project)); + ContainerUtil.sort(editors, new RecentFilesComparator(project)); } catch (Exception e) {// IndexNotReadyException } - for (int i = 0; i < Math.min(MAX_FILES, editorFiles.size()); i++) { - openFiles.add(editorFiles.get(i)); + for (int i = 0; i < Math.min(MAX_FILES, editors.size()); i++) { + filesData.add(editors.get(i)); } } final DefaultListModel filesModel = new DefaultListModel(); - for (VirtualFile openFile : openFiles) { - filesModel.addElement(openFile); + for (FileEditorInfo editor : filesData) { + filesModel.addElement(editor); } files = new JBList(filesModel); @@ -346,7 +342,7 @@ public class Switcher extends AnAction implements DumbAware { private void updatePathLabel() { final Object[] values = files.getSelectedValues(); if (values != null && values.length == 1) { - final VirtualFile parent = ((VirtualFile)values[0]).getParent(); + final VirtualFile parent = ((FileEditorInfo)values[0]).file.getParent(); if (parent != null) { pathLabel.setText(getTitle2Text(parent.getPresentableUrl())); } else { @@ -460,25 +456,28 @@ public class Switcher extends AnAction implements DumbAware { private void closeTabOrToolWindow() { final Object value = getSelectedList().getSelectedValue(); - if (value instanceof VirtualFile) { - final VirtualFile virtualFile = (VirtualFile)value; - final FileEditorManager editorManager = FileEditorManager.getInstance(project); - if (editorManager instanceof FileEditorManagerImpl) { - final JList jList = getSelectedList(); - ((FileEditorManagerImpl)editorManager).closeFile(virtualFile, false); - final int selectedIndex = jList.getSelectedIndex(); - if (jList.getModel().getSize() == 1) { - goLeft(); - ((DefaultListModel)jList.getModel()).removeElementAt(selectedIndex); - this.remove(jList); - this.remove(separator); - } else { - goForward(); - ((DefaultListModel)jList.getModel()).removeElementAt(selectedIndex); - jList.setSize(jList.getPreferredSize()); - } - pack(); + if (value instanceof FileEditorInfo) { + final FileEditorInfo info = (FileEditorInfo)value; + final VirtualFile virtualFile = info.file; + final FileEditorManagerImpl editorManager = ((FileEditorManagerImpl)FileEditorManager.getInstance(project)); + final JList jList = getSelectedList(); + if (info.window == null) { + editorManager.closeFile(virtualFile, false); + } else { + editorManager.closeFile(virtualFile, info.window, false); } + final int selectedIndex = jList.getSelectedIndex(); + if (jList.getModel().getSize() == 1) { + goLeft(); + ((DefaultListModel)jList.getModel()).removeElementAt(selectedIndex); + this.remove(jList); + this.remove(separator); + } else { + goForward(); + ((DefaultListModel)jList.getModel()).removeElementAt(selectedIndex); + jList.setSize(jList.getPreferredSize()); + } + pack(); } else if (value instanceof ToolWindow) { final ToolWindow toolWindow = (ToolWindow)value; if (twManager instanceof ToolWindowManagerImpl) { @@ -587,26 +586,39 @@ public class Switcher extends AnAction implements DumbAware { if (value instanceof ToolWindow) { ((ToolWindow)value).activate(null, true, true); } - else if (value instanceof VirtualFile) { - final VirtualFile file = (VirtualFile)value; + else if (value instanceof FileEditorInfo) { + final FileEditorInfo info = (FileEditorInfo)value; IdeFocusManager.getInstance(project).doWhenFocusSettlesDown(new Runnable() { @Override public void run() { - FileEditorManager.getInstance(project).openFile(file, true, true); + final FileEditorManagerImpl manager = (FileEditorManagerImpl)FileEditorManager.getInstance(project); + if (info.window != null) { + manager.openFileImpl2(info.window, info.file, true); + } else { + manager.openFile(info.file, true); + } } }); } } - private class RecentFilesComparator implements Comparator { - private final VirtualFile[] recentFiles; + private class RecentFilesComparator implements Comparator { + private Map tabs = new HashMap(); public RecentFilesComparator(Project project) { - recentFiles = EditorHistoryManager.getInstance(project).getFiles(); + final List history = ((FileEditorManagerImpl)FileEditorManager.getInstance(project)).getTabsHistory(); + for (int i = 0; i < history.size(); i++) { + final TabInfo info = history.get(i); + if (info.getObject() instanceof VirtualFile && info.getComponent() instanceof EditorWindowHolder) { + tabs.put(new FileEditorInfo((VirtualFile)info.getObject(), ((EditorWindowHolder)info.getComponent()).getEditorWindow()), i); + } + } } - public int compare(VirtualFile vf1, VirtualFile vf2) { - return ArrayUtil.find(recentFiles, vf2) - ArrayUtil.find(recentFiles, vf1); + public int compare(FileEditorInfo vf1, FileEditorInfo vf2) { + final Integer index1 = tabs.get(vf1); + final Integer index2 = tabs.get(vf2); + return index1 == null && index2 == null ? 0 : index1 == null ? 1 : index2 == null ? -1 : index1 - index2; } } @@ -673,8 +685,8 @@ public class Switcher extends AnAction implements DumbAware { } protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { - if (value instanceof VirtualFile) { - final VirtualFile virtualFile = (VirtualFile)value; + if (value instanceof FileEditorInfo) { + final VirtualFile virtualFile = ((FileEditorInfo)value).file; final String name = virtualFile.getPresentableName(); setIcon(IconUtil.getIcon(virtualFile, Iconable.ICON_FLAG_READ_STATUS, myProject)); @@ -745,4 +757,34 @@ public class Switcher extends AnAction implements DumbAware { return new ImageIcon(img); } } + + private static class FileEditorInfo { + final VirtualFile file; + final EditorWindow window; + + private FileEditorInfo(VirtualFile file, EditorWindow window) { + this.file = file; + this.window = window; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FileEditorInfo that = (FileEditorInfo)o; + + if (!file.equals(that.file)) return false; + if (!window.equals(that.window)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = file.hashCode(); + result = 31 * result + window.hashCode(); + return result; + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java index 138b9ef0fb91..2d38e718986d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java @@ -325,15 +325,22 @@ public class EditorWindow { return myTabbedPane; } - protected static class TComp extends JPanel implements DataProvider{ + protected static class TComp extends JPanel implements DataProvider, EditorWindowHolder { final EditorWithProviderComposite myEditor; + protected final EditorWindow myWindow; - TComp(final EditorWithProviderComposite editor) { + TComp(final EditorWindow window, final EditorWithProviderComposite editor) { super(new BorderLayout()); myEditor = editor; + myWindow = window; add(editor.getComponent(), BorderLayout.CENTER); } + @Override + public EditorWindow getEditorWindow() { + return myWindow; + } + public Object getData(String dataId) { if (PlatformDataKeys.VIRTUAL_FILE.is(dataId)){ final VirtualFile virtualFile = myEditor.getFile(); @@ -347,11 +354,8 @@ public class EditorWindow { } protected static class TCompForTablessMode extends TComp{ - private final EditorWindow myWindow; - TCompForTablessMode(final EditorWindow window, final EditorWithProviderComposite editor) { - super(editor); - myWindow = window; + super(window, editor); } public Object getData(String dataId) { @@ -434,7 +438,7 @@ public class EditorWindow { final int indexToInsert = initialIndex == null ? myTabbedPane.getSelectedIndex() + 1 : initialIndex; final VirtualFile file = editor.getFile(); final Icon template = IconLoader.getIcon("/fileTypes/text.png"); - myTabbedPane.insertTab(file, new EmptyIcon(template.getIconWidth(), template.getIconHeight()), new TComp(editor), null, indexToInsert); + myTabbedPane.insertTab(file, new EmptyIcon(template.getIconWidth(), template.getIconHeight()), new TComp(this, editor), null, indexToInsert); trimToSize(UISettings.getInstance().EDITOR_TAB_LIMIT, file, false); setSelectedEditor(editor, focusEditor); myOwner.updateFileIcon(file); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindowHolder.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindowHolder.java new file mode 100644 index 000000000000..c708d058b63b --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindowHolder.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.fileEditor.impl; + +/** + * @author Konstantin Bulenkov + */ +public interface EditorWindowHolder { + EditorWindow getEditorWindow(); +} diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index 36b48a92bd06..d758df4030d3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -60,6 +60,8 @@ import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.StatusBarEx; import com.intellij.ui.docking.DockContainer; import com.intellij.ui.docking.DockManager; +import com.intellij.ui.tabs.TabInfo; +import com.intellij.ui.tabs.TabsListener; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.messages.impl.MessageListenerList; @@ -82,7 +84,7 @@ import java.util.List; * @author Eugene Belyaev * @author Vladimir Kondratyev */ -public class FileEditorManagerImpl extends FileEditorManagerEx implements ProjectComponent, JDOMExternalizable { +public class FileEditorManagerImpl extends FileEditorManagerEx implements ProjectComponent, JDOMExternalizable, TabsListener { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl"); private static final Key WATCH_REQUEST_KEY = Key.create("WATCH_REQUEST_KEY"); private static final Key DUMB_AWARE = Key.create("DUMB_AWARE"); @@ -93,6 +95,8 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec private volatile JPanel myPanels; private EditorsSplitters mySplitters; private final Project myProject; + private final List myTabsHistory = new ArrayList(); + private final MergingUpdateQueue myQueue = new MergingUpdateQueue("FileEditorManagerUpdateQueue", 50, true, null); @@ -102,6 +106,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec private final MyEditorPropertyChangeListener myEditorPropertyChangeListener = new MyEditorPropertyChangeListener(); private DockManager myDockManager; private DockableEditorContainerFactory myContentFactory; + private MessageBusConnection myBusConnection; public FileEditorManagerImpl(final Project project, DockManager dockManager) { /* ApplicationManager.getApplication().assertIsDispatchThread(); */ @@ -109,6 +114,8 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec myDockManager = dockManager; myListenerList = new MessageListenerList(myProject.getMessageBus(), FileEditorManagerListener.FILE_EDITOR_MANAGER); + myBusConnection = project.getMessageBus().connect(project); + myBusConnection.subscribe(TabsListener.TOPIC, this); } private void initDockableContentFactory() { @@ -473,13 +480,13 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec getSplitters().setCurrentWindow(window, true); } - public void closeFile(@NotNull final VirtualFile file, @NotNull final EditorWindow window) { + public void closeFile(@NotNull final VirtualFile file, @NotNull final EditorWindow window, final boolean transferFocus) { assertDispatchThread(); CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { public void run() { if (window.isFileOpen(file)) { - window.closeFile(file); + window.closeFile(file, true, transferFocus); final List windows = window.getOwner().findWindows(file); if (windows.isEmpty()) { // no more windows containing this file left final LocalFileSystem.WatchRequest request = file.getUserData(WATCH_REQUEST_KEY); @@ -490,6 +497,22 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } } }, IdeBundle.message("command.close.active.editor"), null); + removeHistory(file, window); + } + + private void removeHistory(VirtualFile file, EditorWindow window) { + for (Iterator i = myTabsHistory.iterator(); i.hasNext();) { + final TabInfo info = i.next(); + final JComponent c = info.getComponent(); + if (info.getObject() == file && c instanceof EditorWindowHolder && ((EditorWindowHolder)c).getEditorWindow() == window) { + i.remove(); + break; + } + } + } + + public void closeFile(@NotNull final VirtualFile file, @NotNull final EditorWindow window) { + closeFile(file, window, true); } //============================= EditorManager methods ================================ @@ -1182,6 +1205,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec // Dispose created editors. We do not use use closeEditor method because // it fires event and changes history. closeAllFiles(); + myBusConnection.disconnect(); } // BaseCompomemnt methods @@ -1600,4 +1624,25 @@ private final class MyVirtualFileListener extends VirtualFileAdapter { return splitters; } + @Override + public void selectionChanged(TabInfo oldSelection, TabInfo newSelection) { + final JComponent selected = newSelection.getComponent(); + final Iterator i = myTabsHistory.iterator(); + while (i.hasNext()) { + final TabInfo info = i.next(); + if (info.getComponent() == selected) { + i.remove(); + break; + } + } + myTabsHistory.add(0, newSelection); + } + + public List getTabsHistory() { + return myTabsHistory; + } + + @Override + public void beforeSelectionChanged(TabInfo oldSelection, TabInfo newSelection) { + } }