com.intellij.ui.popup.util.MasterDetailPopupBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.ui.popup.util.MasterDetailPopupBuilder.java

Source

/*
 * Copyright 2000-2012 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.popup.util;

import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.*;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.ui.*;
import com.intellij.ui.components.JBList;
import com.intellij.ui.speedSearch.FilteringListModel;
import com.intellij.util.Function;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.*;

public class MasterDetailPopupBuilder implements MasterController {

    private static final Color BORDER_COLOR = Gray._135;
    private final Project myProject;
    private ActionGroup myActions;
    private Delegate myDelegate;
    private boolean myCloseOnEnter;

    private DetailView myDetailView;

    private JLabel myPathLabel;

    private JBPopup myPopup;
    private JComponent myChooserComponent;
    private ActionToolbar myActionToolbar;
    private boolean myAddDetailViewToEast = true;
    private Dimension myMinSize;
    private boolean myCancelOnWindowDeactivation = true;
    private Runnable myDoneRunnable;
    private boolean myCancelOnClickOutside;

    private final DetailController myDetailController = new DetailController(this);

    private boolean myUseDimensionServiceForXYLocation;

    public void setUseDimensionServiceForXYLocation(boolean useDimensionServiceForXYLocation) {
        myUseDimensionServiceForXYLocation = useDimensionServiceForXYLocation;
    }

    public String getDimensionServiceKey() {
        return myDimensionServiceKey;
    }

    public void setDimensionServiceKey(String dimensionServiceKey) {
        myDimensionServiceKey = dimensionServiceKey;
    }

    private String myDimensionServiceKey;

    public MasterDetailPopupBuilder setDetailView(DetailView detailView) {
        myDetailView = detailView;
        myDetailController.setDetailView(myDetailView);
        return this;
    }

    public ActionToolbar getActionToolbar() {
        return myActionToolbar;
    }

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

    public JBPopup createMasterDetailPopup() {

        setupRenderer();

        myPathLabel = new JLabel(" ");
        myPathLabel.setHorizontalAlignment(SwingConstants.RIGHT);

        final Font font = myPathLabel.getFont();
        myPathLabel.setFont(font.deriveFont((float) 10));

        if (myDetailView == null) {
            myDetailView = new DetailViewImpl(myProject);
        }

        JPanel footerPanel = new JPanel(new BorderLayout()) {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.setColor(BORDER_COLOR);
                g.drawLine(0, 0, getWidth(), 0);
            }
        };

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new Runnable() {
                    @Override
                    public void run() {
                        Object[] values = getSelectedItems();
                        if (values.length == 1) {
                            myDelegate.itemChosen((ItemWrapper) values[0], myProject, myPopup, false);
                        } else {
                            for (Object value : values) {
                                if (value instanceof ItemWrapper) {
                                    myDelegate.itemChosen((ItemWrapper) value, myProject, myPopup, false);
                                }
                            }
                        }
                    }
                });
            }
        };

        footerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
        footerPanel.add(myPathLabel);

        JComponent toolBar = null;
        if (myActions != null) {
            myActionToolbar = ActionManager.getInstance().createActionToolbar("", myActions, true);
            myActionToolbar.setReservePlaceAutoPopupIcon(false);
            myActionToolbar.setMinimumButtonSize(new Dimension(20, 20));
            toolBar = myActionToolbar.getComponent();
            toolBar.setOpaque(false);
        }

        final PopupChooserBuilder builder = createInnerBuilder().setMovable(true).setResizable(true)
                .setAutoselectOnMouseMove(false).setSettingButton(toolBar).setSouthComponent(footerPanel)
                .setCancelOnWindowDeactivation(myCancelOnWindowDeactivation)
                .setCancelOnClickOutside(myCancelOnClickOutside)
                .setUseDimensionServiceForXYLocation(myUseDimensionServiceForXYLocation);

        if (myDoneRunnable != null) {

            ActionListener actionListener = new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent event) {
                    myDoneRunnable.run();
                }
            };
            //native button is pretty enough
            if ((SystemInfo.isMacOSLion || SystemInfo.isMacOSMountainLion) && !UIUtil.isUnderDarcula()) {
                final JButton done = new JButton("Done");
                done.setMnemonic('o');
                done.addActionListener(actionListener);

                builder.setCommandButton(new ActiveComponent() {
                    @Override
                    public void setActive(boolean active) {
                    }

                    @Override
                    public JComponent getComponent() {
                        return done;
                    }
                });
            } else {
                builder.setCommandButton(new InplaceButton(
                        new IconButton("Close", AllIcons.Actions.CloseNew, AllIcons.Actions.CloseNewHovered),
                        actionListener));
            }
        }

        String title = myDelegate.getTitle();
        if (title != null) {
            builder.setTitle(title);
        }

        builder.setItemChoosenCallback(runnable).setCloseOnEnter(myCloseOnEnter).setMayBeParent(true)
                .setDimensionServiceKey(myDimensionServiceKey).setFilteringEnabled(new Function<Object, String>() {
                    @Override
                    public String fun(Object o) {
                        return ((ItemWrapper) o).speedSearchText();
                    }
                });

        if (myMinSize != null) {
            builder.setMinSize(myMinSize);
        }

        myPopup = builder.createPopup();
        builder.getScrollPane().setBorder(IdeBorderFactory.createBorder(SideBorder.RIGHT));
        myPopup.addListener(new JBPopupListener() {
            @Override
            public void beforeShown(LightweightWindowEvent event) {
            }

            @Override
            public void onClosed(LightweightWindowEvent event) {
                myDetailView.clearEditor();
            }
        });

        if (myDoneRunnable != null) {
            new AnAction("Done") {
                @Override
                public void actionPerformed(AnActionEvent e) {
                    myDoneRunnable.run();
                }
            }.registerCustomShortcutSet(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK, myPopup.getContent());
        }

        return myPopup;
    }

    private void setupRenderer() {
        if (myChooserComponent instanceof JList) {
            final JList list = (JList) myChooserComponent;
            list.setCellRenderer(new ListItemRenderer(myDelegate, myProject));
        }
    }

    private PopupChooserBuilder createInnerBuilder() {
        if (myChooserComponent instanceof JList) {
            return new MyPopupChooserBuilder((JList) myChooserComponent);
        } else if (myChooserComponent instanceof JTree) {
            return new MyPopupChooserBuilder((JTree) myChooserComponent);
        }
        return null;
    }

    @Override
    public ItemWrapper[] getSelectedItems() {
        Object[] values = new Object[0];
        if (myChooserComponent instanceof JList) {
            values = ((JList) myChooserComponent).getSelectedValues();
        } else if (myChooserComponent instanceof JTree) {
            values = myDelegate.getSelectedItemsInTree();
        }
        ItemWrapper[] items = new ItemWrapper[values.length];
        for (int i = 0; i < values.length; i++) {
            items[i] = (ItemWrapper) values[i];
        }
        return items;
    }

    public void setAddDetailViewToEast(boolean addDetailViewToEast) {
        myAddDetailViewToEast = addDetailViewToEast;
    }

    public MasterDetailPopupBuilder setMinSize(Dimension minSize) {
        myMinSize = minSize;
        return this;
    }

    public MasterDetailPopupBuilder setCancelOnWindowDeactivation(boolean cancelOnWindowDeactivation) {
        myCancelOnWindowDeactivation = cancelOnWindowDeactivation;
        return this;
    }

    public MasterDetailPopupBuilder setDoneRunnable(Runnable doneRunnable) {
        myDoneRunnable = doneRunnable;
        return this;
    }

    public void setCancelOnClickOutside(boolean cancelOnClickOutside) {
        myCancelOnClickOutside = cancelOnClickOutside;
    }

    @Override
    public JLabel getPathLabel() {
        return myPathLabel;
    }

    public static boolean allowedToRemoveItems(Object[] values) {
        for (Object value : values) {
            ItemWrapper item = (ItemWrapper) value;
            if (!item.allowedToRemove()) {
                return false;
            }
        }
        return values.length > 0;
    }

    public void removeSelectedItems(Project project) {
        if (myChooserComponent instanceof JList) {
            final JList list = (JList) myChooserComponent;
            int index = list.getSelectedIndex();
            if (index == -1 || index >= list.getModel().getSize()) {
                return;
            }
            Object[] values = list.getSelectedValues();
            for (Object value : values) {
                ItemWrapper item = (ItemWrapper) value;

                DefaultListModel model = list.getModel() instanceof DefaultListModel
                        ? (DefaultListModel) list.getModel()
                        : (DefaultListModel) ((FilteringListModel) list.getModel()).getOriginalModel();
                if (item.allowedToRemove()) {
                    model.removeElement(item);

                    if (model.getSize() > 0) {
                        if (model.getSize() == index) {
                            list.setSelectedIndex(model.getSize() - 1);
                        } else if (model.getSize() > index) {
                            list.setSelectedIndex(index);
                        }
                    } else {
                        list.clearSelection();
                    }
                    item.removed(project);
                }
            }
        } else {
            myDelegate.removeSelectedItemsInTree();
        }
    }

    public MasterDetailPopupBuilder setActionsGroup(@Nullable ActionGroup actions) {
        myActions = actions;
        return this;
    }

    public MasterDetailPopupBuilder setList(final JBList list) {
        setChooser(list);
        myDetailController.setList(list);
        list.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent event) {
                myDetailController.updateDetailView();
            }
        });
        return this;
    }

    private void setChooser(JComponent list) {
        myChooserComponent = list;
        list.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_DELETE) {
                    removeSelectedItems(myProject);
                } else if (e.getModifiersEx() == 0) {
                    myDelegate.handleMnemonic(e, myProject, myPopup);
                }
            }
        });
        new AnAction() {
            @Override
            public void actionPerformed(AnActionEvent e) {
                chooseItemWithEnterOrDoubleClick();
            }
        }.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)), list);
        new AnAction() {
            @Override
            public void actionPerformed(AnActionEvent e) {
                chooseItemWithEnterOrDoubleClick();
            }
        }.registerCustomShortcutSet(CommonShortcuts.DOUBLE_CLICK_1, list);
    }

    private void chooseItemWithEnterOrDoubleClick() {
        ItemWrapper[] items = getSelectedItems();
        if (items.length > 0) {
            myDelegate.itemChosen(items[0], myProject, myPopup, true);
        }
    }

    public MasterDetailPopupBuilder setDelegate(Delegate delegate) {
        myDelegate = delegate;
        return this;
    }

    public MasterDetailPopupBuilder setCloseOnEnter(boolean closeOnEnter) {
        myCloseOnEnter = closeOnEnter;
        return this;
    }

    public interface Delegate {
        @Nullable
        String getTitle();

        void handleMnemonic(KeyEvent e, Project project, JBPopup popup);

        @Nullable
        JComponent createAccessoryView(Project project);

        Object[] getSelectedItemsInTree();

        void itemChosen(ItemWrapper item, Project project, JBPopup popup, boolean withEnterOrDoubleClick);

        void removeSelectedItemsInTree();
    }

    public static class ListItemRenderer extends JPanel implements ListCellRenderer {
        private final Project myProject;
        private final ColoredListCellRenderer myRenderer;
        private final Delegate myDelegate;

        private ListItemRenderer(Delegate delegate, Project project) {
            super(new BorderLayout());
            myProject = project;
            setBackground(UIUtil.getListBackground());
            this.myDelegate = delegate;
            final JComponent accessory = myDelegate.createAccessoryView(project);

            if (accessory != null) {
                add(accessory, BorderLayout.WEST);
            }

            myRenderer = new ItemWrapperListRenderer(myProject, accessory);
            add(myRenderer, BorderLayout.CENTER);
        }

        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                boolean cellHasFocus) {
            if (value instanceof SplitterItem) {
                String label = ((SplitterItem) value).getText();
                final TitledSeparator separator = new TitledSeparator(label);
                separator.setBackground(UIUtil.getListBackground());
                separator.setForeground(UIUtil.getListForeground());
                return separator;
            }
            myRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            myRenderer.revalidate();

            return this;
        }
    }

    private class MyPopupChooserBuilder extends PopupChooserBuilder {
        public MyPopupChooserBuilder(@NotNull JList list) {
            super(list);
        }

        private MyPopupChooserBuilder(@NotNull JTree tree) {
            super(tree);
        }

        @Override
        protected void addCenterComponentToContentPane(JPanel contentPane, JComponent component) {
            if (myAddDetailViewToEast) {
                JBSplitter splitPane = new JBSplitter(0.3f);
                splitPane.setSplitterProportionKey(getSplitterProportionKey());
                splitPane.setFirstComponent(component);
                splitPane.setSecondComponent((JComponent) myDetailView);

                contentPane.add(splitPane, BorderLayout.CENTER);
            } else {
                super.addCenterComponentToContentPane(contentPane, component);
            }
        }
    }

    private String getSplitterProportionKey() {
        return myDimensionServiceKey + ".splitter";
    }
}