Merge Switcher and Next Splitter action (IDEA-23698, IDEA-63978, IDEA-25757)

This commit is contained in:
Konstantin Bulenkov
2011-02-21 20:00:41 +03:00
parent 33f7f240c7
commit 308455ec70
6 changed files with 187 additions and 65 deletions
@@ -15,7 +15,10 @@
*/
package com.intellij.ui.tabs;
import com.intellij.util.messages.Topic;
public interface TabsListener {
Topic<TabsListener> TOPIC = Topic.create("TabsListener", TabsListener.class);
void selectionChanged(TabInfo oldSelection, TabInfo newSelection);
@@ -243,7 +243,9 @@ public class JBTabsImpl extends JComponent
new LazyUiDisposable<JBTabsImpl>(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);
@@ -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<VirtualFile, FileEditor> files2editors;
//final Map<VirtualFile, FileEditor> files2editors;
final Map<String, ToolWindow> 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<VirtualFile, FileEditor>();
for (FileEditor editor : allEditors) {
files2editors.put(((FileEditorManagerImpl)editorManager).getFile(editor), editor);
final FileEditorManagerImpl editorManager = (FileEditorManagerImpl)FileEditorManager.getInstance(project);
final ArrayList<FileEditorInfo> filesData = new ArrayList<FileEditorInfo>();
final ArrayList<FileEditorInfo> editors = new ArrayList<FileEditorInfo>();
for (EditorsSplitters splitters : editorManager.getAllSplitters()) {
for (EditorWindow window : splitters.getWindows()) {
for (VirtualFile file : window.getFiles()) {
editors.add(new FileEditorInfo(file, window));
}
}
}
final ArrayList<VirtualFile> openFiles = new ArrayList<VirtualFile>();
final ArrayList<VirtualFile> editorFiles = new ArrayList<VirtualFile>();
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<VirtualFile> {
private final VirtualFile[] recentFiles;
private class RecentFilesComparator implements Comparator<FileEditorInfo> {
private Map<FileEditorInfo, Integer> tabs = new HashMap<FileEditorInfo, Integer>();
public RecentFilesComparator(Project project) {
recentFiles = EditorHistoryManager.getInstance(project).getFiles();
final List<TabInfo> 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;
}
}
}
@@ -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);
@@ -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();
}
@@ -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<LocalFileSystem.WatchRequest> WATCH_REQUEST_KEY = Key.create("WATCH_REQUEST_KEY");
private static final Key<Boolean> 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<TabInfo> myTabsHistory = new ArrayList<TabInfo>();
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<FileEditorManagerListener>(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<EditorWindow> 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<TabInfo> 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<TabInfo> i = myTabsHistory.iterator();
while (i.hasNext()) {
final TabInfo info = i.next();
if (info.getComponent() == selected) {
i.remove();
break;
}
}
myTabsHistory.add(0, newSelection);
}
public List<TabInfo> getTabsHistory() {
return myTabsHistory;
}
@Override
public void beforeSelectionChanged(TabInfo oldSelection, TabInfo newSelection) {
}
}