Fixing window switches within the frame

Painfully discovered that wxWidgets does not like it
if you do not handle the close event.  Default
handling is broken, perhaps unavoidably because the base
object does not know about the derived object.
This commit is contained in:
Cheng 2022-04-03 12:07:41 +10:00
parent b26721213d
commit 137a78735b
No known key found for this signature in database
GPG Key ID: D51301E176B31828
13 changed files with 118 additions and 101 deletions

View File

@ -192,7 +192,7 @@ void App::OnError(wxCommandEvent& event)
{
// We use this to display errors where throwing would cause problems, as in a destructor
// Instead we post an event to be handled in due course.
wxMessageDialog dlg(singletonFrame, event.GetString(), _T("Error"), wxICON_ERROR);
wxMessageDialog dlg(singletonFrame, event.GetString(), wsz_error, wxICON_ERROR);
dlg.SetId(myID_ERRORMESSAGE);
dlg.ShowModal();
}

View File

@ -2,7 +2,8 @@
using ro::base58;
display_wallet::display_wallet(wxWindow* parent, wxFileName& walletfile) :
wxPanel(parent, myID_WALLET_UI, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("Wallet")),
m_db(nullptr)
m_db(nullptr),
m_menuitem_close(this, &display_wallet::close_menu_event_handler)
{
wxLogMessage(_T("Loading %s"), walletfile.GetFullPath());
if (!walletfile.IsOk() || !walletfile.HasName() || !walletfile.HasExt()) throw MyException("unexpected file name");
@ -55,8 +56,28 @@ display_wallet::display_wallet(wxWindow* parent, wxFileName& walletfile) :
wxALL, // and make border all around
2);
}
Bind(wxEVT_CLOSE_WINDOW, &display_wallet::OnClose, this);
this->SetSize(this->GetParent()->GetClientSize());
singletonFrame->m_LastUsedSqlite.Assign(walletfile);
wxMenu* menuFile{ singletonFrame->GetMenuBar()->GetMenu(0) };
m_menuitem_close.Insert(menuFile, 1, "close", "test");
}
display_wallet::~display_wallet() {
assert(true);
}
void display_wallet::close_menu_event_handler(wxCommandEvent& event) {
wxMessageDialog dlg(this, event.GetString(), wsz_error, wxICON_ERROR);
Close(true);
}
void display_wallet::OnClose(wxCloseEvent& event) {
// This event gives you the opportunity to clean up anything that needs explicit cleanup, albeit if you have done your work right nothing should need explicit cleanup,
// and to object to the closing in a "file not saved" type situation.
// https://docs.wxwidgets.org/trunk/classwx_close_event.html
DestroyChildren();
Destroy(); //Default handler will destroy the window. This is our handler for the user calling close,
// replacing the default handler.'
if (singletonFrame->m_panel ==this)singletonFrame->m_panel = nullptr;
}

View File

@ -5,8 +5,13 @@ public:
display_wallet(wxWindow*, wxFileName&);
~display_wallet();
private:
typedef MenuLink<display_wallet> MenuLink;
std::unique_ptr<ISqlite3> m_db;
ristretto255::CMasterSecret m_MasterSecret;
wxBoxSizer* m_lSizer;
wxBoxSizer* m_rSizer;
void close_menu_event_handler(wxCommandEvent&);
// MenuLink m_close(display_wallet::close);
MenuLink m_menuitem_close;
void OnClose(wxCloseEvent& event);
};

View File

