/*
  KeePass Password Safe - The Open-Source Password Manager
  Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "StdAfx.h"
#include <afxadv.h>
#include <winuser.h>
#include <mmsystem.h>
#include <set>
#include <boost/regex/mfc.hpp>

#include "PwSafe.h"
#include "PwSafeDlg.h"

#include "../KeePassLibCpp/PwManager.h"
#include "../KeePassLibCpp/DataExchange/PwImport.h"
#include "../KeePassLibCpp/Util/PwUtil.h"
#include "../KeePassLibCpp/Crypto/TestImpl.h"
#include "../KeePassLibCpp/Crypto/MemoryProtectionEx.h"
#include "../KeePassLibCpp/Crypto/KeyTransform_BCrypt.h"
#include "../KeePassLibCpp/Util/MemUtil.h"
#include "../KeePassLibCpp/Util/StrUtil.h"
#include "../KeePassLibCpp/Util/EntryUtil.h"
#include "../KeePassLibCpp/Util/PopularPasswords.h"
#include "../KeePassLibCpp/Util/AppUtil.h"
#include "../KeePassLibCpp/Util/TranslateEx.h"
#include "Util/WinUtil.h"
#include "Util/SendKeysEx.h"
#include "Util/FileLock.h"
#include "Util/CmdLine/CmdArgs.h"
#include "Util/CmdLine/Executable.h"
#include "Util/UpdateCheckEx.h"
#include "Util/RestartManagerEx.h"
#include "Util/KeySourcesPool.h"
#include "Util/ShutdownBlocker.h"
#include "Util/SprEngine/SprEngine.h"
#include "NewGUI/XHyperLink.h"
#include "NewGUI/XPStyleButtonST.h"
#include "NewGUI/SecureEditEx.h"
#include "NewGUI/MsgRelayWnd.h"
#include "NewGUI/TaskDialog/VistaTaskDialog.h"
#include "NewGUI/TaskbarListEx/TaskbarListEx.h"
// #include "NewGUI/VistaMenu/VistaMenu.h"
#include "NewGUI/NewDialogsEx.h"
#include "NewGUI/NewColorizerEx.h"
#include "NewGUI/DragDropEx.h"
#include "NewGUI/DwmUtil.h"
#include "NewGUI/FontUtil.h"
#include "Plugins/KpApiImpl.h"

#include "PasswordDlg.h"
#include "AddEntryDlg.h"
#include "AddGroupDlg.h"
#include "PwGeneratorExDlg.h"
#include "FindInDbDlg.h"
#include "LanguagesDlg.h"
#include "OptionsDlg.h"
#include "GetRandomDlg.h"
#include "EntryPropertiesDlg.h"
#include "TanWizardDlg.h"
#include "EntryListDlg.h"
#include "DbSettingsDlg.h"
#include "PluginsDlg.h"
#include "CheckOptionsDlg.h"
#include "ImportOptionsDlg.h"
#include "HelpSourceDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#ifdef _UNICODE
#pragma message("Unicode builds are not supported. It is recommended to switch to ANSI configuration.")
#endif

#define WM_MY_SYSTRAY_NOTIFY (WM_APP + 10)
#define WM_MY_UPDATECLIPBOARD (WM_APP + 11)

#ifdef _DEBUG
// #define or #undef sample group and entries
#define ___PWSAFE_SAMPLE_DATA
#endif

static TCHAR g_pNullString[4] = { 0, 0, 0, 0 };
static const BYTE g_uuidZero[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static PW_TIME g_tNeverExpire;
static const TCHAR *g_psztNeverExpires = _T("");

static int g_nIndicatorWidths[3] = { 200, 340, -1 }; // Not const (DPI scaling)

BOOL CPwSafeDlg::m_bMiniMode = FALSE;
BOOL CPwSafeDlg::m_bUnintrusiveMiniMode = FALSE;
BOOL CPwSafeDlg::m_bSecureEdits = TRUE;
BOOL CPwSafeDlg::m_bUseLocalTimeFormat = TRUE;
BOOL CPwSafeDlg::m_bDisableUnsafeAtStart = FALSE;
BOOL CPwSafeDlg::m_bSortAutoTypeSelItems = TRUE;

BOOL CPwSafeDlg::m_bForceAllowChangeMasterKey = FALSE;
BOOL CPwSafeDlg::m_bForceAllowPrinting = FALSE;
BOOL CPwSafeDlg::m_bForceAllowImport = FALSE;
BOOL CPwSafeDlg::m_bForceAllowExport = FALSE;
BOOL CPwSafeDlg::m_bDisallowPrintingPasswords = FALSE;

PW_GEN_SETTINGS_EX CPwSafeDlg::m_pgsAutoProfile;

CString CPwSafeDlg::m_strDefaultAutoTypeSequence;

DWORD CPwSafeDlg::m_dwCachedBackupGroupID = 0;
DWORD CPwSafeDlg::m_dwCachedBackupSrcGroupID = 0;

std::map<std_string, std_string> CPwSafeDlg::m_mHtmlToRtf;

const UINT WM_REG_TASKBARCREATED = ::RegisterWindowMessage(_T("TaskbarCreated"));
const UINT WM_REG_TASKBARBUTTONCREATED = ::RegisterWindowMessage(_T("TaskbarButtonCreated"));
const UINT WM_REG_PROCESSMAILSLOT = ::RegisterWindowMessage(_T("KeePassProcessMailslot"));
const UINT WM_REG_KEEPASSCONTROL = ::RegisterWindowMessage(_T("KeePassControl"));

static volatile UINT g_uMasterKeyDlgs = 0;
static volatile HWND g_hLastMasterKeyDlg = NULL;
static std::set<std_string> g_vAutoTypeSelectionDialogs;

/////////////////////////////////////////////////////////////////////////////

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

	CKCSideBannerWnd m_banner;
	HICON m_hWindowIcon;

	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	CXHyperLink	m_hlDonate;
	CXHyperLink	m_hlCredits;
	CXHyperLink	m_hlLicense;
	CXHyperLink	m_hlHelp;
	CXHyperLink	m_hlHomepage;
	CXPStyleButtonST	m_btOK;
	//}}AFX_DATA

	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);
	//}}AFX_VIRTUAL

protected:
	//{{AFX_MSG(CAboutDlg)
	virtual BOOL OnInitDialog();
	//}}AFX_MSG

	afx_msg LRESULT OnXHyperLinkClicked(WPARAM wParam, LPARAM lParam);
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	DDX_Control(pDX, IDC_HLINK_DONATE, m_hlDonate);
	DDX_Control(pDX, IDC_HLINK_CREDITS, m_hlCredits);
	DDX_Control(pDX, IDC_HLINK_LICENSEFILE, m_hlLicense);
	DDX_Control(pDX, IDC_HLINK_HELPFILE, m_hlHelp);
	DDX_Control(pDX, IDC_HLINK_HOMEPAGE, m_hlHomepage);
	DDX_Control(pDX, IDOK, m_btOK);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
	//}}AFX_MSG_MAP

	ON_REGISTERED_MESSAGE(WM_XHYPERLINK_CLICKED, OnXHyperLinkClicked)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

class CPwSafeAppRI : public CNewRandomInterface
{
public:
	BOOL GenerateRandomSequence(unsigned long uRandomSeqSize, unsigned char *pBuffer) const;
};

BOOL CPwSafeAppRI::GenerateRandomSequence(unsigned long uRandomSeqSize,
	unsigned char *pBuffer) const
{
	ASSERT(uRandomSeqSize <= 32); // Only up to 32-byte long random sequence is supported for now!
	if(uRandomSeqSize > 32) uRandomSeqSize = 32;

	CGetRandomDlg dlg;
	if(NewGUI_DoModal(&dlg) == IDCANCEL) return FALSE;

	memcpy(pBuffer, dlg.m_pFinalRandom, uRandomSeqSize);
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////

CPwSafeDlg::CPwSafeDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CPwSafeDlg::IDD, pParent), m_instanceChecker(_T("KeePass Instance"))
	, m_strQuickFind(_T("")), m_lGroupUrlStart(0)
{
	//{{AFX_DATA_INIT(CPwSafeDlg)
	//}}AFX_DATA_INIT
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	m_hIcon48 = (HICON)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(
		IDR_MAINFRAME), IMAGE_ICON, 48, 48, 0);
	ASSERT((m_hIcon != NULL) && (m_hIcon48 != NULL));

	m_pThemeHelper = NULL;

	m_bExiting = FALSE;
	m_bIsLocking = FALSE;
	m_bTrayed = FALSE;
	m_bMinimized = FALSE;
	m_bIgnoreSizeEvent = TRUE;
	m_bCheckForInstance = FALSE;
	m_bRestoreHotKeyRegistered = FALSE;
	m_bInitialCmdLineFile = FALSE;
	m_hPreTrayFocus = NULL;

	m_uLockAt = UINT64_MAX;

	m_hLastSelectedGroup = NULL;
	m_dwLastNumSelectedItems = 0;
	m_dwLastFirstSelectedItem = 0;
	m_dwLastEntryIndex = DWORD_MAX;

	m_nClipboardState = 0;
	m_hwndNextViewer = NULL;
	m_bGlobalAutoTypePending = FALSE;
	m_bBlockQuickFindSelChange = FALSE;

	m_pPwListMenu = m_pGroupListMenu = m_pEntryViewMenu = NULL;
	m_pPwListTrackableMenu = m_pGroupListTrackableMenu = m_pEntryViewTrackableMenu = NULL;

	m_lNormalWndPosX = m_lNormalWndPosY = 0;
	m_lNormalWndSizeW = m_lNormalWndSizeH = -1;
	ZeroMemory(&m_szLastContent, sizeof(SIZE));

	m_clrIcoStoreMain = DWORD_MAX;
	m_hIcoStoreMain = NULL;
	m_clrIcoStoreTrayNormal = DWORD_MAX;
	m_hIcoStoreTrayNormal = NULL;
	// m_clrIcoStoreTrayLocked = DWORD_MAX;
	// m_hIcoStoreTrayLocked = NULL;
	m_bShowTrayOnlyIfTrayed = FALSE;

	m_mgr.InitPrimaryInstance();
}

void CPwSafeDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CPwSafeDlg)
	DDX_Control(pDX, IDC_MENULINE, m_stcMenuLine);
	DDX_Control(pDX, IDC_GROUPLIST, m_cGroups);
	DDX_Control(pDX, IDC_TB_NEW, m_btnTbNew);
	DDX_Control(pDX, IDC_TB_LOCK, m_btnTbLock);
	DDX_Control(pDX, IDC_TB_FIND, m_btnTbFind);
	DDX_Control(pDX, IDC_TB_EDITENTRY, m_btnTbEditEntry);
	DDX_Control(pDX, IDC_TB_DELETEENTRY, m_btnTbDeleteEntry);
	DDX_Control(pDX, IDC_TB_COPYUSER, m_btnTbCopyUser);
	DDX_Control(pDX, IDC_TB_COPYPW, m_btnTbCopyPw);
	DDX_Control(pDX, IDC_TB_ADDENTRY, m_btnTbAddEntry);
	DDX_Control(pDX, IDC_TB_ABOUT, m_btnTbAbout);
	DDX_Control(pDX, IDC_TB_SAVE, m_btnTbSave);
	DDX_Control(pDX, IDC_TB_OPEN, m_btnTbOpen);
	DDX_Control(pDX, IDC_PWLIST, m_cList);
	DDX_Control(pDX, IDC_RE_ENTRYVIEW, m_reEntryView);
	DDX_Control(pDX, IDC_COMBO_QUICKFIND, m_cQuickFind);
	DDX_CBString(pDX, IDC_COMBO_QUICKFIND, m_strQuickFind);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CPwSafeDlg, CDialog)
	//{{AFX_MSG_MAP(CPwSafeDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_SIZE()
	ON_WM_SIZING()
	ON_WM_MEASUREITEM()
	ON_WM_MENUCHAR()
	ON_WM_INITMENUPOPUP()
	ON_WM_ACTIVATE()
	ON_COMMAND(ID_FILE_EXIT, OnFileExit)
	ON_UPDATE_COMMAND_UI(ID_FILE_EXIT, OnUpdateFileExit)
	ON_COMMAND(ID_INFO_ABOUT, OnInfoAbout)
	ON_COMMAND(ID_SAFE_ADDGROUP, OnSafeAddGroup)
	ON_COMMAND(ID_VIEW_HIDESTARS, OnViewHideStars)
	ON_COMMAND(ID_PWLIST_ADD, OnPwlistAdd)
	ON_COMMAND(ID_PWLIST_EDIT, OnPwlistEdit)
	ON_COMMAND(ID_PWLIST_DELETE, OnPwlistDelete)
	ON_NOTIFY(NM_CLICK, IDC_GROUPLIST, OnClickGroupList)
	ON_COMMAND(ID_PWLIST_COPYPW, OnPwlistCopyPw)
	ON_WM_TIMER()
	ON_NOTIFY(NM_DBLCLK, IDC_PWLIST, OnDblclkPwlist)
	ON_COMMAND(ID_PWLIST_COPYUSER, OnPwlistCopyUser)
	ON_COMMAND(ID_PWLIST_VISITURL, OnPwlistVisitUrl)
	ON_COMMAND(ID_FILE_NEW, OnFileNew)
	ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
	ON_COMMAND(ID_FILE_SAVE, OnFileSave)
	ON_COMMAND(ID_FILE_SAVEAS, OnFileSaveAs)
	ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
	ON_COMMAND(ID_SAFE_OPTIONS, OnSafeOptions)
	ON_COMMAND(ID_SAFE_REMOVEGROUP, OnSafeRemoveGroup)
	ON_COMMAND(ID_FILE_CHANGEMASTERPW, OnFileChangeMasterPw)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVEAS, OnUpdateFileSaveAs)
	ON_UPDATE_COMMAND_UI(ID_FILE_CHANGEMASTERPW, OnUpdateFileChangeMasterPw)
	ON_UPDATE_COMMAND_UI(ID_FILE_CLOSE, OnUpdateFileClose)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_COPYPW, OnUpdatePwlistCopyPw)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_COPYUSER, OnUpdatePwlistCopyUser)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_DELETE, OnUpdatePwlistDelete)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_EDIT, OnUpdatePwlistEdit)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_VISITURL, OnUpdatePwlistVisitUrl)
	ON_UPDATE_COMMAND_UI(ID_SAFE_REMOVEGROUP, OnUpdateSafeRemoveGroup)
	ON_UPDATE_COMMAND_UI(ID_SAFE_ADDGROUP, OnUpdateSafeAddGroup)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_ADD, OnUpdatePwlistAdd)
	ON_COMMAND(ID_EXPORT_TXT, OnExportTxt)
	ON_COMMAND(ID_EXPORT_HTML, OnExportHtml)
	ON_COMMAND(ID_EXPORT_XML, OnExportXml)
	ON_COMMAND(ID_EXPORT_CSV, OnExportCsv)
	ON_UPDATE_COMMAND_UI(ID_EXPORT_TXT, OnUpdateExportTxt)
	ON_UPDATE_COMMAND_UI(ID_EXPORT_HTML, OnUpdateExportHtml)
	ON_UPDATE_COMMAND_UI(ID_EXPORT_XML, OnUpdateExportXml)
	ON_UPDATE_COMMAND_UI(ID_EXPORT_CSV, OnUpdateExportCsv)
	ON_COMMAND(ID_FILE_PRINT, OnFilePrint)
	ON_UPDATE_COMMAND_UI(ID_FILE_PRINT, OnUpdateFilePrint)
	ON_COMMAND(ID_EXTRAS_GENPW, OnExtrasGenPw)
	ON_COMMAND(ID_SAFE_MODIFYGROUP, OnSafeModifyGroup)
	ON_UPDATE_COMMAND_UI(ID_SAFE_MODIFYGROUP, OnUpdateSafeModifyGroup)
	ON_COMMAND(ID_PWLIST_FIND, OnPwlistFind)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_FIND, OnUpdatePwlistFind)
	ON_COMMAND(ID_PWLIST_FINDINGROUP, OnPwlistFindInGroup)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_FINDINGROUP, OnUpdatePwlistFindInGroup)
	ON_COMMAND(ID_PWLIST_DUPLICATE, OnPwlistDuplicate)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_DUPLICATE, OnUpdatePwlistDuplicate)
	ON_COMMAND(ID_INFO_HOMEPAGE, OnInfoHomepage)
	ON_COMMAND(ID_VIEW_ALWAYSONTOP, OnViewAlwaysOnTop)
	ON_COMMAND(ID_SAFE_EXPORTGROUP_HTML, OnSafeExportGroupHtml)
	ON_COMMAND(ID_SAFE_EXPORTGROUP_XML, OnSafeExportGroupXml)
	ON_COMMAND(ID_SAFE_EXPORTGROUP_CSV, OnSafeExportGroupCsv)
	ON_UPDATE_COMMAND_UI(ID_SAFE_EXPORTGROUP_HTML, OnUpdateSafeExportGroupHtml)
	ON_UPDATE_COMMAND_UI(ID_SAFE_EXPORTGROUP_XML, OnUpdateSafeExportGroupXml)
	ON_UPDATE_COMMAND_UI(ID_SAFE_EXPORTGROUP_CSV, OnUpdateSafeExportGroupCsv)
	ON_COMMAND(ID_SAFE_PRINTGROUP, OnSafePrintGroup)
	ON_UPDATE_COMMAND_UI(ID_SAFE_PRINTGROUP, OnUpdateSafePrintGroup)
	ON_COMMAND(ID_PWLIST_MOVEUP, OnPwlistMoveUp)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_MOVEUP, OnUpdatePwlistMoveUp)
	ON_COMMAND(ID_PWLIST_MOVETOP, OnPwlistMoveTop)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_MOVETOP, OnUpdatePwlistMoveTop)
	ON_COMMAND(ID_PWLIST_MOVEDOWN, OnPwlistMoveDown)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_MOVEDOWN, OnUpdatePwlistMoveDown)
	ON_COMMAND(ID_PWLIST_MOVEBOTTOM, OnPwlistMoveBottom)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_MOVEBOTTOM, OnUpdatePwlistMoveBottom)
	ON_NOTIFY(LVN_BEGINDRAG, IDC_PWLIST, OnBeginDragPwlist)
	ON_COMMAND(ID_FILE_CHANGELANGUAGE, OnFileChangeLanguage)
	ON_COMMAND(ID_INFO_README, OnInfoReadme)
	ON_COMMAND(ID_VIEW_TITLE, OnViewTitle)
	ON_COMMAND(ID_VIEW_USERNAME, OnViewUsername)
	ON_COMMAND(ID_VIEW_URL, OnViewUrl)
	ON_COMMAND(ID_VIEW_PASSWORD, OnViewPassword)
	ON_COMMAND(ID_VIEW_NOTES, OnViewNotes)
	ON_COMMAND(ID_FILE_LOCK, OnFileLock)
	ON_UPDATE_COMMAND_UI(ID_FILE_LOCK, OnUpdateFileLock)
	ON_COMMAND(ID_GROUP_MOVETOP, OnGroupMoveTop)
	ON_UPDATE_COMMAND_UI(ID_GROUP_MOVETOP, OnUpdateGroupMoveTop)
	ON_COMMAND(ID_GROUP_MOVEBOTTOM, OnGroupMoveBottom)
	ON_UPDATE_COMMAND_UI(ID_GROUP_MOVEBOTTOM, OnUpdateGroupMoveBottom)
	ON_COMMAND(ID_GROUP_MOVEUP, OnGroupMoveUp)
	ON_UPDATE_COMMAND_UI(ID_GROUP_MOVEUP, OnUpdateGroupMoveUp)
	ON_COMMAND(ID_GROUP_MOVEDOWN, OnGroupMoveDown)
	ON_UPDATE_COMMAND_UI(ID_GROUP_MOVEDOWN, OnUpdateGroupMoveDown)
	ON_MESSAGE(WM_MY_SYSTRAY_NOTIFY, OnTrayNotification)
	ON_MESSAGE(WM_MY_UPDATECLIPBOARD, OnUpdateClipboard)
	ON_COMMAND(ID_VIEW_HIDE, OnViewHide)
	ON_COMMAND(ID_IMPORT_CSV, OnImportCsv)
	ON_UPDATE_COMMAND_UI(ID_IMPORT_CSV, OnUpdateImportCsv)
	ON_NOTIFY(NM_CLICK, IDC_PWLIST, OnClickPwlist)
	ON_COMMAND(ID_IMPORT_CWALLET, OnImportCWallet)
	ON_UPDATE_COMMAND_UI(ID_IMPORT_CWALLET, OnUpdateImportCWallet)
	ON_UPDATE_COMMAND_UI(ID_FILE_NEW, OnUpdateFileNew)
	ON_UPDATE_COMMAND_UI(ID_FILE_OPEN, OnUpdateFileOpen)
	ON_COMMAND(ID_IMPORT_PWSAFE, OnImportPwSafe)
	ON_UPDATE_COMMAND_UI(ID_IMPORT_PWSAFE, OnUpdateImportPwSafe)
	ON_COMMAND(ID_VIEW_CREATION, OnViewCreation)
	ON_COMMAND(ID_VIEW_LASTMOD, OnViewLastMod)
	ON_COMMAND(ID_VIEW_LASTACCESS, OnViewLastAccess)
	ON_COMMAND(ID_VIEW_EXPIRE, OnViewExpire)
	ON_COMMAND(ID_VIEW_UUID, OnViewUuid)
	ON_BN_CLICKED(IDC_TB_OPEN, OnTbOpen)
	ON_BN_CLICKED(IDC_TB_SAVE, OnTbSave)
	ON_BN_CLICKED(IDC_TB_NEW, OnTbNew)
	ON_BN_CLICKED(IDC_TB_COPYUSER, OnTbCopyUser)
	ON_BN_CLICKED(IDC_TB_COPYPW, OnTbCopyPw)
	ON_BN_CLICKED(IDC_TB_ADDENTRY, OnTbAddEntry)
	ON_BN_CLICKED(IDC_TB_EDITENTRY, OnTbEditEntry)
	ON_BN_CLICKED(IDC_TB_DELETEENTRY, OnTbDeleteEntry)
	ON_BN_CLICKED(IDC_TB_FIND, OnTbFind)
	ON_BN_CLICKED(IDC_TB_LOCK, OnTbLock)
	ON_BN_CLICKED(IDC_TB_ABOUT, OnTbAbout)
	ON_COMMAND(ID_VIEW_SHOWTOOLBAR, OnViewShowToolBar)
	ON_COMMAND(ID_PWLIST_MASSMODIFY, OnPwlistMassModify)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_MASSMODIFY, OnUpdatePwlistMassModify)
	ON_NOTIFY(LVN_KEYDOWN, IDC_PWLIST, OnKeyDownPwlist)
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_COMMAND(ID_VIEW_ENTRYVIEW, OnViewEntryView)
	ON_COMMAND(ID_RE_COPYSEL, OnReCopySel)
	ON_UPDATE_COMMAND_UI(ID_RE_COPYSEL, OnUpdateReCopySel)
	ON_COMMAND(ID_RE_COPYALL, OnReCopyAll)
	ON_COMMAND(ID_RE_SELECTALL, OnReSelectAll)
	ON_COMMAND(ID_EXTRAS_TANWIZARD, OnExtrasTanWizard)
	ON_UPDATE_COMMAND_UI(ID_EXTRAS_TANWIZARD, OnUpdateExtrasTanWizard)
	ON_COMMAND(ID_FILE_PRINTPREVIEW, OnFilePrintPreview)
	ON_UPDATE_COMMAND_UI(ID_FILE_PRINTPREVIEW, OnUpdateFilePrintPreview)
	ON_COMMAND(ID_INFO_TRANSLATION, OnInfoTranslation)
	ON_COMMAND(ID_SAFE_ADDSUBGROUP, OnSafeAddSubgroup)
	ON_UPDATE_COMMAND_UI(ID_SAFE_ADDSUBGROUP, OnUpdateSafeAddSubgroup)
	ON_NOTIFY(TVN_BEGINDRAG, IDC_GROUPLIST, OnBeginDragGrouplist)
	ON_WM_CANCELMODE()
	ON_COMMAND(ID_GROUP_SORT, OnGroupSort)
	ON_UPDATE_COMMAND_UI(ID_GROUP_SORT, OnUpdateGroupSort)
	ON_COMMAND(ID_PWLIST_SORT_TITLE, OnPwlistSortTitle)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_SORT_TITLE, OnUpdatePwlistSortTitle)
	ON_COMMAND(ID_PWLIST_SORT_USER, OnPwlistSortUser)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_SORT_USER, OnUpdatePwlistSortUser)
	ON_COMMAND(ID_PWLIST_SORT_URL, OnPwlistSortUrl)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_SORT_URL, OnUpdatePwlistSortUrl)
	ON_COMMAND(ID_PWLIST_SORT_PASSWORD, OnPwlistSortPassword)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_SORT_PASSWORD, OnUpdatePwlistSortPassword)
	ON_COMMAND(ID_PWLIST_SORT_NOTES, OnPwlistSortNotes)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_SORT_NOTES, OnUpdatePwlistSortNotes)
	ON_COMMAND(ID_PWLIST_SORT_CREATION, OnPwlistSortCreation)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_SORT_CREATION, OnUpdatePwlistSortCreation)
	ON_COMMAND(ID_PWLIST_SORT_LASTMODIFY, OnPwlistSortLastmodify)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_SORT_LASTMODIFY, OnUpdatePwlistSortLastmodify)
	ON_COMMAND(ID_PWLIST_SORT_LASTACCESS, OnPwlistSortLastaccess)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_SORT_LASTACCESS, OnUpdatePwlistSortLastaccess)
	ON_COMMAND(ID_PWLIST_SORT_EXPIRE, OnPwlistSortExpire)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_SORT_EXPIRE, OnUpdatePwlistSortExpire)
	ON_COMMAND(ID_GROUP_MOVELEFT, OnGroupMoveLeft)
	ON_UPDATE_COMMAND_UI(ID_GROUP_MOVELEFT, OnUpdateGroupMoveLeft)
	ON_COMMAND(ID_GROUP_MOVERIGHT, OnGroupMoveRight)
	ON_UPDATE_COMMAND_UI(ID_GROUP_MOVERIGHT, OnUpdateGroupMoveRight)
	ON_COMMAND(ID_VIEW_HIDEUSERS, OnViewHideUsers)
	ON_COMMAND(ID_VIEW_ATTACH, OnViewAttach)
	ON_COMMAND(ID_PWLIST_SAVEATTACH, OnPwlistSaveAttach)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_SAVEATTACH, OnUpdatePwlistSaveAttach)
	ON_COMMAND(ID_FILE_SHOWDBINFO, OnFileShowDbInfo)
	ON_UPDATE_COMMAND_UI(ID_FILE_SHOWDBINFO, OnUpdateFileShowDbInfo)
	ON_COMMAND(ID_EXTRAS_SHOWEXPIRED, OnExtrasShowExpired)
	ON_UPDATE_COMMAND_UI(ID_EXTRAS_SHOWEXPIRED, OnUpdateExtrasShowExpired)
	ON_COMMAND(ID_IMPORT_PVAULT, OnImportPvault)
	ON_UPDATE_COMMAND_UI(ID_IMPORT_PVAULT, OnUpdateImportPvault)
	ON_COMMAND(ID_SAFE_EXPORTGROUP_TXT, OnSafeExportGroupTxt)
	ON_UPDATE_COMMAND_UI(ID_SAFE_EXPORTGROUP_TXT, OnUpdateSafeExportGroupTxt)
	ON_COMMAND(ID_PWLIST_SELECTALL, OnPwlistSelectAll)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_SELECTALL, OnUpdatePwlistSelectAll)
	ON_COMMAND(ID_VIEW_AUTOSORT_CREATION, OnViewAutosortCreation)
	ON_COMMAND(ID_VIEW_AUTOSORT_EXPIRE, OnViewAutosortExpire)
	ON_COMMAND(ID_VIEW_AUTOSORT_LASTACCESS, OnViewAutosortLastaccess)
	ON_COMMAND(ID_VIEW_AUTOSORT_LASTMODIFY, OnViewAutosortLastmodify)
	ON_COMMAND(ID_VIEW_AUTOSORT_NOSORT, OnViewAutosortNosort)
	ON_COMMAND(ID_VIEW_AUTOSORT_NOTES, OnViewAutosortNotes)
	ON_COMMAND(ID_VIEW_AUTOSORT_PASSWORD, OnViewAutosortPassword)
	ON_COMMAND(ID_VIEW_AUTOSORT_TITLE, OnViewAutosortTitle)
	ON_COMMAND(ID_VIEW_AUTOSORT_URL, OnViewAutosortUrl)
	ON_COMMAND(ID_VIEW_AUTOSORT_USER, OnViewAutosortUser)
	ON_UPDATE_COMMAND_UI(ID_SAFE_OPTIONS, OnUpdateSafeOptions)
	ON_COMMAND(ID_PWLIST_AUTOTYPE, OnPwlistAutoType)
	ON_UPDATE_COMMAND_UI(ID_PWLIST_AUTOTYPE, OnUpdatePwlistAutoType)
	ON_NOTIFY(LVN_COLUMNCLICK, IDC_PWLIST, OnColumnClickPwlist)
	ON_COMMAND(ID_EXTRAS_PLUGINMGR, OnExtrasPluginMgr)
	ON_MESSAGE(WM_HOTKEY, OnHotKey)
	ON_COMMAND(ID_IMPORT_GETMORE, OnImportGetMore)
	ON_COMMAND(ID_INFO_DONATE, OnInfoDonate)
	ON_COMMAND(ID_EXTRAS_REPAIRDB, OnExtrasRepairDb)
	ON_UPDATE_COMMAND_UI(ID_EXTRAS_REPAIRDB, OnUpdateExtrasRepairDb)
	ON_COMMAND(ID_IMPORT_KEEPASS, OnImportKeePass)
	ON_UPDATE_COMMAND_UI(ID_IMPORT_KEEPASS, OnUpdateImportKeePass)
	ON_COMMAND(ID_SAFE_EXPORTGROUP_KEEPASS, OnSafeExportGroupKeePass)
	ON_UPDATE_COMMAND_UI(ID_SAFE_EXPORTGROUP_KEEPASS, OnUpdateSafeExportGroupKeePass)
	ON_COMMAND(ID_EXPORT_KEEPASS, OnExportKeePass)
	ON_UPDATE_COMMAND_UI(ID_EXPORT_KEEPASS, OnUpdateExportKeePass)
	ON_COMMAND(ID_VIEW_SIMPLETANVIEW, OnViewSimpleTANView)
	ON_COMMAND(ID_VIEW_SHOWTANINDICES, OnViewShowTANIndices)
	ON_UPDATE_COMMAND_UI(ID_EXTRAS_GENPW, OnUpdateExtrasGenPw)
	ON_UPDATE_COMMAND_UI(ID_INFO_ABOUT, OnUpdateInfoAbout)
	ON_COMMAND(ID_INFO_CHKFORUPD, OnInfoChkForUpd)
	ON_UPDATE_COMMAND_UI(ID_VIEW_HIDE, OnUpdateViewHide)
	ON_CBN_SELCHANGE(IDC_COMBO_QUICKFIND, &CPwSafeDlg::OnQuickFindSelChange)
	ON_COMMAND(ID_INFO_HELP_SELECTHELPSOURCE, &CPwSafeDlg::OnInfoHelpSelectHelpSource)
	//}}AFX_MSG_MAP

	ON_COMMAND_RANGE(WM_PLUGINS_FIRST, WM_PLUGINS_LAST, OnPluginMessage)
	ON_UPDATE_COMMAND_UI_RANGE(WM_PLUGINS_FIRST, WM_PLUGINS_LAST, OnUpdatePluginMessage)
	ON_WM_RENDERFORMAT()
	ON_WM_RENDERALLFORMATS()
	ON_WM_CHANGECBCHAIN()
	ON_WM_DRAWCLIPBOARD()

	ON_WM_QUERYENDSESSION()
	ON_WM_ENDSESSION()
	ON_WM_COPYDATA()
	ON_WM_CONTEXTMENU()

	ON_MESSAGE(WM_WTSSESSION_CHANGE, OnWTSSessionChange)

	ON_MESSAGE(WM_DWMSENDICONICTHUMBNAIL, OnDwmSendIconicThumbnail)
	ON_MESSAGE(WM_DWMSENDICONICLIVEPREVIEWBITMAP, OnDwmSendIconicLivePreviewBitmap)

	ON_REGISTERED_MESSAGE(WM_REG_TASKBARCREATED, OnTaskbarCreated)
	ON_REGISTERED_MESSAGE(WM_REG_TASKBARBUTTONCREATED, OnTaskbarButtonCreated)
	ON_REGISTERED_MESSAGE(WM_REG_PROCESSMAILSLOT, OnProcessMailslot)
	ON_REGISTERED_MESSAGE(WM_REG_KEEPASSCONTROL, OnKeePassControlMessage)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

BOOL CAboutDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	NewGUI_TranslateCWnd(this);
	EnumChildWindows(this->m_hWnd, NewGUI_TranslateWindowCb, 0);

	NewGUI_XPButton(m_btOK, IDB_OK, IDB_OK);

	NewGUI_ConfigSideBanner(&m_banner, this);
	m_banner.SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME),
		KCSB_ICON_LEFT | KCSB_ICON_VCENTER);

	m_banner.SetTitle(PWM_PRODUCT_NAME);

	CString str = TRL("Version");
	str += _T(" ");
	str += PWM_VERSION_STR;
#ifdef PWM_DEVSNAPSHOT
	str += _T(" (Dev.)");
#endif
	if(CPwSafeDlg::m_bMiniMode == TRUE)
	{
		str += _T(" (");
		str += TRL("in \"Minimal functionality\" mode");
		str += _T(")");
	}
	m_banner.SetCaption(str);

	m_hWindowIcon = AfxGetApp()->LoadIcon(MAKEINTRESOURCE(IDR_MAINFRAME));
	SetIcon(m_hWindowIcon, TRUE);
	SetIcon(m_hWindowIcon, FALSE);

	// NewGUI_MakeHyperLink(&m_hlHomepage);
	// m_hlHomepage.SetURL(CString(PWM_HOMEPAGE));
	str = TRL("KeePass Website");
	m_hlHomepage.SetWindowText(str);
	NewGUI_MakeHyperLink(&m_hlHomepage);
	m_hlHomepage.EnableTooltip(FALSE);
	m_hlHomepage.SetNotifyParent(TRUE);
	m_hlHomepage.EnableURL(FALSE);

	str = TRL("Open &Help File"); RemoveAcceleratorTip(&str);
	m_hlHelp.SetWindowText(str);
	NewGUI_MakeHyperLink(&m_hlHelp);
	m_hlHelp.EnableTooltip(FALSE);
	m_hlHelp.SetNotifyParent(TRUE);
	m_hlHelp.EnableURL(FALSE);

	str = TRL("Open &License File"); RemoveAcceleratorTip(&str);
	m_hlLicense.SetWindowText(str);
	NewGUI_MakeHyperLink(&m_hlLicense);
	m_hlLicense.EnableTooltip(FALSE);
	m_hlLicense.SetNotifyParent(TRUE);
	m_hlLicense.EnableURL(FALSE);

	str = TRL("Acknowledgements");
	m_hlCredits.SetWindowText(str);
	NewGUI_MakeHyperLink(&m_hlCredits);
	m_hlCredits.EnableTooltip(FALSE);
	m_hlCredits.SetNotifyParent(TRUE);
	m_hlCredits.EnableURL(FALSE);

	str = TRL("&Donate..."); RemoveAcceleratorTip(&str); str.Remove(_T('.'));
	m_hlDonate.SetWindowText(str);
	NewGUI_MakeHyperLink(&m_hlDonate);
	m_hlDonate.EnableTooltip(FALSE);
	m_hlDonate.SetNotifyParent(TRUE);
	m_hlDonate.EnableURL(FALSE);

	UpdateData(FALSE);

	return TRUE;
}

LRESULT CAboutDlg::OnXHyperLinkClicked(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);

	if(wParam == IDC_HLINK_HOMEPAGE)
		ShellExecute(NULL, NULL, PWM_HOMEPAGE, NULL, NULL, SW_SHOW);
	else if(wParam == IDC_HLINK_HELPFILE)
		WU_OpenAppHelp(NULL, m_hWnd);
	else if(wParam == IDC_HLINK_LICENSEFILE)
		_OpenLocalFile(PWM_LICENSE_FILE, OLF_OPEN);
	else if(wParam == IDC_HLINK_CREDITS)
		WU_OpenAppHelp(PWM_HELP_CREDITS, m_hWnd);
	else if(wParam == IDC_HLINK_DONATE)
		ShellExecute(NULL, NULL, PWM_URL_DONATE, NULL, NULL, SW_SHOW);
	else return 0;

	OnOK();
	return 0;
}

/////////////////////////////////////////////////////////////////////////////

BOOL CPwSafeDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	SetIcon(m_hIcon, TRUE);
	SetIcon(m_hIcon, FALSE);

	srand((unsigned int)time(NULL));
	KPMI_SetMainDialog(this);

	if(m_bCheckForInstance == TRUE) m_instanceChecker.TrackFirstInstanceRunning();

	m_bWindowsNewLine = TRUE;
	m_bFileOpen = FALSE;
	m_bFileReadOnly = FALSE;
	m_bLocked = FALSE;
	m_bModified = FALSE;
	m_dwOldListParameters = 0;
	m_bWasMaximized = FALSE;
	m_bCachedToolBarUpdate = FALSE;
	m_bCachedPwlistUpdate = FALSE;
	m_bDragging = FALSE;
	m_iDisplayDialog = 0;
	m_iDisplayMenu = 0;
	m_iUIHintsBlocked = 0;
	m_hDraggingGroup = NULL;
	// m_bDraggingEntry = FALSE;
	m_bMenuExit = FALSE;
	m_bBlockPwListUpdate = FALSE;
	m_bRestartApplication = FALSE;
	m_mgr.GetNeverExpireTime(&g_tNeverExpire);
	m_nFileRelockCountdown = -1;
	m_bTANsOnly = FALSE;
	m_dwPwListMode = LVS_REPORT;
	m_nNotesFontSize = 0;

	m_bHashValid = FALSE;
	ZeroMemory(m_aHashOfFile, 32);

	if(m_mHtmlToRtf.size() == 0)
	{
		m_mHtmlToRtf[std_string(HTMLTAG_BOLD_S)] = std_string(_T("\\b "));
		m_mHtmlToRtf[std_string(HTMLTAG_BOLD_E)] = std_string(_T("\\b0 "));
		m_mHtmlToRtf[std_string(HTMLTAG_ITALIC_S)] = std_string(_T("\\i "));
		m_mHtmlToRtf[std_string(HTMLTAG_ITALIC_E)] = std_string(_T("\\i0 "));
		m_mHtmlToRtf[std_string(HTMLTAG_ULINE_S)] = std_string(_T("\\ul "));
		m_mHtmlToRtf[std_string(HTMLTAG_ULINE_E)] = std_string(_T("\\ul0 "));
		m_mHtmlToRtf[std_string(HTMLTAG_STRIKE_S)] = std_string(_T("\\strike "));
		m_mHtmlToRtf[std_string(HTMLTAG_STRIKE_E)] = std_string(_T("\\strike0 "));	
		// _T("<center>") => _T("\\qc ");
		// _T("</center>") => _T("\\ql ");
	}

	m_cList.m_pParentI = this;
	m_cList.m_pbShowColumns = &m_bShowColumn[0];
	m_cGroups.m_pParentI = this;

	m_hArrowCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
	m_hDragLeftRight = AfxGetApp()->LoadStandardCursor(IDC_SIZEWE);
	m_hDragUpDown = AfxGetApp()->LoadStandardCursor(IDC_SIZENS);

	m_menu.LoadMenu(IDR_MAINMENU); // Load the main menu

	// Setup menu style
	m_menu.SetMenuDrawMode(BCMENU_DRAWMODE_XP); // <<<!=>>> BCMENU_DRAWMODE_ORIGINAL
	m_menu.SetSelectDisableMode(FALSE);
	m_menu.SetXPBitmap3D(TRUE);
	m_menu.SetBitmapBackground(RGB(255,0,255));
	m_menu.SetIconSize(16, 16);
	// m_menu.LoadCheckmarkBitmap(IDB_CANCEL, IDB_OK);

	// Insert the group list and entry list menus to the edit menu

	m_popmenu.LoadMenu(IDR_GROUPLIST_MENU);
	BCMenu *pSrc = NewGUI_GetBCMenu(m_popmenu.GetSubMenu(0));
	ASSERT(pSrc != NULL); if(pSrc == NULL) pSrc = &m_popmenu;

	BCMenu *pDest = NewGUI_GetBCMenu(m_menu.GetSubMenu(1));
	ASSERT(pDest != NULL); if(pDest == NULL) pDest = &m_menu;

	UINT i, uState, uID, uLastID = static_cast<UINT>(-1);
	CString str;

	// pDest->AppendMenu(MF_SEPARATOR);
	UINT cItems = static_cast<UINT>(pSrc->GetMenuItemCount());
	for(i = 0; i < cItems; ++i)
	{
		uID = pSrc->GetMenuItemID(i);
		uState = pSrc->GetMenuState(i, MF_BYPOSITION);
		pSrc->GetMenuText(i, str, MF_BYPOSITION);
		if(str == _T("&Rearrange")) continue;
		if(str == _T("&Export Group")) continue;
		if(str == _T("Find in t&his Group...")) continue;
		// if((uID == ID_GROUP_MOVETOP) || (uID == ID_GROUP_MOVEBOTTOM)) continue;
		// if((uID == ID_GROUP_MOVEUP) || (uID == ID_GROUP_MOVEDOWN)) continue;
		if(str == _T("Pr&int Group...")) continue;

		if(uLastID != uID) pDest->AppendMenu(uState, uID, const_cast<LPTSTR>((LPCTSTR)str));
		uLastID = uID;
	}
	m_popmenu.DestroyMenu();

	// pDest->GetMenuText(0, str, MF_BYPOSITION);
	// if(str.GetLength() == 0) pDest->DeleteMenu(0, MF_BYPOSITION);
	if(pDest->GetMenuItemID(0) == 0) pDest->DeleteMenu(0, MF_BYPOSITION);

	m_popmenu.LoadMenu(IDR_PWLIST_MENU);
	pDest = NewGUI_GetBCMenu(m_menu.GetSubMenu(1));
	ASSERT(pDest != NULL); if(pDest == NULL) pDest = &m_menu;
	pSrc = NewGUI_GetBCMenu(m_popmenu.GetSubMenu(0));
	ASSERT(pSrc != NULL); if(pSrc == NULL) pSrc = &m_popmenu;

	// pDest->AppendMenu(MF_SEPARATOR);
	cItems = static_cast<UINT>(pSrc->GetMenuItemCount());
	for(i = 0; i < (cItems - 2); ++i)
	{
		uID = pSrc->GetMenuItemID(i);
		uState = pSrc->GetMenuState(i, MF_BYPOSITION);
		pSrc->GetMenuText(i, str, MF_BYPOSITION);
		if(str == _T("&Rearrange")) continue;
		// if((uID == ID_PWLIST_MOVETOP) || (uID == ID_PWLIST_MOVEBOTTOM)) continue;
		// if((uID == ID_PWLIST_MOVEUP) || (uID == ID_PWLIST_MOVEDOWN)) continue;

		if(uLastID != uID) pDest->AppendMenu(uState, uID, const_cast<LPTSTR>((LPCTSTR)str));
		uLastID = uID;
	}
	m_popmenu.DestroyMenu();

	CPrivateConfigEx cConfig(FALSE);
	TCHAR szTemp[SI_REGSIZE];

	// Load the translation file and cache strings
	CPwSafeApp::LoadTranslationEx(&cConfig);
	g_psztNeverExpires = TRL("Never expires");

	m_bMiniMode = cConfig.GetBool(PWMKEY_MINIMODE, FALSE);
	m_bUnintrusiveMiniMode = cConfig.GetBool(PWMKEY_UNINTRUSIVEMINIMODE, FALSE);

	m_bForceAllowChangeMasterKey = cConfig.GetEnforcedBool(PWMKEY_FORCEALLOWCHANGEMKEY, FALSE, TRUE);
	m_bForceAllowPrinting = cConfig.GetEnforcedBool(PWMKEY_FORCEALLOWPRINTING, FALSE, TRUE);
	m_bForceAllowImport = cConfig.GetEnforcedBool(PWMKEY_FORCEALLOWIMPORT, FALSE, TRUE);
	m_bForceAllowExport = cConfig.GetEnforcedBool(PWMKEY_FORCEALLOWEXPORT, FALSE, TRUE);

	m_bDisallowPrintingPasswords = cConfig.GetBool(PWMKEY_DISALLOWPRINTINGPWS, FALSE);

	cConfig.Get(PWMKEY_DEFAULTATSEQ, szTemp);
	if(szTemp[0] != 0) m_strDefaultAutoTypeSequence = szTemp;
	else m_strDefaultAutoTypeSequence = _T("{USERNAME}{TAB}{PASSWORD}{ENTER}");

	/* cConfig.Get(PWMKEY_PWGEN_OPTIONS, szTemp);
	if(szTemp[0] != 0)
	{
		CString strOptions = szTemp;

		szTemp[0] = 0; szTemp[1] = 0;
		cConfig.Get(PWMKEY_PWGEN_CHARS, szTemp);
		CString strCharSet = szTemp;

		szTemp[0] = 0; szTemp[1] = 0;
		cConfig.Get(PWMKEY_PWGEN_NUMCHARS, szTemp);
		if(szTemp[0] != 0)
			CPwGeneratorDlg::SetOptions(strOptions, strCharSet, (UINT)(_ttoi(szTemp)));
	}
	else CPwGeneratorDlg::SetOptions(CString(_T("11100000001")), CString(_T("")), 16); */

	m_strInitialEnvDir = WU_GetCurrentDirectory();

	cConfig.Get(PWMKEY_LASTDIR, szTemp);
	if(szTemp[0] != 0) WU_SetCurrentDirectory(szTemp);

	CKeySourcesPool::SetEnabled(cConfig.GetBool(PWMKEY_REMEMBERKEYSOURCES, TRUE));
	CKeySourcesPool::Load(&cConfig);

	// cConfig.Get(PWMKEY_CLIPBOARDMETHOD, szTemp);
	// if(szTemp[0] != 0) m_nClipboardMethod = _ttoi(szTemp);
	// else m_nClipboardMethod = CM_TIMED;
	m_nClipboardMethod = CM_TIMED;

	cConfig.Get(PWMKEY_CLIPSECS, szTemp);
	if(szTemp[0] != 0)
	{
		m_dwClipboardSecs = static_cast<DWORD>(_ttol(szTemp));
		if((m_dwClipboardSecs == 0) || (m_dwClipboardSecs == DWORD_MAX))
			m_dwClipboardSecs = 10 + 1;
	}
	else m_dwClipboardSecs = 10 + 1;

	WU_SetConfigBool(WU_CFG_CLIP_NOPERSIST, cConfig.GetBool(
		PWMKEY_CLIPNOPERSIST, TRUE));

	WORD wATHotKeyVK = static_cast<WORD>(_T('A'));
	WORD wATHotKeyMod = (HOTKEYF_CONTROL | HOTKEYF_ALT);
	DWORD dwDefaultATHotKey = ((DWORD)wATHotKeyMod << 16) | (DWORD)wATHotKeyVK;

	cConfig.Get(PWMKEY_AUTOTYPEHOTKEY, szTemp);
	if(szTemp[0] != 0) m_dwATHotKey = static_cast<DWORD>(_ttol(szTemp));
	else m_dwATHotKey = dwDefaultATHotKey;

	cConfig.Get(PWMKEY_DEFAULTEXPIRE, szTemp);
	if(szTemp[0] != 0) m_dwDefaultExpire = static_cast<DWORD>(_ttol(szTemp));
	else m_dwDefaultExpire = 0;

	m_bWindowsNewLine = TRUE; // Assume Windows
	cConfig.Get(PWMKEY_NEWLINE, szTemp);
	if(_tcsicmp(szTemp, _T("Unix")) == 0) m_bWindowsNewLine = FALSE;

	m_bImgButtons = cConfig.GetBool(PWMKEY_IMGBTNS, TRUE);
	NewGUI_SetImgButtons(m_bImgButtons);

	m_bDisableUnsafe = cConfig.GetBool(PWMKEY_DISABLEUNSAFE, FALSE);
	m_bDisableUnsafeAtStart = m_bDisableUnsafe;

	cConfig.Get(PWMKEY_AUTOSORT, szTemp);
	if(szTemp[0] != 0) m_nAutoSort = _ttoi(szTemp);
	else m_nAutoSort = 0;

	m_bUsePuttyForURLs = cConfig.GetBool(PWMKEY_USEPUTTYFORURLS, FALSE);
	m_bSaveOnLATMod = cConfig.GetBool(PWMKEY_SAVEONLATMOD, FALSE);
	m_bEntryGrid = cConfig.GetBool(PWMKEY_ENTRYGRID, FALSE);
	m_bAlwaysOnTop = cConfig.GetBool(PWMKEY_ALWAYSTOP, FALSE);

	m_bShowTitle = cConfig.GetBool(PWMKEY_SHOWTITLE, TRUE);
	m_bShowUserName = cConfig.GetBool(PWMKEY_SHOWUSER, TRUE);
	m_bShowURL = cConfig.GetBool(PWMKEY_SHOWURL, TRUE);
	m_bShowPassword = cConfig.GetBool(PWMKEY_SHOWPASS, TRUE);
	m_bShowNotes = cConfig.GetBool(PWMKEY_SHOWNOTES, TRUE);
	m_bShowCreation = cConfig.GetBool(PWMKEY_SHOWCREATION, FALSE);
	m_bShowLastMod = cConfig.GetBool(PWMKEY_SHOWLASTMOD, FALSE);
	m_bShowLastAccess = cConfig.GetBool(PWMKEY_SHOWLASTACCESS, FALSE);
	m_bShowExpire = cConfig.GetBool(PWMKEY_SHOWEXPIRE, FALSE);
	m_bShowUUID = cConfig.GetBool(PWMKEY_SHOWUUID, FALSE);
	m_bShowAttach = cConfig.GetBool(PWMKEY_SHOWATTACH, FALSE);

	CPwSafeDlg::m_bUseLocalTimeFormat = cConfig.GetBool(PWMKEY_USELOCALTIMEFMT, TRUE);

	m_bPasswordStars = cConfig.GetBool(PWMKEY_HIDESTARS, TRUE);
	m_bUserStars = cConfig.GetBool(PWMKEY_HIDEUSERS, FALSE);

	m_bLockOnMinimize = cConfig.GetBool(PWMKEY_LOCKMIN, FALSE);
	m_bMinimizeOnLock = cConfig.GetBool(PWMKEY_MINLOCK, TRUE);
	m_bMinimizeToTray = cConfig.GetBool(PWMKEY_MINTRAY, FALSE);
	m_bCloseMinimizes = cConfig.GetBool(PWMKEY_CLOSEMIN, FALSE);

	m_bStartMinimized = cConfig.GetBool(PWMKEY_STARTMINIMIZED, FALSE);

	m_bBackupEntries = cConfig.GetBool(PWMKEY_BACKUPENTRIES, TRUE);
	m_bSecureEdits = cConfig.GetBool(PWMKEY_SECUREEDITS, TRUE);
	m_bSingleClickTrayIcon = cConfig.GetBool(PWMKEY_SINGLECLICKTRAY, FALSE);
	m_bShowTrayOnlyIfTrayed = cConfig.GetBool(PWMKEY_SHOWTRAYONLYIFTRAYED, FALSE);
	m_bQuickFindInPasswords = cConfig.GetBool(PWMKEY_QUICKFINDINPWS, FALSE);
	m_bQuickFindIncBackup = cConfig.GetBool(PWMKEY_QUICKFINDINCBK, TRUE);
	m_bQuickFindIncExpired = cConfig.GetBool(PWMKEY_QUICKFINDINCEXP, TRUE);
	m_bDeleteBackupsOnSave = cConfig.GetBool(PWMKEY_DELETEBKONSAVE, FALSE);
	m_bDisableAutoType = cConfig.GetBool(PWMKEY_DISABLEAUTOTYPE, FALSE);
	m_bCopyURLs = cConfig.GetBool(PWMKEY_COPYURLS, FALSE);
	m_bExitInsteadOfLockAT = cConfig.GetBool(PWMKEY_EXITINSTEADLOCK, FALSE);
	m_bAllowSaveIfModifiedOnly = cConfig.GetBool(PWMKEY_ALLOWSAVEIFMODIFIEDONLY, FALSE);
	m_bFocusResAfterQuickFind = cConfig.GetBool(PWMKEY_FOCUSRESAFTERQUICKFIND, FALSE);
	m_bAutoTypeIEFix = cConfig.GetBool(PWMKEY_AUTOTYPEIEFIX, TRUE);
	m_bAutoTypeSameKL = cConfig.GetBool(PWMKEY_AUTOTYPESAMEKEYBLAYOUTS, TRUE);
	m_bSortAutoTypeSelItems = cConfig.GetBool(PWMKEY_SORTAUTOTYPESELITEMS, TRUE);
	m_bAutoTypeNormDashes = cConfig.GetBool(PWMKEY_AUTOTYPENORMDASHES, TRUE);
	m_bDropToBackOnCopy = cConfig.GetBool(PWMKEY_DROPTOBACKONCOPY, FALSE);
	m_bUseTransactedFileWrites = cConfig.GetBool(PWMKEY_TRANSACTEDFILEWRITE, TRUE);

	m_bShowToolBar = cConfig.GetBool(PWMKEY_SHOWTOOLBAR, TRUE);
	m_bShowFullPath = cConfig.GetBool(PWMKEY_SHOWFULLPATH, FALSE);

	m_bSimpleTANView = cConfig.GetBool(PWMKEY_SIMPLETANVIEW, TRUE);
	m_bShowTANIndices = cConfig.GetBool(PWMKEY_SHOWTANINDICES, TRUE);
	m_bDeleteTANsAfterUse = cConfig.GetBool(PWMKEY_DELETETANSAFTERUSE, FALSE);

	m_bCheckForUpdate = cConfig.GetBool(PWMKEY_CHECKFORUPDATE, FALSE);
	m_bCheckForUpdateCfg = cConfig.GetBool(PWMKEY_CHECKFORUPDATECFG, FALSE);

	m_bLockOnWinLock = cConfig.GetBool(PWMKEY_LOCKONWINLOCK, FALSE);
	m_bClearClipOnDbClose = cConfig.GetBool(PWMKEY_CLEARCLIPONDBCLOSE, TRUE);

	m_remoteControl.InitStatic(&m_mgr, this->m_hWnd);
	m_remoteControl.SetAlwaysAllowFullAccess(cConfig.GetBool(PWMKEY_ALWAYSALLOWIPC, FALSE));
	m_remoteControl.EnableRemoteControl(cConfig.GetBool(PWMKEY_ENABLEREMOTECTRL, FALSE));

	WU_SetAppHelpSource((cConfig.GetBool(PWMKEY_USEHELPCENTER, TRUE) == TRUE) ?
		APPHS_ONLINE : APPHS_LOCAL);

	cConfig.Get(PWMKEY_ROWCOLOR, szTemp);
	if(szTemp[0] != 0)
	{
		COLORREF cref = (COLORREF)_ttol(szTemp);
		m_cList.SetRowColorEx(cref);
	}

	cConfig.Get(PWMKEY_AUTOTYPEMETHOD, szTemp);
	if(szTemp[0] != 0) m_nAutoTypeMethod = _ttoi(szTemp);
	else m_nAutoTypeMethod = ATM_DROPBACK;

	cConfig.Get(PWMKEY_URLOVERRIDE, szTemp);
	if(szTemp[0] != 0) m_strURLOverride = szTemp;
	else m_strURLOverride = _T("");

	cConfig.Get(PWMKEY_WNDTITLESUFFIX, szTemp);
	if(szTemp[0] != 0) m_strWindowTitleSuffix = szTemp;
	else m_strWindowTitleSuffix = _T("");

	m_nLockTimeDef = -1;
	cConfig.Get(PWMKEY_LOCKTIMER, szTemp);
	if(szTemp[0] != 0) m_nLockTimeDef = _ttol(szTemp);
	// m_nLockCountdown = m_nLockTimeDef; // NotifyUserActivity at the end of OnInitDialog

	m_bAutoShowExpired = cConfig.GetBool(PWMKEY_AUTOSHOWEXPIRED, FALSE);
	m_bAutoShowExpiredSoon = cConfig.GetBool(PWMKEY_AUTOSHOWEXPIREDS, FALSE);

	m_bEntryView = cConfig.GetBool(PWMKEY_ENTRYVIEW, TRUE);
	m_bColAutoSize = cConfig.GetBool(PWMKEY_COLAUTOSIZE, TRUE);

	cConfig.Get(PWMKEY_BANNERCOLORSTART, szTemp);
	if(szTemp[0] != 0)
		NewGUI_SetBannerColors(static_cast<DWORD>(_ttol(szTemp)), DWORD_MAX, DWORD_MAX);
	cConfig.Get(PWMKEY_BANNERCOLOREND, szTemp);
	if(szTemp[0] != 0)
		NewGUI_SetBannerColors(DWORD_MAX, static_cast<DWORD>(_ttol(szTemp)), DWORD_MAX);
	cConfig.Get(PWMKEY_BANNERCOLORTEXT, szTemp);
	if(szTemp[0] != 0)
		NewGUI_SetBannerColors(DWORD_MAX, DWORD_MAX, static_cast<DWORD>(_ttol(szTemp)));
	cConfig.Get(PWMKEY_BANNERFLIP, szTemp);
	if(szTemp[0] != 0)
		NewGUI_SetBannerParams(cConfig.GetBool(PWMKEY_BANNERFLIP, TRUE) != FALSE);

	// Translate the menu
	BCMenu *pSubMenu = &m_menu;
	const TCHAR *pSuffix = _T("");
	CString strItem;
	int nItem = 0, nSub = 0;
	UINT nID;
	while(true)
	{
		if(pSubMenu->GetMenuString((UINT)nItem, strItem, MF_BYPOSITION) == FALSE) break;
		pSuffix = _GetCmdAccelExt(strItem);
		CString strNew = TRL_VAR(strItem);
		if(_tcslen(pSuffix) != 0) { strNew += _T("\t"); strNew += pSuffix; }
		nID = pSubMenu->GetMenuItemID(nItem);
		if(pSubMenu->ModifyMenu(nItem, MF_BYPOSITION | MF_STRING, nID, strNew) == FALSE) { ASSERT(FALSE); }
		++nItem;
	}
	pSubMenu = NewGUI_GetBCMenu(m_menu.GetSubMenu(nSub));
	while(true)
	{
		_TranslateMenu(pSubMenu, TRUE, &m_bCopyURLs);

		pSubMenu = NewGUI_GetBCMenu(m_menu.GetSubMenu(nSub));
		++nSub;
		if(pSubMenu == NULL) break;
	}

	pSubMenu = NewGUI_GetBCMenu(m_menu.GetSubMenu(TRL("&Tools")));
	ASSERT(pSubMenu != NULL);
	if(pSubMenu != NULL) m_uOriginalExtrasMenuItemCount = pSubMenu->GetMenuItemCount();
	else m_uOriginalExtrasMenuItemCount = MENU_EXTRAS_ITEMCOUNT;

	m_menu.LoadToolbar(IDR_INFOICONS, IDB_INFOICONS_EX);

	m_bMenu = SetMenu(&m_menu); ASSERT(m_bMenu == TRUE);

	RebuildContextMenus();

	g_nIndicatorWidths[0] = NewGUI_Scale(g_nIndicatorWidths[0], this);
	g_nIndicatorWidths[1] = NewGUI_Scale(g_nIndicatorWidths[1], this);

	RECT rectClient;
	RECT rectSB;
	GetClientRect(&rectClient);
	rectSB.top = rectClient.bottom - rectClient.top - 8;
	rectSB.bottom = rectClient.bottom - rectClient.top;
	rectSB.left = 0;
	rectSB.right = rectClient.right - rectClient.left;
	m_sbStatus.Create(WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP | SBT_TOOLTIPS,
		rectSB, this, AFX_IDW_STATUS_BAR);
	// m_sbStatus.SetSimple(TRUE);
	m_sbStatus.SetParts(3, g_nIndicatorWidths);
	m_sbStatus.SetSimple(FALSE);
	::SendMessage(m_sbStatus.m_hWnd, SB_SETTIPTEXT, 0, (LPARAM)TRL("Total number of groups / entries"));
	::SendMessage(m_sbStatus.m_hWnd, SB_SETTIPTEXT, 1, (LPARAM)TRL("Number of selected entries (number of entries in the list)"));
	::SendMessage(m_sbStatus.m_hWnd, SB_SETTIPTEXT, 2, (LPARAM)TRL("Status"));
	m_sbStatus.SetText(_T(""), 0, 0); m_sbStatus.SetText(_T(""), 1, 0); m_sbStatus.SetText(_T(""), 2, 0);
	SetStatusTextEx(TRL("Ready."));

	// m_ilIcons.Create(CPwSafeApp::GetClientIconsResourceID(), 16, 1, RGB(255,0,255)); // purple is transparent
	CPwSafeApp::CreateHiColorImageList(&m_ilIcons, IDB_CLIENTICONS_EX, 16);
	m_cList.SetImageList(&m_ilIcons, LVSIL_SMALL);
	m_cGroups.SetImageList(&m_ilIcons, TVSIL_NORMAL);

	m_bShowColumn[0] = m_bShowTitle; m_bShowColumn[1] = m_bShowUserName;
	m_bShowColumn[2] = m_bShowURL; m_bShowColumn[3] = m_bShowPassword;
	m_bShowColumn[4] = m_bShowNotes; m_bShowColumn[5] = m_bShowCreation;
	m_bShowColumn[6] = m_bShowLastMod; m_bShowColumn[7] = m_bShowLastAccess;
	m_bShowColumn[8] = m_bShowExpire; m_bShowColumn[9] = m_bShowUUID;
	m_bShowColumn[10] = m_bShowAttach;

	if(m_bShowTitle == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_TITLE, MF_BYCOMMAND | uState);
	if(m_bShowUserName == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_USERNAME, MF_BYCOMMAND | uState);
	if(m_bShowURL == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_URL, MF_BYCOMMAND | uState);
	if(m_bShowPassword == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_PASSWORD, MF_BYCOMMAND | uState);
	if(m_bShowNotes == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_NOTES, MF_BYCOMMAND | uState);
	if(m_bShowCreation == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_CREATION, MF_BYCOMMAND | uState);
	if(m_bShowLastMod == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_LASTMOD, MF_BYCOMMAND | uState);
	if(m_bShowLastAccess == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_LASTACCESS, MF_BYCOMMAND | uState);
	if(m_bShowExpire == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_EXPIRE, MF_BYCOMMAND | uState);
	if(m_bShowUUID == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_UUID, MF_BYCOMMAND | uState);
	if(m_bShowAttach == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_ATTACH, MF_BYCOMMAND | uState);
	if(m_bPasswordStars == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_HIDESTARS, MF_BYCOMMAND | uState);
	if(m_bUserStars == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_HIDEUSERS, MF_BYCOMMAND | uState);
	if(m_bAlwaysOnTop == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_ALWAYSONTOP, MF_BYCOMMAND | uState);

	if(m_bShowToolBar == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_SHOWTOOLBAR, MF_BYCOMMAND | uState);
	if(m_bEntryView == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_ENTRYVIEW, MF_BYCOMMAND | uState);

	if(m_bSimpleTANView == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_SIMPLETANVIEW, MF_BYCOMMAND | uState);
	if(m_bShowTANIndices == TRUE) uState = MF_CHECKED; else uState = MF_UNCHECKED;
	m_menu.CheckMenuItem(ID_VIEW_SHOWTANINDICES, MF_BYCOMMAND | uState);

	_CalcColumnSizes();
	// Windows computes the sizes wrong the first time because there aren't
	// any 16x16 icons in the list yet
	m_cList.InsertColumn(0, TRL("Title"), LVCFMT_LEFT, 10 - 4, 0);
	m_cList.InsertColumn(1, TRL("User Name"), LVCFMT_LEFT, 10 - 4, 1);
	m_cList.InsertColumn(2, TRL("URL"), LVCFMT_LEFT, 10 - 3, 2);
	m_cList.InsertColumn(3, TRL("Password"), LVCFMT_LEFT, 10 - 3, 3);
	m_cList.InsertColumn(4, TRL("Notes"), LVCFMT_LEFT, 10 - 3, 4);
	m_cList.InsertColumn(5, TRL("Creation Time"), LVCFMT_LEFT, 10 - 3, 4);
	m_cList.InsertColumn(6, TRL("Last Modification"), LVCFMT_LEFT, 10 - 3, 4);
	m_cList.InsertColumn(7, TRL("Last Access"), LVCFMT_LEFT, 10 - 3, 4);
	m_cList.InsertColumn(8, TRL("Expires"), LVCFMT_LEFT, 10 - 3, 4);
	m_cList.InsertColumn(9, TRL("UUID"), LVCFMT_LEFT, 10 - 3, 4);
	m_cList.InsertColumn(10, TRL("Attachment"), LVCFMT_LEFT, 10 - 3, 4);

	ASSERT(LVM_SETEXTENDEDLISTVIEWSTYLE == (0x1000 + 54));
	_SetListParameters();

	cConfig.Get(PWMKEY_LISTFONT, szTemp);
	if(szTemp[0] == 0) _tcscpy_s(szTemp, _T("MS Shell Dlg;8,0000"));
	_ParseSpecAndSetFont(szTemp, false);

	cConfig.Get(PWMKEY_NOTESFONT, szTemp);
	if(szTemp[0] != 0) _ParseSpecAndSetFont(szTemp, true);

	const CString strNotesRegex = CString(PWMKEY_NOTESREGEX) + _T("%d");
	const CString strNotesFmt = CString(PWMKEY_NOTESFORMAT) + _T("%d");
	for(int iRegex = 0; ; ++iRegex)
	{
		CString strKey;
		strKey.Format(strNotesRegex, iRegex);

		cConfig.Get(strKey, szTemp);
		if(szTemp[0] == 0) break;

		m_lNotesRegex.AddTail(szTemp);

		strKey.Format(strNotesFmt, iRegex);
		cConfig.Get(strKey, szTemp);
		m_lNotesFormat.AddTail(szTemp);
	}

	cConfig.Get(PWMKEY_PASSWORDFONT, szTemp);
	m_strPasswordFontSpec = szTemp;
	CFontUtil::SetPasswordFont(m_strPasswordFontSpec, m_hWnd);

	// int nColumnWidth;
	// RECT rect;
	// m_cGroups.GetClientRect(&rect);
	// nColumnWidth = rect.right - rect.left - GetSystemMetrics(SM_CXVSCROLL);
	// nColumnWidth -= 8;
	// m_cGroups.InsertColumn(0, TRL("Group"), LVCFMT_LEFT, nColumnWidth, 0);

	// m_cGroups.PostMessage(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_SI_MENU | LVS_EX_INFOTIP);
	m_cGroups.ModifyStyle(0, TVS_TRACKSELECT, 0);

	const UINT32 ulTest = TestCryptoImpl();
	if(ulTest != 0)
	{
		CString strTCI = TRL("The following self-tests failed:");
		strTCI += _T("\r\n");

		if((ulTest & TI_ERR_SHAVAR32) != 0)
			{ strTCI += TRL("- SHA 32-bit variables"); strTCI += _T("\r\n"); }
		if((ulTest & TI_ERR_SHAVAR64) != 0)
			{ strTCI += TRL("- SHA 64-bit variables"); strTCI += _T("\r\n"); }
		if((ulTest & TI_ERR_SHACMP256) != 0)
			{ strTCI += TRL("- SHA-256 test vector(s)"); strTCI += _T("\r\n"); }
		if((ulTest & TI_ERR_SHACMP512) != 0)
			{ strTCI += TRL("- SHA-512 test vector(s)"); strTCI += _T("\r\n"); }
		if((ulTest & TI_ERR_RIJNDAEL_ENCRYPT) != 0)
			{ strTCI += TRL("- Rijndael encryption"); strTCI += _T("\r\n"); }
		if((ulTest & TI_ERR_RIJNDAEL_DECRYPT) != 0)
			{ strTCI += TRL("- Rijndael decryption"); strTCI += _T("\r\n"); }
		if((ulTest & TI_ERR_ARCFOUR_CRYPT) != 0)
			{ strTCI += TRL("- Arcfour crypto routine"); strTCI += _T("\r\n"); }
		if((ulTest & TI_ERR_TWOFISH) != 0)
			{ strTCI += TRL("- Twofish algorithm"); strTCI += _T("\r\n"); }

		strTCI += _T("\r\n");
		strTCI += TRL("The program will exit now.");
		MessageBox(strTCI, TRL("Self-Test(s) Failed"), MB_OK | MB_ICONWARNING);
		OnCancel();
	}

	m_strFile.Empty(); m_strFileAbsolute.Empty();
	m_cList.EnableWindow(FALSE);
	m_cGroups.EnableWindow(FALSE);

	// Initialize the xorshift pseudo-random number generator
	const DWORD dwSkip = ((timeGetTime() >> 3) & 0x03FF); // Shift out 8 ms precision
	for(DWORD dwXrPos = 0; dwXrPos < dwSkip; ++dwXrPos) randXorShift();

	// CRestartManagerEx::Register();

	// _UpdateTitleBar(); // Updated by _UpdateToolBar()

	ProcessResize();
	UpdateGroupList();
	UpdatePasswordList();

	CPopularPasswords::AddResUTF8(MAKEINTRESOURCE(IDR_MOSTPOPULARPWS), _T("KPDATA"));
	ASSERT(CPopularPasswords::IsPopular(L"abracadabra", NULL));
	ASSERT(!CPopularPasswords::IsPopular(L"c658ea118c2d4e1da782d79710a99b4b", NULL));

	m_bTimer = TRUE;
	m_nClipboardCountdown = -1;
	SetTimer(APPWND_TIMER_ID, 1000, NULL);
	SetTimer(APPWND_TIMER_ID_UPDATER, 500, NULL);

	m_bRememberLast = cConfig.GetBool(PWMKEY_REMEMBERLAST, TRUE);

	m_strLastDb.Empty();

	m_bOpenLastDb = cConfig.GetBool(PWMKEY_OPENLASTB, TRUE);
	m_bAutoSaveDb = cConfig.GetBool(PWMKEY_AUTOSAVEB, FALSE);

	m_reEntryView.InitEx(false);
	m_reEntryView.SetEventMask(ENM_MOUSEEVENTS | ENM_LINK);
	m_reEntryView.SendMessage(EM_AUTOURLDETECT, TRUE, 0);
	m_reEntryView.SetBackgroundColor(FALSE, GetSysColor(COLOR_3DFACE));

	/* LVBKIMAGE lvbk;
	TCHAR szMe[MAX_PATH * 2];
	GetModuleFileName(NULL, szMe, MAX_PATH * 2);
	CString strImagePath;
	strImagePath.Format(_T("res://%s/#2/#199"), szMe);

	LPCTSTR lpImage = strImagePath.LockBuffer();
	ZeroMemory(&lvbk, sizeof(LVBKIMAGE));
	lvbk.cchImageMax = _tcslen(lpImage);
	lvbk.pszImage = (LPTSTR)lpImage;
	lvbk.ulFlags = LVBKIF_SOURCE_URL | LVBKIF_STYLE_NORMAL;
	lvbk.xOffsetPercent = 90;
	lvbk.yOffsetPercent = 90;
	ListView_SetBkImage(m_cList.m_hWnd, &lvbk);
	strImagePath.UnlockBuffer(); */

	// m_hTrayIconNormal = AfxGetApp()->LoadIcon(IDI_UNLOCKED);
	// m_hTrayIconLocked = AfxGetApp()->LoadIcon(IDI_LOCKED);
	const int dxSmall = GetSystemMetrics(SM_CXSMICON);
	const int dySmall = GetSystemMetrics(SM_CYSMICON);
	m_hTrayIconNormal = (HICON)LoadImage(AfxGetInstanceHandle(),
		MAKEINTRESOURCE(IDI_UNLOCKED), IMAGE_ICON, dxSmall, dySmall, 0);
	m_hTrayIconLocked = (HICON)LoadImage(AfxGetInstanceHandle(),
		MAKEINTRESOURCE(IDI_LOCKED), IMAGE_ICON, dxSmall, dySmall, 0);

	m_hLockOverlayIcon = AfxGetApp()->LoadIcon(IDI_LOCK_OVERLAY);

	CMsgRelayWnd::SetRelayTarget(this->m_hWnd);
	CMsgRelayWnd::AddRelayedMessage(WM_QUERYENDSESSION);
	CMsgRelayWnd::AddRelayedMessage(WM_ENDSESSION);
	CMsgRelayWnd::AddRelayedMessage(WM_COPYDATA);
	CMsgRelayWnd::AddRelayedMessage(WM_WTSSESSION_CHANGE);
	CMsgRelayWnd::AddRelayedMessage(WM_DWMSENDICONICTHUMBNAIL);
	CMsgRelayWnd::AddRelayedMessage(WM_DWMSENDICONICLIVEPREVIEWBITMAP);
	CMsgRelayWnd::AddRelayedMessage(WM_REG_TASKBARCREATED);
	CMsgRelayWnd::AddRelayedMessage(WM_REG_TASKBARBUTTONCREATED);
	CMsgRelayWnd::AddRelayedMessage(WM_REG_PROCESSMAILSLOT);
	CMsgRelayWnd::AddRelayedMessage(WM_REG_KEEPASSCONTROL);

	VERIFY(m_systray.Create(this, WM_MY_SYSTRAY_NOTIFY, PWM_PRODUCT_NAME_SHORT,
		m_hTrayIconNormal, IDR_SYSTRAY_MENU, m_bShowTrayOnlyIfTrayed,
		NULL, NULL, NIIF_NONE, 0));
	m_systray.SetMenuDefaultItem(0, TRUE);
	// m_systray.MoveToRight();
	_UpdateTrayIcon(true); // ShowIcon or HideIcon

	for(DWORD ttc = 0; ttc < 11; ++ttc) m_aHeaderOrder[ttc] = ttc;
	m_aHeaderOrder[2] = 3; m_aHeaderOrder[3] = 2;
	cConfig.Get(PWMKEY_HEADERORDER, szTemp);
	if(szTemp[0] != 0) str2ar(szTemp, m_aHeaderOrder, 11);
	NewGUI_SetHeaderOrder(m_cList.m_hWnd, m_aHeaderOrder, 11);

	m_pThemeHelper = new CThemeHelperST();
	NewGUI_SetThemeHelper((void *)m_pThemeHelper);

	NewGUI_ToolBarButton(&m_btnTbNew, IDB_TB_NEW, IDB_TB_NEW);
	NewGUI_ToolBarButton(&m_btnTbOpen, IDB_TB_OPEN, IDB_TB_OPEN);
	NewGUI_ToolBarButton(&m_btnTbSave, IDB_TB_SAVE, IDB_TB_SAVE);
	NewGUI_ToolBarButton(&m_btnTbAddEntry, IDB_TB_ADDENTRY, IDB_TB_ADDENTRY);
	NewGUI_ToolBarButton(&m_btnTbEditEntry, IDB_TB_EDITENTRY, IDB_TB_EDITENTRY);
	NewGUI_ToolBarButton(&m_btnTbDeleteEntry, IDB_TB_DELETEENTRY, IDB_TB_DELETEENTRY);
	NewGUI_ToolBarButton(&m_btnTbCopyPw, IDB_TB_COPYPW, IDB_TB_COPYPW);
	NewGUI_ToolBarButton(&m_btnTbCopyUser, IDB_TB_COPYUSER, IDB_TB_COPYUSER);
	NewGUI_ToolBarButton(&m_btnTbFind, IDB_TB_FIND, IDB_TB_FIND);
	NewGUI_ToolBarButton(&m_btnTbLock, IDB_TB_LOCK, IDB_TB_LOCK);
	NewGUI_ToolBarButton(&m_btnTbAbout, IDB_TB_ABOUT, IDB_TB_ABOUT);

	_ShowToolBar(m_bShowToolBar);

	m_hAccel = LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ACCEL_MAIN));
	ASSERT(m_hAccel != NULL);

	// VERIFY(m_tipN.Create(this, 0));
	// m_tipN.AddTool(&m_btnTbLock, _T(" "));
	// m_btnTbLock.ActivateTooltip(FALSE); // Prevent duplicate tooltips
	// m_tipN.Activate(TRUE);

	VERIFY(m_tipB.Create(this, TTS_BALLOON));
	CString strQFHint = TRL("Type to search the database");
	strQFHint += _T(" (");
	strQFHint += TRL("Ctrl");
	strQFHint += _T("+E)");
	m_tipB.AddTool(&m_cQuickFind, strQFHint);
	m_tipB.Activate(TRUE);

	NewGUI_SetCueBanner_CB(m_cQuickFind.m_hWnd, TRL("Search..."));

	m_lSplitterPosHoriz = NewGUI_Scale(GUI_GROUPLIST_EXT + 1, this);
	m_lSplitterPosVert = NewGUI_Scale((rectClient.bottom - rectClient.top -
		PWS_DEFAULT_SPLITTER_Y) / 4, this);
	ASSERT(m_lSplitterPosVert > 0);

	int px, py, dx, dy; // Restore window position
	cConfig.Get(PWMKEY_WINDOWPX, szTemp);
	if(szTemp[0] != 0) px = _ttoi(szTemp); else px = -1;
	cConfig.Get(PWMKEY_WINDOWPY, szTemp);
	if(szTemp[0] != 0) py = _ttoi(szTemp); else py = -1;
	cConfig.Get(PWMKEY_WINDOWDX, szTemp);
	if(szTemp[0] != 0) dx = _ttoi(szTemp); else dx = -1;
	cConfig.Get(PWMKEY_WINDOWDY, szTemp);
	if(szTemp[0] != 0) dy = _ttoi(szTemp); else dy = -1;

	const int pxVirtScreen = GetSystemMetrics(SM_XVIRTUALSCREEN);
	const int cxVirtScreen = GetSystemMetrics(SM_CXVIRTUALSCREEN);
	const int pyVirtScreen = GetSystemMetrics(SM_YVIRTUALSCREEN);
	const int cyVirtScreen = GetSystemMetrics(SM_CYVIRTUALSCREEN);

	if((px == 3000) && (py == 3000))
	{
		px = pxVirtScreen;
		py = pyVirtScreen;
	}

	if(px >= (pxVirtScreen + cxVirtScreen)) px = pxVirtScreen;
	if(py >= (pyVirtScreen + cyVirtScreen)) py = pyVirtScreen;

	if((px != -1) && (py != -1) && (dx != -1) && (dy != -1))
	{
		if((px + dx) <= pxVirtScreen) px = pxVirtScreen;
		if((py + dy) <= pyVirtScreen) py = pyVirtScreen;

		SetWindowPos(&wndNoTopMost, px, py, dx, dy, SWP_NOOWNERZORDER | SWP_NOZORDER);
	}

	for(DWORD ulCol = 0; ulCol < 11; ++ulCol) m_nColumnWidths[ulCol] = 0;

	// Restore column sizes
	cConfig.Get(PWMKEY_COLWIDTH0, szTemp);
	if(szTemp[0] != 0) m_nColumnWidths[0] = _ttoi(szTemp);
	cConfig.Get(PWMKEY_COLWIDTH1, szTemp);
	if(szTemp[0] != 0) m_nColumnWidths[1] = _ttoi(szTemp);
	cConfig.Get(PWMKEY_COLWIDTH2, szTemp);
	if(szTemp[0] != 0) m_nColumnWidths[2] = _ttoi(szTemp);
	cConfig.Get(PWMKEY_COLWIDTH3, szTemp);
	if(szTemp[0] != 0) m_nColumnWidths[3] = _ttoi(szTemp);
	cConfig.Get(PWMKEY_COLWIDTH4, szTemp);
	if(szTemp[0] != 0) m_nColumnWidths[4] = _ttoi(szTemp);
	cConfig.Get(PWMKEY_COLWIDTH5, szTemp);
	if(szTemp[0] != 0) m_nColumnWidths[5] = _ttoi(szTemp);
	cConfig.Get(PWMKEY_COLWIDTH6, szTemp);
	if(szTemp[0] != 0) m_nColumnWidths[6] = _ttoi(szTemp);
	cConfig.Get(PWMKEY_COLWIDTH7, szTemp);
	if(szTemp[0] != 0) m_nColumnWidths[7] = _ttoi(szTemp);
	cConfig.Get(PWMKEY_COLWIDTH8, szTemp);
	if(szTemp[0] != 0) m_nColumnWidths[8] = _ttoi(szTemp);
	cConfig.Get(PWMKEY_COLWIDTH9, szTemp);
	if(szTemp[0] != 0) m_nColumnWidths[9] = _ttoi(szTemp);
	cConfig.Get(PWMKEY_COLWIDTH10, szTemp);
	if(szTemp[0] != 0) m_nColumnWidths[10] = _ttoi(szTemp);

	cConfig.Get(PWMKEY_SPLITTERX, szTemp);
	if(szTemp[0] != 0) m_lSplitterPosHoriz = _ttol(szTemp);
	else m_lSplitterPosHoriz = NewGUI_Scale(GUI_GROUPLIST_EXT + 1, this);

	cConfig.Get(PWMKEY_SPLITTERY, szTemp);
	if(szTemp[0] != 0) m_lSplitterPosVert = _ttol(szTemp);
	// else m_lSplitterPosVert = PWS_DEFAULT_SPLITTER_Y; // Set above already

	// if(m_bAlwaysOnTop == TRUE)
	//	SetWindowPos(&wndTopMost, 0, 0, 0, 0,
	//		SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);

	VERIFY(m_cGroups.InitDropHandler());

	cConfig.Get(PWMKEY_GENPROFILEAUTO, szTemp);
	if(szTemp[0] != 0)
	{
		std::basic_string<TCHAR> strAutoProfile = szTemp;
		PwgStringToProfile(strAutoProfile, &CPwSafeDlg::m_pgsAutoProfile);
	}
	else PwgGetDefaultProfile(&CPwSafeDlg::m_pgsAutoProfile);

	// Support legacy auto-generation flag
	if((cConfig.GetBool(PWMKEY_AUTOPWGEN, TRUE) == FALSE) &&
		(CPwSafeDlg::m_bMiniMode == FALSE))
	{
		CPwSafeDlg::m_pgsAutoProfile.dwLength = 0;
	}

	// KP_APP_INFO kpai;
	// ZeroMemory(&kpai, sizeof(KP_APP_INFO));
	// kpai.dwAppVersion = PWM_VERSION_DW;
	// kpai.hwndMain = this->m_hWnd;
	// // kpai.pApp = (void *)AfxGetApp();
	// kpai.pMainDlg = (void *)this;
	// kpai.pPwMgr = (void *)&m_mgr;
	// kpai.lpKPCall = KP_Call;
	// kpai.lpKPQuery = KP_Query;

	// CPluginManager::Instance().SetAppInfo(&kpai);
	VERIFY(CPluginManager::Instance().SetDirectCommandRange(WM_PLUGINS_FIRST,
		WM_PLUGINS_LAST));

	// CString strPluginKey;
	// int jj = 0;
	// while(1)
	// {
	//	strPluginKey.Format(_T("KeePlugin_%d"), jj);
	//	cConfig.Get(strPluginKey, szTemp);
	//	if(szTemp[0] == 0) break;
	//	if(_tcscmp(szTemp, _T("0")) == 0) break;
	//	if(szTemp[0] != _T('-'))
	//	{
	//		std_string strPluginName = szTemp;
	//		CPluginManager::Instance().m_vKnownNames.push_back(strPluginName);
	//	}
	//	++jj;
	// }

	// std_string strBase = Executable::instance().getPathOnly();
	// VERIFY(CPluginManager::Instance().AddAllPlugins(strBase.c_str(), _T("*.dll"), TRUE));
	// CPluginManager::Instance().m_vKnownNames.clear();

	// CString strPluginKey;
	// jj = 0;
	// while(1)
	// {
	//	strPluginKey.Format(_T("KeePlugin_%d"), jj);
	//	cConfig.Get(strPluginKey, szTemp);
	//	if(szTemp[0] == 0) break;
	//	if(_tcscmp(szTemp, _T("0")) == 0) break;
	//	if(szTemp[0] == _T('-'))
	//		CPluginManager::Instance().EnablePluginByStr(&szTemp[1], FALSE);
	//	else
	//		CPluginManager::Instance().EnablePluginByStr(szTemp, TRUE);
	//	jj++;
	// }

	// Plugins might access the command line parameters, so restore the
	// initial working directory now
	const std::basic_string<TCHAR> strCurDir = WU_GetCurrentDirectory();
	WU_SetCurrentDirectory(m_strInitialEnvDir.c_str());

	VERIFY(CPluginManager::Instance().LoadAllPlugins());
	BuildPluginMenu();
	_CallPlugins(KPM_DELAYED_INIT, 0, 0);
	_CallPlugins(KPM_READ_COMMAND_ARGS, 0, 0);

	WU_SetCurrentDirectory(strCurDir.c_str());

	CString strLoadedIniLastDb;
	BOOL bTriedOpenDb = FALSE;
	if(_ParseCommandLine() == FALSE)
	{
		if((m_bOpenLastDb == TRUE) && (m_bRememberLast == TRUE))
		{
			cConfig.Get(PWMKEY_LASTDB, szTemp);
			if(szTemp[0] != 0)
			{
				std_string strTemp = WU_ExpandEnvironmentVars(szTemp);
				strLoadedIniLastDb = strTemp.c_str();

				if(WU_IsAbsolutePath(strTemp.c_str()) == FALSE)
					strTemp = Executable::instance().getPathOnly() + strTemp;

				if(_FileAccessible(strTemp.c_str()) == TRUE)
				{
					_OpenDatabase(NULL, strTemp.c_str(), NULL, NULL, m_bStartMinimized, NULL, FALSE);
					bTriedOpenDb = TRUE;
				}
			}
		}
	}

	m_bIgnoreSizeEvent = FALSE;

	UpdateAutoSortMenuItems();
	_UpdateToolBar(TRUE);
	ProcessResize();

	if(m_bDisableAutoType == FALSE)
		RegisterGlobalHotKey(HOTKEYID_AUTOTYPE, m_dwATHotKey, false);

	m_bRegisterRestoreHotKey = cConfig.GetBool(PWMKEY_RESTOREHOTKEY, TRUE);
	RegisterRestoreHotKey(m_bRegisterRestoreHotKey);

	// PostMessage(WM_NULL, 0, 0);

	m_uACP = GetACP();

	VERIFY(m_sessionNotify.Register(this->m_hWnd));

	CUpdateCheckEx::EnsureConfigured(&m_bCheckForUpdate, &m_bCheckForUpdateCfg,
		this->m_hWnd, AfxGetInstanceHandle());
	if(m_bCheckForUpdate != FALSE)
		CUpdateCheckEx::Check(TRUE, this->m_hWnd, &m_ilIcons, TRUE);

	UpdateWindow();
	RedrawWindow();

	m_bWasMaximized = cConfig.GetBool(PWMKEY_WINSTATE_MAX, FALSE);
	if(m_bWasMaximized == TRUE) ShowWindow(SW_SHOWMAXIMIZED);

	if(m_bAlwaysOnTop == TRUE)
		SetWindowPos(&wndTopMost, 0, 0, 0, 0,
			SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);

	if((m_bStartMinimized == TRUE) || CmdArgs::instance().minimizeInEffect())
		SetViewHideState(FALSE, FALSE);

	_CallPlugins(KPM_WND_INIT_POST, 0, 0);
	if(m_iDisplayDialog != 0) { ASSERT(FALSE); m_iDisplayDialog = 0; }

	SetToMiniModeIfEnabled(&m_menu, TRUE, TRUE);
	if((m_bMiniMode == TRUE) && (m_bFileOpen == FALSE) && (m_bLocked == FALSE))
	{
		if((bTriedOpenDb == FALSE) && (m_bUnintrusiveMiniMode == FALSE))
		{
			OnFileNew();

			if(m_bFileOpen == FALSE)
			{
				OnFileExit();
				return FALSE;
			}
			else if(m_strFile.GetLength() == 0)
				_GetNewDbFileInUserDir(strLoadedIniLastDb, cConfig);
			else { ASSERT(FALSE); }

			SetViewHideState(TRUE, FALSE); // Always show window for newly created DB
		}

		_UpdateToolBar();
	}

	// m_btnTbAbout.SetFocus();
	// if((m_bFileOpen == FALSE) && (m_cQuickFind.IsWindowEnabled() == TRUE)) m_cQuickFind.SetFocus();
	// else m_btnTbAbout.SetFocus();
	_SetDefaultFocus();

	if((bTriedOpenDb == FALSE) && (m_bStartMinimized == TRUE))
	{
		if((m_bMiniMode == TRUE) && (m_bUnintrusiveMiniMode == FALSE))
		{
			SetViewHideState(TRUE, FALSE); // Show window with newly created DB
		}
	}

	NotifyUserActivity(); // Initialize the locking timeout
	return FALSE; // We set the focus ourselves
}

void CPwSafeDlg::_SetDefaultFocus()
{
	if(m_cQuickFind.IsWindowEnabled() == TRUE) m_cQuickFind.SetFocus();
	else m_btnTbAbout.SetFocus();
}

void CPwSafeDlg::_TranslateMenu(BCMenu *pBCMenu, BOOL bAppendSuffix, BOOL *pFlags)
{
	CString strItem, strNew;
	UINT nItem = 0;
	BCMenu *pNext;
	const TCHAR *pSuffix = _T("");
	BOOL bDefaultFlag = FALSE;

	ASSERT(pBCMenu != NULL); if(pBCMenu == NULL) return;

	if(pFlags == NULL) pFlags = &bDefaultFlag;

	while(true)
	{
		if(pBCMenu->GetMenuText((UINT)nItem, strItem, MF_BYPOSITION) == FALSE) break;
		if(bAppendSuffix == TRUE) pSuffix = _GetCmdAccelExt(strItem);

		CString strSource = strItem;

		if((strItem == _T("&Import")) || (strItem == _T("&Export")) ||
			(strItem == _T("Show &Columns")) || (strItem == _T("&Rearrange")) ||
			(strItem == _T("Auto-&Sort Entry List")) || (strItem == _T("TAN View &Options")) ||
			(strItem == _T("&Export Group")) || (strItem == _T("Insert Field Reference")))
		{
			pNext = pBCMenu->GetSubBCMenu(const_cast<LPTSTR>((LPCTSTR)strItem));
			if(pNext != NULL) _TranslateMenu(pNext, TRUE, pFlags);
		}

		// Replace URL menu item if required, pFlags[0] must be a pointer to m_bCopyURLs
		if(*pFlags == TRUE)
			if(strItem == _T("Open &URL(s)"))
				strItem = _T("Copy &URL to Clipboard");

		if(strItem.GetLength() != 0) strNew = TRL_VAR(strItem);
		else strNew = _T("");

		if(_tcslen(pSuffix) != 0) { strNew += _T("\t"); strNew += pSuffix; }
		if(pBCMenu->SetMenuText(nItem, strNew, MF_BYPOSITION) == FALSE) { ASSERT(FALSE); }

		++nItem;

		if(m_bMiniMode == TRUE)
		{
			if(strSource == _T("&Import"))
			{
				--nItem;
				if(m_bForceAllowImport == FALSE)
					pBCMenu->DeleteMenu(nItem, MF_BYPOSITION);
				else ++nItem;
			}
			else if(strSource == _T("&Export"))
			{
				--nItem;
				if(m_bForceAllowExport == FALSE)
				{
					pBCMenu->DeleteMenu(nItem, MF_BYPOSITION); // Export

					if(m_bForceAllowImport == FALSE)
						pBCMenu->DeleteMenu(nItem, MF_BYPOSITION); // Separator
				}
				else ++nItem;
			}
			else if(strSource == _T("TAN View &Options"))
			{
				--nItem;
				pBCMenu->DeleteMenu(nItem, MF_BYPOSITION);
			}
		}
	}
}

BOOL CPwSafeDlg::_ParseCommandLine()
{
	/* CString str;
	LPCTSTR lpPassword = NULL;
	LPCTSTR lpKeyFile = NULL;
	LPCTSTR lpPreSelectPath = NULL;

	if(CPwSafeApp::ParseCurrentCommandLine(&str, &lpPassword, &lpKeyFile, &lpPreSelectPath) == FALSE) return FALSE;

	if(_FileAccessible(str) == TRUE)
		_OpenDatabase(NULL, str, lpPassword, lpKeyFile, FALSE, lpPreSelectPath, FALSE); */

	// Ensure that relative paths on the command line are evaluated relatively to
	// the initial working directory instead of the last used directory that
	// KeePass remembers; FR 3059831
	const std::basic_string<TCHAR> strCurDir = WU_GetCurrentDirectory();
	WU_SetCurrentDirectory(m_strInitialEnvDir.c_str());
	CmdArgs::instance().getDatabase();
	WU_SetCurrentDirectory(strCurDir.c_str());

	m_bFileReadOnly = (CmdArgs::instance().readonlyIsInEffect() ? TRUE : FALSE);

	const FullPathName& fpnDatabase = CmdArgs::instance().getDatabase();
	const bool bFileNameExists = (fpnDatabase.getState() == FullPathName::PATH_AND_FILENAME);
	if(!bFileNameExists) return FALSE;

	const TCHAR* const lpFileName = fpnDatabase.getFullPathName().c_str();
	if(_FileAccessible(lpFileName) == FALSE)
	{
		std_string strMsg = TRL("File access error: failed to open file in read mode");
		strMsg += _T("!\r\n\r\n");
		strMsg += lpFileName;

		MessageBox(strMsg.c_str(), PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);

		return TRUE;
	}

	m_bInitialCmdLineFile = TRUE;

	std_string strPassword = CmdArgs::instance().getPassword();
	const FullPathName& fpnKeyFile = CmdArgs::instance().getKeyfile();
	enum { PATH_EXISTS = FullPathName::PATH_ONLY | FullPathName::PATH_AND_FILENAME };
	const TCHAR* const lpKeyFile = ((fpnKeyFile.getState() & PATH_EXISTS) ?
		fpnKeyFile.getFullPathName().c_str() : NULL);
	const bool bPreSelectIsInEffect = CmdArgs::instance().preselectIsInEffect();
	const BOOL bLocked = (CmdArgs::instance().lockIsInEffect() ? TRUE : FALSE);

	if(strPassword.empty() && CmdArgs::instance().pwStdInIsInEffect())
		strPassword = WU_StdInReadPassword();

	_OpenDatabase(NULL, lpFileName, strPassword.empty() ? NULL : strPassword.c_str(),
		(!bPreSelectIsInEffect) ? lpKeyFile : NULL, bLocked,
		bPreSelectIsInEffect ? lpKeyFile : NULL, FALSE);

	return TRUE;
}

void CPwSafeDlg::_ParseSpecAndSetFont(const TCHAR *pszSpec, bool bNotes)
{
	CFont& fFont = (bNotes ? m_fNotesFont : m_fListFont);
	HFONT hPre = (HFONT)fFont;
	CDC *pDC = GetDC();
	HDC hDC = pDC->m_hDC;
	CString strFontSpec, strTemp;
	LOGFONT lf;
	int nSize;

	ASSERT(pszSpec != NULL);
	if(pszSpec == NULL) { ReleaseDC(pDC); return; }

	ZeroMemory(&lf, sizeof(LOGFONT));

	if(hPre != NULL) fFont.DeleteObject();

	if(_tcslen(pszSpec) != 0) // Font spec format: <FACE>;<SIZE>,<FLAGS>
	{
		strFontSpec = pszSpec;
		CString strFace, strSize, strFlags;
		int nChars = strFontSpec.ReverseFind(_T(';'));
		int nSizeEnd = strFontSpec.ReverseFind(_T(','));
		strFace = strFontSpec.Left(nChars);
		strSize = strFontSpec.Mid(nChars + 1, nSizeEnd - nChars - 1);
		strFlags = strFontSpec.Right(4);
		nSize = _ttoi(strSize);
		int nWeight = FW_NORMAL;
		if(strFlags.GetAt(0) == _T('1')) nWeight = FW_BOLD;
		BYTE bItalic = (BYTE)((strFlags.GetAt(1) == _T('1')) ? TRUE : FALSE);
		BYTE bUnderlined = (BYTE)((strFlags.GetAt(2) == _T('1')) ? TRUE : FALSE);
		BYTE bStrikeOut = (BYTE)((strFlags.GetAt(3) == _T('1')) ? TRUE : FALSE);

		if(nSize < 0) nSize = -nSize;
		if(strFace.GetLength() >= 32) strFace = strFace.Left(31);

		lf.lfCharSet = DEFAULT_CHARSET; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
		lf.lfEscapement = 0; lf.lfItalic = bItalic; lf.lfOrientation = 0;
		lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfQuality = DEFAULT_QUALITY;
		lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
		lf.lfStrikeOut = bStrikeOut; lf.lfUnderline = bUnderlined;
		lf.lfWeight = nWeight; lf.lfWidth = 0;
		_tcscpy_s(lf.lfFaceName, strFace);
		lf.lfHeight = -MulDiv(nSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);

		fFont.CreateFontIndirect(&lf);
	}
	else
	{
		nSize = 8;
		fFont.CreateFont(nSize, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET,
			OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
			DEFAULT_PITCH | FF_DONTCARE, _T("MS Shell Dlg"));
	}

	ASSERT(fFont.m_hObject != NULL);
	ZeroMemory(&lf, sizeof(LOGFONT));
	fFont.GetLogFont(&lf);

	strFontSpec = lf.lfFaceName;
	strFontSpec += _T(';');
	strTemp.Format(_T("%d"), nSize);
	strFontSpec += strTemp;
	strFontSpec += _T(",");
	if(lf.lfWeight == FW_BOLD) strFontSpec += _T('1'); else strFontSpec += _T('0');
	if(lf.lfItalic != 0) strFontSpec += _T('1'); else strFontSpec += _T('0');
	if(lf.lfUnderline != 0) strFontSpec += _T('1'); else strFontSpec += _T('0');
	if(lf.lfStrikeOut != 0) strFontSpec += _T('1'); else strFontSpec += _T('0');

	if(bNotes)
	{
		m_strNotesFontRtfSpec = _T(" ");
		m_strNotesFontRtfSpec += lf.lfFaceName;

		m_strNotesFontSpec = strFontSpec;
		m_nNotesFontSize = nSize;
	}
	else
	{
		m_strFontSpec = strFontSpec;
		m_strListFontFace = lf.lfFaceName;
		m_nListFontSize = nSize;

		m_cGroups.SetFont(&fFont, TRUE);
		m_cList.SetFont(&fFont, TRUE);
	}

	ReleaseDC(pDC);
}

// This should be replaced by a function that scans the accelerator table
const TCHAR *CPwSafeDlg::_GetCmdAccelExt(const TCHAR *psz)
{
	static LPCTSTR pEmpty = _T("");

	ASSERT(psz != NULL); if(psz == NULL) return pEmpty;

	if(_tcsicmp(psz, _T("&New...")) == 0) return _T("Ctrl+N");
	if(_tcsicmp(psz, _T("&Open...")) == 0) return _T("Ctrl+O");
	if(_tcsicmp(psz, _T("&Save")) == 0) return _T("Ctrl+S");
	if(_tcsicmp(psz, _T("&Close")) == 0) return _T("Ctrl+W");
	if(_tcsicmp(psz, _T("&Print...")) == 0) return _T("Ctrl+P");
	if(_tcsicmp(psz, _T("&Database Settings...")) == 0) return _T("Ctrl+I");
	if(_tcsicmp(psz, _T("&Lock Workspace")) == 0) return _T("Ctrl+L");
	if(_tcsicmp(psz, _T("E&xit")) == 0) return _T("Ctrl+Q");

	if(_tcsicmp(psz, _T("&Add Entry...")) == 0) return _T("Ctrl+Y");
	if(_tcsicmp(psz, _T("&Edit/View Entry...")) == 0) return _T("Return");
	if(_tcsicmp(psz, _T("&Delete Entry")) == 0) return _T("Ctrl+D");
	if(_tcsicmp(psz, _T("Se&lect All")) == 0) return _T("Ctrl+A");
	if(_tcsicmp(psz, _T("&Find in Database...")) == 0) return _T("Ctrl+F");
	if(_tcsicmp(psz, _T("Find in t&his Group...")) == 0) return _T("Ctrl+Shift+F");
	if(_tcsicmp(psz, _T("Add &Group...")) == 0) return _T("Ctrl+G");
	if(_tcsicmp(psz, _T("Open &URL(s)")) == 0) return _T("Ctrl+U");
	if(_tcsicmp(psz, _T("Copy &Password")) == 0) return _T("Ctrl+C");
	if(_tcsicmp(psz, _T("Copy User &Name")) == 0) return _T("Ctrl+B");
	if(_tcsicmp(psz, _T("Dupli&cate Entry")) == 0) return _T("Ctrl+K");
	if(_tcsicmp(psz, _T("&Options...")) == 0) return _T("Ctrl+M");
	if(_tcsicmp(psz, _T("Perform Auto-&Type")) == 0) return _T("Ctrl+V");

	if(_tcsicmp(psz, _T("Password &Generator...")) == 0) return _T("Ctrl+Z");

	if(_tcsicmp(psz, _T("Hide &User Names Behind Asterisks (***)")) == 0) return _T("Ctrl+J");
	if(_tcsicmp(psz, _T("Hide &Passwords Behind Asterisks (***)")) == 0) return _T("Ctrl+H");

	if(_tcsicmp(psz, _T("Move Entry to &Top")) == 0) return _T("Alt+Home");
	if(_tcsicmp(psz, _T("Move Entry &One Up")) == 0) return _T("Alt+Up");
	if(_tcsicmp(psz, _T("Mo&ve Entry One Down")) == 0) return _T("Alt+Down");
	if(_tcsicmp(psz, _T("Move Entry to &Bottom")) == 0) return _T("Alt+End");

	if(_tcsicmp(psz, _T("Move Group to &Top")) == 0) return _T("Alt+Home");
	if(_tcsicmp(psz, _T("Move Group One &Up")) == 0) return _T("Alt+Up");
	if(_tcsicmp(psz, _T("Move Group &One Down")) == 0) return _T("Alt+Down");
	if(_tcsicmp(psz, _T("Move Group to &Bottom")) == 0) return _T("Alt+End");
	if(_tcsicmp(psz, _T("Move Group One &Left")) == 0) return _T("Alt+Left");
	if(_tcsicmp(psz, _T("Move Group One &Right")) == 0) return _T("Alt+Right");

	if(_tcsicmp(psz, _T("&Help Contents")) == 0) return _T("F1");

	return pEmpty;
}

void CPwSafeDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	// Map close button to minimize button if the user wants this
	if((nID == SC_CLOSE) && (m_bCloseMinimizes == TRUE))
	{
		::SendMessage(this->m_hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
		return;
	}

	if((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		NotifyUserActivity();
		CAboutDlg dlgAbout;
		NewGUI_DoModal(&dlgAbout);
		return;
	}
	else if(nID == SC_MINIMIZE)
	{
		_SaveWindowPositionAndSize(NULL);

		if(m_bMinimizeToTray == TRUE)
		{
			SetTrayState(TRUE);
			return;
		}
	}

	CDialog::OnSysCommand(nID, lParam);
}

void CPwSafeDlg::OnPaint()
{
	if(IsIconic())
	{
		CPaintDC dc(this);

		SendMessage(WM_ICONERASEBKGND, (WPARAM)dc.GetSafeHdc(), 0);

		const int cxIcon = GetSystemMetrics(SM_CXICON);
		const int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		const int x = ((rect.Width() - cxIcon + 1) / 2);
		const int y = ((rect.Height() - cyIcon + 1) / 2);

		dc.DrawIcon(x, y, m_hIcon);
	}
	else CDialog::OnPaint();
}

HCURSOR CPwSafeDlg::OnQueryDragIcon()
{
	return (HCURSOR)m_hIcon;
}

void CPwSafeDlg::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	bool bSetFlag = false;

	if(lpMeasureItemStruct->CtlType == ODT_MENU)
	{
		if(IsMenu((HMENU)(UINT_PTR)lpMeasureItemStruct->itemID) &&
			BCMenu::IsMenu((HMENU)(UINT_PTR)lpMeasureItemStruct->itemID))
		{
			m_menu.MeasureItem(lpMeasureItemStruct);
			bSetFlag = true;
		}
	}

	if(!bSetFlag) CDialog::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}

LRESULT CPwSafeDlg::OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu)
{
	LRESULT lResult = 0;

	if(BCMenu::IsMenu(pMenu))
		lResult = BCMenu::FindKeyboardShortcut(nChar, nFlags, pMenu);
	else
		lResult = CDialog::OnMenuChar(nChar, nFlags, pMenu);

	return lResult;
}

void CPwSafeDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
	NotifyUserActivity();

	m_dwLastFirstSelectedItem = GetSelectedEntry();
	m_dwLastNumSelectedItems = GetSelectedEntriesCount();
	m_hLastSelectedGroup = m_cGroups.GetSelectedItem();

	if(m_dwLastFirstSelectedItem != DWORD_MAX)
		m_dwLastEntryIndex = _ListSelToEntryIndex(m_dwLastFirstSelectedItem);
	else m_dwLastEntryIndex = DWORD_MAX;

	// CMenu *p = m_menu.GetSubMenu(TRL("&Tools"));
	// if(p == pPopupMenu) RebuildPluginMenu();

	_CallPlugins(KPM_INIT_MENU_POPUP, 0, 0);

	CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);

	OnUpdateFlush(pPopupMenu);

	if(!bSysMenu)
		if(BCMenu::IsMenu(pPopupMenu)) BCMenu::UpdateMenu(pPopupMenu);
}

// BCMenu function
void CPwSafeDlg::OnUpdateFlush(CMenu *pMenu)
{
	ASSERT(pMenu != NULL);
	// check the enabled state of various menu items

	CCmdUI state;
	state.m_pMenu = pMenu;
	ASSERT(state.m_pOther == NULL);
	ASSERT(state.m_pParentMenu == NULL);

	// determine if menu is popup in top-level menu and set m_pOther to
	//  it if so (m_pParentMenu == NULL indicates that it is secondary popup)
	HMENU hParentMenu;
	if(AfxGetThreadState()->m_hTrackingMenu == pMenu->m_hMenu)
		state.m_pParentMenu = pMenu;    // parent == child for tracking popup
	else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL)
	{
		CWnd* pParent = GetTopLevelParent();
		// child windows don't have menus -- need to go to the top!
		if(pParent != NULL && (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL)
		{
			int nIndexMax = ::GetMenuItemCount(hParentMenu);
			for (int nIndex = 0; nIndex < nIndexMax; nIndex++)
			{
				if (::GetSubMenu(hParentMenu, nIndex) == pMenu->m_hMenu)
				{
					// when popup is found, m_pParentMenu is containing menu
					state.m_pParentMenu = CMenu::FromHandle(hParentMenu);
					break;
				}
			}
		}
	}

	state.m_nIndexMax = pMenu->GetMenuItemCount();
	for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++)
	{
		state.m_nID = pMenu->GetMenuItemID(state.m_nIndex);
		if(state.m_nID == 0) continue; // menu separator or invalid cmd - ignore it

		ASSERT(state.m_pOther == NULL);
		ASSERT(state.m_pMenu != NULL);
		if (state.m_nID == (UINT)-1)
		{
			// possibly a popup menu, route to first item of that popup
			state.m_pSubMenu = pMenu->GetSubMenu(state.m_nIndex);
			if((state.m_pSubMenu == NULL) ||
				((state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0) ||
				(state.m_nID == (UINT)(-1)))
			{
				continue;       // first item of popup can't be routed to
			}
			state.DoUpdate(this, FALSE);    // popups are never auto disabled
		}
		else
		{
			// normal menu item
			// Auto enable/disable if frame window has 'm_bAutoMenuEnable'
			//    set and command is _not_ a system command.
			state.m_pSubMenu = NULL;
			state.DoUpdate(this, state.m_nID < 0xF000);
		}

		// adjust for menu deletions and additions
		UINT nCount = pMenu->GetMenuItemCount();
		if (nCount < state.m_nIndexMax)
		{
			state.m_nIndex -= (state.m_nIndexMax - nCount);
			while (state.m_nIndex < nCount &&
				pMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)
			{
				state.m_nIndex++;
			}
		}
		state.m_nIndexMax = nCount;
	}
}

void CPwSafeDlg::OnSize(UINT nType, int cx, int cy)
{
	if(cx < MWS_MIN_CX) cx = MWS_MIN_CX; // Minimum sizes
	if(cy < MWS_MIN_CY) cy = MWS_MIN_CY;

	CDialog::OnSize(nType, cx, cy);

	BOOL bWasInvisible = FALSE;
	if(m_bMinimized == TRUE) bWasInvisible = TRUE;

	if(nType == SIZE_MAXIMIZED) m_bWasMaximized = TRUE;
	else if(m_bTrayed == FALSE)
	{
		if((nType == SIZE_RESTORED) && (m_bWasMaximized == TRUE)) m_bWasMaximized = FALSE;
		else if((nType == SIZE_RESTORED) && (m_bMinimized == FALSE)) m_bWasMaximized = FALSE;
	}

	if(nType == SIZE_MINIMIZED) m_bMinimized = TRUE;
	else if((nType == SIZE_RESTORED) && (m_bMinimized == TRUE)) m_bMinimized = FALSE;
	else if((nType == SIZE_MAXIMIZED) && (m_bMinimized == TRUE)) m_bMinimized = FALSE;

	if(m_bIgnoreSizeEvent == TRUE) return;

	if(nType == SIZE_MINIMIZED)
	{
		if(m_bMinimizeToTray == TRUE) SetTrayState(TRUE);
	}

	if(nType != SIZE_MINIMIZED) ProcessResize();

	BOOL m_bRestore = FALSE;

	if(((nType == SIZE_RESTORED) || (nType == SIZE_MAXIMIZED)) &&
		(bWasInvisible == TRUE))
	{
		_ChangeLockState(FALSE);
	}
	else if(nType == SIZE_MINIMIZED)
	{
		if(m_bLockOnMinimize == TRUE)
		{
			_ChangeLockState(TRUE); // Lock

			// Was the locking successful? If not: restore window
			if((m_bFileOpen == TRUE) && (m_bLocked == FALSE)) m_bRestore = TRUE;
		}
	}

	if(m_bRestore == TRUE)
	{
		if(m_bWasMaximized == TRUE) ShowWindow(SW_MAXIMIZE);
		else ShowWindow(SW_RESTORE);
		_UpdateTrayIcon();
	}

	_UpdatePeekPreview();
	_UpdateToolBar();
	_SaveWindowPositionAndSize(NULL);
}

void CPwSafeDlg::OnSizing(UINT nSide, LPRECT lpRect)
{
	if((lpRect->right - lpRect->left) < MWS_MIN_CX) // Minimum width
	{
		if((nSide == WMSZ_BOTTOMLEFT) || (nSide == WMSZ_LEFT) || (nSide == WMSZ_TOPLEFT))
			lpRect->left = lpRect->right - MWS_MIN_CX;
		else lpRect->right = lpRect->left + MWS_MIN_CX;
	}

	if((lpRect->bottom - lpRect->top) < MWS_MIN_CY) // Minimum height
	{
		if((nSide == WMSZ_TOPLEFT) || (nSide == WMSZ_TOP) || (nSide == WMSZ_TOPRIGHT))
			lpRect->top = lpRect->bottom - MWS_MIN_CY;
		else lpRect->bottom = lpRect->top + MWS_MIN_CY;
	}

	CDialog::OnSizing(nSide, lpRect);

	ProcessResize();
}

void CPwSafeDlg::ProcessResize()
{
	NotifyUserActivity();

	RECT rectClient;
	RECT rectList;
	int cyMenu = GetSystemMetrics(SM_CYMENU);
	LONG nAddTop;
	RECT rectTb;
	int tbHeight = NewGUI_Scale(23, this);
	const long lGuiSpacer = NewGUI_Scale(GUI_SPACER, this);

	if(IsWindow(m_btnTbAbout.m_hWnd))
	{
		if(m_bShowToolBar == TRUE)
		{
			m_btnTbAbout.GetWindowRect(&rectTb);
			tbHeight = rectTb.bottom - rectTb.top;
		}
	}

	if(m_bShowToolBar == TRUE) nAddTop = tbHeight;
	else nAddTop = 0;

	GetClientRect(&rectClient);

	RECT rectWindow;
	GetWindowRect(&rectWindow);
	BOOL bWindowValid = TRUE;
	if((rectWindow.right - rectWindow.left) < (MWS_MIN_CX - 32)) bWindowValid = FALSE;
	if((rectWindow.bottom - rectWindow.top) < (MWS_MIN_CY - 32)) bWindowValid = FALSE;

	if((m_bMinimized == FALSE) && (m_bTrayed == FALSE) && (bWindowValid == TRUE))
	{
		// Range check and correction for splitter windows

		if(m_lSplitterPosHoriz <= 1) m_lSplitterPosHoriz = 1;
		if(m_lSplitterPosHoriz >= rectClient.right - 10) m_lSplitterPosHoriz = rectClient.right - 10;

		if(m_lSplitterPosVert <= 26) m_lSplitterPosVert = 26;

		if((rectClient.bottom - m_lSplitterPosVert) <= (76 + nAddTop))
			m_lSplitterPosVert = rectClient.bottom - (76 + nAddTop);
	}

	LONG nEntryViewHeight = /* rectClient.bottom - rectClient.top - */ m_lSplitterPosVert;
	if(m_bEntryView == FALSE) nEntryViewHeight = -1;

	if(IsWindow(m_cGroups.m_hWnd)) // Resize group box
	{
		rectList.top = lGuiSpacer + nAddTop;
		rectList.bottom = rectClient.bottom - (lGuiSpacer >> 1) - cyMenu - nEntryViewHeight - 1;
		rectList.left = 0;
		rectList.right = lGuiSpacer + m_lSplitterPosHoriz - 1;
		m_cGroups.MoveWindow(&rectList, TRUE);

		// int nColumnWidth = (rectList.right - rectList.left) -
		//	GetSystemMetrics(SM_CXVSCROLL) - 8;
		// m_cGroups.SetColumnWidth(0, nColumnWidth);
	}

	if(IsWindow(m_cList.m_hWnd)) // Resize entry list view
	{
		rectList.top = lGuiSpacer + nAddTop;
		rectList.bottom = rectClient.bottom - (lGuiSpacer >> 1) - cyMenu - nEntryViewHeight - 1;
		rectList.left = lGuiSpacer + m_lSplitterPosHoriz + 2;
		rectList.right = rectClient.right;
		m_cList.MoveWindow(&rectList, TRUE);

		if(m_bColAutoSize == TRUE) _CalcColumnSizes();
		_SetColumnWidths();
	}

	if(IsWindow(m_reEntryView.m_hWnd))
	{
		rectList.top = rectClient.bottom - (lGuiSpacer >> 1) - cyMenu - nEntryViewHeight + (lGuiSpacer >> 1);
		rectList.bottom = rectClient.bottom - (lGuiSpacer >> 1) - cyMenu;
		rectList.left = 0;
		rectList.right = rectClient.right - rectClient.left;
		m_reEntryView.MoveWindow(&rectList, TRUE);
	}

	if(IsWindow(m_stcMenuLine.m_hWnd)) // Resize menu line
	{
		rectList.top = 0;
		rectList.bottom = 2;
		rectList.left = 0;
		rectList.right = rectClient.right - rectClient.left;
		m_stcMenuLine.MoveWindow(&rectList, TRUE);
	}

	if(IsWindow(m_sbStatus.m_hWnd)) // Resize status bar
	{
		RECT rectSB;
		m_sbStatus.GetClientRect(&rectSB);
		rectSB.top = rectClient.bottom - rectClient.top - rectSB.bottom - rectSB.top;
		rectSB.bottom = rectClient.bottom - rectClient.top;
		rectSB.left = 0;
		rectSB.right = rectClient.right - rectClient.left;
		m_sbStatus.MoveWindow(&rectSB, TRUE);
		m_sbStatus.RedrawWindow(NULL);
	}
}

void CPwSafeDlg::CleanUp()
{
	SaveOptions();
	_CallPlugins(KPM_CLEANUP, 0, 0);

	m_sessionNotify.Unregister();
	m_remoteControl.FreeStatic();

	if(m_bTimer == TRUE)
	{
		KillTimer(APPWND_TIMER_ID);
		m_bTimer = FALSE;
	}

	VERIFY(RegisterGlobalHotKey(HOTKEYID_AUTOTYPE, 0, false));
	ASSERT(m_sRegHotKeyIDs.size() == 0);

	RegisterRestoreHotKey(FALSE);

	CPluginManager::Instance().UnloadAllPlugins(TRUE);

	if(m_bMenu == TRUE)
	{
		// Auto-destroyed in BCMenu destructor
		// m_menu.DestroyMenu();

		m_bMenu = FALSE;
	}
	DeleteContextMenus();

	if(m_nClipboardCountdown >= 0)
	{
		m_nClipboardCountdown = -1; // Disable clipboard clear countdown

		if(m_bClearClipOnDbClose == TRUE) ClearClipboardIfOwner();
	}

	_DeleteTemporaryFiles();
	FreeCurrentTranslationTable();

	HICON hReleaser; // Load/get all icons and release them safely
	hReleaser = AfxGetApp()->LoadIcon(MAKEINTRESOURCE(IDI_ICONPIC));
	DestroyIcon(hReleaser);
	hReleaser = AfxGetApp()->LoadIcon(MAKEINTRESOURCE(IDI_KEY));
	DestroyIcon(hReleaser);
	hReleaser = AfxGetApp()->LoadIcon(MAKEINTRESOURCE(IDI_OPTIONS));
	DestroyIcon(hReleaser);
	hReleaser = AfxGetApp()->LoadIcon(MAKEINTRESOURCE(IDI_SEARCH));
	DestroyIcon(hReleaser);
	hReleaser = AfxGetApp()->LoadIcon(MAKEINTRESOURCE(IDI_WORLD));
	DestroyIcon(hReleaser);
	hReleaser = AfxGetApp()->LoadIcon(MAKEINTRESOURCE(IDR_MAINFRAME));
	DestroyIcon(hReleaser);

	VERIFY(DestroyAcceleratorTable(m_hAccel));

	m_mgr.NewDatabase();
	m_cList.DeleteAllItemsEx();
	m_cGroups.DeleteAllItemsEx();

	m_ilIcons.DeleteImageList();

	ASSERT(CGlobalWindowManager::GetCount() == 0);

	NewGUI_SetThemeHelper(NULL);
	SAFE_DELETE(m_pThemeHelper);

	if(m_bRestartApplication == TRUE) RestartApplication();
}

void CPwSafeDlg::SaveOptions()
{
	CPrivateConfigEx pcfg(TRUE);
	TCHAR szTemp[SI_REGSIZE];

	// Save clipboard auto-clear time
	_ultot_s(m_dwClipboardSecs, szTemp, 10);
	pcfg.Set(PWMKEY_CLIPSECS, szTemp);

	pcfg.SetBool(PWMKEY_CLIPNOPERSIST, WU_GetConfigBool(WU_CFG_CLIP_NOPERSIST, TRUE));

	// _itot_s(m_nClipboardMethod, szTemp, 10);
	// pcfg.Set(PWMKEY_CLIPBOARDMETHOD, szTemp);

	_ultot_s(m_dwATHotKey, szTemp, 10);
	pcfg.Set(PWMKEY_AUTOTYPEHOTKEY, szTemp);

	pcfg.SetBool(PWMKEY_RESTOREHOTKEY, m_bRegisterRestoreHotKey);

	// Save newline sequence
	if(m_bWindowsNewLine == TRUE) _tcscpy_s(szTemp, _T("Windows"));
	else _tcscpy_s(szTemp, _T("Unix"));
	pcfg.Set(PWMKEY_NEWLINE, szTemp);

	pcfg.SetBool(PWMKEY_USEPUTTYFORURLS, m_bUsePuttyForURLs);

	_itot_s(m_nAutoSort, szTemp, 10);
	pcfg.Set(PWMKEY_AUTOSORT, szTemp);

	pcfg.SetBool(PWMKEY_SAVEONLATMOD, m_bSaveOnLATMod);
	pcfg.SetBool(PWMKEY_OPENLASTB, m_bOpenLastDb);
	pcfg.SetBool(PWMKEY_AUTOSAVEB, m_bAutoSaveDb);
	pcfg.SetBool(PWMKEY_REMEMBERLAST, m_bRememberLast);
	pcfg.SetBool(PWMKEY_SINGLEINSTANCE, m_bCheckForInstance);

	if(m_bRememberLast == TRUE)
	{
		// GetCurrentDirectory(1024, szTemp);
		// if(szTemp[0] != 0) pcfg.Set(PWMKEY_LASTDIR, szTemp);
		const std_string strCurDir = WU_GetCurrentDirectory();
		if(strCurDir.size() > 0) pcfg.Set(PWMKEY_LASTDIR, strCurDir.c_str());

		// TCHAR tszTemp[SI_REGSIZE];
		// GetModuleFileName(NULL, tszTemp, SI_REGSIZE - 2);
		const std_string strExe = Executable::instance().getFullPathName();
		pcfg.Set(PWMKEY_LASTDB, MakeRelativePathEx(strExe.c_str(), m_strLastDb));
	}
	else
	{
		pcfg.Set(PWMKEY_LASTDIR, _T(""));
		pcfg.Set(PWMKEY_LASTDB, _T(""));
	}

	pcfg.SetBool(PWMKEY_REMEMBERKEYSOURCES, CKeySourcesPool::GetEnabled());
	CKeySourcesPool::Save(&pcfg);

	pcfg.SetBool(PWMKEY_STARTMINIMIZED, m_bStartMinimized);
	pcfg.SetBool(PWMKEY_DISABLEUNSAFE, m_bDisableUnsafe);
	pcfg.SetBool(PWMKEY_IMGBTNS, m_bImgButtons);
	pcfg.SetBool(PWMKEY_ENTRYGRID, m_bEntryGrid);
	pcfg.SetBool(PWMKEY_AUTOSHOWEXPIRED, m_bAutoShowExpired);
	pcfg.SetBool(PWMKEY_AUTOSHOWEXPIREDS, m_bAutoShowExpiredSoon);
	pcfg.SetBool(PWMKEY_BACKUPENTRIES, m_bBackupEntries);
	pcfg.SetBool(PWMKEY_SECUREEDITS, m_bSecureEdits);
	pcfg.SetBool(PWMKEY_SINGLECLICKTRAY, m_bSingleClickTrayIcon);
	pcfg.SetBool(PWMKEY_SHOWTRAYONLYIFTRAYED, m_bShowTrayOnlyIfTrayed);
	pcfg.SetBool(PWMKEY_QUICKFINDINPWS, m_bQuickFindInPasswords);
	pcfg.SetBool(PWMKEY_QUICKFINDINCBK, m_bQuickFindIncBackup);
	pcfg.SetBool(PWMKEY_QUICKFINDINCEXP, m_bQuickFindIncExpired);
	pcfg.SetBool(PWMKEY_DELETEBKONSAVE, m_bDeleteBackupsOnSave);
	pcfg.SetBool(PWMKEY_DISABLEAUTOTYPE, m_bDisableAutoType);
	pcfg.SetBool(PWMKEY_COPYURLS, m_bCopyURLs);
	pcfg.SetBool(PWMKEY_EXITINSTEADLOCK, m_bExitInsteadOfLockAT);
	pcfg.SetBool(PWMKEY_FOCUSRESAFTERQUICKFIND, m_bFocusResAfterQuickFind);
	pcfg.SetBool(PWMKEY_ALWAYSALLOWIPC, m_remoteControl.GetAlwaysAllowFullAccess());
	pcfg.SetBool(PWMKEY_DROPTOBACKONCOPY, m_bDropToBackOnCopy);
	pcfg.SetBool(PWMKEY_SORTAUTOTYPESELITEMS, m_bSortAutoTypeSelItems);
	pcfg.SetBool(PWMKEY_TRANSACTEDFILEWRITE, m_bUseTransactedFileWrites);

	pcfg.SetBool(PWMKEY_SHOWTITLE, m_bShowTitle);
	pcfg.SetBool(PWMKEY_SHOWUSER, m_bShowUserName);
	pcfg.SetBool(PWMKEY_SHOWURL, m_bShowURL);
	pcfg.SetBool(PWMKEY_SHOWPASS, m_bShowPassword);
	pcfg.SetBool(PWMKEY_SHOWNOTES, m_bShowNotes);

	pcfg.SetBool(PWMKEY_SHOWCREATION, m_bShowCreation);
	pcfg.SetBool(PWMKEY_SHOWLASTMOD, m_bShowLastMod);
	pcfg.SetBool(PWMKEY_SHOWLASTACCESS, m_bShowLastAccess);
	pcfg.SetBool(PWMKEY_SHOWEXPIRE, m_bShowExpire);
	pcfg.SetBool(PWMKEY_SHOWUUID, m_bShowUUID);
	pcfg.SetBool(PWMKEY_SHOWATTACH, m_bShowAttach);

	pcfg.SetBool(PWMKEY_USELOCALTIMEFMT, CPwSafeDlg::m_bUseLocalTimeFormat);

	pcfg.SetBool(PWMKEY_ENTRYVIEW, m_bEntryView);

	pcfg.SetBool(PWMKEY_HIDESTARS, m_bPasswordStars);
	pcfg.SetBool(PWMKEY_HIDEUSERS, m_bUserStars);
	pcfg.SetBool(PWMKEY_ALWAYSTOP, m_bAlwaysOnTop);
	pcfg.SetBool(PWMKEY_LOCKMIN, m_bLockOnMinimize);
	pcfg.SetBool(PWMKEY_MINLOCK, m_bMinimizeOnLock);
	pcfg.SetBool(PWMKEY_MINTRAY, m_bMinimizeToTray);
	pcfg.SetBool(PWMKEY_CLOSEMIN, m_bCloseMinimizes);
	pcfg.SetBool(PWMKEY_SHOWTOOLBAR, m_bShowToolBar);
	pcfg.SetBool(PWMKEY_COLAUTOSIZE, m_bColAutoSize);
	pcfg.SetBool(PWMKEY_SHOWFULLPATH, m_bShowFullPath);
	pcfg.SetBool(PWMKEY_DELETETANSAFTERUSE, m_bDeleteTANsAfterUse);

	pcfg.SetBool(PWMKEY_SIMPLETANVIEW, m_bSimpleTANView);
	pcfg.SetBool(PWMKEY_SHOWTANINDICES, m_bShowTANIndices);
	pcfg.SetBool(PWMKEY_ALLOWSAVEIFMODIFIEDONLY, m_bAllowSaveIfModifiedOnly);
	pcfg.SetBool(PWMKEY_CHECKFORUPDATE, m_bCheckForUpdate);
	pcfg.SetBool(PWMKEY_CHECKFORUPDATECFG, m_bCheckForUpdateCfg);
	pcfg.SetBool(PWMKEY_LOCKONWINLOCK, m_bLockOnWinLock);
	pcfg.SetBool(PWMKEY_CLEARCLIPONDBCLOSE, m_bClearClipOnDbClose);
	pcfg.SetBool(PWMKEY_ENABLEREMOTECTRL, m_remoteControl.IsEnabled());

	pcfg.SetBool(PWMKEY_USEHELPCENTER, (WU_GetAppHelpSource() == APPHS_ONLINE) ?
		TRUE : FALSE);

	NewGUI_GetHeaderOrder(m_cList.m_hWnd, m_aHeaderOrder, 11);
	ar2str(szTemp, m_aHeaderOrder, 11);
	pcfg.Set(PWMKEY_HEADERORDER, szTemp);

	/* CString strOptions, strCharSet; UINT nChars;
	CPwGeneratorDlg::GetOptions(&strOptions, &strCharSet, &nChars);
	pcfg.Set(PWMKEY_PWGEN_OPTIONS, strOptions);
	pcfg.Set(PWMKEY_PWGEN_CHARS, strCharSet);
	_itot_s((int)nChars, szTemp, 10);
	pcfg.Set(PWMKEY_PWGEN_NUMCHARS, szTemp); */

	_itot_s(m_nAutoTypeMethod, szTemp, 10);
	pcfg.Set(PWMKEY_AUTOTYPEMETHOD, szTemp);

	pcfg.Set(PWMKEY_LISTFONT, m_strFontSpec);
	pcfg.Set(PWMKEY_NOTESFONT, m_strNotesFontSpec);
	pcfg.Set(PWMKEY_PASSWORDFONT, m_strPasswordFontSpec);

	_SaveWindowPositionAndSize(&pcfg);

	// Save all column widths
	_itot_s(m_nColumnWidths[0], szTemp, 10);
	pcfg.Set(PWMKEY_COLWIDTH0, szTemp);
	_itot_s(m_nColumnWidths[1], szTemp, 10);
	pcfg.Set(PWMKEY_COLWIDTH1, szTemp);
	_itot_s(m_nColumnWidths[2], szTemp, 10);
	pcfg.Set(PWMKEY_COLWIDTH2, szTemp);
	_itot_s(m_nColumnWidths[3], szTemp, 10);
	pcfg.Set(PWMKEY_COLWIDTH3, szTemp);
	_itot_s(m_nColumnWidths[4], szTemp, 10);
	pcfg.Set(PWMKEY_COLWIDTH4, szTemp);
	_itot_s(m_nColumnWidths[5], szTemp, 10);
	pcfg.Set(PWMKEY_COLWIDTH5, szTemp);
	_itot_s(m_nColumnWidths[6], szTemp, 10);
	pcfg.Set(PWMKEY_COLWIDTH6, szTemp);
	_itot_s(m_nColumnWidths[7], szTemp, 10);
	pcfg.Set(PWMKEY_COLWIDTH7, szTemp);
	_itot_s(m_nColumnWidths[8], szTemp, 10);
	pcfg.Set(PWMKEY_COLWIDTH8, szTemp);
	_itot_s(m_nColumnWidths[9], szTemp, 10);
	pcfg.Set(PWMKEY_COLWIDTH9, szTemp);
	_itot_s(m_nColumnWidths[10], szTemp, 10);
	pcfg.Set(PWMKEY_COLWIDTH10, szTemp);

	_ltot_s(m_lSplitterPosHoriz, szTemp, 10);
	pcfg.Set(PWMKEY_SPLITTERX, szTemp);
	_ltot_s(m_lSplitterPosVert, szTemp, 10);
	pcfg.Set(PWMKEY_SPLITTERY, szTemp);

	pcfg.SetBool(PWMKEY_WINSTATE_MAX, m_bWasMaximized);

	_ltot_s((long)m_cList.GetRowColorEx(), szTemp, 10);
	pcfg.Set(PWMKEY_ROWCOLOR, szTemp);

	_ltot_s(m_nLockTimeDef, szTemp, 10);
	pcfg.Set(PWMKEY_LOCKTIMER, szTemp);

	_ltot_s((long)m_dwDefaultExpire, szTemp, 10);
	pcfg.Set(PWMKEY_DEFAULTEXPIRE, szTemp);

	// int j = 0;
	// CString strTemp, strT;
	// for(int i = 0; i < (int)CPluginManager::Instance().m_plugins.size(); ++i)
	// {
	//	if(CPluginManager::Instance().m_plugins[i].bEnabled == FALSE) continue;
	//	strTemp.Format(_T("KeePlugin_%d"), j);
	//	strT = CPluginManager::Instance().m_plugins[i].tszFile;
	//	pcfg.Set(strTemp, CsFileOnly(&strT));
	//	++j;
	// }
	// strTemp.Format(_T("KeePlugin_%d"), j);
	// VERIFY(pcfg.Set(strTemp, _T("0")));

	std::basic_string<TCHAR> strAutoProfile =
		PwgProfileToString(&CPwSafeDlg::m_pgsAutoProfile);
	pcfg.Set(PWMKEY_GENPROFILEAUTO, strAutoProfile.c_str());

	// Support legacy auto-generation flag
	pcfg.SetBool(PWMKEY_AUTOPWGEN, (CPwSafeDlg::m_pgsAutoProfile.dwLength > 0) ?
		TRUE : FALSE);

	if(m_strDefaultAutoTypeSequence.GetLength() > 0)
		pcfg.Set(PWMKEY_DEFAULTATSEQ, m_strDefaultAutoTypeSequence);
	pcfg.SetBool(PWMKEY_AUTOTYPEIEFIX, m_bAutoTypeIEFix);
	pcfg.SetBool(PWMKEY_AUTOTYPESAMEKEYBLAYOUTS, m_bAutoTypeSameKL);
	pcfg.SetBool(PWMKEY_AUTOTYPENORMDASHES, m_bAutoTypeNormDashes);

	pcfg.SetBool(PWMKEY_USEDPAPIFORMEMPROT, *CMemoryProtectionEx::GetEnabledPtr());
	pcfg.SetBool(PWMKEY_USECNGBCRYPTFORKEYT, *CKeyTransformBCrypt::GetEnabledPtr());
}

void CPwSafeDlg::_SaveWindowPositionAndSize(CPrivateConfigEx* pConfig)
{
	RECT rect;
	ZeroMemory(&rect, sizeof(RECT));
	GetWindowRect(&rect);

	if((m_bMinimized == FALSE) && (m_bTrayed == FALSE) && (m_bWasMaximized == FALSE))
	{
		m_lNormalWndPosX = rect.left;
		m_lNormalWndPosY = rect.top;
		m_lNormalWndSizeW = rect.right - rect.left;
		m_lNormalWndSizeH = rect.bottom - rect.top;
	}

	if((m_bMinimized == FALSE) && (m_bTrayed == FALSE))
	{
		const SIZE sz = NewGUI_GetWindowContentSize(this->m_hWnd);
		if((sz.cx > 0) && (sz.cy > 0))
			memcpy(&m_szLastContent, &sz, sizeof(SIZE));
		else { ASSERT(FALSE); }
	}

	if((pConfig != NULL) && (m_lNormalWndSizeW >= 0) && (m_lNormalWndSizeH >= 0))
	{
		TCHAR szTemp[SI_REGSIZE];
		_ltot_s(m_lNormalWndPosX, szTemp, 10);
		pConfig->Set(PWMKEY_WINDOWPX, szTemp);
		_ltot_s(m_lNormalWndPosY, szTemp, 10);
		pConfig->Set(PWMKEY_WINDOWPY, szTemp);
		_ltot_s(m_lNormalWndSizeW, szTemp, 10);
		pConfig->Set(PWMKEY_WINDOWDX, szTemp);
		_ltot_s(m_lNormalWndSizeH, szTemp, 10);
		pConfig->Set(PWMKEY_WINDOWDY, szTemp);
	}
}

void CPwSafeDlg::_DeleteTemporaryFiles()
{
	if(_CallPlugins(KPM_DELETE_TEMP_FILES_PRE, 0, 0) == FALSE) return;

	std::vector<std::basic_string<TCHAR> > vRemaining;
	for(DWORD i = 0; i < m_vTempFiles.size(); ++i)
	{
		if(AU_SecureDeleteFile(m_vTempFiles[i].c_str()) == FALSE)
			vRemaining.push_back(m_vTempFiles[i]);
	}

	m_vTempFiles = vRemaining;
}

void CPwSafeDlg::OnOK()
{
	NotifyUserActivity();

	// Ignore enter in dialog

	// CleanUp();
	// CDialog::OnOK();
}

void CPwSafeDlg::OnCancel()
{
	NotifyUserActivity();

	if(m_hDraggingGroup != NULL)
	{
		SendMessage(WM_CANCELMODE);
		return;
	}

	// Prevent close while other dialog is open
	if(CGlobalWindowManager::GetCount() > 0) return;

	// Are we called because of an Esc key-press?
	if((GetKeyState(VK_ESCAPE) & 0x8000) != 0)
	{
		if(m_bLocked == FALSE) OnFileLock();
		return;
	}

	if((m_bMenuExit == FALSE) && (m_bCloseMinimizes == TRUE)) return;

	if(_CallPlugins(KPM_WM_CANCEL, 0, 0) == FALSE) return;

	m_bExiting = TRUE;
	OnFileClose();

	if(m_bFileOpen == TRUE)
	{
		m_bExiting = FALSE;
		return;
	}

	CleanUp();
	CDialog::OnCancel();
}

void CPwSafeDlg::OnFileExit()
{
	NotifyUserActivity();

	if(_CallPlugins(KPM_FILE_EXIT_PRE, 0, 0) == FALSE) return;

	m_bMenuExit = TRUE;
	OnCancel();
	m_bMenuExit = FALSE;
}

void CPwSafeDlg::OnInfoAbout()
{
	NotifyUserActivity();

	if(_CallPlugins(KPM_INFO_ABOUT_PRE, 0, 0) == FALSE) return;

	CAboutDlg dlg;
	NewGUI_DoModal(&dlg);
}

void CPwSafeDlg::OnSafeAddGroup()
{
	NotifyUserActivity();

	CString strGroupName;
	CAddGroupDlg dlg;
	PW_GROUP pwTemplate;
	PW_TIME pwTime;

	if(m_bFileOpen == FALSE) return;

	dlg.m_nIconId = PWM_STD_ICON_GROUP;
	dlg.m_strGroupName.Empty();
	dlg.m_pParentImageList = &m_ilIcons;
	dlg.m_bEditMode = FALSE;

	if(_CallPlugins(KPM_GROUP_ADD_PRE, 0, 0) == FALSE) return;

	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		_GetCurrentPwTime(&pwTime);
		pwTemplate.pszGroupName = const_cast<LPTSTR>((LPCTSTR)dlg.m_strGroupName);
		pwTemplate.tCreation = pwTime;
		m_mgr.GetNeverExpireTime(&pwTemplate.tExpire);
		pwTemplate.tLastAccess = pwTime;
		pwTemplate.tLastMod = pwTime;
		pwTemplate.uGroupId = 0; // 0 = create new group
		pwTemplate.uImageId = static_cast<DWORD>(dlg.m_nIconId);
		pwTemplate.usLevel = 0; pwTemplate.dwFlags = 0;

		if(_CallPlugins(KPM_GROUP_ADD, (LPARAM)&pwTemplate, 0) == FALSE) return;

		VERIFY(m_mgr.AddGroup(&pwTemplate));
		UpdateGroupList();

		HTREEITEM hLast = _GetLastGroupItem(&m_cGroups);
		ASSERT(hLast != NULL); // We have added a group, there must be at least one
		m_cGroups.EnsureVisible(hLast);
		m_cGroups.SelectItem(hLast);

		UpdatePasswordList();
		m_cList.SetFocus();

		dlg.m_strGroupName.Empty();

		m_bModified = TRUE;
	}

	_UpdateToolBar();
}

void CPwSafeDlg::OnViewHideStars()
{
	NotifyUserActivity();

	if(_CallPlugins(KPM_VIEW_HIDE_STARS_PRE, 0, 0) == FALSE) return;

	if((m_bPasswordStars == TRUE) && (IsUnsafeAllowed(this->m_hWnd) == FALSE)) return;

	UINT uState = m_menu.GetMenuState(ID_VIEW_HIDESTARS, MF_BYCOMMAND);
	ASSERT(uState != 0xFFFFFFFF);

	if((uState & MF_CHECKED) != 0)
	{
		uState = MF_UNCHECKED; // Toggle
		m_bPasswordStars = FALSE;
	}
	else
	{
		uState = MF_CHECKED; // Toggle
		m_bPasswordStars = TRUE;
	}

	m_menu.CheckMenuItem(ID_VIEW_HIDESTARS, MF_BYCOMMAND | uState);

	if(m_bPasswordStars == TRUE)
	{
		LV_ITEM lvi;
		ZeroMemory(&lvi, sizeof(LV_ITEM));
		lvi.mask = LVIF_TEXT;
		lvi.iSubItem = 3;
		lvi.pszText = PWM_PASSWORD_STRING;

		const int nItemCount = m_cList.GetItemCount();
		for(int nItem = 0; nItem < nItemCount; ++nItem)
		{
			lvi.iItem = nItem;
			m_cList.SetItem(&lvi);
		}
	}
	else RefreshPasswordList(); // Refresh list based on UUIDs

	// m_bCachedToolBarUpdate = TRUE;
	_UpdateToolBar(TRUE);
}

DWORD CPwSafeDlg::GetSelectedEntry()
{
	NotifyUserActivity();

	if(m_bFileOpen == FALSE) return DWORD_MAX;

	// LVIS_FOCUSED is not enough here, it must be LVIS_SELECTED
	POSITION posFirstItem = m_cList.GetFirstSelectedItemPosition();
	if(posFirstItem == NULL) return DWORD_MAX;

	return static_cast<DWORD>(m_cList.GetNextSelectedItem(posFirstItem));
}

std::vector<DWORD> CPwSafeDlg::GetSelectedEntriesUIIndices()
{
	std::vector<DWORD> v;
	if(m_bFileOpen == FALSE) return v;

	POSITION pos = m_cList.GetFirstSelectedItemPosition();
	while(pos != NULL)
	{
		v.push_back(static_cast<DWORD>(m_cList.GetNextSelectedItem(pos)));
	}

	return v;
}

std::vector<DWORD> CPwSafeDlg::GetSelectedEntriesMgrIndices()
{
	std::vector<DWORD> v = GetSelectedEntriesUIIndices();
	for(size_t i = 0; i < v.size(); ++i)
		v[i] = _ListSelToEntryIndex(v[i]);
	return v;
}

DWORD CPwSafeDlg::GetSelectedEntriesCount()
{
	NotifyUserActivity();

	// DWORD i, uSelectedItems = 0;
	// UINT uState;

	if(m_bFileOpen == FALSE) return DWORD_MAX;

	// for(i = 0; i < (DWORD)m_cList.GetItemCount(); i++)
	// {
	//	uState = m_cList.GetItemState((int)i, LVIS_SELECTED);
	//	if((uState & LVIS_SELECTED) != 0) uSelectedItems++;
	// }
	// return uSelectedItems;

	return static_cast<DWORD>(m_cList.GetSelectedCount());
}

DWORD CPwSafeDlg::GetSelectedGroupId()
{
	HTREEITEM h = m_cGroups.GetSelectedItem();
	if(h == NULL) return DWORD_MAX;

	return static_cast<DWORD>(m_cGroups.GetItemData(h));
}

void CPwSafeDlg::UpdateGroupList()
{
	NotifyUserActivity();

	HTREEITEM hParent = TVI_ROOT;
	HTREEITEM hLastItem = TVI_ROOT;
	DWORD usLevel = 0;

	const bool bLockedRedraw = m_cGroups.LockRedrawEx(true);
	GroupSyncStates(TRUE); // Synchronize 'expanded'-flag from GUI to list manager
	m_cGroups.SelectItem(NULL);
	m_cGroups.DeleteAllItemsEx();

	if(m_bFileOpen == FALSE) { if(bLockedRedraw) m_cGroups.LockRedrawEx(false); return; }

	TVINSERTSTRUCT tvis;
	ZeroMemory(&tvis, sizeof(TVINSERTSTRUCT));
	tvis.hInsertAfter = TVI_LAST;
	tvis.item.mask = (TVIF_IMAGE | TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_SELECTEDIMAGE);

	DWORD i;
	for(i = 0; i < m_mgr.GetNumberOfGroups(); ++i)
	{
		PW_GROUP *pgrp = m_mgr.GetGroup(i);
		ASSERT(pgrp != NULL); if(pgrp == NULL) continue;
		ASSERT(pgrp->pszGroupName != NULL);

		while(true)
		{
			if(usLevel == pgrp->usLevel) break;
			else if(usLevel == static_cast<USHORT>(pgrp->usLevel - 1))
			{
				hParent = hLastItem;
				++usLevel;
			}
			else if(usLevel < pgrp->usLevel) { ASSERT(FALSE); hParent = TVI_ROOT; break; }
			else if(usLevel > pgrp->usLevel)
			{
				if(hParent == TVI_ROOT) break;

				hParent = m_cGroups.GetParentItem(hParent);
				--usLevel;
			}
		}

		tvis.hParent = hParent;

		tvis.item.pszText = pgrp->pszGroupName;
		tvis.item.iSelectedImage = tvis.item.iImage = static_cast<int>(pgrp->uImageId);
		tvis.item.lParam = pgrp->uGroupId;
		tvis.item.stateMask = TVIS_EXPANDED;
		tvis.item.state = (((pgrp->dwFlags & PWGF_EXPANDED) != 0) ? TVIS_EXPANDED : 0);

		hLastItem = m_cGroups.InsertItem(&tvis);
	}

	if(bLockedRedraw) m_cGroups.LockRedrawEx(false, true);

	GroupSyncStates(FALSE); // Expand all tree items that were open before
	if(i > 0) m_cGroups.SelectItem(m_cGroups.GetRootItem());
}

void CPwSafeDlg::UpdatePasswordList()
{
	NotifyUserActivity();

	if(m_bFileOpen == FALSE) { m_cList.DeleteAllItemsEx(); return; }

	const DWORD dwGroupId = GetSelectedGroupId();
	if(dwGroupId == DWORD_MAX) { m_cList.DeleteAllItemsEx(); return; }

	if(m_bBlockPwListUpdate == TRUE) return;
	m_bBlockPwListUpdate = TRUE;
	_SortListIfAutoSort();
	m_bBlockPwListUpdate = FALSE;

	const bool bLockedRedraw = m_cList.LockRedrawEx(true);

	m_cList.DeleteAllItemsEx();
	m_bTANsOnly = TRUE;

	_UpdateCachedGroupIDs();

	PW_TIME tNow;
	_GetCurrentPwTime(&tNow);

	DWORD j = 0;
	for(DWORD i = 0; i < m_mgr.GetNumberOfEntries(); ++i)
	{
		PW_ENTRY *pwe = m_mgr.GetEntry(i);
		ASSERT_ENTRY(pwe);

		if(pwe != NULL)
		{
			if(pwe->uGroupId == dwGroupId)
			{
				_List_SetEntry(j, pwe, TRUE, &tNow);
				++j;
			}
		}
	}

	if(j == 0) m_bTANsOnly = FALSE; // Use report list view
	AdjustPwListMode();
	if(j == 0) m_bTANsOnly = TRUE; // Now set it to the correct value

	AdjustColumnWidths();

	if(bLockedRedraw) m_cList.LockRedrawEx(false, true);

	ShowEntryDetails(NULL);
}

void CPwSafeDlg::AdjustPwListMode()
{
	const DWORD dwStyle = m_cList.GetStyle();

	// Don't use SetRedraw and Invalidate in this function!

	if((m_bTANsOnly == TRUE) && (m_bSimpleTANView == TRUE))
	{
		if((dwStyle & LVS_REPORT) != 0) // Is in report display mode
		{
			m_dwPwListMode = LVSX_CHANGING;

			m_cList.ModifyStyle(LVS_REPORT, LVS_SMALLICON);

			m_dwPwListMode = LVS_SMALLICON;
			_SetColumnWidths();
			return;
		}
	}
	else // m_bTANsOnly == FALSE
	{
		if((dwStyle & LVS_LIST) != 0)
		{
			m_dwPwListMode = LVSX_CHANGING;

			m_cList.ModifyStyle(LVS_SMALLICON, LVS_REPORT);

			m_dwPwListMode = LVS_REPORT;
			_SetColumnWidths();
			return;
		}
	}
}

void CPwSafeDlg::AdjustColumnWidths()
{
	ASSERT(m_dwPwListMode != LVSX_CHANGING);
	if(m_dwPwListMode != LVS_SMALLICON) return;

	const int nItemCount = m_cList.GetItemCount();
	int nMaxWidth = 0;
	for(int i = 0; i < nItemCount; ++i)
	{
		const int n = m_cList.GetStringWidth(m_cList.GetItemText(i, 0));
		if(n > nMaxWidth) nMaxWidth = n;
	}

	nMaxWidth += 26;
	if(m_cList.GetColumnWidth(-1) != nMaxWidth)
		m_cList.SetColumnWidth(-1, nMaxWidth);
}

void CPwSafeDlg::_List_SetEntry(DWORD dwInsertPos, PW_ENTRY *pwe, BOOL bIsNewEntry, PW_TIME *ptNow)
{
	if((dwInsertPos == DWORD_MAX) && (bIsNewEntry == TRUE))
		dwInsertPos = static_cast<DWORD>(m_cList.GetItemCount());

	const BOOL bIsTAN = CPwUtil::IsTANEntry(pwe);
	BOOL bValidTANIndex = TRUE;

	if(bIsTAN == FALSE) m_bTANsOnly = FALSE;

	LV_ITEM lvi;
	ZeroMemory(&lvi, sizeof(LV_ITEM));
	lvi.iItem = static_cast<int>(dwInsertPos);
	lvi.iSubItem = 0;

	// Set 'expired' image if necessary
	DWORD uImageId;
	if(_pwtimecmp(&pwe->tExpire, ptNow) <= 0) uImageId = 45;
	else uImageId = pwe->uImageId;

	CString strTemp;

	lvi.mask = (LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM);
	if((m_bShowTitle == TRUE) || (bIsTAN == TRUE))
	{
		if((bIsTAN == FALSE) || (m_bShowTANIndices == FALSE))
			lvi.pszText = pwe->pszTitle;
		else
		{
			strTemp = pwe->pszTitle;

			if(pwe->pszUserName[0] != 0)
			{
				for(DWORD i = 0; ; ++i)
				{
					const TCHAR tch = pwe->pszUserName[i];

					if(tch == 0) break;
					else if((tch < _T('0')) || (tch > _T('9')))
					{
						bValidTANIndex = FALSE;
						break;
					}
				}

				if(bValidTANIndex == TRUE)
				{
					strTemp += _T(" (#");
					strTemp += pwe->pszUserName;
					strTemp += _T(")");
				}
			}

			lvi.pszText = const_cast<LPTSTR>((LPCTSTR)strTemp);
		}
	}
	else lvi.pszText = g_pNullString;

	lvi.iImage = uImageId;
	_SetLVItemParam(&lvi, pwe);

	if(bIsNewEntry == TRUE) m_cList.InsertItem(&lvi); // Add
	else m_cList.SetItem(&lvi); // Modify existing

	lvi.mask = LVIF_TEXT;
	lvi.iSubItem = 1;
	if(m_bShowUserName == TRUE)
	{
		// Hide usernames behind "********", if the user has selected this option
		if(m_bUserStars == TRUE)
		{
			lvi.pszText = const_cast<LPTSTR>(PWM_PASSWORD_STRING);
			m_cList.SetItem(&lvi);
		}
		else // Don't hide, display them
		{
			lvi.pszText = pwe->pszUserName;
			m_cList.SetItem(&lvi);
		}
	}
	else
	{
		lvi.pszText = g_pNullString;
		m_cList.SetItem(&lvi);
	}

	lvi.iSubItem = 2;
	if(m_bShowURL == TRUE) lvi.pszText = pwe->pszURL;
	else lvi.pszText = g_pNullString;
	m_cList.SetItem(&lvi);

	lvi.iSubItem = 3;
	if(m_bShowPassword == TRUE)
	{
		// Hide passwords behind "********", if the user has selected this option
		if(m_bPasswordStars == TRUE)
		{
			lvi.pszText = const_cast<LPTSTR>(PWM_PASSWORD_STRING);
			m_cList.SetItem(&lvi);
		}
		else // Don't hide, display them
		{
			m_mgr.UnlockEntryPassword(pwe);
			lvi.pszText = pwe->pszPassword;
			m_cList.SetItem(&lvi);
			m_mgr.LockEntryPassword(pwe);
		}
	}
	else // Hide passwords completely
	{
		lvi.pszText = g_pNullString;
		m_cList.SetItem(&lvi);
	}

	lvi.iSubItem = 4;
	if(m_bShowNotes == TRUE)
	{
		strTemp = pwe->pszAdditional;

		POSITION pFmt = m_lNotesFormat.GetHeadPosition();
		for(POSITION pRegex = m_lNotesRegex.GetHeadPosition(); pRegex != NULL; )
		{
			const CString strRegex = m_lNotesRegex.GetNext(pRegex);
			const CString strFormat = m_lNotesFormat.GetNext(pFmt);

			try
			{
				boost::tregex btrExpr(strRegex);
				strTemp = boost::regex_replace(strTemp, btrExpr, strFormat);
			}
			catch(...) { ASSERT(FALSE); }
		}

		for(std::map<std_string, std_string>::const_iterator it =
			m_mHtmlToRtf.begin(); it != m_mHtmlToRtf.end(); ++it)
			strTemp.Replace(it->first.c_str(), _T(""));

		// Remove newline and break character for better display
		const int nNotesLength = strTemp.GetLength();
		for(int t = 0; t < nNotesLength; ++t)
		{
			const TCHAR tch = strTemp.GetAt(t);
			if((tch == _T('\r')) || (tch == _T('\n')) || (tch == _T('\t')))
				strTemp.SetAt(t, _T(' '));
		}

		lvi.pszText = const_cast<LPTSTR>((LPCTSTR)strTemp);
	}
	else lvi.pszText = g_pNullString;
	m_cList.SetItem(&lvi);

	lvi.iSubItem = 5;
	if(m_bShowCreation == TRUE)
	{
		_PwTimeToStringEx(pwe->tCreation, strTemp, CPwSafeDlg::m_bUseLocalTimeFormat);
		lvi.pszText = const_cast<LPTSTR>((LPCTSTR)strTemp);
	}
	else lvi.pszText = g_pNullString;
	m_cList.SetItem(&lvi);

	lvi.iSubItem = 6;
	if(m_bShowLastMod == TRUE)
	{
		_PwTimeToStringEx(pwe->tLastMod, strTemp, CPwSafeDlg::m_bUseLocalTimeFormat);
		lvi.pszText = const_cast<LPTSTR>((LPCTSTR)strTemp);
	}
	else lvi.pszText = g_pNullString;
	m_cList.SetItem(&lvi);

	lvi.iSubItem = 7;
	if(m_bShowLastAccess == TRUE)
	{
		_PwTimeToStringEx(pwe->tLastAccess, strTemp, CPwSafeDlg::m_bUseLocalTimeFormat);
		lvi.pszText = const_cast<LPTSTR>((LPCTSTR)strTemp);
	}
	else lvi.pszText = g_pNullString;
	m_cList.SetItem(&lvi);

	lvi.iSubItem = 8;
	if(m_bShowExpire == TRUE)
	{
		if(memcmp(&pwe->tExpire, &g_tNeverExpire, sizeof(PW_TIME)) == 0)
			lvi.pszText = const_cast<LPTSTR>(g_psztNeverExpires);
		else
		{
			_PwTimeToStringEx(pwe->tExpire, strTemp, CPwSafeDlg::m_bUseLocalTimeFormat);
			lvi.pszText = const_cast<LPTSTR>((LPCTSTR)strTemp);
		}
	}
	else lvi.pszText = g_pNullString;
	m_cList.SetItem(&lvi);

	// Ignore m_bShowUUID, the UUID field is needed in all cases
	lvi.iSubItem = 9;
	_UuidToString(pwe->uuid, &strTemp);
	lvi.pszText = const_cast<LPTSTR>((LPCTSTR)strTemp);
	m_cList.SetItem(&lvi);

	lvi.iSubItem = 10;
	if(m_bShowAttach == TRUE) lvi.pszText = pwe->pszBinaryDesc;
	else lvi.pszText = g_pNullString;
	m_cList.SetItem(&lvi);
}

void CPwSafeDlg::RefreshPasswordList()
{
	NotifyUserActivity();

	TCHAR szTemp[1024];
	BYTE aUuid[16];

	if(m_bFileOpen == FALSE) return;

	PW_TIME tNow;
	_GetCurrentPwTime(&tNow);

	_UpdateCachedGroupIDs();

	const bool bLockedRedraw = m_cList.LockRedrawEx(true);

	const int nItemCount = m_cList.GetItemCount();
	DWORD j = 0;
	for(int i = 0; i < nItemCount; ++i)
	{
		LV_ITEM lvi;
		ZeroMemory(&lvi, sizeof(LV_ITEM));
		lvi.iItem = i;
		lvi.iSubItem = 9;
		lvi.mask = LVIF_TEXT;
		lvi.cchTextMax = 1024;
		lvi.pszText = szTemp;
		m_cList.GetItem(&lvi);

		_StringToUuid(lvi.pszText, aUuid);
		PW_ENTRY *pwe = m_mgr.GetEntryByUuid(aUuid);
		ASSERT_ENTRY(pwe);

		if(pwe != NULL)
		{
			_List_SetEntry(j, pwe, FALSE, &tNow);
			++j;
		}
	}

	_SortListIfAutoSort();
	AdjustPwListMode();
	AdjustColumnWidths();

	if(bLockedRedraw) m_cList.LockRedrawEx(false, true);
}

void CPwSafeDlg::OnPwlistAdd()
{
	NotifyUserActivity();

	DWORD uGroupId = GetSelectedGroupId();
	PW_ENTRY pwTemplate;
	PW_TIME tNow;
	BYTE aUuid[16];

	if(m_bFileOpen == FALSE) return;
	if(uGroupId == DWORD_MAX) return; // No group selected or other error

	CAddEntryDlg dlg;
	dlg.m_pMgr = &m_mgr;
	dlg.m_dwEntryIndex = DWORD_MAX;
	dlg.m_pParentIcons = &m_ilIcons;
	dlg.m_bStars = m_bPasswordStars;
	dlg.m_nGroupId = static_cast<int>(m_mgr.GetGroupByIdN(uGroupId)); // m_nGroupId of the dialog is an index, not an ID
	dlg.m_bEditMode = FALSE;
	dlg.m_dwDefaultExpire = m_dwDefaultExpire;
	dlg.m_strUserName = m_mgr.GetPropertyString(PWP_DEFAULT_USER_NAME).c_str();
	if(m_fNotesFont.m_hObject != NULL) dlg.m_pfNotes = &m_fNotesFont;

	dlg.m_nIconId = 0;
	PW_GROUP *pgContainer = m_mgr.GetGroupById(uGroupId);
	if(pgContainer != NULL)
	{
		if((pgContainer->uImageId != PWM_STD_ICON_GROUP) &&
			(pgContainer->uImageId != PWM_STD_ICON_GROUP_OPEN) &&
			(pgContainer->uImageId != PWM_STD_ICON_GROUP_PKG) &&
			(pgContainer->uImageId != PWM_STD_ICON_GROUP_EMAIL) &&
			(pgContainer->uImageId != DWORD_MAX))
		{
			dlg.m_nIconId = (int)pgContainer->uImageId;
		}
	}

	DWORD dwInitialGroup = uGroupId; // ID

	if(m_dwDefaultExpire == 0) m_mgr.GetNeverExpireTime(&dlg.m_tExpire);
	else
	{
		CTime t = CTime::GetCurrentTime();
		t += CTimeSpan((LONG)m_dwDefaultExpire, 0, 0, 0);

		dlg.m_tExpire.btDay = static_cast<BYTE>(t.GetDay());
		dlg.m_tExpire.btHour = static_cast<BYTE>(t.GetHour());
		dlg.m_tExpire.btMinute = static_cast<BYTE>(t.GetMinute());
		dlg.m_tExpire.btMonth = static_cast<BYTE>(t.GetMonth());
		dlg.m_tExpire.btSecond = static_cast<BYTE>(t.GetSecond());
		dlg.m_tExpire.shYear = static_cast<USHORT>(t.GetYear());
	}

	if(_CallPlugins(KPM_ADD_ENTRY_PRE, 0, 0) == FALSE) return;

	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		ASSERT(dlg.m_lpPassword != NULL); if(dlg.m_lpPassword == NULL) { dlg.m_lpPassword = new TCHAR[2]; dlg.m_lpPassword[0] = 0; }
		ASSERT(dlg.m_lpRepeatPw != NULL); if(dlg.m_lpRepeatPw == NULL) { dlg.m_lpRepeatPw = new TCHAR[2]; dlg.m_lpRepeatPw[0] = 0; }

		_GetCurrentPwTime(&tNow);
		memset(&pwTemplate, 0, sizeof(PW_ENTRY));
		pwTemplate.pszAdditional = const_cast<LPTSTR>((LPCTSTR)dlg.m_strNotes);
		pwTemplate.pszPassword = dlg.m_lpPassword;
		pwTemplate.pszTitle = const_cast<LPTSTR>((LPCTSTR)dlg.m_strTitle);
		pwTemplate.pszURL = const_cast<LPTSTR>((LPCTSTR)dlg.m_strURL);
		pwTemplate.pszUserName = const_cast<LPTSTR>((LPCTSTR)dlg.m_strUserName);
		pwTemplate.tCreation = tNow;
		pwTemplate.tExpire = dlg.m_tExpire;
		pwTemplate.tLastAccess = tNow;
		pwTemplate.tLastMod = tNow;
		pwTemplate.uGroupId = m_mgr.GetGroupIdByIndex((DWORD)dlg.m_nGroupId);
		pwTemplate.uImageId = static_cast<DWORD>(dlg.m_nIconId);
		pwTemplate.uPasswordLen = static_cast<DWORD>(_tcslen(pwTemplate.pszPassword));
		pwTemplate.pszBinaryDesc = _T("");

		if(_CallPlugins(KPM_ADD_ENTRY, (LPARAM)&pwTemplate, 0) == FALSE) return;

		// Add the entry to the password manager
		VERIFY(m_mgr.AddEntry(&pwTemplate));

		CSecureEditEx::DeletePassword(dlg.m_lpPassword); dlg.m_lpPassword = NULL;
		CSecureEditEx::DeletePassword(dlg.m_lpRepeatPw); dlg.m_lpRepeatPw = NULL;

		PW_ENTRY *pNew = m_mgr.GetLastEditedEntry();
		memcpy(aUuid, pNew->uuid, 16);

		const int nAttachLen = dlg.m_strAttachment.GetLength();
		const int nEscapeLen = static_cast<int>(_tcslen(PWS_NEW_ATTACHMENT));

		if(nAttachLen > nEscapeLen)
		{
			if(dlg.m_strAttachment.Left(nEscapeLen) == CString(PWS_NEW_ATTACHMENT))
			{
				CString strFile = dlg.m_strAttachment.Right(
					dlg.m_strAttachment.GetLength() - nEscapeLen);
				const int e = CPwUtil::AttachFileAsBinaryData(pNew, strFile);
				if(e != PWE_SUCCESS) _ReportAttachError(e, strFile);
			}
		}

		// Add the password to the GUI, but only if it's visible
		if(pNew->uGroupId == dwInitialGroup) // dwInitialGroup is an ID
		{
			_UpdateCachedGroupIDs();
			_List_SetEntry(DWORD_MAX, pNew, TRUE, &tNow); // No unlock needed

			_SortListIfAutoSort();

			AdjustPwListMode();
			AdjustColumnWidths();

			if(m_nAutoSort == 0)
			{
				m_cList.EnsureVisible(m_cList.GetItemCount() - 1, FALSE);
				m_cList.FocusItem(m_cList.GetItemCount() - 1, TRUE);
			}
			else
			{
				const DWORD dw = _EntryUuidToListPos(aUuid); ASSERT(dw != DWORD_MAX);
				if(dw != DWORD_MAX)
				{
					m_cList.EnsureVisible(static_cast<int>(dw), FALSE);
					m_cList.FocusItem(static_cast<int>(dw), TRUE);
				}
			}
		}

		m_bModified = TRUE; // Didn't we? :)
	}

	// Cleanup dialog data
	EraseCString(&dlg.m_strTitle);
	EraseCString(&dlg.m_strUserName);
	EraseCString(&dlg.m_strURL);
	EraseCString(&dlg.m_strNotes);
	EraseCString(&dlg.m_strAttachment);

	_UpdateToolBar();
}

DWORD CPwSafeDlg::_ListSelToEntryIndex(DWORD dwSelected)
{
	DWORD dwSel;
	if(dwSelected == DWORD_MAX) dwSel = GetSelectedEntry();
	else dwSel = dwSelected;

	if(dwSel == DWORD_MAX) return DWORD_MAX;

	TCHAR tszTemp[40];

	LV_ITEM lvi;
	ZeroMemory(&lvi, sizeof(LV_ITEM));
	lvi.iItem = static_cast<int>(dwSel);
	lvi.iSubItem = 9;
	lvi.mask = LVIF_TEXT;
	lvi.pszText = tszTemp;
	lvi.cchTextMax = 40; // Including terminating NULL
	if(m_cList.GetItem(&lvi) == FALSE) return DWORD_MAX;

	BYTE aUuid[16];
	_StringToUuid(lvi.pszText, aUuid);

	dwSel = m_mgr.GetEntryByUuidN(aUuid);
	ASSERT(dwSel != DWORD_MAX);
	return dwSel;
}

void CPwSafeDlg::OnPwlistEdit()
{
	NotifyUserActivity();

	DWORD dwEntryIndex;
	PW_ENTRY *pEntry;
	DWORD dwNewGroupId;
	BOOL bNeedFullUpdate = FALSE, bNeedGroupUpdate = FALSE;
	int nModifyLevel = 0;
	DWORD dwSelectedEntry = GetSelectedEntry();
	BYTE aUuid[16];
	PW_ENTRY pweBackup;
	BOOL bDoBackup = FALSE;

	if(m_bFileOpen == FALSE) return;
	if(dwSelectedEntry == DWORD_MAX) return;

	dwEntryIndex = _ListSelToEntryIndex(dwSelectedEntry);
	ASSERT(dwEntryIndex != DWORD_MAX); if(dwEntryIndex == DWORD_MAX) return;

	pEntry = m_mgr.GetEntry(dwEntryIndex);
	ASSERT_ENTRY(pEntry); if(pEntry == NULL) return;

	CAddEntryDlg dlg;
	dlg.m_pMgr = &m_mgr;
	dlg.m_pOriginalEntry = pEntry;
	dlg.m_dwEntryIndex = dwEntryIndex;
	dlg.m_pParentIcons = &m_ilIcons;
	dlg.m_bEditMode = TRUE;
	dlg.m_bStars = m_bPasswordStars;
	dlg.m_dwDefaultExpire = m_dwDefaultExpire;
	if(m_fNotesFont.m_hObject != NULL) dlg.m_pfNotes = &m_fNotesFont;

	dlg.m_nGroupId = (int)m_mgr.GetGroupByIdN(pEntry->uGroupId); // ID to index
	dlg.m_strTitle = pEntry->pszTitle;
	dlg.m_strUserName = pEntry->pszUserName;
	dlg.m_strURL = pEntry->pszURL;
	dlg.m_strNotes = pEntry->pszAdditional;
	dlg.m_strAttachment = pEntry->pszBinaryDesc; // Copy binary description
	dlg.m_nIconId = (int)pEntry->uImageId;
	dlg.m_tExpire = pEntry->tExpire; // Copy expiration time
	m_mgr.UnlockEntryPassword(pEntry); // We must unlock the entry, otherwise we cannot access the password
	dlg.m_lpPassword = _TcsCryptDupAlloc(pEntry->pszPassword);
	dlg.m_lpRepeatPw = _TcsCryptDupAlloc(pEntry->pszPassword);
	m_mgr.LockEntryPassword(pEntry);

	if(_CallPlugins(KPM_EDIT_ENTRY_PRE, 0, 0) == FALSE) return;

	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		m_mgr.UnlockEntryPassword(pEntry); ASSERT_ENTRY(pEntry);

		if(dlg.m_strTitle != pEntry->pszTitle) nModifyLevel = 2;
		else if(dlg.m_strUserName != pEntry->pszUserName) nModifyLevel = 2;
		else if(dlg.m_strURL != pEntry->pszURL) nModifyLevel = 2;
		else if(dlg.m_strNotes != pEntry->pszAdditional) nModifyLevel = 2;
		else if(dlg.m_strAttachment != pEntry->pszBinaryDesc) nModifyLevel = 2;
		else if(_pwtimecmp(&dlg.m_tExpire, &pEntry->tExpire) != 0) nModifyLevel = 2;
		else if(_tcscmp(dlg.m_lpPassword, pEntry->pszPassword) != 0) nModifyLevel = 2;
		else if(dlg.m_nGroupId != (int)m_mgr.GetGroupByIdN(pEntry->uGroupId)) nModifyLevel = 1;
		else if(dlg.m_nIconId != (int)pEntry->uImageId) nModifyLevel = 1;

		if((nModifyLevel == 2) && (m_bBackupEntries == TRUE))
			bDoBackup = CPwUtil::MemAllocCopyEntry(pEntry, &pweBackup);

		m_mgr.LockEntryPassword(pEntry);

		PW_ENTRY pwTemplate = *pEntry;

		PW_TIME tNow;
		_GetCurrentPwTime(&tNow);

		pwTemplate.pszAdditional = const_cast<LPTSTR>((LPCTSTR)dlg.m_strNotes);
		pwTemplate.pszPassword = dlg.m_lpPassword;
		pwTemplate.pszTitle = const_cast<LPTSTR>((LPCTSTR)dlg.m_strTitle);
		pwTemplate.pszURL = const_cast<LPTSTR>((LPCTSTR)dlg.m_strURL);
		pwTemplate.pszUserName = const_cast<LPTSTR>((LPCTSTR)dlg.m_strUserName);
		// pwTemplate.tCreation = pEntry->tCreation;
		pwTemplate.tExpire = dlg.m_tExpire;
		pwTemplate.tLastAccess = tNow;
		pwTemplate.tLastMod = tNow;
		pwTemplate.uImageId = static_cast<DWORD>(dlg.m_nIconId);
		pwTemplate.uPasswordLen = static_cast<DWORD>(_tcslen(pwTemplate.pszPassword));

		// If the entry has been moved to a different group, a full
		// update of the list is required
		dwNewGroupId = m_mgr.GetGroupIdByIndex(static_cast<DWORD>(dlg.m_nGroupId));
		if(dwNewGroupId != pwTemplate.uGroupId) bNeedFullUpdate = TRUE;
		pwTemplate.uGroupId = dwNewGroupId;

		VERIFY(m_mgr.SetEntry(dwEntryIndex, &pwTemplate));

		CSecureEditEx::DeletePassword(dlg.m_lpPassword); dlg.m_lpPassword = NULL;
		CSecureEditEx::DeletePassword(dlg.m_lpRepeatPw); dlg.m_lpRepeatPw = NULL;

		const int nAttachLen = dlg.m_strAttachment.GetLength();
		const int nEscapeLen = (int)_tcslen(PWS_NEW_ATTACHMENT);

		if((nAttachLen == nEscapeLen) && (dlg.m_strAttachment == CString(PWS_NEW_ATTACHMENT)))
			CPwUtil::RemoveBinaryData(m_mgr.GetEntry(dwEntryIndex));
		else if(nAttachLen > nEscapeLen)
		{
			if(dlg.m_strAttachment.Left(nEscapeLen) == CString(PWS_NEW_ATTACHMENT))
			{
				CString strFile = dlg.m_strAttachment.Right(
					dlg.m_strAttachment.GetLength() - nEscapeLen);
				const int e = CPwUtil::AttachFileAsBinaryData(m_mgr.GetEntry(
					dwEntryIndex), strFile);
				if(e != PWE_SUCCESS) _ReportAttachError(e, strFile);
			}
		}

		PW_ENTRY *pBase = m_mgr.GetEntry(dwEntryIndex); ASSERT_ENTRY(pBase);
		memcpy(aUuid, pBase->uuid, 16);

		if(bDoBackup == TRUE)
		{
			if(m_mgr.BackupEntry(&pweBackup, &bNeedGroupUpdate) == FALSE)
				MessageBox(TRL("Failed to create backup of this entry!"), TRL("An error occurred"), MB_ICONWARNING | MB_OK);

			CPwUtil::MemFreeEntry(&pweBackup);
		}

		if(bNeedGroupUpdate == TRUE)
		{
			_Groups_SaveView(TRUE);
			UpdateGroupList();
			_Groups_RestoreView();
		}

		if(bNeedFullUpdate == TRUE) // Full list update(!) needed
		{
			_List_SaveView();
			UpdatePasswordList(); // Refresh is not enough!
			_List_RestoreView();
		}
		else // Just update the selected item, not the whole list
		{
			_UpdateCachedGroupIDs();

			PW_ENTRY *pUpdated = m_mgr.GetEntry(dwEntryIndex); ASSERT(pUpdated != NULL);
			_List_SetEntry(dwSelectedEntry, pUpdated, FALSE, &tNow);

			AdjustPwListMode();
			AdjustColumnWidths();
		}

		_SortListIfAutoSort();
		const DWORD dwListEntry = _EntryUuidToListPos(aUuid);
		if(dwListEntry != DWORD_MAX)
		{
			m_cList.EnsureVisible(static_cast<int>(dwListEntry), FALSE);
			m_cList.FocusItem(static_cast<int>(dwListEntry), TRUE);
		}

		if(nModifyLevel != 0) m_bModified = TRUE;
	}
	else _TouchEntry(GetSelectedEntry(), FALSE); // User had viewed it only

	// Cleanup dialog data
	EraseCString(&dlg.m_strTitle);
	EraseCString(&dlg.m_strUserName);
	EraseCString(&dlg.m_strURL);
	EraseCString(&dlg.m_strNotes);

	m_ullLastListParams = 0; // Invalidate
	_UpdateToolBar();
}

void CPwSafeDlg::OnPwlistDelete()
{
	NotifyUserActivity();

	if(m_bFileOpen == FALSE) return;
	// if(m_dwLastNumSelectedItems == 0) return;

	_SetDisplayDialog(true);

	if(_CallPlugins(KPM_DELETE_ENTRY_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }

	const std::vector<DWORD> vIdx = GetSelectedEntriesMgrIndices();
	const std::basic_string<TCHAR> strList = CEntryUtil::CreateSummaryList(&m_mgr, vIdx);

	std::basic_string<TCHAR> strContent = TRL("This will remove all selected entries unrecoverably!");
	if(strList.size() > 0)
	{
		strContent += _T("\r\n\r\n");
		strContent += strList;
	}

	CVistaTaskDialog dlgTask(this->m_hWnd, AfxGetInstanceHandle(), false);
	dlgTask.SetContent(strContent.c_str());
	dlgTask.SetMainInstruction(TRL("Are you sure you want to delete all selected entries?"));
	dlgTask.SetWindowTitle(PWM_PRODUCT_NAME_SHORT);
	dlgTask.SetIcon(MTDI_QUESTION);
	dlgTask.AddButton(TRL("&Delete"), NULL, IDOK);
	dlgTask.AddButton(TRL("Cancel"), NULL, IDCANCEL);
	int nMsg = dlgTask.ShowDialog();

	if(nMsg < 0) // No task dialog support?
	{
		CString str;
		str = TRL("This will remove all selected entries unrecoverably!");
		str += _T("\r\n\r\n");
		str += TRL("Are you sure you want to delete all selected entries?");
		if(strList.size() > 0)
		{
			str += _T("\r\n\r\n");
			str += strList.c_str();
		}
		nMsg = MessageBox(str, TRL("Delete Entries Confirmation"), MB_ICONQUESTION | MB_YESNO);
	}
	if((nMsg == IDCANCEL) || (nMsg == IDNO)) { _SetDisplayDialog(false); return; }

	const DWORD dwInvGroup1 = m_mgr.GetGroupId(PWS_BACKUPGROUP);
	const DWORD dwInvGroup2 = m_mgr.GetGroupId(PWS_BACKUPGROUP_SRC);

	BOOL bNeedGroupUpdate = FALSE;
	while(true)
	{
		const DWORD dwSel = GetSelectedEntry();
		if(dwSel == DWORD_MAX) break;

		const DWORD dwIndex = _ListSelToEntryIndex(dwSel);
		ASSERT(dwIndex != DWORD_MAX); if(dwIndex == DWORD_MAX) break;

		if(m_bBackupEntries != FALSE)
		{
			PW_ENTRY *p = m_mgr.GetEntry(dwIndex);
			ASSERT(p != NULL); if(p == NULL) break;

			if((p->uGroupId != dwInvGroup1) && (p->uGroupId != dwInvGroup2))
			{
				m_mgr.UnlockEntryPassword(p);
				BOOL b = FALSE;
				m_mgr.BackupEntry(p, &b);
				bNeedGroupUpdate |= b;
				m_mgr.LockEntryPassword(p);
			}
		}

		VERIFY(m_mgr.DeleteEntry(dwIndex)); // Delete from password manager
		VERIFY(m_cList.DeleteItem(static_cast<int>(dwSel))); // Delete from GUI
	}

	if(bNeedGroupUpdate == TRUE)
	{
		_Groups_SaveView(TRUE);
		UpdateGroupList();
		_Groups_RestoreView();
	}

	m_cList.RedrawWindow();
	m_bModified = TRUE;

	ShowEntryDetails(NULL);

	_UpdateToolBar();
	_SetDisplayDialog(false);
}

void CPwSafeDlg::RebuildContextMenus()
{
	DeleteContextMenus();

	m_pPwListMenu = new BCMenu();
	m_pPwListMenu->LoadMenu(IDR_PWLIST_MENU);

	m_pPwListMenu->SetMenuDrawMode(BCMENU_DRAWMODE_XP); // <<<!=>>> BCMENU_DRAWMODE_ORIGINAL
	m_pPwListMenu->SetSelectDisableMode(FALSE);
	m_pPwListMenu->SetXPBitmap3D(TRUE);
	m_pPwListMenu->SetBitmapBackground(RGB(255, 0, 255));
	m_pPwListMenu->SetIconSize(16, 16);

	m_pPwListMenu->LoadToolbar(IDR_INFOICONS, IDB_INFOICONS_EX);

	SetToMiniModeIfEnabled(m_pPwListMenu, FALSE, FALSE);

	m_pPwListTrackableMenu = NewGUI_GetBCMenu(m_pPwListMenu->GetSubMenu(0));
	_TranslateMenu(m_pPwListTrackableMenu, TRUE, &m_bCopyURLs);

	/////////////////////////////////////////////////////////////////////////

	m_pGroupListMenu = new BCMenu();
	m_pGroupListMenu->LoadMenu(IDR_GROUPLIST_MENU);

	m_pGroupListMenu->SetMenuDrawMode(BCMENU_DRAWMODE_XP); // <<<!=>>> BCMENU_DRAWMODE_ORIGINAL
	m_pGroupListMenu->SetSelectDisableMode(FALSE);
	m_pGroupListMenu->SetXPBitmap3D(TRUE);
	m_pGroupListMenu->SetBitmapBackground(RGB(255, 0, 255));
	m_pGroupListMenu->SetIconSize(16, 16);

	m_pGroupListMenu->LoadToolbar(IDR_INFOICONS, IDB_INFOICONS_EX);

	m_pGroupListTrackableMenu = NewGUI_GetBCMenu(m_pGroupListMenu->GetSubMenu(0));

	if(m_bMiniMode == TRUE)
		m_pGroupListTrackableMenu->DeleteMenu(_T("&Export Group"));

	_TranslateMenu(m_pGroupListTrackableMenu, TRUE, NULL);

	/////////////////////////////////////////////////////////////////////////

	m_pEntryViewMenu = new BCMenu();
	m_pEntryViewMenu->LoadMenu(IDR_RECTX_MENU);

	m_pEntryViewMenu->SetMenuDrawMode(BCMENU_DRAWMODE_XP); // <<<!=>>> BCMENU_DRAWMODE_ORIGINAL
	m_pEntryViewMenu->SetSelectDisableMode(FALSE);
	m_pEntryViewMenu->SetXPBitmap3D(TRUE);
	m_pEntryViewMenu->SetBitmapBackground(RGB(255, 0, 255));
	m_pEntryViewMenu->SetIconSize(16, 16);

	m_pEntryViewMenu->LoadToolbar(IDR_INFOICONS, IDB_INFOICONS_EX);

	m_pEntryViewTrackableMenu = NewGUI_GetBCMenu(m_pEntryViewMenu->GetSubMenu(0));
	_TranslateMenu(m_pEntryViewTrackableMenu, TRUE, NULL);
}

void CPwSafeDlg::DeleteContextMenus()
{
	if(m_pPwListMenu != NULL)
	{
		m_pPwListMenu->DestroyMenu();
		delete m_pPwListMenu;
		m_pPwListMenu = NULL;
	}
	m_pPwListTrackableMenu = NULL;

	if(m_pGroupListMenu != NULL)
	{
		m_pGroupListMenu->DestroyMenu();
		delete m_pGroupListMenu;
		m_pGroupListMenu = NULL;
	}
	m_pGroupListTrackableMenu = NULL;

	if(m_pEntryViewMenu != NULL)
	{
		m_pEntryViewMenu->DestroyMenu();
		delete m_pEntryViewMenu;
		m_pEntryViewMenu = NULL;
	}
	m_pEntryViewTrackableMenu = NULL;
}

void CPwSafeDlg::OnClickGroupList(NMHDR* pNMHDR, LRESULT* pResult)
{
	NotifyUserActivity();

	UNREFERENCED_PARAMETER(pNMHDR);
	*pResult = 0;

	POINT pt;
	GetCursorPos(&pt);
	m_cGroups.ScreenToClient(&pt);
	HTREEITEM h = m_cGroups.HitTest(CPoint(pt));
	if(h == NULL) return;
	m_cGroups.SelectItem(h); // Select the item the user pointed to

	// Remove the search group because we cannot handle it like a normal group
	_Groups_SaveView(TRUE);
	DWORD_PTR dwCachedFirst = m_dwGroupsSaveFirstVisible;
	DWORD_PTR dwCachedSelected = m_cGroups.GetItemData(h); // (h == NULL) => DWORD_MAX
	if(_RemoveSearchGroup() == TRUE)
	{
		// UpdateGroupList();
		m_dwGroupsSaveFirstVisible = dwCachedFirst;
		m_dwGroupsSaveSelected = dwCachedSelected;
		_Groups_RestoreView();
	}

	UpdatePasswordList();
	_UpdateToolBar(TRUE);
}

void CPwSafeDlg::OnPwlistCopyPw()
{
	NotifyUserActivity();
	_SetDisplayDialog(true);
	if(_CallPlugins(KPM_PW_COPY, 0, 0) == FALSE) { _SetDisplayDialog(false); return; }
	_SetDisplayDialog(false);

	const DWORD dwIndex = _ListSelToEntryIndex();
	ASSERT(dwIndex != DWORD_MAX); if(dwIndex == DWORD_MAX) return;

	PW_ENTRY *p = m_mgr.GetEntry(dwIndex);
	ASSERT_ENTRY(p); if(p == NULL) return;

	if(m_nClipboardMethod == CM_TIMED)
	{
		m_mgr.UnlockEntryPassword(p);
		CString strPwCopy = p->pszPassword;
		m_mgr.LockEntryPassword(p);

		CopyStringToClipboard(strPwCopy, p, &m_mgr, this->GetSafeHwnd());
		EraseCString(&strPwCopy);
		SetStatusTextEx(TRL("Field copied to clipboard."));

		m_nClipboardCountdown = static_cast<int>(m_dwClipboardSecs);
	}
	else if(m_nClipboardMethod == CM_ENHSECURE)
	{
		m_nClipboardState = CLIPBOARD_DELAYED_PASSWORD;
		MakeClipboardDelayRender(m_hWnd, &m_hwndNextViewer);

		CString str = TRL("Field copied to clipboard.");
		str += _T(" ");
		str += TRL("Waiting for paste command.");
		SetStatusTextEx(str);
	}

	DropToBackgroundIfOptionEnabled(false);

	if(CPwUtil::IsTANEntry(p) == TRUE) // If it is a TAN entry, expire it
	{
		_PostUseTANEntry(GetSelectedEntry(), dwIndex);
		p = NULL; // p may now be invalid!
	}

	_TouchEntry(GetSelectedEntry(), FALSE);
	_UpdateToolBar(TRUE);
}

void CPwSafeDlg::OnTimer(WPARAM nIDEvent)
{
	if((nIDEvent == APPWND_TIMER_ID_UPDATER))
	{
		if(m_bCachedToolBarUpdate)
		{
			m_bCachedToolBarUpdate = FALSE;
			_UpdateToolBar();
		}

		if(m_bCachedPwlistUpdate)
		{
			m_bCachedPwlistUpdate = FALSE;
			UpdatePasswordList();
			_UpdateToolBar(TRUE);
		}
	}
	else if(nIDEvent == APPWND_TIMER_ID)
	{
		if(m_nClipboardCountdown != -1)
		{
			--m_nClipboardCountdown;

			if(m_nClipboardCountdown == -1)
			{
				SetStatusTextEx(TRL("Ready."));
				ClearClipboardIfOwner();
			}
			else if(m_nClipboardCountdown == 0)
				SetStatusTextEx(TRL("Clipboard cleared."));
			else
			{
				CString str;
				str.Format(TRL("Field copied to clipboard. Clipboard will be cleared in %d seconds."), m_nClipboardCountdown);
				SetStatusTextEx(str);
			}
		}

		if((m_bLocked == FALSE) && (m_bFileOpen == TRUE) && !_IsDisplayingDialog())
		{
			if(m_nLockTimeDef != -1)
			{
				// if(m_nLockCountdown != 0)
				// {

				// TryEnterCriticalSection apparently is available on Windows 98,
				// but it doesn't do anything
				const bool b9x = (AU_IsWin9xSystem() != FALSE);
				if(b9x || (TryEnterCriticalSection(CPwSafeApp::GetLockTimerCS()) != FALSE))
				{
					// --m_nLockCountdown;
					// if(m_nLockCountdown == 0)

					const UINT64 uCurTimeUtc = _GetCurrentTimeUtc();
					if(uCurTimeUtc >= m_uLockAt)
					{
						if(m_bExitInsteadOfLockAT == FALSE)
						{
							OnFileLock();

							if((m_bLocked == TRUE) && (m_bMinimized == FALSE) &&
								(m_bTrayed == FALSE))
							{
								SetViewHideState(FALSE, FALSE); // Minimize/tray
							}
						}
						else OnFileExit();
					}

					if(!b9x) LeaveCriticalSection(CPwSafeApp::GetLockTimerCS());
				}
				
				// }
			}
		}

		if(m_nFileRelockCountdown != -1)
		{
			--m_nFileRelockCountdown;

			if(m_nFileRelockCountdown == -1)
			{
				if(((m_bFileOpen == TRUE) || (m_bLocked == TRUE)) && (m_strLastDb.IsEmpty() == FALSE))
				{
					if(FileLock_Lock(m_strLastDb, TRUE) == TRUE) m_nFileRelockCountdown = FL_TIME_RELOCK_AFTER * 60;
					else m_nFileRelockCountdown = 30;
				}
				else m_nFileRelockCountdown = FL_TIME_RELOCK_AFTER * 60;
			}
		}
	}

	CDialog::OnTimer(nIDEvent);
}

void CPwSafeDlg::OnDblclkPwlist(NMHDR* pNMHDR, LRESULT* pResult)
{
	NotifyUserActivity();

	*pResult = 0;

	const DWORD dwSel = GetSelectedEntry();
	const DWORD dwEntryIndex = _ListSelToEntryIndex(dwSel);
	if((dwSel == DWORD_MAX) || (dwEntryIndex == DWORD_MAX)) return;

	PW_ENTRY *p = m_mgr.GetEntry(dwEntryIndex);
	ASSERT_ENTRY(p); if(p == NULL) return;

	const BOOL bIsTAN = CPwUtil::IsTANEntry(p);

	CString strData;
	NMITEMACTIVATE* pnmItem = (NMITEMACTIVATE*)pNMHDR;
	switch(pnmItem->iSubItem)
	{
	case 0:
		if((bIsTAN == TRUE) && (m_dwPwListMode != LVS_REPORT)) OnPwlistCopyPw();
		else OnPwlistEdit();
		p = NULL; // p may now be invalid!
		break;
	case 1:
		OnPwlistCopyUser();
		p = NULL; // p may now be invalid!
		break;
	case 2:
		OnPwlistVisitUrl();
		break;
	case 3:
		OnPwlistCopyPw();
		p = NULL; // p may now be invalid!
		break;
	case 4:
		if((pnmItem->uKeyFlags & LVKF_ALT) != 0)
			strData = m_cList.GetItemText(static_cast<int>(dwSel), 4);
		else strData = p->pszAdditional;
		strData = CsRemoveMeta(&strData);
		CopyStringToClipboard(strData, p, &m_mgr, this->GetSafeHwnd());
		EraseCString(&strData);
		m_nClipboardCountdown = static_cast<int>(m_dwClipboardSecs);
		SetStatusTextEx(TRL("Field copied to clipboard."));
		DropToBackgroundIfOptionEnabled(false);
		break;
	case 5:
		_PwTimeToStringEx(p->tCreation, strData, CPwSafeDlg::m_bUseLocalTimeFormat);
		CopyStringToClipboard(strData, NULL, NULL, this->GetSafeHwnd());
		m_nClipboardCountdown = static_cast<int>(m_dwClipboardSecs);
		SetStatusTextEx(TRL("Field copied to clipboard."));
		DropToBackgroundIfOptionEnabled(false);
		break;
	case 6:
		_PwTimeToStringEx(p->tLastMod, strData, CPwSafeDlg::m_bUseLocalTimeFormat);
		CopyStringToClipboard(strData, NULL, NULL, this->GetSafeHwnd());
		m_nClipboardCountdown = static_cast<int>(m_dwClipboardSecs);
		SetStatusTextEx(TRL("Field copied to clipboard."));
		DropToBackgroundIfOptionEnabled(false);
		break;
	case 7:
		_PwTimeToStringEx(p->tLastAccess, strData, CPwSafeDlg::m_bUseLocalTimeFormat);
		CopyStringToClipboard(strData, NULL, NULL, this->GetSafeHwnd());
		m_nClipboardCountdown = static_cast<int>(m_dwClipboardSecs);
		SetStatusTextEx(TRL("Field copied to clipboard."));
		DropToBackgroundIfOptionEnabled(false);
		break;
	case 8:
		_PwTimeToStringEx(p->tExpire, strData, CPwSafeDlg::m_bUseLocalTimeFormat);
		CopyStringToClipboard(strData, NULL, NULL, this->GetSafeHwnd());
		m_nClipboardCountdown = static_cast<int>(m_dwClipboardSecs);
		SetStatusTextEx(TRL("Field copied to clipboard."));
		DropToBackgroundIfOptionEnabled(false);
		break;
	case 9:
		_UuidToString(p->uuid, &strData);
		CopyStringToClipboard(strData, NULL, NULL, this->GetSafeHwnd());
		m_nClipboardCountdown = static_cast<int>(m_dwClipboardSecs);
		SetStatusTextEx(TRL("Field copied to clipboard."));
		DropToBackgroundIfOptionEnabled(false);
		break;
	case 10:
		CopyStringToClipboard(p->pszBinaryDesc, p, &m_mgr, this->GetSafeHwnd());
		m_nClipboardCountdown = static_cast<int>(m_dwClipboardSecs);
		SetStatusTextEx(TRL("Field copied to clipboard."));
		DropToBackgroundIfOptionEnabled(false);
		break;
	default:
		ASSERT(FALSE);
		break;
	}

	_TouchEntry(dwSel, FALSE);
	_UpdateToolBar();
}

void CPwSafeDlg::OnPwlistCopyUser()
{
	NotifyUserActivity();
	_SetDisplayDialog(true);
	if(_CallPlugins(KPM_USER_COPY, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }
	_SetDisplayDialog(false);

	if(m_nClipboardMethod == CM_TIMED)
	{
		DWORD dwEntryIndex = _ListSelToEntryIndex();

		ASSERT(dwEntryIndex != DWORD_MAX); if(dwEntryIndex == DWORD_MAX) return;

		PW_ENTRY *p = m_mgr.GetEntry(dwEntryIndex);
		ASSERT_ENTRY(p); if(p == NULL) return;

		if(CPwUtil::IsTANEntry(p) == TRUE)
		{
			// It is a TAN entry, so copy the password instead of the user name
			OnPwlistCopyPw();
			return;
		}

		CopyStringToClipboard(p->pszUserName, p, &m_mgr, this->GetSafeHwnd());
		m_nClipboardCountdown = static_cast<int>(m_dwClipboardSecs);
	}
	else if(m_nClipboardMethod == CM_ENHSECURE)
	{
		m_nClipboardState = CLIPBOARD_DELAYED_USERNAME;
		MakeClipboardDelayRender(m_hWnd, &m_hwndNextViewer);
	}

	SetStatusTextEx(TRL("Field copied to clipboard."));
	DropToBackgroundIfOptionEnabled(false);

	_TouchEntry(GetSelectedEntry(), FALSE);
	_UpdateToolBar();
}

void CPwSafeDlg::ParseAndOpenURLWithEntryInfo(LPCTSTR lpURL, PW_ENTRY *pEntry)
{
	if((pEntry != NULL) && (lpURL != NULL))
	{
		CString strURL = lpURL;

		if(strURL.GetLength() != 0)
		{
			CString strOverride;
			strOverride = ExtractParameterFromString(pEntry->pszAdditional, _T("url-override:"), 0);
			if(strOverride.GetLength() != 0)
				strURL = strOverride;
			else if(m_strURLOverride.GetLength() != 0)
			{
				if(IsUnsafeAllowed(this->m_hWnd) == TRUE)
				{
					if(WU_IsCommandLineURL(strURL) == false)
						strURL = m_strURLOverride; // Apply override
				}
			}

			const bool bCmdQuotes = WU_IsCommandLineURL(strURL);

			strURL = SprCompile(strURL, false, pEntry, &m_mgr, false, bCmdQuotes);

			strURL = strURL.TrimLeft(_T(" \t\r\n"));

			if(WU_IsCommandLineURL(strURL) == false) FixURL(&strURL);

			if(strURL.GetLength() != 0)
			{
				if(m_bUsePuttyForURLs == TRUE)
				{
					CString strUser = SprCompile(pEntry->pszUserName, false, pEntry,
						&m_mgr, false, true);
					if(OpenUrlUsingPutty(strURL, strUser) == FALSE)
						OpenUrlEx(strURL, this->m_hWnd);
				}
				else OpenUrlEx(strURL, this->m_hWnd);
			}
		}
	}
}

void CPwSafeDlg::OnPwlistVisitUrl()
{
	NotifyUserActivity();

	const DWORD dwGroupId = GetSelectedGroupId();
	if(dwGroupId == DWORD_MAX) { ASSERT(FALSE); return; }

	_SetDisplayDialog(true);
	if(_CallPlugins(KPM_URL_VISIT, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }
	_SetDisplayDialog(false);

	bool bOpen = (m_bCopyURLs == FALSE);
	if((GetKeyState(VK_SHIFT) & 0x8000) != 0) bOpen = !bOpen;

	if(bOpen)
	{
		for(int i = 0; i < m_cList.GetItemCount(); ++i)
		{
			const UINT uState = m_cList.GetItemState(i, LVIS_SELECTED);
			if((uState & LVIS_SELECTED) != 0)
			{
				// p = m_mgr.GetEntryByGroup(dwGroupId, (DWORD)i);

				const DWORD dwIndex = _ListSelToEntryIndex(static_cast<DWORD>(i));
				if(dwIndex == DWORD_MAX) { ASSERT(FALSE); continue; }

				PW_ENTRY* p = m_mgr.GetEntry(dwIndex);
				ASSERT_ENTRY(p);

				ParseAndOpenURLWithEntryInfo(p->pszURL, p);

				_TouchEntry(static_cast<DWORD>(i), FALSE);
			}
		}
	}
	else // Copy
	{
		const DWORD dwSelectedEntry = GetSelectedEntry();
		ASSERT(dwSelectedEntry != DWORD_MAX); if(dwSelectedEntry == DWORD_MAX) return;

		const DWORD dwEntryIndex = _ListSelToEntryIndex(dwSelectedEntry);
		ASSERT(dwEntryIndex != DWORD_MAX); if(dwEntryIndex == DWORD_MAX) return;

		PW_ENTRY* p = m_mgr.GetEntry(dwEntryIndex);
		ASSERT_ENTRY(p); if(p == NULL) return;

		CString strURL = p->pszURL;
		const bool bCmdQuotes = WU_IsCommandLineURL(strURL);
		strURL = SprCompile(strURL, false, p, &m_mgr, false, bCmdQuotes);

		bool bLaunched = false;
		if(WU_IsCommandLineURL(strURL))
		{
			if(m_bUsePuttyForURLs == TRUE)
			{
				CString strUser = SprCompile(p->pszUserName, false, p, &m_mgr, false, true);
				if(OpenUrlUsingPutty(strURL, strUser) == FALSE)
					OpenUrlEx(strURL, this->GetSafeHwnd());
			}
			else OpenUrlEx(strURL, this->GetSafeHwnd());

			bLaunched = true;
		}
		else CopyStringToClipboard(strURL, NULL, NULL, this->GetSafeHwnd());

		EraseCString(&strURL);

		_TouchEntry(dwSelectedEntry, FALSE);

		if(!bLaunched)
		{
			m_nClipboardCountdown = (int)m_dwClipboardSecs;
			SetStatusTextEx(TRL("Field copied to clipboard."));
			DropToBackgroundIfOptionEnabled(false);
		}
	}

	_UpdateToolBar();
}

void CPwSafeDlg::OnFileNew()
{
	NotifyUserActivity();

	// if(m_bLocked == TRUE) return;

	if(m_bFileOpen == TRUE) OnFileClose();
	if(m_bFileOpen == TRUE) return;

	if(_CallPlugins(KPM_FILE_NEW_PRE, 0, 0) == FALSE) return;

	m_mgr.NewDatabase();
	if(_ChangeMasterKey(NULL, TRUE) == FALSE) return;

	m_bInitialCmdLineFile = FALSE;

	m_bFileOpen = TRUE;
	m_bLocked = FALSE;
	m_cList.EnableWindow(TRUE);
	m_cGroups.EnableWindow(TRUE);
	m_bModified = TRUE;

	m_strFile.Empty(); m_strFileAbsolute.Empty();

	PW_GROUP pwTemplate;
	PW_TIME tNow;
	ZeroMemory(&pwTemplate, sizeof(PW_GROUP));
	_GetCurrentPwTime(&tNow);
	pwTemplate.tCreation = tNow;
	m_mgr.GetNeverExpireTime(&pwTemplate.tExpire);
	pwTemplate.tLastAccess = tNow;
	pwTemplate.tLastMod = tNow;

	CPrivateConfigEx cfg(FALSE);
	std::vector<std::basic_string<TCHAR> > vNewGroups =
		cfg.GetArray(PWMKEY_GROUPONNEW_PRE);
	bool bCustomGroups = (vNewGroups.size() > 0);
	if(bCustomGroups && (vNewGroups[0] == _T("0")))
		bCustomGroups = false;

	if(bCustomGroups)
	{
		for(DWORD dwNewGroup = DWORD_MAX; dwNewGroup != vNewGroups.size(); ++dwNewGroup)
		{
			std::basic_string<TCHAR> strSpec;

			if(dwNewGroup != DWORD_MAX) strSpec = vNewGroups[dwNewGroup];
			else
			{
				TCHAR lpRootGroup[SI_REGSIZE];
				if(cfg.Get(PWMKEY_ROOTONNEW, lpRootGroup) == TRUE)
					strSpec = lpRootGroup;
				else strSpec = _T("@");
			}

			if(strSpec.size() == 0) continue;

			std::basic_string<TCHAR> strSplit = strSpec.substr(0, 1);
			std::basic_string<TCHAR> strDef = strSpec.substr(1);
			std::vector<std::basic_string<TCHAR> > vSpec;
			SU_Split(vSpec, strDef, strSplit.c_str());

			pwTemplate.pszGroupName = const_cast<TCHAR *>(TRL("General"));
			if((vSpec.size() > 0) && (vSpec[0].size() > 0))
				pwTemplate.pszGroupName = const_cast<TCHAR *>(vSpec[0].c_str());

			pwTemplate.uImageId = 48;
			if((vSpec.size() > 1) && (vSpec[1].size() > 0))
				pwTemplate.uImageId = static_cast<DWORD>(_ttol(vSpec[1].c_str()));

			pwTemplate.usLevel = ((dwNewGroup == DWORD_MAX) ? 0 : 1);
			pwTemplate.uGroupId = 0;
			pwTemplate.dwFlags = ((dwNewGroup == DWORD_MAX) ? PWGF_EXPANDED : 0);
			VERIFY(m_mgr.AddGroup(&pwTemplate));
		}
	}
	else
	{
		pwTemplate.uImageId = 48;
		pwTemplate.pszGroupName = const_cast<TCHAR *>(TRL("General"));
		pwTemplate.usLevel = 0; pwTemplate.uGroupId = 0; // 0 = create new group ID
		pwTemplate.dwFlags |= PWGF_EXPANDED;
		VERIFY(m_mgr.AddGroup(&pwTemplate));

		// Add standard groups
		pwTemplate.uImageId = 38; pwTemplate.pszGroupName = const_cast<LPTSTR>(TRL("Windows"));
		pwTemplate.usLevel = 1; pwTemplate.uGroupId = 0; // 0 = create new group ID
		pwTemplate.dwFlags = 0;
		VERIFY(m_mgr.AddGroup(&pwTemplate));
		pwTemplate.uImageId = 3; pwTemplate.pszGroupName = const_cast<LPTSTR>(TRL("Network"));
		pwTemplate.usLevel = 1; pwTemplate.uGroupId = 0; // 0 = create new group ID
		VERIFY(m_mgr.AddGroup(&pwTemplate));
		pwTemplate.uImageId = 1; pwTemplate.pszGroupName = const_cast<LPTSTR>(TRL("Internet"));
		pwTemplate.usLevel = 1; pwTemplate.uGroupId = 0; // 0 = create new group ID
		VERIFY(m_mgr.AddGroup(&pwTemplate));
		pwTemplate.uImageId = 19; pwTemplate.pszGroupName = const_cast<LPTSTR>(TRL("eMail"));
		pwTemplate.usLevel = 1; pwTemplate.uGroupId = 0; // 0 = create new group ID
		VERIFY(m_mgr.AddGroup(&pwTemplate));
		pwTemplate.uImageId = 37; pwTemplate.pszGroupName = const_cast<LPTSTR>(TRL("Homebanking"));
		pwTemplate.usLevel = 1; pwTemplate.uGroupId = 0; // 0 = create new group ID
		VERIFY(m_mgr.AddGroup(&pwTemplate));
		/* pwTemplate.uImageId = 37; pwTemplate.pszGroupName = const_cast<LPTSTR>(TRL("Mobile devices"));
		pwTemplate.usLevel = 0; pwTemplate.uGroupId = 0; // 0 = create new group ID
		VERIFY(m_mgr.AddGroup(&pwTemplate));
		pwTemplate.uImageId = 37; pwTemplate.pszGroupName = const_cast<LPTSTR>(TRL("Credit cards"));
		pwTemplate.usLevel = 0; pwTemplate.uGroupId = 0; // 0 = create new group ID
		VERIFY(m_mgr.AddGroup(&pwTemplate)); */

		// TESTING CODE, creates entries in the debug version
#ifdef ___PWSAFE_SAMPLE_DATA
		PW_ENTRY pwT;
		pwT.pBinaryData = NULL; pwT.pszBinaryDesc = NULL; pwT.uBinaryDataLen = 0;
		pwT.pszAdditional = _T("Some Notes");
		pwT.pszPassword = _T("The Password");
		pwT.pszURL = _T("google.com");
		pwT.pszUserName = _T("Anonymous");
		pwT.tCreation = tNow; m_mgr.GetNeverExpireTime(&pwT.tExpire); pwT.tLastAccess = tNow;
		pwT.tLastMod = tNow;
		for(int ix = 0; ix < (32 + 32 + 32 + 3); ++ix)
		{
			CString str;
			str.Format(_T("%d Group"), ix);
			pwTemplate.uImageId = (static_cast<DWORD>(rand()) % 30);
			pwTemplate.pszGroupName = const_cast<LPTSTR>((LPCTSTR)str);
			pwTemplate.uGroupId = 0; // 0 = create new group
			pwTemplate.usLevel = static_cast<USHORT>(rand() % 5);
			VERIFY(m_mgr.AddGroup(&pwTemplate));
		}

		PW_TIME tExpired;
		tExpired.btDay = 1; tExpired.btHour = 1; tExpired.btMinute = 1;
		tExpired.btMonth = 1; tExpired.btSecond = 0; tExpired.shYear = 2000;
		for(int ir = 0; ir < 10; ++ir)
		{
			pwT.pszTitle = _T("I am expired");
			pwT.uGroupId = m_mgr.GetGroupIdByIndex(static_cast<DWORD>(rand()) % 6);
			pwT.uImageId = (static_cast<DWORD>(rand()) % 30);
			pwT.tExpire = tExpired;
			ZeroMemory(pwT.uuid, 16);
			VERIFY(m_mgr.AddEntry(&pwT));
		}

		for(int iy = 0; iy < (1024 + 256 + 3); ++iy)
		{
			CString str;
			str.Format(_T("Sample #%d"), iy);
			pwT.pszTitle = const_cast<LPTSTR>((LPCTSTR)str);
			pwT.uGroupId = m_mgr.GetGroupIdByIndex((DWORD)rand() % 8);
			pwT.uImageId = (DWORD)rand() % 30;
			m_mgr.GetNeverExpireTime(&pwT.tExpire);
			ZeroMemory(pwT.uuid, 16);
			VERIFY(m_mgr.AddEntry(&pwT));
		}
#endif
	}

	m_mgr.FixGroupTree();
	UpdateGroupList();
	UpdatePasswordList();
	_UpdateToolBar();
	_SetDefaultFocus();

	_CallPlugins(KPM_FILE_NEW_POST, 0, 0);
}

BOOL CPwSafeDlg::_ChangeMasterKey(CPwManager *pDbMgr, BOOL bCreateNew)
{
	CPasswordDlg dlg;
	int nConfirmAttempts;
	BOOL bSuccess;
	CPwManager *pMgr;
	DWORD_PTR dwOpFlags = 0;
	DWORD_PTR aOpParams[3];

	if(bCreateNew == TRUE) dwOpFlags |= 1;

	aOpParams[0] = 0; // Buffer for return value -- plugins should fill it
	aOpParams[1] = 0; // Deprecated, (DWORD_PTR)pDbMgr;
	aOpParams[2] = dwOpFlags;
	if(_CallPlugins(KPM_CHANGE_MASTER_KEY_PRE, (LPARAM)&aOpParams[0], 0) == FALSE)
		return ((aOpParams[0] == 0) ? FALSE : TRUE);

	if(pDbMgr == NULL) pMgr = &m_mgr;
	else pMgr = pDbMgr;

	_SetDisplayDialog(true);

	dlg.m_bKeyMethod = PWM_KEYMETHOD_OR;
	dlg.m_bLoadMode = FALSE;
	dlg.m_bConfirm = FALSE;
	dlg.m_hWindowIcon = m_hIcon;
	dlg.m_bChanging = ((bCreateNew == FALSE) ? TRUE : FALSE);

	if(NewGUI_DoModal(&dlg) == IDCANCEL) { _SetDisplayDialog(false); return FALSE; }

	if((dlg.m_bKeyFile == FALSE) || (dlg.m_bKeyMethod == PWM_KEYMETHOD_AND))
	{
		bSuccess = FALSE;

		for(nConfirmAttempts = 0; nConfirmAttempts < 3; ++nConfirmAttempts)
		{
			CPasswordDlg *pDlg2 = new CPasswordDlg();
			ASSERT(pDlg2 != NULL); if(pDlg2 == NULL) continue;

			pDlg2->m_bKeyMethod = PWM_KEYMETHOD_OR;
			pDlg2->m_bLoadMode = FALSE;
			pDlg2->m_bConfirm = TRUE;
			pDlg2->m_hWindowIcon = m_hIcon;
			pDlg2->m_bChanging = ((bCreateNew == TRUE) ? FALSE : TRUE);

			if(NewGUI_DoModal(pDlg2) == IDCANCEL)
			{
				ASSERT((pDlg2->m_lpKey == NULL) && (pDlg2->m_lpKey2 == NULL));
				SAFE_DELETE(pDlg2);
				dlg.FreePasswords();
				_SetDisplayDialog(false);
				return FALSE;
			}

			if(dlg.m_bKeyMethod == PWM_KEYMETHOD_OR)
			{
				if((_tcscmp(pDlg2->m_lpKey, dlg.m_lpKey) != 0) || (pDlg2->m_bKeyFile == TRUE))
				{
					pDlg2->FreePasswords();
					SAFE_DELETE(pDlg2);
					MessageBox(TRL("Password and repeated password aren't identical!"),
						PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
					continue;
				}
			}
			else // dlg.m_nKeyMethod == PWM_KEYMETHOD_AND
			{
				if((_tcscmp(pDlg2->m_lpKey, dlg.m_lpKey2) != 0) || (pDlg2->m_bKeyFile == TRUE))
				{
					pDlg2->FreePasswords();
					SAFE_DELETE(pDlg2);
					MessageBox(TRL("Password and repeated password aren't identical!"),
						PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
					continue;
				}
			}

			pDlg2->FreePasswords();
			SAFE_DELETE(pDlg2);
			bSuccess = TRUE;
			break;
		}

		if(bSuccess == FALSE) { dlg.FreePasswords(); _SetDisplayDialog(false); return FALSE; }
	}

	CPwSafeAppRI ri;
	int nErrCode, nErrCode2;

	if(dlg.m_bKeyMethod == PWM_KEYMETHOD_OR)
		nErrCode = pMgr->SetMasterKey(dlg.m_lpKey, dlg.m_bKeyFile, NULL, &ri, FALSE,
			dlg.m_strSelectedKeyProv);
	else
	{
		ASSERT(dlg.m_bKeyFile == TRUE);
		nErrCode = pMgr->SetMasterKey(dlg.m_lpKey, dlg.m_bKeyFile, dlg.m_lpKey2, &ri,
			FALSE, dlg.m_strSelectedKeyProv);
	}

	if(nErrCode != PWE_SUCCESS)
	{
		if(dlg.m_bKeyFile == TRUE)
		{
			if(CPwUtil::IsDatabaseFile(dlg.m_lpKey))
			{
				CString strDF = dlg.m_lpKey;
				strDF += _T("\r\n\r\n");
				strDF += TRL("Database files cannot be used as key files.");

				dlg.FreePasswords();
				MessageBox(strDF, PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
				_SetDisplayDialog(false); return FALSE;
			}

			CVistaTaskDialog dlgTask(this->m_hWnd, AfxGetInstanceHandle(), true);
			CString strTX = TRL("The selected key file already exists");
			strTX += _T(":\r\n");
			strTX += dlg.m_lpKey;
			dlgTask.SetContent(strTX);
			dlgTask.SetMainInstruction(TRL("Overwrite existing key file?"));
			dlgTask.SetWindowTitle(PWM_PRODUCT_NAME_SHORT);
			dlgTask.SetIcon(MAKEINTRESOURCEW(IDI_KEY));
			dlgTask.AddButton(TRL("Overwrite"), TRL("Generate a new key and overwrite the existing key file. The key currently stored in the existing key file will be lost."), IDYES);
			dlgTask.AddButton(TRL("Reuse"), TRL("Load the existing key file and use it as key for the current database. The key file will not be modified."), IDNO);
			dlgTask.AddButton(TRL("Cancel"), TRL("Abort specifying a new key for the current database."), IDCANCEL);
			int nMsg = dlgTask.ShowDialog();

			if(nMsg < 0) // No task dialog support?
			{
				CString strMsg = TRL("A key file already exists on this drive. Do you want to overwrite or reuse it?");
				strMsg += _T("\r\n\r\n");
				strMsg += TRL("Click [Yes] to overwrite the key file."); strMsg += _T("\r\n");
				strMsg += TRL("Click [No] to reuse the selected key file for this database."); strMsg += _T("\r\n");
				strMsg += TRL("Click [Cancel] to abort changing the key.");

				nMsg = MessageBox(strMsg, TRL("Overwrite?"), MB_ICONQUESTION | MB_YESNOCANCEL);
			}

			if(nMsg == IDYES)
			{
				if(dlg.m_bKeyMethod == PWM_KEYMETHOD_OR)
					nErrCode2 = pMgr->SetMasterKey(dlg.m_lpKey, dlg.m_bKeyFile, NULL,
						&ri, TRUE, dlg.m_strSelectedKeyProv);
				else
					nErrCode2 = pMgr->SetMasterKey(dlg.m_lpKey, dlg.m_bKeyFile,
						dlg.m_lpKey2, &ri, TRUE, dlg.m_strSelectedKeyProv);

				if(nErrCode2 != PWE_SUCCESS)
				{
					dlg.FreePasswords();
					CNewDialogsEx::ShowError(this->m_hWnd, nErrCode2, 0);
					_SetDisplayDialog(false); return FALSE;
				}
			}
			else if(nMsg == IDNO)
			{
				if(dlg.m_bKeyMethod == PWM_KEYMETHOD_OR)
					nErrCode2 = pMgr->SetMasterKey(dlg.m_lpKey, dlg.m_bKeyFile,
						NULL, NULL, FALSE, dlg.m_strSelectedKeyProv);
				else
					nErrCode2 = pMgr->SetMasterKey(dlg.m_lpKey, dlg.m_bKeyFile,
						dlg.m_lpKey2, NULL, FALSE, dlg.m_strSelectedKeyProv);

				if(nErrCode2 != PWE_SUCCESS)
				{
					dlg.FreePasswords();
					CNewDialogsEx::ShowError(this->m_hWnd, nErrCode2, 0);
					_SetDisplayDialog(false); return FALSE;
				}
			}
			else { dlg.FreePasswords(); _SetDisplayDialog(false); return FALSE; }
		}
		else
		{
			dlg.FreePasswords();
			CNewDialogsEx::ShowError(this->m_hWnd, nErrCode, 0);
			_SetDisplayDialog(false); return FALSE;
		}
	}

	if(pDbMgr == NULL) m_bModified = TRUE;

	dlg.FreePasswords();

	if(bCreateNew == FALSE)
	{
		CString str = TRL("The master key has been changed!");
		str += _T("\r\n\r\n");
		str += TRL("Save the database now in order to get the new key applied.");
		MessageBox(str, TRL("Success"), MB_ICONINFORMATION | MB_OK);
	}

	_SetDisplayDialog(false);
	return TRUE;
}

// When pszFile == NULL a file selection dialog is displayed
void CPwSafeDlg::_OpenDatabase(CPwManager *pDbMgr, const TCHAR *pszFile,
	const TCHAR *pszPassword, const TCHAR *pszKeyFile, BOOL bOpenLocked,
	LPCTSTR lpPreSelectPath, BOOL bIgnoreCorrupted)
{
	NotifyUserActivity();

	CString strFile, strText;
	DWORD dwFlags;
	INT_PTR nRet = IDCANCEL;
	int nErr;
	const TCHAR *pSuffix = _T("");
	CPasswordDlg *pDlgPass = NULL;
	CPwManager *pMgr;
	DWORD_PTR dwOpFlags = 0;
	DWORD_PTR aOpParams[7];

	if(pDbMgr == NULL) pMgr = &m_mgr;
	else pMgr = pDbMgr;

	PWDB_REPAIR_INFO repairInfo;
	ZeroMemory(&repairInfo, sizeof(PWDB_REPAIR_INFO));

	if(bOpenLocked == TRUE) dwOpFlags |= 1;
	if(bIgnoreCorrupted == TRUE) dwOpFlags |= 2;
	aOpParams[0] = 0; // Currently unused
	aOpParams[1] = 0; // Deprecated, (DWORD_PTR)pDbMgr;
	aOpParams[2] = (DWORD_PTR)pszFile;
	aOpParams[3] = (DWORD_PTR)pszPassword;
	aOpParams[4] = (DWORD_PTR)pszKeyFile;
	aOpParams[5] = (DWORD_PTR)lpPreSelectPath;
	aOpParams[6] = dwOpFlags;
	if(_CallPlugins(KPM_OPENDB_PRE, (LPARAM)&aOpParams[0], 0) == FALSE) return;

	CString strFilter = TRL("KeePass KDB Files");
	strFilter += _T(" (*.kdb, *.pwd)|*.kdb;*.pwd|");
	strFilter += TRL("All Files");
	strFilter += _T(" (*.*)|*.*||");

	dwFlags = (OFN_LONGNAMES | OFN_EXTENSIONDIFFERENT | OFN_EXPLORER | OFN_ENABLESIZING);
	dwFlags |= (OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST); // | OFN_HIDEREADONLY
	if(m_bFileReadOnly == TRUE) dwFlags |= OFN_READONLY;
	if(pDbMgr != NULL) dwFlags |= OFN_NOCHANGEDIR;

	CFileDialog dlg(TRUE, NULL, NULL, dwFlags, strFilter, this);

	if(pszFile == NULL) nRet = NewGUI_DoModal(&dlg);
	else strFile = pszFile;

	if((pszFile != NULL) || (nRet == IDOK))
	{
		if(pDbMgr == NULL)
		{
			if(m_bFileOpen == TRUE) OnFileClose();
			if(m_bFileOpen == TRUE) return;
		}

		if(pszFile == NULL)
		{
			strFile = dlg.GetPathName();
			m_bInitialCmdLineFile = FALSE;
		}

		if(pDbMgr == NULL) m_strLastDb = strFile;

		if((bOpenLocked == TRUE) && (pDbMgr == NULL))
		{
			m_strFile = strFile;
			m_strFileAbsolute = GetShortestAbsolutePath(m_strFile);
			// const FullPathName database((LPCTSTR)m_strFile);
			// m_strFileAbsolute = database.getFullPathName().c_str();            

			m_bLocked = TRUE;
			CString strExtended = TRL("&Unlock Workspace");
			strExtended += _T("\t");
			strExtended += _GetCmdAccelExt(_T("&Lock Workspace"));
			m_menu.SetMenuText(ID_FILE_LOCK, strExtended, MF_BYCOMMAND);
			SetStatusTextEx(CString(TRL("Workspace locked")) + _T("."));
			m_btnTbLock.SetTooltipText(RemoveAcceleratorTipEx(TRL("&Unlock Workspace")));
			// m_tipN.UpdateTipText(RemoveAcceleratorTipEx(TRL("&Unlock Workspace")),
			//	&m_btnTbLock);
		}
		else
		{
			int nAllowedAttempts;
			if((pszPassword != NULL) || (pszKeyFile != NULL)) nAllowedAttempts = 1;
			else nAllowedAttempts = 3;

			for(int nOpenAttempts = 0; nOpenAttempts < nAllowedAttempts; ++nOpenAttempts)
			{
				ASSERT(pDlgPass == NULL); if(pDlgPass != NULL) { pDlgPass->FreePasswords(); delete pDlgPass; }
				pDlgPass = new CPasswordDlg();
				ASSERT(pDlgPass != NULL); if(pDlgPass == NULL) continue;

				pDlgPass->m_bLoadMode = TRUE;
				pDlgPass->m_bConfirm = FALSE;
				pDlgPass->m_hWindowIcon = m_hIcon;
				pDlgPass->m_lpPreSelectPath = lpPreSelectPath;

				if(pszFile == NULL)
				{
					pDlgPass->m_strDatabasePath = dlg.GetPathName();
					pDlgPass->m_strDescriptiveName = dlg.GetFileName();
				}
				else
				{
					pDlgPass->m_strDatabasePath = strFile;
					pDlgPass->m_strDescriptiveName = CsFileOnly(&strFile);
				}

				if((pszPassword == NULL) && (pszKeyFile == NULL))
				{
					++g_uMasterKeyDlgs;
					if(NewGUI_DoModal(pDlgPass) == IDCANCEL)
					{
						delete pDlgPass; pDlgPass = NULL;
						--g_uMasterKeyDlgs;
						CPwSafeDlg::SetLastMasterKeyDlg(NULL);
						return;
					}
					--g_uMasterKeyDlgs;
					CPwSafeDlg::SetLastMasterKeyDlg(NULL);
				}
				else
				{
					if(pszKeyFile != NULL)
					{
						pDlgPass->m_bKeyFile = TRUE;
						pDlgPass->m_lpKey = _TcsSafeDupAlloc(pszKeyFile);

						if(pszPassword != NULL)
						{
							pDlgPass->m_lpKey2 = _TcsSafeDupAlloc(pszPassword);
							pDlgPass->m_bKeyMethod = PWM_KEYMETHOD_AND;
						}
						else pDlgPass->m_bKeyMethod = PWM_KEYMETHOD_OR;
					}
					else // pszKeyFile == NULL
					{
						pDlgPass->m_bKeyFile = FALSE;
						pDlgPass->m_lpKey = _TcsSafeDupAlloc(pszPassword);
						pDlgPass->m_bKeyMethod = PWM_KEYMETHOD_OR;
					}
				}

				// CString strKeyFilePath;
				// if((pDlgPass->m_bKeyFile != FALSE) && (pDlgPass->m_lpKey != NULL))
				//	strKeyFilePath = pDlgPass->m_lpKey;

				if(pDlgPass->m_bKeyMethod == PWM_KEYMETHOD_OR)
					nErr = pMgr->SetMasterKey(pDlgPass->m_lpKey, pDlgPass->m_bKeyFile,
						NULL, NULL, FALSE, pDlgPass->m_strSelectedKeyProv);
				else
					nErr = pMgr->SetMasterKey(pDlgPass->m_lpKey, pDlgPass->m_bKeyFile,
						pDlgPass->m_lpKey2, NULL, FALSE, pDlgPass->m_strSelectedKeyProv);

				pDlgPass->FreePasswords(); delete pDlgPass; pDlgPass = NULL;

				if(nErr != PWE_SUCCESS)
				{
					CNewDialogsEx::ShowError(this->m_hWnd, nErr, 0);
					continue;
				}

				CTaskbarListEx::SetProgressState(this->m_hWnd, TBPF_INDETERMINATE);

				if(bIgnoreCorrupted == TRUE)
					nErr = pMgr->OpenDatabase(strFile, &repairInfo);
				else
					nErr = pMgr->OpenDatabase(strFile, NULL);

				CTaskbarListEx::SetProgressState(this->m_hWnd, TBPF_NOPROGRESS);

				if(nErr == PWE_SUCCESS) // Set or clear key source
					CKeySourcesPool::Set(GetShortestAbsolutePath(strFile),
						pMgr->GetKeySource());

				if(bIgnoreCorrupted == TRUE)
				{
					CString strRepaired = CPwUtil::FormatError(nErr, PWFF_NO_INTRO |
						PWFF_INVKEY_WITH_CODE);
					CString strRTemp = TRL("&Repair Database File...");
					strRTemp.Remove(_T('&'));
					strRTemp.Trim(_T('.'));
					CString strRTitle = strRTemp;

					strRepaired += _T("\r\n\r\n");
					strRepaired += TRL("Group:");
					strRTemp.Format(_T(" %u / %u.\r\n"), pMgr->GetNumberOfGroups(), repairInfo.dwOriginalGroupCount);
					strRepaired += strRTemp;
					strRepaired += TRL("Entry");
					strRTemp.Format(_T(": %u / %u.\r\nMeta: %u."), pMgr->GetNumberOfEntries(),
						repairInfo.dwOriginalEntryCount - repairInfo.dwRecognizedMetaStreamCount,
						repairInfo.dwRecognizedMetaStreamCount);
					strRepaired += strRTemp;

					MessageBox(strRepaired, strRTitle, MB_ICONINFORMATION | MB_OK);

					nErr = PWE_SUCCESS;
				}

				if(nErr != PWE_SUCCESS)
				{
					CNewDialogsEx::ShowError(this->m_hWnd, nErr, 0);

					if(nErr == PWE_UNSUPPORTED_KDBX) nOpenAttempts = nAllowedAttempts;
				}
				else if(pDbMgr == NULL)
				{
					m_bHashValid = SHA256_HashFile(strFile, &m_aHashOfFile[0]);

					if(pszFile == NULL) m_bFileReadOnly = dlg.GetReadOnlyPref();
					// else if(CmdArgs::instance().readonlyIsInEffect() == false)
					//	m_bFileReadOnly = FALSE;

					if(m_bFileReadOnly == FALSE)
					{
						std::basic_string<TCHAR> strLockingUser;
						if(FileLock_IsLocked(strFile, strLockingUser) == TRUE)
						{
							CString strMessage = TRL("The file you are trying to open is locked, i.e. someone else has currently opened the file and is editing it.");
							strMessage += _T("\r\n\r\n");

							if(strLockingUser.size() > 0)
							{
								strMessage += TRL("User name:");
								strMessage += _T(" ");
								strMessage += strLockingUser.c_str();
								strMessage += _T(".");
							}

							CVistaTaskDialog vtd(this->m_hWnd, AfxGetInstanceHandle(), true);
							vtd.SetContent(strMessage);
							vtd.SetMainInstruction(TRL("Open the file in read-only mode?"));
							vtd.SetWindowTitle(PWM_PRODUCT_NAME_SHORT);
							vtd.SetIcon(MTDI_QUESTION);

							vtd.AddButton(TRL("Read-only"), TRL("Open the file in read-only mode. Changes to the database can be saved to a different file."), IDYES);
							vtd.AddButton(TRL("Normal"), TRL("Take ownership of the file and open it in normal writing mode."), IDNO);

							int nOpenRet = vtd.ShowDialog(NULL);
							if(nOpenRet < 0) // No task dialog support?
							{
								strMessage += _T("\r\n\r\n");
								strMessage += TRL("Open the file in read-only mode?");
								strMessage += _T("\r\n\r\n");
								strMessage += TRL("Click [Yes] to open the file in read-only mode.");
								strMessage += _T("\r\n");
								strMessage += TRL("Click [No] to open the file in normal writing mode.");

								nOpenRet = MessageBox(strMessage, TRL("Open the file in read-only mode?"),
									MB_YESNO | MB_ICONQUESTION);
							}

							if((nOpenRet == IDYES) || (nOpenRet == IDOK))
								m_bFileReadOnly = TRUE;
							else
							{
								m_bFileReadOnly = FALSE;

								FileLock_Lock(strFile, TRUE);
								m_nFileRelockCountdown = FL_TIME_RELOCK_AFTER * 60;
							}
						}
						else // if(m_bFileReadOnly == FALSE)
						{
							FileLock_Lock(strFile, TRUE);
							m_nFileRelockCountdown = FL_TIME_RELOCK_AFTER * 60;
						}
					}

					m_strFile = strFile;
					m_strFileAbsolute = GetShortestAbsolutePath(m_strFile);
					// const FullPathName database((LPCTSTR)m_strFile);
					// m_strFileAbsolute = database.getFullPathName().c_str();

					m_bFileOpen = TRUE;

					m_bModified = FALSE;
					m_cList.EnableWindow(TRUE);
					m_cGroups.EnableWindow(TRUE);

					m_bLocked = FALSE;

					pSuffix = _GetCmdAccelExt(_T("&Lock Workspace"));
					strText = TRL("&Lock Workspace");
					if(_tcslen(pSuffix) != 0) { strText += _T("\t"); strText += pSuffix; }
					m_menu.SetMenuText(ID_FILE_LOCK, strText, MF_BYCOMMAND);
					m_btnTbLock.SetTooltipText(RemoveAcceleratorTipEx(TRL("&Lock Workspace")));
					// m_tipN.UpdateTipText(RemoveAcceleratorTipEx(TRL(
					//	"&Lock Workspace")), &m_btnTbLock);

					UpdateGroupList();

					HTREEITEM h;
					h = _GroupIdToHTreeItem(pMgr->m_dwLastSelectedGroupId);
					if(h != NULL) m_cGroups.SelectItem(h);
					UpdatePasswordList();

					h = _GroupIdToHTreeItem(pMgr->m_dwLastTopVisibleGroupId);
					if(h != NULL)
					{
						m_cGroups.Select(h, TVGN_FIRSTVISIBLE);
						// m_cGroups.SetItemState(h, 0, TVIS_SELECTED);
					}

					DWORD dwEntryPos = _EntryUuidToListPos(pMgr->m_aLastTopVisibleEntryUuid);
					if(dwEntryPos != DWORD_MAX)
					{
						m_cList.EnsureVisible(m_cList.GetItemCount() - 1, FALSE);
						m_cList.EnsureVisible(static_cast<int>(dwEntryPos), FALSE);
					}

					dwEntryPos = _EntryUuidToListPos(pMgr->m_aLastSelectedEntryUuid);
					if(dwEntryPos != DWORD_MAX)
						m_cList.FocusItem(static_cast<int>(dwEntryPos), TRUE);

					std::basic_string<TCHAR> bsEmpty;
					NewGUI_ComboBox_UpdateHistory(m_cQuickFind, bsEmpty,
						pMgr->AccessPropertyStrArray(PWPA_SEARCH_HISTORY),
						PWM_STD_MAX_HISTORYITEMS);

					SetStatusTextEx(TRL("Ready."));

					_CallPlugins(KPM_OPENDB_COMMITTED, (LPARAM)pMgr->GetLastDatabaseHeader(), 0);
					break;
				}
				else if(pDbMgr != NULL) break;
			}
		}
	}
	ASSERT(pDlgPass == NULL);

	if(pDbMgr == NULL)
	{
		if(m_bLocked == FALSE)
		{
			if((m_bAutoShowExpired == TRUE) || (m_bAutoShowExpiredSoon == TRUE))
				_ShowExpiredEntries(FALSE, m_bAutoShowExpired, m_bAutoShowExpiredSoon);

			_CallPlugins(KPM_OPENDB_POST, 0, 0);
		}

		_UpdateTrayIcon();
		_UpdateToolBar(TRUE); // Updates titlebar, too
	}
	else { _CallPlugins(KPM_OPENDB_POST, 1, 0); }

	NotifyUserActivity();
	_SetDefaultFocus();
}

void CPwSafeDlg::OnFileOpen()
{
	NotifyUserActivity();

	// if(m_bLocked == TRUE) return;

	_SetDisplayDialog(true);

	if(_CallPlugins(KPM_FILE_OPEN_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }

	_OpenDatabase(NULL, NULL, NULL, NULL, FALSE, NULL, FALSE);
	_UpdateToolBar();
	_SetDisplayDialog(false);
}

void CPwSafeDlg::_UpdateGuiToManager()
{
	m_mgr.m_dwLastSelectedGroupId = GetSelectedGroupId();

	HTREEITEM hGroup = m_cGroups.GetFirstVisibleItem();
	if(hGroup != NULL)
		m_mgr.m_dwLastTopVisibleGroupId = (DWORD)m_cGroups.GetItemData(hGroup);
	else m_mgr.m_dwLastTopVisibleGroupId = DWORD_MAX;

	DWORD dwIndex = _ListSelToEntryIndex((DWORD)m_cList.GetTopIndex());
	if(dwIndex != DWORD_MAX)
	{
		PW_ENTRY *pEn = m_mgr.GetEntry(dwIndex);
		ASSERT_ENTRY(pEn);
		if(pEn != NULL)
		{
			memcpy(m_mgr.m_aLastTopVisibleEntryUuid, pEn->uuid, 16);
		}
	}
	dwIndex = _ListSelToEntryIndex();
	if(dwIndex != DWORD_MAX)
	{
		PW_ENTRY *pEn2 = m_mgr.GetEntry(dwIndex);
		ASSERT_ENTRY(pEn2);
		if(pEn2 != NULL)
		{
			memcpy(m_mgr.m_aLastSelectedEntryUuid, pEn2->uuid, 16);
		}
	}
}

void CPwSafeDlg::_DeleteBackupEntries()
{
	DWORD idGroup;
	BOOL bUpd = FALSE;

	idGroup = m_mgr.GetGroupId(PWS_BACKUPGROUP);
	if((idGroup != 0) && (idGroup != DWORD_MAX)) { VERIFY(m_mgr.DeleteGroupById(idGroup, FALSE)); bUpd = TRUE; }

	idGroup = m_mgr.GetGroupId(PWS_BACKUPGROUP_SRC);
	if((idGroup != 0) && (idGroup != DWORD_MAX)) { VERIFY(m_mgr.DeleteGroupById(idGroup, FALSE)); bUpd = TRUE; }

	if(bUpd == TRUE)
	{
		_Groups_SaveView(TRUE);
		UpdateGroupList();
		_Groups_RestoreView();
		ShowEntryDetails(NULL);
	}
}

void CPwSafeDlg::OnFileSave()
{
	NotifyUserActivity();

	if(m_bFileOpen == FALSE) return;
	if(m_bFileReadOnly == TRUE) return;

	_SetDisplayDialog(true);

	if(_CallPlugins(KPM_FILE_SAVE_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }

	GroupSyncStates(TRUE);

	if(m_strFile.IsEmpty())
	{
		OnFileSaveAs();
		_SetDisplayDialog(false);
		return;
	}

	if(m_bHashValid == TRUE)
	{
		BYTE aNewHash[32];

		if(SHA256_HashFile(m_strFile, (BYTE *)aNewHash) == TRUE)
		{
			if(memcmp(aNewHash, m_aHashOfFile, 32) != 0)
			{
				CString str;
				str = TRL("The database file has been modified!");
				str += _T("\r\n\r\n");
				str += TRL("Most probably someone has changed the file while you were editing it.");
				str += _T("\r\n\r\n");
				str += TRL("Do you want to overwrite it?");
				if(MessageBox(str, TRL("Overwrite?"), MB_YESNO | MB_ICONQUESTION) == IDNO)
				{
					_SetDisplayDialog(false);
					return;
				}
			}
		}
	}

	if(m_bDeleteBackupsOnSave == TRUE) _DeleteBackupEntries();
	_RemoveSearchGroup();
	_UpdateGuiToManager();

	_PreDatabaseWrite();

	// SetStatusTextEx(TRL("Saving database..."));
	CTaskbarListEx::SetProgressState(this->m_hWnd, TBPF_INDETERMINATE);
	CShutdownBlocker sdb(this->m_hWnd, TRL("Saving database..."));

	BYTE vWrittenHash[32];
	const int nErr = m_mgr.SaveDatabase(m_strFile, &vWrittenHash[0]);
	const DWORD dwWinErr = GetLastError();

	sdb.Release();
	CTaskbarListEx::SetProgressState(this->m_hWnd, TBPF_NOPROGRESS);
	// SetStatusTextEx(TRL("Ready."));

	if(nErr != PWE_SUCCESS)
	{
		if(nErr == PWE_GETLASTERROR) SetLastError(dwWinErr); // Restore error
		CNewDialogsEx::ShowError(this->m_hWnd, nErr, PWFF_DATALOSS_WITHOUT_SAVE);
		_SetDisplayDialog(false);
		return;
	}

	WU_FlushStorageBuffersEx(m_strFile, TRUE);

	// Update file contents hash
	m_bHashValid = SHA256_HashFile(m_strFile, (BYTE *)m_aHashOfFile);

	if((m_bHashValid == FALSE) || (memcmp(vWrittenHash, m_aHashOfFile, 32) != 0))
	{
		CNewDialogsEx::ShowError(this->m_hWnd, PWE_FILEERROR_VERIFY,
			PWFF_DATALOSS_WITHOUT_SAVE);
		_SetDisplayDialog(false);
		return;
	}

	// if(m_bCreateBackupFileAfterSaving != FALSE)
	// {
	//	const std::basic_string<TCHAR> strBkRes = WU_CreateBackupFile(m_strFile);
	//	if(strBkRes.size() > 0)
	//		MessageBox(strBkRes.c_str(), PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
	// }

	_CallPlugins(KPM_SAVEDB_POST, (LPARAM)(LPCTSTR)m_strFile, 0);

	m_strLastDb = m_strFile;
	m_bModified = FALSE;
	CKeySourcesPool::Set(GetShortestAbsolutePath(m_strFile), m_mgr.GetKeySource()); // Set or clear

	_UpdateToolBar(); // Updates titlebar, too
	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnFileSaveAs()
{
	NotifyUserActivity();

	if(m_bFileOpen == FALSE) return;

	_SetDisplayDialog(true);

	if(_CallPlugins(KPM_FILE_SAVEAS_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }

	GroupSyncStates(TRUE);

	CString strFilter = TRL("KeePass KDB Files");
	strFilter += _T(" (*.kdb)|*.kdb|");
	strFilter += TRL("All Files");
	strFilter += _T(" (*.*)|*.*||");

	DWORD dwFlags = (OFN_LONGNAMES | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT);
	dwFlags |= OFN_EXTENSIONDIFFERENT;
	// OFN_EXPLORER = 0x00080000, OFN_ENABLESIZING = 0x00800000
	dwFlags |= (0x00080000 | 0x00800000 | OFN_NOREADONLYRETURN);
	
	CFileDialog dlg(FALSE, _T("kdb"), _T("Database.kdb"), dwFlags, strFilter, this);
	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		// strFile = dlg.GetPathName();
		const FullPathName database((LPCTSTR)dlg.GetPathName());
		CString strFile = database.getFullPathName().c_str();

		if(m_bDeleteBackupsOnSave == TRUE) _DeleteBackupEntries();
		_RemoveSearchGroup();
		_UpdateGuiToManager();

		_PreDatabaseWrite();

		// SetStatusTextEx(TRL("Saving database..."));
		CTaskbarListEx::SetProgressState(this->m_hWnd, TBPF_INDETERMINATE);
		CShutdownBlocker sdb(this->m_hWnd, TRL("Saving database..."));

		BYTE vWrittenHash[32];
		const int nErr = m_mgr.SaveDatabase(strFile, &vWrittenHash[0]);

		sdb.Release();
		CTaskbarListEx::SetProgressState(this->m_hWnd, TBPF_NOPROGRESS);
		// SetStatusTextEx(TRL("Ready."));

		if(nErr != PWE_SUCCESS)
			CNewDialogsEx::ShowError(this->m_hWnd, nErr, PWFF_DATALOSS_WITHOUT_SAVE);
		else
		{
			WU_FlushStorageBuffersEx(strFile, TRUE);

			// Update file contents hash
			m_bHashValid = SHA256_HashFile(strFile, (BYTE *)m_aHashOfFile);

			if((m_bHashValid == FALSE) || (memcmp(vWrittenHash, m_aHashOfFile, 32) != 0))
			{
				CNewDialogsEx::ShowError(this->m_hWnd, PWE_FILEERROR_VERIFY,
					PWFF_DATALOSS_WITHOUT_SAVE);
				_SetDisplayDialog(false);
				return;
			}

			// if(m_bCreateBackupFileAfterSaving != FALSE)
			// {
			//	const std::basic_string<TCHAR> strBkRes = WU_CreateBackupFile(strFile);
			//	if(strBkRes.size() > 0)
			//		MessageBox(strBkRes.c_str(), PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
			// }

			_CallPlugins(KPM_SAVEDB_AS_POST, (LPARAM)(LPCTSTR)strFile, 0);

			if(m_bFileReadOnly == FALSE)
			{
				m_nFileRelockCountdown = -1;
				FileLock_Lock(m_strFile, FALSE); // Unlock the previous database file
			}

			m_strFile = strFile;
			m_strFileAbsolute = GetShortestAbsolutePath(m_strFile);

			FileLock_Lock(m_strFile, TRUE);
			m_nFileRelockCountdown = FL_TIME_RELOCK_AFTER * 60;

			m_bModified = FALSE;
			m_strLastDb = strFile;
			CKeySourcesPool::Set(m_strFileAbsolute, m_mgr.GetKeySource()); // Set or clear

			m_bFileReadOnly = FALSE;

			// _UpdateTitleBar(); // Updated by _UpdateToolBar()
		}
	}

	_UpdateToolBar();
	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnFileClose()
{
	NotifyUserActivity();

	_SetDisplayDialog(true);

	if(_CallPlugins(KPM_FILE_CLOSE_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }

	GroupSyncStates(TRUE);

	if((m_bFileOpen == TRUE) && (m_bModified == TRUE))
	{
		int nRes;

		if(m_bAutoSaveDb == TRUE) nRes = IDYES;
		else
		{
			SetForegroundWindow();

			const bool bLockOrExit = ((m_bExiting == TRUE) || (m_bIsLocking == TRUE));
			const bool bDoVerification = ((m_bAutoSaveDb == FALSE) && bLockOrExit);

			CString strContent = TRL("The current database file has been modified");

			if(m_strFileAbsolute.GetLength() > 0)
			{
				strContent += _T(":\r\n");
				strContent += m_strFileAbsolute;
				// strContent += _T("\r\n\r\n");
				// strContent += TRL("Do you want to save the changes?");
			}
			else strContent += _T(".");

			CString strSaveText = TRL("Save all changes made to the database and close the file.");
			if(m_bExiting == TRUE) strSaveText = TRL("Save all changes made to the database and exit KeePass.");
			if(m_bIsLocking == TRUE) strSaveText = TRL("Save all changes made to the database and lock the KeePass workspace.");

			CString strDiscardText = TRL("Discard all changes made to the database and close the file.");
			if(m_bExiting == TRUE) strDiscardText = TRL("Discard all changes made to the database and exit KeePass.");
			if(m_bIsLocking == TRUE) strDiscardText = TRL("Discard all changes made to the database and lock the KeePass workspace.");

			CString strCancelText = TRL("Abort the current operation.");
			strCancelText += _T(" ");
			if(m_bIsLocking == TRUE) strCancelText += TRL("The KeePass workspace will not be locked.");
			else if(m_bExiting == TRUE) strCancelText += TRL("KeePass will not be closed.");
			else strCancelText += TRL("The file will not be closed.");

			CString strMain = TRL("Save database changes before closing the file?");
			if(m_bExiting == TRUE) strMain = TRL("Save database changes before exiting KeePass?");
			if(m_bIsLocking == TRUE) strMain = TRL("Save database changes before locking the workspace?");

			CVistaTaskDialog dlgTask(this->m_hWnd, AfxGetInstanceHandle(), true);
			dlgTask.SetWindowTitle(PWM_PRODUCT_NAME_SHORT);
			dlgTask.SetContent(strContent);
			dlgTask.SetMainInstruction(strMain);
			dlgTask.SetIcon(MTDI_QUESTION);

			if(bDoVerification)
				dlgTask.SetVerification(TRL("Automatically save when closing/locking the database"));

			dlgTask.AddButton(TRL("&Save"), strSaveText, IDYES);
			dlgTask.AddButton(TRL("&Discard changes"), strDiscardText, IDNO);
			dlgTask.AddButton(TRL("Cancel"), strCancelText, IDCANCEL);

			BOOL bAutoSaveCB = FALSE;
			nRes = dlgTask.ShowDialog(bDoVerification ? &bAutoSaveCB : NULL);

			if(nRes < 0)
			{
				CString str = TRL("The current database file has been modified");

				if(m_strFileAbsolute.GetLength() > 0)
				{
					str += _T(":\r\n");
					str += m_strFileAbsolute;
				}
				else str += _T(".");

				str += _T("\r\n\r\n");
				str += TRL("Do you want to save the changes before closing?");

				nRes = MessageBox(str, TRL("KeePass - Save Before Close/Lock?"),
					MB_YESNOCANCEL | MB_ICONQUESTION);
			}
			else if(bDoVerification && (bAutoSaveCB != FALSE) && (nRes == IDYES))
				m_bAutoSaveDb = TRUE;
		}

		if(nRes == IDCANCEL) { _SetDisplayDialog(false); return; }
		else if(nRes == IDYES)
		{
			OnFileSave();

			if((m_bModified == TRUE) && (m_bExiting == TRUE))
			{
				CString strMsg = TRL("The file couldn't be saved.");
				strMsg += _T("\r\n\r\n");
				strMsg += TRL("Maybe it's read-only or the storage media has been removed.");
				strMsg += _T("\r\n");
				strMsg += TRL("If you exit now, all changes to the current database will be lost.");
				strMsg += _T("\r\n\r\n");
				strMsg += TRL("Would you like to exit anyway?");

				int nRet = MessageBox(strMsg, TRL("Exit?"), MB_YESNO | MB_ICONQUESTION);
				if(nRet == IDYES) m_bModified = FALSE;
			}
		}
		else m_bModified = FALSE; // nRes == IDNO
	}

	if(m_bModified == TRUE) { _SetDisplayDialog(false); return; }

	m_cQuickFind.ResetContent();
	ASSERT(m_cQuickFind.GetWindowTextLength() == 0);

	if(((m_bMenuExit == TRUE) || (m_bMiniMode == FALSE)) && (m_bClearClipOnDbClose == TRUE))
		ClearClipboardIfOwner();

	if(m_bFileReadOnly == FALSE)
	{
		m_nFileRelockCountdown = -1;
		FileLock_Lock(m_strFile, FALSE); // Unlock the database file
	}

	m_cList.DeleteAllItemsEx();
	m_cGroups.DeleteAllItemsEx();
	ShowEntryDetails(NULL);
	m_mgr.NewDatabase();
	m_mgr.ClearMasterKey(TRUE, TRUE);

	m_strFile.Empty(); m_strFileAbsolute.Empty();
	m_bFileOpen = FALSE;
	// m_bFileReadOnly = FALSE;
	m_bModified = FALSE;

	m_cList.EnableWindow(FALSE);
	m_cGroups.EnableWindow(FALSE);

	_DeleteTemporaryFiles();

	_UpdateTrayIcon();
	// _UpdateTitleBar(); // Updated by _UpdateToolBar()
	_UpdateToolBar();

	_CallPlugins(KPM_FILE_CLOSE_POST, 0, 0);
	_SetDisplayDialog(false);

	if(m_bTrayed == FALSE) // Prevent focus loss (discussion 110f28cf)
		m_btnTbAbout.SetFocus();
}

void CPwSafeDlg::OnSafeOptions()
{
	NotifyUserActivity();

	COptionsDlg dlg;
	BOOL bRebuildContextMenus = FALSE, bRestartNeeded = FALSE;
	BOOL bStartWithWindows = CPwSafeApp::GetStartWithWindows();

	_SetDisplayDialog(true);

	dlg.m_pParentDlg = this;

	if(m_bWindowsNewLine == TRUE) dlg.m_nNewlineSequence = 0;
	else dlg.m_nNewlineSequence = 1;

	dlg.m_uClipboardSeconds = m_dwClipboardSecs - 1;
	dlg.m_nClipboardMethod = m_nClipboardMethod;
	dlg.m_bClipNoPersist = WU_GetConfigBool(WU_CFG_CLIP_NOPERSIST, TRUE);
	dlg.m_bOpenLastDb = m_bOpenLastDb;
	dlg.m_bStartMinimized = m_bStartMinimized;
	dlg.m_bImgButtons = m_bImgButtons;
	dlg.m_bEntryGrid = m_bEntryGrid;
	dlg.m_bAutoSave = m_bAutoSaveDb;
	dlg.m_strFontSpec = m_strFontSpec;
	dlg.m_strNotesFontSpec = m_strNotesFontSpec;
	dlg.m_strPasswordFontSpec = m_strPasswordFontSpec;
	dlg.m_bLockOnMinimize = m_bLockOnMinimize;
	dlg.m_bMinimizeOnLock = m_bMinimizeOnLock;
	dlg.m_bMinimizeToTray = m_bMinimizeToTray;
	dlg.m_bCloseMinimizes = m_bCloseMinimizes;
	dlg.m_bLockAfterTime = ((m_nLockTimeDef != -1) ? TRUE : FALSE);

	if(m_nLockTimeDef != -1) dlg.m_nLockAfter = static_cast<UINT>(m_nLockTimeDef);
	else dlg.m_nLockAfter = 0;

	dlg.m_rgbRowHighlight = m_cList.GetRowColorEx();
	dlg.m_bColAutoSize = m_bColAutoSize;
	dlg.m_bDisableUnsafe = m_bDisableUnsafe;
	dlg.m_bRememberLast = m_bRememberLast;
	dlg.m_bUsePuttyForURLs = m_bUsePuttyForURLs;
	dlg.m_bSaveOnLATMod = m_bSaveOnLATMod;
	dlg.m_dwATHotKey = m_dwATHotKey;
	dlg.m_bAutoShowExpired = m_bAutoShowExpired;
	dlg.m_bAutoShowExpiredSoon = m_bAutoShowExpiredSoon;
	dlg.m_bStartWithWindows = bStartWithWindows;
	dlg.m_bBackupEntries = m_bBackupEntries;
	dlg.m_bSingleInstance = m_bCheckForInstance;
	dlg.m_bSecureEdits = m_bSecureEdits;
	dlg.m_bSingleClickTrayIcon = m_bSingleClickTrayIcon;
	dlg.m_bShowTrayOnlyIfTrayed = m_bShowTrayOnlyIfTrayed;
	dlg.m_dwDefaultExpire = m_dwDefaultExpire;
	dlg.m_bQuickFindInPasswords = m_bQuickFindInPasswords;
	dlg.m_bQuickFindIncBackup = m_bQuickFindIncBackup;
	dlg.m_bQuickFindIncExpired = m_bQuickFindIncExpired;
	dlg.m_bMinimizeBeforeAT = ((m_nAutoTypeMethod == ATM_MINIMIZE) ? TRUE : FALSE);
	dlg.m_bDeleteBackupsOnSave = m_bDeleteBackupsOnSave;
	dlg.m_bShowFullPath = m_bShowFullPath;
	dlg.m_bDisableAutoType = m_bDisableAutoType;
	dlg.m_bCopyURLs = m_bCopyURLs;
	dlg.m_bExitInsteadOfLockAT = m_bExitInsteadOfLockAT;
	dlg.m_bAllowSaveIfModifiedOnly = m_bAllowSaveIfModifiedOnly;
	dlg.m_bCheckForUpdate = m_bCheckForUpdate;
	dlg.m_bLockOnWinLock = m_bLockOnWinLock;
	dlg.m_bClearClipOnDbClose = m_bClearClipOnDbClose;
	dlg.m_bEnableRemoteCtrl = m_remoteControl.IsEnabled();
	dlg.m_bUseLocalTimeFormat = CPwSafeDlg::m_bUseLocalTimeFormat;
	dlg.m_bRegisterRestoreHotKey = m_bRegisterRestoreHotKey;
	dlg.m_bFocusResAfterQuickFind = m_bFocusResAfterQuickFind;
	dlg.m_bAlwaysAllowIpc = m_remoteControl.GetAlwaysAllowFullAccess();
	dlg.m_strDefaultAutoTypeSequence = m_strDefaultAutoTypeSequence;
	dlg.m_bAutoTypeIEFix = m_bAutoTypeIEFix;
	dlg.m_bAutoTypeSameKL = m_bAutoTypeSameKL;
	dlg.m_bSortAutoTypeSelItems = m_bSortAutoTypeSelItems;
	dlg.m_bAutoTypeNormDashes = m_bAutoTypeNormDashes;
	dlg.m_bDropToBackOnCopy = m_bDropToBackOnCopy;
	dlg.m_bDeleteTANsAfterUse = m_bDeleteTANsAfterUse;
	dlg.m_bUseTransactedFileWrites = m_bUseTransactedFileWrites;
	dlg.m_bRememberKeySources = CKeySourcesPool::GetEnabled();

	if(_CallPlugins(KPM_OPTIONS_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }

	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		m_bWindowsNewLine = ((dlg.m_nNewlineSequence == 0) ? TRUE : FALSE);
		m_dwClipboardSecs = dlg.m_uClipboardSeconds + 1;
		WU_SetConfigBool(WU_CFG_CLIP_NOPERSIST, dlg.m_bClipNoPersist);
		m_bOpenLastDb = dlg.m_bOpenLastDb;
		m_bStartMinimized = dlg.m_bStartMinimized;
		m_bImgButtons = dlg.m_bImgButtons;
		m_bEntryGrid = dlg.m_bEntryGrid;
		m_bAutoSaveDb = dlg.m_bAutoSave;
		m_bLockOnMinimize = dlg.m_bLockOnMinimize;
		m_bMinimizeOnLock = dlg.m_bMinimizeOnLock;
		m_bMinimizeToTray = dlg.m_bMinimizeToTray;
		m_bCloseMinimizes = dlg.m_bCloseMinimizes;
		if(dlg.m_rgbRowHighlight == 0xFF000000) dlg.m_rgbRowHighlight = RGB(238, 238, 255);
		m_cList.SetRowColorEx(dlg.m_rgbRowHighlight);
		m_bColAutoSize = dlg.m_bColAutoSize;
		m_bRememberLast = dlg.m_bRememberLast;
		m_bUsePuttyForURLs = dlg.m_bUsePuttyForURLs;
		m_bSaveOnLATMod = dlg.m_bSaveOnLATMod;
		m_dwATHotKey = dlg.m_dwATHotKey; // HotKey change is made by COptionsDlg
		m_bAutoShowExpired = dlg.m_bAutoShowExpired;
		m_bAutoShowExpiredSoon = dlg.m_bAutoShowExpiredSoon;
		m_bBackupEntries = dlg.m_bBackupEntries;
		m_bCheckForInstance = dlg.m_bSingleInstance;
		m_bSecureEdits = dlg.m_bSecureEdits;
		m_bSingleClickTrayIcon = dlg.m_bSingleClickTrayIcon;
		m_bShowTrayOnlyIfTrayed = dlg.m_bShowTrayOnlyIfTrayed;
		m_bQuickFindInPasswords = dlg.m_bQuickFindInPasswords;
		m_bQuickFindIncBackup = dlg.m_bQuickFindIncBackup;
		m_bQuickFindIncExpired = dlg.m_bQuickFindIncExpired;
		m_bDeleteBackupsOnSave = dlg.m_bDeleteBackupsOnSave;
		m_bShowFullPath = dlg.m_bShowFullPath;
		m_bDisableAutoType = dlg.m_bDisableAutoType;
		m_bExitInsteadOfLockAT = dlg.m_bExitInsteadOfLockAT;
		m_bAllowSaveIfModifiedOnly = dlg.m_bAllowSaveIfModifiedOnly;
		m_bCheckForUpdate = dlg.m_bCheckForUpdate;
		m_bLockOnWinLock = dlg.m_bLockOnWinLock;
		m_bClearClipOnDbClose = dlg.m_bClearClipOnDbClose;
		m_bFocusResAfterQuickFind = dlg.m_bFocusResAfterQuickFind;
		m_strDefaultAutoTypeSequence = dlg.m_strDefaultAutoTypeSequence;
		m_bAutoTypeIEFix = dlg.m_bAutoTypeIEFix;
		m_bAutoTypeSameKL = dlg.m_bAutoTypeSameKL;
		m_bSortAutoTypeSelItems = dlg.m_bSortAutoTypeSelItems;
		m_bAutoTypeNormDashes = dlg.m_bAutoTypeNormDashes;
		m_bDropToBackOnCopy = dlg.m_bDropToBackOnCopy;
		m_bDeleteTANsAfterUse = dlg.m_bDeleteTANsAfterUse;
		m_bUseTransactedFileWrites = dlg.m_bUseTransactedFileWrites;
		CKeySourcesPool::SetEnabled(dlg.m_bRememberKeySources);

		m_remoteControl.SetAlwaysAllowFullAccess(dlg.m_bAlwaysAllowIpc);
		m_remoteControl.EnableRemoteControl(dlg.m_bEnableRemoteCtrl);

		m_bRegisterRestoreHotKey = dlg.m_bRegisterRestoreHotKey;
		RegisterRestoreHotKey(m_bRegisterRestoreHotKey);

		m_nAutoTypeMethod = ((dlg.m_bMinimizeBeforeAT == TRUE) ? ATM_MINIMIZE : ATM_DROPBACK);

		if(dlg.m_bUseLocalTimeFormat != CPwSafeDlg::m_bUseLocalTimeFormat)
		{
			CPwSafeDlg::m_bUseLocalTimeFormat = dlg.m_bUseLocalTimeFormat;
			RefreshPasswordList();
		}

		if(dlg.m_bDefaultExpire == FALSE) m_dwDefaultExpire = 0;
		else
		{
			m_dwDefaultExpire = dlg.m_dwDefaultExpire;
			if(m_dwDefaultExpire > 2920) m_dwDefaultExpire = 2920;
		}

		if(dlg.m_bStartWithWindows != bStartWithWindows)
		{
			if(CPwSafeApp::SetStartWithWindows(dlg.m_bStartWithWindows) == FALSE)
				MessageBox(TRL("Unable to create/remove the auto-start key of KeePass in the Windows registry!"),
					PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
		}

		if(dlg.m_nClipboardMethod != m_nClipboardMethod)
		{
			ClearClipboardIfOwner();
			m_nClipboardMethod = dlg.m_nClipboardMethod;
		}

		if(dlg.m_bDisableUnsafe != m_bDisableUnsafe)
		{
			bRestartNeeded = TRUE;
			m_bDisableUnsafe = dlg.m_bDisableUnsafe;
		}

		if(dlg.m_bLockAfterTime == FALSE) m_nLockTimeDef = -1;
		else m_nLockTimeDef = static_cast<long>(dlg.m_nLockAfter);

		if(dlg.m_strFontSpec != m_strFontSpec)
			_ParseSpecAndSetFont(dlg.m_strFontSpec, false);
		if(dlg.m_strNotesFontSpec != m_strNotesFontSpec)
			_ParseSpecAndSetFont(dlg.m_strNotesFontSpec, true);
		if(dlg.m_strPasswordFontSpec != m_strPasswordFontSpec)
		{
			m_strPasswordFontSpec = dlg.m_strPasswordFontSpec;
			CFontUtil::SetPasswordFont(m_strPasswordFontSpec, m_hWnd);
		}

		if(dlg.m_bCopyURLs != m_bCopyURLs)
		{
			m_bCopyURLs = dlg.m_bCopyURLs;
			bRebuildContextMenus = TRUE;
		}

		NewGUI_SetImgButtons(m_bImgButtons);
		_SetListParameters();

		_CallPlugins(KPM_OPTIONS_POST, 0, 0);

		ProcessResize();

		if(bRebuildContextMenus == TRUE) RebuildContextMenus();

		if(bRestartNeeded == TRUE)
		{
			CString str;

			str = TRL("You have changed an option that requires restarting KeePass to get active.");
			str += _T("\r\n\r\n");
			str += TRL("Do you wish to restart KeePass now?");
			int nRet = MessageBox(str, TRL("Restart KeePass?"), MB_YESNO | MB_ICONQUESTION);
			if(nRet == IDYES)
			{
				m_bRestartApplication = TRUE;
				_SetDisplayDialog(false);
				OnFileExit(); // Also calls SaveOptions!
				return;
			}
		}

		SaveOptions();
	}

	NotifyUserActivity();
	m_ullLastListParams = 0; // Invalidate
	_UpdateToolBar(TRUE);
	_UpdateTrayIcon(); // Update tray icon visibility

	_SetDisplayDialog(false);
}

void CPwSafeDlg::RestartApplication()
{
	// TCHAR szFile[1024];
	// GetModuleFileName(NULL, szFile, 1024);
	const std_string strFile = Executable::instance().getFullPathName();

	if(TWinExec(strFile.c_str(), 0) <= 31)
	{
		CString str;
		str = TRL("Application cannot be restarted automatically!");
		str += _T("\r\n\r\n");
		str += TRL("Please restart KeePass manually.");
		MessageBox(str, TRL("Loading error"), MB_OK | MB_ICONWARNING);
	}
}

void CPwSafeDlg::OnSafeRemoveGroup()
{
	CDWordArray aGroupIds;

	NotifyUserActivity();

	DWORD dwGroupId = GetSelectedGroupId();
	ASSERT(dwGroupId != DWORD_MAX); if(dwGroupId == DWORD_MAX) return;

	_SetDisplayDialog(true);

	if(_CallPlugins(KPM_GROUP_REMOVE_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }

	DWORD dwGroupIndex = m_mgr.GetGroupByIdN(dwGroupId);
	PW_GROUP *p = m_mgr.GetGroup(dwGroupIndex);
	ASSERT(p != NULL); if(p == NULL) { _SetDisplayDialog(false); return; }
	const USHORT usTopLevel = p->usLevel;

	while(true)
	{
		aGroupIds.Add(dwGroupId);

		++dwGroupIndex;
		if(dwGroupIndex == m_mgr.GetNumberOfGroups()) break;

		p = m_mgr.GetGroup(dwGroupIndex);
		ASSERT(p != NULL); if(p == NULL) break;

		if(p->usLevel <= usTopLevel) break;

		dwGroupId = p->uGroupId;
	}

	CVistaTaskDialog dlgTask(this->m_hWnd, AfxGetInstanceHandle(), false);
	dlgTask.SetContent(TRL("Deleting a group will delete all items and subgroups in that group, too."));
	dlgTask.SetMainInstruction(TRL("Are you sure you want to delete this group?"));
	dlgTask.SetWindowTitle(PWM_PRODUCT_NAME_SHORT);
	dlgTask.SetIcon(MTDI_QUESTION);
	dlgTask.AddButton(TRL("&Delete"), NULL, IDOK);
	dlgTask.AddButton(TRL("Cancel"), NULL, IDCANCEL);
	int nMsg = dlgTask.ShowDialog();

	if(nMsg < 0) // No task dialog support?
	{
		CString str;
		str = TRL("Deleting a group will delete all items and subgroups in that group, too.");
		str += _T("\r\n\r\n");
		str += TRL("Are you sure you want to delete this group?");
		nMsg = MessageBox(str, TRL("Delete Group Confirmation"), MB_ICONQUESTION | MB_YESNO);
	}

	if((nMsg == IDOK) || (nMsg == IDYES))
	{
		const INT_PTR nCount = aGroupIds.GetSize();
		for(INT_PTR i = 0; i < nCount; ++i)
		{
			VERIFY(m_mgr.DeleteGroupById(aGroupIds.GetAt(nCount - i - 1), m_bBackupEntries));
		}
		aGroupIds.RemoveAll();

		_Groups_SaveView(FALSE);
		UpdateGroupList();
		_Groups_RestoreView();
		m_cGroups.SelectItem(m_cGroups.GetRootItem());
		UpdatePasswordList();

		m_bModified = TRUE;
	}

	_UpdateToolBar();
	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnFileChangeMasterPw()
{
	NotifyUserActivity();

	if(m_bFileOpen == FALSE) return;

	if(m_bForceAllowChangeMasterKey == FALSE)
	{
		if(IsUnsafeAllowed(this->m_hWnd) == FALSE) return;
	}

	if(_CallPlugins(KPM_FILE_CHANGE_MASTER_KEY_PRE, 0, 0) == FALSE) return;

	_ChangeMasterKey(NULL, FALSE);

	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdateFileSave(CCmdUI* pCmdUI)
{
	if((m_bFileOpen == TRUE) && (m_bFileReadOnly == FALSE) &&
		(m_mgr.GetNumberOfGroups() > 0))
	{
		if(m_bAllowSaveIfModifiedOnly == FALSE) pCmdUI->Enable(TRUE);
		else pCmdUI->Enable(m_bModified);
	}
	else pCmdUI->Enable(FALSE);
}

void CPwSafeDlg::OnUpdateFileSaveAs(CCmdUI* pCmdUI)
{
	if(m_mgr.GetNumberOfGroups() == 0) pCmdUI->Enable(FALSE);
	else pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::OnUpdateFileChangeMasterPw(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::OnUpdateFileClose(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::OnUpdatePwlistCopyPw(CCmdUI* pCmdUI)
{
	bool b = ((m_dwLastFirstSelectedItem != DWORD_MAX) && (m_dwLastNumSelectedItems == 1));

	if((m_dwLastEntryIndex != DWORD_MAX) && (m_dwLastEntryIndex < m_mgr.GetNumberOfEntries()))
	{
		const PW_ENTRY *p = m_mgr.GetEntry(m_dwLastEntryIndex);
		if(p != NULL) b &= (p->uPasswordLen > 0);
	}

	pCmdUI->Enable(b ? TRUE : FALSE);
}

void CPwSafeDlg::OnUpdatePwlistCopyUser(CCmdUI* pCmdUI)
{
	bool b = ((m_dwLastFirstSelectedItem != DWORD_MAX) && (m_dwLastNumSelectedItems == 1));

	if((m_dwLastEntryIndex != DWORD_MAX) && (m_dwLastEntryIndex < m_mgr.GetNumberOfEntries()))
	{
		const PW_ENTRY *p = m_mgr.GetEntry(m_dwLastEntryIndex);
		if(p != NULL) b &= (p->pszUserName[0] != 0);
	}

	pCmdUI->Enable(b ? TRUE : FALSE);
}

void CPwSafeDlg::OnUpdatePwlistDelete(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(((m_dwLastFirstSelectedItem != DWORD_MAX) ? TRUE : FALSE));
}

void CPwSafeDlg::OnUpdatePwlistEdit(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((((m_dwLastFirstSelectedItem != DWORD_MAX) && (m_dwLastNumSelectedItems == 1)) ? TRUE : FALSE));
}

void CPwSafeDlg::OnUpdatePwlistVisitUrl(CCmdUI* pCmdUI)
{
	bool b = true;

	if(m_bCopyURLs == FALSE)
		b &= ((m_dwLastFirstSelectedItem != DWORD_MAX) && (m_dwLastNumSelectedItems >= 1));
	else
		b &= ((m_dwLastFirstSelectedItem != DWORD_MAX) && (m_dwLastNumSelectedItems == 1));

	if((m_dwLastNumSelectedItems == 1) && (m_dwLastEntryIndex != DWORD_MAX) &&
		(m_dwLastEntryIndex < m_mgr.GetNumberOfEntries()))
	{
		const PW_ENTRY *p = m_mgr.GetEntry(m_dwLastEntryIndex);
		if(p != NULL) b &= (p->pszURL[0] != 0);
	}

	pCmdUI->Enable(b ? TRUE : FALSE);
}

void CPwSafeDlg::OnUpdateSafeRemoveGroup(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnUpdateSafeAddGroup(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::OnUpdatePwlistAdd(CCmdUI* pCmdUI)
{
	BOOL b = m_bFileOpen;
	b &= ((m_mgr.GetNumberOfGroups() > 0) ? TRUE : FALSE);
	b &= ((GetSelectedGroupId() != DWORD_MAX) ? TRUE : FALSE);
	if(!_IsDisplayingMenu())
		b &= ((!_IsDisplayingDialog()) ? TRUE : FALSE);
	pCmdUI->Enable(b);
}

#define LCL_CHKOPT_PARAM_COUNT 19

BOOL CPwSafeDlg::GetExportOptions(PWEXPORT_OPTIONS *pOptions,
	CPwExport *pPwExport, BOOL bPrinting)
{
	NotifyUserActivity();

	ASSERT(pOptions != NULL); if(pOptions == NULL) return FALSE;
	ASSERT(pPwExport != NULL); if(pPwExport == NULL) return FALSE;

	*pOptions = pPwExport->m_aDefaults[pPwExport->m_nFormat];

	CCheckOptionsDlg dlg;
	CHKOPT_PARAM pa[LCL_CHKOPT_PARAM_COUNT];

	ZeroMemory(pa, sizeof(CHKOPT_PARAM) * LCL_CHKOPT_PARAM_COUNT);

	DWORD n = DWORD_MAX;

	pa[++n].lpString = TRL("Options");
	pa[n].nIcon = 9;

	if(bPrinting == FALSE)
	{
		pa[++n].lpString = TRL("Encode/replace newline characters by '\\n'");
		pa[n].pbValue = &pOptions->bEncodeNewlines;
	}

	if(bPrinting == TRUE)
		pa[++n].lpString = TRL("Print backup entries (entries in the 'Backup' group)");
	else
		pa[++n].lpString = TRL("Export backup entries (entries in the 'Backup' group)");
	pa[n].pbValue = &pOptions->bExportBackups;

	pa[++n].lpString = _T("");
	pa[n].nIcon = 0;

	if(bPrinting == TRUE) pa[++n].lpString = TRL("Fields to print");
	else pa[++n].lpString = TRL("Fields to export");
	pa[n].nIcon = 8;

	pa[++n].lpString = TRL("Group");
	pa[n].pbValue = &pOptions->bGroup;
	pa[++n].lpString = TRL("Group Tree");
	pa[n].pbValue = &pOptions->bGroupTree;
	pa[++n].lpString = TRL("Title");
	pa[n].pbValue = &pOptions->bTitle;
	pa[++n].lpString = TRL("User Name");
	pa[n].pbValue = &pOptions->bUserName;

	if(m_bDisallowPrintingPasswords == FALSE)
	{
		pa[++n].lpString = TRL("Password");
		pa[n].pbValue = &pOptions->bPassword;
	}
	else pOptions->bPassword = FALSE;
	
	pa[++n].lpString = TRL("URL");
	pa[n].pbValue = &pOptions->bURL;
	pa[++n].lpString = TRL("Notes");
	pa[n].pbValue = &pOptions->bNotes;
	pa[++n].lpString = TRL("Creation Time");
	pa[n].pbValue = &pOptions->bCreationTime;
	pa[++n].lpString = TRL("Last Access");
	pa[n].pbValue = &pOptions->bLastAccTime;
	pa[++n].lpString = TRL("Last Modification");
	pa[n].pbValue = &pOptions->bLastModTime;
	pa[++n].lpString = TRL("Expires");
	pa[n].pbValue = &pOptions->bExpireTime;
	pa[++n].lpString = TRL("Icon");
	pa[n].pbValue = &pOptions->bImage;
	pa[++n].lpString = TRL("UUID");
	pa[n].pbValue = &pOptions->bUUID;
	pa[++n].lpString = TRL("Attachment");
	pa[n].pbValue = &pOptions->bAttachment;

	if(bPrinting == TRUE)
	{
		dlg.m_strTitle = TRL("Print Options");
		dlg.m_strDescription = TRL("Here you can customize the printed sheet.");
	}
	else
	{
		dlg.m_strTitle = TRL("Export Options");
		dlg.m_strDescription = TRL("Here you can customize the exported files.");
	}

	if(bPrinting == TRUE)
	{
		ASSERT((n == (LCL_CHKOPT_PARAM_COUNT - 2)) || (n == (LCL_CHKOPT_PARAM_COUNT - 3)));
	}
	else { ASSERT(n == (LCL_CHKOPT_PARAM_COUNT - 1)); }
	dlg.m_dwNumParams = n + 1;
	dlg.m_pParams = (CHKOPT_PARAM *)pa;

	return ((NewGUI_DoModal(&dlg) == IDOK) ? TRUE : FALSE);
}

CString CPwSafeDlg::GetExportFile(int nFormat, LPCTSTR lpBaseFileName, BOOL bFixFileName)
{
	if(m_bFileOpen == FALSE) return CString(_T(""));

	LPCTSTR lp = _T("exp");
	if(nFormat == PWEXP_TXT) lp = _T("txt");
	else if(nFormat == PWEXP_HTML) lp = _T("html");
	else if(nFormat == PWEXP_XML) lp = _T("xml");
	else if(nFormat == PWEXP_CSV) lp = _T("csv");
	else if(nFormat == PWEXP_KEEPASS) lp = _T("kdb");
	else { ASSERT(FALSE); }

	CString strSample;
	if(lpBaseFileName == NULL) strSample = _T("Export");
	else
	{
		strSample = lpBaseFileName;

		if(bFixFileName == TRUE)
		{
			strSample.Replace(_T('*'), _T('-')); strSample.Replace(_T('?'), _T('-'));
			strSample.Replace(_T(','), _T('-')); strSample.Replace(_T(';'), _T('-'));
			strSample.Replace(_T(':'), _T('-')); strSample.Replace(_T('|'), _T('-'));
			strSample.Replace(_T('<'), _T('-')); strSample.Replace(_T('>'), _T('-'));
			strSample.Replace(_T('/'), _T('-')); strSample.Replace(_T('\"'), _T('-'));
			strSample.Replace(_T('\\'), _T('-'));
		}
	}

	strSample += _T(".");
	strSample += lp;

	CString strFilter = TRL("All Files");
	strFilter += _T(" (*.*)|*.*||");

	const DWORD dwFlags = (OFN_LONGNAMES | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT |
		OFN_EXTENSIONDIFFERENT | OFN_EXPLORER | OFN_ENABLESIZING | OFN_NOREADONLYRETURN);

	CFileDialog dlg(FALSE, lp, strSample, dwFlags, strFilter, this);
	if(NewGUI_DoModal(&dlg) == IDOK) return dlg.GetPathName();

	strSample.Empty();
	return strSample;
}

void CPwSafeDlg::OnExportTxt()
{
	NotifyUserActivity();
	CPwExport cExp; PWEXPORT_OPTIONS pwo;
	CString strFile;
	if(m_bFileOpen == FALSE) return;
	if(m_bForceAllowExport == FALSE)
	{
		if(IsUnsafeAllowed(this->m_hWnd) == FALSE) return;
	}
	cExp.SetManager(&m_mgr);
	cExp.SetNewLineSeq(m_bWindowsNewLine);
	cExp.SetFormat(PWEXP_TXT);
	strFile = GetExportFile(PWEXP_TXT, CsFileOnly(&m_strFile), FALSE);
	if(strFile.GetLength() != 0)
		if(GetExportOptions(&pwo, &cExp, FALSE) == TRUE)
			cExp.ExportAll(strFile, &pwo, NULL);
}

void CPwSafeDlg::OnExportHtml()
{
	NotifyUserActivity();
	CPwExport cExp; PWEXPORT_OPTIONS pwo;
	CString strFile;
	if(m_bFileOpen == FALSE) return;
	if(m_bForceAllowExport == FALSE)
	{
		if(IsUnsafeAllowed(this->m_hWnd) == FALSE) return;
	}
	cExp.SetManager(&m_mgr);
	cExp.SetNewLineSeq(m_bWindowsNewLine);
	cExp.SetFormat(PWEXP_HTML);
	strFile = GetExportFile(PWEXP_HTML, CsFileOnly(&m_strFile), FALSE);
	if(strFile.GetLength() != 0)
		if(GetExportOptions(&pwo, &cExp, FALSE) == TRUE)
			cExp.ExportAll(strFile, &pwo, NULL);
}

void CPwSafeDlg::OnExportXml()
{
	NotifyUserActivity();
	CPwExport cExp; PWEXPORT_OPTIONS pwo;
	CString strFile;
	if(m_bFileOpen == FALSE) return;
	if(m_bForceAllowExport == FALSE)
	{
		if(IsUnsafeAllowed(this->m_hWnd) == FALSE) return;
	}
	cExp.SetManager(&m_mgr);
	cExp.SetNewLineSeq(m_bWindowsNewLine);
	cExp.SetFormat(PWEXP_XML);
	strFile = GetExportFile(PWEXP_XML, CsFileOnly(&m_strFile), FALSE);
	if(strFile.GetLength() != 0)
		if(GetExportOptions(&pwo, &cExp, FALSE) == TRUE)
			cExp.ExportAll(strFile, &pwo, NULL);
}

void CPwSafeDlg::OnExportCsv()
{
	NotifyUserActivity();
	CPwExport cExp; PWEXPORT_OPTIONS pwo;
	CString strFile;
	if(m_bFileOpen == FALSE) return;
	if(m_bForceAllowExport == FALSE)
	{
		if(IsUnsafeAllowed(this->m_hWnd) == FALSE) return;
	}
	cExp.SetManager(&m_mgr);
	cExp.SetNewLineSeq(m_bWindowsNewLine);
	cExp.SetFormat(PWEXP_CSV);
	strFile = GetExportFile(PWEXP_CSV, CsFileOnly(&m_strFile), FALSE);
	if(strFile.GetLength() != 0)
		if(GetExportOptions(&pwo, &cExp, FALSE) == TRUE)
			cExp.ExportAll(strFile, &pwo, NULL);
}

void CPwSafeDlg::OnUpdateExportTxt(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::OnUpdateExportHtml(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::OnUpdateExportXml(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::OnUpdateExportCsv(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::_PrintGroup(DWORD dwGroupId)
{
	NotifyUserActivity();

	if(m_bFileOpen == FALSE) return;
	if(m_bForceAllowPrinting == FALSE)
	{
		if(IsUnsafeAllowed(this->m_hWnd) == FALSE) return;
	}

	CPwExport cExp;
	cExp.SetManager(&m_mgr);
	cExp.SetNewLineSeq(m_bWindowsNewLine);
	cExp.SetFormat(PWEXP_HTML);

	PWEXPORT_OPTIONS pwo;
	if(GetExportOptions(&pwo, &cExp, TRUE) == FALSE) return;

	std::basic_string<TCHAR> tszFile = WU_GetTempFile(_T(".html"));

	const BOOL bRet = cExp.ExportGroup(tszFile.c_str(), dwGroupId, &pwo, NULL);
	if(bRet == FALSE)
	{
		MessageBox(TRL("Cannot open temporary file for printing!"),
			PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
		return;
	}

	WU_PrintHtmlFile(tszFile.c_str(), m_hWnd);
	m_vTempFiles.push_back(tszFile);
}

void CPwSafeDlg::OnFilePrint()
{
	NotifyUserActivity();
	if(_CallPlugins(KPM_FILE_PRINT_PRE, 0, 0) == FALSE) return;
	_PrintGroup(DWORD_MAX); // _PrintGroup handles everything, like unsafe check, etc.
}

void CPwSafeDlg::OnUpdateFilePrint(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::OnExtrasGenPw()
{
	NotifyUserActivity();

	CPwGeneratorExDlg dlg;
	DWORD dwGroupId = GetSelectedGroupId();
	BYTE aUuid[16];

	_SetDisplayDialog(true);

	if(_CallPlugins(KPM_GEN_PASSWORD_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }

	if((m_bFileOpen == FALSE) || (dwGroupId == DWORD_MAX))
	{
		dlg.InitEx(0, m_bPasswordStars, m_bTrayed);
		NewGUI_DoModal(&dlg);
	}
	else // m_bFileOpen == TRUE
	{
		dlg.InitEx(1, m_bPasswordStars, m_bTrayed);

		if(NewGUI_DoModal(&dlg) == IDOK)
		{
			LPTSTR lpPassword = dlg.GetGeneratedPassword();
			PW_TIME tNow;
			PW_ENTRY pwTemplate;

			_GetCurrentPwTime(&tNow);
			memset(&pwTemplate, 0, sizeof(PW_ENTRY));
			pwTemplate.pszAdditional = _T("");
			pwTemplate.pszPassword = ((lpPassword != NULL) ? lpPassword : _T(""));
			pwTemplate.pszTitle = _T("");
			pwTemplate.pszURL = _T("");
			pwTemplate.pszUserName = _T("");
			pwTemplate.tCreation = tNow;
			m_mgr.GetNeverExpireTime(&pwTemplate.tExpire);
			pwTemplate.tLastAccess = tNow;
			pwTemplate.tLastMod = tNow;
			pwTemplate.uGroupId = dwGroupId;
			pwTemplate.uImageId = 0;
			pwTemplate.uPasswordLen = 0;
			pwTemplate.pszBinaryDesc = _T("");

			if(m_mgr.AddEntry(&pwTemplate) == TRUE)
			{
				PW_ENTRY *pNew = m_mgr.GetLastEditedEntry(); ASSERT_ENTRY(pNew);
				DWORD dwListIndex;
				memcpy(aUuid, pNew->uuid, 16);

				_SortListIfAutoSort();
				if(m_nAutoSort == 0) UpdatePasswordList();

				dwListIndex = _EntryUuidToListPos(aUuid);

				m_cList.EnsureVisible(static_cast<int>(dwListIndex), FALSE);
				m_cList.FocusItem(static_cast<int>(dwListIndex), TRUE);
			}

			CSecureEditEx::DeletePassword(lpPassword); lpPassword = NULL;

			m_bModified = TRUE;
		}
	}

	_UpdateToolBar();
	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnSafeModifyGroup()
{
	NotifyUserActivity();

	CString strGroupName;
	CAddGroupDlg dlg;
	DWORD dwGroupId = GetSelectedGroupId();
	DWORD dwGroup;
	PW_GROUP *p;
	PW_GROUP pwTemplate;
	PW_TIME tNow;

	if(dwGroupId == DWORD_MAX) return;

	p = m_mgr.GetGroupById(dwGroupId);
	ASSERT(p != NULL);
	if(p == NULL) return;
	dwGroup = m_mgr.GetGroupByIdN(dwGroupId);
	ASSERT(dwGroup != DWORD_MAX);
	if(dwGroup == DWORD_MAX) return;

	dlg.m_nIconId = (int)p->uImageId;
	dlg.m_strGroupName = p->pszGroupName;
	dlg.m_pParentImageList = &m_ilIcons;
	dlg.m_bEditMode = TRUE;

	if(_CallPlugins(KPM_GROUP_MODIFY_PRE, 0, 0) == FALSE) return;

	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		_GetCurrentPwTime(&tNow);
		pwTemplate = *p; // Copy previous standard values like group ID, etc.
		pwTemplate.tLastAccess = tNow; // Update times
		pwTemplate.tLastMod = tNow;
		pwTemplate.pszGroupName = const_cast<LPTSTR>((LPCTSTR)dlg.m_strGroupName);
		pwTemplate.uImageId = (DWORD)dlg.m_nIconId;

		m_mgr.SetGroup(dwGroup, &pwTemplate);

		_Groups_SaveView();
		UpdateGroupList();
		_Groups_RestoreView();

		UpdatePasswordList();
		m_cList.SetFocus();

		dlg.m_strGroupName.Empty();

		m_bModified = TRUE;
	}
	else _TouchGroup(dwGroupId, FALSE);

	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdateSafeModifyGroup(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnPwlistFind()
{
	NotifyUserActivity();
	if(_CallPlugins(KPM_PWLIST_FIND_PRE, 0, 0) == FALSE) return;
	_Find(DWORD_MAX);
}

#define CPSDFM_CONTINUE { cnt = dw + 1; if(cnt >= dwMaxItems) break; continue; }

void CPwSafeDlg::_Find(DWORD dwFindGroupId)
{
	if(m_bFileOpen == FALSE) return;

	const DWORD dwMaxItems = m_mgr.GetNumberOfEntries();
	if(dwMaxItems == 0) return; // Nothing to search for

	_SetDisplayDialog(true);

	DWORD dwFindGroupIndexMin = DWORD_MAX;
	if(dwFindGroupId != DWORD_MAX)
		dwFindGroupIndexMin = m_mgr.GetGroupByIdN(dwFindGroupId);

	DWORD dwFindGroupIndexMax = DWORD_MAX;
	if(dwFindGroupIndexMin != DWORD_MAX)
		dwFindGroupIndexMax = m_mgr.GetLastChildGroup(dwFindGroupIndexMin);

	CFindInDbDlg dlg;
	CString strGroupName; // Local copy of the group name
	if(dwFindGroupIndexMin != DWORD_MAX)
	{
		const PW_GROUP *pg = m_mgr.GetGroup(dwFindGroupIndexMin);
		if((pg != NULL) && (pg->pszGroupName != NULL))
		{
			strGroupName = pg->pszGroupName;
			dlg.m_lpGroupName = strGroupName;
		}
	}

	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		m_cList.DeleteAllItemsEx();
		m_bTANsOnly = TRUE;

		PW_TIME tNow;
		_GetCurrentPwTime(&tNow);

		DWORD dwFlags = 0;
		if(dlg.m_bTitle != FALSE)      dwFlags |= PWMF_TITLE;
		if(dlg.m_bUserName != FALSE)   dwFlags |= PWMF_USER;
		if(dlg.m_bURL != FALSE)        dwFlags |= PWMF_URL;
		if(dlg.m_bPassword != FALSE)   dwFlags |= PWMF_PASSWORD;
		if(dlg.m_bAdditional != FALSE) dwFlags |= PWMF_ADDITIONAL;
		if(dlg.m_bUUID != FALSE)       dwFlags |= PWMF_UUID;
		if(dlg.m_bGroupName != FALSE)  dwFlags |= PWMF_GROUPNAME;
		if(dlg.m_bRegex != FALSE)      dwFlags |= PWMS_REGEX;

		// Create the search group if it doesn't exist already
		DWORD dwGroupId = m_mgr.GetGroupId(PWS_SEARCHGROUP);
		if(dwGroupId == DWORD_MAX)
		{
			PW_GROUP pwTemplate;
			pwTemplate.pszGroupName = const_cast<LPTSTR>(PWS_SEARCHGROUP);
			pwTemplate.tCreation = tNow; m_mgr.GetNeverExpireTime(&pwTemplate.tExpire);
			pwTemplate.tLastAccess = tNow; pwTemplate.tLastMod = tNow;
			pwTemplate.uGroupId = 0; // 0 = create new group ID
			pwTemplate.uImageId = 40; // 40 = 'search' icon
			pwTemplate.usLevel = 0; pwTemplate.dwFlags = 0;

			VERIFY(m_mgr.AddGroup(&pwTemplate));
			dwGroupId = m_mgr.GetGroupId(PWS_SEARCHGROUP);
		}
		ASSERT((dwGroupId != DWORD_MAX) && (dwGroupId != 0));
		if((dwGroupId == DWORD_MAX) || (dwGroupId == 0)) { _SetDisplayDialog(false); return; }

		_UpdateCachedGroupIDs();

		DWORD cnt = 0;
		PW_ENTRY *pwFirst = NULL;
		std_string strError;

		while(true)
		{
			const DWORD dw = m_mgr.FindEx(dlg.m_strFind, dlg.m_bCaseSensitive,
				dwFlags, cnt, &strError);

			if(dw == DWORD_MAX) break;
			else
			{
				PW_ENTRY *p = m_mgr.GetEntry(dw);
				ASSERT_ENTRY(p);
				if(p == NULL) break;

				if(p->uGroupId != dwGroupId) // Not in 'Search Results' group
				{
					const DWORD dwGroupInx = m_mgr.GetGroupByIdN(p->uGroupId);
					ASSERT(dwGroupInx != DWORD_MAX);

					// Only accept entries that are stored in the specified group
					if((dwFindGroupIndexMin == DWORD_MAX) || ((dwGroupInx >= dwFindGroupIndexMin) &&
						(dwGroupInx <= dwFindGroupIndexMax)))
					{
						if(dlg.m_bExcludeBackups != FALSE)
						{
							if(_tcscmp(m_mgr.GetGroup(dwGroupInx)->pszGroupName, PWS_BACKUPGROUP) == 0)
								CPSDFM_CONTINUE;

							if(_tcscmp(m_mgr.GetGroup(dwGroupInx)->pszGroupName, PWS_BACKUPGROUP_SRC) == 0)
								CPSDFM_CONTINUE;
						}

						if(dlg.m_bExcludeExpired != FALSE)
						{
							if(_pwtimecmp(&tNow, &p->tExpire) > 0)
								CPSDFM_CONTINUE;
						}

						// The entry could get reallocated by AddEntry, therefore
						// save it to a local CString object
						// m_mgr.UnlockEntryPassword(p);
						// CString strTemp = p->pszPassword;
						// m_mgr.LockEntryPassword(p);

						_List_SetEntry(m_cList.GetItemCount(), p, TRUE, &tNow);

						if(pwFirst == NULL) pwFirst = p;

						// EraseCString(&strTemp); // Destroy the plaintext password
					}
				}
			}

			cnt = dw + 1;
			if(cnt >= dwMaxItems) break;
		}

		_Groups_SaveView();
		UpdateGroupList();
		_Groups_RestoreView();
		HTREEITEM hSelect = _GroupIdToHTreeItem(dwGroupId);
		if(hSelect != NULL)
		{
			m_cGroups.EnsureVisible(hSelect);
			m_cGroups.SelectItem(hSelect);
		}

		if(m_cList.GetItemCount() == 0) m_bTANsOnly = FALSE; // Use report list view
		AdjustPwListMode();

		// m_bModified = TRUE;
		if((m_cList.GetItemCount() > 0) && (pwFirst != NULL))
		{
			m_cList.FocusItem(0, TRUE);
			ShowEntryDetails(pwFirst);
		}
		else ShowEntryDetails(NULL);

		m_cList.SetFocus();

		if(strError.size() > 0)
			MessageBox(strError.c_str(), PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
	}

	_UpdateToolBar();
	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnUpdatePwlistFind(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen && (m_mgr.GetNumberOfEntries() != 0));
}

void CPwSafeDlg::OnPwlistFindInGroup()
{
	NotifyUserActivity();
	if(_CallPlugins(KPM_PWLIST_FIND_IN_GROUP_PRE, 0, 0) == FALSE) return;
	const DWORD dwCurGroup = GetSelectedGroupId();
	if(dwCurGroup == DWORD_MAX) return;
	_Find(dwCurGroup);
}

void CPwSafeDlg::OnUpdatePwlistFindInGroup(CCmdUI* pCmdUI)
{
	if((m_bFileOpen == FALSE) || (m_hLastSelectedGroup == NULL))
	{
		pCmdUI->Enable(FALSE);
		return;
	}

	DWORD dwGroupId = (DWORD)m_cGroups.GetItemData(m_hLastSelectedGroup);
	DWORD dwRefId = m_mgr.GetGroupId(PWS_SEARCHGROUP);
	BOOL bEnable = ((dwGroupId != dwRefId) ? TRUE : FALSE);
	pCmdUI->Enable(bEnable && (m_mgr.GetNumberOfEntries() != 0));
}

void CPwSafeDlg::OnPwlistDuplicate()
{
	NotifyUserActivity();

	if(_CallPlugins(KPM_DUPLICATE_ENTRY_PRE, 0, 0) == FALSE) return;

	PW_TIME tNow;
	_GetCurrentPwTime(&tNow);

	std::vector<std::vector<BYTE> > vUuids;
	const int nItemCount = m_cList.GetItemCount();

	for(int i = 0; i < nItemCount; ++i)
	{
		const UINT uState = m_cList.GetItemState(i, LVIS_SELECTED);

		if((uState & LVIS_SELECTED) != 0)
		{
			const DWORD dwEntryIndex = _ListSelToEntryIndex(static_cast<DWORD>(i));
			ASSERT(dwEntryIndex != DWORD_MAX); if(dwEntryIndex == DWORD_MAX) continue;

			PW_ENTRY *p = m_mgr.GetEntry(dwEntryIndex);
			ASSERT_ENTRY(p); if(p == NULL) continue;

			PW_ENTRY pwTemplate = *p; // Duplicate

			memset(pwTemplate.uuid, 0, 16); // We need a new UUID
			pwTemplate.tCreation = tNow; // Set new times
			// pwTemplate.tLastMod = tNow;
			pwTemplate.tLastAccess = tNow;

			std::basic_string<TCHAR> strTitle = pwTemplate.pszTitle;
			strTitle += _T(" - ");
			strTitle += FilterTrlComment(_T("Copy [of an item]"));
			pwTemplate.pszTitle = const_cast<TCHAR *>(strTitle.c_str());

			m_mgr.UnlockEntryPassword(&pwTemplate);
			VERIFY(m_mgr.AddEntry(&pwTemplate));
			m_mgr.LockEntryPassword(&pwTemplate);

			PW_ENTRY *pNew = m_mgr.GetLastEditedEntry();
			ASSERT_ENTRY(pNew);
			if(pNew != NULL)
			{
				std::vector<BYTE> vUuid(16);
				memcpy(&vUuid[0], pNew->uuid, 16);
				vUuids.push_back(vUuid);
			}
		}
	}

	m_bModified = TRUE;
	_UpdateCachedGroupIDs();

	// if(m_nAutoSort == 0) UpdatePasswordList();
	// else _SortListIfAutoSort();
	for(size_t iNew = 0; iNew < vUuids.size(); ++iNew)
	{
		PW_ENTRY *peToAdd = m_mgr.GetEntryByUuid(&(vUuids[iNew][0]));
		if(peToAdd == NULL) { ASSERT(FALSE); continue; }

		_List_SetEntry(static_cast<DWORD>(m_cList.GetItemCount()), peToAdd, TRUE, &tNow);
	}

	NewGUI_DeselectAllItems(&m_cList);
	for(size_t iFocus = 0; iFocus < vUuids.size(); ++iFocus) // Select all copies
	{
		const DWORD dwListItem = _EntryUuidToListPos(&(vUuids[iFocus][0]));
		if(dwListItem != DWORD_MAX)
		{
			m_cList.SetItemState(static_cast<int>(dwListItem), LVIS_SELECTED, LVIS_SELECTED);
			
			if(iFocus == (vUuids.size() - 1))
				m_cList.EnsureVisible(static_cast<int>(dwListItem), FALSE);
		}
	}

	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdatePwlistDuplicate(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_dwLastFirstSelectedItem != DWORD_MAX) ? TRUE : FALSE);
}

void CPwSafeDlg::OnInfoHomepage()
{
	NotifyUserActivity();
	ShellExecute(NULL, NULL, PWM_HOMEPAGE, NULL, NULL, SW_SHOW);
}

void CPwSafeDlg::OnViewAlwaysOnTop()
{
	NotifyUserActivity();

	UINT uState;
	BOOL bChecked;

	uState = m_menu.GetMenuState(ID_VIEW_ALWAYSONTOP, MF_BYCOMMAND);
	ASSERT(uState != 0xFFFFFFFF);

	if(uState & MF_CHECKED) bChecked = TRUE;
	else bChecked = FALSE;

	if(bChecked == TRUE)
	{
		uState = MF_UNCHECKED; // Toggle
		SetWindowPos(&wndNoTopMost, 0, 0, 0, 0,
			SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
		m_bAlwaysOnTop = FALSE;
	}
	else
	{
		uState = MF_CHECKED; // Toggle
		SetWindowPos(&wndTopMost, 0, 0, 0, 0,
			SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
		m_bAlwaysOnTop = TRUE;
	}

	m_menu.CheckMenuItem(ID_VIEW_ALWAYSONTOP, MF_BYCOMMAND | uState);
}

void CPwSafeDlg::ExportSelectedGroup(int nFormat)
{
	CPwExport cExp; PWEXPORT_OPTIONS pwo;
	CString strFile;
	DWORD dwSelectedGroup = GetSelectedGroupId();

	ASSERT(dwSelectedGroup != DWORD_MAX);
	if(dwSelectedGroup == DWORD_MAX) return;
	if(m_bForceAllowExport == FALSE)
	{
		if(IsUnsafeAllowed(this->m_hWnd) == FALSE) return;
	}

	cExp.SetManager(&m_mgr);
	cExp.SetNewLineSeq(m_bWindowsNewLine);
	cExp.SetFormat(nFormat);
	strFile = GetExportFile(nFormat, m_mgr.GetGroupById(dwSelectedGroup)->pszGroupName, TRUE);
	if(strFile.GetLength() != 0)
		if(GetExportOptions(&pwo, &cExp, FALSE) == TRUE)
			cExp.ExportGroup(strFile, dwSelectedGroup, &pwo, NULL);
}

void CPwSafeDlg::OnSafeExportGroupHtml()
{
	NotifyUserActivity();
	ExportSelectedGroup(PWEXP_HTML);
}

void CPwSafeDlg::OnSafeExportGroupXml()
{
	NotifyUserActivity();
	ExportSelectedGroup(PWEXP_XML);
}

void CPwSafeDlg::OnSafeExportGroupCsv()
{
	NotifyUserActivity();
	ExportSelectedGroup(PWEXP_CSV);
}

void CPwSafeDlg::OnUpdateSafeExportGroupHtml(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnUpdateSafeExportGroupXml(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnUpdateSafeExportGroupCsv(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnSafePrintGroup()
{
	NotifyUserActivity();

	const DWORD dwGroup = GetSelectedGroupId();
	if(dwGroup == DWORD_MAX) return;

	if(_CallPlugins(KPM_GROUP_PRINT_PRE, dwGroup, 0) == FALSE) return;

	_PrintGroup(dwGroup);
}

void CPwSafeDlg::OnUpdateSafePrintGroup(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnPwlistMoveUp()
{
	NotifyUserActivity();

	DWORD dwRelativeEntry = GetSelectedEntry();
	if(dwRelativeEntry == 0) return;

	DWORD dwEntryIndex = _ListSelToEntryIndex(dwRelativeEntry);
	ASSERT(dwEntryIndex != DWORD_MAX); if(dwEntryIndex == DWORD_MAX) return;

	_TouchEntry(dwRelativeEntry, FALSE);

	if(m_nAutoSort != 0)
	{
		MessageBox(TRL("Auto-sorting of the entry list is enabled; you cannot move entries manually."),
			PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
		return;
	}

	PW_ENTRY *p = m_mgr.GetEntry(dwEntryIndex);
	ASSERT(p != NULL); if(p == NULL) return;
	m_mgr.MoveEntry(p->uGroupId, dwRelativeEntry, dwRelativeEntry - 1);

	_List_SaveView();
	UpdatePasswordList();
	_List_RestoreView();
	m_cList.FocusItem(dwRelativeEntry - 1, TRUE);
	m_cList.EnsureVisible(dwRelativeEntry - 1, FALSE);
	m_bModified = TRUE;
	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdatePwlistMoveUp(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(((m_dwLastFirstSelectedItem != DWORD_MAX) && (m_dwLastNumSelectedItems == 1)) ? TRUE : FALSE);
}

void CPwSafeDlg::OnPwlistMoveTop()
{
	NotifyUserActivity();

	DWORD dwRelativeEntry = GetSelectedEntry();
	if(dwRelativeEntry == 0) return;

	DWORD dwEntryIndex = _ListSelToEntryIndex(dwRelativeEntry);
	ASSERT(dwEntryIndex != DWORD_MAX); if(dwEntryIndex == DWORD_MAX) return;

	PW_ENTRY *p;

	_TouchEntry(dwRelativeEntry, FALSE);

	if(m_nAutoSort != 0)
	{
		MessageBox(TRL("Auto-sorting of the entry list is enabled; you cannot move entries manually."),
			PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
		return;
	}

	p = m_mgr.GetEntry(dwEntryIndex);
	ASSERT(p != NULL); if(p == NULL) return;
	m_mgr.MoveEntry(p->uGroupId, dwRelativeEntry, 0);

	_List_SaveView();
	UpdatePasswordList();
	_List_RestoreView();
	m_cList.FocusItem(0, TRUE);
	m_cList.EnsureVisible(0, FALSE);
	m_bModified = TRUE;
	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdatePwlistMoveTop(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(((m_dwLastFirstSelectedItem != DWORD_MAX) && (m_dwLastNumSelectedItems == 1)) ? TRUE : FALSE);
}

void CPwSafeDlg::OnPwlistMoveDown()
{
	NotifyUserActivity();

	DWORD dwItemCount = (DWORD)m_cList.GetItemCount();
	PW_ENTRY *p;

	DWORD dwRelativeEntry = GetSelectedEntry();
	if(dwRelativeEntry == (dwItemCount - 1)) return;

	DWORD dwEntryIndex = _ListSelToEntryIndex();
	ASSERT(dwEntryIndex != DWORD_MAX); if(dwEntryIndex == DWORD_MAX) return;

	_TouchEntry(dwRelativeEntry, FALSE);

	if(m_nAutoSort != 0)
	{
		MessageBox(TRL("Auto-sorting of the entry list is enabled; you cannot move entries manually."),
			PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
		return;
	}

	p = m_mgr.GetEntry(dwEntryIndex);
	ASSERT(p != NULL); if(p == NULL) return;
	m_mgr.MoveEntry(p->uGroupId, dwRelativeEntry, dwRelativeEntry + 1);

	_List_SaveView();
	UpdatePasswordList();
	_List_RestoreView();
	const int nSel = min(dwRelativeEntry + 1, dwItemCount - 1);
	m_cList.FocusItem(nSel, TRUE);
	m_cList.EnsureVisible(nSel, FALSE);
	m_bModified = TRUE;
	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdatePwlistMoveDown(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(((m_dwLastFirstSelectedItem != DWORD_MAX) && (m_dwLastNumSelectedItems == 1)) ? TRUE : FALSE);
}

void CPwSafeDlg::OnPwlistMoveBottom()
{
	NotifyUserActivity();

	DWORD dwItemCount = (DWORD)m_cList.GetItemCount();
	PW_ENTRY *p;

	DWORD dwRelativeEntry = GetSelectedEntry();
	if(dwRelativeEntry == (dwItemCount - 1)) return;

	DWORD dwEntryIndex = _ListSelToEntryIndex(dwRelativeEntry);
	if(dwEntryIndex == DWORD_MAX) return;

	_TouchEntry(dwRelativeEntry, FALSE);

	if(m_nAutoSort != 0)
	{
		MessageBox(TRL("Auto-sorting of the entry list is enabled; you cannot move entries manually."),
			PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
		return;
	}

	p = m_mgr.GetEntry(dwEntryIndex);
	ASSERT(p != NULL); if(p == NULL) return;
	m_mgr.MoveEntry(p->uGroupId, dwRelativeEntry, dwItemCount - 1);

	_List_SaveView();
	UpdatePasswordList();
	_List_RestoreView();
	const int nSel = static_cast<int>(dwItemCount - 1);
	m_cList.FocusItem(nSel, TRUE);
	m_cList.EnsureVisible(nSel, FALSE);
	m_bModified = TRUE;
	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdatePwlistMoveBottom(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(((m_dwLastFirstSelectedItem != DWORD_MAX) && (m_dwLastNumSelectedItems == 1)) ? TRUE : FALSE);
}

void CPwSafeDlg::OnBeginDragPwlist(NMHDR* pNMHDR, LRESULT* pResult)
{
	NotifyUserActivity();

	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	*pResult = 0;

	if(m_bFileOpen == FALSE) return;

	const DWORD dwEntryIndex = _ListSelToEntryIndex();
	if(dwEntryIndex == DWORD_MAX) return;

	PW_ENTRY* p = m_mgr.GetEntry(dwEntryIndex);
	ASSERT_ENTRY(p); if(p == NULL) return;

	_SetDisplayDialog(true);

	// Auto-type drag&drop doesn't work with Firefox (always DROPEFFECT_NONE)
	// UINT uFlags = 0;
	// m_cList.HitTest(CPoint(pNMListView->ptAction), &uFlags);
	// const bool bAutoType = ((uFlags & LVHT_ONITEMICON) != 0);

	// if(!bAutoType)
	m_cGroups.m_drop.SetDragAccept(TRUE);

	TRY
	{
		CString strData;
		bool bDereferenceData = false;

		switch(pNMListView->iSubItem)
		{
		case 0:
			strData = p->pszTitle;
			bDereferenceData = true;
			break;
		case 1:
			strData = p->pszUserName;
			bDereferenceData = true;
			break;
		case 2:
			strData = p->pszURL;
			bDereferenceData = true;
			break;
		case 3:
			m_mgr.UnlockEntryPassword(p);
			strData = p->pszPassword;
			m_mgr.LockEntryPassword(p);
			bDereferenceData = true;
			break;
		case 4:
			strData = p->pszAdditional;
			bDereferenceData = true;
			break;
		case 5:
			_PwTimeToStringEx(p->tCreation, strData, CPwSafeDlg::m_bUseLocalTimeFormat);
			break;
		case 6:
			_PwTimeToStringEx(p->tLastMod, strData, CPwSafeDlg::m_bUseLocalTimeFormat);
			break;
		case 7:
			_PwTimeToStringEx(p->tLastAccess, strData, CPwSafeDlg::m_bUseLocalTimeFormat);
			break;
		case 8:
			_PwTimeToStringEx(p->tExpire, strData, CPwSafeDlg::m_bUseLocalTimeFormat);
			break;
		case 9:
			_UuidToString(p->uuid, &strData);
			break;
		case 10:
			strData = p->pszBinaryDesc;
			bDereferenceData = true;
			break;
		default:
			ASSERT(FALSE);
			break;
		}

		CString strToTransfer;
		if(!bDereferenceData) strToTransfer = strData;
		else strToTransfer = SprCompile(strData, false, p, &m_mgr, false, false);

		// if(bAutoType)
		// {
		//	strToTransfer.Empty();
		//	DropToBackgroundIfOptionEnabled(true);
		// }

		CDragDropEx::Perform(strToTransfer);
	}
	CATCH_ALL(eEx) { ASSERT(FALSE); }
	END_CATCH_ALL;

	m_cGroups.m_drop._RemoveDropSelection();
	m_cGroups.m_drop.SetDragAccept(FALSE);

	// if(bAutoType && (de != DROPEFFECT_NONE))
	// {
	//	WU_MouseClick(false);
	//	_AutoType(p, false, 0, NULL);
	// }

	_TouchEntry(GetSelectedEntry(), FALSE);
	_UpdateToolBar();

	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnFileChangeLanguage()
{
	NotifyUserActivity();

	CLanguagesDlg dlg;
	if(!dlg.InitEx(this->m_hWnd)) return;

	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		m_bRestartApplication = TRUE;
		OnFileExit();
		return;
	}
}

void CPwSafeDlg::OnInfoReadme()
{
	NotifyUserActivity();
	WU_OpenAppHelp(NULL, m_hWnd);
}

// void CPwSafeDlg::OnInfoLicense()
// {
//	NotifyUserActivity();
//	_OpenLocalFile(PWM_LICENSE_FILE, OLF_OPEN);
// }

// void CPwSafeDlg::OnInfoPrintLicense()
// {
//	NotifyUserActivity();
//	_OpenLocalFile(PWM_LICENSE_FILE, OLF_PRINT);
// }

void CPwSafeDlg::_SelChangeView(UINT uID)
{
	UINT uState;
	BOOL bChecked;
	BOOL *pFlag = &bChecked; // Just to avoid NULL pointer
	UINT uIndex = 0;
	RECT rect;

	uState = m_menu.GetMenuState(uID, MF_BYCOMMAND);
	ASSERT(uState != 0xFFFFFFFF);

	if(uID == ID_VIEW_TITLE)           { pFlag = &m_bShowTitle;      uIndex = 0; }
	else if(uID == ID_VIEW_USERNAME)   { pFlag = &m_bShowUserName;   uIndex = 1; }
	else if(uID == ID_VIEW_URL)        { pFlag = &m_bShowURL;        uIndex = 2; }
	else if(uID == ID_VIEW_PASSWORD)   { pFlag = &m_bShowPassword;   uIndex = 3; }
	else if(uID == ID_VIEW_NOTES)      { pFlag = &m_bShowNotes;      uIndex = 4; }
	else if(uID == ID_VIEW_CREATION)   { pFlag = &m_bShowCreation;   uIndex = 5; }
	else if(uID == ID_VIEW_LASTMOD)    { pFlag = &m_bShowLastMod;    uIndex = 6; }
	else if(uID == ID_VIEW_LASTACCESS) { pFlag = &m_bShowLastAccess; uIndex = 7; }
	else if(uID == ID_VIEW_EXPIRE)     { pFlag = &m_bShowExpire;     uIndex = 8; }
	else if(uID == ID_VIEW_UUID)       { pFlag = &m_bShowUUID;       uIndex = 9; }
	else if(uID == ID_VIEW_ATTACH)     { pFlag = &m_bShowAttach;     uIndex = 10; }
	else { ASSERT(FALSE); return; }

	if(uState & MF_CHECKED) bChecked = TRUE;
	else bChecked = FALSE;

	if(bChecked == TRUE)
	{
		uState = MF_UNCHECKED; // Toggle
		*pFlag = FALSE;
	}
	else
	{
		uState = MF_CHECKED; // Toggle
		*pFlag = TRUE;
	}
	m_menu.CheckMenuItem(uID, MF_BYCOMMAND | uState);

	GetClientRect(&rect);
	int nColumnWidth = rect.right - rect.left - GetSystemMetrics(SM_CXVSCROLL);
	nColumnWidth -= 8; nColumnWidth >>= 3;

	m_bShowColumn[uIndex] = *pFlag;
	if(*pFlag == TRUE) m_nColumnWidths[uIndex] = nColumnWidth;
	else m_nColumnWidths[uIndex] = 0;

	RefreshPasswordList();
	ProcessResize();
}

void CPwSafeDlg::_EnableViewMenuItems(BCMenu *pMenu)
{
	for(int i = 0; i < 11; ++i)
	{
		UINT uID = ID_VIEW_TITLE;
		BOOL *pFlag;

		if(i == 0) { uID = ID_VIEW_TITLE;           pFlag = &m_bShowTitle; }
		else if(i == 1) { uID = ID_VIEW_USERNAME;   pFlag = &m_bShowUserName; }
		else if(i == 2) { uID = ID_VIEW_URL;        pFlag = &m_bShowURL; }
		else if(i == 3) { uID = ID_VIEW_PASSWORD;   pFlag = &m_bShowPassword; }
		else if(i == 4) { uID = ID_VIEW_NOTES;      pFlag = &m_bShowNotes; }
		else if(i == 5) { uID = ID_VIEW_CREATION;   pFlag = &m_bShowCreation; }
		else if(i == 6) { uID = ID_VIEW_LASTMOD;    pFlag = &m_bShowLastMod; }
		else if(i == 7) { uID = ID_VIEW_LASTACCESS; pFlag = &m_bShowLastAccess; }
		else if(i == 8) { uID = ID_VIEW_EXPIRE;     pFlag = &m_bShowExpire; }
		else if(i == 9) { uID = ID_VIEW_UUID;       pFlag = &m_bShowUUID; }
		else if(i == 10) { uID = ID_VIEW_ATTACH;    pFlag = &m_bShowAttach; }
		else { ASSERT(FALSE); break; }

		const UINT uState = ((*pFlag == TRUE) ? MF_CHECKED : MF_UNCHECKED);
		pMenu->CheckMenuItem(uID, MF_BYCOMMAND | uState);
	}
}

void CPwSafeDlg::OnViewTitle()
{
	NotifyUserActivity();
	_SelChangeView(ID_VIEW_TITLE);
}

void CPwSafeDlg::OnViewUsername()
{
	NotifyUserActivity();
	_SelChangeView(ID_VIEW_USERNAME);
}

void CPwSafeDlg::OnViewUrl()
{
	NotifyUserActivity();
	_SelChangeView(ID_VIEW_URL);
}

void CPwSafeDlg::OnViewPassword()
{
	NotifyUserActivity();
	_SelChangeView(ID_VIEW_PASSWORD);
}

void CPwSafeDlg::OnViewNotes()
{
	NotifyUserActivity();
	_SelChangeView(ID_VIEW_NOTES);
}

void CPwSafeDlg::OnFileLock()
{
	NotifyUserActivity();

	// if(m_bDisplayDialog == TRUE) return; // Cannot do anything while displaying a dialog

	// This is a thread-critical function, therefore we fake to show a dialog, this
	// prevents the timer function from doing anything that could interfer with us
	_SetDisplayDialog(true);

	if(_CallPlugins(KPM_FILE_LOCK_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }

	if((m_bFileOpen == FALSE) && (m_bLocked == FALSE)) { _SetDisplayDialog(false); return; }

	CString strMenuItem;
	m_menu.GetMenuText(ID_FILE_LOCK, strMenuItem, MF_BYCOMMAND);

	LPCTSTR lpSuffix = _GetCmdAccelExt(_T("&Lock Workspace"));
	CString strExtended = TRL("&Lock Workspace");
	strExtended += _T("\t");
	strExtended += lpSuffix;

	const bool bDoLock = ((strMenuItem == TRL("&Lock Workspace")) ||
		(strMenuItem == strExtended));
	if(bDoLock) // Lock
	{
		_DeleteTemporaryFiles();

		// m_nLockedViewParams[0] = (long)m_cGroups.GetFirstVisibleItem();
		m_nLockedViewParams[1] = GetSelectedEntry();
		_Groups_SaveView();
		m_nLockedViewParams[2] = m_cList.GetTopIndex();

		const DWORD dwLastSelected = _ListSelToEntryIndex(DWORD_MAX);
		if(dwLastSelected != DWORD_MAX) memcpy(m_pPreLockItemUuid, m_mgr.GetEntry(dwLastSelected)->uuid, 16);
		else memset(m_pPreLockItemUuid, 0, 16);

		m_bExiting = TRUE;
		m_bIsLocking = TRUE;
        _CallPlugins(KPM_FILE_LOCK_CLOSE_PRE, 0, 0);
		OnFileClose();
        _CallPlugins(KPM_FILE_LOCK_CLOSE_POST, 0, 0);
		m_bIsLocking = FALSE;
		m_bExiting = FALSE;
		if(m_bFileOpen == TRUE)
		{
			// MessageBox(TRL("First close the open file before opening another one!"),
			//	PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
			_SetDisplayDialog(false); return;
		}

		if(m_strLastDb.IsEmpty() == TRUE) { _SetDisplayDialog(false); return; }

		m_bLocked = TRUE;
		strExtended = TRL("&Unlock Workspace");
		strExtended += _T("\t");
		strExtended += _GetCmdAccelExt(_T("&Lock Workspace"));
		m_menu.SetMenuText(ID_FILE_LOCK, strExtended, MF_BYCOMMAND);
		SetStatusTextEx(CString(TRL("Workspace locked")) + _T("."));
		m_btnTbLock.SetTooltipText(RemoveAcceleratorTipEx(TRL("&Unlock Workspace")));
		// m_tipN.UpdateTipText(RemoveAcceleratorTipEx(TRL("&Unlock Workspace")),
		//	&m_btnTbLock);

		// _UpdateTitleBar(); // Updated by _UpdateToolBar()

		ShowEntryDetails(NULL);

		if(m_bTrayed == FALSE) // Prevent focus loss (discussion 110f28cf)
		{
			if(m_bEntryView == TRUE) m_reEntryView.SetFocus();
			else if(m_bShowToolBar == TRUE) m_cQuickFind.SetFocus();
			else m_cList.SetFocus();
		}
	}
	else // Unlock
	{
		_OpenDatabase(NULL, m_strLastDb, NULL, NULL, FALSE, NULL, FALSE);

		if(m_bFileOpen == FALSE)
		{
			// strExtended = PWM_PRODUCT_NAME; strExtended += _T(" [");
			// strExtended += TRL("Workspace locked"); strExtended += _T("]");
			// SetWindowText(strExtended);
			// m_systray.SetTooltipText(strExtended); // Done in _OpenDatabase already
			// m_systray.SetIcon(m_hTrayIconLocked);

			// MessageBox(TRL("Workspace is locked."), PWM_PRODUCT_NAME_SHORT, MB_ICONINFORMATION | MB_OK);
			SetStatusTextEx(TRL("Workspace is locked."));
            _CallPlugins(KPM_FILE_UNLOCK_FAILED, 0, 0);
			_SetDisplayDialog(false); return;
		}

		NotifyUserActivity();
		m_bLocked = FALSE;
		strExtended = TRL("&Lock Workspace");
		strExtended += _T("\t");
		strExtended += _GetCmdAccelExt(_T("&Lock Workspace"));
		m_menu.SetMenuText(ID_FILE_LOCK, strExtended, MF_BYCOMMAND);
		m_btnTbLock.SetTooltipText(RemoveAcceleratorTipEx(TRL("&Lock Workspace")));
		// m_tipN.UpdateTipText(RemoveAcceleratorTipEx(TRL("&Lock Workspace")),
		//	&m_btnTbLock);

		// m_cGroups.SelectSetFirstVisible((HTREEITEM)m_nLockedViewParams[0]);
		// m_cGroups.SelectItem((HTREEITEM)m_nLockedViewParams[1]);
		_Groups_RestoreView();
		UpdatePasswordList();

		m_cList.EnsureVisible(m_cList.GetItemCount() - 1, FALSE);
		m_cList.EnsureVisible(m_nLockedViewParams[2], FALSE);

		if(m_nLockedViewParams[1] != DWORD_MAX)
			m_cList.FocusItem(static_cast<int>(m_nLockedViewParams[1]), TRUE);

		SetStatusTextEx(TRL("Ready."));

		// strExtended = PWM_PRODUCT_NAME;
		// strExtended += _T(" - ");
		// strExtended += CsFileOnly(&m_strFile);
		// SetWindowText(strExtended);
		// m_systray.SetIcon(m_hTrayIconNormal);
		// m_systray.SetTooltipText(strExtended);

		BYTE aNoUuid[16];
		memset(aNoUuid, 0, 16);
		if(memcmp(m_pPreLockItemUuid, aNoUuid, 16) != 0)
		{
			PW_ENTRY *p = m_mgr.GetEntryByUuid(m_pPreLockItemUuid);
			ShowEntryDetails(p); // NULL is allowed
		}

		m_cList.SetFocus();
	}

	_UpdatePeekPreview();
	_UpdateTrayIcon();
	_UpdateToolBar(TRUE);
	_SetDisplayDialog(false);

	if((m_bMinimizeOnLock != FALSE) && bDoLock && (m_bMinimized == FALSE) &&
		(m_bTrayed == FALSE))
		SetViewHideState(FALSE, FALSE);
}

void CPwSafeDlg::OnUpdateFileLock(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((((m_bFileOpen != FALSE) || (m_bLocked != FALSE)) &&
		!_IsDisplayingDialog()) ? TRUE : FALSE);
}

void CPwSafeDlg::OnGroupMoveTop()
{
	NotifyUserActivity();

	const DWORD dwGroupId = GetSelectedGroupId();
	if(dwGroupId == DWORD_MAX) return;

	_TouchGroup(dwGroupId, FALSE);

	// DWORD dwGroup = m_mgr.GetGroupByIdN(dwGroupId);
	// m_mgr.MoveGroup(dwGroup, 0);
	m_mgr.MoveGroupExDir(dwGroupId, -2);

	_Groups_SaveView(FALSE); UpdateGroupList(); _Groups_RestoreView();
	HTREEITEM hItem = _GroupIdToHTreeItem(dwGroupId);
	m_cGroups.EnsureVisible(hItem);
	m_cGroups.SelectItem(hItem);
	_List_SaveView(); UpdatePasswordList(); _List_RestoreView();
	m_bModified = TRUE;
	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdateGroupMoveTop(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnGroupMoveBottom()
{
	NotifyUserActivity();

	DWORD dwGroupId = GetSelectedGroupId();
	if(dwGroupId == DWORD_MAX) return;
	_TouchGroup(dwGroupId, FALSE);

	// DWORD dwGroup = m_mgr.GetGroupByIdN(dwGroupId);
	// m_mgr.MoveGroup(dwGroup, (DWORD)m_cGroups.GetCount() - 1);
	m_mgr.MoveGroupExDir(dwGroupId, 2);

	_Groups_SaveView(FALSE); UpdateGroupList(); _Groups_RestoreView();
	HTREEITEM hItem = _GroupIdToHTreeItem(dwGroupId);
	m_cGroups.EnsureVisible(hItem);
	m_cGroups.SelectItem(hItem);
	_List_SaveView(); UpdatePasswordList(); _List_RestoreView();
	m_bModified = TRUE;
	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdateGroupMoveBottom(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnGroupMoveUp()
{
	NotifyUserActivity();

	DWORD dwGroupId = GetSelectedGroupId();
	if(dwGroupId == DWORD_MAX) return;
	_TouchGroup(dwGroupId, FALSE);

	// DWORD dwGroup = m_mgr.GetGroupByIdN(dwGroupId);
	// if(dwGroup == 0) return; // Already is the top item
	// VERIFY(m_mgr.MoveGroup(dwGroup, dwGroup - 1));
	m_mgr.MoveGroupExDir(dwGroupId, -1);

	_Groups_SaveView(FALSE); UpdateGroupList(); _Groups_RestoreView();
	HTREEITEM hItem = _GroupIdToHTreeItem(dwGroupId);
	m_cGroups.EnsureVisible(hItem);
	m_cGroups.SelectItem(hItem);
	_List_SaveView(); UpdatePasswordList(); _List_RestoreView();
	m_bModified = TRUE;
	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdateGroupMoveUp(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnGroupMoveDown()
{
	NotifyUserActivity();

	DWORD dwGroupId = GetSelectedGroupId();
	if(dwGroupId == DWORD_MAX) return;
	_TouchGroup(dwGroupId, FALSE);

	// DWORD dwGroup = m_mgr.GetGroupByIdN(dwGroupId);
	// if(dwGroup == (m_mgr.GetNumberOfGroups() - 1)) return; // Already is last group
	// VERIFY(m_mgr.MoveGroup(dwGroup, dwGroup + 1));
	m_mgr.MoveGroupExDir(dwGroupId, 1);

	_Groups_SaveView(FALSE); UpdateGroupList(); _Groups_RestoreView();
	HTREEITEM hItem = _GroupIdToHTreeItem(dwGroupId);
	m_cGroups.EnsureVisible(hItem);
	m_cGroups.SelectItem(hItem);
	_List_SaveView(); UpdatePasswordList(); _List_RestoreView();
	m_bModified = TRUE;
	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdateGroupMoveDown(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

LRESULT CPwSafeDlg::OnTrayNotification(WPARAM wParam, LPARAM lParam)
{
	if(m_bSingleClickTrayIcon == TRUE)
	{
		if(lParam == WM_LBUTTONDBLCLK) return 0;
		if(lParam == WM_LBUTTONUP) lParam = WM_LBUTTONDBLCLK;
	}

	return m_systray.OnTrayNotification(wParam, lParam);
}

LRESULT CPwSafeDlg::OnUpdateClipboard(WPARAM wParam, LPARAM lParam)
{
	static int iSpinner = 0;

	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);
	if(m_nClipboardMethod != CM_ENHSECURE) return 0;

	if((!MakeClipboardDelayRender(m_hWnd, &m_hwndNextViewer)) && (iSpinner < 100))
	{
		// If we could not get onwership of the clipboard try again
		// sometimes we post WM_MY_UPDATECLIPBOARD message to soon
		// after putting delayed data on the clipboard whoever
		// requested the clipboard data still hasn't freed their lock
		// on the clipboard.
		++iSpinner;
		PostMessage(WM_MY_UPDATECLIPBOARD); // Failed to own clipboard so try again
	}
	else iSpinner = 0; // Stop posting WM_MY_UPDATECLIPBOARD messages

	return 0;
}

void CPwSafeDlg::OnViewHide()
{
	NotifyUserActivity();
	if(_IsDisplayingDialog()) return;
	ToggleViewHideState(TRUE);
	_UpdateToolBar(TRUE);
}

void CPwSafeDlg::OnImportCsv()
{
	NotifyUserActivity();
	_SetDisplayDialog(true);

	CString strFilter = TRL("CSV Files");
	strFilter += _T(" (*.csv)|*.csv|");
	strFilter += TRL("All Files");
	strFilter += _T(" (*.*)|*.*||");

	std::vector<std::basic_string<TCHAR> > vFiles = WU_GetFileNames(TRUE, _T("csv"),
		strFilter, TRUE, this, FALSE, TRUE);

	const DWORD dwGroupId = GetSelectedGroupId();
	ASSERT(dwGroupId != DWORD_MAX);

	for(size_t iFile = 0; iFile < vFiles.size(); ++iFile)
	{
		CPwImport pvi;
		const DWORD dwResult = pvi.ImportCsvToDb(vFiles[iFile].c_str(), &m_mgr, dwGroupId);

		if(dwResult == DWORD_MAX)
		{
			_SortListIfAutoSort();
			if(m_nAutoSort == 0) UpdatePasswordList();
			m_bModified = TRUE;
		}
		else
		{
			CString str = vFiles[iFile].c_str();
			str += _T("\r\n\r\n");
			str += TRL("An error occurred while importing the file. File cannot be imported.");
			str += _T("\r\n\r\n");
			str += TRL("Entry"); str += _T(": #");
			CString strTemp; strTemp.Format(_T("%u"), dwResult);
			str += strTemp;

			str += _T("\r\n\r\n");
			str += TRL("The help file contains detailed information about the expected input format. Do you want to open the help file?");

			if(MessageBox(str, PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_YESNO) == IDYES)
				WU_OpenAppHelp(PWM_HELP_CSV, m_hWnd);
		}
	}

	_UpdateToolBar();
	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnUpdateImportCsv(CCmdUI* pCmdUI)
{
	BOOL b = m_bFileOpen;
	b &= ((m_mgr.GetNumberOfGroups() != 0) ? TRUE : FALSE);
	pCmdUI->Enable(b);
}

void CPwSafeDlg::OnClickPwlist(NMHDR* pNMHDR, LRESULT* pResult)
{
	NotifyUserActivity();

	*pResult = 0;
	UNREFERENCED_PARAMETER(pNMHDR);

	// This would break the multiselect ability of the list
	// if((GetKeyState(VK_CONTROL) & 0x8000) != 0) OnPwlistEdit();

	_UpdateToolBar();
}

void CPwSafeDlg::_SortList(DWORD dwByField, BOOL bAutoSortCall)
{
	const DWORD dwGroupId = GetSelectedGroupId();

	if(dwGroupId == DWORD_MAX) return;
	if(dwGroupId == m_mgr.GetGroupId(PWS_SEARCHGROUP))
	{
		// if(bAutoSortCall == FALSE)
		// {
		//	_RemoveSearchGroup();
		//	m_cList.DeleteAllItemsEx();
		// }
		return;
	}

	const int nTop = m_cList.GetTopIndex();

	m_mgr.SortGroup(dwGroupId, dwByField);
	if(bAutoSortCall == FALSE) m_bModified = TRUE;
	UpdatePasswordList();
	_UpdateToolBar();

	m_cList.EnsureVisible(m_cList.GetItemCount() - 1, FALSE);
	m_cList.EnsureVisible(nTop, FALSE);
}

void CPwSafeDlg::OnColumnClickPwlist(NMHDR* pNMHDR, LRESULT* pResult)
{
	NotifyUserActivity();

	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	*pResult = 0;
	if(_CheckIfCanSort() == FALSE) return;
	_SortList((DWORD)pNMListView->iSubItem, FALSE);
}

void CPwSafeDlg::OnImportCWallet()
{
	NotifyUserActivity();
	_SetDisplayDialog(true);

	CString strFilter = TRL("Text Files");
	strFilter += _T(" (*.txt)|*.txt|");
	strFilter += TRL("All Files");
	strFilter += _T(" (*.*)|*.*||");

	std::vector<std::basic_string<TCHAR> > vFiles = WU_GetFileNames(TRUE, _T("txt"),
		strFilter, TRUE, this, FALSE, TRUE);

	for(size_t iFile = 0; iFile < vFiles.size(); ++iFile)
	{
		CPwImport pvi;
		if(pvi.ImportCWalletToDb(vFiles[iFile].c_str(), &m_mgr) == TRUE)
		{
			UpdateGroupList();
			m_cGroups.EnsureVisible(_GetLastGroupItem(&m_cGroups));
			_SortListIfAutoSort();
			if(m_nAutoSort == 0) UpdatePasswordList();
			m_bModified = TRUE;
		}
		else
		{
			CString strMsg = vFiles[iFile].c_str();
			strMsg += _T("\r\n\r\n");
			strMsg += TRL("An error occurred while importing the file. File cannot be imported.");
			MessageBox(strMsg, PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
		}
	}

	_UpdateToolBar();
	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnUpdateImportCWallet(CCmdUI* pCmdUI)
{
	BOOL b = m_bFileOpen;
	b &= ((m_mgr.GetNumberOfGroups() != 0) ? TRUE : FALSE);
	pCmdUI->Enable(b);
}

BOOL CPwSafeDlg::PreTranslateMessage(MSG* pMsg)
{
	ASSERT(pMsg != NULL);

	// if(m_hAccel != NULL)
	// {
	//	CWnd *pWnd = GetFocus();
	//	if(pWnd != NULL)
	//	{
	//		if((pWnd != &m_reEntryView) || (m_bFileOpen == FALSE) || (m_bLocked == TRUE))
	//		{
	//			if(TranslateAccelerator(this->m_hWnd, m_hAccel, pMsg))
	//				return(TRUE);
	//		}
	//	}
	// }

	if(((pMsg->message == WM_KEYDOWN) || (pMsg->message == WM_SYSKEYDOWN)) && (m_hAccel != NULL))
	{
		CWnd *pFocusWnd = GetFocus();

		if(pFocusWnd == &m_reEntryView)
		{
			if((pMsg->wParam != 'C') && (pMsg->wParam != 'A'))
			{
				if(TranslateAccelerator(this->m_hWnd, m_hAccel, pMsg) != FALSE)
					return TRUE;
			}
		}
		// else if(pFocusWnd == &m_cQuickFind)
		else if(NewGUI_ComboBox_HasFocus(m_cQuickFind.m_hWnd,
			(pFocusWnd != NULL) ? pFocusWnd->m_hWnd : NULL))
		{
			if((pMsg->wParam != 'C') && (pMsg->wParam != 'A') && (pMsg->wParam != 'V') && (pMsg->wParam != 'X'))
			{
				if(TranslateAccelerator(this->m_hWnd, m_hAccel, pMsg) != FALSE)
					return TRUE;
			}
		}
		else
		{
			if(TranslateAccelerator(this->m_hWnd, m_hAccel, pMsg) != FALSE)
				return TRUE;
		}
	}

	if(((pMsg->message == WM_KEYDOWN) || (pMsg->message == WM_KEYUP)) &&
		(pMsg->wParam == VK_RETURN))
	{
		CWnd *pFocusWnd = GetFocus();

		// if(pFocusWnd == &m_cQuickFind)
		if(NewGUI_ComboBox_HasFocus(m_cQuickFind.m_hWnd,
			(pFocusWnd != NULL) ? pFocusWnd->m_hWnd : NULL))
		{
			if(pMsg->message == WM_KEYDOWN) _DoQuickFind(NULL);
			return TRUE; // Also ignores WM_KEYUP sent to the edit control
		}
		else if(pFocusWnd == &m_cList)
		{
			if(pMsg->message == WM_KEYDOWN) OnPwlistEdit();
			return TRUE; // Also ignores WM_KEYUP sent to the edit control
		}
	}

	// When changing Ctrl+E, also change the tooltip text of the quick find box
	if(((pMsg->message == WM_KEYDOWN) || (pMsg->message == WM_KEYUP)) &&
		(pMsg->wParam == 'E') && ((GetKeyState(VK_CONTROL) & 0x8000) != 0) &&
		// Enforce that Alt is up (e.g. on Polish systems AltGr+E,
		// i.e. Ctrl+Alt+E, is used to enter a character)
		((GetKeyState(VK_MENU) & 0x8000) == 0))
	{
		if(pMsg->message == WM_KEYDOWN) m_cQuickFind.SetFocus();
		return TRUE;
	}

	if((pMsg->message == WM_POWERBROADCAST) &&
		((pMsg->wParam == PBT_APMQUERYSUSPEND) ||
		(pMsg->wParam == PBT_APMSUSPEND)))
	{
		if(m_bLockOnWinLock != FALSE) _ChangeLockState(TRUE);
	}

	// m_tipN.RelayEvent(pMsg);
	m_tipB.RelayEvent(pMsg);

	return CDialog::PreTranslateMessage(pMsg);
}

void CPwSafeDlg::OnUpdateFileNew(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(TRUE); // m_bLocked == FALSE
}

void CPwSafeDlg::OnUpdateFileOpen(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(TRUE); // m_bLocked == FALSE
}

void CPwSafeDlg::OnImportPwSafe()
{
	NotifyUserActivity();

	_SetDisplayDialog(true);

	CString strFilter = TRL("Text Files");
	strFilter += _T(" (*.txt)|*.txt|");
	strFilter += TRL("All Files");
	strFilter += _T(" (*.*)|*.*||");

	std::vector<std::basic_string<TCHAR> > vFiles = WU_GetFileNames(TRUE, _T("txt"),
		strFilter, TRUE, this, FALSE, TRUE);

	for(size_t iFile = 0; iFile < vFiles.size(); ++iFile)
	{
		CPwImport pvi;
		if(pvi.ImportPwSafeToDb(vFiles[iFile].c_str(), &m_mgr) == TRUE)
		{
			_Groups_SaveView(); UpdateGroupList(); _Groups_RestoreView();
			_SortListIfAutoSort();
			if(m_nAutoSort == 0) UpdatePasswordList();
			m_bModified = TRUE;
		}
		else
		{
			CString strMsg = vFiles[iFile].c_str();
			strMsg += _T("\r\n\r\n");
			strMsg += TRL("An error occurred while importing the file. File cannot be imported.");
			MessageBox(strMsg, PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
		}
	}

	_UpdateToolBar();
	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnUpdateImportPwSafe(CCmdUI* pCmdUI)
{
	BOOL b = m_bFileOpen;
	b &= ((m_mgr.GetNumberOfGroups() != 0) ? TRUE : FALSE);
	pCmdUI->Enable(b);
}

void CPwSafeDlg::OnViewCreation()
{
	NotifyUserActivity();
	_SelChangeView(ID_VIEW_CREATION);
}

void CPwSafeDlg::OnViewLastMod()
{
	NotifyUserActivity();
	_SelChangeView(ID_VIEW_LASTMOD);
}

void CPwSafeDlg::OnViewLastAccess()
{
	NotifyUserActivity();
	_SelChangeView(ID_VIEW_LASTACCESS);
}

void CPwSafeDlg::OnViewExpire()
{
	NotifyUserActivity();
	_SelChangeView(ID_VIEW_EXPIRE);
}

void CPwSafeDlg::OnViewUuid()
{
	NotifyUserActivity();
	_SelChangeView(ID_VIEW_UUID);
}

void CPwSafeDlg::_TouchGroup(DWORD dwGroupId, BOOL bEdit)
{
	PW_GROUP *pGroup = m_mgr.GetGroupById(dwGroupId);
	ASSERT(pGroup != NULL);

	PW_TIME tNow;
	_GetCurrentPwTime(&tNow);

	pGroup->tLastAccess = tNow;
	if(bEdit == TRUE) pGroup->tLastMod = tNow;

	if(m_bSaveOnLATMod == TRUE) m_bModified = TRUE;
}

void CPwSafeDlg::_TouchEntry(DWORD dwListIndex, BOOL bEdit)
{
	LV_ITEM lvi;
	TCHAR szTemp[1024];
	CString strTemp;
	BYTE aUuid[16];
	PW_TIME tNow;

	if(dwListIndex >= (DWORD)m_cList.GetItemCount()) return;

	// ASSERT(dwListIndex != DWORD_MAX);
	// if(dwListIndex == DWORD_MAX) return;

	ZeroMemory(&lvi, sizeof(LV_ITEM));
	lvi.iItem = (int)dwListIndex;
	lvi.iSubItem = 9;
	lvi.mask = LVIF_TEXT;
	lvi.pszText = szTemp;
	lvi.cchTextMax = 1024;
	m_cList.GetItem(&lvi);

	strTemp = lvi.pszText;
	_StringToUuid(strTemp, aUuid);

	EraseCString(&strTemp);

	_GetCurrentPwTime(&tNow);

	PW_ENTRY *pEntry = m_mgr.GetEntryByUuid(aUuid);
	ASSERT_ENTRY(pEntry);

	if(pEntry != NULL)
	{
		pEntry->tLastAccess = tNow;
		if(bEdit == TRUE) pEntry->tLastMod = tNow;

		_UpdateCachedGroupIDs();
		_List_SetEntry(dwListIndex, pEntry, FALSE, &tNow);
	}

	if(m_bSaveOnLATMod == TRUE) m_bModified = TRUE;
}

void CPwSafeDlg::OnTbOpen()
{
	OnFileOpen();
}

void CPwSafeDlg::OnTbSave()
{
	OnFileSave();
}

#define UTB_ENSURE_ENABLED_STATE(_pWnd, _bEnabled) { if((_pWnd)->IsWindowEnabled() != (_bEnabled)) { \
	(_pWnd)->EnableWindow(_bEnabled); bNeedSetFocus = TRUE; } }

void CPwSafeDlg::_UpdateToolBar(BOOL bForceUpdate)
{
	_UpdateTitleBar();

	CWnd *pFocusWnd = this->GetFocus();
	BOOL bNeedSetFocus = FALSE;

	const DWORD dwSelectedEntry = ((m_bLocked == FALSE) ? GetSelectedEntry() : DWORD_MAX);
	const DWORD dwNumSelectedEntries = ((m_bLocked == FALSE) ? GetSelectedEntriesCount() : 0);
	const DWORD dwNumberOfGroups = ((m_bLocked == FALSE) ? m_mgr.GetNumberOfGroups() : 0);
	const DWORD dwNumberOfEntries = ((m_bLocked == FALSE) ? m_mgr.GetNumberOfEntries() : 0);

	CString strStatus;
	strStatus.Format(TRL("Total: %u groups / %u entries"), dwNumberOfGroups, dwNumberOfEntries);
	SetStatusTextEx(strStatus, 0);
	strStatus.Format(TRL("%u of %u selected"), (dwNumSelectedEntries == DWORD_MAX) ? 0 : dwNumSelectedEntries,
		(m_bFileOpen == TRUE) ? m_cList.GetItemCount() : 0);
	SetStatusTextEx(strStatus, 1);

	if((m_bMinimized == TRUE) || (m_bTrayed == TRUE)) return;

	const ULONGLONG ullListParams = (((ULONGLONG)(dwSelectedEntry & 0x0000FFFF) << 48) |
		((ULONGLONG)(dwNumSelectedEntries & 0x000000FF) << 40) |
		((ULONGLONG)(dwNumberOfGroups & 0x000000FF) << 32) |
		((ULONGLONG)(dwNumberOfEntries & 0x0000FFFF) << 16) |
		((ULONGLONG)(m_bLocked & 0x00000001) << 15) |
		((ULONGLONG)(m_bModified & 0x00000001) << 14));

	// ullListParams = (((ULONGLONG)dwSelectedEntry) << 34) ^
	//	(((ULONGLONG)dwNumSelectedEntries) << 2) ^ (m_bLocked << 1) ^
	//	m_bModified ^ (((ULONGLONG)dwNumberOfEntries) << 14);

	// Update the rest (toolbar, entry view, ...) only if needed
	if((ullListParams == m_ullLastListParams) && (bForceUpdate == FALSE)) return;
	m_ullLastListParams = ullListParams;

	const DWORD dwFirstEntryIndex = _ListSelToEntryIndex(dwSelectedEntry);
	PW_ENTRY *p = NULL;

	if(dwFirstEntryIndex != DWORD_MAX) p = m_mgr.GetEntry(dwFirstEntryIndex);
	ShowEntryDetails(p);

	if(m_bModified)
		m_btnTbSave.SetBitmaps(IDB_TB_SAVE, RGB(255,0,255));
	else
		m_btnTbSave.SetBitmaps(IDB_TB_SAVE_DISABLED, RGB(255,0,255));

	if(m_bFileOpen == TRUE)
	{
		UTB_ENSURE_ENABLED_STATE(&m_btnTbAddEntry, (m_mgr.GetNumberOfGroups() != 0) ? TRUE : FALSE);
		UTB_ENSURE_ENABLED_STATE(&m_cQuickFind, TRUE);

		if((m_bFileReadOnly == FALSE) && (dwNumberOfGroups > 0))
		{
			if(m_bAllowSaveIfModifiedOnly == FALSE) UTB_ENSURE_ENABLED_STATE(&m_btnTbSave, TRUE)
			else
			{
				if(m_bModified == FALSE) UTB_ENSURE_ENABLED_STATE(&m_btnTbSave, FALSE)
				else UTB_ENSURE_ENABLED_STATE(&m_btnTbSave, TRUE)
			}
		}
		else UTB_ENSURE_ENABLED_STATE(&m_btnTbSave, FALSE)
	}
	else
	{
		UTB_ENSURE_ENABLED_STATE(&m_btnTbAddEntry, FALSE);
		UTB_ENSURE_ENABLED_STATE(&m_cQuickFind, FALSE);
		UTB_ENSURE_ENABLED_STATE(&m_btnTbSave, FALSE);
	}

	if((dwSelectedEntry != DWORD_MAX) && (dwNumSelectedEntries == 1))
	{
		UTB_ENSURE_ENABLED_STATE(&m_btnTbCopyUser, TRUE);
		UTB_ENSURE_ENABLED_STATE(&m_btnTbCopyPw, TRUE);
		UTB_ENSURE_ENABLED_STATE(&m_btnTbEditEntry, TRUE);
	}
	else
	{
		UTB_ENSURE_ENABLED_STATE(&m_btnTbCopyUser, FALSE);
		UTB_ENSURE_ENABLED_STATE(&m_btnTbCopyPw, FALSE);
		UTB_ENSURE_ENABLED_STATE(&m_btnTbEditEntry, FALSE);
	}

	if((dwSelectedEntry != DWORD_MAX) && (dwNumSelectedEntries >= 1))
		UTB_ENSURE_ENABLED_STATE(&m_btnTbDeleteEntry, TRUE)
	else
		UTB_ENSURE_ENABLED_STATE(&m_btnTbDeleteEntry, FALSE)

	if(m_bFileOpen && (dwNumberOfEntries != 0))
		UTB_ENSURE_ENABLED_STATE(&m_btnTbFind, TRUE)
	else
		UTB_ENSURE_ENABLED_STATE(&m_btnTbFind, FALSE)

	if(m_bFileOpen || m_bLocked)
		UTB_ENSURE_ENABLED_STATE(&m_btnTbLock, TRUE)
	else
		UTB_ENSURE_ENABLED_STATE(&m_btnTbLock, FALSE)

	NewGUI_EnableSortHeader(&m_cList, !_IsSearchGroup());

	if((pFocusWnd != NULL) && (bNeedSetFocus == TRUE))
		pFocusWnd->SetFocus(); // Restore the focus!
}

void CPwSafeDlg::OnTbNew()
{
	OnFileNew();
}

void CPwSafeDlg::OnTbCopyUser()
{
	OnPwlistCopyUser();
}

void CPwSafeDlg::OnTbCopyPw()
{
	OnPwlistCopyPw();
}

void CPwSafeDlg::OnTbAddEntry()
{
	OnPwlistAdd();
}

void CPwSafeDlg::OnTbEditEntry()
{
	OnPwlistEdit();
}

void CPwSafeDlg::OnTbDeleteEntry()
{
	OnPwlistDelete();
}

void CPwSafeDlg::OnTbFind()
{
	OnPwlistFind();
}

void CPwSafeDlg::OnTbLock()
{
	OnFileLock();
}

void CPwSafeDlg::OnTbAbout()
{
	OnInfoAbout();
}

void CPwSafeDlg::OnViewShowToolBar()
{
	NotifyUserActivity();

	UINT uState = m_menu.GetMenuState(ID_VIEW_SHOWTOOLBAR, MF_BYCOMMAND);
	ASSERT(uState != 0xFFFFFFFF);

	BOOL bChecked;
	if((uState & MF_CHECKED) != 0) bChecked = TRUE;
	else bChecked = FALSE;

	if(bChecked == TRUE)
	{
		uState = MF_UNCHECKED; // Toggle
		bChecked = FALSE;
	}
	else
	{
		uState = MF_CHECKED; // Toggle
		bChecked = TRUE;
	}
	m_menu.CheckMenuItem(ID_VIEW_SHOWTOOLBAR, MF_BYCOMMAND | uState);

	m_bShowToolBar = bChecked;

	_ShowToolBar(m_bShowToolBar);
	ProcessResize();
}

void CPwSafeDlg::_ShowToolBar(BOOL bShow)
{
	const int nCommand = ((bShow == FALSE) ? SW_HIDE : SW_SHOW);

	m_btnTbNew.ShowWindow(nCommand); m_btnTbOpen.ShowWindow(nCommand);
	m_btnTbSave.ShowWindow(nCommand); m_btnTbAddEntry.ShowWindow(nCommand);
	m_btnTbEditEntry.ShowWindow(nCommand); m_btnTbDeleteEntry.ShowWindow(nCommand);
	m_btnTbCopyPw.ShowWindow(nCommand); m_btnTbCopyUser.ShowWindow(nCommand);
	m_btnTbFind.ShowWindow(nCommand); m_btnTbLock.ShowWindow(nCommand);
	m_btnTbAbout.ShowWindow(nCommand);

	m_cQuickFind.ShowWindow(nCommand);

	GetDlgItem(IDC_STATIC_TBSEP0)->ShowWindow(nCommand); GetDlgItem(IDC_STATIC_TBSEP1)->ShowWindow(nCommand);
	GetDlgItem(IDC_STATIC_TBSEP2)->ShowWindow(nCommand); GetDlgItem(IDC_STATIC_TBSEP3)->ShowWindow(nCommand);
	GetDlgItem(IDC_STATIC_TBSEP4)->ShowWindow(nCommand); GetDlgItem(IDC_STATIC_TBSEP5)->ShowWindow(nCommand);
}

BOOL CPwSafeDlg::_RemoveSearchGroup()
{
	const DWORD dwSearchGroupId = m_mgr.GetGroupId(PWS_SEARCHGROUP);
	if(dwSearchGroupId != DWORD_MAX)
	{
		const DWORD dwSearchItemsCount = m_mgr.GetNumberOfItemsInGroupN(dwSearchGroupId);
		if(dwSearchItemsCount == 0) // Delete only if the group is empty
		{
			m_mgr.DeleteGroupById(dwSearchGroupId, FALSE); // Remove from password manager

			HTREEITEM hTreeNode = _GroupIdToHTreeItem(dwSearchGroupId);
			if(hTreeNode != NULL) m_cGroups.DeleteItem(hTreeNode);
			else { ASSERT(FALSE); UpdateGroupList(); }

			ShowEntryDetails(NULL);
			return TRUE;
		}
	}

	return FALSE;
}

void CPwSafeDlg::OnPwlistMassModify()
{
	NotifyUserActivity();

	if(m_bFileOpen == FALSE) return;

	CEntryPropertiesDlg dlg;
	dlg.m_pMgr = &m_mgr;
	dlg.m_pParentIcons = &m_ilIcons;
	dlg.m_dwDefaultExpire = m_dwDefaultExpire;

	if(_CallPlugins(KPM_MASSMODIFY_ENTRIES_PRE, 0, 0) == FALSE) return;

	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		const DWORD dwGroupId = m_mgr.GetGroupIdByIndex(static_cast<DWORD>(dlg.m_nGroupInx));
		ASSERT(dwGroupId != DWORD_MAX);

		for(DWORD i = 0; i < static_cast<DWORD>(m_cList.GetItemCount()); ++i)
		{
			const UINT uState = m_cList.GetItemState(static_cast<int>(i), LVIS_SELECTED);
			if((uState & LVIS_SELECTED) != 0)
			{
				const DWORD dwIndex = _ListSelToEntryIndex(i); // Uses UUID to get the entry
				ASSERT(dwIndex != DWORD_MAX); if(dwIndex == DWORD_MAX) continue;

				PW_ENTRY *p = m_mgr.GetEntry(dwIndex);
				ASSERT(p != NULL); if(p == NULL) continue;

				if(dlg.m_bModGroup != FALSE) p->uGroupId = dwGroupId;
				if(dlg.m_bModIcon != FALSE) p->uImageId = (DWORD)dlg.m_nIconId;
				if(dlg.m_bModExpire != FALSE) p->tExpire = dlg.m_tExpire;
				if(dlg.m_bDelAttach != FALSE) CPwUtil::RemoveBinaryData(p);

				_TouchEntry(i, TRUE); // Doesn't change the entry except the time fields
			}
		}

		if(dlg.m_bModGroup == TRUE) // We need a full update
		{
			_List_SaveView();
			_SortListIfAutoSort();
			if(m_nAutoSort == 0) UpdatePasswordList();
			_List_RestoreView();
		}
		else // Refresh is enough, no entries have been moved
		{
			_SortListIfAutoSort();
			if(m_nAutoSort == 0) RefreshPasswordList();
		}

		m_bModified = TRUE;
		_UpdateToolBar(TRUE);
	}
}

void CPwSafeDlg::OnUpdatePwlistMassModify(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_bFileOpen && (m_dwLastNumSelectedItems >= 1)) ? TRUE : FALSE);
}

void CPwSafeDlg::OnKeyDownPwlist(NMHDR* pNMHDR, LRESULT* pResult)
{
	NotifyUserActivity();

	UNREFERENCED_PARAMETER(pNMHDR);
	*pResult = 0;

	m_bCachedToolBarUpdate = TRUE;
}

CString CPwSafeDlg::_MakeRtfString(LPCTSTR lptString)
{
	if(lptString == NULL) { ASSERT(FALSE); return CString(); }

#ifdef _UNICODE
	const WCHAR* lpw = lptString;
#else
	const WCHAR* lpw = _StringToUnicode(lptString);
#endif
	if(lpw == NULL) { ASSERT(FALSE); return CString(lptString); }

	CStringBuilderEx sb;

	const size_t uMax = wcslen(lpw);
	for(size_t u = 0; u < uMax; ++u)
	{
		const WCHAR wch = lpw[u];

		if(wch == L'\\') sb.Append(_T("\\\\"));
		else if(wch == L'\r') { }
		else if(wch == L'{') sb.Append(_T("\\{"));
		else if(wch == L'}') sb.Append(_T("\\}"));
		else if(wch == L'\n') sb.Append(_T("\\par "));
		else if(wch <= 255) sb.Append(static_cast<TCHAR>(wch));
		else
		{
			CString strT;
			strT.Format(_T("\\u%d?"), static_cast<int>(wch));
			sb.Append((LPCTSTR)strT);
		}

		// else if((tch < 128) && (tch >= 32)) str += tch;
		// else
		// {
		//	str2.Format(_T("%02x"), (BYTE)(tch & 0xFF));
		//	str += _T("\\'"); str += str2;
		// }
	}

#ifndef _UNICODE
	SAFE_DELETE_ARRAY(lpw);
#endif

	return CString(sb.ToString().c_str());
}

void CPwSafeDlg::ShowEntryDetails(PW_ENTRY *p)
{
	CString str, str2;

	if(p != NULL) { ASSERT_ENTRY(p); }

	if(m_bEntryView == FALSE) return;

	if(p == NULL) // if p == NULL, just clear the view
	{
#ifndef _UNICODE
		m_reEntryView.SetRTF(_T(""), SF_TEXT);
#else
		m_reEntryView.SetRTF(_T(""), SF_TEXT | SF_UNICODE);
#endif
		return;
	}

	CHARRANGE crURL;
	ZeroMemory(&crURL, sizeof(CHARRANGE));

	// === Begin entry view RTF assembly ===

	CString strTemp;
	str = _T("{\\rtf1\\ansi\\ansicpg");
	strTemp.Format(_T("%u"), m_uACP);
	str += strTemp;
	str += _T("\\deff0{\\fonttbl{\\f0\\fswiss MS Sans Serif;}{\\f1\\froman\\fcharset2 Symbol;}{\\f2\\fswiss ");
	str += m_strListFontFace;
	str += _T(";}{\\f3\\fswiss Arial;}");
	if(!m_strNotesFontRtfSpec.IsEmpty())
	{
		str += _T("{\\f4");
		str += m_strNotesFontRtfSpec;
		str += _T(";}");
	}
	str += _T("}");
	str += _T("{\\colortbl\\red0\\green0\\blue0;}");
	str += _T("\\deflang1031\\pard\\plain\\f2\\cf0 ");
	strTemp.Format(_T("%d"), m_nListFontSize * 2);
	str += _T("\\fs"); str += strTemp;
	str += _T("\\b ");
	str += TRL("Group:"); str += _T("\\b0  ");
	const PW_GROUP *pg = m_mgr.GetGroupById(p->uGroupId); ASSERT(pg != NULL);
	if(pg != NULL) str += _MakeRtfString(pg->pszGroupName);

	m_lGroupUrlStart = static_cast<LONG>(_tcslen(TRL("Group:")) + 1);
	crURL.cpMin += (m_lGroupUrlStart + static_cast<LONG>(_tcslen(pg->pszGroupName)));

	if((p->pszTitle != NULL) && (p->pszTitle[0] != 0))
	{
		str += _T(", \\b ");
		str += TRL("Title:"); str += _T("\\b0  ");
		str += _MakeRtfString(p->pszTitle);

		crURL.cpMin += static_cast<LONG>(_tcslen(TRL("Title:")) + 3 +
			_tcslen(p->pszTitle));
	}

	if((p->pszUserName != NULL) && (p->pszUserName[0] != 0))
	{
		str += _T(", \\b ");
		str += TRL("User Name"); str += _T(":\\b0  ");

		crURL.cpMin += static_cast<LONG>(_tcslen(TRL("User Name")) + 4);

		if(m_bUserStars == FALSE)
		{
			str += _MakeRtfString(p->pszUserName);
			crURL.cpMin += static_cast<LONG>(_tcslen(p->pszUserName));
		}
		else
		{
			str += _T("********");
			crURL.cpMin += 8;
		}
	}

	m_mgr.UnlockEntryPassword(p);
	if((p->pszPassword != NULL) && (p->pszPassword[0] != 0))
	{
		str += _T(", \\b ");
		str += TRL("Password:"); str += _T("\\b0  ");

		crURL.cpMin += static_cast<LONG>(_tcslen(TRL("Password:")) + 3);

		if(m_bPasswordStars == FALSE)
		{
			// Do not dereference the password here; if dereferencing should
			// be implemented here in the future, make sure to make local
			// copy of the password in order to not conflict with in-memory
			// protection and the Spr engine
			CString strTempPassword = _MakeRtfString(p->pszPassword);

			str += strTempPassword;
			crURL.cpMin += static_cast<LONG>(_tcslen(p->pszPassword));

			EraseCString(&strTempPassword);
		}
		else
		{
			str += _T("********");
			crURL.cpMin += 8;
		}
	}
	m_mgr.LockEntryPassword(p);

	if((p->pszURL != NULL) && (p->pszURL[0] != 0))
	{
		str += _T(", \\b ");
		str += TRL("URL:"); str += _T("\\b0  ");
		str += _MakeRtfString(p->pszURL);

		crURL.cpMin += static_cast<LONG>(_tcslen(TRL("URL:")) + 3);
	}

	str += _T(", \\b ");
	str += TRL("Creation Time"); str += _T(":\\b0  ");
	_PwTimeToStringEx(p->tCreation, str2, CPwSafeDlg::m_bUseLocalTimeFormat);
	str += _MakeRtfString(str2);

	str += _T(", \\b ");
	str += TRL("Last Modification"); str += _T(":\\b0  ");
	_PwTimeToStringEx(p->tLastMod, str2, CPwSafeDlg::m_bUseLocalTimeFormat);
	str += _MakeRtfString(str2);

	str += _T(", \\b ");
	str += TRL("Last Access"); str += _T(":\\b0  ");
	_PwTimeToStringEx(p->tLastAccess, str2, CPwSafeDlg::m_bUseLocalTimeFormat);
	str += _MakeRtfString(str2);

	if(memcmp(&p->tExpire, &g_tNeverExpire, sizeof(PW_TIME)) != 0)
	{
		str += _T(", \\b ");
		str += TRL("Expires"); str += _T(":\\b0  ");
		_PwTimeToStringEx(p->tExpire, str2, CPwSafeDlg::m_bUseLocalTimeFormat);
		str += _MakeRtfString(str2);
	}

	if((p->pszBinaryDesc != NULL) && (p->pszBinaryDesc[0] != 0))
	{
		str += _T(", \\b ");
		str += TRL("Attachment"); str += _T(":\\b0  ");
		str += _MakeRtfString((LPCTSTR)(p->pszBinaryDesc));
	}

	if((p->pszAdditional != NULL) && (p->pszAdditional[0] != 0))
	{
		str += _T("\\par \\par ");
		str2 = _MakeRtfString(p->pszAdditional);

		if(!m_strNotesFontRtfSpec.IsEmpty())
		{
			strTemp.Format(_T("%d "), m_nNotesFontSize * 2);
			str += _T("\\f4\\fs"); str += strTemp;

			if(m_fNotesFont.m_hObject != NULL)
			{
				LOGFONT lfNotes;
				if(m_fNotesFont.GetLogFont(&lfNotes) != FALSE)
				{
					if(lfNotes.lfWeight > FW_NORMAL)
					{
						str += _T("\\b ");
						str2.Replace(HTMLTAG_BOLD_S, _T(""));
						str2.Replace(HTMLTAG_BOLD_E, _T(""));
					}
					if(lfNotes.lfItalic != FALSE)
					{
						str += _T("\\i ");
						str2.Replace(HTMLTAG_ITALIC_S, _T(""));
						str2.Replace(HTMLTAG_ITALIC_E, _T(""));
					}
					if(lfNotes.lfUnderline != FALSE)
					{
						str += _T("\\ul ");
						str2.Replace(HTMLTAG_ULINE_S, _T(""));
						str2.Replace(HTMLTAG_ULINE_E, _T(""));
					}
					if(lfNotes.lfStrikeOut != FALSE)
					{
						str += _T("\\strike ");
						str2.Replace(HTMLTAG_STRIKE_S, _T(""));
						str2.Replace(HTMLTAG_STRIKE_E, _T(""));
					}
				}
			}
		}

		for(std::map<std_string, std_string>::const_iterator it =
			m_mHtmlToRtf.begin(); it != m_mHtmlToRtf.end(); ++it)
			str2.Replace(it->first.c_str(), it->second.c_str());

		str += str2;
	}

	str += _T("\\pard }");

	// === End entry view RTF assembly ===

#ifndef _UNICODE
	m_reEntryView.SetRTF(str, SF_RTF);
#else
	m_reEntryView.SetRTF(str, SF_RTF | SF_UNICODE);
#endif

	CHARFORMAT cfURL;
	ZeroMemory(&cfURL, sizeof(CHARFORMAT));
	cfURL.cbSize = sizeof(CHARFORMAT);
	cfURL.dwMask = (CFM_LINK | CFM_COLOR | CFM_UNDERLINE);
	cfURL.dwEffects = (CFE_LINK | CFE_UNDERLINE);
	cfURL.crTextColor = RGB(0, 0, 255);

	if((pg->pszGroupName != NULL) && (pg->pszGroupName[0] != 0))
	{
		CHARRANGE crGroup;
		ZeroMemory(&crGroup, sizeof(CHARRANGE));
		crGroup.cpMin = m_lGroupUrlStart;
		crGroup.cpMax = crGroup.cpMin + static_cast<LONG>(_tcslen(pg->pszGroupName));

		m_reEntryView.SetSel(crGroup);
		m_reEntryView.SetSelectionCharFormat(cfURL);
	}

	if((p->pszURL != NULL) && (p->pszURL[0] != 0)) // Make the URL clickable
	{
		crURL.cpMax = crURL.cpMin + static_cast<LONG>(_tcslen(p->pszURL));

		m_reEntryView.SetSel(crURL);
		m_reEntryView.SetSelectionCharFormat(cfURL);
	}

	m_reEntryView.SetSel(0, 0);
}

void CPwSafeDlg::OnMouseMove(UINT nFlags, CPoint point)
{
	NotifyUserActivity();

	if(m_hDraggingGroup != NULL)
	{
		CPoint pt = point; // Local copy of 'point', because we will modify it here
		ClientToScreen(&pt);

		CImageList::DragMove(pt);
		CImageList::DragShowNolock(FALSE);

		if(CWnd::WindowFromPoint(pt) != &m_cGroups) // || (m_bCanDragGroup == FALSE)
		{
			SetCursor(AfxGetApp()->LoadStandardCursor(IDC_NO));
		}
		else
		{
			if((GetKeyState(VK_CONTROL) & 0x8000) != 0)
				SetCursor(AfxGetApp()->LoadStandardCursor(IDC_UPARROW));
			else
				SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));

			TVHITTESTINFO tvhti;
			tvhti.pt = pt;
			m_cGroups.ScreenToClient(&tvhti.pt);
			HTREEITEM hItemSel = m_cGroups.HitTest(&tvhti);

			if(tvhti.flags & (TVHT_ONITEM | TVHT_ONITEMINDENT))
				m_cGroups.SelectDropTarget(tvhti.hItem);
			else
				m_cGroups.SelectDropTarget(NULL);

			if(hItemSel == m_hDraggingGroup) // Cannot drag on itself
				SetCursor(AfxGetApp()->LoadStandardCursor(IDC_NO));
		}

		CImageList::DragShowNolock(TRUE);
		return;
	}

	RECT rectWindow;
	GetWindowRect(&rectWindow);

	const LONG nAddTop = ((m_bShowToolBar == TRUE) ? 26 : 0);
	const int cyMenu = GetSystemMetrics(SM_CYMENU);

	if(m_bDragging == TRUE)
	{
		if(m_bDraggingHoriz == TRUE)
		{
			m_lSplitterPosHoriz = point.x - 3;
		}
		else
		{
			m_lSplitterPosVert = rectWindow.bottom - rectWindow.top - point.y - nAddTop - 3 - (cyMenu * 2);
		}

		ProcessResize();
	}

	RECT rectGroupList; RECT rectPwList; RECT rectEntryView;
	m_cGroups.GetWindowRect(&rectGroupList); ScreenToClient(&rectGroupList);
	m_cList.GetWindowRect(&rectPwList); ScreenToClient(&rectPwList);
	m_reEntryView.GetWindowRect(&rectEntryView); ScreenToClient(&rectEntryView);

	if((point.x >= (rectGroupList.right - 1)) && (point.x <= (rectPwList.left + 1)))
	{
		SetCursor(m_hDragLeftRight);
	}
	else if((point.y >= (rectGroupList.bottom - 1)) && (point.y <= (rectEntryView.top + 1)))
	{
		if(m_bEntryView == TRUE) SetCursor(m_hDragUpDown);
	}
	else SetCursor(m_hArrowCursor);

	CDialog::OnMouseMove(nFlags, point);
}

void CPwSafeDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
	NotifyUserActivity();
	CDialog::OnLButtonDown(nFlags, point);

	if(m_bDragging == FALSE)
	{
		RECT rectGroupList; RECT rectPwList; RECT rectEntryView;
		m_cGroups.GetWindowRect(&rectGroupList); ScreenToClient(&rectGroupList);
		m_cList.GetWindowRect(&rectPwList); ScreenToClient(&rectPwList);
		m_reEntryView.GetWindowRect(&rectEntryView); ScreenToClient(&rectEntryView);

		if((point.x >= (rectGroupList.right - 1)) && (point.x <= (rectPwList.left + 1)))
		{
			SetCursor(m_hDragLeftRight);
			m_bDraggingHoriz = TRUE;
			m_bDragging = TRUE;
		}
		else if((point.y >= (rectGroupList.bottom - 1)) && (point.y <= (rectEntryView.top + 1)))
		{
			if(m_bEntryView == TRUE)
			{
				SetCursor(m_hDragUpDown);
				m_bDraggingHoriz = FALSE;
				m_bDragging = TRUE;
			}
		}

		if(m_bDragging == TRUE) SetCapture();
	}
}

void CPwSafeDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
	NotifyUserActivity();

	if(m_hDraggingGroup != NULL)
	{
		// if(m_bCanDragGroup == TRUE) {

		CPoint pt = point;
		ClientToScreen(&pt);

		const bool bCopy = ((GetKeyState(VK_CONTROL) & 0x8000) != 0);

		HTREEITEM hItemDropTo = m_cGroups.GetDropHilightItem();

		if(hItemDropTo != m_hDraggingGroup)
		{
			const DWORD dwDragGroupId = (DWORD)m_cGroups.GetItemData(m_hDraggingGroup);
			const DWORD dwDragGroupPos = m_mgr.GetGroupByIdN(dwDragGroupId);
			DWORD dwNewGroupId = dwDragGroupId;

			ASSERT(dwDragGroupPos != DWORD_MAX);
			if((dwDragGroupPos != DWORD_MAX) && bCopy)
			{
				PW_GROUP grpNew = *m_mgr.GetGroup(dwDragGroupPos);
				grpNew.usLevel = 0;
				grpNew.uGroupId = 0; // Create new group
				m_mgr.AddGroup(&grpNew);
				PW_GROUP* pNew = m_mgr.GetGroup(m_mgr.GetNumberOfGroups() - 1);
				dwNewGroupId = pNew->uGroupId;
			}

			if(hItemDropTo != NULL) // Dropped on item
			{
				const DWORD dwDragToGroupId = (DWORD)m_cGroups.GetItemData(hItemDropTo);
				ASSERT(dwDragToGroupId != DWORD_MAX);
				const DWORD dwDragToGroupPos = m_mgr.GetGroupByIdN(dwDragToGroupId);
				ASSERT(dwDragToGroupPos != DWORD_MAX);

				// DWORD dwVParent = m_mgr.GetLastChildGroup(dwDragToGroupPos);

				// if(pNew != NULL)
				//	pNew->usLevel = (USHORT)(m_mgr.GetGroup(dwDragToGroupPos)->usLevel + 1);

				m_mgr.GetGroup(dwDragToGroupPos)->dwFlags |= PWGF_EXPANDED;
				
				// m_mgr.MoveGroup(m_mgr.GetNumberOfGroups() - 1, dwVParent + 1);
				m_mgr.MoveGroupEx(dwNewGroupId, dwDragToGroupId);
			}
			else // Dropped on empty space
			{
			}

			// Fix group ID, unassociate all entries from the group that we will delete
			// if(!bCopy)
			// {
			//	m_mgr.SubstEntryGroupIds(dwDragGroupId, dwNewGroupId);
			//	// If moving, delete source group
			//	m_mgr.DeleteGroupById(dwDragGroupId, FALSE);
			// }

			if(hItemDropTo != NULL) _SyncItem(&m_cGroups, hItemDropTo, FALSE);
			m_cGroups.SelectDropTarget(NULL);
			_Groups_SaveView(FALSE); UpdateGroupList(); _Groups_RestoreView();
			m_bModified = TRUE;
		}

		m_cGroups.SelectDropTarget(NULL);
		_FinishDragging(TRUE);
		RedrawWindow();
		_UpdateToolBar();
	}
	else if(m_bDragging == TRUE)
	{
		ReleaseCapture();
		m_bDragging = FALSE;
	}

	CDialog::OnLButtonUp(nFlags, point);
}

void CPwSafeDlg::OnViewEntryView()
{
	NotifyUserActivity();

	UINT uState;
	BOOL bChecked;

	uState = m_menu.GetMenuState(ID_VIEW_ENTRYVIEW, MF_BYCOMMAND);
	ASSERT(uState != 0xFFFFFFFF);

	if(uState & MF_CHECKED) bChecked = TRUE;
	else bChecked = FALSE;

	if(bChecked == TRUE)
	{
		uState = MF_UNCHECKED; // Toggle
		m_bEntryView = FALSE;
	}
	else
	{
		uState = MF_CHECKED; // Toggle
		m_bEntryView = TRUE;
	}

	m_menu.CheckMenuItem(ID_VIEW_ENTRYVIEW, MF_BYCOMMAND | uState);

	m_reEntryView.ShowWindow((m_bEntryView == TRUE) ? SW_SHOW : SW_HIDE);
	m_bCachedToolBarUpdate = TRUE;
	ProcessResize();
}

BOOL CPwSafeDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
	// MSGFILTER *lpMsgFilter = (MSGFILTER *)lParam;
	ENLINK *pEL = (ENLINK *)lParam;

	if(lParam != 0)
	{
		if((LOWORD(wParam) == IDC_RE_ENTRYVIEW) && (pEL->nmhdr.code == EN_LINK) &&
			(pEL->msg == WM_LBUTTONDOWN))
		{
			CHARRANGE cr;
			m_reEntryView.GetSel(cr); // Push current user selection
			m_reEntryView.SetSel(pEL->chrg);
			CString strSelectedURL = m_reEntryView.GetSelText();
			m_reEntryView.SetSel(cr); // Pop current user selection

			DWORD dwCmpEntryIndex = _ListSelToEntryIndex(DWORD_MAX);
			PW_ENTRY *pCmpEntry = m_mgr.GetEntry(dwCmpEntryIndex);

			if(pCmpEntry != NULL)
			{
				PW_GROUP *pGroup = m_mgr.GetGroupById(pCmpEntry->uGroupId);
				if((pEL->chrg.cpMin == m_lGroupUrlStart) && (pGroup != NULL) &&
					(_tcscmp(pGroup->pszGroupName, strSelectedURL) == 0))
				{
					HTREEITEM hAutoSelect = _GroupIdToHTreeItem(pGroup->uGroupId);
					if(hAutoSelect != NULL)
					{
						m_cGroups.EnsureVisible(hAutoSelect);
						m_cGroups.SelectItem(hAutoSelect);
						_List_SaveView();
						UpdatePasswordList();
						_List_RestoreView();
						const DWORD dwAutoPos = _EntryUuidToListPos(pCmpEntry->uuid);
						if(dwAutoPos != DWORD_MAX)
						{
							m_cList.EnsureVisible(static_cast<int>(dwAutoPos), FALSE);
							m_cList.FocusItem(static_cast<int>(dwAutoPos), TRUE);
						}
						ShowEntryDetails(pCmpEntry);
						_UpdateToolBar(TRUE);
						m_cList.SetFocus();
					}
				}
				else if(strSelectedURL == pCmpEntry->pszURL) OnPwlistVisitUrl();
				else ParseAndOpenURLWithEntryInfo(strSelectedURL, pCmpEntry);
			}
			else OpenUrlEx(strSelectedURL, this->m_hWnd);

			if(pResult != NULL) *pResult = 0;
			return TRUE;
		}
		else if((LOWORD(wParam) == IDC_RE_ENTRYVIEW) && (pEL->msg == WM_MOUSEMOVE))
		{
			NotifyUserActivity();
		}
		else if((wParam == 0) && (((NMHDR *)lParam)->code == NM_RCLICK))
		{
			POINT pt;
			ZeroMemory(&pt, sizeof(POINT));
			GetCursorPos(&pt);
			POINT ptList = pt;
			m_cList.ScreenToClient(&ptList);

			HDHITTESTINFO hitTest;
			hitTest.pt.x = ptList.x + m_cList.GetScrollPos(SB_HORZ);
			hitTest.pt.y = ptList.y;

			m_cList.GetHeaderCtrl()->SendMessage(HDM_HITTEST, 0, (LPARAM)&hitTest);

			if((hitTest.flags & (HHT_ONHEADER | HHT_ONDIVIDER | HHT_ONDIVOPEN | HHT_NOWHERE)) > 0)
			{
				_SetDisplayDialog(true);
				m_menuColView.LoadMenu(IDR_COLVIEW_MENU);

				m_menuColView.SetMenuDrawMode(BCMENU_DRAWMODE_XP); // <<<!=>>> BCMENU_DRAWMODE_ORIGINAL
				m_menuColView.SetSelectDisableMode(FALSE);
				m_menuColView.SetXPBitmap3D(TRUE);
				m_menuColView.SetBitmapBackground(RGB(255, 0, 255));
				m_menuColView.SetIconSize(16, 16);

				m_menuColView.LoadToolbar(IDR_INFOICONS, IDB_INFOICONS_EX);

				BCMenu *psub = NewGUI_GetBCMenu(m_menuColView.GetSubMenu(0));
				if(psub != NULL)
				{
					_TranslateMenu(psub, TRUE, NULL);
					_EnableViewMenuItems(psub);
					_SetDisplayMenu(true);
					psub->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, AfxGetMainWnd());
					_SetDisplayMenu(false);
				}
				else { ASSERT(FALSE); }
				m_menuColView.DestroyMenu();
				_SetDisplayDialog(false);

				if(pResult != NULL) *pResult = 1;
				return TRUE;
			}
		}
	}

	return CDialog::OnNotify(wParam, lParam, pResult);
}

void CPwSafeDlg::OnReCopySel()
{
	NotifyUserActivity();
	m_reEntryView.Copy();
}

void CPwSafeDlg::OnUpdateReCopySel(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_reEntryView.GetSelText().IsEmpty() ? FALSE : TRUE);
}

void CPwSafeDlg::OnReCopyAll()
{
	NotifyUserActivity();

	long lStart, lEnd;
	m_reEntryView.GetSel(lStart, lEnd);
	m_reEntryView.SetSel(0, -1);
	m_reEntryView.Copy();
	m_reEntryView.SetSel(lStart, lEnd);
}

void CPwSafeDlg::OnReSelectAll()
{
	NotifyUserActivity();
	m_reEntryView.SetSel(0, -1);
}

void CPwSafeDlg::OnExtrasTanWizard()
{
	NotifyUserActivity();

	const DWORD dwCurGroupId = GetSelectedGroupId();
	ASSERT(dwCurGroupId != DWORD_MAX); if(dwCurGroupId == DWORD_MAX) return;

	if(_CallPlugins(KPM_TANWIZARD_PRE, 0, 0) == FALSE) return;

	CTanWizardDlg dlg;

	const PW_GROUP *pgTo = m_mgr.GetGroupById(dwCurGroupId);
	if(pgTo != NULL) dlg.m_strGroupName = pgTo->pszGroupName;

	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		PW_ENTRY pwTemplate;
		memset(&pwTemplate, 0, sizeof(PW_ENTRY));

		PW_TIME tNow;
		_GetCurrentPwTime(&tNow);

		pwTemplate.tCreation = tNow;
		pwTemplate.tLastMod = tNow;
		pwTemplate.tLastAccess = tNow;
		pwTemplate.pszTitle = const_cast<LPTSTR>(PWS_TAN_ENTRY);
		pwTemplate.pszURL = _T("");
		pwTemplate.pszAdditional = _T("");
		m_mgr.GetNeverExpireTime(&pwTemplate.tExpire);
		pwTemplate.uImageId = 29;
		pwTemplate.uGroupId = dwCurGroupId;
		pwTemplate.pBinaryData = NULL;
		pwTemplate.pszBinaryDesc = _T("");
		pwTemplate.uBinaryDataLen = 0;

		const bool bNatural = NSCAPI_Supported();
		DWORD dwNumber = dlg.m_dwStartNumber;

		TCHAR tchTestSep = _T('!');
		while(dlg.m_strTans.Find(tchTestSep) >= 0) { ++tchTestSep; }
		dlg.m_strTans += tchTestSep; // Append terminating char

		CString strSubString;

		BOOL bValidSubString = FALSE;
		for(int i = 0; i < dlg.m_strTans.GetLength(); ++i)
		{
			const TCHAR tch = dlg.m_strTans.GetAt(i);

			BOOL bAcceptable = FALSE;
			if(dlg.m_strTANChars.Find(tch) >= 0) bAcceptable = TRUE;

			if((bAcceptable == TRUE) && (bValidSubString == FALSE))
			{
				strSubString = tch;
				bValidSubString = TRUE;
			}
			else if((bAcceptable == TRUE) && (bValidSubString == TRUE))
			{
				strSubString += tch;
			}
			else if((bAcceptable == FALSE) && (bValidSubString == TRUE))
			{
				ZeroMemory(&pwTemplate.uuid[0], 16);
				pwTemplate.pszPassword = const_cast<LPTSTR>((LPCTSTR)strSubString);
				pwTemplate.uPasswordLen = strSubString.GetLength();

				CString strNumberTemp;
				if(dlg.m_bAssignNumbers == FALSE)
					pwTemplate.pszUserName = _T("");
				else
				{
					strNumberTemp.Format(bNatural ? _T("%u") : _T("%03u"), dwNumber);
					pwTemplate.pszUserName = const_cast<LPTSTR>((LPCTSTR)strNumberTemp);
					++dwNumber;
				}

				VERIFY(m_mgr.AddEntry(&pwTemplate));

				bValidSubString = FALSE;
				EraseCString(&strSubString);
			}
		}

		EraseCString(&dlg.m_strTans);

		_SortListIfAutoSort();
		if(m_nAutoSort == 0) UpdatePasswordList();
		m_bModified = TRUE;
	}

	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdateExtrasTanWizard(CCmdUI* pCmdUI)
{
	BOOL b = m_bFileOpen;
	b &= ((m_mgr.GetNumberOfGroups() > 0) ? TRUE : FALSE);
	pCmdUI->Enable(b);
}

void CPwSafeDlg::OnFilePrintPreview()
{
	NotifyUserActivity();

	CPwExport cExp;
	PWEXPORT_OPTIONS pwo;

	if(m_bFileOpen == FALSE) return;
	if(m_bForceAllowPrinting == FALSE)
	{
		if(IsUnsafeAllowed(this->m_hWnd) == FALSE) return;
	}

	_SetDisplayDialog(true);

	cExp.SetManager(&m_mgr);
	cExp.SetNewLineSeq(m_bWindowsNewLine);
	cExp.SetFormat(PWEXP_HTML);

	if(_CallPlugins(KPM_FILE_PRINTPREVIEW_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }

	if(GetExportOptions(&pwo, &cExp, TRUE) == FALSE) { _SetDisplayDialog(false); return; }

	std::basic_string<TCHAR> tszFile = WU_GetTempFile(_T(".html"));

	const BOOL bRet = cExp.ExportGroup(tszFile.c_str(), DWORD_MAX, &pwo, NULL); // Export all: set group ID to DWORD_MAX
	if(bRet == FALSE)
	{
		MessageBox(TRL("Cannot open temporary file for printing!"), PWM_PRODUCT_NAME_SHORT,
			MB_OK | MB_ICONWARNING);
		_SetDisplayDialog(false);
		return;
	}

	ShellExecute(m_hWnd, NULL, tszFile.c_str(), NULL, NULL, SW_SHOW);

	m_vTempFiles.push_back(tszFile);
	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnUpdateFilePrintPreview(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::OnInfoTranslation()
{
	NotifyUserActivity();
	_SetDisplayDialog(true);

	CString str = TRL("Currently used language"); str += _T(":\r\n");
	if(_tcscmp(TRL("~LANGUAGENAME"), _T("~LANGUAGENAME")) != 0)
	{
		str += TRL("~LANGUAGENAME");
		str += _T(" (");
		LPCTSTR lpName = GetCurrentTranslationTable();
		str += ((lpName != NULL) ? lpName : _T("<?>"));
		str += _T(".lng)");
	}
	else str += _T("Unknown or English version");
	str += _T("\r\n\r\n");

	str += TRL("Version"); str += _T(":\r\n");
	if(_tcscmp(TRL("~LANGUAGEVERSION"), _T("~LANGUAGEVERSION")) != 0)
		str += TRL("~LANGUAGEVERSION");
	else str += _T("Unknown or English version");
	str += _T("\r\n\r\n");

	str += TRL("Author"); str += _T(":\r\n");
	if(_tcscmp(TRL("~LANGUAGEAUTHOR"), _T("~LANGUAGEAUTHOR")) != 0)
		str += TRL("~LANGUAGEAUTHOR");
	else str += _T("Unknown or English version");
	str += _T("\r\n\r\n");

	str += TRL("Contact"); str += _T(":\r\n");
	if(_tcscmp(TRL("~LANGUAGEAUTHOREMAIL"), _T("~LANGUAGEAUTHOREMAIL")) != 0)
		str += TRL("~LANGUAGEAUTHOREMAIL");
	else str += _T("Unknown or English version");

	// if(CVistaTaskDialog::ShowMessageBox(this->m_hWnd, TRL("Translation Information"),
	//	str, TD_INFORMATION_ICON, TRL("OK"), IDCANCEL, NULL, 0) < 0)
	MessageBox(str, TRL("Translation Information"), MB_OK | MB_ICONINFORMATION);

	_SetDisplayDialog(false);
}

HTREEITEM CPwSafeDlg::_GetLastGroupItem(CTreeCtrl *pTree)
{
	HTREEITEM hPrev = NULL, h;

	h = pTree->GetFirstVisibleItem();

	while(h != NULL)
	{
		hPrev = h;
		h = pTree->GetNextVisibleItem(h);
	}

	return hPrev;
}

void CPwSafeDlg::GroupSyncStates(BOOL bGuiToMgr)
{
	if(m_bFileOpen == FALSE) return;
	if(m_cGroups.GetCount() == 0) return;

	_SyncSubTree(&m_cGroups, m_cGroups.GetRootItem(), bGuiToMgr);
}

void CPwSafeDlg::_SyncSubTree(CTreeCtrl *pTree, HTREEITEM hItem, BOOL bGuiToMgr)
{
	HTREEITEM h;

	ASSERT(pTree != NULL);
	if(hItem == NULL) return;

	h = hItem;
	while(1)
	{
		if(pTree->ItemHasChildren(h) == TRUE)
		{
			_SyncItem(pTree, h, bGuiToMgr);
			_SyncSubTree(pTree, pTree->GetChildItem(h), bGuiToMgr);
		}

		h = pTree->GetNextSiblingItem(h);
		if(h == NULL) break;
	}
}

void CPwSafeDlg::_SyncItem(CTreeCtrl *pTree, HTREEITEM hItem, BOOL bGuiToMgr)
{
	ASSERT((pTree != NULL) && (hItem != NULL));
	if((pTree == NULL) || (hItem == NULL)) return;

	DWORD dwGroupId = (DWORD)pTree->GetItemData(hItem);
	PW_GROUP *pGroup = m_mgr.GetGroupById(dwGroupId);

	if(pGroup != NULL)
	{
		if(bGuiToMgr == TRUE)
		{
			if(pTree->GetItemState(hItem, UINT_MAX) & TVIS_EXPANDED)
				pGroup->dwFlags |= PWGF_EXPANDED; // Set bit
			else
				pGroup->dwFlags &= ~PWGF_EXPANDED; // Remove bit
		}
		else
		{
			if((pGroup->dwFlags & PWGF_EXPANDED) != 0)
				pTree->Expand(hItem, TVE_EXPAND);
			else
				pTree->Expand(hItem, TVE_COLLAPSE);
		}
	}
}

HTREEITEM CPwSafeDlg::_GroupIdToHTreeItem(DWORD_PTR dwGroupId)
{
	if(dwGroupId == DWORD_MAX) return NULL;

	return _FindSelectInTree(&m_cGroups, m_cGroups.GetRootItem(), dwGroupId);
}

HTREEITEM CPwSafeDlg::_FindSelectInTree(CTreeCtrl *pTree, HTREEITEM hRoot, DWORD_PTR dwGroupId)
{
	HTREEITEM h;

	ASSERT(pTree != NULL);
	if(hRoot == NULL) return NULL;

	h = hRoot;
	while(1)
	{
		if(pTree->GetItemData(h) == dwGroupId) return h;

		if(pTree->ItemHasChildren(h) == TRUE)
		{
			HTREEITEM hSub = _FindSelectInTree(pTree, pTree->GetChildItem(h), dwGroupId);
			if(hSub != NULL) return hSub;
		}

		h = pTree->GetNextSiblingItem(h);
		if(h == NULL) break;
	}

	return NULL;
}

void CPwSafeDlg::OnSafeAddSubgroup()
{
	NotifyUserActivity();

	_SetDisplayDialog(true);
	if(_CallPlugins(KPM_GROUP_ADD_SUBGROUP_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }
	_SetDisplayDialog(false);

	DWORD dwGroupCount = m_mgr.GetNumberOfGroups();
	ASSERT(dwGroupCount >= 1); if(dwGroupCount == 0) return;

	HTREEITEM hItem = m_cGroups.GetSelectedItem();
	ASSERT(hItem != NULL); if(hItem == NULL) return;

	DWORD dwRealParentId = (DWORD)m_cGroups.GetItemData(hItem);
	ASSERT(dwRealParentId != DWORD_MAX); if(dwRealParentId == DWORD_MAX) return;

	_Groups_SaveView();
	OnSafeAddGroup();

	// Have we added a group?
	if(m_mgr.GetNumberOfGroups() == (dwGroupCount + 1))
	{
		PW_GROUP *pRealParent = m_mgr.GetGroupById(dwRealParentId);
		ASSERT(pRealParent != NULL); if(pRealParent == NULL) return;

		DWORD dwVParentPos = m_mgr.GetLastChildGroup(m_mgr.GetGroupByIdN(dwRealParentId));
		ASSERT(dwVParentPos != DWORD_MAX);
		if(dwVParentPos == DWORD_MAX) dwVParentPos = m_mgr.GetGroupByIdN(dwRealParentId);

		PW_GROUP *pVParent = m_mgr.GetGroup(dwVParentPos);
		ASSERT(pVParent != NULL); if(pVParent == NULL) return;

		DWORD dwVParentGroupId = pVParent->uGroupId;
		ASSERT(dwVParentGroupId != DWORD_MAX); if(dwVParentGroupId == DWORD_MAX) return;

		ASSERT(dwVParentPos <= m_mgr.GetNumberOfGroups());
		ASSERT(m_mgr.GetGroupById(dwVParentGroupId) != NULL);

		// Get the position of the added group
		DWORD dwNewGroupPos = m_mgr.GetNumberOfGroups() - 1;

		DWORD dwNewGroupId = m_mgr.GetGroup(dwNewGroupPos)->uGroupId;

		// Set new level of the added group and expand its parent
		m_mgr.GetGroup(dwNewGroupPos)->usLevel = (USHORT)(pRealParent->usLevel + 1);
		pRealParent->dwFlags |= PWGF_EXPANDED;
		GroupSyncStates(FALSE); // 'Send' expanded flag to GUI

		// Move it to the correct position
		m_mgr.MoveGroup(dwNewGroupPos, dwVParentPos + 1);

		UpdateGroupList();
		_Groups_RestoreView();

		HTREEITEM h = _GroupIdToHTreeItem(dwNewGroupId);
		if(h != NULL) m_cGroups.SelectItem(h);
		UpdatePasswordList();
	}
}

void CPwSafeDlg::OnUpdateSafeAddSubgroup(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnBeginDragGrouplist(NMHDR* pNMHDR, LRESULT* pResult)
{
	NotifyUserActivity();

	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW *)pNMHDR;
	*pResult = 0;

	_SetDisplayDialog(true);

	CImageList* pDragImageList = NULL;
	POINT ptOffset;
	RECT rcItem;

	pDragImageList = m_cGroups.CreateDragImage(pNMTreeView->itemNew.hItem);
	ASSERT(pDragImageList != NULL); if(pDragImageList == NULL) { _SetDisplayDialog(false); return; }

	if(m_cGroups.GetItemRect(pNMTreeView->itemNew.hItem, &rcItem, TRUE) != FALSE)
	{
		CPoint ptDragBegin;
		int nX = 0, nY = 0;

		ptDragBegin = pNMTreeView->ptDrag;
		ImageList_GetIconSize(pDragImageList->GetSafeHandle(), &nX, &nY);
		ptOffset.x = (ptDragBegin.x - rcItem.left) + (nX - (rcItem.right - rcItem.left));
		ptOffset.y = (ptDragBegin.y - rcItem.top) + (nY - (rcItem.bottom - rcItem.top));

		MapWindowPoints(NULL, &rcItem);
	}
	else
	{
		GetWindowRect(&rcItem);
		ptOffset.x = ptOffset.y = 8;
	}

	BOOL bDragBegun = pDragImageList->BeginDrag(0, ptOffset);
	if(bDragBegun == FALSE)
	{
		SAFE_DELETE(pDragImageList); ASSERT(FALSE); _SetDisplayDialog(false); return;
	}

	CPoint ptDragEnter = pNMTreeView->ptDrag;
	ClientToScreen(&ptDragEnter);
	if(pDragImageList->DragEnter(NULL, ptDragEnter) == FALSE)
	{
		SAFE_DELETE(pDragImageList); ASSERT(FALSE); _SetDisplayDialog(false); return;
	}

	SAFE_DELETE(pDragImageList);

	this->SetFocus();
	InvalidateRect(&rcItem, TRUE);
	UpdateWindow();
	SetCapture();

	m_hDraggingGroup = pNMTreeView->itemNew.hItem;

	// if(m_cGroups.ItemHasChildren(m_hDraggingGroup) == TRUE) m_bCanDragGroup = FALSE;
	// else m_bCanDragGroup = TRUE;

	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnCancelMode()
{
	NotifyUserActivity();

	_FinishDragging(TRUE);
	RedrawWindow();
	CDialog::OnCancelMode();
}

void CPwSafeDlg::_FinishDragging(BOOL bDraggingImageList)
{
	if(m_hDraggingGroup != NULL)
	{
		if(bDraggingImageList == TRUE)
		{
			CImageList::DragLeave(NULL);
			CImageList::EndDrag();
		}

		m_hDraggingGroup = NULL;
		ReleaseCapture();
		ShowCursor(TRUE);
		m_cGroups.SelectDropTarget(NULL);
	}
}

void CPwSafeDlg::OnGroupSort()
{
	NotifyUserActivity();

	if((m_bFileOpen == FALSE) || (m_bLocked == TRUE)) return;

	if(_CallPlugins(KPM_GROUP_SORT_PRE, 0, 0) == FALSE) return;

	m_mgr.SortGroupList();
	UpdateGroupList();
	UpdatePasswordList();
	m_bModified = TRUE;
}

void CPwSafeDlg::OnUpdateGroupSort(CCmdUI* pCmdUI)
{
	BOOL b = m_bFileOpen;
	b &= ((m_mgr.GetNumberOfGroups() != 0) ? TRUE : FALSE);
	pCmdUI->Enable(b);
}

BOOL CPwSafeDlg::_CheckIfCanSort()
{
	if(m_nAutoSort == 0) return TRUE;

	MessageBox(TRL("Auto-sorting of the entry list is enabled; you cannot sort the list manually now."),
		PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
	return FALSE;
}

void CPwSafeDlg::_UpdateSortMenuItemState(CCmdUI* pCmdUI)
{
	if(pCmdUI == NULL) { ASSERT(FALSE); return; }

	bool bEnable = (m_bFileOpen != FALSE);
	bEnable &= !_IsSearchGroup();

	pCmdUI->Enable(bEnable ? TRUE : FALSE);
}

void CPwSafeDlg::OnPwlistSortTitle()
{
	NotifyUserActivity();
	if(_CheckIfCanSort() == FALSE) return;
	_SortList(0, FALSE);
}

void CPwSafeDlg::OnUpdatePwlistSortTitle(CCmdUI* pCmdUI)
{
	_UpdateSortMenuItemState(pCmdUI);
}

void CPwSafeDlg::OnPwlistSortUser()
{
	NotifyUserActivity();
	if(_CheckIfCanSort() == FALSE) return;
	_SortList(1, FALSE);
}

void CPwSafeDlg::OnUpdatePwlistSortUser(CCmdUI* pCmdUI)
{
	_UpdateSortMenuItemState(pCmdUI);
}

void CPwSafeDlg::OnPwlistSortUrl()
{
	NotifyUserActivity();
	if(_CheckIfCanSort() == FALSE) return;
	_SortList(2, FALSE);
}

void CPwSafeDlg::OnUpdatePwlistSortUrl(CCmdUI* pCmdUI)
{
	_UpdateSortMenuItemState(pCmdUI);
}

void CPwSafeDlg::OnPwlistSortPassword()
{
	NotifyUserActivity();
	if(_CheckIfCanSort() == FALSE) return;
	_SortList(3, FALSE);
}

void CPwSafeDlg::OnUpdatePwlistSortPassword(CCmdUI* pCmdUI)
{
	_UpdateSortMenuItemState(pCmdUI);
}

void CPwSafeDlg::OnPwlistSortNotes()
{
	NotifyUserActivity();
	if(_CheckIfCanSort() == FALSE) return;
	_SortList(4, FALSE);
}

void CPwSafeDlg::OnUpdatePwlistSortNotes(CCmdUI* pCmdUI)
{
	_UpdateSortMenuItemState(pCmdUI);
}

void CPwSafeDlg::OnPwlistSortCreation()
{
	NotifyUserActivity();
	if(_CheckIfCanSort() == FALSE) return;
	_SortList(5, FALSE);
}

void CPwSafeDlg::OnUpdatePwlistSortCreation(CCmdUI* pCmdUI)
{
	_UpdateSortMenuItemState(pCmdUI);
}

void CPwSafeDlg::OnPwlistSortLastmodify()
{
	NotifyUserActivity();
	if(_CheckIfCanSort() == FALSE) return;
	_SortList(6, FALSE);
}

void CPwSafeDlg::OnUpdatePwlistSortLastmodify(CCmdUI* pCmdUI)
{
	_UpdateSortMenuItemState(pCmdUI);
}

void CPwSafeDlg::OnPwlistSortLastaccess()
{
	NotifyUserActivity();
	if(_CheckIfCanSort() == FALSE) return;
	_SortList(7, FALSE);
}

void CPwSafeDlg::OnUpdatePwlistSortLastaccess(CCmdUI* pCmdUI)
{
	_UpdateSortMenuItemState(pCmdUI);
}

void CPwSafeDlg::OnPwlistSortExpire()
{
	NotifyUserActivity();
	if(_CheckIfCanSort() == FALSE) return;
	_SortList(8, FALSE);
}

void CPwSafeDlg::OnUpdatePwlistSortExpire(CCmdUI* pCmdUI)
{
	_UpdateSortMenuItemState(pCmdUI);
}

void CPwSafeDlg::OnGroupMoveLeft()
{
	NotifyUserActivity();

	DWORD dwGroupId = GetSelectedGroupId();
	PW_GROUP *pGroup = m_mgr.GetGroupById(dwGroupId);
	ASSERT(pGroup != NULL); if(pGroup == NULL) return;

	if(pGroup->usLevel != 0) pGroup->usLevel--;
	m_mgr.FixGroupTree();
	_Groups_SaveView(); UpdateGroupList(); _Groups_RestoreView();
	m_bModified = TRUE;
}

void CPwSafeDlg::OnUpdateGroupMoveLeft(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnGroupMoveRight()
{
	NotifyUserActivity();

	DWORD dwGroupId = GetSelectedGroupId();
	DWORD dwGroupPos = m_mgr.GetGroupByIdN(dwGroupId);
	PW_GROUP *pGroup = m_mgr.GetGroup(dwGroupPos);
	PW_GROUP *pParent;
	if(dwGroupPos != 0) pParent = m_mgr.GetGroup(dwGroupPos - 1); else pParent = NULL;
	ASSERT(pGroup != NULL); if(pGroup == NULL) return;

	if(pGroup->usLevel != 0xFFFF) pGroup->usLevel++;
	if(pParent != NULL) pParent->dwFlags |= PWGF_EXPANDED;
	m_mgr.FixGroupTree();
	_Groups_SaveView(); UpdateGroupList(); _Groups_RestoreView();
	m_bModified = TRUE;
}

void CPwSafeDlg::OnUpdateGroupMoveRight(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnViewHideUsers()
{
	NotifyUserActivity();

	UINT uState;
	BOOL bChecked;
	int nItem;
	LV_ITEM lvi;

	if(m_bUserStars == TRUE)
	{
		if(IsUnsafeAllowed(this->m_hWnd) == FALSE) return;
	}

	uState = m_menu.GetMenuState(ID_VIEW_HIDEUSERS, MF_BYCOMMAND);
	ASSERT(uState != 0xFFFFFFFF);

	if(uState & MF_CHECKED) bChecked = TRUE;
	else bChecked = FALSE;

	if(bChecked == TRUE)
	{
		uState = MF_UNCHECKED; // Toggle
		m_bUserStars = FALSE;
	}
	else
	{
		uState = MF_CHECKED; // Toggle
		m_bUserStars = TRUE;
	}

	m_menu.CheckMenuItem(ID_VIEW_HIDEUSERS, MF_BYCOMMAND | uState);

	if(m_bUserStars == TRUE)
	{
		ZeroMemory(&lvi, sizeof(LV_ITEM));
		lvi.mask = LVIF_TEXT;
		lvi.iSubItem = 1;
		lvi.pszText = PWM_PASSWORD_STRING;
		for(nItem = 0; nItem < m_cList.GetItemCount(); ++nItem)
		{
			lvi.iItem = nItem;
			m_cList.SetItem(&lvi);
		}
	}
	else
		RefreshPasswordList(); // Refresh list based on UUIDs

	// m_bCachedToolBarUpdate = TRUE;
	_UpdateToolBar(TRUE);
}

void CPwSafeDlg::OnViewAttach()
{
	NotifyUserActivity();
	_SelChangeView(ID_VIEW_ATTACH);
}

void CPwSafeDlg::OnPwlistSaveAttach()
{
	NotifyUserActivity();

	DWORD dwFlags;
	PW_ENTRY *pEntry;
	DWORD dwSelectedInx = _ListSelToEntryIndex();

	if(m_bFileOpen == FALSE) return;
	if(dwSelectedInx == DWORD_MAX) return;

	pEntry = m_mgr.GetEntry(dwSelectedInx);
	ASSERT(pEntry != NULL); if(pEntry == NULL) return;

	_SetDisplayDialog(true);

	if(_tcslen(pEntry->pszBinaryDesc) == 0)
	{
		MessageBox(TRL("There is no file attached with this entry."),
			PWM_PRODUCT_NAME_SHORT, MB_ICONINFORMATION | MB_OK);
		_SetDisplayDialog(false);
		return;
	}

	CString strSample = pEntry->pszBinaryDesc;

	CString strFilter = TRL("All Files");
	strFilter += _T(" (*.*)|*.*||");

	dwFlags = (OFN_LONGNAMES | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT);
	dwFlags |= OFN_EXTENSIONDIFFERENT;
	dwFlags |= (OFN_EXPLORER | OFN_ENABLESIZING | OFN_NOREADONLYRETURN);
	
	CFileDialog dlg(FALSE, NULL, strSample, dwFlags, strFilter, this);
	if(NewGUI_DoModal(&dlg) == IDOK)
		CPwUtil::SaveBinaryData(pEntry, dlg.GetPathName());

	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnUpdatePwlistSaveAttach(CCmdUI* pCmdUI)
{
	BOOL bEnable = TRUE;

	if(m_dwLastFirstSelectedItem == DWORD_MAX) bEnable = FALSE;
	if(m_dwLastNumSelectedItems != 1) bEnable = FALSE;

	if((m_dwLastEntryIndex != DWORD_MAX) && (m_dwLastEntryIndex < m_mgr.GetNumberOfEntries()))
	{
		const PW_ENTRY *p = m_mgr.GetEntry(m_dwLastEntryIndex);
		if(p != NULL) bEnable &= ((p->pBinaryData != NULL) ? TRUE : FALSE);
	}

	pCmdUI->Enable(bEnable);
}

void CPwSafeDlg::_ProcessGroupKey(UINT nChar, UINT nFlags)
{
	NotifyUserActivity();

	if((nFlags & 0x2000) > 0)
	{
		if(nChar == VK_UP) OnGroupMoveUp();
		else if(nChar == VK_DOWN) OnGroupMoveDown();
		else if(nChar == VK_HOME) OnGroupMoveTop();
		else if(nChar == VK_END) OnGroupMoveBottom();
		else if(nChar == VK_LEFT) OnGroupMoveLeft();
		else if(nChar == VK_RIGHT) OnGroupMoveRight();
	}
	else
	{
		if((nChar == VK_UP) || (nChar == VK_DOWN) || (nChar == VK_HOME) ||
			(nChar == VK_END) || (nChar == VK_PRIOR) || (nChar == VK_NEXT) ||
			(nChar == VK_LEFT) || (nChar == VK_RIGHT))
			m_bCachedPwlistUpdate = TRUE;
		else
			m_bCachedPwlistUpdate = FALSE;
	}
}

void CPwSafeDlg::_ProcessListKey(UINT nChar, BOOL bAlt)
{
	NotifyUserActivity();

	if(bAlt == TRUE)
	{
		if(nChar == VK_UP) OnPwlistMoveUp();
		else if(nChar == VK_DOWN) OnPwlistMoveDown();
		else if(nChar == VK_HOME) OnPwlistMoveTop();
		else if(nChar == VK_END) OnPwlistMoveBottom();
	}
	else
	{
		if(nChar == VK_DELETE) OnPwlistDelete();
	}
}

void CPwSafeDlg::CB_OnPwlistColumnWidthChange(int iColumn, int iSize)
{
	ASSERT((iColumn >= 0) && (iColumn < 11));

	if(m_dwPwListMode != LVS_REPORT) return;

	if((iColumn >= 0) && (iColumn < 11))
	{
		if(m_bShowColumn[iColumn] == TRUE)
			m_nColumnWidths[iColumn]= iSize;
	}
}

BOOL CPwSafeDlg::IsUnsafeAllowed(HWND hWndParent)
{
	if(m_bDisableUnsafeAtStart == FALSE) return TRUE;

	CString str = TRL("Unsafe operations are disabled.");
	str += _T("\r\n\r\n");
	str += TRL("To execute this operation you must enable unsafe operations in the options dialog.");
	::MessageBox(hWndParent, (LPCTSTR)str, PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);

	return FALSE;
}

void CPwSafeDlg::OnFileShowDbInfo()
{
	NotifyUserActivity();

	CDbSettingsDlg dlg;

	dlg.m_nAlgorithm = m_mgr.GetAlgorithm();
	const int nAlgorithmOld = dlg.m_nAlgorithm;

	dlg.m_dwNumKeyEnc = m_mgr.GetKeyEncRounds();
	const DWORD dwOldKeyEncRounds = dlg.m_dwNumKeyEnc;

	CString strName = m_mgr.GetPropertyString(PWP_DEFAULT_USER_NAME).c_str();
	dlg.m_strDefaultUserName = strName; // Copy

	const COLORREF clrOld = m_mgr.GetColor();
	dlg.m_clr = clrOld;

	if(_CallPlugins(KPM_FILE_DBSETTINGS_PRE, 0, 0) == FALSE) return;

	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		m_mgr.SetAlgorithm(dlg.m_nAlgorithm);
		if(nAlgorithmOld != dlg.m_nAlgorithm) m_bModified = TRUE;

		m_mgr.SetKeyEncRounds(dlg.m_dwNumKeyEnc);
		if(dwOldKeyEncRounds != dlg.m_dwNumKeyEnc) m_bModified = TRUE;

		if(_tcscmp((LPCTSTR)dlg.m_strDefaultUserName, (LPCTSTR)strName) != 0)
		{
			VERIFY(m_mgr.SetPropertyString(PWP_DEFAULT_USER_NAME,
				(LPCTSTR)dlg.m_strDefaultUserName));
			m_bModified = TRUE;
		}

		m_mgr.SetColor(dlg.m_clr);
		if(clrOld != dlg.m_clr)
		{
			m_bModified = TRUE;
			_UpdateTrayIcon();
		}
	}

	_UpdateToolBar();
}

void CPwSafeDlg::OnUpdateFileShowDbInfo(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::_ShowExpiredEntries(BOOL bShowIfNone, BOOL bShowExpired, BOOL bShowSoonToExpire)
{
	NotifyUserActivity();

	if(m_bFileOpen == FALSE) return;
	if(m_bGlobalAutoTypePending == TRUE) return;

	_SetDisplayDialog(true);

	PW_TIME tNow;
	_GetCurrentPwTime(&tNow);
	const DWORD dwDateNow = ((DWORD)tNow.shYear * 13 * 32) + ((DWORD)tNow.btMonth * 32) +
		((DWORD)tNow.btDay & 0xFF);
	const DWORD dwSoonToExpireDays = _GetSoonToExpireDays();

	CEntryListDlg dlg;
	dlg.m_pMgr = &m_mgr;
	dlg.m_bPasswordStars = m_bPasswordStars;
	dlg.m_bUserStars = m_bUserStars;
	dlg.m_pImgList = &m_ilIcons;
	dlg.m_strBannerTitle = TRL("Expired Entries");
	CString strExpText = TRL("This is a list of all expired entries");
	dlg.m_strBannerCaption = strExpText + _T(".");
	ZeroMemory(dlg.m_aUuid, 16);

	if((bShowExpired == TRUE) && (bShowSoonToExpire == TRUE))
		dlg.m_nDisplayMode = ELDMODE_EXPSOONEXP;
	else if(bShowExpired == TRUE)
		dlg.m_nDisplayMode = ELDMODE_EXPIRED;
	else if(bShowSoonToExpire == TRUE)
		dlg.m_nDisplayMode = ELDMODE_SOONTOEXP;
	else { ASSERT(FALSE); _SetDisplayDialog(false); return; }

	if(bShowIfNone == FALSE)
	{
		BOOL bAtLeastOneExpired = FALSE;
		const DWORD dwInvalidGroup1 = m_mgr.GetGroupId(PWS_BACKUPGROUP_SRC);
		const DWORD dwInvalidGroup2 = m_mgr.GetGroupId(PWS_BACKUPGROUP);

		for(DWORD i = 0; i < m_mgr.GetNumberOfEntries(); ++i)
		{
			PW_ENTRY *pe = m_mgr.GetEntry(i);
			ASSERT(pe != NULL); if(pe == NULL) continue;

			if((pe->uGroupId == dwInvalidGroup1) || (pe->uGroupId == dwInvalidGroup2)) continue;
			if(CPwUtil::IsTANEntry(pe) == TRUE) continue;

			if(bShowExpired == TRUE)
			{
				if(_pwtimecmp(&tNow, &pe->tExpire) > 0)
				{
					bAtLeastOneExpired = TRUE;
					break;
				}
			}

			if(bShowSoonToExpire == TRUE)
			{
				const DWORD dwDate = ((DWORD)pe->tExpire.shYear * 13 * 32) +
					((DWORD)pe->tExpire.btMonth * 32) + ((DWORD)pe->tExpire.btDay & 0xFF);

				if((dwDate >= dwDateNow) && ((dwDate - dwDateNow) <= dwSoonToExpireDays))
				{
					bAtLeastOneExpired = TRUE;
					break;
				}
			}
		}

		if(bAtLeastOneExpired == FALSE) { _SetDisplayDialog(false); return; }
	}

	if(NewGUI_DoModal(&dlg) == IDOK)
	{
		if(memcmp(dlg.m_aUuid, g_uuidZero, 16) != 0)
		{
			PW_ENTRY *p = m_mgr.GetEntryByUuid(dlg.m_aUuid);

			ASSERT(p != NULL);
			if(p != NULL)
			{
				UpdateGroupList();
				HTREEITEM h = _GroupIdToHTreeItem(p->uGroupId);

				m_cGroups.EnsureVisible(h);
				m_cGroups.SelectItem(h);

				UpdatePasswordList();

				DWORD dwPos = m_mgr.GetEntryPosInGroup(p);
				ASSERT(dwPos != DWORD_MAX);

				m_cList.EnsureVisible(static_cast<int>(dwPos), FALSE);
				m_cList.FocusItem(static_cast<int>(dwPos), TRUE);

				m_cList.SetFocus();

				// Overwrite locked view parameters, so the unlocking
				// method doesn't destroy the current view
				m_nLockedViewParams[1] = GetSelectedEntry();
				m_nLockedViewParams[2] = m_cList.GetTopIndex();
			}
		}
	}

	NotifyUserActivity();
	_UpdateToolBar();

	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnExtrasShowExpired()
{
	_ShowExpiredEntries(TRUE, TRUE, FALSE);
}

void CPwSafeDlg::OnUpdateExtrasShowExpired(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

void CPwSafeDlg::OnImportPvault()
{
	NotifyUserActivity();
	_SetDisplayDialog(true);

	CString strFilter = TRL("Text Files");
	strFilter += _T(" (*.txt)|*.txt|");
	strFilter += TRL("All Files");
	strFilter += _T(" (*.*)|*.*||");

	std::vector<std::basic_string<TCHAR> > vFiles = WU_GetFileNames(TRUE, _T("txt"),
		strFilter, TRUE, this, FALSE, TRUE);

	for(size_t iFile = 0; iFile < vFiles.size(); ++iFile)
	{
		CPwImport pvi;
		if(pvi.ImportPVaultToDb(vFiles[iFile].c_str(), &m_mgr) == TRUE)
		{
			_Groups_SaveView(); UpdateGroupList(); _Groups_RestoreView();
			_SortListIfAutoSort();
			if(m_nAutoSort == 0) UpdatePasswordList();
			m_bModified = TRUE;
		}
		else
		{
			CString strMsg = vFiles[iFile].c_str();
			strMsg += _T("\r\n\r\n");
			strMsg += TRL("An error occurred while importing the file. File cannot be imported.");
			MessageBox(strMsg, PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
		}
	}

	_UpdateToolBar();
	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnUpdateImportPvault(CCmdUI* pCmdUI)
{
	BOOL b = m_bFileOpen;
	b &= ((m_mgr.GetNumberOfGroups() != 0) ? TRUE : FALSE);
	pCmdUI->Enable(b);
}

void CPwSafeDlg::_HandleEntryDrop(DWORD dwDropType, HTREEITEM hTreeItem)
{
	const DWORD dwToGroupId = (DWORD)m_cGroups.GetItemData(hTreeItem);
	ASSERT(m_mgr.GetGroupById(dwToGroupId) != NULL); if(m_mgr.GetGroupById(dwToGroupId) == NULL) return;

	if((dwDropType != DROPEFFECT_MOVE) && (dwDropType != DROPEFFECT_COPY)) { ASSERT(FALSE); return; }

	CString strGroupTest;
	const PW_GROUP *pGroupTest = m_mgr.GetGroupById(dwToGroupId);
	if(CPwUtil::IsAllowedStoreGroup(pGroupTest->pszGroupName, PWS_SEARCHGROUP) == FALSE)
	{
		MessageBox(TRL("The group you selected cannot store entries. Please select a different group."),
			PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
		return;
	}

	PW_TIME tNow;
	_GetCurrentPwTime(&tNow);

	for(DWORD i = 0; i < static_cast<DWORD>(m_cList.GetItemCount()); ++i)
	{
		const UINT uState = m_cList.GetItemState(static_cast<int>(i), LVIS_SELECTED);

		if((uState & LVIS_SELECTED) != 0)
		{
			const DWORD dwIndex = _ListSelToEntryIndex(i); // Uses UUID to get the entry
			ASSERT(dwIndex != DWORD_MAX); if(dwIndex == DWORD_MAX) continue;

			PW_ENTRY *p = m_mgr.GetEntry(dwIndex);
			ASSERT(p != NULL); if(p == NULL) continue;

			if(dwDropType == DROPEFFECT_MOVE)
			{
				p->tLastAccess = tNow;
				// p->tLastMod = tNow;
				p->uGroupId = dwToGroupId;
			}
			else if(dwDropType == DROPEFFECT_COPY)
			{
				m_mgr.UnlockEntryPassword(p);
				CString strPasswordCopy = p->pszPassword;
				m_mgr.LockEntryPassword(p);

				PW_ENTRY pwT;
				memcpy(&pwT, p, sizeof(PW_ENTRY)); // Copy entry
				ZeroMemory(pwT.uuid, 16 * sizeof(BYTE)); // Create new UUID
				pwT.uGroupId = dwToGroupId; // Set group ID
				pwT.pszPassword = const_cast<LPTSTR>((LPCTSTR)strPasswordCopy);
				pwT.tCreation = tNow;
				pwT.tLastAccess = tNow;
				// pwT.tLastMod = tNow;
				m_mgr.AddEntry(&pwT); // Add as new entry

				EraseCString(&strPasswordCopy);
			}
			else { ASSERT(FALSE); }
		}
	}

	m_cGroups.m_drop.SetDragAccept(FALSE);

	// Full GUI update
	_Groups_SaveView(TRUE);
	_List_SaveView();
	UpdateGroupList();
	_Groups_RestoreView();
	UpdatePasswordList();
	_List_RestoreView();

	m_bModified = TRUE;
}

void CPwSafeDlg::SetStatusTextEx(LPCTSTR lpStatusText, int nPane)
{
	if(lpStatusText == NULL) { ASSERT(FALSE); lpStatusText = _T(""); }

	if(nPane == -1) nPane = 2;
	ASSERT(nPane < 3); if(nPane >= 3) nPane = 2;

	if(::IsWindow(m_sbStatus.m_hWnd) != FALSE)
		m_sbStatus.SetText(lpStatusText, nPane, 0);
}

void CPwSafeDlg::_DoQuickFind(LPCTSTR lpText)
{
	DWORD dwFlags = (PWMF_TITLE | PWMF_USER | PWMF_URL | PWMF_ADDITIONAL |
		PWMF_UUID | PWMF_GROUPNAME);
	if(m_bQuickFindInPasswords != FALSE) dwFlags |= PWMF_PASSWORD;

	LPCTSTR lpSearch = lpText;
	if(lpSearch == NULL)
	{
		UpdateData(TRUE);
		lpSearch = m_strQuickFind;
	}

	if(_tcscmp(lpSearch, HCMBX_CLEARLIST) == 0)
	{
		m_mgr.AccessPropertyStrArray(PWPA_SEARCH_HISTORY)->clear();
		m_cQuickFind.ResetContent();
		_UpdateToolBar(TRUE);
		return;
	}
	else if(_tcscmp(lpSearch, HCMBX_SEPARATOR) == 0)
	{
		_UpdateToolBar(TRUE);
		return;
	}

	CString strSearchOrg = lpSearch;
	CString strRegex;
	const size_t uLen = _tcslen(lpSearch);
	if(uLen > 4)
	{
		if((lpSearch[0] == _T('/')) && (lpSearch[1] == _T('/')) &&
			(lpSearch[uLen - 2] == _T('/')) && (lpSearch[uLen - 1] == _T('/')))
		{
			strRegex = lpSearch;
			strRegex = strRegex.Mid(2, static_cast<int>(uLen - 4));

			lpSearch = strRegex;
			dwFlags |= PWMS_REGEX;
		}
	}

	m_cList.DeleteAllItemsEx();

	PW_TIME tNow;
	_GetCurrentPwTime(&tNow);

	PW_ENTRY *pwFirst = NULL;
	const DWORD dwMaxItems = m_mgr.GetNumberOfEntries();

	// Create the search group if it doesn't exist already
	DWORD dwGroupId = m_mgr.GetGroupId(PWS_SEARCHGROUP);
	if(dwGroupId == DWORD_MAX)
	{
		PW_GROUP pwTemplate;
		pwTemplate.pszGroupName = const_cast<LPTSTR>(PWS_SEARCHGROUP);
		pwTemplate.tCreation = tNow; CPwManager::GetNeverExpireTime(&pwTemplate.tExpire);
		pwTemplate.tLastAccess = tNow; pwTemplate.tLastMod = tNow;
		pwTemplate.uGroupId = 0; // 0 = create new group ID
		pwTemplate.uImageId = 40; // 40 = 'search' icon
		pwTemplate.usLevel = 0; pwTemplate.dwFlags = 0;

		VERIFY(m_mgr.AddGroup(&pwTemplate));
		dwGroupId = m_mgr.GetGroupId(PWS_SEARCHGROUP);
	}
	ASSERT((dwGroupId != DWORD_MAX) && (dwGroupId != 0));
	if((dwGroupId == DWORD_MAX) || (dwGroupId == 0)) return;

	const DWORD dwBackup1 = m_mgr.GetGroupId(PWS_BACKUPGROUP);
	const DWORD dwBackup2 = m_mgr.GetGroupId(PWS_BACKUPGROUP_SRC);
	_UpdateCachedGroupIDs();

	m_bTANsOnly = TRUE;

	DWORD cnt = 0;
	std_string strError;
	while(true)
	{
		const DWORD dw = m_mgr.FindEx(lpSearch, FALSE, dwFlags, cnt, &strError);

		if(dw == DWORD_MAX) break;
		else
		{
			PW_ENTRY *p = m_mgr.GetEntry(dw);
			ASSERT_ENTRY(p);
			if(p == NULL) break;

			const bool bTimeValid = ((m_bQuickFindIncExpired != FALSE) ||
				(_pwtimecmp(&tNow, &p->tExpire) <= 0));

			if(p->uGroupId != dwGroupId) // Not in 'Search Results' group
			{
				if(((m_bQuickFindIncBackup != FALSE) || ((p->uGroupId != dwBackup1) &&
					(p->uGroupId != dwBackup2))) && bTimeValid)
				{
					ASSERT(m_mgr.GetGroupByIdN(p->uGroupId) != DWORD_MAX);

					// The entry could get reallocated by AddEntry, therefore
					// save it to a local CString object
					// m_mgr.UnlockEntryPassword(p);
					// CString strTemp = p->pszPassword;
					// m_mgr.LockEntryPassword(p);

					_List_SetEntry(m_cList.GetItemCount(), p, TRUE, &tNow);

					if(pwFirst == NULL) pwFirst = p;

					// EraseCString(&strTemp); // Destroy the plaintext password
				}
			}
		}

		cnt = dw + 1;
		if(cnt >= dwMaxItems) break;
	}

	_Groups_SaveView();
	UpdateGroupList();
	_Groups_RestoreView();
	HTREEITEM hSelect = _GroupIdToHTreeItem(dwGroupId);
	if(hSelect != NULL)
	{
		m_cGroups.EnsureVisible(hSelect);
		m_cGroups.SelectItem(hSelect);
	}

	if(m_cList.GetItemCount() == 0) m_bTANsOnly = FALSE; // Use report list view
	AdjustPwListMode();

	// m_bModified = TRUE;
	if((m_cList.GetItemCount() > 0) && (pwFirst != NULL))
	{
		m_cList.FocusItem(0, TRUE);
		ShowEntryDetails(pwFirst);
	}
	else ShowEntryDetails(NULL);

	std::vector<std::basic_string<TCHAR> >* pvHistory =
		m_mgr.AccessPropertyStrArray(PWPA_SEARCH_HISTORY);
	std::basic_string<TCHAR> bsSearchOrg((LPCTSTR)strSearchOrg);
	NewGUI_ComboBox_UpdateHistory(m_cQuickFind, bsSearchOrg, pvHistory,
		PWM_STD_MAX_HISTORYITEMS);

	_UpdateToolBar(TRUE);

	if((m_bFocusResAfterQuickFind == TRUE) && (m_cList.GetItemCount() > 0))
		m_cList.SetFocus();

	if(strError.size() > 0)
		MessageBox(strError.c_str(), PWM_PRODUCT_NAME_SHORT, MB_ICONWARNING | MB_OK);
}

void CPwSafeDlg::NotifyUserActivity()
{
	// m_nLockCountdown = m_nLockTimeDef;

	if(m_nLockTimeDef < 0) m_uLockAt = UINT64_MAX;
	else _SetAutoLockTimeout(m_nLockTimeDef);
}

inline void CPwSafeDlg::_SetAutoLockTimeout(long lSeconds)
{
	m_uLockAt = (_GetCurrentTimeUtc() + (static_cast<UINT64>(lSeconds) *
		0x989680ui64)); // 10000000
}

inline UINT64 CPwSafeDlg::_GetCurrentTimeUtc()
{
	FILETIME ft;
	GetSystemTimeAsFileTime(&ft);

	return ((static_cast<UINT64>(ft.dwHighDateTime) << 32) |
		static_cast<UINT64>(ft.dwLowDateTime));
}

void CPwSafeDlg::OnSafeExportGroupTxt()
{
	NotifyUserActivity();
	ExportSelectedGroup(PWEXP_TXT);
}

void CPwSafeDlg::OnUpdateSafeExportGroupTxt(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnPwlistSelectAll()
{
	NotifyUserActivity();

	ASSERT(m_bFileOpen == TRUE); if(m_bFileOpen == FALSE) return;

	m_cList.SetFocus();
	for(int i = 0; i < m_cList.GetItemCount(); ++i)
		m_cList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);

	_UpdateToolBar(TRUE);
}

void CPwSafeDlg::OnUpdatePwlistSelectAll(CCmdUI* pCmdUI)
{
	BOOL b = m_bFileOpen;
	b &= ((m_cList.GetItemCount() > 0) ? TRUE : FALSE);
	pCmdUI->Enable(b);
}

void CPwSafeDlg::UpdateAutoSortMenuItems()
{
	UINT uCmdID = ID_VIEW_AUTOSORT_NOSORT;

	for(int i = 0; i < 10; ++i)
	{
		switch(i)
		{
			case 0: uCmdID = ID_VIEW_AUTOSORT_NOSORT; break;
			case 1: uCmdID = ID_VIEW_AUTOSORT_TITLE; break;
			case 2: uCmdID = ID_VIEW_AUTOSORT_USER; break;
			case 3: uCmdID = ID_VIEW_AUTOSORT_URL; break;
			case 4: uCmdID = ID_VIEW_AUTOSORT_PASSWORD; break;
			case 5: uCmdID = ID_VIEW_AUTOSORT_NOTES; break;
			case 6: uCmdID = ID_VIEW_AUTOSORT_CREATION; break;
			case 7: uCmdID = ID_VIEW_AUTOSORT_LASTMODIFY; break;
			case 8: uCmdID = ID_VIEW_AUTOSORT_LASTACCESS; break;
			case 9: uCmdID = ID_VIEW_AUTOSORT_EXPIRE; break;
			default: ASSERT(FALSE); break;
		}

		if(i == m_nAutoSort)
			m_menu.CheckMenuItem(uCmdID, MF_BYCOMMAND | MF_CHECKED);
		else
			m_menu.CheckMenuItem(uCmdID, MF_BYCOMMAND | MF_UNCHECKED);
	}
}

void CPwSafeDlg::_SortListIfAutoSort()
{
	if(m_nAutoSort == 0) return;
	_SortList(static_cast<DWORD>(m_nAutoSort - 1), TRUE);
}

void CPwSafeDlg::OnViewAutosortCreation()
{
	NotifyUserActivity();
	m_nAutoSort = 6;
	UpdateAutoSortMenuItems();
	_SortListIfAutoSort();
}

void CPwSafeDlg::OnViewAutosortExpire()
{
	NotifyUserActivity();
	m_nAutoSort = 9;
	UpdateAutoSortMenuItems();
	_SortListIfAutoSort();
}

void CPwSafeDlg::OnViewAutosortLastaccess()
{
	NotifyUserActivity();
	m_nAutoSort = 8;
	UpdateAutoSortMenuItems();
	_SortListIfAutoSort();
}

void CPwSafeDlg::OnViewAutosortLastmodify()
{
	NotifyUserActivity();
	m_nAutoSort = 7;
	UpdateAutoSortMenuItems();
	_SortListIfAutoSort();
}

void CPwSafeDlg::OnViewAutosortNosort()
{
	NotifyUserActivity();
	m_nAutoSort = 0;
	UpdateAutoSortMenuItems();
	_SortListIfAutoSort();
}

void CPwSafeDlg::OnViewAutosortNotes()
{
	NotifyUserActivity();
	m_nAutoSort = 5;
	UpdateAutoSortMenuItems();
	_SortListIfAutoSort();
}

void CPwSafeDlg::OnViewAutosortPassword()
{
	NotifyUserActivity();
	m_nAutoSort = 4;
	UpdateAutoSortMenuItems();
	_SortListIfAutoSort();
}

void CPwSafeDlg::OnViewAutosortTitle()
{
	NotifyUserActivity();
	m_nAutoSort = 1;
	UpdateAutoSortMenuItems();
	_SortListIfAutoSort();
}

void CPwSafeDlg::OnViewAutosortUrl()
{
	NotifyUserActivity();
	m_nAutoSort = 3;
	UpdateAutoSortMenuItems();
	_SortListIfAutoSort();
}

void CPwSafeDlg::OnViewAutosortUser()
{
	NotifyUserActivity();
	m_nAutoSort = 2;
	UpdateAutoSortMenuItems();
	_SortListIfAutoSort();
}

DWORD CPwSafeDlg::_EntryUuidToListPos(BYTE *pUuid)
{
	TCHAR szTemp[66];
	BYTE aUuid[16];

	LV_ITEM lvi;
	ZeroMemory(&lvi, sizeof(LV_ITEM));
	lvi.iSubItem = 9;
	lvi.cchTextMax = 65;
	lvi.pszText = szTemp;
	lvi.mask = LVIF_TEXT;

	const int nItemCount = m_cList.GetItemCount();
	for(int i = 0; i < nItemCount; ++i)
	{
		lvi.iItem = i;
		lvi.pszText = szTemp;
		m_cList.GetItem(&lvi);

		_StringToUuid(lvi.pszText, aUuid);
		if(memcmp(aUuid, pUuid, 16) == 0) return static_cast<DWORD>(i);
	}

	return DWORD_MAX;
}

void CPwSafeDlg::OnUpdateSafeOptions(CCmdUI* pCmdUI)
{
	if(m_bMiniMode == TRUE) pCmdUI->Enable(FALSE);
	else pCmdUI->Enable(_IsDisplayingDialog() ? FALSE : TRUE);
	// else pCmdUI->Enable((((m_bLocked == FALSE) ? TRUE : FALSE) &&
	//	(!m_bDisplayDialog)) ? TRUE : FALSE);
}

void CPwSafeDlg::_AutoType(PW_ENTRY *pEntry, BOOL bLoseFocus, DWORD dwAutoTypeSeq,
	LPCTSTR lpTargetWindowName)
{
	BOOL bRefreshView = FALSE;

	ASSERT_ENTRY(pEntry); if(pEntry == NULL) return;

	if(m_bDisableAutoType == TRUE) return;

	const BOOL bNoBlockInput = AU_IsWin9xSystem();
	if(bNoBlockInput == FALSE) ::BlockInput(TRUE);

	CString strLookup;
	if(dwAutoTypeSeq == 0) strLookup = _T("auto-type:");
	else strLookup.Format(_T("%s%d%s"), _T("auto-type-"), dwAutoTypeSeq, _T(":"));

	CString str = ExtractParameterFromString(pEntry->pszAdditional, strLookup, 0);
	if(str.GetLength() == 0)
	{
		if(CPwUtil::IsTANEntry(pEntry) == TRUE) // Is it a TAN entry?
		{
			str = _T("{PASSWORD}");

			_GetCurrentPwTime(&pEntry->tExpire);
			m_bModified = TRUE;
			bRefreshView = TRUE;
		}
		else str = m_strDefaultAutoTypeSequence;
	}

	str = SprCompile(str, true, pEntry, &m_mgr, true, false);

	if((m_bAutoTypeIEFix == TRUE) && (lpTargetWindowName != NULL) &&
		((_tcsstr(lpTargetWindowName, _T("Windows Internet Explorer")) != NULL) ||
		(_tcsstr(lpTargetWindowName, _T("Maxthon")) != NULL)))
	{
		str = CString(_T("{DELAY 50}1{DELAY 50}{BACKSPACE}")) + str;
	}

	if(bLoseFocus == TRUE)
	{
		if(m_nAutoTypeMethod == ATM_DROPBACK)
		{
			SafeActivateNextWindow(this->m_hWnd);
		}
		else // m_nAutoTypeMethod == ATM_MINIMIZE
		{
			const BOOL bPrevIgnoreSizeEvent = m_bIgnoreSizeEvent;
			m_bIgnoreSizeEvent = TRUE;

			HWND hMe = ::GetForegroundWindow();

			if(m_bMinimized == FALSE)
			{
				if(m_bMinimizeToTray == TRUE)
					SafeActivateNextWindow(this->m_hWnd);

				ShowWindow(SW_MINIMIZE);
			}
			else SafeActivateNextWindow(hMe);

			const DWORD dwStartTime = timeGetTime();
			while(1)
			{
				if(::GetForegroundWindow() != hMe) break;

				if((timeGetTime() - dwStartTime) > 1000) break;
				Sleep(50);
			}

			m_bIgnoreSizeEvent = bPrevIgnoreSizeEvent;
		}
	}

	Sleep(50);

	CSendKeysEx sk;
	sk.SetEnsureSameKeyboardLayout((m_bAutoTypeSameKL == FALSE) ? false : true);

	BOOL bToggleCapsLock = FALSE;
	sk.SetDelay(10);

	// sk.SendKeys(_T("(^@+%)"), false); // Release all modifier keys
	sk.SendKeyUp(VK_LSHIFT); sk.SendKeyUp(VK_RSHIFT); sk.SendKeyUp(VK_SHIFT);
	sk.SendKeyUp(VK_LCONTROL); sk.SendKeyUp(VK_RCONTROL); sk.SendKeyUp(VK_CONTROL);
	sk.SendKeyUp(VK_LMENU); sk.SendKeyUp(VK_RMENU); sk.SendKeyUp(VK_MENU);
	sk.SendKeyUp(VK_LWIN); sk.SendKeyUp(VK_RWIN);

	if((GetKeyState(VK_CAPITAL) & 1) > 0)
		{ bToggleCapsLock = TRUE; sk.SendKeys(_T("({CAPSLOCK})"), true); }

	NewGUI_PumpMessages(NULL, WM_KEYFIRST, WM_KEYLAST);

	sk.SendKeys(str, true);

	if(bToggleCapsLock == TRUE) sk.SendKeys(_T("({CAPSLOCK})"), true);

	sk.Release();

	if(bNoBlockInput == FALSE) ::BlockInput(FALSE);

	if((bLoseFocus == TRUE) && (m_bAlwaysOnTop == TRUE))
		SetWindowPos(&CWnd::wndTopMost, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

	ASSERT(m_bLocked == FALSE);
	if((pEntry != NULL) && (m_bLocked == FALSE)) // Update last access time
	{
		PW_TIME tNow;
		_GetCurrentPwTime(&tNow);
		pEntry->tLastAccess = tNow;

		if(m_bSaveOnLATMod == TRUE) m_bModified = TRUE;

		bRefreshView = TRUE;
	}

	const DWORD uEntryInx = _ListSelToEntryIndex();
	if((CPwUtil::IsTANEntry(pEntry) == TRUE) && (m_mgr.GetEntry(uEntryInx) == pEntry))
		_PostUseTANEntry(GetSelectedEntry(), uEntryInx);

	EraseCString(&str);
	if(bRefreshView == TRUE) RefreshPasswordList();
	_UpdateToolBar(TRUE);
}

CString CPwSafeDlg::_AutoTypeNormalizeWindowText(LPCTSTR lp)
{
	if(lp == NULL) { ASSERT(FALSE); return CString(); }
	if(*lp == _T('\0')) return CString();

	CString str(lp);
	str = str.MakeLower();

	if(m_bAutoTypeNormDashes != FALSE) return SU_NormalizeDashes(str);
	return str;
}

void CPwSafeDlg::OnPwlistAutoType()
{
	NotifyUserActivity();

	if(m_bDisableAutoType == TRUE) return;

	DWORD dwEntry = _ListSelToEntryIndex();
	PW_ENTRY *p;

	ASSERT(dwEntry != DWORD_MAX); if(dwEntry == DWORD_MAX) return;

	_TouchEntry(GetSelectedEntry(), FALSE);

	p = m_mgr.GetEntry(dwEntry);
	ASSERT(p != NULL); if(p == NULL) return;

	_AutoType(p, TRUE, 0, NULL);
}

void CPwSafeDlg::OnUpdatePwlistAutoType(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((((m_dwLastFirstSelectedItem != DWORD_MAX) && (m_dwLastNumSelectedItems == 1)) ? TRUE : FALSE)
		&& (!m_bDisableAutoType));
}

void CPwSafeDlg::OnExtrasPluginMgr()
{
	NotifyUserActivity();

	CPluginsDlg dlg;
	dlg.m_pPiMgr = &CPluginManager::Instance();
	dlg.m_pImgList = &m_ilIcons;

	NewGUI_DoModal(&dlg);
	_UpdateToolBar();
}

void CPwSafeDlg::OnPluginMessage(UINT nID)
{
	NotifyUserActivity();
	_CallPlugins(KPM_DIRECT_EXEC, (LPARAM)nID, 0);
}

void CPwSafeDlg::OnUpdatePluginMessage(CCmdUI* pCmdUI)
{
	for(size_t i = 0; i < CPluginManager::Instance().m_plugins.size(); ++i)
	{
		KP_PLUGIN_INSTANCE* p = &CPluginManager::Instance().m_plugins[i];
		if((p->hinstDLL == NULL) || (p->pInterface == NULL)) { ASSERT(FALSE); continue; }

		const DWORD dwNumCommands = p->pInterface->GetMenuItemCount();
		const KP_MENU_ITEM* pMenuItems = p->pInterface->GetMenuItems();

		if((dwNumCommands == 0) || (pMenuItems == NULL)) continue;

		for(DWORD j = 0; j < dwNumCommands; ++j)
		{
			const KP_MENU_ITEM* pMenuItem = &pMenuItems[j];

			if(pMenuItem->dwCommandID == static_cast<DWORD>(pCmdUI->m_nID))
			{
				pCmdUI->Enable(((pMenuItem->dwFlags & KPMIF_DISABLED) != 0) ? FALSE : TRUE);

				if((pMenuItem->dwFlags & KPMIF_CHECKBOX) != 0)
					pCmdUI->SetCheck((pMenuItem->dwState & KPMIS_CHECKED) ? TRUE : FALSE);

				return;
			}
		}
	}
}

void CPwSafeDlg::BuildPluginMenu()
{
	std::vector<BCMenu *> ptrs;
	BCMenu *psub = NewGUI_GetBCMenu(m_menu.GetSubMenu(const_cast<LPTSTR>(TRL("&Tools"))));
	CString strItem;
	BOOL bMakeSeparator = TRUE;

	ASSERT(psub != NULL); if(psub == NULL) return;

	ptrs.push_back(psub);

	for(size_t i = 0; i < CPluginManager::Instance().m_plugins.size(); ++i)
	{
		KP_PLUGIN_INSTANCE* pPlugin = &CPluginManager::Instance().m_plugins[i];
		if((pPlugin->hinstDLL == NULL) || (pPlugin->pInterface == NULL)) continue;

		const DWORD dwNumCommands = pPlugin->pInterface->GetMenuItemCount();
		KP_MENU_ITEM* pMenuItems = pPlugin->pInterface->GetMenuItems();

		if((dwNumCommands == 0) || (pMenuItems == NULL)) continue;

		for(DWORD j = 0; j < dwNumCommands; ++j)
		{
			psub = ptrs[ptrs.size() - 1];
			KP_MENU_ITEM* p = &pMenuItems[j];

			UINT uFlags = (MF_STRING | MF_OWNERDRAW);

			if(_tcslen(p->lpCommandString) == 0)
			{
				uFlags |= MF_SEPARATOR;
				uFlags &= ~MF_STRING;
			}
			else if((p->dwFlags & KPMIF_CHECKBOX) > 0)
			{
				if((p->dwState & KPMIS_CHECKED) > 0)
					uFlags |= MF_CHECKED;
				else
					uFlags |= MF_UNCHECKED;
			}

			if((p->dwFlags & KPMIF_DISABLED) > 0) uFlags |= MF_GRAYED;
			else uFlags |= MF_ENABLED;

			if((p->dwFlags & KPMIF_POPUP_START) > 0)
				uFlags |= MF_POPUP;

			// psub->AppendMenu(uFlags, p->dwCommandID, p->lpCommandString);

			if((p->dwFlags & KPMIF_POPUP_START) > 0)
			{
				if(bMakeSeparator == TRUE)
					{ psub->AppendODMenu(_T(""), MF_OWNERDRAW | MF_SEPARATOR); bMakeSeparator = FALSE; }

				psub = psub->AppendODPopupMenu(p->lpCommandString);
				if(psub != NULL) ptrs.push_back(psub);
			}
			else if((p->dwFlags & KPMIF_POPUP_END) > 0)
			{
				if(ptrs.size() >= 2) ptrs.pop_back();
			}
			else
			{
				if(bMakeSeparator == TRUE)
					{ psub->AppendODMenu(_T(""), MF_OWNERDRAW | MF_SEPARATOR); bMakeSeparator = FALSE; }

				if(p->dwIcon != 0)
					psub->AppendODMenu(p->lpCommandString, uFlags, (int)p->dwCommandID, &m_ilIcons, (int)(p->dwIcon - 1));
				else
					psub->AppendODMenu(p->lpCommandString, uFlags, (int)p->dwCommandID);
			}
		}

		bMakeSeparator = TRUE;
	}

	ptrs.clear();
}

bool CPwSafeDlg::RegisterGlobalHotKey(int nHotKeyID, DWORD dwHotKey,
	bool bMessageBoxOnFail)
{
	std::set<int>::iterator itEx = m_sRegHotKeyIDs.find(nHotKeyID);
	if(itEx != m_sRegHotKeyIDs.end())
	{
		m_sRegHotKeyIDs.erase(itEx);
		VERIFY(::UnregisterHotKey(this->m_hWnd, nHotKeyID));
	}

	if(dwHotKey == 0) return true; // Nothing to register

	UINT uModifiers = 0;
	if(((dwHotKey >> 16) & HOTKEYF_ALT) > 0) uModifiers |= MOD_ALT;
	if(((dwHotKey >> 16) & HOTKEYF_SHIFT) > 0) uModifiers |= MOD_SHIFT;
	if(((dwHotKey >> 16) & HOTKEYF_CONTROL) > 0) uModifiers |= MOD_CONTROL;

	const BOOL b = ::RegisterHotKey(this->m_hWnd, nHotKeyID, uModifiers,
		static_cast<UINT>(dwHotKey & 0x0000FFFF));
	if(b == FALSE)
	{
		CString str = TRL("Cannot register the global hot key.");

		if(bMessageBoxOnFail)
		{
			str += _T("\r\n\r\n");
			str += TRL("Most probably another application has reserved this hot key already.");
			str += _T("\r\n\r\n");
			str += TRL("You can choose a different hot key combination in 'Tools' - 'Options' - 'Advanced' - 'Auto-Type'.");

			// Show a (global!) warning window
			::MessageBox(::GetDesktopWindow(), str, _T("KeePass"), MB_OK | MB_ICONWARNING);
		}
		else SetStatusTextEx(str);

		return false;
	}

	m_sRegHotKeyIDs.insert(nHotKeyID);
	return true;
}

LRESULT CPwSafeDlg::OnHotKey(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);

	if(wParam == HOTKEYID_AUTOTYPE)
	{
		NotifyUserActivity();

		if(m_bDisableAutoType == TRUE) return 0;
		if(g_uMasterKeyDlgs > 0)
		{
			if(g_hLastMasterKeyDlg != NULL) ::SetForegroundWindow(g_hLastMasterKeyDlg);
			return 0;
		}

		m_bGlobalAutoTypePending = TRUE;

		if(m_bLocked == TRUE)
		{
			HWND hWndSave = ::GetForegroundWindow(); // Save currently active window

			SetForegroundWindow();
			OnFileLock();

			if((hWndSave == NULL) || (hWndSave == this->m_hWnd)) { m_bGlobalAutoTypePending = FALSE; return 0; }
			::SetForegroundWindow(hWndSave);
			Sleep(10);

			if(m_bLocked == TRUE) { m_bGlobalAutoTypePending = FALSE; return 0; }
		}
		if(m_bFileOpen == FALSE) { m_bGlobalAutoTypePending = FALSE; return 0; }

		HWND hWnd = ::GetForegroundWindow();
		const int nLen = ::GetWindowTextLength(hWnd);
		if(nLen <= 0) { m_bGlobalAutoTypePending = FALSE; return 0; }

		_SetDisplayDialog(true);

		std::vector<TCHAR> vWindow(static_cast<size_t>(nLen + 3), _T('\0'));
		::GetWindowText(hWnd, &vWindow[0], nLen + 2);

		const std_string strCurWindowStl(&vWindow[0]);
		const CString strCurWindow(_AutoTypeNormalizeWindowText(&vWindow[0]));

		CString strWindowExp, strWindowLookup;
		DWORD dwWindowField, dwWindowFieldSeq, dwWindowFieldSeqFound = 0;
		PW_UUID_STRUCT pwUuid;

		CEntryListDlg dlg;
		dlg.m_nDisplayMode = ELDMODE_LIST_ATITEMS;
		dlg.m_pMgr = &m_mgr;
		dlg.m_pImgList = &m_ilIcons;
		dlg.m_bPasswordStars = m_bPasswordStars;
		dlg.m_bUserStars = m_bUserStars;
		ZeroMemory(dlg.m_aUuid, 16);
		dlg.m_strBannerTitle = TRL("Auto-Type Entry Selection");
		dlg.m_strBannerCaption = TRL("Multiple entries exist for the current window. Please select the entry to auto-type.");

		if(g_vAutoTypeSelectionDialogs.find(strCurWindowStl) != g_vAutoTypeSelectionDialogs.end())
		{
			SetForegroundWindow();
			m_bGlobalAutoTypePending = FALSE;
			_SetDisplayDialog(false);
			return 0;
		}

		PW_TIME tNow;
		_GetCurrentPwTime(&tNow);

		const DWORD dwInvalidId1 = m_mgr.GetGroupId(PWS_BACKUPGROUP_SRC);
		const DWORD dwInvalidId2 = m_mgr.GetGroupId(PWS_BACKUPGROUP);

		for(DWORD i = 0; i < m_mgr.GetNumberOfEntries(); ++i)
		{
			PW_ENTRY* pe = m_mgr.GetEntry(i);
			if(pe == NULL) { ASSERT(FALSE); continue; }

			if((pe->uGroupId == dwInvalidId1) || (pe->uGroupId == dwInvalidId2)) continue;
			if(_pwtimecmp(&tNow, &pe->tExpire) > 0) continue; // Ignore expired entries

			dwWindowField = 0;
			dwWindowFieldSeq = 0;

			while(true) // Search all auto-type-window strings
			{
				if(dwWindowFieldSeq > 0)
					strWindowLookup.Format(_T("%s%d%s"), _T("auto-type-window-"), dwWindowFieldSeq, _T(":"));
				else
					strWindowLookup = _T("auto-type-window:");

				strWindowExp = ExtractParameterFromString(pe->pszAdditional, strWindowLookup, dwWindowField);
				strWindowExp = SprCompile(strWindowExp, false, pe, &m_mgr, false, false);
				strWindowExp = _AutoTypeNormalizeWindowText(strWindowExp);

				if(strWindowExp.GetLength() != 0) // An auto-type-window definition has been found
				{
					const bool bLeft = (strWindowExp.Left(1) == _T("*"));
					const bool bRight = (strWindowExp.Right(1) == _T("*"));

					if(bLeft) strWindowExp.Delete(0, 1);
					if(bRight) strWindowExp.Delete(strWindowExp.GetLength() - 1, 1);

					const int nSubLen = strWindowExp.GetLength();

					bool bDoAdd = false;
					if(bLeft && bRight && (nSubLen <= nLen))
					{
						if(strCurWindow.Find(strWindowExp, 0) != -1)
							bDoAdd = true;
					}
					else if(bLeft && (nSubLen <= nLen))
					{
						if(strCurWindow.Right(nSubLen) == strWindowExp)
							bDoAdd = true;
					}
					else if(bRight && (nSubLen <= nLen))
					{
						if(strCurWindow.Left(nSubLen) == strWindowExp)
							bDoAdd = true;
					}
					else if(nSubLen == nLen)
					{
						if(strCurWindow == strWindowExp)
							bDoAdd = true;
					}

					if(bDoAdd)
					{
						memcpy(pwUuid.uuid, pe->uuid, 16);
						dlg.m_vEntryList.push_back(pwUuid); 
						dwWindowFieldSeqFound = dwWindowFieldSeq;
						break;
					}

					++dwWindowField;
				}
				else if(dwWindowField != 0) // Previous auto-type-window field was found but did not match, so increment sequence and search again
				{
					dwWindowField = 0;
					++dwWindowFieldSeq;
				}
				else if((dwWindowField == 0) && (dwWindowFieldSeq == 0)) // Entry doesn't have any auto-type-window definition
				{
					strWindowExp = pe->pszTitle;
					strWindowExp = SprCompile(strWindowExp, false, pe, &m_mgr, false, false);
					const int nSubLen = strWindowExp.GetLength();

					if((nSubLen != 0) && (nSubLen <= nLen))
					{
						strWindowExp = _AutoTypeNormalizeWindowText(strWindowExp);
						if(strCurWindow.Find(strWindowExp, 0) != -1)
						{
							memcpy(pwUuid.uuid, pe->uuid, 16);
							dlg.m_vEntryList.push_back(pwUuid);
						}
					}

					break;
				}
				else break;
			}
		}

		const DWORD dwMatchingEntriesCount = static_cast<DWORD>(dlg.m_vEntryList.size());
		if(dwMatchingEntriesCount != 0)
		{
			PW_ENTRY* pe = NULL;
			if(dwMatchingEntriesCount == 1)
				pe = m_mgr.GetEntryByUuid(dlg.m_vEntryList[0].uuid);
			else
			{
				g_vAutoTypeSelectionDialogs.insert(strCurWindowStl);

				SetForegroundWindow();
				NewGUI_DoModal(&dlg);

				g_vAutoTypeSelectionDialogs.erase(strCurWindowStl);

				// Check if the target window still exists / is valid
				if(::IsWindow(hWnd) == TRUE)
				{
					VERIFY(::SetForegroundWindow(hWnd) == TRUE); // Restore window focus

					if(CPwUtil::IsZeroUUID(dlg.m_aUuid) == FALSE)
						pe = m_mgr.GetEntryByUuid(dlg.m_aUuid);
				}

				dwWindowFieldSeqFound = 0; // Set this back to zero as cannot choose the sequence of auto-type in the dialog
			}

			if(pe != NULL)
			{
				if(dwMatchingEntriesCount != 1)
				{
					dwWindowField = 0;
					dwWindowFieldSeq = 0;

					while(true) // Search all auto-type-window strings
					{
						if(dwWindowFieldSeq > 0)
							strWindowLookup.Format(_T("%s%d%s"), _T("auto-type-window-"), dwWindowFieldSeq, _T(":"));
						else
							strWindowLookup = _T("auto-type-window:");

						strWindowExp = ExtractParameterFromString(pe->pszAdditional, strWindowLookup, dwWindowField);
						strWindowExp = SprCompile(strWindowExp, false, pe, &m_mgr, false, false);
						strWindowExp = _AutoTypeNormalizeWindowText(strWindowExp);

						if(strWindowExp.GetLength() != 0) // An auto-type-window definition has been found
						{
							const bool bLeft = (strWindowExp.Left(1) == _T("*"));
							const bool bRight = (strWindowExp.Right(1) == _T("*"));

							if(bLeft) strWindowExp.Delete(0, 1);
							if(bRight) strWindowExp.Delete(strWindowExp.GetLength() - 1, 1);

							const int nSubLen = strWindowExp.GetLength();

							if(bLeft && bRight && (nSubLen <= nLen))
							{
								if(strCurWindow.Find(strWindowExp, 0) != -1)
								{
									dwWindowFieldSeqFound = dwWindowFieldSeq;
									break;
								}
							}
							else if(bLeft && (nSubLen <= nLen))
							{
								if(strCurWindow.Right(nSubLen) == strWindowExp)
								{
									dwWindowFieldSeqFound = dwWindowFieldSeq;
									break;
								}
							}
							else if(bRight && (nSubLen <= nLen))
							{
								if(strCurWindow.Left(nSubLen) == strWindowExp)
								{
									dwWindowFieldSeqFound = dwWindowFieldSeq;
									break;
								}
							}
							else if(nSubLen == nLen)
							{
								if(strCurWindow == strWindowExp)
								{
									dwWindowFieldSeqFound = dwWindowFieldSeq;
									break;
								}
							}

							++dwWindowField;
						}
						else if(dwWindowField != 0) // Previous auto-type-window field was found but did not match, so increment sequence and search again
						{
							dwWindowField = 0;
							++dwWindowFieldSeq;
						}
						else break; // Entry doesn't have any auto-type-window definition
					}
				}

				_AutoType(pe, FALSE, dwWindowFieldSeqFound, &vWindow[0]);
			}
		}

		dlg.m_vEntryList.clear();

		m_bGlobalAutoTypePending = FALSE;
		_SetDisplayDialog(false);
	}
	else if(wParam == HOTKEYID_RESTORE)
	{
		NotifyUserActivity();
		SetForegroundWindow();
		if((m_bMinimized == TRUE) || (m_bTrayed == TRUE))
			SetViewHideState(TRUE, FALSE);
	}
	else { ASSERT(FALSE); }

	return 0;
}

// User-requested clipboard contents
void CPwSafeDlg::OnRenderFormat(UINT nFormat)
{
	if(m_nClipboardMethod != CM_ENHSECURE)
	{
		CDialog::OnRenderFormat(nFormat);
		return;
	}

	const DWORD dwIndex = _ListSelToEntryIndex();

	if(dwIndex == DWORD_MAX) return;
	PW_ENTRY *p = m_mgr.GetEntry(dwIndex);
	ASSERT_ENTRY(p); if(p == NULL) return;

	CString str = TRL("Field has been pasted."); str += _T(" ");
	str += TRL("Clipboard cleared.");

	CString str2;

	// CDialog::OnRenderFormat(nFormat);
	switch(m_nClipboardState)
	{
		case CLIPBOARD_DELAYED_USERNAME:
			m_nClipboardState = CLIPBOARD_DELAYED_NONE;
			CopyDelayRenderedClipboardData(p->pszUserName, &m_mgr);
			m_nClipboardState = CLIPBOARD_DELAYED_NONE;
			SetStatusTextEx(str);
			PostMessage(WM_MY_UPDATECLIPBOARD);
			break;

		case CLIPBOARD_DELAYED_PASSWORD:
			m_nClipboardState = CLIPBOARD_DELAYED_NONE;
			m_mgr.UnlockEntryPassword(p);
			str2 = p->pszPassword;
			m_mgr.LockEntryPassword(p);
			CopyDelayRenderedClipboardData(str2, &m_mgr);
			EraseCString(&str2);
			m_nClipboardState = CLIPBOARD_DELAYED_NONE;
			SetStatusTextEx(str);
			PostMessage(WM_MY_UPDATECLIPBOARD);
			break;

		case CLIPBOARD_DELAYED_NONE:
			CopyDelayRenderedClipboardData(_T(""), NULL);
			str2 = TRL("Paste operation completed."); str2 += _T(" ");
			str2 += TRL("Clipboard cleared.");
			SetStatusTextEx(str2);
			if(m_hwndNextViewer != NULL)
			{
				ChangeClipboardChain(m_hwndNextViewer);
				m_hwndNextViewer = NULL;
			}
			break;

		default:
			ASSERT(FALSE);
			break;
	};
}

// Called when we setup the clipboard for delayed rendering
// but nobody tried to copy from the clipboard before the next
// Copy to the clipboard occurred.
void CPwSafeDlg::OnRenderAllFormats()
{
	if(m_nClipboardMethod != CM_ENHSECURE)
	{
		CDialog::OnRenderAllFormats();
		return;
	}

	// CDialog::OnRenderAllFormats();
	// User copied different contents to the windows clipboard
	// set clipboard contents to blank string
	CopyDelayRenderedClipboardData(_T(""), NULL);
}

void CPwSafeDlg::OnChangeCbChain(HWND hWndRemove, HWND hWndAfter)
{
	CDialog::OnChangeCbChain(hWndRemove, hWndAfter);

	if(m_nClipboardMethod != CM_ENHSECURE) return;

    // If the next window is closing, repair the chain.
    if(hWndRemove == m_hwndNextViewer)
        m_hwndNextViewer = hWndAfter;
    // Otherwise, pass the message to the next link.
    else if(m_hwndNextViewer != NULL)
		::SendMessage(m_hwndNextViewer, WM_CHANGECBCHAIN, (WPARAM)hWndRemove, (LPARAM)hWndAfter);
}

void CPwSafeDlg::OnDrawClipboard()
{
	if(m_nClipboardMethod != CM_ENHSECURE)
	{
		CDialog::OnDrawClipboard();
		return;
	}

	// CDialog::OnDrawClipboard();
	// Clipboard contents changed
}

BOOL CPwSafeDlg::OnQueryEndSession()
{
	if(_IsDisplayingDialog() && (m_bFileOpen == TRUE))
		return FALSE;

	SaveOptions();
	OnFileClose();

	return TRUE;
}

void CPwSafeDlg::OnEndSession(BOOL bEnding)
{
	if(bEnding == TRUE)
		OnFileExit();

	CWnd::OnEndSession(bEnding);
}

void CPwSafeDlg::OnImportGetMore()
{
	ShellExecute(NULL, NULL, PWM_URL_PLUGINS, NULL, NULL, SW_SHOW);
}

BOOL CPwSafeDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
	DWORD dwLen, dwPasswordLen, dwKeyFileLen;
	CString strData, strPassword, strKeyFile, strFile;

	UNREFERENCED_PARAMETER(pWnd);

	NotifyUserActivity();
	if(pCopyDataStruct == NULL) return TRUE;

	if(_IsDisplayingDialog()) return TRUE; // Do nothing if a modal window is displayed

	if(pCopyDataStruct->lpData != NULL)
	{
		dwLen = (DWORD)_tcslen((LPCTSTR)pCopyDataStruct->lpData);
		if((dwLen == 0) && (pCopyDataStruct->dwData == 0xF0FFFFF0))
		{
			if((m_bMinimized == TRUE) || (m_bTrayed == TRUE))
				SetViewHideState(TRUE, FALSE); // Restore window
			return TRUE;
		}
		else if(dwLen == 0) return TRUE;

		strData = (LPCTSTR)pCopyDataStruct->lpData;
		dwPasswordLen = (DWORD)(pCopyDataStruct->dwData >> 16);
		dwKeyFileLen = (DWORD)(pCopyDataStruct->dwData & 0xFFFF);

		if((dwPasswordLen + dwKeyFileLen) >= dwLen) return TRUE;

		if(dwPasswordLen != 0) strPassword = strData.Mid(0, dwPasswordLen);
		if(dwKeyFileLen != 0) strKeyFile = strData.Mid(dwPasswordLen, dwKeyFileLen);

		if((dwPasswordLen + dwKeyFileLen) != 0)
			strFile = strData.Right(strData.GetLength() - (int)dwPasswordLen - (int)dwKeyFileLen);
		else strFile = strData;

		if(strFile.GetLength() == 0) return TRUE;

		if(m_strFile.GetLength() != 0)
		{
			CString strThis = m_strFileAbsolute;
			CString strNew = GetShortestAbsolutePath(strFile);

			strThis = strThis.MakeLower();
			strNew = strNew.MakeLower();

			if(strThis == strNew) return TRUE;
		}

		OnFileClose();
		if(m_bFileOpen == TRUE) return TRUE;

		if((dwPasswordLen != 0) && (dwKeyFileLen != 0))
			_OpenDatabase(NULL, strFile, strPassword, strKeyFile, FALSE, NULL, FALSE);
		else if(dwPasswordLen != 0)
			_OpenDatabase(NULL, strFile, strPassword, NULL, FALSE, NULL, FALSE);
		else if(dwKeyFileLen != 0)
			_OpenDatabase(NULL, strFile, NULL, strKeyFile, FALSE, NULL, FALSE);
		else
			_OpenDatabase(NULL, strFile, NULL, NULL, FALSE, NULL, FALSE);

		if((m_bMinimized == TRUE) || (m_bTrayed == TRUE))
			SetViewHideState(TRUE, FALSE);
	}

	return TRUE;
}

void CPwSafeDlg::OnInfoDonate()
{
	NotifyUserActivity();
	ShellExecute(NULL, NULL, PWM_URL_DONATE, NULL, NULL, SW_SHOW);
}

void CPwSafeDlg::OnExtrasRepairDb()
{
	NotifyUserActivity();

	if(m_bLocked == TRUE) return;

	_SetDisplayDialog(true);

	CString strTitle = TRL("Warning");
	strTitle += _T("!");
	CString strMsg = TRL("In repair mode, the integrity of the data is not checked (in order to rescue as much data as possible). When no integrity checks are performed, corrupted/malicious data might be incorporated into the database.");
	strMsg += _T("\r\n\r\n");
	strMsg += TRL("Thus the repair functionality should only be used when there really is no other solution. If you use it, afterwards you should thoroughly check your whole database for corrupted/malicious data.");
	strMsg += _T("\r\n\r\n");
	strMsg += TRL("Are you sure you want to attempt to repair a database file?");

	int r = CVistaTaskDialog::ShowMessageBox(this->m_hWnd, strTitle, strMsg,
		TD_WARNING_ICON, TRL("&Yes"), IDYES, TRL("&No"), IDNO);
	if(r < 0)
	{
		strMsg = strTitle + _T("\r\n\r\n") + strMsg;
		r = MessageBox(strMsg, PWM_PRODUCT_NAME_SHORT, MB_YESNO | MB_ICONWARNING);
	}
	if((r != IDYES) && (r != IDOK)) { _SetDisplayDialog(false); return; }

	if(_CallPlugins(KPM_FILE_OPEN_PRE, 0, 0) == FALSE)
		{ _SetDisplayDialog(false); return; }

	_OpenDatabase(NULL, NULL, NULL, NULL, FALSE, NULL, TRUE);
	_UpdateToolBar();
	_SetDefaultFocus();
	_SetDisplayDialog(false);
}

void CPwSafeDlg::OnUpdateExtrasRepairDb(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_bFileOpen == FALSE) && (m_bLocked == FALSE));
}

CString CPwSafeDlg::_GetSecureEditTipText(const TCHAR *tszBase)
{
	CString str;

	ASSERT(tszBase != NULL); if(tszBase == NULL) return CString(_T(""));

	str = TRL_VAR(tszBase);
	str = str.TrimRight(_T(':'));
	str += _T("\r\n(");
	str += TRL("Press Shift+Home or Shift+End to clear this edit control");
	str += _T(")");
	return str;
}

void CPwSafeDlg::_UpdateTitleBar()
{
	CString strFull = PWM_PRODUCT_NAME_SHORT, strShort = PWM_PRODUCT_NAME_SHORT;

	if(m_bLocked == TRUE)
	{
		if(m_strLastDb.GetLength() > 0)
		{
			if(m_bShowFullPath == FALSE) strFull = CsFileOnly(&m_strLastDb);
			else strFull = m_strLastDb;

			strFull += _T(" [");
			strFull += TRL("Locked");
			strFull += _T("] - ");
		}
		else
		{
			strFull = TRL("Workspace locked");
			strFull += _T(" - ");
		}

		strFull += PWM_PRODUCT_NAME_SHORT;

		strShort += _T(" - ");
		strShort += TRL("Locked");
		
		if(m_strLastDb.GetLength() > 0)
		{
			if(WU_SupportsMultiLineTooltips() == TRUE) strShort += _T("\r\n");
			else strShort += _T(" - ");

			strShort += CsFileOnly(&m_strLastDb);
		}
	}
	else if(m_bFileOpen == TRUE)
	{
		if((m_strFile.GetLength() > 0) && (m_strFileAbsolute.GetLength() > 0))
		{
			if(m_bShowFullPath == FALSE) strFull = CsFileOnly(&m_strFile);
			else strFull = m_strFileAbsolute;

			if(m_bModified == TRUE) strFull += _T("*");

			strFull += _T(" - ");
		}
		else strFull = _T("");

		strFull += PWM_PRODUCT_NAME_SHORT;

		if(m_strFile.GetLength() > 0)
		{
			if(WU_SupportsMultiLineTooltips() == TRUE) strShort += _T("\r\n");
			else strShort += _T(" - ");

			strShort += CsFileOnly(&m_strFile);

			if(m_bModified == TRUE) strShort += _T("*");
		}
	}

	if(m_strWindowTitleSuffix.GetLength() > 0)
	{
		strFull += _T(" - ");
		strFull += m_strWindowTitleSuffix;
	}

	CString strCurrent;
	this->GetWindowText(strCurrent);
	if(strCurrent != strFull)
	{
		this->SetWindowText(strFull);

		// NOTIFYICONDATA only supports tooltip texts up to 63 characters
		if(strShort.GetLength() > 63)
		{
			strShort = strShort.Left(60); // Replace rest by 3 dots
			strShort += _T("...");
		}

		m_systray.SetTooltipText(strShort);
	}
}

void CPwSafeDlg::OnImportKeePass()
{
	NotifyUserActivity();

	const std::basic_string<TCHAR> strDir = WU_GetCurrentDirectory();

	CPwManager mgrImport;
	_OpenDatabase(&mgrImport, NULL, NULL, NULL, FALSE, NULL, FALSE);

	WU_SetCurrentDirectory(strDir.c_str());

	if(mgrImport.GetNumberOfGroups() != 0)
	{
		_Groups_SaveView(TRUE);
		_List_SaveView();

		CImportOptionsDlg dlg;
		if(NewGUI_DoModal(&dlg) == IDCANCEL) return;

		const BOOL bNewIDs = ((dlg.m_nImportMethod == 0) ? TRUE : FALSE);
		const BOOL bCompareTimes = ((dlg.m_nImportMethod == 2) ? TRUE : FALSE);
		m_mgr.MergeIn(&mgrImport, bNewIDs, bCompareTimes);

		m_bModified = TRUE;
		UpdateGroupList();
		_Groups_RestoreView();
		UpdatePasswordList();
		_List_RestoreView();
		_UpdateToolBar();
	}
}

void CPwSafeDlg::OnUpdateImportKeePass(CCmdUI* pCmdUI)
{
	BOOL b = m_bFileOpen;
	b &= ((m_mgr.GetNumberOfGroups() != 0) ? TRUE : FALSE);
	pCmdUI->Enable(b);
}

void CPwSafeDlg::ExportGroupToKeePass(DWORD dwGroupId)
{
	CPwManager pwTempMgr;
	CPwExport cExp;

	NotifyUserActivity();

	if(m_bForceAllowExport == FALSE)
	{
		if(IsUnsafeAllowed(this->m_hWnd) == FALSE) return;
	}

	cExp.SetManager(&m_mgr);
	cExp.SetNewLineSeq(m_bWindowsNewLine);
	cExp.SetFormat(PWEXP_KEEPASS);

	CString strFile;

	if(dwGroupId != DWORD_MAX) strFile = GetExportFile(PWEXP_KEEPASS, m_mgr.GetGroupById(dwGroupId)->pszGroupName, TRUE);
	else strFile = GetExportFile(PWEXP_KEEPASS, CsFileOnly(&m_strFile), TRUE);

	if(strFile.GetLength() == 0) return;

	CString strText = TRL("Please choose if KeePass should use the key of the currently opened database or ask you to define a new key to encrypt the exported file.");
	strText += _T("\r\n\r\n");
	strText += TRL("Click [Yes] to use the same key as the currently opened database.");
	strText += _T("\r\n");
	strText += TRL("Click [No] to set a new key for the exported file.");
	strText += _T("\r\n\r\n");
	strText += TRL("Reuse the current key/password?");

	const int nUse = MessageBox(strText, PWM_PRODUCT_NAME_SHORT, MB_ICONQUESTION | MB_YESNO);
	if(nUse == IDNO)
	{
		if(_ChangeMasterKey(&pwTempMgr, TRUE) != TRUE) return;
	}
	else
	{
		BYTE aMasterKey[32]; // Copy the master key
		m_mgr.GetRawMasterKey(aMasterKey);
		pwTempMgr.SetRawMasterKey(aMasterKey);
		mem_erase(aMasterKey, 32);
	}

	if(dwGroupId == DWORD_MAX)
	{
		// Clone manager properties when exporting all groups and entries
		pwTempMgr.m_dwLastSelectedGroupId = m_mgr.m_dwLastSelectedGroupId;
		pwTempMgr.m_dwLastTopVisibleGroupId = m_mgr.m_dwLastTopVisibleGroupId;
		memcpy(pwTempMgr.m_aLastSelectedEntryUuid, m_mgr.m_aLastSelectedEntryUuid, 16);
		memcpy(pwTempMgr.m_aLastTopVisibleEntryUuid, m_mgr.m_aLastTopVisibleEntryUuid, 16);
	}

	cExp.ExportGroup(strFile, dwGroupId, NULL, &pwTempMgr);

	pwTempMgr.SetRawMasterKey(NULL); // Clear copy of key
}

void CPwSafeDlg::OnSafeExportGroupKeePass()
{
	DWORD dwSelectedGroup = GetSelectedGroupId();

	ASSERT(dwSelectedGroup != DWORD_MAX);
	if(dwSelectedGroup == DWORD_MAX) return;

	ExportGroupToKeePass(dwSelectedGroup);
}

void CPwSafeDlg::OnUpdateSafeExportGroupKeePass(CCmdUI* pCmdUI)
{
	pCmdUI->Enable((m_hLastSelectedGroup != NULL) ? TRUE : FALSE);
}

void CPwSafeDlg::OnExportKeePass()
{
	ExportGroupToKeePass(DWORD_MAX);
}

void CPwSafeDlg::OnUpdateExportKeePass(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bFileOpen);
}

// Many thanks to Kenneth Ballard for the contribution of this code
// to recreate the tray icon (e.g. after Windows Explorer crashed)
LRESULT CPwSafeDlg::OnTaskbarCreated(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);

	// When the taskbar gets recreated, possibly because of a crash
	// in Explorer, re-add the icon.
	m_systray.AddIcon();
	_UpdateTrayIcon();
	_UpdateTitleBar();
	return 0;
}

LRESULT CPwSafeDlg::OnTaskbarButtonCreated(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);

	_UpdateTrayIcon(); // Set overlay icon
	return 0;
}

void CPwSafeDlg::_CalcColumnSizes()
{
	RECT rect;
	int nColumnWidth, nColumns = 0, i;

	for(i = 0; i < 11; ++i) // Count active columns and reset all widths
	{
		if(m_bShowColumn[i] == TRUE) nColumns++;
		m_nColumnWidths[i] = 0;
	}
	if(nColumns == 0) return;

	m_cList.GetClientRect(&rect); // Calculate and set new widths
	nColumnWidth = ((rect.right - 9) / nColumns) - (GetSystemMetrics(SM_CXVSCROLL) / nColumns);
	for(i = 0; i < 11; ++i)
		if(m_bShowColumn[i] == TRUE)
			m_nColumnWidths[i] = nColumnWidth;
}

void CPwSafeDlg::_SetColumnWidths()
{
	if(m_dwPwListMode == LVS_REPORT)
	{
		for(int i = 0; i < 11; ++i)
		{
			if(m_cList.GetColumnWidth(i) != m_nColumnWidths[i])
				m_cList.SetColumnWidth(i, m_nColumnWidths[i]);
		}
	}
	else if(m_dwPwListMode == LVS_SMALLICON) AdjustColumnWidths();

	// Do nothing if m_dwPwListMode == LVSX_CHANGING
}

void CPwSafeDlg::_SetListParameters()
{
	LPARAM dw = (LVS_EX_SI_REPORT | LVS_EX_FULLROWSELECT | LVS_EX_ONECLICKACTIVATE |
		LVS_EX_UNDERLINEHOT | LVS_EX_HEADERDRAGDROP | LVS_EX_INFOTIP);

	// Windows XP and higher: enable double-buffering
	// Windows Vista and higher: enable alpha-blended marquee selection
	dw |= LVS_EX_DOUBLEBUFFER;

	if(m_bEntryGrid == TRUE) dw |= LVS_EX_GRIDLINES;

	if(m_dwOldListParameters != dw)
		m_cList.PostMessage(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dw);

	m_dwOldListParameters = dw;
}

void CPwSafeDlg::OnViewSimpleTANView() 
{
	m_bSimpleTANView = ((m_bSimpleTANView == FALSE) ? TRUE : FALSE);
	m_menu.CheckMenuItem(ID_VIEW_SIMPLETANVIEW, MF_BYCOMMAND |
		((m_bSimpleTANView == TRUE) ? MF_CHECKED : MF_UNCHECKED));

	RefreshPasswordList();
}

void CPwSafeDlg::OnViewShowTANIndices() 
{
	m_bShowTANIndices = ((m_bShowTANIndices == FALSE) ? TRUE : FALSE);
	m_menu.CheckMenuItem(ID_VIEW_SHOWTANINDICES, MF_BYCOMMAND |
		((m_bShowTANIndices == TRUE) ? MF_CHECKED : MF_UNCHECKED));

	RefreshPasswordList();
}

void CPwSafeDlg::OnUpdateFileExit(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(_IsDisplayingDialog() ? FALSE : TRUE);
}

void CPwSafeDlg::OnUpdateExtrasGenPw(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(_IsDisplayingDialog() ? FALSE : TRUE);
}

void CPwSafeDlg::OnUpdateInfoAbout(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(_IsDisplayingDialog() ? FALSE : TRUE);
}

LRESULT CPwSafeDlg::OnWTSSessionChange(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);

	if(_IsDisplayingDialog()) return 0;

	if(m_bLockOnWinLock == FALSE)
	{
		if(wParam == WTS_SESSION_LOGOFF) SaveOptions();
	}
	else // m_bLockOnWinLock == TRUE
	{
		if((wParam == WTS_SESSION_LOGOFF) || (wParam == WTS_SESSION_LOCK) ||
			(wParam == WTS_CONSOLE_DISCONNECT))
		{
			SaveOptions();

			if((m_bFileOpen == TRUE) && (m_bLocked == FALSE))
				OnFileLock();
		}
	}

	return 0;
}

void CPwSafeDlg::OnInfoChkForUpd()
{
	CUpdateCheckEx::Check(FALSE, this->m_hWnd, &m_ilIcons, FALSE);
}

LRESULT CPwSafeDlg::OnProcessMailslot(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);

	m_remoteControl.ProcessAllRequests();
	if(m_remoteControl.RequiresGUIUpdate() == TRUE)
	{
		m_bModified = TRUE;

		_Groups_SaveView(TRUE);
		UpdateGroupList();
		_Groups_RestoreView();

		_List_SaveView();
		UpdatePasswordList();
		_List_RestoreView();

		_UpdateToolBar();
	}

	return 0;
}

void CPwSafeDlg::_UpdateTrayIcon(bool bUpdateOnlyVisibility)
{
	USES_CONVERSION;

	if((m_bTrayed != FALSE) || (m_bShowTrayOnlyIfTrayed == FALSE))
	{
		VERIFY(m_systray.ShowIcon() != FALSE);
	}
	else { VERIFY(m_systray.HideIcon() != FALSE); }

	if(bUpdateOnlyVisibility) return;

	HICON hAssign = NULL, hDestroy = NULL;
	NewGUI_UpdateColorizedIcon(m_hIcon48, NULL, m_mgr.GetColor(), 0,
		&m_hIcoStoreMain, &m_clrIcoStoreMain, &hAssign, &hDestroy);
	SetIcon(hAssign, FALSE);
	SetIcon(hAssign, TRUE);
	if(hDestroy != NULL) { VERIFY(DestroyIcon(hDestroy)); }

	if(m_bLocked == FALSE)
	{
		const int qSmall = GetSystemMetrics(SM_CXSMICON);
		NewGUI_UpdateColorizedIcon(m_hTrayIconNormal, NULL, m_mgr.GetColor(), qSmall,
			&m_hIcoStoreTrayNormal, &m_clrIcoStoreTrayNormal, &hAssign, &hDestroy);

		m_systray.SetIcon(hAssign);
		if(hDestroy != NULL) { VERIFY(DestroyIcon(hDestroy)); }

		CTaskbarListEx::SetOverlayIcon(this->m_hWnd, NULL, L"");
	}
	else
	{
		// NewGUI_UpdateColorizedIcon(m_hTrayIconNormal, m_hLockOverlayIcon, m_mgr.GetColor(),
		//	qSmall, &m_hIcoStoreTrayLocked, &m_clrIcoStoreTrayLocked, &hAssign, &hDestroy);

		// m_systray.SetIcon(hAssign);
		// if(hDestroy != NULL) { VERIFY(DestroyIcon(hDestroy)); }

		m_systray.SetIcon(m_hTrayIconLocked);

		CTaskbarListEx::SetOverlayIcon(this->m_hWnd, m_hLockOverlayIcon,
			T2CW(TRL("Locked")));
	}
}

UINT CPwSafeDlg::GetKeePassControlMessageID()
{
	return WM_REG_KEEPASSCONTROL;
}

LRESULT CPwSafeDlg::OnKeePassControlMessage(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);

	if(wParam == KPCM_EXIT) OnFileExit();
	else if(wParam == KPCM_AUTOTYPE) OnHotKey(HOTKEYID_AUTOTYPE, 0);
	else if(wParam == KPCM_LOCK) _ChangeLockState(TRUE);
	else if(wParam == KPCM_UNLOCK)
	{
		if(m_bLocked != FALSE) SetForegroundWindow();
		_ChangeLockState(FALSE);
	}

	return 0;
}

void CPwSafeDlg::_ChangeLockState(BOOL bLocked)
{
	if((bLocked != FALSE) && (m_bLocked == FALSE)) // Should lock
		OnFileLock();
	else if((bLocked == FALSE) && (m_bLocked != FALSE)) // Should unlock
		OnFileLock();
}

void CPwSafeDlg::ToggleViewHideState(BOOL bPreferTray)
{
	SetViewHideState(((m_bMinimized == TRUE) || (m_bTrayed == TRUE)) ? TRUE :
		FALSE, bPreferTray);
}

void CPwSafeDlg::SetViewHideState(BOOL bReqVisible, BOOL bPreferTray)
{
	NotifyUserActivity();

	if(bReqVisible == TRUE) // Show the window
	{
		_MiniModeShowWindow();

		if(m_bMinimized == TRUE)
		{
			if(m_bWasMaximized == TRUE) ShowWindow(SW_MAXIMIZE);
			else ShowWindow(SW_RESTORE);
			_UpdateTrayIcon();
		}
		else if(m_bTrayed == TRUE) SetTrayState(FALSE);

		if(m_bAlwaysOnTop == TRUE)
			SetWindowPos(&CWnd::wndTopMost, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
	}
	else // Hide the window
	{
		_SaveWindowPositionAndSize(NULL);
		_UIBlockHints(true); // Prevent drawing bug in peek preview

		if((m_bMinimizeToTray == TRUE) || (bPreferTray == TRUE))
			SetTrayState(TRUE);
		else ShowWindow(SW_MINIMIZE);

		_UIBlockHints(false);
	}
}

void CPwSafeDlg::_MiniModeShowWindow()
{
	if(m_bMiniMode == FALSE) return;

	if((m_bFileOpen == FALSE) && (m_strLastDb.GetLength() == 0))
	{
		OnFileNew();

		if(m_bFileOpen == FALSE) return;
		else if(m_strFile.GetLength() == 0)
		{
			CString strLoadedIniLastDb;

			if((m_bOpenLastDb == TRUE) && (m_bRememberLast == TRUE))
			{
				CPrivateConfigEx cConfig(FALSE);
				TCHAR szTemp[SI_REGSIZE];

				cConfig.Get(PWMKEY_LASTDB, szTemp);
				if(szTemp[0] != 0)
				{
					std_string strTemp = WU_ExpandEnvironmentVars(szTemp);
					strLoadedIniLastDb = strTemp.c_str();

					if(WU_IsAbsolutePath(strTemp.c_str()) == FALSE)
						strTemp = Executable::instance().getPathOnly() + strTemp;

					if(_FileAccessible(strTemp.c_str()) == TRUE)
						_OpenDatabase(NULL, strTemp.c_str(), NULL, NULL, m_bStartMinimized, NULL, FALSE);
				}

				_GetNewDbFileInUserDir(strLoadedIniLastDb, cConfig);
			}
			else { ASSERT(FALSE); }
		}
		else { ASSERT(FALSE); }
	}
}

void CPwSafeDlg::_GetNewDbFileInUserDir(const CString& strLoadedIniLastDb,
	const CPrivateConfigEx& cConfig)
{
	if((_FileAccessible(strLoadedIniLastDb) == FALSE) &&
		(_FileWritable(strLoadedIniLastDb) == TRUE))
	{
		VERIFY(DeleteFile(strLoadedIniLastDb));
		m_strFile = strLoadedIniLastDb;
		m_strFileAbsolute = strLoadedIniLastDb;
	}
	else
	{
		m_strFile = cConfig.GetUserPath().c_str();
		std_string strNewPathSpec = cConfig.GetSafe(PWMKEY_AUTONEWDBBASEPATH);
		if(strNewPathSpec.size() > 0) m_strFile = strNewPathSpec.c_str();

		VERIFY(WU_CreateDirectoryTree(m_strFile) == S_OK); // Ensure the directory exists
		
		m_strFile += _T("\\");
		
		std_string strNewNameSpec = _T("Database");
		std_string strNewNameSpecIni = cConfig.GetSafe(PWMKEY_AUTONEWDBBASENAME);
		if(strNewNameSpecIni.size() > 0) strNewNameSpec = strNewNameSpecIni;

		m_strFile += strNewNameSpec.c_str();

		bool bSuccess = false;
		for(DWORD dwTest = 0; dwTest < 1000; ++dwTest)
		{
			CString strCan;

			if(dwTest > 0)
				strCan.Format(_T("%s%u%s"), (LPCTSTR)m_strFile, dwTest, _T(".kdb"));
			else
				strCan.Format(_T("%s%s"), (LPCTSTR)m_strFile, _T(".kdb"));

			if((_FileAccessible(strCan) == FALSE) &&
				(_FileWritable(strCan) == TRUE))
			{
				VERIFY(DeleteFile(strCan));
				m_strFile = strCan;
				m_strFileAbsolute = strCan;
				bSuccess = true;
				break;
			}
		}

		if(bSuccess == false)
		{
			m_strFile.Empty();

			CString strMsg = TRL("File access error: failed to open file in write mode");
			strMsg += _T(": ");
			strMsg += TRL("KeePass Database");
			strMsg += _T(".");

			MessageBox(strMsg, PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
			exit(1);
			return;
		}
	}
}

void CPwSafeDlg::SetTrayState(BOOL bMinimizeToTray)
{
	if(bMinimizeToTray == TRUE)
	{
		_SaveWindowPositionAndSize(NULL);

		m_bTrayed = TRUE;
		_UpdateTrayIcon(); // Show icon

		// https://sourceforge.net/p/keepass/discussion/329220/thread/a0672c5f/
		m_hPreTrayFocus = ::SetFocus(NULL);

		SetMenu(NULL);

		m_systray.MinimiseToTray(this);
		CMsgRelayWnd::EnableRelaying(TRUE);

		if(m_bLockOnMinimize == TRUE)
			_ChangeLockState(TRUE);
	}
	else // Restore window from tray
	{
		CMsgRelayWnd::EnableRelaying(FALSE);
		m_systray.MaximiseFromTray(this, FALSE, m_bWasMaximized);

		m_bMenu = SetMenu(&m_menu);

		if(m_hPreTrayFocus != NULL)
		{
			if(::IsWindow(m_hPreTrayFocus) != FALSE)
				::SetFocus(m_hPreTrayFocus); // Restore focus
			else { ASSERT(FALSE); }
		}

		m_bTrayed = FALSE;

		_UpdateTrayIcon();
		_ChangeLockState(FALSE);
	}
}

void CPwSafeDlg::OnUpdateViewHide(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(_IsDisplayingDialog() ? FALSE : TRUE);
}

void CPwSafeDlg::SetToMiniModeIfEnabled(BCMenu *pMenu, BOOL bRemoveAccels,
	BOOL bProcessToolBar)
{
	ASSERT(pMenu != NULL); if(pMenu == NULL) return;

	if(m_bMiniMode == FALSE) return; // Mini mode is disabled

	const DWORD dwIDCount = 13;
	const UINT vIDs[dwIDCount] = {
		ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_SAVEAS, ID_FILE_CLOSE,
		ID_FILE_SHOWDBINFO,
		ID_FILE_CHANGELANGUAGE,
		ID_PWLIST_SAVEATTACH, ID_PWLIST_AUTOTYPE,
		ID_EXTRAS_TANWIZARD, ID_EXTRAS_REPAIRDB,
		ID_SAFE_OPTIONS, ID_EXTRAS_PLUGINMGR,
		ID_INFO_CHKFORUPD
	};

	for(DWORD i = 0; i < dwIDCount; ++i)
		NewGUI_RemoveMenuCommand(pMenu, vIDs[i]);

	NewGUI_RemoveInvalidSeparators(pMenu, TRUE);

	if(bRemoveAccels == TRUE)
	{
		const int nAccelCount = CopyAcceleratorTable(m_hAccel, NULL, 0);
		std::vector<ACCEL> vCurAccels(static_cast<size_t>(nAccelCount));
		VERIFY(CopyAcceleratorTable(m_hAccel, &vCurAccels[0], nAccelCount) == nAccelCount);

		std::vector<ACCEL> vNewAccels;
		for(int iAccel = 0; iAccel < nAccelCount; ++iAccel)
		{
			ACCEL *pCur = &vCurAccels[iAccel];
			bool bValid = true;
			for(DWORD dwInvalid = 0; dwInvalid < dwIDCount; ++dwInvalid)
			{
				if(pCur->cmd == vIDs[dwInvalid]) bValid = false;
			}

			if(bValid) vNewAccels.push_back(*pCur);
		}

		VERIFY(DestroyAcceleratorTable(m_hAccel));
		m_hAccel = CreateAcceleratorTable(&vNewAccels[0],
			static_cast<int>(vNewAccels.size()));
	}

	if(bProcessToolBar == TRUE)
	{
		long lToRight = 0;
		NewGUI_DisableHideWnd(&m_btnTbNew);
		NewGUI_DisableHideWnd(&m_btnTbOpen);
		lToRight -= NewGUI_GetWndBasePosDiff(&m_btnTbNew, &m_btnTbSave).cx;
		NewGUI_MoveWnd(&m_btnTbSave, lToRight, 0, this);
		NewGUI_MoveWnd(GetDlgItem(IDC_STATIC_TBSEP0), lToRight, 0, this);
		NewGUI_MoveWnd(&m_btnTbAddEntry, lToRight, 0, this);
		NewGUI_MoveWnd(&m_btnTbEditEntry, lToRight, 0, this);
		NewGUI_MoveWnd(&m_btnTbDeleteEntry, lToRight, 0, this);
		NewGUI_MoveWnd(GetDlgItem(IDC_STATIC_TBSEP1), lToRight, 0, this);
		NewGUI_MoveWnd(&m_btnTbCopyUser, lToRight, 0, this);
		NewGUI_MoveWnd(&m_btnTbCopyPw, lToRight, 0, this);
		NewGUI_MoveWnd(GetDlgItem(IDC_STATIC_TBSEP2), lToRight, 0, this);
		NewGUI_MoveWnd(&m_btnTbFind, lToRight, 0, this);
		NewGUI_MoveWnd(GetDlgItem(IDC_STATIC_TBSEP3), lToRight, 0, this);
		NewGUI_MoveWnd(&m_btnTbLock, lToRight, 0, this);
		NewGUI_MoveWnd(GetDlgItem(IDC_STATIC_TBSEP4), lToRight, 0, this);
		NewGUI_MoveWnd(&m_btnTbAbout, lToRight, 0, this);
		NewGUI_MoveWnd(GetDlgItem(IDC_STATIC_TBSEP5), lToRight, 0, this);
		NewGUI_MoveWnd(&m_cQuickFind, lToRight, 0, this);
	}
}

void CPwSafeDlg::RegisterRestoreHotKey(BOOL bRegister)
{
	if((bRegister == TRUE) && (m_bRestoreHotKeyRegistered == FALSE))
		m_bRestoreHotKeyRegistered = ::RegisterHotKey(this->m_hWnd,
			HOTKEYID_RESTORE, MOD_ALT | MOD_CONTROL, 'K');
	else if((bRegister == FALSE) && (m_bRestoreHotKeyRegistered != FALSE))
		::UnregisterHotKey(this->m_hWnd, HOTKEYID_RESTORE);
}

void CPwSafeDlg::OnQuickFindSelChange()
{
	if(m_bBlockQuickFindSelChange == TRUE) return;
	m_bBlockQuickFindSelChange = TRUE;

	const int iSel = m_cQuickFind.GetCurSel();
	if(iSel == CB_ERR)
	{
		m_bBlockQuickFindSelChange = FALSE;
		return;
	}

	m_cQuickFind.GetLBText(iSel, m_strQuickFind);

	_DoQuickFind(m_strQuickFind);

	if(m_strQuickFind != HCMBX_SEPARATOR)
		m_cQuickFind.SelectString(-1, m_strQuickFind);
	else
		m_cQuickFind.SetCurSel(-1);

	m_bBlockQuickFindSelChange = FALSE;
}

void CPwSafeDlg::DropToBackgroundIfOptionEnabled(bool bForceDrop)
{
	if((m_bDropToBackOnCopy == TRUE) || bForceDrop)
		SafeActivateNextWindow(this->m_hWnd);
}

void CPwSafeDlg::OnInfoHelpSelectHelpSource()
{
	CHelpSourceDlg dlg;
	NewGUI_DoModal(&dlg);
}

void CPwSafeDlg::_PostUseTANEntry(DWORD dwListIndex, DWORD dwEntryIndex)
{
	if(dwEntryIndex == DWORD_MAX) { ASSERT(FALSE); return; }

	PW_ENTRY* p = m_mgr.GetEntry(dwEntryIndex);
	if(p == NULL) { ASSERT(FALSE); return; }

	if(CPwUtil::IsTANEntry(p) == TRUE) // If it is a TAN entry, expire it
	{
		_GetCurrentPwTime(&p->tExpire);
		p->tLastAccess = p->tExpire;
		p->tLastMod = p->tExpire;
		m_bModified = TRUE;

		if(m_bDeleteTANsAfterUse == TRUE)
		{
			if(m_bBackupEntries != FALSE)
			{
				m_mgr.UnlockEntryPassword(p);
				m_mgr.BackupEntry(p, NULL);
				m_mgr.LockEntryPassword(p);
			}

			VERIFY(m_mgr.DeleteEntry(dwEntryIndex)); // Delete from password manager
			VERIFY(m_cList.DeleteItem(static_cast<int>(dwListIndex))); // Delete from GUI
		}
	}
	else { ASSERT(FALSE); }
}

void CPwSafeDlg::SetLastMasterKeyDlg(HWND h)
{
	g_hLastMasterKeyDlg = h;
}

void CPwSafeDlg::_PreDatabaseWrite()
{
	m_mgr.SetTransactedFileWrites(m_bUseTransactedFileWrites);
}

void CPwSafeDlg::_UpdateCachedGroupIDs()
{
	m_dwCachedBackupGroupID = m_mgr.GetGroupId(PWS_BACKUPGROUP);
	m_dwCachedBackupSrcGroupID = m_mgr.GetGroupId(PWS_BACKUPGROUP_SRC);
}

void CPwSafeDlg::_SetLVItemParam(LV_ITEM* pItem, const PW_ENTRY* pe)
{
	if((pItem == NULL) || (pe == NULL)) { ASSERT(FALSE); return; }

	pItem->lParam = 0;

	if((pe->uGroupId == m_dwCachedBackupGroupID) || (pe->uGroupId == m_dwCachedBackupSrcGroupID))
		pItem->lParam |= CLCIF_HIGHLIGHT_GREEN;
}

bool CPwSafeDlg::_IsSearchGroup()
{
	if((m_bFileOpen == FALSE) || (m_bLocked != FALSE)) return false;

	const DWORD dwSelGroup = GetSelectedGroupId();
	const DWORD dwSearchGroup = m_mgr.GetGroupId(PWS_SEARCHGROUP);
	return ((dwSelGroup == dwSearchGroup) && (dwSearchGroup != DWORD_MAX));
}

bool CPwSafeDlg::_IsDisplayingDialog()
{
	ASSERT(m_iDisplayDialog >= 0);
	return ((m_iDisplayDialog > 0) || (CGlobalWindowManager::GetCount() > 0));
}

void CPwSafeDlg::_SetDisplayDialog(bool bDisplay)
{
	if(bDisplay) ++m_iDisplayDialog;
	else --m_iDisplayDialog;

	ASSERT(m_iDisplayDialog >= 0);
}

bool CPwSafeDlg::_IsDisplayingMenu()
{
	ASSERT(m_iDisplayMenu >= 0);
	return (m_iDisplayMenu > 0);
}

void CPwSafeDlg::_SetDisplayMenu(bool bDisplay)
{
	if(bDisplay) ++m_iDisplayMenu;
	else --m_iDisplayMenu;

	ASSERT(m_iDisplayMenu >= 0);
}

void CPwSafeDlg::_AssertStateStacksEmpty()
{
	ASSERT(m_iDisplayDialog == 0);
	ASSERT(m_iDisplayMenu == 0);
	ASSERT(m_iUIHintsBlocked == 0);
}

void CPwSafeDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
	static bool bInRedir = false;
	if(((nState == WA_ACTIVE) || (nState == WA_CLICKACTIVE)) && !bInRedir)
	{
		HWND hRedir = CGlobalWindowManager::GetTopWindow();
		if((hRedir != NULL) && (hRedir != this->m_hWnd))
		{
			const bool bSubValid = ((::IsWindow(hRedir) != FALSE) &&
				(::IsWindowVisible(hRedir) != FALSE));
			const bool bMeEn = (this->IsWindowEnabled() != FALSE);

			// When just closing a sub-dialog (and thus the parent dialog
			// gets activated), bSubValid == false and bMeEn == true
			if(bSubValid && !bMeEn) // Not when closing a dialog
			{
				bInRedir = true;
				::SetActiveWindow(hRedir);
				bInRedir = false;
				return;
			}
		}
	}

	CDialog::OnActivate(nState, pWndOther, bMinimized);
}

LRESULT CPwSafeDlg::OnDwmSendIconicThumbnail(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(wParam);

	CDwmUtil::SetIconicThumbnail(this->m_hWnd, lParam);
	return 0;
}

LRESULT CPwSafeDlg::OnDwmSendIconicLivePreviewBitmap(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);

	CDwmUtil::SetIconicPreview(this->m_hWnd, m_szLastContent);
	return 0;
}

void CPwSafeDlg::_UpdatePeekPreview()
{
	bool b = ((IsIconic() == FALSE) || (m_bLocked == FALSE));
	CDwmUtil::EnableWindowPeekPreview(this->m_hWnd, b);
}

void CPwSafeDlg::_UIBlockHints(bool bBlock)
{
	bool bUpdateState;

	if(bBlock)
	{
		bUpdateState = (m_iUIHintsBlocked == 0);
		++m_iUIHintsBlocked;
	}
	else
	{
		if(m_iUIHintsBlocked <= 0) { ASSERT(FALSE); return; }
		--m_iUIHintsBlocked;
		bUpdateState = (m_iUIHintsBlocked == 0);
	}

	if(bUpdateState)
	{
		const bool bBlocked = (m_iUIHintsBlocked > 0);

		m_btnTbLock.ActivateTooltip(bBlocked ? FALSE : TRUE);

		if(bBlocked)
		{
			// Prevent a drawing bug that can occur when minimizing
			// the main window and viewing it using peek preview
			CToolTipCtrl& tt = m_btnTbLock.GetToolTipCtrl();
			if(tt.m_hWnd != NULL)
			{
				tt.Pop();
				tt.Update();
			}
		}
	}
}

void CPwSafeDlg::OnContextMenu(CWnd* pWnd, CPoint point)
{
	if(pWnd == &m_cList)
	{
		NotifyUserActivity();

		if(m_bFileOpen == FALSE) return;
		if(m_pPwListTrackableMenu == NULL) { ASSERT(FALSE); return; }

		_SetDisplayDialog(true);
		if(_CallPlugins(KPM_PWLIST_CONTEXTMENU, (LPARAM)m_pPwListTrackableMenu,
			0) == FALSE)
		{
			_SetDisplayDialog(false);
			return;
		}

		// When pressing the Apps key, point = (0, 0), thus we
		// get the cursor position ourselves
		POINT pt;
		ZeroMemory(&pt, sizeof(POINT));
		GetCursorPos(&pt);

		_SetDisplayMenu(true);
		m_pPwListTrackableMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
			pt.x, pt.y, this);
		_SetDisplayMenu(false);

		_UpdateToolBar();
		_SetDisplayDialog(false);
	}
	else if(pWnd == &m_cGroups)
	{
		NotifyUserActivity();

		if(m_bFileOpen == FALSE) return;
		if(m_pGroupListTrackableMenu == NULL) { ASSERT(FALSE); return; }

		_SetDisplayDialog(true);
		if(_CallPlugins(KPM_GROUPLIST_CONTEXTMENU, (LPARAM)m_pGroupListTrackableMenu,
			0) == FALSE)
		{
			_SetDisplayDialog(false);
			return;
		}

		// When pressing the Apps key, point = (0, 0), thus we
		// get the cursor position ourselves
		POINT pt;
		ZeroMemory(&pt, sizeof(POINT));
		GetCursorPos(&pt);

		POINT ptClient = pt;
		m_cGroups.ScreenToClient(&ptClient);
		// HTREEITEM hItem = m_cGroups.HitTest(CPoint(ptClient), &uFlags);
		// if(uFlags & (TVHT_ONITEM | TVHT_ONITEMINDENT)) m_cGroups.SelectItem(hItem);
		// else m_cGroups.SelectItem(NULL);
		CPoint cptClient(ptClient);
		UINT uFlags = 0;
		HTREEITEM h = m_cGroups.HitTest(cptClient, &uFlags);
		if(h != NULL) m_cGroups.SelectItem(h);

		_SetDisplayMenu(true);
		m_pGroupListTrackableMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
			pt.x, pt.y, this);
		_SetDisplayMenu(false);

		_UpdateToolBar();
		_SetDisplayDialog(false);
	}
	else if(pWnd == &m_reEntryView)
	{
		// When pressing the Apps key, point = (0, 0), thus we
		// get the cursor position ourselves
		POINT pt;
		ZeroMemory(&pt, sizeof(POINT));
		GetCursorPos(&pt);

		if(m_pEntryViewTrackableMenu != NULL)
		{
			_SetDisplayMenu(true);
			m_pEntryViewTrackableMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
				pt.x, pt.y, this);
			_SetDisplayMenu(false);
		}
		else { ASSERT(FALSE); }
	}
	else { CDialog::OnContextMenu(pWnd, point); }
}

void CPwSafeDlg::_ReportAttachError(int e, LPCTSTR lpFile)
{
	CString str;
	if((lpFile != NULL) && (lpFile[0] != _T('\0')))
	{
		str += TRL("Failed to attach file:");
		str += _T("\r\n\r\n");
		str += lpFile;
		str += _T("\r\n\r\n");
	}

	str += CPwUtil::FormatError(e, PWFF_NO_INTRO | PWFF_MAIN_TEXT_ONLY);

	MessageBox(str, PWM_PRODUCT_NAME_SHORT, MB_OK | MB_ICONWARNING);
}
