/*
 * Properties.java
 *
 * Created on 2011/08/20, 16:33:03
 */
package yukihane.inqubus.gui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import static javax.swing.GroupLayout.DEFAULT_SIZE;
import static javax.swing.GroupLayout.PREFERRED_SIZE;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JRadioButton;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SpinnerNumberModel;
import javax.swing.WindowConstants;
import org.apache.commons.configuration.ConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import yukihane.inqubus.config.Config;
import yukihane.inqubus.config.Config.CookieBrowser;

/**
 *
 * @author yuki
 */
public class ConfigDialog extends JDialog {
    private static final long serialVersionUID = 1L;
    private static final Logger logger = LoggerFactory.getLogger(ConfigDialog.class);
    private static final String NEED_REBOOT = "設定を有効にするにはいんきゅばすの再起動が必要です";

    /** Creates new form Properties */
    public ConfigDialog() {
        this(null);
    }

    public ConfigDialog(JFrame owner){
        super(owner);
        setTitle("いんきゅばす 設定");

        final JTabbedPane tabbedPane = new JTabbedPane();
        final JPanel pnlNetwork = new JPanel();
        final JPanel pnlAccount = new JPanel();
        fldMail = new JTextField();
        fldPassword.setEchoChar('*');
        final JPanel pnlProxy = new JPanel();
        cbProxyUse = new JCheckBox();
        final JLabel lblProxyHost = new JLabel();
        final JLabel lblProxyPort = new JLabel();
        final JPanel pnlFile = new JPanel();
        final JPanel pnlFileOutput = new JPanel();
        final JLabel lblOutputDir = new JLabel();
        final JLabel lblOutputFileNamePattern = new JLabel();
        fldOutputDir = new JTextField();
        fldOutputFileNamePattern = new JTextField();
        cbOutputEnable = new JCheckBox();
        cbOutputCommentOverlay = new JCheckBox();
        cbOutputDisplayProgress = new JCheckBox();
        final JPanel pnlComment = new JPanel();
        final JPanel pnlCommentStandard = new JPanel();
        final JLabel lblCommentSize = new JLabel();
        final JLabel lblCommentSizeManual = new JLabel();
        final JLabel lblCommentMinSize = new JLabel();
        final JLabel lblCommentMinSizeManual = new JLabel();
        final JLabel lblCommentDisplayMax = new JLabel();
        final JLabel lblCommentDisplayMaxManual = new JLabel();
        final JLabel lblCommentOwnerOnly = new JLabel();
        final JPanel pnlCommentAdvanced = new JPanel();
        final JLabel lblFontPath = new JLabel();
        cbFontSizeArrangeDisable = new JCheckBox();
        cbCommentOpaque = new JCheckBox();
        fldFontPath = new JTextField();
        final JLabel lblFontIndex = new JLabel();
        final JLabel lblFontShadow = new JLabel();
        fldFontIndex = new JTextField();
        final JPanel pnlFfmpeg = new JPanel();
        final JPanel pnlFfmpegBinPath = new JPanel();
        final JLabel lblFfmpegPath = new JLabel("ffmpeg");
        final JLabel lblFfmpegDllPath = new JLabel("拡張ライブラリ");
        final JLabel lblMediaInfoPath = new JLabel("MediaInfo");
        final JPanel pnlNgSetting = new JPanel();
        final JPanel pnlSearch = new JPanel();

        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        pnlNetwork.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));

        pnlAccount.setBorder(BorderFactory.createTitledBorder("アカウント"));

        final JLabel lblMail = new JLabel("ID(メールアドレス)");
        final JLabel lblPassword = new JLabel("パスワード");

        GroupLayout gl_pnlAccount = new GroupLayout(pnlAccount);
        pnlAccount.setLayout(gl_pnlAccount);
        gl_pnlAccount.setHorizontalGroup(gl_pnlAccount.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addComponent(btnAccount)
            .addGroup(gl_pnlAccount.createSequentialGroup()
                .addContainerGap()
                .addGap(10)
                .addGroup(gl_pnlAccount.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(lblMail)
                    .addComponent(lblPassword)
                )
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl_pnlAccount.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(fldPassword, GroupLayout.DEFAULT_SIZE, 306, Short.MAX_VALUE)
                    .addComponent(fldMail, GroupLayout.DEFAULT_SIZE, 306, Short.MAX_VALUE)
                )
                .addContainerGap()
            )
        );
        gl_pnlAccount.setVerticalGroup(
            gl_pnlAccount.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlAccount.createSequentialGroup()
                .addContainerGap()
                .addComponent(btnAccount)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl_pnlAccount.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblMail)
                    .addComponent(fldMail, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl_pnlAccount.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblPassword)
                    .addComponent(fldPassword, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
                .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );


        final JButton btnCookie = new JButton("...");
        btnCookie.addActionListener(new FileChooseActionListener(this, JFileChooser.FILES_AND_DIRECTORIES, fldCookie));

        final ButtonGroup grpLoginType = new ButtonGroup();
        grpLoginType.add(btnAccount);
        grpLoginType.add(btnLoginCookie);

        final ButtonGroup grpCookie = new ButtonGroup();
        grpCookie.add(btnCookieIe);
        grpCookie.add(btnCookieFirefox);
        grpCookie.add(btnCookieChrome);
        grpCookie.add(btnCookieChromium);
        grpCookie.add(btnCookieOpera);
        grpCookie.add(btnCookieOther);

        final JPanel pnlCookie = new JPanel();
        pnlCookie.setBorder(BorderFactory.createTitledBorder("cookie"));
        final GroupLayout glCookie = new GroupLayout(pnlCookie);
        pnlCookie.setLayout(glCookie);
        glCookie.setAutoCreateContainerGaps(true);
        glCookie.setAutoCreateGaps(true);

        glCookie.setHorizontalGroup(glCookie.createParallelGroup()
            .addComponent(btnLoginCookie)
            .addGroup(glCookie.createSequentialGroup()
                .addGap(10)
                .addGroup(glCookie.createParallelGroup()
                    .addComponent(btnCookieIe)
                    .addComponent(btnCookieFirefox)
                    .addComponent(btnCookieChrome)
                    .addComponent(btnCookieChromium)
                    .addComponent(btnCookieOpera)
                    .addComponent(btnCookieOther)
                    .addGroup(glCookie.createSequentialGroup()
                        .addComponent(fldCookie)
                        .addComponent(btnCookie)
                    )
                )
            )
        );

        glCookie.setVerticalGroup(glCookie.createSequentialGroup()
            .addComponent(btnLoginCookie)
            .addComponent(btnCookieIe)
            .addComponent(btnCookieFirefox)
            .addComponent(btnCookieChrome)
            .addComponent(btnCookieChromium)
            .addComponent(btnCookieOpera)
            .addComponent(btnCookieOther)
            .addGroup(glCookie.createParallelGroup(Alignment.BASELINE)
                .addComponent(fldCookie)
                .addComponent(btnCookie)
            )
        );


        pnlProxy.setBorder(BorderFactory.createTitledBorder("プロキシ"));

        cbProxyUse.setText("プロキシを使用する");

        lblProxyHost.setText("ホスト名");

        lblProxyPort.setText("ポート番号");

        GroupLayout gl_pnlProxy = new GroupLayout(pnlProxy);
        pnlProxy.setLayout(gl_pnlProxy);
        gl_pnlProxy.setHorizontalGroup(
            gl_pnlProxy.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlProxy.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl_pnlProxy.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(cbProxyUse)
                    .addGroup(gl_pnlProxy.createSequentialGroup()
                        .addComponent(lblProxyHost)
                        .addGap(18, 18, 18)
                        .addComponent(fldProxyHost, GroupLayout.PREFERRED_SIZE, 168, GroupLayout.PREFERRED_SIZE))
                    .addGroup(gl_pnlProxy.createSequentialGroup()
                        .addComponent(lblProxyPort)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(fldProxyPort, GroupLayout.PREFERRED_SIZE, 76, GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(181, Short.MAX_VALUE))
        );
        gl_pnlProxy.setVerticalGroup(
            gl_pnlProxy.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlProxy.createSequentialGroup()
                .addContainerGap()
                .addComponent(cbProxyUse)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl_pnlProxy.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblProxyHost)
                    .addComponent(fldProxyHost, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl_pnlProxy.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblProxyPort)
                    .addComponent(fldProxyPort, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );


        GroupLayout gl_pnlNetwork = new GroupLayout(pnlNetwork);
        pnlNetwork.setLayout(gl_pnlNetwork);
        gl_pnlNetwork.setHorizontalGroup(
            gl_pnlNetwork.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(GroupLayout.Alignment.TRAILING, gl_pnlNetwork.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl_pnlNetwork.createParallelGroup(GroupLayout.Alignment.TRAILING)
                    .addComponent(pnlAccount, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(pnlCookie, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(pnlProxy, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                )
                .addContainerGap())
        );
        gl_pnlNetwork.setVerticalGroup(
            gl_pnlNetwork.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlNetwork.createSequentialGroup()
                .addContainerGap()
                .addComponent(pnlAccount, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(pnlCookie, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(pnlProxy, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addContainerGap()
            )
        );

        tabbedPane.addTab("ネットワーク", pnlNetwork);

        pnlFileVideo.setBorder(BorderFactory.createTitledBorder("動画"));

        pnlFileComment.setBorder(BorderFactory.createTitledBorder("コメント"));

        pnlFileOutput.setBorder(BorderFactory.createTitledBorder("変換動画"));

        lblOutputDir.setText("保存フォルダ");

        lblOutputFileNamePattern.setText("ファイル名規則");

        btnOutputDir.addActionListener(
                new FileChooseActionListener(ConfigDialog.this, JFileChooser.DIRECTORIES_ONLY, fldOutputDir));

        cbOutputEnable.setText("変換");

        cbOutputCommentOverlay.setText("コメント付与");

        cbOutputDisplayProgress.setText("変換中動画表示");

        GroupLayout gl_pnlFileOutput = new GroupLayout(pnlFileOutput);
        pnlFileOutput.setLayout(gl_pnlFileOutput);
        gl_pnlFileOutput.setHorizontalGroup(
            gl_pnlFileOutput.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlFileOutput.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl_pnlFileOutput.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(lblOutputFileNamePattern)
                    .addComponent(lblOutputDir))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl_pnlFileOutput.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addGroup(gl_pnlFileOutput.createSequentialGroup()
                        .addComponent(fldOutputDir, GroupLayout.DEFAULT_SIZE, 262, Short.MAX_VALUE)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(btnOutputDir)
                        .addContainerGap())
                    .addGroup(GroupLayout.Alignment.TRAILING, gl_pnlFileOutput.createSequentialGroup()
                        .addComponent(fldOutputFileNamePattern, GroupLayout.PREFERRED_SIZE, 167, GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(ComponentPlacement.RELATED, 23, Short.MAX_VALUE)
                        .addGroup(gl_pnlFileOutput.createParallelGroup(GroupLayout.Alignment.LEADING)
                            .addComponent(cbOutputDisplayProgress)
                            .addGroup(gl_pnlFileOutput.createSequentialGroup()
                                .addComponent(cbOutputEnable)
                                .addPreferredGap(ComponentPlacement.RELATED)
                                .addComponent(cbOutputCommentOverlay))))))
        );
        gl_pnlFileOutput.setVerticalGroup(
            gl_pnlFileOutput.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlFileOutput.createSequentialGroup()
                .addGroup(gl_pnlFileOutput.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblOutputDir)
                    .addComponent(fldOutputDir, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addComponent(btnOutputDir))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl_pnlFileOutput.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblOutputFileNamePattern)
                    .addComponent(fldOutputFileNamePattern, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addComponent(cbOutputCommentOverlay)
                    .addComponent(cbOutputEnable))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(cbOutputDisplayProgress)
                .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        GroupLayout gl_pnlFile = new GroupLayout(pnlFile);
        pnlFile.setLayout(gl_pnlFile);
        gl_pnlFile.setHorizontalGroup(
            gl_pnlFile.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(GroupLayout.Alignment.TRAILING, gl_pnlFile.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl_pnlFile.createParallelGroup(GroupLayout.Alignment.TRAILING)
                    .addComponent(pnlFileOutput, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(pnlFileVideo, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(pnlFileComment, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                )
                .addContainerGap())
        );
        gl_pnlFile.setVerticalGroup(
            gl_pnlFile.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlFile.createSequentialGroup()
                .addContainerGap()
                .addComponent(pnlFileVideo, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(pnlFileComment, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(pnlFileOutput, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addContainerGap())
        );

        tabbedPane.addTab("ファイル", pnlFile);

        pnlCommentStandard.setBorder(BorderFactory.createTitledBorder(""));

        lblCommentSize.setText("再生時間に応じたコメントの取得数");

        lblCommentSizeManual.setText("手動取得");

        lblCommentMinSize.setText("1分当たりのコメント取得数");

        lblCommentMinSizeManual.setText("手動取得");

        lblCommentDisplayMax.setText("1画面に表示する最大コメント数");

        lblCommentDisplayMaxManual.setText("手動設定");

        lblCommentOwnerOnly.setText("取得するコメントの種類");

        GroupLayout gl_pnlCommentStandard = new GroupLayout(pnlCommentStandard);
        pnlCommentStandard.setLayout(gl_pnlCommentStandard);
        gl_pnlCommentStandard.setAutoCreateContainerGaps(true);
        gl_pnlCommentStandard.setAutoCreateGaps(true);
        gl_pnlCommentStandard.setHorizontalGroup(
            gl_pnlCommentStandard.createSequentialGroup()
                .addGroup(gl_pnlCommentStandard.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(lblCommentSize)
                    .addComponent(lblCommentMinSize)
                    .addComponent(lblCommentDisplayMax)
                    .addComponent(lblCommentOwnerOnly)
                )
                .addGroup(gl_pnlCommentStandard.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addGroup(gl_pnlCommentStandard.createSequentialGroup()
                        .addGroup(gl_pnlCommentStandard.createParallelGroup(GroupLayout.Alignment.LEADING)
                            .addComponent(cbCommentSizeAuto)
                            .addComponent(cbCommentMinDisable)
                            .addComponent(cbCommentMinSizeAuto)
                            .addComponent(cbCommentDisplaySizeDefault)
                        )
                        .addGroup(gl_pnlCommentStandard.createParallelGroup(GroupLayout.Alignment.LEADING)
                            .addComponent(lblCommentSizeManual)
                            .addComponent(lblCommentMinSizeManual)
                            .addComponent(lblCommentDisplayMaxManual))
                        .addGroup(gl_pnlCommentStandard.createParallelGroup(GroupLayout.Alignment.LEADING)
                            .addComponent(fldCommentSizeManual, 48, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                            .addComponent(fldCommentMinSizeManual, 48, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                            .addComponent(fldCommentDisplaySizeManual, 48, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
                    )
                    .addGroup(gl_pnlCommentStandard.createSequentialGroup()
                        .addComponent(cbCommentOwnerOnly)
                    )
                )
        );
        gl_pnlCommentStandard.setVerticalGroup(
            gl_pnlCommentStandard.createSequentialGroup()
                .addGroup(gl_pnlCommentStandard.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblCommentSize)
                    .addComponent(cbCommentSizeAuto)
                    .addComponent(lblCommentSizeManual)
                    .addComponent(fldCommentSizeManual))
                .addGroup(gl_pnlCommentStandard.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblCommentMinSize)
                    .addComponent(cbCommentMinDisable)
                )
                .addGroup(gl_pnlCommentStandard.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(cbCommentMinSizeAuto)
                    .addComponent(lblCommentMinSizeManual)
                    .addComponent(fldCommentMinSizeManual))
                .addGroup(gl_pnlCommentStandard.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblCommentDisplayMax)
                    .addComponent(cbCommentDisplaySizeDefault)
                    .addComponent(lblCommentDisplayMaxManual)
                    .addComponent(fldCommentDisplaySizeManual))
                .addGroup(gl_pnlCommentStandard.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblCommentOwnerOnly)
                    .addComponent(cbCommentOwnerOnly)
                )
        );

        pnlCommentAdvanced.setBorder(BorderFactory.createTitledBorder("高度な設定"));

        lblFontPath.setText("フォントパス");

        cbFontSizeArrangeDisable.setText("フォントサイズ調整を行わない");

        cbCommentOpaque.setText("コメント透明化を行わない");

        final JButton btnFontPath = new JButton("...");
        btnFontPath.addActionListener(
                new FileChooseActionListener(ConfigDialog.this, JFileChooser.FILES_ONLY, fldFontPath));

        lblFontIndex.setText("フォント番号");

        lblFontShadow.setText("影の種類");
        final ComboBoxModel<String> cbmFontShadow
                = new DefaultComboBoxModel<>(new String[]{"0:なし", "1:ニコニコ動画風", "2:右下", "3:囲い込み"});
        cmbFontShadow.setModel(cbmFontShadow);

        GroupLayout gl_pnlCommentAdvanced = new GroupLayout(pnlCommentAdvanced);
        pnlCommentAdvanced.setLayout(gl_pnlCommentAdvanced);
        gl_pnlCommentAdvanced.setHorizontalGroup(
            gl_pnlCommentAdvanced.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlCommentAdvanced.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl_pnlCommentAdvanced.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(cbCommentOpaque)
                    .addGroup(gl_pnlCommentAdvanced.createSequentialGroup()
                        .addGroup(gl_pnlCommentAdvanced.createParallelGroup(GroupLayout.Alignment.LEADING)
                            .addComponent(lblFontPath)
                            .addComponent(lblFontIndex)
                            .addComponent(lblFontShadow))
                        .addGap(18, 18, 18)
                        .addGroup(gl_pnlCommentAdvanced.createParallelGroup(GroupLayout.Alignment.LEADING)
                            .addComponent(fldFontPath, GroupLayout.DEFAULT_SIZE, 275, Short.MAX_VALUE)
                            .addGroup(gl_pnlCommentAdvanced.createParallelGroup(GroupLayout.Alignment.TRAILING, false)
                                .addComponent(cmbFontShadow, GroupLayout.Alignment.LEADING)
                                .addComponent(fldFontIndex, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 48, Short.MAX_VALUE)))
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(btnFontPath))
                    .addComponent(cbFontSizeArrangeDisable))
                .addContainerGap())
        );
        gl_pnlCommentAdvanced.setVerticalGroup(
            gl_pnlCommentAdvanced.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlCommentAdvanced.createSequentialGroup()
                .addGroup(gl_pnlCommentAdvanced.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblFontPath)
                    .addComponent(fldFontPath, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addComponent(btnFontPath))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl_pnlCommentAdvanced.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblFontIndex)
                    .addComponent(fldFontIndex, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl_pnlCommentAdvanced.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblFontShadow)
                    .addComponent(cmbFontShadow, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(cbFontSizeArrangeDisable)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(cbCommentOpaque)
                .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        GroupLayout gl_pnlComment = new GroupLayout(pnlComment);
        pnlComment.setLayout(gl_pnlComment);
        gl_pnlComment.setHorizontalGroup(
            gl_pnlComment.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(GroupLayout.Alignment.TRAILING, gl_pnlComment.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl_pnlComment.createParallelGroup(GroupLayout.Alignment.TRAILING)
                    .addComponent(pnlCommentAdvanced, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(pnlCommentStandard, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        gl_pnlComment.setVerticalGroup(
            gl_pnlComment.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlComment.createSequentialGroup()
                .addContainerGap()
                .addComponent(pnlCommentStandard, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(pnlCommentAdvanced, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addContainerGap(168, Short.MAX_VALUE))
        );

        tabbedPane.addTab("コメント", pnlComment);

        pnlFfmpegBinPath.setBorder(BorderFactory.createTitledBorder("ファイルパス"));

        final JButton btnFfmpegPath = new JButton("...");
        btnFfmpegPath.addActionListener(
                new FileChooseActionListener(ConfigDialog.this, JFileChooser.FILES_ONLY, fldFfmpegPath));

        final JButton btnFfmpegDllPath = new JButton("...");
        btnFfmpegDllPath.addActionListener(
                new FileChooseActionListener(ConfigDialog.this, JFileChooser.FILES_ONLY, fldFfmpegDllPath));

        final JButton btnMediaInfoPath = new JButton("...");
        btnMediaInfoPath.addActionListener(
                new FileChooseActionListener(ConfigDialog.this, JFileChooser.FILES_ONLY, fldMediaInfoPath));

        GroupLayout gl_jPanel10 = new GroupLayout(pnlFfmpegBinPath);
        pnlFfmpegBinPath.setLayout(gl_jPanel10);
        gl_jPanel10.setHorizontalGroup(
            gl_jPanel10.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_jPanel10.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl_jPanel10.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(lblFfmpegPath)
                    .addComponent(lblFfmpegDllPath)
                    .addComponent(lblMediaInfoPath)
                )
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl_jPanel10.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(fldFfmpegDllPath, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 280, Short.MAX_VALUE)
                    .addComponent(fldFfmpegPath, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 280, Short.MAX_VALUE)
                    .addComponent(fldMediaInfoPath, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 280, Short.MAX_VALUE)
                )
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl_jPanel10.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(btnFfmpegPath, GroupLayout.Alignment.TRAILING)
                    .addComponent(btnFfmpegDllPath, GroupLayout.Alignment.TRAILING)
                    .addComponent(btnMediaInfoPath, GroupLayout.Alignment.TRAILING)
                )
                .addContainerGap())
        );
        gl_jPanel10.setVerticalGroup(
            gl_jPanel10.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_jPanel10.createSequentialGroup()
                .addGroup(gl_jPanel10.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblFfmpegPath)
                    .addComponent(fldFfmpegPath, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addComponent(btnFfmpegPath))
                .addGroup(gl_jPanel10.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblFfmpegDllPath)
                    .addComponent(fldFfmpegDllPath, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addComponent(btnFfmpegDllPath))
                .addGroup(gl_jPanel10.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblMediaInfoPath)
                    .addComponent(fldMediaInfoPath, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addComponent(btnMediaInfoPath))
            )
        );

        pnlFfmpegParam.setBorder(BorderFactory.createTitledBorder("ffmpegパラメータ"));

        // ffmpeg - その他
        final JPanel pnlFffmpegEtc = new JPanel();
        pnlFffmpegEtc.setBorder(BorderFactory.createTitledBorder("その他"));
        final GroupLayout glFfmpegEtc = new GroupLayout(pnlFffmpegEtc);
        glFfmpegEtc.setAutoCreateContainerGaps(true);
        glFfmpegEtc.setAutoCreateGaps(true);
        pnlFffmpegEtc.setLayout(glFfmpegEtc);

        glFfmpegEtc.setHorizontalGroup(glFfmpegEtc.createParallelGroup()
            .addComponent(cbDisplayConverting)
            .addComponent(cbDllDisabled)
        );
        glFfmpegEtc.setVerticalGroup(glFfmpegEtc.createSequentialGroup()
            .addComponent(cbDisplayConverting)
            .addComponent(cbDllDisabled)
        );

        final GroupLayout glFfmpeg = new GroupLayout(pnlFfmpeg);
        glFfmpeg.setAutoCreateContainerGaps(true);
        pnlFfmpeg.setLayout(glFfmpeg);
        glFfmpeg.setHorizontalGroup(glFfmpeg.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addComponent(pnlFfmpegBinPath)
            .addComponent(pnlFfmpegParam)
            .addComponent(pnlFffmpegEtc)
        );
        glFfmpeg.setVerticalGroup(glFfmpeg.createSequentialGroup()
            .addComponent(pnlFfmpegBinPath, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(ComponentPlacement.RELATED)
            .addComponent(pnlFfmpegParam)
            .addComponent(pnlFffmpegEtc)
        );

        tabbedPane.addTab("ffmpeg", pnlFfmpeg);

        pnlNgWord.setBorder(BorderFactory.createTitledBorder("NGワード"));
        pnlNgId.setBorder(BorderFactory.createTitledBorder("NGID"));

        GroupLayout gl_pnlNgSetting = new GroupLayout(pnlNgSetting);
        pnlNgSetting.setLayout(gl_pnlNgSetting);
        gl_pnlNgSetting.setHorizontalGroup(
            gl_pnlNgSetting.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(GroupLayout.Alignment.TRAILING, gl_pnlNgSetting.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl_pnlNgSetting.createParallelGroup(GroupLayout.Alignment.TRAILING)
                    .addComponent(pnlNgId, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(pnlNgWord, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        gl_pnlNgSetting.setVerticalGroup(
            gl_pnlNgSetting.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlNgSetting.createSequentialGroup()
                .addContainerGap()
                .addComponent(pnlNgWord, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(pnlNgId, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
        );

        tabbedPane.addTab("NG設定", pnlNgSetting);

        pnlSearchVideo.setBorder(BorderFactory.createTitledBorder("動画検索対象フォルダ"));
        pnlSearchComment.setBorder(BorderFactory.createTitledBorder("コメント検索対象フォルダ"));

        final JLabel lblSearchNeedReboot = new JLabel(NEED_REBOOT);

        GroupLayout gl_pnlSearch = new GroupLayout(pnlSearch);
        pnlSearch.setLayout(gl_pnlSearch);
        gl_pnlSearch.setHorizontalGroup(
            gl_pnlSearch.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(GroupLayout.Alignment.TRAILING, gl_pnlSearch.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl_pnlSearch.createParallelGroup(GroupLayout.Alignment.TRAILING)
                    .addComponent(lblSearchNeedReboot, Alignment.CENTER)
                    .addComponent(pnlSearchComment, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(pnlSearchVideo, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        gl_pnlSearch.setVerticalGroup(
            gl_pnlSearch.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl_pnlSearch.createSequentialGroup()
                .addContainerGap()
                .addComponent(lblSearchNeedReboot)
                .addComponent(pnlSearchVideo, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(pnlSearchComment, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap()
            )
        );

        tabbedPane.addTab("検索", pnlSearch);

        final JButton btnOk = new JButton("保存");
        btnOk.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                saveConfig();
                dispose();
            }
        });
        final JButton btnCancel = new JButton("取消");
        btnCancel.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                dispose();
            }
        });
        final JPanel pnlButton = new JPanel();

        final GroupLayout glButton = new GroupLayout(pnlButton);
        pnlButton.setLayout(glButton);
        glButton.setHorizontalGroup(glButton.createSequentialGroup()
            .addPreferredGap(ComponentPlacement.UNRELATED, PREFERRED_SIZE, Short.MAX_VALUE)
            .addComponent(btnOk)
            .addPreferredGap(ComponentPlacement.UNRELATED)
            .addComponent(btnCancel)
            .addContainerGap()
        );
        glButton.setVerticalGroup(glButton.createSequentialGroup()
            .addGroup(glButton.createParallelGroup(Alignment.BASELINE)
                .addComponent(btnOk)
                .addComponent(btnCancel)
            )
        );

        GroupLayout layout = new GroupLayout(getContentPane());
        layout.setAutoCreateContainerGaps(true);
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addComponent(tabbedPane, GroupLayout.DEFAULT_SIZE, 460, Short.MAX_VALUE)
            .addComponent(pnlButton)
        );
        layout.setVerticalGroup(
            layout.createSequentialGroup()
                .addComponent(tabbedPane,GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
                .addComponent(pnlButton)
        );

        final JLabel lblSystemThreadDownload = new JLabel("ダウンロードスレッド数");
        final JLabel lblSystemWaitDownload = new JLabel("ダウンロード開始最小間隔(秒)");
        final JLabel lblSystemThreadConvert = new JLabel("変換スレッド数");
        final JLabel lblSystemTempDir = new JLabel("作業フォルダ");
        final JButton btnSystemTempDir = new JButton("...");
        btnSystemTempDir.addActionListener(
                new FileChooseActionListener(ConfigDialog.this, JFileChooser.DIRECTORIES_ONLY, fldSystemTempDir));

        mdlSystemThreadDownload.setMinimum(1);
        final JSpinner fldSystemThreadDownload = new JSpinner(mdlSystemThreadDownload);
        mdlSystemWaitDownload.setMinimum(0);
        final JSpinner fldSystemWaitDownload = new JSpinner(mdlSystemWaitDownload);
        mdlSystemThreadConvert.setMinimum(1);
        final JSpinner fldSystemThreadConvert = new JSpinner(mdlSystemThreadConvert);

        final JPanel pnlSystemGeneral = new JPanel();
        pnlSystemGeneral.setBorder(BorderFactory.createTitledBorder("システム"));
        final GroupLayout glSystemGenegal = new GroupLayout(pnlSystemGeneral);
        glSystemGenegal.setAutoCreateGaps(true);
        glSystemGenegal.setAutoCreateContainerGaps(true);
        pnlSystemGeneral.setLayout(glSystemGenegal);

        glSystemGenegal.setHorizontalGroup(glSystemGenegal.createParallelGroup()
            .addGroup(glSystemGenegal.createSequentialGroup()
                .addGroup(glSystemGenegal.createParallelGroup()
                    .addComponent(lblSystemThreadDownload)
                    .addComponent(lblSystemWaitDownload)
                    .addComponent(lblSystemThreadConvert))
                .addGroup(glSystemGenegal.createParallelGroup()
                    .addComponent(fldSystemThreadDownload, PREFERRED_SIZE, 50, PREFERRED_SIZE)
                    .addComponent(fldSystemWaitDownload, PREFERRED_SIZE, 55, PREFERRED_SIZE)
                    .addComponent(fldSystemThreadConvert, PREFERRED_SIZE, 50, PREFERRED_SIZE)
                )
            )
            .addGroup(glSystemGenegal.createSequentialGroup()
                .addComponent(lblSystemTempDir)
                .addComponent(fldSystemTempDir, DEFAULT_SIZE, 400, Short.MAX_VALUE)
                .addComponent(btnSystemTempDir)
                )
            );

        glSystemGenegal.setVerticalGroup(glSystemGenegal.createSequentialGroup()
            .addGroup(glSystemGenegal.createParallelGroup(Alignment.BASELINE)
                .addComponent(lblSystemThreadDownload)
                .addComponent(fldSystemThreadDownload, DEFAULT_SIZE, PREFERRED_SIZE, PREFERRED_SIZE))
            .addGroup(glSystemGenegal.createParallelGroup(Alignment.BASELINE)
                .addComponent(lblSystemWaitDownload)
                .addComponent(fldSystemWaitDownload, DEFAULT_SIZE, PREFERRED_SIZE, PREFERRED_SIZE))
            .addGroup(glSystemGenegal.createParallelGroup(Alignment.BASELINE)
                .addComponent(lblSystemThreadConvert)
                .addComponent(fldSystemThreadConvert, DEFAULT_SIZE, PREFERRED_SIZE, PREFERRED_SIZE))
            .addGroup(glSystemGenegal.createParallelGroup(Alignment.BASELINE)
                .addComponent(lblSystemTempDir)
                .addComponent(fldSystemTempDir, DEFAULT_SIZE, PREFERRED_SIZE, PREFERRED_SIZE)
                .addComponent(btnSystemTempDir)
            )
        );

        final JLabel lblSystemNeedReboot = new JLabel("設定を有効にするにはいんきゅばすの再起動が必要です");

        final JPanel pnlSystem = new JPanel();
        pnlSystem.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
        final GroupLayout glSystem = new GroupLayout(pnlSystem);
        glSystem.setAutoCreateGaps(true);
        glSystem.setAutoCreateContainerGaps(true);
        pnlSystem.setLayout(glSystem);

        glSystem.setHorizontalGroup(glSystem.createParallelGroup()
            .addComponent(lblSystemNeedReboot, Alignment.CENTER)
            .addComponent(pnlSystemGeneral));

        glSystem.setVerticalGroup(glSystem.createSequentialGroup()
            .addComponent(lblSystemNeedReboot)
            .addComponent(pnlSystemGeneral));

        tabbedPane.add("システム", pnlSystem);

        pack();
        setMinimumSize(getSize());
        loadConfig();
    }

    // ネットワーク
    // ネットワーク - アカウント
    final JRadioButton btnAccount = new JRadioButton("次の情報を使用してログインする");
    private final JTextField fldMail;
    private final JPasswordField fldPassword = new JPasswordField();
    // ネットワーク - cookie
    final JRadioButton btnLoginCookie = new JRadioButton("ブラウザのログイン情報を使用してアクセスする");
    final JRadioButton btnCookieIe = new JRadioButton("IE(7以降) (Windows)");
    final JRadioButton btnCookieFirefox = new JRadioButton("Firefox(4以降) (Windows)");
    final JRadioButton btnCookieChrome = new JRadioButton("Google Chrome (Windows)");
    final JRadioButton btnCookieChromium = new JRadioButton("Chrome派生ブラウザ (Windows)");
    final JRadioButton btnCookieOpera = new JRadioButton("Opera (Windows)");
    final JRadioButton btnCookieOther = new JRadioButton("上記以外 - cookie保存ディレクトリを次に入力");
    final JTextField fldCookie = new JTextField();
    // ネットワーク - プロキシ
    private final JCheckBox cbProxyUse;
    private final JTextField fldProxyHost = new JTextField();
    private final NumberFormattedTextField fldProxyPort = new NumberFormattedTextField();
    // ファイル
    // ファイル - 動画
    private final DownloadLocationPanel pnlFileVideo = new DownloadLocationPanel();
    // ファイル - コメント
    private final DownloadLocationPanel pnlFileComment = new DownloadLocationPanel();
    // ファイル - 変換動画
    private final JTextField fldOutputDir;
    private final JTextField fldOutputFileNamePattern;
    private final JButton btnOutputDir = new JButton("...");
    private final JCheckBox cbOutputEnable;
    private final JCheckBox cbOutputCommentOverlay;
    private final JCheckBox cbOutputDisplayProgress;
    // コメント
    // コメント
    private final JCheckBox cbCommentSizeAuto = new JCheckBox("自動調整");
    private final NumberFormattedTextField fldCommentSizeManual = new NumberFormattedTextField();
    private final JCheckBox cbCommentMinDisable = new JCheckBox("少コメ");
    private final JCheckBox cbCommentMinSizeAuto = new JCheckBox("自動調整");
    private final NumberFormattedTextField fldCommentMinSizeManual = new NumberFormattedTextField();
    private final JCheckBox cbCommentDisplaySizeDefault = new JCheckBox("標準(30)");
    private final NumberFormattedTextField fldCommentDisplaySizeManual = new NumberFormattedTextField();
    private final JCheckBox cbCommentOwnerOnly = new JCheckBox("投稿者コメントのみ");
    // コメント - 高度な設定
    private final JTextField fldFontPath;
    private final JTextField fldFontIndex;
    private final JComboBox<String> cmbFontShadow = new JComboBox<>();
    private final JCheckBox cbFontSizeArrangeDisable;
    private final JCheckBox cbCommentOpaque;
    // ffmpeg
    // ffmpeg - ファイルパス
    private final JTextField fldFfmpegPath = new JTextField();
    private final JTextField fldFfmpegDllPath = new JTextField();
    private final JTextField fldMediaInfoPath = new JTextField();
    // ffmpeg - ffmpegパラメータ
    private final FfmpegParamPanel pnlFfmpegParam = new FfmpegParamPanel();
    // ffmpeg - その他
    private final JCheckBox cbDisplayConverting = new JCheckBox("変換中動画表示");
    private final JCheckBox cbDllDisabled = new JCheckBox("拡張DLL無効化(デバッグ用)");
    // NG設定
    // NG設定 - NGワード
    private final NgRegisterPanel pnlNgWord = new NgRegisterPanel();
    // NG設定 - NGID
    private final NgRegisterPanel pnlNgId = new NgRegisterPanel();
    // 検索
    // 検索 - 動画検索対象フォルダ
    private final DirectoryRegisterPanel pnlSearchVideo = new DirectoryRegisterPanel();
    // 検索 - コメント検索対象フォルダ
    private final DirectoryRegisterPanel pnlSearchComment = new DirectoryRegisterPanel();
    // システム
    private final SpinnerNumberModel mdlSystemThreadDownload = new SpinnerNumberModel();
    private final SpinnerNumberModel mdlSystemWaitDownload = new SpinnerNumberModel();
    private final SpinnerNumberModel mdlSystemThreadConvert = new SpinnerNumberModel();
    private final JTextField fldSystemTempDir = new JTextField();


    // End of variables declaration//GEN-END:variables

    private void loadConfig() {
        final Config p = Config.INSTANCE;

        /*
         * ネットワーク - アカウント
         */
        btnAccount.setSelected(p.getNeedsLogin());
        fldMail.setText(p.getId());
        fldPassword.setText(p.getPassword());

        /*
         * ネットワーク - cookie
         */
        btnLoginCookie.setSelected(!p.getNeedsLogin());
        final CookieBrowser cookieBrowser = p.getCookieBrowser();
        final JRadioButton enabledBrowser;
        switch(cookieBrowser){
            case MSIE:
                enabledBrowser = btnCookieIe;
                break;
            case FIREFOX:
                enabledBrowser = btnCookieFirefox;
                break;
            case CHROME:
                enabledBrowser = btnCookieChrome;
                break;
            case CHROMIUM:
                enabledBrowser = btnCookieChromium;
                break;
            case OPERA:
                enabledBrowser = btnCookieOpera;
                break;
            case OTHER:
                enabledBrowser = btnCookieOther;
                break;
            default:
                throw new UnsupportedOperationException();
        }
        enabledBrowser.setSelected(true);
        fldCookie.setText(p.getCookieDir());

        /*
         * ネットワーク - プロキシ
         */
        cbProxyUse.setSelected(p.getProxyUse());
        fldProxyHost.setText(p.getProxyHost());
        fldProxyPort.setText(p.getProxyPort());

        /*
         * ファイル - 動画
         */
        pnlFileVideo.fldDir.setText(p.getVideoDir());
        pnlFileVideo.fldFileNamePattern.setText(p.getVideoFileNamePattern());
        pnlFileVideo.cbLocal.setSelected(p.getVideoUseLocal());

        /*
         * ファイル - コメント
         */
        pnlFileComment.fldDir.setText(p.getCommentDir());
        pnlFileComment.fldFileNamePattern.setText(p.getCommentFileNamePattern());
        pnlFileComment.cbLocal.setSelected(p.getCommentUseLocal());

        /*
         * ファイル - 変換動画
         */
        fldOutputDir.setText(p.getOutputDir());
        fldOutputFileNamePattern.setText(p.getOutputFileNamePattern());
        cbOutputEnable.setSelected(p.getOutputEnable());
        cbOutputCommentOverlay.setSelected(p.getOutputCommentOverlay());
        cbOutputDisplayProgress.setSelected(p.getOutputDisplayProgress());

        /**
         * コメント
         */
        cbCommentSizeAuto.setSelected(p.getCommentSizeAutosize());
        fldCommentSizeManual.setText(p.getCommentSizeManual());
        cbCommentMinDisable.setSelected(p.getCommentMinDisabled());
        cbCommentMinSizeAuto.setSelected(p.getCommentMinSizeAutosize());
        fldCommentSizeManual.setText(p.getCommentMinSizeManual());
        cbCommentDisplaySizeDefault.setSelected(p.getCommentDisplaySizeDefault());
        fldCommentDisplaySizeManual.setText(p.getCommentDisplaySizeManual());
        cbCommentOwnerOnly.setSelected(p.getCommentOwnerOnly());

        /**
         * コメント - 高度な設定
         */
        fldFontPath.setText(p.getFontPath());
        fldFontIndex.setText(p.getFontIndex());
        cmbFontShadow.setSelectedIndex(p.getFontShadow());
        cbFontSizeArrangeDisable.setSelected(p.getFontSizeArrangeDisable());
        cbCommentOpaque.setSelected(p.getCommentOpaque());

        /*
         * ffmpeg - ファイルパス
         */
        fldFfmpegPath.setText(p.getFfmpegPath());
        fldFfmpegDllPath.setText(p.getFfmpegDllPath());
        fldMediaInfoPath.setText(p.getMediaInfoPath());

        /*
         * ffmpeg - ffmpegパラメータ
         */
        pnlFfmpegParam.init(p);

        /*
         * ffmpeg - その他
         */
        cbDisplayConverting.setSelected(p.getFfmpegDisplayConverting());
        cbDllDisabled.setSelected(p.getFfmpegDllDisabled());

        /*
         * NG 設定
         */
        final List<String> ngWords = p.getNgWords();
        pnlNgWord.init(ngWords);
        final List<String> ngIds = p.getNgIds();
        pnlNgId.init(ngIds);

        /*
         * 検索
         */
        pnlSearchVideo.init(p.getSearchVideoDirs());
        pnlSearchComment.init(p.getSearchCommentDirs());

        /*
         * システム
         */
        mdlSystemThreadDownload.setValue(p.getSystemDownloadThread());
        mdlSystemWaitDownload.setValue(p.getSystemDownloadWait());
        mdlSystemThreadConvert.setValue(p.getSystemConvertThread());
        fldSystemTempDir.setText(p.getSystemTempDir());
    }

    private void saveConfig() {
        final Config p = Config.INSTANCE;

        /*
         * ネットワーク - アカウント
         */
        p.setNeedsLogin(btnAccount.isSelected());
        p.setId(fldMail.getText());
        p.setPassword(String.valueOf(fldPassword.getPassword()));

        /*
         * ネットワーク - cookie
         */
        final CookieBrowser cookieBrowser;
        if(btnCookieIe.isSelected()) {
            cookieBrowser = CookieBrowser.MSIE;
        } else if(btnCookieFirefox.isSelected()) {
            cookieBrowser = CookieBrowser.FIREFOX;
        } else if (btnCookieChrome.isSelected()) {
            cookieBrowser = CookieBrowser.CHROME;
        } else if (btnCookieChromium.isSelected()) {
            cookieBrowser = CookieBrowser.CHROMIUM;
        } else if(btnCookieOpera.isSelected()) {
            cookieBrowser = CookieBrowser.OPERA;
        } else if(btnCookieOther.isSelected()) {
            cookieBrowser = CookieBrowser.OTHER;
        } else {
            throw new UnsupportedOperationException();
        }
        p.setCookieBrowser(cookieBrowser);
        p.setCookieDir(fldCookie.getText());

        /*
         * ネットワーク - プロキシ
         */
        p.setProxyUse(cbProxyUse.isSelected());
        p.setProxyHost(fldProxyHost.getText());
        p.setProxyPort(fldProxyPort.getText());

        /*
         * ファイル - 動画
         */
        p.setVideoDir(pnlFileVideo.fldDir.getText());
        p.setVideoFileNamePattern(pnlFileVideo.fldFileNamePattern.getText());
        p.setVideoUseLocal(pnlFileVideo.cbLocal.isSelected());

        /*
         * ファイル - コメント
         */
        p.setCommentDir(pnlFileComment.fldDir.getText());
        p.setCommentFileNamePattern(pnlFileComment.fldFileNamePattern.getText());
        p.setCommentUseLocal(pnlFileComment.cbLocal.isSelected());

        /*
         * ファイル - 変換動画
         */
        p.setOutputDir(fldOutputDir.getText());
        p.setOutputFileNamePattern(fldOutputFileNamePattern.getText());
        p.setOutputEnable(cbOutputEnable.isSelected());
        p.setOutputCommentOverlay(cbOutputCommentOverlay.isSelected());
        p.setOutputDisplayProgress(cbOutputDisplayProgress.isSelected());

        /*
         * コメント
         */
        p.setCommentSizeAutosize(cbCommentSizeAuto.isSelected());
        p.setCommentSizeManual(fldCommentSizeManual.getText());
        p.setCommentMinDisabled(cbCommentMinDisable.isSelected());
        p.setCommentMinSizeAutosize(cbCommentMinSizeAuto.isSelected());
        p.setCommentMinSizeManual(fldCommentSizeManual.getText());
        p.setCommentDisplaySizeDefault(cbCommentDisplaySizeDefault.isSelected());
        p.setCommentDisplaySizeManual(fldCommentDisplaySizeManual.getText());
        p.setCommentOwnerOnly(cbCommentOwnerOnly.isSelected());

        /**
         * コメント - 高度な設定
         */
        p.setFontPath(fldFontPath.getText());
        p.setFontIndex(fldFontIndex.getText());
        p.setFontShadow(cmbFontShadow.getSelectedIndex());
        p.setFontSizeArrangeDisable(cbFontSizeArrangeDisable.isSelected());
        p.setCommentOpaque(cbCommentOpaque.isSelected());

        /*
         * ffmpeg - ファイルパス
         */
        p.setFfmpegPath(fldFfmpegPath.getText());
        p.setFfmpegDllPath(fldFfmpegDllPath.getText());
        p.setMediaInfoPath(fldMediaInfoPath.getText());

        /*
         * ffmpeg - ffmpegパラメータ
         */
        final File fileFfmpegOption = pnlFfmpegParam.mdlFfmpegOption.getSelectedFile();
        p.setFfmpegOptionFile(fileFfmpegOption != null ? fileFfmpegOption.getPath() : null);
        p.setFfmpegExtension(pnlFfmpegParam.fldFfmpegOptionExtension.getText());
        p.setFfmpegMainOption(pnlFfmpegParam.fldFfmpegOptionMain.getText());
        p.setFfmpegInOption(pnlFfmpegParam.fldFfmpegOptionIn.getText());
        p.setFfmpegOutOption(pnlFfmpegParam.fldFfmpegOptionOut.getText());
        p.setFfmpegAvOption(pnlFfmpegParam.fldFfmpegOptionAv.getText());
        p.setFfmpegResizeEnable(pnlFfmpegParam.cbFfmpegOptionResize.isSelected());
        p.setFfmpegResizeWidth(pnlFfmpegParam.fldFfmpegOptionResizeWidth.getText());
        p.setFfmpegResizeHeight(pnlFfmpegParam.fldFfmpegOptionResizeHeight.getText());
        p.setFfmpegKeepAspect(pnlFfmpegParam.cbFfmpegOptionKeepAspect.isSelected());

        /*
         * ffmpeg - その他
         */
        p.setFfmpegDisplayConverting(cbDisplayConverting.isSelected());
        p.setFfmpegDllDisabled(cbDllDisabled.isSelected());

        /*
         * NG 設定
         */
        p.setNgWords(pnlNgWord.getAllElements());
        p.setNgIds(pnlNgId.getAllElements());

        /*
         * 検索
         */
        p.setSearchVideoDirs(pnlSearchVideo.getAllElements());
        p.setSearchCommentDirs(pnlSearchComment.getAllElements());

        /*
         * システム
         */
        p.setSystemDownloadThread(mdlSystemThreadDownload.getNumber().intValue());
        p.setSystemDownloadWait(mdlSystemWaitDownload.getNumber().intValue());
        p.setSystemConvertThread(mdlSystemThreadConvert.getNumber().intValue());
        p.setSystemTempDir(fldSystemTempDir.getText());

        try {
            p.save();
        } catch (ConfigurationException ex) {
            logger.error("コンフィグファイル保存に失敗", ex);
        }
    }
}

class DownloadLocationPanel extends JPanel {

    private static final long serialVersionUID = 1L;
    final JTextField fldDir = new JTextField();
    final JTextField fldFileNamePattern = new JTextField();
    final JCheckBox cbLocal = new JCheckBox("local");

    DownloadLocationPanel(){
        super();

        final JLabel lblVideoDir = new JLabel("保存フォルダ");
        final JLabel lblVideoFileNamepattern = new JLabel("ファイル名規則");

        final JButton btnDir = new JButton("...");
        btnDir.addActionListener(
                new FileChooseActionListener(DownloadLocationPanel.this, JFileChooser.DIRECTORIES_ONLY, fldDir));

        final GroupLayout gl = new GroupLayout(this);
        setLayout(gl);
        gl.setHorizontalGroup(
            gl.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(GroupLayout.Alignment.TRAILING, gl.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(lblVideoFileNamepattern)
                    .addComponent(lblVideoDir))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addGroup(gl.createSequentialGroup()
                        .addComponent(fldDir, GroupLayout.DEFAULT_SIZE, 262, Short.MAX_VALUE)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(btnDir))
                    .addGroup(GroupLayout.Alignment.TRAILING, gl.createSequentialGroup()
                        .addComponent(fldFileNamePattern, GroupLayout.PREFERRED_SIZE, 165, GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(ComponentPlacement.RELATED, 94, Short.MAX_VALUE)
                        .addComponent(cbLocal)))
                .addContainerGap())
        );
        gl.setVerticalGroup(
            gl.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblVideoDir)
                    .addComponent(fldDir, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addComponent(btnDir))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addGroup(gl.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(lblVideoFileNamepattern)
                    .addComponent(fldFileNamePattern, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addComponent(cbLocal))
                .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

    }
}
