/*
 * Copyright (c) 2003-2012 Rony Shapiro <ronys@users.sourceforge.net>.
 * 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"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/filename.h"

////@begin includes
#include "safecombinationchange.h"
#include "about.h"
#include "PWSgrid.h"
#include "PWStree.h"
////@end includes
#include "PWSgridtable.h"
#include "safecombinationsetup.h"

#include "passwordsafeframe.h"
#include "safecombinationprompt.h"
#include "properties.h"
#include "core/PWSprefs.h"
#include "core/PWSdirs.h"
#include "PasswordSafeSearch.h"
#include "pwsclip.h"
#include "SystemTray.h"
#include "wxutils.h"
#include "guiinfo.h"
#include <wx/clipbrd.h>
#include "pwsafeapp.h"
#include "../../os/file.h"
#include "./ImportTextDlg.h"
#include "./ImportXmlDlg.h"
#include "./ExportTextWarningDlg.h"
#include "../../os/sleep.h"
#include "../../core/XML/XMLDefs.h"
#include "./ViewReport.h"
#include "core/XML/XMLDefs.h"  // Required if testing "USE_XML_LIBRARY"
#include <wx/fontdlg.h>
#include "./PWSDragBar.h"
#include "./MergeDlg.h"
#include <algorithm>
#include "./PwsSync.h"
#include "./SystemTrayMenuId.h"
#include "./CompareDlg.h"
#include "../../core/core.h"

// main toolbar images
#include "./PwsToolbarButtons.h"
#include "./pwsmenushortcuts.h"

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

////@begin XPM images
////@end XPM images
#include "./graphics/cpane.xpm"

using pws_os::CUUID;

/*!
 * PasswordSafeFrame type definition
 */

IMPLEMENT_CLASS( PasswordSafeFrame, wxFrame )

DEFINE_EVENT_TYPE(wxEVT_DB_PREFS_CHANGE)
DEFINE_EVENT_TYPE(wxEVT_GUI_DB_PREFS_CHANGE)

/*!
 * PasswordSafeFrame event table definition
 */

BEGIN_EVENT_TABLE( PasswordSafeFrame, wxFrame )

////@begin PasswordSafeFrame event table entries
  EVT_CLOSE( PasswordSafeFrame::OnCloseWindow )

  EVT_MENU( wxID_NEW, PasswordSafeFrame::OnNewClick )

  EVT_MENU( wxID_OPEN, PasswordSafeFrame::OnOpenClick )

  EVT_MENU( wxID_CLOSE, PasswordSafeFrame::OnCloseClick )

  EVT_MENU( wxID_SAVE, PasswordSafeFrame::OnSaveClick )

  EVT_MENU( wxID_SAVEAS, PasswordSafeFrame::OnSaveAsClick )

  EVT_MENU( wxID_PROPERTIES, PasswordSafeFrame::OnPropertiesClick )

  EVT_MENU( wxID_EXIT, PasswordSafeFrame::OnExitClick )

  EVT_MENU( wxID_ADD, PasswordSafeFrame::OnAddClick )

  EVT_MENU( ID_EDIT, PasswordSafeFrame::OnEditClick )

  EVT_MENU( wxID_DELETE, PasswordSafeFrame::OnDeleteClick )

  EVT_MENU( ID_CLEARCLIPBOARD, PasswordSafeFrame::OnClearclipboardClick )

  EVT_MENU( ID_COPYPASSWORD, PasswordSafeFrame::OnCopypasswordClick )

  EVT_MENU( ID_COPYUSERNAME, PasswordSafeFrame::OnCopyusernameClick )

  EVT_MENU( ID_COPYNOTESFLD, PasswordSafeFrame::OnCopynotesfldClick )

  EVT_MENU( ID_COPYURL, PasswordSafeFrame::OnCopyurlClick )

  EVT_MENU( ID_LIST_VIEW, PasswordSafeFrame::OnListViewClick )

  EVT_MENU( ID_TREE_VIEW, PasswordSafeFrame::OnTreeViewClick )

  EVT_MENU( ID_CHANGECOMBO, PasswordSafeFrame::OnChangePasswdClick )

  EVT_MENU( ID_OPTIONS_M, PasswordSafeFrame::OnOptionsMClick )

  EVT_MENU( ID_PWDPOLSM, PasswordSafeFrame::OnPwdPolsMClick )

  EVT_MENU( wxID_ABOUT, PasswordSafeFrame::OnAboutClick )

