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