2 * Copyright 2000-2014 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.openapi.fileEditor.impl;
18 import com.intellij.ide.IdeBundle;
19 import com.intellij.ide.ui.UISettings;
20 import com.intellij.ide.ui.UISettingsListener;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.application.ModalityState;
23 import com.intellij.openapi.components.ServiceManager;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.editor.Document;
26 import com.intellij.openapi.fileEditor.FileDocumentManager;
27 import com.intellij.openapi.fileEditor.FileEditor;
28 import com.intellij.openapi.fileEditor.impl.text.FileDropHandler;
29 import com.intellij.openapi.keymap.Keymap;
30 import com.intellij.openapi.keymap.KeymapManager;
31 import com.intellij.openapi.keymap.KeymapManagerListener;
32 import com.intellij.openapi.progress.ProgressIndicator;
33 import com.intellij.openapi.progress.ProgressManager;
34 import com.intellij.openapi.project.DumbService;
35 import com.intellij.openapi.project.Project;
36 import com.intellij.openapi.ui.Splitter;
37 import com.intellij.openapi.util.*;
38 import com.intellij.openapi.vfs.VfsUtilCore;
39 import com.intellij.openapi.vfs.VirtualFile;
40 import com.intellij.openapi.wm.FocusWatcher;
41 import com.intellij.openapi.wm.IdeFrame;
42 import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
43 import com.intellij.openapi.wm.ex.WindowManagerEx;
44 import com.intellij.openapi.wm.impl.FrameTitleBuilder;
45 import com.intellij.openapi.wm.impl.IdePanePanel;
46 import com.intellij.ui.JBColor;
47 import com.intellij.ui.awt.RelativePoint;
48 import com.intellij.ui.docking.DockManager;
49 import com.intellij.ui.tabs.JBTabs;
50 import com.intellij.util.Alarm;
51 import com.intellij.util.containers.ArrayListSet;
52 import com.intellij.util.containers.ContainerUtil;
53 import com.intellij.util.ui.UIUtil;
54 import org.jdom.Element;
55 import org.jetbrains.annotations.NotNull;
56 import org.jetbrains.annotations.Nullable;
60 import java.awt.datatransfer.DataFlavor;
61 import java.awt.datatransfer.Transferable;
62 import java.awt.event.ContainerEvent;
65 import java.util.List;
66 import java.util.concurrent.CopyOnWriteArraySet;
72 public class EditorsSplitters extends IdePanePanel implements UISettingsListener {
73 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.EditorsSplitters");
74 private static final String PINNED = "pinned";
75 private static final String CURRENT_IN_TAB = "current-in-tab";
77 private static final Key<Object> DUMMY_KEY = Key.create("EditorsSplitters.dummy.key");
79 private final static EditorEmptyTextPainter ourPainter = ServiceManager.getService(EditorEmptyTextPainter.class);
81 private EditorWindow myCurrentWindow;
82 final Set<EditorWindow> myWindows = new CopyOnWriteArraySet<EditorWindow>();
84 private final FileEditorManagerImpl myManager;
85 private Element mySplittersElement; // temporarily used during initialization
86 int myInsideChange = 0;
87 private final MyFocusWatcher myFocusWatcher;
88 private final Alarm myIconUpdaterAlarm = new Alarm();
89 private final KeymapManagerListener myKeymapListener;
90 private final UIBuilder myUIBuilder = new UIBuilder();
92 public EditorsSplitters(final FileEditorManagerImpl manager, DockManager dockManager, boolean createOwnDockableContainer) {
93 super(new BorderLayout());
95 myFocusWatcher = new MyFocusWatcher();
96 setFocusTraversalPolicy(new MyFocusTraversalPolicy());
97 setTransferHandler(new MyTransferHandler());
100 if (createOwnDockableContainer) {
101 DockableEditorTabbedContainer dockable = new DockableEditorTabbedContainer(myManager.getProject(), this, false);
102 Disposer.register(manager.getProject(), dockable);
103 dockManager.register(dockable);
105 myKeymapListener = new KeymapManagerListener() {
107 public void activeKeymapChanged(Keymap keymap) {
112 KeymapManager.getInstance().addKeymapManagerListener(myKeymapListener);
113 UISettings.getInstance().addUISettingsListener(this);
116 public FileEditorManagerImpl getManager() {
120 public void clear() {
123 setCurrentWindow(null);
124 repaint (); // revalidate doesn't repaint correctly after "Close All"
127 public void startListeningFocus() {
128 myFocusWatcher.install(this);
131 private void stopListeningFocus() {
132 myFocusWatcher.deinstall(this);
135 public void dispose() {
136 myIconUpdaterAlarm.cancelAllRequests();
137 stopListeningFocus();
138 KeymapManager.getInstance().removeKeymapManagerListener(myKeymapListener);
139 UISettings.getInstance().removeUISettingsListener(this);
143 public VirtualFile getCurrentFile() {
144 if (myCurrentWindow != null) {
145 return myCurrentWindow.getSelectedFile();
151 protected boolean showEmptyText() {
152 return myCurrentWindow == null || myCurrentWindow.getFiles().length == 0;
156 protected void paintComponent(Graphics g) {
157 super.paintComponent(g);
159 if (myCurrentWindow == null || myCurrentWindow.getFiles().length == 0) {
160 g.setColor(UIUtil.isUnderDarcula()? UIUtil.getBorderColor() : new Color(0, 0, 0, 50));
161 g.drawLine(0, 0, getWidth(), 0);
164 if (showEmptyText()) {
165 ourPainter.paintEmptyText(this, g);
169 public void writeExternal(final Element element) {
170 if (getComponentCount() != 0) {
171 final Component comp = getComponent(0);
172 LOG.assertTrue(comp instanceof JPanel);
173 final JPanel panel = (JPanel)comp;
174 if (panel.getComponentCount() != 0) {
175 final Element res = writePanel(panel);
176 element.addContent(res);
181 @SuppressWarnings({"HardCodedStringLiteral"})
182 private Element writePanel(final JPanel panel) {
183 final Component comp = panel.getComponent(0);
184 if (comp instanceof Splitter) {
185 final Splitter splitter = (Splitter)comp;
186 final Element res = new Element("splitter");
187 res.setAttribute("split-orientation", splitter.getOrientation() ? "vertical" : "horizontal");
188 res.setAttribute("split-proportion", Float.toString(splitter.getProportion()));
189 final Element first = new Element("split-first");
190 first.addContent(writePanel((JPanel)splitter.getFirstComponent()));
191 final Element second = new Element("split-second");
192 second.addContent(writePanel((JPanel)splitter.getSecondComponent()));
193 res.addContent(first);
194 res.addContent(second);
197 else if (comp instanceof JBTabs) {
198 final Element res = new Element("leaf");
199 final EditorWindow window = findWindowWith(comp);
200 writeWindow(res, window);
203 else if (comp instanceof EditorWindow.TCompForTablessMode) {
204 final EditorWithProviderComposite composite = ((EditorWindow.TCompForTablessMode)comp).myEditor;
205 final Element res = new Element("leaf");
206 writeComposite(res, composite.getFile(), composite, false, composite);
210 LOG.error(comp != null ? comp.getClass().getName() : null);
215 private void writeWindow(final Element res, final EditorWindow window) {
216 if (window != null) {
217 final EditorWithProviderComposite[] composites = window.getEditors();
218 for (int i = 0; i < composites.length; i++) {
219 final VirtualFile file = window.getFileAt(i);
220 final boolean isPinned = window.isFilePinned(file);
221 final EditorWithProviderComposite composite = composites[i];
222 final EditorWithProviderComposite selectedEditor = window.getSelectedEditor();
224 writeComposite(res, file, composite, isPinned, selectedEditor);
229 private void writeComposite(final Element res, final VirtualFile file, final EditorWithProviderComposite composite,
230 final boolean pinned,
231 final EditorWithProviderComposite selectedEditor) {
232 final Element fileElement = new Element("file");
233 fileElement.setAttribute("leaf-file-name", file.getName()); // TODO: all files
234 final HistoryEntry entry = composite.currentStateAsHistoryEntry();
235 entry.writeExternal(fileElement, getManager().getProject());
236 fileElement.setAttribute(PINNED, Boolean.toString(pinned));
237 fileElement.setAttribute(CURRENT_IN_TAB, Boolean.toString(composite.equals(selectedEditor)));
238 res.addContent(fileElement);
241 public void openFiles() {
242 if (mySplittersElement != null) {
243 initializeProgress();
244 final JPanel comp = myUIBuilder.process(mySplittersElement, getTopPanel());
245 UIUtil.invokeAndWaitIfNeeded(new Runnable() {
250 add(comp, BorderLayout.CENTER);
251 mySplittersElement = null;
253 // clear empty splitters
254 for (EditorWindow window : getWindows()) {
255 if (window.getEditors().length == 0) {
256 for (EditorWindow sibling : window.findSiblings()) {
257 sibling.unsplit(false);
266 private double myProgressMaximum;
267 private int myCurrentProgress;
269 private void initializeProgress() {
270 ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
271 if (indicator != null) {
272 indicator.setText(IdeBundle.message("loading.editors"));
273 indicator.setText2("");
274 indicator.setIndeterminate(false);
275 indicator.setFraction(0);
277 myProgressMaximum = countFiles(mySplittersElement);
278 myCurrentProgress = 0;
282 private void updateProgress() {
283 ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
284 if (indicator != null) {
286 indicator.setFraction(myCurrentProgress / myProgressMaximum);
290 private static int countFiles(Element element) {
291 Integer value = new ConfigTreeReader<Integer>() {
293 protected Integer processFiles(@NotNull List<Element> fileElements, @Nullable Integer context) {
294 return fileElements.size();
298 protected Integer processSplitter(@NotNull Element element, @Nullable Element firstChild, @Nullable Element secondChild, @Nullable Integer context) {
299 Integer first = process(firstChild, null);
300 Integer second = process(secondChild, null);
301 return (first == null ? 0 : first) + (second == null ? 0 : second);
303 }.process(element, null);
304 return value == null ? 0 : value;
307 public void readExternal(final Element element) {
308 mySplittersElement = element;
311 @NotNull public VirtualFile[] getOpenFiles() {
312 final ArrayListSet<VirtualFile> files = new ArrayListSet<VirtualFile>();
313 for (final EditorWindow myWindow : myWindows) {
314 final EditorWithProviderComposite[] editors = myWindow.getEditors();
315 for (final EditorWithProviderComposite editor : editors) {
316 VirtualFile file = editor.getFile();
317 // background thread may call this method when invalid file is being removed
318 // do not return it here as it will quietly drop out soon
319 if (file.isValid()) {
324 return VfsUtilCore.toVirtualFileArray(files);
327 @NotNull public VirtualFile[] getSelectedFiles() {
328 final ArrayListSet<VirtualFile> files = new ArrayListSet<VirtualFile>();
329 for (final EditorWindow window : myWindows) {
330 final VirtualFile file = window.getSelectedFile();
335 final VirtualFile[] virtualFiles = VfsUtilCore.toVirtualFileArray(files);
336 final VirtualFile currentFile = getCurrentFile();
337 if (currentFile != null) {
338 for (int i = 0; i != virtualFiles.length; ++i) {
339 if (Comparing.equal(virtualFiles[i], currentFile)) {
340 virtualFiles[i] = virtualFiles[0];
341 virtualFiles[0] = currentFile;
349 @NotNull public FileEditor[] getSelectedEditors() {
350 final List<FileEditor> editors = new ArrayList<FileEditor>();
351 final EditorWindow currentWindow = getCurrentWindow();
352 if (currentWindow != null) {
353 final EditorWithProviderComposite composite = currentWindow.getSelectedEditor();
354 if (composite != null) {
355 editors.add (composite.getSelectedEditor());
359 for (final EditorWindow window : myWindows) {
360 if (!window.equals(currentWindow)) {
361 final EditorWithProviderComposite composite = window.getSelectedEditor();
362 if (composite != null) {
363 editors.add(composite.getSelectedEditor());
367 return editors.toArray(new FileEditor[editors.size()]);
370 public void updateFileIcon(@NotNull final VirtualFile file) {
371 updateFileIconLater(file);
374 private void updateFileIconImmediately(final VirtualFile file) {
375 final Collection<EditorWindow> windows = findWindows(file);
376 for (EditorWindow window : windows) {
377 window.updateFileIcon(file);
381 private final Set<VirtualFile> myFilesToUpdateIconsFor = new HashSet<VirtualFile>();
383 private void updateFileIconLater(VirtualFile file) {
384 myFilesToUpdateIconsFor.add(file);
385 myIconUpdaterAlarm.cancelAllRequests();
386 myIconUpdaterAlarm.addRequest(new Runnable() {
389 if (myManager.getProject().isDisposed()) return;
390 for (VirtualFile file : myFilesToUpdateIconsFor) {
391 updateFileIconImmediately(file);
393 myFilesToUpdateIconsFor.clear();
395 }, 200, ModalityState.stateForComponent(this));
398 public void updateFileColor(@NotNull final VirtualFile file) {
399 final Collection<EditorWindow> windows = findWindows(file);
400 for (final EditorWindow window : windows) {
401 final int index = window.findEditorIndex(window.findFileComposite(file));
402 LOG.assertTrue(index != -1);
403 window.setForegroundAt(index, getManager().getFileColor(file));
404 window.setWaveColor(index, getManager().isProblem(file) ? JBColor.red : null);
408 public void trimToSize(final int editor_tab_limit) {
409 for (final EditorWindow window : myWindows) {
410 window.trimToSize(editor_tab_limit, null, true);
414 public void setTabsPlacement(final int tabPlacement) {
415 final EditorWindow[] windows = getWindows();
416 for (int i = 0; i != windows.length; ++ i) {
417 windows[i].setTabsPlacement(tabPlacement);
421 public void setTabLayoutPolicy(int scrollTabLayout) {
422 final EditorWindow[] windows = getWindows();
423 for (int i = 0; i != windows.length; ++ i) {
424 windows[i].setTabLayoutPolicy(scrollTabLayout);
428 public void updateFileName(final VirtualFile updatedFile) {
429 final EditorWindow[] windows = getWindows();
430 for (int i = 0; i != windows.length; ++ i) {
431 windows [i].updateFileName(updatedFile);
434 Project project = myManager.getProject();
436 final IdeFrame frame = getFrame(project);
438 VirtualFile file = getCurrentFile();
440 File ioFile = file == null ? null : new File(file.getPresentableUrl());
441 String fileTitle = null;
443 fileTitle = DumbService.isDumb(project) ? file.getName()
444 : FrameTitleBuilder.getInstance().getFileTitle(project, file);
447 frame.setFileTitle(fileTitle, ioFile);
451 protected IdeFrame getFrame(Project project) {
452 final WindowManagerEx windowManagerEx = WindowManagerEx.getInstanceEx();
453 final IdeFrame frame = windowManagerEx.getFrame(project);
454 LOG.assertTrue(ApplicationManager.getApplication().isUnitTestMode() || frame != null);
458 public boolean isInsideChange() {
459 return myInsideChange > 0;
462 private void setCurrentWindow(@Nullable final EditorWindow currentWindow) {
463 myCurrentWindow = currentWindow;
466 public void updateFileBackgroundColor(final VirtualFile file) {
467 final EditorWindow[] windows = getWindows();
468 for (int i = 0; i != windows.length; ++ i) {
469 windows [i].updateFileBackgroundColor(file);
473 public int getSplitCount() {
474 if (getComponentCount() > 0) {
475 JPanel panel = (JPanel) getComponent(0);
476 return getSplitCount(panel);
481 private static int getSplitCount(JComponent component) {
482 if (component.getComponentCount() > 0) {
483 final JComponent firstChild = (JComponent)component.getComponent(0);
484 if (firstChild instanceof Splitter) {
485 final Splitter splitter = (Splitter)firstChild;
486 return getSplitCount(splitter.getFirstComponent()) + getSplitCount(splitter.getSecondComponent());
493 protected void afterFileClosed(VirtualFile file) {
496 protected void afterFileOpen(VirtualFile file) {
500 public JBTabs getTabsAt(RelativePoint point) {
501 Point thisPoint = point.getPoint(this);
502 Component c = SwingUtilities.getDeepestComponentAt(this, thisPoint.x, thisPoint.y);
504 if (c instanceof JBTabs) {
513 public boolean isEmptyVisible() {
514 EditorWindow[] windows = getWindows();
515 for (EditorWindow each : windows) {
516 if (!each.isEmptyVisible()) {
524 public VirtualFile findNextFile(final VirtualFile file) {
525 final EditorWindow[] windows = getWindows(); // TODO: use current file as base
526 for (int i = 0; i != windows.length; ++i) {
527 final VirtualFile[] files = windows[i].getFiles();
528 for (final VirtualFile fileAt : files) {
529 if (!Comparing.equal(fileAt, file)) {
537 void closeFile(VirtualFile file, boolean moveFocus) {
538 final List<EditorWindow> windows = findWindows(file);
539 if (!windows.isEmpty()) {
540 final VirtualFile nextFile = findNextFile(file);
541 for (final EditorWindow window : windows) {
542 LOG.assertTrue(window.getSelectedEditor() != null);
543 window.closeFile(file, false, moveFocus);
544 if (window.getTabCount() == 0 && nextFile != null && myManager.getProject().isOpen()) {
545 EditorWithProviderComposite newComposite = myManager.newEditorComposite(nextFile);
546 window.setEditor(newComposite, moveFocus); // newComposite can be null
549 // cleanup windows with no tabs
550 for (final EditorWindow window : windows) {
551 if (window.isDisposed()) {
552 // call to window.unsplit() which might make its sibling disposed
555 if (window.getTabCount() == 0) {
556 window.unsplit(false);
563 public void uiSettingsChanged(UISettings source) {
564 for (VirtualFile file : getOpenFiles()) {
565 updateFileBackgroundColor(file);
566 updateFileIcon(file);
567 updateFileColor(file);
571 private final class MyFocusTraversalPolicy extends IdeFocusTraversalPolicy {
573 public final Component getDefaultComponentImpl(final Container focusCycleRoot) {
574 if (myCurrentWindow != null) {
575 final EditorWithProviderComposite selectedEditor = myCurrentWindow.getSelectedEditor();
576 if (selectedEditor != null) {
577 return IdeFocusTraversalPolicy.getPreferredFocusedComponent(selectedEditor.getComponent(), this);
580 return IdeFocusTraversalPolicy.getPreferredFocusedComponent(EditorsSplitters.this, this);
585 public JPanel getTopPanel() {
586 return getComponentCount() > 0 ? (JPanel)getComponent(0) : null;
589 public EditorWindow getCurrentWindow() {
590 return myCurrentWindow;
593 public EditorWindow getOrCreateCurrentWindow(final VirtualFile file) {
594 final List<EditorWindow> windows = findWindows(file);
595 if (getCurrentWindow() == null) {
596 final Iterator<EditorWindow> iterator = myWindows.iterator();
597 if (!windows.isEmpty()) {
598 setCurrentWindow(windows.get(0), false);
600 else if (iterator.hasNext()) {
601 setCurrentWindow(iterator.next(), false);
604 createCurrentWindow();
607 else if (!windows.isEmpty()) {
608 if (!windows.contains(getCurrentWindow())) {
609 setCurrentWindow(windows.get(0), false);
612 return getCurrentWindow();
615 public void createCurrentWindow() {
616 LOG.assertTrue(myCurrentWindow == null);
617 setCurrentWindow(new EditorWindow(this));
618 add(myCurrentWindow.myPanel, BorderLayout.CENTER);
622 * sets the window passed as a current ('focused') window among all splitters. All file openings will be done inside this
624 * @param window a window to be set as current
625 * @param requestFocus whether to request focus to the editor currently selected in this window
627 public void setCurrentWindow(@Nullable final EditorWindow window, final boolean requestFocus) {
628 final EditorWithProviderComposite newEditor = window != null? window.getSelectedEditor() : null;
630 Runnable fireRunnable = new Runnable() {
633 getManager().fireSelectionChanged(newEditor);
637 setCurrentWindow(window);
639 getManager().updateFileName(window == null ? null : window.getSelectedFile());
641 if (window != null) {
642 final EditorWithProviderComposite selectedEditor = window.getSelectedEditor();
643 if (selectedEditor != null) {
648 window.requestFocus(true);
655 //---------------------------------------------------------
657 public EditorWithProviderComposite[] getEditorsComposites() {
658 final ArrayList<EditorWithProviderComposite> res = new ArrayList<EditorWithProviderComposite>();
660 for (final EditorWindow myWindow : myWindows) {
661 final EditorWithProviderComposite[] editors = myWindow.getEditors();
662 ContainerUtil.addAll(res, editors);
664 return res.toArray(new EditorWithProviderComposite[res.size()]);
667 //---------------------------------------------------------
670 public List<EditorWithProviderComposite> findEditorComposites(final VirtualFile file) {
671 final ArrayList<EditorWithProviderComposite> res = new ArrayList<EditorWithProviderComposite>();
672 for (final EditorWindow window : myWindows) {
673 final EditorWithProviderComposite fileComposite = window.findFileComposite(file);
674 if (fileComposite != null) {
675 res.add(fileComposite);
682 public List<EditorWindow> findWindows(final VirtualFile file) {
683 final ArrayList<EditorWindow> res = new ArrayList<EditorWindow>();
684 for (final EditorWindow window : myWindows) {
685 if (window.findFileComposite(file) != null) {
692 @NotNull public EditorWindow [] getWindows() {
693 return myWindows.toArray(new EditorWindow [myWindows.size()]);
696 @NotNull public EditorWindow[] getOrderedWindows() {
697 final ArrayList<EditorWindow> res = new ArrayList<EditorWindow>();
699 // Collector for windows in tree ordering:
701 final void collect(final JPanel panel){
702 final Component comp = panel.getComponent(0);
703 if (comp instanceof Splitter) {
704 final Splitter splitter = (Splitter)comp;
705 collect((JPanel)splitter.getFirstComponent());
706 collect((JPanel)splitter.getSecondComponent());
708 else if (comp instanceof JPanel || comp instanceof JBTabs) {
709 final EditorWindow window = findWindowWith(comp);
710 if (window != null) {
717 // get root component and traverse splitters tree:
718 if (getComponentCount() != 0) {
719 final Component comp = getComponent(0);
720 LOG.assertTrue(comp instanceof JPanel);
721 final JPanel panel = (JPanel)comp;
722 if (panel.getComponentCount() != 0) {
723 new Inner().collect (panel);
727 LOG.assertTrue(res.size() == myWindows.size());
728 return res.toArray(new EditorWindow [res.size()]);
732 private EditorWindow findWindowWith(final Component component) {
733 if (component != null) {
734 for (final EditorWindow window : myWindows) {
735 if (SwingUtilities.isDescendingFrom(component, window.myPanel)) {
743 public boolean isFloating() {
747 private final class MyFocusWatcher extends FocusWatcher {
749 protected void focusedComponentChanged(final Component component, final AWTEvent cause) {
750 EditorWindow newWindow = null;
752 if (component != null) {
753 newWindow = findWindowWith(component);
755 else if (cause instanceof ContainerEvent && cause.getID() == ContainerEvent.COMPONENT_REMOVED) {
756 // do not change current window in case of child removal as in JTable.removeEditor
757 // otherwise Escape in a toolwindow will not focus editor with JTable content
761 setCurrentWindow(newWindow);
762 setCurrentWindow(newWindow, false);
766 private final class MyTransferHandler extends TransferHandler {
767 private final FileDropHandler myFileDropHandler = new FileDropHandler(null);
770 public boolean importData(JComponent comp, Transferable t) {
771 if (myFileDropHandler.canHandleDrop(t.getTransferDataFlavors())) {
772 myFileDropHandler.handleDrop(t, myManager.getProject(), myCurrentWindow);
779 public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
780 return myFileDropHandler.canHandleDrop(transferFlavors);
784 private abstract static class ConfigTreeReader<T> {
786 public T process(@Nullable Element element, @Nullable T context) {
787 if (element == null) {
790 final Element splitterElement = element.getChild("splitter");
791 if (splitterElement != null) {
792 final Element first = splitterElement.getChild("split-first");
793 final Element second = splitterElement.getChild("split-second");
794 return processSplitter(splitterElement, first, second, context);
797 final Element leaf = element.getChild("leaf");
802 List<Element> fileElements = leaf.getChildren("file");
803 final List<Element> children = new ArrayList<Element>(fileElements.size());
805 // trim to EDITOR_TAB_LIMIT, ignoring CLOSE_NON_MODIFIED_FILES_FIRST policy
806 int toRemove = fileElements.size() - UISettings.getInstance().EDITOR_TAB_LIMIT;
807 for (Element fileElement : fileElements) {
808 if (toRemove <= 0 || Boolean.valueOf(fileElement.getAttributeValue(PINNED)).booleanValue()) {
809 children.add(fileElement);
816 return processFiles(children, context);
819 protected abstract @Nullable T processFiles(@NotNull List<Element> fileElements, @Nullable T context);
820 protected abstract @Nullable T processSplitter(@NotNull Element element, @Nullable Element firstChild, @Nullable Element secondChild, @Nullable T context);
823 private class UIBuilder extends ConfigTreeReader<JPanel> {
826 protected JPanel processFiles(@NotNull List<Element> fileElements, final JPanel context) {
827 final Ref<EditorWindow> windowRef = new Ref<EditorWindow>();
828 UIUtil.invokeAndWaitIfNeeded(new Runnable() {
831 windowRef.set(context == null ? new EditorWindow(EditorsSplitters.this) : findWindowWith(context));
834 final EditorWindow window = windowRef.get();
835 LOG.assertTrue(window != null);
836 VirtualFile focusedFile = null;
838 for (int i = 0; i < fileElements.size(); i++) {
839 final Element file = fileElements.get(i);
841 final FileEditorManagerImpl fileEditorManager = getManager();
842 Element historyElement = file.getChild(HistoryEntry.TAG);
843 VirtualFile virtualFile = HistoryEntry.getVirtualFile(historyElement);
844 Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
845 final HistoryEntry entry = new HistoryEntry(fileEditorManager.getProject(), historyElement);
846 final boolean isCurrentInTab = Boolean.valueOf(file.getAttributeValue(CURRENT_IN_TAB)).booleanValue();
847 Boolean pin = Boolean.valueOf(file.getAttributeValue(PINNED));
848 fileEditorManager.openFileImpl4(window, entry.myFile, entry, isCurrentInTab, isCurrentInTab, pin, i);
849 if (isCurrentInTab) {
850 focusedFile = entry.myFile;
852 if (document != null) {
853 // This is just to make sure document reference is kept on stack till this point
854 // so that document is available for folding state deserialization in HistoryEntry constructor
855 // and that document will be created only once during file opening
856 document.putUserData(DUMMY_KEY, null);
860 catch (InvalidDataException e) {
861 if (ApplicationManager.getApplication().isUnitTestMode()) {
866 if (focusedFile != null) {
867 getManager().addSelectionRecord(focusedFile, window);
869 return window.myPanel;
873 protected JPanel processSplitter(@NotNull Element splitterElement, Element firstChild, Element secondChild, final JPanel context) {
874 if (context == null) {
875 final boolean orientation = "vertical".equals(splitterElement.getAttributeValue("split-orientation"));
876 final float proportion = Float.valueOf(splitterElement.getAttributeValue("split-proportion")).floatValue();
877 final JPanel firstComponent = process(firstChild, null);
878 final JPanel secondComponent = process(secondChild, null);
879 final Ref<JPanel> panelRef = new Ref<JPanel>();
880 UIUtil.invokeAndWaitIfNeeded(new Runnable() {
883 JPanel panel = new JPanel(new BorderLayout());
884 panel.setOpaque(false);
885 Splitter splitter = new Splitter(orientation, proportion, 0.1f, 0.9f);
886 panel.add(splitter, BorderLayout.CENTER);
887 splitter.setFirstComponent(firstComponent);
888 splitter.setSecondComponent(secondComponent);
892 return panelRef.get();
894 final Ref<JPanel> firstComponent = new Ref<JPanel>();
895 final Ref<JPanel> secondComponent = new Ref<JPanel>();
896 UIUtil.invokeAndWaitIfNeeded(new Runnable() {
899 if (context.getComponent(0) instanceof Splitter) {
900 Splitter splitter = (Splitter)context.getComponent(0);
901 firstComponent.set((JPanel)splitter.getFirstComponent());
902 secondComponent.set((JPanel)splitter.getSecondComponent());
905 firstComponent.set(context);
906 secondComponent.set(context);
910 process(firstChild, firstComponent.get());
911 process(secondChild, secondComponent.get());