////@end PasswordSafeFrame event table entries
  EVT_MENU( wxID_FIND, PasswordSafeFrame::OnFindClick )

  EVT_MENU( ID_EDITMENU_FIND_NEXT, PasswordSafeFrame::OnFindNext )

  EVT_MENU( ID_EDITMENU_FIND_PREVIOUS, PasswordSafeFrame::OnFindPrevious )

  EVT_MENU( ID_BROWSEURL, PasswordSafeFrame::OnBrowseURL )

  EVT_MENU( ID_BROWSEURLPLUS, PasswordSafeFrame::OnBrowseUrlAndAutotype )

  EVT_MENU( ID_SENDEMAIL, PasswordSafeFrame::OnSendEmail )

  EVT_MENU( ID_RUNCOMMAND, PasswordSafeFrame::OnRunCommand )

  EVT_MENU( ID_AUTOTYPE, PasswordSafeFrame::OnAutoType )

  EVT_MENU( ID_GOTOBASEENTRY, PasswordSafeFrame::OnGotoBase )

  EVT_MENU( ID_EDITBASEENTRY, PasswordSafeFrame::OnEditBase )

  EVT_MENU( ID_CREATESHORTCUT, PasswordSafeFrame::OnCreateShortcut )

  EVT_MENU( ID_DUPLICATEENTRY, PasswordSafeFrame::OnDuplicateEntry )
  
  EVT_MENU( ID_IMPORT_PLAINTEXT, PasswordSafeFrame::OnImportText )

  EVT_MENU( ID_IMPORT_KEEPASS, PasswordSafeFrame::OnImportKeePass )

  EVT_MENU( ID_IMPORT_XML, PasswordSafeFrame::OnImportXML )

  EVT_MENU( ID_EXPORT2OLD1XFORMAT, PasswordSafeFrame::OnExportVx )

  EVT_MENU( ID_EXPORT2V2FORMAT, PasswordSafeFrame::OnExportVx )

  EVT_MENU( ID_EXPORT2PLAINTEXT, PasswordSafeFrame::OnExportPlainText )

  EVT_MENU( ID_EXPORT2XML, PasswordSafeFrame::OnExportXml )

  EVT_MENU(wxID_UNDO,          PasswordSafeFrame::OnUndo )
  EVT_MENU(wxID_REDO,          PasswordSafeFrame::OnRedo )

  EVT_MENU(ID_EXPANDALL,       PasswordSafeFrame::OnExpandAll )
  EVT_MENU(ID_COLLAPSEALL,     PasswordSafeFrame::OnCollapseAll )

  EVT_MENU(ID_CHANGETREEFONT,  PasswordSafeFrame::OnChangeTreeFont )
  EVT_MENU(ID_CHANGEPSWDFONT,  PasswordSafeFrame::OnChangePasswordFont )

  EVT_MENU(ID_SHOWHIDE_TOOLBAR,  PasswordSafeFrame::OnShowHideToolBar )
  EVT_MENU(ID_SHOWHIDE_DRAGBAR,  PasswordSafeFrame::OnShowHideDragBar )
  EVT_MENU( ID_TOOLBAR_NEW,     PasswordSafeFrame::OnChangeToolbarType )
  EVT_MENU( ID_TOOLBAR_CLASSIC, PasswordSafeFrame::OnChangeToolbarType )

  EVT_MENU(ID_MERGE,            PasswordSafeFrame::OnMergeAnotherSafe )
  EVT_MENU(ID_SYNCHRONIZE,      PasswordSafeFrame::OnSynchronize )
  EVT_MENU(ID_COMPARE,          PasswordSafeFrame::OnCompare )

  EVT_MENU( ID_MENU_CLEAR_MRU, PasswordSafeFrame::OnClearRecentHistory )
  EVT_UPDATE_UI( ID_MENU_CLEAR_MRU, PasswordSafeFrame::OnUpdateClearRecentDBHistory )
  
  EVT_MENU(ID_BACKUP,      PasswordSafeFrame::OnBackupSafe)
  EVT_MENU(ID_RESTORE,     PasswordSafeFrame::OnRestoreSafe)

  EVT_ICONIZE(PasswordSafeFrame::OnIconize)

  EVT_UPDATE_UI(wxID_SAVE,          PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_ADDGROUP,        PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_RENAME,          PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_COLLAPSEALL,     PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_EXPANDALL,       PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_GOTOBASEENTRY,   PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_EDITBASEENTRY,   PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_BROWSEURL,       PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_BROWSEURLPLUS,   PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_COPYURL,         PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_SENDEMAIL,       PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_COPYEMAIL,       PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_COPYUSERNAME,    PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_COPYNOTESFLD,    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_DUPLICATEENTRY,  PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_COPYPASSWORD,    PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_AUTOTYPE,        PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_EDIT,            PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_PASSWORDSUBSET,  PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(wxID_UNDO,          PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(wxID_REDO,          PasswordSafeFrame::OnUpdateUI )
  EVT_UPDATE_UI(ID_SYNCHRONIZE,     PasswordSafeFrame::OnUpdateUI )
END_EVENT_TABLE()

static void DisplayFileWriteError(int rc, const StringX &fname);

/*!
 * PasswordSafeFrame constructors
 */

PasswordSafeFrame::PasswordSafeFrame(PWScore &core)
: m_core(core), m_currentView(GRID), m_search(0), m_sysTray(new SystemTray(this)), m_exitFromMenu(false),
  m_RUEList(core), m_guiInfo(new GUIInfo), m_bTSUpdated(false), m_savedDBPrefs(wxEmptyString),
  m_bUnlocking(false)
{
    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(GRID), m_search(0), m_sysTray(new SystemTray(this)), m_exitFromMenu(false),
    m_RUEList(core), m_guiInfo(new GUIInfo), m_bTSUpdated(false), m_savedDBPrefs(wxEmptyString),
    m_bUnlocking(false)
{
    Init();
    if (PWSprefs::GetInstance()->GetPref(PWSprefs::AlwaysOnTop))
      style |= wxSTAY_ON_TOP;
    Create( parent, id, caption, pos, size, style );
}


/*!
 * 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 );

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

void PasswordSafeFrame::CreateDragBar()
{
  wxSizer* origSizer = GetSizer();
  
  wxASSERT(origSizer);
  wxASSERT(origSizer->IsKindOf(wxBoxSizer(wxVERTICAL).GetClassInfo()));
  wxASSERT(((wxBoxSizer*)origSizer)->GetOrientation() == wxVERTICAL);

  PWSDragBar* dragbar = new PWSDragBar(this);
  origSizer->Insert(0, dragbar);
  
  const bool bShow = PWSprefs::GetInstance()->GetPref(PWSprefs::ShowDragbar);
  if (!bShow) {
    dragbar->Hide();
  }
  GetMenuBar()->Check(ID_SHOWHIDE_DRAGBAR, bShow);
}

/*!
 * PasswordSafeFrame destructor
 */

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

  delete m_sysTray;
  m_sysTray = 0;

  delete m_guiInfo;
  m_guiInfo = 0;
  
  m_core.ClearData();
}


/*!
 * Member initialisation
 */

void PasswordSafeFrame::Init()
{
  std::bitset<UIInterFace::NUM_SUPPORTED> bsSupportedFunctions;
  bsSupportedFunctions.set(UIInterFace::DATABASEMODIFIED);
  bsSupportedFunctions.set(UIInterFace::UPDATEGUI);
  bsSupportedFunctions.set(UIInterFace::GUISETUPDISPLAYINFO);
  bsSupportedFunctions.set(UIInterFace::GUIREFRESHENTRY);
  //bsSupportedFunctions.set(UIInterFace::UPDATEWIZARD);

  m_core.SetUIInterFace(this, UIInterFace::NUM_SUPPORTED, bsSupportedFunctions);

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


/*!
 * Control creation for PasswordSafeFrame
 */

void PasswordSafeFrame::CreateControls()
{    
  PWSprefs *prefs = PWSprefs::GetInstance();
  const StringX lastView = prefs->GetPref(PWSprefs::LastView);
  m_currentView = (lastView == _T("list")) ? GRID : TREE;

  PasswordSafeFrame* itemFrame1 = this;

  wxMenuBar* menuBar = new wxMenuBar;
  wxMenu* itemMenu3 = new wxMenu;
  itemMenu3->Append(wxID_NEW);
  itemMenu3->Append(wxID_OPEN);
  itemMenu3->Append(wxID_CLOSE);
  itemMenu3->AppendSeparator();
  itemMenu3->Append(ID_MENU_CLEAR_MRU, _("Clear Recent Safe List"), _T(""), wxITEM_NORMAL);
  itemMenu3->AppendSeparator();
  itemMenu3->Append(wxID_SAVE);
  itemMenu3->Append(wxID_SAVEAS);
  itemMenu3->AppendSeparator();
  wxMenu* itemMenu13 = new wxMenu;
  itemMenu13->Append(ID_EXPORT2OLD1XFORMAT, _("v&1.x format..."), _T(""), wxITEM_NORMAL);
  itemMenu13->Append(ID_EXPORT2V2FORMAT, _("v&2 format..."), _T(""), wxITEM_NORMAL);
  itemMenu13->Append(ID_EXPORT2PLAINTEXT, _("&Plain Text (tab separated)..."), _T(""), wxITEM_NORMAL);
  itemMenu13->Append(ID_EXPORT2XML, _("&XML format..."), _T(""), wxITEM_NORMAL);
  itemMenu3->Append(ID_EXPORTMENU, _("Export &To"), itemMenu13);
  wxMenu* itemMenu18 = new wxMenu;
  itemMenu18->Append(ID_IMPORT_PLAINTEXT, _("&Plain Text..."), _T(""), wxITEM_NORMAL);
  itemMenu18->Append(ID_IMPORT_XML, _("&XML format..."), _T(""), wxITEM_NORMAL);
  itemMenu18->Append(ID_IMPORT_KEEPASS, _("&KeePass..."), _T(""), wxITEM_NORMAL);
  itemMenu3->Append(ID_IMPORTMENU, _("Import &From"), itemMenu18);
  itemMenu3->Append(ID_MERGE, _("Merge..."), _T(""), wxITEM_NORMAL);
  itemMenu3->Append(ID_COMPARE, _("Compare..."), _T(""), wxITEM_NORMAL);
  itemMenu3->Append(ID_SYNCHRONIZE, _("S&ynchronize..."), _T(""), wxITEM_NORMAL);
  itemMenu3->AppendSeparator();
  itemMenu3->Append(wxID_PROPERTIES);
  itemMenu3->AppendSeparator();
  itemMenu3->Append(wxID_EXIT);
  menuBar->Append(itemMenu3, _("&File"));
  wxGetApp().recentDatabases().UseMenu(itemMenu3);
  wxGetApp().recentDatabases().AddFilesToMenu(itemMenu3);  //must add existing history entries manually.
  wxMenu* itemMenu28 = new wxMenu;
  itemMenu28->Append(wxID_ADD, _("&Add Entry...\tCtrl+A"), _T(""), wxITEM_NORMAL);
  itemMenu28->Append(ID_EDIT, _("Edit/&View Entry...\tCtrl+Enter"), _T(""), wxITEM_NORMAL);
  itemMenu28->Append(wxID_DELETE, _("&Delete Entry\tDel"), _T(""), wxITEM_NORMAL);
  itemMenu28->Append(ID_RENAME, _("Rename Entry\tF2"), _T(""), wxITEM_NORMAL);
  itemMenu28->Append(wxID_FIND, _("&Find Entry...\tCtrl+F"), _T(""), wxITEM_NORMAL);
  itemMenu28->Append(ID_DUPLICATEENTRY, _("&Duplicate Entry\tCtrl+D"), _T(""), wxITEM_NORMAL);
  itemMenu28->AppendSeparator();
  itemMenu28->Append(ID_ADDGROUP, _("Add Group"), _T(""), wxITEM_NORMAL);
  itemMenu28->AppendSeparator();
  itemMenu28->Append(wxID_UNDO);
  itemMenu28->Append(wxID_REDO);
  itemMenu28->Append(ID_CLEARCLIPBOARD, _("C&lear Clipboard\tCtrl+Del"), _T(""), wxITEM_NORMAL);
  itemMenu28->AppendSeparator();
  itemMenu28->Append(ID_COPYPASSWORD, _("&Copy Password to Clipboard\tCtrl+C"), _T(""), wxITEM_NORMAL);
  itemMenu28->Append(ID_COPYUSERNAME, _("Copy &Username to Clipboard\tCtrl+U"), _T(""), wxITEM_NORMAL);
  itemMenu28->Append(ID_COPYNOTESFLD, _("Copy &Notes to Clipboard\tCtrl+G"), _T(""), wxITEM_NORMAL);
  itemMenu28->Append(ID_COPYURL, _("Copy URL to Clipboard\tCtrl+Alt+L"), _T(""), wxITEM_NORMAL);
  itemMenu28->Append(ID_BROWSEURL, _("&Browse to URL\tCtrl+L"), _T(""), wxITEM_NORMAL);
  itemMenu28->Append(ID_AUTOTYPE, _("Perform Auto&type\tCtrl+T"), _T(""), wxITEM_NORMAL);
  itemMenu28->Append(ID_GOTOBASEENTRY, _("Go to Base entry"), _T(""), wxITEM_NORMAL);
  menuBar->Append(itemMenu28, _("&Edit"));
  wxMenu* itemMenu47 = new wxMenu;
  itemMenu47->Append(ID_LIST_VIEW, _("Flattened &List"), _T(""), wxITEM_RADIO);
  itemMenu47->Append(ID_TREE_VIEW, _("Nested &Tree"), _T(""), wxITEM_RADIO);
  itemMenu47->AppendSeparator();
  itemMenu47->Append(ID_SHOWHIDE_TOOLBAR, _("Toolbar &visible"), _T(""), wxITEM_CHECK);
  itemMenu47->AppendRadioItem(ID_TOOLBAR_NEW, _("&New Toolbar"));
  itemMenu47->AppendRadioItem(ID_TOOLBAR_CLASSIC, _("&Classic Toolbar"));
  itemMenu47->Append(ID_SHOWHIDE_DRAGBAR, _("&Dragbar visible"), _T(""), wxITEM_CHECK);
  itemMenu47->AppendSeparator();
  itemMenu47->Append(ID_EXPANDALL, _("Expand All"), _T(""), wxITEM_NORMAL);
  itemMenu47->Append(ID_COLLAPSEALL, _("Collapse All"), _T(""), wxITEM_NORMAL);
  wxMenu* itemMenu56 = new wxMenu;
  itemMenu56->Append(ID_EDITFILTER, _("&New/Edit Filter..."), _T(""), wxITEM_NORMAL);
  itemMenu56->Append(ID_APPLYFILTER, _("&Apply current"), _T(""), wxITEM_NORMAL);
  itemMenu56->Append(ID_MANAGEFILTERS, _("&Manage..."), _T(""), wxITEM_NORMAL);
  itemMenu47->Append(ID_FILTERMENU, _("&Filters"), itemMenu56);
  itemMenu47->AppendSeparator();
  itemMenu47->Append(ID_CUSTOMIZETOOLBAR, _("Customize &Main Toolbar..."), _T(""), wxITEM_NORMAL);
  wxMenu* itemMenu62 = new wxMenu;
  itemMenu62->Append(ID_CHANGETREEFONT, _("&Tree/List Font"), _T(""), wxITEM_NORMAL);
  itemMenu62->Append(ID_CHANGEPSWDFONT, _("&Password Font"), _T(""), wxITEM_NORMAL);
  itemMenu47->Append(ID_CHANGEFONTMENU, _("Change &Font"), itemMenu62);
  wxMenu* itemMenu65 = new wxMenu;
  itemMenu65->Append(ID_REPORT_COMPARE, _("&Compare"), _T(""), wxITEM_NORMAL);
  itemMenu65->Append(ID_REPORT_FIND, _("&Find"), _T(""), wxITEM_NORMAL);
  itemMenu65->Append(ID_REPORT_IMPORTTEXT, _("Import &Text"), _T(""), wxITEM_NORMAL);
  itemMenu65->Append(ID_REPORT_IMPORTXML, _("Import &XML"), _T(""), wxITEM_NORMAL);
  itemMenu65->Append(ID_REPORT_MERGE, _("I&Merge"), _T(""), wxITEM_NORMAL);
  itemMenu65->Append(ID_REPORT_VALIDATE, _("&Validate"), _T(""), wxITEM_NORMAL);
  itemMenu47->Append(ID_REPORTSMENU, _("Reports"), itemMenu65);
  menuBar->Append(itemMenu47, _("&View"));
  wxMenu* itemMenu72 = new wxMenu;
  itemMenu72->Append(ID_CHANGECOMBO, _("&Change Safe Combination..."), _T(""), wxITEM_NORMAL);
  itemMenu72->AppendSeparator();
  itemMenu72->Append(ID_BACKUP, _("Make &Backup\tCtrl+B"), _T(""), wxITEM_NORMAL);
  itemMenu72->Append(ID_RESTORE, _("&Restore from Backup...\tCtrl+R"), _T(""), wxITEM_NORMAL);
  itemMenu72->AppendSeparator();
  itemMenu72->Append(ID_OPTIONS_M, _("&Options...\tCtrl+M"), _T(""), wxITEM_NORMAL);
  itemMenu72->Append(ID_PWDPOLSM, _("Password Policies..."), _T(""), wxITEM_NORMAL);
  menuBar->Append(itemMenu72, _("&Manage"));
  wxMenu* itemMenu79 = new wxMenu;
  itemMenu79->Append(wxID_HELP);
  itemMenu79->Append(ID_MENUITEM, _("Visit Password Safe &website..."), _T(""), wxITEM_NORMAL);
  itemMenu79->Append(wxID_ABOUT);
  menuBar->Append(itemMenu79, _("&Help"));
  PWSMenuShortcuts* scmgr = PWSMenuShortcuts::CreateShortcutsManager(menuBar);
  scmgr->ReadApplyUserShortcuts();
  itemFrame1->SetMenuBar(menuBar);

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

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

  m_tree = new PWSTreeCtrl( itemFrame1, m_core, ID_TREECTRL, wxDefaultPosition,
                            wxDefaultSize,
                            wxTR_EDIT_LABELS|wxTR_HAS_BUTTONS |wxTR_HIDE_ROOT|wxTR_SINGLE );
  itemBoxSizer83->Add(m_tree, wxSizerFlags().Expand().Border(0).Proportion(1));
  itemBoxSizer83->Layout();

  if (m_currentView == TREE) {
    itemMenu47->Check(ID_TREE_VIEW, true);
  }
  
  GetMenuBar()->Check(PWSprefs::GetInstance()->GetPref(PWSprefs::UseNewToolbar)? ID_TOOLBAR_NEW: ID_TOOLBAR_CLASSIC, true);
  
  const CRecentDBList& rdb = wxGetApp().recentDatabases();
  Connect(rdb.GetBaseId(), rdb.GetBaseId() + rdb.GetMaxFiles() - 1, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(PasswordSafeFrame::OnOpenRecentDB));
}

/*
 * 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);
}

void PasswordSafeFrame::RefreshToolbarButtons()
{
  wxToolBar* tb = GetToolBar();
  wxASSERT(tb);
  
  if (tb->GetToolsCount() == 0) {  //being created?
    if (PWSprefs::GetInstance()->GetPref(PWSprefs::UseNewToolbar)) {
      for (size_t idx = 0; idx < NumberOf(PwsToolbarButtons); ++idx) {
        if (PwsToolbarButtons[idx].id == ID_SEPARATOR)
          tb->AddSeparator();
        else
          tb->AddTool(PwsToolbarButtons[idx].id, wxEmptyString, wxBitmap(PwsToolbarButtons[idx].bitmap_normal), 
                              wxBitmap(PwsToolbarButtons[idx].bitmap_disabled), wxITEM_NORMAL,
                              PwsToolbarButtons[idx].tooltip);
      }
    }
    else {
      for (size_t idx = 0; idx < NumberOf(PwsToolbarButtons); ++idx) {
        if (PwsToolbarButtons[idx].id == ID_SEPARATOR)
          tb->AddSeparator();
        else
          tb->AddTool(PwsToolbarButtons[idx].id, wxEmptyString, wxBitmap(PwsToolbarButtons[idx].bitmap_classic),
                          PwsToolbarButtons[idx].tooltip);
      }
    }
  }
  else { //toolbar type was changed from the menu
    if (PWSprefs::GetInstance()->GetPref(PWSprefs::UseNewToolbar)) {
      for (size_t idx = 0; idx < NumberOf(PwsToolbarButtons); ++idx) {
        if (PwsToolbarButtons[idx].id == ID_SEPARATOR)
          continue;
        tb->SetToolNormalBitmap(PwsToolbarButtons[idx].id, wxBitmap(PwsToolbarButtons[idx].bitmap_normal));
        tb->SetToolDisabledBitmap(PwsToolbarButtons[idx].id, wxBitmap(PwsToolbarButtons[idx].bitmap_disabled));
      }
    }
    else {
      for (size_t idx = 0; idx < NumberOf(PwsToolbarButtons); ++idx) {
        if (PwsToolbarButtons[idx].id == ID_SEPARATOR)
          continue;
        tb->SetToolNormalBitmap(PwsToolbarButtons[idx].id, wxBitmap(PwsToolbarButtons[idx].bitmap_classic));
        tb->SetToolDisabledBitmap(PwsToolbarButtons[idx].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 == _T("../graphics/wxWidgets/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) {
    SetTitle(m_core.GetCurFile().c_str());
    m_sysTray->SetTrayStatus(SystemTray::TRAY_UNLOCKED);
  } else {
    SetTitle(_T(""));
    m_sysTray->SetTrayStatus(SystemTray::TRAY_CLOSED);
  }
  return status;
}

bool PasswordSafeFrame::Show(bool show)
{
  ShowGrid(show && (m_currentView == GRID));
  ShowTree(show && (m_currentView == TREE));
  return wxFrame::Show(show);
}

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for wxID_EXIT
 */

void PasswordSafeFrame::OnExitClick( wxCommandEvent& /* evt */ )
{
  m_exitFromMenu = true;
  Close();
}

void PasswordSafeFrame::ShowGrid(bool show)
{
  if (show) {
    m_grid->SetTable(new PWSGridTable(m_grid), true, wxGrid::wxGridSelectRows); // true => auto-delete
    m_grid->EnableEditing(false);
    m_grid->DeleteAllItems();
    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++) {
      m_grid->AddItem(iter->second, i++);
    }
  }
  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++) {
      m_tree->AddItem(iter->second);
    }
    if (!m_tree->IsEmpty()) // avoid assertion!
      m_tree->SortChildrenRecursively(m_tree->GetRootItem());
  }

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

void PasswordSafeFrame::OnChangeToolbarType(wxCommandEvent& evt)
{
  //This assumes the menu item is checked before it comes here
  if (GetMenuBar()->IsChecked(evt.GetId())) {
    PWSprefs::GetInstance()->SetPref(PWSprefs::UseNewToolbar, evt.GetId() == ID_TOOLBAR_NEW);
    RefreshToolbarButtons();
    PWSDragBar* dragbar = GetDragBar();
    wxCHECK_RET(dragbar, wxT("Could not find dragbar"));
    dragbar->RefreshButtons();
    wxCHECK_RET(m_search, wxT("Search object not created as expected"));
    m_search->RefreshButtons();
  }
}


/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_LIST_VIEW
 */

void PasswordSafeFrame::OnListViewClick( wxCommandEvent& /* evt */ )
{
  PWSprefs::GetInstance()->SetPref(PWSprefs::LastView, _T("list"));
  ShowTree(false);
  ShowGrid(true);
  m_currentView = GRID;
}


/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_TREE_VIEW
 */

void PasswordSafeFrame::OnTreeViewClick( wxCommandEvent& /* evt */ )
{
  PWSprefs::GetInstance()->SetPref(PWSprefs::LastView, _T("tree"));
  ShowGrid(false);
  ShowTree(true);
  m_currentView = TREE;
}

void PasswordSafeFrame::OnExpandAll(wxCommandEvent& /*evt*/)
{
  wxASSERT(m_currentView == TREE);
  m_tree->ExpandAll();
}

void PasswordSafeFrame::OnCollapseAll(wxCommandEvent& /*evt*/)
{
  wxASSERT(m_currentView == TREE);
  
  //we cannot just call wxTreeCtrl::CollapseAll(), since it tries to 
  //collapse the invisible root item also, and thus ASSERTs
  wxTreeItemIdValue cookie;
  for ( wxTreeItemId root = m_tree->GetRootItem(), idCurr = m_tree->GetFirstChild(root, cookie);
        idCurr.IsOk();
        idCurr = m_tree->GetNextChild(root, cookie) )
  {
      m_tree->CollapseAllChildren(idCurr);
  }
}

void PasswordSafeFrame::OnChangeTreeFont(wxCommandEvent& /*evt*/)
{
  wxFont currentFont(towxstring(PWSprefs::GetInstance()->GetPref(PWSprefs::TreeFont)));

  if (!currentFont.IsOk()) {
    currentFont = IsTreeView() ? m_tree->GetFont() : m_grid->GetDefaultCellFont();
  }
  
  wxFont newFont = ::wxGetFontFromUser(this, currentFont, _("Select Tree/List display font"));
  
  if (newFont.IsOk()) {
    if (IsTreeView()) {
      m_tree->SetFont(newFont);
    }
    else {
      m_grid->SetDefaultCellFont(newFont);
    }
    PWSprefs::GetInstance()->SetPref(PWSprefs::TreeFont, tostringx(newFont.GetNativeFontInfoDesc()));
  }
}

void PasswordSafeFrame::OnChangePasswordFont(wxCommandEvent& /*evt*/)
{
  wxFont passwordFont(towxstring(PWSprefs::GetInstance()->GetPref(PWSprefs::PasswordFont)));

  wxFont newFont = ::wxGetFontFromUser(this, passwordFont, _("Set Password display font"));
  if (newFont.IsOk()) {
    PWSprefs::GetInstance()->SetPref(PWSprefs::PasswordFont, tostringx(newFont.GetNativeFontInfoDesc()));
  }
}

void PasswordSafeFrame::OnShowHideToolBar(wxCommandEvent& evt)
{
  GetToolBar()->Show(evt.IsChecked());
  PWSprefs::GetInstance()->SetPref(PWSprefs::ShowToolbar, evt.IsChecked());
  DoLayout();
  SendSizeEvent();
}

PWSDragBar* PasswordSafeFrame::GetDragBar()
{
  wxSizer* origSizer = GetSizer();
  
  wxASSERT(origSizer);
  wxASSERT(origSizer->IsKindOf(wxBoxSizer(wxVERTICAL).GetClassInfo()));
  wxASSERT(((wxBoxSizer*)origSizer)->GetOrientation() == wxVERTICAL);
  
  wxSizerItem* dragbarItem = origSizer->GetItem(size_t(0));
  wxASSERT_MSG(dragbarItem && dragbarItem->IsWindow() && 
                      wxIS_KIND_OF(dragbarItem->GetWindow(), PWSDragBar),
                    wxT("Found unexpected item while searching for DragBar"));
                    
  PWSDragBar* dragbar = wxDynamicCast(dragbarItem->GetWindow(), PWSDragBar);
  return dragbar;
}

void PasswordSafeFrame::OnShowHideDragBar(wxCommandEvent& evt)
{
  PWSDragBar* dragbar = GetDragBar();
  wxCHECK_RET(dragbar, wxT("Could not find dragbar"));
  
  dragbar->Show(evt.IsChecked());
  PWSprefs::GetInstance()->SetPref(PWSprefs::ShowDragbar, evt.IsChecked());
  DoLayout();
}

int PasswordSafeFrame::Save(SaveType st /* = ST_INVALID*/)
{
  stringT bu_fname; // used to undo backup if save failed
  PWSprefs *prefs = PWSprefs::GetInstance();
  
  // Save Application related preferences
  prefs->SaveApplicationPreferences();
  prefs->SaveShortcuts();

  if (m_core.GetCurFile().empty())
    return SaveAs();

  switch (m_core.GetReadFileVersion()) {
    case PWSfile::VCURRENT:
      if (prefs->GetPref(PWSprefs::BackupBeforeEverySave)) {
        int maxNumIncBackups = prefs->GetPref(PWSprefs::BackupMaxIncremented);
        int backupSuffix = prefs->GetPref(PWSprefs::BackupSuffix);
        std::wstring userBackupPrefix = prefs->GetPref(PWSprefs::BackupPrefixValue).c_str();
        std::wstring userBackupDir = prefs->GetPref(PWSprefs::BackupDir).c_str();
        if (!m_core.BackupCurFile(maxNumIncBackups, backupSuffix,
                                  userBackupPrefix, userBackupDir, bu_fname)) {
          switch (st) {
            case ST_NORMALEXIT:
              if (wxMessageBox(_("Unable to create intermediate backup.  Save database elsewhere or with another name?\n\nClick 'No' to exit without saving."), 
                               _("Write Error"), wxYES_NO | wxICON_EXCLAMATION, this) == wxID_NO)
                return PWScore::SUCCESS;
              else
                return SaveAs();
            case ST_INVALID:
              // No particular end of PWS exit i.e. user clicked Save or
              // saving a changed database before opening another
              wxMessageBox(_("Unable to create intermediate backup."), _("Write Error"), wxOK|wxICON_ERROR, this);
              return PWScore::USER_CANCEL;
            default:
              break;
          }
          wxMessageBox(_("Unable to create intermediate backup."), _("Write Error"), wxOK|wxICON_ERROR, this);
          return SaveAs();
        } // BackupCurFile failed
      } // BackupBeforeEverySave
      break;
    case PWSfile::NEWFILE:
    {
      // file version mis-match
      stringT NewName = PWSUtil::GetNewFileName(m_core.GetCurFile().c_str(), DEFAULT_SUFFIX);

      wxString msg( wxString::Format(_("The original database, \"%s\", is in pre-3.0 format. It will be unchanged.\nYour changes will be written as \"%s\" in the new format, which is unusable by old versions of PasswordSafe. To save your changes in the old format, use the \"File->Export To-> Old (1.x or 2) format\" command."),
                                     m_core.GetCurFile().c_str(), NewName.c_str()));
      if (wxMessageBox(msg, _("File version warning"), wxOK|wxCANCEL|wxICON_INFORMATION, this) == wxID_CANCEL)
        return PWScore::USER_CANCEL;

      m_core.SetCurFile(NewName.c_str());
#if 0
      m_titlebar = PWSUtil::NormalizeTTT(L"Password Safe - " +
                                         m_core.GetCurFile()).c_str();
      SetWindowText(LPCWSTR(m_titlebar));
      app.SetTooltipText(m_core.GetCurFile().c_str());
#endif
      break;
    }
    default:
      ASSERT(0);
      break;
  } // switch on file version

  UUIDList RUElist;
  m_RUEList.GetRUEList(RUElist);
  m_core.SetRUEList(RUElist);

  const int rc = m_core.WriteCurFile();

  if (rc != PWScore::SUCCESS) { // Save failed!
    // Restore backup, if we have one
    if (!bu_fname.empty() && !m_core.GetCurFile().empty())
      pws_os::RenameFile(bu_fname, m_core.GetCurFile().c_str());
    // Show user that we have a problem
    DisplayFileWriteError(rc, m_core.GetCurFile());
    return rc;
  }

  m_core.ResetStateAfterSave();
  m_core.ClearChangedNodes();
  SetChanged(Clear);
//  ChangeOkUpdate();

  // Added/Modified entries now saved - reverse it & refresh display
//  if (m_bUnsavedDisplayed)
//    OnShowUnsavedEntries();

//  if (m_bFilterActive && m_bFilterForStatus) {
//    m_ctlItemList.Invalidate();
//    m_ctlItemTree.Invalidate();
//  }

  // Only refresh views if not existing
  if (st != ST_NORMALEXIT)
    RefreshViews();

  return PWScore::SUCCESS;
}

int PasswordSafeFrame::SaveIfChanged()
{
  if (m_core.IsReadOnly())
    return PWScore::SUCCESS;

  // Note: RUE list saved here via time stamp being updated.
  // Otherwise it won't be saved unless something else has changed
  if ((m_bTSUpdated || m_core.WasDisplayStatusChanged()) &&
       m_core.GetNumEntries() > 0) {
    int rc = Save();
    if (rc != PWScore::SUCCESS)
      return PWScore::USER_CANCEL;
    else
      return PWScore::SUCCESS;
  }


  // offer to save existing database if it was modified.
  // used before loading another
  // returns PWScore::SUCCESS if save succeeded or if user decided
  // not to save

  if (m_core.IsChanged() || m_core.HaveDBPrefsChanged()) {
    wxString prompt(_("Do you want to save changes to the password database"));
    if (!m_core.GetCurFile().empty()) {
      prompt += _(": ");
      prompt += m_core.GetCurFile().c_str();
    }
    prompt += _T("?");
    wxMessageDialog dlg(this, prompt, GetTitle(),
                        (wxICON_QUESTION | wxCANCEL |
                         wxYES_NO | wxYES_DEFAULT));
    int rc = dlg.ShowModal();
    switch (rc) {
      case wxID_CANCEL:
        return PWScore::USER_CANCEL;
      case wxID_YES:
        rc = Save();
        // Make sure that file was successfully written
        if (rc == PWScore::SUCCESS) {
          m_core.UnlockFile(m_core.GetCurFile().c_str());
          break;
        } else
          return PWScore::CANT_OPEN_FILE;
      case wxID_NO:
        // Reset changed flag
        SetChanged(Clear);
        break;
      default:
        ASSERT(0);
    }
  }
  return PWScore::SUCCESS;
}

void PasswordSafeFrame::ClearData()
{
  m_grid->BeginBatch();
  m_grid->ClearGrid();
  m_grid->EndBatch();
  m_tree->Clear();
  //the grid would have deleted the data in one of its callbacks
  //but only if it was initialized, which might not happen
  //if it was never shown.  In those cases, clear the data here
  if (m_core.GetNumEntries() != 0) {
    m_core.ClearData();
  }
  m_core.ReInit();
}

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 NULL;
}

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for wxID_OPEN
 */

