diff --git a/msvc/wallet.vcxproj b/msvc/wallet.vcxproj index 41f374e..d4424b6 100644 --- a/msvc/wallet.vcxproj +++ b/msvc/wallet.vcxproj @@ -43,19 +43,19 @@ true - $(SolutionDir)wxWidgets\include\msvc;$(SolutionDir)wxWidgets\include;$(SolutionDir)libsodium\src\libsodium\include;$(SolutionDir)mpir;$(IncludePath) - $(SolutionDir)wxWidgets\lib\vc_x64_lib\;$(SolutionDir)libsodium\bin\x64\Debug\v143\static;$(SolutionDir)mpir\lib\x64\Debug;$(LibraryPath) + ..\wxWidgets\include\msvc;..\wxWidgets\include;..\libsodium\src\libsodium\include;..\mpir;$(IncludePath) + ..\wxWidgets\lib\vc_x64_lib\;..\libsodium\bin\x64\Debug\v143\static;..\mpir\lib\x64\Debug;$(LibraryPath) - $(SolutionDir)build\$(Configuration)\ - $(SolutionDir)build\$(Configuration)\ + ..\build\$(Configuration)\ + ..\build\$(Configuration)\ false - $(SolutionDir)wxWidgets\include\msvc;$(SolutionDir)wxWidgets\include;$(SolutionDir)libsodium\src\libsodium\include;$(SolutionDir)mpir;$(IncludePath) - $(SolutionDir)wxWidgets\lib\vc_x64_lib\;$(SolutionDir)libsodium\bin\x64\Release\v143\static;$(SolutionDir)mpir\lib\x64\Release;$(LibraryPath) + ..\wxWidgets\include\msvc;..\wxWidgets\include;..\libsodium\src\libsodium\include;..\mpir;$(IncludePath) + ..\wxWidgets\lib\vc_x64_lib\;..\libsodium\bin\x64\Release\v143\static;..\mpir\lib\x64\Release;$(LibraryPath) - $(SolutionDir)build\$(Configuration)\ - $(SolutionDir)build\$(Configuration)\ + ..\build\$(Configuration)\ + ..\build\$(Configuration)\ @@ -168,7 +168,7 @@ NotUsing NotUsing - + NotUsing NotUsing @@ -193,4 +193,4 @@ - \ No newline at end of file + diff --git a/src/display_wallet.cpp b/src/display_wallet.cpp index bc2b98b..5fbdadd 100644 --- a/src/display_wallet.cpp +++ b/src/display_wallet.cpp @@ -3,8 +3,7 @@ using ro::base58; static constexpr char SrcFilename[]{ "src/display_wallet.cpp" }; display_wallet::display_wallet(wxWindow* parent, wxFileName& walletfile) : wxPanel(parent, myID_WALLET_UI, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, wxT("Wallet")), - m_db(nullptr), - m_menuitem_add_name(this, &display_wallet::add_name_event_handler) + m_db(nullptr), m_DisplayWalletEditMenu(1) { wxLogMessage(wxT("Loading %s"), walletfile.GetFullPath()); try { @@ -73,10 +72,16 @@ display_wallet::display_wallet(wxWindow* parent, wxFileName& walletfile) : this->SetSize(this->GetParent()->GetClientSize()); singletonFrame->m_LastUsedWallet.Assign(walletfile); - wxMenu* menuFile{ singletonFrame->GetMenuBar()->GetMenu(0) }; - singletonFrame->GetMenuBar()->EnableTop(1, true); //enable edit menu. - wxMenu* menuEdit{ singletonFrame->GetMenuBar()->GetMenu(1) }; - m_menuitem_add_name.Insert(menuEdit, 0, "add name", "create new Zooko identity"); + m_DisplayWalletEditMenu.Menu->Append( + myID_DISPLAY_WALLET_ADD_NAME, + "add name", "create new Zooko identity" + ); + m_DisplayWalletEditMenu.Menu->Bind( + wxEVT_MENU, + &display_wallet::add_name_event_handler, + this, myID_DISPLAY_WALLET_ADD_NAME + ); + m_DisplayWalletEditMenu.Replace(); } catch (const MyException&) { throw; @@ -91,10 +96,8 @@ display_wallet::display_wallet(wxWindow* parent, wxFileName& walletfile) : } -display_wallet::~display_wallet() { - assert(true); - singletonFrame->GetMenuBar()->EnableTop(1, false); //disable edit menu. +display_wallet::~display_wallet() { } void display_wallet::close_menu_event_handler(wxCommandEvent& event) { diff --git a/src/display_wallet.h b/src/display_wallet.h index 00243fa..0080c5e 100644 --- a/src/display_wallet.h +++ b/src/display_wallet.h @@ -14,6 +14,6 @@ private: wxBoxSizer* m_rSizer; void close_menu_event_handler(wxCommandEvent&); void add_name_event_handler(wxCommandEvent&); - MenuLink m_menuitem_add_name; void OnClose(wxCloseEvent& event); + wxMenuTracker m_DisplayWalletEditMenu; }; diff --git a/src/frame.cpp b/src/frame.cpp index fe9ea89..2ac8cb9 100644 --- a/src/frame.cpp +++ b/src/frame.cpp @@ -1,6 +1,36 @@ #include "stdafx.h" static constexpr char SrcFilename[]{ "src/frame.cpp" }; +// ---------------------------------------------------------------------------- +//wxMenuTracker +// ---------------------------------------------------------------------------- + +wxMenuTracker::wxMenuTracker(const int i) : Menu(new wxMenu), MenuPosition(i) {}; + +wxMenu* wxMenuTracker::InitialAndFinal[]{ nullptr, new wxMenu, nullptr }; + +void wxMenuTracker::Replace() { + singletonFrame->GetMenuBar()->Replace( + MenuPosition, + Menu, + menu_strings[MenuPosition].head + ); + singletonFrame->GetMenuBar()->EnableTop(MenuPosition, true); //enable edit menu. +}; +wxMenuTracker::~wxMenuTracker() { + auto menu_bar = singletonFrame->GetMenuBar(); + if (menu_bar->GetMenu(MenuPosition) == Menu) { + assert(InitialAndFinal[MenuPosition]); + menu_bar->Replace( + MenuPosition, + InitialAndFinal[MenuPosition], + menu_strings[MenuPosition].head + ); + menu_bar->EnableTop(MenuPosition, false); + delete Menu; + } +}; + // ---------------------------------------------------------------------------- // frame // ---------------------------------------------------------------------------- @@ -157,18 +187,36 @@ Frame::Frame(const wxString& wxs) menuFile->Bind(wxEVT_MENU, &Frame::OnDeleteConfiguration, this, myID_DELETECONFIG); menuFile->Append(wxID_EXIT); menuFile->Bind(wxEVT_MENU, &Frame::OnExit, this, wxID_EXIT); - + + wxMenu* menuEdit = wxMenuTracker::InitialAndFinal[1]; wxMenu* menuHelp = new wxMenu; menuHelp->Append(wxID_ABOUT); menuHelp->Bind(wxEVT_MENU, &Frame::OnAbout, this, wxID_ABOUT); wxMenuBar* menuBar = new wxMenuBar; menuBar->Append(menuFile, menu_strings[0].head); - menuBar->Append(new wxMenu, menu_strings[1].head); //Edit menu, initially empty and disabled + menuBar->Append(menuEdit, menu_strings[1].head); //Edit menu, initially empty and disabled menuBar->Append(menuHelp, menu_strings[2].head); SetMenuBar(menuBar); - CreateStatusBar(); menuBar->EnableTop(1, false); //disable edit menu. - // child controls + if constexpr (debug_mode) { + // Check that the initial values of all replaceable menus + // are at their default values as if the window handling them + // had just been destroyed and not yet replaced. + for (int i = 0; i < std::size(wxMenuTracker::InitialAndFinal); i++) { + assert( + !wxMenuTracker::InitialAndFinal[i] + || + ( + wxMenuTracker::InitialAndFinal[i] == menuBar->GetMenu(i) + && + !menuBar->IsEnabledTop(i) + ) + ); + + } + } + CreateStatusBar(); + // child controls wxPanel* panel{ nullptr }; m_panel = panel; wxConfigBase& Config = singletonApp->m_Config; @@ -374,45 +422,59 @@ COMMIT; void Frame::OnSaveNew(wxCommandEvent& WXUNUSED(event)) { - wxString wxstrWalletPath; - wxString wxstrWalletName(wxEmptyString); - if (!m_LastUsedWallet.IsOk() || !m_LastUsedWallet.DirExists()) { - m_LastUsedWallet = m_DefaultWalletLocation; - RecursiveCreateDirectory(m_LastUsedWallet); - } - wxstrWalletPath = m_LastUsedWallet.GetPath(); //Directory guaranteed to exist, so we will not get idiot default - // It took me a ridiculous amount of time to fix that all paths to this file dialog - // are either terminated by an exception, or the directory exists, or is created. - // Any time the program has to deal with something external to itself, anything that can go wrong - // will go wrong. - if (!m_LastUsedWallet.FileExists()) wxstrWalletName = m_LastUsedWallet.GetFullName(); - wxFileDialog dialog(this, - sz_new_wallet_new_secret, - wxstrWalletPath, - wxstrWalletName, - wxString::Format - ("wallet (*.wallet)|*.wallet|All (%s)|%s", - wxFileSelectorDefaultWildcardStr, - wxFileSelectorDefaultWildcardStr - ), - wxFD_SAVE | wxFD_OVERWRITE_PROMPT - ); - dialog.SetFilterIndex(m_FileDialogFilterIndex); - if (dialog.ShowModal() == wxID_OK) - { - wxLogMessage("%s, filter %d", - dialog.GetPath(), - dialog.GetFilterIndex() + wxFileName wxFileWallet; + if (m_DefaultWalletLocation.FileExists()) { + // OK, the default wallet exists, so we need a new + // name and wallet location + wxString wxstrWalletPath; + wxString wxstrWalletName(wxEmptyString); + if (!m_LastUsedWallet.IsOk() || !m_LastUsedWallet.DirExists()) { + m_LastUsedWallet = m_DefaultWalletLocation; + RecursiveCreateDirectory(m_LastUsedWallet); + } + wxstrWalletPath = m_LastUsedWallet.GetPath(); //Directory guaranteed to exist, so we will not get idiot default + // It took me a ridiculous amount of time to fix that all paths to this file dialog + // are either terminated by an exception, or the directory exists, or is created. + // Any time the program has to deal with something external to itself, anything that can go wrong + // will go wrong. + if (!m_LastUsedWallet.FileExists()) wxstrWalletName = m_LastUsedWallet.GetFullName(); + wxFileDialog dialog(this, + sz_new_wallet_new_secret, + wxstrWalletPath, + wxstrWalletName, + wxString::Format + ("wallet (*.wallet)|*.wallet|All (%s)|%s", + wxFileSelectorDefaultWildcardStr, + wxFileSelectorDefaultWildcardStr + ), + wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); - wxFileName wxFileWallet(dialog.GetPath()); - ristretto255::hash<256> WalletSecret( wxFileWallet.GetFullPath().ToUTF8()); + dialog.SetFilterIndex(m_FileDialogFilterIndex); + if (dialog.ShowModal() == wxID_OK) + { + wxLogMessage("%s, filter %d", + dialog.GetPath(), + dialog.GetFilterIndex() + ); + wxFileWallet.Assign(dialog.GetPath()); + m_FileDialogFilterIndex = dialog.GetFilterIndex(); + } + } + else { + // Default does not exist, so we go right + // ahead without asking the user to invent + // a name and select a directory + RecursiveCreateDirectory(m_DefaultWalletLocation); + wxFileWallet = m_DefaultWalletLocation; + } + if (wxFileWallet.IsOk()) { + ristretto255::hash<256> WalletSecret(wxFileWallet.GetFullPath().ToUTF8()); NewWallet(wxFileWallet, WalletSecret); wxLogMessage("new wallet created: %s", wxFileWallet.GetFullPath()); if (m_panel)m_panel->Close(true); m_panel = nullptr; auto panel = new display_wallet(this, wxFileWallet); m_panel = panel; - m_FileDialogFilterIndex = dialog.GetFilterIndex(); m_LastUsedWallet = wxFileWallet; //We do this last, so that if an exception occurs the filename is forgotten. } } @@ -497,11 +559,29 @@ Frame::~Frame() { wxConfigBase& Config = singletonApp->m_Config; StorePositionToConfig(); Config.SetPath(wxT("/TipOfTheDay")); - Config.Write("show", (int)m_showTipsAtStartup); - Config.Write("index", (int)m_TipOfTheDayIndex); - Config.SetPath(wxT("/FileDialog")); - Config.Write("index", (int)m_FileDialogFilterIndex); - Config.Write(wxT("LastUsed"), m_LastUsedWallet.GetFullPath()); - Config.SetPath(wxT("/")); - Config.Flush(); + Config.Write("show", (int)m_showTipsAtStartup); + Config.Write("index", (int)m_TipOfTheDayIndex); + Config.SetPath(wxT("/FileDialog")); + Config.Write("index", (int)m_FileDialogFilterIndex); + Config.Write(wxT("LastUsed"), m_LastUsedWallet.GetFullPath()); + Config.SetPath(wxT("/")); + Config.Flush(); + if constexpr (debug_mode) { + // Check that the final values of all replaceable menus + // are at what they should be because the window handling + // them should have been destroyed. + auto menuBar = this->GetMenuBar(); + for (int i = 0; i < std::size(wxMenuTracker::InitialAndFinal); i++) { + assert( + !wxMenuTracker::InitialAndFinal[i] + || + ( + wxMenuTracker::InitialAndFinal[i] == menuBar->GetMenu(i) + && + !menuBar->IsEnabledTop(i) + ) + ); + + } + } } diff --git a/src/frame.h b/src/frame.h index 056872c..40800b5 100644 --- a/src/frame.h +++ b/src/frame.h @@ -1,4 +1,39 @@ #pragma once + // wxWidgets has drop down menus, wxMenu, owned by the wxMenuBar, + // which is owned by the Frame window, but menus subject to replacement need + // to be destroyed by the child window. + // wxMenuOwner tracks a drop down menu that might be at a location in + // the menubar, places it in that location in the menubar on demand, + // and on destruction destroys it + // If, on destruction wxMenuBar is still pointing to this wxMenu, it + // replaces it with the default (empty) menu for that location. + // and disables it. + // We maintain an array of default empty wxMenu menus that are nullptr for + // locations not subject to replacement, and are the disabled initial and final + // wxMenuBar values for locations in the Menubar where the real drop down + // in the menubar will be installed by wxMenuTracker. +struct wxMenuTracker { + wxMenuTracker(int); + void Replace(); + ~wxMenuTracker() noexcept; + // Non nullptr members of InitialAndFinal have to be explicitly initially placed + // in the menubar in the frame constructor and explicitly disabled in + // the frame initializer. In the debug build, congruence between the menu bar + // and InitialAndFinal should be checked with assert. + static wxMenu* InitialAndFinal[3]; + wxMenuTracker(wxMenuTracker&&) = delete; // Move constructor + wxMenuTracker(const wxMenuTracker&) = delete; // Copy constructor + wxMenuTracker& operator=(wxMenuTracker&&) = delete; // Move assignment. + wxMenuTracker& operator=(const wxMenuTracker&) = delete; // Copy assignment. + bool operator==(const wxMenuTracker&) const = delete; + auto operator<=>(const wxMenuTracker&) const = delete; + wxMenu* Menu; + const int MenuPosition; + // The menu title is defined elsewhere as an array + // of const strings + // menu_strings[MenuPosition].head +}; + template // This class exists to record the needed to unbind a drop down menu action and delete // the corresponding item from the drop down menu when the handler is destroyed. diff --git a/src/stdafx.h b/src/stdafx.h index fbe0ac1..2e633c2 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -106,7 +106,7 @@ enum MyIDs { myID_DELETECONFIG = wxID_HIGHEST + 1, myID_ERRORMESSAGE, myID_Hello, myID_MAINFRAME, myID_MAINFRAME_PANEL, myID_TESTWINDOW, myID_WELCOME_TO_ROCOIN, myID_WALLET_UI, - mID_CLOSE_WALLET, myID_MYEXIT + mID_CLOSE_WALLET, myID_MYEXIT, myID_DISPLAY_WALLET_ADD_NAME }; #include "localization.h" diff --git a/wallet.sln b/wallet.sln index bbd3a98..60c3902 100644 --- a/wallet.sln +++ b/wallet.sln @@ -1,12 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.0.32014.148 +VisualStudioVersion = 17.7.34031.279 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wallet", "msvc\wallet.vcxproj", "{B1EC18D5-FA70-4A59-8CAE-EDC65A358314}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2F7D488C-DC53-4ECE-87E2-4FEA32C153EF}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -22,6 +20,6 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {FAF44986-ACA9-4D9B-A9DA-58DBF57EB341} + SolutionGuid = {3DB554F1-91C7-4C5D-9A2E-D0EF86D89C18} EndGlobalSection EndGlobal