diff --git a/src/generate/gen_cpp.cpp b/src/generate/gen_cpp.cpp index e7684c815..b2ab8f3b1 100644 --- a/src/generate/gen_cpp.cpp +++ b/src/generate/gen_cpp.cpp @@ -61,6 +61,8 @@ struct GenData }; }; +static void GenCppForm(GenData& gen_data, Node* form); + // clang-format off inline constexpr const auto txt_wxueImageFunction = R"===( @@ -125,6 +127,76 @@ R"===(////////////////////////////////////////////////////////////////////////// // clang-format on +#if defined(_DEBUG) || defined(INTERNAL_TESTING) + +void MainFrame::OnGenSingleCpp(wxCommandEvent& WXUNUSED(event)) +{ + auto form = wxGetMainFrame()->getSelectedNode(); + if (form && !form->isForm()) + { + form = form->getForm(); + } + if (!form) + { + wxMessageBox("You must select a form before you can generate code.", "Code Generation"); + return; + } + + GenResults results; + GenData gen_data(results, nullptr); + + if (auto& extProp = Project.as_string(prop_source_ext); extProp.size()) + { + gen_data.source_ext = extProp; + } + else + { + gen_data.source_ext = ".cpp"; + } + + if (auto& extProp = Project.as_string(prop_header_ext); extProp.size()) + { + gen_data.header_ext = extProp; + } + else + { + gen_data.header_ext = ".h"; + } + + std::vector forms; + Project.CollectForms(forms); + Project.FindWxueFunctions(forms); + + GenCppForm(gen_data, form); + + tt_string msg; + if (results.updated_files.size()) + { + if (results.updated_files.size() == 1) + msg << "1 file was updated"; + else + msg << results.updated_files.size() << " files were updated"; + msg << '\n'; + } + else + { + msg << "All " << results.file_count << " generated files are current"; + } + + if (results.msgs.size()) + { + for (auto& iter: results.msgs) + { + msg << '\n'; + msg << iter; + } + } + + wxMessageBox(msg, "C++ Code Generation", wxOK | wxICON_INFORMATION); +} + +#endif + static void GenCppForm(GenData& gen_data, Node* form) { // These are just defined for convenience. diff --git a/src/generate/gen_python.cpp b/src/generate/gen_python.cpp index 83f31484c..eca841432 100644 --- a/src/generate/gen_python.cpp +++ b/src/generate/gen_python.cpp @@ -31,6 +31,8 @@ using namespace code; using namespace GenEnum; +static bool GeneratePythonForm(Node* form, GenResults& results, std::vector* pClassList = nullptr); + // clang-format off inline constexpr const auto txt_PyPerlRubyCmtBlock = @@ -54,6 +56,48 @@ int GenXrcObject(Node* node, pugi::xml_node& object, size_t xrc_flags); #if defined(_DEBUG) || defined(INTERNAL_TESTING) +void MainFrame::OnGenSinglePython(wxCommandEvent& WXUNUSED(event)) +{ + auto form = wxGetMainFrame()->getSelectedNode(); + if (form && !form->isForm()) + { + form = form->getForm(); + } + if (!form) + { + wxMessageBox("You must select a form before you can generate code.", "Code Generation"); + return; + } + + GenResults results; + GeneratePythonForm(form, results); + + tt_string msg; + if (results.updated_files.size()) + { + if (results.updated_files.size() == 1) + msg << "1 file was updated"; + else + msg << results.updated_files.size() << " files were updated"; + msg << '\n'; + } + else + { + msg << "Generated file is current"; + } + + if (results.msgs.size()) + { + for (auto& iter: results.msgs) + { + msg << '\n'; + msg << iter; + } + } + + wxMessageBox(msg, "Python Code Generation", wxOK | wxICON_INFORMATION); +} + void MainFrame::OnGeneratePython(wxCommandEvent& WXUNUSED(event)) { GenResults results; diff --git a/src/generate/gen_ruby.cpp b/src/generate/gen_ruby.cpp index 04cd00695..603c60e13 100644 --- a/src/generate/gen_ruby.cpp +++ b/src/generate/gen_ruby.cpp @@ -31,6 +31,8 @@ using namespace code; using namespace GenEnum; +static bool GenerateRubyForm(Node* form, GenResults& results, std::vector* pClassList = nullptr); + // clang-format off inline constexpr const auto txt_PyPerlRubyCmtBlock = @@ -100,6 +102,48 @@ static const std::vector disable_list = { #if defined(_DEBUG) || defined(INTERNAL_TESTING) +void MainFrame::OnGenSingleRuby(wxCommandEvent& WXUNUSED(event)) +{ + auto form = wxGetMainFrame()->getSelectedNode(); + if (form && !form->isForm()) + { + form = form->getForm(); + } + if (!form) + { + wxMessageBox("You must select a form before you can generate code.", "Code Generation"); + return; + } + + GenResults results; + GenerateRubyForm(form, results); + + tt_string msg; + if (results.updated_files.size()) + { + if (results.updated_files.size() == 1) + msg << "1 file was updated"; + else + msg << results.updated_files.size() << " files were updated"; + msg << '\n'; + } + else + { + msg << "Generated file is current"; + } + + if (results.msgs.size()) + { + for (auto& iter: results.msgs) + { + msg << '\n'; + msg << iter; + } + } + + wxMessageBox(msg, "Ruby Code Generation", wxOK | wxICON_INFORMATION); +} + void MainFrame::OnGenerateRuby(wxCommandEvent& WXUNUSED(event)) { GenResults results; @@ -417,7 +461,7 @@ void BaseCodeGenerator::GenerateRubyClass(PANEL_PAGE panel_type) { if (!images_file_imported) { - tt_string import_name = iter->form->as_string(prop_python_file).filename(); + tt_string import_name = iter->form->as_string(prop_ruby_file).filename(); import_name.remove_extension(); code.Str("require_relative '").Str(import_name) << "'"; m_source->writeLine(code); diff --git a/src/generate/gen_rust.cpp b/src/generate/gen_rust.cpp index 58623f600..984489067 100644 --- a/src/generate/gen_rust.cpp +++ b/src/generate/gen_rust.cpp @@ -1,7 +1,7 @@ ///////////////////////////////////////////////////////////////////////////// // Purpose: Generate Rust code files // Author: Ralph Walden -// Copyright: Copyright (c) 2023 KeyWorks Software (Ralph Walden) +// Copyright: Copyright (c) 2023-2024 KeyWorks Software (Ralph Walden) // License: Apache License -- see ../../LICENSE ///////////////////////////////////////////////////////////////////////////// @@ -29,6 +29,8 @@ using namespace code; using namespace GenEnum; +static bool GenerateRustForm(Node* form, GenResults& results, std::vector* pClassList = nullptr); + // clang-format off inline constexpr const auto txt_RustCmtBlock = @@ -45,6 +47,48 @@ R"===(////////////////////////////////////////////////////////////////////////// #if defined(_DEBUG) || defined(INTERNAL_TESTING) +void MainFrame::OnGenSingleRust(wxCommandEvent& WXUNUSED(event)) +{ + auto form = wxGetMainFrame()->getSelectedNode(); + if (form && !form->isForm()) + { + form = form->getForm(); + } + if (!form) + { + wxMessageBox("You must select a form before you can generate code.", "Code Generation"); + return; + } + + GenResults results; + GenerateRustForm(form, results); + + tt_string msg; + if (results.updated_files.size()) + { + if (results.updated_files.size() == 1) + msg << "1 file was updated"; + else + msg << results.updated_files.size() << " files were updated"; + msg << '\n'; + } + else + { + msg << "Generated file is current"; + } + + if (results.msgs.size()) + { + for (auto& iter: results.msgs) + { + msg << '\n'; + msg << iter; + } + } + + wxMessageBox(msg, "Rust Code Generation", wxOK | wxICON_INFORMATION); +} + void MainFrame::OnGenerateRust(wxCommandEvent& WXUNUSED(event)) { GenResults results; @@ -78,9 +122,107 @@ void MainFrame::OnGenerateRust(wxCommandEvent& WXUNUSED(event)) #endif -bool GenerateRustFiles(GenResults& /* results */, std::vector* /* pClassList */) +static bool GenerateRustForm(Node* form, GenResults& results, std::vector* pClassList) { - return false; + auto [path, has_base_file] = Project.GetOutputPath(form, GEN_LANG_RUST); + if (!has_base_file) + { +#if !defined(_DEBUG) + // For a lot of wxRuby testing of projects with multiple dialogs, there may + // only be a few forms where wxRuby generation is being tested, so don't nag in + // Debug builds. :-) + results.msgs.emplace_back() << "No Ruby filename specified for " << form->as_string(prop_class_name) << '\n'; +#endif // _DEBUG + return false; + } + + BaseCodeGenerator codegen(GEN_LANG_RUBY, form); + + auto h_cw = std::make_unique(path); + codegen.SetHdrWriteCode(h_cw.get()); + + path.replace_extension(".rst"); + auto cpp_cw = std::make_unique(path); + codegen.SetSrcWriteCode(cpp_cw.get()); + + codegen.GenerateRustClass(); + + int flags = flag_no_ui; + if (pClassList) + flags |= flag_test_only; + auto retval = cpp_cw->WriteFile(GEN_LANG_PYTHON, flags); + + if (auto warning_msgs = codegen.getWarnings(); warning_msgs.size()) + { + for (auto& iter: warning_msgs) + { + results.msgs.emplace_back() << iter << '\n'; + } + } + + if (retval > 0) + { + if (!pClassList) + { + results.updated_files.emplace_back(path); + } + else + { + if (form->isGen(gen_Images)) + pClassList->emplace_back(GenEnum::map_GenNames[gen_Images]); + if (form->isGen(gen_Data)) + pClassList->emplace_back(GenEnum::map_GenNames[gen_Data]); + else + pClassList->emplace_back(form->as_string(prop_class_name)); + return true; + } + } + + else if (retval < 0) + { + results.msgs.emplace_back() << "Cannot create or write to the file " << path << '\n'; + } + else // retval == result::exists + { + ++results.file_count; + } + return true; +} + +bool GenerateRustFiles(GenResults& results, std::vector* pClassList) +{ + if (Project.getChildCount() == 0) + { + results.msgs.emplace_back("You cannot generate any code until you have added a top level form.") << '\n'; + wxMessageBox("You cannot generate any code until you have added a top level form.", "Code Generation"); + return false; + } + tt_cwd cwd(true); + Project.ChangeDir(); + + bool generate_result = true; + std::vector forms; + Project.CollectForms(forms); + +#if defined(_DEBUG) || defined(INTERNAL_TESTING) + results.StartClock(); +#endif + + for (const auto& form: forms) + { + GenerateRustForm(form, results, pClassList); + } + + if (results.msgs.size()) + { + results.msgs.emplace_back() << '\n'; + } + +#if defined(_DEBUG) || defined(INTERNAL_TESTING) + results.EndClock(); +#endif + + return generate_result; } void BaseCodeGenerator::GenerateRustClass(PANEL_PAGE panel_type) diff --git a/src/mainframe.cpp b/src/mainframe.cpp index 7d030656e..e9bf072a2 100644 --- a/src/mainframe.cpp +++ b/src/mainframe.cpp @@ -93,6 +93,10 @@ enum id_GeneratePython, id_GenerateRuby, id_GenerateRust, + id_GenSingleCpp, + id_GenSinglePython, + id_GenSingleRuby, + id_GenSingleRust, id_NodeMemory, id_ShowLogger, id_XrcPreviewDlg, @@ -188,9 +192,14 @@ MainFrame::MainFrame() : menuInternal->Append(id_ShowLogger, "Show &Log Window", "Show window containing debug messages"); menuInternal->Append(id_DebugPreferences, "Test &Settings...", "Settings to use in testing builds"); menuInternal->AppendSeparator(); - menuInternal->Append(id_GeneratePython, "&Generate Python", "Generate all python files from current project."); - menuInternal->Append(id_GenerateRuby, "&Generate Ruby", "Generate all ruby files from current project."); - menuInternal->Append(id_GenerateRust, "&Generate Rust", "Generate all rust files from current project."); + menuInternal->Append(id_GenSingleCpp, "&Generate Single C++", "Generate C++ src/hdr files for seletected form."); + menuInternal->Append(id_GenSinglePython, "&Generate Single Python", "Generate Python file for seletected form."); + menuInternal->Append(id_GenSingleRuby, "&Generate Single Ruby", "Generate Ruby file for seletected form."); + menuInternal->Append(id_GenSingleRust, "&Generate Single Rust", "Generate Rust file for seletected form."); + + // menuInternal->Append(id_GeneratePython, "&Generate Python", "Generate all python files from current project."); + // menuInternal->Append(id_GenerateRuby, "&Generate Ruby", "Generate all ruby files from current project."); + // menuInternal->Append(id_GenerateRust, "&Generate Rust", "Generate all rust files from current project."); menuInternal->Append(id_DebugCurrentTest, "&Current Test", "Current debugging test"); ////////////////////// Debug-only menu items ////////////////////// @@ -402,9 +411,14 @@ MainFrame::MainFrame() : }, id_DebugPreferences); - Bind(wxEVT_MENU, &MainFrame::OnGeneratePython, this, id_GeneratePython); - Bind(wxEVT_MENU, &MainFrame::OnGenerateRuby, this, id_GenerateRuby); - Bind(wxEVT_MENU, &MainFrame::OnGenerateRust, this, id_GenerateRust); + Bind(wxEVT_MENU, &MainFrame::OnGenSingleCpp, this, id_GenSingleCpp); + Bind(wxEVT_MENU, &MainFrame::OnGenSinglePython, this, id_GenSinglePython); + Bind(wxEVT_MENU, &MainFrame::OnGenSingleRuby, this, id_GenSingleRuby); + Bind(wxEVT_MENU, &MainFrame::OnGenSingleRust, this, id_GenSingleRust); + + // Bind(wxEVT_MENU, &MainFrame::OnGeneratePython, this, id_GeneratePython); + // Bind(wxEVT_MENU, &MainFrame::OnGenerateRuby, this, id_GenerateRuby); + // Bind(wxEVT_MENU, &MainFrame::OnGenerateRust, this, id_GenerateRust); Bind(wxEVT_MENU, &App::DbgCurrentTest, &wxGetApp(), id_DebugCurrentTest); #endif diff --git a/src/mainframe.h b/src/mainframe.h index 8dd85f62a..08455f532 100644 --- a/src/mainframe.h +++ b/src/mainframe.h @@ -252,6 +252,13 @@ class MainFrame : public MainFrameBase void OnSaveProject(wxCommandEvent& event) override; void OnGenerateCode(wxCommandEvent& event) override; +#if defined(_DEBUG) || defined(INTERNAL_TESTING) + void OnGenSingleCpp(wxCommandEvent& event); + void OnGenSinglePython(wxCommandEvent& event); + void OnGenSingleRuby(wxCommandEvent& event); + void OnGenSingleRust(wxCommandEvent& event); +#endif + protected: void OnAbout(wxCommandEvent& event) override; void OnAppendCrafter(wxCommandEvent& event) override; diff --git a/src/panels/navpopupmenu.cpp b/src/panels/navpopupmenu.cpp index 22a55dc96..2d99afef9 100644 --- a/src/panels/navpopupmenu.cpp +++ b/src/panels/navpopupmenu.cpp @@ -292,6 +292,36 @@ void NavPopupMenu::OnMenuEvent(wxCommandEvent& event) ChangeSizer(gen_wxWrapSizer); break; +#if defined(_DEBUG) || defined(INTERNAL_TESTING) + case MenuSingleGenCpp: + { + wxCommandEvent dummy; + wxGetMainFrame()->OnGenSingleCpp(dummy); + } + break; + + case MenuSingleGenPython: + { + wxCommandEvent dummy; + wxGetMainFrame()->OnGenSinglePython(dummy); + } + break; + + case MenuSingleGenRuby: + { + wxCommandEvent dummy; + wxGetMainFrame()->OnGenSingleRuby(dummy); + } + break; + + case MenuSingleGenRust: + { + wxCommandEvent dummy; + wxGetMainFrame()->OnGenSingleRust(dummy); + } + break; +#endif + case MenuADD_PAGE: if (m_node->isGen(gen_BookPage)) { @@ -458,6 +488,17 @@ void NavPopupMenu::CreateCommonMenu(Node* node) void NavPopupMenu::MenuAddCommands(Node* node) { +#if defined(_DEBUG) || defined(INTERNAL_TESTING) + if (node->isForm()) + { + Append(MenuSingleGenCpp, "Generate C++ for this form"); + Append(MenuSingleGenPython, "Generate Python for this form"); + Append(MenuSingleGenRuby, "Generate Ruby for this form"); + Append(MenuSingleGenRust, "Generate Rust for this form"); + AppendSeparator(); + } +#endif + if (node->isForm() || node->isGen(gen_Images) || node->isGen(gen_embedded_image) || node->isGen(gen_Data) || node->isGen(gen_data_string)) { diff --git a/src/panels/navpopupmenu.h b/src/panels/navpopupmenu.h index 4f96dcb7f..3d5facb0b 100644 --- a/src/panels/navpopupmenu.h +++ b/src/panels/navpopupmenu.h @@ -151,6 +151,11 @@ class NavPopupMenu : public wxMenu MenuEXPAND_ALL, + // These are for Internal builds only + MenuSingleGenCpp, + MenuSingleGenPython, + MenuSingleGenRuby, + MenuSingleGenRust, MenuTESTING_INFO, MenuDEBUG_KEYHH, };