void PasswordSafeFrame::OnOpenClick( wxCommandEvent& /* evt */ )
{
  DoOpen(_("Please Choose a Database to Open:"));
}


/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for wxID_CLOSE
 */

void PasswordSafeFrame::OnCloseClick( wxCommandEvent& /* evt */ )
{
  PWSprefs *prefs = PWSprefs::GetInstance();

  // Save Application related preferences
  prefs->SaveApplicationPreferences();
  if( !m_core.GetCurFile().empty() ) {
    int rc = SaveIfChanged();
    if (rc != PWScore::SUCCESS)
      return;
    m_core.SetCurFile(_T(""));
    ClearData();
    SetTitle(_T(""));
    m_sysTray->SetTrayStatus(SystemTray::TRAY_CLOSED);
  }
}

int PasswordSafeFrame::DoOpen(const wxString& title)
{
  stringT dir = PWSdirs::GetSafeDir();
  //Open-type dialog box
  wxFileDialog fd(this, title, dir.c_str(), _("pwsafe.psafe3"),
                  _("Password Safe Databases (*.psafe3; *.dat)|*.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(wxT("That file is already open."), wxT("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.
  CSafeCombinationPrompt 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) {
      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
  }

  // clear the data before loading the new file
  ClearData();

  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(NULL);
  m_core.SetReporter(NULL);
  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;
    }
#ifdef DEMO
    case PWScore::LIMIT_REACHED:
    {
      CString cs_msg; cs_msg.Format(IDS_LIMIT_MSG, MAXDEMO);
      CString cs_title(MAKEINTRESOURCE(IDS_LIMIT_TITLE));
      const int yn = MessageBox(cs_msg, cs_title, MB_YESNO|MB_ICONWARNING);
      if (yn == IDNO) {
        return PWScore::USER_CANCEL;
      }
      rc = PWScore::SUCCESS;
      m_MainToolBar.GetToolBarCtrl().EnableButton(ID_MENUITEM_ADD, FALSE);
      break;
    }
#endif
    default:
      temp.Format(IDS_UNKNOWNERROR, pszFilename.c_str());
      MessageBox(temp, cs_title, MB_OK|MB_ICONERROR);
      return rc;
  }
  m_core.SetCurFile(pszFilename);
