/**
 * GmailAssistant 1.1 (2008-03-16)
 * Copyright 2008 Zach Scrivena
 * zachscrivena@gmail.com
 * http://gmailassistant.sourceforge.net/
 *
 * Notifier for multiple Gmail accounts.
 *
 * TERMS AND CONDITIONS:
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 *
 * 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/>.
 */

package gmailassistant;

import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.event.DocumentEvent;
import javax.swing.filechooser.FileNameExtensionFilter;


/**
 * "Save Profile" form.
 */
class ProfileSaver
        extends JFrame
{
    /** should account passwords be saved in profile? */
    private static final boolean DEFAULT_SAVE_ACCOUNT_PASSWORDS = false;

    /** recommended minimum password length */
    private static final int RECOMMENDED_MINIMUM_PASSWORD_LENGTH = 8;

    /** parent GmailAssistant object */
    private final GmailAssistant parent;

    /** file chooser for saving profile file */
    private final JFileChooser fileChooser;

    /** are the "Options" selection valid? */
    private volatile boolean optionsValid = false;

    /** is the "Filename" selection valid? */
    private volatile boolean filenameValid = false;


    /**
    * Constructor.
    *
    * @param parent
    *      parent GmailAssistant object
    */
    ProfileSaver(
            final GmailAssistant parent)
    {
        /*********************
        * INITIALIZE FIELDS *
        *********************/

        this.parent = parent;

        /* get current directory */
        File currentDirectory = new File(".");

        try
        {
            currentDirectory = currentDirectory.getCanonicalFile();
        }
        catch (Exception e)
        {
            /* ignore */
        }

        /* file chooser */
        this.fileChooser = new JFileChooser(currentDirectory);
        this.fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        this.fileChooser.setMultiSelectionEnabled(false);
        this.fileChooser.addChoosableFileFilter(new FileNameExtensionFilter(
                GmailAssistant.NAME + " Profile (*." + GmailAssistant.PROFILE_FILENAME_EXTENSION + ")",
                GmailAssistant.PROFILE_FILENAME_EXTENSION));

        /******************************
        * INITIALIZE FORM COMPONENTS *
        ******************************/

        initComponents();

        /***************************
        * CONFIGURE FORM SETTINGS *
        ***************************/

        setTitle("Save Profile - " + this.parent.getTitle());

        addWindowListener(new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent e)
            {
                ProfileSaver.this.cancelButton.doClick();
            }
        });

        /* inherit "always on top" behavior of parent */
        try
        {
            setAlwaysOnTop(this.parent.isAlwaysOnTop());
        }
        catch (Exception e)
        {
            /* ignore */
        }

        /* inherit program icon of parent */
        final List<Image> icons = this.parent.getIconImages();

        if (!icons.isEmpty())
        {
            setIconImage(icons.get(0));
        }

        /* field: "Profile Password" */
        this.passwordField.getDocument().addDocumentListener(new DocumentListenerAdapter()
        {
            @Override
            public void insertUpdate(DocumentEvent e)
            {
                checkOptions();
            }

            @Override
            public void removeUpdate(DocumentEvent e)
            {
                checkOptions();
            }

            @Override
            public void changedUpdate(DocumentEvent e)
            {
                checkOptions();
            }
        });

        /* checkbox: "Save account passwords" */
        this.saveBox.setSelected(ProfileSaver.DEFAULT_SAVE_ACCOUNT_PASSWORDS);

        /* field: "Filename" (initialize to an unused filename) */
        File f = new File(
                currentDirectory,
                GmailAssistant.PROFILE_DEFAULT_FILENAME + "." + GmailAssistant.PROFILE_FILENAME_EXTENSION);

        try
        {
            f = f.getCanonicalFile();
        }
        catch (Exception e)
        {
            /* ignore */
        }

        if (f.exists())
        {
            for (int i = 1; i < Integer.MAX_VALUE; i++)
            {
                f = new File(
                        currentDirectory,
                        GmailAssistant.PROFILE_DEFAULT_FILENAME + "." + i + "." + GmailAssistant.PROFILE_FILENAME_EXTENSION);

                if (f.exists())
                {
                    f = null;
                }
                else
                {
                    break;
                }
            }
        }

        this.filenameField.setText((f == null) ? "" : f.getPath());

        this.filenameField.getDocument().addDocumentListener(new DocumentListenerAdapter()
        {
            @Override
            public void insertUpdate(DocumentEvent e)
            {
                checkFilename();
            }

            @Override
            public void removeUpdate(DocumentEvent e)
            {
                checkFilename();
            }

            @Override
            public void changedUpdate(DocumentEvent e)
            {
                checkFilename();
            }
        });

        /* button: "Browse..." */
        this.browseButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                final int val = ProfileSaver.this.fileChooser.showOpenDialog(ProfileSaver.this);

                if (val == JFileChooser.APPROVE_OPTION)
                {
                    File f = ProfileSaver.this.fileChooser.getSelectedFile();

                    if (!f.getName().contains("."))
                    {
                        f = new File(f.getParentFile(), f.getName() + "." + GmailAssistant.PROFILE_FILENAME_EXTENSION);
                    }

                    ProfileSaver.this.filenameField.setText(f.getPath());
                }
            }
        });

        /* button: "OK" */
        this.okButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                saveProfile();
            }
        });

        /* button: "Cancel" */
        this.cancelButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                cancelForm();
            }
        });

        /* add standard editing popup menu to text fields */
        SwingManipulator.addStandardEditingPopupMenu(new JTextField[]
        {
            this.passwordField,
            this.filenameField
        });

        /* key binding: ENTER key */
        this.scrollPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
                .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "ENTER_OK_BUTTON");

        this.scrollPane.getActionMap().put("ENTER_OK_BUTTON", new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                ProfileSaver.this.okButton.doClick();
            }
        });

        /* key binding: ESCAPE key */
        this.scrollPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
                .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "ESCAPE_CANCEL_BUTTON");

        this.scrollPane.getActionMap().put("ESCAPE_CANCEL_BUTTON", new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                ProfileSaver.this.cancelButton.doClick();
            }
        });

        /* check form for errors */
        checkOptions();
        checkFilename();

        /* center form on the parent form */
        setLocationRelativeTo(this.parent);
    }


    /**
    * Check "Options" selection specified by the user.
    * This method must run on the EDT.
    */
    private void checkOptions()
    {
        String warning = null;

        final char[] password = SwingManipulator.getPasswordJPasswordField(this.passwordField);
        final int passwordLength = password.length;
        Arrays.fill(password, '\0');

        if (passwordLength == 0)
        {
            warning = "A password is strongly recommended";
        }
        else if (passwordLength < ProfileSaver.RECOMMENDED_MINIMUM_PASSWORD_LENGTH)
        {
            warning = "A password of at least " + ProfileSaver.RECOMMENDED_MINIMUM_PASSWORD_LENGTH +
                    " characters is strongly recommended";
        }

        this.optionsValid = true;
        this.optionsError.setText((warning == null) ? " " : "<html><font color='red'>" + warning + "</font></html>");

        if (this.optionsValid &&
                this.filenameValid)
        {
            /* all selections are valid */
            this.okButton.setEnabled(true);
        }
    }


    /**
    * Check "Filename" selection specified by the user.
    * This method must run on the EDT.
    */
    private void checkFilename()
    {
        String error = null;

        if (SwingManipulator.getTextJTextField(this.filenameField).trim().isEmpty())
        {
            error = "A filename must be specified";
        }

        if (error != null)
        {
            this.filenameError.setText("<html><font color='red'>" + error + "</font></html>");
            this.okButton.setEnabled(false);
            this.filenameField.selectAll();
            this.filenameField.requestFocus();
            this.filenameValid = false;
            return;
        }

        this.filenameValid = true;
        this.filenameError.setText(" ");

        if (this.optionsValid &&
                this.filenameValid)
        {
            /* all selections are valid */
            this.okButton.setEnabled(true);
        }
    }


    /**
    * Save profile using the user-specified settings on the form.
    * This method must run on the EDT.
    */
    private void saveProfile()
    {
        final File f = new File(SwingManipulator.getTextJTextField(this.filenameField));

        if (f.isDirectory())
        {
            SwingManipulator.showErrorDialog(this, "Save Profile - " + GmailAssistant.NAME,
                    "Failed to save profile data to \"" + f.getPath() + "\" because it is a directory and not a file.");

            return;
        }

        if (f.exists())
        {
            /* file already exists; prompt on overwriting */
            final int choice = JOptionPane.showConfirmDialog(this,
                    "File \"" + f.getPath() + "\" already exists. Overwrite this file?",
                    "Save Profile - " + GmailAssistant.NAME,
                    JOptionPane.YES_NO_OPTION,
                    JOptionPane.WARNING_MESSAGE);

            if (choice == 1)
            {
                /* user chose NO */
                return;
            }
        }

        /* list of cleartext strings to be converted to a byte array for encryption */
        final List<String> cleartext = new ArrayList<String>();

        /* save program properties */
        for (GmailAssistant.Property p : GmailAssistant.Property.values())
        {
            if (p.keyString != null)
            {
                final String valString = GmailAssistant.propertyValueToString(p, this.parent.getProperty(p));

                if (valString != null)
                {
                    /* "key:value" */
                    cleartext.add(p.keyString);
                    cleartext.add(":");
                    cleartext.add(valString);
                    cleartext.add(GmailAssistant.PROPERTY_DELIMITER);
                }
            }
        }

        /* save account properties */
        final boolean savePasswords = this.saveBox.isSelected();

        synchronized (this.parent.accounts)
        {
            for (Account ac : this.parent.accounts)
            {
                /* "[username]" */
                cleartext.add("[");
                cleartext.add(Account.propertyValueToString(Account.Property.USERNAME, ac.getProperty(Account.Property.USERNAME)));
                cleartext.add("]");
                cleartext.add(GmailAssistant.PROPERTY_DELIMITER);

                for (Account.Property p : Account.Property.values())
                {
                    if ((p.keyString != null) &&
                            ((p != Account.Property.PASSWORD) ||
                            ((p == Account.Property.PASSWORD) && savePasswords)))
                    {
                        final String valString = Account.propertyValueToString(p, ac.getProperty(p));

                        if (valString != null)
                        {
                            /* "key:value" */
                            cleartext.add(p.keyString);
                            cleartext.add(":");
                            cleartext.add(valString);
                            cleartext.add(GmailAssistant.PROPERTY_DELIMITER);
                        }
                    }
                }
            }
        }

        /* get user-specified password */
        final char[] password = SwingManipulator.getPasswordJPasswordField(this.passwordField);

        /* encrypt profile data */
        final byte[] salt = new byte[Encryptor.SALT_LENGTH];
        byte[] cleartextBytes = new byte[0];
        byte[] ciphertextBytes = new byte[0];

        try
        {
            cleartextBytes = Encryptor.stringListToByteArray(cleartext);
            ciphertextBytes = Encryptor.encrypt(salt, String.valueOf(password), cleartextBytes);
            Arrays.fill(password, '\0');
            Arrays.fill(cleartextBytes, (byte) 0x00);
        }
        catch (Exception e)
        {
            Arrays.fill(password, '\0');
            Arrays.fill(salt, (byte) 0x00);
            Arrays.fill(cleartextBytes, (byte) 0x00);
            Arrays.fill(ciphertextBytes, (byte) 0x00);

            SwingManipulator.showErrorDialog(this, "Save Profile - " + GmailAssistant.NAME,
                    "Failed to encrypt profile data because of an unexpected error: " + e +
                    "\nPlease help to improve " + GmailAssistant.NAME + " by filing a bug report at " +
                    GmailAssistant.HOMEPAGE + ".\n\n" +
                    GmailAssistant.stackTraceString(e));

            return;
        }

        /* write encrypted profile data to binary file */
        try
        {
            final FileOutputStream fos = new FileOutputStream(f);
            fos.write(salt);
            fos.write(ciphertextBytes);
            fos.flush();
            fos.close();
            Arrays.fill(salt, (byte) 0x00);
            Arrays.fill(ciphertextBytes, (byte) 0x00);
        }
        catch (Exception e)
        {
            Arrays.fill(salt, (byte) 0x00);
            Arrays.fill(ciphertextBytes, (byte) 0x00);

            SwingManipulator.showErrorDialog(this, "Save Profile - " + GmailAssistant.NAME,
                    "Failed to write profile data to file \"" + f.getPath() + "\": " + e +
                    "\nPlease check that the file can be created and written to.");

            return;
        }

        /* done */
        this.setVisible(false);
    }


    /**
    * Cancel and close the form.
    */
    private void cancelForm()
    {
        this.setVisible(false);
    }


    /**
    * Present the form for saving a profile.
    */
    void showForm()
    {
        this.setVisible(true);
        this.setExtendedState(JFrame.NORMAL);
        this.toFront();
    }

    /***************************
    * NETBEANS-GENERATED CODE *
    ***************************/

    /** This method is called from within the constructor to
    * initialize the form.
    * WARNING: Do NOT modify this code. The content of this method is
    * always regenerated by the Form Editor.
    */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents()
    {

        scrollPane = new javax.swing.JScrollPane();
        panel = new javax.swing.JPanel();
        optionsPanel = new javax.swing.JPanel();
        lock = new javax.swing.JLabel();
        passwordLabel = new javax.swing.JLabel();
        saveBox = new javax.swing.JCheckBox();
        passwordField = new javax.swing.JPasswordField();
        optionsError = new javax.swing.JLabel();
        filenamePanel = new javax.swing.JPanel();
        browseButton = new javax.swing.JButton();
        filenameField = new javax.swing.JTextField();
        filenameError = new javax.swing.JLabel();
        buttonsPanel = new javax.swing.JPanel();
        okButton = new javax.swing.JButton();
        cancelButton = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);

        scrollPane.setBorder(null);
        scrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        panel.setLayout(new javax.swing.BoxLayout(panel, javax.swing.BoxLayout.Y_AXIS));

        optionsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Options"));

        lock.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        lock.setIcon(new javax.swing.ImageIcon(getClass().getResource("/gmailassistant/resources/lock.png"))); // NOI18N
        lock.setToolTipText("GmailAssistant encrypts your profile data using AES-128 encryption");

        passwordLabel.setDisplayedMnemonic('p');
        passwordLabel.setLabelFor(passwordField);
        passwordLabel.setText("Profile password:");
        passwordLabel.setToolTipText("Password to be used for encrypting the profile data");

        saveBox.setMnemonic('s');
        saveBox.setText("Save account passwords in profile");
        saveBox.setToolTipText("Save Gmail account passwords in the profile so they do not need to be entered again when the profile is loaded");

        passwordField.setToolTipText("Password to be used for encrypting the profile data");

        optionsError.setText("<html><font color='red'>options error</font></html>");
        optionsError.setVerticalAlignment(javax.swing.SwingConstants.TOP);

        javax.swing.GroupLayout optionsPanelLayout = new javax.swing.GroupLayout(optionsPanel);
        optionsPanel.setLayout(optionsPanelLayout);
        optionsPanelLayout.setHorizontalGroup(
            optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(optionsPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(saveBox, javax.swing.GroupLayout.DEFAULT_SIZE, 338, Short.MAX_VALUE)
                    .addGroup(optionsPanelLayout.createSequentialGroup()
                        .addComponent(passwordLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(passwordField, javax.swing.GroupLayout.DEFAULT_SIZE, 231, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(lock))
                    .addComponent(optionsError, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 338, Short.MAX_VALUE))
                .addContainerGap())
        );
        optionsPanelLayout.setVerticalGroup(
            optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(optionsPanelLayout.createSequentialGroup()
                .addGroup(optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(passwordLabel)
                        .addComponent(passwordField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(lock))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(saveBox)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(optionsError))
        );

        panel.add(optionsPanel);

        filenamePanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Filename"));

        browseButton.setMnemonic('b');
        browseButton.setText("Browse...");
        browseButton.setToolTipText("Choose a filename for the profile file");

        filenameField.setText("C:\\Path\\To\\File");
        filenameField.setToolTipText("Filename of the profile file");

        filenameError.setText("<html><font color='red'>filename error</font></html>");
        filenameError.setVerticalAlignment(javax.swing.SwingConstants.TOP);

        javax.swing.GroupLayout filenamePanelLayout = new javax.swing.GroupLayout(filenamePanel);
        filenamePanel.setLayout(filenamePanelLayout);
        filenamePanelLayout.setHorizontalGroup(
            filenamePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, filenamePanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(filenamePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(filenameField, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 338, Short.MAX_VALUE)
                    .addGroup(filenamePanelLayout.createSequentialGroup()
                        .addComponent(filenameError, javax.swing.GroupLayout.DEFAULT_SIZE, 253, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(browseButton)))
                .addContainerGap())
        );
        filenamePanelLayout.setVerticalGroup(
            filenamePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(filenamePanelLayout.createSequentialGroup()
                .addComponent(filenameField, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(filenamePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(browseButton)
                    .addComponent(filenameError))
                .addContainerGap(25, Short.MAX_VALUE))
        );

        panel.add(filenamePanel);

        scrollPane.setViewportView(panel);

        buttonsPanel.setLayout(new java.awt.GridLayout(1, 2));

        okButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/gmailassistant/resources/tick.png"))); // NOI18N
        okButton.setMnemonic('O');
        okButton.setText("OK");
        okButton.setToolTipText("Save profile data to the specified file");
        buttonsPanel.add(okButton);

        cancelButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/gmailassistant/resources/cross.png"))); // NOI18N
        cancelButton.setMnemonic('C');
        cancelButton.setText("Cancel");
        cancelButton.setToolTipText("Cancel saving of profile data");
        buttonsPanel.add(cancelButton);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(buttonsPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 386, Short.MAX_VALUE)
                    .addComponent(scrollPane, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 386, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 203, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(buttonsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 37, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton browseButton;
    private javax.swing.JPanel buttonsPanel;
    private javax.swing.JButton cancelButton;
    private javax.swing.JLabel filenameError;
    private javax.swing.JTextField filenameField;
    private javax.swing.JPanel filenamePanel;
    private javax.swing.JLabel lock;
    private javax.swing.JButton okButton;
    private javax.swing.JLabel optionsError;
    private javax.swing.JPanel optionsPanel;
    private javax.swing.JPanel panel;
    private javax.swing.JPasswordField passwordField;
    private javax.swing.JLabel passwordLabel;
    private javax.swing.JCheckBox saveBox;
    private javax.swing.JScrollPane scrollPane;
    // End of variables declaration//GEN-END:variables
}