/*
 * Copyright (c) 2003-2020 Rony Shapiro <ronys@pwsafe.org>.
 * All rights reserved. Use of the code is allowed under the
 * Artistic License 2.0 terms, as specified in the LICENSE file
 * distributed with this code, or available from
 * http://www.opensource.org/licenses/artistic-license-2.0.php
 */

/** \file PasswordSafeFrame.cpp
*
*/

// Generated by DialogBlocks, Wed 14 Jan 2009 10:24:11 PM IST

// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>

#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#ifdef __WXMSW__
#include <wx/msw/msvcrt.h>
#endif

#include <wx/clipbrd.h>
#include <wx/filename.h>
#include <wx/fontdlg.h>

#include "core/core.h"
#include "core/PWScore.h"
#include "core/PWSdirs.h"
#include "core/PWSprefs.h"
#include "core/XML/XMLDefs.h"  // Required if testing "USE_XML_LIBRARY"
#include "os/file.h"
#include "os/sleep.h"

#include "graphics/cpane.xpm"

#include "AboutDlg.h"
#include "Clipboard.h"
#include "CompareDlg.h"
#include "DragBarCtrl.h"
#include "ExportTextWarningDlg.h"
#include "GridCtrl.h"
#include "GridShortcutsValidator.h"
#include "GridTable.h"
#include "GuiInfo.h"
#include "ImportTextDlg.h"
#include "ImportXmlDlg.h"
#include "MergeDlg.h"
#include "PasswordSafeFrame.h"
#include "PasswordSafeSearch.h"
#include "PropertiesDlg.h"
#include "PWSafeApp.h"
#include "QRCodeDlg.h"
#include "SafeCombinationPromptDlg.h"
#include "SafeCombinationSetupDlg.h"
#include "SelectionCriteria.h"
#include "StatusBar.h"
#include "SyncWizard.h"
#include "SystemTray.h"
#include "SystemTrayMenuId.h"
#include "ToolbarButtons.h"
#include "TreeCtrl.h"
#include "ViewReportDlg.h"
#include "wxUtilities.h"

#include <algorithm>

////@begin XPM images
////@end XPM images

using pws_os::CUUID;

using std::get;
using std::make_tuple;

/*!
 * PasswordSafeFrame type definition
 */

IMPLEMENT_CLASS( PasswordSafeFrame, wxFrame )

DEFINE_EVENT_TYPE(wxEVT_GUI_DB_PREFS_CHANGE)

/*!
 * PasswordSafeFrame event table definition
 */