#if !defined(POCKET_PC)
  m_titlebar = PWSUtil::NormalizeTTT(_T("Password Safe - ") +
                                     m_core.GetCurFile()).c_str();
  SetWindowText(LPCTSTR(m_titlebar));
#endif
  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_COMMAND_MENU_SELECTED event handler for wxID_PROPERTIES
 */

void PasswordSafeFrame::OnPropertiesClick( wxCommandEvent& /* evt */ )
{
  CProperties props(this, m_core);
  props.ShowModal();
}


/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_CHANGECOMBO
 */

void PasswordSafeFrame::OnChangePasswdClick( wxCommandEvent& /* evt */ )
{
  CSafeCombinationChange* window = new CSafeCombinationChange(this, m_core);
  int returnValue = window->ShowModal();
  if (returnValue == wxID_OK) {
    m_core.ChangePasskey(tostringx(window->GetNewpasswd()));
  }
  window->Destroy();
}


/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for wxID_SAVE
 */

void PasswordSafeFrame::OnSaveClick( wxCommandEvent& /* evt */ )
{
  Save();
}


void PasswordSafeFrame::OnSaveAsClick(wxCommandEvent& evt)
{
  UNREFERENCED_PARAMETER(evt);
  SaveAs();
}

int PasswordSafeFrame::SaveAs()
{
  if (m_core.GetReadFileVersion() != PWSfile::VCURRENT &&
      m_core.GetReadFileVersion() != PWSfile::UNKNOWN_VERSION) {
    if (wxMessageBox( wxString::Format(_("The original database, '%s', is in pre-3.0 format. The data will now be written in the new format, which is unusable by old versions of PasswordSafe. To save the data in the old format, use the 'File->Export To-> Old (1.x or 2) format' command."),
                                        m_core.GetCurFile().c_str()), _("File version warning"), 
                                        wxOK | wxCANCEL | wxICON_EXCLAMATION, this) == wxCANCEL) {
      return PWScore::USER_CANCEL;
    }
  }

  StringX cf(m_core.GetCurFile());
  if(cf.empty()) {
    cf = _("pwsafe"); // reasonable default for first time user
  }
  wxString v3FileName = towxstring(PWSUtil::GetNewFileName(cf.c_str(), DEFAULT_SUFFIX));

  wxString title = (m_core.GetCurFile().empty()? _("Please choose a name for the current (Untitled) database:") : 
                                    _("Please choose a new name for the current database:"));
  wxFileName filename(v3FileName);
  wxString dir = filename.GetPath();
  if (dir.empty())
    dir = towxstring(PWSdirs::GetSafeDir());

  //filename cannot have the path
  wxFileDialog fd(this, title, dir, filename.GetFullName(),
                  _("Password Safe Databases (*.psafe3; *.dat)|*.psafe3;*.dat|All files (*.*; *)|*.*;*"),
                   wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
                   
  if (fd.ShowModal() != wxID_OK) {
    return PWScore::USER_CANCEL;
  }

  StringX newfile = tostringx(fd.GetPath());
  
  std::wstring locker(L""); // null init is important here
  // Note: We have to lock the new file before releasing the old (on success)
  if (!m_core.LockFile2(newfile.c_str(), locker)) {
    wxMessageBox(wxString::Format(_("%s\n\nFile is currently locked by %s"), newfile.c_str(), locker.c_str()),
                    _("File lock error"), wxOK | wxICON_ERROR, this);
    return PWScore::CANT_OPEN_FILE;
  }

  // Save file UUID, clear it to generate new one, restore if necessary
  pws_os::CUUID file_uuid = m_core.GetFileUUID();
  m_core.ClearFileUUID();

  UUIDList RUElist;
  m_RUEList.GetRUEList(RUElist);
  m_core.SetRUEList(RUElist);

  int rc = m_core.WriteFile(newfile);
  m_core.ResetStateAfterSave();
  m_core.ClearChangedNodes();

  if (rc != PWScore::SUCCESS) {
    m_core.SetFileUUID(file_uuid);
    m_core.UnlockFile2(newfile.c_str());
    DisplayFileWriteError(rc, newfile);
    return PWScore::CANT_OPEN_FILE;
  }
  if (!m_core.GetCurFile().empty())
    m_core.UnlockFile(m_core.GetCurFile().c_str());

  // Move the newfile lock to the right place
  m_core.MoveLock();

  m_core.SetCurFile(newfile);
#if 0
#if !defined(POCKET_PC)
  m_titlebar = PWSUtil::NormalizeTTT(L"Password Safe - " +
                                     m_core.GetCurFile()).c_str();
  SetWindowText(LPCWSTR(m_titlebar));
  app.SetTooltipText(m_core.GetCurFile().c_str());
#endif
#endif
  SetTitle(towxstring(m_core.GetCurFile()));
  SetChanged(Clear);
#if 0
  ChangeOkUpdate();

  // Added/Modified entries now saved - reverse it & refresh display
  if (m_bUnsavedDisplayed)
    OnShowUnsavedEntries();

  if (m_bFilterActive && m_bFilterForStatus) {
    m_ctlItemList.Invalidate();
    m_ctlItemTree.Invalidate();
  }
#endif
  RefreshViews();

  wxGetApp().recentDatabases().AddFileToHistory(towxstring(newfile));

  if (m_core.IsReadOnly()) {
    // reset read-only status (new file can't be read-only!)
    // and so cause toolbar to be the correct version
    m_core.SetReadOnly(false);
  }

  return PWScore::SUCCESS;
}

/*!
 * 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;
      }
    }
    SaveSettings();
    Destroy();
  }
  else {
    const bool lockOnMinimize = PWSprefs::GetInstance()->GetPref(PWSprefs::DatabaseClear);
    HideUI(lockOnMinimize);
  } 
}


/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for wxID_ABOUT
 */

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

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_BROWSEURL
 */

void PasswordSafeFrame::OnBrowseURL(wxCommandEvent& evt)
{
  CItemData rueItem;
  CItemData* item = IsRUEEvent(evt)? (m_RUEList.GetPWEntry(GetEventRUEIndex(evt), rueItem)? &rueItem: NULL) : GetSelectedEntry();
  if (item)
    DoBrowse(*item, false); //false => no autotype
}

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_BROWSEURLPLUS
 */

void PasswordSafeFrame::OnBrowseUrlAndAutotype(wxCommandEvent& evt)
{
  CItemData rueItem;
  CItemData* item = IsRUEEvent(evt)? (m_RUEList.GetPWEntry(GetEventRUEIndex(evt), rueItem)? &rueItem: NULL) : GetSelectedEntry();
  if (item) {
    DoBrowse(*item, true); //true => autotype
  }
}

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_SENDEMAIL
 */

void PasswordSafeFrame::OnSendEmail(wxCommandEvent& evt)
{
  CItemData rueItem;
  CItemData* item = IsRUEEvent(evt)? (m_RUEList.GetPWEntry(GetEventRUEIndex(evt), rueItem)? &rueItem: NULL) : GetSelectedEntry();
  if (item)
    DoEmail(*item);
}

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_RUNCOMMAND
 */

void PasswordSafeFrame::OnRunCommand(wxCommandEvent& evt)
{
  CItemData rueItem;
  CItemData* item = IsRUEEvent(evt)? (m_RUEList.GetPWEntry(GetEventRUEIndex(evt), rueItem)? &rueItem: NULL) : GetSelectedEntry();
  if (item)
    DoRun(*item);
}

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_AUTOTYPE
 */

void PasswordSafeFrame::OnAutoType(wxCommandEvent& evt)
{
  CItemData rueItem;
  CItemData* item = IsRUEEvent(evt)? (m_RUEList.GetPWEntry(GetEventRUEIndex(evt), rueItem)? &rueItem: NULL) : GetSelectedEntry();
  if (item) {
#ifdef __WXMAC__
    Lower();
#endif
    DoAutotype(*item);
  }
}

void PasswordSafeFrame::OnGotoBase(wxCommandEvent& /*evt*/)
{
  CItemData* item = GetSelectedEntry();
  if (item && (item->IsAlias() || item->IsShortcut())) {
    item = m_core.GetBaseEntry(item);
    CUUID base_uuid = item->GetUUID();
    SelectItem(base_uuid);
    UpdateAccessTime(*item);
  }
}

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

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

}

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

