65adf52041b97707365bcfc7b39c2d9b5e93ee60
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / fileEditor / impl / FileEditorManagerImpl.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.openapi.fileEditor.impl;
17
18 import com.intellij.ProjectTopics;
19 import com.intellij.ide.IdeBundle;
20 import com.intellij.ide.plugins.PluginManagerCore;
21 import com.intellij.ide.ui.UISettings;
22 import com.intellij.ide.ui.UISettingsListener;
23 import com.intellij.injected.editor.VirtualFileWindow;
24 import com.intellij.openapi.Disposable;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.application.ModalityState;
27 import com.intellij.openapi.application.ex.ApplicationManagerEx;
28 import com.intellij.openapi.application.impl.LaterInvocator;
29 import com.intellij.openapi.command.CommandProcessor;
30 import com.intellij.openapi.components.ProjectComponent;
31 import com.intellij.openapi.diagnostic.Logger;
32 import com.intellij.openapi.editor.Editor;
33 import com.intellij.openapi.editor.ScrollType;
34 import com.intellij.openapi.editor.impl.EditorComponentImpl;
35 import com.intellij.openapi.extensions.Extensions;
36 import com.intellij.openapi.fileEditor.*;
37 import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
38 import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager;
39 import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
40 import com.intellij.openapi.fileEditor.impl.text.TextEditorImpl;
41 import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
42 import com.intellij.openapi.fileTypes.FileTypeEvent;
43 import com.intellij.openapi.fileTypes.FileTypeListener;
44 import com.intellij.openapi.fileTypes.FileTypeManager;
45 import com.intellij.openapi.project.DumbAwareRunnable;
46 import com.intellij.openapi.project.DumbService;
47 import com.intellij.openapi.project.PossiblyDumbAware;
48 import com.intellij.openapi.project.Project;
49 import com.intellij.openapi.project.impl.ProjectImpl;
50 import com.intellij.openapi.roots.ModuleRootAdapter;
51 import com.intellij.openapi.roots.ModuleRootEvent;
52 import com.intellij.openapi.startup.StartupManager;
53 import com.intellij.openapi.util.*;
54 import com.intellij.openapi.util.io.FileUtil;
55 import com.intellij.openapi.util.registry.Registry;
56 import com.intellij.openapi.vcs.FileStatus;
57 import com.intellij.openapi.vcs.FileStatusListener;
58 import com.intellij.openapi.vcs.FileStatusManager;
59 import com.intellij.openapi.vfs.*;
60 import com.intellij.openapi.wm.IdeFocusManager;
61 import com.intellij.openapi.wm.ToolWindowId;
62 import com.intellij.openapi.wm.ToolWindowManager;
63 import com.intellij.openapi.wm.WindowManager;
64 import com.intellij.openapi.wm.ex.StatusBarEx;
65 import com.intellij.openapi.wm.impl.IdeFrameImpl;
66 import com.intellij.ui.FocusTrackback;
67 import com.intellij.ui.docking.DockContainer;
68 import com.intellij.ui.docking.DockManager;
69 import com.intellij.ui.docking.impl.DockManagerImpl;
70 import com.intellij.ui.tabs.impl.JBTabsImpl;
71 import com.intellij.util.Function;
72 import com.intellij.util.SmartList;
73 import com.intellij.util.containers.ContainerUtil;
74 import com.intellij.util.messages.MessageBusConnection;
75 import com.intellij.util.messages.impl.MessageListenerList;
76 import com.intellij.util.ui.JBInsets;
77 import com.intellij.util.ui.UIUtil;
78 import com.intellij.util.ui.update.MergingUpdateQueue;
79 import com.intellij.util.ui.update.Update;
80 import org.jdom.Element;
81 import org.jetbrains.annotations.NotNull;
82 import org.jetbrains.annotations.Nullable;
83
84 import javax.swing.*;
85 import javax.swing.border.Border;
86 import java.awt.*;
87 import java.awt.event.KeyEvent;
88 import java.awt.event.MouseEvent;
89 import java.beans.PropertyChangeEvent;
90 import java.beans.PropertyChangeListener;
91 import java.lang.ref.WeakReference;
92 import java.util.*;
93 import java.util.List;
94 import java.util.concurrent.atomic.AtomicBoolean;
95
96 /**
97  * @author Anton Katilin
98  * @author Eugene Belyaev
99  * @author Vladimir Kondratyev
100  */
101 public class FileEditorManagerImpl extends FileEditorManagerEx implements ProjectComponent, JDOMExternalizable {
102   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl");
103   private static final Key<Boolean> DUMB_AWARE = Key.create("DUMB_AWARE");
104
105   private static final FileEditor[] EMPTY_EDITOR_ARRAY = {};
106   private static final FileEditorProvider[] EMPTY_PROVIDER_ARRAY = {};
107   public static final Key<Boolean> CLOSING_TO_REOPEN = Key.create("CLOSING_TO_REOPEN");
108   public static final String FILE_EDITOR_MANAGER = "FileEditorManager";
109
110   private volatile JPanel myPanels;
111   private PreviewPanel myPreviewPanel;
112   private EditorsSplitters mySplitters;
113   private final Project myProject;
114   private final List<Pair<VirtualFile, EditorWindow>> mySelectionHistory = new ArrayList<Pair<VirtualFile, EditorWindow>>();
115   private WeakReference<EditorComposite> myLastSelectedComposite = new WeakReference<EditorComposite>(null);
116   private final AtomicBoolean myPreviewBlocker = new AtomicBoolean(false);
117
118
119   private final MergingUpdateQueue myQueue = new MergingUpdateQueue("FileEditorManagerUpdateQueue", 50, true, null);
120
121   private final BusyObject.Impl.Simple myBusyObject = new BusyObject.Impl.Simple();
122
123   /**
124    * Removes invalid myEditor and updates "modified" status.
125    */
126   private final MyEditorPropertyChangeListener myEditorPropertyChangeListener = new MyEditorPropertyChangeListener();
127   private final DockManager myDockManager;
128   private DockableEditorContainerFactory myContentFactory;
129   private EditorHistoryManager myEditorHistoryManager;
130
131   public FileEditorManagerImpl(final Project project, DockManager dockManager, EditorHistoryManager editorHistoryManager) {
132 /*    ApplicationManager.getApplication().assertIsDispatchThread(); */
133     myProject = project;
134     myDockManager = dockManager;
135     myEditorHistoryManager = editorHistoryManager;
136     myListenerList =
137       new MessageListenerList<FileEditorManagerListener>(myProject.getMessageBus(), FileEditorManagerListener.FILE_EDITOR_MANAGER);
138
139     if (Extensions.getExtensions(FileEditorAssociateFinder.EP_NAME).length > 0) {
140       myListenerList.add(new FileEditorManagerAdapter() {
141         @Override
142         public void selectionChanged(@NotNull FileEditorManagerEvent event) {
143           EditorsSplitters splitters = getSplitters();
144           openAssociatedFile(event.getNewFile(), splitters.getCurrentWindow(), splitters);
145         }
146       });
147     }
148
149     myQueue.setTrackUiActivity(true);
150   }
151
152   public void initDockableContentFactory() {
153     if (myContentFactory != null) return;
154
155     myContentFactory = new DockableEditorContainerFactory(myProject, this, myDockManager);
156     myDockManager.register(DockableEditorContainerFactory.TYPE, myContentFactory);
157     Disposer.register(myProject, myContentFactory);
158   }
159
160   public static boolean isDumbAware(FileEditor editor) {
161     return Boolean.TRUE.equals(editor.getUserData(DUMB_AWARE)) &&
162            (!(editor instanceof PossiblyDumbAware) || ((PossiblyDumbAware)editor).isDumbAware());
163   }
164
165   //-------------------------------------------------------------------------------
166
167   @Override
168   public JComponent getComponent() {
169     initUI();
170     return myPanels;
171   }
172
173   @NotNull
174   public EditorsSplitters getMainSplitters() {
175     initUI();
176
177     return mySplitters;
178   }
179
180   public Set<EditorsSplitters> getAllSplitters() {
181     HashSet<EditorsSplitters> all = new LinkedHashSet<EditorsSplitters>();
182     if (Registry.is("editor.use.preview")) {
183       initUI();
184       all.add(myPreviewPanel.getWindow().getOwner());
185     }
186     all.add(getMainSplitters());
187     Set<DockContainer> dockContainers = myDockManager.getContainers();
188     for (DockContainer each : dockContainers) {
189       if (each instanceof DockableEditorTabbedContainer) {
190         all.add(((DockableEditorTabbedContainer)each).getSplitters());
191       }
192     }
193
194     return Collections.unmodifiableSet(all);
195   }
196
197   private AsyncResult<EditorsSplitters> getActiveSplitters(boolean syncUsage) {
198     final boolean async = Registry.is("ide.windowSystem.asyncSplitters") && !syncUsage;
199
200     final AsyncResult<EditorsSplitters> result = new AsyncResult<EditorsSplitters>();
201     final IdeFocusManager fm = IdeFocusManager.getInstance(myProject);
202     Runnable run = new Runnable() {
203       @Override
204       public void run() {
205         if (myProject.isDisposed()) {
206           result.setRejected();
207           return;
208         }
209
210         Component focusOwner = fm.getFocusOwner();
211         if (focusOwner == null && !async) {
212           focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
213         }
214
215         if (focusOwner == null && !async) {
216           focusOwner = fm.getLastFocusedFor(fm.getLastFocusedFrame());
217         }
218
219         DockContainer container = myDockManager.getContainerFor(focusOwner);
220         if (container == null && !async) {
221           focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
222           container = myDockManager.getContainerFor(focusOwner);
223         }
224
225         if (container instanceof DockableEditorTabbedContainer) {
226           result.setDone(((DockableEditorTabbedContainer)container).getSplitters());
227         }
228         else {
229           result.setDone(getMainSplitters());
230         }
231       }
232     };
233
234     if (async) {
235       fm.doWhenFocusSettlesDown(run);
236     }
237     else {
238       UIUtil.invokeAndWaitIfNeeded(run);
239     }
240
241     return result;
242   }
243
244   private final Object myInitLock = new Object();
245
246   private void initUI() {
247     if (myPanels == null) {
248       synchronized (myInitLock) {
249         if (myPanels == null) {
250           myPanels = new JPanel(new BorderLayout());
251           myPanels.setOpaque(false);
252           myPanels.setBorder(new MyBorder());
253           mySplitters = new EditorsSplitters(this, myDockManager, true);
254           myPanels.add(mySplitters, BorderLayout.CENTER);
255         }
256       }
257     }
258     if (myPreviewPanel == null && Registry.is("editor.use.preview")) {
259       synchronized (myInitLock) {
260         myPreviewPanel = new PreviewPanel(myProject, this, myDockManager);
261       }
262     }
263   }
264
265   private static class MyBorder implements Border {
266     @Override
267     public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
268       if (UIUtil.isUnderAquaLookAndFeel()) {
269         g.setColor(JBTabsImpl.MAC_AQUA_BG_COLOR);
270         final Insets insets = getBorderInsets(c);
271         if (insets.top > 0) {
272           g.fillRect(x, y, width, height + insets.top);
273         }
274       }
275     }
276
277     @Override
278     public Insets getBorderInsets(Component c) {
279       return JBInsets.NONE;
280     }
281
282     @Override
283     public boolean isBorderOpaque() {
284       return false;
285     }
286   }
287
288   @Override
289   public JComponent getPreferredFocusedComponent() {
290     assertReadAccess();
291     final EditorWindow window = getSplitters().getCurrentWindow();
292     if (window != null) {
293       final EditorWithProviderComposite editor = window.getSelectedEditor();
294       if (editor != null) {
295         return editor.getPreferredFocusedComponent();
296       }
297     }
298     return null;
299   }
300
301   //-------------------------------------------------------
302
303   /**
304    * @return color of the <code>file</code> which corresponds to the
305    *         file's status
306    */
307   public Color getFileColor(@NotNull final VirtualFile file) {
308     final FileStatusManager fileStatusManager = FileStatusManager.getInstance(myProject);
309     Color statusColor = fileStatusManager != null ? fileStatusManager.getStatus(file).getColor() : UIUtil.getLabelForeground();
310     if (statusColor == null) statusColor = UIUtil.getLabelForeground();
311     return statusColor;
312   }
313
314   public boolean isProblem(@NotNull final VirtualFile file) {
315     return false;
316   }
317
318   public String getFileTooltipText(VirtualFile file) {
319     return FileUtil.getLocationRelativeToUserHome(file.getPresentableUrl());
320   }
321
322   @Override
323   public void updateFilePresentation(@NotNull VirtualFile file) {
324     if (!isFileOpen(file)) return;
325
326     updateFileColor(file);
327     updateFileIcon(file);
328     updateFileName(file);
329     updateFileBackgroundColor(file);
330   }
331
332   /**
333    * Updates tab color for the specified <code>file</code>. The <code>file</code>
334    * should be opened in the myEditor, otherwise the method throws an assertion.
335    */
336   private void updateFileColor(final VirtualFile file) {
337     Set<EditorsSplitters> all = getAllSplitters();
338     for (EditorsSplitters each : all) {
339       each.updateFileColor(file);
340     }
341   }
342
343   private void updateFileBackgroundColor(final VirtualFile file) {
344     Set<EditorsSplitters> all = getAllSplitters();
345     for (EditorsSplitters each : all) {
346       each.updateFileBackgroundColor(file);
347     }
348   }
349
350   /**
351    * Updates tab icon for the specified <code>file</code>. The <code>file</code>
352    * should be opened in the myEditor, otherwise the method throws an assertion.
353    */
354   protected void updateFileIcon(final VirtualFile file) {
355     Set<EditorsSplitters> all = getAllSplitters();
356     for (EditorsSplitters each : all) {
357       each.updateFileIcon(file);
358     }
359   }
360
361   /**
362    * Updates tab title and tab tool tip for the specified <code>file</code>
363    */
364   void updateFileName(@Nullable final VirtualFile file) {
365     // Queue here is to prevent title flickering when tab is being closed and two events arriving: with component==null and component==next focused tab
366     // only the last event makes sense to handle
367     myQueue.queue(new Update("UpdateFileName " + (file == null ? "" : file.getPath())) {
368       @Override
369       public boolean isExpired() {
370         return myProject.isDisposed() || !myProject.isOpen() || (file == null ? super.isExpired() : !file.isValid());
371       }
372
373       @Override
374       public void run() {
375         Set<EditorsSplitters> all = getAllSplitters();
376         for (EditorsSplitters each : all) {
377           each.updateFileName(file);
378         }
379       }
380     });
381   }
382
383   //-------------------------------------------------------
384
385
386   @Override
387   public VirtualFile getFile(@NotNull final FileEditor editor) {
388     final EditorComposite editorComposite = getEditorComposite(editor);
389     if (editorComposite != null) {
390       return editorComposite.getFile();
391     }
392     return null;
393   }
394
395   @Override
396   public void unsplitWindow() {
397     final EditorWindow currentWindow = getActiveSplitters(true).getResult().getCurrentWindow();
398     if (currentWindow != null) {
399       currentWindow.unsplit(true);
400     }
401   }
402
403   @Override
404   public void unsplitAllWindow() {
405     final EditorWindow currentWindow = getActiveSplitters(true).getResult().getCurrentWindow();
406     if (currentWindow != null) {
407       currentWindow.unsplitAll();
408     }
409   }
410
411   @Override
412   public int getWindowSplitCount() {
413     return getActiveSplitters(true).getResult().getSplitCount();
414   }
415
416   @Override
417   public boolean hasSplitOrUndockedWindows() {
418     Set<EditorsSplitters> splitters = getAllSplitters();
419     if (splitters.size() > 1) return true;
420     return getWindowSplitCount() > 1;
421   }
422
423   @Override
424   @NotNull
425   public EditorWindow[] getWindows() {
426     ArrayList<EditorWindow> windows = new ArrayList<EditorWindow>();
427     Set<EditorsSplitters> all = getAllSplitters();
428     for (EditorsSplitters each : all) {
429       EditorWindow[] eachList = each.getWindows();
430       windows.addAll(Arrays.asList(eachList));
431     }
432
433     return windows.toArray(new EditorWindow[windows.size()]);
434   }
435
436   @Override
437   public EditorWindow getNextWindow(@NotNull final EditorWindow window) {
438     final EditorWindow[] windows = getSplitters().getOrderedWindows();
439     for (int i = 0; i != windows.length; ++i) {
440       if (windows[i].equals(window)) {
441         return windows[(i + 1) % windows.length];
442       }
443     }
444     LOG.error("Not window found");
445     return null;
446   }
447
448   @Override
449   public EditorWindow getPrevWindow(@NotNull final EditorWindow window) {
450     final EditorWindow[] windows = getSplitters().getOrderedWindows();
451     for (int i = 0; i != windows.length; ++i) {
452       if (windows[i].equals(window)) {
453         return windows[(i + windows.length - 1) % windows.length];
454       }
455     }
456     LOG.error("Not window found");
457     return null;
458   }
459
460   @Override
461   public void createSplitter(final int orientation, @Nullable final EditorWindow window) {
462     // window was available from action event, for example when invoked from the tab menu of an editor that is not the 'current'
463     if (window != null) {
464       window.split(orientation, true, null, false);
465     }
466     // otherwise we'll split the current window, if any
467     else {
468       final EditorWindow currentWindow = getSplitters().getCurrentWindow();
469       if (currentWindow != null) {
470         currentWindow.split(orientation, true, null, false);
471       }
472     }
473   }
474
475   @Override
476   public void changeSplitterOrientation() {
477     final EditorWindow currentWindow = getSplitters().getCurrentWindow();
478     if (currentWindow != null) {
479       currentWindow.changeOrientation();
480     }
481   }
482
483
484   @Override
485   public void flipTabs() {
486     /*
487     if (myTabs == null) {
488       myTabs = new EditorTabs (this, UISettings.getInstance().EDITOR_TAB_PLACEMENT);
489       remove (mySplitters);
490       add (myTabs, BorderLayout.CENTER);
491       initTabs ();
492     } else {
493       remove (myTabs);
494       add (mySplitters, BorderLayout.CENTER);
495       myTabs.dispose ();
496       myTabs = null;
497     }
498     */
499     myPanels.revalidate();
500   }
501
502   @Override
503   public boolean tabsMode() {
504     return false;
505   }
506
507   private void setTabsMode(final boolean mode) {
508     if (tabsMode() != mode) {
509       flipTabs();
510     }
511     //LOG.assertTrue (tabsMode () == mode);
512   }
513
514
515   @Override
516   public boolean isInSplitter() {
517     final EditorWindow currentWindow = getSplitters().getCurrentWindow();
518     return currentWindow != null && currentWindow.inSplitter();
519   }
520
521   @Override
522   public boolean hasOpenedFile() {
523     final EditorWindow currentWindow = getSplitters().getCurrentWindow();
524     return currentWindow != null && currentWindow.getSelectedEditor() != null;
525   }
526
527   @Override
528   public VirtualFile getCurrentFile() {
529     return getActiveSplitters(true).getResult().getCurrentFile();
530   }
531
532   @Override
533   @NotNull
534   public AsyncResult<EditorWindow> getActiveWindow() {
535     return _getActiveWindow(false);
536   }
537
538   @NotNull
539   private AsyncResult<EditorWindow> _getActiveWindow(boolean now) {
540     return getActiveSplitters(now).subResult(new Function<EditorsSplitters, EditorWindow>() {
541       @Override
542       public EditorWindow fun(EditorsSplitters splitters) {
543         return splitters.getCurrentWindow();
544       }
545     });
546   }
547
548   @Override
549   public EditorWindow getCurrentWindow() {
550     return _getActiveWindow(true).getResult();
551   }
552
553   @Override
554   public void setCurrentWindow(final EditorWindow window) {
555     getActiveSplitters(true).getResult().setCurrentWindow(window, true);
556   }
557
558   public void closeFile(@NotNull final VirtualFile file, @NotNull final EditorWindow window, final boolean transferFocus) {
559     assertDispatchThread();
560
561     CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
562       @Override
563       public void run() {
564         if (window.isFileOpen(file)) {
565           window.closeFile(file, true, transferFocus);
566         }
567       }
568     }, IdeBundle.message("command.close.active.editor"), null);
569     removeSelectionRecord(file, window);
570   }
571
572   @Override
573   public void closeFile(@NotNull final VirtualFile file, @NotNull final EditorWindow window) {
574     closeFile(file, window, true);
575   }
576
577   //============================= EditorManager methods ================================
578
579   @Override
580   public void closeFile(@NotNull final VirtualFile file) {
581     closeFile(file, true, false);
582   }
583
584   public void closeFile(@NotNull final VirtualFile file, final boolean moveFocus, final boolean closeAllCopies) {
585     assertDispatchThread();
586
587     CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
588       @Override
589       public void run() {
590         closeFileImpl(file, moveFocus, closeAllCopies);
591       }
592     }, "", null);
593   }
594
595   private void closeFileImpl(@NotNull final VirtualFile file, final boolean moveFocus, boolean closeAllCopies) {
596     assertDispatchThread();
597     runChange(new FileEditorManagerChange() {
598       @Override
599       public void run(EditorsSplitters splitters) {
600         splitters.closeFile(file, moveFocus);
601       }
602     }, closeAllCopies ? null : getActiveSplitters(true).getResult());
603   }
604
605   //-------------------------------------- Open File ----------------------------------------
606
607   @Override
608   @NotNull
609   public Pair<FileEditor[], FileEditorProvider[]> openFileWithProviders(@NotNull final VirtualFile file,
610                                                                         final boolean focusEditor,
611                                                                         final boolean searchForSplitter) {
612     if (!file.isValid()) {
613       throw new IllegalArgumentException("file is not valid: " + file);
614     }
615     assertDispatchThread();
616
617     if (isOpenInNewWindow(EventQueue.getCurrentEvent())) {
618       return openFileInNewWindow(file);
619     }
620
621
622     EditorWindow wndToOpenIn = null;
623     if (searchForSplitter) {
624       Set<EditorsSplitters> all = getAllSplitters();
625       EditorsSplitters active = getActiveSplitters(true).getResult();
626       if (active.getCurrentWindow() != null && active.getCurrentWindow().isFileOpen(file)) {
627         wndToOpenIn = active.getCurrentWindow();
628       } else {
629         for (EditorsSplitters splitters : all) {
630           final EditorWindow window = splitters.getCurrentWindow();
631           if (window == null) continue;
632
633           if (window.isFileOpen(file)) {
634             wndToOpenIn = window;
635             break;
636           }
637         }
638       }
639     }
640     else {
641       wndToOpenIn = getSplitters().getCurrentWindow();
642     }
643
644     if (wndToOpenIn == null || !wndToOpenIn.isFileOpen(file)) {
645       EditorWindow previewWindow = getPreviewWindow(file, focusEditor, searchForSplitter);
646       if (previewWindow != null) {
647         wndToOpenIn = previewWindow;
648       }
649     }
650
651     EditorsSplitters splitters = getSplitters();
652
653     if (wndToOpenIn == null) {
654       wndToOpenIn = splitters.getOrCreateCurrentWindow(file);
655     }
656
657     openAssociatedFile(file, wndToOpenIn, splitters);
658     return openFileImpl2(wndToOpenIn, file, focusEditor);
659   }
660
661   @Nullable
662   private EditorWindow getPreviewWindow(@NotNull VirtualFile virtualFile, final boolean focusEditor, final boolean searchForSplitter) {
663     EditorWindow wndToOpenIn = null;
664     if (Registry.is("editor.use.preview") && !myPreviewBlocker.get()) {
665       wndToOpenIn = myPreviewPanel.getWindow();
666       if (virtualFile.equals(myPreviewPanel.getCurrentFile())) return wndToOpenIn;
667       final VirtualFile modifiedFile = myPreviewPanel.closeCurrentFile();
668       ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW).activate(null, false);
669       if (modifiedFile != null) {
670         CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
671           @Override
672           public void run() {
673             myPreviewBlocker.set(true);
674             try {
675               openFileWithProviders(modifiedFile, focusEditor, searchForSplitter);
676             } finally {
677               myPreviewBlocker.set(false);
678             }
679           }
680         }, "", null);
681       }
682     }
683     return wndToOpenIn;
684   }
685
686   public Pair<FileEditor[], FileEditorProvider[]> openFileInNewWindow(@NotNull VirtualFile file) {
687     return ((DockManagerImpl)DockManager.getInstance(getProject())).createNewDockContainerFor(file, this);
688   }
689
690   private static boolean isOpenInNewWindow(AWTEvent event) {
691     // Shift was used while clicking
692     if (event instanceof MouseEvent && ((MouseEvent)event).isShiftDown()) {
693       return true;
694     }
695
696     // Shift + Enter
697     if (event instanceof KeyEvent
698         && ((KeyEvent)event).getKeyCode() == KeyEvent.VK_ENTER
699         && ((KeyEvent)event).isShiftDown()) {
700       return true;
701     }
702
703     return false;
704   }
705
706   private void openAssociatedFile(VirtualFile file, EditorWindow wndToOpenIn, EditorsSplitters splitters) {
707     EditorWindow[] windows = splitters.getWindows();
708
709     if (file != null && windows.length == 2) {
710       for (FileEditorAssociateFinder finder : Extensions.getExtensions(FileEditorAssociateFinder.EP_NAME)) {
711         VirtualFile associatedFile = finder.getAssociatedFileToOpen(myProject, file);
712
713         if (associatedFile != null) {
714           EditorWindow currentWindow = splitters.getCurrentWindow();
715           int idx = windows[0] == wndToOpenIn ? 1 : 0;
716           openFileImpl2(windows[idx], associatedFile, false);
717
718           if (currentWindow != null) {
719             splitters.setCurrentWindow(currentWindow, false);
720           }
721
722           break;
723         }
724       }
725     }
726   }
727
728   @NotNull
729   @Override
730   public Pair<FileEditor[], FileEditorProvider[]> openFileWithProviders(@NotNull VirtualFile file,
731                                                                         boolean focusEditor,
732                                                                         @NotNull EditorWindow window) {
733     if (!file.isValid()) {
734       throw new IllegalArgumentException("file is not valid: " + file);
735     }
736     assertDispatchThread();
737
738     return openFileImpl2(window, file, focusEditor);
739   }
740
741   @NotNull
742   public Pair<FileEditor[], FileEditorProvider[]> openFileImpl2(@NotNull final EditorWindow window,
743                                                                 @NotNull final VirtualFile file,
744                                                                 final boolean focusEditor) {
745     final Ref<Pair<FileEditor[], FileEditorProvider[]>> result = new Ref<Pair<FileEditor[], FileEditorProvider[]>>();
746     CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
747       @Override
748       public void run() {
749         result.set(openFileImpl3(window, file, focusEditor, null, true));
750       }
751     }, "", null);
752     return result.get();
753   }
754
755   /**
756    * @param file    to be opened. Unlike openFile method, file can be
757    *                invalid. For example, all file were invalidate and they are being
758    *                removed one by one. If we have removed one invalid file, then another
759    *                invalid file become selected. That's why we do not require that
760    *                passed file is valid.
761    * @param entry   map between FileEditorProvider and FileEditorState. If this parameter
762    */
763   @NotNull
764   Pair<FileEditor[], FileEditorProvider[]> openFileImpl3(@NotNull final EditorWindow window,
765                                                          @NotNull final VirtualFile file,
766                                                          final boolean focusEditor,
767                                                          @Nullable final HistoryEntry entry,
768                                                          boolean current) {
769     return openFileImpl4(window, file, entry, current, focusEditor, null, -1);
770   }
771
772   /**
773    * This method can be invoked from background thread. Of course, UI for returned editors should be accessed from EDT in any case.
774    */
775   @NotNull
776   Pair<FileEditor[], FileEditorProvider[]> openFileImpl4(@NotNull final EditorWindow window,
777                                                          @NotNull final VirtualFile file,
778                                                          @Nullable final HistoryEntry entry,
779                                                          final boolean current, final boolean focusEditor, final Boolean pin,
780                                                          final int index) {
781     ApplicationManager.getApplication().assertReadAccessAllowed();
782
783     final Ref<EditorWithProviderComposite> compositeRef = new Ref<EditorWithProviderComposite>();
784
785     UIUtil.invokeAndWaitIfNeeded(new Runnable() {
786       @Override
787       public void run() {
788         compositeRef.set(window.findFileComposite(file));
789       }
790     });
791
792     final FileEditorProvider[] newProviders;
793     final AsyncFileEditorProvider.Builder[] builders;
794     if (compositeRef.isNull()) {
795       // File is not opened yet. In this case we have to create editors
796       // and select the created EditorComposite.
797       newProviders = getAvailableProviders(file);
798       if (newProviders.length == 0) {
799         return Pair.create(EMPTY_EDITOR_ARRAY, EMPTY_PROVIDER_ARRAY);
800       }
801
802       builders = new AsyncFileEditorProvider.Builder[newProviders.length];
803       for (int i = 0; i < newProviders.length; i++) {
804         try {
805           final FileEditorProvider provider = newProviders[i];
806           LOG.assertTrue(provider != null, "Provider for file "+file+" is null. All providers: "+Arrays.asList(newProviders));
807           LOG.assertTrue(provider.accept(myProject, file), "Provider " + provider + " doesn't accept file " + file);
808           if ((provider instanceof AsyncFileEditorProvider)) {
809             builders[i] = ((AsyncFileEditorProvider)provider).createEditorAsync(myProject, file);
810           }
811         }
812         catch (Exception e) {
813           LOG.error(e);
814         }
815         catch (AssertionError e) {
816           LOG.error(e);
817         }
818       }
819     }
820     else {
821       newProviders = null;
822       builders = null;
823     }
824     UIUtil.invokeAndWaitIfNeeded(new Runnable() {
825       @Override
826       public void run() {
827         if (myProject.isDisposed() || !file.isValid()) {
828           return;
829         }
830         compositeRef.set(window.findFileComposite(file));
831         boolean newEditor = compositeRef.isNull();
832         if (newEditor) {
833           clearWindowIfNeeded(window);
834
835           getProject().getMessageBus().syncPublisher(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER).beforeFileOpened(FileEditorManagerImpl.this, file);
836
837           FileEditor[] newEditors = new FileEditor[newProviders.length];
838           for (int i = 0; i < newProviders.length; i++) {
839             try {
840               final FileEditorProvider provider = newProviders[i];
841               final FileEditor editor = builders[i] == null ? provider.createEditor(myProject, file) : builders[i].build();
842               LOG.assertTrue(editor.isValid());
843               newEditors[i] = editor;
844               // Register PropertyChangeListener into editor
845               editor.addPropertyChangeListener(myEditorPropertyChangeListener);
846               editor.putUserData(DUMB_AWARE, DumbService.isDumbAware(provider));
847             }
848             catch (Exception e) {
849               LOG.error(e);
850             }
851             catch (AssertionError e) {
852               LOG.error(e);
853             }
854           }
855
856           // Now we have to create EditorComposite and insert it into the TabbedEditorComponent.
857           // After that we have to select opened editor.
858           EditorWithProviderComposite composite = new EditorWithProviderComposite(file, newEditors, newProviders, FileEditorManagerImpl.this);
859
860           if (index >= 0) {
861             composite.getFile().putUserData(EditorWindow.INITIAL_INDEX_KEY, index);
862           }
863
864           compositeRef.set(composite);
865         }
866
867         final EditorWithProviderComposite composite = compositeRef.get();
868         FileEditor[] editors = composite.getEditors();
869         FileEditorProvider[] providers = composite.getProviders();
870
871         window.setEditor(composite, current, focusEditor);
872
873         for (int i = 0; i < editors.length; i++) {
874           restoreEditorState(file, providers[i], editors[i], entry, newEditor);
875         }
876
877         // Restore selected editor
878         final FileEditorProvider selectedProvider;
879         if (entry == null) {
880           selectedProvider = ((FileEditorProviderManagerImpl)FileEditorProviderManager.getInstance())
881             .getSelectedFileEditorProvider(myEditorHistoryManager, file, providers);
882         }
883         else {
884           selectedProvider = entry.mySelectedProvider;
885         }
886         if (selectedProvider != null) {
887           for (int i = editors.length - 1; i >= 0; i--) {
888             final FileEditorProvider provider = providers[i];
889             if (provider.equals(selectedProvider)) {
890               composite.setSelectedEditor(i);
891               break;
892             }
893           }
894         }
895
896         // Notify editors about selection changes
897         window.getOwner().setCurrentWindow(window, focusEditor);
898         window.getOwner().afterFileOpen(file);
899         addSelectionRecord(file, window);
900
901         composite.getSelectedEditor().selectNotify();
902
903         // Transfer focus into editor
904         if (!ApplicationManagerEx.getApplicationEx().isUnitTestMode()) {
905           if (focusEditor) {
906             //myFirstIsActive = myTabbedContainer1.equals(tabbedContainer);
907             window.setAsCurrentWindow(true);
908             ToolWindowManager.getInstance(myProject).activateEditorComponent();
909             IdeFocusManager.getInstance(myProject).toFront(window.getOwner());
910           }
911         }
912
913         if (newEditor) {
914           if (window.isShowing()) {
915             window.setPaintBlocked(true);
916           }
917           notifyPublisher(new Runnable() {
918             @Override
919             public void run() {
920               window.setPaintBlocked(false);
921               if (isFileOpen(file)) {
922                 getProject().getMessageBus().syncPublisher(FileEditorManagerListener.FILE_EDITOR_MANAGER)
923                   .fileOpened(FileEditorManagerImpl.this, file);
924               }
925             }
926           });
927         }
928
929         //[jeka] this is a hack to support back-forward navigation
930         // previously here was incorrect call to fireSelectionChanged() with a side-effect
931         ((IdeDocumentHistoryImpl)IdeDocumentHistory.getInstance(myProject)).onSelectionChanged();
932
933         // Update frame and tab title
934         updateFileName(file);
935
936         // Make back/forward work
937         IdeDocumentHistory.getInstance(myProject).includeCurrentCommandAsNavigation();
938
939         if (pin != null) {
940           window.setFilePinned(file, pin);
941         }
942       }
943     });
944     return Pair.create(compositeRef.get().getEditors(), compositeRef.get().getProviders());
945   }
946
947   private void clearWindowIfNeeded(EditorWindow window) {
948     if (UISettings.getInstance().EDITOR_TAB_PLACEMENT == UISettings.TABS_NONE || UISettings.getInstance().PRESENTATION_MODE) {
949       for (EditorWithProviderComposite composite : window.getEditors()) {
950         Disposer.dispose(composite);
951       }
952     }
953   }
954
955   private void restoreEditorState(VirtualFile file,
956                                   FileEditorProvider provider,
957                                   final FileEditor editor,
958                                   HistoryEntry entry,
959                                   boolean newEditor) {
960     FileEditorState state = null;
961     if (entry != null) {
962       state = entry.getState(provider);
963     }
964     if (state == null && newEditor) {
965       // We have to try to get state from the history only in case
966       // if editor is not opened. Otherwise history entry might have a state
967       // out of sync with the current editor state.
968       state = myEditorHistoryManager.getState(file, provider);
969     }
970     if (state != null) {
971       if (!isDumbAware(editor)) {
972         final FileEditorState finalState = state;
973         DumbService.getInstance(getProject()).runWhenSmart(new Runnable() {
974           @Override
975           public void run() {
976             editor.setState(finalState);
977           }
978         });
979       }
980       else {
981         editor.setState(state);
982       }
983     }
984   }
985
986   private FileEditorProvider[] getAvailableProviders(VirtualFile file) {
987     final FileEditorProviderManager editorProviderManager = FileEditorProviderManager.getInstance();
988     FileEditorProvider[] providers = editorProviderManager.getProviders(myProject, file);
989     if (DumbService.getInstance(myProject).isDumb()) {
990       final List<FileEditorProvider> dumbAware = ContainerUtil.findAll(providers, new Condition<FileEditorProvider>() {
991         @Override
992         public boolean value(FileEditorProvider fileEditorProvider) {
993           return DumbService.isDumbAware(fileEditorProvider);
994         }
995       });
996       providers = dumbAware.toArray(new FileEditorProvider[dumbAware.size()]);
997     }
998     return providers;
999   }
1000
1001   @NotNull
1002   @Override
1003   public ActionCallback notifyPublisher(@NotNull final Runnable runnable) {
1004     final IdeFocusManager focusManager = IdeFocusManager.getInstance(myProject);
1005     final ActionCallback done = new ActionCallback();
1006     return myBusyObject.execute(new ActiveRunnable() {
1007       @NotNull
1008       @Override
1009       public ActionCallback run() {
1010         focusManager.doWhenFocusSettlesDown(new ExpirableRunnable.ForProject(myProject) {
1011           @Override
1012           public void run() {
1013             runnable.run();
1014             done.setDone();
1015           }
1016         });
1017         return done;
1018       }
1019     });
1020   }
1021
1022   @Override
1023   public void setSelectedEditor(@NotNull VirtualFile file, String fileEditorProviderId) {
1024     EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file);
1025     if (composite == null) {
1026       final List<EditorWithProviderComposite> composites = getEditorComposites(file);
1027
1028       if (composites.isEmpty()) return;
1029       composite = composites.get(0);
1030     }
1031
1032     final FileEditorProvider[] editorProviders = composite.getProviders();
1033     final FileEditorProvider selectedProvider = composite.getSelectedEditorWithProvider().getSecond();
1034
1035     for (int i = 0; i < editorProviders.length; i++) {
1036       if (editorProviders[i].getEditorTypeId().equals(fileEditorProviderId) && !selectedProvider.equals(editorProviders[i])) {
1037         composite.setSelectedEditor(i);
1038         composite.getSelectedEditor().selectNotify();
1039       }
1040     }
1041   }
1042
1043
1044   @Nullable
1045   EditorWithProviderComposite newEditorComposite(final VirtualFile file) {
1046     if (file == null) {
1047       return null;
1048     }
1049
1050     final FileEditorProviderManager editorProviderManager = FileEditorProviderManager.getInstance();
1051     final FileEditorProvider[] providers = editorProviderManager.getProviders(myProject, file);
1052     final FileEditor[] editors = new FileEditor[providers.length];
1053     for (int i = 0; i < providers.length; i++) {
1054       final FileEditorProvider provider = providers[i];
1055       LOG.assertTrue(provider != null);
1056       LOG.assertTrue(provider.accept(myProject, file));
1057       final FileEditor editor = provider.createEditor(myProject, file);
1058       editors[i] = editor;
1059       LOG.assertTrue(editor.isValid());
1060       editor.addPropertyChangeListener(myEditorPropertyChangeListener);
1061     }
1062
1063     final EditorWithProviderComposite newComposite = new EditorWithProviderComposite(file, editors, providers, this);
1064     final EditorHistoryManager editorHistoryManager = EditorHistoryManager.getInstance(myProject);
1065     for (int i = 0; i < editors.length; i++) {
1066       final FileEditor editor = editors[i];
1067       if (editor instanceof TextEditor) {
1068         // hack!!!
1069         // This code prevents "jumping" on next repaint.
1070         //((EditorEx)((TextEditor)editor).getEditor()).stopOptimizedScrolling();
1071       }
1072
1073       final FileEditorProvider provider = providers[i];
1074
1075 // Restore myEditor state
1076       FileEditorState state = editorHistoryManager.getState(file, provider);
1077       if (state != null) {
1078         editor.setState(state);
1079       }
1080     }
1081     return newComposite;
1082   }
1083
1084   @Override
1085   @NotNull
1086   public List<FileEditor> openEditor(@NotNull final OpenFileDescriptor descriptor, final boolean focusEditor) {
1087     assertDispatchThread();
1088     if (descriptor.getFile() instanceof VirtualFileWindow) {
1089       VirtualFileWindow delegate = (VirtualFileWindow)descriptor.getFile();
1090       int hostOffset = delegate.getDocumentWindow().injectedToHost(descriptor.getOffset());
1091       OpenFileDescriptor realDescriptor = new OpenFileDescriptor(descriptor.getProject(), delegate.getDelegate(), hostOffset);
1092       realDescriptor.setUseCurrentWindow(descriptor.isUseCurrentWindow());
1093       return openEditor(realDescriptor, focusEditor);
1094     }
1095
1096     final List<FileEditor> result = new SmartList<FileEditor>();
1097     CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
1098       @Override
1099       public void run() {
1100         VirtualFile file = descriptor.getFile();
1101         final FileEditor[] editors = openFile(file, focusEditor, !descriptor.isUseCurrentWindow());
1102         ContainerUtil.addAll(result, editors);
1103
1104         boolean navigated = false;
1105         for (final FileEditor editor : editors) {
1106           if (editor instanceof NavigatableFileEditor &&
1107               getSelectedEditor(descriptor.getFile()) == editor) { // try to navigate opened editor
1108             navigated = navigateAndSelectEditor((NavigatableFileEditor)editor, descriptor);
1109             if (navigated) break;
1110           }
1111         }
1112
1113         if (!navigated) {
1114           for (final FileEditor editor : editors) {
1115             if (editor instanceof NavigatableFileEditor && getSelectedEditor(descriptor.getFile()) != editor) { // try other editors
1116               if (navigateAndSelectEditor((NavigatableFileEditor)editor, descriptor)) {
1117                 break;
1118               }
1119             }
1120           }
1121         }
1122       }
1123     }, "", null);
1124
1125     return result;
1126   }
1127
1128   private boolean navigateAndSelectEditor(final NavigatableFileEditor editor, final OpenFileDescriptor descriptor) {
1129     if (editor.canNavigateTo(descriptor)) {
1130       setSelectedEditor(editor);
1131       editor.navigateTo(descriptor);
1132       return true;
1133     }
1134
1135     return false;
1136   }
1137
1138   private void setSelectedEditor(final FileEditor editor) {
1139     final EditorWithProviderComposite composite = getEditorComposite(editor);
1140     if (composite == null) return;
1141
1142     final FileEditor[] editors = composite.getEditors();
1143     for (int i = 0; i < editors.length; i++) {
1144       final FileEditor each = editors[i];
1145       if (editor == each) {
1146         composite.setSelectedEditor(i);
1147         composite.getSelectedEditor().selectNotify();
1148         break;
1149       }
1150     }
1151   }
1152
1153   @Override
1154   @NotNull
1155   public Project getProject() {
1156     return myProject;
1157   }
1158
1159   @Override
1160   @Nullable
1161   public Editor openTextEditor(@NotNull final OpenFileDescriptor descriptor, final boolean focusEditor) {
1162     final Collection<FileEditor> fileEditors = openEditor(descriptor, focusEditor);
1163     for (FileEditor fileEditor : fileEditors) {
1164       if (fileEditor instanceof TextEditor) {
1165         setSelectedEditor(descriptor.getFile(), TextEditorProvider.getInstance().getEditorTypeId());
1166         Editor editor = ((TextEditor)fileEditor).getEditor();
1167         return getOpenedEditor(editor, focusEditor);
1168       }
1169     }
1170
1171     return null;
1172   }
1173
1174   protected Editor getOpenedEditor(final Editor editor, final boolean focusEditor) {
1175     return editor;
1176   }
1177
1178   @Override
1179   public Editor getSelectedTextEditor() {
1180     assertReadAccess();
1181
1182     final EditorWindow currentWindow = getSplitters().getCurrentWindow();
1183     if (currentWindow != null) {
1184       final EditorWithProviderComposite selectedEditor = currentWindow.getSelectedEditor();
1185       if (selectedEditor != null && selectedEditor.getSelectedEditor() instanceof TextEditor) {
1186         return ((TextEditor)selectedEditor.getSelectedEditor()).getEditor();
1187       }
1188     }
1189
1190     return null;
1191   }
1192
1193
1194   @Override
1195   public boolean isFileOpen(@NotNull final VirtualFile file) {
1196     return !getEditorComposites(file).isEmpty();
1197   }
1198
1199   @Override
1200   @NotNull
1201   public VirtualFile[] getOpenFiles() {
1202     HashSet<VirtualFile> openFiles = new HashSet<VirtualFile>();
1203     for (EditorsSplitters each : getAllSplitters()) {
1204       openFiles.addAll(Arrays.asList(each.getOpenFiles()));
1205     }
1206
1207     return VfsUtilCore.toVirtualFileArray(openFiles);
1208   }
1209
1210   @Override
1211   @NotNull
1212   public VirtualFile[] getSelectedFiles() {
1213     HashSet<VirtualFile> selectedFiles = new HashSet<VirtualFile>();
1214     for (EditorsSplitters each : getAllSplitters()) {
1215       selectedFiles.addAll(Arrays.asList(each.getSelectedFiles()));
1216     }
1217
1218     return VfsUtilCore.toVirtualFileArray(selectedFiles);
1219   }
1220
1221   @Override
1222   @NotNull
1223   public FileEditor[] getSelectedEditors() {
1224     HashSet<FileEditor> selectedEditors = new HashSet<FileEditor>();
1225     for (EditorsSplitters each : getAllSplitters()) {
1226       selectedEditors.addAll(Arrays.asList(each.getSelectedEditors()));
1227     }
1228
1229     return selectedEditors.toArray(new FileEditor[selectedEditors.size()]);
1230   }
1231
1232   @Override
1233   @NotNull
1234   public EditorsSplitters getSplitters() {
1235     EditorsSplitters active = getActiveSplitters(true).getResult();
1236     return active == null ? getMainSplitters() : active;
1237   }
1238
1239   @Override
1240   @Nullable
1241   public FileEditor getSelectedEditor(@NotNull final VirtualFile file) {
1242     final Pair<FileEditor, FileEditorProvider> selectedEditorWithProvider = getSelectedEditorWithProvider(file);
1243     return selectedEditorWithProvider == null ? null : selectedEditorWithProvider.getFirst();
1244   }
1245
1246
1247   @Override
1248   @Nullable
1249   public Pair<FileEditor, FileEditorProvider> getSelectedEditorWithProvider(@NotNull VirtualFile file) {
1250     if (file instanceof VirtualFileWindow) file = ((VirtualFileWindow)file).getDelegate();
1251     final EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file);
1252     if (composite != null) {
1253       return composite.getSelectedEditorWithProvider();
1254     }
1255
1256     final List<EditorWithProviderComposite> composites = getEditorComposites(file);
1257     return composites.isEmpty() ? null : composites.get(0).getSelectedEditorWithProvider();
1258   }
1259
1260   @Override
1261   @NotNull
1262   public Pair<FileEditor[], FileEditorProvider[]> getEditorsWithProviders(@NotNull final VirtualFile file) {
1263     assertReadAccess();
1264
1265     final EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file);
1266     if (composite != null) {
1267       return Pair.create(composite.getEditors(), composite.getProviders());
1268     }
1269
1270     final List<EditorWithProviderComposite> composites = getEditorComposites(file);
1271     if (!composites.isEmpty()) {
1272       return Pair.create(composites.get(0).getEditors(), composites.get(0).getProviders());
1273     }
1274     else {
1275       return Pair.create(EMPTY_EDITOR_ARRAY, EMPTY_PROVIDER_ARRAY);
1276     }
1277   }
1278
1279   @Override
1280   @NotNull
1281   public FileEditor[] getEditors(@NotNull VirtualFile file) {
1282     assertReadAccess();
1283     if (file instanceof VirtualFileWindow) file = ((VirtualFileWindow)file).getDelegate();
1284
1285     final EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file);
1286     if (composite != null) {
1287       return composite.getEditors();
1288     }
1289
1290     final List<EditorWithProviderComposite> composites = getEditorComposites(file);
1291     if (!composites.isEmpty()) {
1292       return composites.get(0).getEditors();
1293     }
1294     else {
1295       return EMPTY_EDITOR_ARRAY;
1296     }
1297   }
1298
1299   @NotNull
1300   @Override
1301   public FileEditor[] getAllEditors(@NotNull VirtualFile file) {
1302     List<EditorWithProviderComposite> editorComposites = getEditorComposites(file);
1303     if (editorComposites.isEmpty()) return EMPTY_EDITOR_ARRAY;
1304     List<FileEditor> editors = new ArrayList<FileEditor>();
1305     for (EditorWithProviderComposite composite : editorComposites) {
1306       ContainerUtil.addAll(editors, composite.getEditors());
1307     }
1308     return editors.toArray(new FileEditor[editors.size()]);
1309   }
1310
1311   @Nullable
1312   private EditorWithProviderComposite getCurrentEditorWithProviderComposite(@NotNull final VirtualFile virtualFile) {
1313     final EditorWindow editorWindow = getSplitters().getCurrentWindow();
1314     if (editorWindow != null) {
1315       return editorWindow.findFileComposite(virtualFile);
1316     }
1317     return null;
1318   }
1319
1320   @NotNull
1321   public List<EditorWithProviderComposite> getEditorComposites(final VirtualFile file) {
1322     ArrayList<EditorWithProviderComposite> result = new ArrayList<EditorWithProviderComposite>();
1323     Set<EditorsSplitters> all = getAllSplitters();
1324     for (EditorsSplitters each : all) {
1325       result.addAll(each.findEditorComposites(file));
1326     }
1327     return result;
1328   }
1329
1330   @Override
1331   @NotNull
1332   public FileEditor[] getAllEditors() {
1333     assertReadAccess();
1334     final ArrayList<FileEditor> result = new ArrayList<FileEditor>();
1335     final Set<EditorsSplitters> allSplitters = getAllSplitters();
1336     for (EditorsSplitters splitter : allSplitters) {
1337       final EditorWithProviderComposite[] editorsComposites = splitter.getEditorsComposites();
1338       for (EditorWithProviderComposite editorsComposite : editorsComposites) {
1339         final FileEditor[] editors = editorsComposite.getEditors();
1340         ContainerUtil.addAll(result, editors);
1341       }
1342     }
1343     return result.toArray(new FileEditor[result.size()]);
1344   }
1345
1346   @Override
1347   public void showEditorAnnotation(@NotNull FileEditor editor, @NotNull JComponent annotationComponent) {
1348     addTopComponent(editor, annotationComponent);
1349   }
1350
1351   @Override
1352   public void removeEditorAnnotation(@NotNull FileEditor editor, @NotNull JComponent annotationComponent) {
1353     removeTopComponent(editor, annotationComponent);
1354   }
1355   
1356   @NotNull
1357   public List<JComponent> getTopComponents(@NotNull FileEditor editor) {
1358     final EditorComposite composite = getEditorComposite(editor);
1359     return composite != null ? composite.getTopComponents(editor) : Collections.<JComponent>emptyList();
1360   }
1361
1362   @Override
1363   public void addTopComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) {
1364     final EditorComposite composite = getEditorComposite(editor);
1365     if (composite != null) {
1366       composite.addTopComponent(editor, component);
1367     }
1368   }
1369
1370   @Override
1371   public void removeTopComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) {
1372     final EditorComposite composite = getEditorComposite(editor);
1373     if (composite != null) {
1374       composite.removeTopComponent(editor, component);
1375     }
1376   }
1377
1378   @NotNull
1379   public List<JComponent> getBottomComponents(@NotNull FileEditor editor) {
1380     final EditorComposite composite = getEditorComposite(editor);
1381     return composite != null ? composite.getBottomComponents(editor) : Collections.<JComponent>emptyList();
1382   }
1383
1384   @Override
1385   public void addBottomComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) {
1386     final EditorComposite composite = getEditorComposite(editor);
1387     if (composite != null) {
1388       composite.addBottomComponent(editor, component);
1389     }
1390   }
1391
1392   @Override
1393   public void removeBottomComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) {
1394     final EditorComposite composite = getEditorComposite(editor);
1395     if (composite != null) {
1396       composite.removeBottomComponent(editor, component);
1397     }
1398   }
1399
1400   private final MessageListenerList<FileEditorManagerListener> myListenerList;
1401
1402   @Override
1403   public void addFileEditorManagerListener(@NotNull final FileEditorManagerListener listener) {
1404     myListenerList.add(listener);
1405   }
1406
1407   @Override
1408   public void addFileEditorManagerListener(@NotNull final FileEditorManagerListener listener, @NotNull final Disposable parentDisposable) {
1409     myListenerList.add(listener, parentDisposable);
1410   }
1411
1412   @Override
1413   public void removeFileEditorManagerListener(@NotNull final FileEditorManagerListener listener) {
1414     myListenerList.remove(listener);
1415   }
1416
1417 // ProjectComponent methods
1418
1419   @Override
1420   public void projectOpened() {
1421     //myFocusWatcher.install(myWindows.getComponent ());
1422     getMainSplitters().startListeningFocus();
1423
1424     MessageBusConnection connection = myProject.getMessageBus().connect(myProject);
1425
1426     final FileStatusManager fileStatusManager = FileStatusManager.getInstance(myProject);
1427     if (fileStatusManager != null) {
1428       /**
1429        * Updates tabs colors
1430        */
1431       final MyFileStatusListener myFileStatusListener = new MyFileStatusListener();
1432       fileStatusManager.addFileStatusListener(myFileStatusListener, myProject);
1433     }
1434     connection.subscribe(FileTypeManager.TOPIC, new MyFileTypeListener());
1435     connection.subscribe(ProjectTopics.PROJECT_ROOTS, new MyRootsListener());
1436
1437     /**
1438      * Updates tabs names
1439      */
1440     final MyVirtualFileListener myVirtualFileListener = new MyVirtualFileListener();
1441     VirtualFileManager.getInstance().addVirtualFileListener(myVirtualFileListener, myProject);
1442     /**
1443      * Extends/cuts number of opened tabs. Also updates location of tabs.
1444      */
1445     final MyUISettingsListener myUISettingsListener = new MyUISettingsListener();
1446     UISettings.getInstance().addUISettingsListener(myUISettingsListener, myProject);
1447
1448     StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() {
1449       @Override
1450       public void run() {
1451         if (myProject.isDisposed()) return;
1452         setTabsMode(UISettings.getInstance().EDITOR_TAB_PLACEMENT != UISettings.TABS_NONE);
1453
1454         ToolWindowManager.getInstance(myProject).invokeLater(new Runnable() {
1455           @Override
1456           public void run() {
1457             CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
1458               @Override
1459               public void run() {
1460
1461                 LaterInvocator.invokeLater(new Runnable() {
1462                   @Override
1463                   public void run() {
1464                     long currentTime = System.nanoTime();
1465                     Long startTime = myProject.getUserData(ProjectImpl.CREATION_TIME);
1466                     if (startTime != null) {
1467                       LOG.info("Project opening took " + (currentTime - startTime.longValue()) / 1000000 + " ms");
1468                       PluginManagerCore.dumpPluginClassStatistics();
1469                     }
1470                   }
1471                 }, myProject.getDisposed());
1472 // group 1
1473               }
1474             }, "", null);
1475           }
1476         });
1477       }
1478     });
1479   }
1480
1481   @Override
1482   public void projectClosed() {
1483     //myFocusWatcher.deinstall(myWindows.getComponent ());
1484     getMainSplitters().dispose();
1485
1486 // Dispose created editors. We do not use use closeEditor method because
1487 // it fires event and changes history.
1488     closeAllFiles();
1489   }
1490
1491 // BaseCompomemnt methods
1492
1493   @Override
1494   @NotNull
1495   public String getComponentName() {
1496     return FILE_EDITOR_MANAGER;
1497   }
1498
1499   @Override
1500   public void initComponent() {
1501
1502   }
1503
1504   @Override
1505   public void disposeComponent() { /* really do nothing */ }
1506
1507 //JDOMExternalizable methods
1508
1509   @Override
1510   public void writeExternal(final Element element) {
1511     getMainSplitters().writeExternal(element);
1512   }
1513
1514   @Override
1515   public void readExternal(final Element element) {
1516     getMainSplitters().readExternal(element);
1517   }
1518
1519   @Nullable
1520   private EditorWithProviderComposite getEditorComposite(@NotNull final FileEditor editor) {
1521     for (EditorsSplitters splitters : getAllSplitters()) {
1522       final EditorWithProviderComposite[] editorsComposites = splitters.getEditorsComposites();
1523       for (int i = editorsComposites.length - 1; i >= 0; i--) {
1524         final EditorWithProviderComposite composite = editorsComposites[i];
1525         final FileEditor[] editors = composite.getEditors();
1526         for (int j = editors.length - 1; j >= 0; j--) {
1527           final FileEditor _editor = editors[j];
1528           LOG.assertTrue(_editor != null);
1529           if (editor.equals(_editor)) {
1530             return composite;
1531           }
1532         }
1533       }
1534     }
1535     return null;
1536   }
1537
1538 //======================= Misc =====================
1539
1540   private static void assertDispatchThread() {
1541     ApplicationManager.getApplication().assertIsDispatchThread();
1542   }
1543
1544   private static void assertReadAccess() {
1545     ApplicationManager.getApplication().assertReadAccessAllowed();
1546   }
1547
1548   public void fireSelectionChanged(final EditorComposite newSelectedComposite) {
1549     final Trinity<VirtualFile, FileEditor, FileEditorProvider> oldData = extract(myLastSelectedComposite.get());
1550     final Trinity<VirtualFile, FileEditor, FileEditorProvider> newData = extract(newSelectedComposite);
1551     myLastSelectedComposite = new WeakReference<EditorComposite>(newSelectedComposite);
1552     final boolean filesEqual = oldData.first == null ? newData.first == null : oldData.first.equals(newData.first);
1553     final boolean editorsEqual = oldData.second == null ? newData.second == null : oldData.second.equals(newData.second);
1554     if (!filesEqual || !editorsEqual) {
1555       if (oldData.first != null && newData.first != null) {
1556         for (FileEditorAssociateFinder finder : Extensions.getExtensions(FileEditorAssociateFinder.EP_NAME)) {
1557           VirtualFile associatedFile = finder.getAssociatedFileToOpen(myProject, oldData.first);
1558
1559           if (Comparing.equal(associatedFile, newData.first)) {
1560             return;
1561           }
1562         }
1563       }
1564
1565       final FileEditorManagerEvent event =
1566         new FileEditorManagerEvent(this, oldData.first, oldData.second, oldData.third, newData.first, newData.second, newData.third);
1567       final FileEditorManagerListener publisher = getProject().getMessageBus().syncPublisher(FileEditorManagerListener.FILE_EDITOR_MANAGER);
1568
1569       if (newData.first != null) {
1570         final JComponent component = newData.second.getComponent();
1571         final EditorWindowHolder holder = UIUtil.getParentOfType(EditorWindowHolder.class, component);
1572         if (holder != null) {
1573           addSelectionRecord(newData.first, holder.getEditorWindow());
1574         }
1575       }
1576       notifyPublisher(new Runnable() {
1577         @Override
1578         public void run() {
1579           publisher.selectionChanged(event);
1580         }
1581       });
1582     }
1583   }
1584
1585   @NotNull
1586   private static Trinity<VirtualFile, FileEditor, FileEditorProvider> extract(@Nullable EditorComposite composite) {
1587     final VirtualFile file;
1588     final FileEditor editor;
1589     final FileEditorProvider provider;
1590     if (composite == null || composite.isDisposed()) {
1591       file = null;
1592       editor = null;
1593       provider = null;
1594     }
1595     else {
1596       file = composite.getFile();
1597       final Pair<FileEditor, FileEditorProvider> pair = composite.getSelectedEditorWithProvider();
1598       editor = pair.first;
1599       provider = pair.second;
1600     }
1601     return new Trinity<VirtualFile, FileEditor, FileEditorProvider>(file, editor, provider);
1602   }
1603
1604   @Override
1605   public boolean isChanged(@NotNull final EditorComposite editor) {
1606     final FileStatusManager fileStatusManager = FileStatusManager.getInstance(myProject);
1607     if (fileStatusManager != null) {
1608       VirtualFile file = editor.getFile();
1609       FileStatus status = fileStatusManager.getStatus(file);
1610       if (status == FileStatus.UNKNOWN && !file.isWritable()) {
1611         return false;
1612       }
1613       if (!status.equals(FileStatus.NOT_CHANGED)) {
1614         return true;
1615       }
1616     }
1617     return false;
1618   }
1619
1620   public void disposeComposite(@NotNull EditorWithProviderComposite editor) {
1621     if (getAllEditors().length == 0) {
1622       setCurrentWindow(null);
1623     }
1624
1625     if (editor.equals(getLastSelected())) {
1626       editor.getSelectedEditor().deselectNotify();
1627       getSplitters().setCurrentWindow(null, false);
1628     }
1629
1630     final FileEditor[] editors = editor.getEditors();
1631     final FileEditorProvider[] providers = editor.getProviders();
1632
1633     final FileEditor selectedEditor = editor.getSelectedEditor();
1634     for (int i = editors.length - 1; i >= 0; i--) {
1635       final FileEditor editor1 = editors[i];
1636       final FileEditorProvider provider = providers[i];
1637       if (!editor.equals(selectedEditor)) { // we already notified the myEditor (when fire event)
1638         if (selectedEditor.equals(editor1)) {
1639           editor1.deselectNotify();
1640         }
1641       }
1642       editor1.removePropertyChangeListener(myEditorPropertyChangeListener);
1643       provider.disposeEditor(editor1);
1644     }
1645
1646     Disposer.dispose(editor);
1647   }
1648
1649   @Nullable
1650   EditorComposite getLastSelected() {
1651     final EditorWindow currentWindow = getActiveSplitters(true).getResult().getCurrentWindow();
1652     if (currentWindow != null) {
1653       return currentWindow.getSelectedEditor();
1654     }
1655     return null;
1656   }
1657
1658   /**
1659    * @param splitters - taken getAllSplitters() value if parameter is null
1660    */
1661   public void runChange(@NotNull FileEditorManagerChange change, @Nullable EditorsSplitters splitters) {
1662     Set<EditorsSplitters> target = new HashSet<EditorsSplitters>();
1663     if (splitters == null) {
1664       target.addAll(getAllSplitters());
1665     } else {
1666       target.add(splitters);
1667     }
1668
1669     for (EditorsSplitters each : target) {
1670       each.myInsideChange++;
1671       try {
1672         change.run(each);
1673       }
1674       finally {
1675         each.myInsideChange--;
1676       }
1677     }
1678   }
1679
1680   //================== Listeners =====================
1681
1682   /**
1683    * Closes deleted files. Closes file which are in the deleted directories.
1684    */
1685   private final class MyVirtualFileListener extends VirtualFileAdapter {
1686     @Override
1687     public void beforeFileDeletion(@NotNull VirtualFileEvent e) {
1688       assertDispatchThread();
1689
1690       boolean moveFocus = moveFocusOnDelete();
1691
1692       final VirtualFile file = e.getFile();
1693       final VirtualFile[] openFiles = getOpenFiles();
1694       for (int i = openFiles.length - 1; i >= 0; i--) {
1695         if (VfsUtilCore.isAncestor(file, openFiles[i], false)) {
1696           closeFile(openFiles[i], moveFocus, true);
1697         }
1698       }
1699     }
1700
1701     @Override
1702     public void propertyChanged(@NotNull VirtualFilePropertyEvent e) {
1703       if (VirtualFile.PROP_NAME.equals(e.getPropertyName())) {
1704         assertDispatchThread();
1705         final VirtualFile file = e.getFile();
1706         if (isFileOpen(file)) {
1707           updateFileName(file);
1708           updateFileIcon(file); // file type can change after renaming
1709           updateFileBackgroundColor(file);
1710         }
1711       }
1712       else if (VirtualFile.PROP_WRITABLE.equals(e.getPropertyName()) || VirtualFile.PROP_ENCODING.equals(e.getPropertyName())) {
1713         // TODO: message bus?
1714         updateIconAndStatusBar(e);
1715       }
1716     }
1717
1718     private void updateIconAndStatusBar(final VirtualFilePropertyEvent e) {
1719       assertDispatchThread();
1720       final VirtualFile file = e.getFile();
1721       if (isFileOpen(file)) {
1722         updateFileIcon(file);
1723         if (file.equals(getSelectedFiles()[0])) { // update "write" status
1724           final StatusBarEx statusBar = (StatusBarEx)WindowManager.getInstance().getStatusBar(myProject);
1725           assert statusBar != null;
1726           statusBar.updateWidgets();
1727         }
1728       }
1729     }
1730
1731     @Override
1732     public void fileMoved(@NotNull VirtualFileMoveEvent e) {
1733       final VirtualFile file = e.getFile();
1734       final VirtualFile[] openFiles = getOpenFiles();
1735       for (final VirtualFile openFile : openFiles) {
1736         if (VfsUtilCore.isAncestor(file, openFile, false)) {
1737           updateFileName(openFile);
1738           updateFileBackgroundColor(openFile);
1739         }
1740       }
1741     }
1742   }
1743
1744   private static boolean moveFocusOnDelete() {
1745     final Window window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
1746     if (window != null) {
1747       final Component component = FocusTrackback.getFocusFor(window);
1748       if (component != null) {
1749         return component instanceof EditorComponentImpl;
1750       }
1751       return window instanceof IdeFrameImpl;
1752     }
1753     return true;
1754   }
1755
1756   @Override
1757   public boolean isInsideChange() {
1758     return getSplitters().isInsideChange();
1759   }
1760
1761   private final class MyEditorPropertyChangeListener implements PropertyChangeListener {
1762     @Override
1763     public void propertyChange(final PropertyChangeEvent e) {
1764       assertDispatchThread();
1765
1766       final String propertyName = e.getPropertyName();
1767       if (FileEditor.PROP_MODIFIED.equals(propertyName)) {
1768         final FileEditor editor = (FileEditor)e.getSource();
1769         final EditorComposite composite = getEditorComposite(editor);
1770         if (composite != null) {
1771           updateFileIcon(composite.getFile());
1772         }
1773       }
1774       else if (FileEditor.PROP_VALID.equals(propertyName)) {
1775         final boolean valid = ((Boolean)e.getNewValue()).booleanValue();
1776         if (!valid) {
1777           final FileEditor editor = (FileEditor)e.getSource();
1778           LOG.assertTrue(editor != null);
1779           final EditorComposite composite = getEditorComposite(editor);
1780           if (composite != null) {
1781             closeFile(composite.getFile());
1782           }
1783         }
1784       }
1785
1786     }
1787   }
1788
1789
1790   /**
1791    * Gets events from VCS and updates color of myEditor tabs
1792    */
1793   private final class MyFileStatusListener implements FileStatusListener {
1794     @Override
1795     public void fileStatusesChanged() { // update color of all open files
1796       assertDispatchThread();
1797       LOG.debug("FileEditorManagerImpl.MyFileStatusListener.fileStatusesChanged()");
1798       final VirtualFile[] openFiles = getOpenFiles();
1799       for (int i = openFiles.length - 1; i >= 0; i--) {
1800         final VirtualFile file = openFiles[i];
1801         LOG.assertTrue(file != null);
1802         ApplicationManager.getApplication().invokeLater(new Runnable() {
1803           @Override
1804           public void run() {
1805             if (LOG.isDebugEnabled()) {
1806               LOG.debug("updating file status in tab for " + file.getPath());
1807             }
1808             updateFileStatus(file);
1809           }
1810         }, ModalityState.NON_MODAL, myProject.getDisposed());
1811       }
1812     }
1813
1814     @Override
1815     public void fileStatusChanged(@NotNull final VirtualFile file) { // update color of the file (if necessary)
1816       assertDispatchThread();
1817       if (isFileOpen(file)) {
1818         updateFileStatus(file);
1819       }
1820     }
1821
1822     private void updateFileStatus(final VirtualFile file) {
1823       updateFileColor(file);
1824       updateFileIcon(file);
1825     }
1826   }
1827
1828   /**
1829    * Gets events from FileTypeManager and updates icons on tabs
1830    */
1831   private final class MyFileTypeListener extends FileTypeListener.Adapter {
1832     @Override
1833     public void fileTypesChanged(@NotNull final FileTypeEvent event) {
1834       assertDispatchThread();
1835       final VirtualFile[] openFiles = getOpenFiles();
1836       for (int i = openFiles.length - 1; i >= 0; i--) {
1837         final VirtualFile file = openFiles[i];
1838         LOG.assertTrue(file != null);
1839         updateFileIcon(file);
1840       }
1841     }
1842   }
1843
1844   private class MyRootsListener extends ModuleRootAdapter {
1845     @Override
1846     public void rootsChanged(ModuleRootEvent event) {
1847       EditorFileSwapper[] swappers = Extensions.getExtensions(EditorFileSwapper.EP_NAME);
1848
1849       for (EditorWindow eachWindow : getWindows()) {
1850         EditorWithProviderComposite selected = eachWindow.getSelectedEditor();
1851         EditorWithProviderComposite[] editors = eachWindow.getEditors();
1852         for (int i = 0; i < editors.length; i++) {
1853           EditorWithProviderComposite editor = editors[i];
1854           VirtualFile file = editor.getFile();
1855           if (!file.isValid()) continue;
1856
1857           Pair<VirtualFile, Integer> newFilePair = null;
1858
1859           for (EditorFileSwapper each : swappers) {
1860             newFilePair = each.getFileToSwapTo(myProject, editor);
1861             if (newFilePair != null) break;
1862           }
1863
1864           if (newFilePair == null) continue;
1865
1866           VirtualFile newFile = newFilePair.first;
1867           if (newFile == null) continue;
1868
1869           // already open
1870           if (eachWindow.findFileIndex(newFile) != -1) continue;
1871
1872           try {
1873             newFile.putUserData(EditorWindow.INITIAL_INDEX_KEY, i);
1874             Pair<FileEditor[], FileEditorProvider[]> pair = openFileImpl2(eachWindow, newFile, editor == selected);
1875
1876             if (newFilePair.second != null) {
1877               TextEditorImpl openedEditor = EditorFileSwapper.findSinglePsiAwareEditor(pair.first);
1878               if (openedEditor != null) {
1879                 openedEditor.getEditor().getCaretModel().moveToOffset(newFilePair.second);
1880                 openedEditor.getEditor().getScrollingModel().scrollToCaret(ScrollType.CENTER);
1881               }
1882             }
1883           }
1884           finally {
1885             newFile.putUserData(EditorWindow.INITIAL_INDEX_KEY, null);
1886           }
1887           closeFile(file, eachWindow);
1888         }
1889       }
1890     }
1891   }
1892
1893   /**
1894    * Gets notifications from UISetting component to track changes of RECENT_FILES_LIMIT
1895    * and EDITOR_TAB_LIMIT, etc values.
1896    */
1897   private final class MyUISettingsListener implements UISettingsListener {
1898     @Override
1899     public void uiSettingsChanged(final UISettings source) {
1900       assertDispatchThread();
1901       setTabsMode(source.EDITOR_TAB_PLACEMENT != UISettings.TABS_NONE && !UISettings.getInstance().PRESENTATION_MODE);
1902
1903       for (EditorsSplitters each : getAllSplitters()) {
1904         each.setTabsPlacement(source.EDITOR_TAB_PLACEMENT);
1905         each.trimToSize(source.EDITOR_TAB_LIMIT);
1906
1907         // Tab layout policy
1908         if (source.SCROLL_TAB_LAYOUT_IN_EDITOR) {
1909           each.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
1910         }
1911         else {
1912           each.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT);
1913         }
1914       }
1915
1916       // "Mark modified files with asterisk"
1917       final VirtualFile[] openFiles = getOpenFiles();
1918       for (int i = openFiles.length - 1; i >= 0; i--) {
1919         final VirtualFile file = openFiles[i];
1920         updateFileIcon(file);
1921         updateFileName(file);
1922         updateFileBackgroundColor(file);
1923       }
1924     }
1925   }
1926
1927   @Override
1928   public void closeAllFiles() {
1929     final VirtualFile[] openFiles = getSplitters().getOpenFiles();
1930     for (VirtualFile openFile : openFiles) {
1931       closeFile(openFile);
1932     }
1933   }
1934
1935   @Override
1936   @NotNull
1937   public VirtualFile[] getSiblings(@NotNull VirtualFile file) {
1938     return getOpenFiles();
1939   }
1940
1941   protected void queueUpdateFile(final VirtualFile file) {
1942     myQueue.queue(new Update(file) {
1943       @Override
1944       public void run() {
1945         if (isFileOpen(file)) {
1946           updateFileIcon(file);
1947           updateFileColor(file);
1948           updateFileBackgroundColor(file);
1949         }
1950
1951       }
1952     });
1953   }
1954
1955   @Override
1956   public EditorsSplitters getSplittersFor(Component c) {
1957     EditorsSplitters splitters = null;
1958     DockContainer dockContainer = myDockManager.getContainerFor(c);
1959     if (dockContainer instanceof DockableEditorTabbedContainer) {
1960       splitters = ((DockableEditorTabbedContainer)dockContainer).getSplitters();
1961     }
1962
1963     if (splitters == null) {
1964       splitters = getMainSplitters();
1965     }
1966
1967     return splitters;
1968   }
1969
1970   public List<Pair<VirtualFile, EditorWindow>> getSelectionHistory() {
1971     List<Pair<VirtualFile, EditorWindow>> copy = new ArrayList<Pair<VirtualFile, EditorWindow>>();
1972     for (Pair<VirtualFile, EditorWindow> pair : mySelectionHistory) {
1973       if (pair.second.getFiles().length == 0) {
1974         final EditorWindow[] windows = pair.second.getOwner().getWindows();
1975         if (windows.length > 0 && windows[0] != null && windows[0].getFiles().length > 0) {
1976           final Pair<VirtualFile, EditorWindow> p = Pair.create(pair.first, windows[0]);
1977           if (!copy.contains(p)) {
1978             copy.add(p);
1979           }
1980         }
1981       } else {
1982         if (!copy.contains(pair)) {
1983           copy.add(pair);
1984         }
1985       }
1986     }
1987     mySelectionHistory.clear();
1988     mySelectionHistory.addAll(copy);
1989     return mySelectionHistory;
1990   }
1991
1992   public void addSelectionRecord(VirtualFile file, EditorWindow window) {
1993     final Pair<VirtualFile, EditorWindow> record = Pair.create(file, window);
1994     mySelectionHistory.remove(record);
1995     mySelectionHistory.add(0, record);
1996   }
1997
1998   public void removeSelectionRecord(VirtualFile file, EditorWindow window) {
1999     mySelectionHistory.remove(Pair.create(file, window));
2000   }
2001
2002   @Override
2003   public ActionCallback getReady(@NotNull Object requestor) {
2004     return myBusyObject.getReady(requestor);
2005   }
2006 }