Merge remote-tracking branch 'origin/master' into docs

This commit is contained in:
reaction.la 2023-11-25 08:24:51 +10:00
commit 0a0255a9f7
No known key found for this signature in database
GPG Key ID: 99914792148C8388
9 changed files with 242 additions and 162 deletions

View File

@ -150,6 +150,7 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="../src/app.cpp" /> <ClCompile Include="../src/app.cpp" />
<ClCompile Include="../src/display_wallet.cpp" /> <ClCompile Include="../src/display_wallet.cpp" />
<ClCompile Include="../src/db_accessors.cpp" />
<ClCompile Include="../src/frame.cpp" /> <ClCompile Include="../src/frame.cpp" />
<ClCompile Include="../src/ILog.cpp" /> <ClCompile Include="../src/ILog.cpp" />
<ClCompile Include="../src/ISqlit3Impl.cpp"> <ClCompile Include="../src/ISqlit3Impl.cpp">

View File

@ -1,13 +1,8 @@
#pragma once #pragma once
#include "ILog.h" #include "ILog.h"
// this is pure virtual interface base class between sqlite3, which speaks only C and utf8 char[] // this is an interface base class between sqlite3, which speaks only C and utf8 char[]
// and wxWidgets which speaks only C++ and unicode strings. // and wxWidgets which speaks only C++ and unicode strings.
// Usage: Call the factory function std::shared_ptr<ISqlite3> sqlite3_open(const char *) to get a shared
// pointer to the // Sqlite3 database object. Then call the factory function
// sqlite3_prepare(std::shared_ptr<ISqlite3>, const char *) to get a unique pointer to
// a compiled SQL statement
// Its primary purpose is to avoid code that needs both the wxWidgets header files, // Its primary purpose is to avoid code that needs both the wxWidgets header files,
// and the sqlite3.h header file. // and the sqlite3.h header file.
// //
@ -25,8 +20,6 @@
// substantially more difficult in C++14, because one is effectively rolling one's own // substantially more difficult in C++14, because one is effectively rolling one's own
// unique pointer. // unique pointer.
// //
// It is therefore easier to implement a pure virtual base class with a virtual destructor and
// factory function that returns a smart pointer to a member of the derived implementation
// //
/* This code is at a low level abstraction, because it provides low level C++ interface to inherently low level C /* This code is at a low level abstraction, because it provides low level C++ interface to inherently low level C
It is intended to be wrapped in higher level code that does not know about the nuts and bolts of sqlite3, but which supports throwing, templated functions, and all that.*/ It is intended to be wrapped in higher level code that does not know about the nuts and bolts of sqlite3, but which supports throwing, templated functions, and all that.*/

45
src/db_accessors.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "stdafx.h"
namespace ro {
static constexpr char SrcFilename[]{ "src/db_accessors.cpp" };
dbconnect::dbconnect(const wxFileName& filename) {
if (!filename.IsOk() || !filename.HasName() || !filename.HasExt()) throw
MyException("unexpected file name", __LINE__, __func__, SrcFilename);
if (!filename.FileExists())throw MyException(
std::string(filename.GetFullPath().ToUTF8()) + " does not exist.",
__LINE__, __func__, SrcFilename);
this->reset(Sqlite3_open(filename.GetFullPath().ToUTF8()));
}
// move constructor
dbconnect::dbconnect(dbconnect&& p) noexcept :std::unique_ptr<ISqlite3>(p.release()) {
}
// Move assignment
dbconnect& dbconnect::operator=(dbconnect&& p) noexcept {
std::unique_ptr<ISqlite3>::reset(p.release());
return *this;
}
sql::sql(const std::unique_ptr<ISqlite3>& p, const char* sz) :
std::unique_ptr<Icompiled_sql>(sqlite3_prepare(p.get(), sz)) {
}
// move constructor
sql::sql(sql&& p) noexcept :std::unique_ptr<Icompiled_sql>(p.release()) {
}
// Move assignment
sql& sql::operator=(sql&& p) noexcept {
std::unique_ptr<Icompiled_sql>::reset(p.release());
return *this;
}
sql::sql(Icompiled_sql* p) :
std::unique_ptr<Icompiled_sql>(p) {
}
sql::sql(std::unique_ptr<Icompiled_sql>&& p) :
std::unique_ptr<Icompiled_sql>(p.release()) {
}
}

