/*
 * Copyright 2004 - 2013 Wayne Grant
 *           2013 - 2020 Kai Kramer
 *
 * This file is part of KeyStore Explorer.
 *
 * KeyStore Explorer 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 3 of the License, or
 * (at your option) any later version.
 *
 * KeyStore Explorer 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 KeyStore Explorer.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kse.gui;

import static org.kse.crypto.keystore.KeyStoreType.BCFKS;
import static org.kse.crypto.keystore.KeyStoreType.BKS;
import static org.kse.crypto.keystore.KeyStoreType.BKS_V1;
import static org.kse.crypto.keystore.KeyStoreType.JCEKS;
import static org.kse.crypto.keystore.KeyStoreType.JKS;
import static org.kse.crypto.keystore.KeyStoreType.PKCS12;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragSource;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.Certificate;
import java.text.MessageFormat;
import java.util.*;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.RowSorter;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;

import org.kse.ApplicationSettings;
import org.kse.KSE;
import org.kse.crypto.CryptoException;
import org.kse.crypto.Password;
import org.kse.crypto.keystore.KeyStoreType;
import org.kse.crypto.keystore.KeyStoreUtil;
import org.kse.gui.actions.AboutAction;
import org.kse.gui.actions.AppendToCertificateChainAction;
import org.kse.gui.actions.ChangeTypeAction;
import org.kse.gui.actions.CheckUpdateAction;
import org.kse.gui.actions.CloseAction;
import org.kse.gui.actions.CloseAllAction;
import org.kse.gui.actions.CloseOthersAction;
import org.kse.gui.actions.CopyAction;
import org.kse.gui.actions.CopyKeyPairAction;
import org.kse.gui.actions.CopyTrustedCertificateAction;
import org.kse.gui.actions.CryptographyStrengthAction;
import org.kse.gui.actions.CutAction;
import org.kse.gui.actions.CutKeyPairAction;
import org.kse.gui.actions.CutTrustedCertificateAction;
import org.kse.gui.actions.DeleteKeyAction;
import org.kse.gui.actions.DeleteKeyPairAction;
import org.kse.gui.actions.DeleteMultipleEntriesAction;
import org.kse.gui.actions.DeleteTrustedCertificateAction;
import org.kse.gui.actions.DetectFileTypeAction;
import org.kse.gui.actions.ExamineClipboardAction;
import org.kse.gui.actions.ExamineFileAction;
import org.kse.gui.actions.ExamineSslAction;
import org.kse.gui.actions.ExitAction;
import org.kse.gui.actions.ExportKeyPairAction;
import org.kse.gui.actions.ExportKeyPairCertificateChainAction;
import org.kse.gui.actions.ExportKeyPairPrivateKeyAction;
import org.kse.gui.actions.ExportKeyPairPublicKeyAction;
import org.kse.gui.actions.ExportTrustedCertificateAction;
import org.kse.gui.actions.ExportTrustedCertificatePublicKeyAction;
import org.kse.gui.actions.GenerateCsrAction;
import org.kse.gui.actions.GenerateKeyPairAction;
import org.kse.gui.actions.GenerateSecretKeyAction;
import org.kse.gui.actions.HelpAction;
import org.kse.gui.actions.ImportCaReplyFromClipboardAction;
import org.kse.gui.actions.ImportCaReplyFromFileAction;
import org.kse.gui.actions.ImportKeyPairAction;
import org.kse.gui.actions.ImportTrustedCertificateAction;
import org.kse.gui.actions.JarsAction;
import org.kse.gui.actions.KeyDetailsAction;
import org.kse.gui.actions.KeyPairCertificateChainDetailsAction;
import org.kse.gui.actions.KeyPairPrivateKeyDetailsAction;
import org.kse.gui.actions.KeyPairPublicKeyDetailsAction;
import org.kse.gui.actions.NewAction;
import org.kse.gui.actions.OpenAction;
import org.kse.gui.actions.OpenCaCertificatesAction;
import org.kse.gui.actions.OpenDefaultAction;
import org.kse.gui.actions.OpenMsCapiAction;
import org.kse.gui.actions.OpenPkcs11Action;
import org.kse.gui.actions.PasteAction;
import org.kse.gui.actions.PreferencesAction;
import org.kse.gui.actions.PropertiesAction;
import org.kse.gui.actions.RedoAction;
import org.kse.gui.actions.RemoveFromCertificateChainAction;
import org.kse.gui.actions.RenameKeyAction;
import org.kse.gui.actions.RenameKeyPairAction;
import org.kse.gui.actions.RenameTrustedCertificateAction;
import org.kse.gui.actions.SaveAction;
import org.kse.gui.actions.SaveAllAction;
import org.kse.gui.actions.SaveAsAction;
import org.kse.gui.actions.SecurityProvidersAction;
import org.kse.gui.actions.SetKeyPairPasswordAction;
import org.kse.gui.actions.SetKeyPasswordAction;
import org.kse.gui.actions.SetPasswordAction;
import org.kse.gui.actions.ShowHideStatusBarAction;
import org.kse.gui.actions.ShowHideToolBarAction;
import org.kse.gui.actions.SignCsrAction;
import org.kse.gui.actions.SignJarAction;
import org.kse.gui.actions.SignMidletAction;
import org.kse.gui.actions.SignNewKeyPairAction;
import org.kse.gui.actions.SystemInformationAction;
import org.kse.gui.actions.TabStyleScrollAction;
import org.kse.gui.actions.TabStyleWrapAction;
import org.kse.gui.actions.TipOfTheDayAction;
import org.kse.gui.actions.TrustedCertificateDetailsAction;
import org.kse.gui.actions.TrustedCertificatePublicKeyDetailsAction;
import org.kse.gui.actions.UndoAction;
import org.kse.gui.actions.UnlockKeyAction;
import org.kse.gui.actions.UnlockKeyPairAction;
import org.kse.gui.actions.WebsiteAction;
import org.kse.gui.dnd.DragEntry;
import org.kse.gui.dnd.DragKeyPairEntry;
import org.kse.gui.dnd.DragTrustedCertificateEntry;
import org.kse.gui.dnd.KeyStoreEntryDragGestureListener;
import org.kse.gui.error.DError;
import org.kse.gui.quickstart.JQuickStartPane;
import org.kse.gui.statusbar.StatusBar;
import org.kse.gui.statusbar.StatusBarChangeHandler;
import org.kse.utilities.buffer.Buffer;
import org.kse.utilities.history.KeyStoreHistory;
import org.kse.utilities.history.KeyStoreState;
import org.kse.utilities.os.OperatingSystem;

/**
 * KeyStore Explorer application frame. Wraps an actual JFrame.
 *
 */
public final class KseFrame implements StatusBar {

	private static ResourceBundle res = ResourceBundle.getBundle("org/kse/gui/resources");

	static final String KSE_UPDATE_CHECK_DISABLED = "kse.update.disabled";

	// Default KeyStores tabbed pane width - dictates width of this frame
	public static final int DEFAULT_WIDTH = 700;

	// Default KeyStores tabbed pane - dictates height of this frame
	public static final int DEFAULT_HEIGHT = 450;

	// Maximum number of recent files to maintain in file menu
	public static final int RECENT_FILES_SIZE = 6;

	private ArrayList<KeyStoreHistory> histories = new ArrayList<>();
	private ArrayList<JTable> keyStoreTables = new ArrayList<>();
	private JFrame frame = new JFrame();
	private ApplicationSettings applicationSettings = ApplicationSettings.getInstance();
	private KeyStoreTableColumns keyStoreTableColumns = new KeyStoreTableColumns();
	private static final double FF = 0.7; // fudge factor for font size to column size
	private static int iFontSize = (int) (LnfUtil.getDefaultFontSize() * FF);
	private int autoResizeMode = JTable.AUTO_RESIZE_ALL_COLUMNS;
	private static final int ICON_SIZE = 28;


	//
	// Menu bar controls
	//

	private JMenu jmFile;
	private JMenuItem jmiNew;
	private JMenuItem jmiOpen;
	private JMenu jmOpenSpecial;
	private JMenuItem jmiOpenDefaultKeyStore;
	private JMenuItem jmiOpenCaCertificatesKeyStore;
	private JMenuItem jmiOpenPkcs11KeyStore;
	private JMenuItem jmiOpenMsCapiKeyStore;
	private JMenuItem jmiClose;
	private JMenuItem jmiCloseAll;
	private JMenuItem jmiSave;
	private JMenuItem jmiSaveAs;
	private JMenuItem jmiSaveAll;
	private JMenuRecentFiles jmrfRecentFiles;
	private JMenuItem jmiExit;

	private JMenu jmEdit;
	private JMenuItem jmiUndo;
	private JMenuItem jmiRedo;
	private JMenuItem jmiCut;
	private JMenuItem jmiCopy;
	private JMenuItem jmiPaste;

	private JMenu jmView;
	private JCheckBoxMenuItem jcbmiShowHideToolBar;
	private JCheckBoxMenuItem jcbmiShowHideStatusBar;
	private JMenu jmTabStyle;
	private JRadioButtonMenuItem jrbmiTabStyleWrap;
	private JRadioButtonMenuItem jrbmiTabStyleScroll;

	private JMenu jmTools;
	private JMenuItem jmiGenerateKeyPair;
	private JMenuItem jmiGenerateSecretKey;
	private JMenuItem jmiImportTrustedCertificate;
	private JMenuItem jmiImportKeyPair;
	private JMenu jmChangeType;
	private JRadioButtonMenuItem jrbmiChangeTypeJks;
	private JRadioButtonMenuItem jrbmiChangeTypeJceks;
	private JRadioButtonMenuItem jrbmiChangeTypePkcs12;
	private JRadioButtonMenuItem jrbmiChangeTypeBksV1;
	private JRadioButtonMenuItem jrbmiChangeTypeBks;
	private JRadioButtonMenuItem jrbmiChangeTypeBcfks;
	private JRadioButtonMenuItem jrbmiChangeTypeUber;
	private JMenuItem jmiSetPassword;
	private JMenuItem jmiProperties;
	private JMenuItem jmiPreferences;

	private JMenu jmExamine;
	private JMenuItem jmiExamineFile;
	private JMenuItem jmiExamineClipboard;
	private JMenuItem jmiExamineSsl;
	private JMenuItem jmiDetectFileType;

	private JMenu jmHelp;
	private JMenuItem jmiHelp;
	private JMenuItem jmiTipOfTheDay;
	private JMenu jmOnlineResources;
	private JMenuItem jmiWebsite;
	private JMenuItem jmiSourceforge;
	private JMenuItem jmiSfBugs;
	private JMenuItem jmiCheckUpdate;
	private JMenuItem jmiSecurityProviders;
	private JMenuItem jmiSystemInformation;
	private JMenuItem jmiCryptographyStrength;
	private JMenuItem jmiJars;
	private JMenuItem jmiAbout;

	//
	// Tool Bar controls
	//

	private JToolBar jtbToolBar;
	private JButton jbNew;
	private JButton jbOpen;
	private JButton jbSave;
	private JButton jbUndo;
	private JButton jbRedo;
	private JButton jbCut;
	private JButton jbCopy;
	private JButton jbPaste;
	private JButton jbGenerateKeyPair;
	private JButton jbGenerateSecretKey;
	private JButton jbImportTrustedCertificate;
	private JButton jbImportKeyPair;
	private JButton jbSetPassword;
	private JButton jbProperties;
	private JButton jbExamineFile;
	private JButton jbExamineClipboard;
	private JButton jbExamineSsl;
	private JButton jbHelp;

	//
	// Pop-up menu controls
	//

	private JPopupMenu jpmKeyStoreTab;
	private JMenuItem jmiKeyStoreTabSave;
	private JMenuItem jmiKeyStoreTabSaveAll;
	private JMenuItem jmiKeyStoreTabPaste;
	private JMenuItem jmiKeyStoreTabClose;
	private JMenuItem jmiKeyStoreTabCloseOthers;
	private JMenuItem jmiKeyStoreTabCloseAll;
	private JMenuItem jmiKeyStoreTabProperties;

	private JPopupMenu jpmKeyStore;
	private JMenuItem jmiKeyStoreGenerateKeyPair;
	private JMenuItem jmiKeyStoreGenerateSecretKey;
	private JMenuItem jmiKeyStoreImportTrustedCertificate;
	private JMenuItem jmiKeyStoreImportKeyPair;
	private JMenu jmKeyStoreChangeType;
	private JRadioButtonMenuItem jrbmiKeyStoreChangeTypeJks;
	private JRadioButtonMenuItem jrbmiKeyStoreChangeTypeJceks;
	private JRadioButtonMenuItem jrbmiKeyStoreChangeTypePkcs12;
	private JRadioButtonMenuItem jrbmiKeyStoreChangeTypeBksV1;
	private JRadioButtonMenuItem jrbmiKeyStoreChangeTypeBks;
	private JRadioButtonMenuItem jrbmiKeyStoreChangeTypeBcfks;
	private JRadioButtonMenuItem jrbmiKeyStoreChangeTypeUber;
	private JMenuItem jmiKeyStoreSetPassword;
	private JMenuItem jmiKeyStoreProperties;

	private JPopupMenu jpmKeyPair;
	private JMenu jmKeyPairDetails;
	private JMenuItem jmiKeyPairCertificateChainDetails;
	private JMenuItem jmiKeyPairPrivateKeyDetails;
	private JMenuItem jmiKeyPairPublicKeyDetails;
	private JMenuItem jmiKeyPairCut;
	private JMenuItem jmiKeyPairCopy;
	private JMenu jmKeyPairExport;
	private JMenuItem jmiKeyPairExport;
	private JMenuItem jmiKeyPairExportCertificateChain;
	private JMenuItem jmiKeyPairExportPrivateKey;
	private JMenuItem jmiKeyPairExportPublicKey;
	private JMenuItem jmiKeyPairGenerateCsr;
	private JMenu jmKeyPairImportCaReply;
	private JMenuItem jmiKeyPairImportCaReplyFile;
	private JMenuItem jmiKeyPairImportCaReplyClipboard;
	private JMenu jmKeyPairEditCertChain;
	private JMenuItem jmiKeyPairEditCertChainAppendCert;
	private JMenuItem jmiKeyPairEditCertChainRemoveCert;
	private JMenu jmKeyPairSign;
	private JMenuItem jmiKeyPairSignCsr;
	private JMenuItem jmiKeyPairSignJar;
	private JMenuItem jmiKeyPairSignMidlet;
	private JMenuItem jmiKeyPairSignNewKeyPair;
	private JMenuItem jmiKeyPairUnlock;
	private JMenuItem jmiKeyPairSetPassword;
	private JMenuItem jmiKeyPairDelete;
	private JMenuItem jmiKeyPairRename;

	private JPopupMenu jpmTrustedCertificate;
	private JMenu jmTrustedCertificateDetails;
	private JMenuItem jmiTrustedCertificateDetails;
	private JMenuItem jmiTrustedCertificatePublicKeyDetails;
	private JMenuItem jmiTrustedCertificateCut;
	private JMenuItem jmiTrustedCertificateCopy;
	private JMenu jmTrustedCertificateExport;
	private JMenuItem jmiTrustedCertificateExport;
	private JMenuItem jmiTrustedCertificateExportPublicKey;
	private JMenuItem jmiTrustedCertificateDelete;
	private JMenuItem jmiTrustedCertificateRename;

	private JPopupMenu jpmKey;

	private JMenuItem jmiKeyDetails;
	private JMenuItem jmiKeyUnlock;
	private JMenuItem jmiKeySetPassword;
	private JMenuItem jmiKeyDelete;
	private JMenuItem jmiKeyRename;

	//
	// Main display controls
	//

	private JKeyStoreTabbedPane jkstpKeyStores;
	private JQuickStartPane jQuickStart;

	//
	// Status bar controls
	//

	private JLabel jlStatusBar;

	//
	// Actions - these are shared between menus and tool bar
	//

