manuylov.maxim.ocaml.module.OCamlSourcesPathStep.java Source code

Java tutorial

Introduction

Here is the source code for manuylov.maxim.ocaml.module.OCamlSourcesPathStep.java

Source

/*
 * OCaml Support For IntelliJ Platform.
 * Copyright (C) 2010 Maxim Manuylov
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
 */

package manuylov.maxim.ocaml.module;

import com.intellij.CommonBundle;
import com.intellij.ide.util.BrowseFilesListener;
import com.intellij.ide.util.projectWizard.ModuleWizardStep;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.FieldPanel;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.io.IOException;

/**
 * @author Maxim.Manuylov
 *         Date: 14.05.2010
 */
class OCamlSourcesPathStep extends ModuleWizardStep {
    private final OCamlModuleBuilder myBuilder;
    private final Icon myIcon;
    private final String myHelpId;
    private JRadioButton myRbCreateSource;
    private JRadioButton myRbNoSource;
    private JTextField myTfSourceDirectoryName;
    private JTextField myTfFullPath;

    public OCamlSourcesPathStep(@NotNull final OCamlModuleBuilder builder, @Nullable final Icon icon,
            @NonNls @Nullable final String helpId) {
        myBuilder = builder;
        myIcon = icon;
        myHelpId = helpId;
    }

