Search code examples
javaintellij-ideaintellij-plugin

Retrieving and setting split window settings for IntelliJ IDEA plugin development


I am writing an IntelliJ IDEA plugin for saving sessions of open tabs called Tab Session. This question is a follow-up of IntelliJ IDEA Plugin Development: Save groups of tabs, save them persistently and reload a set of tabs if requested by the user.

Currently, splitted windows are not supported. Therefore i want to do two things:

  1. Retrieve information about all splitted or unsplitted windows that are containers for editor tabs. I need their position and split direction (horizontal or vertical).
  2. When this information is saved and a tab session needs to be loaded, i need to reconstruct the splitted panes and their tabs exactly as they were before.

Due to the lack of documentation i am currently browsing through the source code and found this promising piece of code:

private EditorsSplitters getSplittersFromFocus() {
  return FileEditorManagerEx.getInstanceEx(myProject).getSplitters();
}

It allows me to iterate through the set of splitted windows by using EditorWindow[] windows = getSplittersFromFocus.getOrderedWindows(). They contain the editor tabs and information about their width and height. But i did not find any information about the split direction and how to reconstruct the splitted windows as they were before.

Can anyone help?


Solution

  • This is untested code, but as it closely resmbles the procedures inside EditorsSplitters writeExternal and writePanel functions I am positive this will work.

    Presented are two methods:

    • access output of writeExternal -> should be the more stable API and offers easier access to file information
    • access components of splitter -> this way writeExternal creates it's information; sadly there is at least one protected field without getter involved (window.myPanel inside findWindowWith)
    import com.intellij.openapi.actionSystem.AnAction;
    import com.intellij.openapi.actionSystem.AnActionEvent;
    import com.intellij.openapi.fileEditor.FileEditorManager;
    import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
    import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl;
    import com.intellij.openapi.project.Project;
    import com.intellij.openapi.ui.Splitter;
    import org.jdom.Element;
    
    import javax.swing.*;
    import java.awt.*;
    import java.util.ArrayList;
    
    public class SplitterAction extends AnAction {
    
        public SplitterAction() {
            super("Splitter _Action");
        }
    
        private static class Info {
    
        }
    
        private static class SplitInfo extends Info {
            public Info    first;
            public Info    second;
            public boolean vertical;
            public float   proportions;
        }
    
        private static class FileInfo extends Info {
            public String[] fileNames;
        }
    
        @Override
        public void actionPerformed(AnActionEvent anActionEvent) {
            final Project project = anActionEvent.getProject();
            final FileEditorManagerImpl fileEditorManager = (FileEditorManagerImpl) FileEditorManager.getInstance(project);
            EditorsSplitters splitters = fileEditorManager.getSplitters();
            // com.intellij.openapi.fileEditor.impl.EditorsSplitters.writeExternal() and
            // com.intellij.openapi.fileEditor.impl.EditorsSplitters#writePanel inspired this
            final Component component = splitters.getComponent(0);
            final SplitInfo infos = splitterVisitor(component);
    
            // or you could use this
            Element root = new Element("root");
            splitters.writeExternal(root);
    
            elementVisitor(root);
    
            // to restore from writeExternal the following should suffice
            splitters.readExternal(root);
            splitters.openFiles();
        }
    
        /**
         * Reads writeExternal output
         */
        private Info elementVisitor(Element root) {
            final Element splitter = root.getChild("splitter");
            if (splitter != null) {
                // see com.intellij.openapi.fileEditor.impl.EditorsSplitters#writePanel
                final SplitInfo splitInfo = new SplitInfo();
                // "vertical" or "horizontal"
                splitInfo.vertical = "vertical".equals(splitter.getAttributeValue("split-orientation"));
                splitInfo.proportions = Float.parseFloat(splitter.getAttributeValue("split-proportion"));
                Element first = splitter.getChild("split-first");
                if (first != null) {
                    splitInfo.first = elementVisitor(first);
                }
                Element second = splitter.getChild("split-second");
                if (second != null) {
                    splitInfo.second = elementVisitor(second);
                }
                return splitInfo;
            }
            final Element leaf = root.getChild("leaf");
            if (leaf != null) {
                final ArrayList<String> fileNames = new ArrayList<String>();
                for (Element file : leaf.getChildren("file")) {
                    final String fileName = file.getAttributeValue("leaf-file-name");
                    fileNames.add(fileName);
                    // further attributes see com.intellij.openapi.fileEditor.impl.EditorsSplitters#writeComposite
                }
                final FileInfo fileInfo = new FileInfo();
                fileInfo.fileNames = fileNames.toArray(new String[fileNames.size()]);
                return fileInfo;
            }
            return null;
        }
    
        /**
         * Acts directly upon Component
         */
        private SplitInfo splitterVisitor(Component component) {
            if (component instanceof JPanel && ((JPanel) component).getComponentCount() > 0) {
                final Component child = ((JPanel) component).getComponent(0);
                if (child instanceof Splitter) {
                    final Splitter splitter = (Splitter) child;
                    final SplitInfo splitInfos = new SplitInfo();
                    splitInfos.vertical = splitter.isVertical();
                    splitInfos.proportions = splitter.getProportion();
                    splitInfos.first = splitterVisitor(splitter.getFirstComponent());
                    splitInfos.second = splitterVisitor(splitter.getSecondComponent());
                    return splitInfos;
                }
                // TODO: retrieve file information
            }
            return null;
        }
    }