void PasswordSafeFrame::SetChanged(ChangeType changed)
{
  if (m_core.IsReadOnly())
    return;

  switch (changed) {
    case Data:
      if (PWSprefs::GetInstance()->GetPref(PWSprefs::SaveImmediately)) {
        // Don't save if just adding group as it will just 'disappear'!
        Save();
      } else {
        m_core.SetDBChanged(true);
      }
      break;
    case Clear:
      m_core.SetChanged(false, false);
      m_bTSUpdated = false;
      break;
    case TimeStamp:
      if (PWSprefs::GetInstance()->GetPref(PWSprefs::MaintainDateTimeStamps))
        m_bTSUpdated = true;
      break;
    case DBPrefs:
      m_core.SetDBPrefsChanged(true);
      break;
    case ClearDBPrefs:
      m_core.SetDBPrefsChanged(false);
      break;
    default:
      ASSERT(0);
  }
}

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();
    SetChanged(TimeStamp);
#ifdef NOTYET
    // Need to update view if there
    if (m_nColumnIndexByType[CItemData::ATIME] != -1) {
      // Get index of entry
      DisplayInfo *pdi = (DisplayInfo *)ci.GetDisplayInfo();
      // Get value in correct format
      CString cs_atime = ci.GetATimeL().c_str();
      // Update it
      m_ctlItemList.SetItemText(pdi->list_index,
        m_nColumnIndexByType[CItemData::ATIME], cs_atime);
    }
#endif
  }
}

void PasswordSafeFrame::DispatchDblClickAction(CItemData &item)
{
  switch (PWSprefs::GetInstance()->GetPref(PWSprefs::DoubleClickAction)) {
  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(true);
    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, PWSTreeCtrl* tree, OrderedItemList& olist)
{
  wxTreeItemIdValue cookie;
  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 == NULL) {
    wxMenu groupEditMenu;
    groupEditMenu.Append(wxID_ADD, wxT("Add &Entry"));
    groupEditMenu.Append(ID_ADDGROUP, wxT("Add &Group"));
    groupEditMenu.Append(ID_RENAME, wxT("&Rename Group"));
    groupEditMenu.Append(wxID_DELETE, wxT("&Delete Group"));
    if (m_currentView == TREE)
      m_tree->PopupMenu(&groupEditMenu);
  } else {
    wxMenu itemEditMenu;
    itemEditMenu.Append(ID_COPYUSERNAME,   wxT("Copy &Username to Clipboard"));
    itemEditMenu.Append(ID_COPYPASSWORD,   wxT("&Copy Password to Clipboard"));
    itemEditMenu.Append(ID_PASSWORDSUBSET, wxT("Display subset of Password"));
    itemEditMenu.Append(ID_COPYNOTESFLD,   wxT("Copy &Notes to Clipboard"));
    itemEditMenu.Append(ID_COPYURL,        wxT("Copy UR&L to Clipboard"));
    itemEditMenu.Append(ID_COPYEMAIL,      wxT("Copy email to Clipboard"));
    itemEditMenu.Append(ID_COPYRUNCOMMAND, wxT("Copy Run Command to Clipboard"));
    itemEditMenu.AppendSeparator();
    itemEditMenu.Append(ID_BROWSEURL,      wxT("&Browse to URL"));
    itemEditMenu.Append(ID_BROWSEURLPLUS,  wxT("Browse to URL + &Autotype"));
    itemEditMenu.Append(ID_SENDEMAIL,      wxT("Send &email"));
    itemEditMenu.Append(ID_RUNCOMMAND,     wxT("&Run Command"));
    itemEditMenu.Append(ID_AUTOTYPE,       wxT("Perform Auto &Type"));
    itemEditMenu.AppendSeparator();
    itemEditMenu.Append(ID_EDIT,           wxT("Edit/&View Entry..."));
    itemEditMenu.Append(ID_RENAME,         wxT("Rename Entry"));
    itemEditMenu.Append(ID_DUPLICATEENTRY, wxT("&Duplicate Entry"));
    itemEditMenu.Append(wxID_DELETE,       wxT("Delete Entry"));
    itemEditMenu.Append(ID_CREATESHORTCUT, wxT("Create &Shortcut"));
    itemEditMenu.Append(ID_GOTOBASEENTRY,  wxT("&Go to Base entry"));
    itemEditMenu.Append(ID_EDITBASEENTRY,  wxT("&Edit Base entry"));

    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 ( m_currentView == TREE )
      m_tree->PopupMenu(&itemEditMenu);
    else
      m_grid->PopupMenu(&itemEditMenu);
  }
}

CItemData* PasswordSafeFrame::GetBaseOfSelectedEntry()
{
  CItemData* item = GetSelectedEntry();
  if (item && (item->IsShortcut() || item->IsAlias())) {
    item = m_core.GetBaseEntry(item);
  }
  return item;
}

////////////////////////////////////////////////////////
// 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)
{
  switch (evt.GetId()) {
    case wxID_SAVE:
      evt.Enable(m_core.IsChanged() || m_core.HaveDBPrefsChanged());
      break;
   
    case ID_ADDGROUP:
    case ID_EXPANDALL:
    case ID_COLLAPSEALL:
      evt.Enable(m_currentView == TREE);
      break;
    
    case ID_RENAME:
      // only allowed if an item is selected in tree view
      evt.Enable(m_currentView == TREE && GetSelectedEntry() != NULL );
      break;

    case ID_BROWSEURL:
    case ID_BROWSEURLPLUS:
    case ID_COPYURL:
    {
      CItemData* item = GetBaseOfSelectedEntry();
      evt.Enable( item && !item->IsURLEmpty() && !item->IsURLEmail() );
      break;
    }
    case ID_SENDEMAIL:
    case ID_COPYEMAIL:
    {
      CItemData* item = GetBaseOfSelectedEntry();
      evt.Enable( item && !item->IsEmailEmpty() );
      break;
    }
    case ID_COPYUSERNAME:
    {
      CItemData* item = GetBaseOfSelectedEntry();
      evt.Enable(item && !item->IsUserEmpty());
      break;
    }
    case ID_COPYNOTESFLD:
    {
      CItemData* item = GetBaseOfSelectedEntry();
      evt.Enable(item && !item->IsNotesEmpty());
      break;
    }
    case ID_RUNCOMMAND:
    case ID_COPYRUNCOMMAND:
    {
      CItemData* item = GetBaseOfSelectedEntry();
      evt.Enable(item && item->IsRunCommandEmpty());
      break;
    }
    case ID_CREATESHORTCUT:
    {
      CItemData* item = GetSelectedEntry();
      evt.Enable(item && (item->IsNormal() || item->IsShortcutBase()));
      break;
    }
    case ID_DUPLICATEENTRY:
    case ID_COPYPASSWORD:
    case ID_AUTOTYPE:
    case ID_EDIT:
    case ID_PASSWORDSUBSET:
      // not allowed if a group is selected in tree view
      evt.Enable(m_currentView == GRID || GetSelectedEntry() != NULL );
      break;

    case ID_GOTOBASEENTRY:
    case ID_EDITBASEENTRY:
    {
      const CItemData* item = GetSelectedEntry();
      evt.Enable(item != NULL && (item->IsShortcut() || item->IsAlias()));
      break;
    }
 
    case wxID_UNDO:
      evt.Enable(m_core.AnyToUndo());
      break;
      
    case wxID_REDO:
      evt.Enable(m_core.AnyToRedo());
      break;
      
    case ID_SYNCHRONIZE:
      evt.Enable(!m_core.IsReadOnly() && !m_core.GetCurFile().empty() && m_core.GetNumEntries() != 0);
      break;

    default:
      break;
  }
}

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

// Implementation of UIinterface methods

void PasswordSafeFrame::DatabaseModified(bool modified)
{
  if (!modified)
    return;

  if (m_core.HaveDBPrefsChanged()) {
    wxCommandEvent evt(wxEVT_DB_PREFS_CHANGE, wxID_ANY);
    evt.ResumePropagation(wxEVENT_PROPAGATE_MAX); //let it propagate through the entire window tree
    if (m_tree) {
      m_tree->GetEventHandler()->AddPendingEvent(evt); 
      evt.StopPropagation(); //or else it will come to the frame twice
    }
    if (m_grid) m_grid->GetEventHandler()->AddPendingEvent(evt);
  }
  else if (m_core.IsChanged()) {  //"else if" => both DB and it's prefs can't change at the same time
    if (m_search) m_search->Invalidate();
    if (m_currentView == TREE) {
      if (m_grid != NULL)
        m_grid->OnPasswordListModified();
    }
    else {
#if 0
    if (m_tree != NULL)
      m_tree->???
#endif
    }
  } else {
    wxFAIL_MSG(wxT("What changed in the DB if not entries or preferences?"));
  }
}

void PasswordSafeFrame::GUISetupDisplayInfo(CItemData &ci)
{
  UNREFERENCED_PARAMETER(ci);
  // XXX TBD
}

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);
}

void PasswordSafeFrame::UpdateGUI(UpdateGUICommand::GUI_Action ga,
                                  const CUUID &entry_uuid,
                                  CItemData::FieldType ft,
                                  bool bUpdateGUI)
{
  // Callback from PWScore if GUI needs updating
  // Note: For some values of 'ga', 'ci' & ft are invalid and not used.
 
  // "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
  UNREFERENCED_PARAMETER(ft);
  UNREFERENCED_PARAMETER(bUpdateGUI);

  CItemData *pci(NULL);

  ItemListIter pos = m_core.Find(entry_uuid);
  if (pos != m_core.GetEntryEndIter()) {
    pci = &pos->second;
  } else if (ga == UpdateGUICommand::GUI_ADD_ENTRY ||
             ga == UpdateGUICommand::GUI_REFRESH_ENTRYFIELD ||
             ga == UpdateGUICommand::GUI_REFRESH_ENTRYPASSWORD) {
    pws_os::Trace(_("Couldn't find uuid %s"),
                  StringX(CUUID(entry_uuid)).c_str());
  }

#ifdef NOTYET
  PWSprefs *prefs = PWSprefs::GetInstance();
#endif
  switch (ga) {
    case UpdateGUICommand::GUI_ADD_ENTRY:
      ASSERT(pci != NULL);
      m_tree->AddItem(*pci);
      m_grid->AddItem(*pci);
      break;
    case UpdateGUICommand::GUI_DELETE_ENTRY:
      m_grid->Remove(entry_uuid);
      m_tree->Remove(entry_uuid);
      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;
#ifdef NOTYET
    case UpdateGUICommand::GUI_UPDATE_STATUSBAR:
      UpdateToolBarDoUndo();
      UpdateStatusBar();
      break;
#endif
    case UpdateGUICommand::GUI_REFRESH_ENTRYFIELD:
      ASSERT(pci != NULL);
      RefreshEntryFieldInGUI(*pci, ft);
      break;
    case UpdateGUICommand::GUI_REFRESH_ENTRYPASSWORD:
      ASSERT(pci != NULL);
      RefreshEntryPasswordInGUI(*pci);
      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
      if (m_tree) {
        m_tree->GetEventHandler()->AddPendingEvent(evt); 
        evt.StopPropagation(); //or else it will come to the frame twice
      }
      if (m_grid) m_grid->GetEventHandler()->AddPendingEvent(evt);
      break;
    }
    default:
      break;
  }
}
    
void PasswordSafeFrame::RefreshEntryFieldInGUI(const CItemData& item, CItemData::FieldType ft)
{
  if (m_currentView == GRID) {
    m_grid->RefreshItemField(item.GetUUID(), ft);
  }
  else {
    //even though the sort order might have changed, don't change the position yet
    //as it could be too distracting, and the item may even move off the screen
    m_tree->UpdateItemField(item, ft);
  }
}

void PasswordSafeFrame::RefreshEntryPasswordInGUI(const CItemData& item)
{
  if (m_currentView == GRID) {
    RefreshEntryFieldInGUI(item, CItemData::PASSWORD);
    //TODO: Update password history
  }
  else {
    RefreshEntryFieldInGUI(item, CItemData::PASSWORD);
  }
}

void PasswordSafeFrame::GUIRefreshEntry(const CItemData& item)
{
  if (item.GetStatus() ==CItemData::ES_DELETED) {
    uuid_array_t uuid;
    item.GetUUID(uuid);
    m_tree->Remove(uuid);
    m_grid->Remove(uuid);
  } else {
    m_tree->UpdateItem(item);
    m_grid->UpdateItem(item);
  }
}

