com.intellij.openapi.actionSystem.ex.ComboBoxAction.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.openapi.actionSystem.ex.ComboBoxAction.java

Source

// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.actionSystem.ex;

import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.ide.HelpTooltip;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.ui.UserActivityProviderComponent;
import com.intellij.ui.scale.JBUIScale;
import com.intellij.util.ui.*;
import com.intellij.util.ui.accessibility.ScreenReader;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Path2D;

public abstract class ComboBoxAction extends AnAction implements CustomComponentAction {
    private static Icon myIcon;
    private static Icon myDisabledIcon;

    public static Icon getArrowIcon(boolean enabled) {
        if (myIcon != AllIcons.General.ArrowDown) {
            myIcon = UIManager.getIcon("ComboBoxButton.arrowIcon");
            myDisabledIcon = UIManager.getIcon("ComboBoxButton.arrowIconDisabled");

            if (myIcon == null)
                myIcon = AllIcons.General.ArrowDown;
            if (myDisabledIcon == null)
                myDisabledIcon = IconLoader.getDisabledIcon(AllIcons.General.ArrowDown);
        }
        return enabled ? myIcon : myDisabledIcon;
    }

    private boolean mySmallVariant = true;
    private String myPopupTitle;

    protected ComboBoxAction() {
    }

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Project project = e.getProject();
        if (project == null)
            return;

        JFrame frame = WindowManager.getInstance().getFrame(project);
        if (!(frame instanceof IdeFrame))
            return;

