From 9f30cb250551e2825c7230365378b796408abc21 Mon Sep 17 00:00:00 2001 From: Boyuan Yang Date: Sat, 3 Dec 2022 09:05:57 -0500 Subject: [PATCH] Import dtkcore_5.6.2.2.orig.tar.gz [dgit import orig dtkcore_5.6.2.2.orig.tar.gz] --- .clog.toml | 5 + .github/ISSUE_TEMPLATE/config.yml | 1 + .github/ISSUE_TEMPLATE/empty-issue.md | 8 + .github/ISSUE_TEMPLATE/unit-test-report.md | 12 + .github/workflows/backup-to-gitlab.yml | 17 + .github/workflows/call-auto-tag.yml | 16 + .github/workflows/call-build-deb.yml | 17 + .github/workflows/call-build-distribution.yml | 17 + .github/workflows/call-chatOps.yml | 10 + .github/workflows/call-clacheck.yml | 16 + .github/workflows/call-commitlint.yml | 11 + .github/workflows/call-doc-check.yml | 15 + .github/workflows/call-license-check.yml | 16 + .github/workflows/call-tag-build.yml | 13 + .github/workflows/cppcheck.yml | 26 + .gitignore | 38 + .gitlab-ci.yml | 5 + .packit.yaml | 17 + .project.json | 6 + .release.json | 16 + .reuse/dep5 | 75 + CHANGELOG.md | 266 + CMakeLists.txt | 106 + LICENSE | 304 + LICENSES/CC-BY-4.0.txt | 156 + LICENSES/CC0-1.0.txt | 121 + LICENSES/LGPL-2.1-or-later.txt | 468 + LICENSES/LGPL-3.0-or-later.txt | 304 + LICENSES/MIT.txt | 9 + README.md | 52 + README.zh_CN.md | 52 + archlinux/PKGBUILD | 33 + cmake/DtkCMake/DtkCMakeConfig.cmake | 76 + cmake/DtkTools/DtkSettingsToolsMacros.cmake | 53 + cmake/DtkTools/DtkToolsConfig.cmake | 9 + conanfile.py | 91 + debian/changelog | 47 + debian/compat | 1 + debian/control | 42 + debian/copyright | 22 + debian/libdtkcore-dev.install | 5 + debian/libdtkcore-doc.install | 1 + debian/libdtkcore5-bin.install | 2 + debian/libdtkcore5.install | 1 + .../doxygen-awesome-darkmode-toggle.js | 157 + debian/rules | 25 + debian/source/format | 1 + debian/symbols.amd64 | 909 + docs/CMakeLists.txt | 55 + docs/Specification.md | 81 + docs/doxygentheme/custom-alternative.css | 54 + docs/doxygentheme/custom.css | 101 + .../doxygen-awesome-darkmode-toggle.js | 157 + .../doxygen-awesome-fragment-copy-button.js | 85 + .../doxygen-awesome-interactive-toc.js | 81 + .../doxygen-awesome-paragraph-link.js | 51 + ...n-awesome-sidebar-only-darkmode-toggle.css | 40 + .../doxygen-awesome-sidebar-only.css | 115 + docs/doxygentheme/doxygen-awesome.css | 2405 ++ docs/doxygentheme/header.html | 147 + docs/doxygentheme/logo.drawio.svg | 1 + docs/doxygentheme/toggle-alternative-theme.js | 12 + docs/dsysinfo.zh_CN.dox | 221 + examples/CMakeLists.txt | 2 + examples/dasync-example/CMakeLists.txt | 21 + examples/dasync-example/main.cpp | 444 + examples/expintf-example/CMakeLists.txt | 19 + examples/expintf-example/main.cpp | 55 + include/DtkCore/DAbstractUnitFormatter | 1 + include/DtkCore/DBaseFileWatcher | 1 + include/DtkCore/DCapDir | 1 + include/DtkCore/DCapFile | 1 + include/DtkCore/DCapManager | 1 + include/DtkCore/DConfig | 1 + include/DtkCore/DConfigFile | 1 + include/DtkCore/DDBusExtended | 1 + .../DtkCore/DDBusExtendedAbstractInterface | 1 + include/DtkCore/DDBusInterface | 1 + include/DtkCore/DDBusSender | 1 + include/DtkCore/DDciFile | 1 + include/DtkCore/DDesktopEntry | 1 + include/DtkCore/DDiskSizeFormatter | 1 + include/DtkCore/DError | 1 + include/DtkCore/DExpected | 1 + include/DtkCore/DExportedInterface | 1 + include/DtkCore/DFileServices | 1 + include/DtkCore/DFileSystemWatcher | 1 + include/DtkCore/DFileWatcher | 1 + include/DtkCore/DFileWatcherManager | 1 + include/DtkCore/DLog | 7 + include/DtkCore/DNotifySender | 1 + include/DtkCore/DObject | 1 + include/DtkCore/DObjectPrivate | 1 + include/DtkCore/DPathBuf | 1 + include/DtkCore/DPinyin | 1 + include/DtkCore/DRecentManager | 1 + include/DtkCore/DSGApplication | 1 + include/DtkCore/DSecureString | 1 + include/DtkCore/DSettings | 1 + include/DtkCore/DSettingsGroup | 1 + include/DtkCore/DSettingsOption | 1 + include/DtkCore/DSingleton | 1 + include/DtkCore/DStandardPaths | 1 + include/DtkCore/DSysInfo | 1 + include/DtkCore/DThreadUtils | 1 + include/DtkCore/DTimeUnitFormatter | 1 + include/DtkCore/DTrashManager | 1 + include/DtkCore/DUtil | 1 + include/DtkCore/DVtableHook | 1 + include/DtkCore/DtkCores | 43 + include/base/derror.h | 151 + include/base/dexpected.h | 1539 + include/base/dobject.h | 41 + include/base/dsingleton.h | 72 + include/base/private/dobject_p.h | 29 + include/dci/ddcifile.h | 73 + include/filesystem/dbasefilewatcher.h | 54 + include/filesystem/dcapfile.h | 105 + include/filesystem/dcapmanager.h | 37 + include/filesystem/dfilesystemwatcher.h | 49 + include/filesystem/dfilewatcher.h | 35 + include/filesystem/dfilewatchermanager.h | 46 + include/filesystem/dpathbuf.h | 55 + include/filesystem/dstandardpaths.h | 60 + include/filesystem/dtrashmanager.h | 34 + include/global/dconfig.h | 69 + include/global/dconfigfile.h | 127 + include/global/ddesktopentry.h | 95 + include/global/dsecurestring.h | 20 + include/global/dsgapplication.h | 21 + include/global/dsysinfo.h | 186 + include/global/dtkcore_global.h | 60 + include/log/AbstractAppender.h | 40 + include/log/AbstractStringAppender.h | 35 + include/log/ConsoleAppender.h | 30 + include/log/FileAppender.h | 43 + include/log/LogManager.h | 56 + include/log/Logger.h | 149 + include/log/RollingFileAppender.h | 75 + include/log/dloggerdefs.h | 56 + include/log/win32/OutputDebugAppender.h | 23 + .../backend/dsettingsdconfigbackend.h | 34 + include/settings/backend/gsettingsbackend.h | 34 + include/settings/backend/qsettingbackend.h | 34 + include/settings/dsettings.h | 63 + include/settings/dsettingsbackend.h | 43 + include/settings/dsettingsgroup.h | 49 + include/settings/dsettingsoption.h | 57 + include/util/dabstractunitformatter.h | 37 + include/util/dasync.h | 489 + include/util/ddbusextended.h | 14 + include/util/ddbusextendedabstractinterface.h | 82 + include/util/ddbusinterface.h | 40 + include/util/ddbussender.h | 101 + include/util/ddisksizeformatter.h | 41 + include/util/dexportedinterface.h | 35 + include/util/dfileservices.h | 42 + include/util/dnotifysender.h | 36 + include/util/dpinyin.h | 18 + include/util/drecentmanager.h | 30 + include/util/dthreadutils.h | 158 + include/util/dtimedloop.h | 46 + include/util/dtimeunitformatter.h | 36 + include/util/dutil.h | 47 + include/util/dvtablehook.h | 338 + misc/DtkConfig.cmake.in | 14 + misc/dtkcore.pc.in | 10 + misc/qt_lib_dtkcore.pri.in | 14 + rpm/dtkcore.spec | 76 + src/CMakeLists.txt | 126 + src/base/base.cmake | 8 + src/base/dobject.cpp | 244 + src/dbus/dbus.cmake | 21 + .../org.desktopspec.ConfigManager.Manager.xml | 47 + src/dbus/org.desktopspec.ConfigManager.xml | 22 + src/dci/dci.cmake | 6 + src/dci/ddcifile.cpp | 829 + src/dci/private/ddcifileengine.cpp | 613 + src/dci/private/ddcifileengine_p.h | 127 + src/dconfig.cpp | 676 + src/dconfigfile.cpp | 1363 + src/ddesktopentry.cpp | 1049 + src/dsecurestring.cpp | 20 + src/dsgapplication.cpp | 40 + src/dsysinfo.cpp | 1283 + src/dtkcore_global.cpp | 61 + src/filesystem/dbasefilewatcher.cpp | 187 + src/filesystem/dcapfile.cpp | 386 + src/filesystem/dcapfsfileengine.cpp | 209 + src/filesystem/dcapmanager.cpp | 152 + src/filesystem/dfilesystemwatcher_dummy.cpp | 229 + src/filesystem/dfilesystemwatcher_linux.cpp | 624 + src/filesystem/dfilesystemwatcher_win.cpp | 232 + src/filesystem/dfilewatcher.cpp | 280 + src/filesystem/dfilewatchermanager.cpp | 108 + src/filesystem/dpathbuf.cpp | 80 + src/filesystem/dstandardpaths.cpp | 216 + src/filesystem/dtrashmanager_dummy.cpp | 143 + src/filesystem/dtrashmanager_linux.cpp | 247 + src/filesystem/filesystem.cmake | 57 + src/filesystem/private/dbasefilewatcher_p.h | 32 + src/filesystem/private/dcapfsfileengine_p.h | 46 + .../private/dfilesystemwatcher_dummy_p.h | 32 + .../private/dfilesystemwatcher_linux_p.h | 44 + .../private/dfilesystemwatcher_win_p.h | 32 + src/filesystem/private/private.pri | 11 + src/glob.cmake | 29 + src/log/AbstractAppender.cpp | 159 + src/log/AbstractStringAppender.cpp | 433 + src/log/ConsoleAppender.cpp | 68 + src/log/FileAppender.cpp | 112 + src/log/LogManager.cpp | 121 + src/log/Logger.cpp | 1050 + src/log/OutputDebugAppender.cpp | 48 + src/log/README.md | 63 + src/log/RollingFileAppender.cpp | 243 + src/log/log.cmake | 25 + .../backend/dsettingsdconfigbackend.cpp | 95 + src/settings/backend/gsettingsbackend.cpp | 137 + src/settings/backend/qsettingbackend.cpp | 103 + src/settings/dsettings.cpp | 468 + src/settings/dsettingsgroup.cpp | 246 + src/settings/dsettingsoption.cpp | 320 + src/settings/settings.cmake | 25 + src/util/README.dpinyin | 6 + src/util/dabstractunitformatter.cpp | 145 + src/util/ddbusextendedabstractinterface.cpp | 504 + src/util/ddbusextendedpendingcallwatcher.cpp | 17 + src/util/ddbusextendedpendingcallwatcher_p.h | 46 + src/util/ddbusinterface.cpp | 242 + src/util/ddbusinterface_p.h | 38 + src/util/ddbussender.cpp | 95 + src/util/ddisksizeformatter.cpp | 95 + src/util/dexportedinterface.cpp | 134 + src/util/dfileservices_dummy.cpp | 149 + src/util/dfileservices_linux.cpp | 138 + src/util/dnotifysender.cpp | 97 + src/util/dpinyin.cpp | 63 + src/util/drecentmanager.cpp | 247 + src/util/dthreadutils.cpp | 46 + src/util/dtimedloop.cpp | 152 + src/util/dtimeunitformatter.cpp | 92 + src/util/dvtablehook.cpp | 380 + src/util/resources/dpinyin.dict | 25333 ++++++++++++++++ src/util/util.cmake | 49 + src/util/util.qrc | 5 + tests/CMakeLists.txt | 174 + tests/data.qrc | 12 + tests/data/dconf-example.meta.json | 92 + tests/data/dconf-example.override.json | 11 + ...conf-example_other_app_configure.meta.json | 24 + tests/data/dconf-global.meta.json | 16 + tests/data/dconf-global.override.json | 11 + .../dconf-example.override.a.b.json | 11 + .../dconf-example.override.a.json | 11 + tests/data/dt-settings.json | 218 + tests/main.cpp | 26 + tests/test-recoverage.sh | 31 + tests/test_helper.hpp | 47 + tests/ut_dasync.cpp | 352 + tests/ut_dcapfile.cpp | 318 + tests/ut_dconfig.cpp | 122 + tests/ut_dconfigfile.cpp | 284 + tests/ut_ddci.cpp | 447 + tests/ut_ddesktopentrytest.cpp | 110 + tests/ut_ddisksizeformatter.cpp | 62 + tests/ut_dfilesystemwatcher.cpp | 93 + tests/ut_dfilewatcher.cpp | 190 + tests/ut_dfilewatchermanager.cpp | 72 + tests/ut_dpathbuf.cpp | 80 + tests/ut_dpinyin.cpp | 30 + tests/ut_drecentmanager.cpp | 86 + tests/ut_dsecurestring.cpp | 37 + tests/ut_dsettings.cpp | 188 + tests/ut_dstandardpaths.cpp | 51 + tests/ut_dthreadutils.cpp | 62 + tests/ut_dtimeunitformatter.cpp | 57 + tests/ut_dtrashmanager.cpp | 56 + tests/ut_dutil.cpp | 521 + tests/ut_dutil.h | 25 + tests/ut_dvtablehook.cpp | 177 + tests/ut_gsettingsbackend.cpp | 89 + tests/ut_logger.cpp | 162 + tests/ut_qsettingsbackend.cpp | 97 + tests/ut_singleton.cpp | 26 + tests/ut_singleton.h | 33 + tools/CMakeLists.txt | 4 + tools/dci/CMakeLists.txt | 22 + tools/dci/dci.pro | 23 + tools/dci/main.cpp | 177 + tools/deepin-os-release/CMakeLists.txt | 30 + tools/deepin-os-release/deepin-os-release.pro | 20 + tools/deepin-os-release/main.cpp | 123 + tools/qdbusxml2cpp/CMakeLists.txt | 19 + tools/qdbusxml2cpp/README | 1 + tools/qdbusxml2cpp/qdbusxml2cpp.cpp | 1376 + tools/qdbusxml2cpp/qdbusxml2cpp.pro | 16 + tools/settings/CMakeLists.txt | 29 + tools/settings/main.cpp | 285 + tools/settings/settings.pro | 34 + 300 files changed, 61760 insertions(+) create mode 100644 .clog.toml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/empty-issue.md create mode 100644 .github/ISSUE_TEMPLATE/unit-test-report.md create mode 100644 .github/workflows/backup-to-gitlab.yml create mode 100644 .github/workflows/call-auto-tag.yml create mode 100644 .github/workflows/call-build-deb.yml create mode 100644 .github/workflows/call-build-distribution.yml create mode 100644 .github/workflows/call-chatOps.yml create mode 100644 .github/workflows/call-clacheck.yml create mode 100644 .github/workflows/call-commitlint.yml create mode 100644 .github/workflows/call-doc-check.yml create mode 100644 .github/workflows/call-license-check.yml create mode 100644 .github/workflows/call-tag-build.yml create mode 100644 .github/workflows/cppcheck.yml create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 .packit.yaml create mode 100644 .project.json create mode 100644 .release.json create mode 100644 .reuse/dep5 create mode 100644 CHANGELOG.md create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 LICENSES/CC-BY-4.0.txt create mode 100644 LICENSES/CC0-1.0.txt create mode 100644 LICENSES/LGPL-2.1-or-later.txt create mode 100644 LICENSES/LGPL-3.0-or-later.txt create mode 100644 LICENSES/MIT.txt create mode 100644 README.md create mode 100644 README.zh_CN.md create mode 100644 archlinux/PKGBUILD create mode 100644 cmake/DtkCMake/DtkCMakeConfig.cmake create mode 100644 cmake/DtkTools/DtkSettingsToolsMacros.cmake create mode 100644 cmake/DtkTools/DtkToolsConfig.cmake create mode 100644 conanfile.py create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/libdtkcore-dev.install create mode 100644 debian/libdtkcore-doc.install create mode 100644 debian/libdtkcore5-bin.install create mode 100644 debian/libdtkcore5.install create mode 100644 debian/missing-sources/doxygen-awesome-darkmode-toggle.js create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/symbols.amd64 create mode 100644 docs/CMakeLists.txt create mode 100644 docs/Specification.md create mode 100644 docs/doxygentheme/custom-alternative.css create mode 100644 docs/doxygentheme/custom.css create mode 100644 docs/doxygentheme/doxygen-awesome-darkmode-toggle.js create mode 100644 docs/doxygentheme/doxygen-awesome-fragment-copy-button.js create mode 100644 docs/doxygentheme/doxygen-awesome-interactive-toc.js create mode 100644 docs/doxygentheme/doxygen-awesome-paragraph-link.js create mode 100644 docs/doxygentheme/doxygen-awesome-sidebar-only-darkmode-toggle.css create mode 100644 docs/doxygentheme/doxygen-awesome-sidebar-only.css create mode 100644 docs/doxygentheme/doxygen-awesome.css create mode 100644 docs/doxygentheme/header.html create mode 100644 docs/doxygentheme/logo.drawio.svg create mode 100644 docs/doxygentheme/toggle-alternative-theme.js create mode 100644 docs/dsysinfo.zh_CN.dox create mode 100644 examples/CMakeLists.txt create mode 100644 examples/dasync-example/CMakeLists.txt create mode 100644 examples/dasync-example/main.cpp create mode 100644 examples/expintf-example/CMakeLists.txt create mode 100644 examples/expintf-example/main.cpp create mode 100644 include/DtkCore/DAbstractUnitFormatter create mode 100644 include/DtkCore/DBaseFileWatcher create mode 100644 include/DtkCore/DCapDir create mode 100644 include/DtkCore/DCapFile create mode 100644 include/DtkCore/DCapManager create mode 100644 include/DtkCore/DConfig create mode 100644 include/DtkCore/DConfigFile create mode 100644 include/DtkCore/DDBusExtended create mode 100644 include/DtkCore/DDBusExtendedAbstractInterface create mode 100644 include/DtkCore/DDBusInterface create mode 100644 include/DtkCore/DDBusSender create mode 100644 include/DtkCore/DDciFile create mode 100644 include/DtkCore/DDesktopEntry create mode 100644 include/DtkCore/DDiskSizeFormatter create mode 100644 include/DtkCore/DError create mode 100644 include/DtkCore/DExpected create mode 100644 include/DtkCore/DExportedInterface create mode 100644 include/DtkCore/DFileServices create mode 100644 include/DtkCore/DFileSystemWatcher create mode 100644 include/DtkCore/DFileWatcher create mode 100644 include/DtkCore/DFileWatcherManager create mode 100644 include/DtkCore/DLog create mode 100644 include/DtkCore/DNotifySender create mode 100644 include/DtkCore/DObject create mode 100644 include/DtkCore/DObjectPrivate create mode 100644 include/DtkCore/DPathBuf create mode 100644 include/DtkCore/DPinyin create mode 100644 include/DtkCore/DRecentManager create mode 100644 include/DtkCore/DSGApplication create mode 100644 include/DtkCore/DSecureString create mode 100644 include/DtkCore/DSettings create mode 100644 include/DtkCore/DSettingsGroup create mode 100644 include/DtkCore/DSettingsOption create mode 100644 include/DtkCore/DSingleton create mode 100644 include/DtkCore/DStandardPaths create mode 100644 include/DtkCore/DSysInfo create mode 100644 include/DtkCore/DThreadUtils create mode 100644 include/DtkCore/DTimeUnitFormatter create mode 100644 include/DtkCore/DTrashManager create mode 100644 include/DtkCore/DUtil create mode 100644 include/DtkCore/DVtableHook create mode 100644 include/DtkCore/DtkCores create mode 100644 include/base/derror.h create mode 100644 include/base/dexpected.h create mode 100644 include/base/dobject.h create mode 100644 include/base/dsingleton.h create mode 100644 include/base/private/dobject_p.h create mode 100644 include/dci/ddcifile.h create mode 100644 include/filesystem/dbasefilewatcher.h create mode 100644 include/filesystem/dcapfile.h create mode 100644 include/filesystem/dcapmanager.h create mode 100644 include/filesystem/dfilesystemwatcher.h create mode 100644 include/filesystem/dfilewatcher.h create mode 100644 include/filesystem/dfilewatchermanager.h create mode 100644 include/filesystem/dpathbuf.h create mode 100644 include/filesystem/dstandardpaths.h create mode 100644 include/filesystem/dtrashmanager.h create mode 100644 include/global/dconfig.h create mode 100644 include/global/dconfigfile.h create mode 100644 include/global/ddesktopentry.h create mode 100644 include/global/dsecurestring.h create mode 100644 include/global/dsgapplication.h create mode 100644 include/global/dsysinfo.h create mode 100644 include/global/dtkcore_global.h create mode 100644 include/log/AbstractAppender.h create mode 100644 include/log/AbstractStringAppender.h create mode 100644 include/log/ConsoleAppender.h create mode 100644 include/log/FileAppender.h create mode 100644 include/log/LogManager.h create mode 100644 include/log/Logger.h create mode 100644 include/log/RollingFileAppender.h create mode 100644 include/log/dloggerdefs.h create mode 100644 include/log/win32/OutputDebugAppender.h create mode 100644 include/settings/backend/dsettingsdconfigbackend.h create mode 100644 include/settings/backend/gsettingsbackend.h create mode 100644 include/settings/backend/qsettingbackend.h create mode 100644 include/settings/dsettings.h create mode 100644 include/settings/dsettingsbackend.h create mode 100644 include/settings/dsettingsgroup.h create mode 100644 include/settings/dsettingsoption.h create mode 100644 include/util/dabstractunitformatter.h create mode 100644 include/util/dasync.h create mode 100644 include/util/ddbusextended.h create mode 100644 include/util/ddbusextendedabstractinterface.h create mode 100644 include/util/ddbusinterface.h create mode 100644 include/util/ddbussender.h create mode 100644 include/util/ddisksizeformatter.h create mode 100644 include/util/dexportedinterface.h create mode 100644 include/util/dfileservices.h create mode 100644 include/util/dnotifysender.h create mode 100644 include/util/dpinyin.h create mode 100644 include/util/drecentmanager.h create mode 100644 include/util/dthreadutils.h create mode 100644 include/util/dtimedloop.h create mode 100644 include/util/dtimeunitformatter.h create mode 100644 include/util/dutil.h create mode 100644 include/util/dvtablehook.h create mode 100644 misc/DtkConfig.cmake.in create mode 100644 misc/dtkcore.pc.in create mode 100644 misc/qt_lib_dtkcore.pri.in create mode 100644 rpm/dtkcore.spec create mode 100644 src/CMakeLists.txt create mode 100644 src/base/base.cmake create mode 100644 src/base/dobject.cpp create mode 100644 src/dbus/dbus.cmake create mode 100644 src/dbus/org.desktopspec.ConfigManager.Manager.xml create mode 100644 src/dbus/org.desktopspec.ConfigManager.xml create mode 100644 src/dci/dci.cmake create mode 100644 src/dci/ddcifile.cpp create mode 100644 src/dci/private/ddcifileengine.cpp create mode 100644 src/dci/private/ddcifileengine_p.h create mode 100644 src/dconfig.cpp create mode 100644 src/dconfigfile.cpp create mode 100644 src/ddesktopentry.cpp create mode 100644 src/dsecurestring.cpp create mode 100644 src/dsgapplication.cpp create mode 100644 src/dsysinfo.cpp create mode 100644 src/dtkcore_global.cpp create mode 100644 src/filesystem/dbasefilewatcher.cpp create mode 100644 src/filesystem/dcapfile.cpp create mode 100644 src/filesystem/dcapfsfileengine.cpp create mode 100644 src/filesystem/dcapmanager.cpp create mode 100644 src/filesystem/dfilesystemwatcher_dummy.cpp create mode 100644 src/filesystem/dfilesystemwatcher_linux.cpp create mode 100644 src/filesystem/dfilesystemwatcher_win.cpp create mode 100644 src/filesystem/dfilewatcher.cpp create mode 100644 src/filesystem/dfilewatchermanager.cpp create mode 100644 src/filesystem/dpathbuf.cpp create mode 100644 src/filesystem/dstandardpaths.cpp create mode 100644 src/filesystem/dtrashmanager_dummy.cpp create mode 100644 src/filesystem/dtrashmanager_linux.cpp create mode 100644 src/filesystem/filesystem.cmake create mode 100644 src/filesystem/private/dbasefilewatcher_p.h create mode 100644 src/filesystem/private/dcapfsfileengine_p.h create mode 100644 src/filesystem/private/dfilesystemwatcher_dummy_p.h create mode 100644 src/filesystem/private/dfilesystemwatcher_linux_p.h create mode 100644 src/filesystem/private/dfilesystemwatcher_win_p.h create mode 100644 src/filesystem/private/private.pri create mode 100644 src/glob.cmake create mode 100644 src/log/AbstractAppender.cpp create mode 100644 src/log/AbstractStringAppender.cpp create mode 100644 src/log/ConsoleAppender.cpp create mode 100644 src/log/FileAppender.cpp create mode 100644 src/log/LogManager.cpp create mode 100644 src/log/Logger.cpp create mode 100644 src/log/OutputDebugAppender.cpp create mode 100644 src/log/README.md create mode 100644 src/log/RollingFileAppender.cpp create mode 100644 src/log/log.cmake create mode 100644 src/settings/backend/dsettingsdconfigbackend.cpp create mode 100644 src/settings/backend/gsettingsbackend.cpp create mode 100644 src/settings/backend/qsettingbackend.cpp create mode 100644 src/settings/dsettings.cpp create mode 100644 src/settings/dsettingsgroup.cpp create mode 100644 src/settings/dsettingsoption.cpp create mode 100644 src/settings/settings.cmake create mode 100644 src/util/README.dpinyin create mode 100644 src/util/dabstractunitformatter.cpp create mode 100644 src/util/ddbusextendedabstractinterface.cpp create mode 100644 src/util/ddbusextendedpendingcallwatcher.cpp create mode 100644 src/util/ddbusextendedpendingcallwatcher_p.h create mode 100644 src/util/ddbusinterface.cpp create mode 100644 src/util/ddbusinterface_p.h create mode 100644 src/util/ddbussender.cpp create mode 100644 src/util/ddisksizeformatter.cpp create mode 100644 src/util/dexportedinterface.cpp create mode 100644 src/util/dfileservices_dummy.cpp create mode 100644 src/util/dfileservices_linux.cpp create mode 100644 src/util/dnotifysender.cpp create mode 100644 src/util/dpinyin.cpp create mode 100644 src/util/drecentmanager.cpp create mode 100644 src/util/dthreadutils.cpp create mode 100644 src/util/dtimedloop.cpp create mode 100644 src/util/dtimeunitformatter.cpp create mode 100644 src/util/dvtablehook.cpp create mode 100644 src/util/resources/dpinyin.dict create mode 100644 src/util/util.cmake create mode 100644 src/util/util.qrc create mode 100644 tests/CMakeLists.txt create mode 100644 tests/data.qrc create mode 100755 tests/data/dconf-example.meta.json create mode 100644 tests/data/dconf-example.override.json create mode 100755 tests/data/dconf-example_other_app_configure.meta.json create mode 100755 tests/data/dconf-global.meta.json create mode 100755 tests/data/dconf-global.override.json create mode 100755 tests/data/dconf-override/dconf-example.override.a.b.json create mode 100755 tests/data/dconf-override/dconf-example.override.a.json create mode 100644 tests/data/dt-settings.json create mode 100644 tests/main.cpp create mode 100755 tests/test-recoverage.sh create mode 100644 tests/test_helper.hpp create mode 100644 tests/ut_dasync.cpp create mode 100644 tests/ut_dcapfile.cpp create mode 100644 tests/ut_dconfig.cpp create mode 100644 tests/ut_dconfigfile.cpp create mode 100644 tests/ut_ddci.cpp create mode 100644 tests/ut_ddesktopentrytest.cpp create mode 100644 tests/ut_ddisksizeformatter.cpp create mode 100644 tests/ut_dfilesystemwatcher.cpp create mode 100644 tests/ut_dfilewatcher.cpp create mode 100644 tests/ut_dfilewatchermanager.cpp create mode 100644 tests/ut_dpathbuf.cpp create mode 100644 tests/ut_dpinyin.cpp create mode 100644 tests/ut_drecentmanager.cpp create mode 100644 tests/ut_dsecurestring.cpp create mode 100644 tests/ut_dsettings.cpp create mode 100644 tests/ut_dstandardpaths.cpp create mode 100644 tests/ut_dthreadutils.cpp create mode 100644 tests/ut_dtimeunitformatter.cpp create mode 100644 tests/ut_dtrashmanager.cpp create mode 100644 tests/ut_dutil.cpp create mode 100644 tests/ut_dutil.h create mode 100644 tests/ut_dvtablehook.cpp create mode 100644 tests/ut_gsettingsbackend.cpp create mode 100644 tests/ut_logger.cpp create mode 100644 tests/ut_qsettingsbackend.cpp create mode 100644 tests/ut_singleton.cpp create mode 100644 tests/ut_singleton.h create mode 100644 tools/CMakeLists.txt create mode 100644 tools/dci/CMakeLists.txt create mode 100644 tools/dci/dci.pro create mode 100644 tools/dci/main.cpp create mode 100644 tools/deepin-os-release/CMakeLists.txt create mode 100644 tools/deepin-os-release/deepin-os-release.pro create mode 100644 tools/deepin-os-release/main.cpp create mode 100644 tools/qdbusxml2cpp/CMakeLists.txt create mode 100644 tools/qdbusxml2cpp/README create mode 100644 tools/qdbusxml2cpp/qdbusxml2cpp.cpp create mode 100644 tools/qdbusxml2cpp/qdbusxml2cpp.pro create mode 100644 tools/settings/CMakeLists.txt create mode 100644 tools/settings/main.cpp create mode 100644 tools/settings/settings.pro diff --git a/.clog.toml b/.clog.toml new file mode 100644 index 0000000..646b8fe --- /dev/null +++ b/.clog.toml @@ -0,0 +1,5 @@ +[clog] +repository = "https://github.com/linuxdeepin/dtkcore" +from-latest-tag = true + +changelog = "CHANGELOG.md" diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..0086358 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: true diff --git a/.github/ISSUE_TEMPLATE/empty-issue.md b/.github/ISSUE_TEMPLATE/empty-issue.md new file mode 100644 index 0000000..84240c8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/empty-issue.md @@ -0,0 +1,8 @@ +--- +name: Empty issue +about: File a Empty issue +title: '' +labels: '' +assignees: '' + +--- diff --git a/.github/ISSUE_TEMPLATE/unit-test-report.md b/.github/ISSUE_TEMPLATE/unit-test-report.md new file mode 100644 index 0000000..9a6743c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/unit-test-report.md @@ -0,0 +1,12 @@ +--- +name: Unit test report +about: File a unit test report. +title: 'Test: [class name]' +labels: '' +assignees: '' + +--- + +Path: [class file path] +Interface: [class interface name] + diff --git a/.github/workflows/backup-to-gitlab.yml b/.github/workflows/backup-to-gitlab.yml new file mode 100644 index 0000000..c176335 --- /dev/null +++ b/.github/workflows/backup-to-gitlab.yml @@ -0,0 +1,17 @@ +name: backup to gitlab +on: [push] + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +jobs: + backup-to-gitlabwh: + uses: linuxdeepin/.github/.github/workflows/backup-to-gitlabwh.yml@master + secrets: + BRIDGETOKEN: ${{ secrets.BRIDGETOKEN }} + + backup-to-gitee: + uses: linuxdeepin/.github/.github/workflows/backup-to-gitee.yml@master + secrets: + GITEE_SYNC_TOKEN: ${{ secrets.GITEE_SYNC_TOKEN }} diff --git a/.github/workflows/call-auto-tag.yml b/.github/workflows/call-auto-tag.yml new file mode 100644 index 0000000..5018e5b --- /dev/null +++ b/.github/workflows/call-auto-tag.yml @@ -0,0 +1,16 @@ +name: auto tag + +on: + pull_request_target: + types: [opened, synchronize, closed] + paths: + - "debian/changelog" + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + auto_tag: + uses: linuxdeepin/.github/.github/workflows/auto-tag.yml@master + secrets: inherit diff --git a/.github/workflows/call-build-deb.yml b/.github/workflows/call-build-deb.yml new file mode 100644 index 0000000..fd67cbe --- /dev/null +++ b/.github/workflows/call-build-deb.yml @@ -0,0 +1,17 @@ +name: Call build-deb +on: + pull_request_target: + paths-ignore: + - ".github/workflows/**" + types: [ opened, closed, synchronize ] + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + check_job: + if: github.event.action != 'closed' || github.event.pull_request.merged + uses: linuxdeepin/.github/.github/workflows/build-deb.yml@master + secrets: + BridgeToken: ${{ secrets.BridgeToken }} diff --git a/.github/workflows/call-build-distribution.yml b/.github/workflows/call-build-distribution.yml new file mode 100644 index 0000000..c4c277e --- /dev/null +++ b/.github/workflows/call-build-distribution.yml @@ -0,0 +1,17 @@ +name: Call build-distribution +on: + push: + paths-ignore: + - ".github/workflows/**" + pull_request_target: + paths-ignore: + - ".github/workflows/**" + +jobs: + check_job: + uses: linuxdeepin/.github/.github/workflows/build-distribution.yml@master + secrets: + BUILD_GPG_PRIVATE_KEY: ${{ secrets.BUILD_GPG_PRIVATE_KEY }} + BUILD_SSH_PRIVATE_KEY: ${{ secrets.BUILD_SSH_PRIVATE_KEY }} + WEBDAV_PASSWD: ${{ secrets.WEBDAV_PASSWD }} + WEBDAV_USER: ${{ secrets.WEBDAV_USER }} diff --git a/.github/workflows/call-chatOps.yml b/.github/workflows/call-chatOps.yml new file mode 100644 index 0000000..0eb0b7b --- /dev/null +++ b/.github/workflows/call-chatOps.yml @@ -0,0 +1,10 @@ +name: chatOps +on: + issue_comment: + types: [created] + +jobs: + chatopt: + uses: linuxdeepin/.github/.github/workflows/chatOps.yml@master + secrets: + APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }} diff --git a/.github/workflows/call-clacheck.yml b/.github/workflows/call-clacheck.yml new file mode 100644 index 0000000..3fa07a3 --- /dev/null +++ b/.github/workflows/call-clacheck.yml @@ -0,0 +1,16 @@ +name: Call CLA check +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + clacheck: + uses: linuxdeepin/.github/.github/workflows/cla-check.yml@master + secrets: + APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }} diff --git a/.github/workflows/call-commitlint.yml b/.github/workflows/call-commitlint.yml new file mode 100644 index 0000000..69f5b2d --- /dev/null +++ b/.github/workflows/call-commitlint.yml @@ -0,0 +1,11 @@ +name: Call commitlint +on: + pull_request_target: + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + check_job: + uses: linuxdeepin/.github/.github/workflows/commitlint.yml@master diff --git a/.github/workflows/call-doc-check.yml b/.github/workflows/call-doc-check.yml new file mode 100644 index 0000000..4779992 --- /dev/null +++ b/.github/workflows/call-doc-check.yml @@ -0,0 +1,15 @@ +name: doxygen-check +on: + pull_request_target: + paths-ignore: + - ".github/workflows/**" + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + check_job: + uses: linuxdeepin/.github/.github/workflows/doc-check.yml@master + secrets: + APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }} diff --git a/.github/workflows/call-license-check.yml b/.github/workflows/call-license-check.yml new file mode 100644 index 0000000..347d556 --- /dev/null +++ b/.github/workflows/call-license-check.yml @@ -0,0 +1,16 @@ +name: Call License and README Check +on: + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + pull-requests: write + contents: read + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + license-check: + uses: linuxdeepin/.github/.github/workflows/license-check.yml@master diff --git a/.github/workflows/call-tag-build.yml b/.github/workflows/call-tag-build.yml new file mode 100644 index 0000000..3b1850a --- /dev/null +++ b/.github/workflows/call-tag-build.yml @@ -0,0 +1,13 @@ +name: tag build +on: + push: + tags: "*" + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +jobs: + build: + uses: linuxdeepin/.github/.github/workflows/build-tag.yml@master + secrets: inherit diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml new file mode 100644 index 0000000..6a92b54 --- /dev/null +++ b/.github/workflows/cppcheck.yml @@ -0,0 +1,26 @@ +name: cppcheck +on: + pull_request_target: + paths-ignore: + - ".github/workflows/**" + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + cppchceck: + name: cppcheck + runs-on: ubuntu-latest + steps: + - run: export + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + - uses: linuxdeepin/action-cppcheck@main + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + repository: ${{ github.repository }} + pull_request_id: ${{ github.event.pull_request.number }} + allow_approve: false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3325f3c --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Compiled Object files +*.slo +*.lo +*.o + + +# Compiled Dynamic libraries +*.so +*.dylib + +# Compiled Static libraries +*.lai +*.la +*.a + +build*/ +*.pro.user* +*.DS_Store + +# executeable files +*.qm + +src/DtkCores +src/dtkcore_config.h +cmake/DtkCore/DtkCoreConfig.cmake +src/qt_lib_d*.pri + +bin/ +.qmake* +Makefile + +cmake/DtkCore* +.cache +DtkCoreConfig.cmake +dtkcore.pc +qt_lib_dtkcore.pri +dtkcore_config.h +CMakeLists.txt.user diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..6da6602 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,5 @@ +include: + - remote: 'https://gitlab.deepin.io/dev-tools/letmeci/raw/master/gitlab-ci/dde.yml' +variables: + CPPCHECK: "true" + CODESPELL: "true" diff --git a/.packit.yaml b/.packit.yaml new file mode 100644 index 0000000..307fc7c --- /dev/null +++ b/.packit.yaml @@ -0,0 +1,17 @@ +# See the documentation for more information: +# https://packit.dev/docs/configuration/ + +specfile_path: rpm/dtkcore.spec + +# add or remove files that should be synced +synced_files: + - rpm/dtkcore.spec + - .packit.yaml + +upstream_package_name: dtkcore +# downstream (Fedora) RPM package name +downstream_package_name: dtkcore + +actions: + fix-spec-file: | + bash -c "sed -i -r \"0,/Version:/ s/Version:(\s*)\S*/Version:\1${PACKIT_PROJECT_VERSION}/\" rpm/dtkcore.spec" diff --git a/.project.json b/.project.json new file mode 100644 index 0000000..778d07c --- /dev/null +++ b/.project.json @@ -0,0 +1,6 @@ +{ + "Type": "homebrew", + "3rdparty": ["tools/qdbusxml2cpp"], + "ignore": ["src/translations","src/widgets/assets","doc",".tx"], + "license": ["GPLv3"] +} diff --git a/.release.json b/.release.json new file mode 100644 index 0000000..169c9a8 --- /dev/null +++ b/.release.json @@ -0,0 +1,16 @@ +{ + "commit": { + "quilt": false, + "pkgver": "echo $(git tag | sort -V | tail -n1)'+r'$(git log $(git describe --abbrev=0 --tags)..HEAD --oneline|wc -l)'+g'$(git rev-parse --short HEAD);", + "dist": "experimental" + }, + "release": { + "quilt": true, + "pkgver": "git describe --abbrev=0 --tags %(ref)s", + "dist": "unstable" + }, + "release-candidate": { + "quilt": true, + "dist": "unstable" + } +} diff --git a/.reuse/dep5 b/.reuse/dep5 new file mode 100644 index 0000000..2f81f1d --- /dev/null +++ b/.reuse/dep5 @@ -0,0 +1,75 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: dtkcore +Upstream-Contact: UnionTech Software Technology Co., Ltd. <> +Source: https://github.com/linuxdeepin/dtkcore + +# Sample paragraph, commented out: +# +# Files: src/* +# Copyright: $YEAR $NAME <$CONTACT> +# License: ... + +# ci +Files: .github/* *.yml *.yaml +Copyright: None +License: CC0-1.0 + +# git +Files: .gitignore +Copyright: None +License: CC0-1.0 + +# config +Files: *.toml *.conf *.json +Copyright: None +License: CC0-1.0 + +# qt +Files: *.pro *.pri *.qrc +Copyright: None +License: CC0-1.0 + +# cmake +Files: *.cmake *CMakeLists.txt *.pc.in +Copyright: None +License: CC0-1.0 + +# debian +Files: debian/* +Copyright: None +License: CC0-1.0 + +# Arch +Files: archlinux/* +Copyright: None +License: CC0-1.0 + +# rpm +Files: rpm/* +Copyright: None +License: CC0-1.0 + +# docs +Files: *.dox *.md src/util/README.dpinyin tools/qdbusxml2cpp/README +Copyright: UnionTech Software Technology Co., Ltd. +License: CC-BY-4.0 + +# interface +Files: include/DtkCore/D* +Copyright: None +License: CC0-1.0 + +# others +Files: src/util/resources/dpinyin.dict +Copyright: None +License: CC0-1.0 + + +Files: src/log/* +Copyright: UnionTech Software Technology Co., Ltd. +License: LGPL-3.0-or-later + +#doxygentheme +Files: docs/doxygentheme/* +Copyright: Copyright (c) 2021 - 2022 jothepro +License: MIT \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..15fbd7f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,266 @@ + +## 2.0.14 (2019-05-23) + +#### Bug Fixes + +* **DSettings:** crash when calling getOption() if option doesn't exist ([90ac734b](https://github.com/linuxdeepin/dtkcore/commit/90ac734b872203ea698808a7197aa7a9c7e2b5bd)) + + + + +## 2.0.12 (2019-04-18) + + + + + +## 2.0.11 (2019-04-17) + + +#### Bug Fixes + +* Cross-builds incorrectly, built packages contain paths from build architecture ([8d32577a](https://github.com/linuxdeepin/dtkcore/commit/8d32577a89e54b5c9c834caae83d98e50f59df77)) +* https://github.com/linuxdeepin/dtkcore/issues/10 ([3f99873a](https://github.com/linuxdeepin/dtkcore/commit/3f99873a786f6830688ecd0d8d2e2bf8dfb63ce0)) + + + + +## 2.0.10 (2019-03-27) + + +#### Bug Fixes + +* crash at application ([d852a218](https://github.com/linuxdeepin/dtkcore/commit/d852a21811f9f86e04274fc9f732d7c7a210ef3f)) + +#### Features + +* add DNotifySender ([89bbcd7c](https://github.com/linuxdeepin/dtkcore/commit/89bbcd7c3821985bb2bca51247394fd4a65b25bf)) + + + + +## 2.0.9.17 (2019-02-26) + + + + + +## 2.0.9.16 (2019-02-26) + + +#### Bug Fixes + +* deepin-os-release support cpu model and other info query ([cbeb47c9](https://github.com/linuxdeepin/dtkcore/commit/cbeb47c97e31d2b5dd3c198c60ee74332fecb293)) + + + + +## 2.0.9.15 (2019-01-25) + + +#### Bug Fixes + +* failed build the deepin-os-release on Qt 5.7.1 ([8bae8654](https://github.com/linuxdeepin/dtkcore/commit/8bae8654bdb20a7f773130d22b9db139460ba575)) +* use main project c/cxx/ld flags on build deepin-os-release ([86dbd507](https://github.com/linuxdeepin/dtkcore/commit/86dbd507c1b3b101c1816f091782430ec1ce20ce)) + + + + +## 2.0.9.14 (2019-01-02) + + + + + +## 2.0.9.13 (2018-12-28) + + + + + +## 2.0.9.12 (2018-12-24) + + +#### Bug Fixes + +* **DPathBuf:** missing default constructor ([74374cb4](https://github.com/linuxdeepin/dtkcore/commit/74374cb4cf0245ab1fe73f62fe0d13566f945db3)) + +#### Features + +* support connan build ([ba2d213f](https://github.com/linuxdeepin/dtkcore/commit/ba2d213fd6c7e36e118288305e5892c339250623)) + + + + +## 2.0.9.11 (2018-12-14) + + + + + +## 2.0.9.10 (2018-12-05) + + +#### Bug Fixes + +* include unistd.h instead of sys/unistd.h ([39c50a13](https://github.com/linuxdeepin/dtkcore/commit/39c50a1398c34123e3806a3060a4c64e7f45ed68)) +* url encoding ([4a6b7b61](https://github.com/linuxdeepin/dtkcore/commit/4a6b7b61bb3ad9ab417eda69249b5e9aced0aa97)) + + + + +## 2.0.9.9 (2018-11-19) + + +#### Features + +* add DRecentManager class. ([a2defafd](https://github.com/linuxdeepin/dtkcore/commit/a2defafdcf57078461221c665e322287a43d24a8)) + +#### Bug Fixes + +* compatibility with Qt 5.6 ([0ec7f3ce](https://github.com/linuxdeepin/dtkcore/commit/0ec7f3ce389b323ecb2b103801c1cd1d55f100fa)) +* **drecentmanager:** + * xbel file does not exist. ([c57ffe71](https://github.com/linuxdeepin/dtkcore/commit/c57ffe714f26b1a8a8859e2ffbeeed3d75ee11a1)) + * uniform url format. ([413a8988](https://github.com/linuxdeepin/dtkcore/commit/413a8988116708ab8bcf9efbb9bc8f52e048efa5)) + * url encoded. ([e234a8cc](https://github.com/linuxdeepin/dtkcore/commit/e234a8cc5ad9d2c14a16950838115c4f2f27c605)) +* **recent:** chinese doc ([fb256461](https://github.com/linuxdeepin/dtkcore/commit/fb256461d1bdb0862b1a3a129978fc3932a6bcab)) + + + + +## 2.0.9.8 (2018-11-09) + + +#### Bug Fixes + +* can't get correct disk size in some case ([20a12b62](https://github.com/linuxdeepin/dtkcore/commit/20a12b622ea7b01f0616c15a8af85e31fc2d36cb)) + + + + +## 2.0.9.5 (2018-10-26) + + +#### Features + +* update version number for expermimental ([02b5d5c1](https://github.com/linuxdeepin/dtkcore/commit/02b5d5c1e01a05f57651b774b02cae31ef9a549f)) + + + + +## 2.0.9 (2018-07-20) + + +#### Bug Fixes + +* remove qt symbols ([57ec78ba](https://github.com/linuxdeepin/dtkcore/commit/57ec78ba685a53692b0260d3d558d8b0915fc3e4)) +* non array type value is wrong on parse josn file ([9f138664](https://github.com/linuxdeepin/dtkcore/commit/9f13866439d8d650893434594da023e7d331d866)) + + + + +### 2.0.8.1 (2018-05-14) + + +#### Bug Fixes + +* update symbols ([f6c53cc4](https://github.com/linuxdeepin/dtkcore/commit/f6c53cc493c1bcf55dca54dbf500e2e484af73c9)) +* add LIBDTKCORESHARED_EXPORT for windows ([6fb1096f](https://github.com/linuxdeepin/dtkcore/commit/6fb1096f6d0784937cf84f0e4ae1f5f7587085e5)) +* **changelog:** update email format ([cb09a0ca](https://github.com/linuxdeepin/dtkcore/commit/cb09a0cadcf2fa0ba271b1d98d3b96a993eb892b)) + + + + +## 2.0.8 (2018-05-02) + + +#### Features + +* add symbols ([048de455](https://github.com/linuxdeepin/dtkcore/commit/048de4551bdd770aca5e9c12798362f913061654)) + + + + +## 2.0.7 (2018-03-01) + + +#### Bug Fixes + +* cmake link depends ([cdfcff9e](https://github.com/linuxdeepin/dtkcore/commit/cdfcff9e2f3e92bc6dbb45644d2714d6c4dbdda0)) +* better static lib support ([99886406](https://github.com/linuxdeepin/dtkcore/commit/99886406a0cae849fad23286fdf64bb399e37da0)) +* read settings value failed ([cf1c7698](https://github.com/linuxdeepin/dtkcore/commit/cf1c769893773794dff5a67c235c5d1f3234541a)) +* set default should not use ([146529f6](https://github.com/linuxdeepin/dtkcore/commit/146529f6887e798606f2bf763ab8a760969bff26)) +* fix dtk-settings install path ([1893cff3](https://github.com/linuxdeepin/dtkcore/commit/1893cff301dacb546a246a4f824dab68eac51351)) +* develop package no install the "version.pri" file ([5667b562](https://github.com/linuxdeepin/dtkcore/commit/5667b562630565fca5abed690f3d3478dd3c7603)) +* awk script failed ([524a3fa6](https://github.com/linuxdeepin/dtkcore/commit/524a3fa6021ee54db416503520aea65ef0e2c3a0)) +* set default build version for debian changelog ([ec6e2a83](https://github.com/linuxdeepin/dtkcore/commit/ec6e2a8376c7aca7162b4fbb782b998c9a6ab630)) +* set its value only if VERSION is empty ([1836000c](https://github.com/linuxdeepin/dtkcore/commit/1836000c49eb149a6495322c4cbb1474d5d48204)) + +#### Features + +* add hide support for group ([e7e4fb66](https://github.com/linuxdeepin/dtkcore/commit/e7e4fb669276fbce61c6378e74ae82573e7c0313)) +* add get option interface ([d8682485](https://github.com/linuxdeepin/dtkcore/commit/d8682485a6737da83fb28f22335f1da1afb8956c)) +* add group interface for DSettingsGroup ([c876180f](https://github.com/linuxdeepin/dtkcore/commit/c876180f535e3027dce63628f31379ef874367ed)) +* support generate cmake with qt function ([524b0559](https://github.com/linuxdeepin/dtkcore/commit/524b055929b7be84375a45f9d10cbc3a0ecac6de)) +* config pkg config with dtk_module ([137b9138](https://github.com/linuxdeepin/dtkcore/commit/137b91388d9b9db24c8136dd4e2c6e690a5712c5)) +* support qt module ([17ca0de9](https://github.com/linuxdeepin/dtkcore/commit/17ca0de9156a320cea32208dcff2f8cdf7d6a237)) +* add the "version.pri" file ([07aab9fd](https://github.com/linuxdeepin/dtkcore/commit/07aab9fd6478c83c7bae1062f64b4bd20b21869c)) +* remove build version from install path ([3bf0bfb5](https://github.com/linuxdeepin/dtkcore/commit/3bf0bfb5f49c3e83d4c36cc33f219150bf3731d8)) +* make version parser easier ([6d3b4ead](https://github.com/linuxdeepin/dtkcore/commit/6d3b4ead7080158d1d8977bf7cf99ae842e574ec)) +* set verion when build ([9083dbd3](https://github.com/linuxdeepin/dtkcore/commit/9083dbd3e29bf9d06b1032901ba13848fa964f4c)) +* add .qmake.conf file ([2890f643](https://github.com/linuxdeepin/dtkcore/commit/2890f643a57c3532ab623410f7c6c6dbfdd6788d)) +* add DtkCore and dtkcore_config.h headers ([308a0cc4](https://github.com/linuxdeepin/dtkcore/commit/308a0cc41101499c04308b4ef3bb2fff4ab8d783)) +* **DSettings:** support set default value ([5fe9bfd0](https://github.com/linuxdeepin/dtkcore/commit/5fe9bfd0a5e20cef7393639712302825b803db29)) + + + + +## 2.0.6 (2018-01-15) + + + + + +## 2.0.5.3 (2017-12-27) + + +#### Bug Fixes + +* Adapt lintian ([27df15df](https://github.com/linuxdeepin/dtkcore/commit/27df15df32788002491a24f06f098a5f849a4988)) +* break forever loop for syncing backend data ([f70e500e](https://github.com/linuxdeepin/dtkcore/commit/f70e500ec2fd5c751e40833bdc4df586614bcff2)) + +#### Features + +* **util:** add dpinyin ([128d7d67](https://github.com/linuxdeepin/dtkcore/commit/128d7d678e921bc580dd732b14a454973397899c)) + + + + +## 2.0.5.2 (2017-11-28) + + +#### Bug Fixes + +* make macosx build success ([af04bbe1](https://github.com/linuxdeepin/dtkcore/commit/af04bbe193a4b4251908f830d927ebdc8f4459e7)) +* windows build failed ([66c4c812](https://github.com/linuxdeepin/dtkcore/commit/66c4c812eb29634710642f4e9d6b3d69cc692cb2)) + +#### Features + +* add macro D_DECL_DEPRECATED ([89e49868](https://github.com/linuxdeepin/dtkcore/commit/89e49868f113ef01c03bcf5b6846eec95c428382)) + + + + +## 2.0.5 (2017-11-06) + + +#### Bug Fixes + +* build failed on used dbasefilewatcher.h project ([34fbe4b3](34fbe4b3)) +* add miss libgsettings-qt-dev ([f61c1b54](f61c1b54)) +* not select python version ([7e7e8832](7e7e8832)) + +#### Features + +* support gsettingsbackend, remove dsettings-key ([26a29800](26a29800)) +* create gsettingsbackend ([b94b97b1](b94b97b1)) diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ea82a74 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required (VERSION 3.10) + +set (DVERSION "5.6.2" CACHE STRING "define project version") + +project (DtkCore + VERSION ${DVERSION} + DESCRIPTION "DTK Core module" + HOMEPAGE_URL "https://github.com/linuxdeepin/dtkcore" + LANGUAGES CXX C +) +message(STATUS ${PROJECT_VERSION}) + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX /usr) +endif () +set (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}/libdtk-${CMAKE_PROJECT_VERSION}/DCore") +set (TOOL_INSTALL_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/libdtk-${PROJECT_VERSION}/DCore/bin") +set (LIBRARY_INSTALL_DIR "${CMAKE_INSTALL_FULL_LIBDIR}") +set (MKSPECS_INSTALL_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/qt5/mkspecs/modules" CACHE STRING "INSTALL DIR FOR qt pri files") + +set (BUILD_EXAMPLES ON CACHE BOOL "Build examples") +option(BUILD_VERSION "buildversion" "0") +if(NOT BUILD_VERSION) + set(BUILD_VERSION "0") +endif() + +if(UNIX AND NOT APPLE) + set(LINUX TRUE) +endif() +set (BUILD_DOCS ON CACHE BOOL "Generate doxygen-based documentation") + +# CXX FILAGS +set(CMAKE_CXX_STANDARD 11) +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +if(NOT MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Wextra") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed") + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(BUILD_TESTING ON) + endif () + string(REPLACE "-O3" "-Ofast" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) +endif() + +if (BUILD_DOCS) + add_subdirectory(docs) +endif () + +add_subdirectory(src) +if(BUILD_TESTING) + message("==================================") + message(" Now Testing is enabled ") + message("==================================") + enable_testing() + add_subdirectory(tests) +endif() +if(BUILD_EXAMPLES) + message("===================================") + message("You can build and run examples now ") + message("===================================") + add_subdirectory(examples) +endif() +add_subdirectory(tools) +install(FILES cmake/DtkCMake/DtkCMakeConfig.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkCMake/") +install(FILES cmake/DtkTools/DtkToolsConfig.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkTools") +install(FILES cmake/DtkTools/DtkSettingsToolsMacros.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkTools") + +configure_package_config_file(misc/DtkConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/DtkCoreConfig.cmake + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkCore" + PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR TOOL_INSTALL_DIR) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/DtkCoreConfigVersion.cmake" + VERSION ${DVERSION} + COMPATIBILITY SameMajorVersion +) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/DtkCoreConfig.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkCore") +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/DtkCoreConfigVersion.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkCore") + +configure_file(misc/dtkcore.pc.in dtkcore.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dtkcore.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + +configure_file(misc/qt_lib_dtkcore.pri.in qt_lib_dtkcore.pri @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qt_lib_dtkcore.pri DESTINATION "${MKSPECS_INSTALL_DIR}") +set(CONFIGNAME include/global/dtkcore_config.h) +file(WRITE ${CONFIGNAME} + "// it is auto make config\n" + "#define DTK_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}\n" + "#define DTK_VERSION_MINOR ${PROJECT_VERSION_MINOR}\n" + "#define DTK_VERSION_PATCH ${PROJECT_VERSION_PATCH}\n" + "#define DTK_VERSION_BUILD ${BUILD_VERSION}\n" + "#define DTK_VERSION_STR \"${PROJECT_VERSION}\"\n" + "\n" +) +file(GLOB CONFIGSOURCE include/DtkCore/*) + +foreach(FILENAME ${CONFIGSOURCE}) + get_filename_component(thefile ${FILENAME} NAME) + file(APPEND ${CONFIGNAME} "#define DTKCORE_CLASS_${thefile}\n") +endforeach() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..513d1c0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,304 @@ +GNU LESSER GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. + +0. Additional Definitions. + +As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. + +"The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. + +An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. + +A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". + +The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. + +The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. +You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. +If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: + + a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. + +3. Object Code Incorporating Material from Library Header Files. +The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license document. + +4. Combined Works. +You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license document. + + c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. + + e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) + +5. Combined Libraries. +You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +6. Revised Versions of the GNU Lesser General Public License. +The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. + +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . + +The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . diff --git a/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt new file mode 100644 index 0000000..13ca539 --- /dev/null +++ b/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,156 @@ +Creative Commons Attribution 4.0 International + + Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. + +Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. + +Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. + +Section 1 – Definitions. + + a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + + d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. + + g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. + + i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. + + 3. Term. The term of this Public License is specified in Section 6(a). + + 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. + + 5. Downstream recipients. + + A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. + + B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. + + 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). + +b. Other rights. + + 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this Public License. + + 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified form), You must: + + A. retain the following if it is supplied by the Licensor with the Licensed Material: + + i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + + v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + + B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and + + C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. + + 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; + + b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. +For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + + a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. + + b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. + + c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + + a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + + c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. + + d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. + + e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. + +Section 8 – Interpretation. + + a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. + + c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. + + d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/LICENSES/LGPL-2.1-or-later.txt b/LICENSES/LGPL-2.1-or-later.txt new file mode 100644 index 0000000..04bb156 --- /dev/null +++ b/LICENSES/LGPL-2.1-or-later.txt @@ -0,0 +1,468 @@ +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts as the +successor of the GNU Library Public License, version 2, hence the version +number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. + +This license, the Lesser General Public License, applies to some specially +designated software packages--typically libraries--of the Free Software Foundation +and other authors who decide to use it. You can use it too, but we suggest +you first think carefully about whether this license or the ordinary General +Public License is the better strategy to use in any particular case, based +on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. +Our General Public Licenses are designed to make sure that you have the freedom +to distribute copies of free software (and charge for this service if you +wish); that you receive source code or can get it if you want it; that you +can change the software and use pieces of it in new free programs; and that +you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors +to deny you these rights or to ask you to surrender these rights. These restrictions +translate to certain responsibilities for you if you distribute copies of +the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for +a fee, you must give the recipients all the rights that we gave you. You must +make sure that they, too, receive or can get the source code. If you link +other code with the library, you must provide complete object files to the +recipients, so that they can relink them with the library after making changes +to the library and recompiling it. And you must show them these terms so they +know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, +and (2) we offer you this license, which gives you legal permission to copy, +distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no +warranty for the free library. Also, if the library is modified by someone +else and passed on, the recipients should know that what they have is not +the original version, so that the original author's reputation will not be +affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free +program. We wish to make sure that a company cannot effectively restrict the +users of a free program by obtaining a restrictive license from a patent holder. +Therefore, we insist that any patent license obtained for a version of the +library must be consistent with the full freedom of use specified in this +license. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License. This license, the GNU Lesser General Public License, +applies to certain designated libraries, and is quite different from the ordinary +General Public License. We use this license for certain libraries in order +to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared +library, the combination of the two is legally speaking a combined work, a +derivative of the original library. The ordinary General Public License therefore +permits such linking only if the entire combination fits its criteria of freedom. +The Lesser General Public License permits more lax criteria for linking other +code with the library. + +We call this license the "Lesser" General Public License because it does Less +to protect the user's freedom than the ordinary General Public License. It +also provides other free software developers Less of an advantage over competing +non-free programs. These disadvantages are the reason we use the ordinary +General Public License for many libraries. However, the Lesser license provides +advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the +widest possible use of a certain library, so that it becomes a de-facto standard. +To achieve this, non-free programs must be allowed to use the library. A more +frequent case is that a free library does the same job as widely used non-free +libraries. In this case, there is little to gain by limiting the free library +to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs +enables a greater number of people to use a large body of free software. For +example, permission to use the GNU C Library in non-free programs enables +many more people to use the whole GNU operating system, as well as its variant, +the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' +freedom, it does ensure that the user of a program that is linked with the +Library has the freedom and the wherewithal to run that program using a modified +version of the Library. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code derived +from the library, whereas the latter must be combined with the library in +order to run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program +which contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Lesser General +Public License (also called "this License"). Each licensee is addressed as +"you". + +A "library" means a collection of software functions and/or data prepared +so as to be conveniently linked with application programs (which use some +of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has +been distributed under these terms. A "work based on the Library" means either +the Library or any derivative work under copyright law: that is to say, a +work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation +is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications +to it. For a library, complete source code means all the source code for all +modules it contains, plus any associated interface definition files, plus +the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running a program +using the Library is not restricted, and output from such a program is covered +only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends +on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and disclaimer +of warranty; keep intact all the notices that refer to this License and to +the absence of any warranty; and distribute a copy of this License along with +the Library. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, +thus forming a work based on the Library, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all +of these conditions: + + a) The modified work must itself be a software library. + +b) You must cause the files modified to carry prominent notices stating that +you changed the files and the date of any change. + +c) You must cause the whole of the work to be licensed at no charge to all +third parties under the terms of this License. + +d) If a facility in the modified Library refers to a function or a table of +data to be supplied by an application program that uses the facility, other +than as an argument passed when the facility is invoked, then you must make +a good faith effort to ensure that, in the event an application does not supply +such function or table, the facility still operates, and performs whatever +part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose +that is entirely well-defined independent of the application. Therefore, Subsection +2d requires that any application-supplied function or table used by this function +must be optional: if the application does not supply it, the square root function +must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Library, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Library, the distribution of the whole must be +on the terms of this License, whose permissions for other licensees extend +to the entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise +the right to control the distribution of derivative or collective works based +on the Library. + +In addition, mere aggregation of another work not based on the Library with +the Library (or with a work based on the Library) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. + +3. You may opt to apply the terms of the ordinary GNU General Public License +instead of this License to a given copy of the Library. To do this, you must +alter all the notices that refer to this License, so that they refer to the +ordinary GNU General Public License, version 2, instead of to this License. +(If a newer version than version 2 of the ordinary GNU General Public License +has appeared, then you can specify that version instead if you wish.) Do not +make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, +so the ordinary GNU General Public License applies to all subsequent copies +and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library +into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of +it, under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you accompany it with the complete corresponding +machine-readable source code, which must be distributed under the terms of +Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated +place, then offering equivalent access to copy the source code from the same +place satisfies the requirement to distribute the source code, even though +third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but +is designed to work with the Library by being compiled or linked with it, +is called a "work that uses the Library". Such a work, in isolation, is not +a derivative work of the Library, and therefore falls outside the scope of +this License. + +However, linking a "work that uses the Library" with the Library creates an +executable that is a derivative of the Library (because it contains portions +of the Library), rather than a "work that uses the library". The executable +is therefore covered by this License. Section 6 states terms for distribution +of such executables. + +When a "work that uses the Library" uses material from a header file that +is part of the Library, the object code for the work may be a derivative work +of the Library even though the source code is not. Whether this is true is +especially significant if the work can be linked without the Library, or if +the work is itself a library. The threshold for this to be true is not precisely +defined by law. + +If such an object file uses only numerical parameters, data structure layouts +and accessors, and small macros and small inline functions (ten lines or less +in length), then the use of the object file is unrestricted, regardless of +whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute +the object code for the work under the terms of Section 6. Any executables +containing that work also fall under Section 6, whether or not they are linked +directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work +that uses the Library" with the Library to produce a work containing portions +of the Library, and distribute that work under terms of your choice, provided +that the terms permit modification of the work for the customer's own use +and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library +is used in it and that the Library and its use are covered by this License. +You must supply a copy of this License. If the work during execution displays +copyright notices, you must include the copyright notice for the Library among +them, as well as a reference directing the user to the copy of this License. +Also, you must do one of these things: + +a) Accompany the work with the complete corresponding machine-readable source +code for the Library including whatever changes were used in the work (which +must be distributed under Sections 1 and 2 above); and, if the work is an +executable linked with the Library, with the complete machine-readable "work +that uses the Library", as object code and/or source code, so that the user +can modify the Library and then relink to produce a modified executable containing +the modified Library. (It is understood that the user who changes the contents +of definitions files in the Library will not necessarily be able to recompile +the application to use the modified definitions.) + +b) Use a suitable shared library mechanism for linking with the Library. A +suitable mechanism is one that (1) uses at run time a copy of the library +already present on the user's computer system, rather than copying library +functions into the executable, and (2) will operate properly with a modified +version of the library, if the user installs one, as long as the modified +version is interface-compatible with the version that the work was made with. + +c) Accompany the work with a written offer, valid for at least three years, +to give the same user the materials specified in Subsection 6a, above, for +a charge no more than the cost of performing this distribution. + +d) If distribution of the work is made by offering access to copy from a designated +place, offer equivalent access to copy the above specified materials from +the same place. + +e) Verify that the user has already received a copy of these materials or +that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must +include any data and utility programs needed for reproducing the executable +from it. However, as a special exception, the materials to be distributed +need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component itself +accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of +other proprietary libraries that do not normally accompany the operating system. +Such a contradiction means you cannot use both them and the Library together +in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side +in a single library together with other library facilities not covered by +this License, and distribute such a combined library, provided that the separate +distribution of the work based on the Library and of the other library facilities +is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities. This must be distributed +under the terms of the Sections above. + +b) Give prominent notice with the combined library of the fact that part of +it is a work based on the Library, and explaining where to find the accompanying +uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library +except as expressly provided under this License. Any attempt otherwise to +copy, modify, sublicense, link with, or distribute the Library is void, and +will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not +have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed +it. However, nothing else grants you permission to modify or distribute the +Library or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License +to do so, and all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), +the recipient automatically receives a license from the original licensor +to copy, distribute, link with or modify the Library subject to these terms +and conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), conditions are imposed +on you (whether by court order, agreement or otherwise) that contradict the +conditions of this License, they do not excuse you from the conditions of +this License. If you cannot distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, then as +a consequence you may not distribute the Library at all. For example, if a +patent license would not permit royalty-free redistribution of the Library +by all those who receive copies directly or indirectly through you, then the +only way you could satisfy both it and this License would be to refrain entirely +from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents +or other property right claims or to contest validity of any such claims; +this section has the sole purpose of protecting the integrity of the free +software distribution system which is implemented by public license practices. +Many people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Library under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of +the Lesser General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to address +new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the +Library does not specify a license version number, you may choose any version +ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs +whose distribution conditions are incompatible with these, write to the author +to ask for permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make exceptions +for this. Our decision will be guided by the two goals of preserving the free +status of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY +"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE +OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA +OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES +OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH +HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible +use to the public, we recommend making it free software that everyone can +redistribute and change. You can do so by permitting redistribution under +these terms (or, alternatively, under the terms of the ordinary General Public +License). + +To apply these terms, attach the following notices to the library. It is safest +to attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + + + +Copyright (C) + +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 2.1 of the License, or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this library; if not, write to the Free Software Foundation, Inc., 51 +Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the library, if necessary. Here +is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in + +the library `Frob' (a library for tweaking knobs) written + +by James Random Hacker. + +< signature of Ty Coon > , 1 April 1990 + +Ty Coon, President of Vice + +That's all there is to it! diff --git a/LICENSES/LGPL-3.0-or-later.txt b/LICENSES/LGPL-3.0-or-later.txt new file mode 100644 index 0000000..513d1c0 --- /dev/null +++ b/LICENSES/LGPL-3.0-or-later.txt @@ -0,0 +1,304 @@ +GNU LESSER GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. + +0. Additional Definitions. + +As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. + +"The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. + +An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. + +A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". + +The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. + +The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. +You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. +If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: + + a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. + +3. Object Code Incorporating Material from Library Header Files. +The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license document. + +4. Combined Works. +You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license document. + + c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. + + e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) + +5. Combined Libraries. +You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +6. Revised Versions of the GNU Lesser General Public License. +The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. + +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . + +The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 0000000..2071b23 --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..484359d --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +## Deepin Tool Kit Core + +Deepin Tool Kit (DtkCore) is the base development tool of all C++/Qt Developer work on Deepin. + +You should read the Deepin Application Specification firstly. + +## Dependencies + +### Build dependencies + +* Qt >= 5.10 + +## Installation + +### Build from source code + +1. Make sure you have installed all dependencies. + +2. Build: + +```bash +mkdir build +cd build +cmake .. +make +``` + +3. Install: + +```bash +sudo make install +``` + +## Getting help + +Any usage issues can ask for help via + +* [Telegram group](https://t.me/deepin) +* [Matrix](https://matrix.to/#/#deepin-community:matrix.org) +* [IRC (libera.chat)](https://web.libera.chat/#deepin-community) +* [Forum](https://bbs.deepin.org) +* [WiKi](https://wiki.deepin.org/) + +## Getting involved + +We encourage you to report issues and contribute changes + +* [Contribution guide for developers](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers-en). + +## License + +deepin-tool-kit is licensed under [LGPL-3.0-or-later](LICENSE). diff --git a/README.zh_CN.md b/README.zh_CN.md new file mode 100644 index 0000000..f395e04 --- /dev/null +++ b/README.zh_CN.md @@ -0,0 +1,52 @@ +## Deepin Tool Kit Core + +Deepin Tool Kit Core(DtkCore) 是所有C++/Qt开发人员在Deepin上工作的基础开发工具. + +您应该首先阅读 Deepin应用程序规范. + +## 依赖 + +### 编译依赖 + +* Qt >= 5.10 + +## 安装 + +### 从源代码构建 + +1. 确保已经安装了所有的编译依赖. + +2. 构建: + +```bash +mkdir build +cd build +cmake .. +make +``` + +3. 安装: + +```bash +sudo make install +``` + +## 帮助 + +任何使用问题都可以通过以下方式寻求帮助: + +* [Telegram 群组](https://t.me/deepin) +* [Matrix](https://matrix.to/#/#deepin-community:matrix.org) +* [IRC (libera.chat)](https://web.libera.chat/#deepin-community) +* [Forum](https://bbs.deepin.org) +* [WiKi](https://wiki.deepin.org/) + +## 参与贡献 + +我们鼓励您报告问题并作出更改 + +* [开发者代码贡献指南](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers) + +## 协议 + +DTK工具包遵循协议 [LGPL-3.0-or-later](LICENSE). diff --git a/archlinux/PKGBUILD b/archlinux/PKGBUILD new file mode 100644 index 0000000..5ee33de --- /dev/null +++ b/archlinux/PKGBUILD @@ -0,0 +1,33 @@ +# Maintainer: justforlxz +pkgname=dtkcore-git +pkgver=5.5.23.r5.g74f86b0 +pkgrel=1 +pkgdesc='DTK core modules' +arch=('x86_64' 'aarch64') +url="https://github.com/linuxdeepin/dtkcore" +license=('LGPL3') +depends=('dconf' 'deepin-desktop-base-git' 'python' 'gsettings-qt' 'lshw') +makedepends=('git' 'qt5-tools' 'dtkcommon-git' 'ninja' 'cmake' 'doxygen') +conflicts=('dtkcore') +provides=('dtkcore') +groups=('deepin-git') +source=('source.tar.gz') +sha512sums=('SKIP') + +build() { + cd $deepin_source_name + cmake -GNinja \ + -DMKSPECS_INSTALL_DIR=/usr/lib/qt/mkspecs/modules/\ + -DBUILD_DOCS=ON \ + -DBUILD_EXAMPLES=OFF \ + -DQCH_INSTALL_DESTINATION=/usr/share/doc/qt \ + -DCMAKE_INSTALL_LIBDIR=/usr/lib \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release + ninja +} + +package() { + cd $deepin_source_name + DESTDIR="$pkgdir" ninja install +} diff --git a/cmake/DtkCMake/DtkCMakeConfig.cmake b/cmake/DtkCMake/DtkCMakeConfig.cmake new file mode 100644 index 0000000..a118223 --- /dev/null +++ b/cmake/DtkCMake/DtkCMakeConfig.cmake @@ -0,0 +1,76 @@ +function(addDefinitions macro) + string(TOUPPER ${macro} macro) + add_definitions(-D${macro}) +endfunction() + +add_definitions(-DQ_HOST_NAME=\"${CMAKE_HOST_SYSTEM_PROCESSOR}\") +addDefinitions(Q_HOST_${CMAKE_HOST_SYSTEM_PROCESSOR}) + +find_package(DtkCore REQUIRED) + +set(DEEPIN_OS_RELEASE_TOOL_PATH ${DtkCore_TOOL_DIRS}) +set(DEEPIN_OS_RELEASE_TOOL ${DEEPIN_OS_RELEASE_TOOL_PATH}/deepin-os-release) + +if(NOT EXISTS "${DEEPIN_OS_RELEASE_TOOL}") + message(FATAL_ERROR "\"${DEEPIN_OS_RELEASE_TOOL}\" is not exists. Install \"dtkcore-bin\" first") +endif() + +function(formatString string) + string(REGEX REPLACE "\\s+" "_" string ${string}) +endfunction() + +macro(execDeepinOsRelease args output) + exec_program(${DEEPIN_OS_RELEASE_TOOL} ARGS ${args} OUTPUT_VARIABLE ${output} RETURN_VALUE exitCode) + + if(NOT ${exitCode} EQUAL 0) + message(FATAL_ERROR "exec deepin-os-release failed, with args: ${args}, error message: ${output}") + endif() +endmacro() + +execDeepinOsRelease(--deepin-type DEEPIN_OS_TYPE) +execDeepinOsRelease(--deepin-version DEEPIN_OS_VERSION) +execDeepinOsRelease(--product-type CMAKE_PLATFORM_ID) +execDeepinOsRelease(--product-version CMAKE_PLATFORM_VERSION) + +if("${CMAKE_PLATFORM_ID}" STREQUAL "") + message(WARNING "No value of the \"--product-type\" in the process \"${DEEPIN_OS_RELEASE_TOOL}\"") +else() + formatString(CMAKE_PLATFORM_ID) + + message("OS: ${CMAKE_PLATFORM_ID}, Version: ${CMAKE_PLATFORM_VERSION}") + + if(NOT "${CMAKE_PLATFORM_ID}" STREQUAL "") + addDefinitions(Q_OS_${CMAKE_PLATFORM_ID}) + string(TOUPPER ${CMAKE_PLATFORM_ID} CMAKE_PLATFORM_ID) + set(OS_${CMAKE_PLATFORM_ID} TRUE) + endif() + + formatString(CMAKE_PLATFORM_VERSION) + add_definitions(-DQ_OS_VERSION=\"${CMAKE_PLATFORM_VERSION}\") + + #uos base with deepin + if("${CMAKE_PLATFORM_ID}" STREQUAL "UOS") + addDefinitions(Q_OS_DEEPIN) + set(OS_DEEPIN TRUE) + endif() +endif() + +if("${DEEPIN_OS_TYPE}" STREQUAL "") + message(WARNING "No value of the \"--deepin-type\" in the process \"${DEEPIN_OS_RELEASE_TOOL}\"") +else() + formatString(DEEPIN_OS_TYPE) + + message("Deepin OS Type: ${DEEPIN_OS_TYPE}") + message("Deepin OS Version: ${DEEPIN_OS_VERSION}") + + if(NOT "${DEEPIN_OS_TYPE}" STREQUAL "") + addDefinitions(Q_OS_DEEPIN_${DEEPIN_OS_TYPE}) + addDefinitions(DEEPIN_DDE) + string(TOUPPER ${DEEPIN_OS_TYPE} DEEPIN_OS_TYPE) + set(OS_DEEPIN_${DEEPIN_OS_TYPE} TRUE) + set(DEEPIN_DDE TRUE) + endif() + + formatString(DEEPIN_OS_VERSION) + add_definitions(-DQ_OS_DEEPIN_VERSION=\"${DEEPIN_OS_VERSION}\") +endif() diff --git a/cmake/DtkTools/DtkSettingsToolsMacros.cmake b/cmake/DtkTools/DtkSettingsToolsMacros.cmake new file mode 100644 index 0000000..da1cd10 --- /dev/null +++ b/cmake/DtkTools/DtkSettingsToolsMacros.cmake @@ -0,0 +1,53 @@ +#============================================================================= +# Copyright 2019 Deepin Technology Co., Ltd. +# Copyright 2019 Gary Wang +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of authors nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +function(DTK_CREATE_I18N_FROM_JSON _generated_file_list _input_json_file _output_cpp_file_name) + set (generated_file_list) # 0(failed) or 1(successed) files in the list. + + get_filename_component(_input_json_abs_path ${_input_json_file} ABSOLUTE) + get_filename_component(_input_json_abs_dir ${_input_json_abs_path} DIRECTORY) + set (_output_cpp_abs_path ${_input_json_abs_dir}/${_output_cpp_file_name}) + + if (DTK_SETTINGS_TOOLS_FOUND) + add_custom_command(OUTPUT ${_output_cpp_abs_path} + COMMAND ${DTK_SETTINGS_TOOLS_EXECUTABLE} + ARGS ${_input_json_abs_path} -o ${_output_cpp_abs_path} + DEPENDS ${_input_json_abs_path} VERBATIM) + list(APPEND generated_file_list ${_output_cpp_abs_path}) + else () + message (WARNING "The dtk-settings tools could not be found at ${DTK_SETTINGS_TOOLS_EXECUTABLE}") + message (WARNING "Package distributor may create a seprated package for tools like `libdtkcore-bin`.") + endif () + + set(${_generated_file_list} ${generated_file_list} PARENT_SCOPE) +endfunction() diff --git a/cmake/DtkTools/DtkToolsConfig.cmake b/cmake/DtkTools/DtkToolsConfig.cmake new file mode 100644 index 0000000..49b1f5e --- /dev/null +++ b/cmake/DtkTools/DtkToolsConfig.cmake @@ -0,0 +1,9 @@ +find_package(DtkCore REQUIRED) + +set (DTK_SETTINGS_TOOLS_EXECUTABLE ${DtkCore_TOOL_DIRS}/dtk-settings) + +if (EXISTS ${DTK_SETTINGS_TOOLS_EXECUTABLE}) + set(DTK_SETTINGS_TOOLS_FOUND TRUE) +endif () + +include("${CMAKE_CURRENT_LIST_DIR}/DtkSettingsToolsMacros.cmake") \ No newline at end of file diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000..c1a33e2 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +from conans import ConanFile, tools + + +class DtkcoreConan(ConanFile): + name = 'dtkcore' + version = '2.0.9' + license = 'GPL' + author = 'Iceyer me@iceyer.net' + url = 'https://github.com/linuxdeepin/dtkcore' + description = 'cross platform ui library' + topics = ('qt', 'dtk') + settings = 'os', 'compiler', 'build_type', 'arch' + options = {'shared': [True, False]} + default_options = 'shared=False' + generators = 'qmake' + exports_sources = '*' + requires = 'jom_installer/1.1.2@bincrafters/stable', 'qt/5.6.3@iceyer/stable' + + def extend_include_path(self): + return '%s/include/libdtk-%s/DCore' % (self.package_folder, self.version) + + # def source(self): + # self.run('git clone https://github.com/linuxdeepin/dtkcore.git source') + # self.run('cd source && git checkout 2.0.9.9') + + def build(self): + outdir = self.build_folder + # includedir = outdir + '/include' + mkspecsdir = outdir + '/mkspecs' + # libdir = outdir + '/lib' + + env_vars = tools.vcvars_dict(self.settings) + env_vars['_CL_'] = '/utf-8' + with tools.environment_append(env_vars): + command = 'qmake -r' + command += ' VERSION=%s' % self.version + # command += ' CONFIG-=debug_and_release' + # command += ' CONFIG-=debug_and_release_target' + command += ' CONFIG+=release' + command += ' PREFIX=%s' % outdir + command += ' MKSPECS_INSTALL_DIR=%s' % mkspecsdir + command += ' DTK_STATIC_LIB=YES' + command += ' DTK_STATIC_TRANSLATION=YES' + command += ' DTK_NO_MULTIMEDIA=YES' + command += ' %s' % self.source_folder + self.run(command) + self.run('jom clean') + self.run('jom') + self.run('jom install') + + def package(self): + self.deploy() + + outdir = self.build_folder + self.copy('*', dst='include', src=outdir+'/include') + self.copy('*.lib', dst='lib', src=outdir+'/lib') + self.copy('*', dst='mkspecs', src=outdir+'/mkspecs') + + def package_info(self): + self.cpp_info.libs = ['dtkcore'] + self.cpp_info.includedirs.append(self.extend_include_path()) + self.env_info.QMAKEPATH = self.cpp_info.rootpath + self.env_info.QMAKEFEATURES = self.cpp_info.rootpath + '/mkspecs/features' + + def deploy(self): + try: + content = [] + module_pri = self.build_folder + '/mkspecs/modules/qt_lib_dtkcore.pri' + s = open(module_pri) + for line in s.readlines(): + if line.startswith('QT.dtkcore.tools'): + line = 'QT.dtkcore.tools = %s\n' % ( + self.package_folder + '/bin') + elif line.startswith('QT.dtkcore.libs'): + line = 'QT.dtkcore.libs = %s\n' % ( + self.package_folder + '/lib') + elif line.startswith('QT.dtkcore.includes'): + line = 'QT.dtkcore.includes = %s\n' % ( + self.extend_include_path()) + content.append(line) + s.close() + + # print('create module file', content) + s = open(module_pri, 'w') + s.writelines(content) + except FileNotFoundError: + print('skip update qt module file') diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..a62246e --- /dev/null +++ b/debian/changelog @@ -0,0 +1,47 @@ +dtkcore (5.6.2.2) unstable; urgency=medium + + * release 5.6.2.2 + + -- Deepin Packages Builder Thu, 17 Nov 2022 17:41:10 +0800 + +dtkcore (5.6.2.1) unstable; urgency=medium + + * snipe release 5.6.2.1 + + -- Deepin Packages Builder Mon, 31 Oct 2022 16:53:16 +0800 + +dtkcore (5.6.2) unstable; urgency=medium + + * snipe release 5.6.2 + + -- Deepin Packages Builder Wed, 21 Sep 2022 13:57:29 +0800 + +dtkcore (5.6.0) unstable; urgency=medium + + * snipe release 5.6.0 + + -- Deepin Packages Builder Mon, 11 Jul 2022 17:47:56 +0800 + +dtkcore (5.0.3) unstable; urgency=medium + + * Release 5.0.3 + + -- Deepin Packages Builder Tue, 21 Sep 2019 13:31:03 +0800 + +dtkcore (5.0.0) unstable; urgency=medium + + * Release 5.0.0 + + -- Deepin Packages Builder Tue, 03 Sep 2019 08:47:03 +0800 + +dtkcore (2.0.8) unstable; urgency=medium + + * Release 2.0.8 + + -- Deepin Packages Builder Wed, 02 May 2018 10:52:03 +0800 + +dtkcore (0.3.3-1) unstable; urgency=medium + + * Initial release + + -- Deepin Packages Builder Mon, 10 Oct 2016 16:58:07 +0800 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..c4727bd --- /dev/null +++ b/debian/control @@ -0,0 +1,42 @@ +Source: dtkcore +Section: libdevel +Priority: optional +Maintainer: Deepin Packages Builder +Build-Depends: debhelper (>= 9), pkg-config, + qttools5-dev-tools, qtbase5-private-dev, doxygen, + libgsettings-qt-dev, libgtest-dev, libdtkcommon-dev, cmake +Standards-Version: 3.9.8 + +Package: libdtkcore5 +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, lshw, libdtkcommon +Multi-Arch: same +Description: Deepin Tool Kit Core library + DtkCore is base library of Deepin Qt/C++ applications. + . + This package contains the shared libraries. + +Package: libdtkcore5-bin +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, + libdtkcore5( =${binary:Version}) +Description: Deepin Tool Kit Core Utilities + DtkCore is base devel library of Deepin Qt/C++ applications. + . + This package contains the utilities of DtkCore + +Package: libdtkcore-dev +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, libdtkcore5( =${binary:Version}), libdtkcommon-dev +Description: Deepin Tool Kit Core Devel library + DtkCore is base devel library of Deepin Qt/C++ applications. + . + This package contains the header files and static libraries of DtkCore + +Package: libdtkcore-doc +Architecture: any +Description: Deepin Tool Kit Core (document) + DtkCore is base devel library of Deepin Qt/C++ applications. + . + This package contains the doc files of DtkCore + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..7a0c37f --- /dev/null +++ b/debian/copyright @@ -0,0 +1,22 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: dtkcore +Source: https://github.com/linuxdeepin/dtkcore + +Files: * +Copyright: 2017 Deepin Technology Co., Ltd. +License: LGPL-3+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU Lesser General + Public License version 3 can be found in "/usr/share/common-licenses/LGPL-3". diff --git a/debian/libdtkcore-dev.install b/debian/libdtkcore-dev.install new file mode 100644 index 0000000..f1f7613 --- /dev/null +++ b/debian/libdtkcore-dev.install @@ -0,0 +1,5 @@ +usr/lib/*/lib*.so +usr/include +usr/lib/*/pkgconfig/*.pc +usr/lib/*/cmake/*/*.cmake +usr/lib/*/qt5/* diff --git a/debian/libdtkcore-doc.install b/debian/libdtkcore-doc.install new file mode 100644 index 0000000..06733f0 --- /dev/null +++ b/debian/libdtkcore-doc.install @@ -0,0 +1 @@ +usr/share/qt5/doc/dtkcore.qch diff --git a/debian/libdtkcore5-bin.install b/debian/libdtkcore5-bin.install new file mode 100644 index 0000000..0b8a68c --- /dev/null +++ b/debian/libdtkcore5-bin.install @@ -0,0 +1,2 @@ +usr/lib/*/*/DCore/bin/* +usr/bin/* \ No newline at end of file diff --git a/debian/libdtkcore5.install b/debian/libdtkcore5.install new file mode 100644 index 0000000..3ddde58 --- /dev/null +++ b/debian/libdtkcore5.install @@ -0,0 +1 @@ +usr/lib/*/lib*.so.* diff --git a/debian/missing-sources/doxygen-awesome-darkmode-toggle.js b/debian/missing-sources/doxygen-awesome-darkmode-toggle.js new file mode 100644 index 0000000..05ae338 --- /dev/null +++ b/debian/missing-sources/doxygen-awesome-darkmode-toggle.js @@ -0,0 +1,157 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2021 - 2022 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeDarkModeToggle extends HTMLElement { + // SVG icons from https://fonts.google.com/icons + // Licensed under the Apache 2.0 license: + // https://www.apache.org/licenses/LICENSE-2.0.html + static lightModeIcon = `` + static darkModeIcon = `` + static title = "Toggle Light/Dark Mode" + + static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode" + static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode" + + static _staticConstructor = function () { + DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference) + // Update the color scheme when the browsers preference changes + // without user interaction on the website. + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { + DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged() + }) + // Update the color scheme when the tab is made visible again. + // It is possible that the appearance was changed in another tab + // while this tab was in the background. + document.addEventListener("visibilitychange", visibilityState => { + if (document.visibilityState === 'visible') { + DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged() + } + }); + }() + + static init() { + $(function () { + $(document).ready(function () { + const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle') + toggleButton.title = DoxygenAwesomeDarkModeToggle.title + toggleButton.updateIcon() + + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { + toggleButton.updateIcon() + }) + document.addEventListener("visibilitychange", visibilityState => { + if (document.visibilityState === 'visible') { + toggleButton.updateIcon() + } + }); + + $(document).ready(function () { + document.getElementById("MSearchBox").parentNode.appendChild(toggleButton) + }) + $(window).resize(function () { + document.getElementById("MSearchBox").parentNode.appendChild(toggleButton) + }) + }) + }) + } + + constructor() { + super(); + this.onclick = this.toggleDarkMode + } + + /** + * @returns `true` for dark-mode, `false` for light-mode system preference + */ + static get systemPreference() { + return window.matchMedia('(prefers-color-scheme: dark)').matches + } + + /** + * @returns `true` for dark-mode, `false` for light-mode user preference + */ + static get userPreference() { + return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) || + (DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)) + } + + static set userPreference(userPreference) { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference + if (!userPreference) { + if (DoxygenAwesomeDarkModeToggle.systemPreference) { + localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true) + } else { + localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey) + } + } else { + if (!DoxygenAwesomeDarkModeToggle.systemPreference) { + localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true) + } else { + localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey) + } + } + DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged() + } + + static enableDarkMode(enable) { + if (enable) { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = true + document.documentElement.classList.add("dark-mode") + document.documentElement.classList.remove("light-mode") + } else { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = false + document.documentElement.classList.remove("dark-mode") + document.documentElement.classList.add("light-mode") + } + } + + static onSystemPreferenceChanged() { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference + DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled) + } + + static onUserPreferenceChanged() { + DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled) + } + + toggleDarkMode() { + DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference + this.updateIcon() + } + + updateIcon() { + if (DoxygenAwesomeDarkModeToggle.darkModeEnabled) { + this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon + } else { + this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon + } + } +} + +customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle); diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..a216f3b --- /dev/null +++ b/debian/rules @@ -0,0 +1,25 @@ +#!/usr/bin/make -f +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/default.mk +export QT_SELECT = qt5 +export DEB_CXXFLAGS_MAINT_APPEND = -Ofast + +DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) + +VERSION = $(DEB_VERSION_UPSTREAM) +PACK_VER = $(shell echo $(VERSION) | awk -F'[+_~-]' '{print $$1}') +# Fix: invalid digit "8" in octal constant. e.g. u008 ==> 008 ==> 8 +BUILD_VER = $(shell echo $(VERSION) | awk -F'[+_~-]' '{print $$2}' | sed 's/[^0-9]//g' | awk '{print int($$1)}') + + +%: + dh $@ --parallel + +override_dh_auto_configure: + dh_auto_configure -- -DBUILD_EXAMPLES=OFF -DBUILD_DOCS=ON -DBUILD_VERSION=$(BUILD_VER) -DDVERSION=$(PACK_VER) + +#override_dh_auto_test: +# echo "skip auto test" + +override_dh_makeshlibs: + dh_makeshlibs -V "libdtkcore5 (>= $(shell echo $(VERSION) | cut -d '.' -f 1,2))" diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/debian/symbols.amd64 b/debian/symbols.amd64 new file mode 100644 index 0000000..502b661 --- /dev/null +++ b/debian/symbols.amd64 @@ -0,0 +1,909 @@ +libdtkcore.so.5 libdtkcore5 #MINVER# + _Z19qInitResources_utilv@Base 5.0.3 + _Z22qCleanupResources_utilv@Base 5.0.3 + _ZGVZN3Dtk4Core11DLogManager8instanceEvE8instance@Base 5.0.3 + _ZN10QByteArrayD1Ev@Base 5.0.3 + _ZN10QByteArrayD2Ev@Base 5.0.3 + _ZN11DDBusCaller3argI7QStringEES_RKT_@Base 5.0.3 + _ZN11DDBusCaller4callEv@Base 5.0.3 + _ZN11DDBusCallerC1ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3 + _ZN11DDBusCallerC2ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3 + _ZN11DDBusCallerD1Ev@Base 5.0.3 + _ZN11DDBusCallerD2Ev@Base 5.0.3 + _ZN11DDBusSender4pathERK7QString@Base 5.0.3 + _ZN11DDBusSender4typeEN15QDBusConnection7BusTypeE@Base 5.0.3 + _ZN11DDBusSender6methodERK7QString@Base 5.0.3 + _ZN11DDBusSender7serviceERK7QString@Base 5.0.3 + _ZN11DDBusSender8propertyERK7QString@Base 5.0.3 + _ZN11DDBusSender9interfaceERK7QString@Base 5.0.3 + _ZN11DDBusSenderC1Ev@Base 5.0.3 + _ZN11DDBusSenderC2Ev@Base 5.0.3 + _ZN12QWeakPointerI7QObjectED1Ev@Base 5.0.3 + _ZN12QWeakPointerI7QObjectED2Ev@Base 5.0.3 + _ZN13DDBusProperty3getEv@Base 5.0.3 + _ZN13DDBusPropertyC1ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3 + _ZN13DDBusPropertyC2ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3 + _ZN14QScopedPointerIN3Dtk4Core23GSettingsBackendPrivateE21QScopedPointerDeleterIS2_EED1Ev@Base 5.0.3 + _ZN14QScopedPointerIN3Dtk4Core23GSettingsBackendPrivateE21QScopedPointerDeleterIS2_EED2Ev@Base 5.0.3 + _ZN15QVarLengthArrayIcLi4096EEC1Ei@Base 5.0.3 + _ZN15QVarLengthArrayIcLi4096EEC2Ei@Base 5.0.3 + _ZN3Dtk4Core10doUnescapeER7QStringRK5QHashI5QCharS4_E@Base 5.0.3 + _ZN3Dtk4Core11DLogManager12setLogFormatERK7QString@Base 5.0.3 + _ZN3Dtk4Core11DLogManager14getlogFilePathEv@Base 5.0.3 + _ZN3Dtk4Core11DLogManager14setlogFilePathERK7QString@Base 5.0.3 + _ZN3Dtk4Core11DLogManager19initConsoleAppenderEv@Base 5.0.3 + _ZN3Dtk4Core11DLogManager20registerFileAppenderEv@Base 5.0.3 + _ZN3Dtk4Core11DLogManager23initRollingFileAppenderEv@Base 5.0.3 + _ZN3Dtk4Core11DLogManager23registerConsoleAppenderEv@Base 5.0.3 + _ZN3Dtk4Core11DLogManager8joinPathERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core11DLogManagerC1Ev@Base 5.0.3 + _ZN3Dtk4Core11DLogManagerC2Ev@Base 5.0.3 + _ZN3Dtk4Core11DLogManagerD1Ev@Base 5.0.3 + _ZN3Dtk4Core11DLogManagerD2Ev@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook10copyVtableEPPy@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook11originalFunEPKvy@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook11resetVtableEPKv@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook12ensureVtableEPKvSt8functionIFvvEE@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook13resetVfptrFunEPKvy@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook14objDestructFunE@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook15autoCleanVtableEPKv@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook15objToGhostVfptrE@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook16clearGhostVtableEPKv@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook16forceWriteMemoryEPvPKvm@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook18objToOriginalVfptrE@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook19getDestructFunIndexEPPySt8functionIFvvEE@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook7resolveEPKc@Base 5.0.3 + _ZN3Dtk4Core11DVtableHook9hasVtableEPKv@Base 5.0.3 + _ZN3Dtk4Core11unqtifyNameERK7QString@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcher11onFileMovedERK7QStringS4_S4_S4_@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcher11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcher11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcher12onFileClosedERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcher13onFileCreatedERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcher13onFileDeletedERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcher14onFileModifiedERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcher16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcher22onFileAttributeChangedERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcherC1ERK7QStringP7QObject@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcherC2ERK7QStringP7QObject@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcherD0Ev@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcherD1Ev@Base 5.0.3 + _ZN3Dtk4Core12DFileWatcherD2Ev@Base 5.0.3 + _ZN3Dtk4Core12FileAppender11setFileNameERK7QString@Base 5.0.3 + _ZN3Dtk4Core12FileAppender6appendERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3 + _ZN3Dtk4Core12FileAppender8openFileEv@Base 5.0.3 + _ZN3Dtk4Core12FileAppender9closeFileEv@Base 5.0.3 + _ZN3Dtk4Core12FileAppenderC1ERK7QString@Base 5.0.3 + _ZN3Dtk4Core12FileAppenderC2ERK7QString@Base 5.0.3 + _ZN3Dtk4Core12FileAppenderD0Ev@Base 5.0.3 + _ZN3Dtk4Core12FileAppenderD1Ev@Base 5.0.3 + _ZN3Dtk4Core12FileAppenderD2Ev@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntry10escapeExecER7QString@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntry11removeEntryERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntry11setRawValueERK7QStringS4_S4_@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntry12unescapeExecER7QString@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntry14setStringValueERK7QStringS4_S4_@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntry16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntry17setLocalizedValueERK7QStringS4_S4_S4_@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntry6escapeER7QString@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntry8unescapeER7QStringb@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntry9setStatusERKNS1_6StatusE@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntryC1ERK7QString@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntryC2ERK7QString@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntryD1Ev@Base 5.0.3 + _ZN3Dtk4Core13DDesktopEntryD2Ev@Base 5.0.3 + _ZN3Dtk4Core13DFileServices10showFolderE4QUrlRK7QString@Base 5.0.3 + _ZN3Dtk4Core13DFileServices10showFolderE7QStringRKS2_@Base 5.0.3 + _ZN3Dtk4Core13DFileServices11showFoldersE5QListI4QUrlERK7QString@Base 5.0.3 + _ZN3Dtk4Core13DFileServices11showFoldersE5QListI7QStringERKS3_@Base 5.0.3 + _ZN3Dtk4Core13DFileServices12errorMessageEv@Base 5.0.3 + _ZN3Dtk4Core13DFileServices12showFileItemE4QUrlRK7QString@Base 5.0.3 + _ZN3Dtk4Core13DFileServices12showFileItemE7QStringRKS2_@Base 5.0.3 + _ZN3Dtk4Core13DFileServices13showFileItemsE5QListI4QUrlERK7QString@Base 5.0.3 + _ZN3Dtk4Core13DFileServices13showFileItemsE5QListI7QStringERKS3_@Base 5.0.3 + _ZN3Dtk4Core13DFileServices21showFileItemPropertieE4QUrlRK7QString@Base 5.0.3 + _ZN3Dtk4Core13DFileServices21showFileItemPropertieE7QStringRKS2_@Base 5.0.3 + _ZN3Dtk4Core13DFileServices22showFileItemPropertiesE5QListI4QUrlERK7QString@Base 5.0.3 + _ZN3Dtk4Core13DFileServices22showFileItemPropertiesE5QListI7QStringERKS3_@Base 5.0.3 + _ZN3Dtk4Core13DFileServices5trashE4QUrl@Base 5.0.3 + _ZN3Dtk4Core13DFileServices5trashE5QListI4QUrlE@Base 5.0.3 + _ZN3Dtk4Core13DFileServices5trashE5QListI7QStringE@Base 5.0.3 + _ZN3Dtk4Core13DFileServices5trashE7QString@Base 5.0.3 + _ZN3Dtk4Core13DSecureStringC1ERK7QString@Base 5.0.3 + _ZN3Dtk4Core13DSecureStringC2ERK7QString@Base 5.0.3 + _ZN3Dtk4Core13DSecureStringD1Ev@Base 5.0.3 + _ZN3Dtk4Core13DSecureStringD2Ev@Base 5.0.3 + _ZN3Dtk4Core13DTrashManager10cleanTrashEv@Base 5.0.3 + _ZN3Dtk4Core13DTrashManager11moveToTrashERK7QStringb@Base 5.0.3 + _ZN3Dtk4Core13DTrashManager8instanceEv@Base 5.0.3 + _ZN3Dtk4Core13DTrashManagerC1Ev@Base 5.0.3 + _ZN3Dtk4Core13DTrashManagerC2Ev@Base 5.0.3 + _ZN3Dtk4Core13DTrashManagerD0Ev@Base 5.0.3 + _ZN3Dtk4Core13DTrashManagerD1Ev@Base 5.0.3 + _ZN3Dtk4Core13DTrashManagerD2Ev@Base 5.0.3 + _ZN3Dtk4Core13LoggerPrivate14globalInstanceE@Base 5.0.3 + _ZN3Dtk4Core13LoggerPrivate18globalInstanceLockE@Base 5.0.3 + _ZN3Dtk4Core14Chinese2PinyinERK7QString@Base 5.0.3 + _ZN3Dtk4Core14DObjectPrivateC1EPNS0_7DObjectE@Base 5.0.3 + _ZN3Dtk4Core14DObjectPrivateC2EPNS0_7DObjectE@Base 5.0.3 + _ZN3Dtk4Core14DObjectPrivateD0Ev@Base 5.0.3 + _ZN3Dtk4Core14DObjectPrivateD1Ev@Base 5.0.3 + _ZN3Dtk4Core14DObjectPrivateD2Ev@Base 5.0.3 + _ZN3Dtk4Core14DRecentManager10removeItemERK7QString@Base 5.0.3 + _ZN3Dtk4Core14DRecentManager11removeItemsERK11QStringList@Base 5.0.3 + _ZN3Dtk4Core14DRecentManager7addItemERK7QStringRNS0_11DRecentDataE@Base 5.0.3 + _ZN3Dtk4Core14DSettingsGroup11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core14DSettingsGroup11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core14DSettingsGroup14setParentGroupE8QPointerIS1_E@Base 5.0.3 + _ZN3Dtk4Core14DSettingsGroup16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core14DSettingsGroup8fromJsonERK7QStringRK11QJsonObject@Base 5.0.3 + _ZN3Dtk4Core14DSettingsGroup9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3 + _ZN3Dtk4Core14DSettingsGroupC1EP7QObject@Base 5.0.3 + _ZN3Dtk4Core14DSettingsGroupC2EP7QObject@Base 5.0.3 + _ZN3Dtk4Core14DSettingsGroupD0Ev@Base 5.0.3 + _ZN3Dtk4Core14DSettingsGroupD1Ev@Base 5.0.3 + _ZN3Dtk4Core14DSettingsGroupD2Ev@Base 5.0.3 + _ZN3Dtk4Core14DStandardPaths14findExecutableERK7QStringRK11QStringList@Base 5.0.3 + _ZN3Dtk4Core14DStandardPaths16writableLocationEN14QStandardPaths16StandardLocationE@Base 5.0.3 + _ZN3Dtk4Core14DStandardPaths17standardLocationsEN14QStandardPaths16StandardLocationE@Base 5.0.3 + _ZN3Dtk4Core14DStandardPaths6locateEN14QStandardPaths16StandardLocationERK7QString6QFlagsINS2_12LocateOptionEE@Base 5.0.3 + _ZN3Dtk4Core14DStandardPaths7setModeENS1_4ModeE@Base 5.0.3 + _ZN3Dtk4Core14DStandardPaths9locateAllEN14QStandardPaths16StandardLocationERK7QString6QFlagsINS2_12LocateOptionEE@Base 5.0.3 + _ZN3Dtk4Core14DTrashManager_D0Ev@Base 5.0.3 + _ZN3Dtk4Core14DTrashManager_D1Ev@Base 5.0.3 + _ZN3Dtk4Core14DTrashManager_D2Ev@Base 5.0.3 + _ZN3Dtk4Core14loggerInstanceEv@Base 5.0.3 + _ZN3Dtk4Core14parentPathListERK7QString@Base 5.0.3 + _ZN3Dtk4Core15ConsoleAppender24ignoreEnvironmentPatternEb@Base 5.0.3 + _ZN3Dtk4Core15ConsoleAppender6appendERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3 + _ZN3Dtk4Core15ConsoleAppenderC1Ev@Base 5.0.3 + _ZN3Dtk4Core15ConsoleAppenderC2Ev@Base 5.0.3 + _ZN3Dtk4Core15ConsoleAppenderD0Ev@Base 5.0.3 + _ZN3Dtk4Core15ConsoleAppenderD1Ev@Base 5.0.3 + _ZN3Dtk4Core15ConsoleAppenderD2Ev@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOption11dataChangedERK7QString8QVariant@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOption11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOption11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOption12valueChangedE8QVariant@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOption14setParentGroupE8QPointerINS0_14DSettingsGroupEE@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOption16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOption7setDataERK7QString8QVariant@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOption8fromJsonERK7QStringRK11QJsonObject@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOption8setValueE8QVariant@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOption9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOptionC1EP7QObject@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOptionC2EP7QObject@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOptionD0Ev@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOptionD1Ev@Base 5.0.3 + _ZN3Dtk4Core15DSettingsOptionD2Ev@Base 5.0.3 + _ZN3Dtk4Core15DSysInfoPrivate13parseInfoFileER5QFile@Base 5.0.3 + _ZN3Dtk4Core15DSysInfoPrivate16ensureDeepinInfoEv@Base 5.0.3 + _ZN3Dtk4Core15DSysInfoPrivate17ensureReleaseInfoEv@Base 5.0.3 + _ZN3Dtk4Core15DSysInfoPrivate18ensureComputerInfoEv@Base 5.0.3 + _ZN3Dtk4Core15DSysInfoPrivateC1Ev@Base 5.0.3 + _ZN3Dtk4Core15DSysInfoPrivateC2Ev@Base 5.0.3 + _ZN3Dtk4Core15QSettingBackend11doSetOptionERK7QStringRK8QVariant@Base 5.0.3 + _ZN3Dtk4Core15QSettingBackend11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core15QSettingBackend11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core15QSettingBackend16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core15QSettingBackend6doSyncEv@Base 5.0.3 + _ZN3Dtk4Core15QSettingBackendC1ERK7QStringP7QObject@Base 5.0.3 + _ZN3Dtk4Core15QSettingBackendC2ERK7QStringP7QObject@Base 5.0.3 + _ZN3Dtk4Core15QSettingBackendD0Ev@Base 5.0.3 + _ZN3Dtk4Core15QSettingBackendD1Ev@Base 5.0.3 + _ZN3Dtk4Core15QSettingBackendD2Ev@Base 5.0.3 + _ZN3Dtk4Core16AbstractAppender15setDetailsLevelENS0_6Logger8LogLevelE@Base 5.0.3 + _ZN3Dtk4Core16AbstractAppender15setDetailsLevelERK7QString@Base 5.0.3 + _ZN3Dtk4Core16AbstractAppender5writeERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3 + _ZN3Dtk4Core16AbstractAppenderC1Ev@Base 5.0.3 + _ZN3Dtk4Core16AbstractAppenderC2Ev@Base 5.0.3 + _ZN3Dtk4Core16AbstractAppenderD0Ev@Base 5.0.3 + _ZN3Dtk4Core16AbstractAppenderD1Ev@Base 5.0.3 + _ZN3Dtk4Core16AbstractAppenderD2Ev@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher10fileClosedERK4QUrl@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher11fileDeletedERK4QUrl@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher11ghostSignalERK4QUrlMS1_FvS4_ES4_@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher11ghostSignalERK4QUrlMS1_FvS4_S4_ES4_S4_@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher11stopWatcherEv@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher12fileModifiedERK4QUrl@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher12startWatcherEv@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher14restartWatcherEv@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher14subfileCreatedERK4QUrl@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher20fileAttributeChangedERK4QUrl@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher24setEnabledSubfileWatcherERK4QUrlb@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcher9fileMovedERK4QUrlS4_@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcherC1ERNS0_23DBaseFileWatcherPrivateERK4QUrlP7QObject@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcherC2ERNS0_23DBaseFileWatcherPrivateERK4QUrlP7QObject@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcherD0Ev@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcherD1Ev@Base 5.0.3 + _ZN3Dtk4Core16DBaseFileWatcherD2Ev@Base 5.0.3 + _ZN3Dtk4Core16DSettingsBackend11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core16DSettingsBackend11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core16DSettingsBackend13optionChangedERK7QStringRK8QVariant@Base 5.0.3 + _ZN3Dtk4Core16DSettingsBackend16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core16DSettingsBackend4syncEv@Base 5.0.3 + _ZN3Dtk4Core16DSettingsBackend9setOptionERK7QStringRK8QVariant@Base 5.0.3 + _ZN3Dtk4Core16GSettingsBackend11doSetOptionERK7QStringRK8QVariant@Base 5.0.3 + _ZN3Dtk4Core16GSettingsBackend11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core16GSettingsBackend11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core16GSettingsBackend16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core16GSettingsBackend6doSyncEv@Base 5.0.3 + _ZN3Dtk4Core16GSettingsBackendC1EPNS0_9DSettingsEP7QObject@Base 5.0.3 + _ZN3Dtk4Core16GSettingsBackendC2EPNS0_9DSettingsEP7QObject@Base 5.0.3 + _ZN3Dtk4Core16GSettingsBackendD0Ev@Base 5.0.3 + _ZN3Dtk4Core16GSettingsBackendD1Ev@Base 5.0.3 + _ZN3Dtk4Core16GSettingsBackendD2Ev@Base 5.0.3 + _ZN3Dtk4Core16readLineFromDataERK10QByteArrayRiS4_S4_S4_@Base 5.0.3 + _ZN3Dtk4Core18DDiskSizeFormatter4rateEi@Base 5.0.3 + _ZN3Dtk4Core18DDiskSizeFormatterC1Ev@Base 5.0.3 + _ZN3Dtk4Core18DDiskSizeFormatterC2Ev@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher10fileClosedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher10removePathERK7QString@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher11fileCreatedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher11fileDeletedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher11removePathsERK11QStringList@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher12fileModifiedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher20fileAttributeChangedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher7addPathERK7QString@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher8addPathsERK11QStringList@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcher9fileMovedERK7QStringS4_S4_S4_NS1_14QPrivateSignalE@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcherC1EP7QObject@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcherC1ERK11QStringListP7QObject@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcherC2EP7QObject@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcherC2ERK11QStringListP7QObject@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcherD0Ev@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcherD1Ev@Base 5.0.3 + _ZN3Dtk4Core18DFileSystemWatcherD2Ev@Base 5.0.3 + _ZN3Dtk4Core18DTimeUnitFormatterC1Ev@Base 5.0.3 + _ZN3Dtk4Core18DTimeUnitFormatterC2Ev@Base 5.0.3 + _ZN3Dtk4Core18LoggerTimingHelper5startEPKcz@Base 5.0.3 + _ZN3Dtk4Core18LoggerTimingHelper5startERK7QString@Base 5.0.3 + _ZN3Dtk4Core18LoggerTimingHelperD1Ev@Base 5.0.3 + _ZN3Dtk4Core18LoggerTimingHelperD2Ev@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManager10fileClosedERK7QString@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManager11fileDeletedERK7QString@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManager11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManager11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManager12fileModifiedERK7QString@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManager14subfileCreatedERK7QString@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManager16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManager20fileAttributeChangedERK7QString@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManager3addERK7QString@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManager6removeERK7QString@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManager9fileMovedERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManagerC1EP7QObject@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManagerC2EP7QObject@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManagerD0Ev@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManagerD1Ev@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherManagerD2Ev@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivate10formatPathERK7QString@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivate18_q_handleFileCloseERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivate18_q_handleFileMovedERK7QStringS4_S4_S4_@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivate20_q_handleFileCreatedERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivate20_q_handleFileDeletedERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivate21_q_handleFileModifiedERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivate22filePathToWatcherCountE@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivate29_q_handleFileAttributeChangedERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivate4stopEv@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivate5startEv@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivateD0Ev@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivateD1Ev@Base 5.0.3 + _ZN3Dtk4Core19DFileWatcherPrivateD2Ev@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppender14removeOldFilesEv@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppender14setDatePatternENS1_11DatePatternE@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppender14setDatePatternERK7QString@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppender16computeFrequencyEv@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppender16setLogFilesLimitEi@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppender19computeRollOverTimeEv@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppender20setDatePatternStringERK7QString@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppender6appendERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppender8rollOverEv@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppenderC1ERK7QString@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppenderC2ERK7QString@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppenderD0Ev@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppenderD1Ev@Base 5.0.3 + _ZN3Dtk4Core19RollingFileAppenderD2Ev@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntryPrivate20initSectionsFromDataERK10QByteArray@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntryPrivate3getERK7QStringS4_PS2_@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntryPrivate3setERK7QStringS4_S4_@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntryPrivate6removeERK7QStringS4_@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntryPrivate9fuzzyLoadEv@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntryPrivateC1ERK7QStringPNS0_13DDesktopEntryE@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntryPrivateC2ERK7QStringPNS0_13DDesktopEntryE@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntryPrivateD1Ev@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntryPrivateD2Ev@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntrySection23ensureSectionDataParsedEv@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntrySectionD1Ev@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntrySectionD2Ev@Base 5.0.3 + _ZN3Dtk4Core20DDesktopEntrySectionaSERKS1_@Base 5.0.3 + _ZN3Dtk4Core20DTrashManagerPrivate15removeFileOrDirERK7QString@Base 5.0.3 + _ZN3Dtk4Core20DTrashManagerPrivate18removeFromIteratorER12QDirIterator@Base 5.0.3 + _ZN3Dtk4Core20DTrashManagerPrivateD0Ev@Base 5.0.3 + _ZN3Dtk4Core20DTrashManagerPrivateD1Ev@Base 5.0.3 + _ZN3Dtk4Core20DTrashManagerPrivateD2Ev@Base 5.0.3 + _ZN3Dtk4Core21DSettingsGroupPrivate9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3 + _ZN3Dtk4Core22AbstractStringAppender16qCleanupFuncinfoEPKc@Base 5.0.3 + _ZN3Dtk4Core22AbstractStringAppender17stripFunctionNameEPKc@Base 5.0.3 + _ZN3Dtk4Core22AbstractStringAppender9setFormatERK7QString@Base 5.0.3 + _ZN3Dtk4Core22AbstractStringAppenderC1Ev@Base 5.0.3 + _ZN3Dtk4Core22AbstractStringAppenderC2Ev@Base 5.0.3 + _ZN3Dtk4Core22AbstractStringAppenderD0Ev@Base 5.0.3 + _ZN3Dtk4Core22AbstractStringAppenderD1Ev@Base 5.0.3 + _ZN3Dtk4Core22AbstractStringAppenderD2Ev@Base 5.0.3 + _ZN3Dtk4Core22DAbstractUnitFormatterC1Ev@Base 5.0.3 + _ZN3Dtk4Core22DAbstractUnitFormatterC2Ev@Base 5.0.3 + _ZN3Dtk4Core22DAbstractUnitFormatterD1Ev@Base 5.0.3 + _ZN3Dtk4Core22DAbstractUnitFormatterD2Ev@Base 5.0.3 + _ZN3Dtk4Core22DSettingsOptionPrivate9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3 + _ZN3Dtk4Core23DBaseFileWatcherPrivate11watcherListE@Base 5.0.3 + _ZN3Dtk4Core23DBaseFileWatcherPrivateC1EPNS0_16DBaseFileWatcherE@Base 5.0.3 + _ZN3Dtk4Core23DBaseFileWatcherPrivateC2EPNS0_16DBaseFileWatcherE@Base 5.0.3 + _ZN3Dtk4Core25DFileSystemWatcherPrivate11removePathsERK11QStringListPS2_S5_@Base 5.0.3 + _ZN3Dtk4Core25DFileSystemWatcherPrivate13onFileChangedERK7QStringb@Base 5.0.3 + _ZN3Dtk4Core25DFileSystemWatcherPrivate18_q_readFromInotifyEv@Base 5.0.3 + _ZN3Dtk4Core25DFileSystemWatcherPrivate18onDirectoryChangedERK7QStringb@Base 5.0.3 + _ZN3Dtk4Core25DFileSystemWatcherPrivate8addPathsERK11QStringListPS2_S5_@Base 5.0.3 + _ZN3Dtk4Core25DFileSystemWatcherPrivateC1EiPNS0_18DFileSystemWatcherE@Base 5.0.3 + _ZN3Dtk4Core25DFileSystemWatcherPrivateC2EiPNS0_18DFileSystemWatcherE@Base 5.0.3 + _ZN3Dtk4Core25DFileSystemWatcherPrivateD0Ev@Base 5.0.3 + _ZN3Dtk4Core25DFileSystemWatcherPrivateD1Ev@Base 5.0.3 + _ZN3Dtk4Core25DFileSystemWatcherPrivateD2Ev@Base 5.0.3 + _ZN3Dtk4Core26DFileWatcherManagerPrivateC1EPNS0_19DFileWatcherManagerE@Base 5.0.3 + _ZN3Dtk4Core26DFileWatcherManagerPrivateC2EPNS0_19DFileWatcherManagerE@Base 5.0.3 + _ZN3Dtk4Core26DFileWatcherManagerPrivateD0Ev@Base 5.0.3 + _ZN3Dtk4Core26DFileWatcherManagerPrivateD1Ev@Base 5.0.3 + _ZN3Dtk4Core26DFileWatcherManagerPrivateD2Ev@Base 5.0.3 + _ZN3Dtk4Core5DUtil13DNotifySender4callEv@Base 5.0.3 + _ZN3Dtk4Core5DUtil13DNotifySender5hintsERK4QMapI7QString8QVariantE@Base 5.0.3 + _ZN3Dtk4Core5DUtil13DNotifySender7actionsERK11QStringList@Base 5.0.3 + _ZN3Dtk4Core5DUtil13DNotifySender7appBodyERK7QString@Base 5.0.3 + _ZN3Dtk4Core5DUtil13DNotifySender7appIconERK7QString@Base 5.0.3 + _ZN3Dtk4Core5DUtil13DNotifySender7appNameERK7QString@Base 5.0.3 + _ZN3Dtk4Core5DUtil13DNotifySender7timeOutEi@Base 5.0.3 + _ZN3Dtk4Core5DUtil13DNotifySender9replaceIdEj@Base 5.0.3 + _ZN3Dtk4Core5DUtil13DNotifySenderC1ERK7QString@Base 5.0.3 + _ZN3Dtk4Core5DUtil13DNotifySenderC2ERK7QString@Base 5.0.3 + _ZN3Dtk4Core5DUtil18DExportedInterface11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core5DUtil18DExportedInterface11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core5DUtil18DExportedInterface14registerActionERK7QStringS5_St8functionIF8QVariantS3_EE@Base 5.0.3 + _ZN3Dtk4Core5DUtil18DExportedInterface16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core5DUtil18DExportedInterfaceC1EP7QObject@Base 5.0.3 + _ZN3Dtk4Core5DUtil18DExportedInterfaceC2EP7QObject@Base 5.0.3 + _ZN3Dtk4Core5DUtil18DExportedInterfaceD0Ev@Base 5.0.3 + _ZN3Dtk4Core5DUtil18DExportedInterfaceD1Ev@Base 5.0.3 + _ZN3Dtk4Core5DUtil18DExportedInterfaceD2Ev@Base 5.0.3 + _ZN3Dtk4Core5DUtil25DExportedInterfacePrivate10actionHelpE7QStringi@Base 5.0.3 + _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateC1EPNS1_18DExportedInterfaceE@Base 5.0.3 + _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateC2EPNS1_18DExportedInterfaceE@Base 5.0.3 + _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateD0Ev@Base 5.0.3 + _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateD1Ev@Base 5.0.3 + _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateD2Ev@Base 5.0.3 + _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface4helpERK7QString@Base 5.0.3 + _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface4listEv@Base 5.0.3 + _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface6invokeE7QStringS3_@Base 5.0.3 + _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceC1EPNS1_25DExportedInterfacePrivateE@Base 5.0.3 + _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceC2EPNS1_25DExportedInterfacePrivateE@Base 5.0.3 + _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceD0Ev@Base 5.0.3 + _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceD1Ev@Base 5.0.3 + _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceD2Ev@Base 5.0.3 + _ZN3Dtk4Core6Logger11writeAssertEPKciS3_S3_@Base 5.0.3 + _ZN3Dtk4Core6Logger13levelToStringENS1_8LogLevelE@Base 5.0.3 + _ZN3Dtk4Core6Logger14globalInstanceEv@Base 5.0.3 + _ZN3Dtk4Core6Logger15levelFromStringERK7QString@Base 5.0.3 + _ZN3Dtk4Core6Logger16registerAppenderEPNS0_16AbstractAppenderE@Base 5.0.3 + _ZN3Dtk4Core6Logger18setDefaultCategoryERK7QString@Base 5.0.3 + _ZN3Dtk4Core6Logger19logToGlobalInstanceERK7QStringb@Base 5.0.3 + _ZN3Dtk4Core6Logger24registerCategoryAppenderERK7QStringPNS0_16AbstractAppenderE@Base 5.0.3 + _ZN3Dtk4Core6Logger5writeENS1_8LogLevelEPKciS4_S4_@Base 5.0.3 + _ZN3Dtk4Core6Logger5writeENS1_8LogLevelEPKciS4_S4_RK7QString@Base 5.0.3 + _ZN3Dtk4Core6Logger5writeERK9QDateTimeNS1_8LogLevelEPKciS7_S7_RK7QString@Base 5.0.3 + _ZN3Dtk4Core6Logger5writeERK9QDateTimeNS1_8LogLevelEPKciS7_S7_RK7QStringb@Base 5.0.3 + _ZN3Dtk4Core6LoggerC1ERK7QString@Base 5.0.3 + _ZN3Dtk4Core6LoggerC1Ev@Base 5.0.3 + _ZN3Dtk4Core6LoggerC2ERK7QString@Base 5.0.3 + _ZN3Dtk4Core6LoggerC2Ev@Base 5.0.3 + _ZN3Dtk4Core6LoggerD1Ev@Base 5.0.3 + _ZN3Dtk4Core6LoggerD2Ev@Base 5.0.3 + _ZN3Dtk4Core7DObjectC1EPS1_@Base 5.0.3 + _ZN3Dtk4Core7DObjectC1ERNS0_14DObjectPrivateEPS1_@Base 5.0.3 + _ZN3Dtk4Core7DObjectC2EPS1_@Base 5.0.3 + _ZN3Dtk4Core7DObjectC2ERNS0_14DObjectPrivateEPS1_@Base 5.0.3 + _ZN3Dtk4Core7DObjectD0Ev@Base 5.0.3 + _ZN3Dtk4Core7DObjectD1Ev@Base 5.0.3 + _ZN3Dtk4Core7DObjectD2Ev@Base 5.0.3 + _ZN3Dtk4Core8DPathBufC1ERK7QString@Base 5.0.3 + _ZN3Dtk4Core8DPathBufC1Ev@Base 5.0.3 + _ZN3Dtk4Core8DPathBufC2ERK7QString@Base 5.0.3 + _ZN3Dtk4Core8DPathBufC2Ev@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo10deepinTypeEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo11productTypeEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo12computerNameEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo12cpuModelNameEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo13deepinEditionEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo13deepinVersionEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo14productVersionEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo14systemDiskSizeEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo15deepinCopyrightEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo15memoryTotalSizeEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo17productTypeStringEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo18isCommunityEditionEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo19distributionOrgLogoENS1_7OrgTypeENS1_8LogoTypeERK7QString@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo19distributionOrgNameENS1_7OrgTypeERK7QLocale@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo19memoryInstalledSizeEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo19operatingSystemNameEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo20distributionInfoPathEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo21deepinDistributorLogoENS1_8LogoTypeERK7QString@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo21deepinDistributorNameEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo21deepinTypeDisplayNameERK7QLocale@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo22distributionOrgWebsiteENS1_7OrgTypeE@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo24deepinDistributorWebsiteEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo26deepinDistributionInfoPathEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo27distributionInfoSectionNameENS1_7OrgTypeE@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo5isDDEEv@Base 5.0.3 + _ZN3Dtk4Core8DSysInfo8isDeepinEv@Base 5.0.3 + _ZN3Dtk4Core8doEscapeER7QStringRK5QHashI5QCharS4_E@Base 5.0.3 + _ZN3Dtk4Core9DSettings10setBackendEPNS0_16DSettingsBackendE@Base 5.0.3 + _ZN3Dtk4Core9DSettings11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3 + _ZN3Dtk4Core9DSettings11qt_metacastEPKc@Base 5.0.3 + _ZN3Dtk4Core9DSettings12fromJsonFileERK7QString@Base 5.0.3 + _ZN3Dtk4Core9DSettings12valueChangedERK7QStringRK8QVariant@Base 5.0.3 + _ZN3Dtk4Core9DSettings16staticMetaObjectE@Base 5.0.3 + _ZN3Dtk4Core9DSettings4syncEv@Base 5.0.3 + _ZN3Dtk4Core9DSettings5resetEv@Base 5.0.3 + _ZN3Dtk4Core9DSettings8fromJsonERK10QByteArray@Base 5.0.3 + _ZN3Dtk4Core9DSettings9loadValueEv@Base 5.0.3 + _ZN3Dtk4Core9DSettings9parseJsonERK10QByteArray@Base 5.0.3 + _ZN3Dtk4Core9DSettings9setOptionERK7QStringRK8QVariant@Base 5.0.3 + _ZN3Dtk4Core9DSettingsC1EP7QObject@Base 5.0.3 + _ZN3Dtk4Core9DSettingsC2EP7QObject@Base 5.0.3 + _ZN3Dtk4Core9DSettingsD0Ev@Base 5.0.3 + _ZN3Dtk4Core9DSettingsD1Ev@Base 5.0.3 + _ZN3Dtk4Core9DSettingsD2Ev@Base 5.0.3 + _ZN3Dtk4Core9LogDevice8readDataEPcx@Base 5.0.3 + _ZN3Dtk4Core9LogDevice9writeDataEPKcx@Base 5.0.3 + _ZN3Dtk4Core9LogDeviceD0Ev@Base 5.0.3 + _ZN3Dtk4Core9LogDeviceD1Ev@Base 5.0.3 + _ZN3Dtk4Core9LogDeviceD2Ev@Base 5.0.3 + _ZN3Dtk4Core9qtifyNameERK7QString@Base 5.0.3 + _ZN4QMapI7QString5QPairIS0_yEE13detach_helperEv@Base 5.0.3 + _ZN4QMapI7QString5QPairIS0_yEED1Ev@Base 5.0.3 + _ZN4QMapI7QString5QPairIS0_yEED2Ev@Base 5.0.3 + _ZN4QMapI7QString5QPairIS0_yEEixERKS0_@Base 5.0.3 + _ZN4QMapI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE13detach_helperEv@Base 5.0.3 + _ZN4QMapI7QString8QVariantE6insertERKS0_RKS1_@Base 5.0.3 + _ZN4QMapI7QString8QVariantEC1ERKS2_@Base 5.0.3 + _ZN4QMapI7QString8QVariantEC2ERKS2_@Base 5.0.3 + _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEE13detach_helperEv@Base 5.0.3 + _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEED1Ev@Base 5.0.3 + _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEED2Ev@Base 5.0.3 + _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEEixERKS0_@Base 5.0.3 + _ZN4QMapI7QStringPN3Dtk4Core12DFileWatcherEE13detach_helperEv@Base 5.0.3 + _ZN4QMapI7QStringS0_E13detach_helperEv@Base 5.0.3 + _ZN4QMapI7QStringS0_E6removeERKS0_@Base 5.0.3 + _ZN4QMapI7QStringS0_EC1ERKS1_@Base 5.0.3 + _ZN4QMapI7QStringS0_EC2ERKS1_@Base 5.0.3 + _ZN4QMapI7QStringS0_ED1Ev@Base 5.0.3 + _ZN4QMapI7QStringS0_ED2Ev@Base 5.0.3 + _ZN4QMapI7QStringS0_EixERKS0_@Base 5.0.3 + _ZN4QMapI7QStringiE13detach_helperEv@Base 5.0.3 + _ZN4QMapI7QStringiED1Ev@Base 5.0.3 + _ZN4QMapI7QStringiED2Ev@Base 5.0.3 + _ZN4QMapI9QDateTime7QStringED1Ev@Base 5.0.3 + _ZN4QMapI9QDateTime7QStringED2Ev@Base 5.0.3 + _ZN4QMapIPKvPyE13detach_helperEv@Base 5.0.3 + _ZN4QMapIPKvPyED1Ev@Base 5.0.3 + _ZN4QMapIPKvPyED2Ev@Base 5.0.3 + _ZN4QMapIPKvyE13detach_helperEv@Base 5.0.3 + _ZN4QMapIPKvyED1Ev@Base 5.0.3 + _ZN4QMapIPKvyED2Ev@Base 5.0.3 + _ZN4QMapIPPyS0_E13detach_helperEv@Base 5.0.3 + _ZN4QMapIPPyS0_ED1Ev@Base 5.0.3 + _ZN4QMapIPPyS0_ED2Ev@Base 5.0.3 + _ZN4QMapIi7QStringED1Ev@Base 5.0.3 + _ZN4QMapIi7QStringED2Ev@Base 5.0.3 + _ZN5QHashI5QCharS0_E11deleteNode2EPN9QHashData4NodeE@Base 5.0.3 + _ZN5QHashI5QCharS0_E13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3 + _ZN5QHashI5QCharS0_E6insertERKS0_S3_@Base 5.0.3 + _ZN5QHashI5QCharS0_ED1Ev@Base 5.0.3 + _ZN5QHashI5QCharS0_ED2Ev@Base 5.0.3 + _ZN5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3 + _ZN5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3 + _ZN5QHashI7QStringiE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3 + _ZN5QHashI7QStringiE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3 + _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3 + _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3 + _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueED1Ev@Base 5.0.3 + _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueED2Ev@Base 5.0.3 + _ZN5QHashIi15QHashDummyValueE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3 + _ZN5QHashIi15QHashDummyValueE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3 + _ZN5QHashIi7QStringE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3 + _ZN5QHashIi7QStringE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3 + _ZN5QHashIi7QStringE5eraseENS1_14const_iteratorE@Base 5.0.3 + _ZN5QHashIi7QStringED1Ev@Base 5.0.3 + _ZN5QHashIi7QStringED2Ev@Base 5.0.3 + _ZN5QHashIj7QStringE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3 + _ZN5QHashIj7QStringE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3 + _ZN5QHashIj7QStringED1Ev@Base 5.0.3 + _ZN5QHashIj7QStringED2Ev@Base 5.0.3 + _ZN5QListI10QByteArrayED1Ev@Base 5.0.3 + _ZN5QListI10QByteArrayED2Ev@Base 5.0.3 + _ZN5QListI4QUrlE13detach_helperEi@Base 5.0.3 + _ZN5QListI4QUrlE18detach_helper_growEii@Base 5.0.3 + _ZN5QListI4QUrlE6appendERKS0_@Base 5.0.3 + _ZN5QListI4QUrlEC1ERKS1_@Base 5.0.3 + _ZN5QListI4QUrlEC2ERKS1_@Base 5.0.3 + _ZN5QListI4QUrlED1Ev@Base 5.0.3 + _ZN5QListI4QUrlED2Ev@Base 5.0.3 + _ZN5QListI5QPairI7QStringiEE13detach_helperEi@Base 5.0.3 + _ZN5QListI5QPairI7QStringiEE18detach_helper_growEii@Base 5.0.3 + _ZN5QListI5QPairI7QStringiEE6appendERKS2_@Base 5.0.3 + _ZN5QListI5QPairI7QStringiEED1Ev@Base 5.0.3 + _ZN5QListI5QPairI7QStringiEED2Ev@Base 5.0.3 + _ZN5QListI5QPairIdiEE18detach_helper_growEii@Base 5.0.3 + _ZN5QListI5QPairIdiEE6appendERKS1_@Base 5.0.3 + _ZN5QListI5QPairIdiEED1Ev@Base 5.0.3 + _ZN5QListI5QPairIdiEED2Ev@Base 5.0.3 + _ZN5QListI7QStringE13detach_helperEi@Base 5.0.3 + _ZN5QListI7QStringE18detach_helper_growEii@Base 5.0.3 + _ZN5QListI7QStringE6appendERKS0_@Base 5.0.3 + _ZN5QListI7QStringE7reserveEi@Base 5.0.3 + _ZN5QListI7QStringE9removeAllERKS0_@Base 5.0.3 + _ZN5QListI7QStringEC1ERKS1_@Base 5.0.3 + _ZN5QListI7QStringEC2ERKS1_@Base 5.0.3 + _ZN5QListI7QStringED1Ev@Base 5.0.3 + _ZN5QListI7QStringED2Ev@Base 5.0.3 + _ZN5QListI8QPointerIN3Dtk4Core14DSettingsGroupEEE13detach_helperEi@Base 5.0.3 + _ZN5QListI8QPointerIN3Dtk4Core14DSettingsGroupEEE18detach_helper_growEii@Base 5.0.3 + _ZN5QListI8QPointerIN3Dtk4Core14DSettingsGroupEEE6appendERKS4_@Base 5.0.3 + _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEE13detach_helperEi@Base 5.0.3 + _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEE18detach_helper_growEii@Base 5.0.3 + _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEE6appendERKS4_@Base 5.0.3 + _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEED1Ev@Base 5.0.3 + _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEED2Ev@Base 5.0.3 + _ZN5QListI8QVariantE13detach_helperEi@Base 5.0.3 + _ZN5QListI8QVariantE18detach_helper_growEii@Base 5.0.3 + _ZN5QListI8QVariantE6appendERKS0_@Base 5.0.3 + _ZN5QListI8QVariantEC1ERKS1_@Base 5.0.3 + _ZN5QListI8QVariantEC2ERKS1_@Base 5.0.3 + _ZN5QListI8QVariantED1Ev@Base 5.0.3 + _ZN5QListI8QVariantED2Ev@Base 5.0.3 + _ZN5QListI9QFileInfoE13detach_helperEi@Base 5.0.3 + _ZN5QListI9QFileInfoED1Ev@Base 5.0.3 + _ZN5QListI9QFileInfoED2Ev@Base 5.0.3 + _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEE13detach_helperEi@Base 5.0.3 + _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEE18detach_helper_growEii@Base 5.0.3 + _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEE6appendERKS3_@Base 5.0.3 + _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEED1Ev@Base 5.0.3 + _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEED2Ev@Base 5.0.3 + _ZN5QListIP13inotify_eventE13detach_helperEi@Base 5.0.3 + _ZN5QListIP13inotify_eventE18detach_helper_growEii@Base 5.0.3 + _ZN5QListIP13inotify_eventE6appendERKS1_@Base 5.0.3 + _ZN5QListIP13inotify_eventED1Ev@Base 5.0.3 + _ZN5QListIP13inotify_eventED2Ev@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16AbstractAppenderEE18detach_helper_growEii@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16AbstractAppenderEE6appendERKS3_@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16AbstractAppenderEEC1ERKS4_@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16AbstractAppenderEEC2ERKS4_@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16AbstractAppenderEED1Ev@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16AbstractAppenderEED2Ev@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE13detach_helperEi@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE18detach_helper_growEii@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE6appendERKS3_@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE9removeOneERKS3_@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEED1Ev@Base 5.0.3 + _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEED2Ev@Base 5.0.3 + _ZN5QPairISt8functionIF8QVariant7QStringEES2_ED1Ev@Base 5.0.3 + _ZN5QPairISt8functionIF8QVariant7QStringEES2_ED2Ev@Base 5.0.3 + _ZN7QStringC1EPKc@Base 5.0.3 + _ZN7QStringC2EPKc@Base 5.0.3 + _ZN7QStringD1Ev@Base 5.0.3 + _ZN7QStringD2Ev@Base 5.0.3 + _ZN8QMapDataI7QStringN3Dtk4Core20DDesktopEntrySectionEE10createNodeERKS0_RKS3_P8QMapNodeIS0_S3_Eb@Base 5.0.3 + _ZN8QMapDataI7QStringPN3Dtk4Core12DFileWatcherEE7destroyEv@Base 5.0.3 + _ZN8QMapDataI7QStringPN3Dtk4Core16AbstractAppenderEE7destroyEv@Base 5.0.3 + _ZN8QMapDataI7QStringbE7destroyEv@Base 5.0.3 + _ZN8QMapDataI7QStringiE7destroyEv@Base 5.0.3 + _ZN8QMapDataI9QDateTime7QStringE7destroyEv@Base 5.0.3 + _ZN8QMapDataIi7QStringE7destroyEv@Base 5.0.3 + _ZN8QMapNodeI7QString5QPairIS0_yEE14destroySubTreeEv@Base 5.0.3 + _ZN8QMapNodeI7QString8QPointerIN3Dtk4Core14DSettingsGroupEEE14destroySubTreeEv@Base 5.0.3 + _ZN8QMapNodeI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE14destroySubTreeEv@Base 5.0.3 + _ZN8QMapNodeI7QString8QVariantE14destroySubTreeEv@Base 5.0.3 + _ZN8QMapNodeI7QStringN3Dtk4Core20DDesktopEntrySectionEE14destroySubTreeEv@Base 5.0.3 + _ZN8QMapNodeI7QStringPN3Dtk4Core12DFileWatcherEE14destroySubTreeEv@Base 5.0.3 + _ZN8QMapNodeI7QStringPN3Dtk4Core16AbstractAppenderEE14destroySubTreeEv@Base 5.0.3 + _ZN8QMapNodeI7QStringS0_E14destroySubTreeEv@Base 5.0.3 + _ZN8QMapNodeI7QStringbE14destroySubTreeEv@Base 5.0.3 + _ZN8QMapNodeI7QStringiE14destroySubTreeEv@Base 5.0.3 + _ZN8QMapNodeI9QDateTime7QStringE14destroySubTreeEv@Base 5.0.3 + _ZN8QMapNodeIi7QStringE14destroySubTreeEv@Base 5.0.3 + _ZN9DDBusDataC1Ev@Base 5.0.3 + _ZN9DDBusDataC2Ev@Base 5.0.3 + _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core12DFileWatcherEFvRK7QStringS6_ENS_4ListIJS6_S6_EEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3 + _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core12DFileWatcherEFvRK7QStringS6_S6_S6_ENS_4ListIJS6_S6_S6_S6_EEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3 + _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core16DSettingsBackendEFvRK7QStringRK8QVariantENS_4ListIJS6_S9_EEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3 + _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core16DSettingsBackendEFvvENS_4ListIJEEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3 + _ZN9QtPrivate8RefCount3refEv@Base 5.0.3 + _ZNK3Dtk4Core12DFileWatcher10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core12FileAppender4sizeEv@Base 5.0.3 + _ZNK3Dtk4Core12FileAppender8fileNameEv@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry11genericNameEv@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry11stringValueERK7QStringS4_S4_@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry14ddeDisplayNameEv@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry14localizedValueERK7QStringRK7QLocaleS4_S4_@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry14localizedValueERK7QStringS4_S4_S4_@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry15stringListValueERK7QStringS4_@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry4keysERK7QString@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry4nameEv@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry4saveEv@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry6statusEv@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry7commentEv@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry8containsERK7QStringS4_@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry8rawValueERK7QStringS4_S4_@Base 5.0.3 + _ZNK3Dtk4Core13DDesktopEntry9allGroupsEb@Base 5.0.3 + _ZNK3Dtk4Core13DTrashManager12trashIsEmptyEv@Base 5.0.3 + _ZNK3Dtk4Core14DSettingsGroup10childGroupERK7QString@Base 5.0.3 + _ZNK3Dtk4Core14DSettingsGroup10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core14DSettingsGroup11childGroupsEv@Base 5.0.3 + _ZNK3Dtk4Core14DSettingsGroup11parentGroupEv@Base 5.0.3 + _ZNK3Dtk4Core14DSettingsGroup12childOptionsEv@Base 5.0.3 + _ZNK3Dtk4Core14DSettingsGroup3keyEv@Base 5.0.3 + _ZNK3Dtk4Core14DSettingsGroup4nameEv@Base 5.0.3 + _ZNK3Dtk4Core14DSettingsGroup6optionERK7QString@Base 5.0.3 + _ZNK3Dtk4Core14DSettingsGroup7optionsEv@Base 5.0.3 + _ZNK3Dtk4Core14DSettingsGroup8isHiddenEv@Base 5.0.3 + _ZNK3Dtk4Core15ConsoleAppender6formatEv@Base 5.0.3 + _ZNK3Dtk4Core15DSettingsOption10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core15DSettingsOption11parentGroupEv@Base 5.0.3 + _ZNK3Dtk4Core15DSettingsOption12defaultValueEv@Base 5.0.3 + _ZNK3Dtk4Core15DSettingsOption3keyEv@Base 5.0.3 + _ZNK3Dtk4Core15DSettingsOption4dataERK7QString@Base 5.0.3 + _ZNK3Dtk4Core15DSettingsOption4nameEv@Base 5.0.3 + _ZNK3Dtk4Core15DSettingsOption5valueEv@Base 5.0.3 + _ZNK3Dtk4Core15DSettingsOption8canResetEv@Base 5.0.3 + _ZNK3Dtk4Core15DSettingsOption8isHiddenEv@Base 5.0.3 + _ZNK3Dtk4Core15DSettingsOption8viewTypeEv@Base 5.0.3 + _ZNK3Dtk4Core15QSettingBackend10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core15QSettingBackend4keysEv@Base 5.0.3 + _ZNK3Dtk4Core15QSettingBackend9getOptionERK7QString@Base 5.0.3 + _ZNK3Dtk4Core16AbstractAppender12detailsLevelEv@Base 5.0.3 + _ZNK3Dtk4Core16DBaseFileWatcher10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core16DBaseFileWatcher7fileUrlEv@Base 5.0.3 + _ZNK3Dtk4Core16DSettingsBackend10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core16GSettingsBackend10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core16GSettingsBackend4keysEv@Base 5.0.3 + _ZNK3Dtk4Core16GSettingsBackend9getOptionERK7QString@Base 5.0.3 + _ZNK3Dtk4Core17CuteMessageLogger5writeEPKcz@Base 5.0.3 + _ZNK3Dtk4Core17CuteMessageLogger5writeERK7QString@Base 5.0.3 + _ZNK3Dtk4Core17CuteMessageLogger5writeEv@Base 5.0.3 + _ZNK3Dtk4Core18DDiskSizeFormatter15unitConvertRateEi@Base 5.0.3 + _ZNK3Dtk4Core18DDiskSizeFormatter7unitMaxEv@Base 5.0.3 + _ZNK3Dtk4Core18DDiskSizeFormatter7unitMinEv@Base 5.0.3 + _ZNK3Dtk4Core18DDiskSizeFormatter7unitStrEi@Base 5.0.3 + _ZNK3Dtk4Core18DFileSystemWatcher10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core18DFileSystemWatcher11directoriesEv@Base 5.0.3 + _ZNK3Dtk4Core18DFileSystemWatcher5filesEv@Base 5.0.3 + _ZNK3Dtk4Core18DTimeUnitFormatter15unitConvertRateEi@Base 5.0.3 + _ZNK3Dtk4Core18DTimeUnitFormatter7unitMaxEv@Base 5.0.3 + _ZNK3Dtk4Core18DTimeUnitFormatter7unitMinEv@Base 5.0.3 + _ZNK3Dtk4Core18DTimeUnitFormatter7unitStrEi@Base 5.0.3 + _ZNK3Dtk4Core19DFileWatcherManager10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core19RollingFileAppender11datePatternEv@Base 5.0.3 + _ZNK3Dtk4Core19RollingFileAppender13logFilesLimitEv@Base 5.0.3 + _ZNK3Dtk4Core19RollingFileAppender17datePatternStringEv@Base 5.0.3 + _ZNK3Dtk4Core20DDesktopEntryPrivate10isWritableEv@Base 5.0.3 + _ZNK3Dtk4Core20DDesktopEntryPrivate10sectionPosERK7QString@Base 5.0.3 + _ZNK3Dtk4Core20DDesktopEntryPrivate4keysERK7QString@Base 5.0.3 + _ZNK3Dtk4Core20DDesktopEntryPrivate5writeER9QIODevice@Base 5.0.3 + _ZNK3Dtk4Core20DDesktopEntryPrivate8containsERK7QStringS4_@Base 5.0.3 + _ZNK3Dtk4Core20DDesktopEntryPrivate9setStatusERKNS0_13DDesktopEntry6StatusE@Base 5.0.3 + _ZNK3Dtk4Core22AbstractStringAppender15formattedStringERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3 + _ZNK3Dtk4Core22AbstractStringAppender6formatEv@Base 5.0.3 + _ZNK3Dtk4Core22DAbstractUnitFormatter12unitValueMaxEi@Base 5.0.3 + _ZNK3Dtk4Core22DAbstractUnitFormatter12unitValueMinEi@Base 5.0.3 + _ZNK3Dtk4Core22DAbstractUnitFormatter16formatAsUnitListEdi@Base 5.0.3 + _ZNK3Dtk4Core22DAbstractUnitFormatter6formatEdi@Base 5.0.3 + _ZNK3Dtk4Core22DAbstractUnitFormatter8formatAsEdii@Base 5.0.3 + _ZNK3Dtk4Core5DUtil18DExportedInterface10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core5DUtil18DExportedInterface6invokeERK7QStringS5_@Base 5.0.3 + _ZNK3Dtk4Core5DUtil31DExportedInterfaceDBusInterface10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core6Logger15defaultCategoryEv@Base 5.0.3 + _ZNK3Dtk4Core9DSettings10metaObjectEv@Base 5.0.3 + _ZNK3Dtk4Core9DSettings4keysEv@Base 5.0.3 + _ZNK3Dtk4Core9DSettings4metaEv@Base 5.0.3 + _ZNK3Dtk4Core9DSettings5groupERK7QString@Base 5.0.3 + _ZNK3Dtk4Core9DSettings5valueERK7QString@Base 5.0.3 + _ZNK3Dtk4Core9DSettings6groupsEv@Base 5.0.3 + _ZNK3Dtk4Core9DSettings6optionERK7QString@Base 5.0.3 + _ZNK3Dtk4Core9DSettings7optionsEv@Base 5.0.3 + _ZNK3Dtk4Core9DSettings9getOptionERK7QString@Base 5.0.3 + _ZNK3Dtk4Core9DSettings9groupKeysEv@Base 5.0.3 + _ZNK4QMapI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE6valuesEv@Base 5.0.3 + _ZNK4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEE4keysEv@Base 5.0.3 + _ZNK4QMapIi7QStringE6valuesERKi@Base 5.0.3 + _ZNK5QHashI5QCharS0_E8findNodeERKS0_Pj@Base 5.0.3 + _ZNK5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE4keysEv@Base 5.0.3 + _ZNK5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE8findNodeERKS0_Pj@Base 5.0.3 + _ZNK5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE8findNodeERKS0_j@Base 5.0.3 + _ZNK5QHashI7QStringiE8findNodeERKS0_Pj@Base 5.0.3 + _ZNK5QHashI7QStringiE8findNodeERKS0_j@Base 5.0.3 + _ZNK5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueE8findNodeERKS3_Pj@Base 5.0.3 + _ZNK5QHashIi15QHashDummyValueE8findNodeERKiPj@Base 5.0.3 + _ZNK5QHashIi7QStringE6valuesERKi@Base 5.0.3 + _ZNK5QHashIi7QStringE8findNodeERKiPj@Base 5.0.3 + _ZNK5QHashIj7QStringE8findNodeERKjPj@Base 5.0.3 + _ZNK5QListIPN3Dtk4Core16AbstractAppenderEE5toSetEv@Base 5.0.3 + _ZNK8QMapDataI7QString8QPointerIN3Dtk4Core14DSettingsGroupEEE8findNodeERKS0_@Base 5.0.3 + _ZNK8QMapDataI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE8findNodeERKS0_@Base 5.0.3 + _ZNK8QMapDataI7QString8QVariantE8findNodeERKS0_@Base 5.0.3 + _ZNK8QMapDataI7QStringN3Dtk4Core20DDesktopEntrySectionEE8findNodeERKS0_@Base 5.0.3 + _ZNK8QMapDataI7QStringPN3Dtk4Core12DFileWatcherEE8findNodeERKS0_@Base 5.0.3 + _ZNK8QMapDataI7QStringPN3Dtk4Core16AbstractAppenderEE8findNodeERKS0_@Base 5.0.3 + _ZNK8QMapDataI7QStringS0_E8findNodeERKS0_@Base 5.0.3 + _ZNK8QMapDataI7QStringiE8findNodeERKS0_@Base 5.0.3 + _ZNK8QMapDataIPKvPyE8findNodeERKS1_@Base 5.0.3 + _ZNK8QMapDataIPKvyE8findNodeERKS1_@Base 5.0.3 + _ZNK8QMapNodeI7QString5QPairIS0_yEE4copyEP8QMapDataIS0_S2_E@Base 5.0.3 + _ZNK8QMapNodeI7QString8QPointerIN3Dtk4Core14DSettingsGroupEEE4copyEP8QMapDataIS0_S5_E@Base 5.0.3 + _ZNK8QMapNodeI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE4copyEP8QMapDataIS0_S5_E@Base 5.0.3 + _ZNK8QMapNodeI7QString8QVariantE4copyEP8QMapDataIS0_S1_E@Base 5.0.3 + _ZNK8QMapNodeI7QStringN3Dtk4Core20DDesktopEntrySectionEE4copyEP8QMapDataIS0_S3_E@Base 5.0.3 + _ZNK8QMapNodeI7QStringPN3Dtk4Core12DFileWatcherEE4copyEP8QMapDataIS0_S4_E@Base 5.0.3 + _ZNK8QMapNodeI7QStringPN3Dtk4Core16AbstractAppenderEE4copyEP8QMapDataIS0_S4_E@Base 5.0.3 + _ZNK8QMapNodeI7QStringS0_E4copyEP8QMapDataIS0_S0_E@Base 5.0.3 + _ZNK8QMapNodeI7QStringbE4copyEP8QMapDataIS0_bE@Base 5.0.3 + _ZNK8QMapNodeI7QStringiE4copyEP8QMapDataIS0_iE@Base 5.0.3 + _ZNK8QMapNodeI9QDateTime7QStringE4copyEP8QMapDataIS0_S1_E@Base 5.0.3 + _ZNK8QMapNodeIPKvPyE4copyEP8QMapDataIS1_S2_E@Base 5.0.3 + _ZNK8QMapNodeIPKvyE4copyEP8QMapDataIS1_yE@Base 5.0.3 + _ZNK8QMapNodeIPPyS0_E4copyEP8QMapDataIS1_S0_E@Base 5.0.3 + _ZNK8QMapNodeIi7QStringE4copyEP8QMapDataIiS0_E@Base 5.0.3 + _ZNKSt5ctypeIcE8do_widenEc@Base 5.0.3 + _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE10_M_releaseEv@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE10_M_destroyEv@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE10_M_disposeEv@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EED0Ev@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EED1Ev@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EED2Ev@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE10_M_destroyEv@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE10_M_disposeEv@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EED0Ev@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EED1Ev@Base 5.0.3 + _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EED2Ev@Base 5.0.3 + _ZNSt8functionIF8QVariant7QStringEEC1ERKS3_@Base 5.0.3 + _ZNSt8functionIF8QVariant7QStringEEC2ERKS3_@Base 5.0.3 + _ZSt9__find_ifIPK7QStringN9__gnu_cxx5__ops16_Iter_equals_valIS1_EEET_S7_S7_T0_St26random_access_iterator_tag@Base 5.0.3 + _ZTI12QDBusContext@Base 5.0.3 + _ZTIN3Dtk4Core12DFileWatcherE@Base 5.0.3 + _ZTIN3Dtk4Core12FileAppenderE@Base 5.0.3 + _ZTIN3Dtk4Core13DTrashManagerE@Base 5.0.3 + _ZTIN3Dtk4Core14DObjectPrivateE@Base 5.0.3 + _ZTIN3Dtk4Core14DSettingsGroupE@Base 5.0.3 + _ZTIN3Dtk4Core14DTrashManager_E@Base 5.0.3 + _ZTIN3Dtk4Core15ConsoleAppenderE@Base 5.0.3 + _ZTIN3Dtk4Core15DSettingsOptionE@Base 5.0.3 + _ZTIN3Dtk4Core15QSettingBackendE@Base 5.0.3 + _ZTIN3Dtk4Core16AbstractAppenderE@Base 5.0.3 + _ZTIN3Dtk4Core16DBaseFileWatcherE@Base 5.0.3 + _ZTIN3Dtk4Core16DSettingsBackendE@Base 5.0.3 + _ZTIN3Dtk4Core16GSettingsBackendE@Base 5.0.3 + _ZTIN3Dtk4Core18DDiskSizeFormatterE@Base 5.0.3 + _ZTIN3Dtk4Core18DFileSystemWatcherE@Base 5.0.3 + _ZTIN3Dtk4Core18DTimeUnitFormatterE@Base 5.0.3 + _ZTIN3Dtk4Core19DFileWatcherManagerE@Base 5.0.3 + _ZTIN3Dtk4Core19DFileWatcherPrivateE@Base 5.0.3 + _ZTIN3Dtk4Core19RollingFileAppenderE@Base 5.0.3 + _ZTIN3Dtk4Core20DTrashManagerPrivateE@Base 5.0.3 + _ZTIN3Dtk4Core22AbstractStringAppenderE@Base 5.0.3 + _ZTIN3Dtk4Core22DAbstractUnitFormatterE@Base 5.0.3 + _ZTIN3Dtk4Core23DBaseFileWatcherPrivateE@Base 5.0.3 + _ZTIN3Dtk4Core25DFileSystemWatcherPrivateE@Base 5.0.3 + _ZTIN3Dtk4Core26DFileWatcherManagerPrivateE@Base 5.0.3 + _ZTIN3Dtk4Core5DUtil18DExportedInterfaceE@Base 5.0.3 + _ZTIN3Dtk4Core5DUtil25DExportedInterfacePrivateE@Base 5.0.3 + _ZTIN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceE@Base 5.0.3 + _ZTIN3Dtk4Core7DObjectE@Base 5.0.3 + _ZTIN3Dtk4Core9DSettingsE@Base 5.0.3 + _ZTIN3Dtk4Core9LogDeviceE@Base 5.0.3 + _ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3 + _ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3 + _ZTISt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3 + _ZTISt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3 + _ZTS12QDBusContext@Base 5.0.3 + _ZTSN3Dtk4Core12DFileWatcherE@Base 5.0.3 + _ZTSN3Dtk4Core12FileAppenderE@Base 5.0.3 + _ZTSN3Dtk4Core13DTrashManagerE@Base 5.0.3 + _ZTSN3Dtk4Core14DObjectPrivateE@Base 5.0.3 + _ZTSN3Dtk4Core14DSettingsGroupE@Base 5.0.3 + _ZTSN3Dtk4Core14DTrashManager_E@Base 5.0.3 + _ZTSN3Dtk4Core15ConsoleAppenderE@Base 5.0.3 + _ZTSN3Dtk4Core15DSettingsOptionE@Base 5.0.3 + _ZTSN3Dtk4Core15QSettingBackendE@Base 5.0.3 + _ZTSN3Dtk4Core16AbstractAppenderE@Base 5.0.3 + _ZTSN3Dtk4Core16DBaseFileWatcherE@Base 5.0.3 + _ZTSN3Dtk4Core16DSettingsBackendE@Base 5.0.3 + _ZTSN3Dtk4Core16GSettingsBackendE@Base 5.0.3 + _ZTSN3Dtk4Core18DDiskSizeFormatterE@Base 5.0.3 + _ZTSN3Dtk4Core18DFileSystemWatcherE@Base 5.0.3 + _ZTSN3Dtk4Core18DTimeUnitFormatterE@Base 5.0.3 + _ZTSN3Dtk4Core19DFileWatcherManagerE@Base 5.0.3 + _ZTSN3Dtk4Core19DFileWatcherPrivateE@Base 5.0.3 + _ZTSN3Dtk4Core19RollingFileAppenderE@Base 5.0.3 + _ZTSN3Dtk4Core20DTrashManagerPrivateE@Base 5.0.3 + _ZTSN3Dtk4Core22AbstractStringAppenderE@Base 5.0.3 + _ZTSN3Dtk4Core22DAbstractUnitFormatterE@Base 5.0.3 + _ZTSN3Dtk4Core23DBaseFileWatcherPrivateE@Base 5.0.3 + _ZTSN3Dtk4Core25DFileSystemWatcherPrivateE@Base 5.0.3 + _ZTSN3Dtk4Core26DFileWatcherManagerPrivateE@Base 5.0.3 + _ZTSN3Dtk4Core5DUtil18DExportedInterfaceE@Base 5.0.3 + _ZTSN3Dtk4Core5DUtil25DExportedInterfacePrivateE@Base 5.0.3 + _ZTSN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceE@Base 5.0.3 + _ZTSN3Dtk4Core7DObjectE@Base 5.0.3 + _ZTSN3Dtk4Core9DSettingsE@Base 5.0.3 + _ZTSN3Dtk4Core9LogDeviceE@Base 5.0.3 + _ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3 + _ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3 + _ZTSSt19_Sp_make_shared_tag@Base 5.0.3 + _ZTSSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3 + _ZTSSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3 + _ZTVN3Dtk4Core12DFileWatcherE@Base 5.0.3 + _ZTVN3Dtk4Core12FileAppenderE@Base 5.0.3 + _ZTVN3Dtk4Core13DTrashManagerE@Base 5.0.3 + _ZTVN3Dtk4Core14DObjectPrivateE@Base 5.0.3 + _ZTVN3Dtk4Core14DSettingsGroupE@Base 5.0.3 + _ZTVN3Dtk4Core14DTrashManager_E@Base 5.0.3 + _ZTVN3Dtk4Core15ConsoleAppenderE@Base 5.0.3 + _ZTVN3Dtk4Core15DSettingsOptionE@Base 5.0.3 + _ZTVN3Dtk4Core15QSettingBackendE@Base 5.0.3 + _ZTVN3Dtk4Core16AbstractAppenderE@Base 5.0.3 + _ZTVN3Dtk4Core16DBaseFileWatcherE@Base 5.0.3 + _ZTVN3Dtk4Core16DSettingsBackendE@Base 5.0.3 + _ZTVN3Dtk4Core16GSettingsBackendE@Base 5.0.3 + _ZTVN3Dtk4Core18DDiskSizeFormatterE@Base 5.0.3 + _ZTVN3Dtk4Core18DFileSystemWatcherE@Base 5.0.3 + _ZTVN3Dtk4Core18DTimeUnitFormatterE@Base 5.0.3 + _ZTVN3Dtk4Core19DFileWatcherManagerE@Base 5.0.3 + _ZTVN3Dtk4Core19DFileWatcherPrivateE@Base 5.0.3 + _ZTVN3Dtk4Core19RollingFileAppenderE@Base 5.0.3 + _ZTVN3Dtk4Core20DTrashManagerPrivateE@Base 5.0.3 + _ZTVN3Dtk4Core22AbstractStringAppenderE@Base 5.0.3 + _ZTVN3Dtk4Core22DAbstractUnitFormatterE@Base 5.0.3 + _ZTVN3Dtk4Core23DBaseFileWatcherPrivateE@Base 5.0.3 + _ZTVN3Dtk4Core25DFileSystemWatcherPrivateE@Base 5.0.3 + _ZTVN3Dtk4Core26DFileWatcherManagerPrivateE@Base 5.0.3 + _ZTVN3Dtk4Core5DUtil18DExportedInterfaceE@Base 5.0.3 + _ZTVN3Dtk4Core5DUtil25DExportedInterfacePrivateE@Base 5.0.3 + _ZTVN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceE@Base 5.0.3 + _ZTVN3Dtk4Core7DObjectE@Base 5.0.3 + _ZTVN3Dtk4Core9DSettingsE@Base 5.0.3 + _ZTVN3Dtk4Core9LogDeviceE@Base 5.0.3 + _ZTVSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3 + _ZTVSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3 + _ZThn16_N3Dtk4Core12DFileWatcherD0Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core12DFileWatcherD1Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core13DTrashManagerD0Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core13DTrashManagerD1Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core14DTrashManager_D0Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core14DTrashManager_D1Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core16DBaseFileWatcherD0Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core16DBaseFileWatcherD1Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core18DFileSystemWatcherD0Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core18DFileSystemWatcherD1Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core19DFileWatcherManagerD0Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core19DFileWatcherManagerD1Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core5DUtil18DExportedInterfaceD0Ev@Base 5.0.3 + _ZThn16_N3Dtk4Core5DUtil18DExportedInterfaceD1Ev@Base 5.0.3 + _ZZN3Dtk4Core11DLogManager8instanceEvE8instance@Base 5.0.3 + _ZZN9QtPrivate15ConnectionTypesINS_4ListIJRK7QStringRK8QVariantEEELb1EE5typesEvE1t@Base 5.0.3 + _ZZNSt19_Sp_make_shared_tag5_S_tiEvE5__tag@Base 5.0.3 diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 0000000..0abf026 --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required (VERSION 3.10) + +find_package (Doxygen REQUIRED) + +set (QCH_INSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/qt5/doc CACHE STRING "QCH install location") + +set (DOXYGEN_GENERATE_HTML YES CACHE STRING "Doxygen HTML output") +set (DOXYGEN_GENERATE_XML YES CACHE STRING "Doxygen XML output") +set (DOXYGEN_GENERATE_QHP YES CACHE STRING "Doxygen QHP output") +set (DOXYGEN_FILE_PATTERNS *.cpp *.h *.zh_CN.md *.zh_CN.dox CACHE STRING "Doxygen File Patterns") +set (DOXYGEN_PROJECT_NUMBER ${CMAKE_PROJECT_VERSION} CACHE STRING "") # Should be the same as this project is using. +set (DOXYGEN_EXTRACT_STATIC YES) +set (DOXYGEN_OUTPUT_LANGUAGE "Chinese") +set (DOXYGEN_QHG_LOCATION "qhelpgenerator") +set (DOXYGEN_QHP_NAMESPACE "org.deepin.dtk.core") +set (DOXYGEN_QCH_FILE "dtkcore.qch") +set (DOXYGEN_QHP_VIRTUAL_FOLDER "dtkcore") +set (DOXYGEN_HTML_EXTRA_STYLESHEET "" CACHE STRING "Doxygen custom stylesheet for HTML output") +set (DOXYGEN_TAGFILES "qtcore.tags=qthelp://org.qt-project.qtcore/qtcore/" CACHE STRING "Doxygen tag files") + +set (DOXYGEN_MACRO_EXPANSION "YES") +set (DOXYGEN_PREDEFINED + "\"DCORE_BEGIN_NAMESPACE=namespace Dtk { namespace Core {\"" + "\"DCORE_END_NAMESPACE=}}\"" + "\"DCORE_USE_NAMESPACE=using namespace Dtk::Core\;\"" + "Q_OS_LINUX=1" +) +set (DOXYGEN_EXPAND_ONLY_PREDEF "YES") + +if(BUILD_THEME) +set (DOXYGEN_HTML_EXTRA_STYLESHEET "docs/doxygentheme/doxygen-awesome.css" + "docs/doxygentheme/doxygen-awesome-sidebar-only.css" + "docs/doxygentheme/doxygen-awesome-sidebar-only-darkmode-toggle.css" + ) +set (DOXYGEN_HTML_EXTRA_FILES "docs/doxygentheme/doxygen-awesome-darkmode-toggle.js" + "docs/doxygentheme/doxygen-awesome-fragment-copy-button.js" + "docs/doxygentheme/doxygen-awesome-paragraph-link.js" + "docs/doxygentheme/doxygen-awesome-interactive-toc.js" + ) +set (DOXYGEN_GENERATE_TREEVIEW "YES") +set (DOXYGEN_DISABLE_INDEX "NO") +set (DOXYGEN_FULL_SIDEBAR "NO") +set (DOXYGEN_HTML_HEADER "docs/doxygentheme/header.html") +endif() + +doxygen_add_docs (doxygen + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/docs + ALL + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMENT "Generate documentation via Doxygen" +) + +install (FILES ${PROJECT_BINARY_DIR}/docs/html/dtkcore.qch DESTINATION ${QCH_INSTALL_DESTINATION}) diff --git a/docs/Specification.md b/docs/Specification.md new file mode 100644 index 0000000..94bf3a8 --- /dev/null +++ b/docs/Specification.md @@ -0,0 +1,81 @@ +# Deepin Application Specification {#Specification} + +Every application should keep the rule in this document. + +## 1. Application Information + +Application should set the property Organization-name and Application-name. + +Organization/Application name can contains alphabet, number and other visible ASCII code, BUT space MUST NOT appear in the name. And we do not approve +of no-ASCII code character in the Organization/Application name + +Application can stay Organization-name empty, but it should always set an Application-name. + +## 2. Standard Path + +The log, configure and runtime cache of Application should store in specific path. + +**If Organization-name is empty, "{Organization-name}/" would not appear in path.** + +### 2.1 User Application Standard Path + +As an application run for user session, the Standard path should be: + +````bash +XGD_LOG_HOME_DEEPIN: + Where deepin-user-specific log should be written. + XGD_USER_HOME/.log + +DAPP_CONFIG_HOME: + Where application-specific configurations should be written. + XDG_CONFIG_HOME/{Organization-name}/{Application-name} + +DAPP_LOG_HOME: + Where application-specific log should be written. + XGD_LOG_HOME_DEEPIN/{Organization-name}/{Application-name} + +DAPP_CACHE_HOME: + Where application-specific cache files should be written. + XGD_CACHE_HOME/{Organization-name}/{Application-name} + +DAPP_DATA_HOME: + Where application-specific data files should be written. + $XDG_DATA_HOME/{Organization-name}/{Application-name} + +```` + +Simple example: + +The dde-dock is offcial application of deepin and the standard path will be: + +````bash +DAPP_CONFIG_HOME: $HOME/.config/deepin/dde-dock +DAPP_LOG_HOME: $HOME/.log/deepin/dde-dock +DAPP_CACHE_HOME: $HOME/.cache/deepin/dde-dock +DAPP_DATA_HOME: $HOME/.local/share/deepin/dde-dock +```` + +### 2.2 System Application Standard Path + +Application run as system daemon, or with user with no home directory should place it's file in this standard path: + +````bash +DAPP_CONFIG_SYS: + /etc/{Organization-name}/{Application-name} + +DAPP_LOG_SYS: + /var/log/{Organization-name}/{Application-name} + +DAPP_DATA_SYS: + /usr/share/{Organization-name}/{Application-name} + +DAPP_CACHE_SYS: + /var/cache/{Organization-name}/{Application-name} +```` + + +Refs: + +[XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.8.html) + +[XDG Base Directory support](https://wiki.archlinux.org/index.php/XDG_Base_Directory_support) diff --git a/docs/doxygentheme/custom-alternative.css b/docs/doxygentheme/custom-alternative.css new file mode 100644 index 0000000..e66c1ae --- /dev/null +++ b/docs/doxygentheme/custom-alternative.css @@ -0,0 +1,54 @@ +html.alternative { + /* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */ + --primary-color: #AF7FE4; + --primary-dark-color: #9270E4; + --primary-light-color: #7aabd6; + --primary-lighter-color: #cae1f1; + --primary-lightest-color: #e9f1f8; + + /* page base colors */ + --page-background-color: white; + --page-foreground-color: #2c3e50; + --page-secondary-foreground-color: #67727e; + + + --border-radius-large: 22px; + --border-radius-small: 9px; + --border-radius-medium: 14px; + --spacing-small: 8px; + --spacing-medium: 14px; + --spacing-large: 19px; + + --top-height: 125px; + + --side-nav-background: #324067; + --side-nav-foreground: #F1FDFF; + --header-foreground: var(--side-nav-foreground); + --searchbar-background: var(--side-nav-foreground); + --searchbar-border-radius: var(--border-radius-medium); + --header-background: var(--side-nav-background); + --header-foreground: var(--side-nav-foreground); + + --toc-background: rgb(243, 240, 252); + --toc-foreground: var(--page-foreground-color); +} + +html.alternative.dark-mode { + color-scheme: dark; + + --primary-color: #AF7FE4; + --primary-dark-color: #9270E4; + --primary-light-color: #4779ac; + --primary-lighter-color: #191e21; + --primary-lightest-color: #191a1c; + + --page-background-color: #1C1D1F; + --page-foreground-color: #d2dbde; + --page-secondary-foreground-color: #859399; + --separator-color: #3a3246; + --side-nav-background: #171D32; + --side-nav-foreground: #F1FDFF; + --toc-background: #20142C; + --searchbar-background: var(--page-background-color); + +} \ No newline at end of file diff --git a/docs/doxygentheme/custom.css b/docs/doxygentheme/custom.css new file mode 100644 index 0000000..0e694c0 --- /dev/null +++ b/docs/doxygentheme/custom.css @@ -0,0 +1,101 @@ +.github-corner svg { + fill: var(--primary-light-color); + color: var(--page-background-color); + width: 72px; + height: 72px; +} + +@media screen and (max-width: 767px) { + .github-corner svg { + width: 50px; + height: 50px; + } + #projectnumber { + margin-right: 22px; + } +} + +.alter-theme-button { + display: inline-block; + cursor: pointer; + background: var(--primary-color); + color: var(--page-background-color) !important; + border-radius: var(--border-radius-medium); + padding: var(--spacing-small) var(--spacing-medium); + text-decoration: none; +} + +.next_section_button { + display: block; + padding: var(--spacing-large) 0 var(--spacing-small) 0; + color: var(--page-background-color); + user-select: none; +} + +.next_section_button::after { + /* clearfix */ + content: ""; + clear: both; + display: table; +} + +.next_section_button a { + overflow: hidden; + float: right; + border: 1px solid var(--separator-color); + padding: var(--spacing-medium) calc(var(--spacing-large) / 2) var(--spacing-medium) var(--spacing-large); + border-radius: var(--border-radius-medium); + color: var(--page-secondary-foreground-color) !important; + text-decoration: none; + background-color: var(--page-background-color); + transition: color .08s ease-in-out, background-color .1s ease-in-out; +} + +.next_section_button a:hover { + color: var(--page-foreground-color) !important; + background-color: var(--odd-color); +} + +.next_section_button a::after { + content: '〉'; + color: var(--page-secondary-foreground-color) !important; + padding-left: var(--spacing-large); + display: inline-block; + transition: color .08s ease-in-out, transform .09s ease-in-out; +} + +.next_section_button a:hover::after { + color: var(--page-foreground-color) !important; + transform: translateX(3px); +} + +.alter-theme-button:hover { + background: var(--primary-dark-color); +} + +html.dark-mode .darkmode_inverted_image img, /* < doxygen 1.9.3 */ +html.dark-mode .darkmode_inverted_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ { + filter: brightness(87%) hue-rotate(180deg) invert(); +} + +.bordered_image { + border-radius: var(--border-radius-small); + border: 1px solid var(--separator-color); + display: inline-block; + overflow: hidden; +} + +html.dark-mode .bordered_image img, /* < doxygen 1.9.3 */ +html.dark-mode .bordered_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ { + border-radius: var(--border-radius-small); +} + +.title_screenshot { + filter: drop-shadow(0px 3px 10px rgba(0,0,0,0.22)); + max-width: 500px; + margin: var(--spacing-large) 0; +} + +.title_screenshot .caption { + display: none; +} \ No newline at end of file diff --git a/docs/doxygentheme/doxygen-awesome-darkmode-toggle.js b/docs/doxygentheme/doxygen-awesome-darkmode-toggle.js new file mode 100644 index 0000000..f2c5853 --- /dev/null +++ b/docs/doxygentheme/doxygen-awesome-darkmode-toggle.js @@ -0,0 +1,157 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2021 - 2022 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeDarkModeToggle extends HTMLElement { + // SVG icons from https://fonts.google.com/icons + // Licensed under the Apache 2.0 license: + // https://www.apache.org/licenses/LICENSE-2.0.html + static lightModeIcon = `` + static darkModeIcon = `` + static title = "Toggle Light/Dark Mode" + + static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode" + static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode" + + static _staticConstructor = function() { + DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference) + // Update the color scheme when the browsers preference changes + // without user interaction on the website. + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { + DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged() + }) + // Update the color scheme when the tab is made visible again. + // It is possible that the appearance was changed in another tab + // while this tab was in the background. + document.addEventListener("visibilitychange", visibilityState => { + if (document.visibilityState === 'visible') { + DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged() + } + }); + }() + + static init() { + $(function() { + $(document).ready(function() { + const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle') + toggleButton.title = DoxygenAwesomeDarkModeToggle.title + toggleButton.updateIcon() + + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { + toggleButton.updateIcon() + }) + document.addEventListener("visibilitychange", visibilityState => { + if (document.visibilityState === 'visible') { + toggleButton.updateIcon() + } + }); + + $(document).ready(function(){ + document.getElementById("MSearchBox").parentNode.appendChild(toggleButton) + }) + $(window).resize(function(){ + document.getElementById("MSearchBox").parentNode.appendChild(toggleButton) + }) + }) + }) + } + + constructor() { + super(); + this.onclick=this.toggleDarkMode + } + + /** + * @returns `true` for dark-mode, `false` for light-mode system preference + */ + static get systemPreference() { + return window.matchMedia('(prefers-color-scheme: dark)').matches + } + + /** + * @returns `true` for dark-mode, `false` for light-mode user preference + */ + static get userPreference() { + return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) || + (DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)) + } + + static set userPreference(userPreference) { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference + if(!userPreference) { + if(DoxygenAwesomeDarkModeToggle.systemPreference) { + localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true) + } else { + localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey) + } + } else { + if(!DoxygenAwesomeDarkModeToggle.systemPreference) { + localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true) + } else { + localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey) + } + } + DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged() + } + + static enableDarkMode(enable) { + if(enable) { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = true + document.documentElement.classList.add("dark-mode") + document.documentElement.classList.remove("light-mode") + } else { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = false + document.documentElement.classList.remove("dark-mode") + document.documentElement.classList.add("light-mode") + } + } + + static onSystemPreferenceChanged() { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference + DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled) + } + + static onUserPreferenceChanged() { + DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled) + } + + toggleDarkMode() { + DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference + this.updateIcon() + } + + updateIcon() { + if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) { + this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon + } else { + this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon + } + } +} + +customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle); diff --git a/docs/doxygentheme/doxygen-awesome-fragment-copy-button.js b/docs/doxygentheme/doxygen-awesome-fragment-copy-button.js new file mode 100644 index 0000000..7d06b34 --- /dev/null +++ b/docs/doxygentheme/doxygen-awesome-fragment-copy-button.js @@ -0,0 +1,85 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2022 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeFragmentCopyButton extends HTMLElement { + constructor() { + super(); + this.onclick=this.copyContent + } + static title = "Copy to clipboard" + static copyIcon = `` + static successIcon = `` + static successDuration = 980 + static init() { + $(function() { + $(document).ready(function() { + if(navigator.clipboard) { + const fragments = document.getElementsByClassName("fragment") + for(const fragment of fragments) { + const fragmentWrapper = document.createElement("div") + fragmentWrapper.className = "doxygen-awesome-fragment-wrapper" + const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button") + fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon + fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title + + fragment.parentNode.replaceChild(fragmentWrapper, fragment) + fragmentWrapper.appendChild(fragment) + fragmentWrapper.appendChild(fragmentCopyButton) + + } + } + }) + }) + } + + + copyContent() { + const content = this.previousSibling.cloneNode(true) + // filter out line number from file listings + content.querySelectorAll(".lineno, .ttc").forEach((node) => { + node.remove() + }) + let textContent = content.textContent + // remove trailing newlines that appear in file listings + let numberOfTrailingNewlines = 0 + while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') { + numberOfTrailingNewlines++; + } + textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines) + navigator.clipboard.writeText(textContent); + this.classList.add("success") + this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon + window.setTimeout(() => { + this.classList.remove("success") + this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon + }, DoxygenAwesomeFragmentCopyButton.successDuration); + } +} + +customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton) diff --git a/docs/doxygentheme/doxygen-awesome-interactive-toc.js b/docs/doxygentheme/doxygen-awesome-interactive-toc.js new file mode 100644 index 0000000..b049f57 --- /dev/null +++ b/docs/doxygentheme/doxygen-awesome-interactive-toc.js @@ -0,0 +1,81 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2022 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeInteractiveToc { + static topOffset = 38 + static hideMobileMenu = true + static headers = [] + + static init() { + window.addEventListener("load", () => { + let toc = document.querySelector(".contents > .toc") + if(toc) { + toc.classList.add("interactive") + if(!DoxygenAwesomeInteractiveToc.hideMobileMenu) { + toc.classList.add("open") + } + document.querySelector(".contents > .toc > h3")?.addEventListener("click", () => { + if(toc.classList.contains("open")) { + toc.classList.remove("open") + } else { + toc.classList.add("open") + } + }) + + document.querySelectorAll(".contents > .toc > ul a").forEach((node) => { + let id = node.getAttribute("href").substring(1) + DoxygenAwesomeInteractiveToc.headers.push({ + node: node, + headerNode: document.getElementById(id) + }) + + document.getElementById("doc-content")?.addEventListener("scroll", () => { + DoxygenAwesomeInteractiveToc.update() + }) + }) + DoxygenAwesomeInteractiveToc.update() + } + }) + } + + static update() { + let active = DoxygenAwesomeInteractiveToc.headers[0]?.node + DoxygenAwesomeInteractiveToc.headers.forEach((header) => { + let position = header.headerNode.getBoundingClientRect().top + header.node.classList.remove("active") + header.node.classList.remove("aboveActive") + if(position < DoxygenAwesomeInteractiveToc.topOffset) { + active = header.node + active?.classList.add("aboveActive") + } + }) + active?.classList.add("active") + active?.classList.remove("aboveActive") + } +} \ No newline at end of file diff --git a/docs/doxygentheme/doxygen-awesome-paragraph-link.js b/docs/doxygentheme/doxygen-awesome-paragraph-link.js new file mode 100644 index 0000000..6424dbd --- /dev/null +++ b/docs/doxygentheme/doxygen-awesome-paragraph-link.js @@ -0,0 +1,51 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2022 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeParagraphLink { + // Icon from https://fonts.google.com/icons + // Licensed under the Apache 2.0 license: + // https://www.apache.org/licenses/LICENSE-2.0.html + static icon = `` + static title = "Permanent Link" + static init() { + $(function() { + $(document).ready(function() { + document.querySelectorAll(".contents a.anchor[id], .contents .groupheader > a[id]").forEach((node) => { + let anchorlink = document.createElement("a") + anchorlink.setAttribute("href", `#${node.getAttribute("id")}`) + anchorlink.setAttribute("title", DoxygenAwesomeParagraphLink.title) + anchorlink.classList.add("anchorlink") + node.classList.add("anchor") + anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon + node.parentElement.appendChild(anchorlink) + }) + }) + }) + } +} diff --git a/docs/doxygentheme/doxygen-awesome-sidebar-only-darkmode-toggle.css b/docs/doxygentheme/doxygen-awesome-sidebar-only-darkmode-toggle.css new file mode 100644 index 0000000..b988b6f --- /dev/null +++ b/docs/doxygentheme/doxygen-awesome-sidebar-only-darkmode-toggle.css @@ -0,0 +1,40 @@ + +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2021 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +@media screen and (min-width: 768px) { + + #MSearchBox { + width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px); + } + + #MSearchField { + width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height)); + } +} diff --git a/docs/doxygentheme/doxygen-awesome-sidebar-only.css b/docs/doxygentheme/doxygen-awesome-sidebar-only.css new file mode 100644 index 0000000..656ebbf --- /dev/null +++ b/docs/doxygentheme/doxygen-awesome-sidebar-only.css @@ -0,0 +1,115 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2021 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + */ + +html { + /* side nav width. MUST be = `TREEVIEW_WIDTH`. + * Make sure it is wide enough to contain the page title (logo + title + version) + */ + --side-nav-fixed-width: 335px; + --menu-display: none; + + --top-height: 120px; + --toc-sticky-top: -25px; + --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px); +} + +#projectname { + white-space: nowrap; +} + + +@media screen and (min-width: 768px) { + html { + --searchbar-background: var(--page-background-color); + } + + #side-nav { + min-width: var(--side-nav-fixed-width); + max-width: var(--side-nav-fixed-width); + top: var(--top-height); + overflow: visible; + } + + #nav-tree, #side-nav { + height: calc(100vh - var(--top-height)) !important; + } + + #nav-tree { + padding: 0; + } + + #top { + display: block; + border-bottom: none; + height: var(--top-height); + margin-bottom: calc(0px - var(--top-height)); + max-width: var(--side-nav-fixed-width); + overflow: hidden; + background: var(--side-nav-background); + } + #main-nav { + float: left; + padding-right: 0; + } + + .ui-resizable-handle { + cursor: default; + width: 1px !important; + box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color); + } + + #nav-path { + position: fixed; + right: 0; + left: var(--side-nav-fixed-width); + bottom: 0; + width: auto; + } + + #doc-content { + height: calc(100vh - 31px) !important; + padding-bottom: calc(3 * var(--spacing-large)); + padding-top: calc(var(--top-height) - 80px); + box-sizing: border-box; + margin-left: var(--side-nav-fixed-width) !important; + } + + #MSearchBox { + width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium))); + } + + #MSearchField { + width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px); + } + + #MSearchResultsWindow { + left: var(--spacing-medium) !important; + right: auto; + } +} diff --git a/docs/doxygentheme/doxygen-awesome.css b/docs/doxygentheme/doxygen-awesome.css new file mode 100644 index 0000000..abd2893 --- /dev/null +++ b/docs/doxygentheme/doxygen-awesome.css @@ -0,0 +1,2405 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2021 - 2022 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +html { + /* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */ + --primary-color: #1779c4; + --primary-dark-color: #335c80; + --primary-light-color: #70b1e9; + + /* page base colors */ + --page-background-color: #ffffff; + --page-foreground-color: #2f4153; + --page-secondary-foreground-color: #6f7e8e; + + /* color for all separators on the website: hr, borders, ... */ + --separator-color: #dedede; + + /* border radius for all rounded components. Will affect many components, like dropdowns, memitems, codeblocks, ... */ + --border-radius-large: 8px; + --border-radius-small: 4px; + --border-radius-medium: 6px; + + /* default spacings. Most components reference these values for spacing, to provide uniform spacing on the page. */ + --spacing-small: 5px; + --spacing-medium: 10px; + --spacing-large: 16px; + + /* default box shadow used for raising an element above the normal content. Used in dropdowns, search result, ... */ + --box-shadow: 0 2px 8px 0 rgba(0,0,0,.075); + + --odd-color: rgba(0,0,0,.028); + + /* font-families. will affect all text on the website + * font-family: the normal font for text, headlines, menus + * font-family-monospace: used for preformatted text in memtitle, code, fragments + */ + --font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif; + --font-family-monospace: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; + + /* font sizes */ + --page-font-size: 15.6px; + --navigation-font-size: 14.4px; + --toc-font-size: 13.4px; + --code-font-size: 14px; /* affects code, fragment */ + --title-font-size: 22px; + + /* content text properties. These only affect the page content, not the navigation or any other ui elements */ + --content-line-height: 27px; + /* The content is centered and constraint in it's width. To make the content fill the whole page, set the variable to auto.*/ + --content-maxwidth: 1050px; + --table-line-height: 24px; + --toc-sticky-top: var(--spacing-medium); + --toc-width: 200px; + --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 85px); + + /* colors for various content boxes: @warning, @note, @deprecated @bug */ + --warning-color: #f8d1cc; + --warning-color-dark: #b61825; + --warning-color-darker: #75070f; + --note-color: #faf3d8; + --note-color-dark: #f3a600; + --note-color-darker: #5f4204; + --todo-color: #e4f3ff; + --todo-color-dark: #1879C4; + --todo-color-darker: #274a5c; + --deprecated-color: #ecf0f3; + --deprecated-color-dark: #5b6269; + --deprecated-color-darker: #43454a; + --bug-color: #e4dafd; + --bug-color-dark: #5b2bdd; + --bug-color-darker: #2a0d72; + --invariant-color: #d8f1e3; + --invariant-color-dark: #44b86f; + --invariant-color-darker: #265532; + + /* blockquote colors */ + --blockquote-background: #f8f9fa; + --blockquote-foreground: #636568; + + /* table colors */ + --tablehead-background: #f1f1f1; + --tablehead-foreground: var(--page-foreground-color); + + /* menu-display: block | none + * Visibility of the top navigation on screens >= 768px. On smaller screen the menu is always visible. + * `GENERATE_TREEVIEW` MUST be enabled! + */ + --menu-display: block; + + --menu-focus-foreground: var(--page-background-color); + --menu-focus-background: var(--primary-color); + --menu-selected-background: rgba(0,0,0,.05); + + + --header-background: var(--page-background-color); + --header-foreground: var(--page-foreground-color); + + /* searchbar colors */ + --searchbar-background: var(--side-nav-background); + --searchbar-foreground: var(--page-foreground-color); + + /* searchbar size + * (`searchbar-width` is only applied on screens >= 768px. + * on smaller screens the searchbar will always fill the entire screen width) */ + --searchbar-height: 33px; + --searchbar-width: 210px; + --searchbar-border-radius: var(--searchbar-height); + + /* code block colors */ + --code-background: #f5f5f5; + --code-foreground: var(--page-foreground-color); + + /* fragment colors */ + --fragment-background: #F8F9FA; + --fragment-foreground: #37474F; + --fragment-keyword: #bb6bb2; + --fragment-keywordtype: #8258b3; + --fragment-keywordflow: #d67c3b; + --fragment-token: #438a59; + --fragment-comment: #969696; + --fragment-link: #5383d6; + --fragment-preprocessor: #46aaa5; + --fragment-linenumber-color: #797979; + --fragment-linenumber-background: #f4f4f5; + --fragment-linenumber-border: #e3e5e7; + --fragment-lineheight: 20px; + + /* sidebar navigation (treeview) colors */ + --side-nav-background: #fbfbfb; + --side-nav-foreground: var(--page-foreground-color); + --side-nav-arrow-opacity: 0; + --side-nav-arrow-hover-opacity: 0.9; + + --toc-background: var(--side-nav-background); + --toc-foreground: var(--side-nav-foreground); + + /* height of an item in any tree / collapsable table */ + --tree-item-height: 30px; + + --memname-font-size: var(--code-font-size); + --memtitle-font-size: 18px; + + --webkit-scrollbar-size: 7px; + --webkit-scrollbar-padding: 4px; + --webkit-scrollbar-color: var(--separator-color); +} + +@media screen and (max-width: 767px) { + html { + --page-font-size: 16px; + --navigation-font-size: 16px; + --toc-font-size: 15px; + --code-font-size: 15px; /* affects code, fragment */ + --title-font-size: 22px; + } +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) { + color-scheme: dark; + + --primary-color: #1982d2; + --primary-dark-color: #86a9c4; + --primary-light-color: #4779ac; + + --box-shadow: 0 2px 8px 0 rgba(0,0,0,.35); + + --odd-color: rgba(100,100,100,.06); + + --menu-selected-background: rgba(0,0,0,.4); + + --page-background-color: #1C1D1F; + --page-foreground-color: #d2dbde; + --page-secondary-foreground-color: #859399; + --separator-color: #38393b; + --side-nav-background: #252628; + + --code-background: #2a2c2f; + + --tablehead-background: #2a2c2f; + + --blockquote-background: #222325; + --blockquote-foreground: #7e8c92; + + --warning-color: #2e1917; + --warning-color-dark: #ad2617; + --warning-color-darker: #f5b1aa; + --note-color: #3b2e04; + --note-color-dark: #f1b602; + --note-color-darker: #ceb670; + --todo-color: #163750; + --todo-color-dark: #1982D2; + --todo-color-darker: #dcf0fa; + --deprecated-color: #2e323b; + --deprecated-color-dark: #738396; + --deprecated-color-darker: #abb0bd; + --bug-color: #2a2536; + --bug-color-dark: #7661b3; + --bug-color-darker: #ae9ed6; + --invariant-color: #303a35; + --invariant-color-dark: #76ce96; + --invariant-color-darker: #cceed5; + + --fragment-background: #282c34; + --fragment-foreground: #dbe4eb; + --fragment-keyword: #cc99cd; + --fragment-keywordtype: #ab99cd; + --fragment-keywordflow: #e08000; + --fragment-token: #7ec699; + --fragment-comment: #999999; + --fragment-link: #98c0e3; + --fragment-preprocessor: #65cabe; + --fragment-linenumber-color: #cccccc; + --fragment-linenumber-background: #35393c; + --fragment-linenumber-border: #1f1f1f; + } +} + +/* dark mode variables are defined twice, to support both the dark-mode without and with doxygen-awesome-darkmode-toggle.js */ +html.dark-mode { + color-scheme: dark; + + --primary-color: #1982d2; + --primary-dark-color: #86a9c4; + --primary-light-color: #4779ac; + + --box-shadow: 0 2px 8px 0 rgba(0,0,0,.30); + + --odd-color: rgba(100,100,100,.06); + + --menu-selected-background: rgba(0,0,0,.4); + + --page-background-color: #1C1D1F; + --page-foreground-color: #d2dbde; + --page-secondary-foreground-color: #859399; + --separator-color: #38393b; + --side-nav-background: #252628; + + --code-background: #2a2c2f; + + --tablehead-background: #2a2c2f; + + --blockquote-background: #222325; + --blockquote-foreground: #7e8c92; + + --warning-color: #2e1917; + --warning-color-dark: #ad2617; + --warning-color-darker: #f5b1aa; + --note-color: #3b2e04; + --note-color-dark: #f1b602; + --note-color-darker: #ceb670; + --todo-color: #163750; + --todo-color-dark: #1982D2; + --todo-color-darker: #dcf0fa; + --deprecated-color: #2e323b; + --deprecated-color-dark: #738396; + --deprecated-color-darker: #abb0bd; + --bug-color: #2a2536; + --bug-color-dark: #7661b3; + --bug-color-darker: #ae9ed6; + --invariant-color: #303a35; + --invariant-color-dark: #76ce96; + --invariant-color-darker: #cceed5; + + --fragment-background: #282c34; + --fragment-foreground: #dbe4eb; + --fragment-keyword: #cc99cd; + --fragment-keywordtype: #ab99cd; + --fragment-keywordflow: #e08000; + --fragment-token: #7ec699; + --fragment-comment: #999999; + --fragment-link: #98c0e3; + --fragment-preprocessor: #65cabe; + --fragment-linenumber-color: #cccccc; + --fragment-linenumber-background: #35393c; + --fragment-linenumber-border: #1f1f1f; +} + +body { + color: var(--page-foreground-color); + background-color: var(--page-background-color); + font-size: var(--page-font-size); +} + +body, table, div, p, dl, #nav-tree .label, .title, +.sm-dox a, .sm-dox a:hover, .sm-dox a:focus, #projectname, +.SelectItem, #MSearchField, .navpath li.navelem a, +.navpath li.navelem a:hover, p.reference, p.definition { + font-family: var(--font-family); +} + +h1, h2, h3, h4, h5 { + margin-top: .9em; + font-weight: 600; + line-height: initial; +} + +p, div, table, dl, p.reference, p.definition { + font-size: var(--page-font-size); +} + +p.reference, p.definition { + color: var(--page-secondary-foreground-color); +} + +a:link, a:visited, a:hover, a:focus, a:active { + color: var(--primary-color) !important; + font-weight: 500; +} + +a.anchor { + scroll-margin-top: var(--spacing-large); + display: block; +} + +/* + Title and top navigation + */ + +#top { + background: var(--header-background); + border-bottom: 1px solid var(--separator-color); +} + +@media screen and (min-width: 768px) { + #top { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + } +} + +#main-nav { + flex-grow: 5; + padding: var(--spacing-small) var(--spacing-medium); +} + +#titlearea { + width: auto; + padding: var(--spacing-medium) var(--spacing-large); + background: none; + color: var(--header-foreground); + border-bottom: none; +} + +@media screen and (max-width: 767px) { + #titlearea { + padding-bottom: var(--spacing-small); + } +} + +#titlearea table tbody tr { + height: auto !important; +} + +#projectname { + font-size: var(--title-font-size); + font-weight: 600; +} + +#projectnumber { + font-family: inherit; + font-size: 60%; +} + +#projectbrief { + font-family: inherit; + font-size: 80%; +} + +#projectlogo { + vertical-align: middle; +} + +#projectlogo img { + max-height: calc(var(--title-font-size) * 2); + margin-right: var(--spacing-small); +} + +.sm-dox, .tabs, .tabs2, .tabs3 { + background: none; + padding: 0; +} + +.tabs, .tabs2, .tabs3 { + border-bottom: 1px solid var(--separator-color); + margin-bottom: -1px; +} + +.main-menu-btn-icon, .main-menu-btn-icon:before, .main-menu-btn-icon:after { + background: var(--page-secondary-foreground-color); +} + +@media screen and (max-width: 767px) { + .sm-dox a span.sub-arrow { + background: var(--code-background); + } + + #main-menu a.has-submenu span.sub-arrow { + color: var(--page-secondary-foreground-color); + border-radius: var(--border-radius-medium); + } + + #main-menu a.has-submenu:hover span.sub-arrow { + color: var(--page-foreground-color); + } +} + +@media screen and (min-width: 768px) { + .sm-dox li, .tablist li { + display: var(--menu-display); + } + + .sm-dox a span.sub-arrow { + border-color: var(--header-foreground) transparent transparent transparent; + } + + .sm-dox a:hover span.sub-arrow { + border-color: var(--menu-focus-foreground) transparent transparent transparent; + } + + .sm-dox ul a span.sub-arrow { + border-color: transparent transparent transparent var(--page-foreground-color); + } + + .sm-dox ul a:hover span.sub-arrow { + border-color: transparent transparent transparent var(--menu-focus-foreground); + } +} + +.sm-dox ul { + background: var(--page-background-color); + box-shadow: var(--box-shadow); + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium) !important; + padding: var(--spacing-small); + animation: ease-out 150ms slideInMenu; +} + +@keyframes slideInMenu { + from { + opacity: 0; + transform: translate(0px, -2px); + } + + to { + opacity: 1; + transform: translate(0px, 0px); + } +} + +.sm-dox ul a { + color: var(--page-foreground-color) !important; + background: var(--page-background-color); + font-size: var(--navigation-font-size); +} + +.sm-dox>li>ul:after { + border-bottom-color: var(--page-background-color) !important; +} + +.sm-dox>li>ul:before { + border-bottom-color: var(--separator-color) !important; +} + +.sm-dox ul a:hover, .sm-dox ul a:active, .sm-dox ul a:focus { + font-size: var(--navigation-font-size) !important; + color: var(--menu-focus-foreground) !important; + text-shadow: none; + background-color: var(--menu-focus-background); + border-radius: var(--border-radius-small) !important; +} + +.sm-dox a, .sm-dox a:focus, .tablist li, .tablist li a, .tablist li.current a { + text-shadow: none; + background: transparent; + background-image: none !important; + color: var(--header-foreground) !important; + font-weight: normal; + font-size: var(--navigation-font-size); + border-radius: var(--border-radius-small) !important; +} + +.sm-dox a:focus { + outline: auto; +} + +.sm-dox a:hover, .sm-dox a:active, .tablist li a:hover { + text-shadow: none; + font-weight: normal; + background: var(--menu-focus-background); + color: var(--menu-focus-foreground) !important; + border-radius: var(--border-radius-small) !important; + font-size: var(--navigation-font-size); +} + +.tablist li.current { + border-radius: var(--border-radius-small); + background: var(--menu-selected-background); +} + +.tablist li { + margin: var(--spacing-small) 0 var(--spacing-small) var(--spacing-small); +} + +.tablist a { + padding: 0 var(--spacing-large); +} + + +/* + Search box + */ + +#MSearchBox { + height: var(--searchbar-height); + background: var(--searchbar-background); + border-radius: var(--searchbar-border-radius); + border: 1px solid var(--separator-color); + overflow: hidden; + width: var(--searchbar-width); + position: relative; + box-shadow: none; + display: block; + margin-top: 0; +} + +/* until Doxygen 1.9.4 */ +.left img#MSearchSelect { + left: 0; + user-select: none; + padding-left: 8px; +} + +/* Doxygen 1.9.5 */ +.left span#MSearchSelect { + left: 0; + user-select: none; + margin-left: 8px; + padding: 0; +} + +.left #MSearchSelect[src$=".png"] { + padding-left: 0 +} + +.SelectionMark { + user-select: none; +} + +.tabs .left #MSearchSelect { + padding-left: 0; +} + +.tabs #MSearchBox { + position: absolute; + right: var(--spacing-medium); +} + +@media screen and (max-width: 767px) { + .tabs #MSearchBox { + position: relative; + right: 0; + margin-left: var(--spacing-medium); + margin-top: 0; + } +} + +#MSearchSelectWindow, #MSearchResultsWindow { + z-index: 9999; +} + +#MSearchBox.MSearchBoxActive { + border-color: var(--primary-color); + box-shadow: inset 0 0 0 1px var(--primary-color); +} + +#main-menu > li:last-child { + margin-right: 0; +} + +@media screen and (max-width: 767px) { + #main-menu > li:last-child { + height: 50px; + } +} + +#MSearchField { + font-size: var(--navigation-font-size); + height: calc(var(--searchbar-height) - 2px); + background: transparent; + width: calc(var(--searchbar-width) - 64px); +} + +.MSearchBoxActive #MSearchField { + color: var(--searchbar-foreground); +} + +#MSearchSelect { + top: calc(calc(var(--searchbar-height) / 2) - 11px); +} + +#MSearchBox span.left, #MSearchBox span.right { + background: none; + background-image: none; +} + +#MSearchBox span.right { + padding-top: calc(calc(var(--searchbar-height) / 2) - 12px); + position: absolute; + right: var(--spacing-small); +} + +.tabs #MSearchBox span.right { + top: calc(calc(var(--searchbar-height) / 2) - 12px); +} + +@keyframes slideInSearchResults { + from { + opacity: 0; + transform: translate(0, 15px); + } + + to { + opacity: 1; + transform: translate(0, 20px); + } +} + +#MSearchResultsWindow { + left: auto !important; + right: var(--spacing-medium); + border-radius: var(--border-radius-large); + border: 1px solid var(--separator-color); + transform: translate(0, 20px); + box-shadow: var(--box-shadow); + animation: ease-out 280ms slideInSearchResults; + background: var(--page-background-color); +} + +iframe#MSearchResults { + margin: 4px; +} + +iframe { + color-scheme: normal; +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) iframe#MSearchResults { + filter: invert() hue-rotate(180deg); + } +} + +html.dark-mode iframe#MSearchResults { + filter: invert() hue-rotate(180deg); +} + +#MSearchResults .SRPage { + background-color: transparent; +} + +#MSearchResults .SRPage .SREntry { + font-size: 10pt; + padding: var(--spacing-small) var(--spacing-medium); +} + +#MSearchSelectWindow { + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium); + box-shadow: var(--box-shadow); + background: var(--page-background-color); + padding-top: var(--spacing-small); + padding-bottom: var(--spacing-small); +} + +#MSearchSelectWindow a.SelectItem { + font-size: var(--navigation-font-size); + line-height: var(--content-line-height); + margin: 0 var(--spacing-small); + border-radius: var(--border-radius-small); + color: var(--page-foreground-color) !important; + font-weight: normal; +} + +#MSearchSelectWindow a.SelectItem:hover { + background: var(--menu-focus-background); + color: var(--menu-focus-foreground) !important; +} + +@media screen and (max-width: 767px) { + #MSearchBox { + margin-top: var(--spacing-medium); + margin-bottom: var(--spacing-medium); + width: calc(100vw - 30px); + } + + #main-menu > li:last-child { + float: none !important; + } + + #MSearchField { + width: calc(100vw - 110px); + } + + @keyframes slideInSearchResultsMobile { + from { + opacity: 0; + transform: translate(0, 15px); + } + + to { + opacity: 1; + transform: translate(0, 20px); + } + } + + #MSearchResultsWindow { + left: var(--spacing-medium) !important; + right: var(--spacing-medium); + overflow: auto; + transform: translate(0, 20px); + animation: ease-out 280ms slideInSearchResultsMobile; + width: auto !important; + } + + /* + * Overwrites for fixing the searchbox on mobile in doxygen 1.9.2 + */ + label.main-menu-btn ~ #searchBoxPos1 { + top: 3px !important; + right: 6px !important; + left: 45px; + display: flex; + } + + label.main-menu-btn ~ #searchBoxPos1 > #MSearchBox { + margin-top: 0; + margin-bottom: 0; + flex-grow: 2; + float: left; + } +} + +/* + Tree view + */ + +#side-nav { + padding: 0 !important; + background: var(--side-nav-background); +} + +@media screen and (max-width: 767px) { + #side-nav { + display: none; + } + + #doc-content { + margin-left: 0 !important; + } +} + +#nav-tree { + background: transparent; +} + +#nav-tree .label { + font-size: var(--navigation-font-size); +} + +#nav-tree .item { + height: var(--tree-item-height); + line-height: var(--tree-item-height); +} + +#nav-sync { + bottom: 12px; + right: 12px; + top: auto !important; + user-select: none; +} + +#nav-tree .selected { + text-shadow: none; + background-image: none; + background-color: transparent; + position: relative; +} + +#nav-tree .selected::after { + content: ""; + position: absolute; + top: 1px; + bottom: 1px; + left: 0; + width: 4px; + border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; + background: var(--primary-color); +} + + +#nav-tree a { + color: var(--side-nav-foreground) !important; + font-weight: normal; +} + +#nav-tree a:focus { + outline-style: auto; +} + +#nav-tree .arrow { + opacity: var(--side-nav-arrow-opacity); +} + +.arrow { + color: inherit; + cursor: pointer; + font-size: 45%; + vertical-align: middle; + margin-right: 2px; + font-family: serif; + height: auto; + text-align: right; +} + +#nav-tree div.item:hover .arrow, #nav-tree a:focus .arrow { + opacity: var(--side-nav-arrow-hover-opacity); +} + +#nav-tree .selected a { + color: var(--primary-color) !important; + font-weight: bolder; + font-weight: 600; +} + +.ui-resizable-e { + background: var(--separator-color); + width: 1px; +} + +/* + Contents + */ + +div.header { + border-bottom: 1px solid var(--separator-color); + background-color: var(--page-background-color); + background-image: none; +} + +@media screen and (min-width: 1000px) { + #doc-content > div > div.contents, + .PageDoc > div.contents { + display: flex; + flex-direction: row-reverse; + flex-wrap: nowrap; + align-items: flex-start; + } + + div.contents .textblock { + min-width: 200px; + flex-grow: 1; + } +} + +div.contents, div.header .title, div.header .summary { + max-width: var(--content-maxwidth); +} + +div.contents, div.header .title { + line-height: initial; + margin: calc(var(--spacing-medium) + .2em) auto var(--spacing-medium) auto; +} + +div.header .summary { + margin: var(--spacing-medium) auto 0 auto; +} + +div.headertitle { + padding: 0; +} + +div.header .title { + font-weight: 600; + font-size: 225%; + padding: var(--spacing-medium) var(--spacing-large); + word-break: break-word; +} + +div.header .summary { + width: auto; + display: block; + float: none; + padding: 0 var(--spacing-large); +} + +td.memSeparator { + border-color: var(--separator-color); +} + +span.mlabel { + background: var(--primary-color); + border: none; + padding: 4px 9px; + border-radius: 12px; + margin-right: var(--spacing-medium); +} + +span.mlabel:last-of-type { + margin-right: 2px; +} + +div.contents { + padding: 0 var(--spacing-large); +} + +div.contents p, div.contents li { + line-height: var(--content-line-height); +} + +div.contents div.dyncontent { + margin: var(--spacing-medium) 0; +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) div.contents div.dyncontent img, + html:not(.light-mode) div.contents center img, + html:not(.light-mode) div.contents > table img, + html:not(.light-mode) div.contents div.dyncontent iframe, + html:not(.light-mode) div.contents center iframe, + html:not(.light-mode) div.contents table iframe { + filter: hue-rotate(180deg) invert(); + } +} + +html.dark-mode div.contents div.dyncontent img, +html.dark-mode div.contents center img, +html.dark-mode div.contents > table img, +html.dark-mode div.contents div.dyncontent iframe, +html.dark-mode div.contents center iframe, +html.dark-mode div.contents table iframe { + filter: hue-rotate(180deg) invert(); +} + +h2.groupheader { + border-bottom: 0px; + color: var(--page-foreground-color); + box-shadow: + 100px 0 var(--page-background-color), + -100px 0 var(--page-background-color), + 100px 0.75px var(--separator-color), + -100px 0.75px var(--separator-color), + 500px 0 var(--page-background-color), + -500px 0 var(--page-background-color), + 500px 0.75px var(--separator-color), + -500px 0.75px var(--separator-color), + 900px 0 var(--page-background-color), + -900px 0 var(--page-background-color), + 900px 0.75px var(--separator-color), + -900px 0.75px var(--separator-color), + 1400px 0 var(--page-background-color), + -1400px 0 var(--page-background-color), + 1400px 0.75px var(--separator-color), + -1400px 0.75px var(--separator-color), + 1900px 0 var(--page-background-color), + -1900px 0 var(--page-background-color), + 1900px 0.75px var(--separator-color), + -1900px 0.75px var(--separator-color); +} + +blockquote { + margin: 0 var(--spacing-medium) 0 var(--spacing-medium); + padding: var(--spacing-small) var(--spacing-large); + background: var(--blockquote-background); + color: var(--blockquote-foreground); + border-left: 0; + overflow: visible; + border-radius: var(--border-radius-medium); + overflow: visible; + position: relative; +} + +blockquote::before, blockquote::after { + font-weight: bold; + font-family: serif; + font-size: 360%; + opacity: .15; + position: absolute; +} + +blockquote::before { + content: "“"; + left: -10px; + top: 4px; +} + +blockquote::after { + content: "”"; + right: -8px; + bottom: -25px; +} + +blockquote p { + margin: var(--spacing-small) 0 var(--spacing-medium) 0; +} +.paramname { + font-weight: 600; + color: var(--primary-dark-color); +} + +.paramname > code { + border: 0; +} + +table.params .paramname { + font-weight: 600; + font-family: var(--font-family-monospace); + font-size: var(--code-font-size); + padding-right: var(--spacing-small); + line-height: var(--table-line-height); +} + +h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { + text-shadow: 0 0 15px var(--primary-light-color); +} + +.alphachar a { + color: var(--page-foreground-color); +} + +/* + Table of Contents + */ + +div.contents .toc { + max-height: var(--toc-max-height); + min-width: var(--toc-width); + border: 0; + border-left: 1px solid var(--separator-color); + border-radius: 0; + background-color: transparent; + box-shadow: none; + position: sticky; + top: var(--toc-sticky-top); + padding: 0 var(--spacing-large); + margin: var(--spacing-small) 0 var(--spacing-large) var(--spacing-large); +} + +div.toc h3 { + color: var(--toc-foreground); + font-size: var(--navigation-font-size); + margin: var(--spacing-large) 0 var(--spacing-medium) 0; +} + +div.toc li { + padding: 0; + background: none; + line-height: var(--toc-font-size); + margin: var(--toc-font-size) 0 0 0; +} + +div.toc li::before { + display: none; +} + +div.toc ul { + margin-top: 0 +} + +div.toc li a { + font-size: var(--toc-font-size); + color: var(--page-foreground-color) !important; + text-decoration: none; +} + +div.toc li a:hover, div.toc li a.active { + color: var(--primary-color) !important; +} + +div.toc li a.aboveActive { + color: var(--page-secondary-foreground-color) !important; +} + + +@media screen and (max-width: 999px) { + div.contents .toc { + max-height: 45vh; + float: none; + width: auto; + margin: 0 0 var(--spacing-medium) 0; + position: relative; + top: 0; + position: relative; + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium); + background-color: var(--toc-background); + box-shadow: var(--box-shadow); + } + + div.contents .toc.interactive { + max-height: calc(var(--navigation-font-size) + 2 * var(--spacing-large)); + overflow: hidden; + } + + div.contents .toc > h3 { + -webkit-tap-highlight-color: transparent; + cursor: pointer; + position: sticky; + top: 0; + background-color: var(--toc-background); + margin: 0; + padding: var(--spacing-large) 0; + display: block; + } + + div.contents .toc.interactive > h3::before { + content: ""; + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 5px solid var(--primary-color); + display: inline-block; + margin-right: var(--spacing-small); + margin-bottom: calc(var(--navigation-font-size) / 4); + transform: rotate(-90deg); + transition: transform 0.25s ease-out; + } + + div.contents .toc.interactive.open > h3::before { + transform: rotate(0deg); + } + + div.contents .toc.interactive.open { + max-height: 45vh; + overflow: auto; + transition: max-height 0.2s ease-in-out; + } + + div.contents .toc a, div.contents .toc a.active { + color: var(--primary-color) !important; + } + + div.contents .toc a:hover { + text-decoration: underline; + } +} + +/* + Code & Fragments + */ + +code, div.fragment, pre.fragment { + border-radius: var(--border-radius-small); + border: 1px solid var(--separator-color); + overflow: hidden; +} + +code { + display: inline; + background: var(--code-background); + color: var(--code-foreground); + padding: 2px 6px; +} + +div.fragment, pre.fragment { + margin: var(--spacing-medium) 0; + padding: calc(var(--spacing-large) - (var(--spacing-large) / 6)) var(--spacing-large); + background: var(--fragment-background); + color: var(--fragment-foreground); + overflow-x: auto; +} + +@media screen and (max-width: 767px) { + div.fragment, pre.fragment { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: 0; + } + + .contents > div.fragment, + .textblock > div.fragment, + .textblock > pre.fragment, + .contents > .doxygen-awesome-fragment-wrapper > div.fragment, + .textblock > .doxygen-awesome-fragment-wrapper > div.fragment, + .textblock > .doxygen-awesome-fragment-wrapper > pre.fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-large)); + border-radius: 0; + border-left: 0; + } + + .textblock li > .fragment, + .textblock li > .doxygen-awesome-fragment-wrapper > .fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-large)); + } + + .memdoc li > .fragment, + .memdoc li > .doxygen-awesome-fragment-wrapper > .fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-medium)); + } + + .textblock ul, .memdoc ul { + overflow: initial; + } + + .memdoc > div.fragment, + .memdoc > pre.fragment, + dl dd > div.fragment, + dl dd pre.fragment, + .memdoc > .doxygen-awesome-fragment-wrapper > div.fragment, + .memdoc > .doxygen-awesome-fragment-wrapper > pre.fragment, + dl dd > .doxygen-awesome-fragment-wrapper > div.fragment, + dl dd .doxygen-awesome-fragment-wrapper > pre.fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-medium)); + border-radius: 0; + border-left: 0; + } +} + +code, code a, pre.fragment, div.fragment, div.fragment .line, div.fragment span, div.fragment .line a, div.fragment .line span { + font-family: var(--font-family-monospace); + font-size: var(--code-font-size) !important; +} + +div.line:after { + margin-right: var(--spacing-medium); +} + +div.fragment .line, pre.fragment { + white-space: pre; + word-wrap: initial; + line-height: var(--fragment-lineheight); +} + +div.fragment span.keyword { + color: var(--fragment-keyword); +} + +div.fragment span.keywordtype { + color: var(--fragment-keywordtype); +} + +div.fragment span.keywordflow { + color: var(--fragment-keywordflow); +} + +div.fragment span.stringliteral { + color: var(--fragment-token) +} + +div.fragment span.comment { + color: var(--fragment-comment); +} + +div.fragment a.code { + color: var(--fragment-link) !important; +} + +div.fragment span.preprocessor { + color: var(--fragment-preprocessor); +} + +div.fragment span.lineno { + display: inline-block; + width: 27px; + border-right: none; + background: var(--fragment-linenumber-background); + color: var(--fragment-linenumber-color); +} + +div.fragment span.lineno a { + background: none; + color: var(--fragment-link) !important; +} + +div.fragment .line:first-child .lineno { + box-shadow: -999999px 0px 0 999999px var(--fragment-linenumber-background), -999998px 0px 0 999999px var(--fragment-linenumber-border); +} + +div.line { + border-radius: var(--border-radius-small); +} + +div.line.glow { + background-color: var(--primary-light-color); + box-shadow: none; +} + +/* + dl warning, attention, note, deprecated, bug, ... + */ + +dl.bug dt a, dl.deprecated dt a, dl.todo dt a { + font-weight: bold !important; +} + +dl.warning, dl.attention, dl.note, dl.deprecated, dl.bug, dl.invariant, dl.pre, dl.todo, dl.remark { + padding: var(--spacing-medium); + margin: var(--spacing-medium) 0; + color: var(--page-background-color); + overflow: hidden; + margin-left: 0; + border-radius: var(--border-radius-small); +} + +dl.section dd { + margin-bottom: 2px; +} + +dl.warning, dl.attention { + background: var(--warning-color); + border-left: 8px solid var(--warning-color-dark); + color: var(--warning-color-darker); +} + +dl.warning dt, dl.attention dt { + color: var(--warning-color-dark); +} + +dl.note, dl.remark { + background: var(--note-color); + border-left: 8px solid var(--note-color-dark); + color: var(--note-color-darker); +} + +dl.note dt, dl.remark dt { + color: var(--note-color-dark); +} + +dl.todo { + background: var(--todo-color); + border-left: 8px solid var(--todo-color-dark); + color: var(--todo-color-darker); +} + +dl.todo dt { + color: var(--todo-color-dark); +} + +dl.bug dt a { + color: var(--todo-color-dark) !important; +} + +dl.bug { + background: var(--bug-color); + border-left: 8px solid var(--bug-color-dark); + color: var(--bug-color-darker); +} + +dl.bug dt a { + color: var(--bug-color-dark) !important; +} + +dl.deprecated { + background: var(--deprecated-color); + border-left: 8px solid var(--deprecated-color-dark); + color: var(--deprecated-color-darker); +} + +dl.deprecated dt a { + color: var(--deprecated-color-dark) !important; +} + +dl.section dd, dl.bug dd, dl.deprecated dd, dl.todo dd { + margin-inline-start: 0px; +} + +dl.invariant, dl.pre { + background: var(--invariant-color); + border-left: 8px solid var(--invariant-color-dark); + color: var(--invariant-color-darker); +} + +dl.invariant dt, dl.pre dt { + color: var(--invariant-color-dark); +} + +/* + memitem + */ + +div.memdoc, div.memproto, h2.memtitle { + box-shadow: none; + background-image: none; + border: none; +} + +div.memdoc { + padding: 0 var(--spacing-medium); + background: var(--page-background-color); +} + +h2.memtitle, div.memitem { + border: 1px solid var(--separator-color); + box-shadow: var(--box-shadow); +} + +h2.memtitle { + box-shadow: 0px var(--spacing-medium) 0 -1px var(--fragment-background), var(--box-shadow); +} + +div.memitem { + transition: none; +} + +div.memproto, h2.memtitle { + background: var(--fragment-background); +} + +h2.memtitle { + font-weight: 500; + font-size: var(--memtitle-font-size); + font-family: var(--font-family-monospace); + border-bottom: none; + border-top-left-radius: var(--border-radius-medium); + border-top-right-radius: var(--border-radius-medium); + word-break: break-all; + position: relative; +} + +h2.memtitle:after { + content: ""; + display: block; + background: var(--fragment-background); + height: var(--spacing-medium); + bottom: calc(0px - var(--spacing-medium)); + left: 0; + right: -14px; + position: absolute; + border-top-right-radius: var(--border-radius-medium); +} + +h2.memtitle > span.permalink { + font-size: inherit; +} + +h2.memtitle > span.permalink > a { + text-decoration: none; + padding-left: 3px; + margin-right: -4px; + user-select: none; + display: inline-block; + margin-top: -6px; +} + +h2.memtitle > span.permalink > a:hover { + color: var(--primary-dark-color) !important; +} + +a:target + h2.memtitle, a:target + h2.memtitle + div.memitem { + border-color: var(--primary-light-color); +} + +div.memitem { + border-top-right-radius: var(--border-radius-medium); + border-bottom-right-radius: var(--border-radius-medium); + border-bottom-left-radius: var(--border-radius-medium); + overflow: hidden; + display: block !important; +} + +div.memdoc { + border-radius: 0; +} + +div.memproto { + border-radius: 0 var(--border-radius-small) 0 0; + overflow: auto; + border-bottom: 1px solid var(--separator-color); + padding: var(--spacing-medium); + margin-bottom: -1px; +} + +div.memtitle { + border-top-right-radius: var(--border-radius-medium); + border-top-left-radius: var(--border-radius-medium); +} + +div.memproto table.memname { + font-family: var(--font-family-monospace); + color: var(--page-foreground-color); + font-size: var(--memname-font-size); + text-shadow: none; +} + +div.memproto div.memtemplate { + font-family: var(--font-family-monospace); + color: var(--primary-dark-color); + font-size: var(--memname-font-size); + margin-left: 2px; + text-shadow: none; +} + +table.mlabels, table.mlabels > tbody { + display: block; +} + +td.mlabels-left { + width: auto; +} + +td.mlabels-right { + margin-top: 3px; + position: sticky; + left: 0; +} + +table.mlabels > tbody > tr:first-child { + display: flex; + justify-content: space-between; + flex-wrap: wrap; +} + +.memname, .memitem span.mlabels { + margin: 0 +} + +/* + reflist + */ + +dl.reflist { + box-shadow: var(--box-shadow); + border-radius: var(--border-radius-medium); + border: 1px solid var(--separator-color); + overflow: hidden; + padding: 0; +} + + +dl.reflist dt, dl.reflist dd { + box-shadow: none; + text-shadow: none; + background-image: none; + border: none; + padding: 12px; +} + + +dl.reflist dt { + font-weight: 500; + border-radius: 0; + background: var(--code-background); + border-bottom: 1px solid var(--separator-color); + color: var(--page-foreground-color) +} + + +dl.reflist dd { + background: none; +} + +/* + Table + */ + +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname), +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody { + display: inline-block; + max-width: 100%; +} + +.contents > table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname):not(.classindex) { + margin-left: calc(0px - var(--spacing-large)); + margin-right: calc(0px - var(--spacing-large)); + max-width: calc(100% + 2 * var(--spacing-large)); +} + +table.fieldtable, +table.markdownTable tbody, +table.doxtable tbody { + border: none; + margin: var(--spacing-medium) 0; + box-shadow: 0 0 0 1px var(--separator-color); + border-radius: var(--border-radius-small); +} + +table.doxtable caption { + display: block; +} + +table.fieldtable { + border-collapse: collapse; + width: 100%; +} + +th.markdownTableHeadLeft, +th.markdownTableHeadRight, +th.markdownTableHeadCenter, +th.markdownTableHeadNone, +table.doxtable th { + background: var(--tablehead-background); + color: var(--tablehead-foreground); + font-weight: 600; + font-size: var(--page-font-size); +} + +th.markdownTableHeadLeft:first-child, +th.markdownTableHeadRight:first-child, +th.markdownTableHeadCenter:first-child, +th.markdownTableHeadNone:first-child, +table.doxtable tr th:first-child { + border-top-left-radius: var(--border-radius-small); +} + +th.markdownTableHeadLeft:last-child, +th.markdownTableHeadRight:last-child, +th.markdownTableHeadCenter:last-child, +th.markdownTableHeadNone:last-child, +table.doxtable tr th:last-child { + border-top-right-radius: var(--border-radius-small); +} + +table.markdownTable td, +table.markdownTable th, +table.fieldtable td, +table.fieldtable th, +table.doxtable td, +table.doxtable th { + border: 1px solid var(--separator-color); + padding: var(--spacing-small) var(--spacing-medium); +} + +table.markdownTable td:last-child, +table.markdownTable th:last-child, +table.fieldtable td:last-child, +table.fieldtable th:last-child, +table.doxtable td:last-child, +table.doxtable th:last-child { + border-right: none; +} + +table.markdownTable td:first-child, +table.markdownTable th:first-child, +table.fieldtable td:first-child, +table.fieldtable th:first-child, +table.doxtable td:first-child, +table.doxtable th:first-child { + border-left: none; +} + +table.markdownTable tr:first-child td, +table.markdownTable tr:first-child th, +table.fieldtable tr:first-child td, +table.fieldtable tr:first-child th, +table.doxtable tr:first-child td, +table.doxtable tr:first-child th { + border-top: none; +} + +table.markdownTable tr:last-child td, +table.markdownTable tr:last-child th, +table.fieldtable tr:last-child td, +table.fieldtable tr:last-child th, +table.doxtable tr:last-child td, +table.doxtable tr:last-child th { + border-bottom: none; +} + +table.markdownTable tr, table.doxtable tr { + border-bottom: 1px solid var(--separator-color); +} + +table.markdownTable tr:last-child, table.doxtable tr:last-child { + border-bottom: none; +} + +table.fieldtable th { + font-size: var(--page-font-size); + font-weight: 600; + background-image: none; + background-color: var(--tablehead-background); + color: var(--tablehead-foreground); +} + +table.fieldtable td.fieldtype, .fieldtable td.fieldname, .fieldtable td.fielddoc, .fieldtable th { + border-bottom: 1px solid var(--separator-color); + border-right: 1px solid var(--separator-color); +} + +table.fieldtable tr:last-child td:first-child { + border-bottom-left-radius: var(--border-radius-small); +} + +table.fieldtable tr:last-child td:last-child { + border-bottom-right-radius: var(--border-radius-small); +} + +.memberdecls td.glow, .fieldtable tr.glow { + background-color: var(--primary-light-color); + box-shadow: none; +} + +table.memberdecls { + display: block; + -webkit-tap-highlight-color: transparent; +} + +table.memberdecls tr[class^='memitem'] { + font-family: var(--font-family-monospace); + font-size: var(--code-font-size); +} + +table.memberdecls tr[class^='memitem'] .memTemplParams { + font-family: var(--font-family-monospace); + font-size: var(--code-font-size); + color: var(--primary-dark-color); + white-space: normal; +} + +table.memberdecls .memItemLeft, +table.memberdecls .memItemRight, +table.memberdecls .memTemplItemLeft, +table.memberdecls .memTemplItemRight, +table.memberdecls .memTemplParams { + transition: none; + padding-top: var(--spacing-small); + padding-bottom: var(--spacing-small); + border-top: 1px solid var(--separator-color); + border-bottom: 1px solid var(--separator-color); + background-color: var(--fragment-background); +} + +table.memberdecls .memTemplItemLeft, +table.memberdecls .memTemplItemRight { + padding-top: 2px; +} + +table.memberdecls .memTemplParams { + border-bottom: 0; + border-left: 1px solid var(--separator-color); + border-right: 1px solid var(--separator-color); + border-radius: var(--border-radius-small) var(--border-radius-small) 0 0; + padding-bottom: var(--spacing-small); +} + +table.memberdecls .memTemplItemLeft { + border-radius: 0 0 0 var(--border-radius-small); + border-left: 1px solid var(--separator-color); + border-top: 0; +} + +table.memberdecls .memTemplItemRight { + border-radius: 0 0 var(--border-radius-small) 0; + border-right: 1px solid var(--separator-color); + padding-left: 0; + border-top: 0; +} + +table.memberdecls .memItemLeft { + border-radius: var(--border-radius-small) 0 0 var(--border-radius-small); + border-left: 1px solid var(--separator-color); + padding-left: var(--spacing-medium); + padding-right: 0; +} + +table.memberdecls .memItemRight { + border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; + border-right: 1px solid var(--separator-color); + padding-right: var(--spacing-medium); + padding-left: 0; + +} + +table.memberdecls .mdescLeft, table.memberdecls .mdescRight { + background: none; + color: var(--page-foreground-color); + padding: var(--spacing-small) 0; +} + +table.memberdecls .memItemLeft, +table.memberdecls .memTemplItemLeft { + padding-right: var(--spacing-medium); +} + +table.memberdecls .memSeparator { + background: var(--page-background-color); + height: var(--spacing-large); + border: 0; + transition: none; +} + +table.memberdecls .groupheader { + margin-bottom: var(--spacing-large); +} + +table.memberdecls .inherit_header td { + padding: 0 0 var(--spacing-medium) 0; + text-indent: -12px; + color: var(--page-secondary-foreground-color); +} + +table.memberdecls img[src="closed.png"], +table.memberdecls img[src="open.png"], +div.dynheader img[src="open.png"], +div.dynheader img[src="closed.png"] { + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 5px solid var(--primary-color); + margin-top: 8px; + display: block; + float: left; + margin-left: -10px; + transition: transform 0.25s ease-out; +} + +table.memberdecls img { + margin-right: 10px; +} + +table.memberdecls img[src="closed.png"], +div.dynheader img[src="closed.png"] { + transform: rotate(-90deg); + +} + +.compoundTemplParams { + font-family: var(--font-family-monospace); + color: var(--primary-dark-color); + font-size: var(--code-font-size); +} + +@media screen and (max-width: 767px) { + + table.memberdecls .memItemLeft, + table.memberdecls .memItemRight, + table.memberdecls .mdescLeft, + table.memberdecls .mdescRight, + table.memberdecls .memTemplItemLeft, + table.memberdecls .memTemplItemRight, + table.memberdecls .memTemplParams { + display: block; + text-align: left; + padding-left: var(--spacing-large); + margin: 0 calc(0px - var(--spacing-large)) 0 calc(0px - var(--spacing-large)); + border-right: none; + border-left: none; + border-radius: 0; + white-space: normal; + } + + table.memberdecls .memItemLeft, + table.memberdecls .mdescLeft, + table.memberdecls .memTemplItemLeft { + border-bottom: 0; + padding-bottom: 0; + } + + table.memberdecls .memTemplItemLeft { + padding-top: 0; + } + + table.memberdecls .mdescLeft { + margin-bottom: calc(0px - var(--page-font-size)); + } + + table.memberdecls .memItemRight, + table.memberdecls .mdescRight, + table.memberdecls .memTemplItemRight { + border-top: 0; + padding-top: 0; + padding-right: var(--spacing-large); + overflow-x: auto; + } + + table.memberdecls tr[class^='memitem']:not(.inherit) { + display: block; + width: calc(100vw - 2 * var(--spacing-large)); + } + + table.memberdecls .mdescRight { + color: var(--page-foreground-color); + } + + table.memberdecls tr.inherit { + visibility: hidden; + } + + table.memberdecls tr[style="display: table-row;"] { + display: block !important; + visibility: visible; + width: calc(100vw - 2 * var(--spacing-large)); + animation: fade .5s; + } + + @keyframes fade { + 0% { + opacity: 0; + max-height: 0; + } + + 100% { + opacity: 1; + max-height: 200px; + } + } +} + + +/* + Horizontal Rule + */ + +hr { + margin-top: var(--spacing-large); + margin-bottom: var(--spacing-large); + height: 1px; + background-color: var(--separator-color); + border: 0; +} + +.contents hr { + box-shadow: 100px 0 0 var(--separator-color), + -100px 0 0 var(--separator-color), + 500px 0 0 var(--separator-color), + -500px 0 0 var(--separator-color), + 1500px 0 0 var(--separator-color), + -1500px 0 0 var(--separator-color), + 2000px 0 0 var(--separator-color), + -2000px 0 0 var(--separator-color); +} + +.contents img, .contents .center, .contents center, .contents div.image object { + max-width: 100%; + overflow: auto; +} + +@media screen and (max-width: 767px) { + .contents .dyncontent > .center, .contents > center { + margin-left: calc(0px - var(--spacing-large)); + margin-right: calc(0px - var(--spacing-large)); + max-width: calc(100% + 2 * var(--spacing-large)); + } +} + +/* + Directories + */ +div.directory { + border-top: 1px solid var(--separator-color); + border-bottom: 1px solid var(--separator-color); + width: auto; +} + +table.directory { + font-family: var(--font-family); + font-size: var(--page-font-size); + font-weight: normal; + width: 100%; +} + +table.directory td.entry, table.directory td.desc { + padding: calc(var(--spacing-small) / 2) var(--spacing-small); + line-height: var(--table-line-height); +} + +table.directory tr.even td:last-child { + border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; +} + +table.directory tr.even td:first-child { + border-radius: var(--border-radius-small) 0 0 var(--border-radius-small); +} + +table.directory tr.even:last-child td:last-child { + border-radius: 0 var(--border-radius-small) 0 0; +} + +table.directory tr.even:last-child td:first-child { + border-radius: var(--border-radius-small) 0 0 0; +} + +table.directory td.desc { + min-width: 250px; +} + +table.directory tr.even { + background-color: var(--odd-color); +} + +table.directory tr.odd { + background-color: transparent; +} + +.icona { + width: auto; + height: auto; + margin: 0 var(--spacing-small); +} + +.icon { + background: var(--primary-color); + border-radius: var(--border-radius-small); + font-size: var(--page-font-size); + padding: calc(var(--page-font-size) / 5); + line-height: var(--page-font-size); + transform: scale(0.8); + height: auto; + width: var(--page-font-size); + user-select: none; +} + +.iconfopen, .icondoc, .iconfclosed { + background-position: center; + margin-bottom: 0; + height: var(--table-line-height); +} + +.icondoc { + filter: saturate(0.2); +} + +@media screen and (max-width: 767px) { + div.directory { + margin-left: calc(0px - var(--spacing-large)); + margin-right: calc(0px - var(--spacing-large)); + } +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) .iconfopen, html:not(.light-mode) .iconfclosed { + filter: hue-rotate(180deg) invert(); + } +} + +html.dark-mode .iconfopen, html.dark-mode .iconfclosed { + filter: hue-rotate(180deg) invert(); +} + +/* + Class list + */ + +.classindex dl.odd { + background: var(--odd-color); + border-radius: var(--border-radius-small); +} + +.classindex dl.even { + background-color: transparent; +} + +/* + Class Index Doxygen 1.8 +*/ + +table.classindex { + margin-left: 0; + margin-right: 0; + width: 100%; +} + +table.classindex table div.ah { + background-image: none; + background-color: initial; + border-color: var(--separator-color); + color: var(--page-foreground-color); + box-shadow: var(--box-shadow); + border-radius: var(--border-radius-large); + padding: var(--spacing-small); +} + +div.qindex { + background-color: var(--odd-color); + border-radius: var(--border-radius-small); + border: 1px solid var(--separator-color); + padding: var(--spacing-small) 0; +} + +/* + Footer and nav-path + */ + +#nav-path { + width: 100%; +} + +#nav-path ul { + background-image: none; + background: var(--page-background-color); + border: none; + border-top: 1px solid var(--separator-color); + border-bottom: 1px solid var(--separator-color); + border-bottom: 0; + box-shadow: 0 0.75px 0 var(--separator-color); + font-size: var(--navigation-font-size); +} + +img.footer { + width: 60px; +} + +.navpath li.footer { + color: var(--page-secondary-foreground-color); +} + +address.footer { + color: var(--page-secondary-foreground-color); + margin-bottom: var(--spacing-large); +} + +#nav-path li.navelem { + background-image: none; + display: flex; + align-items: center; +} + +.navpath li.navelem a { + text-shadow: none; + display: inline-block; + color: var(--primary-color) !important; +} + +.navpath li.navelem b { + color: var(--primary-dark-color); + font-weight: 500; +} + +li.navelem { + padding: 0; + margin-left: -8px; +} + +li.navelem:first-child { + margin-left: var(--spacing-large); +} + +li.navelem:first-child:before { + display: none; +} + +#nav-path li.navelem:after { + content: ''; + border: 5px solid var(--page-background-color); + border-bottom-color: transparent; + border-right-color: transparent; + border-top-color: transparent; + transform: translateY(-1px) scaleY(4.2); + z-index: 10; + margin-left: 6px; +} + +#nav-path li.navelem:before { + content: ''; + border: 5px solid var(--separator-color); + border-bottom-color: transparent; + border-right-color: transparent; + border-top-color: transparent; + transform: translateY(-1px) scaleY(3.2); + margin-right: var(--spacing-small); +} + +.navpath li.navelem a:hover { + color: var(--primary-color); +} + +/* + Scrollbars for Webkit +*/ + +#nav-tree::-webkit-scrollbar, +div.fragment::-webkit-scrollbar, +pre.fragment::-webkit-scrollbar, +div.memproto::-webkit-scrollbar, +.contents center::-webkit-scrollbar, +.contents .center::-webkit-scrollbar, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar, +div.contents .toc::-webkit-scrollbar { + background: transparent; + width: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); + height: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); +} + +#nav-tree::-webkit-scrollbar-thumb, +div.fragment::-webkit-scrollbar-thumb, +pre.fragment::-webkit-scrollbar-thumb, +div.memproto::-webkit-scrollbar-thumb, +.contents center::-webkit-scrollbar-thumb, +.contents .center::-webkit-scrollbar-thumb, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-thumb, +div.contents .toc::-webkit-scrollbar-thumb { + background-color: transparent; + border: var(--webkit-scrollbar-padding) solid transparent; + border-radius: calc(var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); + background-clip: padding-box; +} + +#nav-tree:hover::-webkit-scrollbar-thumb, +div.fragment:hover::-webkit-scrollbar-thumb, +pre.fragment:hover::-webkit-scrollbar-thumb, +div.memproto:hover::-webkit-scrollbar-thumb, +.contents center:hover::-webkit-scrollbar-thumb, +.contents .center:hover::-webkit-scrollbar-thumb, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody:hover::-webkit-scrollbar-thumb, +div.contents .toc:hover::-webkit-scrollbar-thumb { + background-color: var(--webkit-scrollbar-color); +} + +#nav-tree::-webkit-scrollbar-track, +div.fragment::-webkit-scrollbar-track, +pre.fragment::-webkit-scrollbar-track, +div.memproto::-webkit-scrollbar-track, +.contents center::-webkit-scrollbar-track, +.contents .center::-webkit-scrollbar-track, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-track, +div.contents .toc::-webkit-scrollbar-track { + background: transparent; +} + +#nav-tree::-webkit-scrollbar-corner { + background-color: var(--side-nav-background); +} + +#nav-tree, +div.fragment, +pre.fragment, +div.memproto, +.contents center, +.contents .center, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody, +div.contents .toc { + overflow-x: auto; + overflow-x: overlay; +} + +#nav-tree { + overflow-x: auto; + overflow-y: auto; + overflow-y: overlay; +} + +/* + Scrollbars for Firefox +*/ + +#nav-tree, +div.fragment, +pre.fragment, +div.memproto, +.contents center, +.contents .center, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody, +div.contents .toc { + scrollbar-width: thin; +} + +/* + Optional Dark mode toggle button +*/ + +doxygen-awesome-dark-mode-toggle { + display: inline-block; + margin: 0 0 0 var(--spacing-small); + padding: 0; + width: var(--searchbar-height); + height: var(--searchbar-height); + background: none; + border: none; + border-radius: var(--searchbar-height); + vertical-align: middle; + text-align: center; + line-height: var(--searchbar-height); + font-size: 22px; + display: flex; + align-items: center; + justify-content: center; + user-select: none; + cursor: pointer; +} + +doxygen-awesome-dark-mode-toggle > svg { + transition: transform .1s ease-in-out; +} + +doxygen-awesome-dark-mode-toggle:active > svg { + transform: scale(.5); +} + +doxygen-awesome-dark-mode-toggle:hover { + background-color: rgba(0,0,0,.03); +} + +html.dark-mode doxygen-awesome-dark-mode-toggle:hover { + background-color: rgba(0,0,0,.18); +} + +/* + Optional fragment copy button +*/ +.doxygen-awesome-fragment-wrapper { + position: relative; +} + +doxygen-awesome-fragment-copy-button { + opacity: 0; + background: var(--fragment-background); + width: 28px; + height: 28px; + position: absolute; + right: calc(var(--spacing-large) - (var(--spacing-large) / 2.5)); + top: calc(var(--spacing-large) - (var(--spacing-large) / 2.5)); + border: 1px solid var(--fragment-foreground); + cursor: pointer; + border-radius: var(--border-radius-small); + display: flex; + justify-content: center; + align-items: center; +} + +.doxygen-awesome-fragment-wrapper:hover doxygen-awesome-fragment-copy-button, doxygen-awesome-fragment-copy-button.success { + opacity: .28; +} + +doxygen-awesome-fragment-copy-button:hover, doxygen-awesome-fragment-copy-button.success { + opacity: 1 !important; +} + +doxygen-awesome-fragment-copy-button:active:not([class~=success]) svg { + transform: scale(.91); +} + +doxygen-awesome-fragment-copy-button svg { + fill: var(--fragment-foreground); + width: 18px; + height: 18px; +} + +doxygen-awesome-fragment-copy-button.success svg { + fill: rgb(14, 168, 14); +} + +doxygen-awesome-fragment-copy-button.success { + border-color: rgb(14, 168, 14); +} + +@media screen and (max-width: 767px) { + .textblock > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + .textblock li > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + .memdoc li > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + .memdoc > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + dl dd > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button { + right: 0; + } +} + +/* + Optional paragraph link button +*/ + +a.anchorlink { + font-size: 90%; + margin-left: var(--spacing-small); + color: var(--page-foreground-color) !important; + text-decoration: none; + opacity: .15; + display: none; + transition: opacity .1s ease-in-out, color .1s ease-in-out; +} + +a.anchorlink svg { + fill: var(--page-foreground-color); +} + +h3 a.anchorlink svg, h4 a.anchorlink svg { + margin-bottom: -3px; + margin-top: -4px; +} + +a.anchorlink:hover { + opacity: .45; +} + +h2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a.anchorlink { + display: inline-block; +} diff --git a/docs/doxygentheme/header.html b/docs/doxygentheme/header.html new file mode 100644 index 0000000..cda3d32 --- /dev/null +++ b/docs/doxygentheme/header.html @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + $projectname: $title + + + $title + + + + + + + + + + + + $treeview + $search + $mathjax + + $extrastylesheet + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
$projectname +  $projectnumber + +
+ +
$projectbrief
+ +
+
$projectbrief
+
$searchbox
+
+ + \ No newline at end of file diff --git a/docs/doxygentheme/logo.drawio.svg b/docs/doxygentheme/logo.drawio.svg new file mode 100644 index 0000000..a506ee0 --- /dev/null +++ b/docs/doxygentheme/logo.drawio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/doxygentheme/toggle-alternative-theme.js b/docs/doxygentheme/toggle-alternative-theme.js new file mode 100644 index 0000000..72c3731 --- /dev/null +++ b/docs/doxygentheme/toggle-alternative-theme.js @@ -0,0 +1,12 @@ + +let original_theme_active = true; + +function toggle_alternative_theme() { + if(original_theme_active) { + document.documentElement.classList.add("alternative") + original_theme_active = false; + } else { + document.documentElement.classList.remove("alternative") + original_theme_active = true; + } +} \ No newline at end of file diff --git a/docs/dsysinfo.zh_CN.dox b/docs/dsysinfo.zh_CN.dox new file mode 100644 index 0000000..07912a6 --- /dev/null +++ b/docs/dsysinfo.zh_CN.dox @@ -0,0 +1,221 @@ +/*! +@~chinese +@file includes/global/dsysinfo.h + +dsysinfo.h是一组用于查询系统信息的静态类 + +@enum Dtk::Core::DSysInfo::ProductType dsysinfo.h +@brief 产品信息 +| 值 | 序号 | 含义 | +|-------------|----|-------| +| UnknownType | 0 | 未知类型 | +| deepin | 1 | 深度社区版 | +| ArchLinux | 2 | | +| CentOS | 3 | | +| Debian | 4 | | +| Fedora | 5 | | +| LinuxMint | 6 | | +| Manjaro | 7 | | +| openSUSE | 8 | | +| SailfishOS | 9 | | +| Ubuntu | 10 | | +| Uos | 11 | | +| Gentoo | 12 | | +| NixOS | 13 | | + + +@enum Dtk::Core::DSysInfo::DeepinType dsysinfo.h +@brief 深度操作系统版本 +| 值 | 序号 | 含义 | +|--------------------|----|-----------------------| +| UnknownDeepin | 0 | 未知版本 | +| DeepinDesktop | 1 | deepin桌面发行版 | +| DeepinProfessional | 2 | deepin专业版,现为uos专业版 | +| DeepinServer | 3 | deepin服务器版本,现为uos服务器版 | +| DeepinPersonal | 4 | deepin个人版,现为uos家庭版 | + +@enum Dtk::Core::DSysInfo::LogoType +@brief 系统的logo类型 +| 值 | 序号 | 含义 | +|--------------|----|----| +| Normal | 0 | 正常 | +| Light | 1 | 亮色 | +| Symbolic | 2 | 符号 | +| Transparent | 3 | 水印 | + +@enum Dtk::Core::DSysInfo::OrgType +@brief +| 值 | 序号 | 含义 | +|--------------|----|--------------| +| Distribution | 0 | 当前版本 | +| Distributor | 1 | 当前发行版 | +| Manufacturer | 2 | 当前发行版或设备的制造商 | + +@enum Dtk::Core::DSysInfo::UosType +@brief UOS版本类型 +| 值 | 序号 | 含义 | +|-----------------|----|-------------| +| UosTypeUnknown | 0 | 未知版本 | +| UosDesktop | 1 | UOS桌面版 | +| UosServer | 2 | UOS服务器版 | +| UosDevice | 3 | UOS设备版 | +| UosTypeCount | 4 | 记录枚举数量 | + +@enum Dtk::Core::DSysInfo::UosEdition +@brief 详细uos版本 +| 值 | 序号 | 含义 | +|-------------------|----|---------------------| +| UosEditionUnknown | 0 | 未知发行版 | +| UosProfessional | 1 | UOS专业版 | +| UosHome | 2 | UOS家庭版 | +| UosCommunity | 3 | 社区版 | +| UosMilitary | 4 | * | +| UosEnterprise | 5 | UOS企业版 | +| UosEnterpriseC | 6 | UOS行业版 | +| UosEuler | 7 | UOS服务器欧拉版 | +| UosMilitaryS | 8 | * | +| UosDeviceEdition | 9 | UOS专用设备版 | +| UosEducation | 10 | UOS教育版 | +| UosEditionCount | 11 | 记录枚举数量 | + +@enum Dtk::Core::DSysInfo::UosArch +@brief UOS使用的架构 +| 值 | 序号 | 含义 | +|----------------|----|--------| +| UosArchUnknown | 0 | 未知架构 | +| UosAMD64 | 1 | x86_64 | +| UosARM64 | 2 | arm64 | +| UosMIPS64 | 4 | mips64 | +| UosSW64 | 8 | sw_64 | + + + + +@fn static bool Dtk::Core::DSysInfo::isDeepin() //FIXME: 显示错乱 无法修复 +@brief 是否为deepin系统 +@return 0 不是deepin系统 +@return 1 是deepin系统 + +@fn static bool Dtk::Core::DSysInfo::isDDE() +@brief 是否使用dde桌面环境 +@note 此方法仅在linux平台下可用 + +@fn static DeepinType Dtk::Core::DSysInfo::deepinType() +@brief deepin系统类型 +@note 此方法仅在linux平台下可用 + +@fn static QString Dtk::Core::DSysInfo::deepinTypeDisplayName (const QLocale &locale=QLocale::system()) +@brief 显示的deepin发行版类型名称 +@note 此方法仅在linux平台下可用 + +@fn static QString Dtk::Core::DSysInfo::deepinVersion() +@brief deepin版本 +@note 此方法仅在linux平台下可用 + +@fn static QString Dtk::Core::DSysInfo::deepinCopyright() +@brief deepin 开源许可协议 +@note 此方法仅在linux平台下可用 + +@fn static UosEdition Dtk::Core::DSysInfo::uosEditionType() +@brief DSysInfo::osEditionType 版本类型 显示版本类型 专业版/个人版/社区版.. +@note 根据 osBuild.B && osBuild.D +@note 此方法仅在linux平台下可用 + +@fn static UosArch Dtk::Core::DSysInfo::uosArch() +@brief DSysInfo::osArch 架构信息(使用一个字节的二进制位,从低位到高位) 【0x8 sw64】【0x4 mips64】【0x2 arm64】【0x1 amd64】 +@note 此处架构是从OsBuild获取的系统版本的Arch信息,并不是指硬件的Arch信息 +@note 此方法仅在linux平台下可用 + +@fn static QString uosProductTypeName(const QLocale &locale=QLocale::system()) +@brief DSysInfo::osProductTypeName 版本名称 ProductType[xx] 项对应的值, 如果找不到对应语言的默认使用 ProductType的值(Desktop/Server/Device) locale 当前系统语言 + +@fn static QString Dtk::Core::DSysInfo::uosSystemName(const QLocale &locale=QLocale::system()) +@brief SystemName[xx] 项对应的值, 如果找不到对应语言的默认使用 SystemName 的值 Uniontech OS locale 当前系统语言 +@note 此方法仅在linux平台下可用 + +@fn static QString Dtk::Core::DSysInfo::function uosEditionName(const QLocale &locale=QLocale::system()) +@brief 版本名称 EditionName[xx] 项对应的值, 如果找不到对应语言的默认使用 EditionName 的值(Professional/Home/Community...) locale 当前系统语言 +@note 此方法仅在linux平台下可用 + +@fn static QString Dtk::Core::DSysInfo::spVersion() +@brief 阶段版本名称 小版本号 A-BC-D 中 BC、 A.B.C 中的 B 返回 SP1-SPxx, 如果正式版返回空 X.Y.Z模式下暂不支持返回此版本号 +@note minVersion.BC == 00 正式版本, minVersion.BC | minVersion.B == 01-99 SP1….SP99 +@note 此方法仅在linux平台下可用 + +@fn static QString Dtk::Core::DSysInfo::udpateVersion() +@brief 更新版本名称 小版本号 A-BC-D 中 D、A.B.C 模式中的 C 返回 update1… update9, 如果正式版返回空 X.Y.Z模式下暂不支持返回此版本号 +@note minVersion.D == 0;正式版本 minVersion.D | minVersion.C == 1-9;update1… update9,updateA...updateZ +@note 此方法仅在linux平台下可用 + +@fn static QString Dtk::Core::DSysInfo::majorVersion() +@brief 主版本号 主版本号 【20】【23】【25】【26】【29】【30】 +@note 此方法仅在linux平台下可用 + +@fn static QString Dtk::Core::DSysInfo::minorVersion() +@brief 小版本号 【ABCD】 ·[0-9]{4} 【A.B.C】 或者【X.Y.Z】 +@note 此方法仅在linux平台下可用 + +@fn static QString Dtk::Core::DSysInfo::buildVersion() +@brief 小版本号 系统镜像批次号,按时间顺序(不可回退)从100-999递增 +@note 此方法仅在linux平台下可用 + +@fn static QString Dtk::Core::DSysInfo::distributionInfoPath () +@brie 返回distribution文件地址 一般在`/usr/share/deepin/`目录下 + +@fn static QString Dtk::Core::DSysInfo::distributionInfoSectionName(OrgType type) +@brief 返回distribution.info 文件中SectionName字段的值 + +@fn static Dtk::Core::DSysInfo::distributionOrgName(OrgType type=Distribution, const QLocale &locale=QLocale::system()) +@brief 返回组织名称。使用类型为Distribution来获取当前deepin distribution本身的名称 + +@fn static QPairDtk::Core::DSysInfo::distributionOrgWebsite(OrgType type=Distribution) +@brief 发行版组织的网站名称和网址。使用 type 作为 Distribution 获取当前 deepin 发行版本身的名称。 + +@fn static QString Dtk::Core::DSysInfo::distributionOrgLogo(OrgType orgType=Distribution, LogoType type=Normal, const QString &fallback=QString()) +@brief 获得的组织logo路径,如果不存在,则返回给定的其他路径。 使用 type 作为 Distribution 获取当前 deepin 发行版本身的logo。 + +@fn static QString Dtk::Core::DSysInfo::operatingSystemName() +@brief 操作系统名 + +@fn static ProductType Dtk::Core::DSysInfo::productType() +@brief 产品类型 + +@fn static QString Dtk::Core::DSysInfo::productVersion() +@brief 产品版本 + +@fn static bool Dtk::Core::DSysInfo::isCommunityEdition() +@brief 检查当前版本是否是社区版 开发者可以使用这种方式来检查我们是否需要启用或禁用社区版或企业版的功能。 +目前的规则: + 专业版、服务器版、个人版(DeepinType)将被视为企业版。 + Uos(ProductType)将被视为企业版。 + +@fn static QString Dtk::Core::DSysInfo::computerName() +@brief 电脑名 + +@fn static QString Dtk::Core::DSysInfo::cpuModelName() +@brief cpu模式名 + +@fn static qint64 Dtk::Core::DSysInfo::memoryInstalledSize() +@brief 内存安装大小 + +@fn static qint64 Dtk::Core::DSysInfo::memoryTotalSize() +@brief 实际内存大小 + +@fn static qint64 Dtk::Core::DSysInfo::systemDiskSize() +@brief 系统磁盘大小 + +@fn static QDateTime Dtk::Core::DSysInfo::bootTime () +@brief 系统启动时间点 + +@fn static QDateTime Dtk::Core::DSysInfo::shutdownTime () +@brief 上一次正常关机时间点(重启也会被记录在内) + +@fn static qint64 Dtk::Core::DSysInfo::uptime() +@brief 系统启动到现在时长 +@note 参见`cat /proc/uptime`命令 + +@fn static Arch Dtk::Core::DSysInfo::arch +@brief cpu架构信息 +@note 此处架构是从gcc编译器获取的 + +*/ \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..6a01ca4 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(dasync-example) +add_subdirectory(expintf-example) diff --git a/examples/dasync-example/CMakeLists.txt b/examples/dasync-example/CMakeLists.txt new file mode 100644 index 0000000..9813915 --- /dev/null +++ b/examples/dasync-example/CMakeLists.txt @@ -0,0 +1,21 @@ +set(BINNAME dasync) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +find_package(Qt5 REQUIRED COMPONENTS Widgets) +find_package(Qt5 REQUIRED COMPONENTS Core) + +add_executable(${BINNAME} + main.cpp +) + +target_link_libraries( + ${BINNAME} PRIVATE + Qt5::Core + Qt5::Widgets + dtkcore + -lpthread +) +target_include_directories(${BINNAME} PUBLIC + ../../include/global/ + ../../include/util/ + ../../include/ +) diff --git a/examples/dasync-example/main.cpp b/examples/dasync-example/main.cpp new file mode 100644 index 0000000..84c2a7d --- /dev/null +++ b/examples/dasync-example/main.cpp @@ -0,0 +1,444 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#include + +#include + +#include "util/dasync.h" +#include "util/dthreadutils.h" + +#ifdef QT_DEBUG +#include +#endif + +DCORE_USE_NAMESPACE + +#define XLog() qDebug() << __LINE__ << " " + +#define RUN_IN_SUB_THREAD 1 + +#define THREAD_BEGIN std::thread thread([&]{ +#define THREAD_END }); thread.detach(); + +#if RUN_IN_SUB_THREAD +# define OPT_THREAD_BEGIN THREAD_BEGIN +# define OPT_THREAD_END THREAD_END +#else +# define OPT_THREAD_BEGIN +# define OPT_THREAD_END +#endif + +#define TIMED_EXIT(second, loop) QTimer::singleShot(second * 1000, [&]{ loop.exit(); }) + +struct Configure +{ + QObject *o = nullptr; + QWidget *w = nullptr; +} conf; +static Configure *config = &conf; + +int main1(int argc, char *argv[]); +int main2(int argc, char *argv[]); +int main3(int argc, char *argv[]); + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + + // 将以下所有新建的对象都托管给 w、o + config->w = new QWidget; + config->o = new QObject; + + config->w->show(); + + main1(argc, argv); + main2(argc, argv); + main3(argc, argv); + + QTimer::singleShot(1 * 1000, [&]{ + config->w->deleteLater(); + config->o->deleteLater(); + }); + + QTimer::singleShot(2 * 1000, [&]{ + qApp->exit(0); + }); + + qDebug() << "finished xxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + + return app.exec(); +} + +#pragma mark main1 ------------------------------------------------------------ + +// 这是一个最基础的示例程序 +DAsync *testTask() { + auto task = new DAsync(config->o); + + int i = 0; + while (i < 100) { + task->postData(i++); + } + + task->post([](int arg) { + + Q_ASSERT(!D_THREAD_IN_MAIN()); + XLog() << "run in child thread: " << arg; + return arg * 2; + + })->then([&](int arg) { + + Q_ASSERT(D_THREAD_IN_MAIN()); + XLog() << "run in main thread:" << arg; + + })->start(); + + task->postData(i++); + + return task; +} + +void runTest() +{ + OPT_THREAD_BEGIN + auto task = testTask(); + // 删除前应该先等待所有的任务执行完或取消未执行的任务 + // 主线程中只能用 isFinished 查询状态,用 cancelAll 取消之后的任务队列 + // 其它子线程中(非 post、非 then 函数)中可以直接 waitForFinished 然后删除 + // 也可以使用 task->setParent 去托管,自动释放 + if (!D_THREAD_IN_MAIN()) { + task->waitForFinished(false); + // task->deleteLater(); + } + OPT_THREAD_END +} + +int main1(int argc, char *argv[]) { + QEventLoop loop; + TIMED_EXIT(3, loop); + + XLog() << "in main thread: " << pthread_self(); + + // DAsync 依赖事件循环,不能被阻塞,比如 thread.join 就不行 + // 运行在主线程中和运行在子线程中应该有一样的结果才对 + OPT_THREAD_BEGIN + runTest(); + OPT_THREAD_END + + return loop.exec(); +} + +#pragma mark main2 ------------------------------------------------------------ + +int main2(int argc, char *argv[]) { + QEventLoop loop; + TIMED_EXIT(3, loop); + + OPT_THREAD_BEGIN + QWidget *w = DThreadUtil::runInMainThread([&](){ + QWidget *w = new QWidget(config->w); + w->setBackgroundRole(QPalette::HighlightedText); + w->show(); + return w; + }); + // w->show(); + OPT_THREAD_END + + return loop.exec(); +} + +#pragma mark main3 ------------------------------------------------------------ + +int test1(); +int test2(); +int test3(); +int test4(); +int test5(); +int test6(); +int test7(); +int test8(); + +int main3(int argc, char *argv[]) { + std::clog << "in main thread: " << pthread_self() << std::endl; + + // 示例 1,输入输出都是基本类型 + test1(); + + // 示例 2,输入基本类型,输出复合类型 + test2(); + + // 示例 3,输入输出都是复合类型 + test3(); + + // 示例 4,输入输出都是自定义类型的指针 + test4(); + + // 示例 5, 异步执行一个输入复合类型、没有输出的一次性任务,执行结束后通知主线程 + // 间歇性输入数据,要保证生产者消费者模型的正确性。 + test5(); + + // 示例 6, 异步执行一个没有输入、输出参数的一次性任务,执行结束后通知在主线 + test6(); + + // 示例 7, 异步运行一个没有输入参数的一次性任务,执行后在主线程处理结果 + test7(); + + // 示例 8, 在子线程中异步创建一个 widget 并显示出来: + test8(); + // std::thread thread([&]{ test8(); }); + // thread.detach(); + + return 0; +} + +int test1() { + QEventLoop loop; + TIMED_EXIT(2, loop); + + // 加 static 防止函数执行结束后线程中继续 postData 访问已经释放的栈上变量 + static auto task1 = new DAsync(config->o); + static int i = 0; + + while (i < 100) { + task1->postData(i++); + } + + task1->post([](int arg) { + + XLog() << "async task: " << arg; + return arg * 2; + + })->then([](int arg) { + + XLog() << "get result: " << arg; + + })->start(); + + return loop.exec(); +} + +int test2() { + QEventLoop loop; + + static auto task2 = new DAsync(config->o); + + static int i = 0; + static bool stopFlag = false; + + // TIMED_EXIT(3, loop); + QTimer::singleShot(3 * 1000, [&]{ + stopFlag = true; + loop.exit(); + }); + + while(i < 100) { + task2->postData(i++); + } + + task2->post([](int arg) -> QString { + + XLog() << "async task: " << arg; + return QString::number(arg); + + })->then([](QString arg) { + + XLog() << "get result: " << arg; + + })->start(); + + THREAD_BEGIN + while (!stopFlag && i < 220) { + XLog() << "post data: " << i; + task2->postData(i++); + usleep(200 * 1000); + } + THREAD_END + + // task2->waitForFinished(); + // task2->deleteLater(); + + return loop.exec(); +} + +int test3() { + QEventLoop loop; + TIMED_EXIT(3, loop); + + static auto task3 = new DAsync(config->o); + + static int i = 0; + while (i < 100) { + task3->postData(QString::number(i++)); + } + + task3->post([](QString arg) -> QString { + + XLog() << "async task: " << arg; + return arg; + + })->then([](QString arg) { + + XLog() << "get result " << arg; + + })->start(); + + // task3->waitForFinished(); + // task3->deleteLater(); + + return loop.exec(); +} + +int test4() { + QEventLoop loop; + TIMED_EXIT(3, loop); + + class Test : public QObject { + public: + Test(int in, QObject *parent = nullptr) + : QObject (parent) + , count (in) + { + } + int count = 0; + }; + + static auto task4 = new DAsync(config->o); + static int i = 0; + + while (i < 100) { + task4->postData(new Test(i++, config->o)); + } + + task4->post([](Test *arg) -> Test * { + + XLog() << "async task: " << arg->count; + return arg; + + })->then([](Test *arg) { + + XLog() << "get result " << arg->count; + + })->start(); + + return loop.exec(); +} + +int test5() { + QEventLoop loop; + // TIMED_EXIT(3, loop); + static bool stopFlag = false; + QTimer::singleShot(3 * 1000, [&]{ + stopFlag = true; + loop.exit(); + }); + + static auto task5 = new DAsync(config->o); + static int i = 0; + + while (i < 100) { + task5->postData(QString::number(i++)); + } + + task5->post([](QString arg) { + + XLog() << "async task." << arg; + + })->then([]() { + + XLog() << "get void"; + + })->start(); + + OPT_THREAD_BEGIN + while (!stopFlag) { + usleep(200 * 1000); + task5->postData(QString::number(i++)); + } + OPT_THREAD_END + + return loop.exec(); +} + +int test6() { + QEventLoop loop; + TIMED_EXIT(1, loop); + + static auto task6 = new DAsync(config->o); + + task6->post([]() { + + XLog() << "async task."; + + })->then([]() { + + XLog() << "get result."; + + })->start(); + + // 如果只想在子线程执行一个任务,不需要主线程的任何处理,按照以下方式, + // 其实也只是只设置一个函数就可以了: + // task6->post([]() { XLog() << "async task."; }); + // task6->startUp(); + + return loop.exec(); +} + +int test7() { + QEventLoop loop; + TIMED_EXIT(1, loop); + + static auto task7 = new DAsync(config->o); + static int i = 0; + + task7->post([&]() { + + XLog() << "async task."; + return QString("%1").arg(i++); + + })->then([](QString arg) { + + XLog() << "get result " << arg; + + })->start(); + + return loop.exec(); +} + +int test8() { + QEventLoop loop; + TIMED_EXIT(1, loop); + + static auto task8 = new DAsync(config->o); + + // 注意,任务是异步执行的,传进去的一定不能是栈区变量! + static int i = 0; + task8->post([&]() -> QString { + Q_ASSERT(!D_THREAD_IN_MAIN()); + QWidget *w = DThreadUtil::runInMainThread([](){ + Q_ASSERT(D_THREAD_IN_MAIN()); + QWidget *w = new QWidget(config->w); + w->setBackgroundRole(QPalette::Text); + w->show(); + return w; + }); + + // 在外面调用并不合适,虽然也能显示出来。比如 mac 上这么用就显示不出来 + // w->setBackgroundRole(QPalette::Text); + // w->show(); + + XLog() << "async task." << QString("%1").arg(i++); + return QString("%1").arg(i++); + + })->then([](QString str) { + + XLog() << "get result " << str; + + })->start(); + + return loop.exec(); +} diff --git a/examples/expintf-example/CMakeLists.txt b/examples/expintf-example/CMakeLists.txt new file mode 100644 index 0000000..9cdb1e4 --- /dev/null +++ b/examples/expintf-example/CMakeLists.txt @@ -0,0 +1,19 @@ +set(BINNAME exprintf) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +find_package(Qt5 REQUIRED COMPONENTS DBus) + +add_executable(${BINNAME} + main.cpp +) + +target_link_libraries( + ${BINNAME} PRIVATE + Qt5::DBus + dtkcore +) +target_include_directories(${BINNAME} PUBLIC + ../../include/base + ../../include/base/private/ + ../../include/filesystem/ + ../../include/ +) diff --git a/examples/expintf-example/main.cpp b/examples/expintf-example/main.cpp new file mode 100644 index 0000000..ef5558c --- /dev/null +++ b/examples/expintf-example/main.cpp @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "util/dexportedinterface.h" + +#include +#include +#include +#include + +#include + +//#define ALTERNATE_USAGE + +DCORE_USE_NAMESPACE + +class CustomInterface : public DUtil::DExportedInterface +{ + QVariant invoke(const QString &action, const QString ¶meters) const + { + QJsonDocument d = QJsonDocument::fromJson(parameters.toUtf8()); + if (action == "pow") { + return QVariant(pow(d["a"].toDouble(), d["b"].toDouble())); + } + return QVariant(); + } +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + QDBusConnection::sessionBus().registerService("com.deepin.ExpIntfTest"); + +#ifndef ALTERNATE_USAGE + DUtil::DExportedInterface *ei = new DUtil::DExportedInterface(); + ei->registerAction("quit", "quit the application", [&app](QString)->QVariant { + app.quit(); + return QVariant(); + }); + + ei->registerAction("answer", "answer to the ultimate question of life, the universe, and everything", + [](QString)->QVariant {return QVariant(42);}); + + ei->registerAction("sum", "returns the sum of two integers", [](QString p)->QVariant { + QJsonDocument d = QJsonDocument::fromJson(p.toUtf8()); + return QVariant(d["a"].toInt() + d["b"].toInt()); + }); +#else + CustomInterface *cei = new CustomInterface(); + cei->registerAction("pow", "raise a number to a power"); +#endif + + return app.exec(); +} diff --git a/include/DtkCore/DAbstractUnitFormatter b/include/DtkCore/DAbstractUnitFormatter new file mode 100644 index 0000000..2d494ea --- /dev/null +++ b/include/DtkCore/DAbstractUnitFormatter @@ -0,0 +1 @@ +#include "dabstractunitformatter.h" diff --git a/include/DtkCore/DBaseFileWatcher b/include/DtkCore/DBaseFileWatcher new file mode 100644 index 0000000..1348031 --- /dev/null +++ b/include/DtkCore/DBaseFileWatcher @@ -0,0 +1 @@ +#include "dbasefilewatcher.h" diff --git a/include/DtkCore/DCapDir b/include/DtkCore/DCapDir new file mode 100644 index 0000000..ee2c873 --- /dev/null +++ b/include/DtkCore/DCapDir @@ -0,0 +1 @@ +#include "dcapfile.h" diff --git a/include/DtkCore/DCapFile b/include/DtkCore/DCapFile new file mode 100644 index 0000000..ee2c873 --- /dev/null +++ b/include/DtkCore/DCapFile @@ -0,0 +1 @@ +#include "dcapfile.h" diff --git a/include/DtkCore/DCapManager b/include/DtkCore/DCapManager new file mode 100644 index 0000000..3a4f25b --- /dev/null +++ b/include/DtkCore/DCapManager @@ -0,0 +1 @@ +#include "dcapmanager.h" diff --git a/include/DtkCore/DConfig b/include/DtkCore/DConfig new file mode 100644 index 0000000..6157c75 --- /dev/null +++ b/include/DtkCore/DConfig @@ -0,0 +1 @@ +#include "dconfig.h" diff --git a/include/DtkCore/DConfigFile b/include/DtkCore/DConfigFile new file mode 100644 index 0000000..bb79fb3 --- /dev/null +++ b/include/DtkCore/DConfigFile @@ -0,0 +1 @@ +#include "dconfigfile.h" diff --git a/include/DtkCore/DDBusExtended b/include/DtkCore/DDBusExtended new file mode 100644 index 0000000..2798dc4 --- /dev/null +++ b/include/DtkCore/DDBusExtended @@ -0,0 +1 @@ +#include "ddbusextended.h" diff --git a/include/DtkCore/DDBusExtendedAbstractInterface b/include/DtkCore/DDBusExtendedAbstractInterface new file mode 100644 index 0000000..b536959 --- /dev/null +++ b/include/DtkCore/DDBusExtendedAbstractInterface @@ -0,0 +1 @@ +#include "ddbusextendedabstractinterface.h" diff --git a/include/DtkCore/DDBusInterface b/include/DtkCore/DDBusInterface new file mode 100644 index 0000000..c32d931 --- /dev/null +++ b/include/DtkCore/DDBusInterface @@ -0,0 +1 @@ +#include "ddbusinterface.h" diff --git a/include/DtkCore/DDBusSender b/include/DtkCore/DDBusSender new file mode 100644 index 0000000..8881ba7 --- /dev/null +++ b/include/DtkCore/DDBusSender @@ -0,0 +1 @@ +#include "ddbussender.h" diff --git a/include/DtkCore/DDciFile b/include/DtkCore/DDciFile new file mode 100644 index 0000000..6ac5d91 --- /dev/null +++ b/include/DtkCore/DDciFile @@ -0,0 +1 @@ +#include "ddcifile.h" diff --git a/include/DtkCore/DDesktopEntry b/include/DtkCore/DDesktopEntry new file mode 100644 index 0000000..847127a --- /dev/null +++ b/include/DtkCore/DDesktopEntry @@ -0,0 +1 @@ +#include "ddesktopentry.h" diff --git a/include/DtkCore/DDiskSizeFormatter b/include/DtkCore/DDiskSizeFormatter new file mode 100644 index 0000000..6584e09 --- /dev/null +++ b/include/DtkCore/DDiskSizeFormatter @@ -0,0 +1 @@ +#include "ddisksizeformatter.h" diff --git a/include/DtkCore/DError b/include/DtkCore/DError new file mode 100644 index 0000000..7988039 --- /dev/null +++ b/include/DtkCore/DError @@ -0,0 +1 @@ +#include "derror.h" diff --git a/include/DtkCore/DExpected b/include/DtkCore/DExpected new file mode 100644 index 0000000..e197682 --- /dev/null +++ b/include/DtkCore/DExpected @@ -0,0 +1 @@ +#include "dexpected.h" diff --git a/include/DtkCore/DExportedInterface b/include/DtkCore/DExportedInterface new file mode 100644 index 0000000..eea6f16 --- /dev/null +++ b/include/DtkCore/DExportedInterface @@ -0,0 +1 @@ +#include "dexportedinterface.h" diff --git a/include/DtkCore/DFileServices b/include/DtkCore/DFileServices new file mode 100644 index 0000000..77b70de --- /dev/null +++ b/include/DtkCore/DFileServices @@ -0,0 +1 @@ +#include "dfileservices.h" diff --git a/include/DtkCore/DFileSystemWatcher b/include/DtkCore/DFileSystemWatcher new file mode 100644 index 0000000..97400f2 --- /dev/null +++ b/include/DtkCore/DFileSystemWatcher @@ -0,0 +1 @@ +#include "dfilesystemwatcher.h" diff --git a/include/DtkCore/DFileWatcher b/include/DtkCore/DFileWatcher new file mode 100644 index 0000000..768151a --- /dev/null +++ b/include/DtkCore/DFileWatcher @@ -0,0 +1 @@ +#include "dfilewatcher.h" diff --git a/include/DtkCore/DFileWatcherManager b/include/DtkCore/DFileWatcherManager new file mode 100644 index 0000000..a9f8682 --- /dev/null +++ b/include/DtkCore/DFileWatcherManager @@ -0,0 +1 @@ +#include "dfilewatchermanager.h" diff --git a/include/DtkCore/DLog b/include/DtkCore/DLog new file mode 100644 index 0000000..a9ab429 --- /dev/null +++ b/include/DtkCore/DLog @@ -0,0 +1,7 @@ +#include "RollingFileAppender.h" +#include "Logger.h" +#include "LogManager.h" +#include "FileAppender.h" +#include "ConsoleAppender.h" +#include "AbstractStringAppender.h" +#include "AbstractAppender.h" diff --git a/include/DtkCore/DNotifySender b/include/DtkCore/DNotifySender new file mode 100644 index 0000000..85796fc --- /dev/null +++ b/include/DtkCore/DNotifySender @@ -0,0 +1 @@ +#include "dnotifysender.h" diff --git a/include/DtkCore/DObject b/include/DtkCore/DObject new file mode 100644 index 0000000..75fbcb7 --- /dev/null +++ b/include/DtkCore/DObject @@ -0,0 +1 @@ +#include "dobject.h" diff --git a/include/DtkCore/DObjectPrivate b/include/DtkCore/DObjectPrivate new file mode 100644 index 0000000..0c145c8 --- /dev/null +++ b/include/DtkCore/DObjectPrivate @@ -0,0 +1 @@ +#include "dobject_p.h" diff --git a/include/DtkCore/DPathBuf b/include/DtkCore/DPathBuf new file mode 100644 index 0000000..961c40b --- /dev/null +++ b/include/DtkCore/DPathBuf @@ -0,0 +1 @@ +#include "dpathbuf.h" diff --git a/include/DtkCore/DPinyin b/include/DtkCore/DPinyin new file mode 100644 index 0000000..0df4d4c --- /dev/null +++ b/include/DtkCore/DPinyin @@ -0,0 +1 @@ +#include "dpinyin.h" diff --git a/include/DtkCore/DRecentManager b/include/DtkCore/DRecentManager new file mode 100644 index 0000000..a1be4b0 --- /dev/null +++ b/include/DtkCore/DRecentManager @@ -0,0 +1 @@ +#include "drecentmanager.h" diff --git a/include/DtkCore/DSGApplication b/include/DtkCore/DSGApplication new file mode 100644 index 0000000..a1c01bc --- /dev/null +++ b/include/DtkCore/DSGApplication @@ -0,0 +1 @@ +#include "dsgapplication.h" diff --git a/include/DtkCore/DSecureString b/include/DtkCore/DSecureString new file mode 100644 index 0000000..83f6c1a --- /dev/null +++ b/include/DtkCore/DSecureString @@ -0,0 +1 @@ +#include "dsecurestring.h" diff --git a/include/DtkCore/DSettings b/include/DtkCore/DSettings new file mode 100644 index 0000000..572ed1b --- /dev/null +++ b/include/DtkCore/DSettings @@ -0,0 +1 @@ +#include "dsettings.h" diff --git a/include/DtkCore/DSettingsGroup b/include/DtkCore/DSettingsGroup new file mode 100644 index 0000000..1c85d70 --- /dev/null +++ b/include/DtkCore/DSettingsGroup @@ -0,0 +1 @@ +#include "dsettingsgroup.h" diff --git a/include/DtkCore/DSettingsOption b/include/DtkCore/DSettingsOption new file mode 100644 index 0000000..9c180d5 --- /dev/null +++ b/include/DtkCore/DSettingsOption @@ -0,0 +1 @@ +#include "dsettingsoption.h" diff --git a/include/DtkCore/DSingleton b/include/DtkCore/DSingleton new file mode 100644 index 0000000..280d2f9 --- /dev/null +++ b/include/DtkCore/DSingleton @@ -0,0 +1 @@ +#include "dsingleton.h" diff --git a/include/DtkCore/DStandardPaths b/include/DtkCore/DStandardPaths new file mode 100644 index 0000000..9d0b963 --- /dev/null +++ b/include/DtkCore/DStandardPaths @@ -0,0 +1 @@ +#include "dstandardpaths.h" diff --git a/include/DtkCore/DSysInfo b/include/DtkCore/DSysInfo new file mode 100644 index 0000000..c026f58 --- /dev/null +++ b/include/DtkCore/DSysInfo @@ -0,0 +1 @@ +#include "dsysinfo.h" diff --git a/include/DtkCore/DThreadUtils b/include/DtkCore/DThreadUtils new file mode 100644 index 0000000..13b83d8 --- /dev/null +++ b/include/DtkCore/DThreadUtils @@ -0,0 +1 @@ +#include "dthreadutils.h" diff --git a/include/DtkCore/DTimeUnitFormatter b/include/DtkCore/DTimeUnitFormatter new file mode 100644 index 0000000..07d52a6 --- /dev/null +++ b/include/DtkCore/DTimeUnitFormatter @@ -0,0 +1 @@ +#include "dtimeunitformatter.h" diff --git a/include/DtkCore/DTrashManager b/include/DtkCore/DTrashManager new file mode 100644 index 0000000..e763093 --- /dev/null +++ b/include/DtkCore/DTrashManager @@ -0,0 +1 @@ +#include "dtrashmanager.h" diff --git a/include/DtkCore/DUtil b/include/DtkCore/DUtil new file mode 100644 index 0000000..2dc0935 --- /dev/null +++ b/include/DtkCore/DUtil @@ -0,0 +1 @@ +#include "dutil.h" diff --git a/include/DtkCore/DVtableHook b/include/DtkCore/DVtableHook new file mode 100644 index 0000000..37dffef --- /dev/null +++ b/include/DtkCore/DVtableHook @@ -0,0 +1 @@ +#include "dvtablehook.h" diff --git a/include/DtkCore/DtkCores b/include/DtkCore/DtkCores new file mode 100644 index 0000000..8a12540 --- /dev/null +++ b/include/DtkCore/DtkCores @@ -0,0 +1,43 @@ +#ifndef DTK_CORE_MODULE_H +#define DTK_CORE_MODULE_H +#include "dtkcore_global.h" +#include "dconfigfile.h" +#include "dobject.h" +#include "dsingleton.h" +#include "dutil.h" +#include "dpinyin.h" +#include "dtimeunitformatter.h" +#include "dabstractunitformatter.h" +#include "ddisksizeformatter.h" +#include "ddbussender.h" +#include "drecentmanager.h" +#include "dnotifysender.h" +#include "dexportedinterface.h" +#include "dvtablehook.h" +#include "dfileservices.h" +#include "dthreadutils.h" +#include "dasync.h" +#include "dtimedloop.h" +#include "RollingFileAppender.h" +#include "Logger.h" +#include "FileAppender.h" +#include "ConsoleAppender.h" +#include "AbstractStringAppender.h" +#include "AbstractAppender.h" +#include "LogManager.h" +#include "dbasefilewatcher.h" +#include "dfilesystemwatcher.h" +#include "dfilewatcher.h" +#include "dfilewatchermanager.h" +#include "dpathbuf.h" +#include "dstandardpaths.h" +#include "dtrashmanager.h" +#include "gsettingsbackend.h" +#include "qsettingbackend.h" +#include "dsettings.h" +#include "dsettingsoption.h" +#include "dsettingsgroup.h" +#include "dsettingsbackend.h" +#include "dsettingsdconfigbackend.h" +#include "ddcifile.h" +#endif diff --git a/include/base/derror.h b/include/base/derror.h new file mode 100644 index 0000000..5f58caf --- /dev/null +++ b/include/base/derror.h @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DERROR_H +#define DERROR_H +#include "dtkcore_global.h" +#include +#include + +DCORE_BEGIN_NAMESPACE +/** + * @brief 对于错误的包装类 + */ +class DError +{ +public: + /*! + * @brief 默认构造函数 + * @attention 错误代码默认为-1,错误信息默认为空 + */ + DError() noexcept + : m_code(-1) + , m_msg() + { + } + + /*! + * @brief 拷贝构造函数 + */ + DError(const DError &e) noexcept + : m_code(e.m_code) + , m_msg(e.m_msg) + { + } + + /*! + * @brief 移动构造函数 + * @attention 移动后原对象不可用 + */ + DError(DError &&e) noexcept + : m_code(e.m_code) + , m_msg(std::move(e).m_msg) + { + } + + /*! + * @brief 构造函数 + * @param[in] code 错误代码 + * @param[in] msg 错误信息 + */ + DError(qint64 code, const QString &msg) noexcept + : m_code(code) + , m_msg(msg) + { + } + + /*! + * @brief 构造函数 + * @param[in] code 错误代码 + * @param[in] msg 错误信息 + * @attention 使用此构造函数后原错误信息不可用 + */ + DError(qint64 code, QString &&msg) noexcept + : m_code(code) + , m_msg(std::move(msg)) + { + } + + /*! + * @brief 重载拷贝赋值运算符 + */ + DError &operator=(const DError &e) + { + m_code = e.m_code; + m_msg = e.m_msg; + return *this; + } + + /*! + * @brief 重载移动赋值运算符 + * @attention 赋值后原对象不可用 + */ + DError &operator=(DError &&e) + { + m_code = e.m_code; + m_msg = std::move(e).m_msg; + return *this; + } + + /*! + * @brief 默认析构函数 + */ + ~DError() = default; + + /*! + * @brief 获取错误代码 + * @return 错误代码 + */ + qint64 getErrorCode() const noexcept { return m_code; } + + /*! + * @brief 设置错误代码 + * @param[in] code 错误代码 + */ + void setErrorCode(qint64 code) &noexcept { m_code = code; } + + /*! + * @brief 获取错误信息 + * @return 错误信息的const引用 + */ + const QString &getErrorMessage() const & { return m_msg; } + + /*! + * @brief 获取错误信息 + * @attention 函数返回错误信息后,原信息不可用 + * @return 错误信息 + */ + QString getErrorMessage() const && { return std::move(m_msg); } + + /*! + * @brief 设置错误信息 + * @param[in] msg 错误信息 + */ + void setErrorMessage(const QString &msg) & { m_msg = msg; } + + /*! + * @brief 重载相等运算符 + */ + friend bool operator==(const DError &x, const DError &y) noexcept { return x.m_code == y.m_code and x.m_msg == y.m_msg; } + + /*! + * @brief 重载不等运算符 + */ + friend bool operator!=(const DError &x, const DError &y) noexcept { return !(x == y); } + + /*! + * @brief 重载输出运算符 + */ + friend QDebug operator<<(QDebug debug, const DError &e) + { + debug << "Error Code:" << e.m_code << "Message:" << e.m_msg; + return debug; + } + +private: + qint64 m_code; + QString m_msg; +}; +DCORE_END_NAMESPACE +#endif diff --git a/include/base/dexpected.h b/include/base/dexpected.h new file mode 100644 index 0000000..a44514c --- /dev/null +++ b/include/base/dexpected.h @@ -0,0 +1,1539 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DEXPECTED_H +#define DEXPECTED_H + +#include +#include +#include +#include +#include +#include + +#include "derror.h" + +DCORE_BEGIN_NAMESPACE + +#define likely(x) __builtin_expect(static_cast((x)), 1) +#define unlikely(x) __builtin_expect(reinterpret_cast((x)), 0) + +#if __cpp_exceptions +#define _DEXPECTED_THROW_OR_ABORT(_EXC) (throw(_EXC)) +#else +#define _DEXPECTED_THROW_OR_ABORT(_EXC) (std::abort()) +#endif + +template +class DExpected; + +template +class DUnexpected; + +template +using _bool_constant = std::integral_constant; + +/*! + * @brief 原位构造标签 + */ +enum class emplace_tag { USE_EMPLACE /**< 使用原位构造 */ }; + +/*! + * @brief 从DUnexpected构造标签 + */ +enum class dunexpected_tag { DUNEXPECTED /**< 从DUnexpected构造 */ }; + +template +struct remove_cvref +{ + using type = typename std::remove_cv::type>::type; +}; + +template +class bad_result_access; + +template <> +class bad_result_access : public std::exception +{ +protected: + bad_result_access() noexcept {} + bad_result_access(const bad_result_access &) = default; + bad_result_access(bad_result_access &&) = default; + bad_result_access &operator=(const bad_result_access &) = default; + bad_result_access &operator=(bad_result_access &&) = default; + ~bad_result_access() = default; + +public: + const char *what() const noexcept override { return "bad access to DExpected without value"; } +}; + +template +class bad_result_access : public bad_result_access +{ +public: + explicit bad_result_access(E _e) + : m_error(std::move(_e)) + { + } + + E &error() &noexcept { return m_error; } + const E &error() const &noexcept { return m_error; } + + E error() &&noexcept { return std::move(m_error); } + const E error() const &&noexcept { return std::move(m_error); } + +private: + E m_error; +}; + +template +auto construct_at(Type *p, Args &&...args) noexcept(noexcept(::new((void *)0) Type(std::declval()...))) + -> decltype(::new((void *)0) Type(std::declval()...)) +{ + return ::new ((void *)p) Type(std::forward(args)...); +} + +namespace __dexpected { +template +void destroy_at_obj(ObjType *p) +{ + p->~ObjType(); +} + +template +void destroy_at_arr(ArrType *p) +{ + for (auto &elem : *p) + destroy_at_obj(std::addressof(elem)); +} +} // namespace __dexpected + +template ::value, bool>::type = true> +void destroy_at(Type *p) +{ + __dexpected::destroy_at_arr(p); +} + +template ::value, bool>::type = true> +void destroy_at(Type *p) +{ + __dexpected::destroy_at_obj(p); +} + +namespace __dexpected { +template +struct Guard +{ + static_assert(std::is_nothrow_move_constructible::value, "type T must bu nothrow_move_constructible"); + explicit Guard(T &_x) + : m_guarded(std::addressof(_x)) + , m_tmp(std::move(_x)) + { + destroy_at(m_guarded); + } + + ~Guard() + { + if (unlikely(m_guarded)) { + construct_at(m_guarded, std::move(m_tmp)); + } + } + Guard(const Guard &) = delete; + Guard &operator=(const Guard &) = delete; + + T &&release() noexcept { return std::move(m_tmp); } + +private: + T *m_guarded; + T m_tmp; +}; +} // namespace __dexpected + +namespace __dexpected { + +template +struct _is_dexpected : public std::false_type +{ +}; + +template +struct _is_dexpected> : public std::true_type +{ +}; + +template +struct _is_dunexpected : public std::false_type +{ +}; + +template +struct _is_dunexpected> : public std::true_type +{ +}; + +template +constexpr bool _can_be_dunexpected() +{ + return std::is_object::value and !std::is_array::value and !_is_dunexpected() and !std::is_const::value and + !std::is_volatile::value; +} + +template ::value and std::is_nothrow_move_constructible::value, + bool>::type = true> +void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible::value) +{ + destroy_at(_oldVal); + construct_at(_newVal, std::forward(_arg)); +} + +template ::value and !std::is_nothrow_move_constructible::value, + bool>::type = true> +void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible::value) +{ + destroy_at(_oldVal); + construct_at(_newVal, std::forward(_arg)); +} + +template ::value and std::is_nothrow_move_constructible::value, + bool>::type = true> +void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible::value) +{ + Tp _tmp(std::forward(_arg)); + destroy_at(_oldVal); + construct_at(_newVal, std::move(_tmp)); +} + +template < + typename Tp, + typename Up, + typename Vp, + typename std::enable_if::value and !std::is_nothrow_move_constructible::value, + bool>::type = true> +void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible::value) +{ + __dexpected::Guard _guard(*_oldVal); + construct_at(_newVal, std::forward(_arg)); + _guard.release(); +} + +} // namespace __dexpected + +/*! + * @brief + * 类模板Dtk::Core::DUnexpected代表一个Dtk::Core::DExpected中存储的不期待的值 + * @tparam E + * 不期待的值的类型,该类型不能是非对象类型,数组类型,Dtk::Core::DUnexpected的特化类型或有cv限定符的类型 + */ +template +class DUnexpected +{ + static_assert(__dexpected::_can_be_dunexpected(), "can't be dunexpected"); + +public: + /*! + * @brief Dtk::Core::DUnexpected的默认拷贝构造函数 + */ + constexpr DUnexpected(const DUnexpected &) = default; + + /*! + * @brief Dtk::Core::DUnexpected的默认移动构造函数 + */ + constexpr DUnexpected(DUnexpected &&) = default; + + /*! + * @brief 使用类型E直接初始化一个Dtk::Core::DUnexpected对象 + * @tparam Er 错误类型,默认为E + * @param[in] _e 一个类型为Er的值 + */ + template ::type, DUnexpected>::value and + !std::is_same::type, emplace_tag>::value and + std::is_constructible::value, + bool>::type = true> + constexpr explicit DUnexpected(Er &&_e) noexcept(std::is_nothrow_constructible::value) + : m_error(std::forward(_e)) + { + } + + /*! + * @brief + * 直接从参数构造出一个包含错误类型为E的对象的Dtk::Core::DUnexpected对象 + * @tparam Args 可变参数模板类型,这里是构造类型为E的对象所需要的参数的类型 + * @param[in] args 构造类型为E的对象用到的参数 + * @attention + * 为了区分是构造E还是Dtk::Core::DUnexpected,需要在第一个参数使用emplace_tag进行标识 + */ + template + constexpr explicit DUnexpected(emplace_tag, Args &&...args) noexcept(std::is_nothrow_constructible::value) + : m_error(std::forward(args)...) + { + static_assert(std::is_constructible::value, "can't construct E from args."); + } + + /*! + * @brief + * 从参数和初始化列表构造出一个包含错误类型为E的对象的Dtk::Core::DUnexpected对象 + * @tparam U 初始化列表的模板类型 + * @tparam Args 可变参数模板类型,这里是构造类型为E的对象所需要的参数的类型 + * @param _li 模板类型为U的初始化列表 + * @param[in] args 构造类型为E的对象用到的参数 + * @attention + * 为了区分是构造E还是Dtk::Core::DUnexpected,需要在第一个参数使用emplace_tag进行标识 + */ + template + constexpr explicit DUnexpected(emplace_tag, std::initializer_list _li, Args &&...args) noexcept( + std::is_nothrow_constructible &, Args...>::value) + : m_error(_li, std::forward(args)...) + { + } + + /*! + * @brief Dtk::Core::DUnexpected的默认拷贝赋值运算符 + */ + DUnexpected &operator=(const DUnexpected &) = default; + + /*! + * @brief Dtk::Core::DUnexpected的默认移动赋值运算符 + */ + DUnexpected &operator=(DUnexpected &&) = default; + + /*! + * @brief 获取Dtk::Core::DUnexpected持有的不期待值 + * @return 不期待值的const左值引用 + */ + constexpr const E &error() const &noexcept { return m_error; } + + /*! + * @brief 获取Dtk::Core::DUnexpected持有的不期待值 + * @return 不期待值的左值引用 + */ + E &error() &noexcept { return m_error; } + + /*! + * @brief 获取Dtk::Core::DUnexpected持有的不期待值 + * @attention 获取后原Dtk::Core::DUnexpected不可用 + * @return 不期待值的const右值引用 + */ + constexpr const E &&error() const &&noexcept { return std::move(m_error); } + + /*! + * @brief 获取Dtk::Core::DUnexpected持有的不期待值 + * @attention 获取后原Dtk::Core::DUnexpected不可用 + * @return 不期待值的右值引用 + */ + E &&error() &&noexcept { return std::move(m_error); } + + /*! + * @brief 交换两个Dtk::Core::DUnexpected的值 + * @param[in] _other 另一个模板参数为E的Dtk::Core::DUnexpected对象 + */ + void swap(DUnexpected &_other) + { + using std::swap; + swap(m_error, _other.m_error); + } + + /*! + * @brief 重载相等运算符 + * @tparam Er 另一个Dtk::Core::DUnexpected的模板类型 + * @param[in] _x 模板参数为E的Dtk::Core::DUnexpected对象 + * @param[in] _y 模板参数为Er的Dtk::Core::DUnexpected对象 + */ + template + friend constexpr bool operator==(const DUnexpected &_x, const DUnexpected _y) + { + return _x.m_error == _y.error(); + } + + /*! + * @brief 交换两个Dtk::Core::DUnexpected的值 + */ + friend void swap(DUnexpected &_x, DUnexpected &_y) { _x.swap(_y); } + +private: + E m_error; +}; + +/*! + * @brief + * 模板类Dtk::Core::DExpected提供存储两个值之一的方式。Dtk::Core::DExpected的对象要么保有一个期待的T类型值,要么保有一个不期待的E类型值,不会没有值。 + * @tparam T 期待的类型 + * @tparam E 不期待的类型 + */ +template +class DExpected +{ + template + friend class DExpected; + static_assert(!std::is_reference::value, "type T can't be reference type"); + static_assert(!std::is_function::value, "type T can't be function type"); + static_assert(!std::is_same::type, dunexpected_tag>::value, "type T can't be dunexpected_tag"); + static_assert(!std::is_same::type, emplace_tag>::value, "type T can't be emplace_tag"); + static_assert(!__dexpected::_is_dunexpected::type>::value, "type T can't be DUnexpected"); + static_assert(__dexpected::_can_be_dunexpected(), "type E can't be dunexpected"); + + template > + static constexpr bool __cons_from_DExpected() + { + return std::is_constructible &>::value or std::is_constructible>::value or + std::is_constructible>::value or + std::is_constructible &>::value or std::is_convertible &, T>::value or + std::is_convertible, T>::value or std::is_convertible &, T>::value or + std::is_convertible, T>::value or std::is_constructible &>::value or + std::is_constructible>::value or + std::is_constructible &>::value or + std::is_constructible>::value; + } + + template + static constexpr bool __explicit_conv() + { + return !std::is_convertible::value or !std::is_convertible::value; + } + + static constexpr bool des_value() + { + return !std::is_trivially_destructible::value or !std::is_trivially_destructible::value; + } + + template + void assign_val(V &&_v) + { + if (m_has_value) { + m_value = std::forward(_v); + } else { + __dexpected::reinit(std::addressof(m_value), std::addressof(m_error), std::forward(_v)); + m_has_value = true; + } + } + + template + void assign_err(V &&_v) + { + if (m_has_value) { + __dexpected::reinit(std::addressof(m_error), std::addressof(m_value), std::forward(_v)); + m_has_value = false; + } else { + m_error = std::forward(_v); + } + } + + template ::value, bool>::type = true> + void swap_val_err(DExpected &_other) noexcept( + std::is_nothrow_move_constructible::value and std::is_nothrow_move_constructible::value) + { + __dexpected::Guard _guard(_other.m_error); + construct_at(std::addressof(_other.m_value), std::move(m_value)); + _other.m_has_value = true; + destroy_at(std::addressof(m_value)); + construct_at(std::addressof(m_error), _guard.release()); + m_has_value = false; + } + + template ::value, bool>::type = true> + void swap_val_err(DExpected &_other) noexcept( + std::is_nothrow_move_constructible::value and std::is_nothrow_move_constructible::value) + { + __dexpected::Guard _guard(_other.m_value); + construct_at(std::addressof(m_error), std::move(_other.m_error)); + m_has_value = false; + destroy_at(std::addressof(_other.m_error)); + construct_at(std::addressof(_other.m_value), _guard.release()); + _other.m_has_value = true; + } + +public: + using value_type = T; + using error_type = E; + using dunexpected_type = DUnexpected; + template + using rebind = DExpected; + + /*! + * @brief Dtk::Core::DExpected的默认构造函数 + */ + template ::value, bool>::type = true> + constexpr DExpected() noexcept(std::is_nothrow_default_constructible::value) + : m_has_value(true) + , m_value() + { + } + + /*! + * @brief Dtk::Core::DExpected的默认拷贝构造函数 + */ + DExpected(const DExpected &) = default; + + /*! + * @brief Dtk::Core::DExpected的拷贝构造函数 + */ + template ::value and std::is_copy_constructible::value and + (!std::is_trivially_copy_constructible::value or + !std::is_trivially_copy_constructible::value), + bool>::type = true> + DExpected(const DExpected &_x) noexcept( + std::is_nothrow_copy_constructible::value and std::is_nothrow_copy_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), _x.m_value); + else + construct_at(std::addressof(m_error), _x.m_error); + } + + /*! + * @brief Dtk::Core::DExpected的默认移动构造函数 + */ + DExpected(DExpected &&) = default; + + /*! + * @brief Dtk::Core::DExpected的移动构造函数 + */ + template ::value and std::is_move_constructible::value and + (!std::is_trivially_move_constructible::value or + !std::is_trivially_move_constructible::value), + bool>::type = true> + DExpected(DExpected &&_x) noexcept( + std::is_nothrow_move_constructible::value and std::is_nothrow_move_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), std::move(_x).m_value); + else + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + /*! + * @brief Dtk::Core::DExpected的拷贝构造函数 + * @tparam U 另一个Dtk::Core::DExpected的期待类型 + * @tparam G 另一个Dtk::Core::DExpected的不期待类型 + * @param[in] _x 模板类型分别为U和G的Dtk::Core::DExpected对象 + */ + template < + typename U, + typename G, + typename std::enable_if::value and std::is_constructible::value and + !__cons_from_DExpected() and !__explicit_conv(), + bool>::type = true> + DExpected(const DExpected &_x) noexcept( + std::is_nothrow_constructible::value and std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), _x.m_value); + else + construct_at(std::addressof(m_error), _x.m_error); + } + + /*! + * @brief Dtk::Core::DExpected的拷贝构造函数 + * @tparam U 另一个Dtk::Core::DExpected的期待类型 + * @tparam G 另一个Dtk::Core::DExpected的不期待类型 + * @param[in] _x 模板类型分别为U和G的Dtk::Core::DExpected对象 + * @attention 该拷贝构造函数有explicit标识 + */ + template < + typename U, + typename G, + typename std::enable_if::value and std::is_constructible::value and + !__cons_from_DExpected() and __explicit_conv(), + bool>::type = true> + explicit DExpected(const DExpected &_x) noexcept( + std::is_nothrow_constructible::value and std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), _x.m_value); + else + construct_at(std::addressof(m_error), _x.m_error); + } + + /*! + * @brief Dtk::Core::DExpected的移动构造函数 + * @tparam U 另一个Dtk::Core::DExpected的期待类型 + * @tparam G 另一个Dtk::Core::DExpected的不期待类型 + * @param[in] _x 模板类型分别为U和G的Dtk::Core::DExpected对象 + * @attention 构造后另一个Dtk::Core::DExpected不可用 + */ + template ::value and std::is_constructible::value and + !__cons_from_DExpected() and !__explicit_conv(), + bool>::type = true> + DExpected(DExpected &&_x) noexcept( + std::is_nothrow_constructible::value and std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), std::move(_x).m_value); + else + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + /*! + * @brief Dtk::Core::DExpected的移动构造函数 + * @tparam U 另一个Dtk::Core::DExpected的期待类型 + * @tparam G 另一个Dtk::Core::DExpected的不期待类型 + * @param[in] _x 模板类型分别为U和G的Dtk::Core::DExpected对象 + * @attention 构造后另一个Dtk::Core::DExpected不可用,该函数有explicit标识 + */ + template ::value and std::is_constructible::value and + !__cons_from_DExpected() and __explicit_conv(), + bool>::type = true> + explicit DExpected(DExpected &&_x) noexcept( + std::is_nothrow_constructible::value and std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), std::move(_x).m_value); + else + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + /*! + * @brief + * Dtk::Core::DExpected的移动构造函数,直接从期待类型构造出Dtk::Core::DExpected对象 + * @tparam U Dtk::Core::DExpected的期待类型,默认为类型T + * @param[in] _v 期待类型为U的对象 + * @attention 构造后原对象不可用,该函数有explicit标识 + */ + template ::type, DExpected>::value and + !std::is_same::type, emplace_tag>::value and + !__dexpected::_is_dunexpected::type>::value and + std::is_constructible::value and !std::is_convertible::value, + bool>::type = true> + constexpr explicit DExpected(U &&_v) noexcept(std::is_nothrow_constructible::value) + : m_has_value(true) + , m_value(std::forward(_v)) + + { + } + + /*! + * @brief + * Dtk::Core::DExpected的移动构造函数,直接从期待类型构造出Dtk::Core::DExpected对象 + * @tparam U Dtk::Core::DExpected的期待类型,默认为类型T + * @param[in] _v 期待类型为U的对象 + * @attention 构造后原对象不可用 + */ + template ::type, DExpected>::value and + !std::is_same::type, emplace_tag>::value and + !__dexpected::_is_dunexpected::type>::value and + std::is_constructible::value and std::is_convertible::value, + bool>::type = true> + constexpr DExpected(U &&_v) noexcept(std::is_nothrow_constructible::value) + : m_has_value(true) + , m_value(std::forward(_v)) + { + } + + /*! + * @brief + * Dtk::Core::DExpected的拷贝构造函数,从Dtk::Core::DUnexpected构造出Dtk::Core::DExpected对象 + * @tparam G Dtk::Core::DExpected的期待类型,默认为类型E + * @param[in] _u 期待类型为G的Dtk::Core::DUnexpected对象 + * @attention 该函数有explicit标识 + */ + template ::value and !std::is_convertible::value, + bool>::type = true> + constexpr explicit DExpected(const DUnexpected &_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(_u.error()) + { + } + + /*! + * @brief + * Dtk::Core::DExpected的拷贝构造函数,从Dtk::Core::DUnexpected构造出Dtk::Core::DExpected对象 + * @tparam G Dtk::Core::DExpected的期待类型,默认为类型E + * @param[in] _u 期待类型为G的Dtk::Core::DUnexpected对象 + */ + template ::value and std::is_convertible::value, + bool>::type = true> + constexpr DExpected(const DUnexpected &_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(_u.error()) + { + } + + /*! + * @brief + * Dtk::Core::DExpected的移动构造函数,从Dtk::Core::DUnexpected构造出Dtk::Core::DExpected对象 + * @tparam G Dtk::Core::DExpected的期待类型,默认为类型E + * @param[in] _u 期待类型为G的Dtk::Core::DUnexpected对象 + * @attention 构造后原对象不可用,该函数有explicit标识 + */ + template < + typename G = E, + typename std::enable_if::value and !std::is_convertible::value, bool>::type = true> + constexpr explicit DExpected(DUnexpected &&_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::move(_u).error()) + { + } + + /*! + * @brief + * Dtk::Core::DExpected的移动构造函数,从Dtk::Core::DUnexpected构造出Dtk::Core::DExpected对象 + * @tparam G Dtk::Core::DExpected的期待类型,默认为类型E + * @param[in] _u 期待类型为G的Dtk::Core::DUnexpected对象 + * @attention 构造后原对象不可用 + */ + template ::value and std::is_convertible::value, bool>::type = true> + constexpr DExpected(DUnexpected &&_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::move(_u).error()) + { + } + + /*! + * @brief Dtk::Core::DExpected的转发构造函数,从参数直接构造出期待值 + * @tparam Args 构造期待类型T所用到的参数的类型 + * @param[in] args 构造期待类型T所用到的参数 + * @attention + * 为了区分是构造T还是Dtk::Core::DExpected,需要在第一个参数使用emplace_tag进行标识 + */ + template + constexpr explicit DExpected(emplace_tag, Args &&...args) noexcept(std::is_nothrow_constructible::value) + : m_has_value(true) + , m_value(std::forward(args)...) + + { + static_assert(std::is_constructible::value, "can't construct T from args."); + } + + /*! + * @brief Dtk::Core::DExpected的转发构造函数,从参数直接构造出期待值 + * @tparam U 初始化列表的模板参数 + * @tparam Args 构造期待类型T所用到的参数的类型 + * @param[in] _li 构造期待类型T所用到的初始化列表 + * @param[in] args 构造期待类型T所用到的参数 + * @attention + * 为了区分是构造T还是Dtk::Core::DExpected,需要在第一个参数使用emplace_tag进行标识 + */ + template + constexpr explicit DExpected(emplace_tag, std::initializer_list _li, Args &&...args) noexcept( + std::is_nothrow_constructible &, Args...>::value) + : m_has_value(true) + , m_value(_li, std::forward(args)...) + { + static_assert(std::is_constructible &, Args...>::value, "can't construct T from args."); + } + + /*! + * @brief Dtk::Core::DExpected的转发构造函数,从参数直接构造出不期待值 + * @tparam Args 构造不期待类型E所用到的参数的类型 + * @param[in] args 构造不期待类型E所用到的参数 + * @attention + * 为了区分是构造E还是Dtk::Core::DExpected,需要在第一个参数使用dunexpected_tag进行标识 + */ + template + constexpr explicit DExpected(dunexpected_tag, Args &&...args) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::forward(args)...) + + { + static_assert(std::is_constructible::value, "can't construct E from args."); + } + + /*! + * @brief Dtk::Core::DExpected的转发构造函数,从参数直接构造出不期待值 + * @tparam U 初始化列表的模板参数 + * @tparam Args 构造不期待类型E所用到的参数的类型 + * @param[in] _li 构造不期待类型E所用到的初始化列表 + * @param[in] args 构造不期待类型E所用到的参数 + * @attention + * 为了区分是构造E还是Dtk::Core::DExpected,需要在第一个参数使用dunexpected_tag进行标识 + */ + template + constexpr explicit DExpected(dunexpected_tag, std::initializer_list _li, Args &&...args) noexcept( + std::is_nothrow_constructible &, Args...>::value) + : m_has_value(false) + , m_error(_li, std::forward(args)...) + { + static_assert(std::is_constructible &, Args...>::value, "can't construct E from args."); + } + + /*! + * @brief Dtk::Core::DExpected的析构函数 + */ + ~DExpected() + { + if (des_value()) { + if (m_has_value) { + destroy_at(std::addressof(m_value)); + } else { + destroy_at(std::addressof(m_error)); + } + } + } + + /*! + * @brief Dtk::Core::DExpected的默认拷贝赋值运算符 + */ + DExpected &operator=(const DExpected &) = delete; + + /*! + * @brief Dtk::Core::DExpected的拷贝赋值运算符 + * @param[in] _x 同类型的Dtk::Core::DExpected对象 + */ + template ::value and std::is_copy_constructible::value and + std::is_copy_assignable::value and std::is_copy_constructible::value and + (std::is_nothrow_move_constructible::value or + std::is_nothrow_move_constructible::value), + bool>::type = true> + DExpected &operator=(const DExpected &_x) noexcept( + std::is_nothrow_copy_constructible::value and std::is_nothrow_copy_constructible::value + and std::is_nothrow_copy_assignable::value and std::is_nothrow_copy_assignable::value) + { + if (_x.m_has_value) + this->assign_val(_x.m_value); + else + this->assign_err(_x.m_error); + return *this; + } + + /*! + * @brief Dtk::Core::DExpected的移动赋值运算符 + * @param[in] _x 同类型的Dtk::Core::DExpected对象 + * @attention 赋值后原对象不可用 + */ + template ::value and std::is_move_constructible::value and + std::is_move_assignable::value and std::is_move_constructible::value and + (std::is_nothrow_move_constructible::value or + std::is_nothrow_move_constructible::value), + bool>::type = true> + DExpected &operator=(DExpected &&_x) noexcept( + std::is_nothrow_move_constructible::value and std::is_nothrow_move_constructible::value + and std::is_nothrow_move_assignable::value and std::is_nothrow_move_assignable::value) + { + if (_x.m_has_value) + assign_val(std::move(_x.m_value)); + else + assign_err(std::move(_x.m_error)); + return *this; + } + + /*! + * @brief Dtk::Core::DExpected的转发赋值运算符 + * @tparam U 期待类型,默认为T + * @param[in] _v 期待类型U的对象 + */ + template < + typename U = T, + typename std::enable_if::type>::value and + !__dexpected::_is_dunexpected::type>::value and + std::is_constructible::value and std::is_assignable::value and + (std::is_nothrow_constructible::value or std::is_nothrow_move_constructible::value or + std::is_nothrow_move_constructible::value), + bool>::type = true> + DExpected &operator=(U &&_v) + { + assign_val(std::forward(_v)); + return *this; + } + + /*! + * @brief Dtk::Core::DExpected的拷贝赋值运算符 + * @tparam G 不期待类型 + * @param[in] _e 模板类型为G的Dtk::Core::DUnexpected对象 + */ + template ::value and std::is_assignable::value and + (std::is_nothrow_constructible::value or + std::is_nothrow_move_constructible::value or std::is_move_constructible::value), + bool>::type = true> + DExpected &operator=(const DUnexpected &_e) + { + assign_err(_e.error()); + return *this; + } + + /*! + * @brief Dtk::Core::DExpected的移动赋值运算符 + * @tparam G 不期待类型 + * @param[in] _e 模板类型为G的Dtk::Core::DUnexpected对象 + * @attention 赋值后原对象不可用 + */ + template ::value and std::is_assignable::value and + (std::is_nothrow_constructible::value or + std::is_nothrow_move_constructible::value or std::is_move_constructible::value), + bool>::type = true> + DExpected &operator=(DUnexpected &&_e) + { + assign_err(std::move(_e).error()); + return *this; + } + + /*! + * @brief 从参数直接转发构造期待值 + * @tparam Args 构造期待值所用到的参数的类型 + * @param[in] args 构造期待值所用到的参数 + * @return 返回构造好的期待值的引用 + */ + template + T &emplace(Args &&...args) noexcept + { + static_assert(std::is_nothrow_constructible::value, "type T should have nothrow_constructible"); + if (m_has_value) + destroy_at(std::addressof(m_value)); + else { + destroy_at(std::addressof(m_error)); + m_has_value = true; + } + construct_at(std::addressof(m_value), std::forward(args)...); + return m_value; + } + + /*! + * @brief 从参数直接转发构造期待值 + * @tparam U 初始化列表的模板参数 + * @tparam Args 构造期待值所用到的参数的类型 + * @param[in] args 构造期待值所用到的参数 + * @param[in] li 构造期待值所用到的参数化列表 + * @return 返回构造好的期待值的引用 + */ + template + T &emplace(std::initializer_list li, Args &&...args) noexcept + { + static_assert(std::is_nothrow_constructible &, Args...>::value, + "type T should have a noexcept constructor"); + if (m_has_value) + destroy_at(std::addressof(m_value)); + else { + destroy_at(std::addressof(m_error)); + } + construct_at(std::addressof(m_value), li, std::forward(args)...); + return m_value; + } + + // TODO:需要swap吗? + /*! + * @brief 交换两个Dtk::Core::DExpected的值 + * @param[in] _x 另一个Dtk::Core::DExpected对象 + */ + template ::value and std::is_move_constructible::value and + (std::is_nothrow_move_constructible::value or + std::is_nothrow_move_constructible::value), + bool>::type = true> + void + swap(DExpected &_x) noexcept(std::is_nothrow_move_constructible::value and std::is_nothrow_move_constructible::value) + { + if (m_has_value) { + if (_x.m_has_value) { + using std::swap; + swap(m_value, _x.m_value); + } else { + this->swap_val_err(_x); + } + } else { + if (_x.m_has_value) + _x.swap_val_err(*this); + else { + using std::swap; + swap(m_error, _x.m_error); + } + } + } + + /*! + * @brief 重载箭头运算符 + * @return 一个指向期待值的const指针 + */ + const T *operator->() const noexcept + { + assert(m_has_value); + return std::addressof(m_value); + } + + /*! + * @brief 重载箭头运算符 + * @return 一个指向期待值的指针 + */ + T *operator->() noexcept + { + assert(m_has_value); + return std::addressof(m_value); + } + + /*! + * @brief 重载解引用运算符 + * @return 一个期待值的const左值引用 + */ + const T &operator*() const &noexcept + { + assert(m_has_value); + return m_value; + } + + /*! + * @brief 重载解引用运算符 + * @return 一个期待值的左值引用 + */ + T &operator*() &noexcept + { + assert(m_has_value); + return m_value; + } + + /*! + * @brief 重载解引用运算符 + * @return 一个期待值的const右值引用 + */ + const T &&operator*() const &&noexcept + { + assert(m_has_value); + return std::move(m_value); + } + + /*! + * @brief 重载解引用运算符 + * @return 一个期待值的右值引用 + */ + T &&operator*() &&noexcept + { + assert(m_has_value); + return std::move(m_value); + } + + /*! + * @brief bool转换函数 + * @return 表示Dtk::Core::DExpected是否有值的bool值 + */ + constexpr explicit operator bool() const noexcept { return m_has_value; } + + /*! + * @brief 判断Dtk::Core::DExpected是否有值 + * @return 表示是否有值的bool值 + */ + constexpr bool hasValue() const noexcept { return m_has_value; } + + /*! + * @brief 获取Dtk::Core::DExpected的期待值 + * @return 期待值的const左值引用 + */ + const T &value() const & + { + if (likely(m_has_value)) { + return m_value; + } + _DEXPECTED_THROW_OR_ABORT(bad_result_access(m_error)); + } + + /*! + * @brief 获取Dtk::Core::DExpected的期待值 + * @return 期待值的左值引用 + */ + T &value() & + { + if (likely(m_has_value)) { + return m_value; + } + _DEXPECTED_THROW_OR_ABORT(bad_result_access(m_error)); + } + + /*! + * @brief 获取Dtk::Core::DExpected的期待值 + * @return 期待值的const右值引用 + * @attention 调用后期待值不可用 + */ + const T &&value() const && + { + if (likely(m_has_value)) { + return std::move(m_value); + } + _DEXPECTED_THROW_OR_ABORT(bad_result_access(m_error)); + } + + /*! + * @brief 获取Dtk::Core::DExpected的期待值 + * @return 期待值的右值引用 + * @attention 调用后期待值不可用 + */ + T &&value() && + { + if (likely(m_has_value)) { + return std::move(m_value); + } + _DEXPECTED_THROW_OR_ABORT(bad_result_access(m_error)); + } + + /*! + * @brief 获取Dtk::Core::DExpected的不期待值 + * @return 不期待值的const左值引用 + */ + const E &error() const &noexcept + { + assert(!m_has_value); + return m_error; + } + + /*! + * @brief 获取Dtk::Core::DExpected的不期待值 + * @return 不期待值的左值引用 + */ + E &error() &noexcept + { + assert(!m_has_value); + return m_error; + } + + /*! + * @brief 获取Dtk::Core::DExpected的不期待值 + * @return 不期待值的const右值引用 + * @attention 调用后不期待值不可用 + */ + const E &&error() const &&noexcept + { + assert(!m_has_value); + return std::move(m_error); + } + + /*! + * @brief 获取Dtk::Core::DExpected的不期待值 + * @return 不期待值的右值引用 + * @attention 调用后不期待值不可用 + */ + E &&error() &&noexcept + { + assert(!m_has_value); + return std::move(m_error); + } + + // TODO:因为无法确定U转T时是否会抛出异常,所以都按抛出异常来 + /*! + * @brief 如果有期待值返回期待值,否则返回传入的默认值 + * @tparam U 期待值的类型 + * @param[in] _v 默认的期待值 + * @return 期待值 + */ + template + T value_or(U &&_v) const & + { + static_assert(std::is_copy_constructible::value, "type T should have an copy constructor."); + static_assert(std::is_convertible::value, "type U must can be converted to T."); + if (m_has_value) + return m_value; + return static_cast(std::forward(_v)); + } + + /*! + * @brief 如果有期待值返回期待值,否则返回传入的默认值 + * @tparam U 期待值的类型 + * @param[in] _v 默认的期待值 + * @return 期待值 + * @attention 如果由期待值,调用后原期待值不可用,同时类型U要可以转换成类型T + */ + template + T value_or(U &&_v) && + { + static_assert(std::is_move_constructible::value, "type T must bu copy_constructible."); + static_assert(std::is_convertible::value, "type U must can be converted to T."); + if (m_has_value) + return std::move(m_value); + return static_cast(std::forward(_v)); + } + + /*! + *@brief 重载相等运算符 + */ + template ::value, bool>::type = true> + friend bool + operator==(const DExpected &_x, + const DExpected &_y) noexcept(noexcept(bool(*_x == *_y)) and noexcept(bool(_x.error() == _y.error()))) + { + if (_x.hasValue()) + return _y.hasValue() and bool(*_x == *_y); + else + return !_y.hasValue() and bool(_x.error() == _x.error()); + } + + /*! + *@brief 重载相等运算符 + */ + template + friend constexpr bool operator==(const DExpected &_x, const U &_v) noexcept(noexcept(bool(*_x == _v))) + { + return _x.hasValue() && bool(*_x == _v); + } + + /*! + *@brief 重载相等运算符 + */ + template + friend constexpr bool operator==(const DExpected &_x, + const DUnexpected &_e) noexcept(noexcept(bool(_x.error() == _e.error()))) + { + return !_x.hasValue() && bool(_x.error() == _e.error()); + } + + /*! + *@brief 交换两个Dtk::Core::DExpected中的值 + */ + friend void swap(DExpected &_x, DExpected &_y) noexcept(noexcept(_x.swap(_y))) { _x.swap(_y); } + +private: + bool m_has_value; + union + { + T m_value; + E m_error; + }; +}; + +/*! + * @brief 对于Dtk::Core::DExpected的void偏特化,其他函数参考原模板类 + * @tparam E 不期待值的类型 + */ +template +class DExpected +{ + static_assert(__dexpected::_can_be_dunexpected(), "type E can't be DUnexpected."); + static constexpr bool des_value() { return !std::is_trivially_destructible::value; } + + template + friend class DExpected; + + template > + static constexpr bool __cons_from_DExpected() + { + return std::is_constructible &>::value and std::is_constructible>::value and + std::is_constructible &>::value and + std::is_constructible>::value; + } + + template + void assign_err(V &&_v) + { + if (m_has_value) { + construct_at(std::addressof(m_error), std::forward(_v)); + m_has_value = false; + } else { + m_error = std::forward(_v); + } + } + +public: + using value_type = void; + using error_type = E; + using dunexpected_type = DUnexpected; + template + using rebind = DExpected; + + constexpr DExpected() noexcept + : m_has_value(true) + , m_void() + { + } + + DExpected(const DExpected &) = default; + + template ::value and !std::is_trivially_copy_constructible::value, + bool>::type = true> + DExpected(const DExpected &_x) noexcept(std::is_nothrow_copy_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), _x.m_error); + } + + DExpected(DExpected &&) = default; + + template ::value and !std::is_trivially_move_constructible::value, + bool>::type = true> + DExpected(DExpected &&_x) noexcept(std::is_nothrow_move_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + template ::value and std::is_constructible::value and + !__cons_from_DExpected() and !std::is_convertible::value, + bool>::type = true> + explicit DExpected(const DExpected &_x) noexcept(std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), _x.m_error); + } + + template ::value and std::is_constructible::value and + !__cons_from_DExpected() and std::is_convertible::value, + bool>::type = true> + DExpected(const DExpected &_x) noexcept(std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), _x.m_error); + } + + template ::value and std::is_constructible::value and + __cons_from_DExpected() and !std::is_convertible::value, + bool>::type = true> + explicit DExpected(DExpected &&_x) noexcept(std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + template ::value and std::is_constructible::value and + __cons_from_DExpected() and std::is_convertible::value, + bool>::type = true> + DExpected(DExpected &&_x) noexcept(std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + template ::value and !std::is_convertible::value, + bool>::type = true> + constexpr explicit DExpected(const DUnexpected &_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(_u.error()) + { + } + + template ::value and std::is_convertible::value, + bool>::type = true> + constexpr DExpected(const DUnexpected &_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(_u.error()) + { + } + + template < + typename G = E, + typename std::enable_if::value and !std::is_convertible::value, bool>::type = true> + constexpr explicit DExpected(DUnexpected &&_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::move(_u).error()) + { + } + + template ::value and std::is_convertible::value, bool>::type = true> + constexpr DExpected(DUnexpected &&_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::move(_u).error()) + { + } + + template + constexpr explicit DExpected(emplace_tag) noexcept + : DExpected() + { + } + + template + constexpr explicit DExpected(dunexpected_tag, Args &&...args) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::forward(args)...) + { + static_assert(std::is_constructible::value, "type E can't construct from args"); + } + + template + constexpr explicit DExpected(dunexpected_tag, + std::initializer_list _li, + Args &&...args) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(_li, std::forward(args)...) + { + static_assert(std::is_constructible &, Args...>::value, "type E can't construct from args"); + } + + ~DExpected() + { + if (des_value() and !m_has_value) { + destroy_at(std::addressof(m_error)); + } + } + + DExpected &operator=(const DExpected &) = delete; + + template < + typename std::enable_if::value and std::is_copy_assignable::value, bool>::type = true> + DExpected &operator=(const DExpected &_x) noexcept( + std::is_nothrow_copy_constructible::value and std::is_nothrow_copy_assignable::value) + { + if (_x.m_has_value) + emplace(); + else + assign_err(_x.m_error); + return *this; + } + + template < + typename std::enable_if::value and std::is_move_assignable::value, bool>::type = true> + DExpected & + operator=(DExpected &&_x) noexcept(std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value) + { + if (_x.m_has_value) + emplace(); + else + assign_err(std::move(_x.m_error)); + return *this; + } + + template ::value and std::is_assignable::value, + bool>::type = true> + DExpected &operator=(const DUnexpected &_e) + { + assign_err(_e.error()); + return *this; + } + + template < + typename G, + typename std::enable_if::value and std::is_assignable::value, bool>::type = true> + DExpected &operator=(DUnexpected &&_e) + { + assign_err(std::move(_e.error())); + return *this; + } + + void emplace() noexcept + { + if (!m_has_value) { + destroy_at(std::addressof(m_error)); + m_has_value = true; + } + } + + template ::value, bool>::type = true> + void swap(DExpected &_x) noexcept(std::is_nothrow_move_constructible::value) + { + if (m_has_value) { + if (!_x.m_has_value) { + construct_at(std::addressof(m_error), std::move(_x.m_error)); + destroy_at(std::addressof(_x.m_error)); + m_has_value = false; + _x.m_has_value = true; + } + } else { + if (_x.m_has_value) { + construct_at(std::addressof(_x.m_error), std::move(m_error)); + destroy_at(std::addressof(m_error)); + m_has_value = true; + _x.m_has_value = false; + } else { + using std::swap; + swap(m_error, _x.m_error); + } + } + } + + constexpr explicit operator bool() const noexcept { return m_has_value; } + + constexpr bool hasValue() const noexcept { return m_has_value; } + + void operator*() const noexcept { assert(m_has_value); } + + void value() const & + { + if (likely(m_has_value)) + return; + _DEXPECTED_THROW_OR_ABORT(bad_result_access(m_error)); + } + + void value() && + { + if (likely(m_has_value)) + return; + _DEXPECTED_THROW_OR_ABORT(bad_result_access(std::move(m_error))); + } + + const E &error() const &noexcept + { + assert(!m_has_value); + return m_error; + } + + E &error() &noexcept + { + assert(!m_has_value); + return m_error; + } + + const E &&error() const &&noexcept + { + assert(!m_has_value); + return std::move(m_error); + } + + E &&error() &&noexcept + { + assert(!m_has_value); + return std::move(m_error); + } + + template ::value, bool>::type = true> + friend bool operator==(const DExpected &_x, const DExpected &_y) noexcept(noexcept(bool(_x.error() == _y.error()))) + { + if (_x.hasValue()) + return _y.hasValue(); + else + return !_y.hasValue() and bool(_x.error() == _y.error()); + } + + template + friend bool operator==(const DExpected &_x, const DUnexpected &_e) noexcept(noexcept(bool(_x.error() == _e.error()))) + { + return !_x.hasValue() && bool(_x.error() == _e.error()); + } + + // TODO:可能没有swap + friend void swap(DExpected &_x, DExpected &_y) noexcept(noexcept(_x.swap(_y))) { _x.swap(_y); } + +private: + bool m_has_value; + union + { + struct + { + } m_void; + E m_error; + }; +}; + +DCORE_END_NAMESPACE + +#endif diff --git a/include/base/dobject.h b/include/base/dobject.h new file mode 100644 index 0000000..4e2c020 --- /dev/null +++ b/include/base/dobject.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DOBJECT_H +#define DOBJECT_H + +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +#define D_DECLARE_PRIVATE(Class) Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_d_ptr),Class) +#define D_DECLARE_PUBLIC(Class) Q_DECLARE_PUBLIC(Class) +#define D_D(Class) Q_D(Class) +#define D_Q(Class) Q_Q(Class) +#define D_DC(Class) Q_D(const Class) +#define D_QC(Class) Q_Q(const Class) +#define D_PRIVATE_SLOT(Func) Q_PRIVATE_SLOT(d_func(), Func) + +class DObjectPrivate; + +class LIBDTKCORESHARED_EXPORT DObject +{ +protected: + DObject(DObject *parent = nullptr); + + DObject(DObjectPrivate &dd, DObject *parent = nullptr); + + virtual ~DObject(); + + QScopedPointer d_d_ptr; + + Q_DISABLE_COPY(DObject) + D_DECLARE_PRIVATE(DObject) +}; + +DCORE_END_NAMESPACE + +#endif // DOBJECT_H diff --git a/include/base/dsingleton.h b/include/base/dsingleton.h new file mode 100644 index 0000000..e8a9cbd --- /dev/null +++ b/include/base/dsingleton.h @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DSINGLETON_H +#define DSINGLETON_H + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +/*! + a simple singleton template for std c++ 11 or later. + + example: + + \code + class ExampleSingleton : public QObject, public Dtk::DSingleton + { + Q_OBJECT + friend class DSingleton; + }; + \endcode + + \note: for Qt, "public DSingleton" must be after QObject. + */ + +/*! + 通过c++11的特性实现的单例模板 + + 使用示例: + +``` + class ExampleSingleton : public QObject, public Dtk::DSingleton + { + Q_OBJECT + friend class DSingleton; + }; +``` + + \note 对于Qt程序 public DSingleton" 必须在卸载QObject后面出现。 + */ + +template +class LIBDTKCORESHARED_EXPORT DSingleton +{ +public: + QT_DEPRECATED_X("Use ref") + static inline T *instance() + { + static T *_instance = new T; + return _instance; + } + + static T& ref() + { + static T instance; + return instance; + } + + DSingleton(T&&) = delete; + DSingleton(const T&) = delete; + void operator= (const T&) = delete; + +protected: + DSingleton() = default; + virtual ~DSingleton() = default; +}; + +DCORE_END_NAMESPACE + +#endif // DSINGLETON_H diff --git a/include/base/private/dobject_p.h b/include/base/private/dobject_p.h new file mode 100644 index 0000000..7b26d0f --- /dev/null +++ b/include/base/private/dobject_p.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DOBJECT_P_H +#define DOBJECT_P_H + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DObject; +class LIBDTKCORESHARED_EXPORT DObjectPrivate +{ +public: + virtual ~DObjectPrivate(); + +protected: + DObjectPrivate(DObject *qq); + + DObject *q_ptr; + + Q_DECLARE_PUBLIC(DObject) +}; + +DCORE_END_NAMESPACE + +#endif // DOBJECT_P_H + diff --git a/include/dci/ddcifile.h b/include/dci/ddcifile.h new file mode 100644 index 0000000..09c2d45 --- /dev/null +++ b/include/dci/ddcifile.h @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#ifndef DTK_NO_PROJECT +#include +#include +#else +#define DCORE_BEGIN_NAMESPACE +#define DCORE_END_NAMESPACE +#define LIBDTKCORESHARED_EXPORT +#define D_DECLARE_PRIVATE(Class) Class##Private *d; +#endif + +#include + +QT_BEGIN_HEADER +class QIODevice; +QT_END_NAMESPACE + +DCORE_BEGIN_NAMESPACE + +class DDciFilePrivate; +class LIBDTKCORESHARED_EXPORT DDciFile +#ifndef DTK_NO_PROJECT + : public DObject +#endif +{ + D_DECLARE_PRIVATE(DDciFile) +public: + enum FileType { + UnknowFile, + File = 1, + Directory = 2, + Symlink = 3 + }; + + static void registerFileEngine(); + + DDciFile(); + explicit DDciFile(const QString &fileName); + explicit DDciFile(const QByteArray &data); + + bool isValid() const; + QString lastErrorString() const; + + bool writeToFile(const QString &fileName) const; + bool writeToDevice(QIODevice *device) const; + QByteArray toData() const; + + static constexpr int metadataSizeV1(); + + // for reader + QStringList list(const QString &dir, bool onlyFileName = false) const; + int childrenCount(const QString &dir) const; + bool exists(const QString &filePath) const; + FileType type(const QString &filePath) const; + QByteArray dataRef(const QString &filePath) const; + QString name(const QString &filePath) const; + QString symlinkTarget(const QString &filePath, bool originData = false) const; + + // for writer + bool mkdir(const QString &filePath); + bool writeFile(const QString &filePath, const QByteArray &data, bool override = false); + bool remove(const QString &filePath); + bool rename(const QString &filePath, const QString &newFilePath, bool override = false); + bool copy(const QString &from, const QString &to); + bool link(const QString &source, const QString &to); +}; + +DCORE_END_NAMESPACE diff --git a/include/filesystem/dbasefilewatcher.h b/include/filesystem/dbasefilewatcher.h new file mode 100644 index 0000000..51028f6 --- /dev/null +++ b/include/filesystem/dbasefilewatcher.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DBASEFILEWATCHER_H +#define DBASEFILEWATCHER_H + +#include "dtkcore_global.h" +#include "dobject.h" + +#include + +DCORE_BEGIN_NAMESPACE + +class DBaseFileWatcherPrivate; +class LIBDTKCORESHARED_EXPORT DBaseFileWatcher : public QObject, public DObject +{ + Q_OBJECT + +public: + ~DBaseFileWatcher(); + + QUrl fileUrl() const; + + bool startWatcher(); + bool stopWatcher(); + bool restartWatcher(); + + virtual void setEnabledSubfileWatcher(const QUrl &subfileUrl, bool enabled = true); + + using SignalType1 = void(DBaseFileWatcher::*)(const QUrl &); + using SignalType2 = void(DBaseFileWatcher::*)(const QUrl &, const QUrl &); + static bool ghostSignal(const QUrl &targetUrl, SignalType1 signal, const QUrl &arg1); + static bool ghostSignal(const QUrl &targetUrl, SignalType2 signal, const QUrl &arg1, const QUrl &arg2); + +Q_SIGNALS: + void fileDeleted(const QUrl &url); + void fileAttributeChanged(const QUrl &url); + void fileMoved(const QUrl &fromUrl, const QUrl &toUrl); + void subfileCreated(const QUrl &url); + void fileModified(const QUrl &url); + void fileClosed(const QUrl &url); + +protected: + explicit DBaseFileWatcher(DBaseFileWatcherPrivate &dd, const QUrl &url, QObject *parent = 0); + +private: + Q_DISABLE_COPY(DBaseFileWatcher) + D_DECLARE_PRIVATE(DBaseFileWatcher) +}; + +DCORE_END_NAMESPACE + +#endif // DBASEFILEWATCHER_H diff --git a/include/filesystem/dcapfile.h b/include/filesystem/dcapfile.h new file mode 100644 index 0000000..826391e --- /dev/null +++ b/include/filesystem/dcapfile.h @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DCAPFILE_H +#define DCAPFILE_H + +#include + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DCapFilePrivate; +class DCapFile : public QFile, public DObject +{ + Q_OBJECT + D_DECLARE_PRIVATE(DCapFile) + Q_DISABLE_COPY(DCapFile) + +public: + explicit DCapFile(QObject *parent = nullptr); + DCapFile(const QString &name, QObject *parent = nullptr); + + ~DCapFile() override; + + void setFileName(const QString &name); + + bool exists() const; + static bool exists(const QString &fileName); + +#if QT_DEPRECATED_SINCE(5, 13) + D_DECL_DEPRECATED_X("Use QFile::symLinkTarget() instead") + QString readLink() const; +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + QString symLinkTarget() const; +#endif + bool remove(); + static bool remove(const QString &fileName); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + bool moveToTrash(); + static bool moveToTrash(const QString &fileName, QString *pathInTrash = nullptr); +#endif + + bool rename(const QString &newName); + static bool rename(const QString &oldName, const QString &newName); + + bool link(const QString &newName); + static bool link(const QString &oldname, const QString &newName); + + bool copy(const QString &newName); + static bool copy(const QString &fileName, const QString &newName); + + bool open(OpenMode flags) override; + + bool resize(qint64 sz) override; + static bool resize(const QString &filename, qint64 sz); + +private: + bool open(FILE *f, OpenMode ioFlags, FileHandleFlags handleFlags=DontCloseHandle); + bool open(int fd, OpenMode ioFlags, FileHandleFlags handleFlags=DontCloseHandle); +}; + +class DCapDirPrivate; +class DCapDir : public QDir +{ +public: + DCapDir(const DCapDir &); + DCapDir(const QString &path = QString()); + DCapDir(const QString &path, const QString &nameFilter, + SortFlags sort = SortFlags(Name | IgnoreCase), Filters filter = AllEntries); + ~DCapDir(); + + void setPath(const QString &path); + + bool cd(const QString &dirName); + + QStringList entryList(Filters filters = NoFilter, SortFlags sort = NoSort) const; + QStringList entryList(const QStringList &nameFilters, Filters filters = NoFilter, + SortFlags sort = NoSort) const; + + QFileInfoList entryInfoList(Filters filters = NoFilter, SortFlags sort = NoSort) const; + QFileInfoList entryInfoList(const QStringList &nameFilters, Filters filters = NoFilter, + SortFlags sort = NoSort) const; + + bool mkdir(const QString &dirName) const; + bool rmdir(const QString &dirName) const; + bool mkpath(const QString &dirPath) const; + bool rmpath(const QString &dirPath) const; + bool exists() const; + bool remove(const QString &fileName); + bool rename(const QString &oldName, const QString &newName); + bool exists(const QString &name) const; + +private: + QSharedDataPointer dd_ptr; +}; + +DCORE_END_NAMESPACE +Q_DECLARE_SHARED(DTK_CORE_NAMESPACE::DCapDir) +#endif // DCAPFILE_H diff --git a/include/filesystem/dcapmanager.h b/include/filesystem/dcapmanager.h new file mode 100644 index 0000000..35b206e --- /dev/null +++ b/include/filesystem/dcapmanager.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DCAPMANAGER_H +#define DCAPMANAGER_H + +#include + +#include + +DCORE_BEGIN_NAMESPACE +class DCapManagerPrivate; +class DCapManager : public QObject, public DObject +{ + Q_OBJECT + Q_DISABLE_COPY(DCapManager) + D_DECLARE_PRIVATE(DCapManager) +public: + static DCapManager *instance(); + static void registerFileEngine(); + static void unregisterFileEngine(); + + void appendPath(const QString &path); + void appendPaths(const QStringList &pathList); + + void removePath(const QString &path); + void removePaths(const QStringList &paths); + + QStringList paths() const; + +protected: + explicit DCapManager(); +}; + +DCORE_END_NAMESPACE +#endif // DCAPMANAGER_H diff --git a/include/filesystem/dfilesystemwatcher.h b/include/filesystem/dfilesystemwatcher.h new file mode 100644 index 0000000..923b760 --- /dev/null +++ b/include/filesystem/dfilesystemwatcher.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILESYSTEMWATCHER_H +#define DFILESYSTEMWATCHER_H + +#include "dtkcore_global.h" +#include "dobject.h" + +#include + +DCORE_BEGIN_NAMESPACE + +class DFileSystemWatcherPrivate; +class LIBDTKCORESHARED_EXPORT DFileSystemWatcher : public QObject, public DObject +{ + Q_OBJECT + D_DECLARE_PRIVATE(DFileSystemWatcher) + +public: + DFileSystemWatcher(QObject *parent = Q_NULLPTR); + DFileSystemWatcher(const QStringList &paths, QObject *parent = Q_NULLPTR); + ~DFileSystemWatcher(); + + bool addPath(const QString &file); + QStringList addPaths(const QStringList &files); + bool removePath(const QString &file); + QStringList removePaths(const QStringList &files); + + QStringList files() const; + QStringList directories() const; + +Q_SIGNALS: + void fileDeleted(const QString &path, const QString &name, QPrivateSignal); + void fileAttributeChanged(const QString &path, const QString &name, QPrivateSignal); + void fileClosed(const QString &path, const QString &name, QPrivateSignal); + void fileMoved(const QString &fromPath, const QString &fromName, + const QString &toPath, const QString &toName, QPrivateSignal); + void fileCreated(const QString &path, const QString &name, QPrivateSignal); + void fileModified(const QString &path, const QString &name, QPrivateSignal); + +private: + Q_PRIVATE_SLOT(d_func(), void _q_readFromInotify()) +}; + +DCORE_END_NAMESPACE + +#endif // DFILESYSTEMWATCHER_H diff --git a/include/filesystem/dfilewatcher.h b/include/filesystem/dfilewatcher.h new file mode 100644 index 0000000..c5b3d30 --- /dev/null +++ b/include/filesystem/dfilewatcher.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILEWATCHER_H +#define DFILEWATCHER_H + +#include "dbasefilewatcher.h" + +DCORE_BEGIN_NAMESPACE + +class DFileWatcherPrivate; +class LIBDTKCORESHARED_EXPORT DFileWatcher : public DBaseFileWatcher +{ + Q_OBJECT + +public: + explicit DFileWatcher(const QString &filePath, QObject *parent = 0); + +private Q_SLOTS: + void onFileDeleted(const QString &path, const QString &name); + void onFileAttributeChanged(const QString &path, const QString &name); + void onFileMoved(const QString &fromPath, const QString &fromName, + const QString &toPath, const QString &toName); + void onFileCreated(const QString &path, const QString &name); + void onFileModified(const QString &path, const QString &name); + void onFileClosed(const QString &path, const QString &name); + +private: + D_DECLARE_PRIVATE(DFileWatcher) +}; + +DCORE_END_NAMESPACE + +#endif // DFILEWATCHER_H diff --git a/include/filesystem/dfilewatchermanager.h b/include/filesystem/dfilewatchermanager.h new file mode 100644 index 0000000..355564b --- /dev/null +++ b/include/filesystem/dfilewatchermanager.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILEWATCHERMANAGER_H +#define DFILEWATCHERMANAGER_H + +#include "dtkcore_global.h" +#include "dobject.h" + +#include + +DCORE_BEGIN_NAMESPACE + +class DFileWatcher; + +class DFileWatcherManagerPrivate; +class LIBDTKCORESHARED_EXPORT DFileWatcherManager : public QObject, public DObject +{ + Q_OBJECT + +public: + explicit DFileWatcherManager(QObject *parent = 0); + ~DFileWatcherManager(); + + DFileWatcher *add(const QString &filePath); + void remove(const QString &filePath); + +Q_SIGNALS: + void fileDeleted(const QString &filePath); + void fileAttributeChanged(const QString &filePath); + void fileMoved(const QString &fromFilePath, const QString &toFilePath); + void subfileCreated(const QString &filePath); + void fileModified(const QString &filePath); + void fileClosed(const QString &filePath); + +private: + QScopedPointer d_ptr; + + D_DECLARE_PRIVATE(DFileWatcherManager) + Q_DISABLE_COPY(DFileWatcherManager) +}; + +DCORE_END_NAMESPACE + +#endif // DFILEWATCHERMANAGER_H diff --git a/include/filesystem/dpathbuf.h b/include/filesystem/dpathbuf.h new file mode 100644 index 0000000..bfea79c --- /dev/null +++ b/include/filesystem/dpathbuf.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DPathBuf +{ +public: + DPathBuf(); + DPathBuf(const QString &path); + + DPathBuf operator/(const QString &p) const + { + return DPathBuf(m_path + "/" + p); + } + + DPathBuf &operator/=(const QString &p) + { + return join(p); + } + + DPathBuf operator/(const char *p) const + { + return operator /(QString(p)); + } + + DPathBuf &operator/=(const char *p) + { + return operator /=(QString(p)); + } + + DPathBuf &join(const QString &p) + { + m_path += "/" + p; + m_path = QDir(m_path).absolutePath(); + return *this; + } + + QString toString() const + { + return QDir::toNativeSeparators(m_path); + } + +private: + QString m_path; +}; + +DCORE_END_NAMESPACE diff --git a/include/filesystem/dstandardpaths.h b/include/filesystem/dstandardpaths.h new file mode 100644 index 0000000..f2f1f34 --- /dev/null +++ b/include/filesystem/dstandardpaths.h @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DTK_CORE_FILESYSTEM_DSTANDARDPATHS_H +#define DTK_CORE_FILESYSTEM_DSTANDARDPATHS_H + +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DStandardPathsPrivate; +class LIBDTKCORESHARED_EXPORT DStandardPaths +{ +public: + enum Mode { + Auto, + Snap, + Test, + }; + + static QString writableLocation(QStandardPaths::StandardLocation type); + static QStringList standardLocations(QStandardPaths::StandardLocation type); + + static QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = QStandardPaths::LocateFile); + static QStringList locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = QStandardPaths::LocateFile); + static QString findExecutable(const QString &executableName, const QStringList &paths = QStringList()); + static void setMode(Mode mode); + + enum class XDG { + DataHome, + ConfigHome, + CacheHome, + RuntimeTime + }; + + enum class DSG { + AppData, + DataDir + }; + + static QString homePath(); + static QString homePath(const uint uid); + static QString path(XDG type); + static QString path(DSG type); + static QStringList paths(DSG type); + static QString filePath(XDG type, QString fileName); + static QString filePath(DSG type, QString fileName); + +private: + DStandardPaths(); + ~DStandardPaths(); + Q_DISABLE_COPY(DStandardPaths) +}; + +DCORE_END_NAMESPACE + +#endif // DTK_CORE_FILESYSTEM_DSTANDARDPATHS_H diff --git a/include/filesystem/dtrashmanager.h b/include/filesystem/dtrashmanager.h new file mode 100644 index 0000000..72382bc --- /dev/null +++ b/include/filesystem/dtrashmanager.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DTRASHMANAGER_H +#define DTRASHMANAGER_H + +#include +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class DTrashManagerPrivate; +class LIBDTKCORESHARED_EXPORT DTrashManager : public QObject, public DObject +{ +public: + static DTrashManager *instance(); + + bool trashIsEmpty() const; + bool cleanTrash(); + bool moveToTrash(const QString &filePath, bool followSymlink = false); + +protected: + DTrashManager(); + +private: + D_DECLARE_PRIVATE(DTrashManager) +}; + +DCORE_END_NAMESPACE + +#endif // DTRASHMANAGER_H diff --git a/include/global/dconfig.h b/include/global/dconfig.h new file mode 100644 index 0000000..dc405ae --- /dev/null +++ b/include/global/dconfig.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DCONFIG_H +#define DCONFIG_H + +#include +#include + +#include +#include + +DCORE_BEGIN_NAMESPACE +class DConfigBackend { +public: + virtual ~DConfigBackend(); + virtual bool isValid() const = 0; + virtual bool load(const QString &/*appId*/) = 0; + virtual QStringList keyList() const = 0; + virtual QVariant value(const QString &/*key*/, const QVariant &/*fallback*/) const = 0; + virtual void setValue(const QString &/*key*/, const QVariant &/*value*/) = 0; + virtual void reset(const QString &key) { setValue(key, QVariant());} + virtual QString name() const {return QString("");} +}; + +class DConfigPrivate; +class LIBDTKCORESHARED_EXPORT DConfig : public QObject, public DObject +{ + Q_OBJECT + D_DECLARE_PRIVATE(DConfig) + + Q_PROPERTY(QStringList keyList READ keyList FINAL) + +public: + explicit DConfig(const QString &name, const QString &subpath = QString(), + QObject *parent = nullptr); + + explicit DConfig(DConfigBackend *backend, const QString &name, const QString &subpath = QString(), + QObject *parent = nullptr); + + static DConfig *create(const QString &appId, const QString &name, const QString &subpath = QString(), + QObject *parent = nullptr); + static DConfig *create(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath = QString(), + QObject *parent = nullptr); + + QString backendName() const; + + QStringList keyList() const; + + bool isValid() const; + QVariant value(const QString &key, const QVariant &fallback = QVariant()) const; + void setValue(const QString &key, const QVariant &value); + void reset(const QString &key); + + QString name() const; + QString subpath() const; + +Q_SIGNALS: + void valueChanged(const QString &key); + +private: + explicit DConfig(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, + QObject *parent = nullptr); +}; + +DCORE_END_NAMESPACE + +#endif // DCONFIG_H diff --git a/include/global/dconfigfile.h b/include/global/dconfigfile.h new file mode 100644 index 0000000..cd4fd8c --- /dev/null +++ b/include/global/dconfigfile.h @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DCONFIGFILE_H +#define DCONFIGFILE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +DCORE_BEGIN_NAMESPACE + +class DConfigMeta; +class DConfigCache; +class DConfigFilePrivate; +class LIBDTKCORESHARED_EXPORT DConfigFile : public DObject{ + D_DECLARE_PRIVATE(DConfigFile) +public: + enum Flag { + NoOverride = 1 << 0, + Global = 1 << 1 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + enum Permissions { + ReadOnly, + ReadWrite + }; + + enum Visibility { + Private, + Public + }; + + struct Version { + quint16 major; + quint16 minor; + }; + + static constexpr Version supportedVersion(); + + explicit DConfigFile(const QString &appId, const QString &name, + const QString &subpath = QString()); + explicit DConfigFile(const DConfigFile &other); + + bool load(const QString &localPrefix = QString()); + bool load(QIODevice *meta, const QList &overrides); + + bool save(const QString &localPrefix = QString(), QJsonDocument::JsonFormat format = QJsonDocument::Indented, + bool sync = false) const; + + bool isValid() const; + QVariant value(const QString &key, DConfigCache *userCache = nullptr) const; + bool setValue(const QString &key, const QVariant &value, const QString &callerAppid, + DConfigCache *userCache = nullptr); + + DConfigCache *createUserCache(const uint uid); + DConfigCache *globalCache() const; + + DConfigMeta *meta(); + +protected: + friend QDebug operator<<(QDebug, const DConfigFile &); +}; + +class LIBDTKCORESHARED_EXPORT DConfigMeta { +public: + virtual ~DConfigMeta(); + virtual DConfigFile::Version version() const = 0; + virtual void setVersion(quint16 major, quint16 minor) = 0; + + virtual bool load(const QString &localPrefix = QString()) = 0; + + virtual bool load(QIODevice *meta, const QList &overrides) = 0; + + virtual QStringList keyList() const = 0; + virtual DConfigFile::Flags flags(const QString &key) const = 0; + virtual DConfigFile::Permissions permissions(const QString &key) const = 0; + virtual DConfigFile::Visibility visibility(const QString &key) const = 0; + virtual int serial(const QString &key) const = 0; + + virtual QString displayName(const QString &key, const QLocale &locale) = 0; + virtual QString description(const QString &key, const QLocale &locale) = 0; + + virtual QString metaPath(const QString &localPrefix = QString(), bool *useAppId = nullptr) const = 0; + virtual QStringList allOverrideDirs(const bool useAppId, const QString &prefix = QString()) const = 0; + + virtual QVariant value(const QString &key) const = 0; +}; + +class LIBDTKCORESHARED_EXPORT DConfigCache { +public: + virtual ~DConfigCache(); + + virtual bool load(const QString &localPrefix = QString()) = 0; + virtual bool save(const QString &localPrefix = QString(), + QJsonDocument::JsonFormat format = QJsonDocument::Indented, bool sync = false) = 0; + virtual bool isGlobal() const = 0; + + virtual void remove(const QString &key) = 0; + virtual QStringList keyList() const = 0; + virtual bool setValue(const QString &key, const QVariant &value, const int serial, + const uint uid, const QString &callerAppid) = 0; + virtual QVariant value(const QString &key) const = 0; + virtual int serial(const QString &key) const = 0; + virtual uint uid() const = 0; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_CORE_EXPORT QDebug operator<<(QDebug, const DConfigFile &); +#endif + +Q_DECLARE_OPERATORS_FOR_FLAGS(DConfigFile::Flags) + +DCORE_END_NAMESPACE + +#endif // DCONFIGFILE_H diff --git a/include/global/ddesktopentry.h b/include/global/ddesktopentry.h new file mode 100644 index 0000000..877bdfd --- /dev/null +++ b/include/global/ddesktopentry.h @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include "dtkcore_global.h" + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DDesktopEntryPrivate; +class LIBDTKCORESHARED_EXPORT DDesktopEntry +{ + Q_GADGET +public: + enum EntryType { + Unknown = 0, //!< Unknown desktop file type. Maybe is invalid. + Application, //!< The file describes application. + Link, //!< The file describes URL. + Directory, //!< The file describes directory settings. + ServiceType, //!< KDE specific type. mentioned in the spec, so listed here too. + Service, //!< KDE specific type. mentioned in the spec, so listed here too. + FSDevice //!< KDE specific type. mentioned in the spec, so listed here too. + }; + Q_ENUM(EntryType) + + enum ValueType { + Unparsed = 0, // Maybe useless, consider remove it? + String, + Strings, + Boolean, + Numeric, + NotExisted = 99 + }; + Q_ENUM(ValueType) + + enum Status { + NoError = 0, //!< No error occurred. + AccessError, //!< An access error occurred (e.g. trying to write to a read-only file). + FormatError //!< A format error occurred (e.g. loading a malformed desktop entry file). + }; + Q_ENUM(Status) + + explicit DDesktopEntry(const QString &filePath) noexcept; + ~DDesktopEntry(); + + bool save() const; + + Status status() const; + QStringList keys(const QString §ion = "Desktop Entry") const; + QStringList allGroups(bool sorted = false) const; + + bool contains(const QString &key, const QString §ion = "Desktop Entry") const; + + QString name() const; + QString genericName() const; + QString ddeDisplayName() const; + QString comment() const; + + QString rawValue(const QString &key, const QString §ion = "Desktop Entry", + const QString &defaultValue = QString()) const; + QString stringValue(const QString &key, const QString §ion = "Desktop Entry", + const QString &defaultValue = QString()) const; + QString localizedValue(const QString &key, const QString &localeKey = "default", + const QString §ion = "Desktop Entry", const QString& defaultValue = QString()) const; + QString localizedValue(const QString &key, const QLocale &locale, + const QString §ion = "Desktop Entry", const QString& defaultValue = QString()) const; + QStringList stringListValue(const QString &key, const QString §ion = "Desktop Entry") const; + + bool setRawValue(const QString &value, const QString &key, const QString& section = "Desktop Entry"); + bool setStringValue(const QString &value, const QString &key, const QString& section = "Desktop Entry"); + bool setLocalizedValue(const QString &value, const QString& localeKey, + const QString &key, const QString& section = "Desktop Entry"); + + bool removeEntry(const QString &key, const QString §ion = "Desktop Entry"); + + static QString &escape(QString &str); + static QString &escapeExec(QString &str); + static QString &unescape(QString &str, bool unescapeSemicolons = false); + static QString &unescapeExec(QString &str); + +protected: + bool setStatus(const Status &status); + +private: + QScopedPointer d_ptr; + + Q_DECLARE_PRIVATE(DDesktopEntry) +}; + +DCORE_END_NAMESPACE diff --git a/include/global/dsecurestring.h b/include/global/dsecurestring.h new file mode 100644 index 0000000..a2f146b --- /dev/null +++ b/include/global/dsecurestring.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include "dtkcore_global.h" +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DSecureString : public QString +{ +public: + using QString::QString; + DSecureString(const QString &other) noexcept; + ~DSecureString(); +}; + +DCORE_END_NAMESPACE diff --git a/include/global/dsgapplication.h b/include/global/dsgapplication.h new file mode 100644 index 0000000..147fa35 --- /dev/null +++ b/include/global/dsgapplication.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DSGAPPLICATION_H +#define DSGAPPLICATION_H + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DSGApplication +{ +public: + static QByteArray id(); + static QByteArray getId(qint64 pid); +}; + +DCORE_END_NAMESPACE + +#endif // DSGAPPLICATION_H diff --git a/include/global/dsysinfo.h b/include/global/dsysinfo.h new file mode 100644 index 0000000..807f111 --- /dev/null +++ b/include/global/dsysinfo.h @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DSYSINFO_H +#define DSYSINFO_H + +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class DSysInfoPrivate; +class LIBDTKCORESHARED_EXPORT DSysInfo +{ + Q_GADGET +public: + enum ProductType { + UnknownType = 0, + Deepin, + ArchLinux, + CentOS, + Debian, + Fedora, + LinuxMint, + Manjaro, + openSUSE, + SailfishOS, + Ubuntu, + Uos, + Gentoo, + NixOS + }; + + enum DeepinType { + UnknownDeepin = 0, + DeepinDesktop, + DeepinProfessional, + DeepinServer, + DeepinPersonal + }; + + enum LogoType { + Normal = 0, + Light, + Symbolic, + Transparent + }; + + enum OrgType { + Distribution, //!< distribution itself + Distributor, //!< distributer of the current distribution + Manufacturer //!< manufacturer of the current distribution or device + }; + + enum UosType { + UosTypeUnknown, + UosDesktop, + UosServer, + UosDevice, + + UosTypeCount // must at last + }; + + enum UosEdition { + UosEditionUnknown, + UosProfessional, + UosHome, + UosCommunity, + UosMilitary, + UosEnterprise, + UosEnterpriseC, + UosEuler, + UosMilitaryS, // for Server + UosDeviceEdition, + UosEducation, + + UosEditionCount // must at last + }; + + enum UosArch { + UosArchUnknown, + UosAMD64 = 1 << 0, + UosARM64 = 1 << 1, + UosMIPS64 = 1 << 2, + UosSW64 = 1 << 3 + }; + + //! @~english enum.Arch + enum Arch { + ARM64, /*!< @~english arm64 */ + ARM64_BE, /*!< @~english arm64-be */ + ARM, /*!< @~english arm */ + ARM_BE, /*!< @~english arm-be */ + ALPHA, /*!< @~english alpha */ + SW_64, /*!< @~english sw_64 */ + ARC, /*!< @~english arc */ + ARC_BE, /*!< @~english arc-be */ + CRIS, /*!< @~english cris */ + X86_64, /*!< @~english x86-64 */ + X86, /*!< @~english x86 */ + IA64, /*!< @~english ia64 */ + LOONGARCH64,/*!< @~english loongarch64 */ + M68K, /*!< @~english m68k */ + MIPS64_LE, /*!< @~english mips64-le */ + MIPS64, /*!< @~english mips64 */ + MIPS_LE, /*!< @~english mips-le */ + MIPS, /*!< @~english mips */ + NIOS2, /*!< @~english nios2 */ + PARISC64, /*!< @~english parisc64 */ + PARISC, /*!< @~english parisc */ + PPC64_LE, /*!< @~english ppc64-le */ + PPC64, /*!< @~english ppc64 */ + PPC, /*!< @~english ppc */ + PPC_LE, /*!< @~english ppc-le */ + RISCV32, /*!< @~english riscv32 */ + RISCV64, /*!< @~english riscv64 */ + S390X, /*!< @~english s390x */ + S390, /*!< @~english s390 */ + SH64, /*!< @~english sh64 */ + SH, /*!< @~english sh */ + SPARC64, /*!< @~english sparc64 */ + SPARC, /*!< @~english sparc */ + TILEGX, /*!< @~english tilegx */ + + NUM_ARCHES, /*!< @~english number of defined Arch types */ + }; + Q_ENUM(Arch) // Q_GADGET + +#ifdef Q_OS_LINUX + static bool isDeepin(); + static bool isDDE(); + static DeepinType deepinType(); + static QString deepinTypeDisplayName(const QLocale &locale = QLocale::system()); + static QString deepinVersion(); + static QString deepinEdition(); + static QString deepinCopyright(); + + // uos version interface + Q_DECL_DEPRECATED_X("Use arch() instead") static UosType uosType(); + static UosEdition uosEditionType(); + static UosArch uosArch(); + static QString uosProductTypeName(const QLocale &locale = QLocale::system()); + static QString uosSystemName(const QLocale &locale = QLocale::system()); + static QString uosEditionName(const QLocale &locale = QLocale::system()); + + static QString spVersion(); // SP1...SP99 + static QString udpateVersion(); // update1...update9 + static QString majorVersion(); + static QString minorVersion(); + static QString buildVersion(); // xyzs +#endif + + Q_DECL_DEPRECATED_X("Use distributionInfoPath() instead") static QString deepinDistributionInfoPath(); + static QString distributionInfoPath(); + static QString distributionInfoSectionName(OrgType type); + + static QString distributionOrgName(OrgType type = Distribution, const QLocale &locale = QLocale::system()); + Q_DECL_DEPRECATED_X("Use deepinDistributionOrgName() instead") static QString deepinDistributorName(); + static QPair distributionOrgWebsite(OrgType type = Distribution); + Q_DECL_DEPRECATED_X("Use deepinDistributionOrgWebsite() instead") static QPair deepinDistributorWebsite(); + static QString distributionOrgLogo(OrgType orgType = Distribution, LogoType type = Normal, const QString & fallback = QString()); + Q_DECL_DEPRECATED_X("Use deepinDistributionOrgLogo() instead") static QString deepinDistributorLogo(LogoType type = Normal, const QString & fallback = QString()); + + static QString operatingSystemName(); + static ProductType productType(); + static QString productTypeString(); + static QString productVersion(); + static bool isCommunityEdition(); + + static QString computerName(); + static QString cpuModelName(); + static qint64 memoryInstalledSize(); + static qint64 memoryTotalSize(); + static qint64 systemDiskSize(); + + static QDateTime bootTime(); + static QDateTime shutdownTime(); + static qint64 uptime(); + static Arch arch(); +}; + +DCORE_END_NAMESPACE + +#endif // DSYSINFO_H diff --git a/include/global/dtkcore_global.h b/include/global/dtkcore_global.h new file mode 100644 index 0000000..c3efe0c --- /dev/null +++ b/include/global/dtkcore_global.h @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#define DTK_NAMESPACE Dtk + +#if !defined(DTK_NAMESPACE) +# define DTK_BEGIN_NAMESPACE +# define DTK_END_NAMESPACE +# define DTK_USE_NAMESPACE +#else +# define DTK_BEGIN_NAMESPACE namespace DTK_NAMESPACE { +# define DTK_END_NAMESPACE } +# define DTK_USE_NAMESPACE using namespace DTK_NAMESPACE; +#endif + +#define DCORE_NAMESPACE Core +#define DTK_CORE_NAMESPACE DTK_NAMESPACE::DCORE_NAMESPACE + +#if !defined(DCORE_NAMESPACE) +# define DCORE_BEGIN_NAMESPACE +# define DCORE_END_NAMESPACE +# define DCORE_USE_NAMESPACE +#else +# define DCORE_BEGIN_NAMESPACE namespace DTK_NAMESPACE { namespace DCORE_NAMESPACE { +# define DCORE_END_NAMESPACE }} +# define DCORE_USE_NAMESPACE using namespace DTK_CORE_NAMESPACE; +#endif + + +#if defined(DTK_STATIC_LIB) +# define LIBDTKCORESHARED_EXPORT +#else +#if defined(LIBDTKCORE_LIBRARY) +# define LIBDTKCORESHARED_EXPORT Q_DECL_EXPORT +#else +# define LIBDTKCORESHARED_EXPORT Q_DECL_IMPORT +#endif +#endif + +#ifdef D_DEPRECATED_CHECK +#define D_DECL_DEPRECATED_X(text) Q_DECL_HIDDEN +#define D_DECL_DEPRECATED Q_DECL_HIDDEN +#else +#define D_DECL_DEPRECATED Q_DECL_DEPRECATED +#define D_DECL_DEPRECATED_X Q_DECL_DEPRECATED_X +#endif + +#define DTK_VERSION_CHECK(major, minor, patch, build) ((major<<24)|(minor<<16)|(patch<<8)|build) +#define DTK_VERSION DTK_VERSION_CHECK(DTK_VERSION_MAJOR, DTK_VERSION_MINOR, DTK_VERSION_PATCH, DTK_VERSION_BUILD) + +extern "C" { +int LIBDTKCORESHARED_EXPORT dtkVersion(); +const LIBDTKCORESHARED_EXPORT char *dtkVersionString(); +} diff --git a/include/log/AbstractAppender.h b/include/log/AbstractAppender.h new file mode 100644 index 0000000..9435da3 --- /dev/null +++ b/include/log/AbstractAppender.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef ABSTRACTAPPENDER_H +#define ABSTRACTAPPENDER_H + +#include "dtkcore_global.h" +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT AbstractAppender +{ +public: + AbstractAppender(); + virtual ~AbstractAppender(); + + Logger::LogLevel detailsLevel() const; + void setDetailsLevel(Logger::LogLevel level); + void setDetailsLevel(const QString &level); + + void write(const QDateTime &time, Logger::LogLevel level, const char *file, int line, + const char *func, const QString &category, const QString &msg); + +protected: + virtual void append(const QDateTime &time, Logger::LogLevel level, const char *file, int line, + const char *func, const QString &category, const QString &msg) = 0; + +private: + QMutex m_writeMutex; + + Logger::LogLevel m_detailsLevel; + mutable QMutex m_detailsLevelMutex; +}; + +DCORE_END_NAMESPACE +#endif // ABSTRACTAPPENDER_H diff --git a/include/log/AbstractStringAppender.h b/include/log/AbstractStringAppender.h new file mode 100644 index 0000000..3172c8b --- /dev/null +++ b/include/log/AbstractStringAppender.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef ABSTRACTSTRINGAPPENDER_H +#define ABSTRACTSTRINGAPPENDER_H + +#include "AbstractAppender.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT AbstractStringAppender : public AbstractAppender +{ +public: + AbstractStringAppender(); + virtual QString format() const; + void setFormat(const QString &format); + + static QString stripFunctionName(const char *name); +protected: + QString formattedString(const QDateTime &time, Logger::LogLevel level, const char *file, int line, + const char *func, const QString &category, const QString &msg) const; + +private: + static QByteArray qCleanupFuncinfo(const char*); + + QString m_format; + mutable QReadWriteLock m_formatLock; +}; + +DCORE_END_NAMESPACE +#endif // ABSTRACTSTRINGAPPENDER_H diff --git a/include/log/ConsoleAppender.h b/include/log/ConsoleAppender.h new file mode 100644 index 0000000..be63061 --- /dev/null +++ b/include/log/ConsoleAppender.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef CONSOLEAPPENDER_H +#define CONSOLEAPPENDER_H + +#include "dtkcore_global.h" +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT ConsoleAppender : public AbstractStringAppender +{ +public: + ConsoleAppender(); + virtual QString format() const; + void ignoreEnvironmentPattern(bool ignore); + +protected: + virtual void append(const QDateTime &time, Logger::LogLevel level, const char *file, int line, + const char *func, const QString &category, const QString &msg); + +private: + bool m_ignoreEnvPattern; +}; + +DCORE_END_NAMESPACE + +#endif // CONSOLEAPPENDER_H diff --git a/include/log/FileAppender.h b/include/log/FileAppender.h new file mode 100644 index 0000000..9f5e378 --- /dev/null +++ b/include/log/FileAppender.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef FILEAPPENDER_H +#define FILEAPPENDER_H + +// Logger +#include "dtkcore_global.h" +#include + +// Qt +#include +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT FileAppender : public AbstractStringAppender +{ +public: + FileAppender(const QString &fileName = QString()); + ~FileAppender(); + + QString fileName() const; + void setFileName(const QString &s); + + qint64 size() const; + +protected: + virtual void append(const QDateTime &time, Logger::LogLevel level, const char *file, int line, + const char *func, const QString &category, const QString &msg); + bool openFile(); + void closeFile(); + +private: + QFile m_logFile; + QTextStream m_logStream; + mutable QMutex m_logFileMutex; +}; + +DCORE_END_NAMESPACE + +#endif // FILEAPPENDER_H diff --git a/include/log/LogManager.h b/include/log/LogManager.h new file mode 100644 index 0000000..8e761f1 --- /dev/null +++ b/include/log/LogManager.h @@ -0,0 +1,56 @@ +// Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef LOGMANAGER_H +#define LOGMANAGER_H + +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class ConsoleAppender; +class RollingFileAppender; + +class LIBDTKCORESHARED_EXPORT DLogManager +{ +public: + static void registerConsoleAppender(); + static void registerFileAppender(); + + static QString getlogFilePath(); + + /*! + * \brief setlogFilePath will change log file path of registerFileAppender + * \a logFilePath is the full path of file appender log + */ + static void setlogFilePath(const QString &logFilePath); + + static void setLogFormat(const QString &format); + +private: + QString m_format; + QString m_logPath; + ConsoleAppender* m_consoleAppender; + RollingFileAppender* m_rollingFileAppender; + + void initConsoleAppender(); + void initRollingFileAppender(); + QString joinPath(const QString &path, const QString &fileName); + + inline static DLogManager* instance(){ + static DLogManager instance; + return &instance; + } + explicit DLogManager(); + ~DLogManager(); + DLogManager(const DLogManager &); + DLogManager & operator = (const DLogManager &); +}; + +DCORE_END_NAMESPACE + +#endif // LOGMANAGER_H diff --git a/include/log/Logger.h b/include/log/Logger.h new file mode 100644 index 0000000..d69557e --- /dev/null +++ b/include/log/Logger.h @@ -0,0 +1,149 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include + +#include "dloggerdefs.h" + +DCORE_BEGIN_NAMESPACE + +class AbstractAppender; +class LoggerPrivate; +class LIBDTKCORESHARED_EXPORT Logger +{ + Q_DISABLE_COPY(Logger) +public: + //! the log levels + enum LogLevel { + Trace, //!< ~english Trace level. Can be used for mostly unneeded records used for internal code tracing. + Debug, //!< ~english Debug level.for the debugging of the software. + Info, //!< ~english Info level. Can be used for informational records, which may be interesting for not only developers. + Warning, //!< ~english Warning. May be used to log some non-fatal warnings detected by your application. + Error, //!< ~english May be used for a big problems making your application work wrong but not crashing. + Fatal //!< ~english Fatal. Used for unrecoverable errors, crashes the application (abort) right after the log record is written. + }; + + Logger(); + Logger(const QString &defaultCategory); + ~Logger(); + + static Logger *globalInstance(); + + static QString levelToString(LogLevel level); + static LogLevel levelFromString(const QString &str); + + void registerAppender(AbstractAppender *appender); + void registerCategoryAppender(const QString &category, AbstractAppender *appender); + + void logToGlobalInstance(const QString &category, bool logToGlobal = false); + + void setDefaultCategory(const QString &category); + QString defaultCategory() const; + + void write(const QDateTime &time, LogLevel level, const char *file, int line, + const char *func, const char *category, const QString &msg); + void write(LogLevel level, const char *file, int line, + const char *func, const char *category, const QString &msg); + QDebug write(LogLevel level, const char *file, int line, + const char *func, const char *category); + void writeAssert(const char *file, int line, + const char *func, const char *condition); + +private: + void write(const QDateTime &time, LogLevel level, const char *file, int line, + const char *func, const char *category, + const QString &msg, bool fromLocalInstance); + Q_DECLARE_PRIVATE(Logger) + LoggerPrivate *d_ptr; +}; + + +class LIBDTKCORESHARED_EXPORT CuteMessageLogger +{ + Q_DISABLE_COPY(CuteMessageLogger) + +public: + Q_DECL_CONSTEXPR CuteMessageLogger(Logger *l, Logger::LogLevel level, + const char *file, int line, const char *func) + : m_l(l), + m_level(level), + m_file(file), + m_line(line), + m_function(func), + m_category(nullptr) + {} + + Q_DECL_CONSTEXPR CuteMessageLogger(Logger *l, Logger::LogLevel level, const char *file, + int line, const char *func, const char *category) + : m_l(l), + m_level(level), + m_file(file), + m_line(line), + m_function(func), + m_category(category) + {} + + void write(const char *msg, ...) const +#if defined(Q_CC_GNU) && !defined(__INSURE__) + #if defined(Q_CC_MINGW) && !defined(Q_CC_CLANG) + __attribute__((format(gnu_printf, 2, 3))); + #else + __attribute__((format(printf, 2, 3))); + #endif +#endif + + void write(const QString &msg) const; + QDebug write() const; + +private: + Logger *m_l; + Logger::LogLevel m_level; + const char *m_file; + int m_line; + const char *m_function; + const char *m_category; +}; + +class LIBDTKCORESHARED_EXPORT LoggerTimingHelper +{ + Q_DISABLE_COPY(LoggerTimingHelper) +public: + inline explicit LoggerTimingHelper(Logger *l, Logger::LogLevel level, + const char *file, int line, const char *func) + : m_logger(l), + m_logLevel(level), + m_file(file), + m_line(line), + m_function(func) + {} + + void start(const char *msg, ...) +#if defined(Q_CC_GNU) && !defined(__INSURE__) + #if defined(Q_CC_MINGW) && !defined(Q_CC_CLANG) + __attribute__((format(gnu_printf, 2, 3))); + #else + __attribute__((format(printf, 2, 3))); + #endif +#endif + + void start(const QString &msg = QString()); + + ~LoggerTimingHelper(); + +private: + Logger *m_logger; + QTime m_time; + Logger::LogLevel m_logLevel; + const char *m_file; + int m_line; + const char *m_function; + QString m_block; +}; + +DCORE_END_NAMESPACE +#endif // LOGGER_H diff --git a/include/log/RollingFileAppender.h b/include/log/RollingFileAppender.h new file mode 100644 index 0000000..534c572 --- /dev/null +++ b/include/log/RollingFileAppender.h @@ -0,0 +1,75 @@ +// Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef ROLLINGFILEAPPENDER_H +#define ROLLINGFILEAPPENDER_H + +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT RollingFileAppender : public FileAppender +{ + public: + /*! + The enum DatePattern defines constants for date patterns. + \sa setDatePattern(DatePattern) + */ + enum DatePattern + { + /*! The minutely date pattern string is "'.'yyyy-MM-dd-hh-mm". */ + MinutelyRollover = 0, + /*! The hourly date pattern string is "'.'yyyy-MM-dd-hh". */ + HourlyRollover, + /*! The half-daily date pattern string is "'.'yyyy-MM-dd-a". */ + HalfDailyRollover, + /*! The daily date pattern string is "'.'yyyy-MM-dd". */ + DailyRollover, + /*! The weekly date pattern string is "'.'yyyy-ww". */ + WeeklyRollover, + /*! The monthly date pattern string is "'.'yyyy-MM". */ + MonthlyRollover + }; + + RollingFileAppender(const QString &fileName = QString()); + + DatePattern datePattern() const; + void setDatePattern(DatePattern datePattern); + void setDatePattern(const QString &datePattern); + + QString datePatternString() const; + + void setLogFilesLimit(int limit); + int logFilesLimit() const; + + void setLogSizeLimit(int qint64); + qint64 logSizeLimit() const; + + protected: + virtual void append(const QDateTime &time, Logger::LogLevel level, const char *file, int line, + const char *func, const QString &category, const QString &msg); + + private: + void rollOver(); + void computeRollOverTime(); + void computeFrequency(); + void removeOldFiles(); + void setDatePatternString(const QString &datePatternString); + + QString m_datePatternString; + DatePattern m_frequency; + + QDateTime m_rollOverTime; + QString m_rollOverSuffix; + int m_logFilesLimit; + qint64 m_logSizeLimit; + mutable QMutex m_rollingMutex; +}; + +DCORE_END_NAMESPACE + +#endif // ROLLINGFILEAPPENDER_H diff --git a/include/log/dloggerdefs.h b/include/log/dloggerdefs.h new file mode 100644 index 0000000..8395103 --- /dev/null +++ b/include/log/dloggerdefs.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef DLOGGER_DEFINE_H +#define DLOGGER_DEFINE_H + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class Logger; +class CuteMessageLogger; +class LoggerTimingHelper; +LIBDTKCORESHARED_EXPORT Logger *loggerInstance(); +#define logger loggerInstance() + +#define dTrace CuteMessageLogger(loggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO).write +#define dDebug CuteMessageLogger(loggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO).write +#define dInfo CuteMessageLogger(loggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO).write +#define dWarning CuteMessageLogger(loggerInstance(), Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO).write +#define dError CuteMessageLogger(loggerInstance(), Logger::Error, __FILE__, __LINE__, Q_FUNC_INFO).write +#define dFatal CuteMessageLogger(loggerInstance(), Logger::Fatal, __FILE__, __LINE__, Q_FUNC_INFO).write + +#define dCDebug(category) CuteMessageLogger(loggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO, category).write() +#define dCInfo(category) CuteMessageLogger(loggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO, category).write() +#define dCWarning(category) CuteMessageLogger(loggerInstance(), Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO, category).write() +#define dCError(category) CuteMessageLogger(loggerInstance(), Logger::Error, __FILE__, __LINE__, Q_FUNC_INFO, category).write() +#define dCFatal(category) CuteMessageLogger(loggerInstance(), Logger::Fatal, __FILE__, __LINE__, Q_FUNC_INFO, category).write() + +#define dTraceTime LoggerTimingHelper loggerTimingHelper(loggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start +#define dDebugTime LoggerTimingHelper loggerTimingHelper(loggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start +#define dInfoTime LoggerTimingHelper loggerTimingHelper(loggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start + +#define dAssert(cond) ((!(cond)) ? loggerInstance()->writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, #cond) : qt_noop()) +#define dAssertX(cond, msg) ((!(cond)) ? loggerInstance()->writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, msg) : qt_noop()) + +#define dCategory(category) \ + private:\ + Logger* loggerInstance()\ + {\ + static Logger customLoggerInstance(category);\ + return &customLoggerInstance;\ + }\ + +#define dGlobalCategory(category) \ + private:\ + Logger* loggerInstance()\ + {\ + static Logger customLoggerInstance(category);\ + customLoggerInstance.logToGlobalInstance(category, true);\ + return &customLoggerInstance;\ + }\ + +DCORE_END_NAMESPACE + +#endif // DLOGGER_DEFINE_H diff --git a/include/log/win32/OutputDebugAppender.h b/include/log/win32/OutputDebugAppender.h new file mode 100644 index 0000000..73bb115 --- /dev/null +++ b/include/log/win32/OutputDebugAppender.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2010 Karl-Heinz Reichel (khreichel at googlemail dot com) +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef OUTPUTDEBUGAPPENDER_H +#define OUTPUTDEBUGAPPENDER_H + +#include "dtkcore_global.h" +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT OutputDebugAppender : public AbstractStringAppender +{ + protected: + virtual void append(const QDateTime &time, Logger::LogLevel level, const char *file, int line, + const char *func, const QString &category, const QString &msg); +}; + +DCORE_END_NAMESPACE + +#endif // OUTPUTDEBUGAPPENDER_H diff --git a/include/settings/backend/dsettingsdconfigbackend.h b/include/settings/backend/dsettingsdconfigbackend.h new file mode 100644 index 0000000..c3770da --- /dev/null +++ b/include/settings/backend/dsettingsdconfigbackend.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "dsettingsbackend.h" + +DCORE_BEGIN_NAMESPACE + +class DSettingsDConfigBackendPrivate; +class LIBDTKCORESHARED_EXPORT DSettingsDConfigBackend : public Dtk::Core::DSettingsBackend +{ + Q_OBJECT +public: + explicit DSettingsDConfigBackend(const QString &name, const QString &subpath = QString(), QObject *parent = nullptr); + ~DSettingsDConfigBackend() Q_DECL_OVERRIDE; + + virtual QStringList keys() const Q_DECL_OVERRIDE; + virtual QVariant getOption(const QString &key) const Q_DECL_OVERRIDE; + +protected Q_SLOTS: + virtual void doSetOption(const QString &key, const QVariant &value) Q_DECL_OVERRIDE; + virtual void doSync() Q_DECL_OVERRIDE; + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), DSettingsDConfigBackend) +}; + +DCORE_END_NAMESPACE diff --git a/include/settings/backend/gsettingsbackend.h b/include/settings/backend/gsettingsbackend.h new file mode 100644 index 0000000..184ad5e --- /dev/null +++ b/include/settings/backend/gsettingsbackend.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "dsettingsbackend.h" + +DCORE_BEGIN_NAMESPACE + +class GSettingsBackendPrivate; +class LIBDTKCORESHARED_EXPORT GSettingsBackend: public DSettingsBackend +{ + Q_OBJECT +public: + explicit GSettingsBackend(DSettings *settings, QObject *parent = nullptr); + ~GSettingsBackend(); + + virtual QStringList keys() const Q_DECL_OVERRIDE; + virtual QVariant getOption(const QString &key) const Q_DECL_OVERRIDE; + +protected Q_SLOTS: + virtual void doSetOption(const QString &key, const QVariant &value) Q_DECL_OVERRIDE; + virtual void doSync() Q_DECL_OVERRIDE; + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), GSettingsBackend) +}; + +DCORE_END_NAMESPACE diff --git a/include/settings/backend/qsettingbackend.h b/include/settings/backend/qsettingbackend.h new file mode 100644 index 0000000..496838f --- /dev/null +++ b/include/settings/backend/qsettingbackend.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "dsettingsbackend.h" + +DCORE_BEGIN_NAMESPACE + +class QSettingBackendPrivate; +class LIBDTKCORESHARED_EXPORT QSettingBackend : public Dtk::Core::DSettingsBackend +{ + Q_OBJECT +public: + explicit QSettingBackend(const QString &filepath, QObject *parent = 0); + ~QSettingBackend(); + + virtual QStringList keys() const Q_DECL_OVERRIDE; + virtual QVariant getOption(const QString &key) const Q_DECL_OVERRIDE; + +protected Q_SLOTS: + virtual void doSetOption(const QString &key, const QVariant &value) Q_DECL_OVERRIDE; + virtual void doSync() Q_DECL_OVERRIDE; + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), QSettingBackend) +}; + +DCORE_END_NAMESPACE diff --git a/include/settings/dsettings.h b/include/settings/dsettings.h new file mode 100644 index 0000000..d407c13 --- /dev/null +++ b/include/settings/dsettings.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DSettingsBackend; +class DSettingsOption; +class DSettingsGroup; +class DSettingsPrivate; +class LIBDTKCORESHARED_EXPORT DSettings : public QObject +{ + Q_OBJECT +public: + explicit DSettings(QObject *parent = Q_NULLPTR); + ~DSettings(); + + void setBackend(DSettingsBackend *backend = nullptr); + + static QPointer fromJson(const QByteArray &json); + static QPointer fromJsonFile(const QString &filepath); + QJsonObject meta() const; + + QStringList keys() const; + QList> options() const; + QPointer option(const QString &key) const; + QVariant value(const QString &key) const; + + QStringList groupKeys() const; + QList> groups() const; + QPointer group(const QString &key) const; + + QVariant getOption(const QString &key) const; + +Q_SIGNALS: + void valueChanged(const QString &key, const QVariant &value); + +public Q_SLOTS: + //! + //! \brief sync + //! WARNING: sync will block + void sync() ; + + void setOption(const QString &key, const QVariant &value); + void reset() ; + +private: + void parseJson(const QByteArray &json); + void loadValue(); + + QScopedPointer dd_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettings) +}; + +DCORE_END_NAMESPACE diff --git a/include/settings/dsettingsbackend.h b/include/settings/dsettingsbackend.h new file mode 100644 index 0000000..1cf1673 --- /dev/null +++ b/include/settings/dsettingsbackend.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DSettings; +class LIBDTKCORESHARED_EXPORT DSettingsBackend : public QObject +{ + Q_OBJECT +public: + explicit DSettingsBackend(QObject *parent = Q_NULLPTR): QObject(parent) + { + connect(this, &DSettingsBackend::sync, this, &DSettingsBackend::doSync, Qt::QueuedConnection); + connect(this, &DSettingsBackend::setOption, this, &DSettingsBackend::doSetOption, Qt::QueuedConnection); + } + virtual ~DSettingsBackend() {} + + virtual QStringList keys() const = 0; + virtual QVariant getOption(const QString &key) const = 0; + + virtual void doSync() = 0; + +protected: + virtual void doSetOption(const QString &key, const QVariant &value) = 0; + +Q_SIGNALS: + void optionChanged(const QString &key, const QVariant &value); + + // private signals; +Q_SIGNALS: + void sync(); + void setOption(const QString &key, const QVariant &value); +}; + +DCORE_END_NAMESPACE diff --git a/include/settings/dsettingsgroup.h b/include/settings/dsettingsgroup.h new file mode 100644 index 0000000..821d6f5 --- /dev/null +++ b/include/settings/dsettingsgroup.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "dsettingsoption.h" + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DSettingsGroupPrivate; +class LIBDTKCORESHARED_EXPORT DSettingsGroup : public QObject +{ + Q_OBJECT +public: + explicit DSettingsGroup(QObject *parent = Q_NULLPTR); + ~DSettingsGroup(); + + QPointer parentGroup() const; + void setParentGroup(QPointer parentGroup); + + QString key() const; + QString name() const; + bool isHidden() const; + + QPointer childGroup(const QString &groupKey) const; + QPointer option(const QString &key) const; + + QList > childGroups() const; + QList > childOptions() const; + QList > options() const; + + static QPointer fromJson(const QString &prefixKey, const QJsonObject &group); + +private: + void parseJson(const QString &prefixKey, const QJsonObject &group); + + QScopedPointer dd_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettingsGroup) +}; + +typedef QPointer GroupPtr; + +DCORE_END_NAMESPACE diff --git a/include/settings/dsettingsoption.h b/include/settings/dsettingsoption.h new file mode 100644 index 0000000..83226e3 --- /dev/null +++ b/include/settings/dsettingsoption.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DSettingsGroup; +class DSettingsOptionPrivate; +class LIBDTKCORESHARED_EXPORT DSettingsOption : public QObject +{ + Q_OBJECT + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + +public: + explicit DSettingsOption(QObject *parent = Q_NULLPTR); + ~DSettingsOption(); + + QPointer parentGroup() const; + void setParentGroup(QPointer parentGroup); + + QString key() const; + QString name() const; + bool canReset() const; + QVariant defaultValue() const; + QVariant value() const; + QVariant data(const QString &dataType) const; + + QString viewType() const; + bool isHidden() const; + + static QPointer fromJson(const QString &prefixKey, const QJsonObject &json); +Q_SIGNALS: + void valueChanged(QVariant value); + void dataChanged(const QString &dataType, QVariant value); + +public Q_SLOTS: + void setValue(QVariant value); + void setData(const QString &dataType, QVariant value); + +private: + void parseJson(const QString &prefixKey, const QJsonObject &option); + + QScopedPointer dd_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettingsOption) +}; + +typedef QPointer OptionPtr; + +DCORE_END_NAMESPACE diff --git a/include/util/dabstractunitformatter.h b/include/util/dabstractunitformatter.h new file mode 100644 index 0000000..4b8ecc4 --- /dev/null +++ b/include/util/dabstractunitformatter.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DABSTRACTUNITFORMATTER_H +#define DABSTRACTUNITFORMATTER_H + +#include "dtkcore_global.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DAbstractUnitFormatter +{ +public: + DAbstractUnitFormatter(); + ~DAbstractUnitFormatter(); + +protected: + virtual int unitMax() const = 0; + virtual int unitMin() const = 0; + virtual uint unitConvertRate(int unitId) const = 0; + virtual qreal unitValueMax(int unitId) const { return unitConvertRate(unitId) - 1; } + virtual qreal unitValueMin(int unitId) const { Q_UNUSED(unitId); return 1; } + virtual QString unitStr(int unitId) const = 0; + +public: + qreal formatAs(qreal value, int currentUnit, const int targetUnit) const; + QPair format(const qreal value, const int unit) const; + QList> formatAsUnitList(const qreal value, int unit) const; +}; + +DCORE_END_NAMESPACE + +#endif // DABSTRACTUNITFORMATTER_H diff --git a/include/util/dasync.h b/include/util/dasync.h new file mode 100644 index 0000000..957fb90 --- /dev/null +++ b/include/util/dasync.h @@ -0,0 +1,489 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DASYNC_H +#define DASYNC_H +#include + +#include +#include +#include +#include +#include + +#include +#include + +DCORE_BEGIN_NAMESPACE + +#define GUARDED_BY(...) +#define D_THREAD_IN_MAIN() (qApp->instance() && qApp->instance()->thread() == QThread::currentThread()) + +// TODO: 添加 DtkCorePrivate 到 dtkcore_global.h +namespace DtkCorePrivate +{ + // 本类是继承实现的,只有子类方法是安全的,暂不对外提供接口 + template + class DSafeQueue : public QQueue { + public: + inline void enqueue(const T &t) { + QMutexLocker lkc(&m_mtx); + QQueue::enqueue(t); + } + inline T dequeue() { + QMutexLocker lkc(&m_mtx); + return QQueue::dequeue(); + } + inline int size() { + QMutexLocker lkc(&m_mtx); + return QQueue::size(); + } + inline T &head() { + QMutexLocker lkc(&m_mtx); + return QQueue::head(); + } + inline const T &head() const { + QMutexLocker lkc(&m_mtx); + return QQueue::head(); + } + private: + mutable QMutex m_mtx; + }; + + // 内部使用,不对外提供接口 + class MainWorker : public QObject { + Q_OBJECT + std::function m_handle; + std::function m_handleProxy; + + std::function m_handleV; + std::function m_handleVProxy; + + bool m_dasyncDestroyed = false; + char __padding[7]; + public: + void setDAsyncDestroyed() { + m_dasyncDestroyed = true; + } + bool dasyncDestroyed() { + return m_dasyncDestroyed; + } + public: + MainWorker(QObject *parent = nullptr) + : QObject (parent) + { + // Ensure that QApplication is initialized + Q_ASSERT(qApp->instance() && qApp->instance()->thread()); + moveToThread(qApp->instance()->thread()); + + bool isStartInMain = D_THREAD_IN_MAIN(); + + QObject::connect(this, &MainWorker::sigRunInMain, + this, &MainWorker::slotRunInMain, + isStartInMain ? Qt::AutoConnection : Qt::BlockingQueuedConnection); + + QObject::connect(this, &MainWorker::sigRunInMainVoid, + this, &MainWorker::slotRunInMainVoid, + isStartInMain ? Qt::AutoConnection : Qt::BlockingQueuedConnection); + } + + // 1. handle arg is non void + template + typename std::enable_if::value>::type + setHandle(FUNC &&func) { + m_handle = [&] (void *arg) { + DSafeQueue *q = static_cast*>(arg); + while (q && q->size()) { + // 这里是 then 回调真正执行到的地方 + func(q->dequeue()); + } + }; + + m_handleProxy = [this] (void *arg) { + if (m_handle) { + m_handle(arg); + } + }; + } + + // 2. handle arg is void + template + typename std::enable_if::value>::type + setHandle(FUNC &&func) { + m_handleV = [&] (void) { + // 这里是 then 回调真正执行到的地方 + func(); + }; + + m_handleVProxy = [this] (void) { + if (m_handleV) { + m_handleV(); + } + }; + } + Q_SIGNALS: + void sigRunInMain(void *arg); + void sigRunInMainVoid(); + public Q_SLOTS: + void slotRunInMain(void *arg) { + Q_ASSERT(D_THREAD_IN_MAIN()); + if (m_handleProxy && !m_dasyncDestroyed) { + m_handleProxy(arg); + } + } + void slotRunInMainVoid(void) { + Q_ASSERT(D_THREAD_IN_MAIN()); + if (m_handleVProxy && !m_dasyncDestroyed) { + m_handleVProxy(); + } + } + }; +} + +class DAsyncState : public QObject { + Q_OBJECT +public: + explicit DAsyncState(QObject *parent = nullptr) noexcept + : QObject (parent) + { + } + enum AsyncTaskState { + NotReady = 0x00, // initial state + Ready = 0x02, // deffered = false + Running = 0x04, // thread started + Pending = Ready | Running, // condition wait + Cancel = 0x08, // set thread canceled + WaitFinished = 0x10, // wiaitForFinished + Finished = 0x20, // thread exit + Forever = 0x30, // TODO: DAsync::post execute forever + }; + Q_DECLARE_FLAGS(AsyncTaskStatus, AsyncTaskState) +}; + +// Template classes not supported by Q_OBJECT, so class MainWorker is independent +template +class DAsync : public QObject { + + class Helper; + + std::mutex m_mtxIn; + std::condition_variable m_cvIn; + + std::mutex m_mtxForWaitTask; + std::condition_variable m_cvForWaitTask; + + class Guard { + DAsync *m_as; + // 如果 DAsync 已经析构了,工作线程还没结束 + // DAsync 中的有些数据就不能在 guard 的析构里面访问了 + bool m_dasDestructed = false; + public: + bool destructed() { + return m_dasDestructed; + } + void setDestructed() { + m_dasDestructed = true; + } + public: + explicit Guard(DAsync *as) noexcept : m_as (as) + { + m_as->m_status.setFlag(DAsyncState::Ready); + m_as->m_status.setFlag(DAsyncState::Finished, false); // 防止重入 + } + ~Guard() { + if (destructed()) { + return; + } + m_as->m_threadGuard = nullptr; + m_as->m_status.setFlag(DAsyncState::Finished); + m_as->m_status.setFlag(DAsyncState::Ready, false); // 防止重入 + if (m_as->m_status.testFlag(DAsyncState::WaitFinished)) { + m_as->m_cvForWaitTask.notify_one(); + } + setPending(false); + } + void setPending(bool isPending) { + if (!destructed()) { + m_as->m_status.setFlag(DAsyncState::Pending, isPending); + } + } + }; + Guard *m_threadGuard = nullptr; + + /* + * m_QueueIn 的作用是存储 PostData 传进来的数据 + * m_QueueOut 的作用是将 post 处理完的结果暂存起来然后传入到 then 中 + * 在 emitHelper 中调用 post 进来的任务,然后将结果传到主线程中处理 + * 数据传递使用 void * 做转换,对于复合类型避免了使用 qRegisterMetaType + */ + template + struct DataQueueType { DtkCorePrivate::DSafeQueue m_queue; }; + template + struct DataQueueType::value>::type> { }; + using DataInQueue = DataQueueType; + using DataOutQueue = DataQueueType; + // Queue 中处理完的结果经由 m_QueueIn 变量暂存,然后经由 signal、slot 传给 then 中的回调函数做参数 + DataInQueue m_QueueIn; + DataOutQueue m_QueueOut; + + // 存储不同类型的输入函数 + template + struct FuncType { + }; + template + struct FuncType::value>::type, + typename std::enable_if::value>::type> { + std::function cbp; + }; + template + struct FuncType::value>::type, + typename std::enable_if::value>::type> { + std::function cbp; + }; + template + struct FuncType::value>::type, + typename std::enable_if::value>::type> { + std::function cbp; + }; + template + struct FuncType::value>::type, + typename std::enable_if::value>::type> { + std::function cbp; + }; + + std::mutex m_mtxFunc; + FuncType m_func GUARDED_BY(m_mtxFunc); + DAsyncState::AsyncTaskStatus m_status; + +public: + explicit DAsync(QObject *parent = nullptr) noexcept + : QObject (parent) + , m_func ({nullptr}) + , m_status (DAsyncState::NotReady) + { + m_mainWorker = new DtkCorePrivate::MainWorker(); + m_helper = new Helper(this, this); + } + ~DAsync() { + if (m_threadGuard) { + m_threadGuard->setDestructed(); + } + m_status.setFlag(DAsyncState::Cancel); + if (m_status.testFlag(DAsyncState::Pending)) { + m_cvIn.notify_one(); + } + if (m_mainWorker) { + m_mainWorker->setDAsyncDestroyed(); + m_mainWorker->deleteLater(); + m_mainWorker = nullptr; + } + } + +private: + // 1. input void & emit void + template + typename std::enable_if::value && std::is_void::value>::type + emitHelper() { + m_func.cbp(); + Q_EMIT m_mainWorker->sigRunInMainVoid(); + } + // 2. input non void & emit non void + template + typename std::enable_if::value && !std::is_void::value>::type + emitHelper() { + m_QueueOut.m_queue.enqueue(m_func.cbp(m_QueueIn.m_queue.dequeue())); + Q_EMIT m_mainWorker->sigRunInMain(static_cast(&(m_QueueOut.m_queue))); + } + // 3. input non void & emit void + template + typename std::enable_if::value && std::is_void::value>::type + emitHelper() { + m_func.cbp(m_QueueIn.m_queue.dequeue()); + Q_EMIT m_mainWorker->sigRunInMainVoid(); + } + // 4. input void & emit non void + template + typename std::enable_if::value && !std::is_void::value>::type + emitHelper() { + m_QueueOut.m_queue.enqueue(m_func.cbp()); + Q_EMIT m_mainWorker->sigRunInMain(static_cast(&(m_QueueOut.m_queue))); + } + +public: + void startUp() { + if (m_status.testFlag(DAsyncState::Cancel)) { + return; + } + m_helper->start(); + } + void cancelAll() { + m_status.setFlag(DAsyncState::Cancel); + if (m_status.testFlag(DAsyncState::Pending)) { + m_cvIn.notify_one(); + } + } + bool isFinished() { + return m_status.testFlag(DAsyncState::Finished); + } + /* + * 不能在 QTimer 中使用 waitForFinished,防止阻塞主线程 + * 也不能在主线程执行前使用 waitForFinished() + * 它的默认参数为 true,等同于 waitForFinished(false) + + * cancelAll, 如果调用了后者, 会一直阻塞等待任务,直到 + * cancelAll 被调用之后 waitForFinished 才会在任务完成完 + * 成后退出,此时就可以删除DAsync了。最好的管理方式还是采用 + * QObject 的内存托管。主线程中使用,可以采用托管的方式, + * 任务结束只要调用 cancelAll + isFinished 轮询判断就行了, + * DAsync 的工作线程就会在完成后自动退出。 + */ + void waitForFinished(bool cancelAllWorks = true) { + Q_ASSERT(!D_THREAD_IN_MAIN()); + if (cancelAllWorks) { + cancelAll(); + } + if (!m_status.testFlag(DAsyncState::Finished)) { + if (m_status.testFlag(DAsyncState::Pending)) { + m_cvIn.notify_one(); + } + m_status.setFlag(DAsyncState::WaitFinished); + std::unique_lock lck(m_mtxForWaitTask); + m_cvForWaitTask.wait(lck); + } + } + // 输入数据不是 void 类型则依赖于 m_QueueIn + template + typename std::enable_if::value, Helper *>::type + post(FUNC &&func) { + m_func.cbp = std::forward(func); + if (m_postProxy) { + return m_helper; + } + m_postProxy = [this] () { + std::thread thread([this] { + if (m_status.testFlag(DAsyncState::Cancel)) { + return; + } + Guard guard(this); + m_threadGuard = &guard; + + std::unique_lock lck(m_mtxIn); + while (true) { + while (!m_status.testFlag(DAsyncState::Ready) || !m_QueueIn.m_queue.size()) { + guard.setPending(true); + // 定时查询 flag,防止睡死的情况发生 + m_cvIn.wait_for(lck, std::chrono::milliseconds(200)); + if (guard.destructed() || m_status.testFlag(DAsyncState::Cancel)) { + return; + } + } + guard.setPending(false); + + while (m_func.cbp && m_QueueIn.m_queue.size()) { + emitHelper(); + } + } + }); + thread.detach(); + }; + + return m_helper; + } + + template + typename std::enable_if::value, Helper *>::type + post(FUNC &&func) { + { + std::lock_guard lckFunc(m_mtxFunc); + m_func.cbp = std::forward(func); + } + if (m_postProxy) { + return m_helper; + } + m_postProxy = [this] () { + std::thread thread([this] { + if (m_status.testFlag(DAsyncState::Cancel)) { + return; + } + Guard guard(this); + m_threadGuard = &guard; + + std::unique_lock lck(m_mtxIn); + while (true) { + if (!m_status.testFlag(DAsyncState::Ready)) { + guard.setPending(true); + // 定时查询 flag,防止睡死的情况发生 + m_cvIn.wait_for(lck, std::chrono::milliseconds(200)); + if (guard.destructed() || m_status.testFlag(DAsyncState::Cancel)){ + return; + } + } + guard.setPending(false); + + if (m_func.cbp) { + std::lock_guard lckFunc(m_mtxFunc); + emitHelper(); + m_func.cbp = nullptr; // reset + } + } + }); + thread.detach(); + }; + + return m_helper; + } + + // only support DAsync + template + typename std::enable_if::value>::type + postData(const InputType &data) { + if (Q_UNLIKELY(!m_status.testFlag(DAsyncState::Cancel))) { + m_QueueIn.m_queue.enqueue(data); + if (m_status.testFlag(DAsyncState::Pending)) { + m_cvIn.notify_one(); + } + } + } + +private: + std::function m_postProxy; + class Helper : public QObject { + DAsync *m_async; + public: + explicit Helper(DAsync *async, QObject *parent = nullptr) noexcept + : QObject (parent) + , m_async (async) + { + } + + template + Helper *then(FUNC &&func) { + m_async->m_mainWorker->template setHandle(std::forward(func)); + return this; + } + // 仅启动,非阻塞 + void start(bool immediately = true) { + if (m_async->m_postProxy) { + m_async->m_postProxy(); + } + if (!immediately) { + m_async->m_status.setFlag(DAsyncState::Ready, false); + } else { + m_async->m_status.setFlag(DAsyncState::Ready); + if (m_async->m_status.testFlag(DAsyncState::Pending)) { + m_async->m_cvIn.notify_one(); + } + } + } + }; + + Helper *m_helper = nullptr; + DtkCorePrivate::MainWorker *m_mainWorker = nullptr; +}; + +DCORE_END_NAMESPACE +#endif //DASYNC_H diff --git a/include/util/ddbusextended.h b/include/util/ddbusextended.h new file mode 100644 index 0000000..9ca5cb7 --- /dev/null +++ b/include/util/ddbusextended.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2015 Jolla Ltd. +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifndef QT_DBUS_EXTENDED_H +#define QT_DBUS_EXTENDED_H + +#if defined(QT_DBUS_EXTENDED_LIBRARY) +# define QT_DBUS_EXTENDED_EXPORT Q_DECL_EXPORT +#else +# define QT_DBUS_EXTENDED_EXPORT Q_DECL_IMPORT +#endif + +#endif /* QT_DBUS_EXTENDED_H */ diff --git a/include/util/ddbusextendedabstractinterface.h b/include/util/ddbusextendedabstractinterface.h new file mode 100644 index 0000000..af72b14 --- /dev/null +++ b/include/util/ddbusextendedabstractinterface.h @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: 2015 Jolla Ltd. +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifndef DBUSEXTENDEDABSTRACTINTERFACE_H +#define DBUSEXTENDEDABSTRACTINTERFACE_H + +#include + +#include +#include + +class QDBusPendingCallWatcher; +class DDBusExtendedPendingCallWatcher; + +class QT_DBUS_EXTENDED_EXPORT DDBusExtendedAbstractInterface: public QDBusAbstractInterface +{ + Q_OBJECT + +public: + virtual ~DDBusExtendedAbstractInterface(); + + Q_PROPERTY(bool sync READ sync WRITE setSync) + inline bool sync() const { return m_sync; } + void setSync(bool sync); + void setSync(bool sync, bool autoStart); + + Q_PROPERTY(bool useCache READ useCache WRITE setUseCache) + inline bool useCache() const { return m_useCache; } + inline void setUseCache(bool useCache) { m_useCache = useCache; } + + void getAllProperties(); + inline QDBusError lastExtendedError() const { return m_lastExtendedError; } + +public Q_SLOTS: + void startServiceProcess(); + +protected: + DDBusExtendedAbstractInterface(const QString &service, + const QString &path, + const char *interface, + const QDBusConnection &connection, + QObject *parent); + + void connectNotify(const QMetaMethod &signal); + void disconnectNotify(const QMetaMethod &signal); + QVariant internalPropGet(const char *propname, void *propertyPtr); + void internalPropSet(const char *propname, const QVariant &value, void *propertyPtr); + +Q_SIGNALS: + void serviceValidChanged(const bool valid) const; + void serviceStartFinished(const quint32 ret) const; + void propertyChanged(const QString &propertyName, const QVariant &value); + void propertyInvalidated(const QString &propertyName); + void asyncPropertyFinished(const QString &propertyName); + void asyncSetPropertyFinished(const QString &propertyName); + void asyncGetAllPropertiesFinished(); + +private Q_SLOTS: + void onPropertiesChanged(const QString& interfaceName, + const QVariantMap& changedProperties, + const QStringList& invalidatedProperties); + void onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + void onAsyncPropertyFinished(QDBusPendingCallWatcher *w); + void onAsyncSetPropertyFinished(QDBusPendingCallWatcher *w); + void onAsyncGetAllPropertiesFinished(QDBusPendingCallWatcher *watcher); + void onStartServiceProcessFinished(QDBusPendingCallWatcher *w); + +private: + QVariant asyncProperty(const QString &propertyName); + void asyncSetProperty(const QString &propertyName, const QVariant &value); + static QVariant demarshall(const QString &interface, const QMetaProperty &metaProperty, const QVariant &value, QDBusError *error); + + bool m_sync; + bool m_useCache; + QDBusPendingCallWatcher *m_getAllPendingCallWatcher; + QDBusError m_lastExtendedError; + QString m_dbusOwner; + bool m_propertiesChangedConnected; +}; + +#endif /* DBUSEXTENDEDABSTRACTINTERFACE_H */ diff --git a/include/util/ddbusinterface.h b/include/util/ddbusinterface.h new file mode 100644 index 0000000..d0fe593 --- /dev/null +++ b/include/util/ddbusinterface.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once +#include "dtkcore_global.h" +#include + +DCORE_BEGIN_NAMESPACE + +class DDBusInterfacePrivate; + +class DDBusInterface : public QDBusAbstractInterface +{ + Q_OBJECT + +public: + explicit DDBusInterface(const QString &service, + const QString &path, + const QString &interface = QString(), + const QDBusConnection &connection = QDBusConnection::sessionBus(), + QObject *parent = nullptr); + virtual ~DDBusInterface() override; + + bool serviceValid() const; + QString suffix() const; + void setSuffix(const QString &suffix); + + QVariant property(const char *propName); + void setProperty(const char *propName, const QVariant &value); + +Q_SIGNALS: + void serviceValidChanged(const bool valid) const; + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(DDBusInterface) + Q_DISABLE_COPY(DDBusInterface) +}; +DCORE_END_NAMESPACE diff --git a/include/util/ddbussender.h b/include/util/ddbussender.h new file mode 100644 index 0000000..b6410d9 --- /dev/null +++ b/include/util/ddbussender.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DDBUSSENDER_H +#define DDBUSSENDER_H + +#include "dtkcore_global.h" + +#include +#include +#include +#include + +#include + +class LIBDTKCORESHARED_EXPORT DDBusData +{ +public: + DDBusData(); + + QString service; + QString path; + QString interface; + QString queryName; + QDBusConnection connection; +}; + +class LIBDTKCORESHARED_EXPORT DDBusCaller +{ + friend class DDBusSender; + +public: + QDBusPendingCall call(); + + template + DDBusCaller arg(const T &argument); + +private: + explicit DDBusCaller(const QString &method, std::shared_ptr data); + +private: + std::shared_ptr m_dbusData; + QString m_methodName; + QVariantList m_arguments; +}; + +template +DDBusCaller DDBusCaller::arg(const T &argument) +{ + m_arguments << QVariant::fromValue(argument); + + return *this; +} + +class LIBDTKCORESHARED_EXPORT DDBusProperty +{ + friend class DDBusSender; + +public: + QDBusPendingCall get(); + template + QDBusPendingCall set(const T &value); + +private: + explicit DDBusProperty(const QString &property, std::shared_ptr data); + +private: + std::shared_ptr m_dbusData; + QString m_propertyName; +}; + +template +QDBusPendingCall DDBusProperty::set(const T &value) +{ + QDBusInterface iface(m_dbusData->service, m_dbusData->path, QStringLiteral("org.freedesktop.DBus.Properties"), m_dbusData->connection); + + const QVariantList args = { QVariant::fromValue(m_dbusData->interface), QVariant::fromValue(m_propertyName), QVariant::fromValue(QDBusVariant(value)) }; + + return iface.asyncCallWithArgumentList(QStringLiteral("Set"), args); +} + +class LIBDTKCORESHARED_EXPORT DDBusSender +{ +public: + explicit DDBusSender(); + + DDBusSender service(const QString &service); + DDBusSender interface(const QString &interface); + DDBusSender path(const QString &path); + DDBusCaller method(const QString &method); + DDBusProperty property(const QString &property); + +private: + DDBusSender type(const QDBusConnection::BusType busType); + +private: + std::shared_ptr m_dbusData; +}; + +#endif // DDBUSSENDER_H diff --git a/include/util/ddisksizeformatter.h b/include/util/ddisksizeformatter.h new file mode 100644 index 0000000..7dfd8ba --- /dev/null +++ b/include/util/ddisksizeformatter.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DISKSIZEFORMATTER_H +#define DISKSIZEFORMATTER_H + +#include "dabstractunitformatter.h" + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DDiskSizeFormatter : public DAbstractUnitFormatter +{ +public: + DDiskSizeFormatter(); + + enum DiskUnits + { + B, + K, + M, + G, + T, + }; + + QString unitStr(int unitId) const override; + + DDiskSizeFormatter rate(int rate); + +protected: + int unitMin() const override { return B; } + int unitMax() const override { return T; } + uint unitConvertRate(int unitId) const override { Q_UNUSED(unitId); return m_rate; } + +private: + int m_rate = 1000; +}; + +DCORE_END_NAMESPACE + +#endif // DISKSIZEFORMATTER_H diff --git a/include/util/dexportedinterface.h b/include/util/dexportedinterface.h new file mode 100644 index 0000000..c067aac --- /dev/null +++ b/include/util/dexportedinterface.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DEXPORTEDINTERFACE_H +#define DEXPORTEDINTERFACE_H + +#include +#include + +#include + +#include + +DCORE_BEGIN_NAMESPACE + +namespace DUtil { +class DExportedInterfacePrivate; +class LIBDTKCORESHARED_EXPORT DExportedInterface : public QObject, public DObject +{ + Q_OBJECT +public: + explicit DExportedInterface(QObject *parent = nullptr); + ~DExportedInterface(); + + void registerAction(const QString &action, const QString &description, const std::function handler = nullptr); + virtual QVariant invoke(const QString &action, const QString ¶meters) const; +private: + D_DECLARE_PRIVATE(DExportedInterface) +}; +} + +DCORE_END_NAMESPACE + +#endif diff --git a/include/util/dfileservices.h b/include/util/dfileservices.h new file mode 100644 index 0000000..a8fdcf1 --- /dev/null +++ b/include/util/dfileservices.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILESERVICES_H +#define DFILESERVICES_H + +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DFileServices +{ +public: + static bool showFolder(QString localFilePath, const QString &startupId = QString()); + static bool showFolders(const QList localFilePaths, const QString &startupId = QString()); + static bool showFolder(QUrl url, const QString &startupId = QString()); + static bool showFolders(const QList urls, const QString &startupId = QString()); + + static bool showFileItemPropertie(QString localFilePath, const QString &startupId = QString()); + static bool showFileItemProperties(const QList localFilePaths, const QString &startupId = QString()); + static bool showFileItemPropertie(QUrl url, const QString &startupId = QString()); + static bool showFileItemProperties(const QList urls, const QString &startupId = QString()); + + static bool showFileItem(QString localFilePath, const QString &startupId = QString()); + static bool showFileItems(const QList localFilePaths, const QString &startupId = QString()); + static bool showFileItem(QUrl url, const QString &startupId = QString()); + static bool showFileItems(const QList urls, const QString &startupId = QString()); + + static bool trash(QString localFilePath); + static bool trash(const QList localFilePaths); + static bool trash(QUrl urlstartupId); + static bool trash(const QList urls); + + static QString errorMessage(); +}; + +DCORE_END_NAMESPACE + +#endif // DFILESERVICES_H diff --git a/include/util/dnotifysender.h b/include/util/dnotifysender.h new file mode 100644 index 0000000..7ab062c --- /dev/null +++ b/include/util/dnotifysender.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DNOTIFYSENDER_H +#define DNOTIFYSENDER_H + +#include "dtkcore_global.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +namespace DUtil { +struct DNotifyData; +class LIBDTKCORESHARED_EXPORT DNotifySender { +public: + DNotifySender(const QString &summary); + DNotifySender appName(const QString &appName = QString()); + DNotifySender appIcon(const QString &appIcon = QString()); + DNotifySender appBody(const QString &appBody = QString()); + DNotifySender replaceId(const uint replaceId = 0); + DNotifySender timeOut(const int timeOut = -1); + DNotifySender actions(const QStringList &actions = QStringList()); + DNotifySender hints(const QVariantMap &hints = QVariantMap()); + QDBusPendingCall call(); + +private: + std::shared_ptr m_dbusData; +}; +} // namespace DUtil + +DCORE_END_NAMESPACE + +#endif // DNOTIFYSENDER_H diff --git a/include/util/dpinyin.h b/include/util/dpinyin.h new file mode 100644 index 0000000..a98dbb2 --- /dev/null +++ b/include/util/dpinyin.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DPINYIN_H +#define DPINYIN_H + +#include + +#include + +DCORE_BEGIN_NAMESPACE + +QString LIBDTKCORESHARED_EXPORT Chinese2Pinyin(const QString& words); + +DCORE_END_NAMESPACE + +#endif // DPINYIN_H diff --git a/include/util/drecentmanager.h b/include/util/drecentmanager.h new file mode 100644 index 0000000..27c0b02 --- /dev/null +++ b/include/util/drecentmanager.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DRECENTMANAGER_H +#define DRECENTMANAGER_H + +#include "dtkcore_global.h" +#include + +DCORE_BEGIN_NAMESPACE + +struct LIBDTKCORESHARED_EXPORT DRecentData +{ + QString appName; + QString appExec; + QString mimeType; +}; + +class LIBDTKCORESHARED_EXPORT DRecentManager +{ +public: + static bool addItem(const QString &uri, DRecentData &data); + static void removeItem(const QString &target); + static void removeItems(const QStringList &list); +}; + +DCORE_END_NAMESPACE + +#endif // DRECENTMANAGER_H diff --git a/include/util/dthreadutils.h b/include/util/dthreadutils.h new file mode 100644 index 0000000..2b6d212 --- /dev/null +++ b/include/util/dthreadutils.h @@ -0,0 +1,158 @@ +// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DTHREADUTILS_H +#define DTHREADUTILS_H + +#include +#include +#include +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +namespace DThreadUtil { +typedef std::function FunctionType; + +class LIBDTKCORESHARED_EXPORT FunctionCallProxy : public QObject +{ + Q_OBJECT +public: + explicit FunctionCallProxy(QThread *thread); + + static void proxyCall(QSemaphore *s, QThread *thread, QObject *target, FunctionType fun); + +Q_SIGNALS: + void callInLiveThread(QSemaphore *s, QPointer target, FunctionType *func); +}; + +template +class LIBDTKCORESHARED_EXPORT _TMP +{ +public: + inline static ReturnType runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function fun) + { + ReturnType result; + FunctionType proxyFun = [&result, &fun] () { + result = fun(); + }; + + FunctionCallProxy::proxyCall(s, thread, target, proxyFun); + return result; + } + + template + inline static typename std::enable_if::value, ReturnType>::type + runInThread(QSemaphore *s, QThread *thread, T *, std::function fun) + { + return runInThread(s, thread, static_cast(nullptr), fun); + } +}; +template <> +class LIBDTKCORESHARED_EXPORT _TMP +{ +public: + inline static void runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function fun) + { + FunctionCallProxy::proxyCall(s, thread, target, fun); + } + + template + inline static typename std::enable_if::value, void>::type + runInThread(QSemaphore *s, QThread *thread, T *, std::function fun) + { + return runInThread(s, thread, static_cast(nullptr), fun); + } +}; + +template +inline auto runInThread(QSemaphore *s, QThread *thread, QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + return _TMP::runInThread(s, thread, target, std::bind(fun, std::forward(args)...)); +} +template +inline auto runInThread(QSemaphore *s, QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + return runInThread(s, thread, nullptr, fun, std::forward(args)...); +} +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInThread(QSemaphore *s, QThread *thread, QObject *target, typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + return _TMP::ReturnType>::runInThread(s, thread, target, std::bind(fun, obj, std::forward(args)...)); +} +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInThread(QSemaphore *s, QThread *thread, typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + return _TMP::ReturnType>::runInThread(s, thread, obj, std::bind(fun, obj, std::forward(args)...)); +} + +template +inline auto runInThread(QThread *thread, QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + QSemaphore s; + + return runInThread(&s, thread, target, fun, std::forward(args)...); +} +template +inline auto runInThread(QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + return runInThread(thread, nullptr, fun, std::forward(args)...); +} +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInThread(QThread *thread, T *target, typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + QSemaphore s; + + return runInThread(&s, thread, target, obj, fun, std::forward(args)...); +} + +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInThread(QThread *thread, typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + return runInThread(thread, obj, obj, fun, std::forward(args)...); +} + +template +inline auto runInMainThread(QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + if (!QCoreApplication::instance()) { + return fun(std::forward(args)...); + } + + return runInThread(QCoreApplication::instance()->thread(), target, fun, std::forward(args)...); +} +template +inline auto runInMainThread(Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + return runInMainThread(nullptr, fun, std::forward(args)...); +} + +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInMainThread(T *target, typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + if (!QCoreApplication::instance()) { + return (obj->*fun)(std::forward(args)...); + } + + return runInThread(QCoreApplication::instance()->thread(), target, obj, fun, std::forward(args)...); +} +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInMainThread(typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + return runInMainThread(obj, obj, fun, std::forward(args)...); +} +} + +DCORE_END_NAMESPACE + +#endif // DTHREADUTILS_H diff --git a/include/util/dtimedloop.h b/include/util/dtimedloop.h new file mode 100644 index 0000000..d25782a --- /dev/null +++ b/include/util/dtimedloop.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DTIMEDLOOP_H +#define DTIMEDLOOP_H +#include +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class DObject; +class DTimedLoopPrivate; +class DTimedLoop : public QEventLoop, public DObject { + Q_OBJECT +public: + explicit DTimedLoop() noexcept; + explicit DTimedLoop(QObject *parent) noexcept; + + ~DTimedLoop(); + + // 如果是 isRunning 则返回从开始到现在的 exec 执行时间,否则返回上次运行的时间 + int runningTime(); + void setTimeDump(bool flag = true); + + void exit(int returnCode = 0); + + // 方式1:不传定时时间,如果不退出就一直执行,配合 exit 使用 + // 方式2:传入durationMs 参数的是定时执行的,也能调用 exit 提前退出 + // 如果传入了 executionName 就会为本次执行设置一个名字,会输出到 log + // 在执行结束将会打印 exec 的执行时间,可以用 setTimeDump 控制其是否打印 + int exec(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents); + int exec(int durationMs, QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents); + int exec(const QString &executionName, QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents); + int exec(int durationMs, const QString &executionName, QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents); + +private: + Q_DISABLE_COPY(DTimedLoop) + D_DECLARE_PRIVATE(DTimedLoop) +}; + +DCORE_END_NAMESPACE + +#endif // DTIMEDLOOP_H diff --git a/include/util/dtimeunitformatter.h b/include/util/dtimeunitformatter.h new file mode 100644 index 0000000..111bb86 --- /dev/null +++ b/include/util/dtimeunitformatter.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DTIMEUNITFORMATTER_H +#define DTIMEUNITFORMATTER_H + +#include "dtkcore_global.h" +#include "dabstractunitformatter.h" + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DTimeUnitFormatter : public DAbstractUnitFormatter +{ +public: + DTimeUnitFormatter(); + + enum TimeUnits + { + Seconds, + Minute, + Hour, + Day, + }; + + QString unitStr(int unitId) const override; + +protected: + int unitMax() const override { return Day; } + int unitMin() const override { return Seconds; } + uint unitConvertRate(int unitId) const override; +}; + +DCORE_END_NAMESPACE + +#endif // DTIMEUNITFORMATTER_H diff --git a/include/util/dutil.h b/include/util/dutil.h new file mode 100644 index 0000000..7462fcc --- /dev/null +++ b/include/util/dutil.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include + +namespace DUtil +{ + +template +inline void TimerSingleShot(int msec, Func1 slot) +{ +#if QT_VERSION >= 0x050500 + QTimer::singleShot(msec, slot); +#else + QTimer *timer = new QTimer; + timer->setSingleShot(true); + timer->setInterval(msec); + timer->moveToThread(qApp->thread()); + QObject::connect(timer, &QTimer::timeout, slot); + QObject::connect(timer, &QTimer::timeout, timer, &QTimer::deleteLater); + if (QThread::currentThread() == qApp->thread()) { timer->start(); } + else { QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection); } +#endif +} + +template +void SecureErase(T *p, size_t size) +{ + memset(p, 0, size); +} + +template +void SecureErase(T &obj) +{ + for (typename T::iterator i = obj.begin(); i != obj.end(); ++i) { + *i = 0; + } + obj.clear(); +} + +} diff --git a/include/util/dvtablehook.h b/include/util/dvtablehook.h new file mode 100644 index 0000000..79be2f7 --- /dev/null +++ b/include/util/dvtablehook.h @@ -0,0 +1,338 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DVTABLEHOOK_H +#define DVTABLEHOOK_H + +#include + +#include +#include +#include + +#include +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DVtableHook +{ +public: + static inline quintptr toQuintptr(const void *ptr) + { + return *(quintptr*)ptr; + } + + static inline int getVtableSize(quintptr **obj) + { + quintptr *begin = *obj; + while(*begin) ++begin; + return begin - *obj; + } + + static inline quintptr *getVtableOfObject(const void *obj) + { + return *(quintptr**)obj; + } + + template + static quintptr *getVtableOfClass() + { + QByteArray vtable_symbol(typeid(T).name()); + vtable_symbol.prepend("_ZTV"); + + quintptr *vfptr_t1 = reinterpret_cast(resolve(vtable_symbol.constData())); + + if (!vfptr_t1) + return nullptr; + + // symbol address + 2 * sizeof(quintptr) = virtal table start address + return vfptr_t1 + 2; + } + + static int getDestructFunIndex(quintptr **obj, std::function destoryObjFun); + static constexpr const QObject *getQObject(...) { return nullptr;} + static constexpr const QObject *getQObject(const QObject *obj) { return obj;} + static void autoCleanVtable(const void *obj); + static bool ensureVtable(const void *obj, std::function destoryObjFun); + static bool hasVtable(const void *obj); + static void resetVtable(const void *obj); + static quintptr resetVfptrFun(const void *obj, quintptr functionOffset); + static quintptr originalFun(const void *obj, quintptr functionOffset); + static bool forceWriteMemory(void *adr, const void *data, size_t length); + static QFunctionPointer resolve(const char *symbol); + + template class OverrideDestruct : public T { ~OverrideDestruct() override;}; + template struct CheckCompatibleArguments { enum { value = false }; }; + template struct CheckCompatibleArguments { enum { value = true }; }; + + template + static bool overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, quintptr *vfptr_t2, Fun2 fun2, bool forceWrite) + { + typedef QtPrivate::FunctionPointer FunInfo1; + typedef QtPrivate::FunctionPointer FunInfo2; + + //compilation error if the arguments does not match. + Q_STATIC_ASSERT_X((CheckCompatibleArguments::value), + "Function1 and Function2 arguments are not compatible."); + Q_STATIC_ASSERT_X((CheckCompatibleArguments, QtPrivate::List>::value), + "Function1 and Function2 return type are not compatible.."); + + //! ({code}) in the form of a code is to eliminate - Wstrict - aliasing build warnings + quintptr fun1_offset = toQuintptr(&fun1); + quintptr fun2_offset = toQuintptr(&fun2); + + if (fun1_offset < 0 || fun1_offset > UINT_LEAST16_MAX) + return false; + + quintptr *vfun = vfptr_t1 + fun1_offset / sizeof(quintptr); + + // if the fun2 is not virtual function + if (fun2_offset <= UINT_LEAST16_MAX) { + fun2_offset = *(vfptr_t2 + fun2_offset / sizeof(quintptr)); + } + + if (forceWrite) + return forceWriteMemory(vfun, &fun2_offset, sizeof(fun2_offset)); + + *vfun = fun2_offset; + + return true; + } + + template + static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer::Object *t1, Fun1 fun1, + const typename QtPrivate::FunctionPointer::Object *t2, Fun2 fun2) + { + typedef QtPrivate::FunctionPointer FunInfo1; + // 检查析构函数是否为虚 + class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;}; + + if (!ensureVtable((void*)t1, std::bind(&_destory_helper, t1))) { + return false; + } + + quintptr *vfptr_t1 = getVtableOfObject(t1); + quintptr *vfptr_t2 = getVtableOfObject(t2); + + bool ok = overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, false); + + if (!ok) { + // 恢复旧环境 + resetVtable(t1); + } + + return ok; + } + + template + static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer::Object *t2, Fun2 fun2) + { + quintptr *vfptr_t1 = getVtableOfClass(); + + if (!vfptr_t1) { + abort(); + } + + quintptr *vfptr_t2 = getVtableOfObject(t2); + + return overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, true); + } + + template + static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer::Object *t2, Fun2 fun2) + { + typedef QtPrivate::FunctionPointer FunInfo1; + return overrideVfptrFun(fun1, t2, fun2); + } + + template struct FunctionPointer { }; + template struct FunctionPointer + { + typedef QtPrivate::List Arguments; + }; + template struct FunctionPointer + { + typedef QtPrivate::List Arguments; + }; + template + static typename std::enable_if::ArgumentCount >= 0, bool>::type + overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite) + { + typedef QtPrivate::FunctionPointer FunInfo1; + typedef QtPrivate::FunctionPointer FunInfo2; + + Q_STATIC_ASSERT(!FunInfo2::IsPointerToMemberFunction); + //compilation error if the arguments does not match. + Q_STATIC_ASSERT_X((CheckCompatibleArguments::Arguments, typename FunInfo2::Arguments>::value), + "Function1 and Function2 arguments are not compatible."); + Q_STATIC_ASSERT_X((CheckCompatibleArguments, QtPrivate::List>::value), + "Function1 and Function2 return type are not compatible.."); + + //! ({code}) in the form of a code is to eliminate - Wstrict - aliasing build warnings + quintptr fun1_offset = toQuintptr(&fun1); + quintptr fun2_offset = toQuintptr(&fun2); + + if (fun1_offset < 0 || fun1_offset > UINT_LEAST16_MAX) + return false; + + quintptr *vfun = vfptr_t1 + fun1_offset / sizeof(quintptr); + + if (forceWrite) + return forceWriteMemory(vfun, &fun2_offset, sizeof(fun2_offset)); + + *vfun = fun2_offset; + + return true; + } + + template struct StdFunWrap {}; + template + struct StdFunWrap { + typedef std::function StdFunType; + static inline StdFunType fun(StdFunType f, bool check = true) { + static StdFunType fun = f; + static bool initialized = false; + if (initialized && check) { + qWarning("The StdFunWrap is dirty! Don't use std::bind(use lambda functions)."); + } + initialized = true; + return fun; + } + static Ret call(Obj *o, Args... args) { + return fun(call, false)(o, std::forward(args)...); + } + }; + template + struct StdFunWrap : StdFunWrap{}; + + template + static inline typename std::enable_if::ArgumentCount == -1, bool>::type + overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite) + { + typedef QtPrivate::FunctionPointer FunInfo1; + const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount::Arguments>::Value; + + Q_STATIC_ASSERT_X((FunctorArgumentCount >= 0), + "Function1 and Function2 arguments are not compatible."); + const int Fun2ArgumentCount = (FunctorArgumentCount >= 0) ? FunctorArgumentCount : 0; + typedef typename QtPrivate::FunctorReturnType::Arguments, Fun2ArgumentCount>::Value>::Value Fun2ReturnType; + + Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible::value), + "Function1 and Function2 return type are not compatible."); + + StdFunWrap::fun(fun2); + return overrideVfptrFun(vfptr_t1, fun1, StdFunWrap::call, forceWrite); + } + + /*! + * \fn template static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer::Object *t1, Fun1 fun1, Fun2 fun2) + * + * \note 重载多继承类中的多个虚函数时,fun1务必标记成一个类名的函数。否则可能出现内存泄露的情况 + * \note 例如 class A 继承于 B,C,D,当需要重载B中的foo1,C中的foo2时,以下函数的fun1需要统一标记为&A::foo1和&A::foo2 + * \note 因为如果分开写为&B::foo1和&C::foo2的话,指针转换内部会记录多张虚表,重载多份析构函数,可能导致最开始的部分无法正常析构! + */ + template + static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer::Object *t1, Fun1 fun1, Fun2 fun2) + { + typedef QtPrivate::FunctionPointer FunInfo1; + // 检查析构函数是否为虚 + class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;}; + + if (!ensureVtable((void*)t1, std::bind(&_destory_helper, t1))) { + return false; + } + + bool ok = overrideVfptrFun(getVtableOfObject(t1), fun1, fun2, false); + + if (!ok) { + // 恢复旧环境 + resetVtable(t1); + } + + return true; + } + + template + static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2) + { + quintptr *vfptr_t1 = getVtableOfClass(); + + if (!vfptr_t1) { + abort(); + } + + return overrideVfptrFun(vfptr_t1, fun1, fun2, true); + } + + template + static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2) + { + typedef QtPrivate::FunctionPointer FunInfo1; + return overrideVfptrFun(fun1, fun2); + } + + template + static bool resetVfptrFun(const typename QtPrivate::FunctionPointer::Object *obj, Fun1 fun) + { + return resetVfptrFun((void*)obj, toQuintptr(&fun)) > 0; + } + + template + static Fun originalFun(const typename QtPrivate::FunctionPointer::Object *obj, Fun fun) + { + quintptr o_fun = originalFun((void*)obj, toQuintptr(&fun)); + + return *reinterpret_cast(o_fun); + } + + template + static typename QtPrivate::FunctionPointer::ReturnType + callOriginalFun(typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) + { + quintptr fun_offset = toQuintptr(&fun); + + class _ResetVFun + { + public: + ~_ResetVFun() { + *(vfptr + offset / sizeof(quintptr)) = oldFun; + } + quintptr *vfptr = nullptr; + quint16 offset = 0; + quintptr oldFun = 0; + }; + + _ResetVFun rvf; + + rvf.vfptr = *(quintptr**)(obj); + rvf.offset = fun_offset; + rvf.oldFun = DVtableHook::resetVfptrFun((void*)obj, fun_offset); + + if (!rvf.oldFun) { + qWarning() << "Reset the function failed, object:" << obj; + abort(); + } + + // call + return (obj->*fun)(std::forward(args)...); + } + +private: + static bool copyVtable(quintptr **obj); + static bool clearGhostVtable(const void *obj); + + template + static void _destory_helper(const T *obj) { + delete obj; + } + + static QMap objToOriginalVfptr; + static QMap objToGhostVfptr; + static QMap objDestructFun; +}; + +DCORE_END_NAMESPACE + +#endif // DVTABLEHOOK_H diff --git a/misc/DtkConfig.cmake.in b/misc/DtkConfig.cmake.in new file mode 100644 index 0000000..9d3df4e --- /dev/null +++ b/misc/DtkConfig.cmake.in @@ -0,0 +1,14 @@ +@PACKAGE_INIT@ + +set_and_check(DtkCore_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIR@") +set_and_check(DtkCore_LIBRARY_DIRS "@PACKAGE_LIBRARY_INSTALL_DIR@") +set(DtkCore_TOOL_DIRS "@PACKAGE_TOOL_INSTALL_DIR@") +set(DtkCore_LIBRARIES dtkcore) + +include_directories("${DtkCore_INCLUDE_DIRS}") + +check_required_components(DtkCore) + +# Keep deprecated variables for compatibility +set(DTKCORE_INCLUDE_DIRS ${DtkCore_INCLUDE_DIRS}) +set(DTKCORE_TOOL_DIRS ${DtkCore_TOOL_DIRS}) diff --git a/misc/dtkcore.pc.in b/misc/dtkcore.pc.in new file mode 100644 index 0000000..58763ca --- /dev/null +++ b/misc/dtkcore.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=@LIBRARY_INSTALL_DIR@ +includedir=@INCLUDE_INSTALL_DIR@ + +Name: dtkcore +Description: Deepin Tool Kit dtkcore header files +Version: @CMAKE_PROJECT_VERSION@ +Libs: -L${libdir} -ldtkcore +Cflags: -I${includedir} diff --git a/misc/qt_lib_dtkcore.pri.in b/misc/qt_lib_dtkcore.pri.in new file mode 100644 index 0000000..44afcfa --- /dev/null +++ b/misc/qt_lib_dtkcore.pri.in @@ -0,0 +1,14 @@ +QT.dtkcore.VERSION = @CMAKE_PROJECT_VERSION@ +QT.dtkcore.MAJOR_VERSION = @PROJECT_VERSION_MAJOR@ +QT.dtkcore.MINOR_VERSION = @PROJECT_VERSION_MINOR@ +QT.dtkcore.PATCH_VERSION = @PROJECT_VERSION_PATCH@ +QT.dtkcore.name = dtkcore +QT.dtkcore.module = dtkcore +QT.dtkcore.tools = @TOOL_INSTALL_DIR@ +QT.dtkcore.libs = @LIBRARY_INSTALL_DIR@ +QT.dtkcore.includes = @INCLUDE_INSTALL_DIR@ +QT.dtkcore.frameworks = +QT.dtkcore.depends = core dbus xml +QT.dtkcore.module_config = v2 ltcg +QT.dtkcore.DEFINES = +QT_MODULES += diff --git a/rpm/dtkcore.spec b/rpm/dtkcore.spec new file mode 100644 index 0000000..b3ede9f --- /dev/null +++ b/rpm/dtkcore.spec @@ -0,0 +1,76 @@ +Name: dtkcore +Version: 5.5.18 +Release: 1%{?dist} +Summary: Deepin tool kit core modules +License: LGPLv3+ +URL: https://github.com/linuxdeepin/dtkcore +%if 0%{?fedora} +Source0: %{url}/archive/%{version}/%{name}-%{version}.tar.gz +%else +Source0: %{name}-%{version}.orig.tar.xz +%endif +BuildRequires: dtkcommon-devel +BuildRequires: gcc-c++ +BuildRequires: annobin +BuildRequires: pkgconfig(Qt5Core) +BuildRequires: pkgconfig(gsettings-qt) +BuildRequires: gtest-devel + +# since f30 +Obsoletes: deepin-tool-kit <= 0.3.3 +Obsoletes: deepin-tool-kit-devel <= 0.3.3 +Obsoletes: dtksettings <= 0.1.7 +Obsoletes: dtksettings-devel <= 0.1.7 + +%description +Deepin tool kit core modules. + +%package devel +Summary: Development package for %{name} +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: dtkcommon-devel +Requires: qt5-qtbase-devel%{?_isa} + +%description devel +Header files and libraries for %{name}. + +%prep +%autosetup -p1 + +%build +# help find (and prefer) qt5 utilities, e.g. qmake, lrelease +export PATH=%{_qt5_bindir}:$PATH +%qmake_qt5 PREFIX=%{_prefix} \ + DTK_VERSION=%{version} \ + LIB_INSTALL_DIR=%{_libdir} \ + BIN_INSTALL_DIR=%{_libexecdir}/dtk5 \ + TOOL_INSTALL_DIR=%{_libexecdir}/dtk5 +%make_build + +%install +%make_install INSTALL_ROOT=%{buildroot} + +%files +%doc README.md +%license LICENSE +%{_libdir}/lib%{name}.so.5* +%dir %{_libexecdir}/dtk5/ +%{_libexecdir}/dtk5/dtk-settings +%{_libexecdir}/dtk5/dtk-license.py +%{_libexecdir}/dtk5/dtk-translate.py +%{_libexecdir}/dtk5/deepin-os-release +%{_prefix}/bin/qdbusxml2cpp-fix + +%files devel +%doc doc/Specification.md +%{_includedir}/libdtk-*/ +%{_qt5_archdatadir}/mkspecs/modules/*.pri +%{_libdir}/cmake/DtkCore/ +%{_libdir}/cmake/DtkCMake/ +%{_libdir}/cmake/DtkTools/ +%{_libdir}/pkgconfig/dtkcore.pc +%{_libdir}/lib%{name}.so + +%changelog +* Thu Jun 11 2020 uoser - 5.2.2.3 +- Update to 5.2.2.3 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..41ed7f6 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,126 @@ +#cmake_minimum_required(VERSION 3.5) +set(LIBNAME dtkcore) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +set (DSG_PREFIX_PATH "${CMAKE_INSTALL_PREFIX}" CACHE STRING "PREFIX of DSG_DATA_DIRS") +add_definitions(-DPREFIX="${DSG_PREFIX_PATH}") +add_definitions(-DLIBDTKCORE_LIBRARY) +find_package(Qt5 REQUIRED COMPONENTS Core) +if(LINUX) +find_package(PkgConfig REQUIRED) +pkg_check_modules(QGSettings REQUIRED gsettings-qt) +find_package(Qt5 REQUIRED COMPONENTS DBus) +endif() +find_package(Qt5 REQUIRED COMPONENTS Xml) + +# start base +include(base/base.cmake) +# end base +if(LINUX) + include(dbus/dbus.cmake) +endif() +#message(${dbus_SRCS}) +# end dbus + +# start dci +include(dci/dci.cmake) +#end dci + +#start filesystem +include(filesystem/filesystem.cmake) +#end filesystem +# start log +include(log/log.cmake) +#end log +# start settings +include(settings/settings.cmake) +#end settings + +#start utils +include(util/util.cmake) +#end utils + +#GLOB +include(glob.cmake) +#endGLOG +if(LINUX) + add_library(${LIBNAME} SHARED + ${dbus_SRCS} + ${base_SRCS} + ${dci_SRCS} + ${filesystem_SRCS} + ${log_SRCS} + ${settings_SRC} + ${utils_SRC} + ${glob_SRC} + ) + target_link_libraries( + ${LIBNAME} PRIVATE + Qt5::Core + Qt5::DBus + Qt5::Xml + ${QGSettings_LIBRARIES} + ) +else() + add_library(${LIBNAME} SHARED + ${base_SRCS} + ${dci_SRCS} + ${filesystem_SRCS} + ${log_SRCS} + ${settings_SRC} + ${utils_SRC} + ${glob_SRC} + ) + target_link_libraries( + ${LIBNAME} PRIVATE + Qt5::Core + Qt5::Xml + ) +endif() +set_target_properties(${LIBNAME} PROPERTIES + VERSION ${CMAKE_PROJECT_VERSION} + SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR} +) +target_include_directories( ${LIBNAME} PUBLIC + ${QGSettings_INCLUDE_DIRS} + ${Qt5Core_PRIVATE_INCLUDE_DIRS} + ../include/util/ + ../include/dci/ + ../include/log/ + ../include/base/ + ../include/base/private + ../include/global/ + ../include/DtkCore/ + ../include/settings/ + ../include/filesystem/ + ../include/ +) +set(TOINSTALLBASE + ../include/base/dobject.h + ../include/base/dsingleton.h + ../include/base/private/dobject_p.h + ../include/base/derror.h + ../include/base/dexpected.h +) +install(FILES ${TOINSTALLBASE} DESTINATION "${INCLUDE_INSTALL_DIR}") +install(DIRECTORY ../include/dci/ DESTINATION "${INCLUDE_INSTALL_DIR}" FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ../include/DtkCore/ DESTINATION "${INCLUDE_INSTALL_DIR}" FILES_MATCHING PATTERN "*") +install(DIRECTORY ../include/filesystem/ DESTINATION "${INCLUDE_INSTALL_DIR}" FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ../include/global/ DESTINATION "${INCLUDE_INSTALL_DIR}" FILES_MATCHING PATTERN "*.h") +file(GLOB TOINSTALLLOG + ../include/log/*.h +) +install(FILES ${TOINSTALLLOG} DESTINATION "${INCLUDE_INSTALL_DIR}") +file(GLOB TOINSTALLSETTINGS + ../include/settings/*.h + ../include/settings/backend/*.h +) +install(FILES ${TOINSTALLSETTINGS} DESTINATION "${INCLUDE_INSTALL_DIR}") +install(DIRECTORY ../include/util/ DESTINATION "${INCLUDE_INSTALL_DIR}" FILES_MATCHING PATTERN "*.h") +install(TARGETS ${LIBNAME} DESTINATION ${LIBRARY_INSTALL_DIR}) diff --git a/src/base/base.cmake b/src/base/base.cmake new file mode 100644 index 0000000..4ad1ae5 --- /dev/null +++ b/src/base/base.cmake @@ -0,0 +1,8 @@ +set(base_SRCS + ${CMAKE_CURRENT_LIST_DIR}/../../include/base/dobject.h + ${CMAKE_CURRENT_LIST_DIR}/../../include/base/private/dobject_p.h + ${CMAKE_CURRENT_LIST_DIR}/../../include/base/dsingleton.h + ${CMAKE_CURRENT_LIST_DIR}/../../include/base/dexpected.h + ${CMAKE_CURRENT_LIST_DIR}/../../include/base/derror.h + ${CMAKE_CURRENT_LIST_DIR}/dobject.cpp +) diff --git a/src/base/dobject.cpp b/src/base/dobject.cpp new file mode 100644 index 0000000..f3bddd7 --- /dev/null +++ b/src/base/dobject.cpp @@ -0,0 +1,244 @@ +// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dobject.h" +#include "base/private/dobject_p.h" + +DCORE_BEGIN_NAMESPACE + +DObjectPrivate::DObjectPrivate(DObject *qq) + : q_ptr(qq) +{ + +} + +DObjectPrivate::~DObjectPrivate() +{ + +} + +/*! + \headerfile + \inmodule dtkcore + + \brief 一些宏的定义. +*/ + +/*! + \class Dtk::Core::DObject + \inmodule dtkcore + \brief deepin-tool-kit 中所有公开类的祖先类. + + 通过和 D_DECLARE_PRIVATE 、D_DECLARE_PUBLIC + 等宏的配合方便派生类中实现 D-Point 结构。虽然 QObject 中已经有了这样的实现结构,但是没有 + 办法在不使用 Qt 私有模块的情况下,在 DTK 库中达到同样的目的。D-Point 结构由“公共接口类” + 和“私有数据类”两部分组成,在 DTK 中,DObjectPrivate 是所有数据类的祖先类。在这种结构下, + 只有 DObject 这个基类中定了一个指向于私有数据类的对象指针,派生类中不会也不应该再定义任何 + 成员变量,派生类中需要添加数据成员时,可以继承 DObjectPrivate,将新的成员变量放到私有类中 + + 例子: + + a.h + \code + class APrivate; + class A : public DObject + { + D_DECLARE_PRIVATE(A) + public: + A(); + int test() const; + + protected: + A(APrivate &dd, DObject *parent = nullptr); + }; + \endcode + a.cpp + \code + class APrivate : public DObjectPrivate + { + public: + APrivate(A *qq) + : DObjectPrivate(qq) + { + + } + + D_DECLARE_PUBLIC(A) + // 此处添加数据成员 + int data; + }; + + A::A() + : DObject(*new APrivate(this)) + { + + } + + int test() const + { + D_D(A); + + return d->data; + } + + A::A(APrivate &dd, DObject *parent) + : DObject(dd, parent) + { + + } + \endcode + 一般来讲,DObject 只会用在 DTK 库中定义的类,对于使用 DTK 库的应用程序来说不用关心它的存在 + \sa {https://wiki.qt.io/D-Pointer/zh}{类的 D-Point 结构} + */ + +/*! + \brief 只有在不需要数据成员的派生类中才会使用 + \a parent 父类指针 + */ +DObject::DObject(DObject * /*parent = nullptr*/) +{ + +} + +/*! + \brief 在派生类中比较常用的构造函数 + \a dd 私有类对象 + */ +DObject::DObject(DObjectPrivate &dd, DObject * /*parent = nullptr*/): + d_d_ptr(&dd) +{ + +} + +DObject::~DObject() +{ + +} + +/*! + \macro D_DECLARE_PRIVATE(Class) + \relates Dtk::Core::DObject + + \brief 这个宏一定要放到类的私有区域,它定义了 d_func() 这个函数用于返回私有类的对象, + 这个对象只应该在类的内部使用,另外将私有类声明为公开类的友元类。 + \a Class 公开类的类名 + \sa D_DECLARE_PUBLIC D_D D_DC +*/ + +/*! + \macro D_DECLARE_PUBLIC(Class) + \relates Dtk::Core::DObject + + \brief 这个宏用于私有类中,它定义了 q_func() 这个函数用于返回公开类的对象,另外将公开类 + 声明为私有类的友元类。 + \a Class 公开类的类名 + \sa D_DECLARE_PRIVATE D_Q D_QC +*/ + +/*! + \macro D_D(Class) + \relates Dtk::Core::DObject + + \brief 这个宏用于公开类中,它定义了一个名字为 d 的变量存储 d_func() 的返回值。用于在公开 + 类中需要访问私有类的数据成员的函数中。 + \a Class 公开类的类名 + \sa D_DECLARE_PRIVATE D_DC +*/ + +/*! + \macro D_DC(Class) + \relates Dtk::Core::DObject + + \brief 同 D_D,用在公开类加了 const 修饰符的成员函数中。 + \a Class 公开类的类名 + \sa D_DECLARE_PRIVATE D_D +*/ + +/*! + \macro D_Q(Class) + \relates Dtk::Core::DObject + + \brief 这个宏用于私有类中,它定义了一个名字为 q 的变量存储 q_func() 的返回值。用于在私有 + 类中需要调用公开类的成员函数时。 + \a Class 公开类的类名 + \sa D_DECLARE_PUBLIC D_QC +*/ + +/*! + \macro D_QC(Class) + \relates Dtk::Core::DObject + + \brief 同 D_Q,用在私有类加了 const 修饰符的成员函数中。 + \a Class 公开类的类名 + \sa D_DECLARE_PUBLIC D_Q +*/ + +/*! + \macro D_PRIVATE_SLOT(Func) + \relates Dtk::Core::DObject + + \brief 同 Q_PRIVATE_SLOT,用在继承了 QObject 的公开类中,在公开类中定一个槽函数,且函数 + 必须在私有类中有实现。用这个方式定义的槽函数无法被直接调用,只能用于 QObject::connect + 使用 SIGNAL 和 SLOT 的方式连接信号,或者使用 QMetaObject::invokeMethod 调用。 + 一般来讲,这个槽函数应该只在类内部使用,外界不应该通过任何方式来调用它。 + + 例子: + + a.h + \code + class APrivate; + class A : public DObject + { + D_DECLARE_PRIVATE(A) + public: + A(); + + protected: + A(APrivate &dd, DObject *parent = nullptr); + + private: + D_PRIVATE_SLOT(void _q_testSlot() const) + }; + \endcode +a.cpp + \code + class APrivate : public DObjectPrivate + { + public: + D_DECLARE_PUBLIC(A) + + APrivate(A *qq) + : DObjectPrivate(qq) + { + QTimer *timer = new QTimer(); + QObject::connect(timer, SIGNAL(timeout()), qq, SLOT(_q_testSlot())); + timer->start(1000); + } + + void _q_testSlot() const + { + qDebug() << "slot"; + } + }; + + A::A() + : DObject(*new APrivate(this)) + { + + } + + A::A(APrivate &dd, DObject *parent) + : DObject(dd, parent) + { + + } + + \#include "moc_a.cpp" + \endcode + \a Func 槽函数的完整签名 + \note 添加或更新私有槽之后需要重新手动调用 qmake + \sa D_DECLARE_PUBLIC D_Q +*/ + +DCORE_END_NAMESPACE diff --git a/src/dbus/dbus.cmake b/src/dbus/dbus.cmake new file mode 100644 index 0000000..2d239e1 --- /dev/null +++ b/src/dbus/dbus.cmake @@ -0,0 +1,21 @@ +set(dbus_SRCS) +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/org.desktopspec.ConfigManager.xml + PROPERTIES + NO_NAMESPACE ON + CLASSNAME DSGConfig +) +qt5_add_dbus_interface(dbus_SRCS + ${CMAKE_CURRENT_LIST_DIR}/org.desktopspec.ConfigManager.xml + configmanager_interface +) +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/org.desktopspec.ConfigManager.Manager.xml + PROPERTIES + NO_NAMESPACE ON + CLASSNAME DSGConfigManager +) +qt5_add_dbus_interface(dbus_SRCS + ${CMAKE_CURRENT_LIST_DIR}/org.desktopspec.ConfigManager.Manager.xml + manager_interface +) diff --git a/src/dbus/org.desktopspec.ConfigManager.Manager.xml b/src/dbus/org.desktopspec.ConfigManager.Manager.xml new file mode 100644 index 0000000..89a2aab --- /dev/null +++ b/src/dbus/org.desktopspec.ConfigManager.Manager.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ' + + diff --git a/src/dbus/org.desktopspec.ConfigManager.xml b/src/dbus/org.desktopspec.ConfigManager.xml new file mode 100644 index 0000000..f265bc8 --- /dev/null +++ b/src/dbus/org.desktopspec.ConfigManager.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/dci/dci.cmake b/src/dci/dci.cmake new file mode 100644 index 0000000..23aaf90 --- /dev/null +++ b/src/dci/dci.cmake @@ -0,0 +1,6 @@ +set(dci_SRCS + ${CMAKE_CURRENT_LIST_DIR}/../../include/dci/ddcifile.h + ${CMAKE_CURRENT_LIST_DIR}/ddcifile.cpp + ${CMAKE_CURRENT_LIST_DIR}/private/ddcifileengine_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/ddcifileengine.cpp +) diff --git a/src/dci/ddcifile.cpp b/src/dci/ddcifile.cpp new file mode 100644 index 0000000..0894daf --- /dev/null +++ b/src/dci/ddcifile.cpp @@ -0,0 +1,829 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "ddcifile.h" +#include "private/ddcifileengine_p.h" + +#ifndef DTK_NO_PROJECT +#include +#else +#define D_D(class) +#define D_DC(class) +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +#define MAGIC "DCI" + +#define MAGIC_SIZE 4 +#define VERSION_SIZE 1 +#define FILE_COUNT_SIZE 3 +#define FILE_META_SIZE 72 + +#define FILE_TYPE_SIZE 1 +#define FILE_NAME_SIZE 63 +#define FILE_DATA_SIZE 8 + +#define FILE_TYPE_FILE DDciFile::FileType::File +#define FILE_TYPE_DIR DDciFile::FileType::Directory +#define FILE_TYPE_SYMLINK DDciFile::FileType::Symlink + +#ifdef QT_DEBUG +Q_LOGGING_CATEGORY(logDF, "dtk.dci.file") +#else +Q_LOGGING_CATEGORY(logDF, "dtk.dci.file", QtInfoMsg) +#endif + +class DDciFilePrivate +#ifndef DTK_NO_PROJECT + : public DObjectPrivate +#endif +{ +public: + DDciFilePrivate(DDciFile *qq) +#ifndef DTK_NO_PROJECT + : DObjectPrivate(qq) + { +#else + { + Q_UNUSED(qq) +#endif + + } + ~DDciFilePrivate(); + + void setErrorString(const QString &message); + + void load(const QString &fileName); + void load(const QByteArray &data); + + QString errorMessage; + struct Node { + qint8 type = DDciFile::UnknowFile; + QString name; + + Node *parent = nullptr; + QVector children; // for directory + QByteArray data; // for file + + ~Node() { + qDeleteAll(children); + } + + QString path() const { + QString p = name; + Node *current = parent; + while (current) { + p.prepend(current->name + "/"); + current = current->parent; + } + + return p; + } + + QString linkPath() const { + const QString &path = QString::fromUtf8(data); + if (path.startsWith('/')) + return path; + // 转为绝对路径 + auto pNode = parent; + int pathStart = 0; + while (pathStart < path.size()) { + if (path.midRef(pathStart, 3) == QLatin1String("../")) { + pathStart += 3; + pNode = pNode->parent; + if (!pNode) + return QString(); + } else if (path.midRef(pathStart, 2) == QLatin1String("./")) { + pathStart += 2; + } else { + break; + } + } + Q_ASSERT(pNode); + return pNode->path() + QLatin1Char('/') + path.midRef(pathStart); + } + }; + + qint64 writeMetaDataForNode(QIODevice *device, Node *node, qint64 dataSize) const; + qint64 writeDataForNode(QIODevice *device, Node *node) const; + qint64 writeNode(QIODevice *device, Node *node) const; + + Node *mkNode(const QString &filePath); + void removeNode(Node *node); + void copyNode(const Node *from, Node *to); + + bool loadDirectory(Node *directory, + const QByteArray &data, qint64 &begin, qint64 end, + QHash &pathToNode); + + // 按标准中规定的文件排序计算此 name 在这个列表中的位置 + static int getOrderedIndexOfNodeName(const decltype(Node::children) &list, const QString &name); + + qint8 version = 0; + QScopedPointer root; + QHash pathToNode; + QByteArray rawData; +}; + +DDciFilePrivate::~DDciFilePrivate() +{ + +} + +void DDciFilePrivate::setErrorString(const QString &message) +{ + qCDebug(logDF, "%s", qPrintable(message)); + errorMessage = message; +} + +void DDciFilePrivate::load(const QString &fileName) +{ + QFile file(fileName); + + if (!file.open(QIODevice::ReadOnly)) { + setErrorString(file.errorString()); + return; + } + + return load(file.readAll()); +} + +void DDciFilePrivate::load(const QByteArray &data) +{ + // check magic + if (!data.startsWith("DCI")) { + setErrorString(QString("Expect value is \"DCI\", " + "but actually value is \"%1\"") + .arg(QString::fromLatin1(data.left(3)))); + return; + } + + qint8 version = data.at(MAGIC_SIZE); + if (version != 1) { + setErrorString(QString("Not supported version: %1").arg(version)); + return; + } + + char fileCountData[4]; + int fileCountOffset = MAGIC_SIZE + VERSION_SIZE; + memcpy(fileCountData, data.constData() + fileCountOffset, FILE_COUNT_SIZE); + fileCountData[3] = 0; + int fileCount = qFromLittleEndian(fileCountData); + + if (fileCount < 0) { + setErrorString(QString("Invalid file count: %1").arg(fileCount)); + return; + } + + qint64 offset = MAGIC_SIZE + VERSION_SIZE + FILE_COUNT_SIZE; + Node *root = new Node; + root->type = FILE_TYPE_DIR; + root->parent = nullptr; + + QHash pathToNode; + + if (!loadDirectory(root, data, offset, data.size() - 1, pathToNode) + || fileCount != root->children.count()) { + delete root; + return; + } + + this->version = version; + this->root.reset(root); + this->pathToNode = pathToNode; + this->pathToNode["/"] = root; + // Node 中保存的文件数据仅是此数据的引用,因此要确保此数据一直存在 + this->rawData = data; +} + +qint64 DDciFilePrivate::writeMetaDataForNode(QIODevice *device, DDciFilePrivate::Node *node, qint64 dataSize) const +{ + qint64 size = 0; + device->putChar(static_cast(node->type)); + size += FILE_TYPE_SIZE; + + const QByteArray rawName = node->name.toUtf8().left(FILE_NAME_SIZE - 1); + size += device->write(rawName); + // 填充未使用的部分 + size += device->write(QByteArray(FILE_NAME_SIZE - rawName.size(), '\0')); + + char int64[FILE_DATA_SIZE]; + qToLittleEndian(dataSize, int64); + size += device->write(int64, FILE_DATA_SIZE); + Q_ASSERT(size == FILE_META_SIZE); + + return size; +} + +qint64 DDciFilePrivate::writeDataForNode(QIODevice *device, DDciFilePrivate::Node *node) const +{ + if (node->type == FILE_TYPE_FILE + || node->type == FILE_TYPE_SYMLINK) { + return device->write(node->data); + } else if (node->type == FILE_TYPE_DIR) { + qint64 dataSize = 0; + for (Node *child : node->children) { + dataSize += writeNode(device, child); + } + return dataSize; + } + + return 0; +} + +qint64 DDciFilePrivate::writeNode(QIODevice *device, DDciFilePrivate::Node *node) const +{ + const qint64 metaDataPos = device->pos(); + device->seek(metaDataPos + FILE_META_SIZE); + const qint64 dataSize = writeDataForNode(device, node); + device->seek(metaDataPos); + const qint64 metaDataSize = writeMetaDataForNode(device, node, dataSize); + device->seek(device->pos() + dataSize); + return metaDataSize + dataSize; +} + +DDciFilePrivate::Node *DDciFilePrivate::mkNode(const QString &filePath) +{ + qCDebug(logDF, "Request create a node"); + + if (pathToNode.contains(filePath)) { + setErrorString(QString("The \"%1\" is existed").arg(filePath)); + return nullptr; + } + + const QFileInfo info(filePath); + qCDebug(logDF, "The parent directory is \"%s\"", qPrintable(info.path())); + + if (Node *parentNode = pathToNode.value(info.path())) { + if (parentNode->type != FILE_TYPE_DIR) { + setErrorString(QString("The \"%1\" is not a directory").arg(info.path())); + return nullptr; + } + + // 检查文件名长度,避免溢出 + if (info.fileName().toUtf8().size() > FILE_NAME_SIZE - 1) { + setErrorString(QString("The file name size must less then %1 bytes").arg(FILE_NAME_SIZE)); + return nullptr; + } + + Node *newNode = new Node; + newNode->name = info.fileName(); + newNode->parent = parentNode; + + const int index = getOrderedIndexOfNodeName(parentNode->children, newNode->name); + parentNode->children.insert(index, newNode); + pathToNode[newNode->path()] = newNode; + + return newNode; + } else { + setErrorString("The parent directory is not exists"); + return nullptr; + } +} + +void DDciFilePrivate::removeNode(DDciFilePrivate::Node *node) +{ + Q_ASSERT(node != root.data()); + + node->parent->children.removeOne(node); + auto removedNode = pathToNode.take(node->path()); + Q_ASSERT(removedNode == node); + + for (Node *child : node->children) { + Q_ASSERT(child->parent == node); + auto removedNode = pathToNode.take(child->path()); + Q_ASSERT(removedNode == child); + } + + delete node; +} + +void DDciFilePrivate::copyNode(const DDciFilePrivate::Node *from, DDciFilePrivate::Node *to) +{ + QList> copyPendingList; + copyPendingList << qMakePair(from, to); + + for (int i = 0; i < copyPendingList.size(); ++i) { + auto f = copyPendingList.at(i).first; + auto t = copyPendingList.at(i).second; + + t->type = f->type; + t->data = f->data; + + for (const auto child : f->children) { + if (child == to) + continue; + + Node *newChild = new Node; + newChild->parent = t; + newChild->name = child->name; + pathToNode[newChild->path()] = newChild; + + const int index = getOrderedIndexOfNodeName(t->children, newChild->name); + t->children.insert(index, newChild); + copyPendingList << qMakePair(child, newChild); + } + } +} + +bool DDciFilePrivate::loadDirectory(DDciFilePrivate::Node *directory, + const QByteArray &data, qint64 &offset, qint64 end, + QHash &pathToNode) +{ + // load files + while (offset < end) { + Node *node = new Node; + + node->parent = directory; + node->type = data.at(offset); + offset += FILE_TYPE_SIZE; + // 计算文件名的长度 + const int nameLength = data.indexOf('\0', offset) - offset; + if (nameLength <= 0 || nameLength >= FILE_NAME_SIZE) { + setErrorString(QString("Invalid file name, the data offset: %1").arg(offset)); + delete node; + return false; + } + node->name = QString::fromUtf8(data.constData() + offset, nameLength); + offset += FILE_NAME_SIZE; + + const qint64 dataSize = qFromLittleEndian(data.constData() + offset); + offset += FILE_DATA_SIZE; + + // 无失败时调用 break + do { + if (node->type == FILE_TYPE_DIR) { + if (loadDirectory(node, data, offset, offset + dataSize - 1, pathToNode)) { + break; + } + } else if (node->type == FILE_TYPE_FILE + || node->type == FILE_TYPE_SYMLINK) { + // 跳过文件内容 + node->data = QByteArray::fromRawData(data.constData() + offset, dataSize); + + if (node->data.size() == dataSize) { + offset += dataSize; + break; + } else { + setErrorString(QString("Invalid data size of \"%1\" file").arg(node->path())); + } + } else { + setErrorString(QString("Invalid file type: %1").arg(node->type)); + } + + delete node; + return false; + } while (false); + + directory->children << node; + pathToNode[node->path()] = node; + } + + return true; +} + +int DDciFilePrivate::getOrderedIndexOfNodeName(const decltype(Node::children) &list, const QString &name) +{ + QCollator collator(QLocale::English); + collator.setNumericMode(true); + for (int i = 0; i < list.count(); ++i) { + const Node *node = list.at(i); + if (collator.compare(name, node->name) < 0) + return i; + } + + return list.count(); +} + +void DDciFile::registerFileEngine() +{ + // 在 QAbstractFileEngineHandler 的构造函数中会注册自己,后续 + // 在使用 QFile 时会调用 DDciFileEngineHandler::create + static DDciFileEngineHandler globalHandler; + Q_UNUSED(globalHandler); +} + +#ifndef DTK_NO_PROJECT +DDciFile::DDciFile() + : DObject(*new DDciFilePrivate(this)) +{ + d_func()->load(QByteArrayLiteral("DCI\0\1\0\0\0")); +} + +DDciFile::DDciFile(const QString &fileName) + : DObject(*new DDciFilePrivate(this)) +{ + d_func()->load(fileName); +} + +DDciFile::DDciFile(const QByteArray &data) + : DObject(*new DDciFilePrivate(this)) +{ + d_func()->load(data); +} +#else +DDciFile::DDciFile() + : d(new DDciFilePrivate(this)) +{ + d->load(QByteArrayLiteral("DCI\0\1\0\0\0")); +} + +DDciFile::DDciFile(const QString &fileName) + : d(new DDciFilePrivate(this)) +{ + d->load(fileName); +} + +DDciFile::DDciFile(const QByteArray &data) + : d(new DDciFilePrivate(this)) +{ + d->load(data); +} +#endif + +bool DDciFile::isValid() const +{ + D_DC(DDciFile); + return d->root; +} + +QString DDciFile::lastErrorString() const +{ + D_DC(DDciFile); + return d->errorMessage; +} + +bool DDciFile::writeToFile(const QString &fileName) const +{ + QSaveFile sf(fileName); + do { + if (!sf.open(QIODevice::WriteOnly)) { + break; + } + if (!writeToDevice(&sf)) + return false; + if (!sf.commit()) + break; + return true; + } while (false); + + qCDebug(logDF, "Failed on write to file \"%s\", error message is: \"%s\"", + qPrintable(fileName), qPrintable(sf.errorString())); + return false; +} + +bool DDciFile::writeToDevice(QIODevice *device) const +{ + Q_ASSERT(isValid()); + D_DC(DDciFile); + + // magic + device->write(QByteArrayLiteral("DCI\0")); + // version + device->putChar(static_cast(d->version)); + char fileCountData[sizeof(int)]; + qToLittleEndian(d->root->children.count(), fileCountData); + // file count + device->write(fileCountData, FILE_COUNT_SIZE); + d->writeDataForNode(device, d->root.data()); + + return device->size() >= metadataSizeV1() + + (d->pathToNode.count() - 1) * FILE_META_SIZE; +} + +QByteArray DDciFile::toData() const +{ + if (!isValid()) + return QByteArray(); + + D_DC(DDciFile); + + qint64 allFilesContentSize = 0; + for (auto node : d->pathToNode) { + if (node->type == FILE_TYPE_FILE + || node->type == FILE_TYPE_SYMLINK) + allFilesContentSize += node->data.size(); + } + + QByteArray data; + // -1 是排除根目录 + data.resize(metadataSizeV1() + (d->pathToNode.count() - 1) * FILE_META_SIZE + + allFilesContentSize); + QBuffer buffer(&data); + + if (!buffer.open(QIODevice::WriteOnly) || !writeToDevice(&buffer)) + return QByteArray(); + + return data; +} + +constexpr int DDciFile::metadataSizeV1() +{ + return MAGIC_SIZE + VERSION_SIZE + FILE_COUNT_SIZE; +} + +QStringList DDciFile::list(const QString &dir, bool onlyFileName) const +{ + if (!isValid()) + return {}; + + D_DC(DDciFile); + + auto dirNode = d->pathToNode.value(dir); + if (!dirNode) { + qCDebug(logDF, "The \"%s\" is not exists", qPrintable(dir)); + return {}; + } + + if (dirNode->type != FILE_TYPE_DIR) { + qCWarning(logDF, "The \"%s\" is not a directory", qPrintable(dir)); + return {}; + } + + QStringList children; + for (auto child : dirNode->children) { + children << (onlyFileName ? child->name : QDir(dir).filePath(child->name)); + } + + return children; +} + +int DDciFile::childrenCount(const QString &dir) const +{ + if (!isValid()) + return 0; + + D_DC(DDciFile); + + auto dirNode = d->pathToNode.value(dir); + if (!dirNode) { + return 0; + } + + return dirNode->children.count(); +} + +bool DDciFile::exists(const QString &filePath) const +{ + if (!isValid()) + return false; + + D_DC(DDciFile); + return d->pathToNode.contains(filePath); +} + +DDciFile::FileType DDciFile::type(const QString &filePath) const +{ + if (!isValid()) + return UnknowFile; + + D_DC(DDciFile); + + auto node = d->pathToNode.value(filePath); + if (!node) { + qCDebug(logDF, "The \"%s\" is not exists", qPrintable(filePath)); + return DDciFile::UnknowFile; + } + + return static_cast(node->type); +} + +QByteArray DDciFile::dataRef(const QString &filePath) const +{ + if (!isValid()) + return QByteArray(); + + D_DC(DDciFile); + + auto node = d->pathToNode.value(filePath); + if (!node) { + qCDebug(logDF, "The \"%s\" is not exists", qPrintable(filePath)); + return QByteArray(); + } + + if (node->type == FILE_TYPE_SYMLINK) { + return dataRef(node->linkPath()); + } + + return node->data; +} + +QString DDciFile::name(const QString &filePath) const +{ + if (!isValid()) + return QString(); + + D_DC(DDciFile); + if (auto node = d->pathToNode.value(filePath)) { + return node->name; + } + + return QString(); +} + +QString DDciFile::symlinkTarget(const QString &filePath, bool originData) const +{ + if (!isValid()) + return QString(); + + D_DC(DDciFile); + if (auto node = d->pathToNode.value(filePath)) { + if (node->type != FILE_TYPE_SYMLINK) + return QString(); + + if (originData) { + return QString::fromUtf8(node->data); + } + + const QString &linkPath = node->linkPath(); + const auto targetNode = d->pathToNode.value(linkPath); + + // 链接的目标只能是“不存在的路径”、“文件”、“链接”,不可是目录 + if (!targetNode || targetNode->type == FILE_TYPE_FILE + || targetNode->type == FILE_TYPE_SYMLINK) + return linkPath; + } + + return QString(); +} + +bool DDciFile::mkdir(const QString &filePath) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + qCDebug(logDF, "Request create the \"%s\" directory", qPrintable(filePath)); + auto node = d->mkNode(filePath); + if (!node) + return false; + node->type = FILE_TYPE_DIR; + return true; +} + +bool DDciFile::writeFile(const QString &filePath, const QByteArray &data, bool override) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + qCDebug(logDF, "Request create the \"%s\" file", qPrintable(filePath)); + // 先删除旧的数据 + if (auto node = d->pathToNode.value(filePath)) { + if (override) { + if (node->type == FILE_TYPE_SYMLINK) { + const QString &linkPath = node->linkPath(); + qCDebug(logDF(), "Follow the symlink to \"%s\"", qPrintable(linkPath)); + + if (!d->pathToNode.contains(linkPath)) { + qCDebug(logDF(), "Can't write to a symlink target file if it is not existed"); + return false; + } + + return writeFile(linkPath, data, override); + } + + qCDebug(logDF, "Try override the file"); + if (node->type != FILE_TYPE_FILE) { + qCWarning(logDF, "The \"%s\" is existed and it is not a file", qPrintable(filePath)); + return false; + } + + node->data = data; + return true; + } else { + d->setErrorString("No the \"override\" flag and the file is existed, can't write"); + return false; + } + } + + auto node = d->mkNode(filePath); + if (!node) + return false; + + node->type = FILE_TYPE_FILE; + node->data = data; + return true; +} + +bool DDciFile::remove(const QString &filePath) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + if (auto node = d->pathToNode.value(filePath)) { + if (node == d->root.data()) { + for (auto child : d->root->children) + d->removeNode(child); + d->root->children.clear(); + } else { + d->removeNode(node); + } + return true; + } else { + d->setErrorString("The file is not exists"); + return false; + } +} + +bool DDciFile::rename(const QString &filePath, const QString &newFilePath, bool override) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + qCDebug(logDF, "Rename from \"%s\" to \"%s\"", qPrintable(filePath), qPrintable(newFilePath)); + if (filePath == newFilePath) + return false; + + if (newFilePath.toUtf8().size() >= FILE_META_SIZE) { + d->setErrorString(QString("The new name size must less then %1 bytes").arg(FILE_NAME_SIZE)); + return false; + } + + if (!override && d->pathToNode.contains(newFilePath)) { + d->setErrorString("The target file is existed"); + return false; + } + auto overrideNode = override ? d->pathToNode.take(newFilePath) : nullptr; + + if (auto node = d->pathToNode.take(filePath)) { + QFileInfo info(newFilePath); + if (auto parent = d->pathToNode.value(info.absolutePath())) { + node->name = info.fileName(); + + // 从旧节点删除添加到新的节点 + if (node->parent != parent) { + bool ok = node->parent->children.removeOne(node); + Q_ASSERT(ok); + const int index = d->getOrderedIndexOfNodeName(parent->children, node->name); + parent->children.insert(index, node); + node->parent = parent; + } + + d->pathToNode[info.absoluteFilePath()] = node; + Q_ASSERT(node->path() == info.absoluteFilePath()); + + // 删除被覆盖的节点 + if (overrideNode) { + Q_ASSERT(overrideNode->parent == parent); + overrideNode->parent->children.removeOne(overrideNode); + delete overrideNode; + } + + return true; + } else { + d->setErrorString(QString("The \"%1\" directory is not exists").arg(info.absolutePath())); + return false; + } + } else { + d->setErrorString("The file is not exists"); + return false; + } +} + +bool DDciFile::copy(const QString &from, const QString &to) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + const auto fromNode = d->pathToNode.value(from); + if (!fromNode) { + d->setErrorString(QString("The \"%1\" is not exists").arg(from)); + return false; + } + + auto toNode = d->mkNode(to); + if (!toNode) { + return false; + } + + d->copyNode(fromNode, toNode); + return true; +} + +bool DDciFile::link(const QString &source, const QString &to) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + if (source == to || source.isEmpty()) + return false; + + auto toNode = d->mkNode(to); + if (!toNode) + return false; + toNode->type = FILE_TYPE_SYMLINK; + toNode->data = source.toUtf8(); + + return true; +} + +DCORE_END_NAMESPACE diff --git a/src/dci/private/ddcifileengine.cpp b/src/dci/private/ddcifileengine.cpp new file mode 100644 index 0000000..9e4fd6f --- /dev/null +++ b/src/dci/private/ddcifileengine.cpp @@ -0,0 +1,613 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#define private public +#define protected public +#include +#undef private +#undef protected + +#include "ddcifileengine_p.h" +#include "dci/ddcifile.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +#ifdef QT_DEBUG +Q_LOGGING_CATEGORY(logFE, "dtk.dci.fileengine") +#else +Q_LOGGING_CATEGORY(logFE, "dtk.dci.fileengine", QtInfoMsg) +#endif + +#define DCI_FILE_SCHEME "dci:" +#define DCI_FILE_SUFFIX ".dci" + +QAbstractFileEngine *DDciFileEngineHandler::create(const QString &fileName) const +{ + if (!fileName.startsWith(QStringLiteral(DCI_FILE_SCHEME))) + return nullptr; + + DDciFileEngine *engine = new DDciFileEngine(fileName); + if (!engine->isValid()) { + delete engine; + return nullptr; + } + + return engine; +} + +// 共享同个线程内的同个 DDciFile +static thread_local QHash> sharedDciFile; +static void doDeleteSharedDciFile(const QString &path, DDciFile *file) { + int count = sharedDciFile.remove(path); + Q_ASSERT(count > 0); + delete file; +} + +static DDciFileShared getDciFile(const QString &dciFilePath, bool usePath = true) +{ + if (auto shared = sharedDciFile.value(dciFilePath)) { + return shared.toStrongRef(); + } + + DDciFileShared shared(usePath ? new DDciFile(dciFilePath) : new DDciFile(), + std::bind(doDeleteSharedDciFile, dciFilePath, + std::placeholders::_1)); + sharedDciFile[dciFilePath] = shared.toWeakRef(); + return shared; +} + +DDciFileEngineIterator::DDciFileEngineIterator(QDir::Filters filters, const QStringList &nameFilters) + : QAbstractFileEngineIterator(filters, nameFilters) +{ + +} + +QString DDciFileEngineIterator::next() +{ + current = nextValid; + return DDciFileEngineIterator::currentFileName(); +} + +bool DDciFileEngineIterator::hasNext() const +{ + if (!file) { + const auto paths = DDciFileEngine::resolvePath(path()); + if (paths.first.isEmpty() + || paths.second.isEmpty()) + return false; + + file = getDciFile(paths.first); + list = file->list(paths.second); + } + + for (int i = current + 1; i < list.count(); ++i) { + // 先检查文件类型 + const auto filters = this->filters(); + const auto fileType = file->type(list.at(i)); + if (fileType == DDciFile::Directory) { + if (!filters.testFlag(QDir::Files)) + continue; + } else if (fileType == DDciFile::File) { + if (!filters.testFlag(QDir::Files)) + continue; + } else if (fileType == DDciFile::Symlink) { + if (filters.testFlag(QDir::NoSymLinks)) + continue; + } else { // DDciFile::UnknowFile + continue; + } + + // 按名称进行过滤 + if (!nameFilters().isEmpty() && !QDir::match(nameFilters(), list.at(i))) + continue; + + nextValid = i; + return true; + } + + return false; +} + +QString DDciFileEngineIterator::currentFileName() const +{ + return file->name(list.at(current)); +} + +DDciFileEngine::DDciFileEngine(const QString &fullPath) +{ + setFileName(fullPath); +} + +DDciFileEngine::~DDciFileEngine() +{ + close(); +} + +bool DDciFileEngine::isValid() const +{ + return file && file->isValid(); +} + +bool DDciFileEngine::open(QIODevice::OpenMode openMode) +{ + if (fileBuffer) { + setError(QFile::OpenError, "The file is opened"); + return false; + } + + if (!file->isValid()) { + setError(QFile::OpenError, "The DCI file is invalid"); + return false; + } + + if (file->type(subfilePath) == DDciFile::Directory) { + setError(QFile::OpenError, "Can't open a directory"); + return false; + } + + if (file->type(subfilePath) == DDciFile::Symlink) { + if (!file->exists(file->symlinkTarget(subfilePath))) { + setError(QFile::OpenError, "The symlink target is not existed"); + return false; + } + } + + if (openMode & QIODevice::Text) { + setError(QFile::OpenError, "Not supported open mode"); + return false; + } + + if (openMode & QIODevice::NewOnly) { + if (file->exists(subfilePath)) { + setError(QFile::OpenError, "The file is existed"); + return false; + } + } + + if ((openMode & QIODevice::ExistingOnly) + || !(openMode & QIODevice::WriteOnly)) { + if (!file->exists(subfilePath)) { + setError(QFile::OpenError, "The file is not exists"); + return false; + } + } + + // 此时当文件不存在时应当创建它 + if (openMode & QIODevice::WriteOnly) { + realDciFile.setFileName(dciFilePath); + if (!realDciFile.open(openMode)) { + return false; + } + + // 不存在时尝试新建 + if (!file->exists(subfilePath) + && !file->writeFile(subfilePath, QByteArray())) { + return false; + } + } + + // 加载数据 + fileData = file->dataRef(subfilePath); + fileBuffer = new QBuffer(&fileData); + bool ok = fileBuffer->open(openMode); + Q_ASSERT(ok); + if (Q_UNLIKELY(!ok)) { + delete fileBuffer; + fileBuffer = nullptr; + return false; + } + + return true; +} + +bool DDciFileEngine::close() +{ + if (!fileBuffer) { + return false; + } + + fileBuffer->close(); + delete fileBuffer; + fileBuffer = nullptr; + + bool ok = flush(); + realDciFile.close(); + return ok; +} + +bool DDciFileEngine::flushToFile(QFile *target, bool writeFile) const +{ + if (target->isWritable()) { + if (writeFile && !file->writeFile(subfilePath, fileData, true)) + return false; + if (!target->resize(0)) + return false; + const QByteArray &data = file->toData(); + if (target->write(data) != data.size()) + return false; + return true; + } + + return false; +} + +bool DDciFileEngine::flush() +{ + if (!flushToFile(&realDciFile, true)) + return false; + + return realDciFile.flush(); +} + +bool DDciFileEngine::syncToDisk() +{ + if (!flush()) + return false; + return realDciFile.d_func()->engine()->syncToDisk(); +} + +qint64 DDciFileEngine::size() const +{ + if (fileBuffer) { + return fileData.size(); + } + + return file->dataRef(subfilePath).size(); +} + +qint64 DDciFileEngine::pos() const +{ + return fileBuffer->size(); +} + +bool DDciFileEngine::seek(qint64 pos) +{ + return fileBuffer->seek(pos); +} + +bool DDciFileEngine::isSequential() const +{ + return false; +} + +bool DDciFileEngine::remove() +{ + return file->isValid() && file->remove(subfilePath) && forceSave(); +} + +bool DDciFileEngine::copy(const QString &newName) +{ + if (!file->isValid()) + return false; + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(newName, dciFilePath); + if (paths.second.isEmpty()) + return false; + + return file->copy(subfilePath, paths.second) && forceSave(); +} + +bool DDciFileEngine::rename(const QString &newName) +{ + if (!file->isValid()) + return false; + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(newName, dciFilePath); + if (paths.second.isEmpty()) + return false; + + return file->rename(subfilePath, paths.second, false) && forceSave(); +} + +bool DDciFileEngine::renameOverwrite(const QString &newName) +{ + if (!file->isValid()) + return false; + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(newName, dciFilePath); + if (paths.second.isEmpty()) + return false; + + return file->rename(subfilePath, paths.second, true) && forceSave(); +} + +bool DDciFileEngine::link(const QString &newName) +{ + if (!file->isValid()) + return false; + + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(newName, dciFilePath); + const QString &linkPath = paths.second.isEmpty() ? newName : paths.second; + + return file->link(subfilePath, linkPath) && forceSave(); +} + +bool DDciFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const +{ + if (!file->isValid()) + return false; + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(dirName, dciFilePath); + if (paths.second.isEmpty()) + return false; + + if (!createParentDirectories) + return file->mkdir(paths.second) && forceSave(); + + const QStringList dirItems = paths.second.split('/'); + QString currentPath; + for (const QString &newDir : dirItems) { + if (newDir.isEmpty()) + continue; + currentPath += ("/" + newDir); + if (file->exists(currentPath)) { + continue; + } + // 创建此路径 + if (!file->mkdir(currentPath)) + return false; + } + + return forceSave(); +} + +bool DDciFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const +{ + if (!file->isValid()) + return false; + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(dirName, dciFilePath); + if (paths.second.isEmpty()) + return false; + + if (!file->remove(paths.second)) + return false; + if (!recurseParentDirectories) + return forceSave(); + + // 查找空的父目录 + QDir dir(paths.second); + + while (dir.cdUp()) { + // 不删除根 + if (dir.isRoot()) + break; + + if (file->childrenCount(dir.absolutePath()) > 0) + continue; + if (!file->remove(dir.absolutePath())) + return false; + } + + return forceSave(); +} + +bool DDciFileEngine::setSize(qint64 size) +{ + if (!fileBuffer) { + fileData = file->dataRef(subfilePath); + } + + // 确保新数据填充为 0 + if (size > fileData.size()) { + fileData.append(size - fileData.size(), '\0'); + } else { + fileData.resize(size); + } + + return fileBuffer ? true : forceSave(true); +} + +bool DDciFileEngine::caseSensitive() const +{ + return true; +} + +bool DDciFileEngine::isRelativePath() const +{ + return !subfilePath.startsWith('/'); +} + +QByteArray DDciFileEngine::id() const +{ + return fileName().toUtf8(); +} + +uint DDciFileEngine::ownerId(QAbstractFileEngine::FileOwner owner) const +{ + QFileInfo info(dciFilePath); + return owner == OwnerUser ? info.ownerId() : info.groupId(); +} + +QString DDciFileEngine::owner(QAbstractFileEngine::FileOwner owner) const +{ + QFileInfo info(dciFilePath); + return owner == OwnerUser ? info.owner() : info.group(); +} + +QAbstractFileEngine::FileFlags DDciFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const +{ + auto flags = QAbstractFileEngine::FileFlags(); + + if (!file->isValid()) + return flags; + + if (type & TypesMask) { + const auto fileType = file->type(subfilePath); + + if (fileType == DDciFile::Directory) { + flags |= DirectoryType; + } else if (fileType == DDciFile::File) { + flags |= FileType; + } else if (fileType == DDciFile::Symlink) { + flags |= LinkType; + } + } + + if ((type & FlagsMask)) { + if (file->exists(subfilePath)) + flags |= ExistsFlag; + + if (subfilePath == QLatin1Char('/')) + flags |= RootFlag; + } + + if ((type & PermsMask) && file->exists(subfilePath)) { + flags |= static_cast(static_cast(QFileInfo(dciFilePath).permissions())); + } + + return flags; +} + +QString DDciFileEngine::fileName(QAbstractFileEngine::FileName file) const +{ + switch (file) { + case AbsoluteName: + case CanonicalName: + case DefaultName: + return QDir::cleanPath(DCI_FILE_SCHEME + dciFilePath + subfilePath); + case AbsolutePathName: + return QDir::cleanPath(DCI_FILE_SCHEME + dciFilePath); + case BaseName: + return QFileInfo(subfilePath).baseName(); + case LinkName: + return this->file->type(subfilePath) == DDciFile::Symlink + ? this->file->symlinkTarget(subfilePath) + : QString(); + default: + break; + } + + return QString(); +} + +void DDciFileEngine::setFileName(const QString &fullPath) +{ + // 销毁旧的内容 + close(); + file.reset(nullptr); + dciFilePath.clear(); + subfilePath.clear(); + + const auto paths = resolvePath(fullPath, QString(), false); + if (paths.first.isEmpty() + || paths.second.isEmpty()) + return; + + dciFilePath = paths.first; + subfilePath = paths.second; + file = getDciFile(dciFilePath, QFile::exists(dciFilePath)); +} + +QDateTime DDciFileEngine::fileTime(QAbstractFileEngine::FileTime time) const +{ + return QFileInfo(dciFilePath).fileTime(static_cast(time)); +} + +DDciFileEngine::Iterator *DDciFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) +{ + return new DDciFileEngineIterator(filters, filterNames); +} + +DDciFileEngine::Iterator *DDciFileEngine::endEntryList() +{ + return nullptr; +} + +qint64 DDciFileEngine::read(char *data, qint64 maxlen) +{ + return fileBuffer->read(data, maxlen); +} + +qint64 DDciFileEngine::write(const char *data, qint64 len) +{ + return fileBuffer->write(data, len); +} + +bool DDciFileEngine::extension(QAbstractFileEngine::Extension extension, + const QAbstractFileEngine::ExtensionOption *option, + QAbstractFileEngine::ExtensionReturn *output) +{ + Q_UNUSED(option) + Q_UNUSED(output) + return extension == AtEndExtension && fileBuffer->atEnd(); +} + +bool DDciFileEngine::supportsExtension(QAbstractFileEngine::Extension extension) const +{ + return extension == AtEndExtension; +} + +bool DDciFileEngine::cloneTo(QAbstractFileEngine *target) +{ + const QByteArray &data = file->dataRef(subfilePath); + return target->write(data.constData(), data.size()) == data.size(); +} + +bool DDciFileEngine::forceSave(bool writeFile) const +{ + QFile file(dciFilePath); + if (!file.open(QIODevice::WriteOnly)) { + return false; + } + + return flushToFile(&file, writeFile); +} + +QPair DDciFileEngine::resolvePath(const QString &fullPath, + const QString &realFilePath, + bool needRealFileExists) +{ + if (!fullPath.startsWith(QStringLiteral(DCI_FILE_SCHEME) + realFilePath)) + return {}; + + qCDebug(logFE(), "Resolve the path: \"%s\"", qPrintable(fullPath)); + // 此路径来源于调用方,将其格式化为标准格式,尾部添加 "/" 以确保下文中得到的 + // subfilePath 绝对不为空 + QString formatFullPath = QDir::cleanPath(fullPath) + "/"; + QString dciFilePath = realFilePath, subfilePath; + const int schemeLength = qstrlen(DCI_FILE_SCHEME); + const int suffixLength = qstrlen(DCI_FILE_SUFFIX); + + if (dciFilePath.isEmpty()) { + // 尾部加 "/" 是确保 ".dci" 为一个文件的结尾 + int dciSuffixIndex = formatFullPath.indexOf(DCI_FILE_SUFFIX "/", schemeLength); + + while (dciSuffixIndex > 0) { + dciSuffixIndex += suffixLength; + dciFilePath = formatFullPath.mid(schemeLength, dciSuffixIndex - schemeLength); + // 查找一个有效的后缀名是 ".dci" 的文件 + if (needRealFileExists) { + if (QFileInfo(dciFilePath).isFile()) + break; + } else { + QFileInfo info(dciFilePath); + // 不存在的文件允许被新建 + if (!info.exists() && !info.isSymLink()) + break; + } + + dciSuffixIndex = dciFilePath.indexOf(DCI_FILE_SUFFIX, dciSuffixIndex + 1); + } + } else { + qCDebug(logFE(), "The base file path of user is: \"%s\"", qPrintable(realFilePath)); + } + + // 未找到有效的 dci 文件 + if (dciFilePath.isEmpty()) + return {}; + + subfilePath = QDir::cleanPath(formatFullPath.mid(schemeLength + dciFilePath.length())); + qCDebug(logFE(), "The DCI file path is: \"%s\", the subfile path is: \"%s\"", + qPrintable(dciFilePath), qPrintable(subfilePath)); + Q_ASSERT(!subfilePath.isEmpty()); + + return qMakePair(dciFilePath, subfilePath); +} + +DCORE_END_NAMESPACE diff --git a/src/dci/private/ddcifileengine_p.h b/src/dci/private/ddcifileengine_p.h new file mode 100644 index 0000000..5a2b84c --- /dev/null +++ b/src/dci/private/ddcifileengine_p.h @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#ifndef DTK_NO_PROJECT +#include +#else +#define DCORE_BEGIN_NAMESPACE +#define DCORE_END_NAMESPACE +#endif +#include + +#include +#include + +QT_BEGIN_NAMESPACE +class QBuffer; +QT_END_NAMESPACE + +DCORE_BEGIN_NAMESPACE + +class DDciFileEngineHandler : public QAbstractFileEngineHandler +{ +public: + QAbstractFileEngine *create(const QString &fileName) const override; +}; + +class DDciFile; +using DDciFileShared = QSharedPointer; +class DDciFileEngineIterator : public QAbstractFileEngineIterator +{ + friend class DDciFileEngine; +public: + DDciFileEngineIterator(QDir::Filters filters, const QStringList &nameFilters); + + QString next() override; + bool hasNext() const override; + + QString currentFileName() const override; + +private: + mutable DDciFileShared file; + mutable QStringList list; + mutable int nextValid = -1; + int current = -1; +}; + +class DDciFileEngine : public QAbstractFileEngine +{ + friend class DDciFileEngineIterator; +public: + explicit DDciFileEngine(const QString &fullPath); + ~DDciFileEngine(); + + bool isValid() const; + bool open(QIODevice::OpenMode openMode) override; + bool close() override; + bool flushToFile(QFile *target, bool writeFile) const; + bool flush() override; + bool syncToDisk() override; + + qint64 size() const override; + qint64 pos() const override; + bool seek(qint64 pos) override; + bool isSequential() const override; + bool remove() override; + bool copy(const QString &newName) override; + bool rename(const QString &newName) override; + bool renameOverwrite(const QString &newName) override; + bool link(const QString &newName) override; + bool mkdir(const QString &dirName, bool createParentDirectories) const override; + bool rmdir(const QString &dirName, bool recurseParentDirectories) const override; + bool setSize(qint64 size) override; + bool caseSensitive() const override; + bool isRelativePath() const override; + + QByteArray id() const override; + uint ownerId(FileOwner owner) const override; + QString owner(FileOwner owner) const override; + + FileFlags fileFlags(FileFlags type = FileInfoAll) const override; + QString fileName(FileName file = DefaultName) const override; + + void setFileName(const QString &fullPath) override; + + QDateTime fileTime(FileTime time) const override; + + typedef DDciFileEngineIterator Iterator; + Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override; + Iterator *endEntryList() override; + + qint64 read(char *data, qint64 maxlen) override; + qint64 write(const char *data, qint64 len) override; + + bool extension(Extension extension, const ExtensionOption *option = 0, + ExtensionReturn *output = 0) override; + bool supportsExtension(Extension extension) const override; + + bool cloneTo(QAbstractFileEngine *target) override; + +private: + bool forceSave(bool writeFile = false) const; + + /* + * fullPath 格式:"dci:" + "真实文件路径" + "DCI 内部文件的路径" + * 例如:"dci:/home/user/test.dci/subfile.png" + * 其中 "/home/user/test.dci" 为真实文件路径,"/subfile.png" + * 是 DCI 文件的内部路径。 + * 函数返回的第一个数据是"真实文件路径"。 + */ + static QPair resolvePath(const QString &fullPath, + const QString &realFilePath = QString(), + bool needRealFileExists = true); + + DDciFileShared file; + QString dciFilePath; + QFile realDciFile; + QString subfilePath; + + QByteArray fileData; + QBuffer *fileBuffer = nullptr; +}; + + +DCORE_END_NAMESPACE diff --git a/src/dconfig.cpp b/src/dconfig.cpp new file mode 100644 index 0000000..86e244e --- /dev/null +++ b/src/dconfig.cpp @@ -0,0 +1,676 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dconfig.h" +#ifndef D_DISABLE_DCONFIG +#include "dconfigfile.h" +#ifndef D_DISABLE_DBUS_CONFIG +#include "configmanager_interface.h" +#include "manager_interface.h" +#endif +#else +#include +#endif +#include "dobject_p.h" +#include + +#include +#include +#include + +// https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/3 + +DCORE_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(cfLog) + +/*! + \class Dtk::Core::DConfigBackend + \inmodule dtkcore + + \brief 配置后端的抽象接口. + + 所有DConfig使用的配置后端都继承此类,用户可以继承此类实现自己的配置后端. + */ + +/*! + \fn bool DConfigBackend::load(const QString &) = 0 + + \brief 初始化后端 + + \a appId 管理的配置信息key值,默认为应用程序名称 + */ + +/*! + \fn bool DConfigBackend::isValid() const = 0 + + \sa DConfig::isValid(). + + */ + +/*! + \fn QStringList DConfigBackend::keyList() const = 0 + + \sa DConfig::keyList() + + */ + +/*! + \fn QVariant DConfigBackend::value(const QString &key, const QVariant &fallback = QVariant()) const = 0 + + \sa DConfig::value() + */ + +/*! + \fn void DConfigBackend::setValue(const QString &key, const QVariant &value) = 0 + + \sa DConfig::setValue() + */ + +/*! + \fn void DConfigBackend::reset(const QString &key) + + \sa DConfig::reset() + */ + +/*! + \fn QString DConfigBackend::name() const = 0 + + \brief 后端配置的唯一标识 + + */ + +DConfigBackend::~DConfigBackend() +{ +} + +class Q_DECL_HIDDEN DConfigPrivate : public DObjectPrivate +{ +public: + explicit DConfigPrivate(DConfig *qq, + const QString &appId, + const QString &name, + const QString &subpath) + : DObjectPrivate(qq) + , appId(appId.isEmpty() ? DSGApplication::id() : appId) + , name(name) + , subpath(subpath) + { + } + + virtual ~DConfigPrivate() override; + + inline bool invalid() const + { + const bool valid = backend && backend->isValid(); + if (!valid) + qCWarning(cfLog, "DConfig is invalid of appid=%s name=%s, subpath=%s", + qPrintable(appId), qPrintable(name), qPrintable(subpath)); + + return !valid; + } + + DConfigBackend *getOrCreateBackend(); + DConfigBackend *createBackendByEnv(); + + QString appId; + QString name; + QString subpath; + QScopedPointer backend; + + D_DECLARE_PUBLIC(DConfig) +}; + +namespace { + +#ifndef D_DISABLE_DCONFIG +class Q_DECL_HIDDEN FileBackend : public DConfigBackend +{ +public: + explicit FileBackend(DConfigPrivate *o) + : owner(o) + { + } + + virtual ~FileBackend() override; + + virtual bool isValid() const override + { + return configFile && configFile->isValid(); + } + + virtual bool load(const QString &/*appId*/) override + { + if (configFile) + return true; + + configFile.reset(new DConfigFile(owner->appId,owner->name, owner->subpath)); + configCache.reset(configFile->createUserCache(getuid())); + const QString &prefix = localPrefix(); + + return configFile->load(prefix) && + configCache->load(prefix); + } + + virtual QStringList keyList() const override + { + return configFile->meta()->keyList(); + } + + virtual QVariant value(const QString &key, const QVariant &fallback) const override + { + const QVariant &v = configFile->value(key, configCache.get()); + return v.isValid() ? v : fallback; + } + + virtual void setValue(const QString &key, const QVariant &value) override + { + // setValue's callerAppid is itself instead of config's appId. + if (configFile->setValue(key, value, DSGApplication::id(), configCache.get())) { + Q_EMIT owner->q_func()->valueChanged(key); + } + } + + virtual void reset(const QString &key) override + { + const auto &originValue = configFile->meta()->value(key); + setValue(key, originValue); + } + + virtual QString name() const override + { + return QString("FileBackend"); + } + +private: + QString localPrefix() const + { + if (!envLocalPrefix.isEmpty()) { + return QString::fromLocal8Bit(envLocalPrefix); + } + return QString(); + } + +private: + QScopedPointer configFile; + QScopedPointer configCache; + DConfigPrivate* owner; + const QByteArray envLocalPrefix = qgetenv("DSG_DCONFIG_FILE_BACKEND_LOCAL_PREFIX"); +}; + +FileBackend::~FileBackend() +{ + const QString &prefix = localPrefix(); + if (configCache) { + configCache->save(prefix); + configCache.reset(); + } + if (configFile) { + configFile->save(prefix); + configFile.reset(); + } +} + +#ifndef D_DISABLE_DBUS_CONFIG + +#define DSG_CONFIG "org.desktopspec.ConfigManager" +#define DSG_CONFIG_MANAGER "org.desktopspec.ConfigManager" + +class Q_DECL_HIDDEN DBusBackend : public DConfigBackend +{ +public: + explicit DBusBackend(DConfigPrivate* o): + owner(o) + { + } + + virtual ~DBusBackend() override; + + static bool isServiceRegistered() + { + return QDBusConnection::systemBus().interface()->isServiceRegistered(DSG_CONFIG); + } + + static bool isServiceActivatable() + { + const QDBusReply activatableNames = QDBusConnection::systemBus().interface()-> + callWithArgumentList(QDBus::AutoDetect, + QLatin1String("ListActivatableNames"), + QList()); +// qInfo() << activatableNames.value() << activatableNames.value().contains(DSG_CONFIG); + + return activatableNames.value().contains(DSG_CONFIG); + } + + virtual bool isValid() const override + { + return config && config->isValid(); + } + + /*! + \internal + + 初始化DBus连接,会先调用acquireManager动态获取一个配置连接, + 再通过这个配置连接进行配置文件的访问. + */ + virtual bool load(const QString &/*appId*/) override + { + if (config) + return true; + + qCDebug(cfLog, "Try acquire config manager object form DBus"); + DSGConfig dsg_config(DSG_CONFIG, "/", QDBusConnection::systemBus()); + QDBusPendingReply dbus_reply = dsg_config.acquireManager(owner->appId, owner->name, owner->subpath); + const QDBusObjectPath dbus_path = dbus_reply.value(); + if (dbus_reply.isError() || dbus_path.path().isEmpty()) { + qCWarning(cfLog, "Can't acquire config manager. error:\"%s\"", qPrintable(dbus_reply.error().message())); + return false; + } else { + qCDebug(cfLog(), "dbus path=\"%s\"", qPrintable(dbus_path.path())); + config.reset(new DSGConfigManager(DSG_CONFIG_MANAGER, dbus_path.path(), + QDBusConnection::systemBus(), owner->q_func())); + if (!config->isValid()) { + qCWarning(cfLog(), "Can't acquire config path=\"%s\"", qPrintable(dbus_path.path())); + config.reset(); + return false; + } else { + QObject::connect(config.data(), &DSGConfigManager::valueChanged, owner->q_func(), &DConfig::valueChanged); + } + } + return true; + } + + virtual QStringList keyList() const override + { + return config->keyList(); + } + + static QVariant decodeQDBusArgument(const QVariant &v) + { + if (v.canConvert()) { + // we use QJsonValue to resolve all data type in DConfigInfo class, so it's type is equal QJsonValue::Type, + // now we parse Map and Array type to QVariant explicitly. + const QDBusArgument &complexType = v.value(); + switch (complexType.currentType()) { + case QDBusArgument::MapType: { + QVariantMap list; + complexType >> list; + QVariantMap res; + for (auto iter = list.begin(); iter != list.end(); iter++) { + res[iter.key()] = decodeQDBusArgument(iter.value()); + } + return res; + } + case QDBusArgument::ArrayType: { + QVariantList list; + complexType >> list; + QVariantList res; + res.reserve(list.size()); + for (const auto &item : qAsConst(list)) { + res << decodeQDBusArgument(item); + } + return res; + } + default: + qWarning("Can't parse the type, it maybe need user to do it, " + "QDBusArgument::ElementType: %d.", complexType.currentType()); + } + } + return v; + } + + virtual QVariant value(const QString &key, const QVariant &fallback) const override + { + auto reply = config->value(key); + reply.waitForFinished(); + if (reply.isError()) { + qWarning() << "value error key:" << key << ", error message:" << reply.error().message(); + return fallback; + } + return decodeQDBusArgument(reply.value().variant()); + } + + virtual void setValue(const QString &key, const QVariant &value) override + { + config->setValue(key, QDBusVariant(value)); + } + + virtual void reset(const QString &key) override + { + config->reset(key); + } + + virtual QString name() const override + { + return QString("DBusBackend"); + } + +private: + QScopedPointer config; + DConfigPrivate* owner; +}; + +DBusBackend::~DBusBackend() +{ + if (config) { + config->release(); + } +} +#endif //D_DISABLE_DBUS_CONFIG +#else + +class Q_DECL_HIDDEN QSettingBackend : public DConfigBackend +{ +public: + explicit QSettingBackend(DConfigPrivate* o): + owner(o) + { + } + + virtual ~QSettingBackend() override; + + virtual bool isValid() const override + { + return settings; + } + + virtual bool load(const QString &appid) override + { + Q_UNUSED(appid); + + if (settings) + return true; + + settings = new QSettings(owner->name, QSettings::IniFormat, owner->q_func()); + settings->beginGroup(owner->subpath); + return true; + } + + virtual QStringList keyList() const override + { + return settings->childKeys(); + } + + virtual QVariant value(const QString &key, const QVariant &fallback) const override + { + return settings->value(key, fallback); + } + + virtual void setValue(const QString &key, const QVariant &value) override + { + settings->setValue(key, value); + } + + virtual QString name() const override + { + return QString("QSettingBackend"); + } + +private: + QSettings *settings = nullptr; + DConfigPrivate* owner; +}; + +QSettingBackend::~QSettingBackend() +{ +} + +#endif //D_DISABLE_DCONFIG +} + +DConfigPrivate::~DConfigPrivate() +{ + backend.reset(); +} + +/*! + \internal + + \brief 创建一个配置后端 + + 默认使用的配置后端会优先根据环境变量来选择配置中心的D-Bus接口还是文件配置后端接口。 + 若没有配置此环境变量,则根据是否有配置中心提供D-Bus服务来选择配置中心服务还是文件配置后端接口. + */ +DConfigBackend *DConfigPrivate::getOrCreateBackend() +{ + if (backend) { + return backend.data(); + } + if (auto backendEnv = createBackendByEnv()) { + backend.reset(backendEnv); + return backend.data(); + } +#ifndef D_DISABLE_DCONFIG +#ifndef D_DISABLE_DBUS_CONFIG + if (DBusBackend::isServiceRegistered() || DBusBackend::isServiceActivatable()) { + qCDebug(cfLog, "Fallback to DBus mode"); + backend.reset(new DBusBackend(this)); + } + if (!backend) { + qCDebug(cfLog, "Can't use DBus config service, fallback to DConfigFile mode"); + backend.reset(new FileBackend(this)); + } +#else + backend.reset(new FileBackend(this)); +#endif //D_DISABLE_DBUS_CONFIG +#else + qCDebug(cfLog, "Fallback to QSettings mode"); + backend.reset(new QSettingBackend(this)); +#endif //D_DISABLE_DCONFIG + return backend.data(); +} + +/*! + \internal + + \brief 创建一个配置后端 + + 尝试根据环境变量来选择配置中心的D-Bus接口还是文件配置后端接口。 + */ +DConfigBackend *DConfigPrivate::createBackendByEnv() +{ + const QByteArray &envBackend = qgetenv("DSG_DCONFIG_BACKEND_TYPE"); + if (!envBackend.isEmpty()) { + if (envBackend == "DBusBackend") { + +#ifndef D_DISABLE_DCONFIG +#ifndef D_DISABLE_DBUS_CONFIG + if (DBusBackend::isServiceRegistered() || DBusBackend::isServiceActivatable()) { + qCDebug(cfLog, "Fallback to DBus mode"); + return new DBusBackend(this); + } +#endif //D_DISABLE_DBUS_CONFIG +#endif //D_DISABLE_DCONFIG + } else if (envBackend == "FileBackend") { + +#ifndef D_DISABLE_DCONFIG + qCDebug(cfLog, "Fallback to DConfigFile mode"); + return new FileBackend(this); +#endif //D_DISABLE_DCONFIG + } else { + +#ifndef D_DISABLE_DCONFIG +#else + qCDebug(cfLog, "Fallback to QSettings mode"); + return new QSettingBackend(this); +#endif //D_DISABLE_DCONFIG + } + } + return nullptr; +} + +/*! + \class Dtk::Core::DConfig + \inmodule dtkcore + + \brief 配置策略提供的接口类 + + 此接口规范定义了开发库所提供的关于配置文件读写的相关接口, + 如果应用程序所使用的开发库实现了此规范,则程序应当优先使用开发库提供的接口。 + */ + + +/*! + * \brief 构造配置策略提供的对象 + * \a name 配置文件名 + * \a subpath 配置文件对应的子目录 + * \a parent 父对象 + */ +DConfig::DConfig(const QString &name, const QString &subpath, QObject *parent) + : DConfig(nullptr, name, subpath, parent) +{ +} + +DConfig::DConfig(DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent) + : DConfig(backend, QString(), name, subpath, parent) +{ + +} +/*! + * \brief 构造配置策略提供的对象, 指定配置所属的应用Id + * \a appId + * \a name + * \a subpath + * \a parent + * \return 构造的配置策略对象,由调用者释放 + */ +DConfig *DConfig::create(const QString &appId, const QString &name, const QString &subpath, QObject *parent) +{ + return new DConfig(nullptr, appId, name, subpath, parent); +} + +DConfig *DConfig::create(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent) +{ + return new DConfig(backend, appId, name, subpath, parent); +} + +/*! + * \brief 使用自定义的配置策略后端构造对象 + * \a backend 调用者继承于DConfigBackend的配置策略后端 + * \a appId 配置文件所属的应用Id,为空时默认为本应用Id + * \a name 配置文件名 + * \a subpath 配置文件对应的子目录 + * \a parent 父对象 + * \note 调用者只构造backend,由DConfig释放。 + */ +DConfig::DConfig(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent) + : QObject(parent) + , DObject(*new DConfigPrivate(this, appId, name, subpath)) +{ + D_D(DConfig); + + Q_ASSERT(!d->appId.isEmpty()); + + qCDebug(cfLog, "Load config of appid=%s name=%s, subpath=%s", + qPrintable(d->appId), qPrintable(d->name), qPrintable(d->subpath)); + + if (backend) { + d->backend.reset(backend); + } + + if (auto backend = d->getOrCreateBackend()) { + backend->load(d->appId); + } +} + +/*! + * \brief DConfig::backendName + * \return 配置策略后端名称 + * \note 调用者只能用DConfig访问DConfigBackend对象,所以不返回DConfigBackend对象。 + */ +QString DConfig::backendName() const +{ + D_DC(DConfig); + if (d->invalid()) + return QString(); + + return d->backend->name(); +} + +/*! + * \brief 获得所有可用的配置项名称 + * \return 配置项名称集合 + */ +QStringList DConfig::keyList() const +{ + D_DC(DConfig); + if (d->invalid()) + return QStringList(); + + return d->backend->keyList(); +} + +/*! + * \brief 判断此后端是否可用 + * \return + */ +bool DConfig::isValid() const +{ + D_DC(DConfig); + return !d->invalid(); +} + +/*! + * \brief 根据配置项名称获得对应值 + * \param key 配置项名称 + * \param fallback 没有获取到配置项值后提供的默认值 + * \return + */ +QVariant DConfig::value(const QString &key, const QVariant &fallback) const +{ + D_DC(DConfig); + if (d->invalid()) + return fallback; + + return d->backend->value(key, fallback); +} + +/*! + * \brief 根据配置项名称设置其值 + * \param 配置项名称 + * \param 需要更新的值 + */ +void DConfig::setValue(const QString &key, const QVariant &value) +{ + D_D(DConfig); + if (d->invalid()) + return; + + d->backend->setValue(key, value); +} + +/*! + * \brief 设置其配置项对应的默认值,此值为经过override机制覆盖后的值,不一定为此配置文件中meta中定义的值 + * \param 配置项名称 + */ +void DConfig::reset(const QString &key) +{ + D_D(DConfig); + if (d->invalid()) + return; + + d->backend->reset(key); +} + +/*! + * \brief 返回配置文件名称 + * \return + */ +QString DConfig::name() const +{ + D_DC(DConfig); + return d->name; +} + +/*! + * \brief 返回配置文件对应的子目录 + * \return + */ +QString DConfig::subpath() const +{ + D_DC(DConfig); + return d->subpath; +} + +DCORE_END_NAMESPACE diff --git a/src/dconfigfile.cpp b/src/dconfigfile.cpp new file mode 100644 index 0000000..6b875a4 --- /dev/null +++ b/src/dconfigfile.cpp @@ -0,0 +1,1363 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dconfigfile.h" + +#include "dobject_p.h" +#include "filesystem/dstandardpaths.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/3 + +DCORE_BEGIN_NAMESPACE + +#ifndef QT_DEBUG +Q_LOGGING_CATEGORY(cfLog, "dtk.dsg.config" , QtInfoMsg); +#else +Q_LOGGING_CATEGORY(cfLog, "dtk.dsg.config"); +#endif + +#define FILE_SUFFIX QLatin1String(".json") + +/*! + \internal + + \brief 按子目录查找机制查找配置文件 + + 在 \a baseDir目录下,查找名称为 \a name的文件, + 若存在 \a subpath,则从 \a subpath叶子目录逐级向上查找名称为 \a name的文件, + 若不存在此文件,则返回无效路径. + */ +inline QString getFile(const QString &baseDir, const QString &subpath, const QString &name, + bool canFallbackUp = true) { + qCDebug(cfLog, "load json file from base dir:\"%s\", subpath = \"%s\", file name =\"%s\".", + qPrintable(baseDir), qPrintable(subpath), qPrintable(name)); + + const QDir base_dir(baseDir); + QDir target_dir = base_dir; + + if (!subpath.isEmpty()) + target_dir.cd(subpath.mid(1)); + + do { + qCDebug(cfLog, "load json file from: \"%s\"", qPrintable(target_dir.path())); + + if (QFile::exists(target_dir.filePath(name))) { + return target_dir.filePath(name); + } + + if (base_dir == target_dir) + break; + } while (canFallbackUp && target_dir.cdUp()); + + return QString(); +} + +inline QFile *loadFile(const QString &baseDir, const QString &subpath, const QString &name, + bool canFallbackUp = true) +{ + QString path = getFile(baseDir, subpath, name, canFallbackUp); + if (!path.isEmpty()) { + return new QFile(path); + } + return nullptr; +} + +static QJsonDocument loadJsonFile(QIODevice *data) +{ + if (!data->open(QIODevice::ReadOnly)) { + if (auto file = qobject_cast(data)) { + qCDebug(cfLog, "Falied on open file: \"%s\", error message: \"%s\"", + qPrintable(file->fileName()), qPrintable(file->errorString())); + } + return QJsonDocument(); + } + + QJsonParseError error; + auto document = QJsonDocument::fromJson(data->readAll(), &error); + data->close(); + + if (error.error != QJsonParseError::NoError) { + qCWarning(cfLog, "%s", qPrintable(error.errorString())); + return QJsonDocument(); + } + + return document; +} + +static DConfigFile::Version parseVersion(const QJsonObject &obj) { + DConfigFile::Version version {0, 0}; + const QString &verStr = obj[QLatin1String("version")].toString(); + + if (verStr.isEmpty()) { + return version; + } + + const QStringList &items = verStr.split(QLatin1Char('.')); + + if (items.size() != 2) + return version; + + bool ok = false; + quint16 major = items.first().toUShort(&ok); + + if (!ok) + return version; + + quint16 minor = items.last().toUShort(&ok); + + if (!ok) + return version; + + version.major = major; + version.minor = minor; + + return version; +} + +#define MAGIC_META QLatin1String("dsg.config.meta") +#define MAGIC_OVERRIDE QLatin1String("dsg.config.override") +#define MAGIC_CACHE QLatin1String("dsg.config.cache") + +static const uint InvalidUID = 0xFFFF; + +inline static bool checkMagic(const QJsonObject &obj, QLatin1String request) { + return obj[QLatin1String("magic")].toString() == request; +} + +inline static bool versionIsValid(const DConfigFile::Version &v) { + return v.major > 0 || v.minor > 0; +} + +inline static bool checkVersion(const QJsonObject &obj, const DConfigFile::Version &request) { + const DConfigFile::Version &v = parseVersion(obj); + return versionIsValid(v) && v.major == request.major; +} + +inline void overrideValue(QLatin1String subkey, const QJsonValue &from, QVariantHash &target) { + const QJsonValue &v = from[subkey]; + + if (!v.isUndefined()) + target[subkey] = v.toVariant(); +} + +inline static QString getUserName(const uint uid) { + passwd *pw = getpwuid(uid); + return pw ? QString::fromLocal8Bit(pw->pw_name) : QString(); +} + +/*! + \class Dtk::Core::DConfigFile + \inmodule dtkcore + + \brief 规范配置文件读写的相关接口的配置文件实现. + */ + +/*! + \enum DConfigFile::Flag + + \value NoOverride 存在此标记时,将表明则此配置项不可被覆盖(详见下述 override 机制)。 + 反之,不存在此标记时表明此配置项允许被覆盖,对于此类配置项, + 如若其有界面设置入口,则当此项不可写时,应当隐藏或禁用界面的设置入口. + \value Global 当读写此类配置时,将忽略用户身份,无论程序使用哪个用户身份执行,读操作都将获取到同样的数据, + 写操作将对所有用户都生效。但是,如果对应的配置存储目录不存在或无权限写入,则忽略此标志. +*/ + +/*! + \enum DConfigFile::Permissions + + \value ReadOnly 将配置项覆盖为只读, + \value ReadWrite 将配置项覆盖为可读可写. +*/ + +/*! + \enum DConfigFile::Visibility + + \value Private 仅限程序内部使用, + 对外不可见。此类配置项完全由程序自己读写,可随意增删改写其含义,无需做兼容性考虑, + \value Public 外部程序可使用。 + 此类配置项一旦发布,在兼容性版本的升级中,要保障此配置项向下兼容, + 简而言之,只允许在程序/库的大版本升级时才允许删除或修改此类配置项, + 当配置项的 permissions、visibility、flags 任意一个属性被修改则认为此配置项被修改, + 除此之外修改 value、name、description 属性时则不需要考虑兼容性. +*/ + +/*! + \struct Dtk::Core::DConfigFile::Version + \inmodule dtkcore + \brief 版本信息 + + 此文件的内容格式的版本。版本号使用两位数字描述, + 首位数字不同的描述文件相互之间不兼容,第二位数字不同的描述文件需满足向下兼容。 + 读取此描述文件的程序要根据版本进行内容分析,当遇到不兼容的版本时,需要立即终止解析,忽略此文件, + 并在程序日志中写入警告信息,如 “1.0” 和 “2.0” 版本之间不兼容, + 如果解析程序最高只支持 1.0 版本,则遇到 2.0 版本的描述文件时应该终止解析, + 但是如果遇到 1.1 版本,则可以继续执行。 + 写入此描述文件时,遇到不兼容的版本时,需要先清空当前内容再写入,每次写入皆需更新此字段。 +*/ + +DConfigMeta::~DConfigMeta() {} + +Dtk::Core::DConfigCache::~DConfigCache() {} + +struct DConfigKey { + DConfigKey(const QString &aappId, const QString &afileName, const QString &asubpath) + : appId(aappId), + fileName(afileName), + subpath(asubpath) + { + } + + explicit DConfigKey(const DConfigKey &src) + : DConfigKey(src.appId, src.fileName, src.subpath) + { + } + + DConfigKey &operator = (const DConfigKey &src) + { + this->appId = src.appId; + this->fileName = src.fileName; + this->subpath = src.subpath; + return *this; + } + + QString appId; + QString fileName; + QString subpath; +}; + +class Q_DECL_HIDDEN DConfigInfo { +public: + DConfigInfo() + { + + } + DConfigInfo(const DConfigInfo &other) + { + this->values = other.values; + } + DConfigInfo operator = (const DConfigInfo &other) + { + this->values = other.values; + return *this; + } + inline static bool checkSerial(const int metaSerial, const int cacheSerial) + { + if (cacheSerial < 0) + return true; + if (metaSerial >= 0 && metaSerial == cacheSerial) + return true; + return false; + } + + DConfigFile::Visibility visibility(const QString &key) const + { + DConfigFile::Visibility p = DConfigFile::Private; + const auto &tmp = values[key][QLatin1String("visibility")].toString(); + if (tmp == QLatin1String("public")) + p = DConfigFile::Public; + + return p; + } + + DConfigFile::Permissions permissions(const QString &key) const + { + DConfigFile::Permissions p = DConfigFile::ReadOnly; + const auto &tmp = values[key][QLatin1String("permissions")].toString(); + if (tmp == QLatin1String("readwrite")) + p = DConfigFile::ReadWrite; + + return p; + } + + DConfigFile::Flags flags(const QString &key) const + { + DConfigFile::Flags flags = {}; + const auto &tmp = values[key][QLatin1String("flags")]; + Q_FOREACH(const QString &flag, tmp.toStringList()) { + if (flag == QLatin1String("nooverride")) { + flags |= DConfigFile::NoOverride; + } else if (flag == QLatin1String("global")) { + flags |= DConfigFile::Global; + } + } + + return flags; + } + + QString displayName(const QString &key, const QLocale &locale) const + { + if (locale == QLocale::AnyLanguage) + return values[key][QLatin1String("name")].toString(); + + return values[key].value(QString("name[%1]") + .arg(locale.name())).toString(); + } + + QString description(const QString &key, const QLocale &locale) const + { + if (locale == QLocale::AnyLanguage) + return values[key][QLatin1String("description")].toString(); + + return values[key].value(QString("description[%1]") + .arg(locale.name())).toString(); + } + + inline QVariant value(const QString &key) const + { + return values[key][QLatin1String("value")]; + } + + inline int serial(const QString &key) const + { + bool status = false; + const int tmp = values[key][QLatin1String("serial")].toInt(&status); + if (status) { + return tmp; + } + return -1; + } + + inline void setValue(const QString &key, const QVariant &value) + { + values[key]["value"] = value; + } + + inline void setSerial(const QString &key, const int &value) + { + values[key]["serial"] = value; + } + + inline void setTime(const QString &key, const QString &value) + { + values[key]["time"] = value; + } + + inline void setUser(const QString &key, const uint &value) + { + values[key]["user"] = getUserName(value); + } + + inline void setAppId(const QString &key, const QString &value) + { + values[key]["appid"] = value; + } + + inline QStringList keyList() const + { + return values.keys(); + } + + inline void remove(const QString &key) + { + values.remove(key); + } + + inline bool update(const QString &key, const QVariantHash &value) + { + if (!value.contains("value")) { + return false; + } + values[key] = value; + return true; + } + + inline bool updateValue(const QString &key, const QJsonValue &value) + { + return overrideValue(key, "value", value); + } + + inline void updateSerial(const QString &key, const QJsonValue &value) + { + overrideValue(key, "serial", value); + } + + inline void updatePermissions(const QString &key, const QJsonValue &value) + { + overrideValue(key, "permissions", value); + } + + QJsonObject content() const + { + QJsonObject contents; + for (auto i = values.constBegin(); i != values.constEnd(); ++i) { + contents[i.key()] = QJsonObject::fromVariantHash(i.value()); + } + return contents; + } +private: + bool overrideValue(const QString &key, const QString &subkey, const QJsonValue &from) { + const QJsonValue &v = from[subkey]; + + if (v.isUndefined()) { + return false; + } + + values[key][subkey] = v.toVariant(); + return true; + } + + QHash values; +}; + + +/*! + \class Dtk::Core::DConfigMeta + \inmodule dtkcore + + \brief 提供配置文件的原型和覆盖机制的访问接口. + +*/ + +/*! + \fn DConfigFile::Version DConfigMeta::version() const = 0; + + \brief 返回配置版本信息. + \return +*/ + +/*! + \fn void DConfigMeta::setVersion(quint16 major, quint16 minor) = 0; + + \brief 设置配置版本信息 + \a major 主板本号 + \a minor 次版本号 +*/ + +/*! + \fn bool DConfigMeta::load(const QString &localPrefix = QString()) = 0; + + \brief 解析配置文件 + \a localPrefix 为目录前缀 + \return +*/ + +/*! + \fn bool DConfigMeta::load(QIODevice *meta, const QList &overrides) = 0; + + \brief 解析配置文件流 + \a meta 为原型流 + \a overrides 为覆盖机制查找的文件流 + \return +*/ + +/*! + \fn QStringList DConfigMeta::keyList() const = 0; + + \brief 返回配置内容的所有配置项 + \return +*/ + +/*! + \fn DConfigFile::Flags DConfigMeta::flags(const QString &key) const = 0; + + \brief 返回指定配置项的特性 + \a key 配置项名称, NoOverride为此配置项不可被覆盖, Global为忽略用户身份 + \return +*/ + +/*! + \fn DConfigFile::Permissions DConfigMeta::permissions(const QString &key) const = 0; + + \brief 返回指定配置项的权限 + \a key 配置项名称 + \return + +*/ + +/*! + \fn DConfigFile::Visibility DConfigMeta::visibility(const QString &key) const = 0; + + \brief 返回指定配置项的可见性 + \a key 配置项名称 + \return + +*/ + +/*! + \fn int DConfigMeta::serial(const QString &key) const = 0; + + \brief 返回配置项的单调递增值 + \a key 配置项名称 + \return -1为无效值,表明没有配置此项 +*/ + +/*! + \fn QString DConfigMeta::displayName(const QString &key, const QLocale &locale) = 0; + + \brief 返回指定配置项的显示名 + \a key 配置项名称 + \a locale 为语言版本 + \return +*/ + +/*! + \fn QString DConfigMeta::description(const QString &key, const QLocale &locale) = 0; + + \brief 返回指定配置项的描述信息 + \a key 配置项名称 + \a locale 为语言版本 + \return + +*/ + +/*! + \fn QString DConfigMeta::metaPath(const QString &localPrefix = QString(), bool *useAppId = nullptr) const = 0; + + \brief 返回描述文件的路径 + \a localPrefix 目录的所有需要查找的覆盖机制目录 + \return +*/ + +/*! + \fn QStringList DConfigMeta::allOverrideDirs(const bool useAppId, const QString &prefix = QString()) const = 0; + + \brief 获得前缀为 \a prefix 目录的所有需要查找的覆盖机制目录 + \a userAppId 是否不使用通用目录 + \return +*/ + +/*! + \fn QVariant DConfigMeta::value(const QString &key) const = 0; + + \brief meta初始值经过覆盖机制覆盖后的原始值 + \a key 配置项名称 + \return +*/ + +class Q_DECL_HIDDEN DConfigMetaImpl : public DConfigMeta { + // DConfigMeta interface +public: + explicit DConfigMetaImpl(const DConfigKey &configKey); + virtual ~DConfigMetaImpl() override; + + inline virtual QStringList keyList() const override + { + return values.keyList(); + } + inline virtual DConfigFile::Flags flags(const QString &key) const override + { + return values.flags(key); + } + inline virtual DConfigFile::Permissions permissions(const QString &key) const override + { + return values.permissions(key); + } + inline virtual DConfigFile::Visibility visibility(const QString &key) const override + { + return values.visibility(key); + } + inline virtual int serial(const QString &key) const override + { + return values.serial(key); + } + inline virtual QString description(const QString &key, const QLocale &locale) override + { + return values.description(key, locale); + } + virtual DConfigFile::Version version() const override + { + return m_version; + } + inline virtual void setVersion(quint16 major, quint16 minor) override + { + m_version.major = major; + m_version.minor = minor; + } + inline virtual QString displayName(const QString &key, const QLocale &locale) override + { + return values.displayName(key, locale); + } + inline virtual QVariant value(const QString &key) const override + { + return values.value(key); + } + + inline QStringList applicationMetaDirs(const QString &prefix) const + { + QStringList paths; + // lower priority is higher. + const auto &dataPaths = DStandardPaths::paths(DStandardPaths::DSG::DataDir); + paths.reserve(dataPaths.size()); + for (auto item : dataPaths) { + paths.prepend(QString("%1/%2/configs/%3").arg(prefix, item, configKey.appId)); + } + return paths; + } + + inline static QStringList genericMetaDirs(const QString &prefix) { + QStringList paths; + for (auto item: DStandardPaths::paths(DStandardPaths::DSG::DataDir)) { + paths.prepend(QString("%1/%2/configs").arg(prefix, item)); + } + return paths; + } + + QString metaPath(const QString &localPrefix, bool *useAppId) const override + { + bool useAppIdForOverride = true; + + QString path; + const QStringList &applicationMetas = applicationMetaDirs(localPrefix); + for (auto iter = applicationMetas.rbegin(); iter != applicationMetas.rend(); iter++) { + path = getFile(*iter, configKey.subpath, configKey.fileName + FILE_SUFFIX); + if (!path.isEmpty()) + break; + } + + if (path.isEmpty()) { + useAppIdForOverride = false; + const QStringList &genericnMetas = genericMetaDirs(localPrefix); + for (auto iter = genericnMetas.rbegin(); iter != genericnMetas.rend(); iter++) { + path = getFile(*iter, configKey.subpath, configKey.fileName + FILE_SUFFIX); + if (!path.isEmpty()) + break; + } + } + if (useAppId) { + *useAppId = useAppIdForOverride; + } + return path; + } + + bool load(const QString &localPrefix) override + { + bool useAppIdForOverride = true; + QString path = metaPath(localPrefix, &useAppIdForOverride); + if (path.isEmpty()) { + qCWarning(cfLog, "Can't load meta file from local prefix: \"%s\"", qPrintable(localPrefix)); + return false; + } + + QScopedPointer meta(new QFile(path)); + + struct _ScopedPointer { + explicit _ScopedPointer(const QList &list) + : m_list(list) {} + ~_ScopedPointer() {qDeleteAll(m_list);} + + QList m_list; + }; + _ScopedPointer overrides(loadOverrides(localPrefix, useAppIdForOverride)); + + return load(meta.data(), overrides.m_list); + } + + bool load(QIODevice *meta, const QList &overrides) override + { + { + const QJsonDocument &doc = loadJsonFile(meta); + if (!doc.isObject()) + return false; + + // 检查标识 + const QJsonObject &root = doc.object(); + if (!checkMagic(root, MAGIC_META)) { + qCWarning(cfLog, "The meta magic does not match"); + return false; + } + + // 检查版本兼容性 + const auto &v = parseVersion(root); + if (!versionIsValid(v) || v.major > DConfigFile::supportedVersion().major) { + qCWarning(cfLog, "The meta version number does not match, " + "the file major version=%i, supported major version<=%i", + v.major, DConfigFile::supportedVersion().major); + return false; + } + + m_version = v; + + const auto &contents = root[QLatin1String("contents")].toObject(); + auto i = contents.constBegin(); + + // 初始化原始值 + for (; i != contents.constEnd(); ++i) { + if (!values.update(i.key(), i.value().toObject().toVariantHash())) { + qWarning() << "key:" << i.key() << "has no value"; + return false; + } + } + } + // for override + Q_FOREACH(auto override, overrides) { + const QJsonDocument &doc = loadJsonFile(override); + if (doc.isObject()) { + const QJsonObject &root = doc.object(); + if (!checkMagic(root, MAGIC_OVERRIDE)) { + if (auto file = static_cast(override)) { + qCWarning(cfLog, "The override magic does not match, file: \"%s\", error message: \"%s\"", + qPrintable(file->fileName()), qPrintable(file->errorString())); + } else { + qCWarning(cfLog, "The override magic does not match"); + } + break; //TODO don't continue parse? + } + if (!checkVersion(root, m_version)) { + qCWarning(cfLog, "The override version number does not match"); + break; + } + + if (auto file = static_cast(override)) { + qCDebug(cfLog, "The override will be applied, file: \"%s\"", qPrintable(file->fileName())); + } + + const auto &contents = root[QLatin1String("contents")].toObject(); + auto i = contents.constBegin(); + + for (; i != contents.constEnd(); ++i) { + // 检查是否允许 override + if (values.flags(i.key()) & DConfigFile::NoOverride) + continue; + + if (!values.updateValue(i.key(), i.value())) { + qWarning() << "key (override):" << i.key() << "has no value"; + return false; + } + values.updateSerial(i.key(), i.value()); + values.updatePermissions(i.key(), i.value()); + } + } + } + + return true; + } + /*! + \internal + + \brief 获得前缀为\a prefix目录的应用或公共库的所有覆盖机制目录,越后优先级越高 + */ + inline QStringList overrideDirs(const QString & prefix, bool useAppId) const { + const QString &path2 = QString("%1/etc/dsg/configs/overrides/%2/%3") + .arg(prefix, useAppId ? configKey.appId : QString(), configKey.fileName); + + QStringList paths; + const QStringList &dataPaths = DStandardPaths::paths(DStandardPaths::DSG::DataDir); + paths.reserve(dataPaths.size() + 1); + for (auto path: dataPaths) { + // reverse `DataDir`'s paths, previous `DataDir`'s value has high priority + paths.prepend(QString("%1%2/configs/overrides/%3/%4") + .arg(prefix, path, useAppId ? configKey.appId : QString(), configKey.fileName)); + } + // 在后面的优先级更高 + paths.append(path2); + return paths; + } + + inline QStringList allOverrideDirs(const bool useAppId, const QString &prefix) const override + { + QStringList dirs; + // 只有当允许不使用 appid 时才能回退到通用目录 + if (!useAppId) { + dirs << overrideDirs(prefix, false); + } + // 无论如何都先从带 appid 的目录下加载override文件 + // 在列表后面的更优先 + dirs << overrideDirs(prefix, true); + return dirs; + } + /*! + \internal + + \brief 获得所有遵守覆盖机制的文件流 + + 在override文件放置路径下按优先级查找覆盖文件,支持子目录查找机制, + 使用自然排序(如“a2”在“a11”之前)规则按文件名进行排序 + */ + QList loadOverrides(const QString &prefix, bool useAppId) const + { + auto filters = QDir::Files | QDir::NoDotAndDotDot | QDir::Readable; + const QStringList nameFilters {"*" + FILE_SUFFIX}; + + QStringList dirs = allOverrideDirs(useAppId, prefix); + + QList list; + list.reserve(50); + QCollator collator(QLocale::English); + collator.setNumericMode(true); + collator.setIgnorePunctuation(true); + + Q_FOREACH(const auto &dir, dirs) { + const QDir base_dir(QDir::cleanPath(dir)); + + if (!base_dir.exists()) + continue; + + QDir target_dir = base_dir; + target_dir.setFilter(filters); + target_dir.setNameFilters(nameFilters); + + if (!configKey.subpath.isEmpty()) + target_dir.cd(configKey.subpath.mid(1)); + + do { + qCDebug(cfLog, "load override file from: \"%s\"", qPrintable(target_dir.path())); + + QDirIterator iterator(target_dir); + QList sublist; + sublist.reserve(50); + while(iterator.hasNext()) { + sublist.append(new QFile(iterator.next())); + } + + // 从小到大排序 + std::sort(sublist.begin(), sublist.end(), [&collator](QIODevice *f1, QIODevice *f2){ + if (collator.compare(static_cast(f1)->fileName(), + static_cast(f2)->fileName()) < 0) + return true; + return false; + }); + + list = sublist + list; + + if (base_dir.path() == target_dir.path()) + break; + } while (target_dir.cdUp()); + } + + return list; + } + + DConfigKey configKey; + DConfigInfo values; + DConfigFile::Version m_version = {0, 0}; + char padding [4] = {}; +}; + +DConfigMetaImpl::DConfigMetaImpl(const DConfigKey &configKey) + : DConfigMeta (), + configKey(configKey) +{ +} + +DConfigMetaImpl::~DConfigMetaImpl() +{ +} + +/*! + \class Dtk::Core::DConfigCache + \inmodule dtkcore + + \brief 提供配置文件的用户和全局运行缓存访问接口. + +*/ + +/*! + \fn bool DConfigCache::load(const QString &localPrefix = QString()) = 0; + \brief 解析缓存配置文件 + \return +*/ + +/*! + \fn bool DConfigCache::save(const QString &localPrefix = QString(), QJsonDocument::JsonFormat format = QJsonDocument::Indented, bool sync = false) = 0; + \brief 保存缓存的值到磁盘中 + \a localPrefix 为目录前缀 + \a format 保存格式 + \a sync 是否立即刷新 + \return +*/ + +/*! + \fn bool DConfigCache::isGlobal() const = 0; + \brief 是否是全局缓存 + \return +*/ + +/*! + \fn void DConfigCache::remove(const QString &key) = 0; + \brief 删除缓存中的配置项 + \a key 配置项名称 + \return +*/ + +/*! + \fn QStringList DConfigCache::keyList() const = 0; + \brief 返回配置内容的所有配置项 + \return +*/ + +/*! + \fn bool DConfigCache::setValue(const QString &key, const QVariant &value, const int serial, const uint uid, const QString &callerAppid) = 0; + \brief 设置缓存中的值 + \a key 配置项名称 + \a value 需要设置的值 + \a uid 设置时的用户id + \a callerAppid 设置时的应用id + \return 为true时表示重新设置了新值,false表示没有设置 +*/ + +/*! + \fn QVariant DConfigCache::value(const QString &key) const = 0; + \brief 获取缓存中的值 + \a key 配置项名称 + \return +*/ + +/*! + \fn int DConfigCache::serial(const QString &key) const = 0; + \brief 返回配置项的单调递增值 + \a key 配置项名称 + \return -1为无效值,表明没有配置此项 +*/ + +/*! + \fn uint DConfigCache::uid() const = 0; + \brief 用户标识,为全局缓存时,uid为非用户标识的特定值 + \return +*/ + +class Q_DECL_HIDDEN DConfigCacheImpl : public DConfigCache { +public: + DConfigCacheImpl(const DConfigKey &configKey, const uint uid, bool global); + virtual ~DConfigCacheImpl() override; + + // DConfigCache interface +public: + inline virtual int serial(const QString &key) const override + { + return values.serial(key); + } + + inline virtual uint uid() const override + { + return userid; + } + + inline virtual QStringList keyList() const override + { + return values.keyList(); + } + + inline static QString applicationCacheDir(const QString &prefix, const QString &suffix, + uint userid, const QString &appId) { + // If target user is current user, then get the home path by environment variable first. + const QString &homePath = (getuid() == userid) ? DStandardPaths::homePath() + : DStandardPaths::homePath(userid); + if (homePath.isEmpty()) { + return QString(); + } + const QString userHomeConfigDir = homePath + QStringLiteral("/.config/dsg/configs") + suffix; + return prefix + userHomeConfigDir + QDir::separator() + appId; + } + + inline QString applicationCacheDir(const QString &prefix) const { + return applicationCacheDir(prefix, QString(), userid, configKey.appId); + } + + inline QString cacheDir(const QString &basePath) { + QDir dir(basePath + configKey.subpath); + return dir.filePath(configKey.fileName + FILE_SUFFIX); + } + + inline QString globalCacheDir(const QString &prefix) const { + // TODO `DSG_APP_DATA` is not set and `appid` is not captured in `DStandardPaths::path`. + QString appDataDir = DStandardPaths::path(DStandardPaths::DSG::AppData); + if (appDataDir.isEmpty()) { +#ifdef D_DSG_APP_DATA_FALLBACK + appDataDir = QStringLiteral(D_DSG_APP_DATA_FALLBACK); + QFileInfo tmp(appDataDir); + if (!tmp.exists() && !tmp.isSymLink() && !QDir::current().mkpath(appDataDir)) { + qCDebug(cfLog, "Not found a valid DSG_APP_DATA directory"); + return QString(); + } +#else + return QString(); +#endif + } + + return QString("%1/%2/configs/%3").arg(prefix, appDataDir, configKey.appId); + } + + QString getCacheDir(const QString &localPrefix = QString()) + { + if (isGlobal()) { + const QString &dir = globalCacheDir(localPrefix); + if (!dir.isEmpty()) + return dir; + + // Not supported the global config, fallback the config cache data to user directory. + return applicationCacheDir(localPrefix, "-fake-global", getuid(), configKey.appId); + } else { + return applicationCacheDir(localPrefix); + } + } + + bool load(const QString &localPrefix = QString()) override; + + bool isGlobal() const override + { + return global; + } + + inline void remove(const QString &key) override + { + values.remove(key); + } + bool setValue(const QString &key, const QVariant &value, const int serial, const uint uid, const QString &appid) override + { + if (values.value(key) == value) { + return false; + } + values.setValue(key, value); + values.setSerial(key, serial); + values.setTime(key, QDateTime::currentDateTime().toString(Qt::ISODate)); + values.setUser(key, uid); + values.setAppId(key, appid.isEmpty() ? configKey.appId : appid); + cacheChanged = true; + return true; + } + + inline QVariant value(const QString &key) const override + { + return values.value(key); + } + + bool save(const QString &localPrefix, QJsonDocument::JsonFormat format, bool sync) override; + + DConfigKey configKey; + DConfigInfo values; + uint userid; + bool global; + bool cacheChanged = false; +}; + +DConfigCacheImpl::DConfigCacheImpl(const DConfigKey &configKey, const uint uid, bool global) + : DConfigCache(), + configKey(configKey), + userid(uid), + global(global) +{ +} + +DConfigCacheImpl::~DConfigCacheImpl() +{ +} + +bool DConfigCacheImpl::load(const QString &localPrefix) +{ + // cache 文件要严格匹配 subpath + const QString &dir = getCacheDir(localPrefix); + if (dir.isEmpty()) { + return true; + } + QScopedPointer cache(loadFile(dir, + configKey.subpath, + configKey.fileName + FILE_SUFFIX, + false)); + if (!cache) { + return true; + } + + const QJsonDocument &doc = loadJsonFile(cache.data()); + + if (doc.isObject()) { + const QJsonObject &root = doc.object(); + if (!checkMagic(root, MAGIC_CACHE)) + return false; + if (!checkVersion(root, DConfigFile::supportedVersion())) + return false; + + auto &&contents = root[QLatin1String("contents")].toObject(); + auto i = contents.constBegin(); + + // 原样保存原始数据 + for (; i != contents.constEnd(); ++i) { + values.update(i.key(), i.value().toObject().toVariantHash()); + } + } + return true; +} + +bool DConfigCacheImpl::save(const QString &localPrefix, QJsonDocument::JsonFormat format, bool sync) +{ + if (!cacheChanged) + return true; + + const QString &dir = getCacheDir(localPrefix); + if (dir.isEmpty()) { + qCWarning(cfLog, "Falied on saveing, the config cache directory is empty for the user[%d], " + "the current user[%d].", userid, getuid()); + return false; + } + QString path = cacheDir(dir); + + QFile cache(path); + if (!QFile::exists(QFileInfo(cache.fileName()).path())) { + QDir().mkpath(QFileInfo(cache.fileName()).path()); + } + + if (!cache.open(QIODevice::WriteOnly)) { + qCWarning(cfLog, "Falied on saveing data when open file: \"%s\", error message: \"%s\"", + qPrintable(cache.fileName()), qPrintable(cache.errorString())); + return false; + } + + qCDebug(cfLog, "Save cache file \"%s\".", qPrintable(cache.fileName())); + + QJsonObject root; + + root[QLatin1String("magic")] = MAGIC_CACHE; + const DConfigFile::Version version = DConfigFile::supportedVersion(); + root[QLatin1String("version")] = QString("%1.%2").arg(version.major) + .arg(version.minor); + + root[QLatin1String("contents")] = values.content(); + QJsonDocument doc; + doc.setObject(root); + const QByteArray &json = doc.toJson(format); + + bool status = cache.write(json) == json.size(); + if (status && sync) { + cache.flush(); + } + return status; +} + +class Q_DECL_HIDDEN DConfigFilePrivate : public DObjectPrivate { +public: + DConfigFilePrivate(DConfigFile *qq, const QString &appId, + const QString &name, const QString &subpath) + : DObjectPrivate(qq), + configKey(appId, name ,subpath), + configMeta(new DConfigMetaImpl(configKey)) + { + } + DConfigFilePrivate(DConfigFile *qq, const DConfigKey &configKey) + : DObjectPrivate(qq), + configKey(configKey), + configMeta(new DConfigMetaImpl(configKey)) + { + } + + ~DConfigFilePrivate() override; + + bool load(const QString &localPrefix) + { + bool status = configMeta->load(localPrefix); + if (status) { + // for cache + status &= globalCache->load(localPrefix); + } + return status; + } + bool setValue(const QString &key, const QVariant &value, + DConfigCache *userCache, const QString &appid) + { + // 此处不要检查权限,在获取 value 时会检查 + if (auto cache = getCache(key, userCache)) { + if (!value.isValid()) { + cache->remove(key); + return true; + } else { + const auto &metaValue = configMeta->value(key); + // sample judgement to reduce a copy of convert. + if (metaValue.type() == value.type()) + return cache->setValue(key, value, configMeta->serial(key), cache->uid(), appid); + + // convert copy to meta's type, it promises `setValue` don't change meta's type. + auto copy = value; + if (!copy.convert(metaValue.userType())) { + qCWarning(cfLog) << "check type error, meta type is " << metaValue.type() + << ", and now type is " << value.type(); + return false; + } + + return cache->setValue(key, copy, configMeta->serial(key), cache->uid(), appid); + } + } + return false; + } + DConfigCache* getCache(const QString &key, DConfigCache *userCache) const + { + if(configMeta->flags(key).testFlag(DConfigFile::Global)) { + return globalCache; + } + return userCache; + } + QVariant value(const QString &key, DConfigCache *userCache) const + { + // 检查权限 + if (configMeta->permissions(key) != DConfigFile::ReadOnly) { + if (auto cache = getCache(key, userCache)) { + if (DConfigInfo::checkSerial(configMeta->serial(key), cache->serial(key))) { + const QVariant &tmp = cache->value(key); + if (tmp.isValid()) + return tmp; + } + } + } + + return configMeta->value(key); + } + + D_DECLARE_PUBLIC(DConfigFile) + +private: + DConfigCacheImpl* globalCache; + DConfigKey configKey; + DConfigMeta *configMeta; +}; + +DConfigFilePrivate::~DConfigFilePrivate() +{ + if (globalCache) { + delete globalCache; + globalCache = nullptr; + } + if (configMeta) { + delete configMeta; + configMeta = nullptr; + } +} + +/*! + \brief 支持的版本 + \return + */ +constexpr DConfigFile::Version DConfigFile::supportedVersion() +{ + return DConfigFile::Version{1, 0}; +} + +/*! + \brief 构造配置文件管理对象 + \a appId 应用程序唯一标识 + \a name 配置文件名 + \a subpath 子目录 + */ +DConfigFile::DConfigFile(const QString &appId, const QString &name, const QString &subpath) + : DObject(*new DConfigFilePrivate(this, appId, name, subpath)) +{ + Q_ASSERT(!name.isEmpty()); + + D_D(DConfigFile); + d->globalCache = new DConfigCacheImpl(d->configKey, InvalidUID, true); +} + +DConfigFile::DConfigFile(const DConfigFile &other) + : DObject(*new DConfigFilePrivate(this, other.d_func()->configKey)) +{ + D_D(DConfigFile); + auto cache = new DConfigCacheImpl(d->configKey, InvalidUID, true); + cache->values = other.d_func()->globalCache->values; + d->globalCache = cache; +} + +/*! + \brief 解析配置文件 + \a localPrefix 为目录前缀 + \return +*/ +bool DConfigFile::load(const QString &localPrefix) +{ + D_D(DConfigFile); + return d->load(localPrefix); +} + +/*! + \brief 解析配置文件流 + \a meta 为原型流 + \a overrides 为覆盖机制查找的文件流 + \return +*/ +bool DConfigFile::load(QIODevice *meta, const QList &overrides) +{ + return this->meta()->load(meta, overrides); +} + +/*! + \brief 保存缓存的值到磁盘中 + \a format 保存格式 + \a sync 是否立即刷新 + \return +*/ +bool DConfigFile::save(const QString &localPrefix, QJsonDocument::JsonFormat format, bool sync) const +{ + D_DC(DConfigFile); + + bool ok = d->globalCache->save(localPrefix, format, sync); + + return ok; +} + +/*! + * \brief DConfigFile::value + * \param key 配置项名称 + * \param uid 用户id,当key为全局项时,uid无效 + * \return + */ +QVariant DConfigFile::value(const QString &key, DConfigCache *userCache) const +{ + D_DC(DConfigFile); + return d->value(key, userCache); +} + +/*! + \brief 设置缓存中的值 + \a key 配置项名称 + \a value 需要设置的值 + \a uid 设置时的用户id + \a appid 设置时的应用id + \return 为true时表示重新设置了新值,false表示没有设置 + */ +bool DConfigFile::setValue(const QString &key, const QVariant &value, const QString &callerAppid, DConfigCache *userCache) +{ + D_D(DConfigFile); + return d->setValue(key, value, userCache, callerAppid); +} + +DConfigCache *DConfigFile::createUserCache(const uint uid) +{ + D_D(DConfigFile); + return new DConfigCacheImpl(d->configKey, uid, false); +} + + +/*! + \brief 返回全局缓存 + \return + */ +DConfigCache *DConfigFile::globalCache() const +{ + D_DC(DConfigFile); + return d->globalCache; +} + +/*! + \brief 返回原型对象 + \return + */ +DConfigMeta *DConfigFile::meta() +{ + D_D(DConfigFile); + return d->configMeta; +} + +/*! + \brief 检测配置文件是否有效 + \return + */ +bool DConfigFile::isValid() const +{ + D_DC(DConfigFile); + return versionIsValid(d->configMeta->version()); +} + +DCORE_END_NAMESPACE diff --git a/src/ddesktopentry.cpp b/src/ddesktopentry.cpp new file mode 100644 index 0000000..87941e6 --- /dev/null +++ b/src/ddesktopentry.cpp @@ -0,0 +1,1049 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "ddesktopentry.h" + +#include +#include +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +enum { Space = 0x1, Special = 0x2 }; + +static const char charTraits[256] = { + // Space: '\t', '\n', '\r', ' ' + // Special: '\n', '\r', ';', '=', '\\', '#' + // Please note that '"' is NOT a special character + + 0, 0, 0, 0, 0, 0, 0, 0, 0, Space, Space | Special, 0, 0, Space | Special, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Space, 0, 0, Special, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, Special, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +bool readLineFromData(const QByteArray &data, int &dataPos, int &lineStart, int &lineLen, int &equalsPos) +{ + int dataLen = data.length(); + + equalsPos = -1; + + lineStart = dataPos; + while (lineStart < dataLen && (charTraits[uint(uchar(data.at(lineStart)))] & Space)) + ++lineStart; + + int i = lineStart; + while (i < dataLen) { + while (!(charTraits[uint(uchar(data.at(i)))] & Special)) { + if (++i == dataLen) + goto break_out_of_outer_loop; + } + + char ch = data.at(i++); + if (ch == '=') { + if (equalsPos == -1) + equalsPos = i - 1; + } else if (ch == '\n' || ch == '\r') { + if (i == lineStart + 1) { + ++lineStart; + } else { + --i; + goto break_out_of_outer_loop; + } + } else if (ch == '\\') { + if (i < dataLen) { + char ch = data.at(i++); + if (i < dataLen) { + char ch2 = data.at(i); + // \n, \r, \r\n, and \n\r are legitimate line terminators in INI files + if ((ch == '\n' && ch2 == '\r') || (ch == '\r' && ch2 == '\n')) + ++i; + } + } + } else if (ch == ';') { + // The multiple values should be separated by a semicolon and the value of the key + // may be optionally terminated by a semicolon. Trailing empty strings must always + // be terminated with a semicolon. Semicolons in these values need to be escaped + // using \; . + // Don't need to do anything here. + } else { + Q_ASSERT(ch == '#'); + + if (i == lineStart + 1) { + char ch; + while (i < dataLen && (((ch = data.at(i)) != '\n') && ch != '\r')) + ++i; + lineStart = i; + } + } + } + +break_out_of_outer_loop: + dataPos = i; + lineLen = i - lineStart; + return lineLen > 0; +} + +QString &doEscape(QString& str, const QHash &repl) +{ + // First we replace slash. + str.replace(QLatin1Char('\\'), QLatin1String("\\\\")); + + QHashIterator i(repl); + while (i.hasNext()) { + i.next(); + if (i.key() != QLatin1Char('\\')) + str.replace(i.key(), QString::fromLatin1("\\\\%1").arg(i.value())); + } + + return str; +} + +QString &doUnescape(QString& str, const QHash &repl) +{ + int n = 0; + while (1) { + n=str.indexOf(QLatin1String("\\"), n); + if (n < 0 || n > str.length() - 2) + break; + + if (repl.contains(str.at(n+1))) { + str.replace(n, 2, repl.value(str.at(n+1))); + } + + n++; + } + + return str; +} + +/*! \internal */ +class DDesktopEntrySection +{ +public: + DDesktopEntrySection() {} + + QString name; + QMap valuesMap; + QByteArray unparsedDatas; + int sectionPos = 99; + + inline operator QString() const { + return QLatin1String("DDesktopEntrySection(") + name + QLatin1String(")"); + } + + QByteArray sectionData() const { + if (unparsedDatas.isEmpty()) { + // construct data and return + QByteArray data; + + data.append(QString("[%1]\n").arg(name)); + + QMap::const_iterator i; + for (i = valuesMap.begin(); i != valuesMap.end(); i++) { + data.append(QString("%1=%2\n").arg(i.key(), i.value())); + } + + return data; + } else { + return unparsedDatas; + } + } + + bool ensureSectionDataParsed() { + if (unparsedDatas.isEmpty()) return true; + + valuesMap.clear(); + + // for readLineFromFileData() + int dataPos = 0; + int lineStart; + int lineLen; + int equalsPos; + + while(readLineFromData(unparsedDatas, dataPos, lineStart, lineLen, equalsPos)) { + if (unparsedDatas.at(lineStart) == '[') continue; // section name already parsed + + if (equalsPos != -1) { + QString key = unparsedDatas.mid(lineStart, equalsPos - lineStart).trimmed(); + QString rawValue = unparsedDatas.mid(equalsPos + 1, lineStart + lineLen - equalsPos - 1).trimmed(); + + valuesMap[key] = rawValue; + } + } + + unparsedDatas.clear(); + + return true; + } + + bool contains(const QString &key) const { + const_cast(this)->ensureSectionDataParsed(); + return valuesMap.contains(key); + } + + QStringList allKeys() const { + const_cast(this)->ensureSectionDataParsed(); + return valuesMap.keys(); + } + + QString get(const QString &key, QString &defaultValue) { + if (this->contains(key)) { + return valuesMap[key]; + } else { + return defaultValue; + } + } + + bool set(const QString &key, const QString &value) { + if (this->contains(key)) { + valuesMap.remove(key); + } + valuesMap[key] = value; + return true; + } + + bool remove(const QString &key) { + if (this->contains(key)) { + valuesMap.remove(key); + return true; + } + return false; + } +}; + +typedef QMap SectionMap; + +class DDesktopEntryPrivate +{ +public: + DDesktopEntryPrivate(const QString &filePath, DDesktopEntry *qq); + ~DDesktopEntryPrivate(); + + bool isWritable() const; + bool fuzzyLoad(); + bool initSectionsFromData(const QByteArray &data); + void setStatus(const DDesktopEntry::Status &newStatus) const; + bool write(QIODevice &device) const; + + int sectionPos(const QString §ionName) const; + bool contains(const QString §ionName, const QString &key) const; + QStringList keys(const QString §ionName) const; + bool get(const QString §ionName, const QString &key, QString *value); + bool set(const QString §ionName, const QString &key, const QString &value); + bool remove(const QString §ionName, const QString &key); + +protected: + QString filePath; + QMutex fileMutex; + SectionMap sectionsMap; + mutable DDesktopEntry::Status status; + +private: + bool __padding[4]; + DDesktopEntry *q_ptr = nullptr; + + Q_DECLARE_PUBLIC(DDesktopEntry) +}; + +DDesktopEntryPrivate::DDesktopEntryPrivate(const QString &filePath, DDesktopEntry *qq) + : filePath(filePath), q_ptr(qq) +{ + fuzzyLoad(); +} + +DDesktopEntryPrivate::~DDesktopEntryPrivate() +{ + +} + +bool DDesktopEntryPrivate::isWritable() const +{ + QFileInfo fileInfo(filePath); + +#ifndef QT_NO_TEMPORARYFILE + if (fileInfo.exists()) { +#endif + QFile file(filePath); + return file.open(QFile::ReadWrite); +#ifndef QT_NO_TEMPORARYFILE + } else { + // Create the directories to the file. + QDir dir(fileInfo.absolutePath()); + if (!dir.exists()) { + if (!dir.mkpath(dir.absolutePath())) + return false; + } + + // we use a temporary file to avoid race conditions + QTemporaryFile file(filePath); + return file.open(); + } +#endif +} + +bool DDesktopEntryPrivate::fuzzyLoad() +{ + QFile file(filePath); + QFileInfo fileInfo(filePath); + + if (fileInfo.exists() && !file.open(QFile::ReadOnly)) { + setStatus(DDesktopEntry::AccessError); + return false; + } + + if (file.isReadable() && file.size() != 0) { + bool ok = false; + QByteArray data = file.readAll(); + + ok = initSectionsFromData(data); + + if (!ok) { + setStatus(DDesktopEntry::FormatError); + return false; + } + } + + setStatus(DDesktopEntry::NoError); + return true; +} + +bool DDesktopEntryPrivate::initSectionsFromData(const QByteArray &data) +{ + sectionsMap.clear(); + + QString lastSectionName; + int lastSectionStart = 0; + bool formatOk = true; + int sectionIdx = 0; + // for readLineFromFileData() + int dataPos = 0; + int lineStart; + int lineLen; + int equalsPos; + + auto commitSection = [=](const QString &name, int sectionStartPos, int sectionLength, int sectionIndex) { + DDesktopEntrySection lastSection; + lastSection.name = name; + lastSection.unparsedDatas = data.mid(sectionStartPos, sectionLength); + lastSection.sectionPos = sectionIndex; + sectionsMap[name] = lastSection; + }; + + // TODO: here we only need to find the section start, so things like equalsPos are useless here. + // maybe we can do some optimization here via adding extra argument to readLineFromData(). + while(readLineFromData(data, dataPos, lineStart, lineLen, equalsPos)) { + // qDebug() << "CurrentLine:" << data.mid(lineStart, lineLen); + if (data.at(lineStart) == '[') { + // commit the last section we've ever read before we read the new one. + if (!lastSectionName.isEmpty()) { + commitSection(lastSectionName, lastSectionStart, lineStart - lastSectionStart, sectionIdx); + sectionIdx++; + } + // process section name line + QByteArray sectionName; + int idx = data.indexOf(']', lineStart); + if (idx == -1 || idx >= lineStart + lineLen) { + qWarning() << "Bad desktop file format while reading line:" << data.mid(lineStart, lineLen); + formatOk = false; + sectionName = data.mid(lineStart + 1, lineLen - 1).trimmed(); + } else { + sectionName = data.mid(lineStart + 1, idx - lineStart - 1).trimmed(); + } + lastSectionName = sectionName; + lastSectionStart = lineStart; + } + } + + Q_ASSERT(lineStart == data.length()); + if (!lastSectionName.isEmpty()) { + commitSection(lastSectionName, lastSectionStart, lineStart - lastSectionStart, sectionIdx); + } + + return formatOk; +} + +// Always keep the first meet error status. and allowed clear the status. +void DDesktopEntryPrivate::setStatus(const DDesktopEntry::Status &newStatus) const +{ + if (newStatus == DDesktopEntry::NoError || this->status == DDesktopEntry::NoError) { + this->status = newStatus; + } +} + +bool DDesktopEntryPrivate::write(QIODevice &device) const +{ + Q_Q(const DDesktopEntry); + + QStringList sortedKeys = q->allGroups(true); + + for (const QString &key : sortedKeys) { + qint64 ret = device.write(sectionsMap[key].sectionData()); + if (ret == -1) return false; + } + + return true; +} + +int DDesktopEntryPrivate::sectionPos(const QString §ionName) const +{ + if (sectionsMap.contains(sectionName)) { + return sectionsMap[sectionName].sectionPos; + } + + return -1; +} + +bool DDesktopEntryPrivate::contains(const QString §ionName, const QString &key) const +{ + if (sectionName.isNull() || key.isNull()) { + return false; + } + + if (sectionsMap.contains(sectionName)) { + return sectionsMap[sectionName].contains(key); + } + + return false; +} + +QStringList DDesktopEntryPrivate::keys(const QString §ionName) const +{ + if (sectionName.isNull()) { + return {}; + } + + if (sectionsMap.contains(sectionName)) { + return sectionsMap[sectionName].allKeys(); + } + + return {}; +} + +// return true if we found the value, and set the value to *value +bool DDesktopEntryPrivate::get(const QString §ionName, const QString &key, QString *value) +{ + if (!this->contains(sectionName, key)) { + return false; + } + + if (sectionsMap.contains(sectionName)) { + QString &&result = sectionsMap[sectionName].get(key, *value); + *value = result; + return true; + } + + return false; +} + +bool DDesktopEntryPrivate::set(const QString §ionName, const QString &key, const QString &value) +{ + if (sectionsMap.contains(sectionName)) { + bool result = sectionsMap[sectionName].set(key, value); + return result; + } else { + // create new section. + DDesktopEntrySection newSection; + newSection.name = sectionName; + newSection.set(key, value); + sectionsMap[sectionName] = newSection; + return true; + } + + return false; +} + +bool DDesktopEntryPrivate::remove(const QString §ionName, const QString &key) +{ + if (this->contains(sectionName, key)) { + return sectionsMap[sectionName].remove(key); + } + return false; +} + +/*! + \class Dtk::Core::DDesktopEntry + \inmodule dtkcore + \brief Handling desktop entry files. + + DDesktopEntry provide method for handling XDG desktop entry read and write. The interface + of this class is similar to QSettings. + + For more details about the spec itself, please refer to: + https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html + */ + +DDesktopEntry::DDesktopEntry(const QString &filePath) noexcept + : d_ptr(new DDesktopEntryPrivate(filePath, this)) +{ + +} + +DDesktopEntry::~DDesktopEntry() +{ + +} + +/*! + \brief Write back data to the desktop entry file. + \return true if write success; otherwise returns false. + */ +bool DDesktopEntry::save() const +{ + Q_D(const DDesktopEntry); + + // write to file. + if (d->isWritable()) { + bool ok = false; + bool createFile = false; + QFileInfo fileInfo(d->filePath); + +#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile) + QSaveFile sf(d->filePath); + sf.setDirectWriteFallback(true); +#else + QFile sf(d->filePath); +#endif + if (!sf.open(QIODevice::WriteOnly)) { + d->setStatus(DDesktopEntry::AccessError); + return false; + } + + ok = d->write(sf); + +#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile) + if (ok) { + ok = sf.commit(); + } +#endif + + if (ok) { + // If we have created the file, apply the file perms + if (createFile) { + QFile::Permissions perms = fileInfo.permissions() | QFile::ReadOwner | QFile::WriteOwner + | QFile::ReadGroup | QFile::ReadOther; + QFile(d->filePath).setPermissions(perms); + } + return true; + } else { + d->setStatus(DDesktopEntry::AccessError); + return false; + } + } + + return false; +} + +/*! + \brief Get data parse status + + \return Returns a status code indicating the first error that was met by DDesktopEntry, or QSettings::NoError if no error occurred. + + Be aware that DDesktopEntry delays performing some operations. + */ +DDesktopEntry::Status DDesktopEntry::status() const +{ + Q_D(const DDesktopEntry); + return d->status; +} + +/*! + \brief Get a list of all section keys inside the given \a section. + + \return all available section keys. + */ +QStringList DDesktopEntry::keys(const QString §ion) const +{ + Q_D(const DDesktopEntry); + + if (section.isEmpty()) { + qWarning("DDesktopEntry::keys: Empty section name passed"); + return {}; + } + + return d->keys(section); +} + +/*! + \brief Get a list of all section groups inside the desktop entry. + + If \a sorted is set to true, the returned result will keep the order as-is when reading the entry file. + + \return all available section groups. + */ +QStringList DDesktopEntry::allGroups(bool sorted) const +{ + Q_D(const DDesktopEntry); + + if (!sorted) { + return d->sectionsMap.keys(); + } else { + using StrIntPair = QPair; + + QStringList keys = d->sectionsMap.keys(); + QList result; + + for (const QString & key : keys) { + result << StrIntPair(key, d->sectionPos(key)); + } + + std::sort(result.begin(), result.end(), [](const StrIntPair& a, const StrIntPair& b) -> bool { + return a.second < b.second; + }); + + keys.clear(); + + for (const StrIntPair& pair : result) { + keys << pair.first; + } + + return keys; + } +} + +/*! + \brief Check if the desktop entry file have the given \a section contains the given \a key + + \return true if the desktop entry contains the \a key in \a section; otherwise returns false. + */ +bool DDesktopEntry::contains(const QString &key, const QString §ion) const +{ + Q_D(const DDesktopEntry); + + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::contains: Empty key or section passed"); + return false; + } + + return d->contains(section, key); +} + +/*! + \brief Returns the localized string value of the "Name" key under "Desktop Entry" section. + + It's equivalent to calling localizedValue("Name"). + + \return Returns the localized string value of the "Name" key under "Desktop Entry" section. + + \sa localizedValue(), genericName(), ddeDisplayName() + */ +QString DDesktopEntry::name() const +{ + return localizedValue(QStringLiteral("Name")); +} + +/*! + \brief Returns the localized string value of the "GenericName" key under "Desktop Entry" section. + + It's equivalent to calling localizedValue("GenericName"). It will NOT fallback to "Name" if "GenericName" + is not existed. + + \return Returns the localized string value of the "GenericName" key under "Desktop Entry" section. + + \sa localizedValue(), name(), ddeDisplayName() + */ +QString DDesktopEntry::genericName() const +{ + return localizedValue(QStringLiteral("GenericName")); +} + +/*! + \brief Display name specially for DDE applications. + + This will check "X-Deepin-Vendor" and will return the localized string value of "GenericName" if + "X-Deepin-Vendor" is "deepin", or it will return the localized string value of "Name". + + \return Returns the display name specially for DDE applications. + + \sa localizedValue(), name(), genericName() + */ +QString DDesktopEntry::ddeDisplayName() const +{ + QString deepinVendor = stringValue("X-Deepin-Vendor"); + QString genericNameStr = genericName(); + if (deepinVendor == QStringLiteral("deepin") && !genericNameStr.isEmpty()) { + return genericNameStr; + } + + return name(); +} + +/*! + \brief Returns the localized string value of the "Comment" key under "Desktop Entry" section. + + It's equivalent to calling localizedValue("Comment"). + + \return Returns the localized string value of the "Comment" key under "Desktop Entry" section. + + \sa localizedValue() + */ +QString DDesktopEntry::comment() const +{ + return localizedValue(QStringLiteral("Comment")); +} + +/*! + \brief Returns the raw string value associated with the given \a key in \a section. + + If the entry contains no item with the key, the function returns a constructed \a defaultValue. + + \return Returns the raw string value associated with the given \a key in \a section. + + \sa stringValue() localizedValue() stringListValue() + */ +QString DDesktopEntry::rawValue(const QString &key, const QString §ion, const QString &defaultValue) const +{ + Q_D(const DDesktopEntry); + QString result = defaultValue; + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::value: Empty key or section passed"); + return result; + } + const_cast(d)->get(section, key, &result); // FIXME: better way than const_cast? + return result; +} + +/*! + \brief Returns the unescaped string value associated with the given \a key in \a section. + + If the entry contains no item with the key, the function returns a constructed \a defaultValue. + + \return Returns the unescaped string value associated with the given \a key in \a section. + + \sa rawValue() localizedValue() stringListValue() + */ +QString DDesktopEntry::stringValue(const QString &key, const QString §ion, const QString &defaultValue) const +{ + QString rawResult = rawValue(key, section, defaultValue); + rawResult = DDesktopEntry::unescape(rawResult); + return rawResult; +} + +/*! + \brief Returns the localized string value associated with the given \a key and \a localeKey in \a section. + + If the given \a localeKey can't be found, it will fallback to "C", if still cannot found, will fallback to the + key without localeKey. + + If the entry contains no item with the key, the function returns a constructed \a defaultValue. + + \return Returns the localized string value associated with the given \a key and \a localeKey in \a section. + + \sa rawValue() stringValue() stringListValue() + */ +QString DDesktopEntry::localizedValue(const QString &key, const QString &localeKey, const QString §ion, const QString &defaultValue) const +{ + Q_D(const DDesktopEntry); + QString result = defaultValue; + QString actualLocaleKey = QLatin1String("C"); + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::localizedValue: Empty key or section passed"); + return result; + } + + QStringList possibleKeys; + + // 此处添加 bcp47Name() 是为了兼容 desktop 文件中的语言长短名解析。 + // 比如芬兰语,有 [fi] 和 [fi_FI] 两种情况,QLocale::name() 对应 fi_FI,QLocale::bcp47Name() 对应 fi。 + if (!localeKey.isEmpty()) { + if (localeKey == "empty") { + possibleKeys << key; + } else if (localeKey == "default") { + possibleKeys << QString("%1[%2]").arg(key, QLocale().name()); + possibleKeys << QString("%1[%2]").arg(key, QLocale().bcp47Name()); + } else if (localeKey == "system") { + possibleKeys << QString("%1[%2]").arg(key, QLocale::system().name()); + possibleKeys << QString("%1[%2]").arg(key, QLocale::system().bcp47Name()); + } else { + possibleKeys << QString("%1[%2]").arg(key, localeKey); + } + } + + if (!actualLocaleKey.isEmpty()) { + possibleKeys << QString("%1[%2]").arg(key, actualLocaleKey); + } + possibleKeys << QString("%1[%2]").arg(key, "C"); + possibleKeys << key; + + for (const QString &oneKey : possibleKeys) { + if (d->contains(section, oneKey)) { + const_cast(d)->get(section, oneKey, &result); + break; + } + } + + return result; +} + +/*! + \brief Returns the localized string value associated with the given \a key and \a locale in \a section. + + If the given \a locale can't be found, it will fallback to "C", if still cannot found, will fallback to the + key without a locale key. + + If the entry contains no item with the key, the function returns a default-constructed value. + + \return Returns the localized string value associated with the given \a key and \a locale in \a section. + + \sa rawValue() stringValue() stringListValue() + */ +QString DDesktopEntry::localizedValue(const QString &key, const QLocale &locale, const QString §ion, const QString &defaultValue) const +{ + return localizedValue(key, locale.name(), section, defaultValue); +} + +/*! + \brief Returns a list of strings associated with the given \a key in the given \a section. + + If the entry contains no item with the key, the function returns a empty string list. + + \return Returns a list of strings associated with the given \a key in the given \a section. + + \sa rawValue() stringValue() localizedValue() + */ +QStringList DDesktopEntry::stringListValue(const QString &key, const QString §ion) const +{ + Q_D(const DDesktopEntry); + + QString value; + + const_cast(d)->get(section, key, &value); + + if (value.endsWith(';')) { + value = value.left(value.length() - 1); + } + QStringList&& strings = value.split(';'); + + QString combine; + QStringList result; + for (QString oneStr : strings) { + if (oneStr.endsWith('\\')) { + combine = combine + oneStr + ';'; + continue; + } + if (!combine.isEmpty()) { + oneStr = combine + oneStr; + combine.clear(); + } + result << DDesktopEntry::unescape(oneStr, true); + } + + return result; +} + +bool DDesktopEntry::setRawValue(const QString &value, const QString &key, const QString §ion) +{ + Q_D(DDesktopEntry); + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::setRawValue: Empty key or section passed"); + return false; + } + + bool result = d->set(section, key, value); + return result; +} + +bool DDesktopEntry::setStringValue(const QString &value, const QString &key, const QString §ion) +{ + QString escapedValue = value; + DDesktopEntry::escape(escapedValue); + bool result = setRawValue(escapedValue, key, section); + return result; +} + +bool DDesktopEntry::setLocalizedValue(const QString &value, const QString &localeKey, const QString &key, const QString §ion) +{ + Q_D(DDesktopEntry); + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::setLocalizedValue: Empty key or section passed"); + return false; + } + + QString actualKey = localeKey.isEmpty() ? key : QString("%1[%2]").arg(key, localeKey); + + bool result = d->set(section, actualKey, value); + return result; +} + +bool DDesktopEntry::removeEntry(const QString &key, const QString §ion) +{ + Q_D(DDesktopEntry); + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::setLocalizedValue: Empty key or section passed"); + return false; + } + bool result = d->remove(section, key); + return result; +} + +/************************************************ + The escape sequences \s, \n, \t, \r, and \\ are supported for values + of type string and localestring, meaning ASCII space, newline, tab, + carriage return, and backslash, respectively. + ************************************************/ +QString &DDesktopEntry::escape(QString &str) +{ + QHash repl; + repl.insert(QLatin1Char('\n'), QLatin1Char('n')); + repl.insert(QLatin1Char('\t'), QLatin1Char('t')); + repl.insert(QLatin1Char('\r'), QLatin1Char('r')); + + return doEscape(str, repl); +} + +/************************************************ + Quoting must be done by enclosing the argument between double quotes and + escaping the + double quote character, + backtick character ("`"), + dollar sign ("$") and + backslash character ("\") +by preceding it with an additional backslash character. +Implementations must undo quoting before expanding field codes and before +passing the argument to the executable program. + +Note that the general escape rule for values of type string states that the +backslash character can be escaped as ("\\") as well and that this escape +rule is applied before the quoting rule. As such, to unambiguously represent a +literal backslash character in a quoted argument in a desktop entry file +requires the use of four successive backslash characters ("\\\\"). +Likewise, a literal dollar sign in a quoted argument in a desktop entry file +is unambiguously represented with ("\\$"). + ************************************************/ +QString &DDesktopEntry::escapeExec(QString &str) +{ + QHash repl; + // The parseCombinedArgString() splits the string by the space symbols, + // we temporarily replace them on the special characters. + // Replacement will reverse after the splitting. + repl.insert(QLatin1Char('"'), QLatin1Char('"')); // double quote, + repl.insert(QLatin1Char('\''), QLatin1Char('\'')); // single quote ("'"), + repl.insert(QLatin1Char('\\'), QLatin1Char('\\')); // backslash character ("\"), + repl.insert(QLatin1Char('$'), QLatin1Char('$')); // dollar sign ("$"), + + return doEscape(str, repl); +} + +/* + The escape sequences \s, \n, \t, \r, and \\ are supported for values of type string and localestring, + meaning ASCII space, newline, tab, carriage return, and backslash, respectively. + + Some keys can have multiple values. In such a case, the value of the key is specified as a plural: for + example, string(s). The multiple values should be separated by a semicolon and the value of the key may + be optionally terminated by a semicolon. Trailing empty strings must always be terminated with a semicolon. + Semicolons in these values need to be escaped using \;. + + https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#value-types +*/ +QString &DDesktopEntry::unescape(QString &str, bool unescapeSemicolons) +{ + QHash repl; + repl.insert(QLatin1Char('\\'), QLatin1Char('\\')); + repl.insert(QLatin1Char('s'), QLatin1Char(' ')); + repl.insert(QLatin1Char('n'), QLatin1Char('\n')); + repl.insert(QLatin1Char('t'), QLatin1Char('\t')); + repl.insert(QLatin1Char('r'), QLatin1Char('\r')); + + if (unescapeSemicolons) { + repl.insert(QLatin1Char(';'), QLatin1Char(';')); + } + + return doUnescape(str, repl); +} + +/************************************************ + Quoting must be done by enclosing the argument between double quotes and + escaping the + double quote character, + backtick character ("`"), + dollar sign ("$") and + backslash character ("\") +by preceding it with an additional backslash character. +Implementations must undo quoting before expanding field codes and before +passing the argument to the executable program. + +Reserved characters are + space (" "), + tab, + newline, + double quote, + single quote ("'"), + backslash character ("\"), + greater-than sign (">"), + less-than sign ("<"), + tilde ("~"), + vertical bar ("|"), + ampersand ("&"), + semicolon (";"), + dollar sign ("$"), + asterisk ("*"), + question mark ("?"), + hash mark ("#"), + parenthesis ("(") and (")") + backtick character ("`"). + +Note that the general escape rule for values of type string states that the +backslash character can be escaped as ("\\") as well and that this escape +rule is applied before the quoting rule. As such, to unambiguously represent a +literal backslash character in a quoted argument in a desktop entry file +requires the use of four successive backslash characters ("\\\\"). +Likewise, a literal dollar sign in a quoted argument in a desktop entry file +is unambiguously represented with ("\\$"). + ************************************************/ +QString &DDesktopEntry::unescapeExec(QString &str) +{ + unescape(str); + QHash repl; + // The parseCombinedArgString() splits the string by the space symbols, + // we temporarily replace them on the special characters. + // Replacement will reverse after the splitting. + repl.insert(QLatin1Char(' '), 01); // space + repl.insert(QLatin1Char('\t'), 02); // tab + repl.insert(QLatin1Char('\n'), 03); // newline, + + repl.insert(QLatin1Char('"'), QLatin1Char('"')); // double quote, + repl.insert(QLatin1Char('\''), QLatin1Char('\'')); // single quote ("'"), + repl.insert(QLatin1Char('\\'), QLatin1Char('\\')); // backslash character ("\"), + repl.insert(QLatin1Char('>'), QLatin1Char('>')); // greater-than sign (">"), + repl.insert(QLatin1Char('<'), QLatin1Char('<')); // less-than sign ("<"), + repl.insert(QLatin1Char('~'), QLatin1Char('~')); // tilde ("~"), + repl.insert(QLatin1Char('|'), QLatin1Char('|')); // vertical bar ("|"), + repl.insert(QLatin1Char('&'), QLatin1Char('&')); // ampersand ("&"), + repl.insert(QLatin1Char(';'), QLatin1Char(';')); // semicolon (";"), + repl.insert(QLatin1Char('$'), QLatin1Char('$')); // dollar sign ("$"), + repl.insert(QLatin1Char('*'), QLatin1Char('*')); // asterisk ("*"), + repl.insert(QLatin1Char('?'), QLatin1Char('?')); // question mark ("?"), + repl.insert(QLatin1Char('#'), QLatin1Char('#')); // hash mark ("#"), + repl.insert(QLatin1Char('('), QLatin1Char('(')); // parenthesis ("(") + repl.insert(QLatin1Char(')'), QLatin1Char(')')); // parenthesis (")") + repl.insert(QLatin1Char('`'), QLatin1Char('`')); // backtick character ("`"). + + return doUnescape(str, repl); +} + +bool DDesktopEntry::setStatus(const DDesktopEntry::Status &status) +{ + Q_D(DDesktopEntry); + d->setStatus(status); + + return true; +} + +DCORE_END_NAMESPACE diff --git a/src/dsecurestring.cpp b/src/dsecurestring.cpp new file mode 100644 index 0000000..6469be9 --- /dev/null +++ b/src/dsecurestring.cpp @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dsecurestring.h" +#include "dutil.h" + +DCORE_BEGIN_NAMESPACE + +DSecureString::DSecureString(const QString &other) noexcept + : QString(other) +{ +} + +DSecureString::~DSecureString() +{ + DUtil::SecureErase(*this); +} + +DCORE_END_NAMESPACE diff --git a/src/dsgapplication.cpp b/src/dsgapplication.cpp new file mode 100644 index 0000000..c00d355 --- /dev/null +++ b/src/dsgapplication.cpp @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dsgapplication.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +static inline QByteArray getSelfAppId() { + // The env is set by the application starter(eg, org.desktopspec.ApplicationManager service) + QByteArray selfId = qgetenv("DSG_APP_ID"); + if (!selfId.isEmpty()) + return selfId; + selfId = DSGApplication::getId(QCoreApplication::applicationPid()); + if (selfId.isEmpty() && !qEnvironmentVariableIsSet("DTK_DISABLED_FALLBACK_APPID")) { + selfId = QCoreApplication::applicationName().toLocal8Bit(); + } + Q_ASSERT(!selfId.isEmpty()); + if (selfId.isEmpty()) { + qt_assert("The application ID is empty", __FILE__, __LINE__); + } + return selfId; +} + +QByteArray DSGApplication::id() +{ + static QByteArray selfId = getSelfAppId(); + return selfId; +} + +QByteArray DSGApplication::getId(qint64) +{ + // TODO(zccrs): Call the org.desktopspec.ApplicationManager DBus service + return nullptr; +} + +DCORE_END_NAMESPACE diff --git a/src/dsysinfo.cpp b/src/dsysinfo.cpp new file mode 100644 index 0000000..fd3fe6d --- /dev/null +++ b/src/dsysinfo.cpp @@ -0,0 +1,1283 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dsysinfo.h" +#include "ddesktopentry.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_LINUX +#include +#include +#include +#endif + +#ifndef OS_VERSION_TEST_FILE +#define OS_VERSION_FILE "/etc/os-version" +#else +#define OS_VERSION_FILE OS_VERSION_TEST_FILE +#endif + +DCORE_BEGIN_NAMESPACE + +class Q_DECL_HIDDEN DSysInfoPrivate +{ +public: + DSysInfoPrivate(); + +#ifdef Q_OS_LINUX + void ensureDeepinInfo(); + bool ensureOsVersion(); + void ensureDistributionInfo(); + bool splitA_BC_DMode(); +#endif + void ensureReleaseInfo(); + void ensureComputerInfo(); + QMap parseInfoFile(QFile &file); + +#ifdef Q_OS_LINUX + DSysInfo::DeepinType deepinType = DSysInfo::DeepinType(-1); + QMap deepinTypeMap; //Type Name with Language + QString deepinVersion; + QString deepinEdition; + QString deepinCopyright; + + QString majorVersion; + QString minorVersion; + struct MinVersion { + enum Type { + A_BC_D, /*!< Professional Edition*/ + X_Y_Z, /*!< Home Edition*/ + A_B_C /*!< Community Edition*/ + }; + MinVersion() + : A(0) + , B(0) + , BC(0) + , C(0) + , D(0) + , X(0) + , Y(0) + , Z(0) + { + } + + uint A, B, BC, C, D; // A-BC-D + uint X, Y, Z; + Type type; + }; + struct OSBuild { + OSBuild():A(0), B(0), C(0), D(0), xyz(100){ + } + uint A, B, C, D, E, xyz; // ABCDE.xyz + }; + + MinVersion minVersion; + OSBuild osBuild; +#endif + + QScopedPointer distributionInfo; + + DSysInfo::ProductType productType = DSysInfo::ProductType(-1); + QString prettyName; + QString productTypeString; + QString productVersion; + + QString computerName; + QString cpuModelName; + qint64 memoryAvailableSize = -1; + qint64 memoryInstalledSize = -1; + qint64 diskSize = 0; +}; + +DSysInfoPrivate::DSysInfoPrivate() +{ + +} + +#ifdef Q_OS_LINUX +void DSysInfoPrivate::ensureDistributionInfo() +{ + if (distributionInfo) + return; + + const QString distributionInfoFile(DSysInfo::distributionInfoPath()); + // Generic DDE distribution info + distributionInfo.reset(new DDesktopEntry(distributionInfoFile)); +} + +bool DSysInfoPrivate::splitA_BC_DMode() +{ + // A-BC-D + bool ok = false; + uint minv = minorVersion.toUInt(&ok); + if (ok) { + minVersion.D = minv % 10; + } else if (minorVersion.length() > 0) { + const QString D = minorVersion.right(1); + if (D.contains(QRegExp("[0-9A-Z]"))) { + // 0-9...A-Z + minVersion.D = 10 + static_cast(D.data()->toLatin1() - 'A'); + } else { + qWarning() << "invalid minorVersion"; + minVersion.D = 0; + } + } + uint minVer = minorVersion.left(3).toUInt(); + minVersion.BC = minVer % 100; + minVer /= 100; + minVersion.A = minVer % 10; + minVersion.type = MinVersion::A_BC_D; + return ok; +} + +void DSysInfoPrivate::ensureDeepinInfo() +{ + if (static_cast(deepinType) >= 0) + return; + + QFile file("/etc/deepin-version"); + + if (!file.open(QFile::ReadOnly)) { + deepinType = DSysInfo::UnknownDeepin; + + return; + } + + char buf[1024]; + int buf_length = 0; + + Q_FOREVER { + buf_length = file.readLine(buf, sizeof(buf)); + + if (buf_length < 0) + break; + + const QByteArray line(buf, buf_length); + const QByteArrayList &list = line.split('='); + + if (list.count() != 2) { + continue; + } + + const auto key_value = qMakePair(list.first().trimmed(), list.last().trimmed()); + + if (key_value.first == "Version") { + deepinVersion = key_value.second; + } else if (line.startsWith("Type")) { + if (key_value.first == "Type") { + deepinTypeMap[QString()] = QString::fromLatin1(key_value.second); + } else if (key_value.first.at(4) == '[' && key_value.first.at(key_value.first.size() - 1) == ']') { + const QByteArray &language = key_value.first.mid(5, key_value.first.size() - 6); + + if (!language.isEmpty()) { + deepinTypeMap[QString::fromLatin1(language)] = QString::fromUtf8(key_value.second); + } + } + } else if (key_value.first == "Edition") { + deepinEdition = QString::fromUtf8(key_value.second); + } else if (key_value.first == "Copyright") { + deepinCopyright = QString::fromUtf8(key_value.second); + } + + if (!deepinTypeMap.isEmpty() && !deepinEdition.isEmpty() && !deepinCopyright.isEmpty()) { + break; + } + } + + file.close(); + + const QString &deepin_type = deepinTypeMap[QString()]; + + if (deepin_type.isEmpty()) { + deepinType = DSysInfo::UnknownDeepin; + } else if (deepin_type == "Desktop") { + deepinType = DSysInfo::DeepinDesktop; + } else if (deepin_type == "Professional") { + deepinType = DSysInfo::DeepinProfessional; + } else if (deepin_type == "Server") { + deepinType = DSysInfo::DeepinServer; + } else if (deepin_type == "Personal") { + deepinType = DSysInfo::DeepinPersonal; + } else { + deepinType = DSysInfo::UnknownDeepin; + } +} + +bool DSysInfoPrivate::ensureOsVersion() +{ +#ifndef OS_VERSION_TEST_FILE // Always re-read the file when testing + if (osBuild.A > 0) + return true; +#endif + + DDesktopEntry entry(OS_VERSION_FILE); + bool ok = false; + +#define D_ASSET_EXIT(con, msg) do { \ + if (!(con)) { \ + qWarning() << __func__ << msg; \ + return false; \ + } \ +} while (false) + + D_ASSET_EXIT(entry.status() == DDesktopEntry::NoError, entry.status()); + + // 先获取版本信息 + // ABCDE.xyz.abc + QString osb = entry.stringValue("OsBuild", "Version"); + QStringList osbs = osb.split("."); + ok = (osbs.size() >= 2 && osbs.value(0).size() == 5); + D_ASSET_EXIT(ok, "OsBuild version invalid!"); + + const QStringList &left = osbs.value(0).split(QString(), QString::SkipEmptyParts); + D_ASSET_EXIT(left.size() == 5, "OsBuild version(ls) invalid!"); + + int idx = 0; + osBuild.A = left.value(idx++, "0").toUInt(&ok); + D_ASSET_EXIT(ok, "OsBuild version(A) invalid!"); + osBuild.B = left.value(idx++, "0").toUInt(&ok); + D_ASSET_EXIT(ok, "OsBuild version(B) invalid!"); + osBuild.C = left.value(idx++, "0").toUInt(&ok); + if (!ok) { + auto c = left.value(idx-1, "0").toLatin1(); + D_ASSET_EXIT(c.size()>0, "OsBuild version(C) invalid!"); + osBuild.C = uint(c.at(0)); + } + osBuild.D = left.value(idx++, "0").toUInt(&ok); + D_ASSET_EXIT(ok, "OsBuild version(D) invalid!"); + osBuild.E = left.value(idx++, "0").toUInt(&ok); + D_ASSET_EXIT(ok, "OsBuild version(E) invalid!"); + + // xyz + osBuild.xyz = osbs.value(1).trimmed().toUInt(&ok); + + majorVersion = entry.stringValue("MajorVersion", "Version"); + minorVersion = entry.stringValue("MinorVersion", "Version"); + + switch (osBuild.D) { + case 7: { + // Home Edition uses the form of "full version number coding -x.y.z" + const QStringList &versionList = minorVersion.split('.'); + if (versionList.isEmpty()) { + // If the reading fails, return it directly to empty + qWarning() << "no minorVersion"; + return false; + } else if (versionList.length() == 2) { + // Z is 0 + minVersion.X = versionList.first().toUInt(); + minVersion.Y = versionList.last().toUInt(); + minVersion.Z = 0; + } else if (versionList.length() == 3) { + // X.Y.Z exists + minVersion.X = versionList.at(0).toUInt(); + minVersion.Y = versionList.at(1).toUInt(); + minVersion.Z = versionList.at(2).toUInt(); + } + minVersion.type = MinVersion::X_Y_Z; + } break; + + case 3: { + // The community version uses the form of "full version number coding A.B.C" + bool a_bc_dMode = false; + const QStringList &versionList = minorVersion.split('.'); + if (versionList.isEmpty()) { + // If the reading fails, return it directly to empty + qWarning() << "no minorVersion"; + return false; + } else if (versionList.length() == 1) { + QString modeVersion = versionList.first(); + if (modeVersion.length() == 2) { + //A.B.C mode and B c are 0 + minVersion.A = modeVersion.toUInt(); + minVersion.B = 0; + minVersion.C = 0; + } else { + // A_BC_D mode + splitA_BC_DMode(); + a_bc_dMode = true; + } + } else if (versionList.length() == 2) { + // C=0 + minVersion.A = versionList.first().toUInt(); + minVersion.B = versionList.last().toUInt(); + minVersion.C = 0; + } else if (versionList.length() == 3) { + // A.B.C exists + minVersion.A = versionList.at(0).toUInt(); + minVersion.B = versionList.at(1).toUInt(); + minVersion.C = versionList.at(2).toUInt(); + } + + if (!a_bc_dMode) + minVersion.type = MinVersion::A_B_C; + } break; + default: { + // A-BC-D + ok = splitA_BC_DMode(); + } break; + } + return ok; +} + +static QString unquote(const QByteArray &value) +{ + if (value.at(0) == '"' || value.at(0) == '\'') { + return QString::fromLatin1(value.mid(1, value.size() - 2)); + } + + return QString::fromLatin1(value); +} + +static bool readEtcFile(DSysInfoPrivate *info, const char *filename, + const QByteArray &idKey, const QByteArray &versionKey, const QByteArray &prettyNameKey) +{ + + QFile file(QString::fromLatin1(filename)); + + if (!file.open(QIODevice::ReadOnly)) { + return false; + } + + quint8 valid_data_count = 0; + char buf[1024]; + + while (valid_data_count < 3) { + int buf_length = file.readLine(buf, sizeof(buf)); + + if (buf_length < 0) + break; + + const QByteArray line(buf, buf_length - 1); + + if (info->productTypeString.isEmpty() && line.startsWith(idKey)) { + const QByteArray value(line.constData() + idKey.size()); + info->productTypeString = unquote(value); + ++valid_data_count; + continue; + } + + if (info->prettyName.isEmpty() && line.startsWith(prettyNameKey)) { + const QByteArray value(line.constData() + prettyNameKey.size()); + info->prettyName = unquote(value); + ++valid_data_count; + continue; + } + + if (info->productVersion.isEmpty() && line.startsWith(versionKey)) { + const QByteArray value(line.constData() + versionKey.size()); + info->productVersion = unquote(value); + ++valid_data_count; + continue; + } + } + + file.close(); + + return valid_data_count != 0; +} + +static bool readOsRelease(DSysInfoPrivate *info) +{ + if (!readEtcFile(info, "/etc/os-release", "ID=", "VERSION_ID=", "PRETTY_NAME=")) + return readEtcFile(info, "/usr/lib/os-release", "ID=", "VERSION_ID=", "PRETTY_NAME="); + + return true; +} + +static bool readLsbRelease(DSysInfoPrivate *info) +{ + return readEtcFile(info, "/etc/lsb-release", "DISTRIB_ID=", "DISTRIB_RELEASE=", "DISTRIB_DESCRIPTION="); +} +#endif + +void DSysInfoPrivate::ensureReleaseInfo() +{ + if (productType >= 0) { + return; + } + +#ifdef Q_OS_LINUX + readOsRelease(this); + readLsbRelease(this); + + if (productTypeString.isEmpty()) { + productType = DSysInfo::UnknownType; + } else { + switch (productTypeString.at(0).unicode()) { + case 'd': + case 'D': + if (productTypeString.compare("deepin", Qt::CaseInsensitive) == 0) { + productType = DSysInfo::Deepin; + } else if (productTypeString.compare("debian", Qt::CaseInsensitive) == 0) { + productType = DSysInfo::Debian; + } + break; + case 'a': + case 'A': + if (productTypeString.compare("arch", Qt::CaseInsensitive) == 0) + productType = DSysInfo::ArchLinux; + break; + case 'c': + case 'C': + if (productTypeString.compare("centos", Qt::CaseInsensitive) == 0) + productType = DSysInfo::CentOS; + break; + case 'f': + case 'F': + if (productTypeString.compare("fedora", Qt::CaseInsensitive) == 0) + productType = DSysInfo::Fedora; + break; + case 'g': + case 'G': + if (productTypeString.compare("gentoo", Qt::CaseInsensitive) == 0) + productType = DSysInfo::Gentoo; + break; + case 'l': + case 'L': + if (productTypeString.compare("linuxmint", Qt::CaseInsensitive) == 0) + productType = DSysInfo::LinuxMint; + break; + case 'm': + case 'M': + if (productTypeString.compare("manjaro", Qt::CaseInsensitive) == 0) + productType = DSysInfo::Manjaro; + break; + case 'n': + case 'N': + if (productTypeString.compare("nixos", Qt::CaseInsensitive) == 0) + productType = DSysInfo::NixOS; + break; + case 'o': + case 'O': + if (productTypeString.compare("opensuse", Qt::CaseInsensitive) == 0) + productType = DSysInfo::openSUSE; + break; + case 's': + case 'S': + if (productTypeString.compare("sailfishos", Qt::CaseInsensitive) == 0) + productType = DSysInfo::SailfishOS; + break; + case 'u': + case 'U': + if (productTypeString.compare("ubuntu", Qt::CaseInsensitive) == 0) { + productType = DSysInfo::Ubuntu; + } else if (productTypeString.compare("uos", Qt::CaseInsensitive) == 0 || productTypeString.compare("UnionTech OS", Qt::CaseInsensitive) == 0) { + productType = DSysInfo::Uos; + } + break; + default: + productType = DSysInfo::UnknownType; + break; + } + } +#endif +} + +void DSysInfoPrivate::ensureComputerInfo() +{ +#ifdef Q_OS_LINUX + +#endif +} + +QMap DSysInfoPrivate::parseInfoFile(QFile &file) +{ + char buf[1024]; + qint64 lineLength = 0; + QMap map; + do { + lineLength = file.readLine(buf, sizeof(buf)); + QString s(buf); + if (s.contains(':')) { + QStringList list = s.split(':'); + if (list.size() == 2) { + map.insert(list.first().trimmed(), list.back().trimmed()); + } + } + } while (lineLength >= 0); + return map; +} + +Q_GLOBAL_STATIC(DSysInfoPrivate, siGlobal) + +QString DSysInfo::operatingSystemName() +{ + siGlobal->ensureReleaseInfo(); + + return siGlobal->prettyName; +} + +#ifdef Q_OS_LINUX +/*! + \brief Check current distro is Deepin or not. + \note Uos will also return true. + */ +bool DSysInfo::isDeepin() +{ + siGlobal->ensureReleaseInfo(); + + return productType() == Deepin || productType() == Uos; +} + +bool DSysInfo::isDDE() +{ + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinType != UnknownDeepin; +} + +DSysInfo::DeepinType DSysInfo::deepinType() +{ + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinType; +} + +QString DSysInfo::deepinTypeDisplayName(const QLocale &locale) +{ + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinTypeMap.value(locale.name(), siGlobal->deepinTypeMap.value(QString())); +} + +QString DSysInfo::deepinVersion() +{ + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinVersion; +} + +QString DSysInfo::deepinEdition() +{ + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinEdition; +} + +QString DSysInfo::deepinCopyright() +{ + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinCopyright; +} + +/*! +@~english + \brief + Display system type [1: desktop] [2: server] [3: special devices] + \note 根据 osBuild.B 判断 + */ +DSysInfo::UosType DSysInfo::uosType() +{ + siGlobal->ensureOsVersion(); + + UosType ost = UosTypeUnknown; + if ((siGlobal->osBuild.B > UosTypeUnknown && siGlobal->osBuild.B < UosTypeCount)) { + ost = static_cast(siGlobal->osBuild.B); + } + + return ost; +} + +/*! +@~english + \brief + Editions: professional version/personal version/community version ... + \note According to osbuild.b && osbuild.d + */ +DSysInfo::UosEdition DSysInfo::uosEditionType() +{ + siGlobal->ensureOsVersion(); + UosEdition ospt = UosEditionUnknown; + if (siGlobal->osBuild.B == UosDesktop) { + switch (siGlobal->osBuild.D) { + case 1: + return UosProfessional; + case 2: + case 7: + //The new version of the family version (7) and the old version of the personal version (2) The same as the home does not modify the old logic (7) to ensure the adaptation of the old version + return UosHome; + case 3: + return UosCommunity; + case 4: + return UosMilitary; + case 5: + return UosDeviceEdition; + case 6: + return UosEducation; + default: + break; + } + } else if (siGlobal->osBuild.B == UosServer) { + switch (siGlobal->osBuild.D) { + case 1: + return UosEnterprise; + case 2: + return UosEnterpriseC; + case 3: + return UosEuler; + case 4: + return UosMilitaryS; + case 5: + return UosDeviceEdition; + default: + break; + } + } else if (siGlobal->osBuild.B == UosDevice){ + ospt = UosEnterprise; // os-version 1.4 if B==Device then et=Enterprise + } + + return ospt; +} + +/*! +@~english + \brief Architecture information (using bit flags of a byte) + 【0x8 sw64】【0x4 mips64】【0x2 arm64】【0x1 amd64】 + */ +DSysInfo::UosArch DSysInfo::uosArch() +{ + siGlobal->ensureOsVersion(); + + return static_cast(siGlobal->osBuild.E); +} + +static QString getUosVersionValue(const QString &key, const QLocale &locale) +{ + DDesktopEntry entry(OS_VERSION_FILE); + QString localKey = QString("%1[%2]").arg(key, locale.name()); + + return entry.stringValue(localKey, "Version", entry.stringValue(key, "Version")); +} + +/*! +@~english + \brief Version name + ProductType[xx] The corresponding value of the item, if you can't find the value of the corresponding language, use the value of the productType (desktop/server/device) + \a locale Current system language + */ +QString DSysInfo::uosProductTypeName(const QLocale &locale) +{ + return getUosVersionValue("ProductType", locale); +} + +/*! +@~english + \brief DSysInfo::osSystemName Version name + + The corresponding value corresponding to SystemName [xx] item, if you can't find the default language of the corresponding language, use the value of SystemName uniontech os + \a locale Current system language + */ +QString DSysInfo::uosSystemName(const QLocale &locale) +{ + return getUosVersionValue("SystemName", locale); +} + +/*! +@~english + \brief DSysInfo::osEditionName Version name + EditionName[xx] The corresponding value of the item, if you can't find the value of the corresponding language, use the value of EditionName (Professional/Home/Community ...) + \a locale Current system language + */ +QString DSysInfo::uosEditionName(const QLocale &locale) +{ + return getUosVersionValue("EditionName", locale); +} + +/*! +@~english + \brief DSysInfo::spVersion Period version name + BC, A.B.C in the small version number a-bc-d + Return to SP1-SPXX, if the official version returns empty + In the x.y.z mode, it will not support returning this version number for the time being + \ note minversion.bc == 00: The official version minversion.bc | minversion.b == 01-99: SP1 ... .sp99 + */ +QString DSysInfo::spVersion() +{ + siGlobal->ensureOsVersion(); + switch (siGlobal->minVersion.type) { + case DSysInfoPrivate::MinVersion::A_BC_D: { + if (siGlobal->minVersion.BC > 0) { + return QString("SP%1").arg(siGlobal->minVersion.BC); + } else { + return QString(); // 00 正式版 + } + } + + case DSysInfoPrivate::MinVersion::A_B_C: { + if (siGlobal->minVersion.B > 0) { + return QStringLiteral("SP%1").arg(siGlobal->minVersion.B); + } else { + return {}; + } + } + + case DSysInfoPrivate::MinVersion::X_Y_Z: + qWarning() << "Getting the SP version in this mode is not supported."; + return {}; + } + return QString(); +} + +/*! +@~english + \brief DSysInfo::udpateVersion Update version name + minor version number D in A-BC-D mode、C in A.B.C mode + Return to Update1 ... Update9, if the official version returns to empty + In the x.y.z mode, it will not support returning this version number for the time being + \note minVersion.D == 0:official version minVersion.D | minVersion.C == 1-9:update1… update9,updateA...updateZ + */ +QString DSysInfo::udpateVersion() +{ + siGlobal->ensureOsVersion(); + switch (siGlobal->minVersion.type) { + case DSysInfoPrivate::MinVersion::A_BC_D: { + if (siGlobal->minVersion.D > 0) { + uint uv = siGlobal->minVersion.D; + if (uv < 10) { + return QString("update%1").arg(uv); + } else if (uv < 36) { + return QString("update").append(QChar(uv - 10 + 'A')); + } else { + qWarning() << "invalid update versoin"; + break; + } + } else { + break; // 0 正式版 + } + } + + case DSysInfoPrivate::MinVersion::A_B_C: { + if (siGlobal->minVersion.C > 0) { + return QStringLiteral("update%1").arg(siGlobal->minVersion.C); + } else { + break; + } + } + + case DSysInfoPrivate::MinVersion::X_Y_Z: + qWarning() << "Getting the update version in this mode is not supported."; + break; + } + + return {}; +} + +/*! +@~english + \brief Main edition number + Main edition number 【20】【23】【25】【26】【29】【30】 + \note Return to Majorversion value + */ +QString DSysInfo::majorVersion() +{ + siGlobal->ensureOsVersion(); + return siGlobal->majorVersion; +} + +/*! +@~english + \brief DSysInfo::minorVersion minor version + *【ABCD】 ·[0-9]{4} + *【A.B.C】 or【X.Y.Z】 + @return the value of minorversion + */ +QString DSysInfo::minorVersion() +{ + siGlobal->ensureOsVersion(); + return siGlobal->minorVersion; +} + +/*! +@~english + \brief DSysInfo::buildVersion Small version number + System mirror batch number, in order of time (non-retreat) increase from 100-999 + \note Return osbuild.xyz value + */ +QString DSysInfo::buildVersion() +{ + DDesktopEntry entry(OS_VERSION_FILE); + QString osb = entry.stringValue("OsBuild", "Version"); + return osb.mid(6).trimmed(); +} +#endif + +QString DSysInfo::deepinDistributionInfoPath() +{ + return distributionInfoPath(); +} + +QString DSysInfo::distributionInfoPath() +{ +#ifdef Q_OS_LINUX + return "/usr/share/deepin/distribution.info"; +#else + return QDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)).filePath("deepin-distribution.info"); +#endif // Q_OS_LINUX +} + +QString DSysInfo::distributionInfoSectionName(DSysInfo::OrgType type) +{ + switch (type) { + case Distribution: + return "Distribution"; + case Distributor: + return "Distributor"; + case Manufacturer: + return "Manufacturer"; + } + + return QString(); +} + +/*! +@~english + \return the organization name. + + use \a type as Distribution to get the name of current deepin distribution itself. + + \sa deepinDistributionInfoPath() + */ +QString DSysInfo::distributionOrgName(DSysInfo::OrgType type, const QLocale &locale) +{ +#ifdef Q_OS_LINUX + siGlobal->ensureDistributionInfo(); +#endif + + QString fallback = type == Distribution ? QStringLiteral("Deepin") : QString(); + + return siGlobal->distributionInfo->localizedValue("Name", locale, distributionInfoSectionName(type), fallback); +} + +QString DSysInfo::deepinDistributorName() +{ + return distributionOrgName(Distributor); +} + +/*! +@~english + \return the organization website name and url. + + use \a type as Distribution to get the name of current deepin distribution itself. + + \sa deepinDistributionInfoPath() + */ +QPair DSysInfo::distributionOrgWebsite(DSysInfo::OrgType type) +{ +#ifdef Q_OS_LINUX + siGlobal->ensureDistributionInfo(); +#endif + + QString fallbackSiteName = type == Distribution ? QStringLiteral("www.deepin.org") : QString(); + QString fallbackSiteUrl = type == Distribution ? QStringLiteral("https://www.deepin.org") : QString(); + + return { + siGlobal->distributionInfo->stringValue("WebsiteName", distributionInfoSectionName(type), fallbackSiteName), + siGlobal->distributionInfo->stringValue("Website", distributionInfoSectionName(type), fallbackSiteUrl), + }; +} + +QPair DSysInfo::deepinDistributorWebsite() +{ + return distributionOrgWebsite(Distributor); +} + +/*! +@~english + \return the obtained organization logo path, or the given \a fallback one if there are no such logo. + + use \a type as Distribution to get the logo of current deepin distribution itself. + + \sa deepinDistributionInfoPath() + */ +QString DSysInfo::distributionOrgLogo(DSysInfo::OrgType orgType, DSysInfo::LogoType type, const QString &fallback) +{ + DDesktopEntry distributionInfo(distributionInfoPath()); + QString orgSectionName = distributionInfoSectionName(orgType); + + switch (type) { + case Normal: + return distributionInfo.stringValue("Logo", orgSectionName, fallback); + case Light: + return distributionInfo.stringValue("LogoLight", orgSectionName, fallback); + case Symbolic: + return distributionInfo.stringValue("LogoSymbolic", orgSectionName, fallback); + case Transparent: + return distributionInfo.stringValue("LogoTransparent", orgSectionName, fallback); + } + + return QString(); +} + +QString DSysInfo::deepinDistributorLogo(DSysInfo::LogoType type, const QString &fallback) +{ + return distributionOrgLogo(Distributor, type, fallback); +} + +DSysInfo::ProductType DSysInfo::productType() +{ + siGlobal->ensureReleaseInfo(); + + return siGlobal->productType; +} + +QString DSysInfo::productTypeString() +{ + siGlobal->ensureReleaseInfo(); + + return siGlobal->productTypeString; +} + +QString DSysInfo::productVersion() +{ + siGlobal->ensureReleaseInfo(); + + return siGlobal->productVersion; +} + +/*! +@~english + \brief Check if current edition is a community edition + + Developer can use this way to check if we need enable or disable features + for community or enterprise edition. + + Current rule: + - Professional, Server, Personal edition (DeepinType) will be treat as Enterprise edition. + - Uos (ProductType) will be treat as Enterprise edition. + + \return true if it's on a community edition distro/installation + */ +bool DSysInfo::isCommunityEdition() +{ +#ifdef Q_OS_LINUX + DeepinType type = deepinType(); + QList enterpriseTypes { + DeepinProfessional, DeepinServer, DeepinPersonal + }; + + if (enterpriseTypes.contains(type)) { + return false; + } + + if (productType() == Uos) { + return false; + } +#endif // Q_OS_LINUX + + return true; +} + +QString DSysInfo::computerName() +{ +#ifdef Q_OS_LINUX + struct utsname u; + if (uname(&u) == 0) + siGlobal->computerName = QString::fromLatin1(u.nodename); + + return siGlobal->computerName; +#endif + return QString(); +} + +QString DSysInfo::cpuModelName() +{ +#ifdef Q_OS_LINUX + static QFile file("/proc/cpuinfo"); + + if (file.open(QFile::ReadOnly)) { + QMap map = siGlobal->parseInfoFile(file); + if (map.contains("Processor")) { + // arm-cpuinfo hw_kirin-cpuinfo + siGlobal->cpuModelName = map.value("Processor"); + } else if (map.contains("model name")) { + // cpuinfo + siGlobal->cpuModelName = map.value("model name"); + } else if (map.contains("cpu model")) { + // loonson3-cpuinfo sw-cpuinfo + siGlobal->cpuModelName = map.value("cpu model"); + } + + file.close(); + } + return siGlobal->cpuModelName; +#endif + return QString(); +} + +/*! +@~english + \return the installed memory size + */ +qint64 DSysInfo::memoryInstalledSize() +{ +#ifdef Q_OS_LINUX + // Getting Memory Installed Size + // TODO: way to not dept on lshw? + if (!QStandardPaths::findExecutable("lshw").isEmpty()) { + QProcess lshw; + + lshw.start("lshw", {"-c", "memory", "-json", "-sanitize"}, QIODevice::ReadOnly); + + if (!lshw.waitForFinished()) { + return -1; + } + + const QByteArray &lshwInfoJson = lshw.readAllStandardOutput(); + QJsonArray lshwResultArray = QJsonDocument::fromJson(lshwInfoJson).array(); + if (!lshwResultArray.isEmpty()) { + QJsonValue memoryHwInfo = lshwResultArray.first(); + QString id = memoryHwInfo.toObject().value("id").toString(); + Q_ASSERT(id == "memory"); + siGlobal->memoryInstalledSize = memoryHwInfo.toObject().value("size").toDouble(); // TODO: check "units" is "bytes" ? + } + } + + return siGlobal->memoryInstalledSize; +#endif + return -1; +} + +/*! +@~english + \return the total available to use memory size + */ +qint64 DSysInfo::memoryTotalSize() +{ +#ifdef Q_OS_LINUX + siGlobal->memoryAvailableSize = get_phys_pages() * sysconf(_SC_PAGESIZE); + return siGlobal->memoryAvailableSize; +#endif + return -1; +} + +qint64 DSysInfo::systemDiskSize() +{ +#ifdef Q_OS_LINUX + // Getting Disk Size + const QString &deviceName = QStorageInfo::root().device(); + QProcess lsblk; + + lsblk.start("lsblk", {"-Jlpb", "-oNAME,KNAME,PKNAME,SIZE"}, QIODevice::ReadOnly); + + if (!lsblk.waitForFinished()) { + return -1; + } + + const QByteArray &diskStatusJson = lsblk.readAllStandardOutput(); + QJsonDocument diskStatus = QJsonDocument::fromJson(diskStatusJson); + QJsonValue diskStatusJsonValue = diskStatus.object().value("blockdevices"); + QMap> deviceParentAndSizeMap; + + if (!diskStatusJsonValue.isUndefined()) { + QJsonArray diskStatusArray = diskStatusJsonValue.toArray(); + QString keyName; + + for (const QJsonValue oneValue : diskStatusArray) { + QString name = oneValue.toObject().value("name").toString(); + QString kname = oneValue.toObject().value("kname").toString(); + QString pkname = oneValue.toObject().value("pkname").toString(); + qulonglong size = oneValue.toObject().value("size").toVariant().toULongLong(); + + if (keyName.isNull() && deviceName == name) { + keyName = kname; + } + + deviceParentAndSizeMap[kname] = QPair(pkname, size); + } + + while (!deviceParentAndSizeMap[keyName].first.isNull()) { + keyName = deviceParentAndSizeMap[keyName].first; + } + + siGlobal->diskSize = deviceParentAndSizeMap[keyName].second; + } + + return siGlobal->diskSize; + +#endif + + return -1; +} + +/*! @~english DSysInfo::bootTime + * @~english \sa DSysInfo::uptime + * @~english \return the boot time(currentDateTime - uptime) +*/ +QDateTime DSysInfo::bootTime() +{ + qint64 ut = uptime(); + return ut > 0 ? QDateTime::currentDateTime().addSecs(-ut) : QDateTime(); +} + +/*! @~english DSysInfo::shutdownTime + * @~english \return the last shutdown time +*/ +QDateTime DSysInfo::shutdownTime() +{ + QDateTime dt; +#if defined Q_OS_LINUX + QProcess lastx; + lastx.start("last", {"-x", "-F" }, QIODevice::ReadOnly); + if (!lastx.waitForFinished()) { + qWarning() << lastx.errorString(); + return QDateTime(); + } + + while (lastx.canReadLine()) { + const QByteArray data = lastx.readLine(1024); + //shutdown system down 4.19.0-amd64-des Fri Sep 30 17:53:17 2022 - Sat Oct 8 08:32:47 2022 (7+14:39) + if (data.startsWith("shutdown")) { + QString timeFmt = QString(data).split(' ', QString::SkipEmptyParts).mid(4, 5).join(' '); + dt = QDateTime::fromString(timeFmt); + break; + } + } +#else + +#endif + return dt; +} + +/*! @~english DSysInfo::uptime + * @~english \return the up time (/proc/uptime) +*/ +qint64 DSysInfo::uptime() +{ +#if defined Q_OS_LINUX + QFile file("/proc/uptime"); + if (!file.open(QFile::ReadOnly)) { + qWarning() << file.errorString(); + return -1; + } + + QByteArray upTime = file.readAll(); + bool ok = false; + qint64 sec = qCeil(upTime.split(' ').value(0).toDouble(&ok)); // [0]: uptime [1]: idletime + + return ok ? sec : -1; +#elif defined Q_OS_WIN64 + return GetTickCount64(); +#elif defined Q_OS_WIN32 + return GetTickCount(); +#else + return -1; +#endif +} + +/*! @~english DSysInfo::arch + * @~english \return the architecture of processor +*/ +DSysInfo::Arch DSysInfo::arch() +{ +#if defined(__x86_64__) + return X86_64; +#elif defined(__i386__) + return X86; +#elif defined(__powerpc64__) +# if __BYTE_ORDER == __BIG_ENDIAN + return PPC64; +# else + return PPC64_LE; +# endif +#elif defined(__powerpc__) +# if __BYTE_ORDER == __BIG_ENDIAN + return PPC; +# else + return PPC_LE; +# endif +#elif defined(__ia64__) + return IA64; +#elif defined(__hppa64__) + return PARISC64; +#elif defined(__hppa__) + return PARISC; +#elif defined(__s390x__) + return S390X; +#elif defined(__s390__) + return S390; +#elif defined(__sparc__) && defined (__arch64__) + return SPARC64; +#elif defined(__sparc__) + return SPARC; +#elif defined(__mips64) && defined(__LP64__) +# if __BYTE_ORDER == __BIG_ENDIAN + return MIPS64; +# else + return MIPS64_LE; +# endif +#elif defined(__mips64) +# if __BYTE_ORDER == __BIG_ENDIAN + return MIPS64; +# else + return MIPS64_LE; +# endif +#elif defined(__mips__) +# if __BYTE_ORDER == __BIG_ENDIAN + return MIPS; +# else + return MIPS_LE; +# endif +#elif defined(__alpha__) + return ALPHA; +#elif defined(__aarch64__) +# if __BYTE_ORDER == __BIG_ENDIAN + return ARM64_BE; +# else + return ARM64; +# endif +#elif defined(__arm__) +# if __BYTE_ORDER == __BIG_ENDIAN + return ARM_BE; +# else + return ARM; +# endif +#elif defined(__sh64__) + return SH64; +#elif defined(__sh__) + return SH; +#elif defined(__loongarch64) + return LOONGARCH64; +#elif defined(__m68k__) + return M68K; +#elif defined(__tilegx__) + return TILEGX; +#elif defined(__cris__) + return CRIS; +#elif defined(__nios2__) + return NIOS2; +#elif defined(__riscv) +# if __SIZEOF_POINTER__ == 4 + return RISCV32; +# elif __SIZEOF_POINTER__ == 8 + return RISCV64; +# else +# error "Unrecognized riscv architecture variant" +# endif +#elif defined(__arc__) +# if __BYTE_ORDER == __BIG_ENDIAN + return ARC_BE; +# else + return ARC; +# endif +#elif defined(__sw_64__) + return SW_64; +#else +# error "Please register your architecture here!" +#endif +} + +DCORE_END_NAMESPACE diff --git a/src/dtkcore_global.cpp b/src/dtkcore_global.cpp new file mode 100644 index 0000000..c22d020 --- /dev/null +++ b/src/dtkcore_global.cpp @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtkcore_global.h" +#include +#include + +#if (!defined DTK_VERSION) || (!defined DTK_VERSION_STR) +#error "DTK_VERSION or DTK_VERSION_STR not defined!" +#endif + +void doubleLoadCheck() +{ + QFile f("/proc/self/maps"); + if (!f.open(QIODevice::ReadOnly)) + qFatal("%s", f.errorString().toLocal8Bit().data()); + + const QByteArray &data = f.readAll(); + QTextStream ts(data); + QString modulePath; + while (Q_UNLIKELY(!ts.atEnd())) { + const QString line = ts.readLine(); + const QStringList &maps = line.split(' ', QString::SplitBehavior::SkipEmptyParts); + if (Q_UNLIKELY(maps.size() < 6)) + continue; + + QFileInfo info(maps.value(5)); + const QString &infoAbPath = info.absoluteFilePath(); + if (modulePath == infoAbPath || !info.fileName().contains("dtkcore") || info.fileName().contains("dtkcore.so.2")) + continue; + + if (modulePath.isEmpty()) { + modulePath = infoAbPath; + } else { + // modulePath != infoAbPath + QByteArray msg; + msg += modulePath + " and " + info.absoluteFilePath() + " both loaded"; + qFatal("%s", msg.data()); + } + } +} + +// 在库被加载时就执行此函数 +__attribute__((constructor)) void init() +{ + doubleLoadCheck(); +} + +int dtkVersion() +{ + return DTK_VERSION; +} + +const char *dtkVersionString() +{ +#ifdef QT_DEBUG + qWarning() << "Use DTK_VERSION_STR instead."; +#endif + return "";//DTK_VERSION_STR; +} diff --git a/src/filesystem/dbasefilewatcher.cpp b/src/filesystem/dbasefilewatcher.cpp new file mode 100644 index 0000000..f626a24 --- /dev/null +++ b/src/filesystem/dbasefilewatcher.cpp @@ -0,0 +1,187 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dbasefilewatcher.h" +#include "private/dbasefilewatcher_p.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +QList DBaseFileWatcherPrivate::watcherList; +DBaseFileWatcherPrivate::DBaseFileWatcherPrivate(DBaseFileWatcher *qq) + : DObjectPrivate(qq) +{ + +} + +/*! + \class Dtk::Core::DBaseFileWatcher + \inmodule dtkcore + + \brief The DBaseFileWatcher class provides an interface for monitoring files and directories for modifications. + \brief DBaseFileWatcher 类提供了一系列接口可供监视文件和目录的变动。 +*/ + +DBaseFileWatcher::~DBaseFileWatcher() +{ + stopWatcher(); + DBaseFileWatcherPrivate::watcherList.removeOne(this); +} + +QUrl DBaseFileWatcher::fileUrl() const +{ + Q_D(const DBaseFileWatcher); + + return d->url; +} + +/*! + \brief 开始文件变动监视 + \brief Let file watcher start watching file changes. + \return 成功开始返回 true ,否则返回 false. + + \sa stopWatcher(), restartWatcher() + */ +bool DBaseFileWatcher::startWatcher() +{ + Q_D(DBaseFileWatcher); + + if (d->started) + return true; + + if (d->start()) { + d->started = true; + + return true; + } + + return false; +} + +/*! + \brief 停止文件变动监视. + \brief Stop watching file changes. + \return 成功停止返回 true ,否则返回 false. + + \sa startWatcher(), restartWatcher() + */ +bool DBaseFileWatcher::stopWatcher() +{ + Q_D(DBaseFileWatcher); + + if (!d->started) + return false; + + if (d->stop()) { + d->started = false; + + return true; + } + + return false; +} + +/*! + \brief 重新开始文件变动监视. + \brief Stop file watcher and then restart it to watching file changes. + \return 成功开启返回 true,否则返回 false. + + \sa startWatcher(), stopWatcher() + */ +bool DBaseFileWatcher::restartWatcher() +{ + bool ok = stopWatcher(); + return ok && startWatcher(); +} + +/*! + \brief 设置是否对 \a subfileUrl 目录启用文件监视 + \brief Set enable file watcher for \a subfileUrl or not + + \a subfileUrl 设置所针对的 Url + \a subfileUrl The given url + + \a enabled 是否启用文件变动监视 + \a enabled Enable file change watching or not. + */ +void DBaseFileWatcher::setEnabledSubfileWatcher(const QUrl &subfileUrl, bool enabled) +{ + Q_UNUSED(subfileUrl) + Q_UNUSED(enabled) +} + +/*! + \brief 发送一个信号表示目标目录 \a targetUrl 得到了一个 \a signal 信号,包含参数 \a arg1 。 + \brief Emit a signal about \a targetUrl got a \a signal with \a arg1 + + 示例用法: + Example usage: + + \code + DBaseFileWatcher::ghostSignal(QUrl("bookmark:///"), &DBaseFileWatcher::fileDeleted, QUrl("bookmark:///bookmarkFile1")); + \endcode + + \return 成功发送返回 true,否则返回 false. + */ +bool DBaseFileWatcher::ghostSignal(const QUrl &targetUrl, DBaseFileWatcher::SignalType1 signal, const QUrl &arg1) +{ + if (!signal) + return false; + + bool ok = false; + + for (DBaseFileWatcher *watcher : DBaseFileWatcherPrivate::watcherList) { + if (watcher->fileUrl() == targetUrl) { + ok = true; + (watcher->*signal)(arg1); + } + } + + return ok; +} + +/*! + \brief 发送一个信号表示目标目录 \a targetUrl 得到了一个 \a signal 信号,包含参数 \a arg1 和 arg2。 + \brief Emit a signal about \a targetUrl got a \a signal with \a arg1 and \a arg2 + + 示例用法: + Example usage: + + \code + DBaseFileWatcher::ghostSignal(QUrl("bookmark:///"), &DBaseFileWatcher::fileMoved, QUrl("bookmark:///bookmarkFile1"), QUrl("bookmark:///NewNameFile1")); + \endcode + */ +bool DBaseFileWatcher::ghostSignal(const QUrl &targetUrl, DBaseFileWatcher::SignalType2 signal, const QUrl &arg1, const QUrl &arg2) +{ + if (!signal) + return false; + + bool ok = false; + + for (DBaseFileWatcher *watcher : DBaseFileWatcherPrivate::watcherList) { + if (watcher->fileUrl() == targetUrl) { + ok = true; + (watcher->*signal)(arg1, arg2); + } + } + + return ok; +} + +DBaseFileWatcher::DBaseFileWatcher(DBaseFileWatcherPrivate &dd, + const QUrl &url, QObject *parent) + : QObject(parent) + , DObject(dd) +{ + Q_ASSERT(url.isValid()); + + d_func()->url = url; + DBaseFileWatcherPrivate::watcherList << this; +} + +DCORE_END_NAMESPACE + +//#include "moc_dbasefilewatcher.cpp" diff --git a/src/filesystem/dcapfile.cpp b/src/filesystem/dcapfile.cpp new file mode 100644 index 0000000..e1cafea --- /dev/null +++ b/src/filesystem/dcapfile.cpp @@ -0,0 +1,386 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dcapfile.h" +#include "dobject_p.h" +#include "dcapmanager.h" +#include "private/dcapfsfileengine_p.h" + +#include + +DCORE_BEGIN_NAMESPACE + +extern QString _d_cleanPath(const QString &path); +extern bool _d_isSubFileOf(const QString &filePath, const QString &directoryPath); + +class DCapFilePrivate : public DObjectPrivate +{ + D_DECLARE_PUBLIC(DCapFile) +public: + DCapFilePrivate(DCapFile *qq, const QString &fileName = QString()); + static bool canReadWrite(const QString &path); + + QString fileName; +}; + +DCapFilePrivate::DCapFilePrivate(DCapFile *qq, const QString &fileName) + : DObjectPrivate(qq) + , fileName(fileName) +{ +} + +bool DCapFilePrivate::canReadWrite(const QString &path) +{ + DCapFSFileEngine engine(path); + return engine.canReadWrite(path); +} + +DCapFile::DCapFile(QObject *parent) + : QFile(parent) + , DObject(*new DCapFilePrivate(this)) +{ + +} + +DCapFile::DCapFile(const QString &name, QObject *parent) + : QFile(name, parent) + , DObject(*new DCapFilePrivate(this, name)) +{ +} + +DCapFile::~DCapFile() +{ + +} + +void DCapFile::setFileName(const QString &name) +{ + D_D(DCapFile); + d->fileName = name; + return QFile::setFileName(name); +} + +bool DCapFile::exists() const +{ + D_DC(DCapFile); + if (!d->canReadWrite(d->fileName)) + return false; + + return QFile::exists(); +} + +bool DCapFile::exists(const QString &fileName) +{ + return DCapFile(fileName).exists(); +} + +QString DCapFile::readLink() const +{ + D_DC(DCapFile); + if (!d->canReadWrite(d->fileName)) + return {}; + + return QFile::readLink(); +} + +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) +QString DCapFile::symLinkTarget() const +{ + return readLink(); +} +#endif + +bool DCapFile::remove() +{ + D_D(DCapFile); + if (!d->canReadWrite(d->fileName)) + return false; + + return QFile::remove(); +} + +bool DCapFile::remove(const QString &fileName) +{ + return DCapFile(fileName).remove(); +} + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) +bool DCapFile::moveToTrash() +{ + D_D(DCapFile); + if (!d->canReadWrite(d->fileName)) + return false; + + return QFile::moveToTrash(); +} + +bool DCapFile::moveToTrash(const QString &fileName, QString *pathInTrash) +{ + DCapFile file(fileName); + if (file.moveToTrash()) { + if (pathInTrash) + *pathInTrash = file.fileName(); + return true; + } + return false; +} +#endif + +bool DCapFile::rename(const QString &newName) +{ + D_D(DCapFile); + if (!d->canReadWrite(newName)) + return false; + + return QFile::rename(newName); +} + +bool DCapFile::rename(const QString &oldName, const QString &newName) +{ + if (!DCapFilePrivate::canReadWrite(oldName)) + return false; + + return DCapFile(oldName).rename(newName); +} + +bool DCapFile::link(const QString &newName) +{ + D_D(DCapFile); + if (!d->canReadWrite(newName)) + return false; + + return QFile::link(newName); +} + +bool DCapFile::link(const QString &oldName, const QString &newName) +{ + if (!DCapFilePrivate::canReadWrite(oldName)) + return false; + + return DCapFile(oldName).link(newName); +} + +bool DCapFile::copy(const QString &newName) +{ + D_D(DCapFile); + if (!d->canReadWrite(newName)) + return false; + + return QFile::copy(newName); +} + +bool DCapFile::copy(const QString &fileName, const QString &newName) +{ + if (!DCapFilePrivate::canReadWrite(fileName)) + return false; + + return DCapFile(fileName).copy(newName); +} + +bool DCapFile::open(QIODevice::OpenMode flags) +{ + D_D(DCapFile); + if (!d->canReadWrite(d->fileName)) + return false; + + return QFile::open(flags); +} + +bool DCapFile::resize(qint64 sz) +{ + D_D(DCapFile); + if (!d->canReadWrite(d->fileName)) + return false; + + return QFile::resize(sz); +} + +bool DCapFile::resize(const QString &fileName, qint64 sz) +{ + return DCapFile(fileName).resize(sz); +} + +bool DCapFile::open(FILE *, QIODevice::OpenMode, QFileDevice::FileHandleFlags) +{ + return false; +} + +bool DCapFile::open(int, QIODevice::OpenMode, QFileDevice::FileHandleFlags) +{ + return false; +} + +class DCapDirPrivate : public QSharedData +{ +public: + DCapDirPrivate(QString filePath); + explicit DCapDirPrivate(const DCapDirPrivate ©); + + QString filePath; +}; + +DCapDirPrivate::DCapDirPrivate(QString filePath) + : filePath(filePath) +{ +} + +DCapDirPrivate::DCapDirPrivate(const DCapDirPrivate ©) + : QSharedData(copy) + , filePath(copy.filePath) +{ +} + +DCapDir::DCapDir(const DCapDir &dir) + : QDir(dir) + , dd_ptr(dir.dd_ptr) +{ + +} + +DCapDir::DCapDir(const QString &path) + : QDir(path) + , dd_ptr(new DCapDirPrivate(path)) +{ + +} + +DCapDir::DCapDir(const QString &path, const QString &nameFilter, + QDir::SortFlags sort, QDir::Filters filter) + : QDir(path, nameFilter, sort, filter) + , dd_ptr(new DCapDirPrivate(path)) +{ + +} + +DCapDir::~DCapDir() +{ + +} + +void DCapDir::setPath(const QString &path) +{ + dd_ptr = new DCapDirPrivate(path); + return QDir::setPath(path); +} + +bool DCapDir::cd(const QString &dirName) +{ + auto old_d = d_ptr; + bool ret = QDir::cd(dirName); + if (!ret) + return ret; + + // take the new path. + auto path = QDir::filePath(""); + QScopedPointer fsEngine(new DCapFSFileEngine(path)); + if (fsEngine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::ExistsFlag) { + dd_ptr = new DCapDirPrivate(path); + return true; + } + d_ptr = old_d; + return false; +} + +QStringList DCapDir::entryList(DCapDir::Filters filters, DCapDir::SortFlags sort) const +{ + const QDirPrivate* d = d_ptr.constData(); + return entryList(d->nameFilters, filters, sort); +} + +QStringList DCapDir::entryList(const QStringList &nameFilters, DCapDir::Filters filters, DCapDir::SortFlags sort) const +{ + if (!DCapFilePrivate::canReadWrite(dd_ptr->filePath)) + return {}; + return QDir::entryList(nameFilters, filters, sort); +} + +QFileInfoList DCapDir::entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const +{ + const QDirPrivate* d = d_ptr.constData(); + return entryInfoList(d->nameFilters, filters, sort); +} + +QFileInfoList DCapDir::entryInfoList(const QStringList &nameFilters, DCapDir::Filters filters, DCapDir::SortFlags sort) const +{ + if (!DCapFilePrivate::canReadWrite(dd_ptr->filePath)) + return {}; + return QDir::entryInfoList(nameFilters, filters, sort); +} + +bool DCapDir::mkdir(const QString &dirName) const +{ + QString fn = filePath(dirName); + if (!DCapFilePrivate::canReadWrite(fn)) + return false; + + return QDir::mkdir(dirName); +} + +bool DCapDir::rmdir(const QString &dirName) const +{ + QString fn = filePath(dirName); + if (!DCapFilePrivate::canReadWrite(fn)) + return false; + + return QDir::rmdir(dirName); +} + +bool DCapDir::mkpath(const QString &dirPath) const +{ + QString fn = filePath(dirPath); + if (!DCapFilePrivate::canReadWrite(fn)) + return false; + + return QDir::mkpath(dirPath); +} + +bool DCapDir::rmpath(const QString &dirPath) const +{ + QString fn = filePath(dirPath); + if (!DCapFilePrivate::canReadWrite(fn)) + return false; + + return QDir::rmpath(dirPath); +} + +bool DCapDir::exists() const +{ + if (!DCapFilePrivate::canReadWrite(dd_ptr->filePath)) + return false; + + return QDir::exists(); +} + +bool DCapDir::exists(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("DCapFile::exists: Empty or null file name"); + return false; + } + return DCapFile::exists(filePath(name)); +} + +bool DCapDir::remove(const QString &fileName) +{ + if (fileName.isEmpty()) { + qWarning("DCapDir::remove: Empty or null file name"); + return false; + } + return DCapFile::remove(filePath(fileName)); +} + +bool DCapDir::rename(const QString &oldName, const QString &newName) +{ + if (oldName.isEmpty() || newName.isEmpty()) { + qWarning("DCapDir::rename: Empty or null file name(s)"); + return false; + } + + DCapFile file(filePath(oldName)); + if (!file.exists()) + return false; + return file.rename(filePath(newName)); +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dcapfsfileengine.cpp b/src/filesystem/dcapfsfileengine.cpp new file mode 100644 index 0000000..09df413 --- /dev/null +++ b/src/filesystem/dcapfsfileengine.cpp @@ -0,0 +1,209 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "private/dcapfsfileengine_p.h" +#include "private/dobject_p.h" +#include "dvtablehook.h" + +#include "dcapmanager.h" +#include + +DCORE_BEGIN_NAMESPACE + +extern QString _d_cleanPath(const QString &path); +extern bool _d_isSubFileOf(const QString &filePath, const QString &directoryPath); + +static bool capDirIteraterHasNext(QAbstractFileEngineIterator *it) +{ + const QStringList &paths = DCapManager::instance()->paths(); + QString path = it->path(); + QFileInfo info(path); + if (info.isSymLink()) + info = info.symLinkTarget(); + + bool ret = std::any_of(paths.cbegin(), paths.cend(), std::bind(_d_isSubFileOf, path, std::placeholders::_1)); + + if (!ret) + return ret; + return DVtableHook::callOriginalFun(it, &QAbstractFileEngineIterator::hasNext); +} + +QAbstractFileEngine *DCapFSFileEngineHandler::create(const QString &fileName) const +{ + return new DCapFSFileEngine(fileName); +} + + +class DCapFSFileEnginePrivate : public DObjectPrivate +{ + D_DECLARE_PUBLIC(DCapFSFileEngine) +public: + DCapFSFileEnginePrivate(const QString &file, DCapFSFileEngine *qq); + + bool canReadWrite(const QString &path) const; + + QString file; +}; + +DCapFSFileEnginePrivate::DCapFSFileEnginePrivate(const QString &file, DCapFSFileEngine *qq) + : DObjectPrivate(qq) + , file(file) +{ + +} + +bool DCapFSFileEnginePrivate::canReadWrite(const QString &path) const +{ + if (path.isEmpty()) + return false; + + QString target = path; + if (path == this->file) { + D_QC(DCapFSFileEngine); + target = q->fileName(DCapFSFileEngine::AbsoluteName); + } else { + QFSFileEngine engine(path); + target = engine.fileName(DCapFSFileEngine::AbsoluteName); + } + + auto paths = DCapManager::instance()->paths(); + return std::any_of(paths.cbegin(), paths.cend(), + std::bind(_d_isSubFileOf, target, std::placeholders::_1)); +} + +DCapFSFileEngine::DCapFSFileEngine(const QString &file) + : QFSFileEngine(file) + , DObject(*new DCapFSFileEnginePrivate(file, this)) +{ + +} + +DCapFSFileEngine::~DCapFSFileEngine() +{ +} + +bool DCapFSFileEngine::open(QIODevice::OpenMode openMode) +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(d->file)) + return false; + return QFSFileEngine::open(openMode); +} + +bool DCapFSFileEngine::remove() +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(d->file)) + return false; + return QFSFileEngine::remove(); +} + +bool DCapFSFileEngine::copy(const QString &newName) +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(newName)) { + // ###(Chen Bin): If false is returned here, QFile + // will use the interface of qtemporaryfile for + // file operation, and the restrictions in + // DCapFSFileEngine cannot be used. And it will be + // copied successfully. + qWarning() << "DCapFSFileEngine: " << QStringLiteral("The file [%1] has no permission to copy!").arg(newName); + return true; + } + return QFSFileEngine::copy(newName); +} + +bool DCapFSFileEngine::rename(const QString &newName) +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(newName)) + return false; + return QFSFileEngine::rename(newName); +} + +bool DCapFSFileEngine::renameOverwrite(const QString &newName) +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(newName)) + return false; + return QFSFileEngine::renameOverwrite(newName); +} + +bool DCapFSFileEngine::link(const QString &newName) +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(newName)) + return false; + return QFSFileEngine::link(newName); +} + +bool DCapFSFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const +{ + D_DC(DCapFSFileEngine); + if (!d->canReadWrite(dirName)) + return false; + return QFSFileEngine::mkdir(dirName, createParentDirectories); +} + +bool DCapFSFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const +{ + D_DC(DCapFSFileEngine); + if (!d->canReadWrite(dirName)) + return false; + return QFSFileEngine::rmdir(dirName, recurseParentDirectories); +} + +QAbstractFileEngine::FileFlags DCapFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const +{ + D_DC(DCapFSFileEngine); + FileFlags ret = QFSFileEngine::fileFlags(type); + if (ret | ExistsFlag) { + if (!d->canReadWrite(d->file)) { + ret &= ~ExistsFlag; + } + } + + return ret; +} + +bool DCapFSFileEngine::cloneTo(QAbstractFileEngine *target) +{ + D_DC(DCapFSFileEngine); + const QString targetPath = target->fileName(DCapFSFileEngine::AbsolutePathName); + if (!d->canReadWrite(targetPath)) + return false; + return QFSFileEngine::cloneTo(target); +} + +bool DCapFSFileEngine::setSize(qint64 size) +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(d->file)) + return false; + return QFSFileEngine::setSize(size); +} + +QStringList DCapFSFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const +{ + D_DC(DCapFSFileEngine); + if (!d->canReadWrite(d->file)) + return {}; + return QFSFileEngine::entryList(filters, filterNames); +} + +QAbstractFileEngine::Iterator *DCapFSFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) +{ + auto ret = QFSFileEngine::beginEntryList(filters, filterNames); + DVtableHook::overrideVfptrFun(ret, &QAbstractFileEngineIterator::hasNext, &capDirIteraterHasNext); + return ret; +} + +bool DCapFSFileEngine::canReadWrite(const QString &path) const +{ + D_DC(DCapFSFileEngine); + return d->canReadWrite(path); +} + +DCORE_END_NAMESPACE + diff --git a/src/filesystem/dcapmanager.cpp b/src/filesystem/dcapmanager.cpp new file mode 100644 index 0000000..5a4729f --- /dev/null +++ b/src/filesystem/dcapmanager.cpp @@ -0,0 +1,152 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dcapmanager.h" +#include "dobject_p.h" +#include "dstandardpaths.h" +#include "private/dcapfsfileengine_p.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +QString _d_cleanPath(const QString &path) { + return path.size() < 2 || !path.endsWith(QDir::separator()) ? path : path.chopped(1); +} + +bool _d_isSubFileOf(const QString &filePath, const QString &directoryPath) +{ + QString path = _d_cleanPath(filePath); + bool ret = path.startsWith(directoryPath); + return ret; +} + +static QStringList defaultWriteablePaths() { + QStringList paths; + int list[] = { + QStandardPaths::AppConfigLocation, QStandardPaths::AppDataLocation, + QStandardPaths::CacheLocation, QStandardPaths::TempLocation, + QStandardPaths::DataLocation, QStandardPaths::GenericConfigLocation, + QStandardPaths::HomeLocation, QStandardPaths::MusicLocation, + QStandardPaths::DocumentsLocation, QStandardPaths::MoviesLocation, + QStandardPaths::PicturesLocation, QStandardPaths::DownloadLocation + }; + + for (uint i = 0; i < sizeof (list) / sizeof (int); ++i) { + const QString &path = QStandardPaths::writableLocation(QStandardPaths::StandardLocation(list[i])); + if (path.isEmpty()) + continue; + + paths.append(path); + } + + for (int i = 0; i <= static_cast(DStandardPaths::XDG::RuntimeTime); ++i) { + const QString &path = DStandardPaths::path(DStandardPaths::XDG(i)); + if (path.isEmpty()) + continue; + + paths.append(path); + } + + for (int i = 0; i <= static_cast(DStandardPaths::DSG::DataDir); ++i) { + const QStringList &pathList = DStandardPaths::paths(DStandardPaths::DSG(i)); + if (pathList.isEmpty()) + continue; + + for (auto path : pathList) { + if (path.isEmpty() || paths.contains(path)) + continue; + + paths.append(path); + } + } + return paths; +} + +static DCapFSFileEngineHandler *globalHandler = nullptr; + +class DCapManagerPrivate : public DObjectPrivate +{ + D_DECLARE_PUBLIC(DCapManager) +public: + DCapManagerPrivate(DCapManager *qq); + + QStringList pathList; +}; + +class DCapManager_ : public DCapManager {}; +Q_GLOBAL_STATIC(DCapManager_, capManager) + +DCapManagerPrivate::DCapManagerPrivate(DCapManager *qq) + : DObjectPrivate(qq) +{ + pathList = defaultWriteablePaths(); +} + +DCapManager::DCapManager() + : DObject(*new DCapManagerPrivate(this)) +{ + +} + +DCapManager *DCapManager::instance() +{ + return capManager; +} + +void DCapManager::registerFileEngine() +{ + if (globalHandler) + return; + globalHandler = new DCapFSFileEngineHandler; +} + +void DCapManager::unregisterFileEngine() +{ + if (!globalHandler) + return; + delete globalHandler; + globalHandler = nullptr; +} + +void DCapManager::appendPath(const QString &path) +{ + D_D(DCapManager); + const QString &targetPath = _d_cleanPath(path); + bool exist = std::any_of(d->pathList.cbegin(), d->pathList.cend(), + std::bind(_d_isSubFileOf, targetPath, std::placeholders::_1)); + if (exist) + return; + d->pathList.append(targetPath); +} + +void DCapManager::appendPaths(const QStringList &pathList) +{ + for (auto path : pathList) + appendPath(path); +} + +void DCapManager::removePath(const QString &path) +{ + D_D(DCapManager); + const QString &targetPath = _d_cleanPath(path); + if (!d->pathList.contains(targetPath)) + return; + d->pathList.removeOne(targetPath); +} + +void DCapManager::removePaths(const QStringList &paths) +{ + for (auto path : paths) + removePath(path); +} + +QStringList DCapManager::paths() const +{ + D_DC(DCapManager); + return d->pathList; +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dfilesystemwatcher_dummy.cpp b/src/filesystem/dfilesystemwatcher_dummy.cpp new file mode 100644 index 0000000..bb3f5f1 --- /dev/null +++ b/src/filesystem/dfilesystemwatcher_dummy.cpp @@ -0,0 +1,229 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dfilesystemwatcher.h" +#include "private/dfilesystemwatcher_dummy_p.h" + +DCORE_BEGIN_NAMESPACE + +DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq) + : DObjectPrivate(qq) +{ + +} + +DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate() +{ + +} + +/*! + \class Dtk::Core::DFileSystemWatcher + \inmodule dtkcore + \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications. + + DFileSystemWatcher monitors the file system for changes to files + and directories by watching a list of specified paths. + + Call addPath() to watch a particular file or directory. Multiple + paths can be added using the addPaths() function. Existing paths can + be removed by using the removePath() and removePaths() functions. + + DFileSystemWatcher examines each path added to it. Files that have + been added to the DFileSystemWatcher can be accessed using the + files() function, and directories using the directories() function. + + The fileChanged() signal is emitted when a file has been modified, + renamed or removed from disk. Similarly, the directoryChanged() + signal is emitted when a directory or its contents is modified or + removed. Note that DFileSystemWatcher stops monitoring files once + they have been renamed or removed from disk, and directories once + they have been removed from disk. + + \note On systems running a Linux kernel without inotify support, + file systems that contain watched paths cannot be unmounted. + + \note Windows CE does not support directory monitoring by + default as this depends on the file system driver installed. + + \note The act of monitoring files and directories for + modifications consumes system resources. This implies there is a + limit to the number of files and directories your process can + monitor simultaneously. On all BSD variants, for + example, an open file descriptor is required for each monitored + file. Some system limits the number of open file descriptors to 256 + by default. This means that addPath() and addPaths() will fail if + your process tries to add more than 256 files or directories to + the file system monitor. Also note that your process may have + other file descriptors open in addition to the ones for files + being monitored, and these other open descriptors also count in + the total. OS X uses a different backend and does not + suffer from this issue. +*/ + + +/*! + Constructs a new file system watcher object with the given \a parent. +*/ +DFileSystemWatcher::DFileSystemWatcher(QObject *parent) + : QObject(parent) + , DObject() +{ + +} + +/*! + Constructs a new file system watcher object with the given \a parent + which monitors the specified \a paths list. +*/ +DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent) + : DFileSystemWatcher(parent) +{ + addPaths(paths); +} + +/*! + Destroys the file system watcher. +*/ +DFileSystemWatcher::~DFileSystemWatcher() +{ } + +/*! + Adds \a path to the file system watcher if \a path exists. The + path is not added if it does not exist, or if it is already being + monitored by the file system watcher. + + If \a path specifies a directory, the directoryChanged() signal + will be emitted when \a path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when \a path is + modified, renamed or removed. + + If the watch was successful, true is returned. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit is been reached, \a path will not be monitored, + and false is returned. + + \sa addPaths(), removePath() +*/ +bool DFileSystemWatcher::addPath(const QString &path) +{ + return false; +} + +/*! + Adds each path in \a paths to the file system watcher. Paths are + not added if they not exist, or if they are already being + monitored by the file system watcher. + + If a path specifies a directory, the directoryChanged() signal + will be emitted when the path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when the path is + modified, renamed, or removed. + + The return value is a list of paths that could not be watched. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit has been reached, the excess \a paths will not + be monitored, and they will be added to the returned QStringList. + + \sa addPath(), removePaths() +*/ +QStringList DFileSystemWatcher::addPaths(const QStringList &paths) +{ + return QStringList(); +} + +/*! + Removes the specified \a path from the file system watcher. + + If the watch is successfully removed, true is returned. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePaths(), addPath() +*/ +bool DFileSystemWatcher::removePath(const QString &path) +{ + return false; +} + +/*! + Removes the specified \a paths from the file system watcher. + + The return value is a list of paths which were not able to be + unwatched successfully. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePath(), addPaths() +*/ +QStringList DFileSystemWatcher::removePaths(const QStringList &paths) +{ + return QStringList(); +} + +/*! + \fn void DFileSystemWatcher::fileChanged(const QString &path) + + This signal is emitted when the file at the specified \a path is + modified, renamed or removed from disk. + + \sa directoryChanged() +*/ + +/*! + \fn void DFileSystemWatcher::directoryChanged(const QString &path) + + This signal is emitted when the directory at a specified \a path + is modified (e.g., when a file is added or deleted) or removed + from disk. Note that if there are several changes during a short + period of time, some of the changes might not Q_EMIT this signal. + However, the last change in the sequence of changes will always + generate this signal. + + \sa fileChanged() +*/ + +/*! + \fn QStringList DFileSystemWatcher::directories() const + + Returns a list of paths to directories that are being watched. + + \sa files() +*/ + +/*! + \fn QStringList DFileSystemWatcher::files() const + + Returns a list of paths to files that are being watched. + + \sa directories() +*/ + +QStringList DFileSystemWatcher::directories() const +{ + return QStringList(); +} + +QStringList DFileSystemWatcher::files() const +{ + return QStringList(); +} + +DCORE_END_NAMESPACE + +#include "moc_dfilesystemwatcher.cpp" diff --git a/src/filesystem/dfilesystemwatcher_linux.cpp b/src/filesystem/dfilesystemwatcher_linux.cpp new file mode 100644 index 0000000..32ce28c --- /dev/null +++ b/src/filesystem/dfilesystemwatcher_linux.cpp @@ -0,0 +1,624 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dfilesystemwatcher.h" +#include "private/dfilesystemwatcher_linux_p.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq) + : DObjectPrivate(qq) + , inotifyFd(fd) + , notifier(fd, QSocketNotifier::Read, qq) +{ + fcntl(inotifyFd, F_SETFD, FD_CLOEXEC); + qq->connect(¬ifier, SIGNAL(activated(int)), qq, SLOT(_q_readFromInotify())); +} + +DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate() +{ + notifier.setEnabled(false); + Q_FOREACH (int id, pathToID) + inotify_rm_watch(inotifyFd, id < 0 ? -id : id); + + ::close(inotifyFd); +} + +QStringList DFileSystemWatcherPrivate::addPaths(const QStringList &paths, QStringList *files, QStringList *directories) +{ + QStringList p = paths; + QMutableListIterator it(p); + while (it.hasNext()) { + QString path = it.next(); + QFileInfo fi(path); + bool isDir = fi.isDir(); + if (isDir) { + if (directories->contains(path)) + continue; + } else { + if (files->contains(path)) + continue; + } + + int wd = inotify_add_watch(inotifyFd, + QFile::encodeName(path), + (isDir + ? (0 + | IN_ATTRIB + | IN_MOVE + | IN_MOVE_SELF + | IN_CREATE + | IN_DELETE + | IN_DELETE_SELF + | IN_MODIFY + ) + : (0 + | IN_ATTRIB + | IN_CLOSE_WRITE + | IN_MODIFY + | IN_MOVE + | IN_MOVE_SELF + | IN_DELETE_SELF + ))); + if (wd < 0) { + perror("DFileSystemWatcherPrivate::addPaths: inotify_add_watch failed"); + continue; + } + + it.remove(); + + int id = isDir ? -wd : wd; + if (id < 0) { + directories->append(path); + } else { + files->append(path); + } + + pathToID.insert(path, id); + idToPath.insert(id, path); + } + + return p; +} + +QStringList DFileSystemWatcherPrivate::removePaths(const QStringList &paths, QStringList *files, QStringList *directories) +{ + QStringList p = paths; + QMutableListIterator it(p); + while (it.hasNext()) { + QString path = it.next(); + int id = pathToID.take(path); + for (auto hit = idToPath.find(id); hit != idToPath.end() && hit.key() == id; ++hit) { + if (hit.value() == path) { + idToPath.erase(hit); + break; + } + } + + it.remove(); + + if (!idToPath.contains(id)) { + int wd = id < 0 ? -id : id; + //qDebug() << "removing watch for path" << path << "wd" << wd; + inotify_rm_watch(inotifyFd, wd); + } + + if (id < 0) { + directories->removeAll(path); + } else { + files->removeAll(path); + } + } + + return p; +} + +void DFileSystemWatcherPrivate::_q_readFromInotify() +{ + Q_Q(DFileSystemWatcher); +// qDebug() << "QInotifyFileSystemWatcherEngine::readFromInotify"; + + int buffSize = 0; + ioctl(inotifyFd, FIONREAD, (char *) &buffSize); + QVarLengthArray buffer(buffSize); + buffSize = read(inotifyFd, buffer.data(), buffSize); + char *at = buffer.data(); + char * const end = at + buffSize; + + QList eventList; + QMultiHash batch_pathmap; + /// only save event: IN_MOVE_TO + QMultiMap cookieToFilePath; + QMultiMap cookieToFileName; + QSet hasMoveFromByCookie; +#ifdef QT_DEBUG + int exist_count = 0; +#endif + while (at < end) { + inotify_event *event = reinterpret_cast(at); + QStringList paths; + + at += sizeof(inotify_event) + event->len; + + int id = event->wd; + paths = idToPath.values(id); + if (paths.empty()) { + // perhaps a directory? + id = -id; + paths = idToPath.values(id); + if (paths.empty()) + continue; + } + + if (!(event->mask & IN_MOVED_TO) || !hasMoveFromByCookie.contains(event->cookie)) { + auto it = std::find_if(eventList.begin(), eventList.end(), [event](inotify_event *e){ + return event->wd == e->wd && event->mask == e->mask && + event->cookie == e->cookie && + event->len == e->len && + !strcmp(event->name, e->name); + }); + + if (it==eventList.end()) { + eventList.append(event); + } +#ifdef QT_DEBUG + else { + qDebug() << "exist event:" << "event->wd" << event->wd << + "event->mask" << event->mask << + "event->cookie" << event->cookie << "exist counts " << ++exist_count; + } +#endif + const QList bps = batch_pathmap.values(id); + for (auto &path : paths) { + if (!bps.contains(path)) { + batch_pathmap.insert(id, path); + } + } + } + + if (event->mask & IN_MOVED_TO) { + for (auto &path : paths) { + cookieToFilePath.insert(event->cookie, path); + } + cookieToFileName.insert(event->cookie, QString::fromUtf8(event->name)); + } + + if (event->mask & IN_MOVED_FROM) + hasMoveFromByCookie << event->cookie; + } + +// qDebug() << "event count:" << eventList.count(); + + QList::const_iterator it = eventList.constBegin(); + while (it != eventList.constEnd()) { + const inotify_event &event = **it; + ++it; + +// qDebug() << "inotify event, wd" << event.wd << "cookie" << event.cookie << "mask" << hex << event.mask; + + int id = event.wd; + QStringList paths = batch_pathmap.values(id); + + if (paths.empty()) { + id = -id; + paths = batch_pathmap.values(id); + + if (paths.empty()) + continue; + } + const QString &name = QString::fromUtf8(event.name); + + for (auto &path : paths) { +// qDebug() << "event for path" << path; + +// /// TODO: Existence of invalid utf8 characters QFile can not read the file information +// if (event.name != QString::fromLocal8Bit(event.name).toLocal8Bit()) { +// if (event.mask & (IN_CREATE | IN_MOVED_TO)) { +// DFMGlobal::fileNameCorrection(path); +// } +// } + + if ((event.mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)) != 0) { + do { + if (event.mask & IN_MOVE_SELF) { + QMap::const_iterator iterator = cookieToFilePath.constBegin(); + + bool isMove = false; + + while (iterator != cookieToFilePath.constEnd()) { + const QString &_path = iterator.value(); + const QString &_name = cookieToFileName.value(iterator.key()); + + if (QFileInfo(_path + QDir::separator() + _name) == QFileInfo(path)) { + isMove = true; + break; + } + + ++iterator; + } + + if (isMove) + break; + } + + /// Keep watcher +// pathToID.remove(path); +// idToPath.remove(id, getPathFromID(id)); +// if (!idToPath.contains(id)) +// inotify_rm_watch(inotifyFd, event.wd); + +// if (id < 0) +// onDirectoryChanged(path, true); +// else +// onFileChanged(path, true); + + Q_EMIT q->fileDeleted(path, QString(), DFileSystemWatcher::QPrivateSignal()); + } while (false); + } else { + if (id < 0) + onDirectoryChanged(path, false); + else + onFileChanged(path, false); + } + + QString filePath = path; + + if (id < 0) { + if (path.endsWith(QDir::separator())) + filePath = path + name; + else + filePath = path + QDir::separator() + name; + } + + if (event.mask & IN_CREATE) { +// qDebug() << "IN_CREATE" << filePath << name; + + if (name.isEmpty()) { + if (pathToID.contains(path)) { + q->removePath(path); + q->addPath(path); + } + } else if (pathToID.contains(filePath)) { + q->removePath(filePath); + q->addPath(filePath); + } + + Q_EMIT q->fileCreated(path, name, DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_DELETE) { +// qDebug() << "IN_DELETE" << filePath; + + Q_EMIT q->fileDeleted(path, name, DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_MOVED_FROM) { + const QString toName = cookieToFileName.value(event.cookie); + + if (cookieToFilePath.values(event.cookie).empty()) { + Q_EMIT q->fileMoved(path, name, QString(), QString(), DFileSystemWatcher::QPrivateSignal()); + } else { + for (QString &toPath : cookieToFilePath.values(event.cookie)) { +// qDebug() << "IN_MOVED_FROM" << filePath << "to path:" << toPath << "to name:" << toName; + + Q_EMIT q->fileMoved(path, name, toPath, toName, DFileSystemWatcher::QPrivateSignal()); + } + } + } + + if (event.mask & IN_MOVED_TO) { +// qDebug() << "IN_MOVED_TO" << filePath; + + if (!hasMoveFromByCookie.contains(event.cookie)) + Q_EMIT q->fileMoved(QString(), QString(), path, name, DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_ATTRIB) { +// qDebug() << "IN_ATTRIB" << event.mask << filePath; + + Q_EMIT q->fileAttributeChanged(path, name, DFileSystemWatcher::QPrivateSignal()); + } + + /*only monitor file close event which is opend by write mode*/ + if (event.mask & IN_CLOSE_WRITE) { +// qDebug() << "IN_CLOSE_WRITE" << event.mask << filePath; + + Q_EMIT q->fileClosed(path, id < 0 ? name : QString(), DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_MODIFY) { +// qDebug() << "IN_MODIFY" << event.mask << filePath << name; + + Q_EMIT q->fileModified(path, name, DFileSystemWatcher::QPrivateSignal()); + } + } + } +} + +void DFileSystemWatcherPrivate::onFileChanged(const QString &path, bool removed) +{ + Q_Q(DFileSystemWatcher); + if (!files.contains(path)) { + // the path was removed after a change was detected, but before we delivered the signal + return; + } + if (removed) { + files.removeAll(path); + } +// Q_EMIT q->fileChanged(path, DFileSystemWatcher::QPrivateSignal()); +} + +void DFileSystemWatcherPrivate::onDirectoryChanged(const QString &path, bool removed) +{ + Q_Q(DFileSystemWatcher); + if (!directories.contains(path)) { + // perhaps the path was removed after a change was detected, but before we delivered the signal + return; + } + if (removed) { + directories.removeAll(path); + } +// Q_EMIT q->directoryChanged(path, DFileSystemWatcher::QPrivateSignal()); +} + +/*! + \class Dtk::Core::DFileSystemWatcher + \inmodule dtkcore + \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications. + + DFileSystemWatcher monitors the file system for changes to files + and directories by watching a list of specified paths. + + Call addPath() to watch a particular file or directory. Multiple + paths can be added using the addPaths() function. Existing paths can + be removed by using the removePath() and removePaths() functions. + + DFileSystemWatcher examines each path added to it. Files that have + been added to the DFileSystemWatcher can be accessed using the + files() function, and directories using the directories() function. + + \note On systems running a Linux kernel without inotify support, + file systems that contain watched paths cannot be unmounted. + + \note Windows CE does not support directory monitoring by + default as this depends on the file system driver installed. + + \note The act of monitoring files and directories for + modifications consumes system resources. This implies there is a + limit to the number of files and directories your process can + monitor simultaneously. On all BSD variants, for + example, an open file descriptor is required for each monitored + file. Some system limits the number of open file descriptors to 256 + by default. This means that addPath() and addPaths() will fail if + your process tries to add more than 256 files or directories to + the file system monitor. Also note that your process may have + other file descriptors open in addition to the ones for files + being monitored, and these other open descriptors also count in + the total. OS X uses a different backend and does not + suffer from this issue. +*/ + + +/*! + Constructs a new file system watcher object with the given \a parent. +*/ +DFileSystemWatcher::DFileSystemWatcher(QObject *parent) + : QObject(parent) + , DObject() +{ + int fd = -1; +#ifdef IN_CLOEXEC + fd = inotify_init1(IN_CLOEXEC | O_NONBLOCK); +#endif + if (fd == -1) { + fd = inotify_init1(O_NONBLOCK); + } + + if (fd != -1) { + d_d_ptr.reset(new DFileSystemWatcherPrivate(fd, this)); + } else { + qCritical() << "inotify_init1 failed, and the DFileSystemWatcher is invalid." << strerror(errno); + } +} + +/*! + Constructs a new file system watcher object with the given \a parent + which monitors the specified \a paths list. +*/ +DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent) + : DFileSystemWatcher(parent) +{ + addPaths(paths); +} + +/*! + Destroys the file system watcher. +*/ +DFileSystemWatcher::~DFileSystemWatcher() +{ } + +/*! + Adds \a path to the file system watcher if \a path exists. The + path is not added if it does not exist, or if it is already being + monitored by the file system watcher. + + If \a path specifies a directory, the directoryChanged() signal + will be emitted when \a path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when \a path is + modified, renamed or removed. + + If the watch was successful, true is returned. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit is been reached, \a path will not be monitored, + and false is returned. + + \sa addPaths(), removePath() +*/ +bool DFileSystemWatcher::addPath(const QString &path) +{ + const QStringList &paths = addPaths(QStringList(path)); + return paths.isEmpty(); +} + +/*! + Adds each path in \a paths to the file system watcher. Paths are + not added if they not exist, or if they are already being + monitored by the file system watcher. + + If a path specifies a directory, the directoryChanged() signal + will be emitted when the path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when the path is + modified, renamed, or removed. + + The return value is a list of paths that could not be watched. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit has been reached, the excess \a paths will not + be monitored, and they will be added to the returned QStringList. + + \sa addPath(), removePaths() +*/ +QStringList DFileSystemWatcher::addPaths(const QStringList &paths) +{ + Q_D(DFileSystemWatcher); + + if (!d) + return paths; + + QStringList p = paths; + QMutableListIterator it(p); + + while (it.hasNext()) { + const QString &path = it.next(); + if (path.isEmpty()) { + qWarning() << Q_FUNC_INFO << "the path is empty and it is not be watched"; + it.remove(); + } + } + + if (p.isEmpty()) { + qWarning() << Q_FUNC_INFO << "all path are filtered and they are not be watched, paths are " << paths; + return paths; + } + + p = d->addPaths(p, &d->files, &d->directories); + + return p; +} + +/*! + Removes the specified \a path from the file system watcher. + + If the watch is successfully removed, true is returned. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePaths(), addPath() +*/ +bool DFileSystemWatcher::removePath(const QString &path) +{ + const QStringList &paths = removePaths(QStringList(path)); + return paths.isEmpty(); +} + +/*! + Removes the specified \a paths from the file system watcher. + + The return value is a list of paths which were not able to be + unwatched successfully. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePath(), addPaths() +*/ +QStringList DFileSystemWatcher::removePaths(const QStringList &paths) +{ + Q_D(DFileSystemWatcher); + + if (!d) + return paths; + + QStringList p = paths; + QMutableListIterator it(p); + + while (it.hasNext()) { + const QString &path = it.next(); + if (path.isEmpty()) { + qWarning() << Q_FUNC_INFO << "the path is empty and it is not be removed from watched list"; + it.remove(); + } + } + + if (p.isEmpty()) { + qWarning() << Q_FUNC_INFO << "all path are filtered and they are not be watched, paths are " << paths; + return paths; + } + + p = d->removePaths(p, &d->files, &d->directories); + + return p; +} + +/*! + \fn QStringList DFileSystemWatcher::directories() const + + Returns a list of paths to directories that are being watched. + + \sa files() +*/ + +/*! + \fn QStringList DFileSystemWatcher::files() const + + Returns a list of paths to files that are being watched. + + \sa directories() +*/ + +QStringList DFileSystemWatcher::directories() const +{ + Q_D(const DFileSystemWatcher); + + if (!d) + return QStringList(); + + return d->directories; +} + +QStringList DFileSystemWatcher::files() const +{ + Q_D(const DFileSystemWatcher); + + if (!d) + return QStringList(); + + return d->files; +} + +DCORE_END_NAMESPACE + +#include "moc_dfilesystemwatcher.cpp" diff --git a/src/filesystem/dfilesystemwatcher_win.cpp b/src/filesystem/dfilesystemwatcher_win.cpp new file mode 100644 index 0000000..09af4e4 --- /dev/null +++ b/src/filesystem/dfilesystemwatcher_win.cpp @@ -0,0 +1,232 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dfilesystemwatcher.h" +#include "private/dfilesystemwatcher_win_p.h" + +DCORE_BEGIN_NAMESPACE + +DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq) + : DObjectPrivate(qq) +{ + +} + +DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate() +{ + +} + +/*! + \class Dtk::Core::DFileSystemWatcher + \inmodule dtkcore + \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications. + + DFileSystemWatcher monitors the file system for changes to files + and directories by watching a list of specified paths. + + Call addPath() to watch a particular file or directory. Multiple + paths can be added using the addPaths() function. Existing paths can + be removed by using the removePath() and removePaths() functions. + + DFileSystemWatcher examines each path added to it. Files that have + been added to the DFileSystemWatcher can be accessed using the + files() function, and directories using the directories() function. + + The fileChanged() signal is emitted when a file has been modified, + renamed or removed from disk. Similarly, the directoryChanged() + signal is emitted when a directory or its contents is modified or + removed. Note that DFileSystemWatcher stops monitoring files once + they have been renamed or removed from disk, and directories once + they have been removed from disk. + + \note On systems running a Linux kernel without inotify support, + file systems that contain watched paths cannot be unmounted. + + \note Windows CE does not support directory monitoring by + default as this depends on the file system driver installed. + + \note The act of monitoring files and directories for + modifications consumes system resources. This implies there is a + limit to the number of files and directories your process can + monitor simultaneously. On all BSD variants, for + example, an open file descriptor is required for each monitored + file. Some system limits the number of open file descriptors to 256 + by default. This means that addPath() and addPaths() will fail if + your process tries to add more than 256 files or directories to + the file system monitor. Also note that your process may have + other file descriptors open in addition to the ones for files + being monitored, and these other open descriptors also count in + the total. OS X uses a different backend and does not + suffer from this issue. + + + \sa QFile, QDir +*/ + + +/*! + Constructs a new file system watcher object with the given \a parent. +*/ +DFileSystemWatcher::DFileSystemWatcher(QObject *parent) + : QObject(parent) + , DObject() +{ + +} + +/*! + Constructs a new file system watcher object with the given \a parent + which monitors the specified \a paths list. +*/ +DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent) + : DFileSystemWatcher(parent) +{ + addPaths(paths); +} + +/*! + Destroys the file system watcher. +*/ +DFileSystemWatcher::~DFileSystemWatcher() +{ } + +/*! + Adds \a path to the file system watcher if \a path exists. The + path is not added if it does not exist, or if it is already being + monitored by the file system watcher. + + If \a path specifies a directory, the directoryChanged() signal + will be emitted when \a path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when \a path is + modified, renamed or removed. + + If the watch was successful, true is returned. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit is been reached, \a path will not be monitored, + and false is returned. + + \sa addPaths(), removePath() +*/ +bool DFileSystemWatcher::addPath(const QString &path) +{ + return false; +} + +/*! + Adds each path in \a paths to the file system watcher. Paths are + not added if they not exist, or if they are already being + monitored by the file system watcher. + + If a path specifies a directory, the directoryChanged() signal + will be emitted when the path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when the path is + modified, renamed, or removed. + + The return value is a list of paths that could not be watched. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit has been reached, the excess \a paths will not + be monitored, and they will be added to the returned QStringList. + + \sa addPath(), removePaths() +*/ +QStringList DFileSystemWatcher::addPaths(const QStringList &paths) +{ + return QStringList(); +} + +/*! + Removes the specified \a path from the file system watcher. + + If the watch is successfully removed, true is returned. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePaths(), addPath() +*/ +bool DFileSystemWatcher::removePath(const QString &path) +{ + return false; +} + +/*! + Removes the specified \a paths from the file system watcher. + + The return value is a list of paths which were not able to be + unwatched successfully. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePath(), addPaths() +*/ +QStringList DFileSystemWatcher::removePaths(const QStringList &paths) +{ + return QStringList(); +} + +/*! + \fn void DFileSystemWatcher::fileChanged(const QString &path) + + This signal is emitted when the file at the specified \a path is + modified, renamed or removed from disk. + + \sa directoryChanged() +*/ + +/*! + \fn void DFileSystemWatcher::directoryChanged(const QString &path) + + This signal is emitted when the directory at a specified \a path + is modified (e.g., when a file is added or deleted) or removed + from disk. Note that if there are several changes during a short + period of time, some of the changes might not Q_EMIT this signal. + However, the last change in the sequence of changes will always + generate this signal. + + \sa fileChanged() +*/ + +/*! + \fn QStringList DFileSystemWatcher::directories() const + + Returns a list of paths to directories that are being watched. + + \sa files() +*/ + +/*! + \fn QStringList DFileSystemWatcher::files() const + + Returns a list of paths to files that are being watched. + + \sa directories() +*/ + +QStringList DFileSystemWatcher::directories() const +{ + return QStringList(); +} + +QStringList DFileSystemWatcher::files() const +{ + return QStringList(); +} + +DCORE_END_NAMESPACE + +#include "moc_dfilesystemwatcher.cpp" diff --git a/src/filesystem/dfilewatcher.cpp b/src/filesystem/dfilewatcher.cpp new file mode 100644 index 0000000..00b5039 --- /dev/null +++ b/src/filesystem/dfilewatcher.cpp @@ -0,0 +1,280 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dfilewatcher.h" +#include "private/dbasefilewatcher_p.h" + +#include "dfilesystemwatcher.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +static QString joinFilePath(const QString &path, const QString &name) +{ + if (path.endsWith(QDir::separator())) + return path + name; + + return path + QDir::separator() + name; +} + +class DFileWatcherPrivate : DBaseFileWatcherPrivate +{ +public: + DFileWatcherPrivate(DFileWatcher *qq) + : DBaseFileWatcherPrivate(qq) {} + + bool start() Q_DECL_OVERRIDE; + bool stop() Q_DECL_OVERRIDE; + + void _q_handleFileDeleted(const QString &path, const QString &parentPath); + void _q_handleFileAttributeChanged(const QString &path, const QString &parentPath); + void _q_handleFileMoved(const QString &from, const QString &fromParent, const QString &to, const QString &toParent); + void _q_handleFileCreated(const QString &path, const QString &parentPath); + void _q_handleFileModified(const QString &path, const QString &parentPath); + void _q_handleFileClose(const QString &path, const QString &parentPath); + + static QString formatPath(const QString &path); + + QString path; + QStringList watchFileList; + + static QMap filePathToWatcherCount; + + Q_DECLARE_PUBLIC(DFileWatcher) +}; + +QMap DFileWatcherPrivate::filePathToWatcherCount; +Q_GLOBAL_STATIC(DFileSystemWatcher, watcher_file_private) + +QStringList parentPathList(const QString &path) +{ + QStringList list; + QDir dir(path); + + list << path; + + while (dir.cdUp()) { + list << dir.absolutePath(); + } + + return list; +} + +bool DFileWatcherPrivate::start() +{ + Q_Q(DFileWatcher); + + started = true; + + Q_FOREACH (const QString &path, parentPathList(this->path)) { + if (watchFileList.contains(path)) + continue; + + if (filePathToWatcherCount.value(path, -1) <= 0) { + if (!watcher_file_private->addPath(path)) { + qWarning() << Q_FUNC_INFO << "start watch failed, file path =" << path; + q->stopWatcher(); + started = false; + return false; + } + } + + watchFileList << path; + filePathToWatcherCount[path] = filePathToWatcherCount.value(path, 0) + 1; + } + + q->connect(watcher_file_private, &DFileSystemWatcher::fileDeleted, + q, &DFileWatcher::onFileDeleted); + q->connect(watcher_file_private, &DFileSystemWatcher::fileAttributeChanged, + q, &DFileWatcher::onFileAttributeChanged); + q->connect(watcher_file_private, &DFileSystemWatcher::fileMoved, + q, &DFileWatcher::onFileMoved); + q->connect(watcher_file_private, &DFileSystemWatcher::fileCreated, + q, &DFileWatcher::onFileCreated); + q->connect(watcher_file_private, &DFileSystemWatcher::fileModified, + q, &DFileWatcher::onFileModified); + q->connect(watcher_file_private, &DFileSystemWatcher::fileClosed, + q, &DFileWatcher::onFileClosed); + + return true; +} + +bool DFileWatcherPrivate::stop() +{ + Q_Q(DFileWatcher); + + q->disconnect(watcher_file_private, 0, q, 0); + + bool ok = true; + + Q_FOREACH (const QString &path, watchFileList) { + int count = filePathToWatcherCount.value(path, 0); + + --count; + + if (count <= 0) { + filePathToWatcherCount.remove(path); + watchFileList.removeOne(path); + ok = ok && watcher_file_private->removePath(path); + } else { + filePathToWatcherCount[path] = count; + } + } + + return ok; +} + +void DFileWatcherPrivate::_q_handleFileDeleted(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileDeleted(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileAttributeChanged(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileAttributeChanged(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileMoved(const QString &from, const QString &fromParent, const QString &to, const QString &toParent) +{ + Q_Q(DFileWatcher); + + if ((fromParent == this->path && toParent == this->path) || from == this->path) { + Q_EMIT q->fileMoved(QUrl::fromLocalFile(from), QUrl::fromLocalFile(to)); + } else if (fromParent == this->path) { + Q_EMIT q->fileDeleted(QUrl::fromLocalFile(from)); + } else if (watchFileList.contains(from)) { + Q_EMIT q->fileDeleted(url); + } else if (toParent == this->path) { + Q_EMIT q->subfileCreated(QUrl::fromLocalFile(to)); + } +} + +void DFileWatcherPrivate::_q_handleFileCreated(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->subfileCreated(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileModified(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileModified(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileClose(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileClosed(QUrl::fromLocalFile(path)); +} + +QString DFileWatcherPrivate::formatPath(const QString &path) +{ + QString p = QFileInfo(path).absoluteFilePath(); + + if (p.endsWith(QDir::separator())) + p.chop(1); + + return p.isEmpty() ? path : p; +} + +/*! + \class Dtk::Core::DFileWatcher + \inmodule dtkcore + + \brief The DFileWatcher class provides an implementation of DBaseFileWatcher for monitoring files and directories for modifications. + \brief DFileWatcher 类提供了对 DBaseFileWatcher 接口的实现,可供监视文件和目录的变动。 +*/ + +DFileWatcher::DFileWatcher(const QString &filePath, QObject *parent) + : DBaseFileWatcher(*new DFileWatcherPrivate(this), QUrl::fromLocalFile(filePath), parent) +{ + d_func()->path = DFileWatcherPrivate::formatPath(filePath); +} + +void DFileWatcher::onFileDeleted(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileDeleted(path, QString()); + else + d_func()->_q_handleFileDeleted(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileAttributeChanged(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileAttributeChanged(path, QString()); + else + d_func()->_q_handleFileAttributeChanged(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileMoved(const QString &from, const QString &fname, const QString &to, const QString &tname) +{ + QString fromPath, fpPath; + QString toPath, tpPath; + + if (fname.isEmpty()) { + fromPath = from; + } else { + fromPath = joinFilePath(from, fname); + fpPath = from; + } + + if (tname.isEmpty()) { + toPath = to; + } else { + toPath = joinFilePath(to, tname); + tpPath = to; + } + + d_func()->_q_handleFileMoved(fromPath, fpPath, toPath, tpPath); +} + +void DFileWatcher::onFileCreated(const QString &path, const QString &name) +{ + d_func()->_q_handleFileCreated(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileModified(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileModified(path, QString()); + else + d_func()->_q_handleFileModified(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileClosed(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileClose(path, QString()); + else + d_func()->_q_handleFileClose(joinFilePath(path, name), path); +} + +DCORE_END_NAMESPACE + +#include "moc_dfilewatcher.cpp" diff --git a/src/filesystem/dfilewatchermanager.cpp b/src/filesystem/dfilewatchermanager.cpp new file mode 100644 index 0000000..8ac0530 --- /dev/null +++ b/src/filesystem/dfilewatchermanager.cpp @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dfilewatchermanager.h" +#include "dfilewatcher.h" +#include "base/private/dobject_p.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DFileWatcherManagerPrivate : public DObjectPrivate +{ +public: + DFileWatcherManagerPrivate(DFileWatcherManager *qq); + + QMap watchersMap; + + D_DECLARE_PUBLIC(DFileWatcherManager) +}; + +DFileWatcherManagerPrivate::DFileWatcherManagerPrivate(DFileWatcherManager *qq) + : DObjectPrivate(qq) +{ + +} + +/*! + \class Dtk::Core::DFileWatcherManager + \inmodule dtkcore + \brief The DFileWatcherManager class can help you manage file watchers and get signal when file got changed. + \brief DFileWatcherManager 类可以帮助管理一系列 DFileWatcher 文件监视器,并在文件变动时发送信号通知. +*/ + +DFileWatcherManager::DFileWatcherManager(QObject *parent) + : QObject(parent) + , DObject(*new DFileWatcherManagerPrivate(this)) +{ + +} + +DFileWatcherManager::~DFileWatcherManager() +{ + +} + +/*! + \brief 为路径 \a filePath 创建 DFileWatcher 并将其添加到 DFileWatcherManager 中. + \brief Add file watcher for \a filePath to the file watcher manager. + + \return 被创建并添加到 DFileWatcherManager 的 DFileWatcher + \return The file watcher which got created and added into the file watcher manager. + */ +DFileWatcher *DFileWatcherManager::add(const QString &filePath) +{ + Q_D(DFileWatcherManager); + + DFileWatcher *watcher = d->watchersMap.value(filePath); + + if (watcher) { + return watcher; + } + + watcher = new DFileWatcher(filePath, this); + + connect(watcher, &DFileWatcher::fileAttributeChanged, this, [this](const QUrl & url) { + Q_EMIT fileAttributeChanged(url.toLocalFile()); + }); + connect(watcher, &DFileWatcher::fileClosed, this, [this](const QUrl & url) { + Q_EMIT fileClosed(url.toLocalFile()); + }); + connect(watcher, &DFileWatcher::fileDeleted, this, [this](const QUrl & url) { + Q_EMIT fileDeleted(url.toLocalFile()); + }); + connect(watcher, &DFileWatcher::fileModified, this, [this](const QUrl & url) { + Q_EMIT fileModified(url.toLocalFile()); + }); + connect(watcher, &DFileWatcher::fileMoved, this, [this](const QUrl & fromUrl, const QUrl & toUrl) { + Q_EMIT fileMoved(fromUrl.toLocalFile(), toUrl.toLocalFile()); + }); + connect(watcher, &DFileWatcher::subfileCreated, this, [this](const QUrl & url) { + Q_EMIT subfileCreated(url.toLocalFile()); + }); + + d->watchersMap[filePath] = watcher; + watcher->startWatcher(); + + return watcher; +} + +/*! + \brief 从当前 DFileWatcherManager 中移除监视 \a filePath 的 DFileWatcher. + \brief Remove file watcher for \a filePath from the file watcher manager. + */ +void DFileWatcherManager::remove(const QString &filePath) +{ + Q_D(DFileWatcherManager); + + DFileWatcher *watcher = d->watchersMap.take(filePath); + + if (watcher) { + watcher->deleteLater(); + } +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dpathbuf.cpp b/src/filesystem/dpathbuf.cpp new file mode 100644 index 0000000..b4f6fec --- /dev/null +++ b/src/filesystem/dpathbuf.cpp @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dpathbuf.h" + +/*! + \class Dtk::Core::DPathBuf + \inmodule dtkcore + \brief Dtk::Core::DPathBuf cat path friendly and supoort multiplatform. + \brief Dtk::Core::DPathBuf是一个用于跨平台拼接路径的辅助类. + + 它能够方便的写出链式结构的路径拼接代码。 + \code + DPathBuf logPath(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first()); + logPath = logPath / ".cache" / "deepin" / "deepin-test-dtk" / "deepin-test-dtk.log"; + \endcode + */ + +DCORE_BEGIN_NAMESPACE + +/*! + \fn DPathBuf DPathBuf::operator/(const QString &p) const + \brief join path with operator / + \a p is subpath + \return a new DPathBuf with subpath p + */ + +/*! + \fn DPathBuf &DPathBuf::operator/=(const QString &p) + \brief join path to self with operator /= + \a p is subpath to join + \return self object + */ + +/*! + \fn DPathBuf DPathBuf::operator/(const char *p) const + \brief join path with operator / + \a p is subpath + \return a new DPathBuf with subpath p + \sa Dtk::Core::DPathBuf::operator/(const QString &p) + */ + +/*! + \fn DPathBuf &DPathBuf::operator/=(const char *p) + \brief join path to self with operator /= + \a p is subpath to join + \return self object + \sa operator/=(const QString &p) + */ + +/*! + \fn DPathBuf &DPathBuf::join(const QString &p) + \brief join add subpath p to self + \a p is subpath to join + \return slef object with subpath joined + */ + +/*! + \fn QString DPathBuf::toString() const + \brief toString export native separators format string. + \return string with native separators + */ + +/*! + \brief Create Dtk::Core::DPathBuf from string. + \a path + */ +DPathBuf::DPathBuf(const QString &path) +{ + m_path = QDir(path).absolutePath(); +} + +DPathBuf::DPathBuf() + : DPathBuf(QString()) +{ + +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dstandardpaths.cpp b/src/filesystem/dstandardpaths.cpp new file mode 100644 index 0000000..6348b7c --- /dev/null +++ b/src/filesystem/dstandardpaths.cpp @@ -0,0 +1,216 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dstandardpaths.h" + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DSnapStandardPathsPrivate +{ +public: + inline static QString writableLocation(QStandardPaths::StandardLocation /*type*/) + { + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + return env.value("SNAP_USER_COMMON"); + } + + inline static QStringList standardLocations(QStandardPaths::StandardLocation type) + { + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + + switch (type) { + case QStandardPaths::GenericDataLocation: { + QString snapRoot = env.value("SNAP"); + QString genericDataDir = snapRoot + PREFIX"/share/"; + return QStringList() << genericDataDir; + } + default: + break; + } + + return QStringList() << env.value("SNAP_USER_COMMON"); + } + +private: + DSnapStandardPathsPrivate(); + ~DSnapStandardPathsPrivate(); + Q_DISABLE_COPY(DSnapStandardPathsPrivate) +}; + + +/*! + \class Dtk::Core::DStandardPaths + \inmodule dtkcore + \brief DStandardPaths提供兼容Snap/Dtk标准的路径模式。DStandardPaths实现了Qt的QStandardPaths主要接口. + \sa QStandardPaths + */ + +/*! + \enum Dtk::Core::DStandardPaths::Mode + \brief DStandardPaths支持的路径产生模式。 + \value Auto + \brief 和Qt标准的行为表现一致。 + \value Snap + \brief 读取SNAP相关的环境变量,支持将配置存储在SNAP对应目录。 + \value Test + \brief 和Qt标准的行为表现一致,但是会开启测试模式,参考QStandardPaths::setTestModeEnabled。 + */ + + +static DStandardPaths::Mode s_mode = DStandardPaths::Auto; + +QString DStandardPaths::writableLocation(QStandardPaths::StandardLocation type) +{ + switch (s_mode) { + case Auto: + case Test: + return QStandardPaths::writableLocation(type); + case Snap: + return DSnapStandardPathsPrivate::writableLocation(type); + } + return QStandardPaths::writableLocation(type); +} + +QStringList DStandardPaths::standardLocations(QStandardPaths::StandardLocation type) +{ + switch (s_mode) { + case Auto: + case Test: + return QStandardPaths::standardLocations(type); + case Snap: + return DSnapStandardPathsPrivate::standardLocations(type); + } + return QStandardPaths::standardLocations(type); +} + +QString DStandardPaths::locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options) +{ + return QStandardPaths::locate(type, fileName, options); +} + +QStringList DStandardPaths::locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options) +{ + return QStandardPaths::locateAll(type, fileName, options); +} + +QString DStandardPaths::findExecutable(const QString &executableName, const QStringList &paths) +{ + return QStandardPaths::findExecutable(executableName, paths); +} + +void DStandardPaths::setMode(DStandardPaths::Mode mode) +{ + s_mode = mode; + QStandardPaths::setTestModeEnabled(mode == Test); +} + +// https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/21 + +QString DStandardPaths::homePath() +{ + const QByteArray &home = qgetenv("HOME"); + + if (!home.isEmpty()) + return QString::fromLocal8Bit(home); + + struct passwd *pw = getpwuid(getuid()); + const char *homedir = pw->pw_dir; + return QString::fromLocal8Bit(homedir); +} + +QString DStandardPaths::path(DStandardPaths::XDG type) +{ + switch (type) { + case XDG::DataHome: { + const QByteArray &path = qgetenv("XDG_DATA_HOME"); + if (!path.isEmpty()) + return QString::fromLocal8Bit(path); + return homePath() + QStringLiteral("/.local/share"); + } + case XDG::CacheHome: { + const QByteArray &path = qgetenv("XDG_CACHE_HOME"); + if (!path.isEmpty()) + return QString::fromLocal8Bit(path); + return homePath() + QStringLiteral("/.cache"); + } + case XDG::ConfigHome: { + const QByteArray &path = qgetenv("XDG_CONFIG_HOME"); + if (!path.isEmpty()) + return QString::fromLocal8Bit(path); + return homePath() + QStringLiteral("/.config"); + } + case XDG::RuntimeTime: { + const QByteArray &path = qgetenv("XDG_RUNTIME_DIR"); + if (!path.isEmpty()) + return QString::fromLocal8Bit(path); + return QStringLiteral("/run/user/") + QString::number(getuid()); + } + } + return QString(); +} + +QString DStandardPaths::path(DStandardPaths::DSG type) +{ + const auto list = paths(type); + return list.isEmpty() ? nullptr : list.first(); +} + +QStringList DStandardPaths::paths(DSG type) +{ + QStringList paths; + + if (type == DSG::DataDir) { + const QByteArray &path = qgetenv("DSG_DATA_DIRS"); + if (path.isEmpty()) { + return {QLatin1String(PREFIX"/share/dsg")}; + } + const auto list = path.split(':'); + paths.reserve(list.size()); + for (const auto &i : list) + paths.push_back(QString::fromLocal8Bit(i)); + } else if (type == DSG::AppData) { + const QByteArray &path = qgetenv("DSG_APP_DATA"); + //TODO 应用数据目录规范:`/persistent/appdata/{appid}`, now `appid` is not captured. + paths.push_back(QString::fromLocal8Bit(path)); + } + + return paths; +} + +QString DStandardPaths::filePath(DStandardPaths::XDG type, QString fileName) +{ + const QString &dir = path(type); + + if (dir.isEmpty()) + return QString(); + + return dir + QLatin1Char('/') + fileName; +} + +QString DStandardPaths::filePath(DStandardPaths::DSG type, const QString fileName) +{ + const QString &dir = path(type); + + if (dir.isEmpty()) + return QString(); + + return dir + QLatin1Char('/') + fileName; +} + +QString DStandardPaths::homePath(const uint uid) +{ + struct passwd *pw = getpwuid(uid); + + if (!pw) + return QString(); + + const char *homedir = pw->pw_dir; + return QString::fromLocal8Bit(homedir); +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dtrashmanager_dummy.cpp b/src/filesystem/dtrashmanager_dummy.cpp new file mode 100644 index 0000000..38853ac --- /dev/null +++ b/src/filesystem/dtrashmanager_dummy.cpp @@ -0,0 +1,143 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtrashmanager.h" + +#include "DObjectPrivate" + +#include +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DTrashManager_ : public DTrashManager {}; +Q_GLOBAL_STATIC(DTrashManager_, globalTrashManager) + +static QString getNotExistsFileName(const QString &fileName, const QString &targetPath) +{ + QByteArray name = fileName.toUtf8(); + + int index = name.lastIndexOf('.'); + QByteArray suffix; + + if (index >= 0) + { + suffix = name.mid(index); + } + + if (suffix.size() > 200) + { + suffix = suffix.left(200); + } + + name.chop(suffix.size()); + name = name.left(200 - suffix.size()); + + while (QFile::exists(targetPath + "/" + name + suffix)) + { + name = QCryptographicHash::hash(name, QCryptographicHash::Md5).toHex(); + } + + return QString::fromUtf8(name + suffix); +} + +static bool renameFile(const QFileInfo &fileInfo, const QString &target, QString *errorString = NULL) +{ + if (fileInfo.isFile() || fileInfo.isSymLink()) + { + QFile file(fileInfo.filePath()); + + if (!file.rename(target)) + { + if (errorString) + { + *errorString = file.errorString(); + } + + return false; + } + + return true; + } + else + { + QDirIterator iterator(fileInfo.filePath(), + QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); + + while (iterator.hasNext()) + { + iterator.next(); + + const QString newFile = iterator.filePath().replace(0, fileInfo.filePath().length(), target); + + if (!QDir().mkpath(QFileInfo(newFile).path())) + { + if (errorString) + { + *errorString = QString("Make the %1 path is failed").arg(QFileInfo(newFile).path()); + } + + return false; + } + + if (!renameFile(iterator.fileInfo(), newFile, errorString)) + { + return false; + } + } + + if (!QDir().rmdir(fileInfo.filePath())) + { + if (errorString) + { + *errorString = QString("Cannot remove the %1 dir").arg(fileInfo.filePath()); + } + + return false; + } + } + + return true; +} + +class DTrashManagerPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate +{ +public: + DTrashManagerPrivate(DTrashManager *q_ptr) + : DObjectPrivate(q_ptr) {} + + D_DECLARE_PUBLIC(DTrashManager) +}; + +DTrashManager *DTrashManager::instance() +{ + return globalTrashManager; +} + +bool DTrashManager::trashIsEmpty() const +{ + return false; +} + +bool DTrashManager::cleanTrash() +{ + return false; +} + +bool DTrashManager::moveToTrash(const QString &filePath, bool followSymlink) +{ + return false; +} + +DTrashManager::DTrashManager() + : QObject() + , DObject(*new DTrashManagerPrivate(this)) +{ + +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dtrashmanager_linux.cpp b/src/filesystem/dtrashmanager_linux.cpp new file mode 100644 index 0000000..80a6dc2 --- /dev/null +++ b/src/filesystem/dtrashmanager_linux.cpp @@ -0,0 +1,247 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtrashmanager.h" +#include "dstandardpaths.h" +#include "base/private/dobject_p.h" + +#include +#include +#include +#include +#include + +#define TRASH_PATH \ + DStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/Trash" +#define TRASH_INFO_PATH TRASH_PATH"/info" +#define TRASH_FILES_PATH TRASH_PATH"/files" + +DCORE_BEGIN_NAMESPACE + +class DTrashManager_ : public DTrashManager {}; +Q_GLOBAL_STATIC(DTrashManager_, globalTrashManager) + +static QString getNotExistsFileName(const QString &fileName, const QString &targetPath) +{ + QByteArray name = fileName.toUtf8(); + + int index = name.lastIndexOf('.'); + QByteArray suffix; + + if (index >= 0) { + suffix = name.mid(index); + } + + if (suffix.size() > 200) { + suffix = suffix.left(200); + } + + name.chop(suffix.size()); + name = name.left(200 - suffix.size()); + + while (QFile::exists(targetPath + "/" + name + suffix)) { + name = QCryptographicHash::hash(name, QCryptographicHash::Md5).toHex(); + } + + return QString::fromUtf8(name + suffix); +} + +static bool writeTrashInfo(const QString &fileBaseName, const QString &sourceFilePath, const QDateTime &datetime, QString *errorString = NULL) +{ + QFile metadata(TRASH_INFO_PATH"/" + fileBaseName + ".trashinfo"); + + if (metadata.exists()) { + if (errorString) { + *errorString = QString("The %1 file is exists").arg(metadata.fileName()); + } + + return false; + } + + if (!metadata.open(QIODevice::WriteOnly)) { + if (errorString) { + *errorString = metadata.errorString(); + } + + return false; + } + + QByteArray data; + + data.append("[Trash Info]\n"); + data.append("Path=").append(sourceFilePath.toUtf8().toPercentEncoding("/")).append("\n"); + data.append("DeletionDate=").append(datetime.toString(Qt::ISODate)).append("\n"); + + qint64 size = metadata.write(data); + metadata.close(); + + if (size <= 0) { + if (errorString) { + *errorString = metadata.errorString(); + } + + return false; + } + + return true; +} + +static bool renameFile(const QFileInfo &fileInfo, const QString &target, QString *errorString = NULL) +{ + if (fileInfo.isFile() || fileInfo.isSymLink()) { + QFile file(fileInfo.filePath()); + + if (!file.rename(target)) { + if (errorString) { + *errorString = file.errorString(); + } + + return false; + } + + return true; + } else { + QDirIterator iterator(fileInfo.filePath(), + QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); + + while (iterator.hasNext()) { + iterator.next(); + + const QString newFile = iterator.filePath().replace(0, fileInfo.filePath().length(), target); + + if (!QDir().mkpath(QFileInfo(newFile).path())) { + if (errorString) { + *errorString = QString("Make the %1 path is failed").arg(QFileInfo(newFile).path()); + } + + return false; + } + + if (!renameFile(iterator.fileInfo(), newFile, errorString)) { + return false; + } + } + + if (!QDir().rmdir(fileInfo.filePath())) { + if (errorString) { + *errorString = QString("Cannot remove the %1 dir").arg(fileInfo.filePath()); + } + + return false; + } + } + + return true; +} + +class DTrashManagerPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate +{ +public: + DTrashManagerPrivate(DTrashManager *q_ptr) + : DObjectPrivate(q_ptr) {} + + static bool removeFileOrDir(const QString &path); + static bool removeFromIterator(QDirIterator &iter); + + D_DECLARE_PUBLIC(DTrashManager) +}; + +DTrashManager *DTrashManager::instance() +{ + return globalTrashManager; +} + +bool DTrashManager::trashIsEmpty() const +{ + QDirIterator iterator(TRASH_INFO_PATH, +// QStringList() << "*.trashinfo", + QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden); + + return !iterator.hasNext(); +} + +bool DTrashManager::cleanTrash() +{ + QDirIterator iterator_info(TRASH_INFO_PATH, + QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden); + + QDirIterator iterator_files(TRASH_FILES_PATH, + QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System, + QDirIterator::Subdirectories); + + return DTrashManagerPrivate::removeFromIterator(iterator_info) && + DTrashManagerPrivate::removeFromIterator(iterator_files); +} + +bool DTrashManager::moveToTrash(const QString &filePath, bool followSymlink) +{ + QFileInfo fileInfo(filePath); + + if (!fileInfo.exists() && (followSymlink || !fileInfo.isSymLink())) { + return false; + } + + QDir trashDir(TRASH_FILES_PATH); + QStorageInfo storageInfo(fileInfo.filePath()); + QStorageInfo trashStorageInfo(trashDir); + + if (storageInfo != trashStorageInfo) { + return false; + } + + if (!trashDir.mkpath(TRASH_INFO_PATH)) { + return false; + } + + if (!trashDir.mkpath(TRASH_FILES_PATH)) { + return false; + } + + if (followSymlink && fileInfo.isSymLink()) { + fileInfo.setFile(fileInfo.symLinkTarget()); + } + + const QString &fileName = getNotExistsFileName(fileInfo.fileName(), TRASH_FILES_PATH); + + if (!writeTrashInfo(fileName, fileInfo.filePath(), QDateTime::currentDateTime())) { + return false; + } + + const QString &newFilePath = TRASH_FILES_PATH"/" + fileName; + + return renameFile(fileInfo, newFilePath); +} + +DTrashManager::DTrashManager() + : QObject() + , DObject(*new DTrashManagerPrivate(this)) +{ + +} + +bool DTrashManagerPrivate::removeFileOrDir(const QString &path) +{ + QFileInfo fileInfo(path); + if (fileInfo.isDir() && !fileInfo.isSymLink()) { + QDir dir(path); + return dir.removeRecursively(); + } else { + return QFile::remove(path); + } +} + +bool DTrashManagerPrivate::removeFromIterator(QDirIterator &iter) +{ + bool ok = true; + while (iter.hasNext()) { + QString nextPath = iter.next(); +// qDebug() << iter.fileName() << iterator_info.filePath(); + if (!DTrashManagerPrivate::removeFileOrDir(nextPath)) { + ok = false; + } + } + return ok; +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/filesystem.cmake b/src/filesystem/filesystem.cmake new file mode 100644 index 0000000..e8fad48 --- /dev/null +++ b/src/filesystem/filesystem.cmake @@ -0,0 +1,57 @@ +if(APPLE) + set(FILESYSTEM_SOURCE + ${CMAKE_CURRENT_LIST_DIR}/private/dbasefilewatcher_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dfilesystemwatcher_linux_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dcapfsfileengine_p.h + ${CMAKE_CURRENT_LIST_DIR}/dbasefilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilesystemwatcher_dummy.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatchermanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dpathbuf.cpp + ${CMAKE_CURRENT_LIST_DIR}/dtrashmanager_dummy.cpp + ${CMAKE_CURRENT_LIST_DIR}/dstandardpaths.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfile.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapmanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfsfileengine.cpp + ) +elseif(WIN32) + set(FILESYSTEM_SOURCE + ${CMAKE_CURRENT_LIST_DIR}/private/dbasefilewatcher_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dfilesystemwatcher_linux_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dcapfsfileengine_p.h + ${CMAKE_CURRENT_LIST_DIR}/dbasefilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilesystemwatcher_win.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatchermanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dpathbuf.cpp + ${CMAKE_CURRENT_LIST_DIR}/dtrashmanager_dummy.cpp + ${CMAKE_CURRENT_LIST_DIR}/dstandardpaths.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfile.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapmanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfsfileengine.cpp + ) +else() + set(FILESYSTEM_SOURCE + ${CMAKE_CURRENT_LIST_DIR}/private/dbasefilewatcher_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dfilesystemwatcher_linux_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dcapfsfileengine_p.h + ${CMAKE_CURRENT_LIST_DIR}/dbasefilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilesystemwatcher_linux.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatchermanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dpathbuf.cpp + ${CMAKE_CURRENT_LIST_DIR}/dtrashmanager_linux.cpp + ${CMAKE_CURRENT_LIST_DIR}/dstandardpaths.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfile.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapmanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfsfileengine.cpp + ) +endif() +file(GLOB FILESYSTEM_HEAD + ${CMAKE_CURRENT_LIST_DIR}/../../include/filesystem/* +) +set(filesystem_SRCS + ${FILESYSTEM_HEAD} + ${FILESYSTEM_SOURCE} +) + diff --git a/src/filesystem/private/dbasefilewatcher_p.h b/src/filesystem/private/dbasefilewatcher_p.h new file mode 100644 index 0000000..fac5027 --- /dev/null +++ b/src/filesystem/private/dbasefilewatcher_p.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DBASEFILEWATCHER_P_H +#define DBASEFILEWATCHER_P_H + +#include "base/private/dobject_p.h" + +#include + +DCORE_BEGIN_NAMESPACE + +class DBaseFileWatcher; +class DBaseFileWatcherPrivate : public DObjectPrivate +{ +public: + DBaseFileWatcherPrivate(DBaseFileWatcher *qq); + + virtual bool start() = 0; + virtual bool stop() = 0; + + QUrl url; + bool started = false; + static QList watcherList; + + D_DECLARE_PUBLIC(DBaseFileWatcher) +}; + +DCORE_END_NAMESPACE + +#endif // DBASEFILEWATCHER_P_H diff --git a/src/filesystem/private/dcapfsfileengine_p.h b/src/filesystem/private/dcapfsfileengine_p.h new file mode 100644 index 0000000..b032405 --- /dev/null +++ b/src/filesystem/private/dcapfsfileengine_p.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DCAPFSFILEENGINE_P_H +#define DCAPFSFILEENGINE_P_H + +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class DCapFSFileEngineHandler : public QAbstractFileEngineHandler +{ +public: + QAbstractFileEngine *create(const QString &fileName) const override; +}; + +class DCapFSFileEnginePrivate; +class DCapFSFileEngine : public QFSFileEngine, public DObject +{ + D_DECLARE_PRIVATE(DCapFSFileEngine); +public: + DCapFSFileEngine(const QString &file); + ~DCapFSFileEngine() override; + + bool open(QIODevice::OpenMode openMode) override; + bool remove() override; + bool copy(const QString &newName) override; + bool rename(const QString &newName) override; + bool renameOverwrite(const QString &newName) override; + bool link(const QString &newName) override; + bool mkdir(const QString &dirName, bool createParentDirectories) const override; + bool rmdir(const QString &dirName, bool recurseParentDirectories) const override; + FileFlags fileFlags(FileFlags type) const override; + bool cloneTo(QAbstractFileEngine *target) override; + bool setSize(qint64 size) override; + QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const override; + Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override; + + bool canReadWrite(const QString &path) const; +}; + +DCORE_END_NAMESPACE +#endif // DCAPFSFILEENGINE_P_H diff --git a/src/filesystem/private/dfilesystemwatcher_dummy_p.h b/src/filesystem/private/dfilesystemwatcher_dummy_p.h new file mode 100644 index 0000000..3063840 --- /dev/null +++ b/src/filesystem/private/dfilesystemwatcher_dummy_p.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILESYSTEMWATCHER_WIN_P_H +#define DFILESYSTEMWATCHER_WIN_P_H + +#include "base/private/dobject_p.h" + +DCORE_BEGIN_NAMESPACE + +class DFileSystemWatcher; +class DFileSystemWatcherPrivate : public DObjectPrivate +{ + Q_DECLARE_PUBLIC(DFileSystemWatcher) + +public: + DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq); + ~DFileSystemWatcherPrivate(); + + // private slots + void _q_readFromInotify(); +}; + +void DFileSystemWatcherPrivate::_q_readFromInotify() +{ + +} + +DCORE_END_NAMESPACE + +#endif // DFILESYSTEMWATCHER_WIN_P_H diff --git a/src/filesystem/private/dfilesystemwatcher_linux_p.h b/src/filesystem/private/dfilesystemwatcher_linux_p.h new file mode 100644 index 0000000..0f112be --- /dev/null +++ b/src/filesystem/private/dfilesystemwatcher_linux_p.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILESYSTEMWATCHER_P_H +#define DFILESYSTEMWATCHER_P_H + +#include "dobject_p.h" + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DFileSystemWatcher; +class DFileSystemWatcherPrivate : public DObjectPrivate +{ + Q_DECLARE_PUBLIC(DFileSystemWatcher) + +public: + DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq); + ~DFileSystemWatcherPrivate(); + + QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories); + QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories); + + QStringList files, directories; + int inotifyFd; + QHash pathToID; + QMultiHash idToPath; + QSocketNotifier notifier; + + // private slots + void _q_readFromInotify(); + +private: + void onFileChanged(const QString &path, bool removed); + void onDirectoryChanged(const QString &path, bool removed); +}; + +DCORE_END_NAMESPACE + +#endif // DFILESYSTEMWATCHER_P_H diff --git a/src/filesystem/private/dfilesystemwatcher_win_p.h b/src/filesystem/private/dfilesystemwatcher_win_p.h new file mode 100644 index 0000000..3063840 --- /dev/null +++ b/src/filesystem/private/dfilesystemwatcher_win_p.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILESYSTEMWATCHER_WIN_P_H +#define DFILESYSTEMWATCHER_WIN_P_H + +#include "base/private/dobject_p.h" + +DCORE_BEGIN_NAMESPACE + +class DFileSystemWatcher; +class DFileSystemWatcherPrivate : public DObjectPrivate +{ + Q_DECLARE_PUBLIC(DFileSystemWatcher) + +public: + DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq); + ~DFileSystemWatcherPrivate(); + + // private slots + void _q_readFromInotify(); +}; + +void DFileSystemWatcherPrivate::_q_readFromInotify() +{ + +} + +DCORE_END_NAMESPACE + +#endif // DFILESYSTEMWATCHER_WIN_P_H diff --git a/src/filesystem/private/private.pri b/src/filesystem/private/private.pri new file mode 100644 index 0000000..6fcc278 --- /dev/null +++ b/src/filesystem/private/private.pri @@ -0,0 +1,11 @@ +HEADERS += \ + $$PWD/dbasefilewatcher_p.h \ + $$PWD/dcapfsfileengine_p.h + +linux { + HEADERS += \ + $$PWD/dfilesystemwatcher_linux_p.h +} else:win* { + HEADERS += \ + $$PWD/dfilesystemwatcher_win_p.h +} diff --git a/src/glob.cmake b/src/glob.cmake new file mode 100644 index 0000000..9703bbd --- /dev/null +++ b/src/glob.cmake @@ -0,0 +1,29 @@ +if(LINUX) + file(GLOB OUTER_SOURCE + ${CMAKE_CURRENT_LIST_DIR}/*.cpp + ) + file(GLOB OUTER_HEADER + ${CMAKE_CURRENT_LIST_DIR}/../include/global/*.h + ) +else() + set(OUTER_SOURCE + ${CMAKE_CURRENT_LIST_DIR}/dconfig.cpp + ${CMAKE_CURRENT_LIST_DIR}/dsgapplication.cpp + ${CMAKE_CURRENT_LIST_DIR}/dsysinfo.cpp + ${CMAKE_CURRENT_LIST_DIR}/dsecurestring.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddesktopentry.cpp + ${CMAKE_CURRENT_LIST_DIR}/dtkcore_global.cpp + ) + set(OUTER_HEADER + ${CMAKE_CURRENT_LIST_DIR}/../include/global/dtkcore_global.h + ${CMAKE_CURRENT_LIST_DIR}/../include/dconfig.h + ${CMAKE_CURRENT_LIST_DIR}/../include/dsgapplication.h + ${CMAKE_CURRENT_LIST_DIR}/../include/dsysinfo.h + ${CMAKE_CURRENT_LIST_DIR}/../include/dsecurestring.h + ${CMAKE_CURRENT_LIST_DIR}/../include/ddesktopentry.h + ) +endif() +set(glob_SRC + ${OUTER_HEADER} + ${OUTER_SOURCE} +) diff --git a/src/log/AbstractAppender.cpp b/src/log/AbstractAppender.cpp new file mode 100644 index 0000000..5ac16eb --- /dev/null +++ b/src/log/AbstractAppender.cpp @@ -0,0 +1,159 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "AbstractAppender.h" + +DCORE_BEGIN_NAMESPACE + +/*! + \class Dtk::Core::AbstractAppender + \inmodule dtkcore + + \brief The AbstractAppender class provides an abstract base class for writing a log entries. + + The AbstractAppender class is the base interface class for all log appenders that could be used with Logger. + + AbstractAppender provides a common implementation for the thread safe, mutex-protected logging of application + messages, such as ConsoleAppender, FileAppender or something else. AbstractAppender is abstract and can not be + instantiated, but you can use any of its subclasses or create a custom log appender at your choice. + + Appenders are the logical devices that is aimed to be attached to Logger object by calling + Logger::registerAppender(). On each log record call from the application Logger object sequentially calls write() + function on all the appenders registered in it. + + You can subclass AbstractAppender to implement a logging target of any kind you like. It may be the external logging + subsystem (for example, syslog in *nix), XML file, SQL database entries, D-Bus messages or anything else you can + imagine. + + For the simple non-structured plain text logging (for example, to a plain text file or to the console output) you may + like to subclass the AbstractStringAppender instead of AbstractAppender, which will give you a more convenient way to + control the format of the log output. + + \sa AbstractStringAppender + \sa Logger::registerAppender() + */ + + +/*! + \brief Constructs a AbstractAppender object. + */ +AbstractAppender::AbstractAppender() + :m_detailsLevel(Logger::Debug) +{ + +} + +/*! + \brief Destructs the AbstractAppender object. + */ +AbstractAppender::~AbstractAppender() +{ + +} + +/*! + \brief Returns the current details level of appender. + + Log records with a log level lower than a current detailsLevel() will be silently ignored by appender and would not + be sent to its append() function. + + It provides additional logging flexibility, allowing you to set the different severity levels for different types + of logs. + + \note This function is thread safe. + + \return The log level. + + \sa setDetailsLevel() + \sa Logger::LogLevel + */ +Logger::LogLevel AbstractAppender::detailsLevel() const +{ + QMutexLocker locker(&m_detailsLevelMutex); + return m_detailsLevel; +} + +/*! + \brief Sets the current details level of appender. + + Default details \a level is Logger::Debug + + \note This function is thread safe. + + \sa detailsLevel() + \sa Logger::LogLevel + */ +void Dtk::Core::AbstractAppender::setDetailsLevel(Logger::LogLevel level) +{ + QMutexLocker locker(&m_detailsLevelMutex); + m_detailsLevel = level; +} + +/*! + \brief Sets the current details \a level of appender. + + This function is provided for convenience, it behaves like an above function. + + \sa detailsLevel() + \sa Logger::LogLevel + */ +void AbstractAppender::setDetailsLevel(const QString &level) +{ + setDetailsLevel(Logger::levelFromString(level)); +} + +/*! + \brief Tries to write the log record to this logger. + + This is the function called by Logger object to write a log \a message to the appender. + + The \a time parameter indicates the time stamp. + The \a level parameter describes the LogLevel. + The \a file parameter is the current file name. + The \a line parameter indicates the number of lines to output. + The \a func parameter indicates the function name to output. + The \a category parameter indicates the log category. + The \a msg parameter indicates the output message. + + \note This function is thread safe. + + \sa Logger::write() + \sa detailsLevel() + */ +void AbstractAppender::write(const QDateTime &time, Logger::LogLevel level, const char *file, int line, const char *func, const QString &category, const QString &msg) +{ + if (level < detailsLevel()) + return; + + QMutexLocker locker(&m_writeMutex); + append(time, level, file, line, func, category, msg); +} + +/*! + \fn virtual void AbstractAppender::append(const QDateTime &timeStamp, Logger::LogLevel level, const char *file, int line, + const char *function, const QString &category, const QString &message) = 0 + + \brief Writes the log record to the logger instance + + This function is called every time when user tries to write a message to this AbstractAppender instance using + the write() function. Write function works as proxy and transfers only the messages with log level more or equal + to the current logLevel(). + + Overload this function when you are implementing a custom appender. + + The \a time parameter indicates the time stamp. + The \a level parameter describes the LogLevel. + The \a file parameter is the current file name. + The \a line parameter indicates the number of lines to output. + The \a func parameter indicates the function name to output. + The \a category parameter indicates the log category. + The \a msg parameter indicates the output message. + + \note This function is not needed to be thread safe because it is never called directly by Logger object. The + write() function works as a proxy and protects this function from concurrent access. + + \sa Logger::write() + */ + +DCORE_END_NAMESPACE diff --git a/src/log/AbstractStringAppender.cpp b/src/log/AbstractStringAppender.cpp new file mode 100644 index 0000000..dcf4c37 --- /dev/null +++ b/src/log/AbstractStringAppender.cpp @@ -0,0 +1,433 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "AbstractStringAppender.h" + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +/*! + \class Dtk::Core::AbstractStringAppender + \inmodule dtkcore + + \brief The AbstractStringAppender class provides a convenient base for appenders working with plain text formatted + logs. + + AbstractSringAppender is the simple extension of the AbstractAppender class providing the convenient way to create + custom log appenders working with a plain text formatted log targets. + + It have the formattedString() protected function that formats the logging arguments according to a format set with + setFormat(). + + This class can not be directly instantiated because it contains pure virtual function inherited from AbstractAppender + class. + + For more detailed description of customizing the log output format see the documentation on the setFormat() function. + */ + +const char formattingMarker = '%'; + +/*! + \brief Constructs a new string appender object. + */ +AbstractStringAppender::AbstractStringAppender() + : m_format(QLatin1String("%{time}{yyyy-MM-ddTHH:mm:ss.zzz} [%{type:-7}] <%{function}> %{message}\n")) +{ + +} + +/*! + \brief Returns the current log format string. + + The default format is set to "%{time}{yyyy-MM-ddTHH:mm:ss.zzz} [%{type:-7}] <%{function}> %{message}\n". You can set a different log record + format using the setFormat() function. + + \sa setFormat(const QString&) + */ +QString AbstractStringAppender::format() const +{ + QReadLocker locker(&m_formatLock); + return m_format; +} + +/*! + \brief Sets the logging format for writing strings to the log target with this appender. + + The string format seems to be very common to those developers who have used a standard sprintf function. + + Log output format is a simple QString with the special markers (starting with % sign) which will be replaced with + it's internal meaning when writing a log record. + + Controlling marker begins with the percent sign (%) which is followed by the command inside {} brackets + (the command describes, what will be put to log record instead of marker). + Optional field width argument may be specified right after the command (through the colon symbol before the closing bracket) + Some commands requires an additional formatting argument (in the second {} brackets). + + Field width argument works almost identically to the QString::arg() fieldWidth argument (and uses it + internally). For example, "%{type:-7}" will be replaced with the left padded debug level of the message + ("Debug ") or something. For the more detailed description of it you may consider to look to the Qt + Reference Documentation. + + Supported marker commands are: + \list + \li %{time} - timestamp. You may specify your custom timestamp \a format using the second {} brackets after the marker, + \li timestamp \a format here will be similar to those used in QDateTime::toString() function. For example, + \li "%{time}{dd-MM-yyyy, HH:mm}" may be replaced with "17-12-2010, 20:17" depending on current date and time. + \li The default \a format used here is "HH:mm:ss.zzz". + \li %{type} - Log level. Possible log levels are shown in the Logger::LogLevel enumerator. + \li %{Type} - Uppercased log level. + \li %{typeOne} - One letter log level. + \li %{TypeOne} - One uppercase letter log level. + \li %{File} - Full source file name (with path) of the file that requested log recording. Uses the __FILE__ + \li preprocessor macro. + \li %{file} - Short file name (with stripped path). + \li %{line} - Line number in the source file. Uses the __LINE__ preprocessor macro. + \li %{Function} - Name of function that called on of the LOG_* macros. Uses the Q_FUNC_INFO macro provided with + \li Qt. + \li %{function} - Similar to the %{Function}, but the function name is stripped using stripFunctionName + \li %{message} - The log message sent by the caller. + \li %{category} - The log category. + \li %{appname} - Application name (returned by QCoreApplication::applicationName() function). + \li %{pid} - Application pid (returned by QCoreApplication::applicationPid() function). + \li %{threadid} - ID of current thread. + \li %% - Convinient marker that is replaced with the single % mark. + \endlist + + \note Format doesn't add '\\n' to the end of the \a format line. Please consider adding it manually. + + \sa format() + \sa stripFunctionName() + \sa Logger::LogLevel + */ +void AbstractStringAppender::setFormat(const QString &format) +{ + QWriteLocker locker(&m_formatLock); + m_format = format; +} + +/*! + \brief Strips the long function signature (as added by Q_FUNC_INFO macro). + + The string processing drops the returning type, arguments and template parameters of function. It is definitely + useful for enchancing the log output readability. + + The \a name parameter is the function name. + + \return stripped function name + */ +QString AbstractStringAppender::stripFunctionName(const char *name) +{ + return QString::fromLatin1(qCleanupFuncinfo(name)); +} + +// The function was backported from Qt5 sources (qlogging.h) +QByteArray AbstractStringAppender::qCleanupFuncinfo(const char *name) +{ + QByteArray info(name); + + // Strip the function info down to the base function name + // note that this throws away the template definitions, + // the parameter types (overloads) and any const/volatile qualifiers. + if (info.isEmpty()) + return info; + + int pos; + + // skip trailing [with XXX] for templates (gcc) + pos = info.size() - 1; + if (info.endsWith(']')) { + while (--pos) { + if (info.at(pos) == '[') + info.truncate(pos); + } + } + + bool hasLambda = false; + QRegExp lambdaRegex("::"); + int lambdaIndex = lambdaRegex.indexIn(QString::fromLatin1(info)); + if (lambdaIndex != -1) + { + hasLambda = true; + info.remove(lambdaIndex, lambdaRegex.matchedLength()); + } + + // operator names with '(', ')', '<', '>' in it + static const char operator_call[] = "operator()"; + static const char operator_lessThan[] = "operator<"; + static const char operator_greaterThan[] = "operator>"; + static const char operator_lessThanEqual[] = "operator<="; + static const char operator_greaterThanEqual[] = "operator>="; + + // canonize operator names + info.replace("operator ", "operator"); + + // remove argument list + Q_FOREVER { + int parencount = 0; + pos = info.lastIndexOf(')'); + if (pos == -1) { + // Don't know how to parse this function name + return info; + } + + // find the beginning of the argument list + --pos; + ++parencount; + while (pos && parencount) { + if (info.at(pos) == ')') + ++parencount; + else if (info.at(pos) == '(') + --parencount; + --pos; + } + if (parencount != 0) + return info; + + info.truncate(++pos); + + if (info.at(pos - 1) == ')') { + if (info.indexOf(operator_call) == pos - (int)strlen(operator_call)) + break; + + // this function returns a pointer to a function + // and we matched the arguments of the return type's parameter list + // try again + info.remove(0, info.indexOf('(')); + info.chop(1); + continue; + } else { + break; + } + } + + if (hasLambda) + info.append("::lambda"); + + // find the beginning of the function name + int parencount = 0; + int templatecount = 0; + --pos; + + // make sure special characters in operator names are kept + if (pos > -1) { + switch (info.at(pos)) { + case ')': + if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1) + pos -= 2; + break; + case '<': + if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1) + --pos; + break; + case '>': + if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1) + --pos; + break; + case '=': { + int operatorLength = (int)strlen(operator_lessThanEqual); + if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1) + pos -= 2; + else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1) + pos -= 2; + break; + } + default: + break; + } + } + + while (pos > -1) { + if (parencount < 0 || templatecount < 0) + return info; + + char c = info.at(pos); + if (c == ')') + ++parencount; + else if (c == '(') + --parencount; + else if (c == '>') + ++templatecount; + else if (c == '<') + --templatecount; + else if (c == ' ' && templatecount == 0 && parencount == 0) + break; + + --pos; + } + info = info.mid(pos + 1); + + // remove trailing '*', '&' that are part of the return argument + while ((info.at(0) == '*') + || (info.at(0) == '&')) + info = info.mid(1); + + // we have the full function name now. + // clean up the templates + while ((pos = info.lastIndexOf('>')) != -1) { + if (!info.contains('<')) + break; + + // find the matching close + int end = pos; + templatecount = 1; + --pos; + while (pos && templatecount) { + register char c = info.at(pos); + if (c == '>') + ++templatecount; + else if (c == '<') + --templatecount; + --pos; + } + ++pos; + info.remove(pos, end - pos + 1); + } + + return info; +} + +/*! + \brief Returns the string to record to the logging target, formatted according to the format(). + + \a time The time stamp. + The \a level parameter describes the LogLevel, and the \a file parameter is the current file name, + and the \a line parameter indicates the number of lines to output. + The \a func parameter indicates the function name to output. + The \a category parameter indicates the log category. + The \a msg parameter indicates the output message. + + \sa format() + \sa setFormat(const QString&) + */ +QString AbstractStringAppender::formattedString(const QDateTime &time, Logger::LogLevel level, + const char *file, int line, const char *func, + const QString &category, const QString &msg) const +{ + QString f = format(); + const int size = f.size(); + + QString result; + + int i = 0; + while (i < f.size()) { + QChar c = f.at(i); + + // We will silently ignore the broken % marker at the end of string + if (c != QLatin1Char(formattingMarker) || (i + 2) >= size) { + result.append(c); + } else { + i += 2; + QChar currentChar = f.at(i); + QString command; + int fieldWidth = 0; + + if (currentChar.isLetter()) { + command.append(currentChar); + int j = 1; + while ((i + j) < size && f.at(i + j).isLetter()) { + command.append(f.at(i+j)); + j++; + } + + i+=j; + currentChar = f.at(i); + + // Check for the padding instruction + if (currentChar == QLatin1Char(':')) { + currentChar = f.at(++i); + if (currentChar.isDigit() || currentChar.category() == QChar::Punctuation_Dash) { + int j = 1; + while ((i + j) < size && f.at(i + j).isDigit()) j++; + + fieldWidth = f.mid(i, j).toInt(); + i += j; + } + } + } + + // Log record chunk to insert instead of formatting instruction + QString chunk; + + // Time stamp + if (command == QLatin1String("time")) { + if (f.at(i + 1) == QLatin1Char('{')) { + int j = 1; + while ((i + 2 + j) < size && f.at(i + 2 + j) != QLatin1Char('}')) j++; + + if ((i + 2 + j) < size) { + chunk = time.toString(f.mid(i + 2, j)); + + i += j; + i += 2; + } + } + + if (chunk.isNull()) + chunk = time.toString(QLatin1String("HH:mm:ss.zzz")); + + } else if (command == QLatin1String("type")) { + // Log level + chunk = Logger::levelToString(level); + } else if (command == QLatin1String("Type")) { + // Uppercased log level + chunk = Logger::levelToString(level).toUpper(); + } else if (command == QLatin1String("typeOne")) { + // One letter log level + chunk = Logger::levelToString(level).left(1).toLower(); + } else if (command == QLatin1String("TypeOne")) { + // One uppercase letter log level + chunk = Logger::levelToString(level).left(1).toUpper(); + } else if (command == QLatin1String("File")) { + // Filename + chunk = QLatin1String(file); + } else if (command == QLatin1String("file")) { + // Filename without a path + chunk = QString(QLatin1String(file)).section('/', -1); + } else if (command == QLatin1String("line")) { + // Source line number + chunk = QString::number(line); + } else if (command == QLatin1String("Function")) { + // Function name, as returned by Q_FUNC_INFO + chunk = QString::fromLatin1(func); + } else if (command == QLatin1String("function")) { + // Stripped function name + chunk = stripFunctionName(func); + } else if (command == QLatin1String("message")) { + // Log message + chunk = msg; + } else if (command == QLatin1String("category")) { + // Log message + chunk = category; + } else if (command == QLatin1String("pid")) { + // Application pid + chunk = QString::number(QCoreApplication::applicationPid()); + } else if (command == QLatin1String("appname")) { + // Application name + chunk = QCoreApplication::applicationName(); + } else if (command == QLatin1String("threadid")) { + // Thread ID (duplicates Qt5 threadid debbuging way) + chunk = QLatin1String("0x") + QString::number(qlonglong(QThread::currentThread()->currentThread()), 16); + } else if (command == QString(formattingMarker)) { + // We simply replace the double formatting marker (%) with one + chunk = QLatin1Char(formattingMarker); + } else { + // Do not process any unknown commands + chunk = QString(formattingMarker); + chunk.append(command); + } + + if (!chunk.isEmpty() && chunk != "0") { + result.append(QString(QLatin1String("%1")).arg(chunk, fieldWidth)); + } + } + ++i; + } + + return result; +} + +DCORE_END_NAMESPACE diff --git a/src/log/ConsoleAppender.cpp b/src/log/ConsoleAppender.cpp new file mode 100644 index 0000000..b3bc852 --- /dev/null +++ b/src/log/ConsoleAppender.cpp @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +// Local +#include "ConsoleAppender.h" + +// STL +#include + +DCORE_BEGIN_NAMESPACE + +/*! + \class Dtk::Core::ConsoleAppender + \inmodule dtkcore + + \brief ConsoleAppender is the simple appender that writes the log records to the std::cerr output stream. + + ConsoleAppender uses "[%{type:-7}] <%{function}> %{message}\n" as a default output format. It is similar to the + AbstractStringAppender but doesn't show a time. + + You can modify ConsoleAppender output format without modifying your code by using \c QT_MESSAGE_PATTERN environment + variable. If you need your application to ignore this environment variable you can call + ConsoleAppender::ignoreEnvironmentPattern(true) + */ + + +ConsoleAppender::ConsoleAppender() + : AbstractStringAppender() + ,m_ignoreEnvPattern(false) +{ + setFormat("[%{type:-7}] <%{function}> %{message}\n"); +} + + +QString ConsoleAppender::format() const +{ + const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN")); + return (m_ignoreEnvPattern || envPattern.isEmpty()) ? AbstractStringAppender::format() : (envPattern + "\n"); +} + + +void ConsoleAppender::ignoreEnvironmentPattern(bool ignore) +{ + m_ignoreEnvPattern = ignore; +} + +/*! + \brief Writes the log record to the std::cerr stream. + \reimp + + The \a time parameter indicates the time stamp. + The \a level parameter describes the LogLevel. + The \a file parameter is the current file name. + The \a line parameter indicates the number of lines to output. + The \a func parameter indicates the function name to output. + The \a category parameter indicates the log category. + The \a msg parameter indicates the output message. + + \sa AbstractStringAppender::format() + */ +void ConsoleAppender::append(const QDateTime &time, Logger::LogLevel level, const char *file, int line, + const char *func, const QString &category, const QString &msg) +{ + std::cerr << qPrintable(formattedString(time, level, file, line, func, category, msg)); +} + +DCORE_END_NAMESPACE diff --git a/src/log/FileAppender.cpp b/src/log/FileAppender.cpp new file mode 100644 index 0000000..971ca23 --- /dev/null +++ b/src/log/FileAppender.cpp @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "FileAppender.h" + +#include + +DCORE_BEGIN_NAMESPACE + +/*! + \class Dtk::Core::FileAppender + \inmodule dtkcore + + \brief Simple appender that writes the log records to the plain text file. + */ + + +/*! + \brief Constructs the new file appender assigned to file with the given \a fileName. + */ +FileAppender::FileAppender(const QString &fileName) +{ + setFileName(fileName); +} + + +FileAppender::~FileAppender() +{ + closeFile(); +} + +/*! + \brief Returns the name set by setFileName() or to the FileAppender constructor. + + \sa setFileName() + */ +QString FileAppender::fileName() const +{ + QMutexLocker locker(&m_logFileMutex); + return m_logFile.fileName(); +} + +/*! + \brief Sets the \a s name of the file. The name can have no path, a relative path, or an absolute path. + + \sa fileName() + */ +void FileAppender::setFileName(const QString &s) +{ + QMutexLocker locker(&m_logFileMutex); + if (m_logFile.isOpen()) + m_logFile.close(); + + m_logFile.setFileName(s); +} + +qint64 FileAppender::size() const +{ + return m_logFile.size(); +} + + +bool FileAppender::openFile() +{ + bool isOpen = m_logFile.isOpen(); + if (!isOpen) { + isOpen = m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text); + if (isOpen) + m_logStream.setDevice(&m_logFile); + else + std::cerr << " Cannot open the log file " << qPrintable(m_logFile.fileName()) << std::endl; + } + return isOpen; +} + +/*! + \brief Write the log record to the file. + \reimp + + The \a time parameter indicates the time stamp. + The \a level parameter describes the LogLevel. + The \a file parameter is the current file name. + The \a line parameter indicates the number of lines to output. + The \a func parameter indicates the func name to output. + The \a category parameter indicates the log category. + The \a msg parameter indicates the output message. + + \sa fileName() + \sa AbstractStringAppender::format() + */ +void FileAppender::append(const QDateTime &time, Logger::LogLevel level, const char *file, int line, + const char *func, const QString &category, const QString &msg) +{ + QMutexLocker locker(&m_logFileMutex); + + if (openFile()) + { + m_logStream << formattedString(time, level, file, line, func, category, msg); + m_logStream.flush(); + m_logFile.flush(); + } +} + + +void FileAppender::closeFile() +{ + QMutexLocker locker(&m_logFileMutex); + m_logFile.close(); +} + +DCORE_END_NAMESPACE diff --git a/src/log/LogManager.cpp b/src/log/LogManager.cpp new file mode 100644 index 0000000..6749f72 --- /dev/null +++ b/src/log/LogManager.cpp @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "LogManager.h" +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +/*! + \class Dtk::Core::DLogManager + \inmodule dtkcore + + \brief DLogManager is the deepin user application log manager. + */ + +DLogManager::DLogManager() +{ +#if !defined(QT_DEBUG) && !defined(QT_MESSAGELOGCONTEXT) + m_format = "%{time}{yyyy-MM-dd, HH:mm:ss.zzz} [%{type:-7}] %{message}\n"; +#else + m_format = "%{time}{yyyy-MM-dd, HH:mm:ss.zzz} [%{type:-7}] [%{file:-20} %{function:-35} %{line}] %{message}\n"; +#endif +} + +void DLogManager::initConsoleAppender(){ + m_consoleAppender = new ConsoleAppender; + m_consoleAppender->setFormat(m_format); + logger->registerAppender(m_consoleAppender); +} + +void DLogManager::initRollingFileAppender(){ + m_rollingFileAppender = new RollingFileAppender(getlogFilePath()); + m_rollingFileAppender->setFormat(m_format); + m_rollingFileAppender->setLogFilesLimit(5); + m_rollingFileAppender->setDatePattern(RollingFileAppender::DailyRollover); + logger->registerAppender(m_rollingFileAppender); +} + +/*! + \brief Registers the appender to write the log records to the Console. + + \sa registerFileAppender + */ +void DLogManager::registerConsoleAppender(){ + DLogManager::instance()->initConsoleAppender(); +} + +/*! + \brief Registers the appender to write the log records to the file. + + \sa getlogFilePath + \sa registerConsoleAppender + */ +void DLogManager::registerFileAppender() { + DLogManager::instance()->initRollingFileAppender(); +} + +/*! + \brief Return the path file log storage. + + \brief DLogManager::getlogFilePath 获取日志文件路径 + \brief 默认日志路径是 ~/.cache/organizationName/applicationName.log + \brief 如果获取 HOME 环境变量失败将不写日志 + \sa registerFileAppender + */ +QString DLogManager::getlogFilePath() +{ + // 不再构造时去设置默认logpath(且mkdir), 而在getlogPath时再去判断是否设置默认值 + // 修复设置了日志路径还是会在默认的位置创建目录的问题 + if (DLogManager::instance()->m_logPath.isEmpty()) { + if (QDir::homePath() == QDir::rootPath()) { + qWarning() << "unable to locate the cache directory." + << "logfile path is empty, the log will not be written.\r\n" + << (qgetenv("HOME").isEmpty() ? "the HOME environment variable not set" : ""); + return QString(); + } + + QString cachePath = QStandardPaths::standardLocations(QStandardPaths::CacheLocation).at(0); + if (!QDir(cachePath).exists()) { + QDir(cachePath).mkpath(cachePath); + } + DLogManager::instance()->m_logPath = DLogManager::instance()->joinPath(cachePath, QString("%1.log").arg(qApp->applicationName())); + } + + return QDir::toNativeSeparators(DLogManager::instance()->m_logPath); +} + +/*! + \brief DLogManager::setlogFilePath 设置日志文件路径 + \a logFilePath 日志文件路径 + \brief 如果设置的文件路进不是文件路径将什么都不做,输出一条警告 + */ +void DLogManager::setlogFilePath(const QString &logFilePath) +{ + QFileInfo info(logFilePath); + if (info.exists() && !info.isFile()) + qWarning() << "invalid file path:" << logFilePath << " is not a file"; + else + DLogManager::instance()->m_logPath = logFilePath; +} + +void DLogManager::setLogFormat(const QString &format) +{ + //m_format = "%{time}{yyyy-MM-dd, HH:mm:ss.zzz} [%{type:-7}] [%{file:-20} %{function:-35} %{line}] %{message}\n"; + DLogManager::instance()->m_format = format; +} + +QString DLogManager::joinPath(const QString &path, const QString &fileName){ + QString separator(QDir::separator()); + return QString("%1%2%3").arg(path, separator, fileName); +} + +DLogManager::~DLogManager() +{ + +} + +DCORE_END_NAMESPACE diff --git a/src/log/Logger.cpp b/src/log/Logger.cpp new file mode 100644 index 0000000..5529760 --- /dev/null +++ b/src/log/Logger.cpp @@ -0,0 +1,1050 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "Logger.h" +#include "AbstractAppender.h" +#include "AbstractStringAppender.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +DCORE_BEGIN_NAMESPACE + +/*! + \headerfile + \inmodule dtkcore + \brief A file containing the description of Logger class and and additional useful macros for logging. + */ + +/*! + \macro Dtk::Core::logger + \relates Dtk::Core::Logger + \keyword Dtk::Core::logger + + \brief Macro returning the current instance of Logger object + + If you haven't created a local Logger object it returns the same value as the Logger::globalInstance() functions. + This macro is a recommended way to get an access to the Logger instance used in current class. + + Example: + \code + ConsoleAppender* consoleAppender = new ConsoleAppender; + logger->registerAppender(consoleAppender); + \endcode + + \sa Dtk::Core::Logger::globalInstance() + */ + +/*! + \macro Dtk::Core::dTrace + \relates Dtk::Core::Logger + \keyword Dtk::Core::dTrace + + \brief Writes the trace log record + + This macro is the convenient way to call Logger::write(). It uses the common preprocessor macros \c __FILE__, + \c __LINE__ and the standard Qt \c Q_FUNC_INFO macros to automatically determine the needed parameters to call + Logger::write(). + + \note This and other (dInfo() etc...) macros uses the variadic macro arguments to give convenient usage form for + the different versions of Logger::write() (using the QString or const char *argument or returning the QDebug class + instance). Not all compilers will support this. Please, consider reviewing your compiler documentation to ensure + it support __VA_ARGS__ macro. + + \sa Dtk::Core::dInfo Dtk::Core::dDebug Dtk::Core::dWarning Dtk::Core::dError + \sa Dtk::Core::Logger::LogLevel + \sa Dtk::Core::Logger::write() + */ + +/*! + \macro Dtk::Core::dDebug + \relates Dtk::Core::Logger + \keyword Dtk::Core::dDebug + + \brief Writes the debug log record + + This macro records the debug log record using the Logger::write() function. It works similar to the dTrace() + macro. + + \sa Dtk::Core::dTrace Dtk::Core::dInfo Dtk::Core::dWarning Dtk::Core::dError + \sa Dtk::Core::Logger::LogLevel + \sa Dtk::Core::Logger::write() + */ + +/*! + \macro Dtk::Core::dInfo + \relates Dtk::Core::Logger + \keyword Dtk::Core::dInfo + + \brief Writes the info log record + + This macro records the info log record using the Logger::write() function. It works similar to the dTrace() + macro. + + \sa Dtk::Core::dTrace Dtk::Core::dDebug Dtk::Core::dWarning Dtk::Core::dError + \sa Dtk::Core::Logger::LogLevel + \sa Dtk::Core::Logger::write() + */ + +/*! + \macro Dtk::Core::dWarning + \relates Dtk::Core::Logger + \keyword Dtk::Core::dWarning + + \brief Write the warning log record + + This macro records the warning log record using the Logger::write() function. It works similar to the dTrace() + macro. + + \sa Dtk::Core::dTrace Dtk::Core::dInfo Dtk::Core::dDebug Dtk::Core::dError + \sa Dtk::Core::Logger::LogLevel + \sa Dtk::Core::Logger::write() + */ + +/*! + \macro Dtk::Core::dError + \relates Dtk::Core::Logger + \keyword Dtk::Core::dError + + \brief Write the error log record + This macro records the error log record using the Logger::write() function. It works similar to the dTrace() + macro. + + \sa Dtk::Core::dTrace + \sa Dtk::Core::Logger::LogLevel + \sa Dtk::Core::Logger::write() + */ + +/*! + \macro Dtk::Core::dFatal + \relates Dtk::Core::Logger + \keyword Dtk::Core::dFatal + + \brief Write the fatal log record + + This macro records the fatal log record using the Logger::write() function. It works similar to the dTrace() + macro. + + \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort() + function, which will interrupt the running of your software and begin the writing of the core dump. + + \sa Dtk::Core::dTrace + \sa Dtk::Core::Logger::LogLevel + \sa Dtk::Core::Logger::write() + */ + +/*! + \macro Dtk::Core::dCTrace(category) + \relates Dtk::Core::Logger + \keyword Dtk::Core::dCTrace() + + \brief Writes the trace log record to the specific category + + This macro is the similar to the dTrace() macro, but has a category parameter + to write only to the category appenders (registered using Logger::registerCategoryAppender() method). + + \a category category name string + + \sa Dtk::Core::dTrace + \sa Dtk::Core::Logger::LogLevel + \sa Dtk::Core::Logger::registerCategoryAppender() + \sa Dtk::Core::Logger::write() + \sa Dtk::Core::dCategory(), Dtk::Core::dGlobalCategory() + */ + + +/*! + \macro Dtk::Core::dCDebug + \relates Dtk::Core::Logger + \keyword Dtk::Core::dCDebug + + \brief Writes the debug log record to the specific category + + This macro records the debug log record using the Logger::write() function. It works similar to the dCTrace() + macro. + + \sa Dtk::Core::dCTrace() + */ + +/*! + \macro Dtk::Core::dCInfo + \relates Dtk::Core::Logger + \keyword Dtk::Core::dCInfo + + \brief Writes the info log record to the specific category + + This macro records the info log record using the Logger::write() function. It works similar to the dCTrace() + macro. + + \sa Dtk::Core::dCTrace() + */ + +/*! + \macro Dtk::Core::dCWarning + \relates Dtk::Core::Logger + \keyword Dtk::Core::dCWarning + + \brief Writes the warning log record to the specific category + + This macro records the warning log record using the Logger::write() function. It works similar to the dCTrace() + macro. + + \sa Dtk::Core::dCTrace() + */ + +/*! + \macro Dtk::Core::dCError + \relates Dtk::Core::Logger + \keyword Dtk::Core::dCError + + \brief Writes the error log record to the specific category + + This macro records the error log record using the Logger::write() function. It works similar to the dCTrace() + macro. + + \sa Dtk::Core::dCTrace() + */ + +/*! + \macro Dtk::Core::dCFatal + \relates Dtk::Core::Logger + \keyword Dtk::Core::dCFatal + + \brief Write the fatal log record to the specific category + + This macro records the fatal log record using the Logger::write() function. It works similar to the dCTrace() + macro. + + \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort() + function, which will interrupt the running of your software and begin the writing of the core dump. + + \sa Dtk::Core::dCTrace() + */ + +/*! + \macro Dtk::Core::dCategory(category) + \relates Dtk::Core::Logger + \keyword Dtk::Core::dCategory() + + \brief Create logger instance inside your custom class to log all messages to the specified \a category + + This macro is used to pass all log messages inside your custom class to the specific \a category. + You must include this macro inside your class declaration (similarly to the Q_OBJECT macro). + Internally, this macro redefines loggerInstance() function, creates the local Logger object inside your class and + sets the default category to the specified parameter. + + Thus, any call to loggerInstance() (for example, inside dTrace() macro) will return the local Logger object, + so any logging message will be directed to the default category. + + \note This macro does not register any appender to the newly created logger instance. You should register + logger appenders manually, inside your class. + + Usage example: + \code + class CustomClass : public QObject + { + Q_OBJECT + dCategory("custom_category") + ... + }; + + CustomClass::CustomClass(QObject* parent) : QObject(parent) + { + logger->registerAppender(new FileAppender("custom_category_log")); + dTrace() << "Trace to the custom category log"; + } + \endcode + + \sa Dtk::Core::Logger::write() + \sa Dtk::Core::dTrace() + \sa Dtk::Core::Logger::registerCategoryAppender() + \sa Dtk::Core::Logger::setDefaultCategory() + */ + +/*! + \macro Dtk::Core::dGlobalCategory(category) + \relates Dtk::Core::Logger + \keyword Dtk::Core::dGlobalCategory() + + \brief Create logger instance inside your custom class to log all messages both to the specified \a category and to + the global logger instance. + + This macro is similar to dCategory(), but also passes all log messages to the global logger instance appenders. + It is equal to defining the local \a category logger using dCategory macro and calling: + \code + logger->logToGlobalInstance(logger->defaultCategory(), true); + \endcode + + \sa Dtk::Core::dCategory + \sa Dtk::Core::Logger::logToGlobalInstance() + \sa Dtk::Core::Logger::defaultCategory() + \sa Dtk::Core::Logger::registerCategoryAppender() + \sa Dtk::Core::Logger::write() + */ + +/*! + \macro Dtk::Core::dAssert + \relates Dtk::Core::Logger + \keyword Dtk::Core::dAssert + + \brief Check the assertion + + This macro is a convenient and recommended to use way to call Logger::writeAssert() function. It uses the + preprocessor macros (as the dDebug() does) to fill the necessary arguments of the Logger::writeAssert() call. It + also uses undocumented but rather mature and stable \c qt_noop() function (which does nothing) when the assertion + is true. + + Example: + \code + bool b = checkSomething(); + ... + dAssert(b == true); + \endcode + + \sa Dtk::Core::Logger::writeAssert() + */ + +/*! + \macro Dtk::Core::dTraceTime + \relates Dtk::Core::Logger + \keyword Dtk::Core::dTraceTime + + \brief Logs the processing time of current function / code block + + This macro automagically measures the function or code of block execution time and outputs it as a Logger::Trace + level log record. + + Example: + \code + int foo() + { + dTraceTime(); + ... // Do some long operations + return 0; + } // Outputs: Function foo finished in