com.intellij.openapi.ui.ComponentWithBrowseButton.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.openapi.ui.ComponentWithBrowseButton.java

Source

// Copyright 2000-2020 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.ui;

import com.intellij.diagnostic.LoadingState;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CustomShortcutSet;
import com.intellij.openapi.actionSystem.ShortcutSet;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Experiments;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.ui.GuiUtils;
import com.intellij.ui.UIBundle;
import com.intellij.ui.components.fields.ExtendableTextComponent;
import com.intellij.util.ui.StartupUiUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.accessibility.ScreenReader;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
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.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;

public class ComponentWithBrowseButton<Comp extends JComponent> extends JPanel implements Disposable {
    private final Comp myComponent;
    private final FixedSizeButton myBrowseButton;
    private boolean myButtonEnabled = true;

    @ApiStatus.Internal
    public static boolean isUseInlineBrowserButton() {
        return !LoadingState.COMPONENTS_REGISTERED.isOccurred()
                || Experiments.getInstance().isFeatureEnabled("inline.browse.button");
    }

    public ComponentWithBrowseButton(@NotNull Comp component, @Nullable ActionListener browseActionListener) {
        super(new BorderLayout(SystemInfo.isMac || StartupUiUtil.isUnderDarcula() ? 0 : 2, 0));

        myComponent = component;
        // required! otherwise JPanel will occasionally gain focus instead of the component
        setFocusable(false);
        boolean inlineBrowseButton = myComponent instanceof ExtendableTextComponent && isUseInlineBrowserButton();
        if (inlineBrowseButton) {
            ((ExtendableTextComponent) myComponent).addExtension(ExtendableTextComponent.Extension
                    .create(getDefaultIcon(), getHoveredIcon(), getIconTooltip(), this::notifyActionListeners));
            new DumbAwareAction() {
                @Override
                public void actionPerformed(@NotNull AnActionEvent e) {
                    notifyActionListeners();
                }
            }.registerCustomShortcutSet(
                    new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK)),
                    myComponent);
        }
        add(myComponent, BorderLayout.CENTER);

        myBrowseButton = new FixedSizeButton(myComponent);
        if (browseActionListener != null) {
            myBrowseButton.addActionListener(browseActionListener);
        }
        if (!inlineBrowseButton) {
            add(myBrowseButton, BorderLayout.EAST);
        }

        myBrowseButton.setToolTipText(getIconTooltip());
        // FixedSizeButton isn't focusable but it should be selectable via keyboard.
        if (ApplicationManager.getApplication() != null) { // avoid crash at design time
            new MyDoClickAction(myBrowseButton).registerShortcut(myComponent);
        }
        if (ScreenReader.isActive()) {
            myBrowseButton.setFocusable(true);
            myBrowseButton.getAccessibleContext().setAccessibleName("Browse");
        }
    }

    @NotNull
    protected Icon getDefaultIcon() {
        return AllIcons.General.OpenDisk;
    }

    @NotNull
    protected Icon getHoveredIcon() {
        return AllIcons.General.OpenDiskHover;
    }

    @NotNull
    protected String getIconTooltip() {
        return UIBundle.message("component.with.browse.button.browse.button.tooltip.text") + " ("
                + KeymapUtil.getKeystrokeText(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK))
                + ")";
    }

    private void notifyActionListeners() {
        ActionEvent event = new ActionEvent(myComponent, ActionEvent.ACTION_PERFORMED, "action");
        for (ActionListener listener : myBrowseButton.getActionListeners())
            listener.actionPerformed(event);
    }

    @NotNull
    public final Comp getChildComponent() {
        return myComponent;
    }

    public void setTextFieldPreferredWidth(final int charCount) {
        JComponent comp = getChildComponent();
        Dimension size = GuiUtils.getSizeByChars(charCount, comp);
        comp.setPreferredSize(size);
        Dimension preferredSize = myBrowseButton.getPreferredSize();

        boolean keepHeight = UIUtil.isUnderWin10LookAndFeel();
        preferredSize.setSize(size.width + preferredSize.width + 2,
                keepHeight ? preferredSize.height : preferredSize.height + 2);

        setPreferredSize(preferredSize);
    }

    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        myBrowseButton.setEnabled(enabled && myButtonEnabled);
        myComponent.setEnabled(enabled);
    }

    public void setButtonEnabled(boolean buttonEnabled) {
        myButtonEnabled = buttonEnabled;
        setEnabled(isEnabled());
    }

    public void setButtonIcon(@NotNull Icon icon) {
        myBrowseButton.setIcon(icon);
        myBrowseButton.setDisabledIcon(IconLoader.getDisabledIcon(icon));
    }

    /**
     * Adds specified {@code listener} to the browse button.
     */
    public void addActionListener(ActionListener listener) {
        myBrowseButton.addActionListener(listener);
    }

    public void removeActionListener(ActionListener listener) {
        myBrowseButton.removeActionListener(listener);
    }

    public void addBrowseFolderListener(@Nullable @Nls(capitalization = Nls.Capitalization.Title) String title,
            @Nullable @Nls(capitalization = Nls.Capitalization.Sentence) String description,
            @Nullable Project project, FileChooserDescriptor fileChooserDescriptor,
            TextComponentAccessor<? super Comp> accessor) {
        addActionListener(new BrowseFolderActionListener<>(title, description, this, project, fileChooserDescriptor,
                accessor));
    }

    /**
     * @deprecated use {@link #addBrowseFolderListener(String, String, Project, FileChooserDescriptor, TextComponentAccessor)} instead
     */
    @Deprecated
    public void addBrowseFolderListener(@Nullable @Nls(capitalization = Nls.Capitalization.Title) String title,
            @Nullable @Nls(capitalization = Nls.Capitalization.Sentence) String description,
            @Nullable Project project, FileChooserDescriptor fileChooserDescriptor,
            TextComponentAccessor<? super Comp> accessor, boolean autoRemoveOnHide) {
        addBrowseFolderListener(title, description, project, fileChooserDescriptor, accessor);
    }

    /**
     * @deprecated use {@link #addActionListener(ActionListener)} instead
     */
    @Deprecated
    public void addBrowseFolderListener(@Nullable Project project,
            final BrowseFolderActionListener<Comp> actionListener) {
        addActionListener(actionListener);
    }

    @Override
    public void dispose() {
        ActionListener[] listeners = myBrowseButton.getActionListeners();
        for (ActionListener listener : listeners) {
            myBrowseButton.removeActionListener(listener);
        }
    }

    public FixedSizeButton getButton() {
        return myBrowseButton;
    }

    /**
     * Do not use this class directly it is public just to hack other implementation of controls similar to TextFieldWithBrowseButton.
     */
    public static final class MyDoClickAction extends DumbAwareAction {
        private final FixedSizeButton myBrowseButton;

        public MyDoClickAction(FixedSizeButton browseButton) {
            myBrowseButton = browseButton;
        }

        @Override
        public void update(@NotNull AnActionEvent e) {
            e.getPresentation().setEnabled(myBrowseButton.isVisible() && myBrowseButton.isEnabled());
        }

        @Override
        public void actionPerformed(@NotNull AnActionEvent e) {
            myBrowseButton.doClick();
        }

        public void registerShortcut(JComponent textField) {
            ShortcutSet shiftEnter = new CustomShortcutSet(
                    KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK));
            registerCustomShortcutSet(shiftEnter, textField);
            myBrowseButton.setToolTipText(KeymapUtil.getShortcutsText(shiftEnter.getShortcuts()));
        }

        public static void addTo(FixedSizeButton browseButton, JComponent aComponent) {
            new MyDoClickAction(browseButton).registerShortcut(aComponent);
        }
    }

    public static class BrowseFolderActionListener<T extends JComponent> extends BrowseFolderRunnable<T>
            implements ActionListener {
        public BrowseFolderActionListener(@Nullable @Nls(capitalization = Nls.Capitalization.Title) String title,
                @Nullable @Nls(capitalization = Nls.Capitalization.Sentence) String description,
                @Nullable ComponentWithBrowseButton<T> textField, @Nullable Project project,
                FileChooserDescriptor fileChooserDescriptor, TextComponentAccessor<? super T> accessor) {
            super(title, description, project, fileChooserDescriptor,
                    textField != null ? textField.getChildComponent() : null, accessor);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            run();
        }
    }

    @Override
    public final void requestFocus() {
        IdeFocusManager.getGlobalInstance()
                .doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(myComponent, true));
    }

    @SuppressWarnings("deprecation")
    @Override
    public final void setNextFocusableComponent(Component aComponent) {
        super.setNextFocusableComponent(aComponent);
        myComponent.setNextFocusableComponent(aComponent);
    }

    private KeyEvent myCurrentEvent = null;

    @Override
    protected final boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
        if (condition == WHEN_FOCUSED && myCurrentEvent != e) {
            try {
                myCurrentEvent = e;
                myComponent.dispatchEvent(e);
            } finally {
                myCurrentEvent = null;
            }
        }
        if (e.isConsumed())
            return true;
        return super.processKeyBinding(ks, e, condition, pressed);
    }

    /**
     * @deprecated use {@link #addActionListener(ActionListener)} instead
     */
    @Deprecated
    public void addBrowseFolderListener(@Nullable Project project,
            final BrowseFolderActionListener<Comp> actionListener, boolean autoRemoveOnHide) {
        addActionListener(actionListener);
    }

}