@ -4,6 +4,8 @@
// frame
// ----------------------------------------------------------------------------
Frame* singletonFrame{nullptr};
void Frame::RestorePositionFromConfig(const wxSize& bestSize) {
// SetPath() understands ".." but you should probably never use it.
singletonApp->pConfig->SetPath(_T("/MainFrame")); wxPoint scr{ wxSystemSettings::GetMetric(wxSYS_SCREEN_X), wxSystemSettings::GetMetric(wxSYS_SCREEN_Y) };
@ -116,7 +118,6 @@ try {
SetIcon(wxICON(AAArho)); //Does not appear to do anything. Maybe it does something in Unix.
//wxICON is a namestring on windows, and a symbol on Unix
Bind(wxEVT_CLOSE_WINDOW, &Frame::OnClose, this);
Bind(wxEVT_CLOSE_WINDOW, &Frame::OnClose, this);
wxMenu* menuFile = new wxMenu;
menuFile->Append(wxID_NEW, menu_strings[0].tail[0][0], menu_strings[0].tail[0][1]);
@ -129,31 +130,27 @@ try {
menuFile->Bind(wxEVT_MENU, &Frame::OnFileOpen, this, wxID_OPEN);
menuFile->Append(wxID_DELETE, menu_strings[0].tail[3][0], menu_strings[0].tail[3][1] + m_LastUsedSqlite.GetFullPath());
wxLogMessage(m_LastUsedSqlite.GetFullPath()+" wallet path");
menuFile->Bind(wxEVT_MENU, &Frame::OnDelete, this, wxID_DELETE);
menuFile->AppendSeparator();
menuFile->Append(myID_DELETECONFIG, menu_strings[0].tail[4][0], menu_strings[0].tail[4][1] + m_LastUsedSqlite.GetFullPath());
menuFile->Bind(wxEVT_MENU, &Frame::OnDeleteConfiguration, this, myID_DELETECONFIG);
menuFile->Append(myID_MYEXIT,"my exit, testing destruction");
menuFile->Bind(wxEVT_MENU, &Frame::OnMyCloseMpanel, this, myID_MYEXIT);
menuFile->Append(wxID_EXIT);
menuFile->Bind(wxEVT_MENU, &Frame::OnExit, this, wxID_EXIT);
wxMenu* menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT);
menuHelp->Bind(wxEVT_MENU, &Frame::OnAbout, this, wxID_ABOUT);
menuHelp->Append(myID_ADD_SUBWINDOW, _T("insert new window"), _T("insert"));
menuHelp->Bind(wxEVT_MENU, &Frame::OnAddSubwindow, this, myID_ADD_SUBWINDOW);
menu_OnDeleteSubwindow.Append(menuHelp,this, _T("delete first subwindow"), _T("delete"));
menuHelp->Append(myID_DELETE_LAST_SUBWINDOW, _T("delete last subwindow"), _T("delete"));
menuHelp->Bind(wxEVT_MENU, &Frame::OnDeleteLastSubwindow, this, myID_DELETE_LAST_SUBWINDOW);
// menu_OnFirstUse.Append(menuHelp, this, _T("New wallet"), _T("add new wallet"));
wxMenuBar* menuBar = new wxMenuBar;
menuBar->Append(menuFile, menu_strings[0].head);
menuBar->Append(new wxMenu, menu_strings[1].head);
menuBar->Append(menuHelp, menu_strings[2].head);
SetMenuBar(menuBar);
CreateStatusBar();
menuBar->EnableTop(1, false);
// child controls
m_LastUsedSqlite.Assign(singletonApp->pConfig->Read(_T("/Wallet/LastUsed"), _T("")));
wxString debug_data{ m_LastUsedSqlite.GetFullPath() };
if (!m_LastUsedSqlite.IsOk() || !m_LastUsedSqlite.HasName() || !m_LastUsedSqlite.HasExt()) {
m_panel = new welcome_to_rhocoin(this); //Owner is "this", via the base class wxFrame. m_panel is a
// non owning pointer in the derived class that duplicates the owning pointer in the base class.
@ -166,14 +163,25 @@ try {
SetClientSize(GetClientSize());
}
catch (const std::exception& e) {
// cannot throw when no window is available. Construction of the base frame has to be completed, come what may.
// cannot throw when no window is available. Construction of the base frame has to be completed,
// come what may.
// if an exception propagated from the constructor of the derived frame, it would destruct the base frame
// and chaos would ensue as a windowing program attempts to handle an error with no main window.
// so exceptions in the constructor of the main frame have to be caught and not rethrown.
queue_error_message(e.what());
}
}
void Frame::OnMyCloseMpanel(wxCommandEvent& event) {
if (m_panel) {
m_panel->Close(true);
}
}
void Frame::OnExit(wxCommandEvent& event) {
if (m_panel) {
m_panel->Close(true);
m_panel = nullptr;
}
Close(true);
}
@ -181,14 +189,15 @@ void Frame::OnClose(wxCloseEvent& event) {
// This event gives you the opportunity to clean up anything that needs explicit cleanup, albeit if you have done your work right nothing should need explicit cleanup,
// and to object to the closing in a "file not saved" type situation.
// https://docs.wxwidgets.org/trunk/classwx_close_event.html
if (sqlite3_shutdown())wxMessageBox(_T(R"|(Sqlite3 shutdown error)|"), _T("Error"), wxICON_ERROR);
if (sqlite3_shutdown())wxMessageBox(_T(R"|(Sqlite3 shutdown error)|"), wsz_error, wxICON_ERROR);
DestroyChildren();
Destroy(); //Default handler will destroy the window. This is our handler for the user calling close, replacing the default handler.
}
void Frame::OnAbout(wxCommandEvent& event)
{
wxMessageBox("This is a wxWidgets' Config sample",
"About sample code", wxOK | wxICON_INFORMATION);
wxMessageBox(szAboutWallet,
szAboutTitle, wxOK | wxICON_INFORMATION);
}
void Frame::OnDeleteConfiguration(wxCommandEvent&)
@ -213,45 +222,6 @@ void Frame::OnDeleteConfiguration(wxCommandEvent&)
}
}
void Frame::OnAddSubwindow(wxCommandEvent& WXUNUSED(event)) {
wxBoxSizer* sizer(static_cast<wxBoxSizer*>(m_panel->GetSizer()));
sizer->Prepend(new wxStaticText(m_panel, myID_TESTWINDOW, _T("test")));
sizer->Layout();
}
void Frame::OnDeleteSubwindow(wxCommandEvent& WXUNUSED(event)) {
auto sizer(m_panel->GetSizer());
int item_number(0);
if (sizer->GetItemCount()) {
wxSizerItem* t1 = sizer->GetItem(item_number);
if (t1) {
auto t2 = t1->GetWindow();
if (t2) {
sizer->Detach(t2);
t2->Destroy();
sizer->Layout();
}
}
}
}
void Frame::OnDeleteLastSubwindow(wxCommandEvent& WXUNUSED(event)) {
auto sizer(m_panel->GetSizer());
int item_number(sizer->GetItemCount()-1);
if (item_number >= 0) {
wxSizerItem* t1 = sizer->GetItem(item_number);
if (t1) {
auto t2 = t1->GetWindow();
if (t2) {
sizer->Detach(t2);
t2->Destroy();
sizer->Layout();
}
}
}
}
using ro::bin2hex, ro::to_base64_string;
void Frame::NewWalletNewSecret(wxCommandEvent&) {
@ -439,4 +409,4 @@ Frame::~Frame(){
if (pConfig == nullptr)return;
StorePositionToConfig();
pConfig->Write(_T("/Wallet/LastUsed"), m_LastUsedSqlite.GetFullPath());
}
}