View File

@ -15,26 +15,42 @@ namespace ro {
static_assert(is_sqlite3_field_type<int>::value); static_assert(is_sqlite3_field_type<int>::value);
//Owns a database connection and closes it when it is deconstructed
//Has move semantics
struct dbconnect : std::unique_ptr<ISqlite3> {
dbconnect(const wxFileName&); //And here we wrap our Cish implementation in C++ish implementation
dbconnect() = delete;
~dbconnect() = default; //Our base class takes care of it
// copy constructor
dbconnect(const dbconnect& a) = delete; //Move semantics
// move constructor
dbconnect(dbconnect&& p) noexcept;
// copy assignment
dbconnect& operator=(const dbconnect) = delete; //Move semantics
// Move assignment
dbconnect& operator=(dbconnect&& p) noexcept;
};
//Owns a compiled sql statement and destroys it when it is deconstructed. //Owns a compiled sql statement and destroys it when it is deconstructed.
//Has move semantics. //Has move semantics.
class sql : public std::unique_ptr<Icompiled_sql> { class sql : public std::unique_ptr<Icompiled_sql> {
public: public:
class monostate {}; class monostate {};
sql() = delete;
sql(ISqlite3* p, const char* sz) :std::unique_ptr<Icompiled_sql>(sqlite3_prepare(p, sz)) {} sql(ISqlite3* p, const char* sz) :std::unique_ptr<Icompiled_sql>(sqlite3_prepare(p, sz)) {}
sql(const std::unique_ptr<ISqlite3>& p, const char* sz) :std::unique_ptr<Icompiled_sql>(sqlite3_prepare(p.get(), sz)) {} sql(const std::unique_ptr<ISqlite3>& p, const char* sz);
// copy constructor // copy constructor
sql(const sql& a) = delete; sql(const sql& a) = delete;
// move constructor // move constructor
sql(sql&& p) noexcept :std::unique_ptr<Icompiled_sql>(p.release()) { } sql(sql&& p) noexcept;
// copy assignment // copy assignment
sql& operator=(const sql) = delete; sql& operator=(const sql) = delete;
// Move assignment // Move assignment
sql& operator=(sql&& p) noexcept { sql& operator=(sql&& p) noexcept;
std::unique_ptr<Icompiled_sql>::reset(p.release()); sql(Icompiled_sql* p);
} sql(std::unique_ptr<Icompiled_sql>&& p);
sql(Icompiled_sql* p) :std::unique_ptr<Icompiled_sql>(p) {}
sql(std::unique_ptr<Icompiled_sql>&& p) :std::unique_ptr<Icompiled_sql>(p.release()) { }
~sql() = default; ~sql() = default;
template <typename T>auto column(int i) const { template <typename T>auto column(int i) const {
if constexpr (ro::blob_type<T>) { if constexpr (ro::blob_type<T>) {
auto st = (*this)->Isqlite3_column_blob(i); auto st = (*this)->Isqlite3_column_blob(i);
@ -180,45 +196,13 @@ public:
}; };
class sql_insert_name { class sql_insert_name {
ro::sql csql_begin;
ro::sql csql_into_names; ro::sql csql_into_names;
ro::sql csql_namekey_into_keys;
ro::sql csql_commit;
ro::sql csql_rollback;
public: public:
sql_insert_name(ISqlite3* p) : sql_insert_name(ISqlite3* p) :
csql_begin(p, R"|(BEGIN IMMEDIATE;)|"), csql_into_names(p, R"|(INSERT OR FAIL INTO "UserZookoIDs" VALUES(?1, ?2);)|"){}
csql_into_names(p, R"|(INSERT OR FAIL INTO "Names" VALUES(NULL, ?1);)|"),
csql_namekey_into_keys(p, R"|(INSERT OR FAIL INTO "Keys" VALUES(NULL, ?1, last_insert_rowid(), 1);)|"),
/* NULL triggers special nonstandard Sqlite behavior to autogenerate a unique ROWID
Because we explicitly make the ROWID a named field in the table to avoid too much
non standard SQlite behavior, we must explicitly provide a placeholder in the INSERT
statement. So at least we only get special SQlite3 behavior when we deliberately
invoke it. */
csql_commit(p, R"|(COMMIT;)|"),
csql_rollback(p, R"|(ROLLBACK;)|")
{}
sql_insert_name(const std::unique_ptr<ISqlite3>& p) : sql_insert_name(p.get()) {} sql_insert_name(const std::unique_ptr<ISqlite3>& p) : sql_insert_name(p.get()) {}
void operator()(const char* psz, const ristretto255::point& pt) { void operator()(const char* psz, const ristretto255::point& pt) {
csql_begin.do_one(); csql_into_names.do_one(psz,pt);
try {
csql_into_names.do_one(psz);
csql_namekey_into_keys.do_one(pt);
}
catch (const MyException& e) {
csql_rollback.do_one();
if (e.what_num() == 19) {
throw MyException("Name already in database");
}
else {
throw;
}
}
catch (const std::exception &) {
csql_rollback.do_one();
throw;
}
csql_commit.do_one();
} }
}; };
@ -234,7 +218,5 @@ public:
} }
}; };
constexpr auto WALLET_FILE_IDENTIFIER (0x56d34bc5a655dd1fi64); constexpr auto WALLET_FILE_IDENTIFIER (0x56d34bc5a655dd1fi64);
constexpr auto WALLET_FILE_SCHEMA_VERSION_0_0(1); constexpr auto WALLET_FILE_SCHEMA_VERSION_0_0(1);

View File

@ -1,75 +1,32 @@
#include "stdafx.h" #include "stdafx.h"
using ro::base58; using ro::base58;
static constexpr char SrcFilename[]{ "src/display_wallet.cpp" }; static constexpr char SrcFilename[]{ "src/display_wallet.cpp" };
display_wallet::display_wallet(wxWindow* parent, wxFileName& walletfile) : display_wallet::display_wallet(wxWindow* parent, wxFileName& walletfile) :
wxPanel(parent, myID_WALLET_UI, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, wxT("Wallet")), wxPanel(parent, myID_WALLET_UI, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, wxT("Wallet")),
m_db(nullptr), m_DisplayWalletEditMenu(1) m_db(walletfile), m_DisplayWalletEditMenu(1),
m_read_names_and_keys(m_db, R"|(SELECT * FROM "UserZookoIDs"; )|"),
m_read_from_misc(m_db),
m_insert_name(m_db),
m_find_position(m_db, R"|( SELECT COUNT(*) FROM UserZookoIDs
WHERE LOWER("name")<LOWER(?1) OR (LOWER("name")=LOWER(?1) AND "name"<?1);)|")
{ {
wxLogMessage(wxT("Loading %s"), walletfile.GetFullPath()); wxLogMessage(wxT("Loading %s"), walletfile.GetFullPath());
try { try {
if (!walletfile.IsOk() || !walletfile.HasName() || !walletfile.HasExt()) throw MyException("unexpected file name", __LINE__, __func__, SrcFilename); if (!m_read_from_misc(1) || m_read_from_misc.value<int64_t>() != WALLET_FILE_IDENTIFIER)throw MyException(sz_unrecognizable_wallet_file_format);
if (!walletfile.FileExists())throw MyException( if (!m_read_from_misc(2) || m_read_from_misc.value<int64_t>() != WALLET_FILE_SCHEMA_VERSION_0_0 || !m_read_from_misc(4))throw MyException(sz_unrecognized_wallet_schema);
walletfile.GetFullPath().append(" does not exist.").ToUTF8(), m_MasterSecret= *(m_read_from_misc.value<ristretto255::scalar>());
__LINE__, __func__, SrcFilename);
m_db.reset(Sqlite3_open(walletfile.GetFullPath().ToUTF8()));
m_read_from_misc.reset(new sql_read_from_misc(m_db));
m_read_names_and_keys.reset(new ro::sql(m_db,
R"|(SELECT "Names".name AS name, "Keys".pubkey AS pubkey FROM "Names" INNER JOIN "Keys" )|"
R"|(ON "Names"."ROWID"="Keys".id AND "Keys".use=1 ORDER BY LOWER(name), name COLLATE BINARY;)|"));
if (!(*m_read_from_misc)(1) || m_read_from_misc->value<int64_t>() != WALLET_FILE_IDENTIFIER)throw MyException(sz_unrecognizable_wallet_file_format);
if (!(*m_read_from_misc)(2) || m_read_from_misc->value<int64_t>() != WALLET_FILE_SCHEMA_VERSION_0_0 || !(*m_read_from_misc)(4))throw MyException(sz_unrecognized_wallet_schema);
m_MasterSecret= *(m_read_from_misc->value<ristretto255::scalar>());
ILogMessage(std::format("\t\tmaster secret: #{}", ro::base58(m_MasterSecret).operator const char* ()).c_str()); ILogMessage(std::format("\t\tmaster secret: #{}", ro::base58(m_MasterSecret).operator const char* ()).c_str());
if(!m_MasterSecret.valid()) throw MyException(sz_cold_wallets_not_yet_implemented, __LINE__, __func__, SrcFilename); if(!m_MasterSecret.valid()) throw MyException(sz_cold_wallets_not_yet_implemented, __LINE__, __func__, SrcFilename);
auto sizer = new wxBoxSizer(wxHORIZONTAL); auto sizer = new wxBoxSizer(wxHORIZONTAL);
m_lSizer = new wxBoxSizer(wxVERTICAL); m_lSizer = new wxBoxSizer(wxVERTICAL);
m_rSizer = new wxBoxSizer(wxVERTICAL); m_rSizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(m_lSizer, 0, wxGROW, 4); sizer->Add(m_lSizer, 0, 0, 4);
sizer->Add(m_rSizer, 50, wxGROW, 4); sizer->Add(m_rSizer, 50, 0, 4);
SetSizer(sizer); SetSizer(sizer);
try { refresh_from_database();
while (m_read_names_and_keys->step() == Icompiled_sql::ROW) {
std::string name = m_read_names_and_keys->column<const char*>(0);
auto pubkey = *(m_read_names_and_keys->column<ristretto255::point>(1));
if (m_MasterSecret(name).timesBase() != pubkey)throw MyException(std::string(sz_public_key_of) + name + sz_fails_to_correspond);
m_lSizer->Add(
new wxStaticText(
this,
wxID_ANY,
name,
wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT | wxST_ELLIPSIZE_END
),
10,
wxEXPAND | // make horizontally stretchable
wxALL, // and make border all around
2);
m_rSizer->Add(
new wxStaticText(
this,
wxID_ANY,
"#" + base58(pubkey).operator std::string(),
wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT | wxST_ELLIPSIZE_END
),
10,
wxEXPAND | // make horizontally stretchable
wxALL, // and make border all around
2);
}
}
catch (const MyException& e) {
throw MyException(e, __LINE__, __func__, SrcFilename);
// sql exceptions tend to be unintelligible and excessively generic
// because unclear what statement provoked them.
}
catch (const std::exception& e) {
throw MyException(e, __LINE__, __func__, SrcFilename);
}
catch (...) {
szError = sz_unknown_error;
throw MyException(sz_unknown_error, __LINE__, __func__, SrcFilename);
}
Bind(wxEVT_CLOSE_WINDOW, &display_wallet::OnClose, this);
this->SetSize(this->GetParent()->GetClientSize()); this->SetSize(this->GetParent()->GetClientSize());
Bind(wxEVT_CLOSE_WINDOW, &display_wallet::OnClose, this);
singletonFrame->m_LastUsedWallet.Assign(walletfile); singletonFrame->m_LastUsedWallet.Assign(walletfile);
m_DisplayWalletEditMenu.Menu->Append( m_DisplayWalletEditMenu.Menu->Append(
@ -93,8 +50,6 @@ display_wallet::display_wallet(wxWindow* parent, wxFileName& walletfile) :
szError = sz_unknown_error; szError = sz_unknown_error;
throw MyException(sz_unknown_error, __LINE__, __func__, SrcFilename); throw MyException(sz_unknown_error, __LINE__, __func__, SrcFilename);
} }
} }
display_wallet::~display_wallet() { display_wallet::~display_wallet() {
@ -104,6 +59,39 @@ void display_wallet::close_menu_event_handler(wxCommandEvent& event) {
Close(true); Close(true);
} }
struct display_wallet::cleanup {
cleanup() = delete;
// copy constructor
cleanup(const cleanup& a) = delete;
// move constructor
cleanup(cleanup&& p) = delete;
// copy assignment
cleanup& operator=(cleanup) = delete;
// Move assignment
cleanup& operator=(cleanup&&) = delete;
display_wallet* m_win;
wxSizer* sizer;
wxSize dirty_area;
cleanup(display_wallet* win) :
m_win(win),
sizer(win->GetSizer()),
dirty_area(sizer->ComputeFittingClientSize(singletonFrame))
{
assert(sizer);
}
~cleanup() {
auto desired_size = sizer->ComputeFittingClientSize(singletonFrame);
dirty_area.IncTo(desired_size);
//singletonFrame->SetMinClientSize(desired_size);
auto clientSize = singletonFrame->GetClientSize();
desired_size.IncTo(clientSize);
if (desired_size.GetHeight() > clientSize.GetHeight()
|| desired_size.GetWidth() > clientSize.GetWidth()
)singletonFrame->SetClientSize(desired_size);
}
};
void display_wallet::add_name_event_handler(wxCommandEvent& event) { void display_wallet::add_name_event_handler(wxCommandEvent& event) {
wxTextEntryDialog dialog(this, wxTextEntryDialog dialog(this,
R"("A Zooko name has a human readable name, R"("A Zooko name has a human readable name,
@ -114,13 +102,37 @@ and a public key defined by the name and the wallet master secret)",
if (dialog.ShowModal() == wxID_OK) if (dialog.ShowModal() == wxID_OK)
{ {
std::string zookoNickname(dialog.GetValue().ToUTF8()); std::string zookoNickname(dialog.GetValue().ToUTF8());
sql_insert_name insert_name(m_db);
auto zookoNickname_psz = zookoNickname.c_str(); auto zookoNickname_psz = zookoNickname.c_str();
insert_name( auto pubkey = m_MasterSecret(zookoNickname_psz).timesBase();
m_insert_name(
zookoNickname_psz, zookoNickname_psz,
m_MasterSecret(zookoNickname_psz).timesBase() pubkey
); );
m_find_position.read_one(zookoNickname_psz);
auto position = m_find_position.column<int64_t>(0);
cleanup cln(this);
m_lSizer->Insert(position,
new wxStaticText(
this,
wxID_ANY,
zookoNickname_psz,
wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT | wxEXPAND | wxFIXED_MINSIZE | wxST_ELLIPSIZE_END
),
10,
wxALL, // and make border all around
2);
m_rSizer->Insert(position,
new wxStaticText(
this,
wxID_ANY,
"#" + base58(pubkey).operator std::string(),
wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT | wxEXPAND | wxFIXED_MINSIZE | wxST_ELLIPSIZE_END
),
10,
wxALL, // and make border all around
2);
} }
} }
@ -134,3 +146,47 @@ void display_wallet::OnClose(wxCloseEvent& event) {
if (singletonFrame->m_panel ==this)singletonFrame->m_panel = nullptr; if (singletonFrame->m_panel ==this)singletonFrame->m_panel = nullptr;
} }
void display_wallet::refresh_from_database() {
cleanup cln(this);
try {
m_read_names_and_keys.reset();
while (m_read_names_and_keys.step() == Icompiled_sql::ROW) {
std::string name = m_read_names_and_keys.column<const char*>(0);
auto pubkey = *(m_read_names_and_keys.column<ristretto255::point>(1));
if (m_MasterSecret(name).timesBase() != pubkey)throw MyException(std::string(sz_public_key_of) + name + sz_fails_to_correspond);
m_lSizer->Add(
new wxStaticText(
this,
wxID_ANY,
name,
wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT | wxEXPAND| wxFIXED_MINSIZE| wxST_ELLIPSIZE_END
),
10,
wxALL, // and make border all around
2);
m_rSizer->Add(
new wxStaticText(
this,
wxID_ANY,
"#" + base58(pubkey).operator std::string(),
wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT | wxEXPAND | wxFIXED_MINSIZE | wxST_ELLIPSIZE_END
),
10,
wxALL, // and make border all around
2);
}
}
catch (const MyException& e) {
throw MyException(e, __LINE__, __func__, SrcFilename);
// sql exceptions tend to be unintelligible and excessively generic
// because unclear what statement provoked them.
}
catch (const std::exception& e) {
throw MyException(e, __LINE__, __func__, SrcFilename);
}
catch (...) {
szError = sz_unknown_error;
throw MyException(sz_unknown_error, __LINE__, __func__, SrcFilename);
}
}

View File

@ -5,10 +5,14 @@ public:
display_wallet(wxWindow*, wxFileName&); display_wallet(wxWindow*, wxFileName&);
~display_wallet(); ~display_wallet();
private: private:
struct cleanup;
typedef MenuLink<display_wallet> MenuLink; typedef MenuLink<display_wallet> MenuLink;
std::unique_ptr<ISqlite3> m_db; ro::dbconnect m_db;
std::unique_ptr <sql_read_from_misc> m_read_from_misc; sql_read_from_misc m_read_from_misc;
std::unique_ptr <ro::sql> m_read_names_and_keys; ro::sql m_read_names_and_keys;
sql_insert_name m_insert_name;
ro::sql m_find_position;
ristretto255::CMasterSecret m_MasterSecret; ristretto255::CMasterSecret m_MasterSecret;
wxBoxSizer* m_lSizer; wxBoxSizer* m_lSizer;
wxBoxSizer* m_rSizer; wxBoxSizer* m_rSizer;
@ -16,4 +20,5 @@ private:
void add_name_event_handler(wxCommandEvent&); void add_name_event_handler(wxCommandEvent&);
void OnClose(wxCloseEvent& event); void OnClose(wxCloseEvent& event);
wxMenuTracker m_DisplayWalletEditMenu; wxMenuTracker m_DisplayWalletEditMenu;
void refresh_from_database();
}; };

View File

@ -10,27 +10,50 @@ wxMenuTracker::wxMenuTracker(const int i) : Menu(new wxMenu), MenuPosition(i) {}
wxMenu* wxMenuTracker::InitialAndFinal[]{ nullptr, new wxMenu, nullptr }; wxMenu* wxMenuTracker::InitialAndFinal[]{ nullptr, new wxMenu, nullptr };
void wxMenuTracker::Replace() { void wxMenuTracker::Replace() {
singletonFrame->GetMenuBar()->Replace( auto menuBar = singletonFrame->GetMenuBar();
MenuPosition, if ((menuBar->GetMenu(MenuPosition) != Menu)) {
Menu, menuBar->Replace(
menu_strings[MenuPosition].head MenuPosition,
); Menu,
singletonFrame->GetMenuBar()->EnableTop(MenuPosition, true); //enable edit menu. menu_strings[MenuPosition].head
);
menuBar->EnableTop(MenuPosition, true); //enable edit menu.
}
}; };
wxMenuTracker::~wxMenuTracker() { wxMenuTracker::~wxMenuTracker() {
auto menu_bar = singletonFrame->GetMenuBar(); auto menuBar = singletonFrame->GetMenuBar();
if (menu_bar->GetMenu(MenuPosition) == Menu) { if (menuBar->GetMenu(MenuPosition) == Menu) {
assert(InitialAndFinal[MenuPosition]); assert(InitialAndFinal[MenuPosition]);
menu_bar->Replace( menuBar->Replace(
MenuPosition, MenuPosition,
InitialAndFinal[MenuPosition], InitialAndFinal[MenuPosition],
menu_strings[MenuPosition].head menu_strings[MenuPosition].head
); );
menu_bar->EnableTop(MenuPosition, false); menuBar->EnableTop(MenuPosition, false);
delete Menu; delete Menu;
} }
}; };
void wxMenuTracker::check_dynamic_menus_absent() {
if constexpr (debug_mode) {
// Check that the values of all replaceable menus
// are at their default values as if the window handling them
// had just been destroyed and not yet replaced.
auto menuBar = singletonFrame->GetMenuBar();
for (int i = 0; i < std::size(InitialAndFinal); i++) {
assert(
!InitialAndFinal[i]
||
(
InitialAndFinal[i] == menuBar->GetMenu(i)
&&
!menuBar->IsEnabledTop(i)
)
);
}
}
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// frame // frame
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -199,21 +222,10 @@ Frame::Frame(const wxString& wxs)
SetMenuBar(menuBar); SetMenuBar(menuBar);
menuBar->EnableTop(1, false); //disable edit menu. menuBar->EnableTop(1, false); //disable edit menu.
if constexpr (debug_mode) { if constexpr (debug_mode) {
// Check that the initial values of all replaceable menus wxMenuTracker::check_dynamic_menus_absent();
// are at their default values as if the window handling them // Because the initial values of the dynamic menus are not being deleted by the child windows
// had just been destroyed and not yet replaced. // that own the modified windows, they need to be the same as the final values,
for (int i = 0; i < std::size(wxMenuTracker::InitialAndFinal); i++) { // which is to say, the values in wxMenuTracker::InitialAndFinal[]
assert(
!wxMenuTracker::InitialAndFinal[i]
||
(
wxMenuTracker::InitialAndFinal[i] == menuBar->GetMenu(i)
&&
!menuBar->IsEnabledTop(i)
)
);
}
} }
CreateStatusBar(); CreateStatusBar();
// child controls // child controls
@ -374,17 +386,16 @@ SELECT
"Keys".pubkey AS pubkey "Keys".pubkey AS pubkey
FROM "Names" INNER JOIN "Keys" FROM "Names" INNER JOIN "Keys"
ON "Names"."ROWID"="Keys"."id" AND "Keys"."use"=1 ON "Names"."ROWID"="Keys"."id" AND "Keys"."use"=1
ORDER BY LOWER("name"), "name" ORDER BY LOWER("name"), "name" COLLATE BINARY;
COLLATE BINARY;
COMMIT; COMMIT;
BEGIN IMMEDIATE TRANSACTION; BEGIN IMMEDIATE TRANSACTION;
CREATE TRIGGER InsertUserZookoID INSTEAD OF INSERT ON UserZookoIDs FOR EACH ROW BEGIN CREATE TRIGGER InsertUserZookoID INSTEAD OF INSERT ON UserZookoIDs FOR EACH ROW BEGIN
INSERT OR FAIL INTO "Names" VALUES( INSERT OR ROLLBACK INTO "Names" VALUES(
NULL, NULL,
NEW."name" NEW."name"
); );
INSERT OR FAIL INTO "Keys" VALUES( INSERT OR ROLLBACK INTO "Keys" VALUES(
NULL, NULL,
NEW."pubkey", NEW."pubkey",
last_insert_rowid(), last_insert_rowid(),
@ -555,7 +566,6 @@ void Frame::OnMenuOpen(wxMenuEvent& evt) {
Frame::~Frame() { Frame::~Frame() {
assert(singletonFrame == this); assert(singletonFrame == this);
singletonFrame = nullptr;
wxConfigBase& Config = singletonApp->m_Config; wxConfigBase& Config = singletonApp->m_Config;
StorePositionToConfig(); StorePositionToConfig();
Config.SetPath(wxT("/TipOfTheDay")); Config.SetPath(wxT("/TipOfTheDay"));
@ -567,21 +577,7 @@ Frame::~Frame() {
Config.SetPath(wxT("/")); Config.SetPath(wxT("/"));
Config.Flush(); Config.Flush();
if constexpr (debug_mode) { if constexpr (debug_mode) {
// Check that the final values of all replaceable menus wxMenuTracker::check_dynamic_menus_absent();
// 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)
)
);
}
} }
singletonFrame = nullptr;
} }

View File

@ -21,6 +21,7 @@ struct wxMenuTracker {
// the frame initializer. In the debug build, congruence between the menu bar // the frame initializer. In the debug build, congruence between the menu bar
// and InitialAndFinal should be checked with assert. // and InitialAndFinal should be checked with assert.
static wxMenu* InitialAndFinal[3]; static wxMenu* InitialAndFinal[3];
static void check_dynamic_menus_absent();
wxMenuTracker(wxMenuTracker&&) = delete; // Move constructor wxMenuTracker(wxMenuTracker&&) = delete; // Move constructor
wxMenuTracker(const wxMenuTracker&) = delete; // Copy constructor wxMenuTracker(const wxMenuTracker&) = delete; // Copy constructor
wxMenuTracker& operator=(wxMenuTracker&&) = delete; // Move assignment. wxMenuTracker& operator=(wxMenuTracker&&) = delete; // Move assignment.

View File

@ -138,6 +138,7 @@ welcome_to_rhocoin::welcome_to_rhocoin(
Bind(wxEVT_CLOSE_WINDOW, &welcome_to_rhocoin::OnClose, this); Bind(wxEVT_CLOSE_WINDOW, &welcome_to_rhocoin::OnClose, this);
assert(singletonWelcome == nullptr); assert(singletonWelcome == nullptr);
singletonWelcome = this; singletonWelcome = this;
singletonFrame->SetMinClientSize(sizer->ComputeFittingClientSize(singletonFrame));
} }
welcome_to_rhocoin::~welcome_to_rhocoin() { welcome_to_rhocoin::~welcome_to_rhocoin() {