void PasswordSafeFrame::UpdateWizard(const stringT &)
{
  // Stub
}

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for wxID_NEW
 */

void PasswordSafeFrame::OnNewClick( wxCommandEvent& /* evt */ )
{
  New();
}

/*!
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENU_CLEAR_MRU
 */

void PasswordSafeFrame::OnClearRecentHistory(wxCommandEvent& evt)
{
  UNREFERENCED_PARAMETER(evt);
  wxGetApp().recentDatabases().Clear();
}

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

static void DisplayFileWriteError(int rc, const StringX &fname)
{
  ASSERT(rc != PWScore::SUCCESS);

  wxString cs_temp, cs_title(_("Write Error"));
  switch (rc) {
  case PWScore::CANT_OPEN_FILE:
    cs_temp = fname.c_str();
    cs_temp += _("\n\nCoud not open file for writing!");
    break;
  case PWScore::FAILURE:
    cs_temp =_("Write operation failed!\nFile may have been corrupted.\nTry saving in a different location");
    break;
  default:
    cs_temp = fname.c_str();
    cs_temp += _("\n\nUnknown error");
    break;
  }
  wxMessageDialog(NULL, cs_temp, cs_title, wxOK | wxICON_ERROR);
}

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

int PasswordSafeFrame::New()
{
  int rc, rc2;

  if (!m_core.IsReadOnly() && m_core.IsChanged()) {
    wxString msg(_("Do you want to save changes to the password database: "));
    msg += m_core.GetCurFile().c_str();
    wxMessageDialog mbox(this, msg, GetTitle(), wxCANCEL | wxYES_NO | wxICON_QUESTION);
    rc = mbox.ShowModal();
    switch (rc) {
    case wxID_CANCEL:
      return PWScore::USER_CANCEL;
    case wxID_YES:
      rc2 = Save();
        /*
        Make sure that writing the file was successful
        */
        if (rc2 == PWScore::SUCCESS)
          break;
        else
          return PWScore::CANT_OPEN_FILE;
    case wxID_NO:
      // Reset changed flag
      SetChanged(Clear);
      break;
    default:
      ASSERT(0);
    }
  }

  StringX cs_newfile;
  rc = NewFile(cs_newfile);
  if (rc == PWScore::USER_CANCEL) {
    /*
    Everything stays as is...
    Worst case, they saved their file....
    */
    return PWScore::USER_CANCEL;
  }

  m_core.SetCurFile(cs_newfile);
  m_core.ClearFileUUID();

  rc = m_core.WriteCurFile();
  if (rc != PWScore::SUCCESS) {
    DisplayFileWriteError(rc, cs_newfile);
    return PWScore::USER_CANCEL;
  }
  m_core.ClearChangedNodes();

  SetLabel(PWSUtil::NormalizeTTT(L"Password Safe - " + cs_newfile).c_str());

  m_sysTray->SetTrayStatus(SystemTray::TRAY_UNLOCKED);
  m_RUEList.ClearEntries();
  wxGetApp().recentDatabases().AddFileToHistory(towxstring(cs_newfile));
#ifdef notyet
  if (!m_bOpen) {
    // Previous state was closed - reset DCA in status bar
    SetDCAText();
  }

  // Set Dragbar images correctly
  m_DDGroup.SetStaticState(false);
  m_DDTitle.SetStaticState(false);
  m_DDPassword.SetStaticState(false);
  m_DDUser.SetStaticState(false);
  m_DDNotes.SetStaticState(false);
  m_DDURL.SetStaticState(false);
  m_DDemail.SetStaticState(false);

  UpdateMenuAndToolBar(true);

  // Set timer for user-defined idle lockout, if selected (DB preference)
  KillTimer(TIMER_LOCKDBONIDLETIMEOUT);
  if (PWSprefs::GetInstance()->GetPref(PWSprefs::LockDBOnIdleTimeout)) {
    ResetIdleLockCounter();
    SetTimer(TIMER_LOCKDBONIDLETIMEOUT, IDLE_CHECK_INTERVAL, NULL);
  }
#endif
  return PWScore::SUCCESS;
}


int PasswordSafeFrame::NewFile(StringX &fname)
{
  wxString cs_msg, cs_title, cs_temp;
  wxString cs_text(_("Please choose a name for the new database"));

  wxString cf(_("pwsafe")); // reasonable default for first time user
  wxString v3FileName = towxstring(PWSUtil::GetNewFileName(tostdstring(cf), DEFAULT_SUFFIX));
  wxString dir = towxstring(PWSdirs::GetSafeDir());
  int rc;

  while (1) {
    wxFileDialog fd(static_cast<wxWindow *>(this), cs_text, dir, v3FileName,
                    _("psafe3 files (*.psafe3)|*.psafe3|All files(*.*; *)|*.*;*"),
                    wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
    rc = fd.ShowModal();

    if (rc == wxID_OK) {
      fname = fd.GetPath();
      wxFileName wxfn(fname.c_str());
      if (wxfn.GetExt().empty()) {
        wxfn.SetExt(DEFAULT_SUFFIX);
        fname = wxfn.GetFullPath().c_str();
      }
      break;
    } else
      return PWScore::USER_CANCEL;
  }

  CSafeCombinationSetup dbox_pksetup(this);
  rc = dbox_pksetup.ShowModal();

  if (rc == wxID_CANCEL)
    return PWScore::USER_CANCEL;  //User cancelled password entry

  // Reset core
  m_core.ReInit(true);

  ClearData();
  PWSprefs::GetInstance()->SetDatabasePrefsToDefaults();
  const StringX &oldfilename = m_core.GetCurFile();
  // The only way we're the locker is if it's locked & we're !readonly
  if (!oldfilename.empty() &&
      !m_core.IsReadOnly() &&
      m_core.IsLockedFile(oldfilename.c_str()))
    m_core.UnlockFile(oldfilename.c_str());

  m_core.SetCurFile(fname);

  // Now lock the new file
  std::wstring locker(L""); // null init is important here
  m_core.LockFile(fname.c_str(), locker);

  m_core.SetReadOnly(false); // new file can't be read-only...
  m_core.NewFile(tostringx(dbox_pksetup.GetPassword()));
#ifdef notyet
  startLockCheckTimer();
#endif
  return PWScore::SUCCESS;
}

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

  //Save alerts the user
  if (!m_core.IsChanged() || Save() == PWScore::SUCCESS) {
    ClearData();
    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(wxT("Could not re-load database: ")) << towxstring(m_core.GetCurFile()), 
                     wxT("Error re-loading last database"), wxOK|wxICON_ERROR, this);
  }
  m_sysTray->SetTrayStatus(SystemTray::TRAY_CLOSED);
}

void PasswordSafeFrame::UnlockSafe(bool restoreUI)
{
  StringX password;
  if (m_sysTray->IsLocked()) {
    AutoRestore<bool> unlocking(m_bUnlocking,true);
    if (VerifySafeCombination(password)) {
      if (ReloadDatabase(password)) {
        m_sysTray->SetTrayStatus(SystemTray::TRAY_UNLOCKED);
      }
      else {
        CleanupAfterReloadFailure(true);
        return;
      }
    }
    else {
      return;
    }
    if (m_savedDBPrefs != wxEmptyString) {
      const StringX savedPrefs = tostringx(m_savedDBPrefs);
      PWSprefs::GetInstance()->Load(savedPrefs);
      if (m_core.HaveHeaderPreferencesChanged(savedPrefs))
        m_core.SetDBPrefsChanged(true);
      m_savedDBPrefs = wxEmptyString;
    }
  }
  
  if (restoreUI) {
    if (!IsShown()) {
      ShowWindowRecursively(hiddenWindows);
    }
    if (IsIconized()) {
      Iconize(false);
    }
    Show(true); //show the grid/tree
    m_guiInfo->Restore(this);
    Raise();
  }
  else if (IsShown()) { /* if it is somehow visible, show it correctly */
    Show(true);
    m_guiInfo->Restore(this);
  }
}

bool PasswordSafeFrame::VerifySafeCombination(StringX& password)
{
  CSafeCombinationPrompt scp(NULL, m_core, towxstring(m_core.GetCurFile()));
  if (scp.ShowModal() == wxID_OK) {
    password = scp.GetPassword();
    return true;
  }
  return false;
}

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

void PasswordSafeFrame::OnIconize(wxIconizeEvent& evt)
{
  const bool beingRestored = 
#if wxCHECK_VERSION(2,9,0)
    !evt.IsIconized();
#else
    !evt.Iconized();
#endif

  if (beingRestored) {
    if (m_sysTray->IsLocked() && !m_bUnlocking) {
      /* 
       * the frame is automatically un-iconized by gtk when the Safe Combination Entry dialog comes up
       * which causes the code here to throw up a second SCE dialog.  This ugly hack (m_bUnlocking) is
       *  the only way I could come up with to prevent that
       */
      StringX password;
      if (VerifySafeCombination(password)) {
        if (ReloadDatabase(password)) {
          ShowWindowRecursively(hiddenWindows);
          m_sysTray->SetTrayStatus(SystemTray::TRAY_UNLOCKED);
          Show(true); //show the tree/grid
          m_guiInfo->Restore(this);
        }
        else {
          CleanupAfterReloadFailure(true);
        }
      }
      else {
        // Make sure the window remains iconized
        Iconize();
      }
    }
  }
  else {
    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::TRAY_UNLOCKED && lockOnMinimize) {
      LockDb();
    }
  }
}

