net.groboclown.idea.p4ic.ui.config.P4ConfigPanel.java Source code

Java tutorial

Introduction

Here is the source code for net.groboclown.idea.p4ic.ui.config.P4ConfigPanel.java

Source

/*
 * 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 net.groboclown.idea.p4ic.ui.config;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.uiDesigner.core.Spacer;
import com.intellij.util.ui.AsyncProcessIcon;
import com.intellij.util.ui.UIUtil;
import net.groboclown.idea.p4ic.P4Bundle;
import net.groboclown.idea.p4ic.config.ManualP4Config;
import net.groboclown.idea.p4ic.config.P4Config;
import net.groboclown.idea.p4ic.config.P4Config.ConnectionMethod;
import net.groboclown.idea.p4ic.config.P4ConfigUtil;
import net.groboclown.idea.p4ic.server.exceptions.P4FileException;
import net.groboclown.idea.p4ic.server.exceptions.P4InvalidConfigException;
import net.groboclown.idea.p4ic.ui.connection.*;
import net.groboclown.idea.p4ic.v2.server.connection.*;
import net.groboclown.idea.p4ic.v2.server.connection.ConnectionUIConfiguration.ClientResult;
import net.groboclown.idea.p4ic.v2.server.connection.ProjectConfigSource.Builder;
import net.groboclown.idea.p4ic.v2.ui.alerts.ConfigPanelErrorHandler;
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.util.*;
import java.util.List;
import java.util.Map.Entry;

public class P4ConfigPanel {
    private static final Logger LOG = Logger.getInstance(P4ConfigPanel.class);

    private JPanel myMainPanel;
    private JComboBox/*<ConnectionPanel>*/ myConnectionChoice; // JDK 1.6 does not have generic combo box
    private JButton myRefreshClientList;
    private JComboBox/*<String>*/ myClientList; // JDK 1.6 does not have generic combo box
    private JCheckBox myReuseEnvValueCheckBox;
    private JButton myCheckConnectionButton;
    private JCheckBox mySilentlyGoOfflineOnCheckBox;
    private P4ConfigConnectionPanel myP4ConfigConnectionPanel;
    private ClientPasswordConnectionPanel myClientPasswordConnectionPanel;
    private AuthTicketConnectionPanel authTicketConnectionPanel;
    private SSOConnectionPanel mySSOConnectionPanel;
    private EnvConnectionPanel myEnvConnectionPanel;
    private JPanel myConnectionTypeContainerPanel;
    private JLabel myConnectionDescriptionLabel;
    private RelP4ConfigConnectionPanel myRelP4ConfigPanel;
    private JComboBox/*<String>*/ myResolvePath; // JDK 1.6 does not have generic combo box
    private JTextArea myResolvedValuesField;
    private JLabel myResolvePathLabel;
    private JButton myRefreshResolved;
    private AsyncProcessIcon myCheckConnectionSpinner;
    private AsyncProcessIcon myRefreshClientListSpinner;
    private AsyncProcessIcon myRefreshResolvedSpinner;

    private Project myProject;
    private AlertManager alertManager = AlertManager.getInstance();
    private final Set<String> activeProcesses = new HashSet<String>();
    private boolean initialized = false;

    public P4ConfigPanel() {
        // Initialize GUI constant values
        $$$setupUI$$$();

        myConnectionChoice.setRenderer(new AuthenticationMethodRenderer());
        myConnectionChoice.setEditable(false);
        // Could add checks to ensure that there is 1 and only 1 connection panel for
        // each connection type
        myConnectionChoice.addItem(myP4ConfigConnectionPanel);
        myConnectionChoice.addItem(myRelP4ConfigPanel);
        myConnectionChoice.addItem(myClientPasswordConnectionPanel);
        myConnectionChoice.addItem(authTicketConnectionPanel);

        // Not supported yet
        //myConnectionChoice.addItem(mySSOConnectionPanel);

        myConnectionChoice.addItem(myEnvConnectionPanel);

        // an initial value for connection choice
        myConnectionChoice.setSelectedItem(myEnvConnectionPanel);

        // Initialize GUI listeners
        myConnectionChoice.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                changeConnectionSelection();
            }
        });
        myRefreshClientList.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                refreshClientList();
            }
        });
        myReuseEnvValueCheckBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                boolean allowSelection = !myReuseEnvValueCheckBox.isSelected();
                myRefreshClientList.setEnabled(allowSelection);
                myClientList.setEnabled(allowSelection);
            }
        });
        myCheckConnectionButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                checkConnection();
            }
        });

        myResolvePath.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                refreshResolvedProperties();
            }
        });
        myRefreshResolved.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                refreshConfigPaths();
            }
        });
    }

    void initialize(@NotNull Project project) {
        this.myProject = project;
        myP4ConfigConnectionPanel.initialize(project);
    }

    public JPanel getPanel() {
        return myMainPanel;
    }

    public boolean isModified(@NotNull P4Config config) {
        if (config.hasClientnameSet()) {
            if (myReuseEnvValueCheckBox.isSelected()) {
                return true;
            }
            if (!Comparing.equal(config.getClientname(), myClientList.getSelectedItem())) {
                return true;
            }
        }

        if (config.isAutoOffline() != mySilentlyGoOfflineOnCheckBox.isSelected()) {
            return true;
        }

        //if (config.isPasswordStoredLocally() != mySavePasswordsCheckBox.isSelected()) {
        //    return true;
        //}

        Object selectedItem = myConnectionChoice.getSelectedItem();
        assert (selectedItem != null && (selectedItem instanceof ConnectionPanel));
        ConnectionPanel connection = (ConnectionPanel) selectedItem;
        if (config.getConnectionMethod() != connection.getConnectionMethod()) {
            return true;
        }
        return connection.isModified(config);
    }

    protected void loadSettingsIntoGUI(@NotNull ManualP4Config config) {
        // ----------------------------------------------------------------
        // non-connection values

        if (!config.isConfigured()) {
            // Don't show a big nasty error message just because nothing is
            // configured right.

            LOG.info("Skipping setup because the configuration has nothing.");
            return;
        }

        initializeClientAndPathSelection(config.hasClientnameSet() ? config.getClientname() : null,
                config.getConnectionMethod());

        mySilentlyGoOfflineOnCheckBox.setSelected(config.isAutoOffline());
        //mySavePasswordsCheckBox.setSelected(config.isPasswordStoredLocally());

        // ----------------------------------------------------------------
        // Dynamic setup for connection information
        for (int i = 0; i < myConnectionChoice.getItemCount(); i++) {
            ConnectionPanel conn = (ConnectionPanel) myConnectionChoice.getItemAt(i);
            if (conn.getConnectionMethod() == config.getConnectionMethod()) {
                showConnectionPanel(conn);
                conn.loadSettingsIntoGUI(config);
                myConnectionChoice.setSelectedIndex(i);
            }
        }

        resetResolvedProperties();
        initialized = true;
        refreshClientList();
        refreshConfigPaths();
        // refreshResolvedProperties();
    }

    protected void saveSettingsToConfig(@NotNull ManualP4Config config) {
        // Clear out the connection settings so old ones don't interfere
        // with the new ones
        config.setUsername(null);
        config.setPort(null);
        config.setProtocol(null);
        config.setPassword(null);
        config.setAuthTicketPath(null);
        config.setConfigFile(null);
        config.setTrustTicketPath(null);
        config.setServerFingerprint(null);
        config.setClientHostname(null);
        config.setClientname(null);
        config.setIgnoreFileName(null);

        ConnectionPanel conn = getSelectedConnection();
        conn.saveSettingsToConfig(config);
        config.setConnectionMethod(conn.getConnectionMethod());

        // ----------------------------------------------------------------
        // non-connection values - overwrite whatever the config panel set

        // Client name
        if (myReuseEnvValueCheckBox.isSelected()) {
            config.setClientname(null);
        } else {
            config.setClientname(getSelectedClient());
        }

        config.setAutoOffline(mySilentlyGoOfflineOnCheckBox.isSelected());
        //config.setPasswordStoredLocally(mySavePasswordsCheckBox.isSelected());
    }

    @NotNull
    private ConnectionPanel getSelectedConnection() {
        Object val = myConnectionChoice.getSelectedItem();
        if (val != null && val instanceof ConnectionPanel) {
            return (ConnectionPanel) val;
        }
        throw new IllegalStateException(P4Bundle.message("error.config.invalid-selection"));
    }

    @Nullable
    private String getSelectedClient() {
        if (getSelectedConnection().getConnectionMethod().isRelativeToPath()) {
            // These connections can never declare a client
            return null;
        }
        Object selected = myClientList.getSelectedItem();
        String selectedClient = null;
        if (selected != null) {
            selectedClient = selected.toString();
            if (selectedClient.length() <= 0) {
                selectedClient = null;
            }
        }
        return selectedClient;
    }

    // ---------------------------------------------------------
    // Connection utilities

    @NotNull
    private Collection<Builder> createConnectionConfigs() {
        ManualP4Config partial = new ManualP4Config();
        saveSettingsToConfig(partial);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Creating connection configs from " + P4ConfigUtil.getProperties(partial));
        }
        return ProjectConfigSourceLoader.loadSources(myProject, partial);
    }

    /**
     * Reports a problem to the user if there is any error in a config,
     * and returns null.
     *
     * @return only the valid connections, or null if there are invalid ones.
     */
    // CalledInBackground
    @NotNull
    private ConfigSet getValidConfigs() {
        if (!initialized) {
            LOG.debug("called getValidConfigs before configs were loaded");
            return new ConfigSet();
        }

        final Collection<Builder> sources = createConnectionConfigs();
        final List<ProjectConfigSource> valid = new ArrayList<ProjectConfigSource>(sources.size());
        final List<Builder> invalid = new ArrayList<Builder>();
        for (Builder source : sources) {
            if (source.isInvalid()) {
                invalid.add(source);
            } else {
                try {
                    final ProjectConfigSource config = source.create();
                    valid.add(config);
                } catch (P4InvalidConfigException e) {
                    source.setError(e);
                    invalid.add(source);
                }
            }
        }
        return new ConfigSet(valid, invalid);
        //if (invalidConfigCount <= 0) {
        //    if (valid.isEmpty()) {
        //        alertManager.addCriticalError(new ConfigPanelErrorHandler(
        //                myProject,
        //                P4Bundle.message("configuration.error.title"),
        //                P4Bundle.message("configuration.error.no-config-defined")
        //        ), null);
        //        return null;
        //    }
        //    return valid;
        //}
        //alertManager.addCriticalError(new ConfigPanelErrorHandler(
        //        myProject,
        //        P4Bundle.message("configuration.error.title"),
        //        P4Bundle.message("configuration.error.problem-list",
        //                invalidConfigCount, invalidConfigs.toString())
        //), null);
        //return null;
        //reportProblems(Collections.<Exception>singleton(e));
    }

    // ---------------------------------------------------------
    // UI callbacks

    // CalledInAwt
    private void checkConnection() {
        runBackgroundAwtAction(myCheckConnectionSpinner, new BackgroundAwtAction<Collection<Builder>>() {
            @Override
            public Collection<Builder> runBackgroundProcess() {
                final ConfigSet sources = getValidConfigs();
                final List<Builder> problems = new ArrayList<Builder>();
                problems.addAll(sources.invalid);
                for (Entry<ProjectConfigSource, Exception> entry : ConnectionUIConfiguration
                        .findConnectionProblems(sources.valid, ServerConnectionManager.getInstance()).entrySet()) {
                    final VcsException err;
                    //noinspection ThrowableResultOfMethodCallIgnored
                    if (entry.getValue() instanceof VcsException) {
                        err = (VcsException) entry.getValue();
                    } else {
                        err = new P4FileException(entry.getValue());
                    }
                    problems.add(entry.getKey().causedError(err));
                }
                return problems;
            }

            @Override
            public void runAwtProcess(final Collection<Builder> problems) {
                if (problems != null && problems.isEmpty()) {
                    Messages.showMessageDialog(myProject,
                            P4Bundle.message("configuration.dialog.valid-connection.message"),
                            P4Bundle.message("configuration.dialog.valid-connection.title"),
                            Messages.getInformationIcon());
                } else if (problems != null) {
                    reportConfigProblems(problems);
                }
            }
        });
    }

    /**
     * Reloads the displayed list of clients.
     */
    // CalledInAwt
    private void refreshClientList() {
        final Object selected = myClientList.getSelectedItem();
        runBackgroundAwtAction(myRefreshClientListSpinner, new BackgroundAwtAction<List<String>>() {
            @Override
            public List<String> runBackgroundProcess() {
                return loadClientList(selected);
            }

            @Override
            public void runAwtProcess(final List<String> clientList) {
                if (clientList != null) {
                    myClientList.removeAllItems();
                    for (String client : clientList) {
                        myClientList.addItem(client);
                    }
                    if (!clientList.isEmpty()) {
                        myClientList.setSelectedIndex(0);
                    }
                }
                // else already handled the errors
            }
        });
    }

    // CalledInBackground
    private List<String> loadClientList(@Nullable Object selected) {
        final Map<ProjectConfigSource, ClientResult> clientResults = ConnectionUIConfiguration
                .getClients(getValidConfigs().valid, ServerConnectionManager.getInstance());

        if (clientResults == null) {
            // Don't need a status update or any updates; the user should have
            // seen error dialogs.
            LOG.info("UserClientsLoader returned null");
            return null;
        }

        // Find errors and valid clients
        Set<String> toAdd = new HashSet<String>();
        List<Exception> problems = new ArrayList<Exception>();
        for (Entry<ProjectConfigSource, ClientResult> entry : clientResults.entrySet()) {
            if (entry.getValue().isInalid()) {
                problems.add(entry.getValue().getConnectionProblem());
            } else {
                toAdd.addAll(entry.getValue().getClientNames());
            }
        }
        if (!problems.isEmpty()) {
            reportExceptions(problems);
            return null;
        }

        // Make sure to keep the currently selected item selected.
        // If it wasn't in the original list, it needs to be added
        // and have a custom renderer highlight it as ss invalid.
        // Also, move the currently selected one to the top.

        // TODO if the selected item isn't in the new client list,
        // then mark it as an error.

        List<String> orderedClients = new ArrayList<String>(toAdd);
        Collections.sort(orderedClients);
        if (selected != null) {
            if (orderedClients.remove(selected.toString()) && selected.toString().trim().length() > 0) {
                // in the new client list and it's a valid (not-empty) name.
                orderedClients.add(0, selected.toString());
            }
        }
        return orderedClients;
    }

    // CalledInAwt
    private void changeConnectionSelection() {
        int currentSelectedIndex = myConnectionChoice.getSelectedIndex();
        ConnectionPanel selected = (ConnectionPanel) myConnectionChoice.getItemAt(currentSelectedIndex);

        showConnectionPanel(selected);
    }

    // CalledInAwt
    private void initializeClientAndPathSelection(@Nullable String currentClientName,
            @NotNull final ConnectionMethod connectionMethod) {
        if (connectionMethod.isRelativeToPath()) {
            myReuseEnvValueCheckBox.setSelected(true);
            myReuseEnvValueCheckBox.setEnabled(false);
            myClientList.setEnabled(false);
            myClientList.removeAllItems();
            myRefreshClientList.setEnabled(false);
            myResolvePathLabel.setEnabled(true);
            myResolvePath.setEnabled(true);

            refreshConfigPaths();
            resetResolvedProperties();
        } else {
            myReuseEnvValueCheckBox.setEnabled(true);
            myResolvePathLabel.setEnabled(false);
            myResolvePath.setEnabled(false);
            myClientList.removeAllItems();

            if (currentClientName != null) {
                myReuseEnvValueCheckBox.setSelected(false);
                myRefreshClientList.setEnabled(true);
                myClientList.setEnabled(true);

                // Currently selected client name needs to be added
                myClientList.addItem(currentClientName);
                // and select it
                myClientList.setSelectedItem(currentClientName);
            } else {
                myReuseEnvValueCheckBox.setSelected(true);
                myRefreshClientList.setEnabled(false);
                myClientList.setEnabled(false);
            }

            refreshResolvedProperties();
        }
    }

    // CalledInAwt
    private void showConnectionPanel(@NotNull ConnectionPanel panel) {
        myConnectionDescriptionLabel.setText("<html>" + panel.getDescription());
        ((CardLayout) myConnectionTypeContainerPanel.getLayout()).show(myConnectionTypeContainerPanel,
                panel.getConnectionMethod().name());

        initializeClientAndPathSelection(getSelectedClient(), panel.getConnectionMethod());
    }

    /**
     * Refresh the list of config file paths.  This will indirectly invoke
     * the refreshResolvedProperties
     */
    // CalledInAwt
    private void refreshConfigPaths() {
        runBackgroundAwtAction(myRefreshClientListSpinner, new BackgroundAwtAction<ConfigSet>() {
            @Override
            public ConfigSet runBackgroundProcess() {
                return getValidConfigs();
            }

            @Override
            public void runAwtProcess(final ConfigSet sources) {
                // load the drop-down list with the new values.
                // Make sure to maintain the existing selected item.
                Object current = myResolvePath.getSelectedItem();
                myResolvePath.removeAllItems();
                if (sources != null && !sources.valid.isEmpty()) {
                    boolean found = false;
                    for (ProjectConfigSource source : sources.valid) {
                        for (VirtualFile dir : source.getProjectSourceDirs()) {
                            if (dir.getPath().equals(current)) {
                                found = true;
                            }
                            myResolvePath.addItem(dir);
                        }
                    }
                    if (found) {
                        myResolvePath.setSelectedItem(current);
                    } else if (myResolvePath.getItemCount() > 0) {
                        myResolvePath.setSelectedIndex(0);
                    }
                }

                refreshResolvedProperties();
            }
        });
    }

    /**
     * Refresh just the list of resolved properties.
     */
    // CalledInAwt
    private void refreshResolvedProperties() {
        // This is actually invoked when the config directory drop-down is changed,
        // or when the connection is changed, but this spinner is fine.

        final Object selectedItem;
        if (myResolvePath.getItemCount() <= 0) {
            selectedItem = null;
        } else if (myResolvePath.getSelectedItem() == null) {
            selectedItem = myResolvePath.getItemAt(0);
        } else {
            selectedItem = myResolvePath.getSelectedItem();
        }

        runBackgroundAwtAction(myRefreshResolvedSpinner, new BackgroundAwtAction<StringBuilder>() {
            @Override
            public StringBuilder runBackgroundProcess() {
                // Find the source corresponding to the selected item
                final StringBuilder displayText = new StringBuilder();
                if (LOG.isDebugEnabled()) {
                    LOG.debug("creating connection configs");
                }
                final Collection<Builder> sources = createConnectionConfigs();
                if (LOG.isDebugEnabled()) {
                    LOG.debug(" - found sources " + sources);
                }
                Builder selectedSource = null;
                if (sources.isEmpty()) {
                    selectedSource = null;
                } else if (sources.size() == 1 || selectedItem == null) {
                    selectedSource = sources.iterator().next();
                } else {
                    sourceLoop: for (Builder source : sources) {
                        if (source.isInvalid()) {
                            // skip
                        } else {
                            for (VirtualFile file : source.getDirs()) {
                                if (file.equals(selectedItem)) {
                                    selectedSource = source;
                                    break sourceLoop;
                                }
                            }
                        }
                    }
                }

                if (selectedSource != null) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug(" - selected source is " + selectedSource);
                    }
                    createSourceDisplay(displayText, selectedSource);
                } else {
                    // if there were config problems, report them.
                    sourceLoop: for (Builder source : sources) {
                        if (source.isInvalid()) {
                            for (VirtualFile file : source.getDirs()) {
                                if (file.getPath().equals(selectedItem)) {
                                    selectedSource = source;
                                    break sourceLoop;
                                }
                            }
                        }
                    }
                    if (selectedSource != null) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug(" - selected source is " + selectedSource);
                        }
                        createSourceDisplay(displayText, selectedSource);
                    } else {
                        LOG.info("No selected source, and no sources found");
                        displayText.append(P4Bundle.message("config.display.properties.no_path"));
                    }
                }
                return displayText;
            }

            @Override
            public void runAwtProcess(final StringBuilder displayText) {
                myResolvedValuesField.setText(displayText.toString());
            }
        });
    }

    @NotNull
    private StringBuilder createSourceDisplay(@NotNull StringBuilder display, @NotNull Builder builder) {
        if (builder.isInvalid()) {
            if (builder.getError() != null) {
                display.append(P4Bundle.message("config.display.invalid.reason", builder.getError().getMessage()))
                        .append("\n");
            } else {
                display.append(P4Bundle.message("config.display.invalid")).append("\n");
            }
        }
        final Map<String, String> props = P4ConfigUtil.getProperties(builder.getBaseConfig());
        List<String> keys = new ArrayList<String>(props.keySet());
        Collections.sort(keys);
        String sep = "";
        for (String key : keys) {
            display.append(sep);
            String val = props.get(key);
            if (val == null) {
                val = P4Bundle.getString("config.display.key.no-value");
            }
            display.append(P4Bundle.message("config.display.property-line", key, val));
            sep = "\n";
        }
        return display;
    }

    private void resetResolvedProperties() {
        myResolvedValuesField.setText(P4Bundle.message("config.display.properties.refresh"));
    }

    private void reportConfigProblems(@NotNull Collection<Builder> problems) {
        // TODO use better UI element
        // FIXME localize
        String message = "";
        String sep = "";
        for (Builder builder : problems) {
            //noinspection ThrowableResultOfMethodCallIgnored
            message += sep + builder.getPresentableName() + ": "
                    + (builder.getError() == null ? "(unknown)" : builder.getError().getMessage());
            sep = "\n";
            LOG.info("Config problem: " + builder, builder.getError());
        }
        alertManager.addCriticalError(
                new ConfigPanelErrorHandler(myProject, P4Bundle.message("configuration.error.title"), message),
                null);
    }

    private void reportExceptions(@NotNull Collection<Exception> problems) {
        // TODO use better UI element
        String message = "";
        String sep = "";
        for (Exception ex : problems) {
            //noinspection ThrowableResultOfMethodCallIgnored
            message += sep + ex.getMessage();
            sep = "\n";
            LOG.info("Config problem", ex);
        }
        alertManager.addCriticalError(
                new ConfigPanelErrorHandler(myProject, P4Bundle.message("configuration.error.title"), message),
                null);
    }

    /**
     * Method generated by IntelliJ IDEA GUI Designer
     * >>> IMPORTANT!! <<<
     * DO NOT edit this method OR call it in your code!
     *
     * @noinspection ALL
     */
    private void $$$setupUI$$$() {
        createUIComponents();
        myMainPanel = new JPanel();
        myMainPanel.setLayout(new GridLayoutManager(9, 3, new Insets(0, 0, 0, 0), -1, -1));
        final JLabel label1 = new JLabel();
        label1.setHorizontalAlignment(10);
        this.$$$loadLabelText$$$(label1, ResourceBundle.getBundle("net/groboclown/idea/p4ic/P4Bundle")
                .getString("configuration.connection-choice"));
        myMainPanel.add(label1,
                new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE,
                        GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
                        false));
        final JLabel label2 = new JLabel();
        this.$$$loadLabelText$$$(label2, ResourceBundle.getBundle("net/groboclown/idea/p4ic/P4Bundle")
                .getString("configuration.clientname"));
        myMainPanel.add(label2,
                new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE,
                        GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
                        false));
        final JPanel panel1 = new JPanel();
        panel1.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1));
        myMainPanel.add(panel1,
                new GridConstraints(3, 1, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null,
                        null, 0, false));
        final JPanel panel2 = new JPanel();
        panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));
        panel1.add(panel2,
                new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null,
                        null, 0, false));
        myClientList = new JComboBox();
        myClientList.setEditable(true);
        panel2.add(myClientList,
                new GridConstraints(0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL,
                        GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
                        false));
        final JPanel panel3 = new JPanel();
        panel3.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));
        myMainPanel.add(panel3,
                new GridConstraints(4, 1, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        myRefreshClientList = new JButton();
        myRefreshClientList.setHorizontalAlignment(0);
        this.$$$loadButtonText$$$(myRefreshClientList, ResourceBundle.getBundle("net/groboclown/idea/p4ic/P4Bundle")
                .getString("configuration.choose-client-button"));
        panel3.add(myRefreshClientList);
        panel3.add(myRefreshClientListSpinner);
        myReuseEnvValueCheckBox = new JCheckBox();
        this.$$$loadButtonText$$$(myReuseEnvValueCheckBox, ResourceBundle
                .getBundle("net/groboclown/idea/p4ic/P4Bundle").getString("configuration.clientname.inherit"));
        panel3.add(myReuseEnvValueCheckBox);
        final JLabel label3 = new JLabel();
        label3.setHorizontalAlignment(10);
        label3.setHorizontalTextPosition(11);
        this.$$$loadLabelText$$$(label3,
                ResourceBundle.getBundle("net/groboclown/idea/p4ic/P4Bundle").getString("configuration.options"));
        myMainPanel.add(label3,
                new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE,
                        GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
                        false));
        myConnectionTypeContainerPanel = new JPanel();
        myConnectionTypeContainerPanel.setLayout(new CardLayout(0, 0));
        myMainPanel.add(myConnectionTypeContainerPanel,
                new GridConstraints(2, 1, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null,
                        null, 0, false));
        myConnectionTypeContainerPanel.add(myP4ConfigConnectionPanel.$$$getRootComponent$$$(), "P4CONFIG");
        myClientPasswordConnectionPanel = new ClientPasswordConnectionPanel();
        myConnectionTypeContainerPanel.add(myClientPasswordConnectionPanel.$$$getRootComponent$$$(), "CLIENT");
        authTicketConnectionPanel = new AuthTicketConnectionPanel();
        myConnectionTypeContainerPanel.add(authTicketConnectionPanel.$$$getRootComponent$$$(), "AUTH_TICKET");
        mySSOConnectionPanel = new SSOConnectionPanel();
        myConnectionTypeContainerPanel.add(mySSOConnectionPanel.$$$getRootComponent$$$(), "SSO");
        myEnvConnectionPanel = new EnvConnectionPanel();
        myConnectionTypeContainerPanel.add(myEnvConnectionPanel.$$$getRootComponent$$$(), "DEFAULT");
        myRelP4ConfigPanel = new RelP4ConfigConnectionPanel();
        myConnectionTypeContainerPanel.add(myRelP4ConfigPanel.$$$getRootComponent$$$(), "REL_P4CONFIG");
        final JPanel panel4 = new JPanel();
        panel4.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));
        myMainPanel.add(panel4,
                new GridConstraints(0, 1, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null,
                        null, 0, false));
        myConnectionChoice = new JComboBox();
        myConnectionChoice.setToolTipText(ResourceBundle.getBundle("net/groboclown/idea/p4ic/P4Bundle")
                .getString("configuration.connection-choice.picker.tooltip"));
        panel4.add(myConnectionChoice);
        myCheckConnectionButton = new JButton();
        myCheckConnectionButton.setHorizontalAlignment(10);
        this.$$$loadButtonText$$$(myCheckConnectionButton, ResourceBundle
                .getBundle("net/groboclown/idea/p4ic/P4Bundle").getString("configuration.check-connection.button"));
        panel4.add(myCheckConnectionButton);
        panel4.add(myCheckConnectionSpinner);
        final JPanel panel5 = new JPanel();
        panel5.setLayout(new BorderLayout(0, 0));
        myMainPanel.add(panel5,
                new GridConstraints(1, 1, 1, 2, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_NONE,
                        GridConstraints.SIZEPOLICY_FIXED,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null,
                        null, 0, false));
        myConnectionDescriptionLabel = new JLabel();
        myConnectionDescriptionLabel.setText("");
        myConnectionDescriptionLabel.setVerticalAlignment(1);
        panel5.add(myConnectionDescriptionLabel, BorderLayout.CENTER);
        final JPanel panel6 = new JPanel();
        panel6.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1));
        myMainPanel.add(panel6,
                new GridConstraints(5, 1, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null,
                        null, 0, false));
        mySilentlyGoOfflineOnCheckBox = new JCheckBox();
        this.$$$loadButtonText$$$(mySilentlyGoOfflineOnCheckBox, ResourceBundle
                .getBundle("net/groboclown/idea/p4ic/P4Bundle").getString("configuration.autoconnect"));
        mySilentlyGoOfflineOnCheckBox.setToolTipText(ResourceBundle.getBundle("net/groboclown/idea/p4ic/P4Bundle")
                .getString("configuration.panel.silent.tooltip"));
        panel6.add(mySilentlyGoOfflineOnCheckBox,
                new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JPanel panel7 = new JPanel();
        panel7.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1));
        myMainPanel.add(panel7,
                new GridConstraints(6, 1, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null,
                        null, 0, false));
        final JPanel panel8 = new JPanel();
        panel8.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));
        panel7.add(panel8,
                new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null,
                        null, 0, false));
        myResolvePathLabel = new JLabel();
        this.$$$loadLabelText$$$(myResolvePathLabel, ResourceBundle.getBundle("net/groboclown/idea/p4ic/P4Bundle")
                .getString("configuration.resolved.path"));
        panel8.add(myResolvePathLabel,
                new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE,
                        GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
                        false));
        myResolvePath = new JComboBox();
        myResolvePath.setToolTipText(ResourceBundle.getBundle("net/groboclown/idea/p4ic/P4Bundle")
                .getString("configuration.resolve.configfile.tooltip"));
        panel8.add(myResolvePath,
                new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL,
                        GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
                        false));
        final JLabel label4 = new JLabel();
        this.$$$loadLabelText$$$(label4, ResourceBundle.getBundle("net/groboclown/idea/p4ic/P4Bundle")
                .getString("configuration.resolved.title"));
        myMainPanel.add(label4,
                new GridConstraints(6, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE,
                        GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0,
                        false));
        final JScrollPane scrollPane1 = new JScrollPane();
        myMainPanel.add(scrollPane1,
                new GridConstraints(8, 1, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null,
                        null, 0, false));
        scrollPane1.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLoweredBevelBorder(), null));
        myResolvedValuesField = new JTextArea();
        myResolvedValuesField.setEditable(false);
        myResolvedValuesField.setFont(UIManager.getFont("TextArea.font"));
        myResolvedValuesField.setLineWrap(false);
        myResolvedValuesField.setRows(10);
        myResolvedValuesField.setToolTipText(ResourceBundle.getBundle("net/groboclown/idea/p4ic/P4Bundle")
                .getString("configuration.resolved.tooltip"));
        myResolvedValuesField.setWrapStyleWord(false);
        scrollPane1.setViewportView(myResolvedValuesField);
        final JPanel panel9 = new JPanel();
        panel9.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
        myMainPanel.add(panel9,
                new GridConstraints(7, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        myRefreshResolved = new JButton();
        this.$$$loadButtonText$$$(myRefreshResolved, ResourceBundle.getBundle("net/groboclown/idea/p4ic/P4Bundle")
                .getString("configuration.resolve.refresh"));
        panel9.add(myRefreshResolved);
        panel9.add(myRefreshResolvedSpinner);
        label1.setLabelFor(myConnectionChoice);
        label2.setLabelFor(myClientList);
        myResolvePathLabel.setLabelFor(myResolvedValuesField);
    }

    /**
     * @noinspection ALL
     */
    private void $$$loadLabelText$$$(JLabel component, String text) {
        StringBuffer result = new StringBuffer();
        boolean haveMnemonic = false;
        char mnemonic = '\0';
        int mnemonicIndex = -1;
        for (int i = 0; i < text.length(); i++) {
            if (text.charAt(i) == '&') {
                i++;
                if (i == text.length())
                    break;
                if (!haveMnemonic && text.charAt(i) != '&') {
                    haveMnemonic = true;
                    mnemonic = text.charAt(i);
                    mnemonicIndex = result.length();
                }
            }
            result.append(text.charAt(i));
        }
        component.setText(result.toString());
        if (haveMnemonic) {
            component.setDisplayedMnemonic(mnemonic);
            component.setDisplayedMnemonicIndex(mnemonicIndex);
        }
    }

    /**
     * @noinspection ALL
     */
    private void $$$loadButtonText$$$(AbstractButton component, String text) {
        StringBuffer result = new StringBuffer();
        boolean haveMnemonic = false;
        char mnemonic = '\0';
        int mnemonicIndex = -1;
        for (int i = 0; i < text.length(); i++) {
            if (text.charAt(i) == '&') {
                i++;
                if (i == text.length())
                    break;
                if (!haveMnemonic && text.charAt(i) != '&') {
                    haveMnemonic = true;
                    mnemonic = text.charAt(i);
                    mnemonicIndex = result.length();
                }
            }
            result.append(text.charAt(i));
        }
        component.setText(result.toString());
        if (haveMnemonic) {
            component.setMnemonic(mnemonic);
            component.setDisplayedMnemonicIndex(mnemonicIndex);
        }
    }

    /**
     * @noinspection ALL
     */
    public JComponent $$$getRootComponent$$$() {
        return myMainPanel;
    }

    private static class ConfigSet {
        final Collection<ProjectConfigSource> valid;
        final Collection<Builder> invalid;

        ConfigSet() {
            this.valid = Collections.emptyList();
            this.invalid = Collections.emptyList();
        }

        ConfigSet(@NotNull Collection<ProjectConfigSource> valid, @NotNull Collection<Builder> invalid) {
            this.valid = Collections.unmodifiableCollection(valid);
            this.invalid = Collections.unmodifiableCollection(invalid);
        }
    }

    private interface BackgroundAwtAction<T> {
        T runBackgroundProcess();

        void runAwtProcess(T value);
    }

    // CalledInAwt
    private <T> void runBackgroundAwtAction(@NotNull final AsyncProcessIcon icon,
            @NotNull final BackgroundAwtAction<T> action) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Requested background action " + icon.getName());
        }
        synchronized (activeProcesses) {
            if (activeProcesses.contains(icon.getName())) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug(" - process is already running in background (active: " + activeProcesses + ")");
                }
                return;
            }
            activeProcesses.add(icon.getName());
        }
        icon.resume();
        icon.setVisible(true);
        ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
            @Override
            public void run() {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Running " + icon.getName() + " in background ");
                }
                T tmpValue;
                Exception tmpFailure;
                try {
                    tmpValue = action.runBackgroundProcess();
                    tmpFailure = null;
                } catch (Exception e) {
                    LOG.error("Background processing for " + icon.getName() + " failed", e);
                    tmpValue = null;
                    tmpFailure = e;
                } finally {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Background processing for " + icon.getName()
                                + " completed.  Queueing AWT processing.");
                    }
                }
                final T value = tmpValue;
                final Exception failure = tmpFailure;

                // NOTE: ApplicationManager.getApplication().invokeLater
                // will not work, because it will wait until the UI dialog
                // goes away, which means we can't see the results until
                // the UI element goes away, which is just backwards.

                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Running " + icon.getName() + " in AWT");
                        }
                        try {
                            if (failure == null) {
                                action.runAwtProcess(value);
                            }
                        } finally {
                            icon.suspend();
                            icon.setVisible(false);
                            synchronized (activeProcesses) {
                                activeProcesses.remove(icon.getName());
                                if (LOG.isDebugEnabled()) {
                                    LOG.debug("Remaining background processes active: " + activeProcesses);
                                }
                            }
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("AWT processing for " + icon.getName() + " completed");
                            }
                        }
                    }
                });
            }
        });
    }

    // -----------------------------------------------------------------------
    // UI form stuff

    private void createUIComponents() {
        // Add custom component construction here.
        myP4ConfigConnectionPanel = new P4ConfigConnectionPanel();

        myCheckConnectionSpinner = new AsyncProcessIcon("Check Connection Progress");
        myCheckConnectionSpinner.setName("Check Connection Progress");
        myCheckConnectionSpinner.setVisible(false);

        myRefreshClientListSpinner = new AsyncProcessIcon("Refresh Client List Progress");
        myRefreshClientListSpinner.setName("Refresh Client List Progress");
        myRefreshClientListSpinner.setVisible(false);

        myRefreshResolvedSpinner = new AsyncProcessIcon("Refresh Resolved Progress");
        myRefreshResolvedSpinner.setName("Refresh Resolved Progress");
        myRefreshResolvedSpinner.setVisible(false);
    }

    private static class AuthenticationMethodRenderer extends ListCellRendererWrapper<ConnectionPanel> {
        @Override
        public void customize(JList list, ConnectionPanel value, int index, boolean isSelected, boolean hasFocus) {
            if (isSelected || hasFocus) {
                setBackground(UIUtil.getListSelectionBackground());
                final Color selectedForegroundColor = UIUtil.getListSelectionForeground();
                setForeground(selectedForegroundColor);
            } else {
                setBackground(UIUtil.getListBackground());
                final Color foregroundColor = UIUtil.getListForeground();
                setForeground(foregroundColor);
            }

            if (value == null) {
                setText("");
                setToolTipText("");
            } else {
                setText(value.getName());
                setToolTipText(P4Bundle.message("connection.choice"));
            }
        }
    }
}