	private final NewAction newAction = new NewAction(this);
	private final OpenAction openAction = new OpenAction(this);
	private final OpenDefaultAction openDefaultKeyStoreAction = new OpenDefaultAction(this);
	private final OpenCaCertificatesAction openCaCertificatesKeyStoreAction = new OpenCaCertificatesAction(this);
	private final OpenPkcs11Action openPkcs11KeyStoreAction = new OpenPkcs11Action(this);
	private final OpenMsCapiAction openMsCapiAction = new OpenMsCapiAction(this);
	private final SaveAction saveAction = new SaveAction(this);
	private final SaveAsAction saveAsAction = new SaveAsAction(this);
	private final SaveAllAction saveAllAction = new SaveAllAction(this);
	private final CloseAction closeAction = new CloseAction(this);
	private final CloseOthersAction closeOthersAction = new CloseOthersAction(this);
	private final CloseAllAction closeAllAction = new CloseAllAction(this);
	private final ExitAction exitAction = new ExitAction(this);
	private final UndoAction undoAction = new UndoAction(this);
	private final RedoAction redoAction = new RedoAction(this);
	private final CutAction cutAction = new CutAction(this);
	private final CopyAction copyAction = new CopyAction(this);
	private final PasteAction pasteAction = new PasteAction(this);
	private final ShowHideToolBarAction showHideToolBarAction = new ShowHideToolBarAction(this);
	private final ShowHideStatusBarAction showHideStatusBarAction = new ShowHideStatusBarAction(this);
	private final TabStyleWrapAction tabStyleWrapAction = new TabStyleWrapAction(this);
	private final TabStyleScrollAction tabStyleScrollAction = new TabStyleScrollAction(this);
	private final GenerateKeyPairAction generateKeyPairAction = new GenerateKeyPairAction(this);
	private final GenerateSecretKeyAction generateSecretKeyAction = new GenerateSecretKeyAction(this);
	private final ImportTrustedCertificateAction importTrustedCertificateAction = new ImportTrustedCertificateAction(
			this);
	private final ImportKeyPairAction importKeyPairAction = new ImportKeyPairAction(this);
	private final SetPasswordAction setPasswordAction = new SetPasswordAction(this);
	private final ChangeTypeAction changeTypeJksAction = new ChangeTypeAction(this, KeyStoreType.JKS);
	private final ChangeTypeAction changeTypeJceksAction = new ChangeTypeAction(this, KeyStoreType.JCEKS);
	private final ChangeTypeAction changeTypePkcs12Action = new ChangeTypeAction(this, KeyStoreType.PKCS12);
	private final ChangeTypeAction changeTypeBksV1Action = new ChangeTypeAction(this, KeyStoreType.BKS_V1);
	private final ChangeTypeAction changeTypeBksAction = new ChangeTypeAction(this, KeyStoreType.BKS);
	private final ChangeTypeAction changeTypeBcfksAction = new ChangeTypeAction(this, KeyStoreType.BCFKS);
	private final ChangeTypeAction changeTypeUberAction = new ChangeTypeAction(this, KeyStoreType.UBER);
	private final PropertiesAction propertiesAction = new PropertiesAction(this);
	private final PreferencesAction preferencesAction = new PreferencesAction(this);
	private final ExamineFileAction examineFileAction = new ExamineFileAction(this);
	private final ExamineClipboardAction examineClipboardAction = new ExamineClipboardAction(this);
	private final ExamineSslAction examineSslAction = new ExamineSslAction(this);
	private final DetectFileTypeAction detectFileTypeAction = new DetectFileTypeAction(this);
	private final HelpAction helpAction = new HelpAction(this);
	private final TipOfTheDayAction tipOfTheDayAction = new TipOfTheDayAction(this);
	private final WebsiteAction websiteAction = new WebsiteAction(this, WebsiteAction.Target.MAIN);
	private final WebsiteAction gitHubProjectSiteAction = new WebsiteAction(this, WebsiteAction.Target.GITHUB);
	private final WebsiteAction gitHubIssueTrackerAction = new WebsiteAction(this, WebsiteAction.Target.ISSUE_TRACKER);
	private final CheckUpdateAction checkUpdateAction = new CheckUpdateAction(this);
	private final SecurityProvidersAction securityProvidersAction = new SecurityProvidersAction(this);
	private final SystemInformationAction systemInformationAction = new SystemInformationAction(this);
	private final CryptographyStrengthAction cryptographyStrengthAction = new CryptographyStrengthAction(this);
	private final JarsAction jarsAction = new JarsAction(this);
	private final AboutAction aboutAction = new AboutAction(this);
	private final KeyPairCertificateChainDetailsAction keyPairCertificateChainDetailsAction = new KeyPairCertificateChainDetailsAction(
			this);
	private final KeyPairPrivateKeyDetailsAction keyPairPrivateKeyDetailsAction = new KeyPairPrivateKeyDetailsAction(
			this);
	private final KeyPairPublicKeyDetailsAction keyPairPublicKeyDetailsAction = new KeyPairPublicKeyDetailsAction(this);
	private final CutKeyPairAction cutKeyPairAction = new CutKeyPairAction(this);
	private final CopyKeyPairAction copyKeyPairAction = new CopyKeyPairAction(this);
	private final ExportKeyPairAction exportKeyPairAction = new ExportKeyPairAction(this);
	private final ExportKeyPairCertificateChainAction exportKeyPairCertificateChainAction = new ExportKeyPairCertificateChainAction(
			this);
	private final ExportKeyPairPrivateKeyAction exportKeyPairPrivateKeyAction = new ExportKeyPairPrivateKeyAction(this);
	private final ExportKeyPairPublicKeyAction exportKeyPairPublicKeyAction = new ExportKeyPairPublicKeyAction(this);
	private final GenerateCsrAction generateCsrAction = new GenerateCsrAction(this);
	private final ImportCaReplyFromFileAction importCaReplyFromFileAction = new ImportCaReplyFromFileAction(this);
	private final ImportCaReplyFromClipboardAction importCaReplyFromClipboardAction = new ImportCaReplyFromClipboardAction(this);
	private final AppendToCertificateChainAction appendToCertificateChainAction = new AppendToCertificateChainAction(
			this);
	private final RemoveFromCertificateChainAction removeFromCertificateChainAction = new RemoveFromCertificateChainAction(
			this);
	private final SignCsrAction signCsrAction = new SignCsrAction(this);
	private final SignJarAction signJarAction = new SignJarAction(this);
	private final SignMidletAction signMidletAction = new SignMidletAction(this);
	private final SignNewKeyPairAction signNewKeyPairAction = new SignNewKeyPairAction(this);
	private final UnlockKeyPairAction unlockKeyPairAction = new UnlockKeyPairAction(this);
	private final SetKeyPairPasswordAction setKeyPairPasswordAction = new SetKeyPairPasswordAction(this);
	private final DeleteKeyPairAction deleteKeyPairAction = new DeleteKeyPairAction(this);
	private final RenameKeyPairAction renameKeyPairAction = new RenameKeyPairAction(this);

	private final DeleteMultipleEntriesAction deleteMultipleEntriesAction = new DeleteMultipleEntriesAction(this);

	private final TrustedCertificateDetailsAction trustedCertificateDetailsAction = new TrustedCertificateDetailsAction(
			this);
	private final TrustedCertificatePublicKeyDetailsAction trustedCertificatePublicKeyDetailsAction = new TrustedCertificatePublicKeyDetailsAction(
			this);
	private final CutTrustedCertificateAction cutTrustedCertificateAction = new CutTrustedCertificateAction(this);
	private final CopyTrustedCertificateAction copyTrustedCertificateAction = new CopyTrustedCertificateAction(this);
	private final ExportTrustedCertificateAction exportTrustedCertificateAction = new ExportTrustedCertificateAction(
			this);
	private final ExportTrustedCertificatePublicKeyAction exportTrustedCertificatePublicKeyAction = new ExportTrustedCertificatePublicKeyAction(
			this);
	private final DeleteTrustedCertificateAction deleteTrustedCertificateAction = new DeleteTrustedCertificateAction(
			this);
	private final RenameTrustedCertificateAction renameTrustedCertificateAction = new RenameTrustedCertificateAction(
			this);
	private final KeyDetailsAction keyDetailsAction = new KeyDetailsAction(this);
	private final UnlockKeyAction unlockKeyAction = new UnlockKeyAction(this);
	private final SetKeyPasswordAction setKeyPasswordAction = new SetKeyPasswordAction(this);
	private final DeleteKeyAction deleteKeyAction = new DeleteKeyAction(this);
	private final RenameKeyAction renameKeyAction = new RenameKeyAction(this);

	//
	// Action map keys - map input to action
	//

	private static final String CUT_KEY = "CUT_KEY";
	private static final String COPY_KEY = "COPY_KEY";
	private static final String PASTE_KEY = "PASTE_KEY";
	private static final String RENAME_KEY = "RENAME_KEY";

	public KseFrame() {
		initComponents();
	}

	/**
	 * Get underlying JFrame.
	 *
	 * @return Underlying frame
	 */
	public JFrame getUnderlyingFrame() {
		return frame;
	}

	/**
	 * Get application settings.
	 *
	 * @return Application settings
	 */
	public ApplicationSettings getApplicationSettings() {
		return applicationSettings;
	}

	void display() {
		frame.setVisible(true);

		if (applicationSettings.getShowTipsOnStartUp()) {
			tipOfTheDayAction.showTipOfTheDay();
		}
	}