void PasswordSafeFrame::HideUI(bool lock)
{
  m_guiInfo->Save(this);
  wxGetApp().SaveFrameCoords();
  
  if (lock && m_sysTray->GetTrayStatus() == SystemTray::TRAY_UNLOCKED) {
    LockDb();
  }

  wxClipboard().Clear();
  
#ifndef __WXMAC__
  if (!IsIconized()) {
    Iconize();
  }
#endif
  
  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::LockDb()
{
  wxGetApp().StopIdleTimer();
  m_guiInfo->Save(this);
  if (SaveAndClearDatabase())
    m_sysTray->SetTrayStatus(SystemTray::TRAY_LOCKED);
}

void PasswordSafeFrame::OnOpenRecentDB(wxCommandEvent& evt)
{
  CRecentDBList& db = wxGetApp().recentDatabases();
  const size_t index = evt.GetId() - db.GetBaseId();
  const wxString dbfile = db.GetHistoryFile(index);
  switch(Open(dbfile))
  {
    case PWScore::SUCCESS:
      break;
      
    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;  //file exists.  don't remove it from history
        
      //fall through
    default:
      wxMessageBox(wxString(wxT("There was an error loading the database: ")) << dbfile, 
                     wxT("Could not load database"), wxOK|wxICON_ERROR, this);
      db.RemoveFileFromHistory(index);
      break;
  }
}

//
// ---------- Import/Export ........................
//
void PasswordSafeFrame::OnImportText(wxCommandEvent& evt)
{
  UNREFERENCED_PARAMETER(evt);
  if (m_core.IsReadOnly()) {// disable in read-only mode
    wxMessageBox(wxT("The current database was opened in read-only mode.  You cannot import into it."),
                  wxT("Import text"), wxOK | wxICON_EXCLAMATION, this);
    return;
  }

  // Initialize set
  GTUSet setGTU;
  if (!m_core.GetUniqueGTUValidated() && !m_core.InitialiseGTU(setGTU)) {
    // Database is not unique to start with - tell user to validate it first
    wxMessageBox(wxString() << wxT("The database:\n\n") << m_core.GetCurFile() << wxT("\n\n")
                            << wxT("has duplicate entries with the same group/title/user combination.")
                            << wxT("  Please fix by validating database."),
                            wxT("Import Text failed"), wxOK | wxICON_ERROR, this);
    return;
  }

  CImportTextDlg dlg(this);
  if (dlg.ShowModal() != wxID_OK)
    return;

  StringX ImportedPrefix(dlg.groupName);
  TCHAR fieldSeparator = dlg.FieldSeparator();

  std::wstring strError;
  wxString TxtFileName = dlg.filepath;
  int numImported(0), numSkipped(0), numPWHErrors(0), numRenamed(0), numNoPolicyNames(0);
  wchar_t delimiter = dlg.FieldSeparator();
  bool bImportPSWDsOnly = dlg.importPasswordsOnly;

  /* Create report as we go */
  CReport rpt;
  rpt.StartReport(wxT("Import_Text"), m_core.GetCurFile().c_str());
  wxString header;
  header.Printf(wxT("%s file being imported: %s"), wxT("Text"), TxtFileName.c_str());
  rpt.WriteLine(tostdstring(header));
  rpt.WriteLine();

  Command *pcmd = NULL;
  int rc = m_core.ImportPlaintextFile(ImportedPrefix, tostringx(TxtFileName), fieldSeparator,
                                  delimiter, bImportPSWDsOnly,
                                  strError,
                                  numImported, numSkipped, numPWHErrors, numRenamed, numNoPolicyNames,
                                  rpt, pcmd);

  wxString cs_title, cs_temp;
  
  switch (rc) {
    case PWScore::CANT_OPEN_FILE:
      cs_title = wxT("File Read Error");
      cs_temp << TxtFileName << wxT("\n\nCould not open file for reading!");
      delete pcmd;
      break;
    case PWScore::INVALID_FORMAT:
      cs_title = wxT("File Read Error");
      cs_temp << TxtFileName << wxT("\n\nInvalid format");
      delete pcmd;
      break;
    case PWScore::FAILURE:
      cs_title = wxT("Import Text failed");
      cs_temp = towxstring(strError);
      delete pcmd;
      break;
    case PWScore::SUCCESS:
    case PWScore::OK_WITH_ERRORS:
      // deliberate fallthru
    default:
    {
      if (pcmd != NULL)
        Execute(pcmd);

      rpt.WriteLine();
      cs_temp << (bImportPSWDsOnly ? wxT("Updated ") : wxT("Imported "))
              << numImported << (numImported == 1? wxT(" entry") : wxT(" entries"));
      rpt.WriteLine(tostdstring(cs_temp));

      if (numSkipped != 0) {
        wxString cs_tmp;
        cs_tmp << wxT("\nSkipped ") << numSkipped << (numSkipped == 1? wxT(" entry") : wxT(" entries"));
        rpt.WriteLine(tostdstring(cs_tmp));
        cs_temp += cs_tmp;
      }

      if (numPWHErrors != 0) {
        wxString cs_tmp;
        cs_tmp << wxT("\nwith Password History errors ") << numPWHErrors;
        rpt.WriteLine(tostdstring(cs_tmp));
        cs_temp += cs_tmp;
      }

      if (numRenamed != 0) {
        wxString cs_tmp;
        cs_tmp << wxT("\nRenamed ") << numRenamed << (numRenamed == 1? wxT(" entry") : wxT(" entries"));
        rpt.WriteLine(tostdstring(cs_tmp));
        cs_temp += cs_tmp;
      }

      cs_title = (rc == PWScore::SUCCESS ? wxT("Completed successfully") : wxT("Completed but ...."));

      RefreshViews();
      
      break;
    }
  } // switch
  
  // Finish Report
  rpt.EndReport();

  const int iconType = (rc == PWScore::SUCCESS ? wxICON_INFORMATION : wxICON_EXCLAMATION);
  cs_temp << wxT("\n\nDo you want to see a detailed report?");
  if (wxMessageBox(cs_temp, cs_title, wxYES_NO | iconType, this) == wxYES) {
    ViewReport(rpt);
  }
}

void PasswordSafeFrame::OnImportKeePass(wxCommandEvent& evt)
{
  UNREFERENCED_PARAMETER(evt);
  if (m_core.IsReadOnly()) // disable in read-only mode
    return;

  wxFileDialog fd(this, _("Please Choose a KeePass Text File to Import"),
                  wxEmptyString, wxEmptyString,
                  _("Text files (*.txt)|*.txt|CSV files (*.csv)|*.csv|All files (*.*; *)|*.*;*"),
                  (wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_PREVIEW));

  if (fd.ShowModal() != wxID_OK )
    return;

  const wxString KPsFileName(fd.GetPath());
  CReport rpt;

  enum { KeePassCSV, KeePassTXT } ImportType = wxFileName(KPsFileName).GetExt() == wxT("csv")? KeePassCSV: KeePassTXT;

  if (ImportType == KeePassCSV)
    rpt.StartReport(_("Import_KeePassV1_CSV"), m_core.GetCurFile().c_str());
  else
    rpt.StartReport(_("Import_KeePassV1_TXT"), m_core.GetCurFile().c_str());

  rpt.WriteLine(static_cast<const TCHAR *>(wxString::Format(_("Text file being imported: %s"), static_cast<const TCHAR *>(KPsFileName))));
  rpt.WriteLine();

  int numImported, numSkipped, numRenamed;
  unsigned int uiReasonCode = 0;
  int rc;
  Command *pcmd = NULL;

  if (ImportType == KeePassCSV)
    rc = m_core.ImportKeePassV1CSVFile(tostringx(KPsFileName), numImported, numSkipped, numRenamed,
                                       uiReasonCode, rpt, pcmd);
  else
    rc = m_core.ImportKeePassV1TXTFile(tostringx(KPsFileName), numImported, numSkipped, numRenamed,
                                       uiReasonCode, rpt, pcmd);
  switch (rc) {
    case PWScore::CANT_OPEN_FILE:
    {
      wxMessageBox( wxString::Format(_("%s\n\nCould not open file for reading!"), KPsFileName.GetData()),
                    _("File open error"), wxOK | wxICON_ERROR, this);
      delete [] pcmd;
      break;
    }
    case PWScore::INVALID_FORMAT:
    case PWScore::FAILURE:
    {
      wxString msg;
      if (uiReasonCode > 0) {
        stringT s;
        LoadAString(s, uiReasonCode);
        msg = towxstring(s);
      }
      else
        msg = wxString::Format(_("%s\n\nInvalid format"), KPsFileName.GetData());
      wxMessageBox(msg, _("Import failed"), wxOK | wxICON_ERROR, this);
      delete [] pcmd;
      break;
    }
    case PWScore::SUCCESS:
    default: // deliberate fallthru
      if (pcmd != NULL)
        Execute(pcmd);
      RefreshViews();
#ifdef NOT_YET
      ChangeOkUpdate();
#endif
      rpt.WriteLine();
      wxString cs_type(numImported == 1 ? _("entry") : _("entries"));
      wxString cs_msg = wxString::Format(_("Imported %d %s"), numImported, cs_type.GetData());
      rpt.WriteLine(static_cast<const TCHAR*>(cs_msg));
      rpt.EndReport();
      wxString title(rc == PWScore::SUCCESS ? _("Completed successfully") : _("Completed but ...."));
      int icon = (rc == PWScore::SUCCESS ? wxICON_INFORMATION : wxICON_EXCLAMATION);
      cs_msg << _("\n\nDo you wish to see a detailed report?");
      if (wxMessageBox(cs_msg, title, icon|wxYES_NO, this) == wxYES)
        ViewReport(rpt);
      break;
  } // switch
}

void PasswordSafeFrame::OnImportXML(wxCommandEvent& evt)
{
  UNREFERENCED_PARAMETER(evt);
  if (m_core.IsReadOnly()) // disable in read-only mode
    return;

  // Initialize set
  GTUSet setGTU;
  if (!m_core.GetUniqueGTUValidated() && !m_core.InitialiseGTU(setGTU)) {
    // Database is not unique to start with - tell user to validate it first
    wxMessageBox(wxString::Format( _("The database:\n\n%s\n\nhas duplicate entries with the same group/title/user combination. Please fix by validating database."),
                                    m_core.GetCurFile().c_str()), _("Import XML failed"), wxOK | wxICON_ERROR, this);
    return;
  }

  TCHAR XSDfn[] = wxT("pwsafe.xsd");
  wxFileName XSDFilename(towxstring(PWSdirs::GetXMLDir()), XSDfn);

#if USE_XML_LIBRARY == MSXML || USE_XML_LIBRARY == XERCES
  if (!XSDFilename.FileExists()) {
    wxString filepath(XSDFilename.GetFullPath());
    wxMessageBox(wxString::Format(_("Can't find XML Schema Definition file (%s) in your PasswordSafe Application Directory.\r\nPlease copy it from your installation file, or re-install PasswordSafe."), filepath.c_str()), 
                          wxString(_("Missing XSD File - ")) + wxSTRINGIZE_T(USE_XML_LIBRARY) + _(" Build"), wxOK | wxICON_ERROR, this);
    return;
  }
#endif

  CImportXMLDlg dlg(this);
  if (dlg.ShowModal() != wxID_OK)
    return;

  std::wstring ImportedPrefix(tostdstring(dlg.groupName));
  std::wstring strXMLErrors, strSkippedList, strPWHErrorList, strRenameList;
  wxString XMLFilename = dlg.filepath;
  int numValidated, numImported, numSkipped, numRenamed, numPWHErrors;
  int numRenamedPolicies, numNoPolicy;
  bool bImportPSWDsOnly = dlg.importPasswordsOnly;

  wxBeginBusyCursor();  // This may take a while!

  /* Create report as we go */
  CReport rpt;
  rpt.StartReport(_("Import_XML"), m_core.GetCurFile().c_str());
  rpt.WriteLine(tostdstring(wxString::Format(_("%s file being imported: %s"), _("XML"), XMLFilename.c_str())));
  rpt.WriteLine();
  std::vector<StringX> vgroups;
  Command *pcmd = NULL;

  int rc = m_core.ImportXMLFile(ImportedPrefix, std::wstring(XMLFilename),
                            tostdstring(XSDFilename.GetFullPath()), bImportPSWDsOnly,
                            strXMLErrors, strSkippedList, strPWHErrorList, strRenameList,
                            numValidated, numImported, numSkipped, numPWHErrors, numRenamed,
                            numNoPolicy, numRenamedPolicies,
                            rpt, pcmd);
  wxEndBusyCursor();  // Restore normal cursor

  wxString cs_temp;
  wxString cs_title(_("Import XML failed"));
  
  std::wstring csErrors(wxT(""));
  switch (rc) {
    case PWScore::XML_FAILED_VALIDATION:
      rpt.WriteLine(strXMLErrors.c_str());
      cs_temp = wxString::Format(_("File: %s failed validation against XML Schema:\r\n\r\n%s"),
                                        dlg.filepath.c_str(), wxT(""));
      delete pcmd;
      break;
    case PWScore::XML_FAILED_IMPORT:
      rpt.WriteLine(strXMLErrors.c_str());
      cs_temp = wxString::Format(_("File: %s passed Validation but had the following errors during import:\r\n\r\n%s"),
                              dlg.filepath.c_str(), wxT(""));
      delete pcmd;
      break;
    case PWScore::SUCCESS:
    case PWScore::OK_WITH_ERRORS:
      cs_title = rc == PWScore::SUCCESS ? _("Completed successfully") :  _("Completed but ....");
      if (pcmd != NULL)
        Execute(pcmd);

      if (!strXMLErrors.empty() ||
          numRenamed > 0 || numPWHErrors > 0) {
        if (!strXMLErrors.empty())
          csErrors = strXMLErrors + wxT("\n");

        if (!csErrors.empty()) {
          rpt.WriteLine(csErrors.c_str());
        }

        wxString cs_renamed, cs_PWHErrors, cs_skipped;
        if (numSkipped > 0) {
          cs_skipped = _("The following records were skipped:");
          rpt.WriteLine(tostdstring(cs_skipped));
          cs_skipped.Printf(_(" / skipped %d"), numSkipped);
          rpt.WriteLine(strSkippedList.c_str());
          rpt.WriteLine();
        }
        if (numPWHErrors > 0) {
          cs_PWHErrors = _("The following records had errors in their Password History:");
          rpt.WriteLine(tostdstring(cs_PWHErrors));
          cs_PWHErrors.Printf(_(" / with Password History errors %d"), numPWHErrors);
          rpt.WriteLine(strPWHErrorList.c_str());
          rpt.WriteLine();
        }
        if (numRenamed > 0) {
          cs_renamed = _("The following records were renamed as an entry already exists in your database or in the Import file:");
          rpt.WriteLine(tostdstring(cs_renamed));
          cs_renamed.Printf(_(" / renamed %d"), numRenamed);
          rpt.WriteLine(strRenameList.c_str());
          rpt.WriteLine();
        }

        cs_temp.Printf(_("File: %s was imported (entries validated %d / imported %d%s%s%s). See report for details."),
                       dlg.filepath.c_str(), numValidated, numImported,
                       cs_skipped.c_str(), cs_renamed.c_str(), cs_PWHErrors.c_str());

      } else {
        const TCHAR* cs_validate = numValidated == 1 ? _("entry") : _("entries");
        const TCHAR* cs_imported = numImported == 1 ? _("entry") : _("entries");
        cs_temp.Printf(_("Validated %d %s\r\n\r\nImported %d %s"), numValidated, cs_validate, numImported, cs_imported);
      }

      RefreshViews();
      break;
    case PWScore::UNIMPLEMENTED:
      cs_temp = _("XML import not supported in this release");
      break;
    default:
      cs_temp.Printf(_("XML import: Unexpected return code(%d)"), rc);
      break;
  } // switch

  // Finish Report
  rpt.WriteLine(tostdstring(cs_temp));
  rpt.EndReport();

  const int iconType = (rc != PWScore::SUCCESS || !strXMLErrors.empty()) ? wxICON_EXCLAMATION : wxICON_INFORMATION;

  cs_temp << _("\n\nDo you wish to see a detailed report?");
  if ( wxMessageBox(cs_temp, cs_title, wxYES_NO | iconType, this) == wxYES) {
    ViewReport(rpt);
  }
}

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

void PasswordSafeFrame::OnExportVx(wxCommandEvent& evt)
{
  int rc;
  StringX newfile;
  wxString cs_text, cs_title, cs_temp;

  //SaveAs-type dialog box
  std::wstring OldFormatFileName = PWSUtil::GetNewFileName(m_core.GetCurFile().c_str(),
                                                      wxT("dat"));
  cs_text = _("Please name the exported database");

  //filename cannot have the path. Need to pass it separately
  wxFileName filename(towxstring(OldFormatFileName));
  wxString dir = filename.GetPath();
  if (dir.empty())
    dir = towxstring(PWSdirs::GetSafeDir());

  wxFileDialog fd(this, cs_text, dir, filename.GetFullName(),
                _("Password Safe Databases (*.psafe3; *.dat)|*.psafe3;*.dat|All files (*.*; *)|*.*;*"),
                 wxFD_SAVE | wxFD_OVERWRITE_PROMPT);

  if (fd.ShowModal() != wxID_OK)
    return;

  newfile = tostringx(fd.GetPath());

  switch (evt.GetId()) {
    case ID_EXPORT2OLD1XFORMAT:
      rc = m_core.WriteV17File(newfile);
      break;
    case ID_EXPORT2V2FORMAT:
      rc = m_core.WriteV2File(newfile);
      break;
    default:
      wxFAIL_MSG(_("Could not figure out why PasswordSafeFrame::OnExportVx was invoked"));
      rc = PWScore::FAILURE;
      break;
  }
  if (rc != PWScore::SUCCESS) {
    DisplayFileWriteError(rc, newfile);
  }
}

struct ExportFullText
{
  static wxString GetTitle() {return _("Export Text");}
  static void MakeOrderedItemList(PasswordSafeFrame* frame, OrderedItemList& olist) { 
    frame->FlattenTree(olist);
  }
  static wxString GetFailureMsgTitle() {return _("Export Text failed"); }
  static stringT  FileExtension() { return wxT("txt"); }
  static wxString FileOpenPrompt() { return _("Please name the plaintext file"); }
  static wxString WildCards() {return _("Text files (*.txt)|*.txt|CSV files (*.csv)|*.csv|All files (*.*; *)|*.*;*"); }
  static int Write(PWScore& core, const StringX &filename, const CItemData::FieldBits &bsFields,
                          const stringT &subgroup_name, int subgroup_object, 
                          int subgroup_function, TCHAR delimiter, int &numExported,
                          const OrderedItemList *il)
  {
    return core.WritePlaintextFile(filename, bsFields, subgroup_name, subgroup_object, subgroup_function,
                          delimiter, numExported, il);
  }
  static wxString GetAdvancedSelectionTitle() {
    return _("Advanced Text Export Options");
  }
  
  static bool IsMandatoryField(CItemData::FieldType /*field*/) {
    return false;
  }
  
  static bool ShowFieldSelection() {
    return true;
  }

  static wxString GetTaskWord() {
    return _("export");
  }
};

void PasswordSafeFrame::OnExportPlainText(wxCommandEvent& evt)
{
  UNREFERENCED_PARAMETER(evt);
  DoExportText<ExportFullText>();
}

struct ExportFullXml {
  static wxString GetTitle() {return _("Export XML");}
  static void MakeOrderedItemList(PasswordSafeFrame* frame, OrderedItemList& olist) { 
    frame->FlattenTree(olist);
  }
  static wxString GetFailureMsgTitle() {return _("Export XML failed"); }
  static stringT  FileExtension() { return wxT("xml"); }
  static wxString FileOpenPrompt() { return _("Please name the XML file"); }
  static wxString WildCards() {return _("XML files (*.xml)|*.xml|All files (*.*; *)|*.*;*"); }
  static int Write(PWScore& core, const StringX &filename, const CItemData::FieldBits &bsFields,
                          const stringT &subgroup_name, int subgroup_object, 
                          int subgroup_function, TCHAR delimiter, int &numExported,
                          const OrderedItemList *il)
  {
    bool bFilterActive = false;
    return core.WriteXMLFile(filename, bsFields, subgroup_name, subgroup_object, subgroup_function,
                          delimiter, numExported, il, bFilterActive);
  }
  static wxString GetAdvancedSelectionTitle() {
    return _("Advanced XML Export Options");
  }
  
  static bool IsMandatoryField(CItemData::FieldType field) {
    return field == CItemData::TITLE || field == CItemData::PASSWORD;
  }
  
  static bool ShowFieldSelection() {
    return true;
  }
  static wxString GetTaskWord() {
    return _("export");
  }
};

void PasswordSafeFrame::OnExportXml(wxCommandEvent& evt)
{
  UNREFERENCED_PARAMETER(evt);
  DoExportText<ExportFullXml>();
}

IMPLEMENT_CLASS_TEMPLATE( AdvancedSelectionDlg, wxDialog, ExportFullXml )
IMPLEMENT_CLASS_TEMPLATE( AdvancedSelectionDlg, wxDialog, ExportFullText )

template <class ExportType>
void PasswordSafeFrame::DoExportText()
{
  const wxString title(ExportType::GetTitle());
  
  const StringX sx_temp(m_core.GetCurFile());
  
  //MFC code doesn't do this for XML export, but it probably should, because it tries
  //to use core.GetcurFile() later 
  if (sx_temp.empty()) {
    //  Database has not been saved - prompt user to do so first!
    wxMessageBox(_T("You must save this database before it can be exported."), title, wxOK|wxICON_EXCLAMATION, this);
    return;
  }

  CExportTextWarningDlg<ExportType> et(this);
  if (et.ShowModal() != wxID_OK)
    return;
  
  StringX newfile;
  StringX pw(et.passKey);
  if (m_core.CheckPasskey(sx_temp, pw) == PWScore::SUCCESS) {
    const CItemData::FieldBits bsExport = et.selCriteria.GetSelectedFields();
    const std::wstring subgroup_name = tostdstring(et.selCriteria.SubgroupSearchText());
    const int subgroup_object = et.selCriteria.SubgroupObject();
    const int subgroup_function = et.selCriteria.SubgroupFunctionWithCase();
    wchar_t delimiter = et.delimiter.IsEmpty()? wxT('\xbb') : et.delimiter[0];

    // Note: MakeOrderedItemList gets its members by walking the 
    // tree therefore, if a filter is active, it will ONLY export
    // those being displayed.
    OrderedItemList orderedItemList;
    ExportType::MakeOrderedItemList(this, orderedItemList);

    /*
     * First parameter indicates whether or not the user has specified
     * 'Advanced' to filter the entries to be exported.
     * Effectively, subgroup_* parameters are ignored if 1st param is false.
     */
    int numExported(0);
    switch(m_core.TestSelection(false, subgroup_name, subgroup_object,
                                subgroup_function, &orderedItemList)) {
      case PWScore::SUCCESS:
      {
        // do the export
        // SaveAs-type dialog box
        wxFileName TxtFileName(towxstring(PWSUtil::GetNewFileName(sx_temp.c_str(), ExportType::FileExtension())));

        wxFileDialog fd(this, ExportType::FileOpenPrompt(), TxtFileName.GetPath(), 
                        TxtFileName.GetFullName(), ExportType::WildCards(), 
                        wxFD_SAVE | wxFD_OVERWRITE_PROMPT);

        if (fd.ShowModal() == wxID_OK) {
          newfile = fd.GetPath();
          int rc = ExportType::Write(m_core, newfile, bsExport, subgroup_name, subgroup_object, 
                                      subgroup_function, delimiter, numExported, &orderedItemList);

          orderedItemList.clear(); // cleanup soonest

          if (rc != PWScore::SUCCESS) {
            DisplayFileWriteError(rc, newfile);
          }
        }
        break;
      }
      
      case PWScore::NO_ENTRIES_EXPORTED:
      {
        wxMessageBox(_("No entries satisfied your selection criteria and so none were exported!"),
                      ExportType::GetFailureMsgTitle(), wxOK | wxICON_WARNING, this);
        break;
      }
      
      default:
        break;
        
    } //switch

    orderedItemList.clear(); // cleanup soonest

  } else {
    wxMessageBox(_("Passkey incorrect"), title, wxOK|wxICON_ERROR, this);
    pws_os::sleep_ms(3000); // against automatic attacks
  }
}

//
// ----------  Merge, Synchronize and Compare ------------------
//
void PasswordSafeFrame::OnMergeAnotherSafe(wxCommandEvent& evt)
{
  UNREFERENCED_PARAMETER(evt);
  MergeDlg dlg(this, &m_core);
  if (dlg.ShowModal() == wxID_OK) {
    PWScore othercore;
    if (ReadCore(othercore,
                 dlg.GetOtherSafePath(),
                 dlg.GetOtherSafeCombination(),
                 true,
                 this) == PWScore::SUCCESS) {
        Merge(tostringx(dlg.GetOtherSafePath()), &othercore, dlg.GetSelectionCriteria());
    }
  }
}

void PasswordSafeFrame::Merge(const StringX &sx_Filename2, PWScore *pothercore, const SelectionCriteria& selection)
{
  /* Put up hourglass...this might take a while */
  ::wxBeginBusyCursor();

  /* Create report as we go */
  CReport rpt;

  rpt.StartReport(_("Merge"), m_core.GetCurFile().c_str());
  rpt.WriteLine(tostdstring(wxString(_("Merging database: ")) << towxstring(sx_Filename2) << wxT("\r\n")));
  
  stringT result = m_core.Merge(pothercore, 
                                selection.HasSubgroupRestriction(),
                                tostdstring(selection.SubgroupSearchText()),
                                selection.SubgroupObject(),
                                selection.SubgroupFunction(),
                                &rpt);
  
  ::wxEndBusyCursor();

  rpt.EndReport();

  if (!result.empty() && wxMessageBox(towxstring(result) + _("\r\n\r\nDo you wish to see a detailed report?"),
                                        _("Merge Complete"), wxYES_NO|wxICON_QUESTION, this) == wxYES) {
    ViewReport(rpt);
  }
}


void PasswordSafeFrame::OnSynchronize(wxCommandEvent& /*evt*/)
{
  // disable in read-only mode or empty
  wxCHECK_RET(!m_core.IsReadOnly() && !m_core.GetCurFile().empty() && m_core.GetNumEntries() != 0,
                wxT("Synchronize menu enabled for empty or read-only database!"));

  PwsSyncWizard wiz(this, &m_core);
  wiz.RunWizard(wiz.GetFirstPage());

  if (wiz.GetNumUpdated() > 0)
    SetChanged(Data);

#ifdef NOT_YET
  ChangeOkUpdate();
#endif

  RefreshViews();
  
  if (wiz.ShowReport())
    ViewReport(*wiz.GetReport());
}

void PasswordSafeFrame::OnCompare(wxCommandEvent& /*evt*/)
{
  CompareDlg dlg(this, &m_core);
  dlg.ShowModal();
}

//-----------------------------------------------------------------
// 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??)
//-----------------------------------------------------------------