63
frame.h
View File

@ -1,37 +1,47 @@
#pragma once
template <typename handler_class>
template <typename T>
// 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.
// (Because the handler is a method of an object that is about to be destroyed.)
// Also avoids the need for manually creating a new windowid to link each additional bind
// to each menu item, thus avoids the likelihood of mismatching binds and menu entries.
class MenuLink {
void (handler_class::* m_method)(wxCommandEvent&);
typedef void (T::* method_handler)(wxCommandEvent&);
// (thing.*p)(args) is how you use a pointer to method
T* m_handler = nullptr;
method_handler m_method;
int m_winid;
handler_class* m_handler = nullptr;
wxMenu* m_menu =nullptr;
wxMenuItem* m_menuItem = nullptr;;
wxMenu* m_menu = nullptr;
wxMenuItem* m_menuItem = nullptr;
public:
MenuLink(
void (handler_class::* method)(wxCommandEvent&)) : m_method{ method }, m_winid{ wxWindow::NewControlId()} {}
MenuLink(
void (handler_class::* method)(wxCommandEvent&), MyIDs winid) : m_method{ method }, m_winid{ winid } {}
MenuLink() = delete;
MenuLink(const MenuLink&) = default;
MenuLink(const MenuLink&&) = delete;
MenuLink& operator=(const MenuLink&) = delete;
MenuLink& operator=(MenuLink&) = delete;
MenuLink(T* handler, method_handler method) :
m_handler(handler), m_method{ method }, m_winid{ wxWindow::NewControlId() }{}
MenuLink(T* handler, method_handler method, MyIDs winid) :
m_handler(handler), m_method{ method }, m_winid{ winid } {}
~MenuLink() {
if (m_menu != nullptr) {
m_menu->Unbind(wxEVT_MENU, m_method, m_handler, m_winid);
m_menu->Destroy(m_menuItem);
}
}
auto Append(
wxMenuItem* Insert(
wxMenu* menu,
handler_class* handler,
const wxString& item = wxEmptyString,
const wxString& helpString = wxEmptyString
size_t pos,
const wxString& item,
const wxString& help = wxEmptyString
) {
m_menu = menu;
m_handler = handler;
m_menuItem = menu->Append(m_winid, item, helpString);
menu->Bind(wxEVT_MENU, m_method, handler, m_winid);
if (m_menu == nullptr) m_menu = menu;
else {
assert(false);
throw MyException("Reinsertion of menu item");
}
m_menuItem = menu->Insert(pos, m_winid, item, help);
menu->Bind(wxEVT_MENU, m_method, m_handler, m_winid);
return m_menuItem;
}
void queue_event() {
@ -39,7 +49,10 @@ public:
auto event = new wxCommandEvent(wxEVT_MENU, m_winid);
wxQueueEvent(m_menu, event);
}
else throw MyException("Event sent to uninitialized Menu item");
else {
assert(false);
throw MyException("Event sent to uninitialized Menu item");
}
}
};
@ -51,28 +64,26 @@ public:
Frame(wxString);
~Frame();
wxFileName m_LastUsedSqlite;
wxPanel* m_panel{nullptr}; //The once current child panel.
private:
typedef MenuLink<Frame> MenuLink;
wxPanel* m_panel; //The once current child panel.
void StorePositionToConfig(void);
void RestorePositionFromConfig(const wxSize&);
void OnExit(wxCommandEvent&);
void OnClose(wxCloseEvent&);
void OnAbout(wxCommandEvent&);
void OnDeleteConfiguration(wxCommandEvent&);
void OnMyCloseMpanel(wxCommandEvent&);
public:
void OnSaveNew(wxCommandEvent&);
void NewWalletNewSecret(wxCommandEvent&);
void RecreateWalletFromExistingSecret(wxCommandEvent&);
void OnFileOpen(wxCommandEvent&);
private:
void OnDelete(wxCommandEvent&);
void OnMenuOpen(wxMenuEvent&);
void OnDeleteSubwindow(wxCommandEvent&);
MenuLink menu_OnDeleteSubwindow =
MenuLink(&Frame::OnDeleteSubwindow);
void OnDeleteLastSubwindow(wxCommandEvent&);
void OnAddSubwindow(wxCommandEvent&);
public:
void OnFirstUse(wxCommandEvent&);
@ -80,4 +91,6 @@ public:
// MenuLink menu_OnFirstUse =
// MenuLink(&Frame::OnFirstUse, myID_WELCOME_TO_ROCOIN);
};
inline Frame* singletonFrame(nullptr);
extern Frame* singletonFrame;

