diff --git a/src/app/app_module.cpp b/src/app/app_module.cpp index 602d6767..abc58978 100644 --- a/src/app/app_module.cpp +++ b/src/app/app_module.cpp @@ -82,7 +82,8 @@ QuantityLength shapeChordalDeflection(const TopoDS_Shape& shape) } // namespace AppModule::AppModule() - : m_settings(new Settings), + : m_application(new Application), + m_settings(new Settings), m_props(m_settings), m_stdLocale(std::locale("")), m_qtLocale(QLocale::system()) @@ -94,6 +95,7 @@ AppModule::AppModule() } m_settings->setPropertyValueConversion(this); + Application::defineMayoFormat(m_application); } QStringUtils::TextOptions AppModule::defaultTextOptions() const diff --git a/src/app/app_module.h b/src/app/app_module.h index 2fc4c1b8..56a57fbe 100644 --- a/src/app/app_module.h +++ b/src/app/app_module.h @@ -9,6 +9,7 @@ #include "app_module_properties.h" #include "qstring_utils.h" +#include "../base/application.h" #include "../base/document_tree_node_properties_provider.h" #include "../base/io_parameters_provider.h" #include "../base/io_system.h" @@ -53,6 +54,9 @@ class AppModule : ~AppModule(); + // Application object + const ApplicationPtr& application() const { return m_application; } + // Settings const AppModuleProperties* properties() const { return &m_props; } AppModuleProperties* properties() { return &m_props; } @@ -121,6 +125,7 @@ class AppModule : bool impl_recordRecentFile(RecentFile* recentFile, GuiDocument* guiDoc); + ApplicationPtr m_application; Settings* m_settings = nullptr; IO::System m_ioSystem; AppModuleProperties m_props; diff --git a/src/app/commands_file.cpp b/src/app/commands_file.cpp index 8c9d00b8..e382774c 100644 --- a/src/app/commands_file.cpp +++ b/src/app/commands_file.cpp @@ -229,7 +229,7 @@ void FileCommandTools::importInDocument( QElapsedTimer chrono; chrono.start(); - auto doc = Application::instance()->findDocumentByIdentifier(targetDocId); + auto doc = appModule->application()->findDocumentByIdentifier(targetDocId); const bool okImport = appModule->ioSystem()->importInDocument() .targetDocument(doc) .withFilepaths(listFilePaths) diff --git a/src/app/main.cpp b/src/app/main.cpp index c06e0ce2..450f90af 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -368,9 +368,11 @@ static int runApp(QCoreApplication* qtApp) } // Initialize Base application - auto app = Application::instance().get(); - app->addTranslator(&qtAppTranslate); // Set Qt i18n backend + auto app = appModule->application(); + TextId::addTranslatorFunction(&qtAppTranslate); // Set Qt i18n backend +#ifdef MAYO_OS_WINDOWS initOpenCascadeEnvironment("opencascade.conf"); +#endif // Initialize Gui application auto guiApp = new GuiApplication(app); diff --git a/src/base/application.cpp b/src/base/application.cpp index ab953787..bf82e5f4 100644 --- a/src/base/application.cpp +++ b/src/base/application.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #if OCC_VERSION_HEX < OCC_VERSION_CHECK(7, 5, 0) @@ -20,7 +19,6 @@ #endif #include -#include #include namespace Mayo { @@ -52,37 +50,20 @@ class Document::FormatXmlRetrievalDriver : public XmlXCAFDrivers_DocumentRetriev struct Application::Private { std::atomic m_seqDocumentIdentifier = {}; std::unordered_map m_mapIdentifierDocument; - std::vector m_vecTranslator; }; struct ApplicationI18N { MAYO_DECLARE_TEXT_ID_FUNCTIONS(Mayo::Application) }; -Application::~Application() +Application::Application() + : d(new Private) { - delete d; } -const ApplicationPtr& Application::instance() +Application::~Application() { - static ApplicationPtr appPtr; - if (!appPtr) { - appPtr = new Application; - const char strFougueCopyright[] = "Copyright (c) 2021, Fougue Ltd. "; - appPtr->DefineFormat( - Document::NameFormatBinary, ApplicationI18N::textIdTr("Binary Mayo Document Format").data(), "myb", - new Document::FormatBinaryRetrievalDriver(appPtr), - new BinXCAFDrivers_DocumentStorageDriver - ); - appPtr->DefineFormat( - Document::NameFormatXml, ApplicationI18N::textIdTr("XML Mayo Document Format").data(), "myx", - new Document::FormatXmlRetrievalDriver(appPtr), - new XmlXCAFDrivers_DocumentStorageDriver(strFougueCopyright) - ); - } - - return appPtr; + delete d; } int Application::documentCount() const @@ -117,7 +98,7 @@ DocumentPtr Application::openDocument(const FilePath& filepath, PCDM_ReaderStatu DocumentPtr Application::findDocumentByIndex(int docIndex) const { OccHandle doc; - TDocStd_Application::GetDocument(docIndex + 1, doc); + XCAFApp_Application::GetDocument(docIndex + 1, doc); return !doc.IsNull() ? DocumentPtr::DownCast(doc) : DocumentPtr(); } @@ -150,7 +131,7 @@ int Application::findIndexOfDocument(const DocumentPtr& doc) const void Application::closeDocument(const DocumentPtr& doc) { - TDocStd_Application::Close(doc); + XCAFApp_Application::Close(doc); doc->signalNameChanged.disconnectAll(); doc->signalFilePathChanged.disconnectAll(); doc->signalEntityAdded.disconnectAll(); @@ -158,22 +139,19 @@ void Application::closeDocument(const DocumentPtr& doc) //doc->Main().ForgetAllAttributes(true/*clearChildren*/); } -void Application::addTranslator(Application::Translator fn) -{ - if (fn) - d->m_vecTranslator.push_back(std::move(fn)); -} - -std::string_view Application::translate(const TextId& textId, int n) const +void Application::defineMayoFormat(const ApplicationPtr& app) { - for (auto it = d->m_vecTranslator.rbegin(); it != d->m_vecTranslator.rend(); ++it) { - const Application::Translator& fn = *it; - std::string_view msg = fn(textId, n); - if (!msg.empty()) - return msg; - } - - return textId.key; + const char strFougueCopyright[] = "Copyright (c) 2024, Fougue Ltd. "; + app->DefineFormat( + Document::NameFormatBinary, ApplicationI18N::textIdTr("Binary Mayo Document Format").data(), "myb", + new Document::FormatBinaryRetrievalDriver(app), + new BinXCAFDrivers_DocumentStorageDriver + ); + app->DefineFormat( + Document::NameFormatXml, ApplicationI18N::textIdTr("XML Mayo Document Format").data(), "myx", + new Document::FormatXmlRetrievalDriver(app), + new XmlXCAFDrivers_DocumentStorageDriver(strFougueCopyright) + ); } Span Application::envOpenCascadeOptions() @@ -215,7 +193,7 @@ void Application::NewDocument(const TCollection_ExtendedString&, OccHandle& doc) const void Application::InitDocument(const OccHandle& doc) const #endif { - TDocStd_Application::InitDocument(doc); - XCAFApp_Application::GetApplication()->InitDocument(doc); -} - -Application::Application() - : d(new Private) -{ + XCAFApp_Application::InitDocument(doc); } void Application::notifyDocumentAboutToClose(Document::Identifier docIdent) diff --git a/src/base/application.h b/src/base/application.h index d733ce04..746433e5 100644 --- a/src/base/application.h +++ b/src/base/application.h @@ -11,22 +11,19 @@ #include "occ_handle.h" #include "signal.h" #include "span.h" -#include "text_id.h" #include #include -#include +#include namespace Mayo { // Provides management of Document objects -class Application : public TDocStd_Application { +class Application : public XCAFApp_Application { public: + Application(); ~Application(); - // Global instance(singleton) - static const ApplicationPtr& instance(); - // Iterator over Documents contained in an Application struct DocumentIterator : private CDF_DirectoryIterator { DocumentIterator(const ApplicationPtr& app); @@ -49,13 +46,7 @@ class Application : public TDocStd_Application { void closeDocument(const DocumentPtr& doc); - // Provides internationalization support for text output - // 1st arg: message to be translated(TextId = context+key) - // 2nd arg: when != -1 used to choose an appropriate form for the translation(e.g. "%n file found" vs. "%n files found") - // returns: translated message - using Translator = std::function; - void addTranslator(Translator fn); - std::string_view translate(const TextId& textId, int n = -1) const; + static void defineMayoFormat(const ApplicationPtr& app); static Span envOpenCascadeOptions(); static Span envOpenCascadePaths(); @@ -83,12 +74,11 @@ class Application : public TDocStd_Application { // -> Can't do because PCDM_RetrievalDriver subclasses create explicitly "new TDocStd_Document(...)" // This would break TDocStd_Application::Open(...) - DEFINE_STANDARD_RTTI_INLINE(Application, TDocStd_Application) + DEFINE_STANDARD_RTTI_INLINE(Application, XCAFApp_Application) private: // Implementation friend class Document; - Application(); void notifyDocumentAboutToClose(Document::Identifier docIdent); void addDocument(const DocumentPtr& doc); diff --git a/src/base/application_ptr.h b/src/base/application_ptr.h index a8cbbc2a..bab06e0f 100644 --- a/src/base/application_ptr.h +++ b/src/base/application_ptr.h @@ -7,12 +7,12 @@ #pragma once #include "occ_handle.h" -#include +#include namespace Mayo { class Application; -DEFINE_STANDARD_HANDLE(Application, TDocStd_Application) +DEFINE_STANDARD_HANDLE(Application, XCAFApp_Application) using ApplicationPtr = OccHandle; } // namespace Mayo diff --git a/src/base/text_id.cpp b/src/base/text_id.cpp index c821ea4f..a8847bf9 100644 --- a/src/base/text_id.cpp +++ b/src/base/text_id.cpp @@ -5,13 +5,24 @@ ****************************************************************************/ #include "text_id.h" -#include "application.h" + +#include namespace Mayo { +namespace { + +std::vector& getTranslatorFunctions() +{ + static std::vector vec; + return vec; +} + +} // namespace + std::string_view TextId::tr(int n) const { - return Application::instance()->translate(*this, n); + return TextId::translate(*this, n); } bool TextId::isEmpty() const @@ -19,4 +30,23 @@ bool TextId::isEmpty() const return this->key.empty(); } +void TextId::addTranslatorFunction(TranslatorFunctionPtr fn) +{ + if (fn) + getTranslatorFunctions().push_back(fn); +} + +std::string_view TextId::translate(const TextId& textId, int n) +{ + for (auto it = getTranslatorFunctions().rbegin(); it != getTranslatorFunctions().rend(); ++it) { + TranslatorFunctionPtr fn = *it; + std::string_view msg = fn(textId, n); + if (!msg.empty()) + return msg; + } + + return textId.key; +} + + } // namespace Mayo diff --git a/src/base/text_id.h b/src/base/text_id.h index 7e19effd..9e5d2dbb 100644 --- a/src/base/text_id.h +++ b/src/base/text_id.h @@ -37,6 +37,15 @@ struct TextId { // Whether source text(key) is empty or not bool isEmpty() const; + + // Provides internationalization support for text output + // 1st arg: message to be translated(TextId = context+key) + // 2nd arg: when != -1 used to choose an appropriate form for the translation(e.g. "%n file found" vs. "%n files found") + // returns: translated message + using TranslatorFunction = std::string_view(const TextId&, int); + using TranslatorFunctionPtr = TranslatorFunction*; + static void addTranslatorFunction(TranslatorFunctionPtr fn); + static std::string_view translate(const TextId& textId, int n = -1); }; } // namespace Mayo diff --git a/src/cli/cli_export.cpp b/src/cli/cli_export.cpp index 989267cc..2437a1a3 100644 --- a/src/cli/cli_export.cpp +++ b/src/cli/cli_export.cpp @@ -166,7 +166,10 @@ void exportDocument(const DocumentPtr& doc, const FilePath& filepath, Helper* he } // namespace void cli_asyncExportDocuments( - Application* app, const CliExportArgs& args, std::function fnContinuation) + const ApplicationPtr& app, + const CliExportArgs& args, + std::function fnContinuation + ) { auto helper = new Helper; // Allocated on heap because current function is asynchronous auto taskMgr = &helper->taskMgr; diff --git a/src/cli/cli_export.h b/src/cli/cli_export.h index e57944b6..04e3ce9b 100644 --- a/src/cli/cli_export.h +++ b/src/cli/cli_export.h @@ -6,6 +6,7 @@ #pragma once +#include "../base/application_ptr.h" #include "../base/filepath.h" #include "../base/span.h" @@ -13,8 +14,6 @@ namespace Mayo { -class Application; - // Contains arguments for the cli_asyncExportDocuments() function struct CliExportArgs { bool progressReport = true; @@ -25,7 +24,7 @@ struct CliExportArgs { // Asynchronously exports input file(s) listed in 'args' // Calls 'fnContinuation' at the end of execution void cli_asyncExportDocuments( - Application* app, + const ApplicationPtr& app, const CliExportArgs& args, std::function fnContinuation ); diff --git a/src/cli/main.cpp b/src/cli/main.cpp index 46a694c9..e3512d2f 100644 --- a/src/cli/main.cpp +++ b/src/cli/main.cpp @@ -405,8 +405,8 @@ static int runApp(QCoreApplication* qtApp) } // Initialize Base application - auto app = Application::instance().get(); - app->addTranslator(&qtAppTranslate); // Set Qt i18n backend + auto app = appModule->application(); + TextId::addTranslatorFunction(&qtAppTranslate); // Set Qt i18n backend #ifdef MAYO_OS_WINDOWS initOpenCascadeEnvironment("opencascade.conf"); #endif diff --git a/tests/test_app.cpp b/tests/test_app.cpp index 71933659..0db98011 100644 --- a/tests/test_app.cpp +++ b/tests/test_app.cpp @@ -66,7 +66,7 @@ RecentFile createRecentFile(const QPixmap& thumbnail) void TestApp::DocumentFilesWatcher_test() { - auto app = Application::instance(); + auto app = makeOccHandle(); DocumentFilesWatcher docFilesWatcher(app); docFilesWatcher.enable(true); @@ -87,10 +87,6 @@ void TestApp::DocumentFilesWatcher_test() fnCopyCadFile(); DocumentPtr doc = app->newDocument(); doc->setFilePath(cadFilePath); - auto _ = gsl::finally([=]{ - if (app->findIndexOfDocument(doc) != -1) - app->closeDocument(doc); - }); // Check file change on document is caught fnCopyCadFile(); diff --git a/tests/test_base.cpp b/tests/test_base.cpp index eb6fb229..d4ab9c61 100644 --- a/tests/test_base.cpp +++ b/tests/test_base.cpp @@ -188,7 +188,7 @@ struct SignalEmitSpy { void TestBase::Application_test() { - auto app = Application::instance(); + auto app = makeOccHandle(); auto fnImportInDocument = [=](const DocumentPtr& doc, const FilePath& fp) { return m_ioSystem->importInDocument() .targetDocument(doc) @@ -259,9 +259,10 @@ void TestBase::Application_test() void TestBase::DocumentRefCount_test() { - DocumentPtr doc = Application::instance()->newDocument(); + auto app = makeOccHandle(); + DocumentPtr doc = app->newDocument(); QVERIFY(doc->GetRefCount() > 1); - Application::instance()->closeDocument(doc); + app->closeDocument(doc); QCOMPARE(doc->GetRefCount(), 1); } @@ -609,7 +610,7 @@ void TestBase::IO_bugGitHub166_test() QFETCH(QString, strOutputFilePath); QFETCH(IO::Format, outputFormat); - auto app = Application::instance(); + auto app = makeOccHandle(); DocumentPtr doc = app->newDocument(); const bool okImport = m_ioSystem->importInDocument() .targetDocument(doc) @@ -665,7 +666,7 @@ void TestBase::IO_bugGitHub166_test_data() void TestBase::IO_bugGitHub258_test() { - auto app = Application::instance(); + auto app = makeOccHandle(); DocumentPtr doc = app->newDocument(); const bool okImport = m_ioSystem->importInDocument() .targetDocument(doc) @@ -681,8 +682,6 @@ void TestBase::IO_bugGitHub258_test() QVERIFY(!triangulation.IsNull()); QCOMPARE(triangulation->NbNodes(), 24); QCOMPARE(triangulation->NbTriangles(), 12); - - app->closeDocument(doc); } void TestBase::DoubleToString_test() diff --git a/tests/test_measure.cpp b/tests/test_measure.cpp index 061f93e3..d8f6ea63 100644 --- a/tests/test_measure.cpp +++ b/tests/test_measure.cpp @@ -182,7 +182,8 @@ void TestMeasure::BRepArea_TriangulationFace_test() const bool okRead = reader.readFile("tests/inputs/face_trsf_scale_almost_1.stl", progress); QVERIFY(okRead); - auto doc = Application::instance()->newDocument(); + auto app = makeOccHandle(); + auto doc = app->newDocument(); const TDF_LabelSequence seqLabel = reader.transfer(doc, progress); QCOMPARE(seqLabel.Size(), 1); const TopoDS_Shape shape = doc->xcaf().shape(seqLabel.First()); @@ -193,7 +194,7 @@ void TestMeasure::BRepArea_TriangulationFace_test() void TestMeasure::BRepBoundingBox_Sphere_test() { const double sphereRadius = 50.; - const TopoDS_Shape sphereShape = BRepPrimAPI_MakeSphere(50.); + const TopoDS_Shape sphereShape = BRepPrimAPI_MakeSphere(sphereRadius); const MeasureBoundingBox bndBox = MeasureToolBRep::brepBoundingBox(sphereShape); QVERIFY(bndBox.cornerMin.IsEqual(gp_Pnt{-sphereRadius, -sphereRadius, -sphereRadius}, Precision::Confusion())); QVERIFY(bndBox.cornerMax.IsEqual(gp_Pnt{sphereRadius, sphereRadius, sphereRadius}, Precision::Confusion()));