com.intellij.ui.docking.impl.DockManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.ui.docking.impl.DockManagerImpl.java

Source

/*
 * Copyright 2000-2013 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.ui.docking.impl;

import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.ui.UISettings;
import com.intellij.ide.ui.UISettingsListener;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorProvider;
import com.intellij.openapi.fileEditor.impl.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.FrameWrapper;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.IdeRootPaneNorthExtension;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.ex.WindowManagerEx;
import com.intellij.ui.ScreenUtil;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.awt.RelativeRectangle;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.ui.components.panels.VerticalBox;
import com.intellij.ui.docking.*;
import com.intellij.ui.tabs.impl.JBTabsImpl;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.update.UiNotifyConnector;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.util.*;
import java.util.List;

@State(name = "DockManager", storages = { @Storage(file = StoragePathMacros.WORKSPACE_FILE) })

public class DockManagerImpl extends DockManager implements PersistentStateComponent<Element> {

    private final Project myProject;

    private final Map<String, DockContainerFactory> myFactories = new HashMap<String, DockContainerFactory>();

    private final Set<DockContainer> myContainers = new HashSet<DockContainer>();

    private final MutualMap<DockContainer, DockWindow> myWindows = new MutualMap<DockContainer, DockWindow>();

    private MyDragSession myCurrentDragSession;

    private final BusyObject.Impl myBusyObject = new BusyObject.Impl() {
        @Override
        public boolean isReady() {
            return myCurrentDragSession == null;
        }
    };

    private int myWindowIdCounter = 1;

    private Element myLoadedState;

    public DockManagerImpl(Project project) {
        myProject = project;
    }

    public void register(final DockContainer container) {
        myContainers.add(container);
        Disposer.register(container, new Disposable() {
            @Override
            public void dispose() {
                myContainers.remove(container);
            }
        });
    }

    @Override
    public void register(final String id, DockContainerFactory factory) {
        myFactories.put(id, factory);
        Disposer.register(factory, new Disposable() {
            @Override
            public void dispose() {
                myFactories.remove(id);
            }
        });

        readStateFor(id);
    }

    @Override
    public Set<DockContainer> getContainers() {
        return Collections.unmodifiableSet(myContainers);
    }

    @Override
    public IdeFrame getIdeFrame(DockContainer container) {
        Component parent = UIUtil.findUltimateParent(container.getContainerComponent());
        if (parent instanceof IdeFrame) {
            return (IdeFrame) parent;
        }
        return null;
    }

    @Override
    public String getDimensionKeyForFocus(@NotNull String key) {
        Component owner = IdeFocusManager.getInstance(myProject).getFocusOwner();
        if (owner == null)
            return key;

        DockWindow wnd = myWindows.getValue(getContainerFor(owner));

        return wnd != null ? key + "#" + wnd.myId : key;
    }

    public DockContainer getContainerFor(Component c) {
        if (c == null)
            return null;

        for (DockContainer eachContainer : myContainers) {
            if (SwingUtilities.isDescendingFrom(c, eachContainer.getContainerComponent())) {
                return eachContainer;
            }
        }

        Component parent = UIUtil.findUltimateParent(c);
        if (parent == null)
            return null;

        for (DockContainer eachContainer : myContainers) {
            if (parent == UIUtil.findUltimateParent(eachContainer.getContainerComponent())) {
                return eachContainer;
            }
        }

        return null;
    }

    @Override
    public DragSession createDragSession(MouseEvent mouseEvent, @NotNull DockableContent content) {
        stopCurrentDragSession();

        for (DockContainer each : myContainers) {
            if (each.isEmpty() && each.isDisposeWhenEmpty()) {
                DockWindow window = myWindows.getValue(each);
                if (window != null) {
                    window.setTransparent(true);
                }
            }
        }

        myCurrentDragSession = new MyDragSession(mouseEvent, content);
        return myCurrentDragSession;
    }

    public void stopCurrentDragSession() {
        if (myCurrentDragSession != null) {
            myCurrentDragSession.cancelSession();
            myCurrentDragSession = null;
            myBusyObject.onReady();

            for (DockContainer each : myContainers) {
                if (!each.isEmpty()) {
                    DockWindow window = myWindows.getValue(each);
                    if (window != null) {
                        window.setTransparent(false);
                    }
                }
            }
        }
    }

    private ActionCallback getReady() {
        return myBusyObject.getReady(this);
    }

    private class MyDragSession implements DragSession {

        private final JWindow myWindow;

        private Image myDragImage;
        private final Image myDefaultDragImage;

        @NotNull
        private final DockableContent myContent;

        private DockContainer myCurrentOverContainer;
        private final JLabel myImageContainer;

        private MyDragSession(MouseEvent me, @NotNull DockableContent content) {
            myWindow = new JWindow();
            myContent = content;

            Image previewImage = content.getPreviewImage();

            double requiredSize = 220;

            double width = previewImage.getWidth(null);
            double height = previewImage.getHeight(null);

            double ratio;
            if (width > height) {
                ratio = requiredSize / width;
            } else {
                ratio = requiredSize / height;
            }

            BufferedImage buffer = UIUtil.createImage((int) width, (int) height, BufferedImage.TYPE_INT_ARGB);
            buffer.createGraphics().drawImage(previewImage, 0, 0, (int) width, (int) height, null);

            myDefaultDragImage = buffer.getScaledInstance((int) (width * ratio), (int) (height * ratio),
                    Image.SCALE_SMOOTH);
            myDragImage = myDefaultDragImage;

            myWindow.getContentPane().setLayout(new BorderLayout());
            myImageContainer = new JLabel(new ImageIcon(myDragImage));
            myImageContainer.setBorder(new LineBorder(Color.lightGray));
            myWindow.getContentPane().add(myImageContainer, BorderLayout.CENTER);

            setLocationFrom(me);

            myWindow.setVisible(true);

            WindowManagerEx.getInstanceEx().setAlphaModeEnabled(myWindow, true);
            WindowManagerEx.getInstanceEx().setAlphaModeRatio(myWindow, 0.1f);
            myWindow.getRootPane().putClientProperty("Window.shadow", Boolean.FALSE);
        }

        private void setLocationFrom(MouseEvent me) {
            Point showPoint = me.getPoint();
            SwingUtilities.convertPointToScreen(showPoint, me.getComponent());

            showPoint.x -= myDragImage.getWidth(null) / 2;
            showPoint.y += 10;
            myWindow.setBounds(new Rectangle(showPoint,
                    new Dimension(myDragImage.getWidth(null), myDragImage.getHeight(null))));
        }

        @NotNull
        @Override
        public DockContainer.ContentResponse getResponse(MouseEvent e) {
            RelativePoint point = new RelativePoint(e);
            for (DockContainer each : myContainers) {
                RelativeRectangle rec = each.getAcceptArea();
                if (rec.contains(point)) {
                    DockContainer.ContentResponse response = each.getContentResponse(myContent, point);
                    if (response.canAccept()) {
                        return response;
                    }
                }
            }
            return DockContainer.ContentResponse.DENY;
        }

        @Override
        public void process(MouseEvent e) {
            RelativePoint point = new RelativePoint(e);

            Image img = null;
            if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
                DockContainer over = findContainerFor(point, myContent);
                if (myCurrentOverContainer != null && myCurrentOverContainer != over) {
                    myCurrentOverContainer.resetDropOver(myContent);
                    myCurrentOverContainer = null;
                }

                if (myCurrentOverContainer == null && over != null) {
                    myCurrentOverContainer = over;
                    img = myCurrentOverContainer.startDropOver(myContent, point);
                }

                if (myCurrentOverContainer != null) {
                    img = myCurrentOverContainer.processDropOver(myContent, point);
                }

                if (img == null) {
                    img = myDefaultDragImage;
                }

                if (img != myDragImage) {
                    myDragImage = img;
                    myImageContainer.setIcon(new ImageIcon(myDragImage));
                    myWindow.pack();
                }

                setLocationFrom(e);
            } else if (e.getID() == MouseEvent.MOUSE_RELEASED) {
                if (myCurrentOverContainer == null) {
                    createNewDockContainerFor(myContent, point);
                    stopCurrentDragSession();
                } else {
                    myCurrentOverContainer.add(myContent, point);
                    stopCurrentDragSession();
                }
            }
        }

        @Override
        public void cancel() {
            stopCurrentDragSession();
        }

        private void cancelSession() {
            myWindow.dispose();

            if (myCurrentOverContainer != null) {
                myCurrentOverContainer.resetDropOver(myContent);
                myCurrentOverContainer = null;
            }
        }
    }

    @Nullable
    private DockContainer findContainerFor(RelativePoint point, @NotNull DockableContent content) {
        for (DockContainer each : myContainers) {
            RelativeRectangle rec = each.getAcceptArea();
            if (rec.contains(point) && each.getContentResponse(content, point).canAccept()) {
                return each;
            }
        }

        for (DockContainer each : myContainers) {
            RelativeRectangle rec = each.getAcceptAreaFallback();
            if (rec.contains(point) && each.getContentResponse(content, point).canAccept()) {
                return each;
            }
        }

        return null;
    }

    private DockContainerFactory getFactory(String type) {
        assert myFactories.containsKey(type) : "No factory for content type=" + type;
        return myFactories.get(type);
    }

    public void createNewDockContainerFor(DockableContent content, RelativePoint point) {
        DockContainer container = getFactory(content.getDockContainerType()).createContainer(content);
        register(container);

        final DockWindow window = createWindowFor(null, container);

        Dimension size = content.getPreferredSize();
        Point showPoint = point.getScreenPoint();
        showPoint.x -= size.width / 2;
        showPoint.y -= size.height / 2;

        Rectangle target = new Rectangle(showPoint, size);
        ScreenUtil.moveRectangleToFitTheScreen(target);
        ScreenUtil.cropRectangleToFitTheScreen(target);

        window.setLocation(target.getLocation());
        window.myDockContentUiContainer.setPreferredSize(target.getSize());

        window.show(false);
        window.getFrame().pack();

        container.add(content, new RelativePoint(target.getLocation()));

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                window.myUiContainer.setPreferredSize(null);
            }
        });
    }

    public Pair<FileEditor[], FileEditorProvider[]> createNewDockContainerFor(VirtualFile file,
            FileEditorManagerImpl fileEditorManager) {
        DockContainer container = getFactory(DockableEditorContainerFactory.TYPE).createContainer(null);
        register(container);

        final DockWindow window = createWindowFor(null, container);

        window.show(true);
        final EditorWindow editorWindow = ((DockableEditorTabbedContainer) container).getSplitters()
                .getOrCreateCurrentWindow(file);
        final Pair<FileEditor[], FileEditorProvider[]> result = fileEditorManager.openFileImpl2(editorWindow, file,
                true);
        container.add(EditorTabbedContainer.createDockableEditor(myProject, null, file,
                new Presentation(file.getName()), editorWindow), null);

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                window.myUiContainer.setPreferredSize(null);
            }
        });
        return result;
    }

    private DockWindow createWindowFor(@Nullable String id, DockContainer container) {
        String windowId = id != null ? id : String.valueOf(myWindowIdCounter++);
        DockWindow window = new DockWindow(windowId, myProject, container,
                container instanceof DockContainer.Dialog);
        window.setDimensionKey("dock-window-" + windowId);
        myWindows.put(container, window);
        return window;
    }

    private class DockWindow extends FrameWrapper implements IdeEventQueue.EventDispatcher {

        private final String myId;
        private final DockContainer myContainer;

        private final VerticalBox myNorthPanel = new VerticalBox();
        private final Map<String, IdeRootPaneNorthExtension> myNorthExtensions = new LinkedHashMap<String, IdeRootPaneNorthExtension>();

        private final NonOpaquePanel myUiContainer;
        private final NonOpaquePanel myDockContentUiContainer;

        private DockWindow(String id, Project project, DockContainer container, boolean dialog) {
            super(project, null, dialog);
            myId = id;
            myContainer = container;
            setProject(project);

            if (!(container instanceof DockContainer.Dialog)) {
                setStatusBar(WindowManager.getInstance().getStatusBar(project).createChild());
            }

            myUiContainer = new NonOpaquePanel(new BorderLayout());

            NonOpaquePanel center = new NonOpaquePanel(new BorderLayout(0, 2));
            if (UIUtil.isUnderAquaLookAndFeel()) {
                center.setOpaque(true);
                center.setBackground(JBTabsImpl.MAC_AQUA_BG_COLOR);
            }

            center.add(myNorthPanel, BorderLayout.NORTH);

            myDockContentUiContainer = new NonOpaquePanel(new BorderLayout());
            myDockContentUiContainer.add(myContainer.getContainerComponent(), BorderLayout.CENTER);
            center.add(myDockContentUiContainer, BorderLayout.CENTER);

            myUiContainer.add(center, BorderLayout.CENTER);
            if (!(container instanceof DockContainer.Dialog)) {
                myUiContainer.add(myStatusBar.getComponent(), BorderLayout.SOUTH);
            }

            setComponent(myUiContainer);
            addDisposable(container);

            IdeEventQueue.getInstance().addPostprocessor(this, this);

            myContainer.addListener(new DockContainer.Listener.Adapter() {
                @Override
                public void contentRemoved(Object key) {
                    getReady().doWhenDone(new Runnable() {
                        @Override
                        public void run() {
                            if (myContainer.isEmpty()) {
                                close();
                            }
                        }
                    });
                }
            }, this);

            UISettings.getInstance().addUISettingsListener(new UISettingsListener() {
                @Override
                public void uiSettingsChanged(UISettings source) {
                    updateNorthPanel();
                }
            }, this);

            updateNorthPanel();
        }

        @Override
        protected IdeRootPaneNorthExtension getNorthExtension(String key) {
            return myNorthExtensions.get(key);
        }

        private void updateNorthPanel() {
            myNorthPanel.setVisible(
                    UISettings.getInstance().SHOW_NAVIGATION_BAR && !(myContainer instanceof DockContainer.Dialog)
                            && !UISettings.getInstance().PRESENTATION_MODE);

            IdeRootPaneNorthExtension[] extensions = Extensions.getArea(myProject)
                    .getExtensionPoint(IdeRootPaneNorthExtension.EP_NAME).getExtensions();
            HashSet<String> processedKeys = new HashSet<String>();
            for (IdeRootPaneNorthExtension each : extensions) {
                processedKeys.add(each.getKey());
                if (myNorthExtensions.containsKey(each.getKey()))
                    continue;
                IdeRootPaneNorthExtension toInstall = each.copy();
                myNorthExtensions.put(toInstall.getKey(), toInstall);
                myNorthPanel.add(toInstall.getComponent());
            }

            Iterator<String> existing = myNorthExtensions.keySet().iterator();
            while (existing.hasNext()) {
                String each = existing.next();
                if (processedKeys.contains(each))
                    continue;

                IdeRootPaneNorthExtension toRemove = myNorthExtensions.get(each);
                myNorthPanel.remove(toRemove.getComponent());
                existing.remove();
                Disposer.dispose(toRemove);
            }

            myNorthPanel.revalidate();
            myNorthPanel.repaint();
        }

        public void setTransparent(boolean transparent) {
            if (transparent) {
                WindowManagerEx.getInstanceEx().setAlphaModeEnabled(getFrame(), true);
                WindowManagerEx.getInstanceEx().setAlphaModeRatio(getFrame(), 0.5f);
            } else {
                WindowManagerEx.getInstanceEx().setAlphaModeEnabled(getFrame(), true);
                WindowManagerEx.getInstanceEx().setAlphaModeRatio(getFrame(), 0f);
            }
        }

        @Override
        public void dispose() {
            super.dispose();
            myWindows.remove(myContainer);

            for (IdeRootPaneNorthExtension each : myNorthExtensions.values()) {
                Disposer.dispose(each);
            }
            myNorthExtensions.clear();
        }

        @Override
        public boolean dispatch(AWTEvent e) {
            if (e instanceof KeyEvent) {
                if (myCurrentDragSession != null) {
                    stopCurrentDragSession();
                }
            }
            return false;
        }

        @Override
        protected JFrame createJFrame(IdeFrame parent) {
            JFrame frame = super.createJFrame(parent);
            installListeners(frame);

            return frame;
        }

        @Override
        protected JDialog createJDialog(IdeFrame parent) {
            JDialog frame = super.createJDialog(parent);
            installListeners(frame);

            return frame;
        }

        private void installListeners(Window frame) {
            frame.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                    myContainer.closeAll();
                }
            });

            new UiNotifyConnector(((RootPaneContainer) frame).getContentPane(), myContainer);
        }
    }

    @Override
    public Element getState() {
        Element root = new Element("DockManager");
        for (DockContainer each : myContainers) {
            DockWindow eachWindow = myWindows.getValue(each);
            if (eachWindow != null) {
                if (each instanceof DockContainer.Persistent) {
                    DockContainer.Persistent eachContainer = (DockContainer.Persistent) each;
                    Element eachWindowElement = new Element("window");
                    eachWindowElement.setAttribute("id", eachWindow.myId);
                    Element content = new Element("content");
                    content.setAttribute("type", eachContainer.getDockContainerType());
                    content.addContent(eachContainer.getState());
                    eachWindowElement.addContent(content);

                    root.addContent(eachWindowElement);
                }
            }
        }
        return root;
    }

    @Override
    public void loadState(Element state) {
        myLoadedState = state;
    }

    private void readStateFor(String type) {
        if (myLoadedState == null)
            return;

        List windows = myLoadedState.getChildren("window");
        for (Object window1 : windows) {
            Element eachWindow = (Element) window1;
            if (eachWindow == null)
                continue;

            String eachId = eachWindow.getAttributeValue("id");

            Element eachContent = eachWindow.getChild("content");
            if (eachContent == null)
                continue;

            String eachType = eachContent.getAttributeValue("type");
            if (eachType == null || !type.equals(eachType) || !myFactories.containsKey(eachType))
                continue;

            DockContainerFactory factory = myFactories.get(eachType);
            if (!(factory instanceof DockContainerFactory.Persistent))
                continue;

            DockContainerFactory.Persistent persistentFactory = (DockContainerFactory.Persistent) factory;
            DockContainer container = persistentFactory.loadContainerFrom(eachContent);
            register(container);

            final DockWindow window = createWindowFor(eachId, container);
            UIUtil.invokeLaterIfNeeded(new Runnable() {
                @Override
                public void run() {
                    window.show();
                }
            });
        }
    }
}