View File

@ -105,6 +105,10 @@ const char sz_existing_wallet[] {R"|(existing wallet)|"};
const char sz_text_buffer_overflow[]{R"|(conversion to base64 would overflow text buffer)|"};
const char sz_unknown_error[]{ R"|(unknown error.)|"};
const char szAboutWallet[]{ R"|(A completely useless wallet
that is not even in a fit state for anyone else to work on)|" };
const char szAboutTitle[]{ R"|(About Wallet)|" };
//Error message strings
const wchar_t wsz_error[] { L"Error" };
const wchar_t wsz_operation[] { L"Operation aborted.\n" };

View File

@ -43,7 +43,10 @@ extern const char sz_existing_secret[];
extern const char sz_open_existing_wallet[];
extern const char sz_existing_wallet[];
extern const char sz_text_buffer_overflow[];
extern const char sz_unknown_error[];
extern const char sz_unknown_error[];
extern const char szAboutWallet[];
extern const char szAboutTitle[];
//Error message strings
extern const wchar_t wsz_error[];

View File

@ -27,19 +27,6 @@ namespace ristretto255 {
bool scalar::constant_time_required{ true };
bool point::constant_time_required{ true };
/* constexpr scalar::scalar(intmax_t i) {
if (i >= 0) {
auto k{ intmax_t(i) };
for (auto& j : blob) { j = k; k = k >> 8; }
}
else {
std::array<uint8_t, crypto_core_ristretto255_BYTES> absdata;
auto k{ uintmax_t(-i) };
for (auto& j : absdata) { j = k; k = k >> 8; }
crypto_core_ristretto255_scalar_negate(&blob[0], &absdata[0]);
}
}
*/
point point::operator*(const scalar &sclr) const& noexcept {
point me;
auto i{ crypto_scalarmult_ristretto255(&me.blob[0], &sclr.blob[0], &blob[0]) };

View File

@ -94,12 +94,13 @@ inline wxString _wx(const char* sz) { return wxString::FromUTF8Unchecked(sz); }
namespace testbed { extern void testbed(); }
enum MyIDs {
myID_DELETECONFIG = wxID_HIGHEST + 1, myID_ERRORMESSAGE, myID_Hello,
myID_DELETE_LAST_SUBWINDOW, myID_ADD_SUBWINDOW, myID_MAINFRAME,
myID_MAINFRAME_PANEL, myID_TESTWINDOW, myID_WELCOME_TO_ROCOIN, myID_WALLET_UI
myID_MAINFRAME,
myID_MAINFRAME_PANEL, myID_TESTWINDOW, myID_WELCOME_TO_ROCOIN, myID_WALLET_UI,
mID_CLOSE_WALLET, myID_MYEXIT
};
#include "localization.h"
#include "db_accessors.h"
#include "app.h"
#include "frame.h"
#include "welcome_to_rhocoin.h"
#include "display_wallet.h"
#include "frame.h"
#include "localization.h"

View File

@ -48,5 +48,6 @@ If using a dialog, exceptions within the dialog will result in an error message
*/
void testbed() {
// queue_error_message("hello world");
}
}

View File

@ -342,6 +342,7 @@ static bool TestErrorDialog(void) {
ILogMessage("\t\tposting delayed error message\n\t\tas if inside an exception handler or destructor");
auto event = new wxCommandEvent(wxEVT_MENU, myID_ERRORMESSAGE);
wxQueueEvent(singletonFrame->GetMenuBar(), event);
// wxQueueEvent(singletonApp, event);
unit_test_action = &Waiting_for_dummy_error_message;
return true;
}

View File

@ -135,6 +135,7 @@ welcome_to_rhocoin::welcome_to_rhocoin(
sizer->Add(sizerRow, 40, wxEXPAND | wxALL, 2);
btn_default->SetDefault();
SetSizer(sizer);
Bind(wxEVT_CLOSE_WINDOW, &welcome_to_rhocoin::OnClose, this);
assert(singletonWelcome == nullptr);
singletonWelcome = this;
}
@ -143,3 +144,11 @@ welcome_to_rhocoin::~welcome_to_rhocoin() {
assert(singletonWelcome ==this);
singletonWelcome = nullptr;
}
void welcome_to_rhocoin::OnClose(wxCloseEvent& event) {
// This event gives you the opportunity to clean up anything that needs explicit cleanup, albeit if you have done your work right nothing should need explicit cleanup,
// and to object to the closing in a "file not saved" type situation.
// https://docs.wxwidgets.org/trunk/classwx_close_event.html
DestroyChildren();
Destroy(); //Default handler will destroy the window. This is our handler for the user calling close, replacing the default handler.
}

View File

@ -9,6 +9,8 @@ public:
~welcome_to_rhocoin();
wxButton* AddButton(wxString, void(Frame::*)(wxCommandEvent&));
wxButton* AddButton(const char sz[], void(Frame::*)(wxCommandEvent&));
private:
void OnClose(wxCloseEvent& event);
};
extern welcome_to_rhocoin* singletonWelcome;