        ListPopup popup = createActionPopup(e.getDataContext(), ((IdeFrame) frame).getComponent(), null);
        popup.showCenteredInCurrentWindow(project);
    }

    @NotNull
    private ListPopup createActionPopup(@NotNull DataContext context, @NotNull JComponent component,
            @Nullable Runnable disposeCallback) {
        DefaultActionGroup group = createPopupActionGroup(component, context);
        ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(myPopupTitle, group, context, false,
                shouldShowDisabledActions(), false, disposeCallback, getMaxRows(), getPreselectCondition());
        popup.setMinimumSize(new Dimension(getMinWidth(), getMinHeight()));
        return popup;
    }

    /** @deprecated use {@link ComboBoxAction#createCustomComponent(Presentation, String)} */
    @Deprecated
    @NotNull
    @Override
    public JComponent createCustomComponent(@NotNull Presentation presentation) {
        return createCustomComponent(presentation, ActionPlaces.UNKNOWN);
    }

    @NotNull
    @Override
    public JComponent createCustomComponent(@NotNull Presentation presentation, @NotNull String place) {
        JPanel panel = new JPanel(new GridBagLayout());
        ComboBoxButton button = createComboBoxButton(presentation);
        GridBagConstraints constraints = new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER,
                GridBagConstraints.BOTH, JBInsets.create(0, 3), 0, 0);
        panel.add(button, constraints);
        return panel;
    }

    protected ComboBoxButton createComboBoxButton(Presentation presentation) {
        return new ComboBoxButton(presentation);
    }

    public boolean isSmallVariant() {
        return mySmallVariant;
    }

    public void setSmallVariant(boolean smallVariant) {
        mySmallVariant = smallVariant;
    }

    public void setPopupTitle(String popupTitle) {
        myPopupTitle = popupTitle;
    }

    protected boolean shouldShowDisabledActions() {
        return false;
    }

    @NotNull
    protected abstract DefaultActionGroup createPopupActionGroup(JComponent button);

    @NotNull
    protected DefaultActionGroup createPopupActionGroup(JComponent button, @NotNull DataContext dataContext) {
        return createPopupActionGroup(button);
    }

    protected int getMaxRows() {
        return 30;
    }

    protected int getMinHeight() {
        return 1;
    }

    protected int getMinWidth() {
        return 1;
    }

    protected class ComboBoxButton extends JButton implements UserActivityProviderComponent {
        private final Presentation myPresentation;
        private boolean myForcePressed;
        private String myTooltipText;

        public ComboBoxButton(Presentation presentation) {
            myPresentation = presentation;

            setIcon(myPresentation.getIcon());
            setText(myPresentation.getText());
            setEnabled(myPresentation.isEnabled());

            myTooltipText = myPresentation.getDescription();
            updateTooltipText();

            setModel(new MyButtonModel());
            getModel().setEnabled(myPresentation.isEnabled());
            setVisible(presentation.isVisible());
            setHorizontalAlignment(LEFT);
            setFocusable(ScreenReader.isActive());
            putClientProperty("styleCombo", ComboBoxAction.this);
            setMargin(JBUI.insets(0, 5, 0, 2));
            if (isSmallVariant()) {
                setFont(JBUI.Fonts.toolbarSmallComboBoxFont());
            }

            addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(final MouseEvent e) {
                    if (SwingUtilities.isLeftMouseButton(e)) {
                        e.consume();
                        if (e.isShiftDown()) {
                            doShiftClick();
                        } else {
                            doClick();
                        }
                    }
                }
            });
            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    mouseMoved(MouseEventAdapter.convert(e, e.getComponent(), MouseEvent.MOUSE_MOVED, e.getWhen(),
                            e.getModifiers() | e.getModifiersEx(), e.getX(), e.getY()));
                }
            });

            myPresentation.addPropertyChangeListener(evt -> {
                String propertyName = evt.getPropertyName();
                if (Presentation.PROP_TEXT.equals(propertyName)) {
                    setText((String) evt.getNewValue());
                } else if (Presentation.PROP_DESCRIPTION.equals(propertyName)) {
                    myTooltipText = (String) evt.getNewValue();
                    updateTooltipText();
                } else if (Presentation.PROP_ICON.equals(propertyName)) {
                    setIcon((Icon) evt.getNewValue());
                } else if (Presentation.PROP_ENABLED.equals(propertyName)) {
                    setEnabled((Boolean) evt.getNewValue());
                }
            });
        }

        @Override
        protected void fireActionPerformed(ActionEvent event) {
            if (!myForcePressed) {
                IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> showPopup());
            }
        }

        @NotNull
        private Runnable setForcePressed() {
            myForcePressed = true;
            repaint();

            return () -> {
                // give the button a chance to handle action listener
                ApplicationManager.getApplication().invokeLater(() -> {
                    myForcePressed = false;
                    repaint();
                }, ModalityState.any());
                repaint();
                fireStateChanged();
            };
        }

        @Nullable
        @Override
        public String getToolTipText() {
            return myForcePressed || Registry.is("ide.helptooltip.enabled") ? null : super.getToolTipText();
        }

        public void showPopup() {
            JBPopup popup = createPopup(setForcePressed());
            if (Registry.is("ide.helptooltip.enabled")) {
                HelpTooltip.setMasterPopup(this, popup);
            }

            popup.showUnderneathOf(this);
        }

        protected JBPopup createPopup(Runnable onDispose) {
            return createActionPopup(getDataContext(), this, onDispose);
        }

        protected DataContext getDataContext() {
            return DataManager.getInstance().getDataContext(this);
        }

        @Override
        public void removeNotify() {
            HelpTooltip.dispose(this);
            super.removeNotify();
        }

        @Override
        public void addNotify() {
            super.addNotify();
            updateTooltipText();
        }

        private void updateTooltipText() {
            String tooltip = KeymapUtil.createTooltipText(myTooltipText, ComboBoxAction.this);
            if (Registry.is("ide.helptooltip.enabled") && StringUtil.isNotEmpty(tooltip)) {
                HelpTooltip.dispose(this);
                new HelpTooltip().setDescription(tooltip).installOn(this);
            } else {
                setToolTipText(!tooltip.isEmpty() ? tooltip : null);
            }
        }

        protected class MyButtonModel extends DefaultButtonModel {
            @Override
            public boolean isPressed() {
                return myForcePressed || super.isPressed();
            }

            @Override
            public boolean isArmed() {
                return myForcePressed || super.isArmed();
            }
        }

        @Override
        public boolean isOpaque() {
            return !isSmallVariant();
        }

        @Override
        public Dimension getPreferredSize() {
            Dimension prefSize = super.getPreferredSize();
            int width = prefSize.width + (StringUtil.isNotEmpty(getText()) ? getIconTextGap() : 0)
                    + (myPresentation == null || !isArrowVisible(myPresentation) ? 0 : JBUIScale.scale(16));

            Dimension size = new Dimension(width,
                    isSmallVariant() ? JBUIScale.scale(24) : Math.max(JBUIScale.scale(24), prefSize.height));
            JBInsets.addTo(size, getMargin());
            return size;
        }

        @Override
        public Dimension getMinimumSize() {
            return new Dimension(super.getMinimumSize().width, getPreferredSize().height);
        }

        @Override
        public Font getFont() {
            return isSmallVariant() ? UIUtil.getToolbarFont() : UIUtil.getLabelFont();
        }

        @Override
        protected Graphics getComponentGraphics(Graphics graphics) {
            return JBSwingUtilities.runGlobalCGTransform(this, super.getComponentGraphics(graphics));
        }

        @Override
        public void paint(Graphics g) {
            super.paint(g);
            if (!isArrowVisible(myPresentation)) {
                return;
            }

            if (UIUtil.isUnderWin10LookAndFeel()) {
                Icon icon = getArrowIcon(isEnabled());
                int x = getWidth() - icon.getIconWidth() - getInsets().right - getMargin().right
                        - JBUIScale.scale(3);
                int y = (getHeight() - icon.getIconHeight()) / 2;
                icon.paintIcon(null, g, x, y);
            } else {
                Graphics2D g2 = (Graphics2D) g.create();
                try {
                    int iconSize = JBUIScale.scale(16);
                    int x = getWidth() - iconSize - getInsets().right - getMargin().right; // Different icons correction
                    int y = (getHeight() - iconSize) / 2;

                    g2.translate(x, y);
                    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                    g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);

                    g2.setColor(JBUI.CurrentTheme.Arrow.foregroundColor(isEnabled()));

                    Path2D arrow = new Path2D.Float(Path2D.WIND_EVEN_ODD);
                    arrow.moveTo(JBUIScale.scale(3.5f), JBUIScale.scale(6f));
                    arrow.lineTo(JBUIScale.scale(12.5f), JBUIScale.scale(6f));
                    arrow.lineTo(JBUIScale.scale(8f), JBUIScale.scale(11f));
                    arrow.closePath();

                    g2.fill(arrow);
                } finally {
                    g2.dispose();
                }
            }
        }

        protected boolean isArrowVisible(@NotNull Presentation presentation) {
            return true;
        }

        @Override
        public void updateUI() {
            super.updateUI();
            setMargin(JBUI.insets(0, 5, 0, 2));
        }

        /**
         * @deprecated This method is noop. Set icon, text and tooltip in the constructor
         * or property change listener for proper computation of preferred size.
         * Other updates happen in Swing.
         */
        @Deprecated
        @ApiStatus.ScheduledForRemoval(inVersion = "2021.1")
        protected void updateButtonSize() {
        }

        @ApiStatus.Experimental
        protected void doShiftClick() {
            doClick();
        }
    }

    protected Condition<AnAction> getPreselectCondition() {
        return null;
    }
}