BEGIN_EVENT_TABLE( PasswordSafeFrame, wxFrame )

  EVT_CHAR_HOOK(                        PasswordSafeFrame::OnChar                        )
  EVT_CLOSE(                            PasswordSafeFrame::OnCloseWindow                 )
  EVT_ICONIZE(                          PasswordSafeFrame::OnIconize                     )

  ////////////////////////////////////////////////////////////////////////////////////////
  // Menu: "File"
  ////////////////////////////////////////////////////////////////////////////////////////

  // Connect event handlers
  EVT_MENU( wxID_NEW,                   PasswordSafeFrame::OnNewClick                    )
  EVT_MENU( wxID_OPEN,                  PasswordSafeFrame::OnOpenClick                   )
  EVT_MENU( wxID_CLOSE,                 PasswordSafeFrame::OnCloseClick                  )
  EVT_MENU( ID_LOCK_SAFE,               PasswordSafeFrame::OnLockSafe                    )
  EVT_MENU( ID_UNLOCK_SAFE,             PasswordSafeFrame::OnUnlockSafe                  )
  EVT_MENU( ID_MENU_CLEAR_MRU,          PasswordSafeFrame::OnClearRecentHistory          )
  EVT_MENU( wxID_SAVE,                  PasswordSafeFrame::OnSaveClick                   )
  EVT_MENU( wxID_SAVEAS,                PasswordSafeFrame::OnSaveAsClick                 )

  EVT_MENU( ID_EXPORT2OLD1XFORMAT,      PasswordSafeFrame::OnExportVx                    )
  EVT_MENU( ID_EXPORT2V2FORMAT,         PasswordSafeFrame::OnExportVx                    )
  EVT_MENU( ID_EXPORT2V4FORMAT,         PasswordSafeFrame::OnExportVx                    )
  EVT_MENU( ID_EXPORT2PLAINTEXT,        PasswordSafeFrame::OnExportPlainText             )
  EVT_MENU( ID_EXPORT2XML,              PasswordSafeFrame::OnExportXml                   )

  EVT_MENU( ID_IMPORT_PLAINTEXT,        PasswordSafeFrame::OnImportText                  )
  EVT_MENU( ID_IMPORT_XML,              PasswordSafeFrame::OnImportXML                   )
  EVT_MENU( ID_IMPORT_KEEPASS,          PasswordSafeFrame::OnImportKeePass               )

  EVT_MENU( ID_MERGE,                   PasswordSafeFrame::OnMergeAnotherSafe            )
  EVT_MENU( ID_COMPARE,                 PasswordSafeFrame::OnCompare                     )
  EVT_MENU( ID_SYNCHRONIZE,             PasswordSafeFrame::OnSynchronize                 )
  EVT_MENU( wxID_PROPERTIES,            PasswordSafeFrame::OnPropertiesClick             )
  EVT_MENU( wxID_EXIT,                  PasswordSafeFrame::OnExitClick                   )

  // Update of menu items
  EVT_UPDATE_UI( wxID_CLOSE,            PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_LOCK_SAFE,          PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_UNLOCK_SAFE,        PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_MENU_CLEAR_MRU,     PasswordSafeFrame::OnUpdateClearRecentDBHistory  )
  EVT_UPDATE_UI( wxID_SAVE,             PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( wxID_SAVEAS,           PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_EXPORTMENU,         PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_IMPORTMENU,         PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_MERGE,              PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_COMPARE,            PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_SYNCHRONIZE,        PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( wxID_PROPERTIES,       PasswordSafeFrame::OnUpdateUI                    )
  
  ////////////////////////////////////////////////////////////////////////////////////////
  // Menu: "Edit"
  ////////////////////////////////////////////////////////////////////////////////////////
  
  // Connect event handlers
  EVT_MENU( wxID_ADD,                   PasswordSafeFrame::OnAddClick                    )
  EVT_MENU( ID_EDIT,                    PasswordSafeFrame::OnEditClick                   )
  EVT_MENU( wxID_DELETE,                PasswordSafeFrame::OnDeleteClick                 )
  EVT_MENU( wxID_FIND,                  PasswordSafeFrame::OnFindClick                   )
  EVT_MENU( ID_DUPLICATEENTRY,          PasswordSafeFrame::OnDuplicateEntry              )
  EVT_MENU( ID_PROTECT,                 PasswordSafeFrame::OnProtectUnprotectClick       )

  EVT_MENU( wxID_UNDO,                  PasswordSafeFrame::OnUndo                        )
  EVT_MENU( wxID_REDO,                  PasswordSafeFrame::OnRedo                        )
  EVT_MENU( ID_CLEARCLIPBOARD,          PasswordSafeFrame::OnClearClipboardClick         )
  EVT_MENU( ID_COPYPASSWORD,            PasswordSafeFrame::OnCopyPasswordClick           )
  EVT_MENU( ID_COPYUSERNAME,            PasswordSafeFrame::OnCopyUsernameClick           )
  EVT_MENU( ID_COPYNOTESFLD,            PasswordSafeFrame::OnCopyNotesFieldClick         )
  EVT_MENU( ID_COPYURL,                 PasswordSafeFrame::OnCopyUrlClick                )
  EVT_MENU( ID_BROWSEURL,               PasswordSafeFrame::OnBrowseUrl                   )
  EVT_MENU( ID_AUTOTYPE,                PasswordSafeFrame::OnAutoType                    )
  EVT_MENU( ID_VIEWATTACHMENT,          PasswordSafeFrame::OnViewAttachment              )
  EVT_MENU( ID_GOTOBASEENTRY,           PasswordSafeFrame::OnGotoBase                    )

  // Update menu items
  EVT_UPDATE_UI( wxID_ADD,              PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_EDIT,               PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( wxID_DELETE,           PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( wxID_FIND,             PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_RENAME,             PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_DUPLICATEENTRY,     PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_PROTECT,            PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_ADDGROUP,           PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( wxID_UNDO,             PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( wxID_REDO,             PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_CLEARCLIPBOARD,     PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_COPYPASSWORD,       PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_COPYUSERNAME,       PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_COPYNOTESFLD,       PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_COPYURL,            PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_BROWSEURL,          PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_AUTOTYPE,           PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_VIEWATTACHMENT,     PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_GOTOBASEENTRY,      PasswordSafeFrame::OnUpdateUI                    )

  ////////////////////////////////////////////////////////////////////////////////////////
  // Menu: "View"
  ////////////////////////////////////////////////////////////////////////////////////////

  // Connect event handlers
  EVT_MENU( ID_LIST_VIEW,               PasswordSafeFrame::OnListViewClick               )
  EVT_MENU( ID_TREE_VIEW,               PasswordSafeFrame::OnTreeViewClick               )
  EVT_MENU( ID_SHOWHIDE_TOOLBAR,        PasswordSafeFrame::OnShowHideToolBar             )
  EVT_MENU( ID_TOOLBAR_NEW,             PasswordSafeFrame::OnChangeToolbarType           )
  EVT_MENU( ID_TOOLBAR_CLASSIC,         PasswordSafeFrame::OnChangeToolbarType           )
  EVT_MENU( ID_SHOWHIDE_DRAGBAR,        PasswordSafeFrame::OnShowHideDragBar             )
  EVT_MENU( ID_EXPANDALL,               PasswordSafeFrame::OnExpandAll                   )
  EVT_MENU( ID_COLLAPSEALL,             PasswordSafeFrame::OnCollapseAll                 )
  EVT_MENU( ID_SHOWHIDE_UNSAVED,        PasswordSafeFrame::OnShowUnsavedEntriesClick     )
  EVT_MENU( ID_SHOW_ALL_EXPIRY,         PasswordSafeFrame::OnShowAllExpiryClick          )
  EVT_MENU( ID_SHOW_LAST_FIND_RESULTS,  PasswordSafeFrame::OnShowLastFindClick           )
  EVT_MENU( ID_EDITFILTER,              PasswordSafeFrame::OnEditFilter                  )
  EVT_MENU( ID_APPLYFILTER,             PasswordSafeFrame::OnApplyFilter                 )
  EVT_MENU( ID_MANAGEFILTERS,           PasswordSafeFrame::OnManageFilters               )
  EVT_MENU( ID_CHANGETREEFONT,          PasswordSafeFrame::OnChangeTreeFont              )
  EVT_MENU( ID_CHANGEADDEDITFONT,       PasswordSafeFrame::OnChangeAddEditFont           )
  EVT_MENU( ID_CHANGEPSWDFONT,          PasswordSafeFrame::OnChangePasswordFont          )
  EVT_MENU( ID_CHANGENOTESFONT,         PasswordSafeFrame::OnChangeNotesFont             )
  EVT_MENU( ID_CHANGEVKBFONT,           PasswordSafeFrame::OnChangeVirtualKeyboardFont   )
  // TODO: ID_REPORT_COMPARE
  // TODO: ID_REPORT_FIND
  // TODO: ID_REPORT_IMPORTTEXT
  // TODO: ID_REPORT_IMPORTXML
  // TODO: ID_REPORT_MERGE
  // TODO: ID_REPORT_VALIDATE

  // Update menu items
  EVT_UPDATE_UI( ID_LIST_VIEW,          PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_TREE_VIEW,          PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_SHOWHIDE_TOOLBAR,   PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_SHOWHIDE_DRAGBAR,   PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_SHOWHIDE_UNSAVED,   PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_SHOW_ALL_EXPIRY,    PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_SHOW_LAST_FIND_RESULTS, PasswordSafeFrame::OnUpdateUI                )
  EVT_UPDATE_UI( ID_COLLAPSEALL,        PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_EXPANDALL,          PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_FILTERMENU,         PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_CUSTOMIZETOOLBAR,   PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_REPORTSMENU,        PasswordSafeFrame::OnUpdateUI                    )

  ////////////////////////////////////////////////////////////////////////////////////////
  // Menu: "Manage"
  ////////////////////////////////////////////////////////////////////////////////////////

  // Connect event handlers
  EVT_MENU( ID_CHANGECOMBO,             PasswordSafeFrame::OnChangePasswordClick         )
  EVT_MENU( ID_BACKUP,                  PasswordSafeFrame::OnBackupSafe                  )
  EVT_MENU( ID_RESTORE,                 PasswordSafeFrame::OnRestoreSafe                 )
  EVT_MENU( wxID_PREFERENCES,           PasswordSafeFrame::OnPreferencesClick            )
  EVT_MENU( ID_PWDPOLSM,                PasswordSafeFrame::OnPwdPolsMClick               )
  EVT_MENU( ID_GENERATEPASSWORD,        PasswordSafeFrame::OnGeneratePassword            )
#ifndef NO_YUBI
  EVT_MENU( ID_YUBIKEY_MNG,             PasswordSafeFrame::OnYubikeyMngClick             )
#endif
  EVT_MENU_RANGE( ID_LANGUAGE_BEGIN, ID_LANGUAGE_END, PasswordSafeFrame::OnLanguageClick )

  // Update menu elements
  EVT_UPDATE_UI( ID_CHANGECOMBO,        PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_BACKUP,             PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_RESTORE,            PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_PWDPOLSM,           PasswordSafeFrame::OnUpdateUI                    )
#ifndef NO_YUBI
  EVT_UPDATE_UI( ID_YUBIKEY_MNG,        PasswordSafeFrame::OnUpdateUI                    )
#endif

  ////////////////////////////////////////////////////////////////////////////////////////
  // Menu: "Help"
  ////////////////////////////////////////////////////////////////////////////////////////

  // Connect event handlers
  EVT_MENU( ID_VISITWEBSITE,            PasswordSafeFrame::OnVisitWebsite                )
  EVT_MENU( wxID_ABOUT,                 PasswordSafeFrame::OnAboutClick                  )

  ////////////////////////////////////////////////////////////////////////////////////////
  // Context Menu
  ////////////////////////////////////////////////////////////////////////////////////////

  // Connect event handlers
  EVT_MENU( ID_PASSWORDSUBSET,          PasswordSafeFrame::OnPasswordSubset              )
  EVT_MENU( ID_PASSWORDQRCODE,          PasswordSafeFrame::OnPasswordQRCode              )
  EVT_MENU( ID_COPYEMAIL,               PasswordSafeFrame::OnCopyEmailClick              )
  EVT_MENU( ID_COPYRUNCOMMAND,          PasswordSafeFrame::OnCopyRunCmd                  )
  EVT_MENU( ID_BROWSEURLPLUS,           PasswordSafeFrame::OnBrowseUrlAndAutotype        )
  EVT_MENU( ID_SENDEMAIL,               PasswordSafeFrame::OnSendEmail                   )
  EVT_MENU( ID_RUNCOMMAND,              PasswordSafeFrame::OnRunCommand                  )
  EVT_MENU( ID_CREATESHORTCUT,          PasswordSafeFrame::OnCreateShortcut              )
  EVT_MENU( ID_EDITBASEENTRY,           PasswordSafeFrame::OnEditBase                    )

  // Update menu elements
  EVT_UPDATE_UI( ID_PASSWORDSUBSET,     PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_COPYEMAIL,          PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_BROWSEURLPLUS,      PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_SENDEMAIL,          PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_RUNCOMMAND,         PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_COPYRUNCOMMAND,     PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_CREATESHORTCUT,     PasswordSafeFrame::OnUpdateUI                    )
  EVT_UPDATE_UI( ID_EDITBASEENTRY,      PasswordSafeFrame::OnUpdateUI                    )

  ////////////////////////////////////////////////////////////////////////////////////////
  // Search Control
  ////////////////////////////////////////////////////////////////////////////////////////

  // Connect event handlers
  EVT_MENU( ID_EDITMENU_FIND_NEXT,      PasswordSafeFrame::OnFindNext                    )
  EVT_MENU( ID_EDITMENU_FIND_PREVIOUS,  PasswordSafeFrame::OnFindPrevious                )

END_EVENT_TABLE()

/*!
 * PasswordSafeFrame constructors
 */

PasswordSafeFrame::PasswordSafeFrame(PWScore &core)
: m_core(core), m_currentView(ViewType::GRID), m_search(nullptr), m_sysTray(new SystemTray(this)),
  m_exitFromMenu(false), m_bRestoredDBUnsaved(false),
  m_RUEList(core), m_guiInfo(new GuiInfo), m_bTSUpdated(false), m_savedDBPrefs(wxEmptyString),
  m_CurrentPredefinedFilter(NONE), m_bFilterActive(false), m_InitialTreeDisplayStatusAtOpen(true),
  m_LastClipboardAction(wxEmptyString), m_LastAction(CItem::FieldType::START)
{
  Init();
}

PasswordSafeFrame::PasswordSafeFrame(wxWindow* parent, PWScore &core,
                                     wxWindowID id, const wxString& caption,
                                     const wxPoint& pos, const wxSize& size,
                                     long style)
  : m_core(core), m_currentView(ViewType::GRID), m_search(nullptr), m_sysTray(new SystemTray(this)),
    m_exitFromMenu(false), m_bRestoredDBUnsaved(false),
    m_RUEList(core), m_guiInfo(new GuiInfo), m_bTSUpdated(false), m_savedDBPrefs(wxEmptyString),
    m_CurrentPredefinedFilter(NONE), m_bFilterActive(false), m_InitialTreeDisplayStatusAtOpen(true),
    m_LastClipboardAction(wxEmptyString), m_LastAction(CItem::FieldType::START)
{
  Init();

  if (PWSprefs::GetInstance()->GetPref(PWSprefs::AlwaysOnTop)) {
    style |= wxSTAY_ON_TOP;
  }

  RegisterLanguageMenuItems();
  Create( parent, id, caption, pos, size, style );

  // Register all observers for notifications at the core
  m_core.RegisterObserver(this);

  if (IsTreeView()) {
    m_core.RegisterObserver(m_tree);
  }
  else {
    m_core.RegisterObserver(m_grid);
  }
}

/*!
 * PasswordSafeFrame creator
 */

bool PasswordSafeFrame::Create( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
  ////@begin PasswordSafeFrame creation
  wxFrame::Create( parent, id, caption, pos, size, style );

  CreateMenubar();
  CreateControls();
  SetIcon(GetIconResource(L"graphics/cpane.xpm"));
  Centre();
////@end PasswordSafeFrame creation
  m_search = new PasswordSafeSearch(this);
  CreateMainToolbar();
  CreateDragBar();
  CreateStatusBar();
  return true;
}

void PasswordSafeFrame::CreateDragBar()
{
  wxSizer* origSizer = GetSizer();

  wxASSERT(origSizer);
  wxASSERT(origSizer->IsKindOf(wxBoxSizer(wxVERTICAL).GetClassInfo()));
  wxASSERT(static_cast<wxBoxSizer*>(origSizer)->GetOrientation() == wxVERTICAL);

  DragBarCtrl* dragbar = new DragBarCtrl(this);
  origSizer->Insert(0, dragbar, 0, wxEXPAND);

  const bool bShow = PWSprefs::GetInstance()->GetPref(PWSprefs::ShowDragbar);
  if (!bShow) {
    dragbar->Hide();
  }
  GetMenuBar()->Check(ID_SHOWHIDE_DRAGBAR, bShow);
}

void PasswordSafeFrame::CreateStatusBar()
{
  m_statusBar = new StatusBar(this, ID_STATUSBAR, wxST_SIZEGRIP|wxNO_BORDER);
  m_statusBar->Setup();
  SetStatusBar(m_statusBar);
}

/*!
 * PasswordSafeFrame destructor
 */

PasswordSafeFrame::~PasswordSafeFrame()
{
////@begin PasswordSafeFrame destruction
////@end PasswordSafeFrame destruction
  delete m_search;
  m_search = nullptr;

  delete m_sysTray;
  m_sysTray = nullptr;

  delete m_guiInfo;
  m_guiInfo = nullptr;

  m_core.ClearDBData();
  m_core.UnregisterObserver(this);
}

/*!
 * Member initialisation
 */

void PasswordSafeFrame::Init()
{
  m_currentView = (PWSprefs::GetInstance()->GetPref(PWSprefs::LastView) == _T("list")) ? ViewType::GRID : ViewType::TREE;

  m_RUEList.SetMax(PWSprefs::GetInstance()->PWSprefs::MaxREItems);
////@begin PasswordSafeFrame member initialisation
  m_grid = nullptr;
  m_tree = nullptr;
  m_statusBar = nullptr;
////@end PasswordSafeFrame member initialisation
}

/**
 Register menu items for available languages
*/
void PasswordSafeFrame::RegisterLanguageMenuItems() {
  // Using unlocalized language names here, it will be translated in AddLanguageMenu
  AddLanguage( ID_LANGUAGE_CHINESE,   wxLANGUAGE_CHINESE, L"Chinese"  );  /* code: 'zh' */
  AddLanguage( ID_LANGUAGE_DANISH,    wxLANGUAGE_DANISH,  L"Danish"   );  /* code: 'da' */
  AddLanguage( ID_LANGUAGE_DUTCH,     wxLANGUAGE_DUTCH,   L"Dutch"    );  /* code: 'nl' */
  AddLanguage( ID_LANGUAGE_ENGLISH,   wxLANGUAGE_ENGLISH, L"English"  );  /* code: 'en' */
  AddLanguage( ID_LANGUAGE_FRENCH,    wxLANGUAGE_FRENCH,  L"French"   );  /* code: 'fr' */
  AddLanguage( ID_LANGUAGE_GERMAN,    wxLANGUAGE_GERMAN,  L"German"   );  /* code: 'de' */
  AddLanguage( ID_LANGUAGE_HUNGARIAN, wxLANGUAGE_HUNGARIAN, L"Hungarian"  );  /* code: 'hu' */
  AddLanguage( ID_LANGUAGE_ITALIAN,   wxLANGUAGE_ITALIAN, L"Italian"  );  /* code: 'it' */
  AddLanguage( ID_LANGUAGE_KOREAN,    wxLANGUAGE_KOREAN,  L"Korean"   );  /* code: 'ko' */
  AddLanguage( ID_LANGUAGE_POLISH,    wxLANGUAGE_POLISH,  L"Polish"   );  /* code: 'pl' */
  AddLanguage( ID_LANGUAGE_RUSSIAN,   wxLANGUAGE_RUSSIAN, L"Russian"  );  /* code: 'ru' */
  AddLanguage( ID_LANGUAGE_SPANISH,   wxLANGUAGE_SPANISH, L"Spanish"  );  /* code: 'es' */
  AddLanguage( ID_LANGUAGE_SWEDISH,   wxLANGUAGE_SWEDISH, L"Swedish"  );  /* code: 'sv' */

  m_selectedLanguage = ID_LANGUAGE_ENGLISH;
  wxLanguage current_language = wxGetApp().GetSelectedLanguage();
  for (auto &item : m_languages) {
    if (get<0>(item.second) == current_language) {
      m_selectedLanguage = item.first;
      pws_os::Trace(L"Found user-preferred language: menu id= %d, lang id= %d\n", m_selectedLanguage, current_language);
    }
    // Mark whether language can be activated
    get<2>(item.second) = wxGetApp().ActivateLanguage(get<0>(item.second), true);
  }
  // Don't activate language here!
  // 1st - selected language already activated
  // 2nd - when we called from constructor, its caption parameter points
  //       to string located inside previously selected global translation object
  //       (leads to crash in Release, but works in Debug)
  pws_os::Trace(L"Selected language: menu id= %d\n", m_selectedLanguage);
}

/**
 * Menu bar creation for PasswordSafeFrame
 */

void PasswordSafeFrame::CreateMenubar()
{
  auto menuBar = GetMenuBar();

  // Create a new menu bar if none has been created so far
  if (menuBar == nullptr) {
    menuBar = new wxMenuBar;
  }

  menuBar->Freeze();

  // Removing all existing menu items is necessary for language switching
  while (menuBar->GetMenuCount()) {
    delete menuBar->Remove(0);
  }

  // Create all menu items
  // Recreating the menu items updates also their translation
////@begin PasswordSafeFrame content construction

  /////////////////////////////////////////////////////////////////////////////
  // Menu: "File"
  /////////////////////////////////////////////////////////////////////////////

  auto menuFile = new wxMenu;
  menuFile->Append(wxID_NEW, _("&New..."), wxEmptyString, wxITEM_NORMAL);
  menuFile->Append(wxID_OPEN, _("&Open..."), wxEmptyString, wxITEM_NORMAL);
  menuFile->Append(wxID_CLOSE, _("&Close"), wxEmptyString, wxITEM_NORMAL);

  // Added for window managers which have no iconization concept
  if (m_sysTray->GetTrayStatus() == SystemTray::TrayStatus::LOCKED) {
    menuFile->Append(ID_UNLOCK_SAFE, _("&Unlock Safe\tCtrl+I"), wxEmptyString, wxITEM_NORMAL);
  }
  else {
    menuFile->Append(ID_LOCK_SAFE, _("&Lock Safe\tCtrl+J"), wxEmptyString, wxITEM_NORMAL);
  }

  if (wxGetApp().recentDatabases().GetCount() > 0) {

    // Most recently used DBs listed directly on File menu
    if (PWSprefs::GetInstance()->GetPref(PWSprefs::MRUOnFileMenu)) {
      wxGetApp().recentDatabases().AddFilesToMenu(menuFile);
    }
    // Most recently used DBs listed as submenu of File menu
    else {
      auto recentSafesMenu = new wxMenu;
      wxGetApp().recentDatabases().AddFilesToMenu(recentSafesMenu);
      menuFile->AppendSeparator();
      menuFile->Append(ID_RECENTSAFES, _("&Recent Safes..."), recentSafesMenu);
    }
  }
  else {
    menuFile->AppendSeparator();
  }

  menuFile->Append(ID_MENU_CLEAR_MRU, _("Clear Recent Safe List"), wxEmptyString, wxITEM_NORMAL);
  menuFile->AppendSeparator();
  menuFile->Append(wxID_SAVE, _("&Save..."), wxEmptyString, wxITEM_NORMAL);
  menuFile->Append(wxID_SAVEAS, _("Save &As..."), wxEmptyString, wxITEM_NORMAL);
  menuFile->AppendSeparator();

  auto menuExport = new wxMenu;
  menuExport->Append(ID_EXPORT2OLD1XFORMAT, _("v&1.x format..."), wxEmptyString, wxITEM_NORMAL);
  menuExport->Append(ID_EXPORT2V2FORMAT, _("v&2 format..."), wxEmptyString, wxITEM_NORMAL);
  menuExport->Append(ID_EXPORT2V4FORMAT, _("v&4 format (EXPERIMENTAL)..."), wxEmptyString, wxITEM_NORMAL);
  menuExport->Append(ID_EXPORT2PLAINTEXT, _("&Plain Text (tab separated)..."), wxEmptyString, wxITEM_NORMAL);
  menuExport->Append(ID_EXPORT2XML, _("&XML format..."), wxEmptyString, wxITEM_NORMAL);
  menuFile->Append(ID_EXPORTMENU, _("Export &To"), menuExport);

  auto menuImport = new wxMenu;
  menuImport->Append(ID_IMPORT_PLAINTEXT, _("&Plain Text..."), wxEmptyString, wxITEM_NORMAL);
  menuImport->Append(ID_IMPORT_XML, _("&XML format..."), wxEmptyString, wxITEM_NORMAL);
  menuImport->Append(ID_IMPORT_KEEPASS, _("&KeePass..."), wxEmptyString, wxITEM_NORMAL);
  menuFile->Append(ID_IMPORTMENU, _("Import &From"), menuImport);

  menuFile->Append(ID_MERGE, _("&Merge..."), wxEmptyString, wxITEM_NORMAL);
  menuFile->Append(ID_COMPARE, _("&Compare..."), wxEmptyString, wxITEM_NORMAL);
  menuFile->Append(ID_SYNCHRONIZE, _("S&ynchronize..."), wxEmptyString, wxITEM_NORMAL);
  menuFile->AppendSeparator();
  menuFile->Append(wxID_PROPERTIES, _("&Properties"), wxEmptyString, wxITEM_NORMAL);
  menuFile->AppendSeparator();
  menuFile->Append(wxID_EXIT, _("E&xit"), wxEmptyString, wxITEM_NORMAL);
  menuBar->Append(menuFile, _("&File"));

  /////////////////////////////////////////////////////////////////////////////
  // Menu: "Edit"
  /////////////////////////////////////////////////////////////////////////////

  auto menuEdit = new wxMenu;
  menuEdit->Append(wxID_ADD, _("&Add Entry...\tCtrl+A"), wxEmptyString, wxITEM_NORMAL);
  if (m_core.IsReadOnly()) {
    menuEdit->Append(ID_EDIT, _("&View Entry...\tCtrl+Enter"), wxEmptyString, wxITEM_NORMAL);
  }
  else {
    menuEdit->Append(ID_EDIT, _("Edit Entry...\tCtrl+Enter"), wxEmptyString, wxITEM_NORMAL);
  }
  menuEdit->Append(wxID_DELETE, _("&Delete Entry\tDel"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->Append(ID_RENAME, _("Rename Entry\tF2"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->Append(wxID_FIND, _("&Find Entry...\tCtrl+F"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->Append(ID_DUPLICATEENTRY, _("&Duplicate Entry\tCtrl+D"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->Append(ID_PROTECT, _("Protect Entry"), wxEmptyString, wxITEM_CHECK);
  menuEdit->AppendSeparator();
  menuEdit->Append(ID_ADDGROUP, _("Add Group"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->AppendSeparator();
  menuEdit->Append(wxID_UNDO);
  menuEdit->Append(wxID_REDO);
  menuEdit->Append(ID_CLEARCLIPBOARD, _("C&lear Clipboard\tCtrl+Del"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->AppendSeparator();
  menuEdit->Append(ID_COPYPASSWORD, _("&Copy Password to Clipboard\tCtrl+C"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->Append(ID_COPYUSERNAME, _("Copy &Username to Clipboard\tCtrl+U"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->Append(ID_COPYNOTESFLD, _("Copy &Notes to Clipboard\tCtrl+G"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->Append(ID_COPYURL, _("Copy URL to Clipboard\tCtrl+Alt+L"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->Append(ID_BROWSEURL, _("&Browse to URL\tCtrl+L"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->Append(ID_AUTOTYPE, _("Perform Auto&type\tCtrl+T"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->Append(ID_CREATESHORTCUT, _("Create &Shortcut"), wxEmptyString, wxITEM_NORMAL);
  menuEdit->Append(ID_GOTOBASEENTRY, _("Go to Base entry"), wxEmptyString, wxITEM_NORMAL);
  if (m_core.GetReadFileVersion() == PWSfile::V40) {
    menuEdit->Append(ID_VIEWATTACHMENT, _("View Attachment"), wxEmptyString, wxITEM_NORMAL);
  }
  menuBar->Append(menuEdit, _("&Edit"));

  /////////////////////////////////////////////////////////////////////////////
  // Menu: "View"
  /////////////////////////////////////////////////////////////////////////////

  auto menuView = new wxMenu;
  menuView->Append(ID_LIST_VIEW, _("Flattened &List"), wxEmptyString, wxITEM_RADIO);
  menuView->Append(ID_TREE_VIEW, _("Nested &Tree"), wxEmptyString, wxITEM_RADIO);
  menuView->AppendSeparator();
  menuView->Append(ID_SHOWHIDE_TOOLBAR, _("Toolbar &visible"), wxEmptyString, wxITEM_CHECK);
  menuView->AppendRadioItem(ID_TOOLBAR_NEW, _("&New Toolbar"));
  menuView->AppendRadioItem(ID_TOOLBAR_CLASSIC, _("&Classic Toolbar"));
  menuView->Append(ID_SHOWHIDE_DRAGBAR, _("&Dragbar visible"), wxEmptyString, wxITEM_CHECK);
  menuView->AppendSeparator();
  menuView->Append(ID_EXPANDALL, _("Expand All"), wxEmptyString, wxITEM_NORMAL);
  menuView->Append(ID_COLLAPSEALL, _("Collapse All"), wxEmptyString, wxITEM_NORMAL);

  auto menuSubViews = new wxMenu;
  menuSubViews->Append(ID_SHOWHIDE_UNSAVED, _("&Unsaved Changes"), wxEmptyString, wxITEM_CHECK);
  menuSubViews->Append(ID_SHOW_ALL_EXPIRY, _("Entries with E&xpiry Dates"), wxEmptyString, wxITEM_CHECK);
  menuSubViews->Append(ID_SHOW_LAST_FIND_RESULTS, _("Last Find results"), wxEmptyString, wxITEM_CHECK);
  menuView->Append(ID_SUBVIEWSMENU, _("Subviews"), menuSubViews);
  
  auto menuFilters = new wxMenu;
  menuFilters->Append(ID_EDITFILTER, _("&New/Edit Filter..."), wxEmptyString, wxITEM_NORMAL); // TODO
  menuFilters->Append(ID_APPLYFILTER, _("&Apply current"), wxEmptyString, wxITEM_NORMAL); // TODO
  menuFilters->Append(ID_MANAGEFILTERS, _("&Manage..."), wxEmptyString, wxITEM_NORMAL); // TODO
  menuView->Append(ID_FILTERMENU, _("&Filters"), menuFilters);
  menuView->AppendSeparator();
  menuView->Append(ID_CUSTOMIZETOOLBAR, _("Customize &Main Toolbar..."), wxEmptyString, wxITEM_NORMAL);

  auto menuFonts = new wxMenu;
  menuFonts->Append(ID_CHANGETREEFONT, _("&Tree/List Font"), wxEmptyString, wxITEM_NORMAL);
  menuFonts->Append(ID_CHANGEADDEDITFONT, _("&Add/Edit Font"), wxEmptyString, wxITEM_NORMAL);
  menuFonts->Append(ID_CHANGEPSWDFONT, _("&Password Font"), wxEmptyString, wxITEM_NORMAL);
  menuFonts->Append(ID_CHANGENOTESFONT, _("&Notes Font"), wxEmptyString, wxITEM_NORMAL);
  //TODO: Applying font to virtual keyboard needs to be implemented; Event handler for menu item is already prepared
  //menuFonts->Append(ID_CHANGEVKBFONT, _("&Virtual Keyboard Font"), wxEmptyString, wxITEM_NORMAL);
  menuView->Append(ID_CHANGEFONTMENU, _("Change &Font"), menuFonts);

  auto menuReports = new wxMenu;
  menuReports->Append(ID_REPORT_COMPARE, _("&Compare"), wxEmptyString, wxITEM_NORMAL); // TODO
  menuReports->Append(ID_REPORT_FIND, _("&Find"), wxEmptyString, wxITEM_NORMAL); // TODO
  menuReports->Append(ID_REPORT_IMPORTTEXT, _("Import &Text"), wxEmptyString, wxITEM_NORMAL); // TODO
  menuReports->Append(ID_REPORT_IMPORTXML, _("Import &XML"), wxEmptyString, wxITEM_NORMAL); // TODO
  menuReports->Append(ID_REPORT_MERGE, _("&Merge"), wxEmptyString, wxITEM_NORMAL); // TODO
  menuReports->Append(ID_REPORT_VALIDATE, _("&Validate"), wxEmptyString, wxITEM_NORMAL); // TODO
  menuView->Append(ID_REPORTSMENU, _("Reports"), menuReports);
  menuBar->Append(menuView, _("&View"));

  /////////////////////////////////////////////////////////////////////////////
  // Menu: "Manage"
  /////////////////////////////////////////////////////////////////////////////

  auto menuManage = new wxMenu;
  menuManage->Append(ID_CHANGECOMBO, _("&Change Safe Combination..."), wxEmptyString, wxITEM_NORMAL);
  menuManage->AppendSeparator();
  menuManage->Append(ID_BACKUP, _("Make &Backup...\tCtrl+B"), wxEmptyString, wxITEM_NORMAL);
  menuManage->Append(ID_RESTORE, _("&Restore from Backup...\tCtrl+R"), wxEmptyString, wxITEM_NORMAL);
  menuManage->AppendSeparator();
  menuManage->Append(wxID_PREFERENCES, _("&Options...\tCtrl+M"), wxEmptyString, wxITEM_NORMAL);
  menuManage->Append(ID_PWDPOLSM, _("Password Policies..."), wxEmptyString, wxITEM_NORMAL);
  menuManage->AppendSeparator();
  menuManage->Append(ID_GENERATEPASSWORD, _("Generate &Password...\tCtrl+P"), _("Generate Password"), wxITEM_NORMAL);
#ifndef NO_YUBI
  menuManage->Append(ID_YUBIKEY_MNG, _("YubiKey..."), _("Configure and backup YubiKeys"), wxITEM_NORMAL);
#endif
  menuManage->AppendSeparator();
  AddLanguageMenu( menuManage );
  menuBar->Append(menuManage, _("&Manage"));

  /////////////////////////////////////////////////////////////////////////////
  // Menu: "Help"
  /////////////////////////////////////////////////////////////////////////////

  auto menuHelp = new wxMenu;
  menuHelp->Append(wxID_HELP, _("Get &Help"), wxEmptyString, wxITEM_NORMAL);
  menuHelp->Append(ID_VISITWEBSITE, _("Visit Password Safe &website..."), wxEmptyString, wxITEM_NORMAL);
  menuHelp->Append(wxID_ABOUT, _("&About Password Safe..."), wxEmptyString, wxITEM_NORMAL);
  menuBar->Append(menuHelp, _("&Help"));
  this->SetMenuBar(menuBar);

////@end PasswordSafeFrame content construction

  menuBar->Thaw();

  // If there was no previous menu bar then we set the new created one
  // otherwise the already existing one is triggered to refresh
  if (GetMenuBar() == nullptr) {
    SetMenuBar(menuBar);
  }
  else {
    menuBar->Refresh();
  }

  // Update menu selections
  GetMenuBar()->Check((IsTreeView()) ? ID_TREE_VIEW : ID_LIST_VIEW, true);
  GetMenuBar()->Check(PWSprefs::GetInstance()->GetPref(PWSprefs::UseNewToolbar) ? ID_TOOLBAR_NEW : ID_TOOLBAR_CLASSIC, true);
}

/**
 * Control creation for PasswordSafeFrame
 */
void PasswordSafeFrame::CreateControls()
{
  PWSMenuShortcuts* scmgr = PWSMenuShortcuts::CreateShortcutsManager( GetMenuBar() );
  scmgr->ReadApplyUserShortcuts();

  wxBoxSizer* mainsizer = new wxBoxSizer(wxVERTICAL); //to add the search bar later to the bottom
  wxBoxSizer* itemBoxSizer83 = new wxBoxSizer(wxHORIZONTAL);
  mainsizer->Add(itemBoxSizer83, 1, wxEXPAND);
  SetSizer(mainsizer);

  m_grid = new GridCtrl( this, m_core, ID_LISTBOX, wxDefaultPosition,
                        wxDefaultSize, wxHSCROLL|wxVSCROLL );
  itemBoxSizer83->Add(m_grid, wxSizerFlags().Expand().Border(0).Proportion(1));

  m_tree = new TreeCtrl( this, m_core, ID_TREECTRL, wxDefaultPosition,
                            wxDefaultSize,
                            wxTR_EDIT_LABELS|wxTR_HAS_BUTTONS |wxTR_HIDE_ROOT|wxTR_SINGLE );

  // let the tree ctrl handle ID_ADDGROUP & ID_RENAME all by itself
  Connect(ID_ADDGROUP, wxEVT_COMMAND_MENU_SELECTED,
                       wxCommandEventHandler(TreeCtrl::OnAddGroup), nullptr, m_tree);
  Connect(ID_RENAME, wxEVT_COMMAND_MENU_SELECTED,
                       wxCommandEventHandler(TreeCtrl::OnRenameGroup), nullptr, m_tree);

  itemBoxSizer83->Add(m_tree, wxSizerFlags().Expand().Border(0).Proportion(1));

  itemBoxSizer83->Layout();

  const RecentDbList& rdb = wxGetApp().recentDatabases();
  Connect(rdb.GetBaseId(), rdb.GetBaseId() + rdb.GetMaxFiles() - 1, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(PasswordSafeFrame::OnOpenRecentDB));
}

/**
 * Creates the language sub menu.
 *
 * \param parent the parent to which the sub-menu should be added
 */
void PasswordSafeFrame::AddLanguageMenu(wxMenu* parent)
{
  if (parent == nullptr)
    return;
  wxMenu* child = new wxMenu;
  wxMenuItem* menu_item = nullptr;
  wxLanguage system_language = wxGetApp().GetSystemLanguage();

  for (auto &item : m_languages) {
    wxString lang_name = _(get<1>(item.second));
    if (get<0>(item.second) == system_language) {
      lang_name = L"[ " + lang_name + L" ]";
    }
    if (m_selectedLanguage != ID_LANGUAGE_ENGLISH && item.first == ID_LANGUAGE_ENGLISH) {
      // duplicate English label to simplify switching to it  in case of wrong selection
      lang_name += L" (English)";
    }

    menu_item = child->Append(
        item.first,               /* The key of the map that holds menu item id's */
        lang_name, /* The value of the map is a tuple.
                                     The tuple consists of three elements.
                                     Index 0: the language id as wxLanguage
                                     Index 1: the language literal as wxString
                                     Index 2: the indicator whether the language can be activated
                                     */
        L"",                   /* The menu items tooltip */
        wxITEM_CHECK
        );

    if (menu_item != nullptr)
      menu_item->Enable(get<2>(item.second));
  }

  parent->Append(ID_LANGUAGEMENU, _("Select Language") + L" (Select Language)", child);
  if ((m_selectedLanguage > ID_LANGUAGE_BEGIN) && (m_selectedLanguage < ID_LANGUAGE_END))
    child->Check( m_selectedLanguage, true );
}

/**
 * Adds language specific data to internal map
 * for language switching functionality.
 *
 * \param menu_id the id of the language menu item
 * \param lang_id the wx framework specific language id
 * \param lang_name the textual language representation on the menu
 */
void PasswordSafeFrame::AddLanguage(int menu_id, wxLanguage lang_id, const wxString& lang_name)
{
    m_languages[menu_id] = make_tuple(lang_id, lang_name, false);
}

/**
 * Creates the main toolbar
 */
void PasswordSafeFrame::CreateMainToolbar()
{
  wxToolBar* toolbar = CreateToolBar(wxBORDER_NONE | wxTB_TOP | wxTB_HORIZONTAL, wxID_ANY, wxT("Main Toolbar"));

  RefreshToolbarButtons();

  wxCHECK_RET(toolbar->Realize(), wxT("Could not create main toolbar"));

  const bool bShow = PWSprefs::GetInstance()->GetPref(PWSprefs::ShowToolbar);
  if (!bShow) {
    toolbar->Hide();
  }
  GetMenuBar()->Check(ID_SHOWHIDE_TOOLBAR, bShow);
}

/**
 * Recreates the main toolbar.
 *
 * This assumes that the main toolbar has already been created.
 * If this is the case all existing elements are removed and
 * added again to the toolbar instance.
 */
void PasswordSafeFrame::ReCreateMainToolbar()
{
    wxToolBar* toolbar = GetToolBar();
    wxCHECK_RET(toolbar, wxT("Couldn't find toolbar"));
    toolbar->ClearTools();
    RefreshToolbarButtons();
}

/**
 * Recreates the dragbar.
 *
 * This assumes that the dragbar has already been created.
 * If this is the case all existing elements are removed and
 * re-added.
 */
void PasswordSafeFrame::ReCreateDragToolbar()
{
    DragBarCtrl* dragbar = GetDragBar();
    wxCHECK_RET(dragbar, wxT("Couldn't find dragbar"));
    dragbar->ClearTools();
    dragbar->RefreshButtons();
}

void PasswordSafeFrame::RefreshToolbarButtons()
{
  wxToolBar* tb = GetToolBar();
  wxASSERT(tb);
  if (tb->GetToolsCount() == 0) {  //being created?
    if (PWSprefs::GetInstance()->GetPref(PWSprefs::UseNewToolbar)) {
      for (auto & PwsToolbarButton : PwsToolbarButtons) {
        if (PwsToolbarButton.id == ID_SEPARATOR)
          tb->AddSeparator();
        else
          tb->AddTool(PwsToolbarButton.id, wxEmptyString, wxBitmap(PwsToolbarButton.bitmap_normal),
                              wxBitmap(PwsToolbarButton.bitmap_disabled), wxITEM_NORMAL,
                              wxGetTranslation(PwsToolbarButton.tooltip) );
      }
    }
    else {
      for (auto & PwsToolbarButton : PwsToolbarButtons) {
        if (PwsToolbarButton.id == ID_SEPARATOR)
          tb->AddSeparator();
        else
          tb->AddTool(PwsToolbarButton.id, wxEmptyString, wxBitmap(PwsToolbarButton.bitmap_classic),
                          wxGetTranslation(PwsToolbarButton.tooltip) );
      }
    }
  }
  else { //toolbar type was changed from the menu
    if (PWSprefs::GetInstance()->GetPref(PWSprefs::UseNewToolbar)) {
      for (auto & PwsToolbarButton : PwsToolbarButtons) {
        if (PwsToolbarButton.id == ID_SEPARATOR)
          continue;
        tb->SetToolNormalBitmap(PwsToolbarButton.id, wxBitmap(PwsToolbarButton.bitmap_normal));
        tb->SetToolDisabledBitmap(PwsToolbarButton.id, wxBitmap(PwsToolbarButton.bitmap_disabled));
      }
    }
    else {
      for (auto & PwsToolbarButton : PwsToolbarButtons) {
        if (PwsToolbarButton.id == ID_SEPARATOR)
          continue;
        tb->SetToolNormalBitmap(PwsToolbarButton.id, wxBitmap(PwsToolbarButton.bitmap_classic));
        tb->SetToolDisabledBitmap(PwsToolbarButton.id, wxNullBitmap);
      }
    }
  }
}

/*!
 * Should we show tooltips?
 */

bool PasswordSafeFrame::ShowToolTips()
{
    return true;
}

/*!
 * Get bitmap resources
 */

wxBitmap PasswordSafeFrame::GetBitmapResource( const wxString& name)
{
    // Bitmap retrieval
////@begin PasswordSafeFrame bitmap retrieval
  wxUnusedVar(name);
  return wxNullBitmap;
////@end PasswordSafeFrame bitmap retrieval
}

/*!
 * Get icon resources
 */

wxIcon PasswordSafeFrame::GetIconResource( const wxString& name )
{
    // Icon retrieval
////@begin PasswordSafeFrame icon retrieval
  wxUnusedVar(name);
  if (name == L"graphics/cpane.xpm")
  {
    wxIcon icon(cpane_xpm);
    return icon;
  }
  return wxNullIcon;
////@end PasswordSafeFrame icon retrieval
}

void PasswordSafeFrame::SetTitle(const wxString& title)
{
  wxString newtitle = _T("PasswordSafe");
  if (!title.empty()) {
    newtitle += _T(" - ");
    StringX fname = tostringx(title);
    StringX::size_type findex = fname.rfind(_T("/"));
    if (findex != StringX::npos)
      fname = fname.substr(findex + 1);
    newtitle += fname.c_str();
  }
  wxFrame::SetTitle(newtitle);
}

int PasswordSafeFrame::Load(const StringX &passwd)
{
  int status = m_core.ReadCurFile(passwd);
  if (status == PWScore::SUCCESS) {
    wxGetApp().ConfigureIdleTimer();
    SetTitle(m_core.GetCurFile().c_str());
    m_sysTray->SetTrayStatus(SystemTray::TrayStatus::UNLOCKED);
    m_core.ResumeOnDBNotification();
  } else {
    SetTitle(wxEmptyString);
    m_sysTray->SetTrayStatus(SystemTray::TrayStatus::CLOSED);
  }
  UpdateStatusBar();
  UpdateMenuBar();
  return status;
}

bool PasswordSafeFrame::Show(bool show)
{
  ShowGrid(show && IsGridView());
  ShowTree(show && IsTreeView());

  return wxFrame::Show(show);
}

void PasswordSafeFrame::ShowGrid(bool show)
{
  if (show) {
    m_grid->SetTable(new GridTable(m_grid), true, wxGrid::wxGridSelectRows); // true => auto-delete
    m_grid->EnableEditing(false);
    m_grid->Clear();
    wxFont font(towxstring(PWSprefs::GetInstance()->GetPref(PWSprefs::TreeFont)));
    if (font.IsOk())
      m_grid->SetDefaultCellFont(font);
    ItemListConstIter iter;
    int i;
    for (iter = m_core.GetEntryIter(), i = 0;
         iter != m_core.GetEntryEndIter();
         iter++) {
      if (!m_bFilterActive ||
          m_FilterManager.PassesFiltering(iter->second, m_core))
        m_grid->AddItem(iter->second, i++);
    }

    m_grid->UpdateSorting();

    m_guiInfo->RestoreGridViewInfo(m_grid);
  }
  else {
    m_guiInfo->SaveGridViewInfo(m_grid);
  }

  m_grid->Show(show);
  GetSizer()->Layout();
}

void PasswordSafeFrame::ShowTree(bool show)
{
  if (show) {
    m_tree->Clear();
    wxFont font(towxstring(PWSprefs::GetInstance()->GetPref(PWSprefs::TreeFont)));
    if (font.IsOk())
      m_tree->SetFont(font);
    ItemListConstIter iter;
    for (iter = m_core.GetEntryIter();
         iter != m_core.GetEntryEndIter();
         iter++) {
      if (!m_bFilterActive ||
          m_FilterManager.PassesFiltering(iter->second, m_core))
        m_tree->AddItem(iter->second);
    }

    // Empty groups need to be added separately
    typedef std::vector<StringX> StringVectorX;
    const StringVectorX& emptyGroups = m_core.GetEmptyGroups();
    for (const auto & emptyGroup : emptyGroups)
      m_tree->AddEmptyGroup(emptyGroup);

    if (m_tree->HasItems()) {// avoid assertion!
      m_tree->SortChildrenRecursively(m_tree->GetRootItem());
    }

    if (m_InitialTreeDisplayStatusAtOpen) {
      m_InitialTreeDisplayStatusAtOpen = false;

      switch (PWSprefs::GetInstance()->GetPref(PWSprefs::TreeDisplayStatusAtOpen)) {
        case PWSprefs::AllCollapsed:
          m_tree->SetGroupDisplayStateAllCollapsed();
          break;
        case PWSprefs::AllExpanded:
          m_tree->SetGroupDisplayStateAllExpanded();
          break;
        case PWSprefs::AsPerLastSave:
          m_tree->RestoreGroupDisplayState();
          break;
        default:
          m_tree->SetGroupDisplayStateAllCollapsed();
      }

      m_guiInfo->SaveTreeViewInfo(m_tree);
    }
    else {
      m_guiInfo->RestoreTreeViewInfo(m_tree);
    }
  }
  else {
    m_guiInfo->SaveTreeViewInfo(m_tree);
  }

  m_tree->Show(show);
  GetSizer()->Layout();
}

DragBarCtrl* PasswordSafeFrame::GetDragBar()
{
  wxSizer* origSizer = GetSizer();

  wxASSERT(origSizer);
  wxASSERT(origSizer->IsKindOf(wxBoxSizer(wxVERTICAL).GetClassInfo()));
  wxASSERT(static_cast<wxBoxSizer*>(origSizer)->GetOrientation() == wxVERTICAL);

  wxSizerItem* dragbarItem = origSizer->GetItem(size_t(0));
  wxASSERT_MSG(dragbarItem && dragbarItem->IsWindow() &&
                      wxIS_KIND_OF(dragbarItem->GetWindow(), DragBarCtrl),
                    wxT("Found unexpected item while searching for DragBar"));

  DragBarCtrl* dragbar = wxDynamicCast(dragbarItem->GetWindow(), DragBarCtrl);
  return dragbar;
}

void PasswordSafeFrame::ClearAppData()
{
  m_grid->Clear();
  m_tree->Clear();
  ResetFilters();
}

CItemData *PasswordSafeFrame::GetSelectedEntry() const
{
  if (m_tree->IsShown()) {
    // get selected from tree
    return m_tree->GetItem(m_tree->GetSelection());
  } else if (m_grid->IsShown()) {
    // get selected from grid
    return m_grid->GetItem(m_grid->GetGridCursorRow());
  }
  return nullptr;
}

// Following is "generalized" GetSelectedEntry to support section via RUE
CItemData *PasswordSafeFrame::GetSelectedEntry(const wxCommandEvent& evt, CItemData &rueItem) const
{
  if (!IsRUEEvent(evt))
    return GetSelectedEntry();
  else
    return m_RUEList.GetPWEntry(GetEventRUEIndex(evt), rueItem)? &rueItem: nullptr;
}

int PasswordSafeFrame::DoOpen(const wxString& title)
{
  stringT dir = PWSdirs::GetSafeDir();
  //Open-type dialog box
  wxFileDialog fd(this, title, dir.c_str(), wxT("pwsafe.psafe4"),
                  _("Password Safe Databases (*.psafe4; *.psafe3; *.dat)|*.psafe4;*.psafe3;*.dat|Password Safe Backups (*.bak)|*.bak|Password Safe Intermediate Backups (*.ibak)|*.ibak|All files (*.*; *)|*.*;*"),
                  (wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_CHANGE_DIR));

  while (1) {
    if (fd.ShowModal() == wxID_OK) {
      int rc = Open(fd.GetPath()); // prompt for password of new file and load.
      if (rc == PWScore::SUCCESS) {
        return PWScore::SUCCESS;
      }
    } else { // user cancelled
      return PWScore::USER_CANCEL;
    }
  }
}

int PasswordSafeFrame::Open(const wxString &fname)
{
  //Check that this file isn't already open
  if (wxFileName(fname).SameAs(towxstring(m_core.GetCurFile()))) {
    //It is the same damn file
    wxMessageBox(_("That file is already open."), _("Open database"), wxOK|wxICON_EXCLAMATION, this);
    return PWScore::ALREADY_OPEN;
  }

  int rc = SaveIfChanged();
  if (rc != PWScore::SUCCESS)
    return rc;

  // prompt for password, try to Load.
  SafeCombinationPromptDlg pwdprompt(this, m_core, fname);
  if (pwdprompt.ShowModal() == wxID_OK) {
    m_core.SetCurFile(tostringx(fname));
    StringX password = pwdprompt.GetPassword();
    int retval = Load(password);
    if (retval == PWScore::SUCCESS) {
      m_InitialTreeDisplayStatusAtOpen = true;
      Show();
      wxGetApp().recentDatabases().AddFileToHistory(fname);
    }
    return retval;
  } else
    return PWScore::USER_CANCEL;

#if 0

  rc = GetAndCheckPassword(pszFilename, passkey, GCP_NORMAL, bReadOnly);  // OK, CANCEL, HELP
  switch (rc) {
    case PWScore::SUCCESS:
      app.AddToMRU(pszFilename.c_str());
      m_bAlreadyToldUserNoSave = false;
      break; // Keep going...
    case PWScore::CANT_OPEN_FILE:
      temp.Format(IDS_SAFENOTEXIST, pszFilename.c_str());
      cs_title.LoadString(IDS_FILEOPENERROR);
      MessageBox(temp, cs_title, MB_OK|MB_ICONWARNING);
    case TAR_OPEN:
      return Open();
    case TAR_NEW:
      return New();
    case PWScore::WRONG_PASSWORD:
    case PWScore::USER_CANCEL:
      /*
      If the user just cancelled out of the password dialog,
      assume they want to return to where they were before...
      */
      return PWScore::USER_CANCEL;
    default:
      ASSERT(0); // we should take care of all cases explicitly
      return PWScore::USER_CANCEL; // conservative behaviour for release version
  }

  // Reset core and clear ALL associated data
  m_core.ReInit();

  // clear the application data before loading the new file
  ClearAppData();

  cs_title.LoadString(IDS_FILEREADERROR);
  MFCAsker q;
  MFCReporter r;
  m_core.SetAsker(&q);
  m_core.SetReporter(&r);
  rc = m_core.ReadFile(pszFilename, passkey);
  m_core.SetAsker(nullptr);
  m_core.SetReporter(nullptr);
  switch (rc) {
    case PWScore::SUCCESS:
      break;
    case PWScore::CANT_OPEN_FILE:
      temp.Format(IDS_CANTOPENREADING, pszFilename.c_str());
      MessageBox(temp, cs_title, MB_OK|MB_ICONWARNING);
      /*
      Everything stays as is... Worst case,
      they saved their file....
      */
      return PWScore::CANT_OPEN_FILE;
    case PWScore::BAD_DIGEST:
    {
      temp.Format(IDS_FILECORRUPT, pszFilename.c_str());
      const int yn = MessageBox(temp, cs_title, MB_YESNO|MB_ICONERROR);
      if (yn == IDYES) {
        rc = PWScore::SUCCESS;
        break;
      } else
        return rc;
    }
    default:
      temp.Format(IDS_UNKNOWNERROR, pszFilename.c_str());
      MessageBox(temp, cs_title, MB_OK|MB_ICONERROR);
      return rc;
  }
  m_core.SetCurFile(pszFilename);
  m_titlebar = PWSUtil::NormalizeTTT(_T("Password Safe - ") +
                                     m_core.GetCurFile()).c_str();
  SetWindowText(LPCTSTR(m_titlebar));
  CheckExpiredPasswords();
  ChangeOkUpdate();

  // Tidy up filters
  m_currentfilter.Empty();
  m_bFilterActive = false;

  RefreshViews();
  SetInitialDatabaseDisplay();
  m_core.SetDefUsername(PWSprefs::GetInstance()->
                        GetPref(PWSprefs::DefaultUsername));
  m_core.SetUseDefUser(PWSprefs::GetInstance()->
                       GetPref(PWSprefs::UseDefaultUser) ? true : false);
  m_needsreading = false;
  SelectFirstEntry();

  return rc;
#endif
}

/*!
 * wxEVT_CHAR_HOOK event handler for WXK_ESCAPE
 */

void PasswordSafeFrame::OnChar( wxKeyEvent& evt )
{
  if ((evt.GetKeyCode() == WXK_ESCAPE) && PWSprefs::GetInstance()->GetPref(PWSprefs::EscExits)) {

    Close();
  }

  evt.Skip();
}

/*!
 * wxEVT_CLOSE_WINDOW event handler for ID_PASSWORDSAFEFRAME
 */

void PasswordSafeFrame::OnCloseWindow( wxCloseEvent& evt )
{
  wxGetApp().SaveFrameCoords();
  const bool systrayEnabled = PWSprefs::GetInstance()->GetPref(PWSprefs::UseSystemTray);
  /*
   * Really quit if the user chooses to quit from File menu, or
   * by clicking the 'X' in title bar or the system menu pulldown
   * from the top-left of the titlebar while systray is disabled
   */
  if (m_exitFromMenu || !systrayEnabled) {
    if (evt.CanVeto()) {
      int rc = SaveIfChanged();
      if (rc == PWScore::USER_CANCEL) {
        evt.Veto();
        m_exitFromMenu = false;
        return;
      }
    }

    if (PWSprefs::GetInstance()->GetPref(PWSprefs::ClearClipboardOnExit)) {
      Clipboard::GetInstance()->ClearCBData();
    }

    // Don't leave dangling locks!
    m_core.SafeUnlockCurFile();

    SaveSettings();

    // Reset core and clear ALL associated data
    m_core.ReInit();

    // clear the application data before ending
    ClearAppData();

    Destroy();
  }
  else {
    const bool lockOnMinimize = PWSprefs::GetInstance()->GetPref(PWSprefs::DatabaseClear);

    if (PWSprefs::GetInstance()->GetPref(PWSprefs::ClearClipboardOnExit)) {
      Clipboard::GetInstance()->ClearCBData();
    }
#if wxCHECK_VERSION(2,9,5)
    CallAfter(&PasswordSafeFrame::HideUI, lockOnMinimize);
#else
    HideUI(lockOnMinimize);
#endif
  }
}

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for wxID_ABOUT
 */

void PasswordSafeFrame::OnAboutClick(wxCommandEvent& WXUNUSED(evt))
{
  AboutDlg* window = new AboutDlg(this);
  window->ShowModal();
  window->Destroy();
}

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_SENDEMAIL
 */

void PasswordSafeFrame::OnSendEmail(wxCommandEvent& evt)
{
  CItemData rueItem;
  CItemData* item = GetSelectedEntry(evt, rueItem);
  if (item)
    DoEmail(*item);
}

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_RUNCOMMAND
 */

void PasswordSafeFrame::OnRunCommand(wxCommandEvent& evt)
{
  CItemData rueItem;
  CItemData* item = GetSelectedEntry(evt, rueItem);
  if (item)
    DoRun(*item);
}

void PasswordSafeFrame::OnEditBase(wxCommandEvent& WXUNUSED(evt))
{
  CItemData* item = GetSelectedEntry();
  if (item && item->IsDependent()) {
    item = m_core.GetBaseEntry(item);
    ASSERT(item != nullptr);
    DoEdit(*item);
    UpdateAccessTime(*item);
  }
}

void PasswordSafeFrame::SelectItem(const CUUID& uuid)
{
    if (IsGridView()) {
      m_grid->SelectItem(uuid);
    }
    else {
      m_tree->SelectItem(uuid);
    }
}

void PasswordSafeFrame::SaveSettings(void) const
{
  m_grid->SaveSettings();
}

bool PasswordSafeFrame::IsRUEEvent(const wxCommandEvent& evt) const
{
  const int cmd = int(evt.GetExtraLong());
  return IsRUECommand(cmd) && GetRUEIndex(cmd) < m_RUEList.GetCount();
}

long PasswordSafeFrame::GetEventRUEIndex(const wxCommandEvent& evt) const
{
  return GetRUEIndex(int(evt.GetExtraLong()));
}

void PasswordSafeFrame::UpdateAccessTime(CItemData &ci)
{
  // Mark access time if so configured
  // First add to RUE List
  m_RUEList.AddRUEntry(ci.GetUUID());
  bool bMaintainDateTimeStamps = PWSprefs::GetInstance()->
              GetPref(PWSprefs::MaintainDateTimeStamps);

  if (!m_core.IsReadOnly() && bMaintainDateTimeStamps) {
    ci.SetATime();
    UpdateStatusBar();
  }
}

void PasswordSafeFrame::DispatchDblClickAction(CItemData &item)
{
  /**
   * If entry has a double-click action, use it.
   * (Unless the entry's a shortcut, in which case we ask the base)
   * Otherwise, use the preference.
   */

  bool isShift = ::wxGetKeyState(WXK_SHIFT);
  PWSprefs::IntPrefs pref = (isShift) ?
    PWSprefs::ShiftDoubleClickAction : PWSprefs::DoubleClickAction;

  int16 DCA = int16(PWSprefs::GetInstance()->GetPref(pref));

  CItemData *pci = item.IsShortcut() ? m_core.GetBaseEntry(&item) : &item;

  int16 itemDCA;
  pci->GetDCA(itemDCA, isShift);

  if (itemDCA >= PWSprefs::minDCA && itemDCA <= PWSprefs::maxDCA)
    DCA = itemDCA;

  switch (DCA) {
  case PWSprefs::DoubleClickAutoType:
    DoAutotype(item);
    break;
  case PWSprefs::DoubleClickBrowse:
    DoBrowse(item, false); //false => no autotype
    break;
  case PWSprefs::DoubleClickCopyNotes:
    DoCopyNotes(item);
    break;
  case PWSprefs::DoubleClickCopyPassword:
    DoCopyPassword(item);
    break;
  case PWSprefs::DoubleClickCopyUsername:
    DoCopyUsername(item);
    break;
  case PWSprefs::DoubleClickCopyPasswordMinimize:
    DoCopyPassword(item);
    Iconize();
    break;
  case PWSprefs::DoubleClickViewEdit:
    DoEdit(item);
    break;
  case PWSprefs::DoubleClickBrowsePlus:
    DoBrowse(item, true); //true => autotype
    break;
  case PWSprefs::DoubleClickRun:
    DoRun(item);
    break;
  case PWSprefs::DoubleClickSendEmail:
    DoEmail(item);
    break;
  default: {
    wxString action;
    action.Printf(_("Unknown code: %d"),
                  PWSprefs::GetInstance()->GetPref(PWSprefs::DoubleClickAction));
    wxMessageBox(action, _("Error"), wxOK|wxICON_ERROR, this);
    break;
  }
  }
}

static void FlattenTree(wxTreeItemId id, TreeCtrl* tree, OrderedItemList& olist)
{
  wxTreeItemIdValue cookie;

  if (!id.IsOk()) {
    return;
  }

  for (wxTreeItemId childId = tree->GetFirstChild(id, cookie); childId.IsOk();
                          childId = tree->GetNextChild(id, cookie)) {
    CItemData* item = tree->GetItem(childId);
    if (item)
      olist.push_back(*item);

    if (tree->HasChildren(childId))
      ::FlattenTree(childId, tree, olist);
  }
}

void PasswordSafeFrame::FlattenTree(OrderedItemList& olist)
{
  ::FlattenTree(m_tree->GetRootItem(), m_tree, olist);
}

///////////////////////////////////////////
// Handles right-click event forwarded by the tree and list views
// The logic is the same as DboxMain::OnContextMenu in src/ui/Windows/MainMenu.cpp
void PasswordSafeFrame::OnContextMenu(const CItemData* item)
{
  if (item == nullptr) {
    if (IsTreeView()) {
      wxMenu groupEditMenu;

      groupEditMenu.Append(wxID_ADD, _("Add &Entry"));
      groupEditMenu.Append(ID_ADDGROUP, _("Add &Group"));

      if (m_tree->IsGroupSelected()) {
        groupEditMenu.Append(ID_RENAME, _("&Rename Group"));
        groupEditMenu.Append(wxID_DELETE, _("&Delete Group"));
      }

      m_tree->PopupMenu(&groupEditMenu);
    }
    else {
      wxMenu contextMenu;

      contextMenu.Append(wxID_ADD, _("Add &Entry"));

      m_grid->PopupMenu(&contextMenu);
    }
  }
  else {
    wxMenu itemEditMenu;
    itemEditMenu.Append(ID_COPYUSERNAME,   _("Copy &Username to Clipboard"));
    itemEditMenu.Append(ID_COPYPASSWORD,   _("&Copy Password to Clipboard"));
    itemEditMenu.Append(ID_PASSWORDSUBSET, _("Display subset of Password"));
    if (HasQRCode()) {
      itemEditMenu.Append(ID_PASSWORDQRCODE, _("Display Password as &QR code"));
    }
    itemEditMenu.Append(ID_COPYNOTESFLD,   _("Copy &Notes to Clipboard"));
    itemEditMenu.Append(ID_COPYURL,        _("Copy UR&L to Clipboard"));
    itemEditMenu.Append(ID_COPYEMAIL,      _("Copy email to Clipboard"));
    itemEditMenu.Append(ID_COPYRUNCOMMAND, _("Copy Run Command to Clipboard"));
    itemEditMenu.AppendSeparator();
    itemEditMenu.Append(ID_BROWSEURL,      _("&Browse to URL"));
    itemEditMenu.Append(ID_BROWSEURLPLUS,  _("Browse to URL + &Autotype"));
    itemEditMenu.Append(ID_SENDEMAIL,      _("Send &email"));
    itemEditMenu.Append(ID_RUNCOMMAND,     _("&Run Command"));
    itemEditMenu.Append(ID_AUTOTYPE,       _("Perform Auto &Type"));
    itemEditMenu.AppendSeparator();
    if (m_core.IsReadOnly()) {
      itemEditMenu.Append(ID_EDIT,         _("&View Entry..."));
    }
    else {
      itemEditMenu.Append(ID_EDIT,         _("Edit Entry..."));
    }
    itemEditMenu.Append(ID_DUPLICATEENTRY, _("&Duplicate Entry"));
    itemEditMenu.Append(wxID_DELETE,       _("Delete Entry"));
    itemEditMenu.Append(ID_CREATESHORTCUT, _("Create &Shortcut"));

    UUIDVector shortcutUUIDs;
    m_core.GetAllDependentEntries(item->GetUUID(), shortcutUUIDs, CItemData::EntryType::ET_SHORTCUT);

    if (!shortcutUUIDs.empty()) {

      auto shortcutsMenu = new wxMenu;

      for (auto const& shortcutUUID : shortcutUUIDs) {

        auto entryIterator = m_core.Find(shortcutUUID);

        if (entryIterator != m_core.GetEntryEndIter()) {
          auto shortcutItem = m_core.GetEntry(entryIterator);

          wxString group = wxT("N/A");
          if (!shortcutItem.GetGroup().empty()) {
            group = stringx2std(shortcutItem.GetGroup());
          }

          wxString title = wxT("N/A");
          if (!shortcutItem.GetTitle().empty()) {
            title = stringx2std(shortcutItem.GetTitle());
          }

          wxString username  = wxT("N/A");
          if (!shortcutItem.GetUser().empty()) {
            username = stringx2std(shortcutItem.GetUser());
          }

          auto menuID = wxNewId();
          shortcutsMenu->Append(
            menuID,
            wxT("<") + group + wxT("> / <") + title + wxT("> / <") + username + wxT(">")
          );

          Bind(wxEVT_MENU, [&](wxCommandEvent& event) { SelectItem(shortcutUUID); }, menuID);
        }
      }

      itemEditMenu.AppendSubMenu(shortcutsMenu, _("Goto &Shortcuts..."));
    }

    itemEditMenu.Append(ID_GOTOBASEENTRY,  _("&Go to Base entry"));
    itemEditMenu.Append(ID_EDITBASEENTRY,  _("&Edit Base entry"));
    if (item->HasAttRef()) {
      itemEditMenu.Append(ID_VIEWATTACHMENT, _("View Attachment"));
    }
    if (!item->IsShortcut()) {
      itemEditMenu.AppendCheckItem(ID_PROTECT,  _("Protect Entry"));
      itemEditMenu.Check(ID_PROTECT, item->IsProtected());
    }

    switch (item->GetEntryType()) {
      case CItemData::ET_NORMAL:
      case CItemData::ET_SHORTCUTBASE:
        itemEditMenu.Delete(ID_GOTOBASEENTRY);
        itemEditMenu.Delete(ID_EDITBASEENTRY);
        break;

      case CItemData::ET_ALIASBASE:
        itemEditMenu.Delete(ID_CREATESHORTCUT);
        itemEditMenu.Delete(ID_GOTOBASEENTRY);
        itemEditMenu.Delete(ID_EDITBASEENTRY);
        break;

      case CItemData::ET_ALIAS:
      case CItemData::ET_SHORTCUT:
        itemEditMenu.Delete(ID_CREATESHORTCUT);
        break;

      default:
        wxASSERT_MSG(false, wxT("Unexpected CItemData type"));
        break;
    }

    if (item->IsShortcut()) {
      item = m_core.GetBaseEntry(item);
    }

    if (item->IsUserEmpty())
      itemEditMenu.Delete(ID_COPYUSERNAME);

    if (item->IsNotesEmpty())
      itemEditMenu.Delete(ID_COPYNOTESFLD);

    if (item->IsEmailEmpty() && !item->IsURLEmail()) {
      itemEditMenu.Delete(ID_COPYEMAIL);
      itemEditMenu.Delete(ID_SENDEMAIL);
    }

    if ( item->IsURLEmpty()) {
      itemEditMenu.Delete(ID_COPYURL);
      itemEditMenu.Delete(ID_BROWSEURL);
      itemEditMenu.Delete(ID_BROWSEURLPLUS);
    }

    if (item->IsRunCommandEmpty()) {
      itemEditMenu.Delete(ID_COPYRUNCOMMAND);
      itemEditMenu.Delete(ID_RUNCOMMAND);
    }

    if (IsTreeView()) {
      m_tree->PopupMenu(&itemEditMenu);
    }
    else {
      m_grid->PopupMenu(&itemEditMenu);
    }
  }
}

CItemData* PasswordSafeFrame::GetBaseEntry(const CItemData *item) const
{
  if (item && item->IsDependent()) {
    return m_core.GetBaseEntry(item);
  }
  return nullptr;
}

////////////////////////////////////////////////////////
// This function is used for wxCommandUIEvent handling
// of all commands, to avoid scattering this stuff all
// over the place.  It is just a copy of the logic from
// DboxMain::OnUpdateMenuToolbar() function defined in
// src/ui/Windows/Dboxmain.cpp
//
void PasswordSafeFrame::OnUpdateUI(wxUpdateUIEvent& evt)
{
  const CItemData *pci(nullptr), *pbci(nullptr);

  const bool isFileReadOnly          = m_core.IsReadOnly();
  const bool isTreeView              = IsTreeView();
  const bool isTreeViewGroupSelected = isTreeView && m_tree->IsGroupSelected();
  const bool isTreeViewEmpty         = isTreeView && !m_tree->HasItems(); // excludes the invisible root item
  const bool isTreeViewItemSelected  = isTreeView && m_tree->HasSelection();

  pci = GetSelectedEntry();

  if (pci) {
    if (pci->IsDependent()) {
      pbci = m_core.GetBaseEntry(pci);
    }
  }

  switch (evt.GetId()) {
    case wxID_SAVE:
      evt.Enable(m_core.IsDbOpen() && !isFileReadOnly && (m_core.HasDBChanged() || m_core.HaveDBPrefsChanged()));
      break;

    case wxID_SAVEAS:
    case wxID_PROPERTIES:
    case ID_CLEARCLIPBOARD:
    case ID_LIST_VIEW:
    case ID_TREE_VIEW:
    case ID_REPORTSMENU:
    case ID_BACKUP:
    case ID_RESTORE:
#ifndef NO_YUBI
    case ID_YUBIKEY_MNG:
#endif
      evt.Enable(m_core.IsDbOpen());
      break;

    case ID_EXPORTMENU:
    case ID_COMPARE:
      evt.Enable(m_core.IsDbOpen() && m_core.GetNumEntries() != 0);
      break;

    case ID_ADDGROUP:
      evt.Enable((isTreeViewGroupSelected || isTreeViewEmpty || !isTreeViewItemSelected) && !isFileReadOnly && m_core.IsDbOpen());
      break;

    case ID_EXPANDALL:
    case ID_COLLAPSEALL:
      evt.Enable(!isTreeViewEmpty && m_core.IsDbOpen());
      break;

    case ID_RENAME:
      // only allowed if a GROUP item is selected in tree view
      evt.Enable(isTreeViewGroupSelected && !isFileReadOnly);
      break;

    case ID_BROWSEURL:
    case ID_BROWSEURLPLUS:
    case ID_COPYURL:
      evt.Enable(!isTreeViewGroupSelected && pci && !pci->IsFieldValueEmpty(CItemData::URL, pbci));
      break;

    case ID_SENDEMAIL:
    case ID_COPYEMAIL:
      evt.Enable(!isTreeViewGroupSelected && pci &&
          (!pci->IsFieldValueEmpty(CItemData::EMAIL, pbci) ||
          (!pci->IsFieldValueEmpty(CItemData::URL, pbci) && pci->IsURLEmail(pbci))));
      break;

    case ID_COPYUSERNAME:
      evt.Enable(!isTreeViewGroupSelected && pci && !pci->IsFieldValueEmpty(CItemData::USER, pbci));
      break;

    case ID_COPYNOTESFLD:
      evt.Enable(!isTreeViewGroupSelected && pci && !pci->IsFieldValueEmpty(CItemData::NOTES, pbci));
      break;

    case ID_RUNCOMMAND:
    case ID_COPYRUNCOMMAND:
      evt.Enable(!isTreeViewGroupSelected && pci && !pci->IsFieldValueEmpty(CItemData::RUNCMD, pbci));
      break;

    case ID_CREATESHORTCUT:
      evt.Enable(!isTreeViewGroupSelected && !isFileReadOnly && pci &&
          (pci->IsNormal() || pci->IsShortcutBase()));
      break;

    case ID_EDIT:
    case ID_COPYPASSWORD:
    case ID_AUTOTYPE:
    case ID_PASSWORDSUBSET:
    case ID_PASSWORDQRCODE:
      evt.Enable(!isTreeViewGroupSelected && pci);
      break;

    case ID_VIEWATTACHMENT:
      evt.Enable(pci && pci->HasAttRef());
      break;

    case ID_GOTOBASEENTRY:
    case ID_EDITBASEENTRY:
      evt.Enable(!isTreeViewGroupSelected && pci && (pci->IsShortcut() || pci->IsAlias()));
      break;

    case wxID_UNDO:
      evt.Enable(m_core.AnyToUndo());
      break;

    case wxID_REDO:
      evt.Enable(m_core.AnyToRedo());
      break;

    case ID_SYNCHRONIZE:
    case ID_CHANGECOMBO:
      evt.Enable(!isFileReadOnly && m_core.IsDbOpen() && m_core.GetNumEntries() != 0);
      break;

    case wxID_FIND:
      evt.Enable(m_core.IsDbOpen() && m_core.GetNumEntries() != 0);
      break;

    case wxID_ADD:
      evt.Enable(!isFileReadOnly && m_core.IsDbOpen());
      break;

    case wxID_DELETE:
      evt.Enable(!isFileReadOnly && ((pci && !pci->IsProtected()) || isTreeViewGroupSelected));
      break;

    case ID_DUPLICATEENTRY:
      evt.Enable(!isFileReadOnly && pci);
      break;

    case ID_SHOWHIDE_UNSAVED:
      evt.Enable((m_CurrentPredefinedFilter == NONE || m_CurrentPredefinedFilter == UNSAVED) && m_core.IsDbOpen());
      evt.Check(m_CurrentPredefinedFilter == UNSAVED);
      break;

    case ID_SHOW_ALL_EXPIRY:
      evt.Enable((m_CurrentPredefinedFilter == NONE || m_CurrentPredefinedFilter == EXPIRY) &&
       m_core.IsDbOpen() &&
       m_core.GetExpirySize() != 0);
      evt.Check(m_CurrentPredefinedFilter == EXPIRY);
      break;

    case ID_SHOW_LAST_FIND_RESULTS:
      evt.Enable((m_CurrentPredefinedFilter == NONE || m_CurrentPredefinedFilter == LASTFIND) &&
                  m_core.IsDbOpen() &&
                  m_FilterManager.GetFindFilterSize() != 0);
      evt.Check(m_CurrentPredefinedFilter == LASTFIND);
      break;

    case ID_MERGE:
    case ID_IMPORTMENU:
      evt.Enable(!isFileReadOnly && m_core.IsDbOpen());
      break;

    case ID_PROTECT:
      evt.Enable(!isFileReadOnly && pci && !pci->IsShortcut());
      evt.Check(pci && pci->IsProtected());
      break;

    case wxID_CLOSE:
      evt.Enable(m_sysTray->GetTrayStatus() != SystemTray::TrayStatus::CLOSED);
      break;

    case ID_PWDPOLSM:
    case ID_LOCK_SAFE:
      evt.Enable(m_core.IsDbOpen() && !m_sysTray->IsLocked());
      break;

    case ID_UNLOCK_SAFE:
      evt.Enable(m_core.IsDbOpen() && m_sysTray->IsLocked());
      break;

    case ID_FILTERMENU:
      evt.Enable(false); // Mark unimplemented
      break;

    case ID_SUBVIEWSMENU:
      evt.Enable(m_core.IsDbOpen());
      break;

    case ID_CUSTOMIZETOOLBAR:
      evt.Enable(false); // Mark unimplemented
      break;

    case ID_SHOWHIDE_TOOLBAR:
      GetToolBar() ? evt.Check(GetToolBar()->IsShown()) : evt.Check(false);
      break;

    case ID_SHOWHIDE_DRAGBAR:
      GetDragBar() ? evt.Check(GetDragBar()->IsShown()) : evt.Check(false);
      break;

    default:
      break;
  }
}

bool PasswordSafeFrame::IsClosed() const
{
  return (!m_core.IsDbOpen() && m_core.GetNumEntries() == 0 &&
          !m_core.HasDBChanged() && !m_core.AnyToUndo() && !m_core.AnyToRedo());
}

void PasswordSafeFrame::RebuildGUI(const int iView /*= iBothViews*/)
{
  // assumption: the view get updated on switching between each other,
  // so we don't need to update both at the same time
  if (IsTreeView() && (iView & iTreeOnly)) {
    m_guiInfo->Save(this);
    ShowTree();
    m_guiInfo->Restore(this);
  }
  else if (iView & iListOnly) {
    m_guiInfo->Save(this);
    ShowGrid();
    m_guiInfo->Restore(this);
  }
}

void PasswordSafeFrame::RefreshViews()
{
  m_guiInfo->Save(this);

  if (IsTreeView())
    ShowTree();
  else
    ShowGrid();

  m_guiInfo->Restore(this);
  UpdateStatusBar();
}


/**
 * Implements Observer::DatabaseModified(bool)
 */
void PasswordSafeFrame::DatabaseModified(bool modified)
{
  if (!modified)
    return;

  if (m_core.HaveDBPrefsChanged()) {
    // TODO: Anything that needs to be handled here?
  }
  else if (m_core.HasDBChanged()) {  //"else if" => both DB and it's prefs can't change at the same time
    // TODO: Anything that needs to be handled here?
  } else {
    wxFAIL_MSG(wxT("What changed in the DB if not entries or preferences?"));
  }

  // Save Immediately if user requested it
  if (PWSprefs::GetInstance()->GetPref(PWSprefs::SaveImmediately)) {
    SaveImmediately();
  }
}

/**
 * Implements Observer::UpdateGUI(UpdateGUICommand::GUI_Action, const pws_os::CUUID&, CItemData::FieldType)
 */
void PasswordSafeFrame::UpdateGUI(UpdateGUICommand::GUI_Action ga, const CUUID & WXUNUSED(entry_uuid), CItemData::FieldType WXUNUSED(ft))
{
  // Callback from PWScore if GUI needs updating

  // "bUpdateGUI" is only used by GUI_DELETE_ENTRY when called as part
  // of the Edit Entry Command where the entry is deleted and then added and
  // the GUI should not be updated until after the Add.

  // TODO: bUpdateGUI processing in PasswordSafeFrame::UpdateGUI

#ifdef NOTYET
  PWSprefs *prefs = PWSprefs::GetInstance();
#endif
  switch (ga) {
    case UpdateGUICommand::GUI_ADD_ENTRY:
      // Handled by individual views.
      break;
    case UpdateGUICommand::GUI_DELETE_ENTRY:
      // Handled by individual views.
      break;
    case UpdateGUICommand::GUI_REFRESH_TREE:
      // Caused by Database preference changed about showing username and/or
      // passwords in the Tree View
      RebuildGUI(iTreeOnly);
      break;
    case UpdateGUICommand::GUI_REDO_MERGESYNC:
    case UpdateGUICommand::GUI_UNDO_MERGESYNC:
    case UpdateGUICommand::GUI_REDO_IMPORT:
    case UpdateGUICommand::GUI_UNDO_IMPORT:
      // During these processes, many entries may be added/removed
      // To stop the UI going nuts, updates to the UI are suspended until
      // the action is complete - when these calls are then sent
      RebuildGUI();
      break;
    case UpdateGUICommand::GUI_UPDATE_STATUSBAR:
      // TODO: UpdateToolBarDoUndo();
      UpdateStatusBar();
      break;
    case UpdateGUICommand::GUI_REFRESH_ENTRYFIELD:
      // Handled by individual views.
      break;
    case UpdateGUICommand::GUI_REFRESH_ENTRYPASSWORD:
      // Handled by individual views.
      break;
    case UpdateGUICommand::GUI_DB_PREFERENCES_CHANGED:
    {
      wxCommandEvent evt(wxEVT_GUI_DB_PREFS_CHANGE, wxID_ANY);
      evt.ResumePropagation(wxEVENT_PROPAGATE_MAX); //let it propagate through the entire window tree
      GetEventHandler()->ProcessEvent(evt);
      RefreshViews();
      break;
    }
    default:
      break;
  }
}

void PasswordSafeFrame::OnUpdateClearRecentDBHistory(wxUpdateUIEvent& evt)
{
  evt.Enable(wxGetApp().recentDatabases().GetCount() > 0);
}

void PasswordSafeFrame::Execute(Command *pcmd, PWScore *pcore /*= nullptr*/)
{
  if (pcore == nullptr)
    pcore = &m_core;
  pcore->Execute(pcmd);
}

bool PasswordSafeFrame::SaveAndClearDatabaseOnLock()
{
  //Save UI elements first
  PWSprefs::GetInstance()->SaveApplicationPreferences();
  PWSprefs::GetInstance()->SaveShortcuts();
  m_savedDBPrefs = towxstring(PWSprefs::GetInstance()->Store());

  //Save alerts the user
  if (!m_core.HasDBChanged() || Save() == PWScore::SUCCESS) {
    // Do NOT call PWScore::ReInit as it will clear commands preventing the
    // user from undoing commands after unlocking DB

    // Clear all internal variables EXCEPT command and DB state vectors
    m_core.ClearDBData();

    // clear the application data before locking
    ClearAppData();
    return true;
  }
  return false;
}

bool PasswordSafeFrame::ReloadDatabase(const StringX& password)
{
  return Load(password) == PWScore::SUCCESS;
}

void PasswordSafeFrame::CleanupAfterReloadFailure(bool tellUser)
{
  //TODO: must clear db prefs, UI states, RUE items etc here
  if (tellUser) {
    wxMessageBox(wxString(_("Could not re-load database: ")) << towxstring(m_core.GetCurFile()),
                     _("Error re-loading last database"), wxOK|wxICON_ERROR, this);
  }
  m_sysTray->SetTrayStatus(SystemTray::TrayStatus::CLOSED);
}

/**
 * Unlock database
 * @param restoreUI restore opened windows after unlock
 * @param iconizeOnFailure will iconize if this parameters set to true and
 *   VerifySafeCombination() failed
*/
void PasswordSafeFrame::UnlockSafe(bool restoreUI, bool iconizeOnFailure)
{
  wxMutexTryLocker unlockMutex(m_dblockMutex);
  if (!unlockMutex.IsAcquired()){
    // Another (un)lock in progress, no need to process
    pws_os::Trace0(L"Skipped parallel attempt to unlock DB");
    return;
  }

  if (m_sysTray->IsLocked()) {

    SafeCombinationPromptDlg scp(nullptr, m_core, towxstring(m_core.GetCurFile()));

    switch (scp.ShowModal()) {
      case (wxID_OK):
      {
        if (ReloadDatabase(scp.GetPassword())) {
          m_sysTray->SetTrayStatus(SystemTray::TrayStatus::UNLOCKED);
        }
        else {
          CleanupAfterReloadFailure(true);
          return;
        }
        break;
      }
      case (wxID_EXIT):
      {
        m_exitFromMenu = true; // not an exit request from menu, but OnCloseWindow requires this
        wxCommandEvent event(wxEVT_CLOSE_WINDOW);
        wxPostEvent(this, event);
        return;
      }
      default:
      {
        if (!IsIconized() && iconizeOnFailure)
          Iconize();
        return;
      }
    }

    if (m_savedDBPrefs != wxEmptyString) {
      PWSprefs::GetInstance()->Load(tostringx(m_savedDBPrefs));
      m_savedDBPrefs = wxEmptyString;
    }
  }

  if (restoreUI) {
    if (!IsShown()) {
      ShowWindowRecursively(hiddenWindows);
    }
    if (IsIconized()) {
      Iconize(false);
    }
    Show(true); //show the grid/tree
    Raise();
    // Without this, modal dialogs like msgboxes lose focus and we end up in a different message loop than theirs.
    // See https://sourceforge.net/tracker/?func=detail&aid=3537985&group_id=41019&atid=429579
    wxSafeYield();
  }
  else if (IsShown()) { /* if it is somehow visible, show it correctly */
    Show(true);
  }

  CreateMenubar(); // Recreate menubar to replace menu item 'Unlock Safe' by 'Lock Safe'
}

void PasswordSafeFrame::SetFocus()
{
  if (IsTreeView())
    m_tree->SetFocus();
  else
    m_grid->SetFocus();
}

void PasswordSafeFrame::OnIconize(wxIconizeEvent& evt) {

  // If database was closed than there is nothing to do
  if (!m_core.IsDbOpen()) {
    return;
  }

  const bool beingIconized =
#if wxCHECK_VERSION(2,9,0)
    evt.IsIconized();
#else
    evt.Iconized();
#endif
  pws_os::Trace(L"OnIconize: beingIconized=%d\n", beingIconized);
  // Because  LockDB and UnlockSafe hide/update main or "icon" window, they may
  // produce new iconize events before current processing finished, so to
  // prevent multiple calls we use CallAfter if available
  if (beingIconized) {
    const bool lockOnMinimize = PWSprefs::GetInstance()->GetPref(PWSprefs::DatabaseClear);
    // if not already locked, lock it if "lock on minimize" is set
    if (m_sysTray->GetTrayStatus() == SystemTray::TrayStatus::UNLOCKED && lockOnMinimize) {
      pws_os::Trace0(L"OnIconize: will LockDb()\n");
#if wxCHECK_VERSION(2,9,5)
      CallAfter(&PasswordSafeFrame::LockDb);
#else
      LockDb();
#endif
      if (PWSprefs::GetInstance()->GetPref(PWSprefs::ClearClipboardOnMinimize)) {
        Clipboard::GetInstance()->ClearCBData();
      }
    }
    else {
      m_guiInfo->Save(this);
    }
  }
  else{
#if wxCHECK_VERSION(2,9,5)
      CallAfter(&PasswordSafeFrame::UnlockSafe, true, true);
#else
      UnlockSafe(true, true);
#endif
  }
}

void PasswordSafeFrame::TryIconize(int attempts)
{
  while ( !IsIconized() && attempts-- ) {
    pws_os::Trace0(L"TryIconize attempt\n");
    //don't loop here infinitely while IsIconized
    //"The window manager may choose to ignore the [gdk_window_iconify] request, but normally will honor it."
    Iconize();
    wxSafeYield();
  }
}

void PasswordSafeFrame::HideUI(bool lock)
{
  wxMutexTryLocker hideMutex(m_hideUIMutex);
  if (!hideMutex.IsAcquired()) {
    // UI hide is in progress, no need to process
    pws_os::Trace0(L"Skipped parallel attempt to hide UI");
    return;
  }

  // As HideUI doesn't produce iconize event we need to process clear clipboard options
  if (PWSprefs::GetInstance()->GetPref(PWSprefs::ClearClipboardOnMinimize)) {
    Clipboard::GetInstance()->ClearCBData();
  }

  m_guiInfo->Save(this);
  wxGetApp().SaveFrameCoords();

  if (lock && m_sysTray->GetTrayStatus() == SystemTray::TrayStatus::UNLOCKED) {
    LockDb();
  }

  // Don't call (try)iconize() here, otherwise we'll have two iconization events
  // (iconize and restore few moments after) [wxgtk 3.0.2]
  // skipping wxEVT_ICONIZE while we are here doesn't help

  if (PWSprefs::GetInstance()->GetPref(PWSprefs::UseSystemTray)) {
    //We should not have to show up the icon manually if m_sysTray
    //can be notified of changes to PWSprefs::UseSystemTray
    m_sysTray->ShowIcon();
    hiddenWindows.clear();
    HideWindowRecursively(this, hiddenWindows);

  }
}

void PasswordSafeFrame::IconizeOrHideAndLock()
{
  if (PWSprefs::GetInstance()->GetPref(PWSprefs::UseSystemTray)) {
    HideUI(true);
  }
  else {
    TryIconize();
  }

  // If not already locked by HideUI or OnIconize due to user preference than do it now
  if (m_sysTray->GetTrayStatus() == SystemTray::TrayStatus::UNLOCKED) {
    LockDb();
  }
}

void PasswordSafeFrame::LockDb()
{
  wxMutexTryLocker lockMutex(m_dblockMutex);
  if (!lockMutex.IsAcquired()){
    // Another (un)lock in progress, no need to process
    pws_os::Trace0(L"Skipped parallel attempt to lock DB");
    return;
  }

  m_guiInfo->Save(this);
  if (SaveAndClearDatabaseOnLock()) {
    m_sysTray->SetTrayStatus(SystemTray::TrayStatus::LOCKED);

    CreateMenubar(); // Recreate menubar to replace menu item 'Lock Safe' by 'Unlock Safe'
  }
}

void PasswordSafeFrame::SetTrayStatus(bool locked)
{
  m_sysTray->SetTrayStatus(locked ? SystemTray::TrayStatus::LOCKED : SystemTray::TrayStatus::UNLOCKED);
}

void PasswordSafeFrame::SetTrayClosed()
{
  m_sysTray->SetTrayStatus(SystemTray::TrayStatus::CLOSED);
}

void PasswordSafeFrame::ShowTrayIcon()
{
  if (m_sysTray)
    m_sysTray->ShowIcon();
}

void PasswordSafeFrame::OnOpenRecentDB(wxCommandEvent& evt)
{
  RecentDbList& db = wxGetApp().recentDatabases();
  const size_t index = evt.GetId() - db.GetBaseId();
  const wxString dbfile = db.GetHistoryFile(index);
  switch(Open(dbfile))
  {
    case PWScore::SUCCESS:
      m_core.ResumeOnDBNotification();
      CreateMenubar();  // Recreate the menu with updated list of most recently used DBs
      break;

    case PWScore::ALREADY_OPEN:
      break;            // An already open DB doesn't need to be removed from history

    case PWScore::USER_CANCEL:
      //In case the file doesn't exist, user will have to cancel
      //the safe combination entry box.  In that call, fall through
      //to the default case of removing the file from history
      if (pws_os::FileExists(stringT(dbfile)))
        break;          // An existing file doesn't need to be removed from history

      //[[fallthrough]];
    default:
      wxMessageBox(wxString(_("There was an error loading the database: ")) << dbfile,
                     _("Could not load database"), wxOK|wxICON_ERROR, this);
      db.RemoveFileFromHistory(index);
      CreateMenubar();  // Update menu so that not existing file doesn't appear on File menu anymore
      break;
  }
}

void PasswordSafeFrame::ViewReport(CReport& rpt)
{
  ViewReportDlg vr(this, &rpt);
  vr.ShowModal();
}

void PasswordSafeFrame::OnVisitWebsite(wxCommandEvent&)
{
  wxLaunchDefaultBrowser(L"https://pwsafe.org");
}

void PasswordSafeFrame::UpdateStatusBar()
{

  if (m_core.IsDbOpen()) {
    wxString text;
    // SB_DBLCLICK pane is set per selected entry, not here

    m_statusBar->SetStatusText(m_LastClipboardAction, StatusBar::Field::CLIPBOARDACTION);

    text  = m_core.HasDBChanged()       ? wxT("*") : wxT(" ");
    text += m_core.HaveDBPrefsChanged() ? wxT("°") : wxT(" ");
    m_statusBar->SetStatusText(text, StatusBar::Field::MODIFIED);

    text = m_core.IsReadOnly() ? wxT("R-O") : wxT("R/W");
    m_statusBar->SetStatusText(text, StatusBar::Field::READONLY);

    text.Clear(); text <<  m_core.GetNumEntries();
    m_statusBar->SetStatusText(text, StatusBar::Field::NUM_ENT);

    text = m_bFilterActive ? wxT("[F]") : wxT("   ");
    m_statusBar->SetStatusText(text, StatusBar::Field::FILTER);
  }
  else { // no open file
    m_statusBar->SetStatusText(_(PWSprefs::GetDCAdescription(-1)), StatusBar::Field::DOUBLECLICK);
    m_statusBar->SetStatusText(wxEmptyString, StatusBar::Field::CLIPBOARDACTION);
    m_statusBar->SetStatusText(wxEmptyString, StatusBar::Field::MODIFIED);
    m_statusBar->SetStatusText(wxEmptyString, StatusBar::Field::READONLY);
    m_statusBar->SetStatusText(wxEmptyString, StatusBar::Field::NUM_ENT);
    m_statusBar->SetStatusText(wxEmptyString, StatusBar::Field::FILTER);
  }
}

void PasswordSafeFrame::UpdateMenuBar()
{
  // Add code here for more complex update logic on menu items, otherwise use OnUpdateUI
}

void PasswordSafeFrame::UpdateLastClipboardAction(const CItemData::FieldType field)
{
  // Note use of CItemData::RESERVED for indicating in the
  // Status bar that an old password has been copied
  m_LastClipboardAction = wxEmptyString;
  switch (field) {
    case CItemData::FieldType::GROUP:
      m_LastClipboardAction = _("Group copied ") + wxDateTime::Now().FormatTime();
      break;
    case CItemData::FieldType::TITLE:
      m_LastClipboardAction = _("Title copied ") + wxDateTime::Now().FormatTime();
      break;
    case CItemData::FieldType::USER:
      m_LastClipboardAction = _("User copied ") + wxDateTime::Now().FormatTime();
      break;
    case CItemData::FieldType::PASSWORD:
      m_LastClipboardAction = _("Pswd copied ") + wxDateTime::Now().FormatTime();
      break;
    case CItemData::FieldType::NOTES:
      m_LastClipboardAction = _("Notes copied ") + wxDateTime::Now().FormatTime();
      break;
    case CItemData::FieldType::URL:
      m_LastClipboardAction = _("URL copied ") + wxDateTime::Now().FormatTime();
      break;
    case CItemData::FieldType::AUTOTYPE:
      m_LastClipboardAction = _("Autotype copied ") + wxDateTime::Now().FormatTime();
      break;
    case CItemData::FieldType::RUNCMD:
      m_LastClipboardAction = _("RunCmd copied ") + wxDateTime::Now().FormatTime();
      break;
    case CItemData::FieldType::EMAIL:
      m_LastClipboardAction = _("Email copied ") + wxDateTime::Now().FormatTime();
      break;
    case CItemData::FieldType::PWHIST:
      m_LastClipboardAction = _("Password History copied " ) + wxDateTime::Now().FormatTime();
      break;
    case CItemData::FieldType::RESERVED:
      m_LastClipboardAction = _("Old Pswd copied ") + wxDateTime::Now().FormatTime();
      break;
    case CItemData::FieldType::END:
      m_LastClipboardAction = wxEmptyString;
      break;
    default:
      ASSERT(0);
      return;
  }

  m_LastAction = field;
  UpdateStatusBar();
}

void PasswordSafeFrame::UpdateSelChanged(const CItemData *pci)
{
  int16 dca = -1;

  if (pci != nullptr) {
    pci->GetDCA(dca);
    if (dca == -1)
      dca = PWSprefs::GetInstance()->GetPref(PWSprefs::DoubleClickAction);
  }
  m_statusBar->SetStatusText(_(PWSprefs::GetDCAdescription(dca)), StatusBar::Field::DOUBLECLICK);
}

void PasswordSafeFrame::ChangeFontPreference(const PWSprefs::StringPrefs fontPreference)
{
  wxFont currentFont(towxstring(PWSprefs::GetInstance()->GetPref(fontPreference)));
  wxFont newFont;

  switch (fontPreference)
  {
  case (PWSprefs::StringPrefs::TreeFont):
  {
    if (!currentFont.IsOk()) {
      currentFont = IsTreeView() ? m_tree->GetFont() : m_grid->GetDefaultCellFont();
    }

    newFont = ::wxGetFontFromUser(this, currentFont, _("Select Tree/List display font"));

    if (newFont.IsOk()) {
      if (IsTreeView()) {
        m_tree->SetFont(newFont);
        m_tree->Show(); // Updates the tree items font
      }
      else {
        m_grid->SetDefaultCellFont(newFont);
        // TODO: Update grid font
        // Grid items font doesn't get updated by just calling Show() :-|
      }
    }
  }
  break;

  case (PWSprefs::StringPrefs::AddEditFont):
    newFont = ::wxGetFontFromUser(this, currentFont.IsOk() ? currentFont : GetFont(), _("Select Add/Edit display font"));
    break;

  case (PWSprefs::StringPrefs::PasswordFont):
    newFont = ::wxGetFontFromUser(this, currentFont.IsOk() ? currentFont : GetFont(), _("Select Password display font"));
    break;

  case (PWSprefs::StringPrefs::NotesFont):
    newFont = ::wxGetFontFromUser(this, currentFont.IsOk() ? currentFont : GetFont(), _("Select Notes display font"));
    break;

  case (PWSprefs::StringPrefs::VKeyboardFontName):
    newFont = ::wxGetFontFromUser(this, currentFont.IsOk() ? currentFont : GetFont(), _("Select Virtual Keyboard font"));
    break;

  default:
    return;
  }

  if (newFont.IsOk())
  {
    PWSprefs::GetInstance()->SetPref(fontPreference, tostringx(newFont.GetNativeFontInfoDesc()));
  }
}


void PasswordSafeFrame::SetFilterFindEntries(UUIDVector *pvFoundUUIDs)
{
  // If the "Show entries from last Find" is active, we should not change this
  // as it will override the results - say if the user maximizes the PWS window
  // and the view is Refreshed using these entries rather than the original list
  // used for the filter.
  if (!(m_bFilterActive && m_CurrentPredefinedFilter == LASTFIND))
    m_FilterManager.SetFilterFindEntries(pvFoundUUIDs);
}

void PasswordSafeFrame::ResetFilters()
{ // Tidy up filters
  CurrentFilter().Empty();
  m_bFilterActive = false;
  m_CurrentPredefinedFilter = NONE;
  m_FilterManager.SetFindFilter(false);
  m_FilterManager.SetFilterFindEntries(nullptr);
}

  //-----------------------------------------------------------------
  // Remove all DialogBlock-generated stubs below this line, as we
  // already have them implemented in main*.cpp
  // (how to get DB to stop generating them??)
  //-----------------------------------------------------------------