	private void initComponents() {
		initStatusBar();
		initMenu();
		initToolBar();
		initMainPane();
		initKeyStoreTabPopupMenu();
		initKeyStorePopupMenu();
		initKeyStoreEntryPopupMenus();

		// Handle application close
		frame.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent evt) {
				exitAction.exitApplication();
			}
		});
		frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);

		updateApplicationTitle();
		frame.pack();

		initApplicationPosition();
		initApplicationIcons();

		updateControls(false);
	}

	private void initApplicationPosition() {
		Rectangle sizeAndPosition = applicationSettings.getSizeAndPosition();

		int xPos = sizeAndPosition.x;
		int yPos = sizeAndPosition.y;

		if (xPos <= 0 || yPos <= 0) {
			frame.setLocationRelativeTo(null);
		} else {
			frame.setLocation(new Point(xPos, yPos));
		}

		if (!SwingUtilities.isRectangleContainingRectangle(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()),
				frame.getBounds())) {
			jQuickStart.setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
			frame.setLocationRelativeTo(null);
		}
	}

	private void initApplicationIcons() {
		// Adds many different sizes to give each OS flexibility in choosing a suitable icon for display
		ArrayList<Image> icons = new ArrayList<>();
		icons.add(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/kse-16x16.png")));
		icons.add(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/kse-24x24.png")));
		icons.add(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/kse-32x32.png")));
		icons.add(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/kse-48x48.png")));
		icons.add(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/kse-128x128.png")));
		icons.add(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/kse-256x256.png")));
		icons.add(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/kse-512x512.png")));

		frame.setIconImages(icons);
	}

	private void initMenu() {
		JMenuBar jmbMenuBar = new JMenuBar();

		jmFile = new JMenu(res.getString("KseFrame.jmFile.text"));
		PlatformUtil.setMnemonic(jmFile, res.getString("KseFrame.jmFile.mnemonic").charAt(0));

		jmiNew = new JMenuItem(newAction);
		PlatformUtil.setMnemonic(jmiNew, res.getString("KseFrame.jmiNew.mnemonic").charAt(0));
		jmiNew.setToolTipText(null);
		new StatusBarChangeHandler(jmiNew, (String) newAction.getValue(Action.LONG_DESCRIPTION), this);
		jmFile.add(jmiNew);

		jmiOpen = new JMenuItem(openAction);
		PlatformUtil.setMnemonic(jmiOpen, res.getString("KseFrame.jmiOpen.mnemonic").charAt(0));
		jmiOpen.setToolTipText(null);
		new StatusBarChangeHandler(jmiOpen, (String) openAction.getValue(Action.LONG_DESCRIPTION), this);
		jmFile.add(jmiOpen);

		jmOpenSpecial = new JMenu(res.getString("KseFrame.jmOpenSpecial.text"));
		jmOpenSpecial.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/openspecial.png"))));
		PlatformUtil.setMnemonic(jmOpenSpecial, res.getString("KseFrame.jmOpenSpecial.mnemonic").charAt(0));
		jmFile.add(jmOpenSpecial);

		jmiOpenDefaultKeyStore = new JMenuItem(openDefaultKeyStoreAction);
		PlatformUtil.setMnemonic(jmiOpenDefaultKeyStore, res.getString("KseFrame.jmiOpenDefaultKeyStore.mnemonic")
				.charAt(0));
		jmiOpenDefaultKeyStore.setToolTipText(null);
		new StatusBarChangeHandler(jmiOpenDefaultKeyStore,
				(String) openDefaultKeyStoreAction.getValue(Action.LONG_DESCRIPTION), this);
		jmOpenSpecial.add(jmiOpenDefaultKeyStore);

		jmiOpenCaCertificatesKeyStore = new JMenuItem(openCaCertificatesKeyStoreAction);
		PlatformUtil.setMnemonic(jmiOpenCaCertificatesKeyStore,
				res.getString("KseFrame.jmiOpenCaCertificatesKeyStore.mnemonic").charAt(0));
		jmiOpenCaCertificatesKeyStore.setToolTipText(null);
		new StatusBarChangeHandler(jmiOpenCaCertificatesKeyStore,
				(String) openCaCertificatesKeyStoreAction.getValue(Action.LONG_DESCRIPTION), this);
		jmOpenSpecial.add(jmiOpenCaCertificatesKeyStore);

		jmiOpenPkcs11KeyStore = new JMenuItem(openPkcs11KeyStoreAction);
		PlatformUtil.setMnemonic(jmiOpenPkcs11KeyStore,
				res.getString("KseFrame.jmiOpenPkcs11KeyStore.mnemonic").charAt(0));
		jmiOpenPkcs11KeyStore.setToolTipText(null);
		new StatusBarChangeHandler(jmiOpenPkcs11KeyStore,
				(String) openPkcs11KeyStoreAction.getValue(Action.LONG_DESCRIPTION), this);
		jmOpenSpecial.add(jmiOpenPkcs11KeyStore);

		jmiOpenMsCapiKeyStore = new JMenuItem(openMsCapiAction);
		PlatformUtil.setMnemonic(jmiOpenMsCapiKeyStore, res.getString("KseFrame.jmiOpenPkcs11KeyStore.mnemonic")
				.charAt(0));
		jmiOpenMsCapiKeyStore.setToolTipText(null);
		new StatusBarChangeHandler(jmiOpenMsCapiKeyStore, (String) openMsCapiAction.getValue(Action.LONG_DESCRIPTION),
				this);
		// show menu item for MSCAPI Windows-MY only on Windows
		if (OperatingSystem.isWindows()) {
			jmOpenSpecial.add(jmiOpenMsCapiKeyStore);
		}

		jmFile.addSeparator();

		jmiClose = new JMenuItem(closeAction);
		PlatformUtil.setMnemonic(jmiClose, res.getString("KseFrame.jmiClose.mnemonic").charAt(0));
		jmiClose.setToolTipText(null);
		new StatusBarChangeHandler(jmiClose, (String) closeAction.getValue(Action.LONG_DESCRIPTION), this);
		jmFile.add(jmiClose);

		jmiCloseAll = new JMenuItem(closeAllAction);
		PlatformUtil.setMnemonic(jmiCloseAll, res.getString("KseFrame.jmiCloseAll.mnemonic").charAt(0));
		jmiCloseAll.setToolTipText(null);
		new StatusBarChangeHandler(jmiCloseAll, (String) closeAllAction.getValue(Action.LONG_DESCRIPTION), this);
		jmFile.add(jmiCloseAll);

		jmFile.addSeparator();

		jmiSave = new JMenuItem(saveAction);
		PlatformUtil.setMnemonic(jmiSave, res.getString("KseFrame.jmiSave.mnemonic").charAt(0));
		jmiSave.setToolTipText(null);
		new StatusBarChangeHandler(jmiSave, (String) saveAction.getValue(Action.LONG_DESCRIPTION), this);
		jmFile.add(jmiSave);

		jmiSaveAs = new JMenuItem(saveAsAction);
		PlatformUtil.setMnemonic(jmiSaveAs, res.getString("KseFrame.jmiSaveAs.mnemonic").charAt(0));
		jmiSaveAs.setDisplayedMnemonicIndex(5);
		jmiSaveAs.setToolTipText(null);
		new StatusBarChangeHandler(jmiSaveAs, (String) saveAsAction.getValue(Action.LONG_DESCRIPTION), this);
		jmFile.add(jmiSaveAs);

		jmiSaveAll = new JMenuItem(saveAllAction);
		PlatformUtil.setMnemonic(jmiSaveAll, res.getString("KseFrame.jmiSaveAll.mnemonic").charAt(0));
		jmiSaveAll.setToolTipText(null);
		new StatusBarChangeHandler(jmiSaveAll, (String) saveAllAction.getValue(Action.LONG_DESCRIPTION), this);
		jmFile.add(jmiSaveAll);

		jmFile.addSeparator();

		jmrfRecentFiles = new JMenuRecentFiles(res.getString("KseFrame.jmrfRecentFiles.text"), RECENT_FILES_SIZE);
		jmrfRecentFiles.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/recentfiles.png"))));
		PlatformUtil.setMnemonic(jmrfRecentFiles, res.getString("KseFrame.jmrfRecentFiles.mnemonic").charAt(0));
		jmFile.add(jmrfRecentFiles);

		File[] recentFiles = applicationSettings.getRecentFiles();

		for (int i = recentFiles.length - 1; i >= 0; i--) {
			jmrfRecentFiles.add(createRecentFileMenuItem(jmrfRecentFiles, recentFiles[i]));
		}

		if (!OperatingSystem.isMacOs()) {
			jmFile.addSeparator();

			jmiExit = new JMenuItem(exitAction);
			PlatformUtil.setMnemonic(jmiExit, res.getString("KseFrame.jmiExit.mnemonic").charAt(0));
			jmiExit.setToolTipText(null);
			new StatusBarChangeHandler(jmiExit, (String) exitAction.getValue(Action.LONG_DESCRIPTION), this);
			jmFile.add(jmiExit);
		}

		jmEdit = new JMenu(res.getString("KseFrame.jmEdit.text"));
		PlatformUtil.setMnemonic(jmEdit, res.getString("KseFrame.jmEdit.mnemonic").charAt(0));

		jmiUndo = new JMenuItem(undoAction);
		PlatformUtil.setMnemonic(jmiUndo, res.getString("KseFrame.jmiUndo.mnemonic").charAt(0));
		jmiUndo.setToolTipText(null);
		new StatusBarChangeHandler(jmiUndo, (String) undoAction.getValue(Action.LONG_DESCRIPTION), this);
		jmEdit.add(jmiUndo);

		jmiRedo = new JMenuItem(redoAction);
		PlatformUtil.setMnemonic(jmiRedo, res.getString("KseFrame.jmiRedo.mnemonic").charAt(0));
		jmiRedo.setToolTipText(null);
		new StatusBarChangeHandler(jmiRedo, (String) redoAction.getValue(Action.LONG_DESCRIPTION), this);
		jmEdit.add(jmiRedo);

		jmEdit.addSeparator();

		jmiCut = new JMenuItem(cutAction);
		PlatformUtil.setMnemonic(jmiCut, res.getString("KseFrame.jmiCut.mnemonic").charAt(0));
		jmiCut.setToolTipText(null);
		new StatusBarChangeHandler(jmiCut, (String) cutAction.getValue(Action.LONG_DESCRIPTION), this);
		jmEdit.add(jmiCut);

		jmiCopy = new JMenuItem(copyAction);
		PlatformUtil.setMnemonic(jmiCopy, res.getString("KseFrame.jmiCopy.mnemonic").charAt(0));
		jmiCopy.setToolTipText(null);
		new StatusBarChangeHandler(jmiCopy, (String) copyAction.getValue(Action.LONG_DESCRIPTION), this);
		jmEdit.add(jmiCopy);

		jmiPaste = new JMenuItem(pasteAction);
		PlatformUtil.setMnemonic(jmiPaste, res.getString("KseFrame.jmiPaste.mnemonic").charAt(0));
		jmiPaste.setToolTipText(null);
		new StatusBarChangeHandler(jmiPaste, (String) pasteAction.getValue(Action.LONG_DESCRIPTION), this);
		jmEdit.add(jmiPaste);

		jmView = new JMenu(res.getString("KseFrame.jmView.text"));
		PlatformUtil.setMnemonic(jmView, res.getString("KseFrame.jmView.mnemonic").charAt(0));

		boolean showToolBar = applicationSettings.getShowToolBar();

		jcbmiShowHideToolBar = new JCheckBoxMenuItem(showHideToolBarAction);
		jcbmiShowHideToolBar.setState(showToolBar);
		PlatformUtil.setMnemonic(jcbmiShowHideToolBar, res.getString("KseFrame.jcbmiShowHideToolBar.mnemonic")
				.charAt(0));
		jcbmiShowHideToolBar.setToolTipText(null);
		new StatusBarChangeHandler(jcbmiShowHideToolBar,
				(String) showHideToolBarAction.getValue(Action.LONG_DESCRIPTION), this);
		jmView.add(jcbmiShowHideToolBar);

		jcbmiShowHideStatusBar = new JCheckBoxMenuItem(showHideStatusBarAction);
		jcbmiShowHideStatusBar.setState(applicationSettings.getShowStatusBar());
		PlatformUtil.setMnemonic(jcbmiShowHideStatusBar, res.getString("KseFrame.jcbmiShowHideStatusBar.mnemonic")
				.charAt(0));
		jcbmiShowHideStatusBar.setToolTipText(null);
		new StatusBarChangeHandler(jcbmiShowHideStatusBar,
				(String) showHideStatusBarAction.getValue(Action.LONG_DESCRIPTION), this);
		jmView.add(jcbmiShowHideStatusBar);

		jmView.addSeparator();

		jmTabStyle = new JMenu(res.getString("KseFrame.jmTabStyle.text"));
		jmTabStyle.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/tabstyle.png"))));
		PlatformUtil.setMnemonic(jmTabStyle, res.getString("KseFrame.jmTabStyle.mnemonic").charAt(0));
		jmView.add(jmTabStyle);

		jrbmiTabStyleWrap = new JRadioButtonMenuItem(tabStyleWrapAction);
		PlatformUtil.setMnemonic(jrbmiTabStyleWrap, res.getString("KseFrame.jrbmiTabStyleWrap.mnemonic").charAt(0));
		jrbmiTabStyleWrap.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiTabStyleWrap, (String) tabStyleWrapAction.getValue(Action.LONG_DESCRIPTION),
				this);
		jmTabStyle.add(jrbmiTabStyleWrap);

		jrbmiTabStyleScroll = new JRadioButtonMenuItem(tabStyleScrollAction);
		PlatformUtil.setMnemonic(jrbmiTabStyleScroll, res.getString("KseFrame.jrbmiTabStyleScroll.mnemonic").charAt(0));
		jrbmiTabStyleScroll.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiTabStyleScroll,
				(String) tabStyleScrollAction.getValue(Action.LONG_DESCRIPTION), this);
		jmTabStyle.add(jrbmiTabStyleScroll);

		ButtonGroup bgTabStyles = new ButtonGroup();
		bgTabStyles.add(jrbmiTabStyleWrap);
		bgTabStyles.add(jrbmiTabStyleScroll);

		int tabLayout = applicationSettings.getTabLayout();

		if (tabLayout == JTabbedPane.WRAP_TAB_LAYOUT) {
			jrbmiTabStyleWrap.setSelected(true);
		} else {
			jrbmiTabStyleScroll.setSelected(true);
		}

		jmTools = new JMenu(res.getString("KseFrame.jmTools.text"));
		PlatformUtil.setMnemonic(jmTools, res.getString("KseFrame.jmTools.mnemonic").charAt(0));

		jmiGenerateKeyPair = new JMenuItem(generateKeyPairAction);
		PlatformUtil.setMnemonic(jmiGenerateKeyPair, res.getString("KseFrame.jmiGenerateKeyPair.mnemonic").charAt(0));
		jmiGenerateKeyPair.setToolTipText(null);
		new StatusBarChangeHandler(jmiGenerateKeyPair,
				(String) generateKeyPairAction.getValue(Action.LONG_DESCRIPTION), this);
		jmTools.add(jmiGenerateKeyPair);

		jmiGenerateSecretKey = new JMenuItem(generateSecretKeyAction);
		PlatformUtil.setMnemonic(jmiGenerateSecretKey, res.getString("KseFrame.jmiGenerateSecretKey.mnemonic")
				.charAt(0));
		jmiGenerateSecretKey.setToolTipText(null);
		new StatusBarChangeHandler(jmiGenerateSecretKey,
				(String) generateSecretKeyAction.getValue(Action.LONG_DESCRIPTION), this);
		jmTools.add(jmiGenerateSecretKey);

		jmiImportTrustedCertificate = new JMenuItem(importTrustedCertificateAction);
		PlatformUtil.setMnemonic(jmiImportTrustedCertificate,
				res.getString("KseFrame.jmiImportTrustedCertificate.mnemonic").charAt(0));
		jmiImportTrustedCertificate.setToolTipText(null);
		new StatusBarChangeHandler(jmiImportTrustedCertificate,
				(String) importTrustedCertificateAction.getValue(Action.LONG_DESCRIPTION), this);
		jmTools.add(jmiImportTrustedCertificate);

		jmiImportKeyPair = new JMenuItem(importKeyPairAction);
		PlatformUtil.setMnemonic(jmiImportKeyPair, res.getString("KseFrame.jmiImportKeyPair.mnemonic").charAt(0));
		jmiImportKeyPair.setToolTipText(null);
		new StatusBarChangeHandler(jmiImportKeyPair, (String) importKeyPairAction.getValue(Action.LONG_DESCRIPTION),
				this);
		jmTools.add(jmiImportKeyPair);

		jmTools.addSeparator();

		jmiSetPassword = new JMenuItem(setPasswordAction);
		PlatformUtil.setMnemonic(jmiSetPassword, res.getString("KseFrame.jmiSetPassword.mnemonic").charAt(0));
		jmiSetPassword.setToolTipText(null);
		new StatusBarChangeHandler(jmiSetPassword, (String) setPasswordAction.getValue(Action.LONG_DESCRIPTION), this);
		jmTools.add(jmiSetPassword);

		jmChangeType = new JMenu(res.getString("KseFrame.jmChangeType.text"));
		jmChangeType.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/keystoretype.png"))));
		PlatformUtil.setMnemonic(jmChangeType, res.getString("KseFrame.jmChangeType.mnemonic").charAt(0));
		jmChangeType.setEnabled(false);
		jmTools.add(jmChangeType);

		jrbmiChangeTypeJks = new JRadioButtonMenuItem(changeTypeJksAction);
		PlatformUtil.setMnemonic(jrbmiChangeTypeJks, res.getString("KseFrame.jrbmiChangeTypeJks.mnemonic").charAt(0));
		jrbmiChangeTypeJks.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiChangeTypeJks, (String) changeTypeJksAction.getValue(Action.LONG_DESCRIPTION),
				this);
		jmChangeType.add(jrbmiChangeTypeJks);

		jrbmiChangeTypeJceks = new JRadioButtonMenuItem(changeTypeJceksAction);
		PlatformUtil.setMnemonic(jrbmiChangeTypeJceks, res.getString("KseFrame.jrbmiChangeTypeJceks.mnemonic")
				.charAt(0));
		jrbmiChangeTypeJceks.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiChangeTypeJceks,
				(String) changeTypeJceksAction.getValue(Action.LONG_DESCRIPTION), this);
		jmChangeType.add(jrbmiChangeTypeJceks);

		jrbmiChangeTypePkcs12 = new JRadioButtonMenuItem(changeTypePkcs12Action);
		PlatformUtil.setMnemonic(jrbmiChangeTypePkcs12, res.getString("KseFrame.jrbmiChangeTypePkcs12.mnemonic")
				.charAt(0));
		jrbmiChangeTypePkcs12.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiChangeTypePkcs12,
				(String) changeTypePkcs12Action.getValue(Action.LONG_DESCRIPTION), this);
		jmChangeType.add(jrbmiChangeTypePkcs12);

		jrbmiChangeTypeBksV1 = new JRadioButtonMenuItem(changeTypeBksV1Action);
		PlatformUtil.setMnemonic(jrbmiChangeTypeBksV1, res.getString("KseFrame.jrbmiChangeTypeBksV1.mnemonic").charAt(0));
		jrbmiChangeTypeBksV1.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiChangeTypeBksV1, (String) changeTypeBksV1Action.getValue(Action.LONG_DESCRIPTION),
				this);
		jmChangeType.add(jrbmiChangeTypeBksV1);

		jrbmiChangeTypeBks = new JRadioButtonMenuItem(changeTypeBksAction);
		PlatformUtil.setMnemonic(jrbmiChangeTypeBks, res.getString("KseFrame.jrbmiChangeTypeBks.mnemonic").charAt(0));
		jrbmiChangeTypeBks.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiChangeTypeBks, (String) changeTypeBksAction.getValue(Action.LONG_DESCRIPTION),
				this);
		jmChangeType.add(jrbmiChangeTypeBks);

		jrbmiChangeTypeBcfks = new JRadioButtonMenuItem(changeTypeBcfksAction);
		PlatformUtil.setMnemonic(jrbmiChangeTypeBcfks, res.getString("KseFrame.jrbmiChangeTypeBcfks.mnemonic").charAt(0));
		jrbmiChangeTypeBcfks.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiChangeTypeBcfks, (String) changeTypeBcfksAction.getValue(Action.LONG_DESCRIPTION),
				this);
		jmChangeType.add(jrbmiChangeTypeBcfks);

		jrbmiChangeTypeUber = new JRadioButtonMenuItem(changeTypeUberAction);
		PlatformUtil.setMnemonic(jrbmiChangeTypeUber, res.getString("KseFrame.jrbmiChangeTypeUber.mnemonic").charAt(0));
		jrbmiChangeTypeUber.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiChangeTypeUber,
				(String) changeTypeUberAction.getValue(Action.LONG_DESCRIPTION), this);
		jmChangeType.add(jrbmiChangeTypeUber);

		ButtonGroup changeTypeGroup = new ButtonGroup();
		changeTypeGroup.add(jrbmiChangeTypeJks);
		changeTypeGroup.add(jrbmiChangeTypeJceks);
		changeTypeGroup.add(jrbmiChangeTypePkcs12);
		changeTypeGroup.add(jrbmiChangeTypeBks);
		changeTypeGroup.add(jrbmiChangeTypeBcfks);
		changeTypeGroup.add(jrbmiChangeTypeBksV1);
		changeTypeGroup.add(jrbmiChangeTypeUber);

		jmiProperties = new JMenuItem(propertiesAction);
		PlatformUtil.setMnemonic(jmiProperties, res.getString("KseFrame.jmiProperties.mnemonic").charAt(0));
		jmiProperties.setToolTipText(null);
		new StatusBarChangeHandler(jmiProperties, (String) propertiesAction.getValue(Action.LONG_DESCRIPTION), this);
		jmTools.add(jmiProperties);

		if (!OperatingSystem.isMacOs()) {
			jmTools.addSeparator();

			jmiPreferences = new JMenuItem(preferencesAction);
			PlatformUtil.setMnemonic(jmiPreferences, res.getString("KseFrame.jmiPreferences.mnemonic").charAt(0));
			jmiPreferences.setToolTipText(null);
			new StatusBarChangeHandler(jmiPreferences, (String) preferencesAction.getValue(Action.LONG_DESCRIPTION),
					this);
			jmTools.add(jmiPreferences);
		}

		jmExamine = new JMenu(res.getString("KseFrame.jmExamine.text"));
		PlatformUtil.setMnemonic(jmExamine, res.getString("KseFrame.jmExamine.mnemonic").charAt(0));

		jmiExamineFile = new JMenuItem(examineFileAction);
		PlatformUtil.setMnemonic(jmiExamineFile, res.getString("KseFrame.jmiExamineFile.mnemonic")
				.charAt(0));
		jmiExamineFile.setToolTipText(null);
		new StatusBarChangeHandler(jmiExamineFile,
				(String) examineFileAction.getValue(Action.LONG_DESCRIPTION), this);
		jmExamine.add(jmiExamineFile);

		jmiExamineClipboard = new JMenuItem(examineClipboardAction);
		PlatformUtil.setMnemonic(jmiExamineClipboard, res.getString("KseFrame.jmiExamineClipboard.mnemonic").charAt(0));
		jmiExamineClipboard.setToolTipText(null);
		new StatusBarChangeHandler(jmiExamineClipboard, (String) examineClipboardAction.getValue(Action.LONG_DESCRIPTION), this);
		jmExamine.add(jmiExamineClipboard);

		jmiExamineSsl = new JMenuItem(examineSslAction);
		PlatformUtil.setMnemonic(jmiExamineSsl, res.getString("KseFrame.jmiExamineSsl.mnemonic").charAt(0));
		jmiExamineSsl.setToolTipText(null);
		new StatusBarChangeHandler(jmiExamineSsl, (String) examineSslAction.getValue(Action.LONG_DESCRIPTION), this);
		jmExamine.add(jmiExamineSsl);

		jmExamine.addSeparator();

		jmiDetectFileType = new JMenuItem(detectFileTypeAction);
		PlatformUtil.setMnemonic(jmiDetectFileType, res.getString("KseFrame.jmiDetectFileType.mnemonic").charAt(0));
		jmiDetectFileType.setToolTipText(null);
		new StatusBarChangeHandler(jmiDetectFileType, (String) detectFileTypeAction.getValue(Action.LONG_DESCRIPTION),
				this);
		jmExamine.add(jmiDetectFileType);

		jmHelp = new JMenu(res.getString("KseFrame.jmHelp.text"));
		PlatformUtil.setMnemonic(jmHelp, res.getString("KseFrame.jmHelp.mnemonic").charAt(0));

		jmiHelp = new JMenuItem(helpAction);
		PlatformUtil.setMnemonic(jmiHelp, res.getString("KseFrame.jmiHelp.mnemonic").charAt(0));
		jmiHelp.setToolTipText(null);
		new StatusBarChangeHandler(jmiHelp, (String) helpAction.getValue(Action.LONG_DESCRIPTION), this);
		jmHelp.add(jmiHelp);

		jmiTipOfTheDay = new JMenuItem(tipOfTheDayAction);
		PlatformUtil.setMnemonic(jmiTipOfTheDay, res.getString("KseFrame.jmiTipOfTheDay.mnemonic").charAt(0));
		jmiTipOfTheDay.setToolTipText(null);
		new StatusBarChangeHandler(jmiTipOfTheDay, (String) tipOfTheDayAction.getValue(Action.LONG_DESCRIPTION), this);
		jmHelp.add(jmiTipOfTheDay);

		jmHelp.addSeparator();

		jmOnlineResources = new JMenu(res.getString("KseFrame.jmOnlineResources.text"));
		jmOnlineResources.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/online.png"))));
		PlatformUtil.setMnemonic(jmOnlineResources, res.getString("KseFrame.jmOnlineResources.mnemonic").charAt(0));
		jmHelp.add(jmOnlineResources);

		jmiWebsite = new JMenuItem(websiteAction);
		PlatformUtil.setMnemonic(jmiWebsite, res.getString("KseFrame.jmiWebsite.mnemonic").charAt(0));
		jmiWebsite.setToolTipText(null);
		new StatusBarChangeHandler(jmiWebsite, (String) websiteAction.getValue(Action.LONG_DESCRIPTION), this);
		jmOnlineResources.add(jmiWebsite);

		jmiSourceforge = new JMenuItem(gitHubProjectSiteAction);
		PlatformUtil.setMnemonic(jmiSourceforge, res.getString("KseFrame.jmiSourceforge.mnemonic").charAt(0));
		jmiSourceforge.setToolTipText(null);
		new StatusBarChangeHandler(jmiSourceforge, (String) gitHubProjectSiteAction.getValue(Action.LONG_DESCRIPTION), this);
		jmOnlineResources.add(jmiSourceforge);

		jmiSfBugs = new JMenuItem(gitHubIssueTrackerAction);
		PlatformUtil.setMnemonic(jmiSfBugs, res.getString("KseFrame.jmiSfBugs.mnemonic").charAt(0));
		jmiSfBugs.setToolTipText(null);
		new StatusBarChangeHandler(jmiSfBugs, (String) gitHubIssueTrackerAction.getValue(Action.LONG_DESCRIPTION), this);
		jmOnlineResources.add(jmiSfBugs);

		jmiCheckUpdate = new JMenuItem(checkUpdateAction);
		PlatformUtil.setMnemonic(jmiCheckUpdate, res.getString("KseFrame.jmiCheckUpdate.mnemonic").charAt(0));
		jmiCheckUpdate.setToolTipText(null);
		new StatusBarChangeHandler(jmiCheckUpdate, (String) checkUpdateAction.getValue(Action.LONG_DESCRIPTION), this);
		// no update checks if KSE was packaged as rpm
		if (!Boolean.getBoolean(KSE_UPDATE_CHECK_DISABLED)) {
			jmOnlineResources.add(jmiCheckUpdate);
		}

		jmiSecurityProviders = new JMenuItem(securityProvidersAction);
		PlatformUtil.setMnemonic(jmiSecurityProviders, res.getString("KseFrame.jmiSecurityProviders.mnemonic")
				.charAt(0));
		jmiSecurityProviders.setToolTipText(null);
		new StatusBarChangeHandler(jmiSecurityProviders,
				(String) securityProvidersAction.getValue(Action.LONG_DESCRIPTION), this);
		jmHelp.add(jmiSecurityProviders);

		jmiCryptographyStrength = new JMenuItem(cryptographyStrengthAction);
		PlatformUtil.setMnemonic(jmiCryptographyStrength, res.getString("KseFrame.jmiCryptographyStrength.mnemonic")
				.charAt(0));
		jmiCryptographyStrength.setToolTipText(null);
		new StatusBarChangeHandler(jmiCryptographyStrength,
				(String) cryptographyStrengthAction.getValue(Action.LONG_DESCRIPTION), this);
		jmHelp.add(jmiCryptographyStrength);

		jmiJars = new JMenuItem(jarsAction);
		PlatformUtil.setMnemonic(jmiJars, res.getString("KseFrame.jmiJars.mnemonic").charAt(0));
		jmiJars.setToolTipText(null);
		new StatusBarChangeHandler(jmiJars, (String) jarsAction.getValue(Action.LONG_DESCRIPTION), this);
		jmHelp.add(jmiJars);

		jmiSystemInformation = new JMenuItem(systemInformationAction);
		PlatformUtil.setMnemonic(jmiSystemInformation, res.getString("KseFrame.jmiSystemInformation.mnemonic")
				.charAt(0));
		jmiSystemInformation.setToolTipText(null);
		new StatusBarChangeHandler(jmiSystemInformation,
				(String) systemInformationAction.getValue(Action.LONG_DESCRIPTION), this);
		jmHelp.add(jmiSystemInformation);

		if (!OperatingSystem.isMacOs()) {
			jmHelp.addSeparator();

			jmiAbout = new JMenuItem(aboutAction);
			PlatformUtil.setMnemonic(jmiAbout, res.getString("KseFrame.jmiAbout.mnemonic").charAt(0));
			jmiAbout.setToolTipText(null);
			new StatusBarChangeHandler(jmiAbout, (String) aboutAction.getValue(Action.LONG_DESCRIPTION), this);
			jmHelp.add(jmiAbout);
		}

		jmbMenuBar.add(jmFile);
		jmbMenuBar.add(jmEdit);
		jmbMenuBar.add(jmView);
		jmbMenuBar.add(jmTools);
		jmbMenuBar.add(jmExamine);
		jmbMenuBar.add(jmHelp);

		frame.setJMenuBar(jmbMenuBar);
	}

	private JMenuItemRecentFile createRecentFileMenuItem(JMenuRecentFiles jmRecentFiles, File recentFile) {
		JMenuItemRecentFile jmirfNew = new JMenuItemRecentFile(jmRecentFiles, recentFile);
		jmirfNew.addActionListener(new RecentKeyStoreFileActionListener(recentFile, this));

		new StatusBarChangeHandler(jmirfNew, MessageFormat.format(res.getString("KseFrame.recentfile.statusbar"),
				recentFile), this);
		return jmirfNew;
	}

	private void initToolBar() {
		jbNew = new JButton();
		jbNew.setAction(newAction);
		jbNew.setText(null);
		PlatformUtil.setMnemonic(jbNew, 0);
		jbNew.setFocusable(false);
		jbNew.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) newAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbOpen = new JButton();
		jbOpen.setAction(openAction);
		jbOpen.setText(null);
		PlatformUtil.setMnemonic(jbOpen, 0);
		jbOpen.setFocusable(false);
		jbOpen.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) openAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbSave = new JButton();
		jbSave.setAction(saveAction);
		jbSave.setText(null);
		PlatformUtil.setMnemonic(jbSave, 0);
		jbSave.setFocusable(false);
		jbSave.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) saveAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbUndo = new JButton();
		jbUndo.setAction(undoAction);
		jbUndo.setText(null);
		PlatformUtil.setMnemonic(jbUndo, 0);
		jbUndo.setHideActionText(true); // Ensure text is not displayed when
		// changed dynamically
		// later on action
		jbUndo.setFocusable(false);
		jbUndo.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) undoAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbRedo = new JButton();
		jbRedo.setAction(redoAction);
		jbRedo.setText(null);
		PlatformUtil.setMnemonic(jbRedo, 0);
		jbRedo.setHideActionText(true); // Ensure text is not displayed when
		// changed dynamically
		// later on action
		jbRedo.setFocusable(false);
		jbRedo.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) redoAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbCut = new JButton();
		jbCut.setAction(cutAction);
		jbCut.setText(null);
		PlatformUtil.setMnemonic(jbCut, 0);
		jbCut.setFocusable(false);
		jbCut.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) cutAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbCopy = new JButton();
		jbCopy.setAction(copyAction);
		jbCopy.setText(null);
		PlatformUtil.setMnemonic(jbCopy, 0);
		jbCopy.setFocusable(false);
		jbCopy.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) copyAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbPaste = new JButton();
		jbPaste.setAction(pasteAction);
		jbPaste.setText(null);
		PlatformUtil.setMnemonic(jbPaste, 0);
		jbPaste.setFocusable(false);
		jbPaste.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) pasteAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbGenerateKeyPair = new JButton();
		jbGenerateKeyPair.setAction(generateKeyPairAction);
		jbGenerateKeyPair.setText(null);
		PlatformUtil.setMnemonic(jbGenerateKeyPair, 0);
		jbGenerateKeyPair.setFocusable(false);
		jbGenerateKeyPair.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) generateKeyPairAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbGenerateSecretKey = new JButton();
		jbGenerateSecretKey.setAction(generateSecretKeyAction);
		jbGenerateSecretKey.setText(null);
		PlatformUtil.setMnemonic(jbGenerateSecretKey, 0);
		jbGenerateSecretKey.setFocusable(false);
		jbGenerateSecretKey.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) generateSecretKeyAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbImportTrustedCertificate = new JButton();
		jbImportTrustedCertificate.setAction(importTrustedCertificateAction);
		jbImportTrustedCertificate.setText(null);
		PlatformUtil.setMnemonic(jbImportTrustedCertificate, 0);
		jbImportTrustedCertificate.setFocusable(false);
		jbImportTrustedCertificate.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) importTrustedCertificateAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbImportKeyPair = new JButton();
		jbImportKeyPair.setAction(importKeyPairAction);
		jbImportKeyPair.setText(null);
		PlatformUtil.setMnemonic(jbImportKeyPair, 0);
		jbImportKeyPair.setFocusable(false);
		jbImportKeyPair.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) importKeyPairAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbSetPassword = new JButton();
		jbSetPassword.setAction(setPasswordAction);
		jbSetPassword.setText(null);
		PlatformUtil.setMnemonic(jbSetPassword, 0);
		jbSetPassword.setFocusable(false);
		jbSetPassword.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) setPasswordAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbProperties = new JButton();
		jbProperties.setAction(propertiesAction);
		jbProperties.setText(null);
		PlatformUtil.setMnemonic(jbProperties, 0);
		jbProperties.setFocusable(false);
		jbProperties.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) propertiesAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbExamineFile = new JButton();
		jbExamineFile.setAction(examineFileAction);
		jbExamineFile.setText(null);
		PlatformUtil.setMnemonic(jbExamineFile, 0);
		jbExamineFile.setFocusable(false);
		jbExamineFile.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) examineFileAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbExamineClipboard = new JButton();
		jbExamineClipboard.setAction(examineClipboardAction);
		jbExamineClipboard.setText(null);
		PlatformUtil.setMnemonic(jbExamineClipboard, 0);
		jbExamineClipboard.setFocusable(false);
		jbExamineClipboard.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) examineClipboardAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbExamineSsl = new JButton();
		jbExamineSsl.setAction(examineSslAction);
		jbExamineSsl.setText(null);
		PlatformUtil.setMnemonic(jbExamineSsl, 0);
		jbExamineSsl.setFocusable(false);
		jbExamineSsl.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) examineSslAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jbHelp = new JButton();
		jbHelp.setAction(helpAction);
		jbHelp.setText(null);
		PlatformUtil.setMnemonic(jbHelp, 0);
		jbHelp.setFocusable(false);
		jbHelp.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent evt) {
				setStatusBarText((String) helpAction.getValue(Action.LONG_DESCRIPTION));
			}

			@Override
			public void mouseExited(MouseEvent evt) {
				setDefaultStatusBarText();
			}
		});

		jtbToolBar = new JToolBar();

		// If using Windows need a bottom line on the toolbar to seperate it
		// from the main view
		if (LnfUtil.usingWindowsLnf()) {
			jtbToolBar.setBorder(new MatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY));
		}

		jtbToolBar.setFloatable(false);
		jtbToolBar.setRollover(true);
		jtbToolBar.setName(KSE.getApplicationName());

		jtbToolBar.add(jbNew);
		jtbToolBar.add(jbOpen);
		jtbToolBar.add(jbSave);

		jtbToolBar.addSeparator();

		jtbToolBar.add(jbUndo);
		jtbToolBar.add(jbRedo);

		jtbToolBar.addSeparator();

		jtbToolBar.add(jbCut);
		jtbToolBar.add(jbCopy);
		jtbToolBar.add(jbPaste);

		jtbToolBar.addSeparator();

		jtbToolBar.add(jbGenerateKeyPair);
		jtbToolBar.add(jbGenerateSecretKey);
		jtbToolBar.add(jbImportTrustedCertificate);
		jtbToolBar.add(jbImportKeyPair);
		jtbToolBar.add(jbSetPassword);
		jtbToolBar.add(jbProperties);

		jtbToolBar.addSeparator();

		jtbToolBar.add(jbExamineFile);
		jtbToolBar.add(jbExamineClipboard);
		jtbToolBar.add(jbExamineSsl);

		jtbToolBar.addSeparator();

		jtbToolBar.add(jbHelp);

		if (applicationSettings.getShowToolBar()) {
			frame.getContentPane().add(jtbToolBar, BorderLayout.NORTH);
		}
	}

	private void initMainPane() {

		// Displays Quick Start pane initially and KeyStore tabbed pane when at
		// least one KeyStore is open

		jQuickStart = new JQuickStartPane(this);

		jkstpKeyStores = new JKeyStoreTabbedPane(this);

		int tabLayout = applicationSettings.getTabLayout();
		jkstpKeyStores.setTabLayoutPolicy(tabLayout);

		jkstpKeyStores.setBorder(new EmptyBorder(3, 3, 3, 3));

		jkstpKeyStores.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent evt) {
				// Update controls as selected KeyStore is changed
				updateControls(false);
			}
		});

		jkstpKeyStores.addMouseListener(new MouseAdapter() {
			@Override
			public void mousePressed(MouseEvent evt) {
				maybeShowKeyStoreTabPopup(evt);
			}

			@Override
			public void mouseReleased(MouseEvent evt) {
				maybeShowKeyStoreTabPopup(evt);
			}

			@Override
			public void mouseClicked(MouseEvent evt) {
				// Close tab if it is middle-clicked
				if (evt.getButton() == MouseEvent.BUTTON2) {
					closeAction.closeActiveKeyStore();
				}
			}
		});

		Rectangle sizeAndPosition = applicationSettings.getSizeAndPosition();
		int width = sizeAndPosition.width;
		int height = sizeAndPosition.height;

		if (width <= 0 || height <= 0) {
			jQuickStart.setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
		} else {
			jQuickStart.setPreferredSize(new Dimension(width, height));
		}

		frame.getContentPane().add(jQuickStart, BorderLayout.CENTER);
	}

	private JScrollPane wrapKeyStoreTableInScrollPane(JTable jtKeyStore) {

		jtKeyStore.setOpaque(true);
		jtKeyStore.setShowGrid(false);
		jtKeyStore.setFillsViewportHeight(true);
		JScrollPane jspKeyStoreTable = PlatformUtil.createScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
				ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

		jspKeyStoreTable.setViewportView(jtKeyStore);
		jspKeyStoreTable.setBorder(BorderFactory.createEtchedBorder());
		jspKeyStoreTable.setFocusable(false);

		return jspKeyStoreTable;
	}

	private JTable createEmptyKeyStoreTable() {
		keyStoreTableColumns = applicationSettings.getKeyStoreTableColumns();
		KeyStoreTableModel ksModel = new KeyStoreTableModel(keyStoreTableColumns);
		final JTable jtKeyStore = new JKseTable(ksModel);

		RowSorter<KeyStoreTableModel> sorter = new TableRowSorter<>(ksModel);
		jtKeyStore.setRowSorter(sorter);

		jtKeyStore.setShowGrid(false);
		jtKeyStore.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		jtKeyStore.getTableHeader().setReorderingAllowed(false);
		jtKeyStore.setAutoResizeMode(autoResizeMode);
		jtKeyStore.setRowHeight(Math.max(18, jtKeyStore.getRowHeight())); // min. height of 18 because of 16x16 icons

		// Register cut, copy and paste actions with the relevant keystrokes
		jtKeyStore.getInputMap().put((KeyStroke) cutAction.getValue(Action.ACCELERATOR_KEY), CUT_KEY);
		jtKeyStore.getActionMap().put(CUT_KEY, cutAction);

		jtKeyStore.getInputMap().put((KeyStroke) copyAction.getValue(Action.ACCELERATOR_KEY), COPY_KEY);
		jtKeyStore.getActionMap().put(COPY_KEY, copyAction);

		jtKeyStore.getInputMap().put((KeyStroke) pasteAction.getValue(Action.ACCELERATOR_KEY), PASTE_KEY);
		jtKeyStore.getActionMap().put(PASTE_KEY, pasteAction);

		jtKeyStore.getInputMap().put((KeyStroke) renameKeyPairAction.getValue(Action.ACCELERATOR_KEY), RENAME_KEY);
		jtKeyStore.getActionMap().put(RENAME_KEY, renameKeyPairAction);

		// open keystore entry details when user presses enter key
		KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
		jtKeyStore.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(enter, "Enter");
		jtKeyStore.getActionMap().put("Enter", new AbstractAction() {
			private static final long serialVersionUID = 1L;
			@Override
			public void actionPerformed(ActionEvent ae) {
				try {
					int selectedRow = jtKeyStore.getSelectedRow();
					if (selectedRow != -1) {
						CursorUtil.setCursorBusy(frame);
						showSelectedEntryDetails(jtKeyStore, selectedRow);
					}
				} finally {
					CursorUtil.setCursorFree(frame);
				}
			}
		});

		// Add DnD source support to KeyStore
		DragSource ds = DragSource.getDefaultDragSource();
		ds.createDefaultDragGestureRecognizer(jtKeyStore, DnDConstants.ACTION_MOVE,
				new KeyStoreEntryDragGestureListener(this));
		jtKeyStore.setAutoResizeMode(autoResizeMode);

		// Add custom renderers for headers and cells
		colAdjust(jtKeyStore);

		setColumnsToIconSize(jtKeyStore, 0, 1, 2);

		jtKeyStore.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent evt) {
				maybeShowSelectedEntryDetails(evt);
			}

			@Override
			public void mousePressed(MouseEvent evt) {
				maybeShowSelectedEntryPopupMenu(evt);
			}

			@Override
			public void mouseReleased(MouseEvent evt) {
				maybeShowSelectedEntryPopupMenu(evt);
			}
		});

		jtKeyStore.addKeyListener(new KeyAdapter() {
			boolean deleteLastPressed = false;

			@Override
			public void keyPressed(KeyEvent evt) {
				// Record delete pressed on non-Macs
				if (!OperatingSystem.isMacOs()) {
					deleteLastPressed = evt.getKeyCode() == KeyEvent.VK_DELETE;
				}

				if (evt.getKeyCode() == KeyEvent.VK_F2) {
					renameSelectedEntry();
				}
			}

			@Override
			public void keyReleased(KeyEvent evt) {
				// Delete on non-Mac if delete was pressed and is now released
				if (!OperatingSystem.isMacOs() && deleteLastPressed && evt.getKeyCode() == KeyEvent.VK_DELETE) {
					deleteLastPressed = false;
					handleDeleteSelectedEntry();
				}
			}

			@Override
			public void keyTyped(KeyEvent evt) {
				// Delete on Mac if back space typed
				if (OperatingSystem.isMacOs() && evt.getKeyChar() == 0x08) {
					handleDeleteSelectedEntry();
				}
			}
		});

		return jtKeyStore;
	}

	private void handleDeleteSelectedEntry() {
		KeyStoreHistory history = getActiveKeyStoreHistory();
		KeyStore keyStore = history.getCurrentState().getKeyStore();
		String alias = getSelectedEntryAlias();
		String nextAlias = getNextEntrysAlias();
		int nrEntriesBeforeDeletion = getNumberOfEntries();

		try {
			String[] aliases = getSelectedEntryAliases();
			if (aliases.length == 1) {
				// if only one entry is selected, use the specific action as it displays a nice warning dialog
				if (KeyStoreUtil.isKeyPairEntry(alias, keyStore)) {
					deleteKeyPairAction.deleteSelectedEntry();
				} else if (KeyStoreUtil.isTrustedCertificateEntry(alias, keyStore)) {
					deleteTrustedCertificateAction.deleteSelectedEntry();
				} else {
					deleteKeyAction.deleteSelectedEntry();
				}
			} else {
				deleteMultipleEntriesAction.deleteSelectedEntries();
			}

			// if one entry was deleted, select next entry
			if ((getNumberOfEntries() < nrEntriesBeforeDeletion) && (nextAlias != null)) {
				setSelectedEntriesByAliases(nextAlias);
			}
		} catch (Exception ex) {
			DError.displayError(frame, ex);
		}
	}

	private void initStatusBar() {
		jlStatusBar = new JLabel();

		int rightPadding = 3;

		if (OperatingSystem.isMacOs()) {
			rightPadding = 15; // Allow extra padding in the grow box status bar
			// if using macOS
		}

		jlStatusBar.setBorder(new CompoundBorder(new MatteBorder(1, 0, 0, 0, Color.LIGHT_GRAY), new EmptyBorder(3, 3,
				3, rightPadding)));

		setDefaultStatusBarText();

		if (applicationSettings.getShowStatusBar()) {
			frame.getContentPane().add(jlStatusBar, BorderLayout.SOUTH);
		}
	}

	private void initKeyStoreTabPopupMenu() {
		jpmKeyStoreTab = new JPopupMenu();

		jmiKeyStoreTabSave = new JMenuItem(saveAction);
		jmiKeyStoreTabSave.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreTabSave, (String) saveAction.getValue(Action.LONG_DESCRIPTION), this);
		jpmKeyStoreTab.add(jmiKeyStoreTabSave);

		jmiKeyStoreTabSaveAll = new JMenuItem(saveAllAction);
		jmiKeyStoreTabSaveAll.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreTabSaveAll, (String) saveAllAction.getValue(Action.LONG_DESCRIPTION),
				this);
		jpmKeyStoreTab.add(jmiKeyStoreTabSaveAll);

		jpmKeyStoreTab.addSeparator();

		jmiKeyStoreTabPaste = new JMenuItem(pasteAction);
		jmiKeyStoreTabPaste.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreTabPaste, (String) pasteAction.getValue(Action.LONG_DESCRIPTION), this);
		jpmKeyStoreTab.add(jmiKeyStoreTabPaste);

		jpmKeyStoreTab.addSeparator();

		jmiKeyStoreTabClose = new JMenuItem(closeAction);
		jmiKeyStoreTabClose.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreTabClose, (String) closeAction.getValue(Action.LONG_DESCRIPTION), this);
		jpmKeyStoreTab.add(jmiKeyStoreTabClose);

		jmiKeyStoreTabCloseOthers = new JMenuItem(closeOthersAction);
		jmiKeyStoreTabCloseOthers.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreTabCloseOthers,
				(String) closeOthersAction.getValue(Action.LONG_DESCRIPTION), this);
		jpmKeyStoreTab.add(jmiKeyStoreTabCloseOthers);

		jmiKeyStoreTabCloseAll = new JMenuItem(closeAllAction);
		jmiKeyStoreTabCloseAll.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreTabCloseAll, (String) closeAllAction.getValue(Action.LONG_DESCRIPTION),
				this);
		jpmKeyStoreTab.add(jmiKeyStoreTabCloseAll);

		jpmKeyStoreTab.addSeparator();

		jmiKeyStoreTabProperties = new JMenuItem(propertiesAction);
		jmiKeyStoreTabProperties.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreTabProperties,
				(String) propertiesAction.getValue(Action.LONG_DESCRIPTION), this);
		jpmKeyStoreTab.add(jmiKeyStoreTabProperties);
	}

	private void initKeyStorePopupMenu() {
		jpmKeyStore = new JPopupMenu();

		jmiKeyStoreGenerateKeyPair = new JMenuItem(generateKeyPairAction);
		PlatformUtil.setMnemonic(jmiGenerateKeyPair, res.getString("KseFrame.jmiGenerateKeyPair.mnemonic").charAt(0));
		jmiKeyStoreGenerateKeyPair.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreGenerateKeyPair,
				(String) generateKeyPairAction.getValue(Action.LONG_DESCRIPTION), this);
		jpmKeyStore.add(jmiKeyStoreGenerateKeyPair);

		jmiKeyStoreGenerateSecretKey = new JMenuItem(generateSecretKeyAction);
		PlatformUtil.setMnemonic(jmiKeyStoreGenerateSecretKey, res.getString("KseFrame.jmiGenerateSecretKey.mnemonic")
				.charAt(0));
		jmiKeyStoreGenerateSecretKey.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreGenerateSecretKey,
				(String) generateSecretKeyAction.getValue(Action.LONG_DESCRIPTION), this);
		jpmKeyStore.add(jmiKeyStoreGenerateSecretKey);

		jmiKeyStoreImportTrustedCertificate = new JMenuItem(importTrustedCertificateAction);
		PlatformUtil.setMnemonic(jmiKeyStoreImportTrustedCertificate,
				res.getString("KseFrame.jmiImportTrustedCertificate.mnemonic").charAt(0));
		jmiKeyStoreImportTrustedCertificate.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreImportTrustedCertificate,
				(String) importTrustedCertificateAction.getValue(Action.LONG_DESCRIPTION), this);
		jpmKeyStore.add(jmiKeyStoreImportTrustedCertificate);

		jmiKeyStoreImportKeyPair = new JMenuItem(importKeyPairAction);
		PlatformUtil.setMnemonic(jmiKeyStoreImportKeyPair, res.getString("KseFrame.jmiImportKeyPair.mnemonic")
				.charAt(0));
		jmiKeyStoreImportKeyPair.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreImportKeyPair,
				(String) importKeyPairAction.getValue(Action.LONG_DESCRIPTION), this);
		jpmKeyStore.add(jmiKeyStoreImportKeyPair);

		jpmKeyStore.addSeparator();

		jmiKeyStoreSetPassword = new JMenuItem(setPasswordAction);
		PlatformUtil.setMnemonic(jmiKeyStoreSetPassword, res.getString("KseFrame.jmiSetPassword.mnemonic").charAt(0));
		jmiKeyStoreSetPassword.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreSetPassword,
				(String) setPasswordAction.getValue(Action.LONG_DESCRIPTION), this);
		jpmKeyStore.add(jmiKeyStoreSetPassword);

		jmKeyStoreChangeType = new JMenu(res.getString("KseFrame.jmChangeType.text"));
		jmKeyStoreChangeType.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/keystoretype.png"))));
		PlatformUtil.setMnemonic(jmKeyStoreChangeType, res.getString("KseFrame.jmChangeType.mnemonic").charAt(0));
		jpmKeyStore.add(jmKeyStoreChangeType);

		jrbmiKeyStoreChangeTypeJks = new JRadioButtonMenuItem(changeTypeJksAction);
		PlatformUtil.setMnemonic(jrbmiKeyStoreChangeTypeJks, res.getString("KseFrame.jrbmiChangeTypeJks.mnemonic")
				.charAt(0));
		jrbmiKeyStoreChangeTypeJks.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiKeyStoreChangeTypeJks,
				(String) changeTypeJksAction.getValue(Action.LONG_DESCRIPTION), this);
		jmKeyStoreChangeType.add(jrbmiKeyStoreChangeTypeJks);

		jrbmiKeyStoreChangeTypeJceks = new JRadioButtonMenuItem(changeTypeJceksAction);
		PlatformUtil.setMnemonic(jrbmiKeyStoreChangeTypeJceks, res.getString("KseFrame.jrbmiChangeTypeJceks.mnemonic")
				.charAt(0));
		jrbmiKeyStoreChangeTypeJceks.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiKeyStoreChangeTypeJceks,
				(String) changeTypeJceksAction.getValue(Action.LONG_DESCRIPTION), this);
		jmKeyStoreChangeType.add(jrbmiKeyStoreChangeTypeJceks);

		jrbmiKeyStoreChangeTypePkcs12 = new JRadioButtonMenuItem(changeTypePkcs12Action);
		PlatformUtil.setMnemonic(jrbmiKeyStoreChangeTypePkcs12, res.getString("KseFrame.jrbmiChangeTypePkcs12.mnemonic")
				.charAt(0));
		jrbmiKeyStoreChangeTypePkcs12.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiKeyStoreChangeTypePkcs12,
				(String) changeTypePkcs12Action.getValue(Action.LONG_DESCRIPTION), this);
		jmKeyStoreChangeType.add(jrbmiKeyStoreChangeTypePkcs12);

		jrbmiKeyStoreChangeTypeBksV1 = new JRadioButtonMenuItem(changeTypeBksV1Action);
		PlatformUtil.setMnemonic(jrbmiKeyStoreChangeTypeBksV1, res.getString("KseFrame.jrbmiChangeTypeBksV1.mnemonic")
				.charAt(0));
		jrbmiKeyStoreChangeTypeBksV1.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiKeyStoreChangeTypeBksV1,
				(String) changeTypeBksV1Action.getValue(Action.LONG_DESCRIPTION), this);
		jmKeyStoreChangeType.add(jrbmiKeyStoreChangeTypeBksV1);

		jrbmiKeyStoreChangeTypeBks = new JRadioButtonMenuItem(changeTypeBksAction);
		PlatformUtil.setMnemonic(jrbmiKeyStoreChangeTypeBks, res.getString("KseFrame.jrbmiChangeTypeBks.mnemonic")
				.charAt(0));
		jrbmiKeyStoreChangeTypeBks.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiKeyStoreChangeTypeBks,
				(String) changeTypeBksAction.getValue(Action.LONG_DESCRIPTION), this);
		jmKeyStoreChangeType.add(jrbmiKeyStoreChangeTypeBks);

		jrbmiKeyStoreChangeTypeBcfks = new JRadioButtonMenuItem(changeTypeBcfksAction);
		PlatformUtil.setMnemonic(jrbmiKeyStoreChangeTypeBcfks, res.getString("KseFrame.jrbmiChangeTypeBcfks.mnemonic")
				.charAt(0));
		jrbmiKeyStoreChangeTypeBcfks.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiKeyStoreChangeTypeBcfks,
				(String) changeTypeBcfksAction.getValue(Action.LONG_DESCRIPTION), this);
		jmKeyStoreChangeType.add(jrbmiKeyStoreChangeTypeBcfks);

		jrbmiKeyStoreChangeTypeUber = new JRadioButtonMenuItem(changeTypeUberAction);
		PlatformUtil.setMnemonic(jrbmiKeyStoreChangeTypeUber, res.getString("KseFrame.jrbmiChangeTypeUber.mnemonic")
				.charAt(0));
		jrbmiKeyStoreChangeTypeUber.setToolTipText(null);
		new StatusBarChangeHandler(jrbmiKeyStoreChangeTypeUber,
				(String) changeTypeUberAction.getValue(Action.LONG_DESCRIPTION), this);
		jmKeyStoreChangeType.add(jrbmiKeyStoreChangeTypeUber);

		ButtonGroup changeTypeGroup = new ButtonGroup();
		changeTypeGroup.add(jrbmiKeyStoreChangeTypeJks);
		changeTypeGroup.add(jrbmiKeyStoreChangeTypeJceks);
		changeTypeGroup.add(jrbmiKeyStoreChangeTypePkcs12);
		changeTypeGroup.add(jrbmiKeyStoreChangeTypeBks);
		changeTypeGroup.add(jrbmiKeyStoreChangeTypeBksV1);
		changeTypeGroup.add(jrbmiKeyStoreChangeTypeBcfks);
		changeTypeGroup.add(jrbmiKeyStoreChangeTypeUber);

		jmiKeyStoreProperties = new JMenuItem(propertiesAction);
		PlatformUtil.setMnemonic(jmiKeyStoreProperties, res.getString("KseFrame.jmiProperties.mnemonic").charAt(0));
		jmiKeyStoreProperties.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyStoreProperties, (String) propertiesAction.getValue(Action.LONG_DESCRIPTION), this);
		jpmKeyStore.add(jmiKeyStoreProperties);
	}



	private void initKeyStoreEntryPopupMenus() {

		//
		// Popup menu for key pair entry
		//

		jpmKeyPair = new JPopupMenu();

		jmKeyPairDetails = new JMenu(res.getString("KseFrame.jmKeyPairDetails.text"));
		jmKeyPairDetails.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/keypairdetails.png"))));

		jmiKeyPairCertificateChainDetails = new JMenuItem(keyPairCertificateChainDetailsAction);
		jmiKeyPairCertificateChainDetails.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairCertificateChainDetails,
				(String) keyPairCertificateChainDetailsAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairPrivateKeyDetails = new JMenuItem(keyPairPrivateKeyDetailsAction);
		jmiKeyPairPrivateKeyDetails.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairPrivateKeyDetails,
				(String) keyPairPrivateKeyDetailsAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairPublicKeyDetails = new JMenuItem(keyPairPublicKeyDetailsAction);
		jmiKeyPairPublicKeyDetails.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairPublicKeyDetails,
				(String) keyPairPublicKeyDetailsAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairCut = new JMenuItem(cutKeyPairAction);
		jmiKeyPairCut.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairCut, (String) cutKeyPairAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairCopy = new JMenuItem(copyKeyPairAction);
		jmiKeyPairCopy.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairCopy, (String) copyKeyPairAction.getValue(Action.LONG_DESCRIPTION), this);

		jmKeyPairExport = new JMenu(res.getString("KseFrame.jmKeyPairExport.text"));
		jmKeyPairExport.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/keypairexport.png"))));

		jmiKeyPairExport = new JMenuItem(exportKeyPairAction);
		jmiKeyPairExport.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairExport, (String) exportKeyPairAction.getValue(Action.LONG_DESCRIPTION),
				this);

		jmiKeyPairExportCertificateChain = new JMenuItem(exportKeyPairCertificateChainAction);
		jmiKeyPairExportCertificateChain.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairExportCertificateChain,
				(String) exportKeyPairCertificateChainAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairExportPrivateKey = new JMenuItem(exportKeyPairPrivateKeyAction);
		jmiKeyPairExportPrivateKey.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairExportPrivateKey,
				(String) exportKeyPairPrivateKeyAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairExportPublicKey = new JMenuItem(exportKeyPairPublicKeyAction);
		jmiKeyPairExportPublicKey.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairExportPublicKey,
				(String) exportKeyPairPublicKeyAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairGenerateCsr = new JMenuItem(generateCsrAction);
		jmiKeyPairGenerateCsr.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairGenerateCsr, (String) generateCsrAction.getValue(Action.LONG_DESCRIPTION),
				this);

		jmKeyPairImportCaReply = new JMenu(res.getString("KseFrame.jmKeyPairImportCaReply.text"));
		jmKeyPairImportCaReply.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/keypairimportcareply.png"))));

		jmiKeyPairImportCaReplyFile = new JMenuItem(importCaReplyFromFileAction);
		jmiKeyPairImportCaReplyFile.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairImportCaReplyFile,
				(String) importCaReplyFromFileAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairImportCaReplyClipboard = new JMenuItem(importCaReplyFromClipboardAction);
		jmiKeyPairImportCaReplyClipboard.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairImportCaReplyClipboard,
				(String) importCaReplyFromClipboardAction.getValue(Action.LONG_DESCRIPTION), this);

		jmKeyPairEditCertChain = new JMenu(res.getString("KseFrame.jmKeyPairEditCertChain.text"));
		jmKeyPairEditCertChain.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/keypaireditcertchain.png"))));

		jmiKeyPairEditCertChainAppendCert = new JMenuItem(appendToCertificateChainAction);
		jmiKeyPairEditCertChainAppendCert.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairEditCertChainAppendCert,
				(String) appendToCertificateChainAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairEditCertChainRemoveCert = new JMenuItem(removeFromCertificateChainAction);
		jmiKeyPairEditCertChainRemoveCert.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairEditCertChainRemoveCert,
				(String) removeFromCertificateChainAction.getValue(Action.LONG_DESCRIPTION), this);

		jmKeyPairSign = new JMenu(res.getString("KseFrame.jmKeyPairSign.text"));
		jmKeyPairSign.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/keypairsign.png"))));

		jmiKeyPairSignCsr = new JMenuItem(signCsrAction);
		jmiKeyPairSignCsr.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairSignCsr, (String) signCsrAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairSignJar = new JMenuItem(signJarAction);
		jmiKeyPairSignJar.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairSignJar, (String) signJarAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairSignMidlet = new JMenuItem(signMidletAction);
		jmiKeyPairSignMidlet.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairSignMidlet, (String) signMidletAction.getValue(Action.LONG_DESCRIPTION),
				this);

		jmiKeyPairSignNewKeyPair = new JMenuItem(signNewKeyPairAction);
		jmiKeyPairSignNewKeyPair.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairSignNewKeyPair,
				(String) signNewKeyPairAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairUnlock = new JMenuItem(unlockKeyPairAction);
		jmiKeyPairUnlock.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairUnlock, (String) unlockKeyPairAction.getValue(Action.LONG_DESCRIPTION),
				this);

		jmiKeyPairSetPassword = new JMenuItem(setKeyPairPasswordAction);
		jmiKeyPairSetPassword.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairSetPassword,
				(String) setKeyPairPasswordAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyPairDelete = new JMenuItem(deleteKeyPairAction);
		jmiKeyPairDelete.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairDelete, (String) deleteKeyPairAction.getValue(Action.LONG_DESCRIPTION),
				this);

		jmiKeyPairRename = new JMenuItem(renameKeyPairAction);
		jmiKeyPairRename.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyPairRename, (String) renameKeyPairAction.getValue(Action.LONG_DESCRIPTION),
				this);

		jpmKeyPair.add(jmKeyPairDetails);
		jmKeyPairDetails.add(jmiKeyPairCertificateChainDetails);
		jmKeyPairDetails.add(jmiKeyPairPrivateKeyDetails);
		jmKeyPairDetails.add(jmiKeyPairPublicKeyDetails);
		jpmKeyPair.addSeparator();
		jpmKeyPair.add(jmiKeyPairCut);
		jpmKeyPair.add(jmiKeyPairCopy);
		jpmKeyPair.addSeparator();
		jpmKeyPair.add(jmKeyPairExport);
		jmKeyPairExport.add(jmiKeyPairExport);
		jmKeyPairExport.add(jmiKeyPairExportCertificateChain);
		jmKeyPairExport.add(jmiKeyPairExportPrivateKey);
		jmKeyPairExport.add(jmiKeyPairExportPublicKey);
		jpmKeyPair.add(jmiKeyPairGenerateCsr);
		jpmKeyPair.add(jmKeyPairImportCaReply);
		jmKeyPairImportCaReply.add(jmiKeyPairImportCaReplyFile);
		jmKeyPairImportCaReply.add(jmiKeyPairImportCaReplyClipboard);
		jpmKeyPair.add(jmKeyPairEditCertChain);
		jmKeyPairEditCertChain.add(jmiKeyPairEditCertChainAppendCert);
		jmKeyPairEditCertChain.add(jmiKeyPairEditCertChainRemoveCert);
		jpmKeyPair.addSeparator();
		jpmKeyPair.add(jmKeyPairSign);
		jmKeyPairSign.add(jmiKeyPairSignNewKeyPair);
		jmKeyPairSign.add(jmiKeyPairSignCsr);
		jmKeyPairSign.add(jmiKeyPairSignJar);
		jmKeyPairSign.add(jmiKeyPairSignMidlet);
		jpmKeyPair.addSeparator();
		jpmKeyPair.add(jmiKeyPairUnlock);
		jpmKeyPair.add(jmiKeyPairSetPassword);
		jpmKeyPair.add(jmiKeyPairDelete);
		jpmKeyPair.add(jmiKeyPairRename);

		//
		// Popup menu for trusted cert entry
		//

		jpmTrustedCertificate = new JPopupMenu();

		jmTrustedCertificateDetails = new JMenu(res.getString("KseFrame.jmTrustedCertificateDetails.text"));
		jmTrustedCertificateDetails.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/trustcertdetails.png"))));

		jmiTrustedCertificateDetails = new JMenuItem(trustedCertificateDetailsAction);
		jmiTrustedCertificateDetails.setToolTipText(null);
		new StatusBarChangeHandler(jmiTrustedCertificateDetails,
				(String) trustedCertificateDetailsAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiTrustedCertificatePublicKeyDetails = new JMenuItem(trustedCertificatePublicKeyDetailsAction);
		jmiTrustedCertificatePublicKeyDetails.setToolTipText(null);
		new StatusBarChangeHandler(jmiTrustedCertificatePublicKeyDetails,
				(String) trustedCertificatePublicKeyDetailsAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiTrustedCertificateCut = new JMenuItem(cutTrustedCertificateAction);
		jmiTrustedCertificateCut.setToolTipText(null);
		new StatusBarChangeHandler(jmiTrustedCertificateCut,
				(String) cutTrustedCertificateAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiTrustedCertificateCopy = new JMenuItem(copyTrustedCertificateAction);
		jmiTrustedCertificateCopy.setToolTipText(null);
		new StatusBarChangeHandler(jmiTrustedCertificateCopy,
				(String) copyTrustedCertificateAction.getValue(Action.LONG_DESCRIPTION), this);

		jmTrustedCertificateExport = new JMenu(res.getString("KseFrame.jmTrustedCertificateExport.text"));
		jmTrustedCertificateExport.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
				getClass().getResource("images/menu/trustcertexport.png"))));

		jmiTrustedCertificateExport = new JMenuItem(exportTrustedCertificateAction);
		jmiTrustedCertificateExport.setToolTipText(null);
		new StatusBarChangeHandler(jmiTrustedCertificateExport,
				(String) exportTrustedCertificateAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiTrustedCertificateExportPublicKey = new JMenuItem(exportTrustedCertificatePublicKeyAction);
		jmiTrustedCertificateExportPublicKey.setToolTipText(null);
		new StatusBarChangeHandler(jmiTrustedCertificateExportPublicKey,
				(String) exportTrustedCertificatePublicKeyAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiTrustedCertificateDelete = new JMenuItem(deleteTrustedCertificateAction);
		jmiTrustedCertificateDelete.setToolTipText(null);
		new StatusBarChangeHandler(jmiTrustedCertificateDelete,
				(String) deleteTrustedCertificateAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiTrustedCertificateRename = new JMenuItem(renameTrustedCertificateAction);
		jmiTrustedCertificateRename.setToolTipText(null);
		new StatusBarChangeHandler(jmiTrustedCertificateRename,
				(String) renameTrustedCertificateAction.getValue(Action.LONG_DESCRIPTION), this);

		jpmTrustedCertificate.add(jmTrustedCertificateDetails);
		jmTrustedCertificateDetails.add(jmiTrustedCertificateDetails);
		jmTrustedCertificateDetails.add(jmiTrustedCertificatePublicKeyDetails);
		jpmTrustedCertificate.addSeparator();
		jpmTrustedCertificate.add(jmiTrustedCertificateCut);
		jpmTrustedCertificate.add(jmiTrustedCertificateCopy);
		jpmTrustedCertificate.addSeparator();
		jpmTrustedCertificate.add(jmTrustedCertificateExport);
		jmTrustedCertificateExport.add(jmiTrustedCertificateExport);
		jmTrustedCertificateExport.add(jmiTrustedCertificateExportPublicKey);
		jpmTrustedCertificate.addSeparator();
		jpmTrustedCertificate.add(jmiTrustedCertificateDelete);
		jpmTrustedCertificate.add(jmiTrustedCertificateRename);

		//
		// Popup menu for secret key entry
		//

		jpmKey = new JPopupMenu();

		jmiKeyDetails = new JMenuItem(keyDetailsAction);
		jmiKeyDetails.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyDetails, (String) keyDetailsAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyUnlock = new JMenuItem(unlockKeyAction);
		jmiKeyUnlock.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyUnlock, (String) unlockKeyAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeySetPassword = new JMenuItem(setKeyPasswordAction);
		jmiKeySetPassword.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeySetPassword, (String) setKeyPasswordAction.getValue(Action.LONG_DESCRIPTION),
				this);

		jmiKeyDelete = new JMenuItem(deleteKeyAction);
		jmiKeyDelete.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyDelete, (String) deleteKeyAction.getValue(Action.LONG_DESCRIPTION), this);

		jmiKeyRename = new JMenuItem(renameKeyAction);
		jmiKeyRename.setToolTipText(null);
		new StatusBarChangeHandler(jmiKeyRename, (String) renameKeyAction.getValue(Action.LONG_DESCRIPTION), this);

		jpmKey.add(jmiKeyDetails);
		jpmKey.addSeparator();
		jpmKey.add(jmiKeyUnlock);
		jpmKey.add(jmiKeySetPassword);
		jpmKey.add(jmiKeyDelete);
		jpmKey.add(jmiKeyRename);
	}

	private void maybeShowSelectedEntryDetails(MouseEvent evt) {
		// Check if a double click occurred on the KeyStore table. If it has
		// show the relevant details of the entry clicked upon
		if (evt.getClickCount() > 1) {
			JTable jtKeyStore = (JTable) evt.getComponent();

			Point point = new Point(evt.getX(), evt.getY());
			int row = jtKeyStore.rowAtPoint(point);

			if (row != -1) {
				try {
					CursorUtil.setCursorBusy(frame);
					showSelectedEntryDetails(jtKeyStore, row);
				} finally {
					CursorUtil.setCursorFree(frame);
				}
			}
		}
	}

	private void showSelectedEntryDetails(JTable jtKeyStore, int row) {
		jtKeyStore.setRowSelectionInterval(row, row);
		updateCutCopyPasteControls(); // Selection changed - update edit controls

		KeyStoreHistory history = getActiveKeyStoreHistory();
		KeyStore keyStore = history.getCurrentState().getKeyStore();
		String alias = getSelectedEntryAlias();

		try {
			if (KeyStoreUtil.isKeyPairEntry(alias, keyStore)) {
				keyPairCertificateChainDetailsAction.showCertificateSelectedEntry();
			} else if (KeyStoreUtil.isTrustedCertificateEntry(alias, keyStore)) {
				trustedCertificateDetailsAction.showCertificateSelectedEntry();
			} else if (KeyStoreUtil.isKeyEntry(alias, keyStore)) {
				keyDetailsAction.showKeySelectedEntry();
			}
		} catch (Exception ex) {
			DError.displayError(frame, ex);
		}
	}

	private void maybeShowSelectedEntryPopupMenu(MouseEvent evt) {

		JTable jtKeyStore = (JTable) evt.getComponent();

		Point point = new Point(evt.getX(), evt.getY());
		int row = jtKeyStore.rowAtPoint(point);

		KeyStoreType type = KeyStoreType.resolveJce(getActiveKeyStoreHistory().getCurrentState().getKeyStore().getType());

		if (evt.isPopupTrigger()) {

			if (row != -1) {

				jtKeyStore.setRowSelectionInterval(row, row);

				if (jtKeyStore.getValueAt(row, 0).equals(KeyStoreTableModel.KEY_PAIR_ENTRY)) {

					// For KeyStore types that support password protected entries...
					if (type.hasEntryPasswords()) {
						// Only allow unlocking from menu if entry is currently locked
						boolean locked = ((Boolean) jtKeyStore.getValueAt(row, 1)).booleanValue();
						unlockKeyPairAction.setEnabled(locked);
					}

					jpmKeyPair.show(evt.getComponent(), evt.getX(), evt.getY());

				} else if (jtKeyStore.getValueAt(row, 0).equals(KeyStoreTableModel.TRUST_CERT_ENTRY)) {

					jpmTrustedCertificate.show(evt.getComponent(), evt.getX(), evt.getY());

				} else if (jtKeyStore.getValueAt(row, 0).equals(KeyStoreTableModel.KEY_ENTRY)) {

					// For KeyStore types that support password protected entries...
					if (type.hasEntryPasswords()) {
						// Only allow unlocking from menu if entry is currently locked
						boolean locked = ((Boolean) jtKeyStore.getValueAt(row, 1)).booleanValue();
						unlockKeyAction.setEnabled(locked);
					}

					jpmKey.show(evt.getComponent(), evt.getX(), evt.getY());
				}
			} else {
				jpmKeyStore.show(evt.getComponent(), evt.getX(), evt.getY());
			}
		} else if (evt.getClickCount() > 1 && row == -1) {
			// double click on free space opens generate key pair dialog
			generateKeyPairAction.generateKeyPair();
		}

		// Selection changed - update edit controls
		updateCutCopyPasteControls();
	}

	private void maybeShowKeyStoreTabPopup(MouseEvent evt) {
		if (evt.isPopupTrigger()) {
			int tabCount = jkstpKeyStores.getTabCount();
			TabbedPaneUI tpu = jkstpKeyStores.getUI();

			for (int i = 0; i < tabCount; i++) {
				Rectangle rect = tpu.getTabBounds(jkstpKeyStores, i);

				int x = evt.getX();
				int y = evt.getY();

				if (x < rect.x || x > rect.x + rect.width || y < rect.y || y > rect.y + rect.height) {
					continue;
				}

				jpmKeyStoreTab.show(evt.getComponent(), evt.getX(), evt.getY());
				break;
			}
		}
	}

	private void renameSelectedEntry() {

		KeyStoreHistory history = getActiveKeyStoreHistory();
		KeyStore keyStore = history.getCurrentState().getKeyStore();
		String alias = getSelectedEntryAlias();

		if (alias == null) {
			return;
		}

		try {
			if (KeyStoreUtil.isKeyPairEntry(alias, keyStore)) {
				renameKeyPairAction.renameSelectedEntry();
			} else if (KeyStoreUtil.isTrustedCertificateEntry(alias, keyStore)) {
				renameTrustedCertificateAction.renameSelectedEntry();
			} else {
				renameKeyAction.renameSelectedEntry();
			}
		} catch (Exception ex) {
			DError.displayError(frame, ex);
		}
	}

	/**
	 * Add a KeyStore to the set of loaded KeyStores.
	 *
	 * @param keyStore
	 *            KeyStore
	 * @param keyStoreName
	 *            KeyStore name
	 * @param password
	 *            KeyStore password
	 * @param explicitProvider
	 *            Explicitly specify a provider that is used for this keystore
	 */
	public void addKeyStore(KeyStore keyStore, String keyStoreName, Password password, Provider explicitProvider) {
		KeyStoreHistory history = new KeyStoreHistory(keyStore, keyStoreName, password, explicitProvider);

		addKeyStoreHistory(history);
	}

	/**
	 * Add a KeyStore to the set of loaded KeyStores.
	 *
	 * @param keyStore
	 *            KeyStore
	 * @param keyStoreFile
	 *            KeyStore file
	 * @param password
	 *            KeyStore password
	 */
	public void addKeyStore(KeyStore keyStore, File keyStoreFile, Password password) {
		KeyStoreHistory history = new KeyStoreHistory(keyStore, keyStoreFile, password);

		addKeyStoreHistory(history);
	}

	private void addKeyStoreHistory(KeyStoreHistory history) {
		histories.add(history);

		JTable jtKeyStore = createEmptyKeyStoreTable();
		TableColumnAdjuster tca = new TableColumnAdjuster(jtKeyStore);
		tca.adjustColumns();
		keyStoreTables.add(jtKeyStore);

		JScrollPane jspKeyStore = wrapKeyStoreTableInScrollPane(jtKeyStore);

		KeyStoreTab keyStoreTab = new KeyStoreTab(history.getName(), this, history);
		jkstpKeyStores.addTab(null, jspKeyStore);
		jkstpKeyStores.setTabComponentAt(jkstpKeyStores.getTabCount() - 1, keyStoreTab);
		jkstpKeyStores.setSelectedIndex(jkstpKeyStores.getTabCount() - 1);

		updateControls(true);

		// If KeyStore is backed up by a file add it to the recent files menu
		if (history.getFile() != null) {
			jmrfRecentFiles.add(createRecentFileMenuItem(jmrfRecentFiles, history.getFile()));
		}
	}

	/**
	 * Removed the supplied KeyStore from the set of loaded KeyStores.
	 *
	 * @param keyStore
	 *            KeyStore
	 */
	public void removeKeyStore(KeyStore keyStore) {
		int index = findKeyStoreIndex(keyStore);

		if (index >= 0) {
			keyStoreTables.remove(index);
			histories.remove(index).nullPasswords();
			jkstpKeyStores.remove(index);
		}
	}

	/**
	 * Re-draw all keystore tables
	 * @param applicationSettings
	 */
	public void redrawKeyStores(ApplicationSettings applicationSettings) {
		if (keyStoreTables != null) {

			keyStoreTableColumns = applicationSettings.getKeyStoreTableColumns();

			for (JTable keyStoreTable : keyStoreTables) {
				KeyStoreHistory history = ((KeyStoreTableModel) keyStoreTable.getModel()).getHistory();
				KeyStoreTableModel ksModel = new KeyStoreTableModel(keyStoreTableColumns);
				try {
					ksModel.load(history);
					keyStoreTable.setModel(ksModel);

					RowSorter<KeyStoreTableModel> sorter = new TableRowSorter<>(ksModel);
					keyStoreTable.setRowSorter(sorter);

					setColumnsToIconSize(keyStoreTable, 0, 1, 2);
					colAdjust(keyStoreTable);
				} catch (GeneralSecurityException | CryptoException e) {
					DError.displayError(frame, e);
				}
			}
		}
	}

	private void setColumnsToIconSize(JTable keyStoreTable, int... columnNumbers) {
		for (int i : columnNumbers) {
			TableColumn typeCol = keyStoreTable.getColumnModel().getColumn(i);
			typeCol.setResizable(false);
			typeCol.setMinWidth(ICON_SIZE);
			typeCol.setMaxWidth(ICON_SIZE);
			typeCol.setPreferredWidth(ICON_SIZE);
		}
	}

	/**
	 * Find the supplied KeyStore in the set of loaded KeyStores.
	 *
	 * @param keyStore
	 *            KeyStore to find
	 * @return The KeyStore's index
	 */
	public int findKeyStoreIndex(KeyStore keyStore) {
		for (int i = 0; i < histories.size(); i++) {
			if (keyStore.equals(histories.get(i).getCurrentState().getKeyStore())) {
				return i;
			}
		}

		return -1;
	}

	/**
	 * Get the active KeyStore.
	 *
	 * @return The KeyStore or null if no KeyStore is active
	 */
	public KeyStore getActiveKeyStore() {
		KeyStoreHistory history = getActiveKeyStoreHistory();
		if (history == null) {
			return null;
		}

		KeyStoreState currentState = history.getCurrentState();
		KeyStore keyStore = currentState.getKeyStore();
		return keyStore;
	}

	/**
	 * Get the active KeyStore history.
	 *
	 * @return The KeyStore history or null if no KeyStore is active
	 */
	public KeyStoreHistory getActiveKeyStoreHistory() {
		if (histories.isEmpty()) {
			return null;
		}

		int selected = jkstpKeyStores.getSelectedIndex();
		return histories.get(selected);
	}

	/**
	 * Get all loaded KeyStore histories in display order.
	 *
	 * @return KeyStore histories
	 */
	public KeyStoreHistory[] getKeyStoreHistories() {
		return histories.toArray(new KeyStoreHistory[histories.size()]);
	}

	/**
	 * Focus on the supplied KeyStore.
	 *
	 * @param keyStore
	 *            KeyStore
	 */
	public void focusOnKeyStore(KeyStore keyStore) {
		int index = findKeyStoreIndex(keyStore);

		if (index >= 0) {
			jkstpKeyStores.setSelectedIndex(index);
		}
	}

	private JTable getActiveKeyStoreTable() {
		if (keyStoreTables.isEmpty()) {
			return null;
		}

		int selected = jkstpKeyStores.getSelectedIndex();
		return keyStoreTables.get(selected);
	}

	/**
	 * Get a the selected entry as a drag entry for DnD.
	 *
	 * @return Drag entry or null if entry could not be dragged
	 */
	public DragEntry dragSelectedEntry() {
		try {
			KeyStoreHistory history = getActiveKeyStoreHistory();

			if (history == null) {
				return null; // No KeyStore to drag from
			}

			KeyStoreState currentState = history.getCurrentState();
			KeyStore keyStore = currentState.getKeyStore();
			String alias = getSelectedEntryAlias();
			KeyStoreType type = KeyStoreType.resolveJce(keyStore.getType());

			if (alias == null) {
				// No selected entry to drag
				return null;
			}

			if (KeyStoreUtil.isKeyEntry(alias, keyStore)) {
				JOptionPane.showMessageDialog(frame, res.getString("KseFrame.NoDragKeyEntry.message"),
						KSE.getApplicationName(), JOptionPane.WARNING_MESSAGE);
				return null;
			}

			if (KeyStoreUtil.isKeyPairEntry(alias, keyStore) && type.hasExportablePrivateKeys()) {

				// Otherwise entry must already be unlocked to get password
				Password password = currentState.getEntryPassword(alias);

				if (password == null && type.hasEntryPasswords()) {
					JOptionPane.showMessageDialog(frame, res.getString("KseFrame.NoDragLockedKeyPairEntry.message"),
							KSE.getApplicationName(), JOptionPane.WARNING_MESSAGE);
					return null;
				}

				PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
				Certificate[] certificateChain = keyStore.getCertificateChain(alias);

				return new DragKeyPairEntry(alias, privateKey, password, certificateChain);
			} else {
				Certificate trustedCertificate = keyStore.getCertificate(alias);
				return new DragTrustedCertificateEntry(alias, trustedCertificate);
			}
		} catch (Exception ex) {
			DError.displayError(frame, ex);
			return null;
		}
	}

	/**
	 * Get the alias of the entry currently selected in the KeyStore table if
	 * any.
	 *
	 * @return Alias or null if none selected
	 */
	public String getSelectedEntryAlias() {
		JTable jtKeyStore = getActiveKeyStoreTable();
		int row = jtKeyStore.getSelectedRow();

		if (row == -1) {
			return null;
		}

		return (String) jtKeyStore.getValueAt(row, 3);
	}

	/**
	 * Get the aliases of all entries currently selected in the KeyStore
	 *
	 * @return Selected aliases (may be an empty array, but never null)
	 */
	public String[] getSelectedEntryAliases() {
		JTable jtKeyStore = getActiveKeyStoreTable();
		int[] rows = jtKeyStore.getSelectedRows();

		String[] retval = new String[rows.length];

		for (int i = 0; i < rows.length; i++) {
			retval[i] = (String) jtKeyStore.getValueAt(rows[i], 3);
		}

		return retval;
	}

	public String getNextEntrysAlias() {
		JTable jtKeyStore = getActiveKeyStoreTable();
		int[] rows = jtKeyStore.getSelectedRows();

		// no row selected
		if (rows.length == 0) {
			return null;
		}

		int rowCount = jtKeyStore.getModel().getRowCount();
		if ( rows.length == rowCount) {
			// all rows are selected
			return null;
		} else {
			int idx;
			int max = Arrays.stream(rows).max().getAsInt();
			if (max < (rowCount - 1)) {
				// last selected row is not the last one, return alias of next row
				idx = max + 1;
			} else {
				// last selected row is the last one, return alias of previous row
				int min = Arrays.stream(rows).min().getAsInt();
				if (min >= 1) {
					idx = min - 1;
				} else {
					idx = -1;
					Arrays.sort(rows);
					for (int i = rows.length - 1; i >= 1; i--) {
						if (rows[i] - rows[i - 1] > 1) { // found a gap
							idx = rows[i] - 1;
							break;
						}
					}
				}
			}
			if (idx >= 0) {
				return (String) jtKeyStore.getValueAt(idx, 3);
			} else {
				return null;
			}
		}
	}

	private int getNumberOfEntries() {
		JTable jtKeyStore = getActiveKeyStoreTable();
		return jtKeyStore.getModel().getRowCount();
	}

	public void setSelectedEntriesByAliases(String... aliases) {
		JTable jtKeyStore = getActiveKeyStoreTable();
		jtKeyStore.requestFocusInWindow();

		ListSelectionModel selectionModel = jtKeyStore.getSelectionModel();
		selectionModel.clearSelection();
		Set<String> aliasesToSelect = new HashSet<>(Arrays.asList(aliases));
		for (int i = 0; i < jtKeyStore.getRowCount(); i++) {
			if (aliasesToSelect.contains(jtKeyStore.getValueAt(i, 3))) {
				selectionModel.addSelectionInterval(i, i);
			}
		}
	}

	/**
	 * Update the frame's controls dependent on the state of its open and active
	 * KeyStores.
	 *
	 * @param keyStoreContentsChanged
	 *            Have the active KeyStore's contents changed?
	 */
	public void updateControls(boolean keyStoreContentsChanged) {
		KeyStoreHistory history = getActiveKeyStoreHistory();

		if (history == null) {
			updateControlsNoKeyStoresOpen();
			return;
		}

		KeyStoreState currentState = history.getCurrentState();

		// Reload KeyStore in table if it has changed
		if (keyStoreContentsChanged) {
			try {
				String[] selectedAliases = getSelectedEntryAliases();

				((KeyStoreTableModel) getActiveKeyStoreTable().getModel()).load(history);

				// Loading the model loses the selected entry - preserve it
				if (selectedAliases.length > 0) {
					setSelectedEntriesByAliases(selectedAliases);
				}
			} catch (GeneralSecurityException | CryptoException ex) {
				DError.displayError(frame, ex);
			}
		}

		// Can save if active KeyStore has not been saved
		if (!currentState.isSavedState()) {
			saveAction.setEnabled(true);
		} else {
			saveAction.setEnabled(false);
		}

		// Can save all if any KeyStore has been changed since saved
		boolean saveAll = false;
		for (int i = 0; i < histories.size(); i++) {
			KeyStoreHistory h = histories.get(i);

			if (!h.getCurrentState().isSavedState()) {
				saveAll = true; // Yes - can Save All
				break;
			}
		}

		saveAllAction.setEnabled(saveAll);

		// Can close
		closeAction.setEnabled(true);
		closeAllAction.setEnabled(true);

		// Can close others?
		if (jkstpKeyStores.getTabCount() > 1) {
			closeOthersAction.setEnabled(true);
		} else {
			closeOthersAction.setEnabled(false);
		}

		KeyStore keyStore = currentState.getKeyStore();
		KeyStoreType type = KeyStoreType.resolveJce(keyStore.getType());

		// Can Save As
		if (type.isFileBased()) {
			saveAsAction.setEnabled(true);
		}

		// May be able to undo/redo
		updateUndoRedoControls(currentState);

		// May be able to cut/copy/paste
		if (type.isFileBased()) {
			updateCutCopyPasteControls();
		}

		// Can use tools on
		generateKeyPairAction.setEnabled(true);
		generateSecretKeyAction.setEnabled(type.supportsKeyEntries());
		importTrustedCertificateAction.setEnabled(true);
		importKeyPairAction.setEnabled(true);
		propertiesAction.setEnabled(true);
		if (type.isFileBased()) {
			setPasswordAction.setEnabled(true);
		}

		// Show default status bar display
		setDefaultStatusBarText();

		// Passwords, and therefore unlocking, are not relevant for PKCS #12 or KeyStores that are not file-based
		if (!type.hasEntryPasswords() || !type.isFileBased()) {
			unlockKeyPairAction.setEnabled(false);
			setKeyPairPasswordAction.setEnabled(false);
			unlockKeyAction.setEnabled(false);
			setKeyPasswordAction.setEnabled(false);
		} else {
			unlockKeyPairAction.setEnabled(true);
			setKeyPairPasswordAction.setEnabled(true);
			unlockKeyAction.setEnabled(true);
			setKeyPasswordAction.setEnabled(true);
		}

		// Special restrictions for MSCAPI and PKCS#11 type
		if (type == KeyStoreType.MS_CAPI_PERSONAL || type == KeyStoreType.PKCS11) {

			keyPairPrivateKeyDetailsAction.setEnabled(false);
			keyDetailsAction.setEnabled(false);

			renameKeyAction.setEnabled(false);
			renameKeyPairAction.setEnabled(false);
			renameTrustedCertificateAction.setEnabled(false);

			exportKeyPairAction.setEnabled(false);
			exportKeyPairPrivateKeyAction.setEnabled(false);

			jmKeyPairEditCertChain.setEnabled(false);
			appendToCertificateChainAction.setEnabled(false);
			removeFromCertificateChainAction.setEnabled(false);

			// "UnsupportedOperationException" ...
			jmKeyPairImportCaReply.setEnabled(false);
		} else {
			keyPairPrivateKeyDetailsAction.setEnabled(true);
			keyDetailsAction.setEnabled(true);

			renameKeyAction.setEnabled(true);
			renameKeyPairAction.setEnabled(true);
			renameTrustedCertificateAction.setEnabled(true);

			exportKeyPairAction.setEnabled(true);
			exportKeyPairPrivateKeyAction.setEnabled(true);

			jmKeyPairEditCertChain.setEnabled(true);
			appendToCertificateChainAction.setEnabled(true);
			removeFromCertificateChainAction.setEnabled(true);

			jmKeyPairImportCaReply.setEnabled(true);
		}

		// KeyStore type menu items
		if (type.isFileBased()) {
			jmChangeType.setEnabled(true);

			if (type == JKS) {
				jrbmiChangeTypeJks.setSelected(true);
				jrbmiKeyStoreChangeTypeJks.setSelected(true);
			} else if (type == JCEKS) {
				jrbmiChangeTypeJceks.setSelected(true);
				jrbmiKeyStoreChangeTypeJceks.setSelected(true);
			} else if (type == PKCS12) {
				jrbmiChangeTypePkcs12.setSelected(true);
				jrbmiKeyStoreChangeTypePkcs12.setSelected(true);
			} else if (type == BKS_V1) {
				jrbmiChangeTypeBksV1.setSelected(true);
				jrbmiKeyStoreChangeTypeBksV1.setSelected(true);
			} else if (type == BKS) {
				jrbmiChangeTypeBks.setSelected(true);
				jrbmiKeyStoreChangeTypeBks.setSelected(true);
			} else if (type == BCFKS) {
				jrbmiChangeTypeBcfks.setSelected(true);
				jrbmiKeyStoreChangeTypeBcfks.setSelected(true);
			} else {
				jrbmiChangeTypeUber.setSelected(true);
				jrbmiKeyStoreChangeTypeUber.setSelected(true);
			}
		} else {
			jmKeyStoreChangeType.setEnabled(false);
		}

		// Show KeyStores tabbed pane
		frame.getContentPane().remove(jQuickStart);
		frame.getContentPane().add(jkstpKeyStores, BorderLayout.CENTER);

		updateKeyStoreTabsText();
		updateApplicationTitle();

		frame.repaint();
	}

	private void updateControlsNoKeyStoresOpen() {
		// Nothing to close
		closeAction.setEnabled(false);
		closeOthersAction.setEnabled(false);
		closeAllAction.setEnabled(false);

		// Nothing to save
		saveAction.setEnabled(false);
		saveAsAction.setEnabled(false);
		saveAllAction.setEnabled(false);

		// Nothing to undo/redo
		undoAction.setEnabled(false);
		redoAction.setEnabled(false);

		// Nothing to cut/copy/paste
		cutAction.setEnabled(false);
		cutKeyPairAction.setEnabled(false);
		cutTrustedCertificateAction.setEnabled(false);
		copyAction.setEnabled(false);
		copyKeyPairAction.setEnabled(false);
		copyTrustedCertificateAction.setEnabled(false);
		pasteAction.setEnabled(false);

		// Nothing to use tools on
		generateKeyPairAction.setEnabled(false);
		generateSecretKeyAction.setEnabled(false);
		importTrustedCertificateAction.setEnabled(false);
		importKeyPairAction.setEnabled(false);
		setPasswordAction.setEnabled(false);
		jmChangeType.setEnabled(false);
		propertiesAction.setEnabled(false);

		// No current KeyStore type
		jrbmiChangeTypeJks.setSelected(false);
		jrbmiChangeTypeJceks.setSelected(false);
		jrbmiChangeTypePkcs12.setSelected(false);
		jrbmiChangeTypeBks.setSelected(false);
		jrbmiChangeTypeBcfks.setSelected(false);
		jrbmiChangeTypeBksV1.setSelected(false);
		jrbmiChangeTypeUber.setSelected(false);
		jrbmiKeyStoreChangeTypeJks.setSelected(false);
		jrbmiKeyStoreChangeTypeJceks.setSelected(false);
		jrbmiKeyStoreChangeTypePkcs12.setSelected(false);
		jrbmiKeyStoreChangeTypeBks.setSelected(false);
		jrbmiKeyStoreChangeTypeBksV1.setSelected(false);
		jrbmiKeyStoreChangeTypeBcfks.setSelected(false);
		jrbmiKeyStoreChangeTypeUber.setSelected(false);

		// Show Quick Start pane
		frame.getContentPane().remove(jkstpKeyStores);
		frame.getContentPane().add(jQuickStart, BorderLayout.CENTER);

		updateApplicationTitle();

		setDefaultStatusBarText();

		frame.repaint();
	}

	private void updateUndoRedoControls(KeyStoreState state) {
		undoAction.setEnabled(state.hasPreviousState());
		redoAction.setEnabled(state.hasNextState());
	}

	private void updateCutCopyPasteControls() {
		// Can cut and copy if a KeyStore entry is selected
		boolean cutAndCopyEnabled = getActiveKeyStoreTable().getSelectedRow() != -1;

		cutAction.setEnabled(cutAndCopyEnabled);
		cutKeyPairAction.setEnabled(cutAndCopyEnabled);
		cutTrustedCertificateAction.setEnabled(cutAndCopyEnabled);
		copyAction.setEnabled(cutAndCopyEnabled);
		copyKeyPairAction.setEnabled(cutAndCopyEnabled);
		copyTrustedCertificateAction.setEnabled(cutAndCopyEnabled);

		// Can paste if anything is in buffer
		pasteAction.setEnabled(Buffer.isPopulated());
	}

	private void updateKeyStoreTabsText() {
		for (int i = 0; i < histories.size(); i++) {
			KeyStoreHistory history = histories.get(i);

			KeyStoreTab keyStoreTab = (KeyStoreTab) jkstpKeyStores.getTabComponentAt(i);

			// Tab component may not be available yet
			if (keyStoreTab != null) {
				if (!history.getCurrentState().isSavedState()) {
					// Unsaved Changes - append '*'
					String title = MessageFormat.format("{0} *", history.getName());
					keyStoreTab.updateTitle(title);
				} else {
					String title = history.getName();
					keyStoreTab.updateTitle(title);
				}

				// Set tooltip text to be the same as is displayed for the
				// KeyStore's status bar text
				jkstpKeyStores.setToolTipTextAt(i, getKeyStoreStatusText(history));
				keyStoreTab.validate();
			}
		}
	}

	private void updateApplicationTitle() {
		// Title: "[KeyStore Name [*] - ] Application Name and Version"

		String appName = MessageFormat.format("{0} {1}", KSE.getApplicationName(), KSE.getApplicationVersion());

		KeyStoreHistory history = getActiveKeyStoreHistory();

		if (history == null) {
			frame.setTitle(appName);
		} else {
			String keyStoreName = history.getName();

			if (!history.getCurrentState().isSavedState()) {
				frame.setTitle(MessageFormat.format("{0} * - {1}", keyStoreName, appName));
			} else {
				frame.setTitle(MessageFormat.format("{0} - {1}", keyStoreName, appName));
			}
		}
	}

	/**
	 * Display the supplied text in the status bar.
	 *
	 * @param status
	 *            Text to display
	 */
	@Override
	public void setStatusBarText(String status) {
		jlStatusBar.setText(status);
	}

	/**
	 * Set the text in the status bar to reflect the status of the active
	 * KeyStore.
	 */
	@Override
	public void setDefaultStatusBarText() {
		KeyStoreHistory history = getActiveKeyStoreHistory();

		if (history == null) {
			setStatusBarText(res.getString("KseFrame.noKeyStore.statusbar"));
		} else {
			setStatusBarText(getKeyStoreStatusText(history));
		}
	}

	private String getKeyStoreStatusText(KeyStoreHistory history) {
		// Status Text: 'KeyStore Type, Size, Path'
		KeyStoreState currentState = history.getCurrentState();

		KeyStore ksLoaded = currentState.getKeyStore();

		int size;
		try {
			size = ksLoaded.size();
		} catch (KeyStoreException ex) {
			DError.displayError(frame, ex);
			return "";
		}

		KeyStoreType keyStoreType = currentState.getType();

		return MessageFormat.format(res.getString("KseFrame.entries.statusbar"),
				keyStoreType.friendly(), size, history.getPath());
	}

	/**
	 * Get frame's size and position. Used to get size on exit.
	 *
	 * @param keyStoresClosed
	 *            Were all KeyStores closed on exit?
	 * @return Size and position
	 */
	public Rectangle getSizeAndPosition(boolean keyStoresClosed) {
		Rectangle sizeAndPosition = new Rectangle();

		if (keyStoresClosed) {
			sizeAndPosition.width = jkstpKeyStores.getWidth();
			sizeAndPosition.height = jkstpKeyStores.getHeight();
		} else {
			sizeAndPosition.width = jQuickStart.getWidth();
			sizeAndPosition.height = jQuickStart.getHeight();
		}

		sizeAndPosition.x = frame.getX();
		sizeAndPosition.y = frame.getY();

		return sizeAndPosition;
	}

	/**
	 * Get recently opened files.
	 *
	 * @return Recently opened files
	 */
	public File[] getRecentFiles() {
		return jmrfRecentFiles.getRecentFiles();
	}

	/**
	 * Add a file to the top of the recently opened files.
	 *
	 * @param recentFile
	 *            Recent file
	 */
	public void addRecentFile(File recentFile) {
		jmrfRecentFiles.add(createRecentFileMenuItem(jmrfRecentFiles, recentFile));
	}

	/**
	 * Set tab layout policy - must be one of JTabbedPane.WRAP_TAB_LAYOUT or
	 * JTabbedPane.SCROLL_TAB_LAYOUT to take effect.
	 *
	 * @param tabLayoutPolicy
	 *            Tab layout policy
	 */
	public void setKeyStoreTabLayoutPolicy(int tabLayoutPolicy) {
		if (tabLayoutPolicy == JTabbedPane.WRAP_TAB_LAYOUT || tabLayoutPolicy == JTabbedPane.SCROLL_TAB_LAYOUT) {
			jkstpKeyStores.setTabLayoutPolicy(tabLayoutPolicy);
			applicationSettings.setTabLayout(tabLayoutPolicy);
		}
	}

	/**
	 * If the tool bar is currently displayed hide it and vice versa.
	 */
	public void showHideToolBar() {
		Container contentPane = frame.getContentPane();
		boolean toolBarShown = false;
		for (int i = 0; i < contentPane.getComponentCount(); i++) {
			if (contentPane.getComponent(i).equals(jtbToolBar)) {
				toolBarShown = true;
				break;
			}
		}

		if (toolBarShown) {
			frame.getContentPane().remove(jtbToolBar);
			applicationSettings.setShowToolBar(false);
		} else {
			frame.getContentPane().add(jtbToolBar, BorderLayout.NORTH);
			applicationSettings.setShowToolBar(true);
		}
	}

	/**
	 * If the status bar is currently displayed hide it and vice versa.
	 */
	public void showHideStatusBar() {
		Container contentPane = frame.getContentPane();
		boolean statusBarShown = false;
		for (int i = 0; i < contentPane.getComponentCount(); i++) {
			if (contentPane.getComponent(i).equals(jlStatusBar)) {
				statusBarShown = true;
				break;
			}
		}

		if (statusBarShown) {
			frame.getContentPane().remove(jlStatusBar);
			applicationSettings.setShowStatusBar(false);
		} else {
			frame.getContentPane().add(jlStatusBar, BorderLayout.SOUTH);
			applicationSettings.setShowStatusBar(true);
		}
	}

	private void colAdjust(JTable jtKeyStore) {
		ResourceBundle res = ResourceBundle.getBundle("org/kse/gui/resources");
		jtKeyStore.setAutoResizeMode(autoResizeMode);
		// Add custom renderers for headers and cells
		for (int i = 0; i < jtKeyStore.getColumnCount(); i++) {
			int l = 1;
			int width = 0;
			TableColumn column = jtKeyStore.getColumnModel().getColumn(i);
			// new, size columns based on title. Columns are resizable by default
			TableCellRenderer renderer = column.getHeaderRenderer(); // from:
			// http://www.java2s.com/Tutorial/Java/0240__Swing/Setcolumnwidthbasedoncellrenderer.htm
			for (int row = 0; row < jtKeyStore.getRowCount(); row++) {
				width = 0;
				renderer = jtKeyStore.getCellRenderer(row, i);
				Component comp = renderer.getTableCellRendererComponent(jtKeyStore, jtKeyStore.getValueAt(row, i),
						false, false, row, i);
				width = Math.max(width, comp.getPreferredSize().width);
			}
			l = width;

			if (i == keyStoreTableColumns.colEntryName()) {
				column.setMinWidth((2 + res.getString("KeyStoreTableModel.NameColumn")).length() * iFontSize);
				column.setPreferredWidth(
						Math.max(1 + res.getString("KeyStoreTableModel.NameColumn").length(), 20) * iFontSize);
				column.setMaxWidth(
						Math.max(2 + res.getString("KeyStoreTableModel.NameColumn").length(), 50) * iFontSize);
			}
			if (i == keyStoreTableColumns.colAlgorithm()) {
				column.setMinWidth((4) * iFontSize);
				column.setPreferredWidth(
						Math.max(1 + res.getString("KeyStoreTableModel.AlgorithmColumn").length(), 4) * iFontSize);
				column.setMaxWidth(
						Math.max(2 + res.getString("KeyStoreTableModel.AlgorithmColumn").length(), 5) * iFontSize);
			}
			if (i == keyStoreTableColumns.colKeySize()) {
				column.setMinWidth((4) * iFontSize);
				column.setPreferredWidth(
						Math.max(1 + res.getString("KeyStoreTableModel.KeySizeColumn").length(), (l + 1)) * iFontSize);
				column.setMaxWidth(
						Math.max(2 + res.getString("KeyStoreTableModel.KeySizeColumn").length(), l + 1) * iFontSize);
			}
			if (i == keyStoreTableColumns.colCurve()) {
				column.setMinWidth(8 * iFontSize);
				column.setPreferredWidth(
						1 + Math.max(res.getString("KeyStoreTableModel.CurveColumn").length(), l) * iFontSize);
				column.setMaxWidth(2
						+ Math.max("brainpool999r1".length(), res.getString("KeyStoreTableModel.CurveColumn").length())
						* iFontSize);
			}
			if (i == keyStoreTableColumns.colCertificateExpiry()) {
				l = "20.00.2000 00:00:00 MESZ".length();
				column.setMinWidth("20.00.2000".length() * iFontSize);
				column.setPreferredWidth(
						1 + Math.max(res.getString("KeyStoreTableModel.CertExpiryColumn").length(), l) * iFontSize);
				column.setMaxWidth(
						2 + Math.max(res.getString("KeyStoreTableModel.CertExpiryColumn").length(), l) * iFontSize);
			}
			if (i == keyStoreTableColumns.colLastModified()) {
				l = "20.09.2000 00:00:00 MESZ".length();
				column.setMinWidth("20.00.2000".length() * iFontSize);
				column.setPreferredWidth(
						1 + Math.max(res.getString("KeyStoreTableModel.LastModifiedColumn").length(), l) * iFontSize);
				column.setMaxWidth(
						2 + Math.max(res.getString("KeyStoreTableModel.LastModifiedColumn").length(), l) * iFontSize);
			}
			if (i == keyStoreTableColumns.colAKI()) {
				l = 41;
				column.setMinWidth(8 * iFontSize);
				column.setPreferredWidth(
						1 + Math.max(res.getString("KeyStoreTableModel.AKIColumn").length(), l) * iFontSize);
				column.setMaxWidth(
						Math.max(2 + res.getString("KeyStoreTableModel.AKIColumn").length(), (l + 1)) * iFontSize);
			}
			if (i == keyStoreTableColumns.colSKI()) {
				l = 41;
				column.setMinWidth(8 * iFontSize);
				column.setPreferredWidth(
						Math.max(2 + res.getString("KeyStoreTableModel.SKIColumn").length(), (l + 1)) * iFontSize);
				column.setMaxWidth(
						Math.max(2 + res.getString("KeyStoreTableModel.SKIColumn").length(), (l + 1)) * iFontSize);
			}
			if (i == keyStoreTableColumns.colIssuerCN()) {
				column.setMinWidth(8 * iFontSize);
				column.setPreferredWidth(
						Math.max(2 + res.getString("KeyStoreTableModel.IssuerCNColumn").length(), (l + 1)) * iFontSize);
				column.setMaxWidth(100 * iFontSize);
			}
			if (i == keyStoreTableColumns.colIssuerDN()) {
				column.setMinWidth(8 * iFontSize);
				column.setPreferredWidth(
						Math.max(2 + res.getString("KeyStoreTableModel.IssuerDNColumn").length(), (l + 1)) * iFontSize);
				column.setMaxWidth(100 * iFontSize);
			}
			if (i == keyStoreTableColumns.colIssuerO()) {
				column.setMinWidth(8 * iFontSize);
				column.setPreferredWidth(
						Math.max(2 + res.getString("KeyStoreTableModel.IssuerOColumn").length(), (l + 1)) * iFontSize);
				column.setMaxWidth(100 * iFontSize);
			}
			column.setHeaderRenderer(
					new KeyStoreTableHeadRend(jtKeyStore.getTableHeader().getDefaultRenderer(), keyStoreTableColumns));
			column.setCellRenderer(new KeyStoreTableCellRend());
		}

	}
}