    @NotNull
    public JComponent getComponent() {
        final JPanel panel = new JPanel(new GridBagLayout());

        final JLabel label = new JLabel("Please specify module sources directory.");

        panel.add(label, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0,
                GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));
        myRbCreateSource = new JRadioButton("Create source directory", myBuilder.isShouldCreateSourcesDir());
        panel.add(myRbCreateSource, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0,
                GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));

        myTfSourceDirectoryName = new JTextField(myBuilder.getRelativeSourcesPath());
        final JLabel srcPathLabel = new JLabel("Enter relative path to module content root:");
        panel.add(srcPathLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0,
                GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 30, 0, 0), 0, 0));
        final FileChooserDescriptor chooserDescriptor = new FileChooserDescriptor(false, true, false, false, false,
                false);
        chooserDescriptor.setIsTreeRootVisible(true);
        final FieldPanel fieldPanel = createFieldPanel(myTfSourceDirectoryName, null,
                new BrowsePathListener(myTfSourceDirectoryName, chooserDescriptor));
        panel.add(fieldPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0,
                GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 30, 0, 10), 0, 0));

        myRbNoSource = new JRadioButton("Do not create source directory", !myBuilder.isShouldCreateSourcesDir());
        panel.add(myRbNoSource, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0,
                GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));

        final JLabel fullPathLabel = new JLabel("The following directory will be marked as a source directory:");
        panel.add(fullPathLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0,
                GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));

        myTfFullPath = new JTextField();
        myTfFullPath.setEditable(false);
        final Insets borderInsets = myTfFullPath.getBorder().getBorderInsets(myTfFullPath);
        myTfFullPath.setBorder(BorderFactory.createEmptyBorder(borderInsets.top, borderInsets.left,
                borderInsets.bottom, borderInsets.right));
        panel.add(myTfFullPath, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0,
                GridBagConstraints.SOUTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 10, 8, 10), 0, 0));

        ButtonGroup group = new ButtonGroup();
        group.add(myRbCreateSource);
        group.add(myRbNoSource);
        myTfSourceDirectoryName.getDocument().addDocumentListener(new DocumentAdapter() {
            public void textChanged(DocumentEvent event) {
                updateFullPathField();
            }
        });
        updateFullPathField();

        final StateUpdater stateUpdater = new StateUpdater() {
            public void updateState(final boolean shouldCreateSourcesDir) {
                srcPathLabel.setEnabled(shouldCreateSourcesDir);
                fieldPanel.setEnabled(shouldCreateSourcesDir);
                fullPathLabel.setVisible(shouldCreateSourcesDir);
                myTfFullPath.setVisible(shouldCreateSourcesDir);
                if (shouldCreateSourcesDir) {
                    myTfSourceDirectoryName.requestFocus();
                }
            }
        };

        myRbCreateSource.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                stateUpdater.updateState(e.getStateChange() == ItemEvent.SELECTED);
            }
        });
        stateUpdater.updateState(myBuilder.isShouldCreateSourcesDir());

        return panel;
    }

    private void updateFullPathField() {
        final String sourceDirectoryPath = getSourceDirectoryPath();
        if (sourceDirectoryPath != null) {
            myTfFullPath.setText(sourceDirectoryPath.replace('/', File.separatorChar));
        } else {
            myTfFullPath.setText("");
        }
    }

    public JComponent getPreferredFocusedComponent() {
        return myBuilder.isShouldCreateSourcesDir() ? myTfSourceDirectoryName : myRbNoSource;
    }

    public void updateDataModel() {
        myBuilder.setShouldCreateSourcesDir(myRbCreateSource.isSelected());
        myBuilder.setRelativeSourcesPath(myTfSourceDirectoryName.getText());
    }

    public boolean validate() throws ConfigurationException {
        if (!super.validate()) {
            return false;
        }

        if (myRbCreateSource.isSelected()) {
            final String sourceDirectoryPath = getSourceDirectoryPath();
            final String relativePath = myTfSourceDirectoryName.getText().trim();
            if (relativePath.length() == 0) {
                final String contentRoot = sourceDirectoryPath == null ? " "
                        : "\n\"" + FileUtil.toSystemDependentName(sourceDirectoryPath) + "\"\n";
                final String text = "Relative path to sources is empty.\nWould you like to mark the module content root"
                        + contentRoot + "as a source directory?";
                final int answer = Messages.showDialog(myTfSourceDirectoryName, text, "Mark source directory",
                        new String[] { "Mark", "Do Not Mark", CommonBundle.getCancelButtonText() }, 0,
                        Messages.getQuestionIcon());
                if (answer == 2) {
                    return false; // cancel
                }
                if (answer == 1) { // don't mark
                    myRbNoSource.doClick();
                }
            }
            if (sourceDirectoryPath != null) {
                final File rootDir = new File(getContentRootPath());
                final File srcDir = new File(sourceDirectoryPath);
                try {
                    if (!FileUtil.isAncestor(rootDir, srcDir, false)) {
                        Messages.showErrorDialog(myTfSourceDirectoryName,
                                "Source directory should be under module content root directory",
                                CommonBundle.getErrorTitle());
                        return false;
                    }
                } catch (IOException e) {
                    Messages.showErrorDialog(myTfSourceDirectoryName, e.getMessage(), CommonBundle.getErrorTitle());
                    return false;
                }
            }
        }
        return true;
    }

    @Nullable
    private String getSourceDirectoryPath() {
        final String contentEntryPath = getContentRootPath();
        final String dirName = myTfSourceDirectoryName.getText().trim().replace(File.separatorChar, '/');
        if (contentEntryPath != null) {
            return dirName.length() > 0 ? contentEntryPath + "/" + dirName : contentEntryPath;
        }
        return null;
    }

    @Nullable
    private String getContentRootPath() {
        return myBuilder.getContentEntryPath();
    }

    private class BrowsePathListener extends BrowseFilesListener {
        private final FileChooserDescriptor myChooserDescriptor;
        private final JTextField myField;

        public BrowsePathListener(JTextField textField, final FileChooserDescriptor chooserDescriptor) {
            super(textField, "Select source directory", "", chooserDescriptor);
            myChooserDescriptor = chooserDescriptor;
            myField = textField;
        }

        @Nullable
        private VirtualFile getContentEntryDir() {
            final String contentEntryPath = getContentRootPath();
            if (contentEntryPath != null) {
                return ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
                    public VirtualFile compute() {
                        return LocalFileSystem.getInstance().refreshAndFindFileByPath(contentEntryPath);
                    }
                });
            }
            return null;
        }

        public void actionPerformed(ActionEvent e) {
            final VirtualFile contentEntryDir = getContentEntryDir();
            if (contentEntryDir != null) {
                myChooserDescriptor.setRoot(contentEntryDir);
                final String textBefore = myField.getText().trim();
                super.actionPerformed(e);
                if (!textBefore.equals(myField.getText().trim())) {
                    final String fullPath = myField.getText().trim().replace(File.separatorChar, '/');
                    final VirtualFile fileByPath = LocalFileSystem.getInstance().findFileByPath(fullPath);
                    assert fileByPath != null;
                    myField.setText(VfsUtil.getRelativePath(fileByPath, contentEntryDir, File.separatorChar));
                }
            }
        }
    }

    public Icon getIcon() {
        return myIcon;
    }

    public String getHelpId() {
        return myHelpId;
    }

    private interface StateUpdater {
        void updateState(final boolean shouldCreateSourcesDir);
    }
}