New upstream version 5.6.2.2
authorBoyuan Yang <byang@debian.org>
Sat, 3 Dec 2022 14:01:34 +0000 (09:01 -0500)
committerBoyuan Yang <byang@debian.org>
Sat, 3 Dec 2022 14:01:34 +0000 (09:01 -0500)
368 files changed:
.github/ISSUE_TEMPLATE/config.yml [new file with mode: 0644]
.github/ISSUE_TEMPLATE/empty-issue.md [new file with mode: 0644]
.github/ISSUE_TEMPLATE/unit-test-report.md [new file with mode: 0644]
.github/workflows/backup-to-gitlab.yml
.github/workflows/call-auto-tag.yml [new file with mode: 0644]
.github/workflows/call-build-deb.yml
.github/workflows/call-doc-check.yml [new file with mode: 0644]
.github/workflows/call-license-check.yml [new file with mode: 0644]
.github/workflows/call-tag-build.yml [new file with mode: 0644]
.gitignore
.qmake.conf [deleted file]
.reuse/dep5 [new file with mode: 0644]
CMakeLists.txt
LICENSE
LICENSES/CC-BY-4.0.txt [new file with mode: 0644]
LICENSES/CC0-1.0.txt [new file with mode: 0644]
LICENSES/LGPL-2.1-or-later.txt [new file with mode: 0644]
LICENSES/LGPL-3.0-or-later.txt [new file with mode: 0644]
LICENSES/MIT.txt [new file with mode: 0644]
README.md
README.zh_CN.md [new file with mode: 0644]
archlinux/PKGBUILD
cmake/DtkCMake/DtkCMakeConfig.cmake
cmake/DtkTools/DtkToolsConfig.cmake
conanfile.py
debian/changelog
debian/control
debian/libdtkcore-doc.install [new file with mode: 0644]
debian/missing-sources/doxygen-awesome-darkmode-toggle.js [new file with mode: 0644]
debian/rules
doc/CMakeLists.txt [deleted file]
doc/Specification.md [deleted file]
doc/src/dtkcore-index.qdoc [deleted file]
doc/src/dtkcore.qdoc [deleted file]
docs/CMakeLists.txt [new file with mode: 0644]
docs/Specification.md [new file with mode: 0644]
docs/doxygentheme/custom-alternative.css [new file with mode: 0644]
docs/doxygentheme/custom.css [new file with mode: 0644]
docs/doxygentheme/doxygen-awesome-darkmode-toggle.js [new file with mode: 0644]
docs/doxygentheme/doxygen-awesome-fragment-copy-button.js [new file with mode: 0644]
docs/doxygentheme/doxygen-awesome-interactive-toc.js [new file with mode: 0644]
docs/doxygentheme/doxygen-awesome-paragraph-link.js [new file with mode: 0644]
docs/doxygentheme/doxygen-awesome-sidebar-only-darkmode-toggle.css [new file with mode: 0644]
docs/doxygentheme/doxygen-awesome-sidebar-only.css [new file with mode: 0644]
docs/doxygentheme/doxygen-awesome.css [new file with mode: 0644]
docs/doxygentheme/header.html [new file with mode: 0644]
docs/doxygentheme/logo.drawio.svg [new file with mode: 0644]
docs/doxygentheme/toggle-alternative-theme.js [new file with mode: 0644]
docs/dsysinfo.zh_CN.dox [new file with mode: 0644]
dtkcore.pro [deleted file]
examples/CMakeLists.txt [new file with mode: 0644]
examples/dasync-example/CMakeLists.txt [new file with mode: 0644]
examples/dasync-example/dasync-example.pro [deleted file]
examples/dasync-example/main.cpp
examples/examples.pro [deleted file]
examples/expintf-example/CMakeLists.txt [new file with mode: 0644]
examples/expintf-example/expintf-example.pro [deleted file]
examples/expintf-example/main.cpp
include/DtkCore/DAbstractUnitFormatter [new file with mode: 0644]
include/DtkCore/DBaseFileWatcher [new file with mode: 0644]
include/DtkCore/DCapDir [new file with mode: 0644]
include/DtkCore/DCapFile [new file with mode: 0644]
include/DtkCore/DCapManager [new file with mode: 0644]
include/DtkCore/DConfig [new file with mode: 0644]
include/DtkCore/DConfigFile [new file with mode: 0644]
include/DtkCore/DDBusExtended [new file with mode: 0644]
include/DtkCore/DDBusExtendedAbstractInterface [new file with mode: 0644]
include/DtkCore/DDBusInterface [new file with mode: 0644]
include/DtkCore/DDBusSender [new file with mode: 0644]
include/DtkCore/DDciFile [new file with mode: 0644]
include/DtkCore/DDesktopEntry [new file with mode: 0644]
include/DtkCore/DDiskSizeFormatter [new file with mode: 0644]
include/DtkCore/DError [new file with mode: 0644]
include/DtkCore/DExpected [new file with mode: 0644]
include/DtkCore/DExportedInterface [new file with mode: 0644]
include/DtkCore/DFileServices [new file with mode: 0644]
include/DtkCore/DFileSystemWatcher [new file with mode: 0644]
include/DtkCore/DFileWatcher [new file with mode: 0644]
include/DtkCore/DFileWatcherManager [new file with mode: 0644]
include/DtkCore/DLog [new file with mode: 0644]
include/DtkCore/DNotifySender [new file with mode: 0644]
include/DtkCore/DObject [new file with mode: 0644]
include/DtkCore/DObjectPrivate [new file with mode: 0644]
include/DtkCore/DPathBuf [new file with mode: 0644]
include/DtkCore/DPinyin [new file with mode: 0644]
include/DtkCore/DRecentManager [new file with mode: 0644]
include/DtkCore/DSGApplication [new file with mode: 0644]
include/DtkCore/DSecureString [new file with mode: 0644]
include/DtkCore/DSettings [new file with mode: 0644]
include/DtkCore/DSettingsGroup [new file with mode: 0644]
include/DtkCore/DSettingsOption [new file with mode: 0644]
include/DtkCore/DSingleton [new file with mode: 0644]
include/DtkCore/DStandardPaths [new file with mode: 0644]
include/DtkCore/DSysInfo [new file with mode: 0644]
include/DtkCore/DThreadUtils [new file with mode: 0644]
include/DtkCore/DTimeUnitFormatter [new file with mode: 0644]
include/DtkCore/DTrashManager [new file with mode: 0644]
include/DtkCore/DUtil [new file with mode: 0644]
include/DtkCore/DVtableHook [new file with mode: 0644]
include/DtkCore/DtkCores [new file with mode: 0644]
include/base/derror.h [new file with mode: 0644]
include/base/dexpected.h [new file with mode: 0644]
include/base/dobject.h [new file with mode: 0644]
include/base/dsingleton.h [new file with mode: 0644]
include/base/private/dobject_p.h [new file with mode: 0644]
include/dci/ddcifile.h [new file with mode: 0644]
include/filesystem/dbasefilewatcher.h [new file with mode: 0644]
include/filesystem/dcapfile.h [new file with mode: 0644]
include/filesystem/dcapmanager.h [new file with mode: 0644]
include/filesystem/dfilesystemwatcher.h [new file with mode: 0644]
include/filesystem/dfilewatcher.h [new file with mode: 0644]
include/filesystem/dfilewatchermanager.h [new file with mode: 0644]
include/filesystem/dpathbuf.h [new file with mode: 0644]
include/filesystem/dstandardpaths.h [new file with mode: 0644]
include/filesystem/dtrashmanager.h [new file with mode: 0644]
include/global/dconfig.h [new file with mode: 0644]
include/global/dconfigfile.h [new file with mode: 0644]
include/global/ddesktopentry.h [new file with mode: 0644]
include/global/dsecurestring.h [new file with mode: 0644]
include/global/dsgapplication.h [new file with mode: 0644]
include/global/dsysinfo.h [new file with mode: 0644]
include/global/dtkcore_global.h [new file with mode: 0644]
include/log/AbstractAppender.h [new file with mode: 0644]
include/log/AbstractStringAppender.h [new file with mode: 0644]
include/log/ConsoleAppender.h [new file with mode: 0644]
include/log/FileAppender.h [new file with mode: 0644]
include/log/LogManager.h [new file with mode: 0644]
include/log/Logger.h [new file with mode: 0644]
include/log/RollingFileAppender.h [new file with mode: 0644]
include/log/dloggerdefs.h [new file with mode: 0644]
include/log/win32/OutputDebugAppender.h [new file with mode: 0644]
include/settings/backend/dsettingsdconfigbackend.h [new file with mode: 0644]
include/settings/backend/gsettingsbackend.h [new file with mode: 0644]
include/settings/backend/qsettingbackend.h [new file with mode: 0644]
include/settings/dsettings.h [new file with mode: 0644]
include/settings/dsettingsbackend.h [new file with mode: 0644]
include/settings/dsettingsgroup.h [new file with mode: 0644]
include/settings/dsettingsoption.h [new file with mode: 0644]
include/util/dabstractunitformatter.h [new file with mode: 0644]
include/util/dasync.h [new file with mode: 0644]
include/util/ddbusextended.h [new file with mode: 0644]
include/util/ddbusextendedabstractinterface.h [new file with mode: 0644]
include/util/ddbusinterface.h [new file with mode: 0644]
include/util/ddbussender.h [new file with mode: 0644]
include/util/ddisksizeformatter.h [new file with mode: 0644]
include/util/dexportedinterface.h [new file with mode: 0644]
include/util/dfileservices.h [new file with mode: 0644]
include/util/dnotifysender.h [new file with mode: 0644]
include/util/dpinyin.h [new file with mode: 0644]
include/util/drecentmanager.h [new file with mode: 0644]
include/util/dthreadutils.h [new file with mode: 0644]
include/util/dtimedloop.h [new file with mode: 0644]
include/util/dtimeunitformatter.h [new file with mode: 0644]
include/util/dutil.h [new file with mode: 0644]
include/util/dvtablehook.h [new file with mode: 0644]
misc/DtkConfig.cmake.in [new file with mode: 0644]
misc/dtkcore.pc.in [new file with mode: 0644]
misc/qt_lib_dtkcore.pri.in [new file with mode: 0644]
src/CMakeLists.txt [new file with mode: 0644]
src/DConfig [deleted file]
src/DConfigFile [deleted file]
src/DDesktopEntry [deleted file]
src/DSecureString [deleted file]
src/DSysInfo [deleted file]
src/base/DObject [deleted file]
src/base/DObjectPrivate [deleted file]
src/base/DSingleton [deleted file]
src/base/base.cmake [new file with mode: 0644]
src/base/base.pri [deleted file]
src/base/dobject.cpp
src/base/dobject.h [deleted file]
src/base/dsingleton.h [deleted file]
src/base/private/dobject_p.h [deleted file]
src/base/private/private.pri [deleted file]
src/dbus/dbus.cmake [new file with mode: 0644]
src/dbus/org.desktopspec.ConfigManager.Manager.xml
src/dbus/org.desktopspec.ConfigManager.xml
src/dci/dci.cmake [new file with mode: 0644]
src/dci/ddcifile.cpp [new file with mode: 0644]
src/dci/private/ddcifileengine.cpp [new file with mode: 0644]
src/dci/private/ddcifileengine_p.h [new file with mode: 0644]
src/dconfig.cpp
src/dconfig.h [deleted file]
src/dconfigfile.cpp
src/dconfigfile.h [deleted file]
src/ddesktopentry.cpp
src/ddesktopentry.h [deleted file]
src/dsecurestring.cpp
src/dsecurestring.h [deleted file]
src/dsgapplication.cpp [new file with mode: 0644]
src/dsysinfo.cpp
src/dsysinfo.h [deleted file]
src/dtkcore_global.cpp
src/dtkcore_global.h [deleted file]
src/filesystem/DBaseFileWatcher [deleted file]
src/filesystem/DFileSystemWatcher [deleted file]
src/filesystem/DFileWatcher [deleted file]
src/filesystem/DFileWatcherManager [deleted file]
src/filesystem/DPathBuf [deleted file]
src/filesystem/DStandardPaths [deleted file]
src/filesystem/DTrashManager [deleted file]
src/filesystem/dbasefilewatcher.cpp
src/filesystem/dbasefilewatcher.h [deleted file]
src/filesystem/dcapfile.cpp [new file with mode: 0644]
src/filesystem/dcapfsfileengine.cpp [new file with mode: 0644]
src/filesystem/dcapmanager.cpp [new file with mode: 0644]
src/filesystem/dfilesystemwatcher.h [deleted file]
src/filesystem/dfilesystemwatcher_dummy.cpp
src/filesystem/dfilesystemwatcher_linux.cpp
src/filesystem/dfilesystemwatcher_win.cpp
src/filesystem/dfilewatcher.cpp
src/filesystem/dfilewatcher.h [deleted file]
src/filesystem/dfilewatchermanager.cpp
src/filesystem/dfilewatchermanager.h [deleted file]
src/filesystem/dpathbuf.cpp
src/filesystem/dpathbuf.h [deleted file]
src/filesystem/dstandardpaths.cpp
src/filesystem/dstandardpaths.h [deleted file]
src/filesystem/dtrashmanager.h [deleted file]
src/filesystem/dtrashmanager_dummy.cpp
src/filesystem/dtrashmanager_linux.cpp
src/filesystem/filesystem.cmake [new file with mode: 0644]
src/filesystem/filesystem.pri [deleted file]
src/filesystem/private/dbasefilewatcher_p.h
src/filesystem/private/dcapfsfileengine_p.h [new file with mode: 0644]
src/filesystem/private/dfilesystemwatcher_dummy_p.h
src/filesystem/private/dfilesystemwatcher_linux_p.h
src/filesystem/private/dfilesystemwatcher_win_p.h
src/filesystem/private/private.pri
src/glob.cmake [new file with mode: 0644]
src/log/AbstractAppender.cpp
src/log/AbstractAppender.h [deleted file]
src/log/AbstractStringAppender.cpp
src/log/AbstractStringAppender.h [deleted file]
src/log/ConsoleAppender.cpp
src/log/ConsoleAppender.h [deleted file]
src/log/CuteLogger_global.h [deleted file]
src/log/DLog [deleted file]
src/log/FileAppender.cpp
src/log/FileAppender.h [deleted file]
src/log/LICENSE [deleted file]
src/log/LogManager.cpp
src/log/LogManager.h [deleted file]
src/log/Logger.cpp
src/log/Logger.h [deleted file]
src/log/OutputDebugAppender.cpp
src/log/OutputDebugAppender.h [deleted file]
src/log/README.md
src/log/RollingFileAppender.cpp
src/log/RollingFileAppender.h [deleted file]
src/log/cutelogger.pri [deleted file]
src/log/log.cmake [new file with mode: 0644]
src/log/log.pri [deleted file]
src/settings/DSettings [deleted file]
src/settings/DSettingsGroup [deleted file]
src/settings/DSettingsOption [deleted file]
src/settings/backend/dsettingsdconfigbackend.cpp
src/settings/backend/dsettingsdconfigbackend.h [deleted file]
src/settings/backend/gsettingsbackend.cpp
src/settings/backend/gsettingsbackend.h [deleted file]
src/settings/backend/qsettingbackend.cpp
src/settings/backend/qsettingbackend.h [deleted file]
src/settings/dsettings.cpp
src/settings/dsettings.h [deleted file]
src/settings/dsettingsbackend.h [deleted file]
src/settings/dsettingsgroup.cpp
src/settings/dsettingsgroup.h [deleted file]
src/settings/dsettingsoption.cpp
src/settings/dsettingsoption.h [deleted file]
src/settings/settings.cmake [new file with mode: 0644]
src/settings/settings.pri [deleted file]
src/src.pro [deleted file]
src/util/DAbstractUnitFormatter [deleted file]
src/util/DDBusSender [deleted file]
src/util/DDiskSizeFormatter [deleted file]
src/util/DExportedInterface [deleted file]
src/util/DFileServices [deleted file]
src/util/DNotifySender [deleted file]
src/util/DPinyin [deleted file]
src/util/DRecentManager [deleted file]
src/util/DThreadUtils [deleted file]
src/util/DTimeUnitFormatter [deleted file]
src/util/DUtil [deleted file]
src/util/DVtableHook [deleted file]
src/util/dabstractunitformatter.cpp
src/util/dabstractunitformatter.h [deleted file]
src/util/dasync.h [deleted file]
src/util/ddbusextendedabstractinterface.cpp [new file with mode: 0644]
src/util/ddbusextendedpendingcallwatcher.cpp [new file with mode: 0644]
src/util/ddbusextendedpendingcallwatcher_p.h [new file with mode: 0644]
src/util/ddbusinterface.cpp [new file with mode: 0644]
src/util/ddbusinterface_p.h [new file with mode: 0644]
src/util/ddbussender.cpp
src/util/ddbussender.h [deleted file]
src/util/ddisksizeformatter.cpp
src/util/ddisksizeformatter.h [deleted file]
src/util/dexportedinterface.cpp
src/util/dexportedinterface.h [deleted file]
src/util/dfileservices.h [deleted file]
src/util/dfileservices_dummy.cpp
src/util/dfileservices_linux.cpp
src/util/dnotifysender.cpp
src/util/dnotifysender.h [deleted file]
src/util/dpinyin.cpp
src/util/dpinyin.h [deleted file]
src/util/drecentmanager.cpp
src/util/drecentmanager.h [deleted file]
src/util/dthreadutils.cpp
src/util/dthreadutils.h [deleted file]
src/util/dtimedloop.cpp
src/util/dtimedloop.h [deleted file]
src/util/dtimeunitformatter.cpp
src/util/dtimeunitformatter.h [deleted file]
src/util/dutil.h [deleted file]
src/util/dvtablehook.cpp
src/util/dvtablehook.h [deleted file]
src/util/util.cmake [new file with mode: 0644]
src/util/util.pri [deleted file]
tests/CMakeLists.txt [new file with mode: 0644]
tests/ddesktopentry/ddesktopentry.pro [deleted file]
tests/dthreadutils/dthreadutils.pro [deleted file]
tests/dutils/dutils.pro [deleted file]
tests/dvtablehook/dvtablehook.pro [deleted file]
tests/main.cpp
tests/test-recoverage-qmake.sh [deleted file]
tests/test-recoverage.sh [new file with mode: 0755]
tests/test_helper.hpp
tests/tests.pro [deleted file]
tests/ut_dasync.cpp
tests/ut_dcapfile.cpp [new file with mode: 0644]
tests/ut_dconfig.cpp
tests/ut_dconfigfile.cpp
tests/ut_ddci.cpp [new file with mode: 0644]
tests/ut_ddesktopentrytest.cpp
tests/ut_ddisksizeformatter.cpp
tests/ut_dfilesystemwatcher.cpp
tests/ut_dfilewatcher.cpp
tests/ut_dfilewatchermanager.cpp
tests/ut_dpathbuf.cpp
tests/ut_dpinyin.cpp
tests/ut_drecentmanager.cpp
tests/ut_dsecurestring.cpp
tests/ut_dsettings.cpp
tests/ut_dstandardpaths.cpp
tests/ut_dthreadutils.cpp
tests/ut_dtimeunitformatter.cpp
tests/ut_dtrashmanager.cpp
tests/ut_dutil.cpp
tests/ut_dutil.h
tests/ut_dvtablehook.cpp
tests/ut_gsettingsbackend.cpp
tests/ut_logger.cpp
tests/ut_qsettingsbackend.cpp
tests/ut_singleton.cpp
tests/ut_singleton.h
tools/CMakeLists.txt [new file with mode: 0644]
tools/dci/CMakeLists.txt [new file with mode: 0644]
tools/dci/dci.pro [new file with mode: 0644]
tools/dci/main.cpp [new file with mode: 0644]
tools/deepin-os-release/CMakeLists.txt [new file with mode: 0644]
tools/deepin-os-release/main.cpp
tools/qdbusxml2cpp/CMakeLists.txt [new file with mode: 0644]
tools/qdbusxml2cpp/qdbusxml2cpp.cpp
tools/script/dtk-license.py [deleted file]
tools/script/dtk-translate.py [deleted file]
tools/settings/CMakeLists.txt [new file with mode: 0644]
tools/settings/main.cpp
tools/tools.pro [deleted file]

diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644 (file)
index 0000000..0086358
--- /dev/null
@@ -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 (file)
index 0000000..84240c8
--- /dev/null
@@ -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 (file)
index 0000000..9a6743c
--- /dev/null
@@ -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]
+
index 2ebbd4db7dc16166c36468004a750287c5cbd0e4..c17633593b15ab9c8a4ed1c99d0dd0dc103df8a9 100644 (file)
@@ -6,47 +6,12 @@ concurrency:
   cancel-in-progress: true
 
 jobs:
-  backup-to-gitlab:
-    if: github.repository_owner == 'linuxdeepin'
-    name: backup-to-gitlab
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-        with:
-          repository: "linuxdeepin/jenkins-bridge-client"
-          path: jenkins-bridge-client
-
-      - name: Install Client
-        run: |
-          cd $GITHUB_WORKSPACE/jenkins-bridge-client
-          go build .
-          sudo install -Dvm755 jenkins-bridge-client -t /usr/bin/
-      - name: Trigger sync
-        id: generate-runid
-        run: |
-          echo "::set-output name=RUN_ID::$(jenkins-bridge-client -triggerSync -token '${{ secrets.BRIDGETOKEN }}')"
-      - name: Print log
-        run: |
-          jenkins-bridge-client -printlog -token "${{ secrets.BRIDGETOKEN }}" -runid "${{ steps.generate-runid.outputs.RUN_ID }}"
+  backup-to-gitlabwh:
+    uses: linuxdeepin/.github/.github/workflows/backup-to-gitlabwh.yml@master
+    secrets:
+      BRIDGETOKEN: ${{ secrets.BRIDGETOKEN }}
 
   backup-to-gitee:
-    if: github.repository_owner == 'linuxdeepin'
-    runs-on: ubuntu-latest
-    steps:
-      - name: create-repo
-        run: |
-          repo=${{ github.event.repository.name }}
-          homepage="https://github.com/linuxdeepin/${repo}"
-          description="mirror of ${homepage}"
-          # remove '.' prefix
-          repo=${repo#"."}
-          curl -X POST --header 'Content-Type: application/json;charset=UTF-8' 'https://gitee.com/api/v5/enterprises/linuxdeepin/repos' -d '{"private": 1,"access_token":"${{ secrets.GITEE_SYNC_TOKEN }}","name":"'"$repo"'","description":"'"$description"'","homepage":"'"$homepage"'","has_issues":"false","has_wiki":"false","can_comment":"false"}' || true
-      - name: push
-        run: |
-          git clone --bare https://github.com/linuxdeepin/${{ github.event.repository.name }}.git .git
-          repo=${{ github.event.repository.name }}
-          # remove '.' prefix
-          repo=${repo#"."}
-          git remote set-url origin https://myml:${{ secrets.GITEE_SYNC_TOKEN }}@gitee.com/linuxdeepin/${repo}.git
-          git push -f --all --prune origin
-          git push --tags origin
+    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 (file)
index 0000000..5018e5b
--- /dev/null
@@ -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
index c0b94f44b7d9b22e58ac96f87b8a41fc8c1924b1..fd67cbec9815a501d62f5318478f024a012b6440 100644 (file)
@@ -3,6 +3,7 @@ on:
   pull_request_target:
     paths-ignore:
       - ".github/workflows/**"
+    types: [ opened, closed, synchronize ]
 
 concurrency:
   group: ${{ github.workflow }}-pull/${{ github.event.number }}
@@ -10,6 +11,7 @@ concurrency:
 
 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-doc-check.yml b/.github/workflows/call-doc-check.yml
new file mode 100644 (file)
index 0000000..4779992
--- /dev/null
@@ -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 (file)
index 0000000..347d556
--- /dev/null
@@ -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 (file)
index 0000000..3b1850a
--- /dev/null
@@ -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
index 51c4921b10f2e8c4858f49cc1706ab0b82592ac1..3325f3c48bdb67a21eec6cf2dbaf0b31e2156492 100644 (file)
@@ -3,6 +3,7 @@
 *.lo
 *.o
 
+
 # Compiled Dynamic libraries
 *.so
 *.dylib
@@ -29,3 +30,9 @@ bin/
 Makefile
 
 cmake/DtkCore*
+.cache
+DtkCoreConfig.cmake
+dtkcore.pc
+qt_lib_dtkcore.pri
+dtkcore_config.h
+CMakeLists.txt.user
diff --git a/.qmake.conf b/.qmake.conf
deleted file mode 100644 (file)
index 444dc18..0000000
+++ /dev/null
@@ -1 +0,0 @@
-DTK_VERSION=
diff --git a/.reuse/dep5 b/.reuse/dep5
new file mode 100644 (file)
index 0000000..2f81f1d
--- /dev/null
@@ -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
index 4b69a49ba613a80e4991204327f9257b4dfad8d0..ea82a7495dfdf2bd94cdfdd1d9c9c07e9989fbaa 100644 (file)
 cmake_minimum_required (VERSION 3.10)
 
+set (DVERSION "5.6.2" CACHE STRING "define project version")
+
 project (DtkCore
-       VERSION "${DTK_REPO_MODULE_VERSION}"
-       DESCRIPTION "DTK Core module"
-       HOMEPAGE_URL ""
-       LANGUAGES CXX C
+  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")
 
-find_package (Qt5 CONFIG REQUIRED COMPONENTS DBus Xml)
+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(doc)
+  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
index 0a041280bd00a9d068f503b8ee7ce35214bd24a1..513d1c01fe5b5b9724e999d8eb041ff17d444eaa 100644 (file)
--- a/LICENSE
+++ b/LICENSE
-                   GNU LESSER GENERAL PUBLIC LICENSE
-                       Version 3, 29 June 2007
+GNU LESSER GENERAL PUBLIC LICENSE
+Version 3, 29 June 2007
 
- Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
+Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 
+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.
+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.
+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.
+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.
+"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. <http://fsf.org/>
+
+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.
+
+     <one line to give the program's name and a brief idea of what it does.>
+     Copyright (C) <year>  <name of author>
+
+     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 <http://www.gnu.org/licenses/>.
+
+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:
+
+     <program>  Copyright (C) <year>  <name of author>
+     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 <http://www.gnu.org/licenses/>.
+
+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 <http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt
new file mode 100644 (file)
index 0000000..13ca539
--- /dev/null
@@ -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 (file)
index 0000000..0e259d4
--- /dev/null
@@ -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 (file)
index 0000000..04bb156
--- /dev/null
@@ -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.
+
+<one line to give the library's name and an idea of what it does.>
+
+Copyright (C) <year> <name of author>
+
+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 (file)
index 0000000..513d1c0
--- /dev/null
@@ -0,0 +1,304 @@
+GNU LESSER GENERAL PUBLIC LICENSE
+Version 3, 29 June 2007
+
+Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+
+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. <http://fsf.org/>
+
+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.
+
+     <one line to give the program's name and a brief idea of what it does.>
+     Copyright (C) <year>  <name of author>
+
+     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 <http://www.gnu.org/licenses/>.
+
+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:
+
+     <program>  Copyright (C) <year>  <name of author>
+     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 <http://www.gnu.org/licenses/>.
+
+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 <http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt
new file mode 100644 (file)
index 0000000..2071b23
--- /dev/null
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) <year> <copyright holders>
+
+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.
index 96430ae0ab2abc5e5b0205934a2575f1d14a6175..484359d0bd188c8e8ee173eb6d8f1e3faafbe60a 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-## Deepin Tool Kit Core {#mainpage}
+## Deepin Tool Kit Core
 
-Deepint Tool Kit (Dtk) is the base development tool of all C++/Qt Developer work on Deepin.
+Deepin Tool Kit (DtkCore) is the base development tool of all C++/Qt Developer work on Deepin.
 
-You should read the [Deepin Application Specification](\ref doc/Specification) firstly.
+You should read the <a href=docs/Specification.md>Deepin Application Specification</a> firstly.
 
 ## Dependencies
 
@@ -18,25 +18,26 @@ You should read the [Deepin Application Specification](\ref doc/Specification) f
 
 2. Build:
 
-````
-mkdir build
-cd build
-$ qmake ..
-make
-````
+```bash
+mkdir build
+cd build
+cmake ..
+make
+```
 
 3. Install:
 
-````
-sudo make install
-````
+```bash
+sudo make install
+```
 
 ## Getting help
 
 Any usage issues can ask for help via
 
-* [Gitter](https://gitter.im/orgs/linuxdeepin/rooms)
-* [IRC channel](https://webchat.freenode.net/?channels=deepin)
+* [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/)
 
@@ -44,9 +45,8 @@ Any usage issues can ask for help via
 
 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). (English)
-* [开发者代码贡献指南](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers) (中文)
+* [Contribution guide for developers](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers-en).
 
 ## License
 
-deepin-tool-kit is licensed under [GPLv3](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 (file)
index 0000000..f395e04
--- /dev/null
@@ -0,0 +1,52 @@
+## Deepin Tool Kit Core
+
+Deepin Tool Kit Core(DtkCore) 是所有C++/Qt开发人员在Deepin上工作的基础开发工具.
+
+您应该首先阅读 <a href=docs/Specification.md>Deepin应用程序规范</a>.
+
+## 依赖
+
+### 编译依赖
+
+* 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).
index 099b590e693976d7544d0025879d4434b79c5ef9..5ee33de05e11f81e24837c9212a73be329e2727f 100644 (file)
@@ -7,24 +7,27 @@ 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' 'gtest' 'dtkcommon-git')
+makedepends=('git' 'qt5-tools' 'dtkcommon-git' 'ninja' 'cmake' 'doxygen')
 conflicts=('dtkcore')
 provides=('dtkcore')
 groups=('deepin-git')
 source=('source.tar.gz')
 sha512sums=('SKIP')
 
-prepare() {
-    cd $deepin_source_name
-}
-
 build() {
   cd $deepin_source_name
-  qmake-qt5 PREFIX=/usr DTK_VERSION=$pkgver LIB_INSTALL_DIR=/usr/lib
-  make
+  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
-  make INSTALL_ROOT="$pkgdir" install
+  DESTDIR="$pkgdir" ninja install
 }
index f8d62f13dd27fe9e856550dd6717caffcf1fe6da..a1182231f011a38dbf9167ea77d7492081500b97 100644 (file)
@@ -8,7 +8,7 @@ addDefinitions(Q_HOST_${CMAKE_HOST_SYSTEM_PROCESSOR})
 
 find_package(DtkCore REQUIRED)
 
-set(DEEPIN_OS_RELEASE_TOOL_PATH ${DTKCORE_TOOL_DIR})
+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}")
index 02ec88e098d4dbf26ed44e8a64392f5eb1cd67c8..49b1f5eae7fa5f8d677708895fe1a54ab356ac84 100644 (file)
@@ -1,6 +1,6 @@
 find_package(DtkCore REQUIRED)
 
-set (DTK_SETTINGS_TOOLS_EXECUTABLE ${DTKCORE_TOOL_DIR}/dtk-settings)
+set (DTK_SETTINGS_TOOLS_EXECUTABLE ${DtkCore_TOOL_DIRS}/dtk-settings)
 
 if (EXISTS ${DTK_SETTINGS_TOOLS_EXECUTABLE})
     set(DTK_SETTINGS_TOOLS_FOUND TRUE)
index 3a6161f886204423e0f5cbdf20918bfbdbffa0d0..c1a33e2707451501a660c2c1a666a0226a4418b4 100644 (file)
@@ -1,3 +1,7 @@
+# SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+
 from conans import ConanFile, tools
 
 
index 63463dc7119294cf4173b36d6a53b40014077c42..a62246e7260b8f42d16fb08bba632b8641505b93 100644 (file)
@@ -1,3 +1,27 @@
+dtkcore (5.6.2.2) unstable; urgency=medium
+
+  * release 5.6.2.2
+
+ -- Deepin Packages Builder <packages@deepin.com>  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 <packages@deepin.com>  Mon, 31 Oct 2022 16:53:16 +0800
+
+dtkcore (5.6.2) unstable; urgency=medium
+
+  * snipe release 5.6.2
+
+ -- Deepin Packages Builder <packages@deepin.com>  Wed, 21 Sep 2022 13:57:29 +0800
+
+dtkcore (5.6.0) unstable; urgency=medium
+
+  * snipe release 5.6.0
+
+ -- Deepin Packages Builder <packages@deepin.com>  Mon, 11 Jul 2022 17:47:56 +0800
+
 dtkcore (5.0.3) unstable; urgency=medium
 
   * Release 5.0.3
index cf0c8136c07b33238fd79a8ef7bd57ff23b64a16..c4727bd2fb495152262f60f0ed99cf1815f8add6 100644 (file)
@@ -3,8 +3,8 @@ Section: libdevel
 Priority: optional
 Maintainer: Deepin Packages Builder <packages@deepin.com>
 Build-Depends: debhelper (>= 9), pkg-config,
- qttools5-dev-tools, qtbase5-private-dev,
- libgsettings-qt-dev, libgtest-dev, libdtkcommon-dev
+ qttools5-dev-tools, qtbase5-private-dev, doxygen,
+ libgsettings-qt-dev, libgtest-dev, libdtkcommon-dev, cmake
 Standards-Version: 3.9.8
 
 Package: libdtkcore5
@@ -32,3 +32,11 @@ 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/libdtkcore-doc.install b/debian/libdtkcore-doc.install
new file mode 100644 (file)
index 0000000..06733f0
--- /dev/null
@@ -0,0 +1 @@
+usr/share/qt5/doc/dtkcore.qch
diff --git a/debian/missing-sources/doxygen-awesome-darkmode-toggle.js b/debian/missing-sources/doxygen-awesome-darkmode-toggle.js
new file mode 100644 (file)
index 0000000..05ae338
--- /dev/null
@@ -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 = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FCBF00"><rect fill="none" height="24" width="24"/><circle cx="12" cy="12" opacity=".3" r="3"/><path d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>`
+    static darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FE9700"><rect fill="none" height="24" width="24"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z" opacity=".3"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>`
+    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);
index 63b9b64cbc9c3057f30e78574d83f751457b0eac..a216f3b83f17114f5ecd6e96d3c053ee9e475042 100755 (executable)
@@ -2,23 +2,21 @@
 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}')
-_BUILD_VER = $(shell echo $(VERSION) | awk -F'[+_~-]' '{print $$2}' | sed 's/[^0-9]//g')
-ifeq ($(_BUILD_VER),)
-       CONFIG_VERSION = $(_PACK_VER)
-else
-       CONFIG_VERSION = $(_PACK_VER).$(_BUILD_VER)
-endif
+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 $@ --buildsystem=qmake --parallel
+       dh $@ --parallel
 
 override_dh_auto_configure:
-       dh_auto_configure -- LIB_INSTALL_DIR=/usr/lib/$(DEB_HOST_MULTIARCH) VERSION=$(CONFIG_VERSION)
+       dh_auto_configure -- -DBUILD_EXAMPLES=OFF -DBUILD_DOCS=ON -DBUILD_VERSION=$(BUILD_VER) -DDVERSION=$(PACK_VER)
 
 #override_dh_auto_test:
 #      echo "skip auto test"
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
deleted file mode 100644 (file)
index 6ec0386..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-cmake_minimum_required (VERSION 3.10)
-
-find_package (Doxygen REQUIRED)
-
-set (QCH_INSTALL_DESTINATION ${CMAKE_INSTALLL_PREFIX}/share/DDE/dtk CACHE STRING "QCH install location")
-
-set (DOXYGEN_GENERATE_HTML "NO" CACHE STRING "Doxygen HTML output")
-set (DOXYGEN_GENERATE_XML "NO" CACHE STRING "Doxygen XML output")
-set (DOXYGEN_GENERATE_QHP "YES" CACHE STRING "Doxygen QHP output")
-set (DOXYGEN_FILE_PATTERNS *.cpp *.h *.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_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/docs/)
-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_PREDEFINED
-    "\"DCORE_BEGIN_NAMESPACE=namespace Dtk { namespace Core {\""
-    "\"DCORE_END_NAMESPACE=}}\""
-    "\"DCORE_USE_NAMESPACE=using Dtk::Core\""
-)
-set (DOXYGEN_MACRO_EXPANSION "YES")
-set (DOXYGEN_EXPAND_ONLY_PREDEF "YES")
-
-doxygen_add_docs (doxygen
-    ${PROJECT_SOURCE_DIR}/src
-    ${PROJECT_SOURCE_DIR}/doc
-    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/doc/Specification.md b/doc/Specification.md
deleted file mode 100644 (file)
index 94bf3a8..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-# 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/doc/src/dtkcore-index.qdoc b/doc/src/dtkcore-index.qdoc
deleted file mode 100644 (file)
index d97ca7f..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Chen Bin <chenbin@uniontech.com>
- *
- * Maintainer: Chen Bin <chenbin@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*!
-\page dtkcore-index.html
-
-\keyword DTK Core Reference Documentation
-\title DTK Core Docs
-
-Deepint Tool Kit (Dtk) is the base development tool of all C++/Qt Developer work on Deepin.
-
-\list
-\li \l {DTK Gui Docs}
-\li \l {DTK Gui 模块}
-\li \l {DTK Core 模块}
-\li \l {DTK Widget Docs}
-\li \l {DTK Widget 模块}
-\endlist
-*/
diff --git a/doc/src/dtkcore.qdoc b/doc/src/dtkcore.qdoc
deleted file mode 100644 (file)
index 0ede3e4..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Chen Bin <chenbin@uniontech.com>
- *
- * Maintainer: Chen Bin <chenbin@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*!
-    \module dtkcore
-    \title DTK Core 模块
-
-    \brief Deepin Tool Kit Core Devel library.
-
-    DtkCore is base devel library of Deepin Qt/C++ applications.
-*/
diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0abf026
--- /dev/null
@@ -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 (file)
index 0000000..94bf3a8
--- /dev/null
@@ -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 (file)
index 0000000..e66c1ae
--- /dev/null
@@ -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 (file)
index 0000000..0e694c0
--- /dev/null
@@ -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 (file)
index 0000000..f2c5853
--- /dev/null
@@ -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 = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FCBF00"><rect fill="none" height="24" width="24"/><circle cx="12" cy="12" opacity=".3" r="3"/><path d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>`
+    static darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FE9700"><rect fill="none" height="24" width="24"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z" opacity=".3"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>`
+    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 (file)
index 0000000..7d06b34
--- /dev/null
@@ -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 = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`
+    static successIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>`
+    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 (file)
index 0000000..b049f57
--- /dev/null
@@ -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 (file)
index 0000000..6424dbd
--- /dev/null
@@ -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 = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z"/></svg>`
+    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 (file)
index 0000000..b988b6f
--- /dev/null
@@ -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 (file)
index 0000000..656ebbf
--- /dev/null
@@ -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 (file)
index 0000000..abd2893
--- /dev/null
@@ -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 (file)
index 0000000..cda3d32
--- /dev/null
@@ -0,0 +1,147 @@
+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+    <meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=9" />
+    <meta name="generator" content="Doxygen $doxygenversion" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+
+    <!-- BEGIN opengraph metadata -->
+    <meta property="og:title" content="Doxygen Awesome" />
+    <meta property="og:image"
+        content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
+    <meta property="og:description"
+        content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
+    <meta property="og:url" content="https://jothepro.github.io/doxygen-awesome-css/" />
+    <!-- END opengraph metadata -->
+
+    <!-- BEGIN twitter metadata -->
+    <meta name="twitter:image:src"
+        content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
+    <meta name="twitter:title" content="Doxygen Awesome" />
+    <meta name="twitter:description"
+        content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
+    <!-- END twitter metadata -->
+
+    <!--BEGIN PROJECT_NAME-->
+    <title>$projectname: $title</title>
+    <!--END PROJECT_NAME-->
+    <!--BEGIN !PROJECT_NAME-->
+    <title>$title</title>
+    <!--END !PROJECT_NAME-->
+    <link href="./tabs.css" rel="stylesheet" type="text/css" />
+    <link rel="icon" type="image/svg+xml" href="./logo.drawio.svg" />
+    <script type="text/javascript" src="./jquery.js"></script>
+    <script type="text/javascript" src="./dynsections.js"></script>
+    <script type="text/javascript" src="./doxygen-awesome-darkmode-toggle.js"></script>
+    <script type="text/javascript" src="./doxygen-awesome-fragment-copy-button.js"></script>
+    <script type="text/javascript" src="./doxygen-awesome-paragraph-link.js"></script>
+    <script type="text/javascript" src="./doxygen-awesome-interactive-toc.js"></script>
+    <script type="text/javascript" src="./toggle-alternative-theme.js"></script>
+    <script type="text/javascript">
+        DoxygenAwesomeFragmentCopyButton.init()
+        DoxygenAwesomeDarkModeToggle.init()
+        DoxygenAwesomeParagraphLink.init()
+        DoxygenAwesomeInteractiveToc.init()
+    </script>
+    $treeview
+    $search
+    $mathjax
+    <link href="./$stylesheet" rel="stylesheet" type="text/css" />
+    $extrastylesheet
+</head>
+
+<body>
+
+    <!-- https://tholman.com/github-corners/ -->
+    <a href="https://github.com/linuxdeepin/dtkcore" class="github-corner" title="View source on GitHub"
+        target="_blank">
+        <!--TODO:需要修改此处连接指向项目地址 -->
+        <svg viewBox="0 0 250 250" width="40" height="40"
+            style="position: absolute; top: 0; border: 0; right: 0; z-index: 99;" aria-hidden="true">
+            <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
+            <path
+                d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
+                fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
+            <path
+                d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
+                fill="currentColor" class="octo-body"></path>
+        </svg>
+    </a>
+    <style>
+        .github-corner:hover .octo-arm {
+            animation: octocat-wave 560ms ease-in-out
+        }
+
+        @keyframes octocat-wave {
+
+            0%,
+            100% {
+                transform: rotate(0)
+            }
+
+            20%,
+            60% {
+                transform: rotate(-25deg)
+            }
+
+            40%,
+            80% {
+                transform: rotate(10deg)
+            }
+        }
+
+        @media (max-width:500px) {
+            .github-corner:hover .octo-arm {
+                animation: none
+            }
+
+            .github-corner .octo-arm {
+                animation: octocat-wave 560ms ease-in-out
+            }
+        }
+    </style>
+
+
+    <div id="top">
+        <!-- do not remove this div, it is closed by doxygen! -->
+
+        <!--BEGIN TITLEAREA-->
+        <div id="titlearea">
+            <table cellspacing="0" cellpadding="0">
+                <tbody>
+                    <tr style="height: 56px;">
+                        <!--BEGIN PROJECT_LOGO-->
+                        <td id="projectlogo"><img alt="Logo" src="./$projectlogo" /></td>
+                        <!--END PROJECT_LOGO-->
+                        <!--BEGIN PROJECT_NAME-->
+                        <td id="projectalign" style="padding-left: 0.5em;">
+                            <div id="projectname">$projectname
+                                <!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span>
+                                <!--END PROJECT_NUMBER-->
+                            </div>
+                            <!--BEGIN PROJECT_BRIEF-->
+                            <div id="projectbrief">$projectbrief</div>
+                            <!--END PROJECT_BRIEF-->
+                        </td>
+                        <!--END PROJECT_NAME-->
+                        <!--BEGIN !PROJECT_NAME-->
+                        <!--BEGIN PROJECT_BRIEF-->
+                        <td style="padding-left: 0.5em;">
+                            <div id="projectbrief">$projectbrief</div>
+                        </td>
+                        <!--END PROJECT_BRIEF-->
+                        <!--END !PROJECT_NAME-->
+                        <!--BEGIN DISABLE_INDEX-->
+                        <!--BEGIN SEARCHENGINE-->
+                        <td>$searchbox</td>
+                        <!--END SEARCHENGINE-->
+                        <!--END DISABLE_INDEX-->
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+        <!--END TITLEAREA-->
+        <!-- end header part -->
\ No newline at end of file
diff --git a/docs/doxygentheme/logo.drawio.svg b/docs/doxygentheme/logo.drawio.svg
new file mode 100644 (file)
index 0000000..a506ee0
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="61px" height="74px" viewBox="-0.5 -0.5 61 74" content="&lt;mxfile host=&quot;drawio-plugin&quot; modified=&quot;2021-03-16T23:58:23.462Z&quot; agent=&quot;5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36&quot; version=&quot;13.7.9&quot; etag=&quot;JoeaGLJ54FcERO7YrWLQ&quot; type=&quot;embed&quot;&gt;&lt;diagram id=&quot;JMB9aH8b_oZ7EWDuqJgx&quot; name=&quot;Page-1&quot;&gt;7VdNc5swEP01HDsjkGPDsSVJe+lMZnzoWYENaAwsI8ux6a+vCCtA4KSu62kmSS+M9LT7tB9P0uDxuDx8VaLOv2MKhRew9ODxay8Igigy3xZoCOC8AzIl0w7yB2AtfwKBjNCdTGHrGGrEQsvaBROsKki0gwmlcO+aPWDh7lqLDGbAOhHFHP0hU513aHjFBvwbyCy3O/uMVkphjQnY5iLF/QjiNx6PFaLuRuUhhqKtna1L53f7zGofmIJKn+RAcTyKYkfJUWC6sdlmCnc1mYHScDhWY3Fvzdk8Br/PzCgCsAStGmNCRJy2JDH4pIV8VMG+edS4rCcZcjMDSu+ZVP3fpwpV+rnVh5ndF5hsPP4l16VhvPbN8AErTWI0re7mMRaonpw5Y8tlHBvcsNzKwnpttVDaslZYgcXIhj3NFW56LS1bbrM44l6m4Wq5MLhxzEDfgZKmAKDWtUhklRFNgqVM7LYb0Enu8I9j9dkVC80KtgS6Lb3fGnYVgXSm/1Ez2fFu7oeTYA/CuIUWU1AILR9d/mN9pR3uUJqde7F88leOWhYLl2GLO5UAOY2FP+GxMm3c6CwNlXlKY9oompFZ3Rps59EOkuw8BoH2BTtNs8EfaZbUdYZkXQGuXhDgR9DYRBycXURj00D+UmMT2ktJLnr9B8HG0IzFcPkHYfUe3oPZqfOjMEiDs1+KEw5n9P/+/1f3f/gq1394lt7erqQ+0HVvpsPPRWc+/KHxm18=&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><path d="M 13 57 L 13.01 57.01 L 15.87 50.14 L 18.37 43.14 L 20.91 36.15 L 23.67 29.25 L 26.4 22.33 Q 30 13 33.71 22.28 L 33.55 22.22 L 35.48 26.91 L 37.49 31.64 L 39.48 36.36 L 41.2 40.97 L 43.05 45.63" fill="none" stroke="#010508" stroke-opacity="0.1" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 47.51 56.77 L 47.65 56.93 L 45.43 54.91 L 43.41 53.11 L 41.43 51.35 L 39.63 49.8 L 37.48 47.86 L 37.39 47.64 L 39.79 47.17 L 41.9 45.98 L 44.24 45.37 L 46.48 44.52 L 48.62 43.4 L 48.54 43.39 L 48.58 46.09 L 48.04 48.74 L 48.04 51.43 L 47.8 54.1 L 47.51 56.77 Z Z" fill-opacity="0.1" fill="#010508" stroke="#010508" stroke-opacity="0.1" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 10 43 L 9.94 42.88 L 12.16 41.98 L 14.31 40.96 L 16.51 40.01 L 18.62 38.89 L 20.88 38.1 Q 30 34 40 34 L 40 33.75 L 42 33.83 L 44 33.8 L 46 33.79 L 48 34.05 L 50 34" fill="none" stroke="#010508" stroke-opacity="0.1" stroke-width="7" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 10 54 L 9.97 53.99 L 12.69 47.07 L 15.43 40.16 L 18.07 33.21 L 20.65 26.24 L 23.4 19.33 Q 27 10 30.71 19.28 L 30.66 19.26 L 32.46 23.91 L 34.55 28.66 L 36.26 33.27 L 38.35 38.03 L 40.05 42.63" fill="none" stroke="#1982d2" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 44.51 53.77 L 44.56 53.83 L 42.48 51.97 L 40.5 50.21 L 38.48 48.41 L 36.41 46.56 L 34.48 44.86 L 34.55 45.02 L 36.72 44 L 39 43.24 L 41.21 42.28 L 43.48 41.51 L 45.62 40.4 L 45.78 40.42 L 45.51 43.09 L 45.01 45.74 L 44.87 48.42 L 44.94 51.12 L 44.51 53.77 Z Z" fill="#1982d2" stroke="#1982d2" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 7 40 L 7.02 40.05 L 9.28 39.25 L 11.33 38 L 13.48 36.96 L 15.73 36.14 L 17.88 35.1 Q 27 31 37 31 L 37 30.79 L 39 31.11 L 41 30.85 L 43 30.78 L 45 30.89 L 47 31" fill="none" stroke="#1982d2" stroke-width="8" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/></g></svg>
\ 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 (file)
index 0000000..72c3731
--- /dev/null
@@ -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 (file)
index 0000000..07912a6
--- /dev/null
@@ -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 QPair<QString,QString>Dtk::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/dtkcore.pro b/dtkcore.pro
deleted file mode 100644 (file)
index 28a097f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-load(dtk_lib)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6a01ca4
--- /dev/null
@@ -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 (file)
index 0000000..9813915
--- /dev/null
@@ -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/dasync-example.pro b/examples/dasync-example/dasync-example.pro
deleted file mode 100644 (file)
index 71ee637..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-######################################################################
-# Automatically generated by qmake (3.1) Thu Aug 19 09:48:31 2021
-######################################################################
-
-TEMPLATE = app
-TARGET = thread_util
-INCLUDEPATH += .
-QT+= core widgets testlib
-
-CONFIG += c++11
-# The following define makes your compiler warn you if you use any
-# feature of Qt which has been marked as deprecated (the exact warnings
-# depend on your compiler). Please consult the documentation of the
-# deprecated API in order to know how to port your code away from it.
-DEFINES += QT_DEPRECATED_WARNINGS
-
-CONFIG(debug, debug|release) {
-    LIBS += -lgtest -lgmock
-    QMAKE_CXXFLAGS += -g -Wall -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
-    QMAKE_LFLAGS += -g -Wall -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
-    QMAKE_CXX += -g -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
-}
-
-LIBS += -pthread
-QMAKE_CXXFLAGS += -pthread
-
-#QMAKE_CXXFLAGS_RELEASE += -fvisibility=hidden
-#DEFINES += LIBDTKCORE_LIBRARY
-
-# You can also make your code fail to compile if you use deprecated APIs.
-# In order to do so, uncomment the following line.
-# You can also select to disable deprecated APIs only up to a certain version of Qt.
-#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
-
-INCLUDEPATH += $$PWD/../../src
-INCLUDEPATH += $$PWD/../../src/base
-INCLUDEPATH += $$PWD/../../src/util
-
-# Input
-HEADERS += \
-    $${PWD}/../../src/dtkcore_global.h \
-    $${PWD}/../../src/util/dasync.h \
-    $${PWD}/../../src/util/dthreadutils.h
-
-SOURCES += \
-    $${PWD}/../../src/util/dthreadutils.cpp \
-    main.cpp
-
index 0f89cf1efd9c80718dcb2cf28943e1f899c30d71..84c2a7d3e40ec6d27417896702cff5b006600211 100644 (file)
@@ -1,23 +1,7 @@
-/*
- * Copyright (C) 2021 ~ 2021 UnionTech Technology Co., Ltd.
- *
- * Author:     Wang Peng <993381@qq.com>
- *
- * Maintainer: Wang Peng <wangpenga@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include <QWidget>
 #include <QTimer>
 #include <iostream>
diff --git a/examples/examples.pro b/examples/examples.pro
deleted file mode 100644 (file)
index 2b68c88..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS += expintf-example
-SUBDIRS += dasync-example
-
diff --git a/examples/expintf-example/CMakeLists.txt b/examples/expintf-example/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9cdb1e4
--- /dev/null
@@ -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/expintf-example.pro b/examples/expintf-example/expintf-example.pro
deleted file mode 100644 (file)
index 5663a98..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-TEMPLATE = app
-QT += dbus
-
-SOURCES += \
-    $$PWD/main.cpp
-
-win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../src/release -ldtkcore
-else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../src/debug -ldtkcore
-else:unix: LIBS += -L$$OUT_PWD/../../src -ldtkcore
-
-INCLUDEPATH += $$PWD/../../src
-INCLUDEPATH += $$PWD/../../src/base
-
-CONFIG(debug, debug|release) {
-    unix:QMAKE_RPATHDIR += $$OUT_PWD/../../src
-}
index 5af9bb6fcd40a6ea8f915994f26317e79087cb3f..ef5558ca357911ab4f2d4eb8d324c6c14e48e2e8 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2019 Deepin Technology Co., Ltd.
- *
- * Author:     Chris Xiong <chirs241097@gmail.com>
- *
- * Maintainer: Chris Xiong <chirs241097@gmail.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "util/dexportedinterface.h"
 
diff --git a/include/DtkCore/DAbstractUnitFormatter b/include/DtkCore/DAbstractUnitFormatter
new file mode 100644 (file)
index 0000000..2d494ea
--- /dev/null
@@ -0,0 +1 @@
+#include "dabstractunitformatter.h"
diff --git a/include/DtkCore/DBaseFileWatcher b/include/DtkCore/DBaseFileWatcher
new file mode 100644 (file)
index 0000000..1348031
--- /dev/null
@@ -0,0 +1 @@
+#include "dbasefilewatcher.h"
diff --git a/include/DtkCore/DCapDir b/include/DtkCore/DCapDir
new file mode 100644 (file)
index 0000000..ee2c873
--- /dev/null
@@ -0,0 +1 @@
+#include "dcapfile.h"
diff --git a/include/DtkCore/DCapFile b/include/DtkCore/DCapFile
new file mode 100644 (file)
index 0000000..ee2c873
--- /dev/null
@@ -0,0 +1 @@
+#include "dcapfile.h"
diff --git a/include/DtkCore/DCapManager b/include/DtkCore/DCapManager
new file mode 100644 (file)
index 0000000..3a4f25b
--- /dev/null
@@ -0,0 +1 @@
+#include "dcapmanager.h"
diff --git a/include/DtkCore/DConfig b/include/DtkCore/DConfig
new file mode 100644 (file)
index 0000000..6157c75
--- /dev/null
@@ -0,0 +1 @@
+#include "dconfig.h"
diff --git a/include/DtkCore/DConfigFile b/include/DtkCore/DConfigFile
new file mode 100644 (file)
index 0000000..bb79fb3
--- /dev/null
@@ -0,0 +1 @@
+#include "dconfigfile.h"
diff --git a/include/DtkCore/DDBusExtended b/include/DtkCore/DDBusExtended
new file mode 100644 (file)
index 0000000..2798dc4
--- /dev/null
@@ -0,0 +1 @@
+#include "ddbusextended.h"
diff --git a/include/DtkCore/DDBusExtendedAbstractInterface b/include/DtkCore/DDBusExtendedAbstractInterface
new file mode 100644 (file)
index 0000000..b536959
--- /dev/null
@@ -0,0 +1 @@
+#include "ddbusextendedabstractinterface.h"
diff --git a/include/DtkCore/DDBusInterface b/include/DtkCore/DDBusInterface
new file mode 100644 (file)
index 0000000..c32d931
--- /dev/null
@@ -0,0 +1 @@
+#include "ddbusinterface.h"
diff --git a/include/DtkCore/DDBusSender b/include/DtkCore/DDBusSender
new file mode 100644 (file)
index 0000000..8881ba7
--- /dev/null
@@ -0,0 +1 @@
+#include "ddbussender.h"
diff --git a/include/DtkCore/DDciFile b/include/DtkCore/DDciFile
new file mode 100644 (file)
index 0000000..6ac5d91
--- /dev/null
@@ -0,0 +1 @@
+#include "ddcifile.h"
diff --git a/include/DtkCore/DDesktopEntry b/include/DtkCore/DDesktopEntry
new file mode 100644 (file)
index 0000000..847127a
--- /dev/null
@@ -0,0 +1 @@
+#include "ddesktopentry.h"
diff --git a/include/DtkCore/DDiskSizeFormatter b/include/DtkCore/DDiskSizeFormatter
new file mode 100644 (file)
index 0000000..6584e09
--- /dev/null
@@ -0,0 +1 @@
+#include "ddisksizeformatter.h"
diff --git a/include/DtkCore/DError b/include/DtkCore/DError
new file mode 100644 (file)
index 0000000..7988039
--- /dev/null
@@ -0,0 +1 @@
+#include "derror.h"
diff --git a/include/DtkCore/DExpected b/include/DtkCore/DExpected
new file mode 100644 (file)
index 0000000..e197682
--- /dev/null
@@ -0,0 +1 @@
+#include "dexpected.h"
diff --git a/include/DtkCore/DExportedInterface b/include/DtkCore/DExportedInterface
new file mode 100644 (file)
index 0000000..eea6f16
--- /dev/null
@@ -0,0 +1 @@
+#include "dexportedinterface.h"
diff --git a/include/DtkCore/DFileServices b/include/DtkCore/DFileServices
new file mode 100644 (file)
index 0000000..77b70de
--- /dev/null
@@ -0,0 +1 @@
+#include "dfileservices.h"
diff --git a/include/DtkCore/DFileSystemWatcher b/include/DtkCore/DFileSystemWatcher
new file mode 100644 (file)
index 0000000..97400f2
--- /dev/null
@@ -0,0 +1 @@
+#include "dfilesystemwatcher.h"
diff --git a/include/DtkCore/DFileWatcher b/include/DtkCore/DFileWatcher
new file mode 100644 (file)
index 0000000..768151a
--- /dev/null
@@ -0,0 +1 @@
+#include "dfilewatcher.h"
diff --git a/include/DtkCore/DFileWatcherManager b/include/DtkCore/DFileWatcherManager
new file mode 100644 (file)
index 0000000..a9f8682
--- /dev/null
@@ -0,0 +1 @@
+#include "dfilewatchermanager.h"
diff --git a/include/DtkCore/DLog b/include/DtkCore/DLog
new file mode 100644 (file)
index 0000000..a9ab429
--- /dev/null
@@ -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 (file)
index 0000000..85796fc
--- /dev/null
@@ -0,0 +1 @@
+#include "dnotifysender.h"
diff --git a/include/DtkCore/DObject b/include/DtkCore/DObject
new file mode 100644 (file)
index 0000000..75fbcb7
--- /dev/null
@@ -0,0 +1 @@
+#include "dobject.h"
diff --git a/include/DtkCore/DObjectPrivate b/include/DtkCore/DObjectPrivate
new file mode 100644 (file)
index 0000000..0c145c8
--- /dev/null
@@ -0,0 +1 @@
+#include "dobject_p.h"
diff --git a/include/DtkCore/DPathBuf b/include/DtkCore/DPathBuf
new file mode 100644 (file)
index 0000000..961c40b
--- /dev/null
@@ -0,0 +1 @@
+#include "dpathbuf.h"
diff --git a/include/DtkCore/DPinyin b/include/DtkCore/DPinyin
new file mode 100644 (file)
index 0000000..0df4d4c
--- /dev/null
@@ -0,0 +1 @@
+#include "dpinyin.h"
diff --git a/include/DtkCore/DRecentManager b/include/DtkCore/DRecentManager
new file mode 100644 (file)
index 0000000..a1be4b0
--- /dev/null
@@ -0,0 +1 @@
+#include "drecentmanager.h"
diff --git a/include/DtkCore/DSGApplication b/include/DtkCore/DSGApplication
new file mode 100644 (file)
index 0000000..a1c01bc
--- /dev/null
@@ -0,0 +1 @@
+#include "dsgapplication.h"
diff --git a/include/DtkCore/DSecureString b/include/DtkCore/DSecureString
new file mode 100644 (file)
index 0000000..83f6c1a
--- /dev/null
@@ -0,0 +1 @@
+#include "dsecurestring.h"
diff --git a/include/DtkCore/DSettings b/include/DtkCore/DSettings
new file mode 100644 (file)
index 0000000..572ed1b
--- /dev/null
@@ -0,0 +1 @@
+#include "dsettings.h"
diff --git a/include/DtkCore/DSettingsGroup b/include/DtkCore/DSettingsGroup
new file mode 100644 (file)
index 0000000..1c85d70
--- /dev/null
@@ -0,0 +1 @@
+#include "dsettingsgroup.h"
diff --git a/include/DtkCore/DSettingsOption b/include/DtkCore/DSettingsOption
new file mode 100644 (file)
index 0000000..9c180d5
--- /dev/null
@@ -0,0 +1 @@
+#include "dsettingsoption.h"
diff --git a/include/DtkCore/DSingleton b/include/DtkCore/DSingleton
new file mode 100644 (file)
index 0000000..280d2f9
--- /dev/null
@@ -0,0 +1 @@
+#include "dsingleton.h"
diff --git a/include/DtkCore/DStandardPaths b/include/DtkCore/DStandardPaths
new file mode 100644 (file)
index 0000000..9d0b963
--- /dev/null
@@ -0,0 +1 @@
+#include "dstandardpaths.h"
diff --git a/include/DtkCore/DSysInfo b/include/DtkCore/DSysInfo
new file mode 100644 (file)
index 0000000..c026f58
--- /dev/null
@@ -0,0 +1 @@
+#include "dsysinfo.h"
diff --git a/include/DtkCore/DThreadUtils b/include/DtkCore/DThreadUtils
new file mode 100644 (file)
index 0000000..13b83d8
--- /dev/null
@@ -0,0 +1 @@
+#include "dthreadutils.h"
diff --git a/include/DtkCore/DTimeUnitFormatter b/include/DtkCore/DTimeUnitFormatter
new file mode 100644 (file)
index 0000000..07d52a6
--- /dev/null
@@ -0,0 +1 @@
+#include "dtimeunitformatter.h"
diff --git a/include/DtkCore/DTrashManager b/include/DtkCore/DTrashManager
new file mode 100644 (file)
index 0000000..e763093
--- /dev/null
@@ -0,0 +1 @@
+#include "dtrashmanager.h"
diff --git a/include/DtkCore/DUtil b/include/DtkCore/DUtil
new file mode 100644 (file)
index 0000000..2dc0935
--- /dev/null
@@ -0,0 +1 @@
+#include "dutil.h"
diff --git a/include/DtkCore/DVtableHook b/include/DtkCore/DVtableHook
new file mode 100644 (file)
index 0000000..37dffef
--- /dev/null
@@ -0,0 +1 @@
+#include "dvtablehook.h"
diff --git a/include/DtkCore/DtkCores b/include/DtkCore/DtkCores
new file mode 100644 (file)
index 0000000..8a12540
--- /dev/null
@@ -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 (file)
index 0000000..5f58caf
--- /dev/null
@@ -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 <QString>
+#include <QDebug>
+
+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 (file)
index 0000000..a44514c
--- /dev/null
@@ -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 <cassert>
+#include <cstdlib>
+#include <exception>
+#include <initializer_list>
+#include <memory>
+#include <type_traits>
+
+#include "derror.h"
+
+DCORE_BEGIN_NAMESPACE
+
+#define likely(x) __builtin_expect(static_cast<long int>((x)), 1)
+#define unlikely(x) __builtin_expect(reinterpret_cast<long int>((x)), 0)
+
+#if __cpp_exceptions
+#define _DEXPECTED_THROW_OR_ABORT(_EXC) (throw(_EXC))
+#else
+#define _DEXPECTED_THROW_OR_ABORT(_EXC) (std::abort())
+#endif
+
+template <typename T, typename E>
+class DExpected;
+
+template <typename E>
+class DUnexpected;
+
+template <bool v>
+using _bool_constant = std::integral_constant<bool, v>;
+
+/*!
+ * @brief 原位构造标签
+ */
+enum class emplace_tag { USE_EMPLACE /**< 使用原位构造 */ };
+
+/*!
+ * @brief 从DUnexpected构造标签
+ */
+enum class dunexpected_tag { DUNEXPECTED /**< 从DUnexpected构造 */ };
+
+template <typename Type>
+struct remove_cvref
+{
+    using type = typename std::remove_cv<typename std::remove_reference<Type>::type>::type;
+};
+
+template <typename E>
+class bad_result_access;
+
+template <>
+class bad_result_access<void> : 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 <typename E>
+class bad_result_access : public bad_result_access<void>
+{
+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 <typename Type, typename... Args>
+auto construct_at(Type *p, Args &&...args) noexcept(noexcept(::new((void *)0) Type(std::declval<Args>()...)))
+    -> decltype(::new((void *)0) Type(std::declval<Args>()...))
+{
+    return ::new ((void *)p) Type(std::forward<Args>(args)...);
+}
+
+namespace __dexpected {
+template <typename ObjType>
+void destroy_at_obj(ObjType *p)
+{
+    p->~ObjType();
+}
+
+template <typename ArrType>
+void destroy_at_arr(ArrType *p)
+{
+    for (auto &elem : *p)
+        destroy_at_obj(std::addressof(elem));
+}
+}  // namespace __dexpected
+
+template <typename Type, typename std::enable_if<std::is_array<Type>::value, bool>::type = true>
+void destroy_at(Type *p)
+{
+    __dexpected::destroy_at_arr(p);
+}
+
+template <typename Type, typename std::enable_if<!std::is_array<Type>::value, bool>::type = true>
+void destroy_at(Type *p)
+{
+    __dexpected::destroy_at_obj(p);
+}
+
+namespace __dexpected {
+template <typename T>
+struct Guard
+{
+    static_assert(std::is_nothrow_move_constructible<T>::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 <typename T>
+struct _is_dexpected : public std::false_type
+{
+};
+
+template <typename T, typename E>
+struct _is_dexpected<DExpected<T, E>> : public std::true_type
+{
+};
+
+template <typename T>
+struct _is_dunexpected : public std::false_type
+{
+};
+
+template <typename T>
+struct _is_dunexpected<DUnexpected<T>> : public std::true_type
+{
+};
+
+template <typename E>
+constexpr bool _can_be_dunexpected()
+{
+    return std::is_object<E>::value and !std::is_array<E>::value and !_is_dunexpected<E>() and !std::is_const<E>::value and
+           !std::is_volatile<E>::value;
+}
+
+template <typename Tp,
+          typename Up,
+          typename Vp,
+          typename std::enable_if<std::is_nothrow_constructible<Tp, Vp>::value and std::is_nothrow_move_constructible<Tp>::value,
+                                  bool>::type = true>
+void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible<Tp, Vp>::value)
+{
+    destroy_at(_oldVal);
+    construct_at(_newVal, std::forward<Vp>(_arg));
+}
+
+template <typename Tp,
+          typename Up,
+          typename Vp,
+          typename std::enable_if<std::is_nothrow_constructible<Tp, Vp>::value and !std::is_nothrow_move_constructible<Tp>::value,
+                                  bool>::type = true>
+void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible<Tp, Vp>::value)
+{
+    destroy_at(_oldVal);
+    construct_at(_newVal, std::forward<Vp>(_arg));
+}
+
+template <typename Tp,
+          typename Up,
+          typename Vp,
+          typename std::enable_if<!std::is_nothrow_constructible<Tp, Vp>::value and std::is_nothrow_move_constructible<Tp>::value,
+                                  bool>::type = true>
+void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible<Tp, Vp>::value)
+{
+    Tp _tmp(std::forward<Vp>(_arg));
+    destroy_at(_oldVal);
+    construct_at(_newVal, std::move(_tmp));
+}
+
+template <
+    typename Tp,
+    typename Up,
+    typename Vp,
+    typename std::enable_if<!std::is_nothrow_constructible<Tp, Vp>::value and !std::is_nothrow_move_constructible<Tp>::value,
+                            bool>::type = true>
+void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible<Tp, Vp>::value)
+{
+    __dexpected::Guard<Up> _guard(*_oldVal);
+    construct_at(_newVal, std::forward<Vp>(_arg));
+    _guard.release();
+}
+
+}  // namespace __dexpected
+
+/*!
+ * @brief
+ * 类模板Dtk::Core::DUnexpected代表一个Dtk::Core::DExpected中存储的不期待的值
+ * @tparam E
+ * 不期待的值的类型,该类型不能是非对象类型,数组类型,Dtk::Core::DUnexpected的特化类型或有cv限定符的类型
+ */
+template <typename E = DError>
+class DUnexpected
+{
+    static_assert(__dexpected::_can_be_dunexpected<E>(), "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 <typename Er = E,
+              typename std::enable_if<!std::is_same<typename remove_cvref<Er>::type, DUnexpected>::value and
+                                          !std::is_same<typename remove_cvref<Er>::type, emplace_tag>::value and
+                                          std::is_constructible<E, Er>::value,
+                                      bool>::type = true>
+    constexpr explicit DUnexpected(Er &&_e) noexcept(std::is_nothrow_constructible<E, Er>::value)
+        : m_error(std::forward<Er>(_e))
+    {
+    }
+
+    /*!
+     * @brief
+     * 直接从参数构造出一个包含错误类型为E的对象的Dtk::Core::DUnexpected对象
+     * @tparam Args 可变参数模板类型,这里是构造类型为E的对象所需要的参数的类型
+     * @param[in] args 构造类型为E的对象用到的参数
+     * @attention
+     * 为了区分是构造E还是Dtk::Core::DUnexpected,需要在第一个参数使用emplace_tag进行标识
+     */
+    template <typename... Args>
+    constexpr explicit DUnexpected(emplace_tag, Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value)
+        : m_error(std::forward<Args>(args)...)
+    {
+        static_assert(std::is_constructible<E, Args...>::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 <typename U, typename... Args>
+    constexpr explicit DUnexpected(emplace_tag, std::initializer_list<U> _li, Args &&...args) noexcept(
+        std::is_nothrow_constructible<E, std::initializer_list<U> &, Args...>::value)
+        : m_error(_li, std::forward<Args>(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 <typename Er>
+    friend constexpr bool operator==(const DUnexpected &_x, const DUnexpected<Er> _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 <typename T, typename E = DError>
+class DExpected
+{
+    template <typename, typename>
+    friend class DExpected;
+    static_assert(!std::is_reference<T>::value, "type T can't be reference type");
+    static_assert(!std::is_function<T>::value, "type T can't be function type");
+    static_assert(!std::is_same<typename std::remove_cv<T>::type, dunexpected_tag>::value, "type T can't be dunexpected_tag");
+    static_assert(!std::is_same<typename std::remove_cv<T>::type, emplace_tag>::value, "type T can't be emplace_tag");
+    static_assert(!__dexpected::_is_dunexpected<typename std::remove_cv<T>::type>::value, "type T can't be DUnexpected");
+    static_assert(__dexpected::_can_be_dunexpected<E>(), "type E can't be dunexpected");
+
+    template <typename U, typename G, typename Unex = DUnexpected<E>>
+    static constexpr bool __cons_from_DExpected()
+    {
+        return std::is_constructible<T, DExpected<U, G> &>::value or std::is_constructible<T, DExpected<U, G>>::value or
+               std::is_constructible<T, const DExpected<U, G>>::value or
+               std::is_constructible<T, const DExpected<U, G> &>::value or std::is_convertible<DExpected<U, G> &, T>::value or
+               std::is_convertible<DExpected<U, G>, T>::value or std::is_convertible<const DExpected<U, G> &, T>::value or
+               std::is_convertible<const DExpected<U, G>, T>::value or std::is_constructible<Unex, DExpected<U, G> &>::value or
+               std::is_constructible<Unex, DExpected<U, G>>::value or
+               std::is_constructible<Unex, const DExpected<U, G> &>::value or
+               std::is_constructible<Unex, const DExpected<U, G>>::value;
+    }
+
+    template <typename U, typename G>
+    static constexpr bool __explicit_conv()
+    {
+        return !std::is_convertible<U, T>::value or !std::is_convertible<G, E>::value;
+    }
+
+    static constexpr bool des_value()
+    {
+        return !std::is_trivially_destructible<T>::value or !std::is_trivially_destructible<E>::value;
+    }
+
+    template <typename V>
+    void assign_val(V &&_v)
+    {
+        if (m_has_value) {
+            m_value = std::forward<V>(_v);
+        } else {
+            __dexpected::reinit(std::addressof(m_value), std::addressof(m_error), std::forward<V>(_v));
+            m_has_value = true;
+        }
+    }
+
+    template <typename V>
+    void assign_err(V &&_v)
+    {
+        if (m_has_value) {
+            __dexpected::reinit(std::addressof(m_error), std::addressof(m_value), std::forward<V>(_v));
+            m_has_value = false;
+        } else {
+            m_error = std::forward<V>(_v);
+        }
+    }
+
+    template <typename Ep = E, typename std::enable_if<std::is_nothrow_move_constructible<Ep>::value, bool>::type = true>
+    void swap_val_err(DExpected &_other) noexcept(
+        std::is_nothrow_move_constructible<Ep>::value and std::is_nothrow_move_constructible<T>::value)
+    {
+        __dexpected::Guard<E> _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 <typename Ep = E, typename std::enable_if<!std::is_nothrow_move_constructible<Ep>::value, bool>::type = true>
+    void swap_val_err(DExpected &_other) noexcept(
+        std::is_nothrow_move_constructible<Ep>::value and std::is_nothrow_move_constructible<T>::value)
+    {
+        __dexpected::Guard<T> _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<E>;
+    template <typename U>
+    using rebind = DExpected<U, error_type>;
+
+    /*!
+     * @brief Dtk::Core::DExpected的默认构造函数
+     */
+    template <typename std::enable_if<std::is_default_constructible<T>::value, bool>::type = true>
+    constexpr DExpected() noexcept(std::is_nothrow_default_constructible<T>::value)
+        : m_has_value(true)
+        , m_value()
+    {
+    }
+
+    /*!
+     * @brief Dtk::Core::DExpected的默认拷贝构造函数
+     */
+    DExpected(const DExpected &) = default;
+
+    /*!
+     * @brief Dtk::Core::DExpected的拷贝构造函数
+     */
+    template <typename std::enable_if<std::is_copy_constructible<T>::value and std::is_copy_constructible<E>::value and
+                                          (!std::is_trivially_copy_constructible<T>::value or
+                                           !std::is_trivially_copy_constructible<E>::value),
+                                      bool>::type = true>
+    DExpected(const DExpected &_x) noexcept(
+        std::is_nothrow_copy_constructible<T>::value and std::is_nothrow_copy_constructible<E>::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 <typename std::enable_if<std::is_move_constructible<T>::value and std::is_move_constructible<E>::value and
+                                          (!std::is_trivially_move_constructible<T>::value or
+                                           !std::is_trivially_move_constructible<E>::value),
+                                      bool>::type = true>
+    DExpected(DExpected &&_x) noexcept(
+        std::is_nothrow_move_constructible<T>::value and std::is_nothrow_move_constructible<E>::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<std::is_constructible<T, const U &>::value and std::is_constructible<E, const G &>::value and
+                                    !__cons_from_DExpected<U, G>() and !__explicit_conv<const U &, const G &>(),
+                                bool>::type = true>
+    DExpected(const DExpected<U, G> &_x) noexcept(
+        std::is_nothrow_constructible<T, const U &>::value and std::is_nothrow_constructible<E, const G &>::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<std::is_constructible<T, const U &>::value and std::is_constructible<E, const G &>::value and
+                                    !__cons_from_DExpected<U, G>() and __explicit_conv<const U &, const G &>(),
+                                bool>::type = true>
+    explicit DExpected(const DExpected<U, G> &_x) noexcept(
+        std::is_nothrow_constructible<T, const U &>::value and std::is_nothrow_constructible<E, const G &>::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 <typename U,
+              typename G,
+              typename std::enable_if<std::is_constructible<T, U>::value and std::is_constructible<E, G>::value and
+                                          !__cons_from_DExpected<U, G>() and !__explicit_conv<U, G>(),
+                                      bool>::type = true>
+    DExpected(DExpected<U, G> &&_x) noexcept(
+        std::is_nothrow_constructible<T, U>::value and std::is_nothrow_constructible<E, G>::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 <typename U,
+              typename G,
+              typename std::enable_if<std::is_constructible<T, U>::value and std::is_constructible<E, G>::value and
+                                          !__cons_from_DExpected<U, G>() and __explicit_conv<U, G>(),
+                                      bool>::type = true>
+    explicit DExpected(DExpected<U, G> &&_x) noexcept(
+        std::is_nothrow_constructible<T, U>::value and std::is_nothrow_constructible<E, G>::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 <typename U = T,
+              typename std::enable_if<!std::is_same<typename remove_cvref<U>::type, DExpected>::value and
+                                          !std::is_same<typename remove_cvref<U>::type, emplace_tag>::value and
+                                          !__dexpected::_is_dunexpected<typename remove_cvref<U>::type>::value and
+                                          std::is_constructible<T, U>::value and !std::is_convertible<U, T>::value,
+                                      bool>::type = true>
+    constexpr explicit DExpected(U &&_v) noexcept(std::is_nothrow_constructible<T, U>::value)
+        : m_has_value(true)
+        , m_value(std::forward<U>(_v))
+
+    {
+    }
+
+    /*!
+     * @brief
+     * Dtk::Core::DExpected的移动构造函数,直接从期待类型构造出Dtk::Core::DExpected对象
+     * @tparam U Dtk::Core::DExpected的期待类型,默认为类型T
+     * @param[in] _v 期待类型为U的对象
+     * @attention 构造后原对象不可用
+     */
+    template <typename U = T,
+              typename std::enable_if<!std::is_same<typename remove_cvref<U>::type, DExpected>::value and
+                                          !std::is_same<typename remove_cvref<U>::type, emplace_tag>::value and
+                                          !__dexpected::_is_dunexpected<typename remove_cvref<U>::type>::value and
+                                          std::is_constructible<T, U>::value and std::is_convertible<U, T>::value,
+                                      bool>::type = true>
+    constexpr DExpected(U &&_v) noexcept(std::is_nothrow_constructible<T, U>::value)
+        : m_has_value(true)
+        , m_value(std::forward<U>(_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 <typename G = E,
+              typename std::enable_if<std::is_constructible<E, const G &>::value and !std::is_convertible<const G &, E>::value,
+                                      bool>::type = true>
+    constexpr explicit DExpected(const DUnexpected<G> &_u) noexcept(std::is_nothrow_constructible<E, const G &>::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 <typename G = E,
+              typename std::enable_if<std::is_constructible<E, const G &>::value and std::is_convertible<const G &, E>::value,
+                                      bool>::type = true>
+    constexpr DExpected(const DUnexpected<G> &_u) noexcept(std::is_nothrow_constructible<E, const G &>::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<std::is_constructible<E, G>::value and !std::is_convertible<G, E>::value, bool>::type = true>
+    constexpr explicit DExpected(DUnexpected<G> &&_u) noexcept(std::is_nothrow_constructible<E, G>::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 <typename G = E,
+              typename std::enable_if<std::is_constructible<E, G>::value and std::is_convertible<G, E>::value, bool>::type = true>
+    constexpr DExpected(DUnexpected<G> &&_u) noexcept(std::is_nothrow_constructible<E, G>::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 <typename... Args>
+    constexpr explicit DExpected(emplace_tag, Args &&...args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
+        : m_has_value(true)
+        , m_value(std::forward<Args>(args)...)
+
+    {
+        static_assert(std::is_constructible<T, Args...>::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 <typename U, typename... Args>
+    constexpr explicit DExpected(emplace_tag, std::initializer_list<U> _li, Args &&...args) noexcept(
+        std::is_nothrow_constructible<T, std::initializer_list<U> &, Args...>::value)
+        : m_has_value(true)
+        , m_value(_li, std::forward<Args>(args)...)
+    {
+        static_assert(std::is_constructible<T, std::initializer_list<U> &, 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 <typename... Args>
+    constexpr explicit DExpected(dunexpected_tag, Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value)
+        : m_has_value(false)
+        , m_error(std::forward<Args>(args)...)
+
+    {
+        static_assert(std::is_constructible<E, Args...>::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 <typename U, typename... Args>
+    constexpr explicit DExpected(dunexpected_tag, std::initializer_list<U> _li, Args &&...args) noexcept(
+        std::is_nothrow_constructible<E, std::initializer_list<U> &, Args...>::value)
+        : m_has_value(false)
+        , m_error(_li, std::forward<Args>(args)...)
+    {
+        static_assert(std::is_constructible<E, std::initializer_list<U> &, 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 <typename std::enable_if<std::is_copy_assignable<T>::value and std::is_copy_constructible<T>::value and
+                                          std::is_copy_assignable<E>::value and std::is_copy_constructible<E>::value and
+                                          (std::is_nothrow_move_constructible<T>::value or
+                                           std::is_nothrow_move_constructible<E>::value),
+                                      bool>::type = true>
+    DExpected &operator=(const DExpected &_x) noexcept(
+        std::is_nothrow_copy_constructible<T>::value and std::is_nothrow_copy_constructible<E>::value
+            and std::is_nothrow_copy_assignable<T>::value and std::is_nothrow_copy_assignable<E>::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 <typename std::enable_if<std::is_move_assignable<T>::value and std::is_move_constructible<T>::value and
+                                          std::is_move_assignable<E>::value and std::is_move_constructible<E>::value and
+                                          (std::is_nothrow_move_constructible<T>::value or
+                                           std::is_nothrow_move_constructible<E>::value),
+                                      bool>::type = true>
+    DExpected &operator=(DExpected &&_x) noexcept(
+        std::is_nothrow_move_constructible<T>::value and std::is_nothrow_move_constructible<E>::value
+            and std::is_nothrow_move_assignable<T>::value and std::is_nothrow_move_assignable<E>::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<!std::is_same<DExpected, typename remove_cvref<U>::type>::value and
+                                    !__dexpected::_is_dunexpected<typename remove_cvref<U>::type>::value and
+                                    std::is_constructible<T, U>::value and std::is_assignable<T &, U>::value and
+                                    (std::is_nothrow_constructible<T, U>::value or std::is_nothrow_move_constructible<T>::value or
+                                     std::is_nothrow_move_constructible<E>::value),
+                                bool>::type = true>
+    DExpected &operator=(U &&_v)
+    {
+        assign_val(std::forward<U>(_v));
+        return *this;
+    }
+
+    /*!
+     * @brief Dtk::Core::DExpected的拷贝赋值运算符
+     * @tparam G 不期待类型
+     * @param[in] _e 模板类型为G的Dtk::Core::DUnexpected对象
+     */
+    template <typename G,
+              typename std::enable_if<std::is_constructible<E, const G &>::value and std::is_assignable<E &, const G &>::value and
+                                          (std::is_nothrow_constructible<E, const G &>::value or
+                                           std::is_nothrow_move_constructible<T>::value or std::is_move_constructible<E>::value),
+                                      bool>::type = true>
+    DExpected &operator=(const DUnexpected<G> &_e)
+    {
+        assign_err(_e.error());
+        return *this;
+    }
+
+    /*!
+     * @brief Dtk::Core::DExpected的移动赋值运算符
+     * @tparam G 不期待类型
+     * @param[in] _e 模板类型为G的Dtk::Core::DUnexpected对象
+     * @attention 赋值后原对象不可用
+     */
+    template <typename G,
+              typename std::enable_if<std::is_constructible<E, G>::value and std::is_assignable<E &, G>::value and
+                                          (std::is_nothrow_constructible<E, G>::value or
+                                           std::is_nothrow_move_constructible<T>::value or std::is_move_constructible<E>::value),
+                                      bool>::type = true>
+    DExpected &operator=(DUnexpected<G> &&_e)
+    {
+        assign_err(std::move(_e).error());
+        return *this;
+    }
+
+    /*!
+     * @brief 从参数直接转发构造期待值
+     * @tparam Args 构造期待值所用到的参数的类型
+     * @param[in] args 构造期待值所用到的参数
+     * @return 返回构造好的期待值的引用
+     */
+    template <typename... Args>
+    T &emplace(Args &&...args) noexcept
+    {
+        static_assert(std::is_nothrow_constructible<T, Args...>::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>(args)...);
+        return m_value;
+    }
+
+    /*!
+     * @brief 从参数直接转发构造期待值
+     * @tparam U 初始化列表的模板参数
+     * @tparam Args 构造期待值所用到的参数的类型
+     * @param[in] args 构造期待值所用到的参数
+     * @param[in] li 构造期待值所用到的参数化列表
+     * @return 返回构造好的期待值的引用
+     */
+    template <typename U, typename... Args>
+    T &emplace(std::initializer_list<U> li, Args &&...args) noexcept
+    {
+        static_assert(std::is_nothrow_constructible<T, std::initializer_list<U> &, 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>(args)...);
+        return m_value;
+    }
+
+    // TODO:需要swap吗?
+    /*!
+     * @brief 交换两个Dtk::Core::DExpected的值
+     * @param[in] _x 另一个Dtk::Core::DExpected对象
+     */
+    template <typename std::enable_if<std::is_move_constructible<T>::value and std::is_move_constructible<E>::value and
+                                          (std::is_nothrow_move_constructible<T>::value or
+                                           std::is_nothrow_move_constructible<E>::value),
+                                      bool>::type = true>
+    void
+    swap(DExpected &_x) noexcept(std::is_nothrow_move_constructible<T>::value and std::is_nothrow_move_constructible<E>::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<E>(m_error));
+    }
+
+    /*!
+     * @brief 获取Dtk::Core::DExpected的期待值
+     * @return 期待值的左值引用
+     */
+    T &value() &
+    {
+        if (likely(m_has_value)) {
+            return m_value;
+        }
+        _DEXPECTED_THROW_OR_ABORT(bad_result_access<E>(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<E>(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<E>(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 <typename U>
+    T value_or(U &&_v) const &
+    {
+        static_assert(std::is_copy_constructible<T>::value, "type T should have an copy constructor.");
+        static_assert(std::is_convertible<U, T>::value, "type U must can be converted to T.");
+        if (m_has_value)
+            return m_value;
+        return static_cast<T>(std::forward<U>(_v));
+    }
+
+    /*!
+     * @brief 如果有期待值返回期待值,否则返回传入的默认值
+     * @tparam U 期待值的类型
+     * @param[in] _v 默认的期待值
+     * @return 期待值
+     * @attention 如果由期待值,调用后原期待值不可用,同时类型U要可以转换成类型T
+     */
+    template <typename U>
+    T value_or(U &&_v) &&
+    {
+        static_assert(std::is_move_constructible<T>::value, "type T must bu copy_constructible.");
+        static_assert(std::is_convertible<U, T>::value, "type U must can be converted to T.");
+        if (m_has_value)
+            return std::move(m_value);
+        return static_cast<T>(std::forward<U>(_v));
+    }
+
+    /*!
+     *@brief 重载相等运算符
+     */
+    template <typename U, typename E2, typename std::enable_if<!std::is_void<U>::value, bool>::type = true>
+    friend bool
+    operator==(const DExpected &_x,
+               const DExpected<U, E2> &_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 <typename U>
+    friend constexpr bool operator==(const DExpected &_x, const U &_v) noexcept(noexcept(bool(*_x == _v)))
+    {
+        return _x.hasValue() && bool(*_x == _v);
+    }
+
+    /*!
+     *@brief 重载相等运算符
+     */
+    template <typename E2>
+    friend constexpr bool operator==(const DExpected &_x,
+                                     const DUnexpected<E2> &_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 <typename E>
+class DExpected<void, E>
+{
+    static_assert(__dexpected::_can_be_dunexpected<E>(), "type E can't be DUnexpected.");
+    static constexpr bool des_value() { return !std::is_trivially_destructible<E>::value; }
+
+    template <typename, typename>
+    friend class DExpected;
+
+    template <typename U, typename G, typename Unex = DUnexpected<E>>
+    static constexpr bool __cons_from_DExpected()
+    {
+        return std::is_constructible<Unex, DExpected<U, G> &>::value and std::is_constructible<Unex, DExpected<U, G>>::value and
+               std::is_constructible<Unex, const DExpected<U, G> &>::value and
+               std::is_constructible<Unex, const DExpected<U, G>>::value;
+    }
+
+    template <typename V>
+    void assign_err(V &&_v)
+    {
+        if (m_has_value) {
+            construct_at(std::addressof(m_error), std::forward<V>(_v));
+            m_has_value = false;
+        } else {
+            m_error = std::forward<V>(_v);
+        }
+    }
+
+public:
+    using value_type = void;
+    using error_type = E;
+    using dunexpected_type = DUnexpected<E>;
+    template <typename U>
+    using rebind = DExpected<U, error_type>;
+
+    constexpr DExpected() noexcept
+        : m_has_value(true)
+        , m_void()
+    {
+    }
+
+    DExpected(const DExpected &) = default;
+
+    template <typename std::enable_if<std::is_copy_constructible<E>::value and !std::is_trivially_copy_constructible<E>::value,
+                                      bool>::type = true>
+    DExpected(const DExpected &_x) noexcept(std::is_nothrow_copy_constructible<E>::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 <typename std::enable_if<std::is_move_constructible<E>::value and !std::is_trivially_move_constructible<E>::value,
+                                      bool>::type = true>
+    DExpected(DExpected &&_x) noexcept(std::is_nothrow_move_constructible<E>::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 <typename U,
+              typename G,
+              typename std::enable_if<std::is_void<U>::value and std::is_constructible<E, const G &>::value and
+                                          !__cons_from_DExpected<U, G>() and !std::is_convertible<const G &, E>::value,
+                                      bool>::type = true>
+    explicit DExpected(const DExpected<U, G> &_x) noexcept(std::is_nothrow_constructible<E, const G &>::value)
+        : m_has_value(_x.m_has_value)
+        , m_void()
+    {
+        if (!m_has_value)
+            construct_at(std::addressof(m_error), _x.m_error);
+    }
+
+    template <typename U,
+              typename G,
+              typename std::enable_if<std::is_void<U>::value and std::is_constructible<E, const G &>::value and
+                                          !__cons_from_DExpected<U, G>() and std::is_convertible<const G &, E>::value,
+                                      bool>::type = true>
+    DExpected(const DExpected<U, G> &_x) noexcept(std::is_nothrow_constructible<E, const G &>::value)
+        : m_has_value(_x.m_has_value)
+        , m_void()
+    {
+        if (!m_has_value)
+            construct_at(std::addressof(m_error), _x.m_error);
+    }
+
+    template <typename U,
+              typename G,
+              typename std::enable_if<std::is_void<U>::value and std::is_constructible<E, G>::value and
+                                          __cons_from_DExpected<U, G>() and !std::is_convertible<G, E>::value,
+                                      bool>::type = true>
+    explicit DExpected(DExpected<U, G> &&_x) noexcept(std::is_nothrow_constructible<E, G>::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 <typename U,
+              typename G,
+              typename std::enable_if<std::is_void<U>::value and std::is_constructible<E, G>::value and
+                                          __cons_from_DExpected<U, G>() and std::is_convertible<G, E>::value,
+                                      bool>::type = true>
+    DExpected(DExpected<U, G> &&_x) noexcept(std::is_nothrow_constructible<E, G>::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 <typename G = E,
+              typename std::enable_if<std::is_constructible<E, const G &>::value and !std::is_convertible<const G &, E>::value,
+                                      bool>::type = true>
+    constexpr explicit DExpected(const DUnexpected<G> &_u) noexcept(std::is_nothrow_constructible<E, const G &>::value)
+        : m_has_value(false)
+        , m_error(_u.error())
+    {
+    }
+
+    template <typename G = E,
+              typename std::enable_if<std::is_constructible<E, const G &>::value and std::is_convertible<const G &, E>::value,
+                                      bool>::type = true>
+    constexpr DExpected(const DUnexpected<G> &_u) noexcept(std::is_nothrow_constructible<E, const G &>::value)
+        : m_has_value(false)
+        , m_error(_u.error())
+    {
+    }
+
+    template <
+        typename G = E,
+        typename std::enable_if<std::is_constructible<E, G>::value and !std::is_convertible<G, E>::value, bool>::type = true>
+    constexpr explicit DExpected(DUnexpected<G> &&_u) noexcept(std::is_nothrow_constructible<E, G>::value)
+        : m_has_value(false)
+        , m_error(std::move(_u).error())
+    {
+    }
+
+    template <typename G = E,
+              typename std::enable_if<std::is_constructible<E, G>::value and std::is_convertible<G, E>::value, bool>::type = true>
+    constexpr DExpected(DUnexpected<G> &&_u) noexcept(std::is_nothrow_constructible<E, G>::value)
+        : m_has_value(false)
+        , m_error(std::move(_u).error())
+    {
+    }
+
+    template <typename... Args>
+    constexpr explicit DExpected(emplace_tag) noexcept
+        : DExpected()
+    {
+    }
+
+    template <typename... Args>
+    constexpr explicit DExpected(dunexpected_tag, Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value)
+        : m_has_value(false)
+        , m_error(std::forward<Args>(args)...)
+    {
+        static_assert(std::is_constructible<E, Args...>::value, "type E can't construct from args");
+    }
+
+    template <typename U, typename... Args>
+    constexpr explicit DExpected(dunexpected_tag,
+                                 std::initializer_list<U> _li,
+                                 Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value)
+        : m_has_value(false)
+        , m_error(_li, std::forward<Args>(args)...)
+    {
+        static_assert(std::is_constructible<E, std::initializer_list<U> &, 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<std::is_copy_constructible<E>::value and std::is_copy_assignable<E>::value, bool>::type = true>
+    DExpected &operator=(const DExpected &_x) noexcept(
+        std::is_nothrow_copy_constructible<E>::value and std::is_nothrow_copy_assignable<E>::value)
+    {
+        if (_x.m_has_value)
+            emplace();
+        else
+            assign_err(_x.m_error);
+        return *this;
+    }
+
+    template <
+        typename std::enable_if<std::is_move_constructible<E>::value and std::is_move_assignable<E>::value, bool>::type = true>
+    DExpected &
+    operator=(DExpected &&_x) noexcept(std::is_nothrow_move_constructible<E>::value and std::is_nothrow_move_assignable<E>::value)
+    {
+        if (_x.m_has_value)
+            emplace();
+        else
+            assign_err(std::move(_x.m_error));
+        return *this;
+    }
+
+    template <typename G,
+              typename std::enable_if<std::is_constructible<E, const G &>::value and std::is_assignable<E &, const G &>::value,
+                                      bool>::type = true>
+    DExpected &operator=(const DUnexpected<G> &_e)
+    {
+        assign_err(_e.error());
+        return *this;
+    }
+
+    template <
+        typename G,
+        typename std::enable_if<std::is_constructible<E, G>::value and std::is_assignable<E &, G>::value, bool>::type = true>
+    DExpected &operator=(DUnexpected<G> &&_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 <typename std::enable_if<std::is_move_constructible<E>::value, bool>::type = true>
+    void swap(DExpected &_x) noexcept(std::is_nothrow_move_constructible<E>::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<E>(m_error));
+    }
+
+    void value() &&
+    {
+        if (likely(m_has_value))
+            return;
+        _DEXPECTED_THROW_OR_ABORT(bad_result_access<E>(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 <typename U, typename E2, typename std::enable_if<std::is_void<U>::value, bool>::type = true>
+    friend bool operator==(const DExpected &_x, const DExpected<U, E2> &_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 <typename E2>
+    friend bool operator==(const DExpected &_x, const DUnexpected<E2> &_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 (file)
index 0000000..4e2c020
--- /dev/null
@@ -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 <QScopedPointer>
+
+#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<DObjectPrivate> 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 (file)
index 0000000..e8a9cbd
--- /dev/null
@@ -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<ExampleSingleton>
+   {
+       Q_OBJECT
+       friend class DSingleton<ExampleSingleton>;
+   };
+   \endcode
+
+  \note: for Qt, "public DSingleton<ExampleSingleton>" must be after QObject.
+ */
+
+/*!
+  通过c++11的特性实现的单例模板
+  
+  使用示例:
+
+```
+   class ExampleSingleton : public QObject, public Dtk::DSingleton<ExampleSingleton>
+   {
+       Q_OBJECT
+       friend class DSingleton<ExampleSingleton>;
+   };
+```
+
+  \note 对于Qt程序 public DSingleton<ExampleSingleton>" 必须在卸载QObject后面出现。
+ */
+
+template <class T>
+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 (file)
index 0000000..7b26d0f
--- /dev/null
@@ -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 (file)
index 0000000..09c2d45
--- /dev/null
@@ -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 <DObject>
+#include <dtkcore_global.h>
+#else
+#define DCORE_BEGIN_NAMESPACE
+#define DCORE_END_NAMESPACE
+#define LIBDTKCORESHARED_EXPORT
+#define D_DECLARE_PRIVATE(Class) Class##Private *d;
+#endif
+
+#include <QStringList>
+
+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 (file)
index 0000000..51028f6
--- /dev/null
@@ -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 <QObject>
+
+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 (file)
index 0000000..826391e
--- /dev/null
@@ -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 <dtkcore_global.h>
+
+#include <DObject>
+#include <QDir>
+#include <QFile>
+
+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<DCapDirPrivate> 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 (file)
index 0000000..35b206e
--- /dev/null
@@ -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 <DObject>
+
+#include <QObject>
+
+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 (file)
index 0000000..923b760
--- /dev/null
@@ -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 <QObject>
+
+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 (file)
index 0000000..c5b3d30
--- /dev/null
@@ -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 (file)
index 0000000..355564b
--- /dev/null
@@ -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 <QObject>
+
+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<DFileWatcherManagerPrivate> 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 (file)
index 0000000..bfea79c
--- /dev/null
@@ -0,0 +1,55 @@
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QDir>
+
+#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 (file)
index 0000000..f2f1f34
--- /dev/null
@@ -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 <QStandardPaths>
+
+#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 (file)
index 0000000..72382bc
--- /dev/null
@@ -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 <dtkcore_global.h>
+#include <DObject>
+
+#include <QObject>
+
+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 (file)
index 0000000..dc405ae
--- /dev/null
@@ -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 <dtkcore_global.h>
+#include <DObject>
+
+#include <QObject>
+#include <QVariant>
+
+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 (file)
index 0000000..cd4fd8c
--- /dev/null
@@ -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 <dtkcore_global.h>
+#include <DObject>
+#include <QStringList>
+#include <QFlags>
+#include <QVariant>
+#include <QLocale>
+#include <QJsonDocument>
+#include <QDebug>
+
+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<QIODevice*> &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<QIODevice*> &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 (file)
index 0000000..877bdfd
--- /dev/null
@@ -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 <QIODevice>
+#include <QObject>
+#include <QVariant>
+
+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 &section = "Desktop Entry") const;
+    QStringList allGroups(bool sorted = false) const;
+
+    bool contains(const QString &key, const QString &section = "Desktop Entry") const;
+
+    QString name() const;
+    QString genericName() const;
+    QString ddeDisplayName() const;
+    QString comment() const;
+
+    QString rawValue(const QString &key, const QString &section = "Desktop Entry",
+                     const QString &defaultValue = QString()) const;
+    QString stringValue(const QString &key, const QString &section = "Desktop Entry",
+                        const QString &defaultValue = QString()) const;
+    QString localizedValue(const QString &key, const QString &localeKey = "default",
+                           const QString &section = "Desktop Entry", const QString& defaultValue = QString()) const;
+    QString localizedValue(const QString &key, const QLocale &locale,
+                           const QString &section = "Desktop Entry", const QString& defaultValue = QString()) const;
+    QStringList stringListValue(const QString &key, const QString &section = "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 &section = "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<DDesktopEntryPrivate> 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 (file)
index 0000000..a2f146b
--- /dev/null
@@ -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 <QString>
+
+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 (file)
index 0000000..147fa35
--- /dev/null
@@ -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 (file)
index 0000000..807f111
--- /dev/null
@@ -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 <dtkcore_global.h>
+
+#include <QLocale>
+
+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<QString, QString> distributionOrgWebsite(OrgType type = Distribution);
+    Q_DECL_DEPRECATED_X("Use deepinDistributionOrgWebsite() instead") static QPair<QString, QString> 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 (file)
index 0000000..c3efe0c
--- /dev/null
@@ -0,0 +1,60 @@
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QtCore/qglobal.h>
+#include <dtkcore_config.h>
+
+#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 (file)
index 0000000..9435da3
--- /dev/null
@@ -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 <Logger.h>
+
+#include <QMutex>
+
+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 (file)
index 0000000..3172c8b
--- /dev/null
@@ -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 <QReadWriteLock>
+#include <QDateTime>
+
+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 (file)
index 0000000..be63061
--- /dev/null
@@ -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 <AbstractStringAppender.h>
+
+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 (file)
index 0000000..9f5e378
--- /dev/null
@@ -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 <AbstractStringAppender.h>
+
+// Qt
+#include <QFile>
+#include <QTextStream>
+
+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 (file)
index 0000000..8e761f1
--- /dev/null
@@ -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 <QtCore>
+
+#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 (file)
index 0000000..d69557e
--- /dev/null
@@ -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 <QString>
+#include <QDebug>
+#include <QDateTime>
+
+#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 (file)
index 0000000..534c572
--- /dev/null
@@ -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 <QDateTime>
+
+#include <FileAppender.h>
+
+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 (file)
index 0000000..8395103
--- /dev/null
@@ -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 (file)
index 0000000..73bb115
--- /dev/null
@@ -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 <AbstractStringAppender.h>
+
+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 (file)
index 0000000..c3770da
--- /dev/null
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QObject>
+#include <QScopedPointer>
+
+#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<DSettingsDConfigBackendPrivate> 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 (file)
index 0000000..184ad5e
--- /dev/null
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QObject>
+#include <QScopedPointer>
+
+#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<GSettingsBackendPrivate> 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 (file)
index 0000000..496838f
--- /dev/null
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QObject>
+#include <QScopedPointer>
+
+#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<QSettingBackendPrivate> 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 (file)
index 0000000..d407c13
--- /dev/null
@@ -0,0 +1,63 @@
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QObject>
+#include <QPointer>
+#include <QScopedPointer>
+
+#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<DSettings> fromJson(const QByteArray &json);
+    static QPointer<DSettings> fromJsonFile(const QString &filepath);
+    QJsonObject meta() const;
+
+    QStringList keys() const;
+    QList<QPointer<DSettingsOption>> options() const;
+    QPointer<DSettingsOption> option(const QString &key) const;
+    QVariant value(const QString &key) const;
+
+    QStringList groupKeys() const;
+    QList<QPointer<DSettingsGroup>> groups() const;
+    QPointer<DSettingsGroup> 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<DSettingsPrivate> 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 (file)
index 0000000..1cf1673
--- /dev/null
@@ -0,0 +1,43 @@
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QObject>
+#include <QScopedPointer>
+
+#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 (file)
index 0000000..821d6f5
--- /dev/null
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QObject>
+#include <QPointer>
+
+#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<DSettingsGroup> parentGroup() const;
+    void setParentGroup(QPointer<DSettingsGroup> parentGroup);
+
+    QString key() const;
+    QString name() const;
+    bool isHidden() const;
+
+    QPointer<DSettingsGroup> childGroup(const QString &groupKey) const;
+    QPointer<DSettingsOption> option(const QString &key) const;
+
+    QList<QPointer<DSettingsGroup> > childGroups() const;
+    QList<QPointer<DSettingsOption> > childOptions() const;
+    QList<QPointer<DSettingsOption> > options() const;
+
+    static QPointer<DSettingsGroup> fromJson(const QString &prefixKey, const QJsonObject &group);
+
+private:
+    void parseJson(const QString &prefixKey, const QJsonObject &group);
+
+    QScopedPointer<DSettingsGroupPrivate> dd_ptr;
+    Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettingsGroup)
+};
+
+typedef QPointer<DSettingsGroup> GroupPtr;
+
+DCORE_END_NAMESPACE
diff --git a/include/settings/dsettingsoption.h b/include/settings/dsettingsoption.h
new file mode 100644 (file)
index 0000000..83226e3
--- /dev/null
@@ -0,0 +1,57 @@
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QVariant>
+#include <QObject>
+#include <QPointer>
+
+#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<DSettingsGroup> parentGroup() const;
+    void setParentGroup(QPointer<DSettingsGroup> 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<DSettingsOption> 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<DSettingsOptionPrivate> dd_ptr;
+    Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettingsOption)
+};
+
+typedef QPointer<DSettingsOption> OptionPtr;
+
+DCORE_END_NAMESPACE
diff --git a/include/util/dabstractunitformatter.h b/include/util/dabstractunitformatter.h
new file mode 100644 (file)
index 0000000..4b8ecc4
--- /dev/null
@@ -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 <QPair>
+#include <QList>
+
+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<qreal, int> format(const qreal value, const int unit) const;
+    QList<QPair<qreal, int>> 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 (file)
index 0000000..957fb90
--- /dev/null
@@ -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 <dtkcore_global.h>
+
+#include <QQueue>
+#include <QMutex>
+#include <QThread>
+#include <QMutexLocker>
+#include <QCoreApplication>
+
+#include <functional>
+#include <type_traits>
+
+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 T>
+    class DSafeQueue : public QQueue<T> {
+    public:
+        inline void enqueue(const T &t) {
+            QMutexLocker lkc(&m_mtx);
+            QQueue<T>::enqueue(t);
+        }
+        inline T dequeue() {
+            QMutexLocker lkc(&m_mtx);
+            return QQueue<T>::dequeue();
+        }
+        inline int size() {
+            QMutexLocker lkc(&m_mtx);
+            return QQueue<T>::size();
+        }
+        inline T &head() {
+            QMutexLocker lkc(&m_mtx);
+            return QQueue<T>::head();
+        }
+        inline const T &head() const {
+            QMutexLocker lkc(&m_mtx);
+            return QQueue<T>::head();
+        }
+    private:
+        mutable QMutex m_mtx;
+    };
+
+    // 内部使用,不对外提供接口
+    class MainWorker : public QObject {
+    Q_OBJECT
+        std::function<void(void *)> m_handle;
+        std::function<void(void *)> m_handleProxy;
+
+        std::function<void(void)> m_handleV;
+        std::function<void(void)> 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 FUNC, typename ArgType>
+        typename std::enable_if<!std::is_void<ArgType>::value>::type
+        setHandle(FUNC &&func) {
+            m_handle = [&] (void *arg) {
+                DSafeQueue<ArgType> *q = static_cast<DSafeQueue<ArgType>*>(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 FUNC, typename ArgType>
+        typename std::enable_if<std::is_void<ArgType>::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<void, xxx>::post execute forever
+    };
+    Q_DECLARE_FLAGS(AsyncTaskStatus, AsyncTaskState)
+};
+
+// Template classes not supported by Q_OBJECT, so class MainWorker is independent
+template <typename DataTypeIn, typename DataTypeOut>
+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<typename T, typename Enable = void>
+    struct DataQueueType { DtkCorePrivate::DSafeQueue<T> m_queue; };
+    template<class T>
+    struct DataQueueType<T, typename std::enable_if<std::is_void<T>::value>::type> { };
+    using DataInQueue = DataQueueType<DataTypeIn>;
+    using DataOutQueue = DataQueueType<DataTypeOut>;
+    // Queue 中处理完的结果经由 m_QueueIn 变量暂存,然后经由 signal、slot 传给 then 中的回调函数做参数
+    DataInQueue m_QueueIn;
+    DataOutQueue m_QueueOut;
+
+    // 存储不同类型的输入函数
+    template<typename T1, typename T2, typename Enable1 = void, typename Enable2 = void>
+    struct FuncType {
+    };
+    template<typename T1, typename T2>
+    struct FuncType<T1, T2,
+            typename std::enable_if<std::is_void<T1>::value>::type,
+            typename std::enable_if<std::is_void<T2>::value>::type> {
+        std::function <void(void)> cbp;
+    };
+    template<typename T1, typename T2>
+    struct FuncType<T1, T2,
+            typename std::enable_if<!std::is_void<T1>::value>::type,
+            typename std::enable_if<!std::is_void<T2>::value>::type> {
+        std::function <T2(T1)> cbp;
+    };
+    template<typename T1, typename T2>
+    struct FuncType<T1, T2,
+            typename std::enable_if<std::is_void<T1>::value>::type,
+            typename std::enable_if<!std::is_void<T2>::value>::type> {
+        std::function <T2(void)> cbp;
+    };
+    template<typename T1, typename T2>
+    struct FuncType<T1, T2,
+            typename std::enable_if<!std::is_void<T1>::value>::type,
+            typename std::enable_if<std::is_void<T2>::value>::type> {
+        std::function <void(T1)> cbp;
+    };
+
+    std::mutex m_mtxFunc;
+    FuncType<DataTypeIn, DataTypeOut> 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 PostInType, typename EmitInType>
+    typename std::enable_if<std::is_void<PostInType>::value && std::is_void<EmitInType>::value>::type
+    emitHelper() {
+        m_func.cbp();
+        Q_EMIT m_mainWorker->sigRunInMainVoid();
+    }
+    // 2. input non void & emit non void
+    template <typename PostInType, typename EmitInType>
+    typename std::enable_if<!std::is_void<PostInType>::value && !std::is_void<EmitInType>::value>::type
+    emitHelper() {
+        m_QueueOut.m_queue.enqueue(m_func.cbp(m_QueueIn.m_queue.dequeue()));
+        Q_EMIT m_mainWorker->sigRunInMain(static_cast<void *>(&(m_QueueOut.m_queue)));
+    }
+    // 3. input non void & emit void
+    template <typename PostInType, typename EmitInType>
+    typename std::enable_if<!std::is_void<PostInType>::value && std::is_void<EmitInType>::value>::type
+    emitHelper() {
+        m_func.cbp(m_QueueIn.m_queue.dequeue());
+        Q_EMIT m_mainWorker->sigRunInMainVoid();
+    }
+    // 4. input void & emit non void
+    template <typename PostInType, typename EmitInType>
+    typename std::enable_if<std::is_void<PostInType>::value && !std::is_void<EmitInType>::value>::type
+    emitHelper() {
+        m_QueueOut.m_queue.enqueue(m_func.cbp());
+        Q_EMIT m_mainWorker->sigRunInMain(static_cast<void *>(&(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 <std::mutex> lck(m_mtxForWaitTask);
+            m_cvForWaitTask.wait(lck);
+        }
+    }
+    // 输入数据不是 void 类型则依赖于 m_QueueIn
+    template <typename FUNC, typename InputType = DataTypeIn>
+    typename std::enable_if<!std::is_void<InputType>::value, Helper *>::type
+    post(FUNC &&func) {
+        m_func.cbp = std::forward<FUNC>(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 <std::mutex> 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<DataTypeIn, DataTypeOut>();
+                    }
+                }
+            });
+            thread.detach();
+        };
+
+        return m_helper;
+    }
+
+    template <typename FUNC, typename InputType = DataTypeIn>
+    typename std::enable_if<std::is_void<InputType>::value, Helper *>::type
+    post(FUNC &&func) {
+        {
+            std::lock_guard<std::mutex> lckFunc(m_mtxFunc);
+            m_func.cbp = std::forward<FUNC>(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 <std::mutex> 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<std::mutex> lckFunc(m_mtxFunc);
+                        emitHelper<DataTypeIn, DataTypeOut>();
+                        m_func.cbp = nullptr;       // reset
+                    }
+                }
+            });
+            thread.detach();
+        };
+
+        return m_helper;
+    }
+
+    // only support DAsync<non void type, ...>
+    template <typename InputType = DataTypeIn>
+    typename std::enable_if<!std::is_void<InputType>::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<void()> m_postProxy;
+    class Helper : public QObject {
+        DAsync *m_async;
+    public:
+        explicit Helper(DAsync *async, QObject *parent = nullptr) noexcept
+            : QObject (parent)
+            , m_async (async)
+        {
+        }
+
+        template <typename FUNC>
+        Helper *then(FUNC &&func) {
+            m_async->m_mainWorker->template setHandle<FUNC, DataTypeOut>(std::forward<FUNC>(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 (file)
index 0000000..9ca5cb7
--- /dev/null
@@ -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 (file)
index 0000000..af72b14
--- /dev/null
@@ -0,0 +1,82 @@
+// SPDX-FileCopyrightText: 2015 Jolla Ltd.
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#ifndef DBUSEXTENDEDABSTRACTINTERFACE_H
+#define DBUSEXTENDEDABSTRACTINTERFACE_H
+
+#include <DDBusExtended>
+
+#include <QDBusAbstractInterface>
+#include <QDBusError>
+
+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 (file)
index 0000000..d0fe593
--- /dev/null
@@ -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 <QDBusAbstractInterface>
+
+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<DDBusInterfacePrivate> 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 (file)
index 0000000..b6410d9
--- /dev/null
@@ -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 <QObject>
+#include <QDBusConnection>
+#include <QDBusPendingCall>
+#include <QDBusInterface>
+
+#include <memory>
+
+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 <typename T>
+    DDBusCaller arg(const T &argument);
+
+private:
+    explicit DDBusCaller(const QString &method, std::shared_ptr<DDBusData> data);
+
+private:
+    std::shared_ptr<DDBusData> m_dbusData;
+    QString m_methodName;
+    QVariantList m_arguments;
+};
+
+template <typename T>
+DDBusCaller DDBusCaller::arg(const T &argument)
+{
+    m_arguments << QVariant::fromValue(argument);
+
+    return *this;
+}
+
+class LIBDTKCORESHARED_EXPORT DDBusProperty
+{
+    friend class DDBusSender;
+
+public:
+    QDBusPendingCall get();
+    template <typename T>
+    QDBusPendingCall set(const T &value);
+
+private:
+    explicit DDBusProperty(const QString &property, std::shared_ptr<DDBusData> data);
+
+private:
+    std::shared_ptr<DDBusData> m_dbusData;
+    QString m_propertyName;
+};
+
+template <typename T>
+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<DDBusData> m_dbusData;
+};
+
+#endif // DDBUSSENDER_H
diff --git a/include/util/ddisksizeformatter.h b/include/util/ddisksizeformatter.h
new file mode 100644 (file)
index 0000000..7dfd8ba
--- /dev/null
@@ -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 (file)
index 0000000..c067aac
--- /dev/null
@@ -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 <dtkcore_global.h>
+#include <dobject.h>
+
+#include <QObject>
+
+#include <functional>
+
+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<QVariant(QString)> handler = nullptr);
+    virtual QVariant invoke(const QString &action, const QString &parameters) 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 (file)
index 0000000..a8fdcf1
--- /dev/null
@@ -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 <dtkcore_global.h>
+
+#include <QUrl>
+
+DCORE_BEGIN_NAMESPACE
+
+class LIBDTKCORESHARED_EXPORT DFileServices
+{
+public:
+    static bool showFolder(QString localFilePath, const QString &startupId = QString());
+    static bool showFolders(const QList<QString> localFilePaths, const QString &startupId = QString());
+    static bool showFolder(QUrl url, const QString &startupId = QString());
+    static bool showFolders(const QList<QUrl> urls, const QString &startupId = QString());
+
+    static bool showFileItemPropertie(QString localFilePath, const QString &startupId = QString());
+    static bool showFileItemProperties(const QList<QString> localFilePaths, const QString &startupId = QString());
+    static bool showFileItemPropertie(QUrl url, const QString &startupId = QString());
+    static bool showFileItemProperties(const QList<QUrl> urls, const QString &startupId = QString());
+
+    static bool showFileItem(QString localFilePath, const QString &startupId = QString());
+    static bool showFileItems(const QList<QString> localFilePaths, const QString &startupId = QString());
+    static bool showFileItem(QUrl url, const QString &startupId = QString());
+    static bool showFileItems(const QList<QUrl> urls, const QString &startupId = QString());
+
+    static bool trash(QString localFilePath);
+    static bool trash(const QList<QString> localFilePaths);
+    static bool trash(QUrl urlstartupId);
+    static bool trash(const QList<QUrl> 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 (file)
index 0000000..7ab062c
--- /dev/null
@@ -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 <QDBusPendingCall>
+#include <memory>
+
+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<DNotifyData> 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 (file)
index 0000000..a98dbb2
--- /dev/null
@@ -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 <dtkcore_global.h>
+
+#include <QHash>
+
+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 (file)
index 0000000..27c0b02
--- /dev/null
@@ -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 <QString>
+
+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 (file)
index 0000000..2b6d212
--- /dev/null
@@ -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 <dtkcore_global.h>
+#include <QObject>
+#include <QSemaphore>
+#include <QThread>
+#include <QCoreApplication>
+#include <QPointer>
+#include <QDebug>
+
+DCORE_BEGIN_NAMESPACE
+
+namespace DThreadUtil {
+typedef std::function<void()> 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<QObject> target, FunctionType *func);
+};
+
+template <typename ReturnType>
+class LIBDTKCORESHARED_EXPORT _TMP
+{
+public:
+    inline static ReturnType runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function<ReturnType()> fun)
+    {
+        ReturnType result;
+        FunctionType proxyFun = [&result, &fun] () {
+            result = fun();
+        };
+
+        FunctionCallProxy::proxyCall(s, thread, target, proxyFun);
+        return result;
+    }
+
+    template <typename T>
+    inline static typename std::enable_if<!std::is_base_of<QObject, T>::value, ReturnType>::type
+            runInThread(QSemaphore *s, QThread *thread, T *, std::function<ReturnType()> fun)
+    {
+        return runInThread(s, thread, static_cast<QObject*>(nullptr), fun);
+    }
+};
+template <>
+class LIBDTKCORESHARED_EXPORT _TMP<void>
+{
+public:
+    inline static void runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function<void()> fun)
+    {
+        FunctionCallProxy::proxyCall(s, thread, target, fun);
+    }
+
+    template <typename T>
+    inline static typename std::enable_if<!std::is_base_of<QObject, T>::value, void>::type
+            runInThread(QSemaphore *s, QThread *thread, T *, std::function<void()> fun)
+    {
+        return runInThread(s, thread, static_cast<QObject*>(nullptr), fun);
+    }
+};
+
+template <typename Fun, typename... Args>
+inline auto runInThread(QSemaphore *s, QThread *thread, QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...))
+{
+    return _TMP<decltype(fun(args...))>::runInThread(s, thread, target, std::bind(fun, std::forward<Args>(args)...));
+}
+template <typename Fun, typename... Args>
+inline auto runInThread(QSemaphore *s, QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...))
+{
+    return runInThread(s, thread, nullptr, fun, std::forward<Args>(args)...);
+}
+template <typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInThread(QSemaphore *s, QThread *thread, QObject *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    return _TMP<typename QtPrivate::FunctionPointer<Fun>::ReturnType>::runInThread(s, thread, target, std::bind(fun, obj, std::forward<Args>(args)...));
+}
+template <typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInThread(QSemaphore *s, QThread *thread, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    return _TMP<typename QtPrivate::FunctionPointer<Fun>::ReturnType>::runInThread(s, thread, obj, std::bind(fun, obj, std::forward<Args>(args)...));
+}
+
+template <typename Fun, typename... Args>
+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>(args)...);
+}
+template <typename Fun, typename... Args>
+inline auto runInThread(QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...))
+{
+    return runInThread(thread, nullptr, fun, std::forward<Args>(args)...);
+}
+template <typename T, typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInThread(QThread *thread, T *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    QSemaphore s;
+
+    return runInThread(&s, thread, target, obj, fun, std::forward<Args>(args)...);
+}
+
+template <typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInThread(QThread *thread, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    return runInThread(thread, obj, obj, fun, std::forward<Args>(args)...);
+}
+
+template <typename Fun, typename... Args>
+inline auto runInMainThread(QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...))
+{
+    if (!QCoreApplication::instance()) {
+        return fun(std::forward<Args>(args)...);
+    }
+
+    return runInThread(QCoreApplication::instance()->thread(), target, fun, std::forward<Args>(args)...);
+}
+template <typename Fun, typename... Args>
+inline auto runInMainThread(Fun fun, Args&&... args) -> decltype(fun(args...))
+{
+    return runInMainThread(nullptr, fun, std::forward<Args>(args)...);
+}
+
+template <typename T, typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInMainThread(T *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    if (!QCoreApplication::instance()) {
+        return (obj->*fun)(std::forward<Args>(args)...);
+    }
+
+    return runInThread(QCoreApplication::instance()->thread(), target, obj, fun, std::forward<Args>(args)...);
+}
+template <typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInMainThread(typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    return runInMainThread(obj, obj, fun, std::forward<Args>(args)...);
+}
+}
+
+DCORE_END_NAMESPACE
+
+#endif // DTHREADUTILS_H
diff --git a/include/util/dtimedloop.h b/include/util/dtimedloop.h
new file mode 100644 (file)
index 0000000..d25782a
--- /dev/null
@@ -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 <dtkcore_global.h>
+#include <DObject>
+
+#include <QEventLoop>
+
+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 (file)
index 0000000..111bb86
--- /dev/null
@@ -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 (file)
index 0000000..7462fcc
--- /dev/null
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QTimer>
+#include <QThread>
+#include <QMetaObject>
+#include <QCoreApplication>
+
+namespace DUtil
+{
+
+template <typename Func1>
+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 <class T>
+void SecureErase(T *p, size_t size)
+{
+    memset(p, 0, size);
+}
+
+template <class T>
+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 (file)
index 0000000..79be2f7
--- /dev/null
@@ -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 <dtkcore_global.h>
+
+#include <QObject>
+#include <QSet>
+#include <QDebug>
+
+#include <functional>
+#include <type_traits>
+
+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 <typename T>
+    static quintptr *getVtableOfClass()
+    {
+        QByteArray vtable_symbol(typeid(T).name());
+        vtable_symbol.prepend("_ZTV");
+
+        quintptr *vfptr_t1 = reinterpret_cast<quintptr*>(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<void(void)> 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<void(void)> 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 <typename T> class OverrideDestruct : public T { ~OverrideDestruct() override;};
+    template <typename List1, typename List2> struct CheckCompatibleArguments { enum { value = false }; };
+    template <typename List> struct CheckCompatibleArguments<List, List> { enum { value = true }; };
+
+    template<typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, quintptr *vfptr_t2, Fun2 fun2, bool forceWrite)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        typedef QtPrivate::FunctionPointer<Fun2> FunInfo2;
+
+        //compilation error if the arguments does not match.
+        Q_STATIC_ASSERT_X((CheckCompatibleArguments<typename FunInfo1::Arguments, typename FunInfo2::Arguments>::value),
+                          "Function1 and Function2 arguments are not compatible.");
+        Q_STATIC_ASSERT_X((CheckCompatibleArguments<QtPrivate::List<typename FunInfo1::ReturnType>, QtPrivate::List<typename FunInfo2::ReturnType>>::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<typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1,
+                      const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        // 检查析构函数是否为虚
+        class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;};
+
+        if (!ensureVtable((void*)t1, std::bind(&_destory_helper<typename FunInfo1::Object>, 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<typename Class, typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
+    {
+        quintptr *vfptr_t1 = getVtableOfClass<Class>();
+
+        if (!vfptr_t1) {
+            abort();
+        }
+
+        quintptr *vfptr_t2 = getVtableOfObject(t2);
+
+        return overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, true);
+    }
+
+    template<typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        return overrideVfptrFun<typename FunInfo1::Object>(fun1, t2, fun2);
+    }
+
+    template<typename Func> struct FunctionPointer { };
+    template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...)>
+    {
+        typedef QtPrivate::List<Obj*, Args...> Arguments;
+    };
+    template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) const>
+    {
+        typedef QtPrivate::List<Obj*, Args...> Arguments;
+    };
+    template<typename Fun1, typename Fun2>
+    static typename std::enable_if<QtPrivate::FunctionPointer<Fun2>::ArgumentCount >= 0, bool>::type
+            overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        typedef QtPrivate::FunctionPointer<Fun2> FunInfo2;
+
+        Q_STATIC_ASSERT(!FunInfo2::IsPointerToMemberFunction);
+        //compilation error if the arguments does not match.
+        Q_STATIC_ASSERT_X((CheckCompatibleArguments<typename FunctionPointer<Fun1>::Arguments, typename FunInfo2::Arguments>::value),
+                          "Function1 and Function2 arguments are not compatible.");
+        Q_STATIC_ASSERT_X((CheckCompatibleArguments<QtPrivate::List<typename FunInfo1::ReturnType>, QtPrivate::List<typename FunInfo2::ReturnType>>::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<typename StdFun, typename Func> struct StdFunWrap {};
+    template<typename StdFun, class Obj, typename Ret, typename... Args>
+    struct StdFunWrap<StdFun, Ret (Obj::*) (Args...)> {
+        typedef std::function<Ret(Obj*, Args...)> 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>(args)...);
+        }
+    };
+    template<typename StdFun, class Obj, typename Ret, typename... Args>
+    struct StdFunWrap<StdFun, Ret (Obj::*) (Args...) const> : StdFunWrap<StdFun, Ret (Obj::*) (Args...)>{};
+
+    template<typename Fun1, typename Fun2>
+    static inline typename std::enable_if<QtPrivate::FunctionPointer<Fun2>::ArgumentCount == -1, bool>::type
+            overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<Fun2, typename FunctionPointer<Fun1>::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<Fun2, typename QtPrivate::List_Left<typename FunctionPointer<Fun1>::Arguments, Fun2ArgumentCount>::Value>::Value Fun2ReturnType;
+
+        Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<Fun2ReturnType, typename FunInfo1::ReturnType>::value),
+                          "Function1 and Function2 return type are not compatible.");
+
+        StdFunWrap<Fun2, Fun1>::fun(fun2);
+        return overrideVfptrFun(vfptr_t1, fun1, StdFunWrap<Fun2, Fun1>::call, forceWrite);
+    }
+
+    /*!
+     * \fn template<typename Fun1, typename Fun2> static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::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<typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1, Fun2 fun2)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        // 检查析构函数是否为虚
+        class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;};
+
+        if (!ensureVtable((void*)t1, std::bind(&_destory_helper<typename FunInfo1::Object>, t1))) {
+            return false;
+        }
+
+        bool ok = overrideVfptrFun(getVtableOfObject(t1), fun1, fun2, false);
+
+        if (!ok) {
+            // 恢复旧环境
+            resetVtable(t1);
+        }
+
+        return true;
+    }
+
+    template<typename Class, typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2)
+    {
+        quintptr *vfptr_t1 = getVtableOfClass<Class>();
+
+        if (!vfptr_t1) {
+            abort();
+        }
+
+        return overrideVfptrFun(vfptr_t1, fun1, fun2, true);
+    }
+
+    template<typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        return overrideVfptrFun<typename FunInfo1::Object>(fun1, fun2);
+    }
+
+    template<typename Fun1>
+    static bool resetVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *obj, Fun1 fun)
+    {
+        return resetVfptrFun((void*)obj, toQuintptr(&fun)) > 0;
+    }
+
+    template<typename Fun>
+    static Fun originalFun(const typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun)
+    {
+        quintptr o_fun = originalFun((void*)obj, toQuintptr(&fun));
+
+        return *reinterpret_cast<Fun*>(o_fun);
+    }
+
+    template<typename Fun, typename... Args>
+    static typename QtPrivate::FunctionPointer<Fun>::ReturnType
+    callOriginalFun(typename QtPrivate::FunctionPointer<Fun>::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>(args)...);
+    }
+
+private:
+    static bool copyVtable(quintptr **obj);
+    static bool clearGhostVtable(const void *obj);
+
+    template<typename T>
+    static void _destory_helper(const T *obj) {
+        delete obj;
+    }
+
+    static QMap<quintptr**, quintptr*> objToOriginalVfptr;
+    static QMap<const void*, quintptr*> objToGhostVfptr;
+    static QMap<const void*, quintptr> objDestructFun;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DVTABLEHOOK_H
diff --git a/misc/DtkConfig.cmake.in b/misc/DtkConfig.cmake.in
new file mode 100644 (file)
index 0000000..9d3df4e
--- /dev/null
@@ -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 (file)
index 0000000..58763ca
--- /dev/null
@@ -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 (file)
index 0000000..44afcfa
--- /dev/null
@@ -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/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..41ed7f6
--- /dev/null
@@ -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/DConfig b/src/DConfig
deleted file mode 100644 (file)
index 6157c75..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dconfig.h"
diff --git a/src/DConfigFile b/src/DConfigFile
deleted file mode 100644 (file)
index bb79fb3..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dconfigfile.h"
diff --git a/src/DDesktopEntry b/src/DDesktopEntry
deleted file mode 100644 (file)
index 847127a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "ddesktopentry.h"
diff --git a/src/DSecureString b/src/DSecureString
deleted file mode 100644 (file)
index 83f6c1a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dsecurestring.h"
diff --git a/src/DSysInfo b/src/DSysInfo
deleted file mode 100644 (file)
index c026f58..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dsysinfo.h"
diff --git a/src/base/DObject b/src/base/DObject
deleted file mode 100644 (file)
index 75fbcb7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dobject.h"
diff --git a/src/base/DObjectPrivate b/src/base/DObjectPrivate
deleted file mode 100644 (file)
index 0c145c8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dobject_p.h"
diff --git a/src/base/DSingleton b/src/base/DSingleton
deleted file mode 100644 (file)
index 280d2f9..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dsingleton.h"
diff --git a/src/base/base.cmake b/src/base/base.cmake
new file mode 100644 (file)
index 0000000..4ad1ae5
--- /dev/null
@@ -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/base.pri b/src/base/base.pri
deleted file mode 100644 (file)
index fb8de9e..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-include($$PWD/private/private.pri)
-
-INCLUDEPATH += $$PWD/base
-INCLUDEPATH += $$PWD/private
-
-HEADERS += \
-    $$PWD/dobject.h \
-    $$PWD/dsingleton.h
-
-SOURCES += \
-    $$PWD/dobject.cpp
-
-includes.files += $$PWD/*.h
-includes.files += $$PWD/private/*.h
-includes.files += \
-    $$PWD/DObject \
-    $$PWD/DObjectPrivate \
-    $$PWD/DSingleton
index 5d79d3ef192764eda71d7c082b6c3cc200e8d5a7..f3bddd72ef1481235d4354d5c07042a4aceb02d8 100644 (file)
@@ -1,22 +1,9 @@
-/*
- * Copyright (C) 2015 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dobject.h"
-#include "private/dobject_p.h"
+#include "base/private/dobject_p.h"
 
 DCORE_BEGIN_NAMESPACE
 
@@ -247,7 +234,7 @@ a.cpp
 
  }
 
- #include "moc_a.cpp"
\#include "moc_a.cpp"
  \endcode
  \a Func 槽函数的完整签名
  \note 添加或更新私有槽之后需要重新手动调用 qmake
diff --git a/src/base/dobject.h b/src/base/dobject.h
deleted file mode 100644 (file)
index d981d40..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2015 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DOBJECT_H
-#define DOBJECT_H
-
-#include <QScopedPointer>
-
-#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<DObjectPrivate> d_d_ptr;
-
-    Q_DISABLE_COPY(DObject)
-    D_DECLARE_PRIVATE(DObject)
-};
-
-DCORE_END_NAMESPACE
-
-#endif // DOBJECT_H
diff --git a/src/base/dsingleton.h b/src/base/dsingleton.h
deleted file mode 100644 (file)
index 7af8853..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#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<ExampleSingleton>
-   {
-       Q_OBJECT
-       friend class DSingleton<ExampleSingleton>;
-   };
-   \endcode
-
-  \note: for Qt, "public DSingleton<ExampleSingleton>" must be after QObject.
- */
-
-/*!
-  通过c++11的特性实现的单例模板
-  
-  使用示例:
-
-```
-   class ExampleSingleton : public QObject, public Dtk::DSingleton<ExampleSingleton>
-   {
-       Q_OBJECT
-       friend class DSingleton<ExampleSingleton>;
-   };
-```
-
-  \note 对于Qt程序 public DSingleton<ExampleSingleton>" 必须在卸载QObject后面出现。
- */
-
-template <class T>
-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/src/base/private/dobject_p.h b/src/base/private/dobject_p.h
deleted file mode 100644 (file)
index f3213bd..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#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/src/base/private/private.pri b/src/base/private/private.pri
deleted file mode 100644 (file)
index 5de92e3..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-HEADERS += \
-    $$PWD/dobject_p.h
diff --git a/src/dbus/dbus.cmake b/src/dbus/dbus.cmake
new file mode 100644 (file)
index 0000000..2d239e1
--- /dev/null
@@ -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
+)
index 65881d60387c67d2a7a08919d8dd3f1bd3d990e8..89a2aab67f911b03fb599b80c6a8488565516a90 100644 (file)
@@ -1,3 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+
+SPDX-License-Identifier: LGPL-3.0-or-later
+-->
+
 <interface name='org.desktopspec.ConfigManager.Manager'>
     <property access="read" type="s" name="version"/>
     <property access="read" type="as" name="keyList"/>
index 2044e58276208a345a0b07bfcbf9540fcbbb7101..f265bc892385ecbacf6e50ecd12928d1a9a7301d 100644 (file)
@@ -1,3 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+
+SPDX-License-Identifier: LGPL-3.0-or-later
+-->
+
 <interface name='org.desktopspec.ConfigManager'>
     <method name='acquireManager'>
       <arg type='s' name='appid' direction='in'/>
diff --git a/src/dci/dci.cmake b/src/dci/dci.cmake
new file mode 100644 (file)
index 0000000..23aaf90
--- /dev/null
@@ -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 (file)
index 0000000..0894daf
--- /dev/null
@@ -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 <DObjectPrivate>
+#else
+#define D_D(class)
+#define D_DC(class)
+#endif
+#include <QDataStream>
+#include <QFile>
+#include <QLoggingCategory>
+#include <QtEndian>
+#include <QSaveFile>
+#include <QDir>
+#include <QBuffer>
+#include <QCollator>
+
+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<Node*> 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<QString, Node *> &pathToNode);
+
+    // 按标准中规定的文件排序计算此 name 在这个列表中的位置
+    static int getOrderedIndexOfNodeName(const decltype(Node::children) &list, const QString &name);
+
+    qint8 version = 0;
+    QScopedPointer<Node> root;
+    QHash<QString, Node*> 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<qint32>(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<QString, Node*> 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<char>(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<qint64>(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<QPair<const Node*, Node*>> 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<QString, DDciFilePrivate::Node *> &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<qint64>(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<char>(d->version));
+    char fileCountData[sizeof(int)];
+    qToLittleEndian<int>(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<DDciFile::FileType>(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 (file)
index 0000000..9e4fd6f
--- /dev/null
@@ -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 <private/qfile_p.h>
+#undef private
+#undef protected
+
+#include "ddcifileengine_p.h"
+#include "dci/ddcifile.h"
+
+#include <QBuffer>
+#include <QLoggingCategory>
+
+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<QString, QWeakPointer<DDciFile>> 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<FileFlags>(static_cast<int>(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<QFile::FileTime>(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<QString, QString> 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 (file)
index 0000000..5a2b84c
--- /dev/null
@@ -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 <dtkcore_global.h>
+#else
+#define DCORE_BEGIN_NAMESPACE
+#define DCORE_END_NAMESPACE
+#endif
+#include <private/qabstractfileengine_p.h>
+
+#include <QSharedPointer>
+#include <QDateTime>
+
+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<DDciFile>;
+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<QString, QString> 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
index 18bc6cc69685891a540e3124ef6f0495e9beae92..86e244e5dc292e3d89133a221dc7788fa3649b27 100644 (file)
@@ -1,23 +1,7 @@
-/*
- * Copyright (C) 2021 Uniontech Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// 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"
@@ -29,6 +13,7 @@
 #include <QSettings>
 #endif
 #include "dobject_p.h"
+#include <DSGApplication>
 
 #include <QLoggingCategory>
 #include <QCoreApplication>
@@ -40,11 +25,6 @@ DCORE_BEGIN_NAMESPACE
 
 Q_DECLARE_LOGGING_CATEGORY(cfLog)
 
-inline static QString getAppId() {
-    // TODO: 应该使用更可靠的接口获取 appId
-    return QCoreApplication::applicationName();
-}
-
 /*!
     \class Dtk::Core::DConfigBackend
     \inmodule dtkcore
@@ -113,7 +93,7 @@ public:
                             const QString &name,
                             const QString &subpath)
         : DObjectPrivate(qq)
-        , appId(appId.isEmpty() ? getAppId() : appId)
+        , appId(appId.isEmpty() ? DSGApplication::id() : appId)
         , name(name)
         , subpath(subpath)
     {
@@ -160,12 +140,12 @@ public:
         return configFile && configFile->isValid();
     }
 
-    virtual bool load(const QString &appId) override
+    virtual bool load(const QString &/*appId*/) override
     {
         if (configFile)
             return true;
 
-        configFile.reset(new DConfigFile(appId,owner->name, owner->subpath));
+        configFile.reset(new DConfigFile(owner->appId,owner->name, owner->subpath));
         configCache.reset(configFile->createUserCache(getuid()));
         const QString &prefix = localPrefix();
 
@@ -186,7 +166,8 @@ public:
 
     virtual void setValue(const QString &key, const QVariant &value) override
     {
-        if (configFile->setValue(key, value, getAppId(), configCache.get())) {
+        // 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);
         }
     }
@@ -273,20 +254,20 @@ public:
       初始化DBus连接,会先调用acquireManager动态获取一个配置连接,
       再通过这个配置连接进行配置文件的访问.
      */
-    virtual bool load(const QString &appid) override
+    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<QDBusObjectPath> dbus_reply = dsg_config.acquireManager(appid, owner->name, owner->subpath);
+        QDBusPendingReply<QDBusObjectPath> 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 {
-            qCWarning(cfLog(), "dbus path=\"%s\"", qPrintable(dbus_path.path()));
+            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()) {
diff --git a/src/dconfig.h b/src/dconfig.h
deleted file mode 100644 (file)
index 317e30c..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2021 Uniontech Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-#ifndef DCONFIG_H
-#define DCONFIG_H
-
-#include <dtkcore_global.h>
-#include <DObject>
-
-#include <QObject>
-#include <QVariant>
-
-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
index 98d6424288e4678aed90af14121968ad75a18649..6b875a485c6eae6b05916d1982c077fa5043d5dc 100644 (file)
@@ -1,23 +1,7 @@
-/*
- * Copyright (C) 2021 Uniontech Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "dconfigfile.h"
 
 #include "dobject_p.h"
@@ -152,7 +136,7 @@ static DConfigFile::Version parseVersion(const QJsonObject &obj) {
 #define MAGIC_OVERRIDE QLatin1String("dsg.config.override")
 #define MAGIC_CACHE QLatin1String("dsg.config.cache")
 
-static const uint GlobalUID = 0xFFFF;
+static const uint InvalidUID = 0xFFFF;
 
 inline static bool checkMagic(const QJsonObject &obj, QLatin1String request) {
     return obj[QLatin1String("magic")].toString() == request;
@@ -961,13 +945,20 @@ public:
         return values.keyList();
     }
 
-    inline QString applicationCacheDir(const QString &prefix) const {
-        const QString &homePath = DStandardPaths::homePath(userid);
+    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/");
-        return prefix + userHomeConfigDir + "/" + configKey.appId;
+        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) {
@@ -978,8 +969,18 @@ public:
     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())
-            appDataDir = QString("/var/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);
     }
@@ -987,7 +988,12 @@ public:
     QString getCacheDir(const QString &localPrefix = QString())
     {
         if (isGlobal()) {
-            return globalCacheDir(localPrefix);
+            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);
         }
@@ -1014,6 +1020,7 @@ public:
         values.setTime(key, QDateTime::currentDateTime().toString(Qt::ISODate));
         values.setUser(key, uid);
         values.setAppId(key, appid.isEmpty() ? configKey.appId : appid);
+        cacheChanged = true;
         return true;
     }
 
@@ -1028,7 +1035,7 @@ public:
     DConfigInfo values;
     uint userid;
     bool global;
-    char padding [3] = {};
+    bool cacheChanged = false;
 };
 
 DConfigCacheImpl::DConfigCacheImpl(const DConfigKey &configKey, const uint uid, bool global)
@@ -1080,9 +1087,13 @@ bool DConfigCacheImpl::load(const QString &localPrefix)
 
 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, "save Falied because home directory is not exist for the user[%d].", userid);
+        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);
@@ -1093,7 +1104,7 @@ bool DConfigCacheImpl::save(const QString &localPrefix, QJsonDocument::JsonForma
     }
 
     if (!cache.open(QIODevice::WriteOnly)) {
-        qCWarning(cfLog, "save Falied on open file: \"%s\", error message: \"%s\"",
+        qCWarning(cfLog, "Falied on saveing data when open file: \"%s\", error message: \"%s\"",
                   qPrintable(cache.fileName()), qPrintable(cache.errorString()));
         return false;
     }
@@ -1237,14 +1248,14 @@ DConfigFile::DConfigFile(const QString &appId, const QString &name, const QStrin
     Q_ASSERT(!name.isEmpty());
 
     D_D(DConfigFile);
-    d->globalCache = new DConfigCacheImpl(d->configKey, GlobalUID, true);
+    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, GlobalUID, true);
+    auto cache = new DConfigCacheImpl(d->configKey, InvalidUID, true);
     cache->values = other.d_func()->globalCache->values;
     d->globalCache = cache;
 }
diff --git a/src/dconfigfile.h b/src/dconfigfile.h
deleted file mode 100644 (file)
index b7e294e..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2021 Uniontech Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-#ifndef DCONFIGFILE_H
-#define DCONFIGFILE_H
-
-#include <dtkcore_global.h>
-#include <DObject>
-#include <QStringList>
-#include <QFlags>
-#include <QVariant>
-#include <QLocale>
-#include <QJsonDocument>
-#include <QDebug>
-
-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<QIODevice*> &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<QIODevice*> &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
index f5efa94009f27d18e36055ca6152cb39d03c2d8e..87941e6d862183692cf7d147c4b7fa6d5b6ae527 100644 (file)
@@ -1,24 +1,6 @@
-/*
- * Copyright (C) 2019 Deepin Technology Co., Ltd.
- *               2019 Gary Wang
- *
- * Author:     Gary Wang <wzc782970009@gmail.com>
- *
- * Maintainer: Gary Wang <wzc782970009@gmail.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "ddesktopentry.h"
 
diff --git a/src/ddesktopentry.h b/src/ddesktopentry.h
deleted file mode 100644 (file)
index 3053ef9..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2019 Deepin Technology Co., Ltd.
- *               2019 Gary Wang
- *
- * Author:     Gary Wang <wzc782970009@gmail.com>
- *
- * Maintainer: Gary Wang <wzc782970009@gmail.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <dtkcore_global.h>
-
-#include <QIODevice>
-#include <QObject>
-#include <QVariant>
-
-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 &section = "Desktop Entry") const;
-    QStringList allGroups(bool sorted = false) const;
-
-    bool contains(const QString &key, const QString &section = "Desktop Entry") const;
-
-    QString name() const;
-    QString genericName() const;
-    QString ddeDisplayName() const;
-    QString comment() const;
-
-    QString rawValue(const QString &key, const QString &section = "Desktop Entry",
-                     const QString &defaultValue = QString()) const;
-    QString stringValue(const QString &key, const QString &section = "Desktop Entry",
-                        const QString &defaultValue = QString()) const;
-    QString localizedValue(const QString &key, const QString &localeKey = "default",
-                           const QString &section = "Desktop Entry", const QString& defaultValue = QString()) const;
-    QString localizedValue(const QString &key, const QLocale &locale,
-                           const QString &section = "Desktop Entry", const QString& defaultValue = QString()) const;
-    QStringList stringListValue(const QString &key, const QString &section = "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 &section = "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<DDesktopEntryPrivate> d_ptr;
-
-    Q_DECLARE_PRIVATE(DDesktopEntry)
-};
-
-DCORE_END_NAMESPACE
index cc5965ac85c1fa129844d1acf06085568f80c887..6469be92e7ec094780c819bebc358ca82c57ee09 100644 (file)
@@ -1,23 +1,7 @@
-/*
- * Copyright (C) 2019 Deepin Technology Co., Ltd.
- *
- * Author:     Gary Wang <wzc782970009@gmail.com>
- *
- * Maintainer: Gary Wang <wangzichong@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "dsecurestring.h"
 #include "dutil.h"
 
diff --git a/src/dsecurestring.h b/src/dsecurestring.h
deleted file mode 100644 (file)
index e5ac5a1..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2019 Deepin Technology Co., Ltd.
- *
- * Author:     Gary Wang <wzc782970009@gmail.com>
- *
- * Maintainer: Gary Wang <wangzichong@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-#pragma once
-
-#include <dtkcore_global.h>
-#include <QString>
-
-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/src/dsgapplication.cpp b/src/dsgapplication.cpp
new file mode 100644 (file)
index 0000000..c00d355
--- /dev/null
@@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dsgapplication.h"
+
+#include <QByteArray>
+#include <QCoreApplication>
+
+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
index 715a84ff5daf0f51337897291e9dbef497746b2f..fd3fe6d220f733da1e5ec55b10c0f9980c11644f 100644 (file)
@@ -1,23 +1,7 @@
-/*
- * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "dsysinfo.h"
 #include "ddesktopentry.h"
 
@@ -31,6 +15,8 @@
 #include <QJsonArray>
 #include <QSettings>
 #include <QStandardPaths>
+#include <QDateTime>
+#include <qmath.h>
 
 #ifdef Q_OS_LINUX
 #include <sys/sysinfo.h>
@@ -72,9 +58,9 @@ public:
     QString minorVersion;
     struct MinVersion {
         enum Type {
-            A_BC_D, // 专业版
-            X_Y_Z, // 家庭版
-            A_B_C // 社区版
+            A_BC_D, /*!< Professional Edition*/
+            X_Y_Z,  /*!< Home Edition*/
+            A_B_C  /*!< Community Edition*/
         };
         MinVersion()
             : A(0)
@@ -232,7 +218,7 @@ void DSysInfoPrivate::ensureDeepinInfo()
 
 bool DSysInfoPrivate::ensureOsVersion()
 {
-#ifndef OS_VERSION_TEST_FILE // 测试时总是重新读取文件
+#ifndef OS_VERSION_TEST_FILE // Always re-read the file when testing
     if (osBuild.A > 0)
         return true;
 #endif
@@ -283,19 +269,19 @@ bool DSysInfoPrivate::ensureOsVersion()
 
     switch (osBuild.D) {
     case 7: {
-        // 家庭版使用“完整版本号编码-X.Y.Z”的形式
+        // 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) {
-            // Z0
+            // Z is 0
             minVersion.X = versionList.first().toUInt();
             minVersion.Y = versionList.last().toUInt();
             minVersion.Z = 0;
         } else if (versionList.length() == 3) {
-            // X.Y.Z都存在
+            // X.Y.Z exists
             minVersion.X = versionList.at(0).toUInt();
             minVersion.Y = versionList.at(1).toUInt();
             minVersion.Z = versionList.at(2).toUInt();
@@ -304,32 +290,32 @@ bool DSysInfoPrivate::ensureOsVersion()
     } break;
 
     case 3: {
-        // 社区版使用“完整版本号编码-A.B.C”的形式
+        // 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模式且B C 为0
+                //A.B.C mode and B c are 0
                 minVersion.A = modeVersion.toUInt();
                 minVersion.B = 0;
                 minVersion.C = 0;
             } else {
-                // A_BC_D模式
+                // A_BC_D mode
                 splitA_BC_DMode();
                 a_bc_dMode = true;
             }
         } else if (versionList.length() == 2) {
-            // C0
+            // C=0
             minVersion.A = versionList.first().toUInt();
             minVersion.B = versionList.last().toUInt();
             minVersion.C = 0;
         } else if (versionList.length() == 3) {
-            // A.B.C都存在
+            // A.B.C exists
             minVersion.A = versionList.at(0).toUInt();
             minVersion.B = versionList.at(1).toUInt();
             minVersion.C = versionList.at(2).toUInt();
@@ -454,6 +440,11 @@ void DSysInfoPrivate::ensureReleaseInfo()
             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)
@@ -464,6 +455,11 @@ void DSysInfoPrivate::ensureReleaseInfo()
             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)
@@ -579,8 +575,9 @@ QString DSysInfo::deepinCopyright()
 }
 
 /*!
-  \brief DSysInfo::osType 系统类型
-  显示系统类型【1:桌面】【2:服务器】【3:专用设备】
+@~english
+  \brief
+  Display system type [1: desktop] [2: server] [3: special devices]
   \note 根据 osBuild.B 判断
  */
 DSysInfo::UosType DSysInfo::uosType()
@@ -596,9 +593,10 @@ DSysInfo::UosType DSysInfo::uosType()
 }
 
 /*!
-  \brief DSysInfo::osEditionType 版本类型
-  显示版本类型 专业版/个人版/社区版...
-  \note 根据 osBuild.B && osBuild.D
+@~english
+  \brief
+  Editions: professional version/personal version/community version ...
+  \note According to osbuild.b && osbuild.d
  */
 DSysInfo::UosEdition DSysInfo::uosEditionType()
 {
@@ -610,7 +608,7 @@ DSysInfo::UosEdition DSysInfo::uosEditionType()
             return UosProfessional;
         case 2:
         case 7:
-            // 新版本家庭版(7)与旧版本个人版(2)同为Home 不修改旧有逻辑的情况下新增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;
@@ -646,7 +644,8 @@ DSysInfo::UosEdition DSysInfo::uosEditionType()
 }
 
 /*!
-  \brief DSysInfo::osArch 架构信息(使用一个字节的二进制位,从低位到高位)
+@~english
+   \brief Architecture information (using bit flags of a byte)
   【0x8 sw64】【0x4 mips64】【0x2 arm64】【0x1 amd64】
  */
 DSysInfo::UosArch DSysInfo::uosArch()
@@ -665,9 +664,10 @@ static QString getUosVersionValue(const QString &key, const QLocale &locale)
 }
 
 /*!
-  \brief DSysInfo::osProductTypeName 版本名称
-  ProductType[xx] 项对应的值, 如果找不到对应语言的默认使用 ProductType的值(Desktop/Server/Device)
-  \a locale 当前系统语言
+@~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)
 {
@@ -675,10 +675,11 @@ QString DSysInfo::uosProductTypeName(const QLocale &locale)
 }
 
 /*!
-  \brief DSysInfo::osSystemName 版本名称
-  
-  SystemName[xx] 项对应的值, 如果找不到对应语言的默认使用 SystemName 的值 Uniontech OS
-  \a 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)
 {
@@ -686,9 +687,10 @@ QString DSysInfo::uosSystemName(const QLocale &locale)
 }
 
 /*!
-  \brief DSysInfo::osEditionName 版本名称
-   EditionName[xx] 项对应的值, 如果找不到对应语言的默认使用 EditionName 的值(Professional/Home/Community...)
-  \a 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)
 {
@@ -696,11 +698,12 @@ QString DSysInfo::uosEditionName(const QLocale &locale)
 }
 
 /*!
-  \brief DSysInfo::spVersion 阶段版本名称
-  小版本号 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
+@~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()
 {
@@ -730,11 +733,12 @@ QString DSysInfo::spVersion()
 }
 
 /*!
-  \brief DSysInfo::udpateVersion 更新版本名称
-  小版本号 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
+@~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()
 {
@@ -773,9 +777,10 @@ QString DSysInfo::udpateVersion()
 }
 
 /*!
-  \brief DSysInfo::majorVersion 主版本号
-  主版本号 【20】【23】【25】【26】【29】【30】
-  \note 返回 MajorVersion 的值
+@~english
+  \brief Main edition number
+  Main edition number 【20】【23】【25】【26】【29】【30】
+  \note Return to Majorversion value
  */
 QString DSysInfo::majorVersion()
 {
@@ -784,10 +789,11 @@ QString DSysInfo::majorVersion()
 }
 
 /*!
-  \brief DSysInfo::minorVersion 小版本号
+@~english
+  \brief DSysInfo::minorVersion minor version
  *【ABCD】 ·[0-9]{4}
- *【A.B.C】 或者【X.Y.Z】
-  \note 返回 MinorVersion 的值
+ *【A.B.C】 or【X.Y.Z】
+  @return the value of minorversion
  */
 QString DSysInfo::minorVersion()
 {
@@ -796,9 +802,10 @@ QString DSysInfo::minorVersion()
 }
 
 /*!
-  \brief DSysInfo::buildVersion 小版本号
-  系统镜像批次号,按时间顺序(不可回退)从100-999递增
-  \note 返回 osBuild.xyz 的值
+@~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()
 {
@@ -837,10 +844,11 @@ QString DSysInfo::distributionInfoSectionName(DSysInfo::OrgType type)
 }
 
 /*!
+@~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)
@@ -860,10 +868,11 @@ QString DSysInfo::deepinDistributorName()
 }
 
 /*!
+@~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<QString, QString> DSysInfo::distributionOrgWebsite(DSysInfo::OrgType type)
@@ -887,10 +896,11 @@ QPair<QString, QString> DSysInfo::deepinDistributorWebsite()
 }
 
 /*!
+@~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)
@@ -939,15 +949,16 @@ QString DSysInfo::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()
@@ -1008,6 +1019,7 @@ QString DSysInfo::cpuModelName()
 }
 
 /*!
+@~english
   \return the installed memory size
  */
 qint64 DSysInfo::memoryInstalledSize()
@@ -1040,6 +1052,7 @@ qint64 DSysInfo::memoryInstalledSize()
 }
 
 /*!
+@~english
   \return the total available to use memory size
  */
 qint64 DSysInfo::memoryTotalSize()
@@ -1100,4 +1113,171 @@ qint64 DSysInfo::systemDiskSize()
     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/dsysinfo.h b/src/dsysinfo.h
deleted file mode 100644 (file)
index 136652e..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-#ifndef DSYSINFO_H
-#define DSYSINFO_H
-
-#include <dtkcore_global.h>
-
-#include <QLocale>
-
-DCORE_BEGIN_NAMESPACE
-
-class DSysInfoPrivate;
-class LIBDTKCORESHARED_EXPORT DSysInfo
-{
-public:
-    enum ProductType {
-        UnknownType = 0,
-        Deepin,
-        ArchLinux,
-        CentOS,
-        Debian,
-        Fedora,
-        LinuxMint,
-        Manjaro,
-        openSUSE,
-        SailfishOS,
-        Ubuntu,
-        Uos
-    };
-
-    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
-    };
-
-    // 注意:此处架构是从OsBuild获取的系统版本的Arch信息,并不是指硬件的Arch信息
-    enum UosArch {
-        UosArchUnknown,
-        UosAMD64 = 1 << 0,
-        UosARM64 = 1 << 1,
-        UosMIPS64 = 1 << 2,
-        UosSW64 = 1 << 3
-    };
-
-#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
-    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<QString, QString> distributionOrgWebsite(OrgType type = Distribution);
-    Q_DECL_DEPRECATED_X("Use deepinDistributionOrgWebsite() instead") static QPair<QString, QString> 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();
-};
-
-DCORE_END_NAMESPACE
-
-#endif // DSYSINFO_H
index 23c7269bb93d290d7d89d44cb20bbe5c62e2539a..c22d020ab46fe45fa506ef49e3e384f06b8f1c85 100644 (file)
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "dtkcore_global.h"
 #include <QDebug>
 #include <QFileInfo>
diff --git a/src/dtkcore_global.h b/src/dtkcore_global.h
deleted file mode 100644 (file)
index 7a6ce6e..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <QtCore/qglobal.h>
-#include <dtkcore_config.h>
-
-#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/src/filesystem/DBaseFileWatcher b/src/filesystem/DBaseFileWatcher
deleted file mode 100644 (file)
index 1348031..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dbasefilewatcher.h"
diff --git a/src/filesystem/DFileSystemWatcher b/src/filesystem/DFileSystemWatcher
deleted file mode 100644 (file)
index 97400f2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dfilesystemwatcher.h"
diff --git a/src/filesystem/DFileWatcher b/src/filesystem/DFileWatcher
deleted file mode 100644 (file)
index 768151a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dfilewatcher.h"
diff --git a/src/filesystem/DFileWatcherManager b/src/filesystem/DFileWatcherManager
deleted file mode 100644 (file)
index a9f8682..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dfilewatchermanager.h"
diff --git a/src/filesystem/DPathBuf b/src/filesystem/DPathBuf
deleted file mode 100644 (file)
index 961c40b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dpathbuf.h"
diff --git a/src/filesystem/DStandardPaths b/src/filesystem/DStandardPaths
deleted file mode 100644 (file)
index 9d0b963..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dstandardpaths.h"
diff --git a/src/filesystem/DTrashManager b/src/filesystem/DTrashManager
deleted file mode 100644 (file)
index e763093..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dtrashmanager.h"
index c61808bbc5caae9731fb1bc8665193d0efa1bc5d..f626a242dc9bce47fd2db7e77cff4246f4bd1468 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// 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"
@@ -197,4 +184,4 @@ DBaseFileWatcher::DBaseFileWatcher(DBaseFileWatcherPrivate &dd,
 
 DCORE_END_NAMESPACE
 
-#include "moc_dbasefilewatcher.cpp"
+//#include "moc_dbasefilewatcher.cpp"
diff --git a/src/filesystem/dbasefilewatcher.h b/src/filesystem/dbasefilewatcher.h
deleted file mode 100644 (file)
index e011bd5..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DBASEFILEWATCHER_H
-#define DBASEFILEWATCHER_H
-
-#include "dtkcore_global.h"
-#include "dobject.h"
-
-#include <QObject>
-
-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/src/filesystem/dcapfile.cpp b/src/filesystem/dcapfile.cpp
new file mode 100644 (file)
index 0000000..e1cafea
--- /dev/null
@@ -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 <private/qdir_p.h>
+
+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 &copy);
+
+    QString filePath;
+};
+
+DCapDirPrivate::DCapDirPrivate(QString filePath)
+    : filePath(filePath)
+{
+}
+
+DCapDirPrivate::DCapDirPrivate(const DCapDirPrivate &copy)
+    : 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<DCapFSFileEngine> 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 (file)
index 0000000..09df413
--- /dev/null
@@ -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 <QDebug>
+
+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 (file)
index 0000000..5a4729f
--- /dev/null
@@ -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 <QStandardPaths>
+#include <QDebug>
+
+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<int>(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<int>(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.h b/src/filesystem/dfilesystemwatcher.h
deleted file mode 100644 (file)
index a8be007..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DFILESYSTEMWATCHER_H
-#define DFILESYSTEMWATCHER_H
-
-#include "dtkcore_global.h"
-#include "dobject.h"
-
-#include <QObject>
-
-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
index 4352b0feb585a6e37a7e03456ffc0c134991d63f..bb3f5f12ecee70aceb48a52b799da5f60cf7507c 100644 (file)
@@ -1,19 +1,7 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// 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"
 
index 864073ae95878adce20b2c1cd686a9ee4a34a411..32ce28ca25674c544e0c9ae7cca406174251d5e8 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// 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"
@@ -486,12 +473,7 @@ DFileSystemWatcher::~DFileSystemWatcher()
 */
 bool DFileSystemWatcher::addPath(const QString &path)
 {
-    if (path.isEmpty()) {
-        qWarning("DFileSystemWatcher::addPath: path is empty");
-        return true;
-    }
-
-    QStringList paths = addPaths(QStringList(path));
+    const QStringList &paths = addPaths(QStringList(path));
     return paths.isEmpty();
 }
 
@@ -522,22 +504,26 @@ QStringList DFileSystemWatcher::addPaths(const QStringList &paths)
 {
     Q_D(DFileSystemWatcher);
 
+    if (!d)
+        return paths;
+
     QStringList p = paths;
     QMutableListIterator<QString> it(p);
 
     while (it.hasNext()) {
         const QString &path = it.next();
-        if (path.isEmpty())
+        if (path.isEmpty()) {
+            qWarning() << Q_FUNC_INFO << "the path is empty and it is not be watched";
             it.remove();
+        }
     }
 
     if (p.isEmpty()) {
-        qWarning("DFileSystemWatcher::addPaths: list is empty");
-        return QStringList();
+        qWarning() << Q_FUNC_INFO << "all path are filtered and they are not be watched, paths are " << paths;
+        return paths;
     }
 
-    if (d)
-        p = d->addPaths(p, &d->files, &d->directories);
+    p = d->addPaths(p, &d->files, &d->directories);
 
     return p;
 }
@@ -554,12 +540,7 @@ QStringList DFileSystemWatcher::addPaths(const QStringList &paths)
 */
 bool DFileSystemWatcher::removePath(const QString &path)
 {
-    if (path.isEmpty()) {
-        qWarning("DFileSystemWatcher::removePath: path is empty");
-        return true;
-    }
-
-    QStringList paths = removePaths(QStringList(path));
+    const QStringList &paths = removePaths(QStringList(path));
     return paths.isEmpty();
 }
 
@@ -578,22 +559,26 @@ QStringList DFileSystemWatcher::removePaths(const QStringList &paths)
 {
     Q_D(DFileSystemWatcher);
 
+    if (!d)
+        return paths;
+
     QStringList p = paths;
     QMutableListIterator<QString> it(p);
 
     while (it.hasNext()) {
         const QString &path = it.next();
-        if (path.isEmpty())
+        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("DFileSystemWatcher::removePaths: list is empty");
-        return QStringList();
+        qWarning() << Q_FUNC_INFO << "all path are filtered and they are not be watched, paths are " << paths;
+        return paths;
     }
 
-    if (d)
-        p = d->removePaths(p, &d->files, &d->directories);
+    p = d->removePaths(p, &d->files, &d->directories);
 
     return p;
 }
index 4e845b138a46a7a8b27bb76cf0e9003fb9258c4e..09af4e4efa01baf8efcc63cf0474b7a2a416951a 100644 (file)
@@ -1,19 +1,7 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// 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"
 
index f983512696e434e7a9b48c41c886102eacdf5cfb..00b5039fb77dc7ccc1cc034994edb0cbc6f68fae 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// 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"
@@ -219,7 +206,7 @@ QString DFileWatcherPrivate::formatPath(const QString &path)
     \class Dtk::Core::DFileWatcher
     \inmodule dtkcore
 
-    \brief The DFileWatcher class provides an implemention of DBaseFileWatcher for monitoring files and directories for modifications.
+    \brief The DFileWatcher class provides an implementation of DBaseFileWatcher for monitoring files and directories for modifications.
     \brief DFileWatcher 类提供了对 DBaseFileWatcher 接口的实现,可供监视文件和目录的变动。
 */
 
diff --git a/src/filesystem/dfilewatcher.h b/src/filesystem/dfilewatcher.h
deleted file mode 100644 (file)
index ba893a3..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#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
index 5edea197e4fd4e0bc12c687304088bb565e8a505..8ac05301ed85f1659b0d3171d0a4c2427dd3d75a 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dfilewatchermanager.h"
 #include "dfilewatcher.h"
diff --git a/src/filesystem/dfilewatchermanager.h b/src/filesystem/dfilewatchermanager.h
deleted file mode 100644 (file)
index 4021495..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DFILEWATCHERMANAGER_H
-#define DFILEWATCHERMANAGER_H
-
-#include "dtkcore_global.h"
-#include "dobject.h"
-
-#include <QObject>
-
-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<DFileWatcherManagerPrivate> d_ptr;
-
-    D_DECLARE_PRIVATE(DFileWatcherManager)
-    Q_DISABLE_COPY(DFileWatcherManager)
-};
-
-DCORE_END_NAMESPACE
-
-#endif // DFILEWATCHERMANAGER_H
index 02adf448fb0939a5624ddfbcf5867a50df3468b9..b4f6fecb775cf66060f7d9d45d5bd88c3cbde3b5 100644 (file)
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "dpathbuf.h"
 
 /*!
diff --git a/src/filesystem/dpathbuf.h b/src/filesystem/dpathbuf.h
deleted file mode 100644 (file)
index a4363b6..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <QDir>
-
-#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
index 798045b3cec79397c6e83eb598611e220f58604f..6348b7ce60accf4d1758c5d12215a229544674df 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2021 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dstandardpaths.h"
 
diff --git a/src/filesystem/dstandardpaths.h b/src/filesystem/dstandardpaths.h
deleted file mode 100644 (file)
index a2daf78..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2021 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DTK_CORE_FILESYSTEM_DSTANDARDPATHS_H
-#define DTK_CORE_FILESYSTEM_DSTANDARDPATHS_H
-
-#include <QStandardPaths>
-
-#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/src/filesystem/dtrashmanager.h b/src/filesystem/dtrashmanager.h
deleted file mode 100644 (file)
index d2c031e..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DTRASHMANAGER_H
-#define DTRASHMANAGER_H
-
-#include <dtkcore_global.h>
-#include <DObject>
-
-#include <QObject>
-
-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
index 054b7d7fd53019649b1724723b6eaac7ccc82db0..38853ac5aece673b2bad2e8134a36d59c89da8e8 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dtrashmanager.h"
 
index 38446a51f5a5e6b6b9146c22b1702361b05f6cb4..80a6dc23caef2aa1df049d12f399c5094ea3183a 100644 (file)
@@ -1,23 +1,10 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dtrashmanager.h"
 #include "dstandardpaths.h"
-#include "private/dobject_p.h"
+#include "base/private/dobject_p.h"
 
 #include <QDirIterator>
 #include <QStorageInfo>
diff --git a/src/filesystem/filesystem.cmake b/src/filesystem/filesystem.cmake
new file mode 100644 (file)
index 0000000..e8fad48
--- /dev/null
@@ -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/filesystem.pri b/src/filesystem/filesystem.pri
deleted file mode 100644 (file)
index 53411f0..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-include($$PWD/private/private.pri)
-
-INCLUDEPATH += $$PWD/../base
-
-INSTALL_PREFIX=$$QT_INSTALL_PREFIX
-isEmpty(INSTALL_PREFIX): INSTALL_PREFIX=$$[QT_INSTALL_PREFIX]
-DEFINES += PREFIX=\\\"$$INSTALL_PREFIX\\\"
-
-HEADERS += \
-    $$PWD/dbasefilewatcher.h \
-    $$PWD/dfilesystemwatcher.h \
-    $$PWD/dfilewatcher.h \
-    $$PWD/dfilewatchermanager.h \
-    $$PWD/dpathbuf.h \
-    $$PWD/dstandardpaths.h \
-    $$PWD/dtrashmanager.h
-
-SOURCES += \
-    $$PWD/dbasefilewatcher.cpp \
-    $$PWD/dfilewatcher.cpp \
-    $$PWD/dfilewatchermanager.cpp \
-    $$PWD/dstandardpaths.cpp \
-    $$PWD/dpathbuf.cpp
-
-linux {
-    SOURCES += \
-        $$PWD/dfilesystemwatcher_linux.cpp \
-        $$PWD/dtrashmanager_linux.cpp
-} else:win* {
-    SOURCES += \
-        $$PWD/dfilesystemwatcher_win.cpp \
-        $$PWD/dtrashmanager_dummy.cpp
-} else {
-    SOURCES += \
-        $$PWD/dfilesystemwatcher_dummy.cpp \
-        $$PWD/dtrashmanager_dummy.cpp
-}
-
-includes.files += $$PWD/*.h
-includes.files += \
-    $$PWD/DFileWatcher \
-    $$PWD/DBaseFileWatcher \
-    $$PWD/DFileSystemWatcher \
-    $$PWD/DFileWatcherManager \
-    $$PWD/DPathBuf \
-    $$PWD/DStandardPaths \
-    $$PWD/DTrashManager
index f200e9470ce1a071e6e854e8162db67faf13aefc..fac5027bea375880d3660473816afd2caac7f480 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// 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
diff --git a/src/filesystem/private/dcapfsfileengine_p.h b/src/filesystem/private/dcapfsfileengine_p.h
new file mode 100644 (file)
index 0000000..b032405
--- /dev/null
@@ -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 <DObject>
+
+#include <private/qfsfileengine_p.h>
+
+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
index ed97dd6f32f9697ca0f279a5968e67245c2f79e8..30638409a6ffd323d79c789c9d8e8bbdbc7150f8 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// 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
index 103c30735bb50745a02247c299815ff929c2b1cd..0f112be2e8e9e0f9262513a9e83884cb57f73cab 100644 (file)
@@ -1,24 +1,11 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// 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 "base/private/dobject_p.h"
+#include "dobject_p.h"
 
 #include <QSocketNotifier>
 #include <QHash>
index ed97dd6f32f9697ca0f279a5968e67245c2f79e8..30638409a6ffd323d79c789c9d8e8bbdbc7150f8 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// 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
index b31875f48a7f908de18e33344ceb5ab2eef6ed24..6fcc278ea550acb52b35667a5fca11c5b27dbdf4 100644 (file)
@@ -1,5 +1,6 @@
 HEADERS += \
-    $$PWD/dbasefilewatcher_p.h
+    $$PWD/dbasefilewatcher_p.h \
+    $$PWD/dcapfsfileengine_p.h
 
 linux {
     HEADERS += \
diff --git a/src/glob.cmake b/src/glob.cmake
new file mode 100644 (file)
index 0000000..9703bbd
--- /dev/null
@@ -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}
+)
index e6759e0d4a0563cf15856f02b7f407ad527a91f9..5ac16eb1959a993a650af61e4afa535f2eb651ed 100644 (file)
@@ -1,52 +1,35 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
- // Local
 #include "AbstractAppender.h"
 
-// Qt
-#include <QMutexLocker>
-
 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()
  */
@@ -56,64 +39,68 @@ DCORE_BEGIN_NAMESPACE
     \brief Constructs a AbstractAppender object.
  */
 AbstractAppender::AbstractAppender()
-  : m_detailsLevel(Logger::Debug)
-{}
+    :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;
+    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 AbstractAppender::setDetailsLevel(Logger::LogLevel level)
+void Dtk::Core::AbstractAppender::setDetailsLevel(Logger::LogLevel level)
 {
-  QMutexLocker locker(&m_detailsLevelMutex);
-  m_detailsLevel = 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 QStringlevel)
+void AbstractAppender::setDetailsLevel(const QString &level)
 {
-  setDetailsLevel(Logger::levelFromString(level));
+    setDetailsLevel(Logger::levelFromString(level));
 }
 
 /*!
@@ -121,53 +108,51 @@ void AbstractAppender::setDetailsLevel(const QString& level)
 
   This is the function called by Logger object to write a log \a message to the appender.
 
-  The \a timeStamp parameter indicates the time stamp.
-  The \a logLevel parameter describes the LogLevel.
+  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 function parameter indicates the function name to output.
+  The \a func parameter indicates the function name to output.
   The \a category parameter indicates the log category.
-  The \a message parameter indicates the output message.
-  
+  The \a msg parameter indicates the output message.
+
   \note This function is thread safe.
-  
+
   \sa Logger::write()
   \sa detailsLevel()
  */
-void AbstractAppender::write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
-                             const char* function, const QString& category, const QString& message)
+void AbstractAppender::write(const QDateTime &time, Logger::LogLevel level, const char *file, int line, const char *func, const QString &category, const QString &msg)
 {
-  if (logLevel >= detailsLevel())
-  {
+    if (level < detailsLevel())
+        return;
+
     QMutexLocker locker(&m_writeMutex);
-    append(timeStamp, logLevel, file, line, function, category, message);
-  }
+    append(time, level, file, line, func, category, msg);
 }
 
-
 /*!
-  \fn virtual void AbstractAppender::append(const QDateTime &timeStamp, Logger::LogLevel logLevel, const char *file, int line,
+  \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 timeStamp parameter indicates the time stamp.
-  The \a logLevel parameter describes the LogLevel.
+  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 function parameter indicates the function name to output.
+  The \a func parameter indicates the function name to output.
   The \a category parameter indicates the log category.
-  The \a message parameter indicates the output message.
-  
+  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()
  */
 
diff --git a/src/log/AbstractAppender.h b/src/log/AbstractAppender.h
deleted file mode 100644 (file)
index 91e9479..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1
-  as published by the Free Software Foundation and appearing in the file
-  LICENSE.LGPL included in the packaging of this file.
-
-  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 Lesser General Public License for more details.
-*/
-#ifndef ABSTRACTAPPENDER_H
-#define ABSTRACTAPPENDER_H
-
-// Local
-#include "CuteLogger_global.h"
-#include <Logger.h>
-
-// Qt
-#include <QMutex>
-
-DCORE_BEGIN_NAMESPACE
-
-class CUTELOGGERSHARED_EXPORT AbstractAppender
-{
-public:
-    AbstractAppender();
-    virtual ~AbstractAppender();
-
-    Logger::LogLevel detailsLevel() const;
-    void setDetailsLevel(Logger::LogLevel level);
-    void setDetailsLevel(const QString &level);
-
-    void write(const QDateTime &timeStamp, Logger::LogLevel logLevel, const char *file, int line, const char *function,
-               const QString &category, const QString &message);
-
-protected:
-    virtual void append(const QDateTime &timeStamp, Logger::LogLevel logLevel, const char *file, int line,
-                        const char *function, const QString &category, const QString &message) = 0;
-
-private:
-    QMutex m_writeMutex;
-
-    Logger::LogLevel m_detailsLevel;
-    mutable QMutex m_detailsLevelMutex;
-};
-
-DCORE_END_NAMESPACE
-
-#endif // ABSTRACTAPPENDER_H
index 9c9fb0a4726adc445f7bd6d7ea4f9df4c1b1d484..dcf4c376db085cc1bb0708b406effce500681b83 100644 (file)
@@ -1,25 +1,9 @@
-/*
-  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com) Nikolay Matyunin (matyunin.n at gmail dot com)
-
-  Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1
-  as published by the Free Software Foundation and appearing in the file
-  LICENSE.LGPL included in the packaging of this file.
-
-  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 Lesser General Public License for more details.
-*/
-// Local
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "AbstractStringAppender.h"
 
-// Qt
-#include <QReadLocker>
-#include <QWriteLocker>
-#include <QDateTime>
 #include <QRegExp>
 #include <QCoreApplication>
 #include <QThread>
@@ -29,65 +13,65 @@ 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"))
-{}
+    : 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;
+    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,
@@ -112,20 +96,19 @@ QString AbstractStringAppender::format() const
     \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 QStringformat)
+void AbstractStringAppender::setFormat(const QString &format)
 {
-  QWriteLocker locker(&m_formatLock);
-  m_format = format;
+    QWriteLocker locker(&m_formatLock);
+    m_format = format;
 }
 
-
 /*!
   \brief Strips the long function signature (as added by Q_FUNC_INFO macro).
 
@@ -136,345 +119,315 @@ void AbstractStringAppender::setFormat(const QString& format)
 
   \return stripped function name
  */
-QString AbstractStringAppender::stripFunctionName(const charname)
+QString AbstractStringAppender::stripFunctionName(const char *name)
 {
-  return QString::fromLatin1(qCleanupFuncinfo(name));
+    return QString::fromLatin1(qCleanupFuncinfo(name));
 }
 
-
 // The function was backported from Qt5 sources (qlogging.h)
-QByteArray AbstractStringAppender::qCleanupFuncinfo(const charname)
+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("::<lambda\\(.*\\)>");
-  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;
+    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("::<lambda\\(.*\\)>");
+    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 timeStamp The time stamp.
-  The \a logLevel parameter describes the LogLevel, and the \a file parameter is the current file name,
+  \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 function parameter indicates the function name to output.
+  The \a func parameter indicates the function name to output.
   The \a category parameter indicates the log category.
-  The \a message parameter indicates the output message.
+  The \a msg parameter indicates the output message.
 
   \sa format()
   \sa setFormat(const QString&)
  */
-QString AbstractStringAppender::formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file,
-                                                int line, const char* function, const QString& category, const QString& message) const
+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 f = format();
+    const int size = f.size();
 
-  QString result;
+    QString result;
 
-  int i = 0;
-  while (i < f.size())
-  {
-    QChar c = f.at(i);
+    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 = timeStamp.toString(f.mid(i + 2, j));
-
-            i += j;
+        // 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));
+            }
         }
-
-        if (chunk.isNull())
-          chunk = timeStamp.toString(QLatin1String("HH:mm:ss.zzz"));
-      }
-
-      // Log level
-      else if (command == QLatin1String("type"))
-        chunk = Logger::levelToString(logLevel);
-
-      // Uppercased log level
-      else if (command == QLatin1String("Type"))
-        chunk = Logger::levelToString(logLevel).toUpper();
-
-      // One letter log level
-      else if (command == QLatin1String("typeOne"))
-          chunk = Logger::levelToString(logLevel).left(1).toLower();
-
-      // One uppercase letter log level
-      else if (command == QLatin1String("TypeOne"))
-          chunk = Logger::levelToString(logLevel).left(1).toUpper();
-
-      // Filename
-      else if (command == QLatin1String("File"))
-        chunk = QLatin1String(file);
-
-      // Filename without a path
-      else if (command == QLatin1String("file"))
-        chunk = QString(QLatin1String(file)).section('/', -1);
-
-      // Source line number
-      else if (command == QLatin1String("line"))
-        chunk = QString::number(line);
-
-      // Function name, as returned by Q_FUNC_INFO
-      else if (command == QLatin1String("Function"))
-        chunk = QString::fromLatin1(function);
-
-      // Stripped function name
-      else if (command == QLatin1String("function"))
-        chunk = stripFunctionName(function);
-
-      // Log message
-      else if (command == QLatin1String("message"))
-        chunk = message;
-
-      else if (command == QLatin1String("category"))
-        chunk = category;
-
-      // Application pid
-      else if (command == QLatin1String("pid"))
-        chunk = QString::number(QCoreApplication::applicationPid());
-
-      // Application name
-      else if (command == QLatin1String("appname"))
-        chunk = QCoreApplication::applicationName();
-
-      // Thread ID (duplicates Qt5 threadid debbuging way)
-      else if (command == QLatin1String("threadid"))
-        chunk = QLatin1String("0x") + QString::number(qlonglong(QThread::currentThread()->currentThread()), 16);
-
-      // We simply replace the double formatting marker (%) with one
-      else if (command == QString(formattingMarker))
-        chunk = QLatin1Char(formattingMarker);
-
-      // Do not process any unknown commands
-      else
-      {
-        chunk = QString(formattingMarker);
-        chunk.append(command);
-      }
-
-      if (!chunk.isEmpty() && chunk != "0") {
-          result.append(QString(QLatin1String("%1")).arg(chunk, fieldWidth));
-      }
+        ++i;
     }
 
-    ++i;
-  }
-
-  return result;
+    return result;
 }
 
 DCORE_END_NAMESPACE
diff --git a/src/log/AbstractStringAppender.h b/src/log/AbstractStringAppender.h
deleted file mode 100644 (file)
index b79f194..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1
-  as published by the Free Software Foundation and appearing in the file
-  LICENSE.LGPL included in the packaging of this file.
-
-  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 Lesser General Public License for more details.
-*/
-#ifndef ABSTRACTSTRINGAPPENDER_H
-#define ABSTRACTSTRINGAPPENDER_H
-
-// Local
-#include "CuteLogger_global.h"
-#include <AbstractAppender.h>
-
-// Qt
-#include <QReadWriteLock>
-
-DCORE_BEGIN_NAMESPACE
-
-class CUTELOGGERSHARED_EXPORT AbstractStringAppender : public AbstractAppender
-{
-  public:
-    AbstractStringAppender();
-
-    virtual QString format() const;
-    void setFormat(const QString&);
-
-    static QString stripFunctionName(const char*);
-
-  protected:
-    QString formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
-                            const char* function, const QString& category, const QString& message) const;
-
-  private:
-    static QByteArray qCleanupFuncinfo(const char*);
-
-    QString m_format;
-    mutable QReadWriteLock m_formatLock;
-};
-
-DCORE_END_NAMESPACE
-
-#endif // ABSTRACTSTRINGAPPENDER_H
index d074658dd6250cdc60852d74e9d64e4bf7f6a39a..b3bc852b4531ed8a819725512d1b863797263cbf 100644 (file)
@@ -1,16 +1,7 @@
-/*
-  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1
-  as published by the Free Software Foundation and appearing in the file
-  LICENSE.LGPL included in the packaging of this file.
-
-  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 Lesser General Public License for more details.
-*/
 // Local
 #include "ConsoleAppender.h"
 
@@ -26,7 +17,7 @@ DCORE_BEGIN_NAMESPACE
   \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 timestamp.
+  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
@@ -35,43 +26,43 @@ DCORE_BEGIN_NAMESPACE
 
 
 ConsoleAppender::ConsoleAppender()
-  : AbstractStringAppender(),
-    m_ignoreEnvPattern(false)
+    : AbstractStringAppender()
+    ,m_ignoreEnvPattern(false)
 {
-  setFormat("[%{type:-7}] <%{function}> %{message}\n");
+    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");
+    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;
+   m_ignoreEnvPattern = ignore;
 }
 
 /*!
   \brief Writes the log record to the std::cerr stream.
   \reimp
 
-  The \a timeStamp parameter indicates the time stamp.
-  The \a logLevel parameter describes the LogLevel.
+  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 function parameter indicates the function name to output.
+  The \a func parameter indicates the function name to output.
   The \a category parameter indicates the log category.
-  The \a message parameter indicates the output message.
+  The \a msg parameter indicates the output message.
 
   \sa AbstractStringAppender::format()
  */
-void ConsoleAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
-                             const char* function, const QString& category, const QString& message)
+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(timeStamp, logLevel, file, line, function, category, message));
+    std::cerr << qPrintable(formattedString(time, level, file, line, func, category, msg));
 }
 
 DCORE_END_NAMESPACE
diff --git a/src/log/ConsoleAppender.h b/src/log/ConsoleAppender.h
deleted file mode 100644 (file)
index 26fe8e5..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1
-  as published by the Free Software Foundation and appearing in the file
-  LICENSE.LGPL included in the packaging of this file.
-
-  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 Lesser General Public License for more details.
-*/
-#ifndef CONSOLEAPPENDER_H
-#define CONSOLEAPPENDER_H
-
-#include "CuteLogger_global.h"
-#include <AbstractStringAppender.h>
-
-DCORE_BEGIN_NAMESPACE
-
-class CUTELOGGERSHARED_EXPORT ConsoleAppender : public AbstractStringAppender
-{
-  public:
-    ConsoleAppender();
-    virtual QString format() const;
-    void ignoreEnvironmentPattern(bool ignore);
-
-  protected:
-    virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
-                        const char* function, const QString& category, const QString& message);
-
-  private:
-    bool m_ignoreEnvPattern;
-};
-
-DCORE_END_NAMESPACE
-
-#endif // CONSOLEAPPENDER_H
diff --git a/src/log/CuteLogger_global.h b/src/log/CuteLogger_global.h
deleted file mode 100644 (file)
index aefc915..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef CUTELOGGER_GLOBAL_H
-#define CUTELOGGER_GLOBAL_H
-
-#include "dtkcore_global.h"
-
-#if defined(CUTELOGGER_LIBRARY)
-#  define CUTELOGGERSHARED_EXPORT Q_DECL_EXPORT
-#else
-#if defined(Q_OS_WIN32)
-#  define CUTELOGGERSHARED_EXPORT
-#else
-#  define CUTELOGGERSHARED_EXPORT Q_DECL_IMPORT
-#endif
-#endif
-
-#endif // CUTELOGGER_GLOBAL_H
diff --git a/src/log/DLog b/src/log/DLog
deleted file mode 100644 (file)
index 774c2fc..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "CuteLogger_global.h"
-#include "RollingFileAppender.h"
-#include "Logger.h"
-#include "LogManager.h"
-#include "FileAppender.h"
-#include "ConsoleAppender.h"
-#include "AbstractStringAppender.h"
-#include "AbstractAppender.h"
index 4daec734184817f5153061cb4f9aa35090fcac85..971ca23277c8bcf98946f5722db874831291e0d8 100644 (file)
@@ -1,20 +1,9 @@
-/*
-  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1
-  as published by the Free Software Foundation and appearing in the file
-  LICENSE.LGPL included in the packaging of this file.
-
-  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 Lesser General Public License for more details.
-*/
-// Local
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "FileAppender.h"
 
-// STL
 #include <iostream>
 
 DCORE_BEGIN_NAMESPACE
@@ -30,15 +19,15 @@ DCORE_BEGIN_NAMESPACE
 /*!
     \brief Constructs the new file appender assigned to file with the given \a fileName.
  */
-FileAppender::FileAppender(const QStringfileName)
+FileAppender::FileAppender(const QString &fileName)
 {
-  setFileName(fileName);
+    setFileName(fileName);
 }
 
 
 FileAppender::~FileAppender()
 {
-  closeFile();
+    closeFile();
 }
 
 /*!
@@ -48,8 +37,8 @@ FileAppender::~FileAppender()
  */
 QString FileAppender::fileName() const
 {
-  QMutexLocker locker(&m_logFileMutex);
-  return m_logFile.fileName();
+    QMutexLocker locker(&m_logFileMutex);
+    return m_logFile.fileName();
 }
 
 /*!
@@ -57,13 +46,13 @@ QString FileAppender::fileName() const
 
   \sa fileName()
  */
-void FileAppender::setFileName(const QStrings)
+void FileAppender::setFileName(const QString &s)
 {
-  QMutexLocker locker(&m_logFileMutex);
-  if (m_logFile.isOpen())
-    m_logFile.close();
+    QMutexLocker locker(&m_logFileMutex);
+    if (m_logFile.isOpen())
+        m_logFile.close();
 
-  m_logFile.setFileName(s);
+    m_logFile.setFileName(s);
 }
 
 qint64 FileAppender::size() const
@@ -74,51 +63,50 @@ qint64 FileAppender::size() const
 
 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 << "<FileAppender::append> Cannot open the log file " << qPrintable(m_logFile.fileName()) << std::endl;
-  }
-  return isOpen;
+    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 << "<FileAppender::append> Cannot open the log file " << qPrintable(m_logFile.fileName()) << std::endl;
+    }
+    return isOpen;
 }
 
 /*!
   \brief Write the log record to the file.
   \reimp
 
-  The \a timeStamp parameter indicates the time stamp.
-  The \a logLevel parameter describes the LogLevel.
+  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 function parameter indicates the function name to output.
+  The \a func parameter indicates the func name to output.
   The \a category parameter indicates the log category.
-  The \a message parameter indicates the output message.
+  The \a msg parameter indicates the output message.
 
   \sa fileName()
   \sa AbstractStringAppender::format()
  */
-void FileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
-                          const char* function, const QString& category, const QString& message)
+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(timeStamp, logLevel, file, line, function, category, message);
-    m_logStream.flush();
-    m_logFile.flush();
-  }
+    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();
+    QMutexLocker locker(&m_logFileMutex);
+    m_logFile.close();
 }
 
 DCORE_END_NAMESPACE
diff --git a/src/log/FileAppender.h b/src/log/FileAppender.h
deleted file mode 100644 (file)
index c122580..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1
-  as published by the Free Software Foundation and appearing in the file
-  LICENSE.LGPL included in the packaging of this file.
-
-  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 Lesser General Public License for more details.
-*/
-#ifndef FILEAPPENDER_H
-#define FILEAPPENDER_H
-
-// Logger
-#include "CuteLogger_global.h"
-#include <AbstractStringAppender.h>
-
-// Qt
-#include <QFile>
-#include <QTextStream>
-
-DCORE_BEGIN_NAMESPACE
-
-class CUTELOGGERSHARED_EXPORT FileAppender : public AbstractStringAppender
-{
-  public:
-    FileAppender(const QString& fileName = QString());
-    ~FileAppender();
-
-    QString fileName() const;
-    void setFileName(const QString&);
-
-    qint64 size() const;
-
-  protected:
-    virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
-                        const char* function, const QString& category, const QString& message);
-    bool openFile();
-    void closeFile();
-
-  private:
-    QFile m_logFile;
-    QTextStream m_logStream;
-    mutable QMutex m_logFileMutex;
-};
-
-DCORE_END_NAMESPACE
-
-#endif // FILEAPPENDER_H
diff --git a/src/log/LICENSE b/src/log/LICENSE
deleted file mode 100644 (file)
index 9cecc1d..0000000
+++ /dev/null
@@ -1,674 +0,0 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- 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.
-
-    {one line to give the program's name and a brief idea of what it does.}
-    Copyright (C) {year}  {name of author}
-
-    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 <http://www.gnu.org/licenses/>.
-
-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:
-
-    {project}  Copyright (C) {year}  {fullname}
-    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
-<http://www.gnu.org/licenses/>.
-
-  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
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
index a2637333203407fb9bb2a794e4607b7a0238e7de..6749f72903b863830abd46388b29211d81af07e5 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "LogManager.h"
 #include <Logger.h>
diff --git a/src/log/LogManager.h b/src/log/LogManager.h
deleted file mode 100644 (file)
index d2f1bf0..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LOGMANAGER_H
-#define LOGMANAGER_H
-
-#include <QtCore>
-
-#include "CuteLogger_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
index 58e8a2ca8d89d85cc39649ac28bf2f60a257a3d4..552976042e1d656ee13762794ce63c2f4c1da3ac 100644 (file)
@@ -1,35 +1,19 @@
-/*
-  Copyright (c) 2012 Boris Moiseev (cyberbobs at gmail dot com)
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1
-  as published by the Free Software Foundation and appearing in the file
-  LICENSE.LGPL included in the packaging of this file.
-
-  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 Lesser General Public License for more details.
-*/
-// Local
+// 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"
 
-// Qt
 #include <QCoreApplication>
 #include <QReadWriteLock>
 #include <QSemaphore>
+#include <QMutex>
 #include <QDateTime>
 #include <QIODevice>
 #include <QTextCodec>
 
-#if defined(Q_OS_ANDROID)
-#  include <android/log.h>
-#  include <AndroidAppender.h>
-#endif
-
-// STL
 #include <iostream>
 
 DCORE_BEGIN_NAMESPACE
@@ -44,138 +28,131 @@ DCORE_BEGIN_NAMESPACE
   \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 charargument or returning the QDebug class
+  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()
@@ -190,73 +167,68 @@ DCORE_BEGIN_NAMESPACE
   \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
@@ -297,7 +269,6 @@ DCORE_BEGIN_NAMESPACE
   \sa Dtk::Core::Logger::setDefaultCategory()
  */
 
-
 /*!
   \macro Dtk::Core::dGlobalCategory(category)
   \relates Dtk::Core::Logger
@@ -305,13 +276,13 @@ DCORE_BEGIN_NAMESPACE
 
   \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()
@@ -319,41 +290,38 @@ DCORE_BEGIN_NAMESPACE
   \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()
@@ -363,55 +331,53 @@ DCORE_BEGIN_NAMESPACE
     return 0;
   } // Outputs: Function foo finished in <time> ms.
   \endcode
-  
+
   If you are measuring a code of block execution time you may also add a name of block to the macro:
   \code
   int bar(bool doFoo)
   {
     dTraceTime();
-  
+
     if (doFoo)
     {
       dTraceTime("Foo");
       ...
     }
-  
+
     ...
   }
   // Outputs:
   // "Foo" finished in <time1> ms.
   // Function bar finished in <time2> ms.
   \endcode
-  
+
   \note Macro switches to logging the seconds instead of milliseconds when the execution time reaches 10000 ms.
   \sa Dtk::Core::dDebugTime, Dtk::Core::dInfoTime
  */
 
-
 /*!
   \macro Dtk::Core::dDebugTime
   \relates Dtk::Core::Logger
   \keyword Dtk::Core::dDebugTime
-  
+
   \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::Debug
   level log record. It works similar to dTraceTime() macro.
-  
+
   \sa Dtk::Core::dTraceTime
  */
 
-
 /*!
   \macro Dtk::Core::dInfoTime
   \relates Dtk::Core::Logger
   \keyword Dtk::Core::dInfoTime
-  
+
   \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::Info
   level log record. It works similar to dTraceTime() macro.
-  
+
   \sa Dtk::Core::dTraceTime
  */
 
@@ -434,89 +400,92 @@ DCORE_BEGIN_NAMESPACE
 /*!
   \class Dtk::Core::Logger
   \inmodule dtkcore
-  
+
   \brief Very simple but rather powerful component which may be used for logging your application activities.
-  
+
   Global logger instance created on a first access to it (e.g. registering appenders, calling a dDebug() macro
   etc.) registers itself as a Qt default message handler and captures all the qDebug/dWarning/qCritical output.
-  
+
   \note Qt 4 qDebug set of macro doesn't support capturing source function name, file name or line number so we
         recommend to use dDebug() and other Logger macros instead.
-  
+
   \sa Dtk::Core::logger
  */
 
 class LogDevice : public QIODevice
 {
-  public:
+public:
     LogDevice(Logger* l)
-      : m_logger(l),
-        m_semaphore(1)
+        : m_logger(l),
+          m_semaphore(1)
     {}
 
-    void lock(Logger::LogLevel logLevel, const char* file, int line, const char* function, const char* category)
+    void lock(Logger::LogLevel level, const char *file, int line,
+              const char *func, const char *category)
     {
-      m_semaphore.acquire();
+        m_semaphore.acquire();
 
-      if (!isOpen())
-        open(QIODevice::WriteOnly);
+        if (!isOpen())
+            open(QIODevice::WriteOnly);
 
-      m_logLevel = logLevel;
-      m_file = file;
-      m_line = line;
-      m_function = function;
-      m_category = category;
+        m_logLevel = level;
+        m_file = file;
+        m_line = line;
+        m_function = func;
+        m_category = category;
     }
 
-  protected:
+protected:
     qint64 readData(char*, qint64)
     {
-      return 0;
+        return 0;
     }
 
-    qint64 writeData(const chardata, qint64 maxSize)
+    qint64 writeData(const char *data, qint64 maxSize)
     {
-      if (maxSize > 0)
-        m_logger->write(m_logLevel, m_file, m_line, m_function, m_category, QString::fromLocal8Bit(QByteArray(data, maxSize)));
-
-      m_semaphore.release();
-      return maxSize;
+        if (maxSize > 0) {
+            const QString & msg = QString::fromLocal8Bit(QByteArray(data, int(maxSize)));
+            m_logger->write(m_logLevel, m_file, m_line,
+                            m_function, m_category, msg);
+        }
+
+        m_semaphore.release();
+        return maxSize;
     }
 
-  private:
+private:
     Logger* m_logger;
     QSemaphore m_semaphore;
     Logger::LogLevel m_logLevel;
-    const charm_file;
+    const char *m_file;
     int m_line;
-    const charm_function;
-    const charm_category;
+    const char *m_function;
+    const char *m_category;
 };
 
-
 // Forward declarations
-static void cleanupLoggerGlobalInstance();
+//static void cleanupLoggerGlobalInstance();
 
-#if QT_VERSION >= 0x050000
-static void qtLoggerMessageHandler(QtMsgType, const QMessageLogContext& context, const QString& msg);
-#else
-static void qtLoggerMessageHandler(QtMsgType type, const char* msg);
-#endif
+//#if QT_VERSION >= 0x050000
+//static void qtLoggerMessageHandler(QtMsgType, const QMessageLogContext& context, const QString &msg);
+//#else
+//static void qtLoggerMessageHandler(QtMsgType type, const char *msg);
+//#endif
 
 /*!
   \internal
-  
+
   LoggerPrivate class implements the Singleton pattern in a thread-safe way. It contains a static pointer to the
   global logger instance protected by QReadWriteLock
  */
 class LoggerPrivate
 {
-  public:
+public:
     static Logger* globalInstance;
     static QReadWriteLock globalInstanceLock;
 
     QList<AbstractAppender*> appenders;
-    QMutex loggerMutex;
+    mutable QMutex loggerMutex;
 
     QMap<QString, bool> categories;
     QMultiMap<QString, AbstractAppender*> categoryAppenders;
@@ -525,75 +494,70 @@ class LoggerPrivate
     LogDevice* logDevice;
 };
 
-
-// Static fields initialization
-Logger* LoggerPrivate::globalInstance = 0;
+Logger* LoggerPrivate::globalInstance = nullptr;
 QReadWriteLock LoggerPrivate::globalInstanceLock;
 
-
 static void cleanupLoggerGlobalInstance()
 {
-  QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
+    QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
 
-  delete LoggerPrivate::globalInstance;
-  LoggerPrivate::globalInstance = 0;
+    delete LoggerPrivate::globalInstance;
+    LoggerPrivate::globalInstance = nullptr;
 }
 
-
 #if QT_VERSION >= 0x050000
-static void qtLoggerMessageHandler(QtMsgType type, const QMessageLogContext& context, const QStringmsg)
+static void qtLoggerMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg)
 {
-  Logger::LogLevel level;
-  switch (type)
-  {
+    Logger::LogLevel level = Logger::Warning;
+    switch (type)
+    {
     case QtDebugMsg:
-      level = Logger::Debug;
-      break;
+        level = Logger::Debug;
+        break;
 #if QT_VERSION >= 0x050500
     case QtInfoMsg:
-      level = Logger::Info;
-      break;
+        level = Logger::Info;
+        break;
 #endif
     case QtWarningMsg:
-      level = Logger::Warning;
-      break;
+        level = Logger::Warning;
+        break;
     case QtCriticalMsg:
-      level = Logger::Error;
-      break;
+        level = Logger::Error;
+        break;
     case QtFatalMsg:
-      level = Logger::Fatal;
-      break;
-  default:
-      level = Logger::Warning;
-      break;
-  }
+        level = Logger::Fatal;
+        break;
+    }
 
-  bool isDefaultCategory = QString::fromLatin1(context.category) == "default";
-  Logger::globalInstance()->write(level, context.file, context.line, context.function, isDefaultCategory ? 0 : context.category, msg);
+    bool isDefaultCategory = QString::fromLatin1(context.category) == "default";
+    Logger::globalInstance()->write(level, context.file, context.line, context.function,
+                                    isDefaultCategory ? nullptr : context.category, msg);
 }
 
 #else
 
-static void qtLoggerMessageHandler(QtMsgType type, const charmsg)
+static void qtLoggerMessageHandler(QtMsgType type, const char *msg)
 {
-  switch (type)
-  {
+    switch (type)
+    {
     case QtDebugMsg:
-      loggerInstance()->write(Logger::Debug, "", 0, "qDebug", 0, msg);
-      break;
+        loggerInstance()->write(Logger::Debug, "", 0, "qDebug", 0, msg);
+        break;
     case QtWarningMsg:
-      loggerInstance()->write(Logger::Warning, "", 0, "qDebug", 0, msg);
-      break;
+        loggerInstance()->write(Logger::Warning, "", 0, "qDebug", 0, msg);
+        break;
     case QtCriticalMsg:
-      loggerInstance()->write(Logger::Error, "", 0, "qDebug", 0, msg);
-      break;
+        loggerInstance()->write(Logger::Error, "", 0, "qDebug", 0, msg);
+        break;
     case QtFatalMsg:
-      loggerInstance()->write(Logger::Fatal, "", 0, "qDebug", 0, msg);
-      break;
-  }
+        loggerInstance()->write(Logger::Fatal, "", 0, "qDebug", 0, msg);
+        break;
+    }
 }
 #endif
 
+
 /*!
   \brief Construct the instance of Logger.
 
@@ -601,11 +565,10 @@ static void qtLoggerMessageHandler(QtMsgType type, const char* msg)
   Consider using [logger](@ref logger) macro instead to access the logger instance
  */
 Logger::Logger()
-  : d_ptr(new LoggerPrivate)
+    : d_ptr(new LoggerPrivate)
 {
-  Q_D(Logger);
-
-  d->logDevice = new LogDevice(this);
+    Q_D(Logger);
+    d->logDevice = new LogDevice(this);
 }
 
 /*!
@@ -613,17 +576,14 @@ Logger::Logger()
 
   If you're only using one global instance of logger you wouldn't probably need to use this constructor manually.
   Consider using logger macro instead to access the logger instance and call setDefaultCategory method.
-  
+
   \sa Logger()
   \sa setDefaultCategory()
  */
-Logger::Logger(const QStringdefaultCategory)
-  : d_ptr(new LoggerPrivate)
+Logger::Logger(const QString &defaultCategory)
+    :Logger()
 {
-  Q_D(Logger);
-  d->logDevice = new LogDevice(this);
-
-  setDefaultCategory(defaultCategory);
+    setDefaultCategory(defaultCategory);
 }
 
 /*!
@@ -634,49 +594,79 @@ Logger::Logger(const QString& defaultCategory)
  */
 Logger::~Logger()
 {
-  Q_D(Logger);
+    Q_D(Logger);
+
+    QMutexLocker appendersLocker(&d->loggerMutex);
+
+    QSet<AbstractAppender*> appenderList;
+    appenderList += d->appenders.toSet() += d->categoryAppenders.values().toSet();
+    qDeleteAll(appenderList);
+
+    delete d->logDevice;
+    appendersLocker.unlock();
+
+    delete d_ptr;
+}
+
+/*!
+  \brief Returns the global instance of Logger.
 
-  // Cleanup appenders
-  QMutexLocker appendersLocker(&d->loggerMutex);
+  In a most cases you shouldn't use this function directly. Consider using [logger](@ref logger) macro instead.
 
-  QSet<AbstractAppender*> appenderList;
-  appenderList += d->appenders.toSet() += d->categoryAppenders.values().toSet();
-  qDeleteAll(appenderList);
+  \sa Dtk::Core::logger
+ */
+Logger *Logger::globalInstance()
+{
+    Logger* result = nullptr;
+    {
+        QReadLocker locker(&LoggerPrivate::globalInstanceLock);
+        result = LoggerPrivate::globalInstance;
+    }
+
+    if (!result)
+    {
+        QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
+        LoggerPrivate::globalInstance = new Logger;
 
-  // Cleanup device
-  delete d->logDevice;
-  appendersLocker.unlock();
+#if QT_VERSION >= 0x050000
+        qInstallMessageHandler(qtLoggerMessageHandler);
+#else
+        qInstallMsgHandler(qtLoggerMessageHandler);
+#endif
+        qAddPostRoutine(cleanupLoggerGlobalInstance);
+        result = LoggerPrivate::globalInstance;
+    }
 
-  delete d_ptr;
+    return result;
 }
 
 /*!
   \brief Converts the LogLevel enum value to its string representation.
 
   \a logLevel Log level to convert
-  
+
   \sa LogLevel
   \sa levelFromString()
  */
-QString Logger::levelToString(Logger::LogLevel logLevel)
+QString Logger::levelToString(Logger::LogLevel level)
 {
-  switch (logLevel)
-  {
+    switch (level)
+    {
     case Trace:
-      return QLatin1String("Trace");
+        return QLatin1String("Trace");
     case Debug:
-      return QLatin1String("Debug");
+        return QLatin1String("Debug");
     case Info:
-      return QLatin1String("Info");
+        return QLatin1String("Info");
     case Warning:
-      return QLatin1String("Warning");
+        return QLatin1String("Warning");
     case Error:
-      return QLatin1String("Error");
+        return QLatin1String("Error");
     case Fatal:
-      return QLatin1String("Fatal");
-  }
+        return QLatin1String("Fatal");
+    }
 
-  return QString();
+    return QString();
 }
 
 /*!
@@ -684,64 +674,32 @@ QString Logger::levelToString(Logger::LogLevel logLevel)
 
   Comparation of the strings is case independent. If the log level string provided cannot be understood
   Logger::Debug is returned.
-  
+
   \a s String to be decoded
-  
+
   \sa LogLevel
   \sa levelToString()
  */
-Logger::LogLevel Logger::levelFromString(const QString& s)
-{
-  QString str = s.trimmed().toLower();
-
-  LogLevel result = Debug;
-
-  if (str == QLatin1String("trace"))
-    result = Trace;
-  else if (str == QLatin1String("debug"))
-    result = Debug;
-  else if (str == QLatin1String("info"))
-    result = Info;
-  else if (str == QLatin1String("warning"))
-    result = Warning;
-  else if (str == QLatin1String("error"))
-    result = Error;
-  else if (str == QLatin1String("fatal"))
-    result = Fatal;
-
-  return result;
-}
-
-/*!
-  \brief Returns the global instance of Logger.
-
-  In a most cases you shouldn't use this function directly. Consider using [logger](@ref logger) macro instead.
-  
-  \sa Dtk::Core::logger
- */
-Logger* Logger::globalInstance()
+Logger::LogLevel Logger::levelFromString(const QString &str)
 {
-  Logger* result = 0;
-  {
-    QReadLocker locker(&LoggerPrivate::globalInstanceLock);
-    result = LoggerPrivate::globalInstance;
-  }
-
-  if (!result)
-  {
-    QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
-    LoggerPrivate::globalInstance = new Logger;
-
-#if QT_VERSION >= 0x050000
-    qInstallMessageHandler(qtLoggerMessageHandler);
-#else
-    qInstallMsgHandler(qtLoggerMessageHandler);
-#endif
-    qAddPostRoutine(cleanupLoggerGlobalInstance);
-    result = LoggerPrivate::globalInstance;
-  }
-
-  return result;
+    const QString &s = str.trimmed().toLower();
+
+    LogLevel result = Debug;
+
+    if (s == QLatin1String("trace"))
+        result = Trace;
+    else if (s == QLatin1String("debug"))
+        result = Debug;
+    else if (s == QLatin1String("info"))
+        result = Info;
+    else if (s == QLatin1String("warning"))
+        result = Warning;
+    else if (s == QLatin1String("error"))
+        result = Error;
+    else if (s == QLatin1String("fatal"))
+        result = Fatal;
+
+    return result;
 }
 
 /*!
@@ -750,27 +708,26 @@ Logger* Logger::globalInstance()
   On the log writing call (using one of the macros or the write() function) Logger traverses through the list of
   the appenders and writes a log records to the each of them. Please, look through the AbstractAppender
   documentation to understand the concept of appenders.
-  
+
   If no appenders was added to Logger, it falls back to logging into the \c std::cerr STL stream.
-  
+
   \a appender Appender to register in the Logger
-  
+
   \note Logger takes ownership on the appender and it will delete it on the application exit. According to this,
         appenders must be created on heap to prevent double destruction of the appender.
-  
+
   \sa registerCategoryAppender
   \sa AbstractAppender
  */
-void Logger::registerAppender(AbstractAppenderappender)
+void Logger::registerAppender(AbstractAppender *appender)
 {
-  Q_D(Logger);
-
-  QMutexLocker locker(&d->loggerMutex);
+    Q_D(Logger);
+    QMutexLocker locker(&d->loggerMutex);
 
-  if (!d->appenders.contains(appender))
-    d->appenders.append(appender);
-  else
-    std::cerr << "Trying to register appender that was already registered" << std::endl;
+    if (!d->appenders.contains(appender))
+        d->appenders.append(appender);
+    else
+        std::cerr << "Trying to register appender that was already registered" << std::endl;
 }
 
 /*!
@@ -780,34 +737,61 @@ void Logger::registerAppender(AbstractAppender* appender)
   On the log writing call to the specific category (calling write() with category parameter directly,
   writing to the default category, or using special dCDebug(), dCWarning() etc. macros),
   Logger writes the log message only to the list of registered category appenders.
-  
+
   You can call logToGlobalInstance() to pass all category log messages to the global logger instance appenders
   (registered using registerAppender()).
   If no category appenders with specific name was registered to the Logger,
   it falls back to logging into the \c std::cerr STL stream, both with simple warning message.
-  
+
   \a category Category name
   \a appender Appender to register in the Logger
-  
+
   \note Logger takes ownership on the appender and it will delete it on the application exit. According to this,
         appenders must be created on heap to prevent double destruction of the appender.
-  
+
   \sa registerAppender
   \sa Dtk::Core::dCTrace(), Dtk::Core::dCDebug(), Dtk::Core::dCInfo(), Dtk::Core::dCWarning(), Dtk::Core::dCError(), Dtk::Core::dCFatal()
   \sa Dtk::Core::dCategory(), Dtk::Core::dGlobalCategory()
   \sa logToGlobalInstance()
   \sa setDefaultCategory()
  */
-void Logger::registerCategoryAppender(const QString& category, AbstractAppender* appender)
+void Logger::registerCategoryAppender(const QString &category, AbstractAppender *appender)
 {
-  Q_D(Logger);
+    Q_D(Logger);
+    QMutexLocker locker(&d->loggerMutex);
+
+    if (!d->categoryAppenders.contains(category, appender))
+        d->categoryAppenders.insert(category, appender);
+    else
+        std::cerr << "Trying to register category [" << qPrintable(category) <<
+                     "] appender that was already registered" << std::endl;
+}
+
+/*!
+  \brief Links some logging category with the global logger instance appenders.
+
+  If set to true, all log messages to the specified category appenders will also be written to the global logger instance appenders,
+  registered using registerAppender().
+
+  By default, all messages to the specific category are written only to the specific category appenders
+  (registered using registerCategoryAppender()).
 
-  QMutexLocker locker(&d->loggerMutex);
+  \a category Category name
+  \a logToGlobal Link or onlink the category from global logger instance appender
 
-  if (!d->categoryAppenders.contains(category, appender))
-    d->categoryAppenders.insert(category, appender);
-  else
-    std::cerr << "Trying to register appender that was already registered" << std::endl;
+  \sa globalInstance
+  \sa registerAppender
+  \sa registerCategoryAppender
+ */
+void Logger::logToGlobalInstance(const QString &category, bool logToGlobal)
+{
+    Q_D(Logger);
+    if (this == globalInstance()) {
+        QMutexLocker locker(&d->loggerMutex);
+        d->categories.insert(category, logToGlobal);
+    } else {
+        globalInstance()->logToGlobalInstance(category, logToGlobal);
+    }
 }
 
 /*!
@@ -817,25 +801,23 @@ void Logger::registerCategoryAppender(const QString& category, AbstractAppender*
   using [registerAppender](@ref registerAppender) method), and vice versa.
   In particular, any calls to the dDebug() macro will be treated as category logging,
   so you needn't to specify category name using dCDebug() macro.
-  
+
   To unset the default category, pass a null string as a parameter.
-  
+
   \a category Category name
-  
+
   \note "category" format marker will be set to the category name for all of these messages
   (see [AbstractStringAppender::setFormat](@ref AbstractStringAppender::setFormat)).
-  
+
   \sa defaultCategory()
   \sa registerCategoryAppender()
   \sa logToGlobalInstance()
  */
-void Logger::setDefaultCategory(const QStringcategory)
+void Logger::setDefaultCategory(const QString &category)
 {
-  Q_D(Logger);
-
-  QMutexLocker locker(&d->loggerMutex);
-
-  d->defaultCategory = category;
+    Q_D(Logger);
+    QMutexLocker locker(&d->loggerMutex);
+    d->defaultCategory = category;
 }
 
 //! Returns default logging category name
@@ -844,165 +826,57 @@ void Logger::setDefaultCategory(const QString& category)
  */
 QString Logger::defaultCategory() const
 {
-  Q_D(const Logger);
-  return d->defaultCategory;
-}
-
-/*!
-  \brief Links some logging category with the global logger instance appenders.
-
-  If set to true, all log messages to the specified category appenders will also be written to the global logger instance appenders,
-  registered using registerAppender().
-  
-  By default, all messages to the specific category are written only to the specific category appenders
-  (registered using registerCategoryAppender()).
-  
-  \a category Category name
-  \a logToGlobal Link or onlink the category from global logger instance appender
-  
-  \sa globalInstance
-  \sa registerAppender
-  \sa registerCategoryAppender
- */
-void Logger::logToGlobalInstance(const QString& category, bool logToGlobal)
-{
-  Q_D(Logger);
-
-  if (this == globalInstance())
-  {
+    Q_D(const Logger);
     QMutexLocker locker(&d->loggerMutex);
-    d->categories.insert(category, logToGlobal);
-  }
-  else
-  {
-    globalInstance()->logToGlobalInstance(category, logToGlobal);
-  }
-}
-
-
-void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
-                   const QString& message, bool fromLocalInstance)
-{
-  Q_D(Logger);
-
-  QMutexLocker locker(&d->loggerMutex);
-
-  QString logCategory = QString::fromLatin1(category);
-  if (logCategory.isNull() && !d->defaultCategory.isNull())
-    logCategory = d->defaultCategory;
-
-  bool wasWritten = false;
-  bool isGlobalInstance = this == globalInstance();
-  bool linkedToGlobal = isGlobalInstance && d->categories.value(logCategory, false);
-
-  if (!logCategory.isNull())
-  {
-    QList<AbstractAppender*> appenders = d->categoryAppenders.values(logCategory);
-    if (appenders.length() == 0)
-    {
-      if (logCategory != d->defaultCategory && !linkedToGlobal && !fromLocalInstance)
-        std::cerr << "No appenders assotiated with category " << qPrintable(logCategory) << std::endl;
-    }
-    else
-    {
-      Q_FOREACH (AbstractAppender* appender, appenders)
-        appender->write(timeStamp, logLevel, file, line, function, logCategory, message);
-      wasWritten = true;
-    }
-  }
-
-  // the default category is linked to the main logger appenders
-  // global logger instance also writes all linked categories to the main appenders
-  if (logCategory.isNull() || logCategory == d->defaultCategory || linkedToGlobal)
-  {
-    if (!d->appenders.isEmpty())
-    {
-      Q_FOREACH (AbstractAppender* appender, d->appenders)
-        appender->write(timeStamp, logLevel, file, line, function, logCategory, message);
-      wasWritten = true;
-    }
-    else
-    {
-      static bool noAppendersWarningShown = false;
-      if (!noAppendersWarningShown)
-      {
-#if defined(Q_OS_ANDROID)
-        __android_ write(ANDROID_LOG_WARN, "Logger", "No appenders registered with logger");
-#else
-        std::cerr << "No appenders registered with logger" << std::endl;
-#endif
-        noAppendersWarningShown = true;
-      }
-    }
-  }
-
-  // local logger instances send category messages to the global instance
-  if (!logCategory.isNull() && !isGlobalInstance)
-    globalInstance()->write(timeStamp, logLevel, file, line, function, logCategory.toLatin1(), message, true);
-
-  if (!wasWritten && !fromLocalInstance)
-  {
-    // Fallback
-#if defined(Q_OS_ANDROID)
-    QString result = QString(QLatin1String("<%2> %3")).arg(AbstractStringAppender::stripFunctionName(function)).arg(message);
-    __android_log_write(AndroidAppender::androidLogPriority(logLevel), "Logger", qPrintable(result));
-#else
-    QString result = QString(QLatin1String("[%1] <%2> %3")).arg(levelToString(logLevel), -7)
-                     .arg(AbstractStringAppender::stripFunctionName(function)).arg(message);
-    std::cerr << qPrintable(result) << std::endl;
-#endif
-  }
-
-  if (logLevel == Logger::Fatal)
-    abort();
+    return d->defaultCategory;
 }
 
 /*!
   \brief Writes the log record.
 
   Writes the log records with the supplied arguments to all the registered appenders.
-  
+
   \note It is not recommended to call this function directly. Instead of this you can just call one of the macros
         (dTrace, dTrac, dInfo, dWarning, dError, dFatal) that will supply all the needed
         information to this function.
-  
-  \a timeStamp - the time stamp of the record
+
+  \a time - the time stamp of the record
   \a logLevel - the log level of the record
   \a file - the name of the source file that requested the log record
   \a line - the line of the code of source file that requested the log record
   \a function - name of the function that requested the log record
   \a category - logging category (0 for default category)
   \a message - log message
-  
+
   \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 LogLevel
   \sa Dtk::Core::dTrace(), Dtk::Core::dDebug(), Dtk::Core::dInfo(), Dtk::Core::dWarning(), Dtk::Core::dError(), Dtk::Core::dFatal()
   \sa AbstractAppender
  */
-void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
-                   const QString& message)
+void Logger::write(const QDateTime &time, Logger::LogLevel level, const char *file, int line,
+                   const char *func, const char *category, const QString &msg)
 {
-  write(timeStamp, logLevel, file, line, function, category, message, /* fromLocalInstance = */ false);
+    write(time, level, file, line, func, category, msg, /* fromLocalInstance = */ false);
 }
 
 /*!
   This is the overloaded function provided for the convenience. It behaves similar to the above function.
-  
+
   This function uses the current timestamp obtained with \c QDateTime::currentDateTime().
-  
+
   \sa write()
  */
-void Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const char* category,
-                   const QString& message)
+void Logger::write(Logger::LogLevel level, const char *file, int line,
+                   const char *func, const char *category, const QString &msg)
 {
-  write(QDateTime::currentDateTime(), logLevel, file, line, function, category, message);
+    write(QDateTime::currentDateTime(), level, file, line, func, category, msg);
 }
 
 /*!
   This is the overloaded function provided for the convenience. It behaves similar to the above function.
-  
+
   This function doesn't accept any log message as argument. It returns the \c QDebug object that can be written
   using the stream functions. For example, you may like to write:
   \code
@@ -1013,108 +887,164 @@ void Logger::write(LogLevel logLevel, const char* file, int line, const char* fu
   dDebug(QString(QLatin1String("This is the size %1x%2 of the element %3"))
             .arg(size.x()).arg(size.y()).arg(elementName));
   \endcode
-  
+
   Please consider reading the Qt Reference Documentation for the description of the QDebug class usage syntax.
-  
+
   \note This overload is definitely more pleasant to use than the first write() overload, but it behaves definitely
         slower than all the above overloads.
-  
+
   \sa write()
  */
-QDebug Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const char* category)
+QDebug Logger::write(Logger::LogLevel level, const char *file, int line,
+                     const char *func, const char *category)
 {
-  Q_D(Logger);
+    Q_D(Logger);
 
-  d->logDevice->lock(logLevel, file, line, function, category);
-  return QDebug(d->logDevice);
+    d->logDevice->lock(level, file, line, func, category);
+    return QDebug(d->logDevice);
 }
 
 /*!
   \brief Writes the assertion.
 
   This function writes the assertion record using the write() function.
-  
+
   The assertion record is always written using the Logger::Fatal log level which leads to the abortation of the
   program and generation of the core dump (if supported).
-  
+
   The message written to the appenders will be identical to the \a condition argument prefixed with the
   <tt>ASSERT:</tt> notification.
 
   The \a file parameter is the current file name.
   The \a line parameter indicates the number of lines to output.
-  The \a function parameter indicates the function name to output.
-  
+  The \a func parameter indicates the function name to output.
+
   \note It is not recommended to call this function directly. Instead of this you can just call the LOG_ASSERT
         macro that will supply all the needed information to this function.
-  
+
   \sa write()
  */
-void Logger::writeAssert(const char* file, int line, const char* function, const char* condition)
+void Logger::writeAssert(const char *file, int line,
+                         const char *func, const char *condition)
 {
-  write(Logger::Fatal, file, line, function, 0, QString("ASSERT: \"%1\"").arg(condition));
+    write(Logger::Fatal, file, line, func, nullptr, QString("ASSERT: \"%1\"").arg(condition));
 }
 
-
-Logger* loggerInstance()
+void Logger::write(const QDateTime &time, Logger::LogLevel level, const char *file, int line,
+                   const char *func, const char *category, const QString &msg, bool fromLocalInstance)
 {
-  return Logger::globalInstance();
-}
+    Q_D(Logger);
+    QMutexLocker locker(&d->loggerMutex);
 
+    QString logCategory = QString::fromLatin1(category);
+    if (logCategory.isNull() && !d->defaultCategory.isNull())
+        logCategory = d->defaultCategory;
+
+    bool wasWritten = false;
+    bool isGlobalInstance = this == globalInstance();
+    bool linkedToGlobal = isGlobalInstance && d->categories.value(logCategory, false);
+
+    if (!logCategory.isNull()) {
+        QList<AbstractAppender*> appenders = d->categoryAppenders.values(logCategory);
+        if (appenders.length() == 0) {
+            if (logCategory != d->defaultCategory && !linkedToGlobal && !fromLocalInstance)
+                std::cerr << "No appenders assotiated with category " << qPrintable(logCategory) << std::endl;
+        } else {
+            for (AbstractAppender* appender : appenders)
+                appender->write(time, level, file, line, func, logCategory, msg);
+
+            wasWritten = true;
+        }
+    }
 
+    // the default category is linked to the main logger appenders
+    // global logger instance also writes all linked categories to the main appenders
+    if (logCategory.isNull() || logCategory == d->defaultCategory || linkedToGlobal) {
+        if (!d->appenders.isEmpty()) {
+            for (AbstractAppender* appender : d->appenders)
+                appender->write(time, level, file, line, func, logCategory, msg);
+
+            wasWritten = true;
+        } else {
+            static bool noAppendersWarningShown = false;
+            if (!noAppendersWarningShown) {
+                std::cerr << "No appenders registered with logger" << std::endl;
+                noAppendersWarningShown = true;
+            }
+        }
+    }
 
-void LoggerTimingHelper::start(const char* msg, ...)
-{
-  va_list va;
-  va_start(va, msg);
-  m_block = QString().vsprintf(msg, va);
-  va_end(va);
+    // local logger instances send category messages to the global instance
+    if (!logCategory.isNull() && !isGlobalInstance)
+        globalInstance()->write(time, level, file, line, func, logCategory.toLatin1(), msg, true);
 
-  m_time.start();
-}
+    if (!wasWritten && !fromLocalInstance) {
+        QString result = QString(QLatin1String("[%1] <%2> %3")).arg(levelToString(level), -7)
+                .arg(AbstractStringAppender::stripFunctionName(func)).arg(msg);
+        std::cerr << qPrintable(result) << std::endl;
+    }
 
+    if (level == Logger::Fatal) {
+        std::cerr << "fatal level error occured, the program will abort!";
+        abort();
+    }
+}
 
-void LoggerTimingHelper::start(const QString& block)
+void CuteMessageLogger::write(const char *msg, ...) const
 {
-  m_block = block;
-  m_time.start();
+    va_list va;
+    va_start(va, msg);
+    m_l->write(m_level, m_file, m_line, m_function, m_category, QString().vsprintf(msg, va));
+    va_end(va);
 }
 
-LoggerTimingHelper::~LoggerTimingHelper()
+void CuteMessageLogger::write(const QString &msg) const
 {
-  QString message;
-  if (m_block.isEmpty())
-    message = QString(QLatin1String("Function %1 finished in ")).arg(AbstractStringAppender::stripFunctionName(m_function));
-  else
-    message = QString(QLatin1String("\"%1\" finished in ")).arg(m_block);
-
-  int elapsed = m_time.elapsed();
-  if (elapsed >= 10000)
-    message += QString(QLatin1String("%1 s.")).arg(elapsed / 1000);
-  else
-    message += QString(QLatin1String("%1 ms.")).arg(elapsed);
-
-  m_logger->write(m_logLevel, m_file, m_line, m_function, 0, message);
+    m_l->write(m_level, m_file, m_line, m_function, m_category, msg);
 }
 
-
-void CuteMessageLogger::write(const char* msg, ...) const
+QDebug CuteMessageLogger::write() const
 {
-  va_list va;
-  va_start(va, msg);
-  m_l->write(m_level, m_file, m_line, m_function, m_category, QString().vsprintf(msg, va));
-  va_end(va);
+    return m_l->write(m_level, m_file, m_line, m_function, m_category);
 }
 
+void LoggerTimingHelper::start(const char *msg, ...)
+{
+    va_list va;
+    va_start(va, msg);
+    m_block = QString().vsprintf(msg, va);
+    va_end(va);
+
+    m_time.start();
+}
 
-void CuteMessageLogger::write(const QString& msg) const
+void LoggerTimingHelper::start(const QString &msg)
 {
-  m_l->write(m_level, m_file, m_line, m_function, m_category, msg);
+    m_block = msg;
+    m_time.start();
 }
 
+LoggerTimingHelper::~LoggerTimingHelper()
+{
+    QString message;
+    if (m_block.isEmpty())
+        message = QString(QLatin1String("Function %1 finished in ")).arg(AbstractStringAppender::stripFunctionName(m_function));
+    else
+        message = QString(QLatin1String("\"%1\" finished in ")).arg(m_block);
 
-QDebug CuteMessageLogger::write() const
+    int elapsed = m_time.elapsed();
+    if (elapsed >= 10000)
+        message += QString(QLatin1String("%1 s.")).arg(elapsed / 1000);
+    else
+        message += QString(QLatin1String("%1 ms.")).arg(elapsed);
+
+    m_logger->write(m_logLevel, m_file, m_line, m_function, nullptr, message);
+}
+
+Logger* loggerInstance()
 {
-  return m_l->write(m_level, m_file, m_line, m_function, m_category);
+    return Logger::globalInstance();
 }
 
 DCORE_END_NAMESPACE
+#include "Logger.moc"
diff --git a/src/log/Logger.h b/src/log/Logger.h
deleted file mode 100644 (file)
index 87ac2e2..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
-  Copyright (c) 2012 Boris Moiseev (cyberbobs at gmail dot com)
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1
-  as published by the Free Software Foundation and appearing in the file
-  LICENSE.LGPL included in the packaging of this file.
-
-  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 Lesser General Public License for more details.
-*/
-#ifndef LOGGER_H
-#define LOGGER_H
-
-// Qt
-#include <QString>
-#include <QDebug>
-#include <QDateTime>
-
-// Local
-#include "CuteLogger_global.h"
-
-DCORE_BEGIN_NAMESPACE
-
-class AbstractAppender;
-class Logger;
-CUTELOGGERSHARED_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 dCTrace(category)   CuteMessageLogger(loggerInstance(), Logger::Trace,   __FILE__, __LINE__, Q_FUNC_INFO, category).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;\
-    }\
-
-
-    class LoggerPrivate;
-    class CUTELOGGERSHARED_EXPORT Logger
-    {
-        Q_DISABLE_COPY(Logger)
-
-    public:
-        Logger();
-        Logger(const QString &defaultCategory);
-        ~Logger();
-
-        //! Describes the possible severity levels of the log records
-        enum LogLevel {
-            Trace,   //!< Trace level. Can be used for mostly unneeded records used for internal code tracing.
-            Debug,   //!< Debug level. Useful for non-necessary records used for the debugging of the software.
-            Info,    //!< Info level. Can be used for informational records, which may be interesting for not only developers.
-            Warning, //!< Warning. May be used to log some non-fatal warnings detected by your application.
-            Error,   //!< Error. May be used for a big problems making your application work wrong but not crashing.
-            Fatal    //!< Fatal. Used for unrecoverable errors, crashes the application right after the log record is written.
-        };
-
-        static QString levelToString(LogLevel logLevel);
-        static LogLevel levelFromString(const QString &s);
-
-        static Logger *globalInstance();
-
-        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 &timeStamp, LogLevel logLevel, const char *file, int line, const char *function, const char *category,
-                   const QString &message);
-        void write(LogLevel logLevel, const char *file, int line, const char *function, const char *category, const QString &message);
-        QDebug write(LogLevel logLevel, const char *file, int line, const char *function, const char *category);
-
-        void writeAssert(const char *file, int line, const char *function, const char *condition);
-
-    private:
-        void write(const QDateTime &timeStamp, LogLevel logLevel, const char *file, int line, const char *function, const char *category,
-                   const QString &message, bool fromLocalInstance);
-        Q_DECLARE_PRIVATE(Logger)
-        LoggerPrivate *d_ptr;
-    };
-
-
-    class CUTELOGGERSHARED_EXPORT CuteMessageLogger
-    {
-        Q_DISABLE_COPY(CuteMessageLogger)
-
-    public:
-        Q_DECL_CONSTEXPR CuteMessageLogger(Logger *l, Logger::LogLevel level, const char *file, int line, const char *function)
-            : m_l(l),
-              m_level(level),
-              m_file(file),
-              m_line(line),
-              m_function(function),
-              m_category(0)
-        {}
-
-        Q_DECL_CONSTEXPR CuteMessageLogger(Logger *l, Logger::LogLevel level, const char *file, int line, const char *function, const char *category)
-            : m_l(l),
-              m_level(level),
-              m_file(file),
-              m_line(line),
-              m_function(function),
-              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 CUTELOGGERSHARED_EXPORT LoggerTimingHelper
-    {
-        Q_DISABLE_COPY(LoggerTimingHelper)
-
-    public:
-        inline explicit LoggerTimingHelper(Logger *l, Logger::LogLevel logLevel, const char *file, int line,
-                                           const char *function)
-            : m_logger(l),
-              m_logLevel(logLevel),
-              m_file(file),
-              m_line(line),
-              m_function(function)
-        {}
-
-        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
index fedc751fe41f1047d7542c4ea09a9858282f9d92..f22f9c2d4869fb509b68e8b9d09b6dd1227734fa 100644 (file)
@@ -1,18 +1,9 @@
-/*
-  Copyright (c) 2010 Karl-Heinz Reichel (khreichel at googlemail dot com)
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1
-  as published by the Free Software Foundation and appearing in the file
-  LICENSE.LGPL included in the packaging of this file.
-
-  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 Lesser General Public License for more details.
-*/
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 // Local
-#include "OutputDebugAppender.h"
+#include "win32/OutputDebugAppender.h"
 
 // STL
 #include <windows.h>
@@ -32,25 +23,25 @@ DCORE_BEGIN_NAMESPACE
 
   \brief Writes the log record to the windows debug log.
 
-  The \a timeStamp parameter indicates the time stamp.
-  The \a logLevel parameter describes the LogLevel.
+  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 function parameter indicates the function name to output.
+  The \a func parameter indicates the function name to output.
   The \a category parameter indicates the log category.
-  The \a message parameter indicates the output message.
+  The \a msg parameter indicates the output message.
 
   \sa AbstractStringAppender::format()
  */
-void OutputDebugAppender::append(const QDateTime& timeStamp,
-                                 Logger::LogLevel logLevel,
-                                 const charfile,
+void OutputDebugAppender::append(const QDateTime &time,
+                                 Logger::LogLevel level,
+                                 const char *file,
                                  int line,
-                                 const char* function,
-                                 const QStringcategory,
-                                 const QString& message)
+                                 const char *func,
+                                 const QString &category,
+                                 const QString &msg)
 {
-    QString s = formattedString(timeStamp, logLevel, file, line, function, category, message);
+    QString s = formattedString(time, level, file, line, function, category, msg);
     OutputDebugStringW((LPCWSTR) s.utf16());
 }
 
diff --git a/src/log/OutputDebugAppender.h b/src/log/OutputDebugAppender.h
deleted file mode 100644 (file)
index f12004f..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-  Copyright (c) 2010 Karl-Heinz Reichel (khreichel at googlemail dot com)
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1
-  as published by the Free Software Foundation and appearing in the file
-  LICENSE.LGPL included in the packaging of this file.
-
-  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 Lesser General Public License for more details.
-*/
-
-#ifndef OUTPUTDEBUGAPPENDER_H
-#define OUTPUTDEBUGAPPENDER_H
-
-#include "CuteLogger_global.h"
-#include <AbstractStringAppender.h>
-
-DCORE_BEGIN_NAMESPACE
-
-class CUTELOGGERSHARED_EXPORT OutputDebugAppender : public AbstractStringAppender
-{
-  protected:
-    virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
-                        const char* function, const QString& category, const QString& message);
-};
-
-DCORE_END_NAMESPACE
-
-#endif // OUTPUTDEBUGAPPENDER_H
index d974b365ae076f200bd064d3704ed76960b59eb1..2f5840fef6d1984682f27d5552ed3694c8b0fa71 100644 (file)
@@ -19,16 +19,16 @@ Note
 
 Just add pkgconfig in .pro file
 
-````
+```
 unix {
     CONFIG+=link_pkgconfig
     PKGCONFIG+=dtkcore
 }
-````
+```
 
 ### Example
 
-````
+```cpp
 
 #include <QCoreApplication>
 #include <DLog>
@@ -56,7 +56,7 @@ int main(int argc, char* argv[])
     dWarning() << "Something went wrong." << "Result code is" << result;
     return result;
 }
-````
+```
 
 \sa Dtk::Core::DLogManager
 
index 3f87f69f51f14f138c9bd5357484cde6ff4ac53b..0c34419c5811f28d6848a615094fab0bab908030 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <QDateTime>
 #include <QDir>
@@ -39,227 +26,218 @@ DCORE_BEGIN_NAMESPACE
   (so no more than logFilesLimit recent log files exist in the directory at any moment).
  */
 
-RollingFileAppender::RollingFileAppender(const QString& fileName)
-  : FileAppender(fileName),
-    m_logFilesLimit(0),
-    m_logSizeLimit(1024*1024*20)
-{}
+RollingFileAppender::RollingFileAppender(const QString &fileName)
+    : FileAppender(fileName),
+      m_logFilesLimit(0),
+      m_logSizeLimit(1024 * 1024 * 20)
+{
+
+}
 
-void RollingFileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
-    const char* function, const QString& category, const QString& message)
+void RollingFileAppender::append(const QDateTime &time, Logger::LogLevel level, const char *file, int line,
+                                 const char *func, const QString &category, const QString &msg)
 {
 
-  if (!m_rollOverTime.isNull() && QDateTime::currentDateTime() > m_rollOverTime)
-    rollOver();
+    if (!m_rollOverTime.isNull() && QDateTime::currentDateTime() > m_rollOverTime)
+        rollOver();
 
-  if (size()> m_logSizeLimit)
-    rollOver();
+    if (size()> m_logSizeLimit)
+        rollOver();
 
-   FileAppender::append(timeStamp, logLevel, file, line , function, category, message);
+    FileAppender::append(time, level, file, line , func, category, msg);
 }
 
-
 RollingFileAppender::DatePattern RollingFileAppender::datePattern() const
 {
-  QMutexLocker locker(&m_rollingMutex);
-  return m_frequency;
+    QMutexLocker locker(&m_rollingMutex);
+    return m_frequency;
 }
 
-
 QString RollingFileAppender::datePatternString() const
 {
-  QMutexLocker locker(&m_rollingMutex);
-  return m_datePatternString;
+    QMutexLocker locker(&m_rollingMutex);
+    return m_datePatternString;
 }
 
-
 void RollingFileAppender::setDatePattern(DatePattern datePattern)
 {
-  setDatePatternString(QLatin1String("'.'yyyy-MM-dd-hh-mm-ss-zzz"));
+    setDatePatternString(QLatin1String("'.'yyyy-MM-dd-hh-mm-ss-zzz"));
 
-  QMutexLocker locker(&m_rollingMutex);
-  m_frequency = datePattern;
+    QMutexLocker locker(&m_rollingMutex);
+    m_frequency = datePattern;
 
-  computeRollOverTime();
+    computeRollOverTime();
 }
 
-
-void RollingFileAppender::setDatePattern(const QString& datePattern)
+void RollingFileAppender::setDatePattern(const QString &datePattern)
 {
-  setDatePatternString(datePattern);
-  computeFrequency();
+    setDatePatternString(datePattern);
+    computeFrequency();
 
-  computeRollOverTime();
+    computeRollOverTime();
 }
 
-
-void RollingFileAppender::setDatePatternString(const QString& datePatternString)
+void RollingFileAppender::setDatePatternString(const QString &datePatternString)
 {
-  QMutexLocker locker(&m_rollingMutex);
-  m_datePatternString = datePatternString;
+    QMutexLocker locker(&m_rollingMutex);
+    m_datePatternString = datePatternString;
 }
 
-
 void RollingFileAppender::computeFrequency()
 {
-  QMutexLocker locker(&m_rollingMutex);
-
-  const QDateTime startTime(QDate(1999, 1, 1), QTime(0, 0));
-  const QString startString = startTime.toString(m_datePatternString);
-
-  if (startString != startTime.addSecs(60).toString(m_datePatternString))
-    m_frequency = MinutelyRollover;
-  else if (startString != startTime.addSecs(60 * 60).toString(m_datePatternString))
-    m_frequency = HourlyRollover;
-  else if (startString != startTime.addSecs(60 * 60 * 12).toString(m_datePatternString))
-    m_frequency = HalfDailyRollover;
-  else if (startString != startTime.addDays(1).toString(m_datePatternString))
-    m_frequency = DailyRollover;
-  else if (startString != startTime.addDays(7).toString(m_datePatternString))
-    m_frequency = WeeklyRollover;
-  else if (startString != startTime.addMonths(1).toString(m_datePatternString))
-    m_frequency = MonthlyRollover;
-  else
-  {
-    Q_ASSERT_X(false, "DailyRollingFileAppender::computeFrequency", "The pattern '%1' does not specify a frequency");
-    return;
-  }
+    QMutexLocker locker(&m_rollingMutex);
+
+    const QDateTime startTime(QDate(1999, 1, 1), QTime(0, 0));
+    const QString startString = startTime.toString(m_datePatternString);
+
+    if (startString != startTime.addSecs(60).toString(m_datePatternString))
+        m_frequency = MinutelyRollover;
+    else if (startString != startTime.addSecs(60 * 60).toString(m_datePatternString))
+        m_frequency = HourlyRollover;
+    else if (startString != startTime.addSecs(60 * 60 * 12).toString(m_datePatternString))
+        m_frequency = HalfDailyRollover;
+    else if (startString != startTime.addDays(1).toString(m_datePatternString))
+        m_frequency = DailyRollover;
+    else if (startString != startTime.addDays(7).toString(m_datePatternString))
+        m_frequency = WeeklyRollover;
+    else if (startString != startTime.addMonths(1).toString(m_datePatternString))
+        m_frequency = MonthlyRollover;
+    else
+    {
+        Q_ASSERT_X(false, "DailyRollingFileAppender::computeFrequency", "The pattern '%1' does not specify a frequency");
+        return;
+    }
 }
 
-
 void RollingFileAppender::removeOldFiles()
 {
-  if (m_logFilesLimit <= 1)
-    return;
-
-  QFileInfo fileInfo(fileName());
-  QDir logDirectory(fileInfo.absoluteDir());
-  logDirectory.setFilter(QDir::Files);
-  logDirectory.setNameFilters(QStringList() << fileInfo.fileName() + "*");
-  QFileInfoList logFiles = logDirectory.entryInfoList();
-
-  QMap<QDateTime, QString> fileDates;
-  for (int i = 0; i < logFiles.length(); ++i)
-  {
-    QString name = logFiles[i].fileName();
-    QString suffix = name.mid(name.indexOf(fileInfo.fileName()) + fileInfo.fileName().length());
-    QDateTime fileDateTime = QDateTime::fromString(suffix, datePatternString());
-
-    if (fileDateTime.isValid())
-      fileDates.insert(fileDateTime, logFiles[i].absoluteFilePath());
-  }
-
-  QList<QString> fileDateNames = fileDates.values();
-  for (int i = 0; i < fileDateNames.length() - m_logFilesLimit + 1; ++i)
-    QFile::remove(fileDateNames[i]);
-}
+    if (m_logFilesLimit <= 1)
+        return;
+
+    QFileInfo fileInfo(fileName());
+    QDir logDirectory(fileInfo.absoluteDir());
+    logDirectory.setFilter(QDir::Files);
+    logDirectory.setNameFilters(QStringList() << fileInfo.fileName() + "*");
+    QFileInfoList logFiles = logDirectory.entryInfoList();
+
+    QMap<QDateTime, QString> fileDates;
+    for (int i = 0; i < logFiles.length(); ++i)
+    {
+        QString name = logFiles[i].fileName();
+        QString suffix = name.mid(name.indexOf(fileInfo.fileName()) + fileInfo.fileName().length());
+        QDateTime fileDateTime = QDateTime::fromString(suffix, datePatternString());
 
+        if (fileDateTime.isValid())
+            fileDates.insert(fileDateTime, logFiles[i].absoluteFilePath());
+    }
+
+    QList<QString> fileDateNames = fileDates.values();
+    for (int i = 0; i < fileDateNames.length() - m_logFilesLimit + 1; ++i)
+        QFile::remove(fileDateNames[i]);
+}
 
 void RollingFileAppender::computeRollOverTime()
 {
-  Q_ASSERT_X(!m_datePatternString.isEmpty(), "DailyRollingFileAppender::computeRollOverTime()", "No active date pattern");
+    Q_ASSERT_X(!m_datePatternString.isEmpty(), "DailyRollingFileAppender::computeRollOverTime()", "No active date pattern");
 
-  QDateTime now = QDateTime::currentDateTime();
-  QDate nowDate = now.date();
-  QTime nowTime = now.time();
-  QDateTime start;
+    QDateTime now = QDateTime::currentDateTime();
+    QDate nowDate = now.date();
+    QTime nowTime = now.time();
+    QDateTime start;
 
-  switch (m_frequency)
-  {
+    switch (m_frequency)
+    {
     case MinutelyRollover:
     {
-      start = QDateTime(nowDate, nowTime);
-      m_rollOverTime = start.addSecs(60);
+        start = QDateTime(nowDate, nowTime);
+        m_rollOverTime = start.addSecs(60);
     }
-    break;
+        break;
     case HourlyRollover:
     {
-      start = QDateTime(nowDate, nowTime);
-      m_rollOverTime = start.addSecs(60*60);
+        start = QDateTime(nowDate, nowTime);
+        m_rollOverTime = start.addSecs(60*60);
     }
-    break;
+        break;
     case HalfDailyRollover:
     {
-      int hour = nowTime.hour();
-      if (hour >=  12)
-        hour = 12;
-      else
-        hour = 0;
-      start = QDateTime(nowDate, nowTime);
-      m_rollOverTime = start.addSecs(60*60*12);
+        int hour = nowTime.hour();
+        if (hour >=  12)
+            hour = 12;
+        else
+            hour = 0;
+        start = QDateTime(nowDate, nowTime);
+        m_rollOverTime = start.addSecs(60*60*12);
     }
-    break;
+        break;
     case DailyRollover:
     {
-      start = QDateTime(nowDate, nowTime);
-      m_rollOverTime = start.addDays(1);
+        start = QDateTime(nowDate, nowTime);
+        m_rollOverTime = start.addDays(1);
     }
-    break;
+        break;
     case WeeklyRollover:
     {
-      // Qt numbers the week days 1..7. The week starts on Monday.
-      // Change it to being numbered 0..6, starting with Sunday.
-      int day = nowDate.dayOfWeek();
-      if (day == Qt::Sunday)
-        day = 0;
-      start = QDateTime(nowDate, nowTime).addDays(-1 * day);
-      m_rollOverTime = start.addDays(7);
+        // Qt numbers the week days 1..7. The week starts on Monday.
+        // Change it to being numbered 0..6, starting with Sunday.
+        int day = nowDate.dayOfWeek();
+        if (day == Qt::Sunday)
+            day = 0;
+        start = QDateTime(nowDate, nowTime).addDays(-1 * day);
+        m_rollOverTime = start.addDays(7);
     }
-    break;
+        break;
     case MonthlyRollover:
     {
-      start = QDateTime(QDate(nowDate.year(), nowDate.month(), 1), nowTime);
-      m_rollOverTime = start.addMonths(1);
+        start = QDateTime(QDate(nowDate.year(), nowDate.month(), 1), nowTime);
+        m_rollOverTime = start.addMonths(1);
     }
-    break;
+        break;
     default:
-      Q_ASSERT_X(false, "DailyRollingFileAppender::computeInterval()", "Invalid datePattern constant");
-      m_rollOverTime = QDateTime::fromTime_t(0);
-  }
-
-  m_rollOverSuffix = start.toString(m_datePatternString);
-  Q_ASSERT_X(now.toString(m_datePatternString) == m_rollOverSuffix,
-      "DailyRollingFileAppender::computeRollOverTime()", "File name changes within interval");
-  Q_ASSERT_X(m_rollOverSuffix != m_rollOverTime.toString(m_datePatternString),
-      "DailyRollingFileAppender::computeRollOverTime()", "File name does not change with rollover");
-}
+        Q_ASSERT_X(false, "DailyRollingFileAppender::computeInterval()", "Invalid datePattern constant");
+        m_rollOverTime = QDateTime::fromTime_t(0);
+    }
 
+    m_rollOverSuffix = start.toString(m_datePatternString);
+    Q_ASSERT_X(now.toString(m_datePatternString) == m_rollOverSuffix,
+               "DailyRollingFileAppender::computeRollOverTime()", "File name changes within interval");
+    Q_ASSERT_X(m_rollOverSuffix != m_rollOverTime.toString(m_datePatternString),
+               "DailyRollingFileAppender::computeRollOverTime()", "File name does not change with rollover");
+}
 
 void RollingFileAppender::rollOver()
 {
-  Q_ASSERT_X(!m_datePatternString.isEmpty(), "DailyRollingFileAppender::rollOver()", "No active date pattern");
+    Q_ASSERT_X(!m_datePatternString.isEmpty(), "DailyRollingFileAppender::rollOver()", "No active date pattern");
 
-  QString rollOverSuffix = m_rollOverSuffix;
-  computeRollOverTime();
-  if (rollOverSuffix == m_rollOverSuffix)
-    return;
+    QString rollOverSuffix = m_rollOverSuffix;
+    computeRollOverTime();
+    if (rollOverSuffix == m_rollOverSuffix)
+        return;
 
-  closeFile();
+    closeFile();
 
-  QString targetFileName = fileName() + rollOverSuffix;
-  QFile f(targetFileName);
-  if (f.exists() && !f.remove())
-    return;
-  f.setFileName(fileName());
-  if (!f.rename(targetFileName))
-    return;
+    QString targetFileName = fileName() + rollOverSuffix;
+    QFile f(targetFileName);
+    if (f.exists() && !f.remove())
+        return;
+    f.setFileName(fileName());
+    if (!f.rename(targetFileName))
+        return;
 
-  openFile();
-  removeOldFiles();
+    openFile();
+    removeOldFiles();
 }
 
-
 void RollingFileAppender::setLogFilesLimit(int limit)
 {
-  QMutexLocker locker(&m_rollingMutex);
-  m_logFilesLimit = limit;
+    QMutexLocker locker(&m_rollingMutex);
+    m_logFilesLimit = limit;
 }
 
-
 int RollingFileAppender::logFilesLimit() const
 {
-  QMutexLocker locker(&m_rollingMutex);
-  return m_logFilesLimit;
+    QMutexLocker locker(&m_rollingMutex);
+    return m_logFilesLimit;
 }
 
 DCORE_END_NAMESPACE
diff --git a/src/log/RollingFileAppender.h b/src/log/RollingFileAppender.h
deleted file mode 100644 (file)
index 3627908..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ROLLINGFILEAPPENDER_H
-#define ROLLINGFILEAPPENDER_H
-
-#include <QDateTime>
-
-#include <FileAppender.h>
-
-DCORE_BEGIN_NAMESPACE
-
-class CUTELOGGERSHARED_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& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
-                        const char* function, const QString& category, const QString& message);
-
-  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/src/log/cutelogger.pri b/src/log/cutelogger.pri
deleted file mode 100644 (file)
index c37152c..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-INCLUDEPATH += $$PWD
-
-HEADERS += \
-    $$PWD/RollingFileAppender.h \
-    $$PWD/Logger.h \
-    $$PWD/FileAppender.h \
-    $$PWD/CuteLogger_global.h \
-    $$PWD/ConsoleAppender.h \
-    $$PWD/AbstractStringAppender.h \
-    $$PWD/AbstractAppender.h \
-    $$PWD/LogManager.h
-
-SOURCES += \
-    $$PWD/RollingFileAppender.cpp \
-    $$PWD/Logger.cpp \
-    $$PWD/FileAppender.cpp \
-    $$PWD/ConsoleAppender.cpp \
-    $$PWD/AbstractStringAppender.cpp \
-    $$PWD/AbstractAppender.cpp \
-    $$PWD/LogManager.cpp
-
-win32 {
-    SOURCES += $$PWD/OutputDebugAppender.cpp
-    HEADERS += $$PWD/OutputDebugAppender.h
-}
diff --git a/src/log/log.cmake b/src/log/log.cmake
new file mode 100644 (file)
index 0000000..befccfd
--- /dev/null
@@ -0,0 +1,25 @@
+file(GLOB LOG_HEADER
+  ${CMAKE_CURRENT_LIST_DIR}/../../include/log/*.h
+)
+set(LOG_SOURCE 
+  ${CMAKE_CURRENT_LIST_DIR}/RollingFileAppender.cpp 
+  ${CMAKE_CURRENT_LIST_DIR}/Logger.cpp 
+  ${CMAKE_CURRENT_LIST_DIR}/FileAppender.cpp 
+  ${CMAKE_CURRENT_LIST_DIR}/ConsoleAppender.cpp 
+  ${CMAKE_CURRENT_LIST_DIR}/AbstractStringAppender.cpp 
+  ${CMAKE_CURRENT_LIST_DIR}/AbstractAppender.cpp 
+  ${CMAKE_CURRENT_LIST_DIR}/LogManager.cpp
+)
+if(WIN32)
+  set(log_SRCS 
+    ${LOG_HEADER}
+    ${LOG_SOURCE}
+    ${CMAKE_CURRENT_LIST_DIR}/OutputDebugAppender.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/../../include/log/win32/OutputDebugAppender.h
+  )
+else()
+  set(log_SRCS 
+    ${LOG_HEADER}
+    ${LOG_SOURCE}
+  )
+endif()
diff --git a/src/log/log.pri b/src/log/log.pri
deleted file mode 100644 (file)
index 7e91ff9..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-include(cutelogger.pri)
-
-includes.files += $$PWD/*.h
-includes.files += \
-    $$PWD/DLog
diff --git a/src/settings/DSettings b/src/settings/DSettings
deleted file mode 100644 (file)
index 572ed1b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dsettings.h"
diff --git a/src/settings/DSettingsGroup b/src/settings/DSettingsGroup
deleted file mode 100644 (file)
index 1c85d70..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dsettingsgroup.h"
diff --git a/src/settings/DSettingsOption b/src/settings/DSettingsOption
deleted file mode 100644 (file)
index 9c180d5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dsettingsoption.h"
index 39ba914064e1af17ef08a9293a49df5512eec78a..d3c199167091f478137f4d60a70ffd49c3673792 100644 (file)
@@ -1,25 +1,8 @@
-/*
- * Copyright (C) 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
-#include "dsettingsdconfigbackend.h"
+#include "settings/backend/dsettingsdconfigbackend.h"
 
 #include <QDebug>
 #include <QMutex>
diff --git a/src/settings/backend/dsettingsdconfigbackend.h b/src/settings/backend/dsettingsdconfigbackend.h
deleted file mode 100644 (file)
index c316f31..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QScopedPointer>
-
-#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<DSettingsDConfigBackendPrivate> d_ptr;
-    Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), DSettingsDConfigBackend)
-};
-
-DCORE_END_NAMESPACE
index 4874f13521fac0619cc61bde181d5483097ca59e..efc2873e465adc13b655dbdfb1162f3fa0a4af6d 100644 (file)
@@ -1,4 +1,8 @@
-#include "gsettingsbackend.h"
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "settings/backend/gsettingsbackend.h"
 
 //#include <QDebug>
 #include <QFile>
diff --git a/src/settings/backend/gsettingsbackend.h b/src/settings/backend/gsettingsbackend.h
deleted file mode 100644 (file)
index 8b02ebb..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#include <QObject>
-#include <QScopedPointer>
-
-#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<GSettingsBackendPrivate> d_ptr;
-    Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), GSettingsBackend)
-};
-
-DCORE_END_NAMESPACE
index 4151af530b2d071e02316d660eed2b844c6d72f9..eb0106d91be24c8da6abfa6d1297cf5793615230 100644 (file)
@@ -1,21 +1,8 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
-#include "qsettingbackend.h"
+#include "settings/backend/qsettingbackend.h"
 
 #include <QDebug>
 #include <QMutex>
diff --git a/src/settings/backend/qsettingbackend.h b/src/settings/backend/qsettingbackend.h
deleted file mode 100644 (file)
index 5cfa8da..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QScopedPointer>
-
-#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<QSettingBackendPrivate> d_ptr;
-    Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), QSettingBackend)
-};
-
-DCORE_END_NAMESPACE
index 2d2663eff5e680aa848349e346744ad6eecaeed1..f675515c73bada490bef65beb2d2a17dc6b77b14 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dsettings.h"
 
diff --git a/src/settings/dsettings.h b/src/settings/dsettings.h
deleted file mode 100644 (file)
index 5a1e828..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QPointer>
-#include <QScopedPointer>
-
-#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<DSettings> fromJson(const QByteArray &json);
-    static QPointer<DSettings> fromJsonFile(const QString &filepath);
-    QJsonObject meta() const;
-
-    QStringList keys() const;
-    QList<QPointer<DSettingsOption>> options() const;
-    QPointer<DSettingsOption> option(const QString &key) const;
-    QVariant value(const QString &key) const;
-
-    QStringList groupKeys() const;
-    QList<QPointer<DSettingsGroup>> groups() const;
-    QPointer<DSettingsGroup> 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<DSettingsPrivate> dd_ptr;
-    Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettings)
-};
-
-DCORE_END_NAMESPACE
diff --git a/src/settings/dsettingsbackend.h b/src/settings/dsettingsbackend.h
deleted file mode 100644 (file)
index d66dc96..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QScopedPointer>
-
-#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
index c9a67fafe5f6f013fc79d37a86c16a23f355b683..0bd399f0435c3fa5d76be05d3cfe50d1464e4b9d 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dsettingsgroup.h"
 
diff --git a/src/settings/dsettingsgroup.h b/src/settings/dsettingsgroup.h
deleted file mode 100644 (file)
index 8397aef..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QPointer>
-
-#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<DSettingsGroup> parentGroup() const;
-    void setParentGroup(QPointer<DSettingsGroup> parentGroup);
-
-    QString key() const;
-    QString name() const;
-    bool isHidden() const;
-
-    QPointer<DSettingsGroup> childGroup(const QString &groupKey) const;
-    QPointer<DSettingsOption> option(const QString &key) const;
-
-    QList<QPointer<DSettingsGroup> > childGroups() const;
-    QList<QPointer<DSettingsOption> > childOptions() const;
-    QList<QPointer<DSettingsOption> > options() const;
-
-    static QPointer<DSettingsGroup> fromJson(const QString &prefixKey, const QJsonObject &group);
-
-private:
-    void parseJson(const QString &prefixKey, const QJsonObject &group);
-
-    QScopedPointer<DSettingsGroupPrivate> dd_ptr;
-    Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettingsGroup)
-};
-
-typedef QPointer<DSettingsGroup> GroupPtr;
-
-DCORE_END_NAMESPACE
index 67764a136a8bae65f0ea3b3f8a4404b275159065..b365502fb6520bc81a8fe2118179a0eb9d6cdce5 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dsettingsoption.h"
 
diff --git a/src/settings/dsettingsoption.h b/src/settings/dsettingsoption.h
deleted file mode 100644 (file)
index f8d6a34..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <QVariant>
-#include <QObject>
-#include <QPointer>
-
-#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<DSettingsGroup> parentGroup() const;
-    void setParentGroup(QPointer<DSettingsGroup> 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<DSettingsOption> 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<DSettingsOptionPrivate> dd_ptr;
-    Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettingsOption)
-};
-
-typedef QPointer<DSettingsOption> OptionPtr;
-
-DCORE_END_NAMESPACE
diff --git a/src/settings/settings.cmake b/src/settings/settings.cmake
new file mode 100644 (file)
index 0000000..e09e26a
--- /dev/null
@@ -0,0 +1,25 @@
+if(LINUX)
+  file(GLOB SETTINGS_SOURCE
+    ${CMAKE_CURRENT_LIST_DIR}/*.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/backend/*.cpp
+  )
+  file(GLOB SETTINGS_HEADER 
+    ${CMAKE_CURRENT_LIST_DIR}/../../include/settings/*.h 
+    ${CMAKE_CURRENT_LIST_DIR}/../../include/settings/backend/*.h 
+  )
+else()
+  file(GLOB SETTINGS_SOURCE
+    ${CMAKE_CURRENT_LIST_DIR}/*.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/backend/dsettingsdconfigbackend.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/backend/qsettingbackend.cpp
+  )
+  file(GLOB SETTINGS_HEADER 
+    ${CMAKE_CURRENT_LIST_DIR}/../../include/settings/*.h 
+    ${CMAKE_CURRENT_LIST_DIR}/../../include/settings/backend/dsettingsdconfigbackend.h 
+    ${CMAKE_CURRENT_LIST_DIR}/../../include/settings/backend/qsettingbackend.h
+  )
+endif()
+set(settings_SRC
+  ${SETTINGS_HEADER}
+  ${SETTINGS_SOURCE}
+)
diff --git a/src/settings/settings.pri b/src/settings/settings.pri
deleted file mode 100644 (file)
index e16c984..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-linux {
-    PKGCONFIG += gsettings-qt
-
-SOURCES += \
-    $$PWD/backend/gsettingsbackend.cpp
-
-HEADERS +=\
-    $$PWD/backend/gsettingsbackend.h
-}
-
-INCLUDEPATH += $$PWD
-
-SOURCES += \
-    $$PWD/backend/qsettingbackend.cpp \
-    $$PWD/dsettings.cpp \
-    $$PWD/dsettingsoption.cpp \
-    $$PWD/dsettingsgroup.cpp \
-    $$PWD/backend/dsettingsdconfigbackend.cpp
-
-HEADERS +=\
-    $$PWD/backend/qsettingbackend.h \
-    $$PWD/dsettings.h \
-    $$PWD/dsettingsoption.h \
-    $$PWD/dsettingsgroup.h \
-    $$PWD/dsettingsbackend.h \
-    $$PWD/backend/dsettingsdconfigbackend.h
-
-includes.files += $${PWD}/*.h
-includes.files += $${PWD}/backend/*.h
-includes.files += \
-    $${PWD}/DSettings \
-    $${PWD}/DSettingsGroup \
-    $${PWD}/DSettingsOption
diff --git a/src/src.pro b/src/src.pro
deleted file mode 100644 (file)
index 9b147a8..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-QT -= gui
-QT += dbus
-QT += xml
-CONFIG += link_pkgconfig
-TARGET = dtkcore
-
-# 龙芯架构上没有默认添加PT_GNU_STACK-section,所以此处手动指定一下
-contains(QMAKE_HOST.arch, mips.*): QMAKE_LFLAGS_SHLIB += "-Wl,-z,noexecstack"
-
-QMAKE_CXXFLAGS_RELEASE += -fvisibility=hidden
-
-INCLUDEPATH += $$PWD
-HEADERS += $$PWD/dtkcore_global.h \
-    dconfig.h \
-    dsysinfo.h \
-    dsecurestring.h \
-    ddesktopentry.h
-
-SOURCES += \
-    dconfig.cpp \
-    dsysinfo.cpp \
-    dsecurestring.cpp \
-    ddesktopentry.cpp \
-    dtkcore_global.cpp
-
-linux: {
-    HEADERS += \
-        $$PWD/dconfigfile.h
-
-    SOURCES += \
-        $$PWD/dconfigfile.cpp
-
-    # generic dbus interfaces
-    isEmpty(DTK_DISABLE_DBUS_CONFIG) {
-        QT += dbus
-
-        config.files = $$PWD/dbus/org.desktopspec.ConfigManager.xml
-        config.header_flags += -c DSGConfig -N
-        config.source_flags += -c DSGConfig -N
-
-        manager.files = $$PWD/dbus/org.desktopspec.ConfigManager.Manager.xml
-        manager.header_flags += -c DSGConfigManager -N
-        manager.source_flags += -c DSGConfigManager -N
-
-        DBUS_INTERFACES += config manager
-    } else {
-        DEFINES += D_DISABLE_DBUS_CONFIG
-    }
-} else {
-    DEFINES += D_DISABLE_DCONFIG
-}
-
-include($$PWD/base/base.pri)
-include($$PWD/util/util.pri)
-include($$PWD/log/log.pri)
-include($$PWD/filesystem/filesystem.pri)
-include($$PWD/settings/settings.pri)
-
-includes.files += \
-    $$PWD/*.h \
-    $$PWD/dtkcore_config.h \
-    $$PWD/DtkCores \
-    $$PWD/DSysInfo \
-    $$PWD/DSecureString \
-    $$PWD/DDesktopEntry \
-    $$PWD/DConfigFile \
-    $$PWD/DConfig
-
-# ----------------------------------------------
-# install config
-
-DTK_MODULE_NAME = $$TARGET
-load(dtk_build)
-
-INSTALLS += includes target
-
-isEmpty(DTK_STATIC_LIB){
-    DEFINES += LIBDTKCORE_LIBRARY
-} else {
-    DEFINES += DTK_STATIC_LIB
-}
-
-#cmake
-load(dtk_cmake)
-
-#qt module
-load(dtk_module)
-
-!isEmpty(DTK_MULTI_VERSION) {
-# 支持上游一包多依赖
-load(dtk_multiversion)
-# 5.5 5.6可通过重复调用此函数,来增加对更多版本的支持
-dtkBuildMultiVersion(5.5)
-
-# INSTALL变量增加多版本下的配置文件
-load(dtk_install_multiversion)
-}
diff --git a/src/util/DAbstractUnitFormatter b/src/util/DAbstractUnitFormatter
deleted file mode 100644 (file)
index 2d494ea..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dabstractunitformatter.h"
diff --git a/src/util/DDBusSender b/src/util/DDBusSender
deleted file mode 100644 (file)
index 8881ba7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "ddbussender.h"
diff --git a/src/util/DDiskSizeFormatter b/src/util/DDiskSizeFormatter
deleted file mode 100644 (file)
index 6584e09..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "ddisksizeformatter.h"
diff --git a/src/util/DExportedInterface b/src/util/DExportedInterface
deleted file mode 100644 (file)
index eea6f16..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dexportedinterface.h"
diff --git a/src/util/DFileServices b/src/util/DFileServices
deleted file mode 100644 (file)
index 77b70de..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dfileservices.h"
diff --git a/src/util/DNotifySender b/src/util/DNotifySender
deleted file mode 100644 (file)
index 85796fc..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dnotifysender.h"
diff --git a/src/util/DPinyin b/src/util/DPinyin
deleted file mode 100644 (file)
index 0df4d4c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dpinyin.h"
diff --git a/src/util/DRecentManager b/src/util/DRecentManager
deleted file mode 100644 (file)
index a1be4b0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "drecentmanager.h"
diff --git a/src/util/DThreadUtils b/src/util/DThreadUtils
deleted file mode 100644 (file)
index 13b83d8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dthreadutils.h"
diff --git a/src/util/DTimeUnitFormatter b/src/util/DTimeUnitFormatter
deleted file mode 100644 (file)
index 07d52a6..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dtimeunitformatter.h"
diff --git a/src/util/DUtil b/src/util/DUtil
deleted file mode 100644 (file)
index 2dc0935..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dutil.h"
diff --git a/src/util/DVtableHook b/src/util/DVtableHook
deleted file mode 100644 (file)
index 37dffef..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "dvtablehook.h"
index da8d8101b7583730a15a355cc5bf291702ed1613..0249bce54958831d454b4e4a6df0848da36175ec 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dabstractunitformatter.h"
 
diff --git a/src/util/dabstractunitformatter.h b/src/util/dabstractunitformatter.h
deleted file mode 100644 (file)
index 98bc4d6..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DABSTRACTUNITFORMATTER_H
-#define DABSTRACTUNITFORMATTER_H
-
-#include "dtkcore_global.h"
-
-#include <QPair>
-#include <QList>
-
-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<qreal, int> format(const qreal value, const int unit) const;
-    QList<QPair<qreal, int>> formatAsUnitList(const qreal value, int unit) const;
-};
-
-DCORE_END_NAMESPACE
-
-#endif // DABSTRACTUNITFORMATTER_H
diff --git a/src/util/dasync.h b/src/util/dasync.h
deleted file mode 100644 (file)
index e9f0d07..0000000
+++ /dev/null
@@ -1,505 +0,0 @@
-/*
- * Copyright (C) 2021 ~ 2021 UnionTech Technology Co., Ltd.
- *
- * Author:     Wang Peng <993381@qq.com>
- *
- * Maintainer: Wang Peng <wangpenga@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-#ifndef DASYNC_H
-#define DASYNC_H
-#include <dtkcore_global.h>
-
-#include <QQueue>
-#include <QMutex>
-#include <QThread>
-#include <QMutexLocker>
-#include <QCoreApplication>
-
-#include <functional>
-#include <type_traits>
-
-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 T>
-    class DSafeQueue : public QQueue<T> {
-    public:
-        inline void enqueue(const T &t) {
-            QMutexLocker lkc(&m_mtx);
-            QQueue<T>::enqueue(t);
-        }
-        inline T dequeue() {
-            QMutexLocker lkc(&m_mtx);
-            return QQueue<T>::dequeue();
-        }
-        inline int size() {
-            QMutexLocker lkc(&m_mtx);
-            return QQueue<T>::size();
-        }
-        inline T &head() {
-            QMutexLocker lkc(&m_mtx);
-            return QQueue<T>::head();
-        }
-        inline const T &head() const {
-            QMutexLocker lkc(&m_mtx);
-            return QQueue<T>::head();
-        }
-    private:
-        QMutex m_mtx;
-    };
-
-    // 内部使用,不对外提供接口
-    class MainWorker : public QObject {
-    Q_OBJECT
-        std::function<void(void *)> m_handle;
-        std::function<void(void *)> m_handleProxy;
-
-        std::function<void(void)> m_handleV;
-        std::function<void(void)> 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 FUNC, typename ArgType>
-        typename std::enable_if<!std::is_void<ArgType>::value>::type
-        setHandle(FUNC &&func) {
-            m_handle = [&] (void *arg) {
-                DSafeQueue<ArgType> *q = static_cast<DSafeQueue<ArgType>*>(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 FUNC, typename ArgType>
-        typename std::enable_if<std::is_void<ArgType>::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<void, xxx>::post execute forever
-    };
-    Q_DECLARE_FLAGS(AsyncTaskStatus, AsyncTaskState)
-};
-
-// Template classes not supported by Q_OBJECT, so class MainWorker is independent
-template <typename DataTypeIn, typename DataTypeOut>
-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<typename T, typename Enable = void>
-    struct DataQueueType { DtkCorePrivate::DSafeQueue<T> m_queue; };
-    template<class T>
-    struct DataQueueType<T, typename std::enable_if<std::is_void<T>::value>::type> { };
-    using DataInQueue = DataQueueType<DataTypeIn>;
-    using DataOutQueue = DataQueueType<DataTypeOut>;
-    // Queue 中处理完的结果经由 m_QueueIn 变量暂存,然后经由 signal、slot 传给 then 中的回调函数做参数
-    DataInQueue m_QueueIn;
-    DataOutQueue m_QueueOut;
-
-    // 存储不同类型的输入函数
-    template<typename T1, typename T2, typename Enable1 = void, typename Enable2 = void>
-    struct FuncType {
-    };
-    template<typename T1, typename T2>
-    struct FuncType<T1, T2,
-            typename std::enable_if<std::is_void<T1>::value>::type,
-            typename std::enable_if<std::is_void<T2>::value>::type> {
-        std::function <void(void)> cbp;
-    };
-    template<typename T1, typename T2>
-    struct FuncType<T1, T2,
-            typename std::enable_if<!std::is_void<T1>::value>::type,
-            typename std::enable_if<!std::is_void<T2>::value>::type> {
-        std::function <T2(T1)> cbp;
-    };
-    template<typename T1, typename T2>
-    struct FuncType<T1, T2,
-            typename std::enable_if<std::is_void<T1>::value>::type,
-            typename std::enable_if<!std::is_void<T2>::value>::type> {
-        std::function <T2(void)> cbp;
-    };
-    template<typename T1, typename T2>
-    struct FuncType<T1, T2,
-            typename std::enable_if<!std::is_void<T1>::value>::type,
-            typename std::enable_if<std::is_void<T2>::value>::type> {
-        std::function <void(T1)> cbp;
-    };
-
-    std::mutex m_mtxFunc;
-    FuncType<DataTypeIn, DataTypeOut> 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 PostInType, typename EmitInType>
-    typename std::enable_if<std::is_void<PostInType>::value && std::is_void<EmitInType>::value>::type
-    emitHelper() {
-        m_func.cbp();
-        Q_EMIT m_mainWorker->sigRunInMainVoid();
-    }
-    // 2. input non void & emit non void
-    template <typename PostInType, typename EmitInType>
-    typename std::enable_if<!std::is_void<PostInType>::value && !std::is_void<EmitInType>::value>::type
-    emitHelper() {
-        m_QueueOut.m_queue.enqueue(m_func.cbp(m_QueueIn.m_queue.dequeue()));
-        Q_EMIT m_mainWorker->sigRunInMain(static_cast<void *>(&(m_QueueOut.m_queue)));
-    }
-    // 3. input non void & emit void
-    template <typename PostInType, typename EmitInType>
-    typename std::enable_if<!std::is_void<PostInType>::value && std::is_void<EmitInType>::value>::type
-    emitHelper() {
-        m_func.cbp(m_QueueIn.m_queue.dequeue());
-        Q_EMIT m_mainWorker->sigRunInMainVoid();
-    }
-    // 4. input void & emit non void
-    template <typename PostInType, typename EmitInType>
-    typename std::enable_if<std::is_void<PostInType>::value && !std::is_void<EmitInType>::value>::type
-    emitHelper() {
-        m_QueueOut.m_queue.enqueue(m_func.cbp());
-        Q_EMIT m_mainWorker->sigRunInMain(static_cast<void *>(&(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 <std::mutex> lck(m_mtxForWaitTask);
-            m_cvForWaitTask.wait(lck);
-        }
-    }
-    // 输入数据不是 void 类型则依赖于 m_QueueIn
-    template <typename FUNC, typename InputType = DataTypeIn>
-    typename std::enable_if<!std::is_void<InputType>::value, Helper *>::type
-    post(FUNC &&func) {
-        m_func.cbp = std::forward<FUNC>(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 <std::mutex> 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<DataTypeIn, DataTypeOut>();
-                    }
-                }
-            });
-            thread.detach();
-        };
-
-        return m_helper;
-    }
-
-    template <typename FUNC, typename InputType = DataTypeIn>
-    typename std::enable_if<std::is_void<InputType>::value, Helper *>::type
-    post(FUNC &&func) {
-        {
-            std::lock_guard<std::mutex> lckFunc(m_mtxFunc);
-            m_func.cbp = std::forward<FUNC>(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 <std::mutex> 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<std::mutex> lckFunc(m_mtxFunc);
-                        emitHelper<DataTypeIn, DataTypeOut>();
-                        m_func.cbp = nullptr;       // reset
-                    }
-                }
-            });
-            thread.detach();
-        };
-
-        return m_helper;
-    }
-
-    // only support DAsync<non void type, ...>
-    template <typename InputType = DataTypeIn>
-    typename std::enable_if<!std::is_void<InputType>::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<void()> m_postProxy;
-    class Helper : public QObject {
-        DAsync *m_async;
-    public:
-        explicit Helper(DAsync *async, QObject *parent = nullptr) noexcept
-            : QObject (parent)
-            , m_async (async)
-        {
-        }
-
-        template <typename FUNC>
-        Helper *then(FUNC &&func) {
-            m_async->m_mainWorker->template setHandle<FUNC, DataTypeOut>(std::forward<FUNC>(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/src/util/ddbusextendedabstractinterface.cpp b/src/util/ddbusextendedabstractinterface.cpp
new file mode 100644 (file)
index 0000000..e8013ac
--- /dev/null
@@ -0,0 +1,504 @@
+// SPDX-FileCopyrightText: 2015 Jolla Ltd.
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include "ddbusextendedpendingcallwatcher_p.h"
+
+#include <DDBusExtendedAbstractInterface>
+
+#include <QtDBus/QDBusMetaType>
+#include <QtDBus/QDBusMessage>
+#include <QtDBus/QDBusPendingCall>
+#include <QtDBus/QDBusPendingCallWatcher>
+#include <QtDBus/QDBusPendingReply>
+
+#include <QtCore/QDebug>
+#include <QtCore/QMetaProperty>
+
+
+Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, dBusInterface, ("org.freedesktop.DBus"))
+Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, dBusPropertiesInterface, ("org.freedesktop.DBus.Properties"))
+Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, dBusPropertiesChangedSignal, ("PropertiesChanged"))
+Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, propertyChangedSignature, ("propertyChanged(QString,QVariant)"))
+Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, propertyInvalidatedSignature, ("propertyInvalidated(QString)"))
+
+
+DDBusExtendedAbstractInterface::DDBusExtendedAbstractInterface(const QString &service, const QString &path, const char *interface, const QDBusConnection &connection, QObject *parent)
+    : QDBusAbstractInterface(service, path, interface, connection, parent)
+    , m_sync(true)
+    , m_useCache(false)
+    , m_getAllPendingCallWatcher(0)
+    , m_propertiesChangedConnected(false)
+{
+    const_cast<QDBusConnection&>(connection).connect(QString("org.freedesktop.DBus"), QString("/org/freedesktop/DBus"), QString("org.freedesktop.DBus"), QString("NameOwnerChanged"), this, SLOT(onDBusNameOwnerChanged(QString,QString,QString)));
+}
+
+DDBusExtendedAbstractInterface::~DDBusExtendedAbstractInterface()
+{
+}
+
+void DDBusExtendedAbstractInterface::setSync(bool sync) { setSync(sync, true); }
+
+/*
+ * Note: After sync is set to false, it will always return a empty value
+ * if you call the property's get function directly. So you can only get it
+ * through the changed signal when you get an property, and it's also a good idea
+ * to save a cache yourself.
+ */
+
+/*
+ * 注意: 如果设置 sync 为 false 那么在调用属性的 get 函数获取一个属性时会一直返回空值,
+ * 解决方法是监听属性的 changed 信号并自行保存一份缓存, 让 changed 信号修改这个缓存
+ */
+void DDBusExtendedAbstractInterface::setSync(bool sync, bool autoStart)
+{
+    m_sync = sync;
+
+    // init all properties
+    if (autoStart && !m_sync && !isValid())
+        startServiceProcess();
+}
+
+void DDBusExtendedAbstractInterface::getAllProperties()
+{
+    m_lastExtendedError = QDBusError();
+
+    if (!isValid()) {
+        QString errorMessage = QStringLiteral("This Extended DBus interface is not valid yet.");
+        m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
+        qDebug() << Q_FUNC_INFO << errorMessage;
+        return;
+    }
+
+    if (!m_sync && m_getAllPendingCallWatcher) {
+        // Call already in place, not repeating ...
+        return;
+    }
+
+    QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("GetAll"));
+    msg << interface();
+
+    if (m_sync) {
+        QDBusMessage reply = connection().call(msg);
+
+        if (reply.type() != QDBusMessage::ReplyMessage) {
+            m_lastExtendedError = QDBusError(reply);
+            qWarning() << Q_FUNC_INFO << m_lastExtendedError.message();
+            return;
+        }
+
+        if (reply.signature() != QLatin1String("a{sv}")) {
+            QString errorMessage = QStringLiteral("Invalid signature \"%1\" in return from call to %2")
+                .arg(reply.signature(),
+                     QString(*dBusPropertiesInterface()));
+            qWarning() << Q_FUNC_INFO << errorMessage;
+            m_lastExtendedError = QDBusError(QDBusError::InvalidSignature, errorMessage);
+            return;
+        }
+
+        QVariantMap value = reply.arguments().at(0).toMap();
+        onPropertiesChanged(interface(), value, QStringList());
+    } else {
+        QDBusPendingReply<QVariantMap> async = connection().asyncCall(msg);
+        m_getAllPendingCallWatcher = new QDBusPendingCallWatcher(async, this);
+
+        connect(m_getAllPendingCallWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(onAsyncGetAllPropertiesFinished(QDBusPendingCallWatcher*)));
+        return;
+    }
+}
+
+void DDBusExtendedAbstractInterface::connectNotify(const QMetaMethod &signal)
+{
+    if (signal.methodType() == QMetaMethod::Signal
+        && (signal.methodSignature() == *propertyChangedSignature()
+            || signal.methodSignature() == *propertyInvalidatedSignature())) {
+        if (!m_propertiesChangedConnected) {
+            QStringList argumentMatch;
+            argumentMatch << interface();
+            connection().connect(service(), path(), *dBusPropertiesInterface(), *dBusPropertiesChangedSignal(),
+                                 argumentMatch, QString(),
+                                 this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList)));
+
+            m_propertiesChangedConnected = true;
+            return;
+        }
+    } else {
+        QDBusAbstractInterface::connectNotify(signal);
+    }
+}
+
+void DDBusExtendedAbstractInterface::disconnectNotify(const QMetaMethod &signal)
+{
+    if (signal.methodType() == QMetaMethod::Signal
+        && (signal.methodSignature() == *propertyChangedSignature()
+            || signal.methodSignature() == *propertyInvalidatedSignature())) {
+        if (m_propertiesChangedConnected
+            && 0 == receivers(propertyChangedSignature()->constData())
+            && 0 == receivers(propertyInvalidatedSignature()->constData())) {
+            QStringList argumentMatch;
+            argumentMatch << interface();
+            connection().disconnect(service(), path(), *dBusPropertiesInterface(), *dBusPropertiesChangedSignal(),
+                                 argumentMatch, QString(),
+                                 this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList)));
+
+            m_propertiesChangedConnected = false;
+            return;
+        }
+    } else {
+        QDBusAbstractInterface::disconnectNotify(signal);
+    }
+}
+
+QVariant DDBusExtendedAbstractInterface::internalPropGet(const char *propname, void *propertyPtr)
+{
+    m_lastExtendedError = QDBusError();
+
+    if (m_useCache) {
+        int propertyIndex = metaObject()->indexOfProperty(propname);
+        QMetaProperty metaProperty = metaObject()->property(propertyIndex);
+        return QVariant(metaProperty.userType(), propertyPtr);
+    }
+
+    if (m_sync) {
+        QVariant ret = property(propname);
+
+        QMetaType::construct(ret.userType(), propertyPtr, ret.constData());
+
+        return ret;
+    } else {
+        if (!isValid()) {
+            QString errorMessage = QStringLiteral("This Extended DBus interface is not valid yet.");
+            m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
+            qDebug() << Q_FUNC_INFO << errorMessage;
+            return QVariant();
+        }
+
+        int propertyIndex = metaObject()->indexOfProperty(propname);
+
+        if (-1 == propertyIndex) {
+            QString errorMessage = QStringLiteral("Got unknown property \"%1\" to read")
+                .arg(QString::fromLatin1(propname));
+            m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
+            qWarning() << Q_FUNC_INFO << errorMessage;
+            return QVariant();
+        }
+
+        QMetaProperty metaProperty = metaObject()->property(propertyIndex);
+
+        if (!metaProperty.isReadable()) {
+            QString errorMessage = QStringLiteral("Property \"%1\" is NOT readable")
+                .arg(QString::fromLatin1(propname));
+            m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
+            qWarning() << Q_FUNC_INFO << errorMessage;
+            return QVariant();
+        }
+
+        // is this metatype registered?
+        const char *expectedSignature = "";
+        if (int(metaProperty.type()) != QMetaType::QVariant) {
+            expectedSignature = QDBusMetaType::typeToSignature(metaProperty.userType());
+            if (0 == expectedSignature) {
+                QString errorMessage =
+                    QStringLiteral("Type %1 must be registered with Qt D-Bus "
+                                   "before it can be used to read property "
+                                   "%2.%3")
+                    .arg(metaProperty.typeName(),
+                         interface(),
+                         propname);
+                m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
+                qWarning() << Q_FUNC_INFO << errorMessage;
+                return QVariant();
+            }
+        }
+
+        asyncProperty(propname);
+        return QVariant(metaProperty.userType(), propertyPtr);
+    }
+}
+
+void DDBusExtendedAbstractInterface::internalPropSet(const char *propname, const QVariant &value, void *propertyPtr)
+{
+    m_lastExtendedError = QDBusError();
+
+    if (m_sync) {
+        setProperty(propname, value);
+    } else {
+        if (!isValid()) {
+            QString errorMessage = QStringLiteral("This interface is not yet valid");
+            m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
+            qDebug() << Q_FUNC_INFO << errorMessage;
+            return;
+        }
+
+        int propertyIndex = metaObject()->indexOfProperty(propname);
+
+        if (-1 == propertyIndex) {
+            QString errorMessage = QStringLiteral("Got unknown property \"%1\" to write")
+                .arg(QString::fromLatin1(propname));
+            m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
+            qWarning() << Q_FUNC_INFO << errorMessage;
+            return;
+        }
+
+        QMetaProperty metaProperty = metaObject()->property(propertyIndex);
+
+        if (!metaProperty.isWritable()) {
+            QString errorMessage = QStringLiteral("Property \"%1\" is NOT writable")
+                .arg(QString::fromLatin1(propname));
+            m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage);
+            qWarning() << Q_FUNC_INFO << errorMessage;
+            return;
+        }
+
+        QVariant variant = QVariant(metaProperty.type(), propertyPtr);
+        variant = value;
+
+        asyncSetProperty(propname, variant);
+    }
+}
+
+QVariant DDBusExtendedAbstractInterface::asyncProperty(const QString &propertyName)
+{
+    QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("Get"));
+    msg << interface() << propertyName;
+    QDBusPendingReply<QVariant> async = connection().asyncCall(msg);
+    DDBusExtendedPendingCallWatcher *watcher = new DDBusExtendedPendingCallWatcher(async, propertyName, QVariant(), this);
+
+    connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(onAsyncPropertyFinished(QDBusPendingCallWatcher*)));
+
+    return QVariant();
+}
+
+void DDBusExtendedAbstractInterface::asyncSetProperty(const QString &propertyName, const QVariant &value)
+{
+    QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("Set"));
+
+    msg << interface() << propertyName << QVariant::fromValue(QDBusVariant(value));
+    QDBusPendingReply<> async = connection().asyncCall(msg);
+    DDBusExtendedPendingCallWatcher *watcher = new DDBusExtendedPendingCallWatcher(async, propertyName, value, this);
+
+    connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(onAsyncSetPropertyFinished(QDBusPendingCallWatcher*)));
+}
+
+void DDBusExtendedAbstractInterface::startServiceProcess()
+{
+    const QString &servName = service();
+
+    if (isValid())
+    {
+        qWarning() << "Service" << servName << "is already started.";
+        return;
+    }
+
+    QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus", "/", *dBusInterface(), QStringLiteral("StartServiceByName"));
+    msg << servName << quint32(0);
+    QDBusPendingReply<quint32> async = connection().asyncCall(msg);
+    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this);
+
+    connect(watcher, &QDBusPendingCallWatcher::finished, this, &DDBusExtendedAbstractInterface::onStartServiceProcessFinished);
+}
+
+void DDBusExtendedAbstractInterface::onStartServiceProcessFinished(QDBusPendingCallWatcher *w)
+{
+    if (w->isError())
+    {
+        m_lastExtendedError = w->error();
+    } else {
+        m_lastExtendedError = QDBusError();
+    }
+
+    QDBusPendingReply<quint32> reply = *w;
+
+    Q_EMIT serviceStartFinished(reply.value());
+
+    w->deleteLater();
+}
+
+void DDBusExtendedAbstractInterface::onAsyncPropertyFinished(QDBusPendingCallWatcher *w)
+{
+    DDBusExtendedPendingCallWatcher *watcher = qobject_cast<DDBusExtendedPendingCallWatcher *>(w);
+    Q_ASSERT(watcher);
+
+    QDBusPendingReply<QVariant> reply = *watcher;
+
+    if (reply.isError()) {
+        m_lastExtendedError = reply.error();
+    } else {
+        int propertyIndex = metaObject()->indexOfProperty(watcher->asyncProperty().toLatin1().constData());
+        QVariant value = demarshall(interface(),
+                                    metaObject()->property(propertyIndex),
+                                    reply.value(),
+                                    &m_lastExtendedError);
+
+        if (m_lastExtendedError.isValid()) {
+            Q_EMIT propertyInvalidated(watcher->asyncProperty());
+        } else {
+            Q_EMIT propertyChanged(watcher->asyncProperty(), value);
+        }
+    }
+
+    Q_EMIT asyncPropertyFinished(watcher->asyncProperty());
+    watcher->deleteLater();
+}
+
+void DDBusExtendedAbstractInterface::onAsyncSetPropertyFinished(QDBusPendingCallWatcher *w)
+{
+    DDBusExtendedPendingCallWatcher *watcher = qobject_cast<DDBusExtendedPendingCallWatcher *>(w);
+    Q_ASSERT(watcher);
+
+    QDBusPendingReply<> reply = *watcher;
+
+    if (reply.isError()) {
+        m_lastExtendedError = reply.error();
+    } else {
+        m_lastExtendedError = QDBusError();
+    }
+
+    Q_EMIT asyncSetPropertyFinished(watcher->asyncProperty());
+
+    // Resetting the property to its previous value after sending the
+    // finished signal
+    if (reply.isError()) {
+        m_lastExtendedError = QDBusError();
+        Q_EMIT propertyChanged(watcher->asyncProperty(), watcher->previousValue());
+    }
+
+    watcher->deleteLater();
+}
+
+void DDBusExtendedAbstractInterface::onAsyncGetAllPropertiesFinished(QDBusPendingCallWatcher *watcher)
+{
+    m_getAllPendingCallWatcher = 0;
+
+    QDBusPendingReply<QVariantMap> reply = *watcher;
+
+    if (reply.isError()) {
+        m_lastExtendedError = reply.error();
+    } else {
+        m_lastExtendedError = QDBusError();
+    }
+
+    Q_EMIT asyncGetAllPropertiesFinished();
+
+    if (!reply.isError()) {
+        onPropertiesChanged(interface(), reply.value(), QStringList());
+    }
+
+    watcher->deleteLater();
+}
+
+void DDBusExtendedAbstractInterface::onPropertiesChanged(const QString& interfaceName,
+                                                        const QVariantMap& changedProperties,
+                                                        const QStringList& invalidatedProperties)
+{
+    if (interfaceName == interface()) {
+        QVariantMap::const_iterator i = changedProperties.constBegin();
+        while (i != changedProperties.constEnd()) {
+            int propertyIndex = metaObject()->indexOfProperty(i.key().toLatin1().constData());
+
+            if (-1 == propertyIndex) {
+                qDebug() << Q_FUNC_INFO << "Got unknown changed property" <<  i.key();
+            } else {
+                QVariant value = demarshall(interface(), metaObject()->property(propertyIndex), i.value(), &m_lastExtendedError);
+
+                if (m_lastExtendedError.isValid()) {
+                    Q_EMIT propertyInvalidated(i.key());
+                } else {
+                    Q_EMIT propertyChanged(i.key(), value);
+                }
+            }
+
+            ++i;
+        }
+
+        QStringList::const_iterator j = invalidatedProperties.constBegin();
+        while (j != invalidatedProperties.constEnd()) {
+            if (-1 == metaObject()->indexOfProperty(j->toLatin1().constData())) {
+                qDebug() << Q_FUNC_INFO << "Got unknown invalidated property" <<  *j;
+            } else {
+                m_lastExtendedError = QDBusError();
+                Q_EMIT propertyInvalidated(*j);
+            }
+
+            ++j;
+        }
+    }
+}
+
+void DDBusExtendedAbstractInterface::onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
+{
+    if (name == service() && oldOwner.isEmpty())
+    {
+        m_dbusOwner = newOwner;
+        Q_EMIT serviceValidChanged(true);
+    }
+    else if (name == m_dbusOwner && newOwner.isEmpty())
+    {
+        m_dbusOwner.clear();
+        Q_EMIT serviceValidChanged(false);
+    }
+}
+
+QVariant DDBusExtendedAbstractInterface::demarshall(const QString &interface, const QMetaProperty &metaProperty, const QVariant &value, QDBusError *error)
+{
+    Q_ASSERT(metaProperty.isValid());
+    Q_ASSERT(error != 0);
+
+    if (value.userType() == metaProperty.userType()) {
+        // No need demarshalling. Passing back straight away ...
+        *error = QDBusError();
+        return value;
+    }
+
+    QVariant result = QVariant(metaProperty.userType(), (void*)0);
+    QString errorMessage;
+    const char *expectedSignature = QDBusMetaType::typeToSignature(metaProperty.userType());
+
+    if (value.userType() == qMetaTypeId<QDBusArgument>()) {
+        // demarshalling a DBus argument ...
+        QDBusArgument dbusArg = value.value<QDBusArgument>();
+
+        if (expectedSignature == dbusArg.currentSignature().toLatin1()) {
+            QDBusMetaType::demarshall(dbusArg, metaProperty.userType(), result.data());
+            if (!result.isValid()) {
+                errorMessage = QStringLiteral("Unexpected failure demarshalling "
+                                              "upon PropertiesChanged signal arrival "
+                                              "for property `%3.%4' (expected type `%5' (%6))")
+                    .arg(interface,
+                         QString::fromLatin1(metaProperty.name()),
+                         QString::fromLatin1(metaProperty.typeName()),
+                         expectedSignature);
+            }
+        } else {
+                errorMessage = QStringLiteral("Unexpected `user type' (%2) "
+                                              "upon PropertiesChanged signal arrival "
+                                              "for property `%3.%4' (expected type `%5' (%6))")
+                    .arg(dbusArg.currentSignature(),
+                         interface,
+                         QString::fromLatin1(metaProperty.name()),
+                         QString::fromLatin1(metaProperty.typeName()),
+                         QString::fromLatin1(expectedSignature));
+        }
+    } else {
+        const char *actualSignature = QDBusMetaType::typeToSignature(value.userType());
+
+        errorMessage = QStringLiteral("Unexpected `%1' (%2) "
+                                      "upon PropertiesChanged signal arrival "
+                                      "for property `%3.%4' (expected type `%5' (%6))")
+            .arg(QString::fromLatin1(value.typeName()),
+                 QString::fromLatin1(actualSignature),
+                 interface,
+                 QString::fromLatin1(metaProperty.name()),
+                 QString::fromLatin1(metaProperty.typeName()),
+                 QString::fromLatin1(expectedSignature));
+    }
+
+    if (errorMessage.isEmpty()) {
+        *error = QDBusError();
+    } else {
+        *error = QDBusMessage::createError(QDBusError::InvalidSignature, errorMessage);
+        qDebug() << Q_FUNC_INFO << errorMessage;
+    }
+
+    return result;
+}
diff --git a/src/util/ddbusextendedpendingcallwatcher.cpp b/src/util/ddbusextendedpendingcallwatcher.cpp
new file mode 100644 (file)
index 0000000..1a8e686
--- /dev/null
@@ -0,0 +1,17 @@
+// SPDX-FileCopyrightText: 2015 Jolla Ltd.
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include "ddbusextendedpendingcallwatcher_p.h"
+
+
+DDBusExtendedPendingCallWatcher::DDBusExtendedPendingCallWatcher(const QDBusPendingCall &call, const QString &asyncProperty, const QVariant &previousValue, QObject *parent)
+    : QDBusPendingCallWatcher(call, parent)
+    , m_asyncProperty(asyncProperty)
+    , m_previousValue(previousValue)
+{
+}
+
+DDBusExtendedPendingCallWatcher::~DDBusExtendedPendingCallWatcher()
+{
+}
diff --git a/src/util/ddbusextendedpendingcallwatcher_p.h b/src/util/ddbusextendedpendingcallwatcher_p.h
new file mode 100644 (file)
index 0000000..cd14d5c
--- /dev/null
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: 2015 Jolla Ltd.
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the public API.  This header file may
+// change from version to version without notice, or even be
+// removed.
+//
+// We mean it.
+//
+//
+
+
+#ifndef DDBUSEXTENDEDPENDINGCALLWATCHER_P_H
+#define DDBUSEXTENDEDPENDINGCALLWATCHER_P_H
+
+#include <QDBusPendingCallWatcher>
+#include <QDBusError>
+
+class DDBusExtendedPendingCallWatcher: public QDBusPendingCallWatcher
+{
+    Q_OBJECT
+
+public:
+    explicit DDBusExtendedPendingCallWatcher(const QDBusPendingCall &call,
+                                            const QString &asyncProperty,
+                                            const QVariant &previousValue,
+                                            QObject *parent = 0);
+    ~DDBusExtendedPendingCallWatcher();
+
+    Q_PROPERTY(QString AsyncProperty READ asyncProperty)
+    inline QString asyncProperty() const { return m_asyncProperty; }
+
+    Q_PROPERTY(QVariant PreviousValue READ previousValue)
+    inline QVariant previousValue() const { return m_previousValue; }
+
+private:
+    QString m_asyncProperty;
+    QVariant m_previousValue;
+};
+
+#endif /* DDBUSEXTENDEDPENDINGCALLWATCHER_P_H */
diff --git a/src/util/ddbusinterface.cpp b/src/util/ddbusinterface.cpp
new file mode 100644 (file)
index 0000000..8eeed6c
--- /dev/null
@@ -0,0 +1,242 @@
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "ddbusinterface.h"
+#include "ddbusinterface_p.h"
+
+#include <QMetaObject>
+#include <qmetaobject.h>
+#include <QDBusConnection>
+#include <QDBusInterface>
+#include <QDBusMetaType>
+#include <QDBusPendingReply>
+#include <QDebug>
+
+DCORE_BEGIN_NAMESPACE
+
+static const QString &FreedesktopService = QStringLiteral("org.freedesktop.DBus");
+static const QString &FreedesktopPath = QStringLiteral("/org/freedesktop/DBus");
+static const QString &FreedesktopInterface = QStringLiteral("org.freedesktop.DBus");
+static const QString &NameOwnerChanged = QStringLiteral("NameOwnerChanged");
+
+static const QString &PropertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties");
+static const QString &PropertiesChanged = QStringLiteral("PropertiesChanged");
+static const char *PropertyName = "propname";
+
+DDBusInterfacePrivate::DDBusInterfacePrivate(DDBusInterface *interface, QObject *parent)
+    : QObject(interface)
+    , m_parent(parent)
+    , m_serviceValid(false)
+    , q_ptr(interface)
+{
+    QDBusMessage message = QDBusMessage::createMethodCall(FreedesktopService, FreedesktopPath, FreedesktopInterface, "NameHasOwner");
+    message << interface->service();
+    interface->connection().callWithCallback(message, this, SLOT(onDBusNameHasOwner(bool)));
+
+    interface->connection().connect(interface->service(),
+                                    interface->path(),
+                                    PropertiesInterface,
+                                    PropertiesChanged,
+                                    {interface->interface()},
+                                    QString(),
+                                    this,
+                                    SLOT(onPropertiesChanged(QString, QVariantMap, QStringList)));
+}
+
+void DDBusInterfacePrivate::updateProp(const char *propName, const QVariant &value)
+{
+    if (!m_parent)
+        return;
+    m_propertyMap.insert(propName, value);
+    const QMetaObject *metaObj = m_parent->metaObject();
+    const char *typeName(value.typeName());
+    void *data = const_cast<void *>(value.data());
+    if (value.canConvert<QDBusArgument>()) {
+        auto dbusType = qvariant_cast<QDBusArgument>(value);
+        auto dbusMetaType = QDBusMetaType::signatureToType(dbusType.currentSignature().toUtf8());
+        typeName = QMetaType::typeName(dbusMetaType);
+
+        void *dbusData = QMetaType::create(dbusMetaType);
+        QDBusMetaType::demarshall(dbusType, dbusMetaType, dbusData);
+        data = dbusData;
+        // release dbus data of `QMetaType::create`.
+        QObject dbusDataDeleter;
+        QObject::connect(&dbusDataDeleter, &QObject::destroyed, m_parent, [dbusData, dbusMetaType]() {
+            QMetaType::destroy(dbusMetaType, dbusData);
+        }, Qt::QueuedConnection);
+    }
+    QByteArray baSignal = QStringLiteral("%1Changed(%2)").arg(propName).arg(typeName).toLatin1();
+    QByteArray baSignalName = QStringLiteral("%1Changed").arg(propName).toLatin1();
+    const char *signal = baSignal.data();
+    const char *signalName = baSignalName.data();
+    int i = metaObj->indexOfSignal(signal);
+    if (i != -1) {
+        QMetaObject::invokeMethod(m_parent, signalName, Qt::DirectConnection, QGenericArgument(typeName, data));
+    } else {
+        qDebug() << "It's not exist the property:[" << propName <<"] for parent:" << m_parent
+                 << ", interface:" << q_ptr->interface()
+                 << ", and It's changed value is:" << value;
+    }
+}
+
+void DDBusInterfacePrivate::initDBusConnection()
+{
+    if (!m_parent)
+        return;
+
+    Q_Q(DDBusInterface);
+    QDBusConnection connection = q->connection();
+    QStringList signalList;
+    QDBusInterface inter(q->service(), q->path(), q->interface(), connection);
+    const QMetaObject *meta = inter.metaObject();
+    for (int i = meta->methodOffset(); i < meta->methodCount(); ++i) {
+        const QMetaMethod &method = meta->method(i);
+        if (method.methodType() == QMetaMethod::Signal) {
+            signalList << method.methodSignature();
+        }
+    }
+    const QMetaObject *parentMeta = m_parent->metaObject();
+    for (const QString &signal : signalList) {
+        int i = parentMeta->indexOfSignal(QMetaObject::normalizedSignature(signal.toLatin1()));
+        if (i != -1) {
+            const QMetaMethod &parentMethod = parentMeta->method(i);
+            connection.connect(q->service(),
+                               q->path(),
+                               q->interface(),
+                               parentMethod.name(),
+                               m_parent,
+                               QT_STRINGIFY(QSIGNAL_CODE) + parentMethod.methodSignature());
+        }
+    }
+}
+
+void DDBusInterfacePrivate::onPropertiesChanged(const QString &interfaceName,
+                                                const QVariantMap &changedProperties,
+                                                const QStringList &invalidatedProperties)
+{
+    Q_UNUSED(interfaceName)
+    Q_UNUSED(invalidatedProperties)
+    for (QVariantMap::const_iterator it = changedProperties.cbegin(); it != changedProperties.cend(); ++it)
+        updateProp((it.key() + m_suffix).toLatin1(), it.value());
+}
+
+void DDBusInterfacePrivate::onAsyncPropertyFinished(QDBusPendingCallWatcher *w)
+{
+    QDBusPendingReply<QVariant> reply = *w;
+    if (!reply.isError()) {
+        updateProp(w->property(PropertyName).toString().toLatin1(), reply.value());
+    }
+    w->deleteLater();
+}
+
+void DDBusInterfacePrivate::setServiceValid(bool valid)
+{
+    if (m_serviceValid != valid) {
+        Q_Q(DDBusInterface);
+        m_serviceValid = valid;
+        Q_EMIT q->serviceValidChanged(m_serviceValid);
+    }
+}
+
+void DDBusInterfacePrivate::onDBusNameHasOwner(bool valid)
+{
+    Q_Q(DDBusInterface);
+    setServiceValid(valid);
+    if (valid)
+        initDBusConnection();
+    else
+        q->connection().connect(FreedesktopService,
+                                FreedesktopPath,
+                                FreedesktopInterface,
+                                NameOwnerChanged,
+                                this,
+                                SLOT(onDBusNameOwnerChanged(QString, QString, QString)));
+}
+
+void DDBusInterfacePrivate::onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
+{
+    Q_Q(DDBusInterface);
+    if (name == q->service() && oldOwner.isEmpty()) {
+        initDBusConnection();
+        q->connection().disconnect(FreedesktopService,
+                                   FreedesktopPath,
+                                   FreedesktopInterface,
+                                   NameOwnerChanged,
+                                   this,
+                                   SLOT(onDBusNameOwnerChanged(QString, QString, QString)));
+        setServiceValid(true);
+    } else if (name == q->service() && newOwner.isEmpty())
+        setServiceValid(false);
+}
+
+//////////////////////////////////////////////////////////
+// class DDBusInterface
+//////////////////////////////////////////////////////////
+
+DDBusInterface::DDBusInterface(const QString &service, const QString &path, const QString &interface, const QDBusConnection &connection, QObject *parent)
+    : QDBusAbstractInterface(service, path, interface.toLatin1(), connection, parent)
+    , d_ptr(new DDBusInterfacePrivate(this, parent))
+{
+}
+
+DDBusInterface::~DDBusInterface() {}
+
+bool DDBusInterface::serviceValid() const
+{
+    Q_D(const DDBusInterface);
+    return d->m_serviceValid;
+}
+
+QString DDBusInterface::suffix() const
+{
+    Q_D(const DDBusInterface);
+    return d->m_suffix;
+}
+
+void DDBusInterface::setSuffix(const QString &suffix)
+{
+    Q_D(DDBusInterface);
+    d->m_suffix = suffix;
+}
+
+inline QString originalPropname(const char *propname, QString suffix)
+{
+    QString propStr(propname);
+    return propStr.left(propStr.length() - suffix.length());
+}
+
+QVariant DDBusInterface::property(const char *propName)
+{
+    Q_D(DDBusInterface);
+    if (d->m_propertyMap.contains(propName))
+        return d->m_propertyMap.value(propName);
+
+    QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), PropertiesInterface, QStringLiteral("Get"));
+    msg << interface() << originalPropname(propName, d->m_suffix);
+    QDBusPendingReply<QVariant> prop = connection().asyncCall(msg);
+    if (prop.value().isValid())
+        return prop.value();
+
+    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(prop, this);
+    watcher->setProperty(PropertyName, propName);
+    connect(watcher, &QDBusPendingCallWatcher::finished, d, &DDBusInterfacePrivate::onAsyncPropertyFinished);
+    if (d->m_propertyMap.contains(propName))
+        return d->m_propertyMap.value(propName);
+
+    return QVariant();
+}
+
+void DDBusInterface::setProperty(const char *propName, const QVariant &value)
+{
+    Q_D(const DDBusInterface);
+    QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), PropertiesInterface, QStringLiteral("Set"));
+    msg << interface() << originalPropname(propName, d->m_suffix) << QVariant::fromValue(QDBusVariant(value));
+
+    QDBusPendingReply<void> reply = connection().asyncCall(msg);
+    reply.waitForFinished();
+    if (!reply.isValid()) {
+        qWarning() << reply.error().message();
+    }
+}
+DCORE_END_NAMESPACE
diff --git a/src/util/ddbusinterface_p.h b/src/util/ddbusinterface_p.h
new file mode 100644 (file)
index 0000000..6bd5db1
--- /dev/null
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+#include "ddbusinterface.h"
+
+class QDBusPendingCallWatcher;
+
+DCORE_BEGIN_NAMESPACE
+class DDBusInterfacePrivate : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit DDBusInterfacePrivate(DDBusInterface *interface, QObject *parent);
+    void updateProp(const char *propName, const QVariant &value);
+    void initDBusConnection();
+    void setServiceValid(bool valid);
+
+private Q_SLOTS:
+    void onPropertiesChanged(const QString &interfaceName,
+                             const QVariantMap &changedProperties,
+                             const QStringList &invalidatedProperties);
+    void onAsyncPropertyFinished(QDBusPendingCallWatcher *w);
+    void onDBusNameHasOwner(bool valid);
+    void onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner);
+
+public:
+    QObject *m_parent;
+    QString m_suffix;
+    QVariantMap m_propertyMap;
+    bool m_serviceValid;
+
+    DDBusInterface *q_ptr;
+    Q_DECLARE_PUBLIC(DDBusInterface)
+};
+DCORE_END_NAMESPACE
index c3498fee0c0b096f4a78eb96abdad50977565fe8..e7edc73067e080bcce76edc558179a520037457c 100644 (file)
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "ddbussender.h"
 
 #include <QDBusInterface>
diff --git a/src/util/ddbussender.h b/src/util/ddbussender.h
deleted file mode 100644 (file)
index 25cb6c6..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-#ifndef DDBUSSENDER_H
-#define DDBUSSENDER_H
-
-#include "dtkcore_global.h"
-
-#include <QObject>
-#include <QDBusConnection>
-#include <QDBusPendingCall>
-#include <QDBusInterface>
-
-#include <memory>
-
-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 <typename T>
-    DDBusCaller arg(const T &argument);
-
-private:
-    explicit DDBusCaller(const QString &method, std::shared_ptr<DDBusData> data);
-
-private:
-    std::shared_ptr<DDBusData> m_dbusData;
-    QString m_methodName;
-    QVariantList m_arguments;
-};
-
-template <typename T>
-DDBusCaller DDBusCaller::arg(const T &argument)
-{
-    m_arguments << QVariant::fromValue(argument);
-
-    return *this;
-}
-
-class LIBDTKCORESHARED_EXPORT DDBusProperty
-{
-    friend class DDBusSender;
-
-public:
-    QDBusPendingCall get();
-    template <typename T>
-    QDBusPendingCall set(const T &value);
-
-private:
-    explicit DDBusProperty(const QString &property, std::shared_ptr<DDBusData> data);
-
-private:
-    std::shared_ptr<DDBusData> m_dbusData;
-    QString m_propertyName;
-};
-
-template <typename T>
-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<DDBusData> m_dbusData;
-};
-
-#endif // DDBUSSENDER_H
index 0aed651a1a98aa100394200464889350ffd0429c..4120e033c734f46d0160b74da3492f7c94c72194 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "ddisksizeformatter.h"
 
diff --git a/src/util/ddisksizeformatter.h b/src/util/ddisksizeformatter.h
deleted file mode 100644 (file)
index 96574cc..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#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
index f8175d2be252883f1dab089fba8ae132d6781844..111305b0b2d41c7189bae60c4bdc6a37e84ce285 100644 (file)
@@ -1,26 +1,9 @@
-/*
- * Copyright (C) 2017 ~ 2019 Deepin Technology Co., Ltd.
- *
- * Author:     Chris Xiong <chirs241097@gmail.com>
- *
- * Maintainer: Chris Xiong <chirs241097@gmail.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dexportedinterface.h"
-#include "private/dobject_p.h"
+#include "base/private/dobject_p.h"
 
 #include <QHash>
 #include <QPair>
diff --git a/src/util/dexportedinterface.h b/src/util/dexportedinterface.h
deleted file mode 100644 (file)
index b8c77fc..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2019 Deepin Technology Co., Ltd.
- *
- * Author:     Chris Xiong <chirs241097@gmail.com>
- *
- * Maintainer: Chris Xiong <chirs241097@gmail.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DEXPORTEDINTERFACE_H
-#define DEXPORTEDINTERFACE_H
-
-#include <dtkcore_global.h>
-#include <dobject.h>
-
-#include <QObject>
-
-#include <functional>
-
-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<QVariant(QString)> handler = nullptr);
-    virtual QVariant invoke(const QString &action, const QString &parameters) const;
-private:
-    D_DECLARE_PRIVATE(DExportedInterface)
-};
-}
-
-DCORE_END_NAMESPACE
-
-#endif
diff --git a/src/util/dfileservices.h b/src/util/dfileservices.h
deleted file mode 100644 (file)
index d78e5c2..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DFILESERVICES_H
-#define DFILESERVICES_H
-
-#include <dtkcore_global.h>
-
-#include <QUrl>
-
-DCORE_BEGIN_NAMESPACE
-
-class LIBDTKCORESHARED_EXPORT DFileServices
-{
-public:
-    static bool showFolder(QString localFilePath, const QString &startupId = QString());
-    static bool showFolders(const QList<QString> localFilePaths, const QString &startupId = QString());
-    static bool showFolder(QUrl url, const QString &startupId = QString());
-    static bool showFolders(const QList<QUrl> urls, const QString &startupId = QString());
-
-    static bool showFileItemPropertie(QString localFilePath, const QString &startupId = QString());
-    static bool showFileItemProperties(const QList<QString> localFilePaths, const QString &startupId = QString());
-    static bool showFileItemPropertie(QUrl url, const QString &startupId = QString());
-    static bool showFileItemProperties(const QList<QUrl> urls, const QString &startupId = QString());
-
-    static bool showFileItem(QString localFilePath, const QString &startupId = QString());
-    static bool showFileItems(const QList<QString> localFilePaths, const QString &startupId = QString());
-    static bool showFileItem(QUrl url, const QString &startupId = QString());
-    static bool showFileItems(const QList<QUrl> urls, const QString &startupId = QString());
-
-    static bool trash(QString localFilePath);
-    static bool trash(const QList<QString> localFilePaths);
-    static bool trash(QUrl urlstartupId);
-    static bool trash(const QList<QUrl> urls);
-
-    static QString errorMessage();
-};
-
-DCORE_END_NAMESPACE
-
-#endif // DFILESERVICES_H
index 24efc48ee446cc82c2663e8879ca470353019a2a..d4198442384df66f62799510584491ecfa23713b 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dfileservices.h"
 
index ad43e0ebe0e2396004fe5326f2f96c803eec1cb3..72816077bbead8d1c6666c880e3f2a7413fce53e 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <QDBusInterface>
 #include <QDBusPendingCall>
index 5f4419f91e29fdf91809da10cb9e1d796df653dc..f9c9d94b5184c27fb5cc0fba3a74a62e866c9131 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2019 Deepin Technology Co., Ltd.
- *
- * Author:     justforlxz <zhangdingyuan@deepin.com>
- *
- * Maintainer: justforlxz <zhangdingyuan@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dnotifysender.h"
 #include "ddbussender.h"
diff --git a/src/util/dnotifysender.h b/src/util/dnotifysender.h
deleted file mode 100644 (file)
index c05b85c..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef DNOTIFYSENDER_H
-#define DNOTIFYSENDER_H
-
-#include "dtkcore_global.h"
-
-#include <QDBusPendingCall>
-#include <memory>
-
-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<DNotifyData> m_dbusData;
-};
-}  // namespace DUtil
-
-DCORE_END_NAMESPACE
-
-#endif  // DNOTIFYSENDER_H
index 30cf1b2e0a602d5a1ea17ea8105718ed348acb04..8431e9d4a378f760837cd50e461fe9f0bf3b94a6 100644 (file)
@@ -1,23 +1,7 @@
-/*
- * Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "dpinyin.h"
 
 #include <QFile>
diff --git a/src/util/dpinyin.h b/src/util/dpinyin.h
deleted file mode 100644 (file)
index 21a32d7..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * Author:     kirigaya <kirigaya@mkacg.com>
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * pinyin.dict from https://github.com/flyerhzm/chinese_pinyin
- */
-
-#ifndef DPINYIN_H
-#define DPINYIN_H
-
-#include <dtkcore_global.h>
-
-#include <QHash>
-
-DCORE_BEGIN_NAMESPACE
-
-QString LIBDTKCORESHARED_EXPORT Chinese2Pinyin(const QString& words);
-
-DCORE_END_NAMESPACE
-
-#endif // DPINYIN_H
index fec46cbd4651fa986d4e6d010ef09cbfae300f0a..d8308077028a1030df66890f7d25f6155d551e05 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
- *
- * Author:     rekols <rekols@foxmail.com>
- *
- * Maintainer: rekols <rekols@foxmail.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "drecentmanager.h"
 #include <QMimeDatabase>
diff --git a/src/util/drecentmanager.h b/src/util/drecentmanager.h
deleted file mode 100644 (file)
index c37ec05..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
- *
- * Author:     rekols <rekols@foxmail.com>
- *
- * Maintainer: rekols <rekols@foxmail.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef DRECENTMANAGER_H
-#define DRECENTMANAGER_H
-
-#include "dtkcore_global.h"
-#include <QString>
-
-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
index e459129006ce38429dda8dcb0347728f0badc1a3..248c74a6f591c9da664632ecb43988be54efb3f6 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2020 ~ 2020 Deepin Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@deepin.com>
- *
- * 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
- * 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 <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dthreadutils.h"
 
diff --git a/src/util/dthreadutils.h b/src/util/dthreadutils.h
deleted file mode 100644 (file)
index ed5e132..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2020 ~ 2020 Deepin Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@deepin.com>
- *
- * 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
- * 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 <http://www.gnu.org/licenses/>.
- */
-#ifndef DTHREADUTILS_H
-#define DTHREADUTILS_H
-
-#include <dtkcore_global.h>
-#include <QObject>
-#include <QSemaphore>
-#include <QThread>
-#include <QCoreApplication>
-#include <QPointer>
-#include <QDebug>
-
-DCORE_BEGIN_NAMESPACE
-
-namespace DThreadUtil {
-typedef std::function<void()> 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<QObject> target, FunctionType *func);
-};
-
-template <typename ReturnType>
-class LIBDTKCORESHARED_EXPORT _TMP
-{
-public:
-    inline static ReturnType runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function<ReturnType()> fun)
-    {
-        ReturnType result;
-        FunctionType proxyFun = [&result, &fun] () {
-            result = fun();
-        };
-
-        FunctionCallProxy::proxyCall(s, thread, target, proxyFun);
-        return result;
-    }
-
-    template <typename T>
-    inline static typename std::enable_if<!std::is_base_of<QObject, T>::value, ReturnType>::type
-            runInThread(QSemaphore *s, QThread *thread, T *, std::function<ReturnType()> fun)
-    {
-        return runInThread(s, thread, static_cast<QObject*>(nullptr), fun);
-    }
-};
-template <>
-class LIBDTKCORESHARED_EXPORT _TMP<void>
-{
-public:
-    inline static void runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function<void()> fun)
-    {
-        FunctionCallProxy::proxyCall(s, thread, target, fun);
-    }
-
-    template <typename T>
-    inline static typename std::enable_if<!std::is_base_of<QObject, T>::value, void>::type
-            runInThread(QSemaphore *s, QThread *thread, T *, std::function<void()> fun)
-    {
-        return runInThread(s, thread, static_cast<QObject*>(nullptr), fun);
-    }
-};
-
-template <typename Fun, typename... Args>
-inline auto runInThread(QSemaphore *s, QThread *thread, QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...))
-{
-    return _TMP<decltype(fun(args...))>::runInThread(s, thread, target, std::bind(fun, std::forward<Args>(args)...));
-}
-template <typename Fun, typename... Args>
-inline auto runInThread(QSemaphore *s, QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...))
-{
-    return runInThread(s, thread, nullptr, fun, std::forward<Args>(args)...);
-}
-template <typename Fun, typename... Args>
-inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
-        runInThread(QSemaphore *s, QThread *thread, QObject *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
-{
-    return _TMP<typename QtPrivate::FunctionPointer<Fun>::ReturnType>::runInThread(s, thread, target, std::bind(fun, obj, std::forward<Args>(args)...));
-}
-template <typename Fun, typename... Args>
-inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
-        runInThread(QSemaphore *s, QThread *thread, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
-{
-    return _TMP<typename QtPrivate::FunctionPointer<Fun>::ReturnType>::runInThread(s, thread, obj, std::bind(fun, obj, std::forward<Args>(args)...));
-}
-
-template <typename Fun, typename... Args>
-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>(args)...);
-}
-template <typename Fun, typename... Args>
-inline auto runInThread(QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...))
-{
-    return runInThread(thread, nullptr, fun, std::forward<Args>(args)...);
-}
-template <typename T, typename Fun, typename... Args>
-inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
-        runInThread(QThread *thread, T *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
-{
-    QSemaphore s;
-
-    return runInThread(&s, thread, target, obj, fun, std::forward<Args>(args)...);
-}
-
-template <typename Fun, typename... Args>
-inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
-        runInThread(QThread *thread, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
-{
-    return runInThread(thread, obj, obj, fun, std::forward<Args>(args)...);
-}
-
-template <typename Fun, typename... Args>
-inline auto runInMainThread(QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...))
-{
-    if (!QCoreApplication::instance()) {
-        return fun(std::forward<Args>(args)...);
-    }
-
-    return runInThread(QCoreApplication::instance()->thread(), target, fun, std::forward<Args>(args)...);
-}
-template <typename Fun, typename... Args>
-inline auto runInMainThread(Fun fun, Args&&... args) -> decltype(fun(args...))
-{
-    return runInMainThread(nullptr, fun, std::forward<Args>(args)...);
-}
-
-template <typename T, typename Fun, typename... Args>
-inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
-        runInMainThread(T *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
-{
-    if (!QCoreApplication::instance()) {
-        return (obj->*fun)(std::forward<Args>(args)...);
-    }
-
-    return runInThread(QCoreApplication::instance()->thread(), target, obj, fun, std::forward<Args>(args)...);
-}
-template <typename Fun, typename... Args>
-inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
-        runInMainThread(typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
-{
-    return runInMainThread(obj, obj, fun, std::forward<Args>(args)...);
-}
-}
-
-DCORE_END_NAMESPACE
-
-#endif // DTHREADUTILS_H
index a401cdfa3917df2f023e90bd7b47e5bcfdf53981..ae7b1eab661d232fbcde2a97469eea3f95982811 100644 (file)
@@ -1,23 +1,7 @@
-/*
- * Copyright (C) 2021 ~ 2021 UnionTech Technology Co., Ltd.
- *
- * Author:     Wang Peng <993381@qq.com>
- *
- * Maintainer: Wang Peng <wangpenga@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "dtimedloop.h"
 #include <DObject>
 #include <DObjectPrivate>
diff --git a/src/util/dtimedloop.h b/src/util/dtimedloop.h
deleted file mode 100644 (file)
index 518083a..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2021 ~ 2021 UnionTech Technology Co., Ltd.
- *
- * Author:     Wang Peng <993381@qq.com>
- *
- * Maintainer: Wang Peng <wangpenga@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-#ifndef DTIMEDLOOP_H
-#define DTIMEDLOOP_H
-#include <dtkcore_global.h>
-#include <DObject>
-
-#include <QEventLoop>
-
-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
index d733e2cb3788bcbe3d618d8c3aaee2f77e63e0d0..5f083f9977a20352475435258a97a0acde59c0ee 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "dtimeunitformatter.h"
 
diff --git a/src/util/dtimeunitformatter.h b/src/util/dtimeunitformatter.h
deleted file mode 100644 (file)
index 78bd336..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#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/src/util/dutil.h b/src/util/dutil.h
deleted file mode 100644 (file)
index d8bcd87..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <QTimer>
-#include <QThread>
-#include <QMetaObject>
-#include <QCoreApplication>
-
-namespace DUtil
-{
-
-template <typename Func1>
-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 <class T>
-void SecureErase(T *p, size_t size)
-{
-    memset(p, 0, size);
-}
-
-template <class T>
-void SecureErase(T &obj)
-{
-    for (typename T::iterator i = obj.begin(); i != obj.end(); ++i) {
-        *i = 0;
-    }
-    obj.clear();
-}
-
-}
index b4c47b7353a93fc7fa06b1c9d5ba16cf13cb66e7..67b81d05aa8fce0bf55d995ce2b92d397594502f 100644 (file)
@@ -1,23 +1,7 @@
-/*
- * Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "dvtablehook.h"
 
 #include <QFileInfo>
diff --git a/src/util/dvtablehook.h b/src/util/dvtablehook.h
deleted file mode 100644 (file)
index 563ab8c..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-#ifndef DVTABLEHOOK_H
-#define DVTABLEHOOK_H
-
-#include <dtkcore_global.h>
-
-#include <QObject>
-#include <QSet>
-#include <QDebug>
-
-#include <functional>
-#include <type_traits>
-
-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 <typename T>
-    static quintptr *getVtableOfClass()
-    {
-        QByteArray vtable_symbol(typeid(T).name());
-        vtable_symbol.prepend("_ZTV");
-
-        quintptr *vfptr_t1 = reinterpret_cast<quintptr*>(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<void(void)> 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<void(void)> 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 <typename T> class OverrideDestruct : public T { ~OverrideDestruct() override;};
-    template <typename List1, typename List2> struct CheckCompatibleArguments { enum { value = false }; };
-    template <typename List> struct CheckCompatibleArguments<List, List> { enum { value = true }; };
-
-    template<typename Fun1, typename Fun2>
-    static bool overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, quintptr *vfptr_t2, Fun2 fun2, bool forceWrite)
-    {
-        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
-        typedef QtPrivate::FunctionPointer<Fun2> FunInfo2;
-
-        //compilation error if the arguments does not match.
-        Q_STATIC_ASSERT_X((CheckCompatibleArguments<typename FunInfo1::Arguments, typename FunInfo2::Arguments>::value),
-                          "Function1 and Function2 arguments are not compatible.");
-        Q_STATIC_ASSERT_X((CheckCompatibleArguments<QtPrivate::List<typename FunInfo1::ReturnType>, QtPrivate::List<typename FunInfo2::ReturnType>>::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<typename Fun1, typename Fun2>
-    static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1,
-                      const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
-    {
-        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
-        // 检查析构函数是否为虚
-        class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;};
-
-        if (!ensureVtable((void*)t1, std::bind(&_destory_helper<typename FunInfo1::Object>, 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<typename Class, typename Fun1, typename Fun2>
-    static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
-    {
-        quintptr *vfptr_t1 = getVtableOfClass<Class>();
-
-        if (!vfptr_t1) {
-            abort();
-        }
-
-        quintptr *vfptr_t2 = getVtableOfObject(t2);
-
-        return overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, true);
-    }
-
-    template<typename Fun1, typename Fun2>
-    static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
-    {
-        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
-        return overrideVfptrFun<typename FunInfo1::Object>(fun1, t2, fun2);
-    }
-
-    template<typename Func> struct FunctionPointer { };
-    template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...)>
-    {
-        typedef QtPrivate::List<Obj*, Args...> Arguments;
-    };
-    template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) const>
-    {
-        typedef QtPrivate::List<Obj*, Args...> Arguments;
-    };
-    template<typename Fun1, typename Fun2>
-    static typename std::enable_if<QtPrivate::FunctionPointer<Fun2>::ArgumentCount >= 0, bool>::type
-            overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite)
-    {
-        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
-        typedef QtPrivate::FunctionPointer<Fun2> FunInfo2;
-
-        Q_STATIC_ASSERT(!FunInfo2::IsPointerToMemberFunction);
-        //compilation error if the arguments does not match.
-        Q_STATIC_ASSERT_X((CheckCompatibleArguments<typename FunctionPointer<Fun1>::Arguments, typename FunInfo2::Arguments>::value),
-                          "Function1 and Function2 arguments are not compatible.");
-        Q_STATIC_ASSERT_X((CheckCompatibleArguments<QtPrivate::List<typename FunInfo1::ReturnType>, QtPrivate::List<typename FunInfo2::ReturnType>>::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<typename StdFun, typename Func> struct StdFunWrap {};
-    template<typename StdFun, class Obj, typename Ret, typename... Args>
-    struct StdFunWrap<StdFun, Ret (Obj::*) (Args...)> {
-        typedef std::function<Ret(Obj*, Args...)> 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>(args)...);
-        }
-    };
-    template<typename StdFun, class Obj, typename Ret, typename... Args>
-    struct StdFunWrap<StdFun, Ret (Obj::*) (Args...) const> : StdFunWrap<StdFun, Ret (Obj::*) (Args...)>{};
-
-    template<typename Fun1, typename Fun2>
-    static inline typename std::enable_if<QtPrivate::FunctionPointer<Fun2>::ArgumentCount == -1, bool>::type
-            overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite)
-    {
-        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
-        const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<Fun2, typename FunctionPointer<Fun1>::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<Fun2, typename QtPrivate::List_Left<typename FunctionPointer<Fun1>::Arguments, Fun2ArgumentCount>::Value>::Value Fun2ReturnType;
-
-        Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<Fun2ReturnType, typename FunInfo1::ReturnType>::value),
-                          "Function1 and Function2 return type are not compatible.");
-
-        StdFunWrap<Fun2, Fun1>::fun(fun2);
-        return overrideVfptrFun(vfptr_t1, fun1, StdFunWrap<Fun2, Fun1>::call, forceWrite);
-    }
-
-    /*!
-     * \fn template<typename Fun1, typename Fun2> static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::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<typename Fun1, typename Fun2>
-    static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1, Fun2 fun2)
-    {
-        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
-        // 检查析构函数是否为虚
-        class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;};
-
-        if (!ensureVtable((void*)t1, std::bind(&_destory_helper<typename FunInfo1::Object>, t1))) {
-            return false;
-        }
-
-        bool ok = overrideVfptrFun(getVtableOfObject(t1), fun1, fun2, false);
-
-        if (!ok) {
-            // 恢复旧环境
-            resetVtable(t1);
-        }
-
-        return true;
-    }
-
-    template<typename Class, typename Fun1, typename Fun2>
-    static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2)
-    {
-        quintptr *vfptr_t1 = getVtableOfClass<Class>();
-
-        if (!vfptr_t1) {
-            abort();
-        }
-
-        return overrideVfptrFun(vfptr_t1, fun1, fun2, true);
-    }
-
-    template<typename Fun1, typename Fun2>
-    static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2)
-    {
-        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
-        return overrideVfptrFun<typename FunInfo1::Object>(fun1, fun2);
-    }
-
-    template<typename Fun1>
-    static bool resetVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *obj, Fun1 fun)
-    {
-        return resetVfptrFun((void*)obj, toQuintptr(&fun)) > 0;
-    }
-
-    template<typename Fun>
-    static Fun originalFun(const typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun)
-    {
-        quintptr o_fun = originalFun((void*)obj, toQuintptr(&fun));
-
-        return *reinterpret_cast<Fun*>(o_fun);
-    }
-
-    template<typename Fun, typename... Args>
-    static typename QtPrivate::FunctionPointer<Fun>::ReturnType
-    callOriginalFun(typename QtPrivate::FunctionPointer<Fun>::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>(args)...);
-    }
-
-private:
-    static bool copyVtable(quintptr **obj);
-    static bool clearGhostVtable(const void *obj);
-
-    template<typename T>
-    static void _destory_helper(const T *obj) {
-        delete obj;
-    }
-
-    static QMap<quintptr**, quintptr*> objToOriginalVfptr;
-    static QMap<const void*, quintptr*> objToGhostVfptr;
-    static QMap<const void*, quintptr> objDestructFun;
-};
-
-DCORE_END_NAMESPACE
-
-#endif // DVTABLEHOOK_H
diff --git a/src/util/util.cmake b/src/util/util.cmake
new file mode 100644 (file)
index 0000000..97d2736
--- /dev/null
@@ -0,0 +1,49 @@
+if(LINUX)
+  set(UTILS_SOURCE
+    ${CMAKE_CURRENT_LIST_DIR}/dtimeunitformatter.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dabstractunitformatter.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/ddisksizeformatter.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/ddbussender.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/drecentmanager.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dnotifysender.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dpinyin.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dexportedinterface.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dvtablehook.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dthreadutils.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dtimedloop.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/dfileservices_linux.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/dexportedinterface.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/ddbusinterface.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/ddbusextendedabstractinterface.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/ddbusextendedpendingcallwatcher.cpp
+  )
+else()
+  set(UTILS_SOURCE
+    ${CMAKE_CURRENT_LIST_DIR}/dtimeunitformatter.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dabstractunitformatter.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/ddisksizeformatter.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/ddbussender.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/drecentmanager.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dnotifysender.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dpinyin.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dexportedinterface.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dvtablehook.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dthreadutils.cpp 
+    ${CMAKE_CURRENT_LIST_DIR}/dtimedloop.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/dfileservices_dummy.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/dexportedinterface.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/ddbusinterface.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/ddbusextendedabstractinterface.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/ddbusextendedpendingcallwatcher.cpp
+  )
+endif()
+file(GLOB UTILS_HEADER
+  ${CMAKE_CURRENT_LIST_DIR}/../../include/util/*
+  ${CMAKE_CURRENT_LIST_DIR}/ddbusinterface_p.h
+  ${CMAKE_CURRENT_LIST_DIR}/ddbusextendedpendingcallwatcher_p.h
+)
+set(utils_SRC 
+  ${UTILS_HEADER}
+  ${UTILS_SOURCE}
+  ${CMAKE_CURRENT_LIST_DIR}/util.qrc
+)
diff --git a/src/util/util.pri b/src/util/util.pri
deleted file mode 100644 (file)
index e8b3874..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-HEADERS += \
-    $$PWD/dutil.h \
-    $$PWD/dpinyin.h \
-    $$PWD/dtimeunitformatter.h \
-    $$PWD/dabstractunitformatter.h \
-    $$PWD/ddisksizeformatter.h \
-    $$PWD/ddbussender.h \
-    $$PWD/drecentmanager.h \
-    $$PWD/dnotifysender.h \
-    $$PWD/dexportedinterface.h \
-    $$PWD/dvtablehook.h \
-    $$PWD/dfileservices.h \
-    $$PWD/dthreadutils.h \
-    $$PWD/dasync.h \
-    $$PWD/dtimedloop.h
-
-INCLUDEPATH += $$PWD
-
-includes.files += $$PWD/*.h
-includes.files += \
-    $$PWD/DUtil \
-    $$PWD/DPinyin \
-    $$PWD/DDBusSender \
-    $$PWD/DRecentManager \
-    $$PWD/DNotifySender \
-    $$PWD/DExportedInterface \
-    $$PWD/DVtableHook \
-    $$PWD/DFileServices \
-    $$PWD/DThreadUtils
-
-RESOURCES += \
-    $$PWD/util.qrc
-
-SOURCES += \
-    $$PWD/dtimeunitformatter.cpp \
-    $$PWD/dabstractunitformatter.cpp \
-    $$PWD/ddisksizeformatter.cpp \
-    $$PWD/ddbussender.cpp \
-    $$PWD/drecentmanager.cpp \
-    $$PWD/dnotifysender.cpp \
-    $$PWD/dpinyin.cpp \
-    $$PWD/dexportedinterface.cpp \
-    $$PWD/dvtablehook.cpp \
-    $$PWD/dthreadutils.cpp \
-    $$PWD/dtimedloop.cpp
-
-linux {
-    QT += dbus
-
-    SOURCES += \
-        $$PWD/dfileservices_linux.cpp
-} else {
-    SOURCES += \
-        $$PWD/dfileservices_dummy.cpp
-}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..16273aa
--- /dev/null
@@ -0,0 +1,174 @@
+set(BIN_NAME "ut-${PROJECT_NAME}")
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTORCC ON)
+# dbus
+set(CMAKE_CXX_FLAGS "-fno-access-control -ldl")
+add_compile_options(-fsanitize=address)
+add_link_options(-fsanitize=address)
+add_definitions(-DPREFIX="${CMAKE_PREFIX_PATH}")
+add_definitions(-DOS_VERSION_TEST_FILE="/tmp/etc/os-version")
+find_package(Qt5 REQUIRED COMPONENTS Gui)
+find_package(Qt5 REQUIRED COMPONENTS Core)
+if(LINUX)
+  find_package(Qt5 REQUIRED COMPONENTS DBus)
+  find_package(PkgConfig REQUIRED)
+  pkg_check_modules(QGSettings REQUIRED gsettings-qt)
+endif()
+find_package(Qt5 REQUIRED COMPONENTS Xml)
+find_package(Qt5 REQUIRED COMPONENTS Concurrent)
+find_package(Qt5 REQUIRED COMPONENTS Test)
+find_package(GTest REQUIRED)
+
+# start base 
+include(../src/base/base.cmake)
+
+# end base
+if(LINUX)
+  include(../src/dbus/dbus.cmake)
+endif()
+#message(${dbus_SRCS})
+# end dbus
+
+
+
+# start dci
+include(../src/dci/dci.cmake)
+#end dci
+
+#start filesystem
+include(../src/filesystem/filesystem.cmake)
+#end filesystem
+# start log 
+include(../src/log/log.cmake)
+#end log
+# start settings 
+include(../src/settings/settings.cmake)
+#message(${settings_SRC})
+#end settings
+
+#start utils 
+
+# TODO match linux and others
+include(../src/util/util.cmake)
+#end utils
+
+#GLOB
+include(../src/glob.cmake)
+
+#endglob
+
+
+# test 
+file(GLOB TEST_HEADER
+  ut_.*h 
+)
+file(GLOB TEST_SOURCE
+  *.cpp 
+)
+set(test_SRC 
+  ${TEST_HEADER}
+  ${TEST_SOURCE}
+)
+
+
+
+# end test
+if(LINUX)
+  add_executable(${BIN_NAME}
+    ${dbus_SRCS}
+    ${base_SRCS}
+    ${dci_SRCS}
+    ${filesystem_SRCS}
+    ${log_SRCS}        
+    ${settings_SRC}
+    ${utils_SRC}
+    ${glob_SRC}
+    ${test_SRC}
+    ./data.qrc
+  )
+  target_link_libraries(
+    ${BIN_NAME} PRIVATE 
+    Qt5::Gui 
+    Qt5::Core 
+    Qt5::DBus
+    Qt5::Xml
+    Qt5::Concurrent 
+    Qt5::Test
+    ${GTEST_LIBRARIES}
+    ${QGSettings_LIBRARIES}
+    -lpthread
+    -lm 
+    -lgcov
+  )
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+    target_compile_options(${BIN_NAME} PRIVATE -fprofile-instr-generate -ftest-coverage)
+  endif()
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    target_compile_options(${BIN_NAME} PRIVATE -fprofile-arcs -ftest-coverage)
+  endif()
+  target_include_directories( ${BIN_NAME} PUBLIC
+    ${Qt5Core_PRIVATE_INCLUDE_DIRS}
+    ${QGSettings_INCULDE_DIRS}
+    ../include/util/
+    ../include/dci/
+    ../include/log/
+    ../include/base/
+    ../include/base/private/
+    ../include/global/
+    ../include/DtkCore/
+    ../include/settings/
+    ../include/filesystem/
+    ../include/
+    ../src/filesystem/private/
+  )
+else()
+  add_executable(${BIN_NAME}
+    ${base_SRCS}
+    ${dci_SRCS}
+    ${filesystem_SRCS}
+    ${log_SRCS}
+    ${settings_SRC}
+    ${utils_SRC}
+    ${glob_SRC}
+    ${test_SRC}
+    ./data.qrc
+  )
+  target_link_libraries(
+    ${BIN_NAME} PRIVATE 
+    Qt5::Gui
+    Qt5::Core
+    Qt5::Xml
+    Qt5::Concurrent
+    Qt5::Test
+    ${GTEST_LIBRARIES}
+    -lpthread
+    -lm 
+    -lgcov
+  )
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+    target_compile_options(${BIN_NAME} PRIVATE -fprofile-instr-generate -ftest-coverage)
+  endif()
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    target_compile_options(${BIN_NAME} PRIVATE -fprofile-arcs -ftest-coverage)
+  endif()
+  target_include_directories( ${BIN_NAME} PUBLIC
+    ${Qt5Core_PRIVATE_INCLUDE_DIRS}
+    ../include/util/
+    ../include/dci/
+    ../include/log/
+    ../include/base/
+    ../include/base/private/
+    ../include/global/
+    ../include/DtkCore/
+    ../include/settings/
+    ../include/filesystem/
+    ../include/
+  )
+endif()
+
+add_test(NAME ${BIN_NAME} COMMAND ${BIN_NAME})
diff --git a/tests/ddesktopentry/ddesktopentry.pro b/tests/ddesktopentry/ddesktopentry.pro
deleted file mode 100644 (file)
index 0b80ab1..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-QT += testlib
-QT -= gui
-
-TARGET = tst_ddesktopentrytest
-TEMPLATE = app
-CONFIG += c++11
-CONFIG -= app_bundle
-
-!isEmpty(DTK_STATIC_LIB){
-    DEFINES += DTK_STATIC_LIB
-}
-
-load(dtk_testcase)
-
-win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../src/release/ -ldtkcore -lgtest
-else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../src/debug/ -ldtkcore -lgtest
-else:unix: LIBS += -L$$OUT_PWD/../../src/ -ldtkcore -lgtest
-
-INCLUDEPATH += $$PWD/../../src
-DEPENDPATH += $$PWD/../../src
-unix:QMAKE_RPATHDIR += $$OUT_PWD/../../src
-
-QMAKE_LFLAGS += -Wl,--export-dynamic
-
-SOURCES += \
-    $$PWD/../../src/ddesktopentry.cpp \
-    ut_ddesktopentrytest.cpp
diff --git a/tests/dthreadutils/dthreadutils.pro b/tests/dthreadutils/dthreadutils.pro
deleted file mode 100644 (file)
index 4bdc78e..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-QT += testlib concurrent
-QT -= gui
-
-TEMPLATE = app
-CONFIG += c++11
-
-!isEmpty(DTK_STATIC_LIB){
-    DEFINES += DTK_STATIC_LIB
-}
-
-load(dtk_testcase)
-
-win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../src/release/ -ldtkcore
-else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../src/debug/ -ldtkcore
-else:unix: LIBS += -L$$OUT_PWD/../../src/ -ldtkcore
-
-INCLUDEPATH += $$PWD/../../src
-DEPENDPATH += $$PWD/../../src
-QMAKE_RPATHDIR += $$OUT_PWD/../../src
-
-QMAKE_LFLAGS += -Wl,--export-dynamic
-
-SOURCES += \
-    $$PWD/../../src/util/dthreadutils.cpp \
-    ut_dthreadutils.cpp
diff --git a/tests/dutils/dutils.pro b/tests/dutils/dutils.pro
deleted file mode 100644 (file)
index 8e56bec..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-QT += testlib dbus
-QT -= gui
-
-TEMPLATE = app
-CONFIG += c++11
-
-!isEmpty(DTK_STATIC_LIB){
-    DEFINES += DTK_STATIC_LIB
-}
-# 使用 tmp 目录下的 os-version
-DEFINES += OS_VERSION_TEST_FILE=\\\"/tmp/etc/os-version\\\"
-
-load(dtk_testcase)
-
-SOURCES += \
-    $$PWD/../../src/util/dtimeunitformatter.cpp \
-    $$PWD/../../src/util/ddisksizeformatter.cpp \
-    $$PWD/../../src/log/LogManager.cpp \
-    $$PWD/../../src/filesystem/dpathbuf.cpp \
-    $$PWD/../../src/util/ddbussender.cpp \
-    $$PWD/../../src/settings/dsettings.cpp \
-    $$PWD/../../src/settings/dsettingsgroup.cpp \
-    $$PWD/../../src/settings/dsettingsoption.cpp \
-    $$PWD/../../src/dsysinfo.cpp \
-    main.cpp \
-    ut_dutil.cpp \
-    ut_singleton.cpp
-
-HEADERS += \
-    $$PWD/../../src/util/dtimeunitformatter.h \
-    $$PWD/../../src/util/ddisksizeformatter.h \
-    $$PWD/../../src/log/LogManager.h \
-    $$PWD/../../src/filesystem/dpathbuf.h \
-    $$PWD/../../src/util/ddbussender.h \
-    $$PWD/../../src/settings/dsettings.h \
-    $$PWD/../../src/settings/dsettingsgroup.h \
-    $$PWD/../../src/settings/dsettingsoption.h \
-    $$PWD/../../src/dsysinfo.h \
-    $$PWD/../../src/base/dsingleton.h \
-    ut_dutil.h \
-    ut_singleton.h
-
-win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../src/release/ -ldtkcore -lgtest
-else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../src/debug/ -ldtkcore -lgtest
-else:unix: LIBS += -L$$OUT_PWD/../../src/ -ldtkcore -lgtest
-
-INCLUDEPATH += \
-    $$PWD/../../src \
-    $$PWD/../../src/log
-DEPENDPATH += $$PWD/../../src
-QMAKE_RPATHDIR += $$PWD/../../src
-
-RESOURCES += \
-    data.qrc
diff --git a/tests/dvtablehook/dvtablehook.pro b/tests/dvtablehook/dvtablehook.pro
deleted file mode 100644 (file)
index 0425266..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-QT += testlib
-QT -= gui
-
-TEMPLATE = app
-CONFIG += c++11
-
-# TODO: vtabhook release test failed
-QMAKE_CXXFLAGS_RELEASE -= -O2
-
-!isEmpty(DTK_STATIC_LIB){
-    DEFINES += DTK_STATIC_LIB
-}
-
-load(dtk_testcase)
-
-win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../src/release/ -ldtkcore -lgtest
-else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../src/debug/ -ldtkcore -lgtest
-else:unix: LIBS += -L$$OUT_PWD/../../src/ -ldtkcore -lgtest -ldl
-
-INCLUDEPATH += $$PWD/../../src
-DEPENDPATH += $$PWD/../../src
-QMAKE_RPATHDIR += $$OUT_PWD/../../src
-
-QMAKE_LFLAGS += -Wl,--export-dynamic
-
-HEADERS += \
-    $$PWD/../../src/util/dvtablehook.h
-
-SOURCES += \
-    $$PWD/../../src/util/dvtablehook.cpp \
-    ut_dvtablehook.cpp
index 98ae7f52e097db35b507b9172128c3c56471cdb6..b13496bc4b5c814baa70cfe60735f24eeaf4c188 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "ut_dutil.h"
 #include <QGuiApplication>
diff --git a/tests/test-recoverage-qmake.sh b/tests/test-recoverage-qmake.sh
deleted file mode 100755 (executable)
index f89898b..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-
-BUILD_DIR=`pwd`/../build-ut
-HTML_DIR=${BUILD_DIR}/html
-XML_DIR=${BUILD_DIR}/report
-#EXTRACT_ARGS="src"
-cd ../
-rm -rf $BUILD_DIR
-mkdir $BUILD_DIR
-cd $BUILD_DIR
-qmake .. CONFIG+=debug
-make -j$(nproc)
-cd ../tests/
-
-rm -rf $BUILD_DIR
-mkdir $BUILD_DIR
-cd $BUILD_DIR
-qmake ../ CONFIG+=debug
-export ASAN_OPTIONS=halt_on_error=0
-TESTARGS="--gtest_output=xml:${XML_DIR}/report_dtkcore.xml"  make check -j$(nproc)
-
-lcov -d ./ -c -o coverage_all.info
-#lcov --extract coverage_all.info $EXTRACT_ARGS --output-file coverage.info
-lcov --remove coverage_all.info "*/tests/*" "*/usr/include*" "*build/src*" "*build-ut/src*" --output-file coverage.info
-cd ..
-genhtml -o $HTML_DIR $BUILD_DIR/coverage.info && mv ${BUILD_DIR}/html/index.html ${BUILD_DIR}/html/cov_dtkcore.html
-
-test -e ${BUILD_DIR}/asan.log* && mv ${BUILD_DIR}/asan.log* ${BUILD_DIR}/asan_dtkcore.log || touch ${BUILD_DIR}/asan.log
-
diff --git a/tests/test-recoverage.sh b/tests/test-recoverage.sh
new file mode 100755 (executable)
index 0000000..9b40cc0
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+
+BUILD_DIR=`pwd`/../build/tests/
+HTML_DIR=${BUILD_DIR}/html
+XML_DIR=${BUILD_DIR}/report
+
+export ASAN_OPTIONS="halt_on_error=0"
+
+# back to project directroy
+cd ..
+
+cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug 
+
+cmake --build build --target ut-DtkCore -j$(nproc)
+
+cd $BUILD_DIR
+
+./ut-DtkCore --gtest_output=xml:${XML_DIR}/report_dtkcore.xml
+
+lcov -d ./ -c -o coverage_all.info
+#lcov --extract coverage_all.info $EXTRACT_ARGS --output-file coverage.info
+lcov --remove coverage_all.info "*/tests/*" "*/usr/include*" "*build/src*" "*build-ut/src*" --output-file coverage.info
+cd ..
+genhtml -o $HTML_DIR $BUILD_DIR/coverage.info && mv ${BUILD_DIR}/html/index.html ${BUILD_DIR}/html/cov_dtkcore.html
+
+test -e ${BUILD_DIR}/asan.log* && mv ${BUILD_DIR}/asan.log* ${BUILD_DIR}/asan_dtkcore.log || touch ${BUILD_DIR}/asan.log
+
index 13f31b590e249cffe2848cca965187beb111b813..d5ece7ecd9587fd1461ada649d19c56521ef3649 100644 (file)
@@ -1,23 +1,7 @@
-/*
- * Copyright (C) 2021 Uniontech Technology Co., Ltd.
- *
- * Author:     yeshanshan <yeshanshan@live.com>
- *
- * Maintainer: yeshanshan <yeshanshan@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #pragma once
 
 #include <QDir>
diff --git a/tests/tests.pro b/tests/tests.pro
deleted file mode 100644 (file)
index a3caf63..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-TEMPLATE = app
-QT += core dbus xml testlib concurrent
-CONFIG += thread c++11 link_pkgconfig
-CONFIG -= app_bundle
-
-QMAKE_LFLAGS += -Wl,--export-dynamic
-
-CONFIG(debug, debug|release) {
-LIBS += -lgtest -lgmock
-QMAKE_CXXFLAGS += -g -Wall -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
-QMAKE_LFLAGS += -g -Wall -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
-QMAKE_CXX += -g -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
-}
-
-load(dtk_testcase)
-
-# 指定moc文件生成目录和src一样
-MOC_DIR=$$OUT_PWD/../src
-
-# 使用 tmp 目录下的 os-version
-DEFINES += OS_VERSION_TEST_FILE=\\\"/tmp/etc/os-version\\\"
-
-DEPENDPATH += $$PWD/../src
-
-unix: {
-QMAKE_RPATHDIR += $$OUT_PWD/../src
-LIBS += -lgtest
-# for dlsym
-LIBS += -ldl
-# TODO: vtabhook release test failed
-QMAKE_CXXFLAGS_RELEASE -= -O2
-}
-
-INCLUDEPATH += \
-    $$PWD/../src \
-    $$PWD/../src/base \
-    $$PWD/../src/base/private \
-    $$PWD/../src/filesystem \
-    $$PWD/../src/log \
-    $$PWD/../src/settings \
-    $$PWD/../src/util
-
-include($$PWD/../src/base/base.pri)
-include($$PWD/../src/filesystem/filesystem.pri)
-include($$PWD/../src/log/log.pri)
-include($$PWD/../src/settings/settings.pri)
-include($$PWD/../src/util/util.pri)
-
-HEADERS += $$PWD/ut_*.h \
-    $$PWD/../src/dtkcore_global.h \
-    $$PWD/../src/dsysinfo.h \
-    $$PWD/../src/dsecurestring.h \
-    $$PWD/../src/ddesktopentry.h \
-    $$PWD/../src/dconfig.h
-
-SOURCES += $$PWD/*.cpp \
-    $$PWD/../src/dsysinfo.cpp \
-    $$PWD/../src/dsecurestring.cpp \
-    $$PWD/../src/ddesktopentry.cpp \
-    $$PWD/../src/dconfig.cpp
-
-linux: {
-    HEADERS += \
-        $$PWD/../src/dconfigfile.h
-
-    SOURCES += \
-        $$PWD/../src/dconfigfile.cpp
-
-    QT += dbus
-
-    config.files = $$PWD/../src/dbus/org.desktopspec.ConfigManager.xml
-    config.header_flags += -c DSGConfig -N
-    config.source_flags += -c DSGConfig -N
-
-    manager.files = $$PWD/../src/dbus/org.desktopspec.ConfigManager.Manager.xml
-    manager.header_flags += -c DSGConfigManager -N
-    manager.source_flags += -c DSGConfigManager -N
-
-    DBUS_INTERFACES += config manager
-
-}
-
-RESOURCES += data.qrc
index d0f2d0d1c45132baa95afa8e887a560879c38cf0..967912fdab06aad4b66a8766a90eb090b8e5d9d1 100644 (file)
@@ -1,23 +1,7 @@
-/*
- * Copyright (C) 2021 ~ 2021 UnionTech Technology Co., Ltd.
- *
- * Author:     Wang Peng <993381@qq.com>
- *
- * Maintainer: Wang Peng <wangpenga@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include <QTest>
 #include <QTimer>
 #include <gtest/gtest.h>
diff --git a/tests/ut_dcapfile.cpp b/tests/ut_dcapfile.cpp
new file mode 100644 (file)
index 0000000..fcafa09
--- /dev/null
@@ -0,0 +1,318 @@
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dcapfsfileengine_p.h"
+#include "filesystem/dcapmanager.h"
+#include "filesystem/dcapfile.h"
+
+#include <gtest/gtest.h>
+#include <QDir>
+#include <QDir>
+#include <QTemporaryDir>
+
+DCORE_USE_NAMESPACE
+
+#ifndef GTEST_SKIP
+#define SKIP return GTEST_SUCCEED() << "Skip all tests"
+#else
+#define SKIP GTEST_SKIP() << "Skip all tests"
+#endif
+
+#define TMPCAP_PATH "/tmp/cap"
+
+class ut_DCapFSFileEngine : public testing::Test
+{
+protected:
+    void SetUp() override;
+    void TearDown() override;
+
+    DCapManager *manager;
+    QFile *file;
+};
+
+void ut_DCapFSFileEngine::SetUp()
+{
+     manager = DCapManager::instance();
+     manager->removePath("/tmp");
+     manager->appendPath(TMPCAP_PATH);
+     manager->registerFileEngine();
+
+     file = new QFile(nullptr);
+     QDir dir(TMPCAP_PATH);
+     if (!dir.exists())
+         ASSERT_TRUE(dir.mkdir(TMPCAP_PATH));
+}
+
+void ut_DCapFSFileEngine::TearDown()
+{
+    manager->unregisterFileEngine();
+    manager->appendPath("/tmp");
+    delete file;
+    QDir dir(TMPCAP_PATH);
+    if (dir.exists())
+        ASSERT_TRUE(dir.removeRecursively());
+}
+
+TEST_F(ut_DCapFSFileEngine, testSubDirCanReadWrite)
+{
+    manager->appendPath("/usr/share/");
+    ASSERT_FALSE(DCapFSFileEngine("").canReadWrite("/tmp/usr/share/file0"));
+    manager->removePath("/usr/share/");
+}
+
+TEST_F(ut_DCapFSFileEngine, testDCapFileOpenFile)
+{
+    file->setFileName("/tmp/file0");
+    bool open = file->open(QIODevice::WriteOnly);
+    ASSERT_FALSE(open);  // path `/tmp` has removed from setup.
+
+    QDir dir(TMPCAP_PATH);
+    ASSERT_TRUE(dir.exists());
+    if (!dir.exists("subdir"))
+        ASSERT_TRUE(dir.mkdir("subdir"));
+    ASSERT_TRUE(dir.cd("subdir"));
+    file->close();
+    file->setFileName(dir.path() + "/file0");  // path: /tmp/etc/subdir/file0
+    ASSERT_TRUE(file->open(QIODevice::WriteOnly));
+}
+
+TEST_F(ut_DCapFSFileEngine, testDCapFileRename)
+{
+    file->setFileName(TMPCAP_PATH"/file0");
+    bool open = file->open(QIODevice::WriteOnly);
+    ASSERT_TRUE(open);  // Default path contains the `/tmp` path.
+    file->close();
+
+    ASSERT_TRUE(file->rename(TMPCAP_PATH"/file1"));
+    ASSERT_FALSE(file->rename("/tmp/file1"));
+
+    QDir dir(TMPCAP_PATH);
+    if (!dir.exists("subdir"))
+        ASSERT_TRUE(dir.mkdir("subdir"));
+    ASSERT_TRUE(file->rename(TMPCAP_PATH"/subdir/file0"));
+}
+
+TEST_F(ut_DCapFSFileEngine, testDCapFileRemove)
+{
+    file->setFileName(TMPCAP_PATH"/test");
+    ASSERT_TRUE(file->open(QFile::WriteOnly));
+    file->close();
+    ASSERT_TRUE(file->exists());
+    ASSERT_TRUE(file->remove());
+    ASSERT_FALSE(file->exists());
+
+    QDir dir(TMPCAP_PATH);
+    if (!dir.exists("subdir"))
+        ASSERT_TRUE(dir.mkdir("subdir"));
+
+    manager->appendPath(TMPCAP_PATH"/subdir");  // create a new subdir file.
+    ASSERT_FALSE(manager->paths().contains(TMPCAP_PATH"/subdir"));  // already has /tmp/cap
+    QFile f(TMPCAP_PATH"/subdir/test");
+    ASSERT_TRUE(f.open(QFile::WriteOnly));
+    f.close();
+    ASSERT_TRUE(f.exists());
+
+    file->setFileName(TMPCAP_PATH"/subdir/test");
+    ASSERT_TRUE(file->remove());
+
+    manager->appendPath("/tmp");
+    ASSERT_TRUE(manager->paths().contains("/tmp"));
+    file->setFileName("/tmp/file0");
+    bool open = file->open(QIODevice::WriteOnly);
+    ASSERT_TRUE(open);
+    manager->removePath("/tmp");
+    ASSERT_FALSE(manager->paths().contains("/tmp"));
+    ASSERT_FALSE(file->remove());
+}
+
+TEST_F(ut_DCapFSFileEngine, testDCapFileLink)
+{
+    file->setFileName(TMPCAP_PATH"/test");
+    ASSERT_TRUE(file->open(QFile::WriteOnly));
+    file->close();
+    ASSERT_TRUE(file->exists());
+    ASSERT_TRUE(file->link(TMPCAP_PATH"/test1"));
+    ASSERT_FALSE(file->link("/tmp/test1"));
+
+    QDir dir(TMPCAP_PATH);
+    if (!dir.exists("subdir"))
+        ASSERT_TRUE(dir.mkdir("subdir"));
+    ASSERT_TRUE(file->link(TMPCAP_PATH"/subdir/test1"));
+
+    ASSERT_TRUE(file->remove());  // clean the file.
+}
+
+TEST_F(ut_DCapFSFileEngine, testDCapFileCopy)
+{
+    file->setFileName(TMPCAP_PATH"/test");
+    ASSERT_TRUE(file->open(QFile::WriteOnly));
+    file->write("test");
+    file->close();
+    ASSERT_TRUE(file->exists());
+    ASSERT_TRUE(file->copy(TMPCAP_PATH"/test2"));
+    ASSERT_TRUE(file->copy("/tmp/test2"));
+    ASSERT_FALSE(QFileInfo::exists(TMPCAP_PATH"/subdir/test2"));
+
+    QDir dir(TMPCAP_PATH);
+    if (!dir.exists("subdir"))
+        ASSERT_TRUE(dir.mkdir("subdir"));
+    ASSERT_TRUE(file->copy(TMPCAP_PATH"/subdir/test2"));
+    ASSERT_TRUE(QFileInfo::exists(TMPCAP_PATH"/subdir/test2"));
+}
+
+TEST_F(ut_DCapFSFileEngine, testDCapFileResize)
+{
+    file->setFileName(TMPCAP_PATH"/test");
+    ASSERT_TRUE(file->open(QFile::WriteOnly));
+    file->write("test");
+    file->close();
+    ASSERT_TRUE(file->exists());
+    ASSERT_TRUE(file->resize(10));
+    ASSERT_EQ(file->size(), 10);
+
+    QDir dir(TMPCAP_PATH);
+    if (!dir.exists("subdir"))
+        ASSERT_TRUE(dir.mkdir("subdir"));
+
+    QFile f(TMPCAP_PATH"/subdir/test");
+    ASSERT_TRUE(f.open(QFile::WriteOnly));
+    f.write("test");
+    f.close();
+    ASSERT_TRUE(f.exists());
+
+    file->setFileName(TMPCAP_PATH"/subdir/test");
+    ASSERT_TRUE(file->resize(10));
+
+    DCapManager::instance()->appendPath("/tmp");
+
+    f.setFileName("/tmp/test");
+    ASSERT_TRUE(f.open(QFile::WriteOnly));
+    f.write("test");
+    f.close();
+    ASSERT_TRUE(f.exists());
+    DCapManager::instance()->removePath("/tmp");
+
+    file->setFileName("/tmp/test");
+    ASSERT_FALSE(file->resize(10));
+    file->remove();}
+
+TEST_F(ut_DCapFSFileEngine, testDCapDirEntry)
+{
+    auto createFiles = [this](const QString & path, int count) {
+        for (int i = 0; i < count; ++i) {
+            file->setFileName(QString(path) + "/test" + QString::number(i));
+            ASSERT_TRUE(file->open(QFile::WriteOnly));
+            file->close();
+            ASSERT_TRUE(file->exists());
+        }
+    };
+
+    createFiles(TMPCAP_PATH, 10);
+    QDir dir(TMPCAP_PATH);
+    ASSERT_TRUE(dir.entryList(QDir::Files).length() == 10);
+
+    if (!dir.exists("subdir"))
+        ASSERT_TRUE(dir.mkdir("subdir"));
+
+    dir.setPath(TMPCAP_PATH"/subdir");
+    createFiles(TMPCAP_PATH"/subdir", 10);
+    ASSERT_TRUE(dir.entryList(QDir::Files).length() == 10);
+    manager->removePath(TMPCAP_PATH);
+    ASSERT_TRUE(dir.entryList(QDir::Files).length() == 0);
+}
+
+TEST(ut_DCapFileAndDir, testDCapFileOpen)
+{
+    DCapFile file("/tmp/test0");
+    ASSERT_TRUE(file.open(DCapFile::WriteOnly));  // Default, '/tmp' is allowable for writing.
+    file.close();
+
+    DCapManager::instance()->removePath("/tmp");
+    ASSERT_FALSE(file.open(DCapFile::WriteOnly));
+    DCapManager::instance()->appendPath("/tmp");
+}
+
+TEST(ut_DCapFileAndDir, testDCapFileOperation)
+{
+    auto CheckResult = [](bool result) {
+        DCapFile file("/tmp/test0");
+        ASSERT_EQ(file.open(DCapFile::WriteOnly), result);
+        file.close();
+
+        ASSERT_EQ(file.exists(), result);
+        ASSERT_EQ(DCapFile::exists(file.fileName()), result);
+        ASSERT_EQ(file.remove(), result);
+
+        ASSERT_EQ(file.open(DCapFile::WriteOnly), result);
+        file.close();
+        ASSERT_EQ(DCapFile::remove(file.fileName()), result);
+
+        ASSERT_EQ(file.open(DCapFile::WriteOnly), result);
+        file.close();
+        ASSERT_EQ(file.rename("/tmp/test1"), result);
+        ASSERT_EQ(DCapFile::rename(file.fileName(), "/tmp/test0"), result);
+
+        file.setFileName("/tmp/test0");
+        ASSERT_EQ(file.link("/tmp/test_link0"), result);
+        ASSERT_EQ(DCapFile::link(file.fileName(), "/tmp/test_link1"), result);
+        ASSERT_EQ(DCapFile::remove("/tmp/test_link0"), result);
+        ASSERT_EQ(DCapFile::remove("/tmp/test_link1"), result);
+
+        file.setFileName("/tmp/test0");
+        ASSERT_EQ(file.copy("/tmp/test_copy0"), result);
+        ASSERT_EQ(DCapFile::copy(file.fileName(), "/tmp/test_copy1"), result);
+        ASSERT_EQ(DCapFile::remove("/tmp/test_copy0"), result);
+        ASSERT_EQ(DCapFile::remove("/tmp/test_copy1"), result);
+
+        ASSERT_EQ(file.resize(10), result);
+        ASSERT_EQ(file.size() == 10, result);
+        ASSERT_EQ(DCapFile::resize(file.fileName(), 5), result);
+        ASSERT_EQ(file.size() == 5, result);
+        ASSERT_EQ(file.remove(), result);
+    };
+
+    CheckResult(true);
+    DCapManager::instance()->removePath("/tmp");
+    CheckResult(false);
+    DCapManager::instance()->appendPath("/tmp");
+}
+
+TEST(ut_DCapFileAndDir, testDCapDirOperation)
+{
+    DCapDir dir("/tmp");
+    ASSERT_TRUE(dir.exists());
+    ASSERT_FALSE(dir.entryList().isEmpty());
+    ASSERT_FALSE(dir.entryInfoList().isEmpty());
+
+    DCapFile file(dir.path() + "/test0");
+    ASSERT_TRUE(file.open(DCapFile::WriteOnly));
+    file.close();
+    ASSERT_TRUE(dir.exists("test0"));
+
+    ASSERT_TRUE(dir.mkdir("cap"));
+    ASSERT_TRUE(dir.exists("cap"));
+    ASSERT_TRUE(dir.rmdir("cap"));
+    ASSERT_FALSE(dir.exists("cap"));
+
+    dir.mkdir("cap");
+    ASSERT_TRUE(dir.cd("cap"));
+    ASSERT_TRUE(dir.exists());
+    DCapManager::instance()->removePath("/tmp");
+    DCapManager::instance()->appendPath(dir.path());
+    ASSERT_FALSE(dir.cd(".."));
+    dir.setPath("/tmp");
+
+    ASSERT_TRUE(dir.entryList().isEmpty());
+    ASSERT_TRUE(dir.entryInfoList().isEmpty());
+    ASSERT_TRUE(dir.exists("cap"));
+    ASSERT_FALSE(dir.remove("test0"));
+    ASSERT_FALSE(dir.rename("test0", "test1"));
+
+    ASSERT_TRUE(dir.mkpath(TMPCAP_PATH"/subdir"));
+    ASSERT_TRUE(dir.rmpath(TMPCAP_PATH"/subdir"));
+
+    DCapManager::instance()->appendPath("/tmp");
+    ASSERT_TRUE(dir.rename("test0", "test1"));
+    ASSERT_TRUE(dir.remove("test1"));
+}
index 9f71611a257f6ceb772c6cfe495412ccbb08b221..6c2f714b6de943cb429514be195d7deebd27def6 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 Uniontech Technology Co., Ltd.
- *
- * Author:     yeshanshan <yeshanshan@live.com>
- *
- * Maintainer: yeshanshan <yeshanshan@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <DConfig>
 #include <QBuffer>
@@ -29,9 +12,9 @@
 
 DCORE_USE_NAMESPACE
 
+static EnvGuard dsgDataDir;
 static constexpr char const *APP_ID = "tests";
 static constexpr char const *FILE_NAME = "example";
-static EnvGuard dsgDataDir;
 class ut_DConfig : public testing::Test
 {
 protected:
index 614a5e8b34eb72f9fcc8fe3ee6a1857385a7219d..b8698d540214fbdc90c1542e9792763625d6c560 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 Uniontech Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <DConfigFile>
 #include <DStandardPaths>
diff --git a/tests/ut_ddci.cpp b/tests/ut_ddci.cpp
new file mode 100644 (file)
index 0000000..4a8b896
--- /dev/null
@@ -0,0 +1,447 @@
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <DDciFile>
+
+#include <QLoggingCategory>
+#include <QDir>
+#include <QDirIterator>
+#include <QFile>
+#include <QFileInfo>
+#include <QDateTime>
+
+#include <gtest/gtest.h>
+
+DCORE_USE_NAMESPACE
+
+class ut_DCI : public testing::Test {
+public:
+    void SetUp() override {
+        QLoggingCategory::setFilterRules("dtk.dci.file.debug=true\n"
+                                         "dtk.dci.fileengine.debug=true");
+    }
+};
+
+TEST_F(ut_DCI, DDciFile) {
+    {
+        // 空文件
+        DDciFile dciFile;
+        ASSERT_TRUE(dciFile.isValid());
+        ASSERT_TRUE(dciFile.exists("/"));
+        ASSERT_EQ(dciFile.list("/").count(), 0);
+        ASSERT_EQ(dciFile.toData(), QByteArrayLiteral("DCI\0\1\0\0\0"));
+    }
+
+    {
+        // 最小数据文件
+        DDciFile dciFile(QByteArrayLiteral("DCI\0\1\0\0\0"));
+        ASSERT_TRUE(dciFile.isValid());
+        ASSERT_TRUE(dciFile.exists("/"));
+        ASSERT_EQ(dciFile.list("/").count(), 0);
+        ASSERT_EQ(dciFile.toData(), QByteArrayLiteral("DCI\0\1\0\0\0"));
+    }
+
+    {
+        // 错误的版本
+        DDciFile dciFile(QByteArrayLiteral("DCI\0\0\0\0\0"));
+        ASSERT_FALSE(dciFile.isValid());
+    }
+
+    {
+        // 错误的文件数量
+        DDciFile dciFile(QByteArrayLiteral("DCI\0\1\255\255\255"));
+        ASSERT_FALSE(dciFile.isValid());
+    }
+
+    {
+        // 创建目录
+        DDciFile dciFile;
+        ASSERT_TRUE(dciFile.mkdir("/test"));
+        ASSERT_TRUE(dciFile.exists("/test"));
+        ASSERT_EQ(dciFile.list("/"), QStringList{"/test"});
+        ASSERT_EQ(dciFile.childrenCount("/"), 1);
+    }
+
+    {
+        // 创建目录
+        DDciFile dciFile;
+        ASSERT_TRUE(dciFile.mkdir("/test"));
+        ASSERT_TRUE(dciFile.exists("/test"));
+        ASSERT_EQ(dciFile.list("/"), QStringList{"/test"});
+        ASSERT_EQ(dciFile.type("/test"), DDciFile::Directory);
+        ASSERT_EQ(dciFile.childrenCount("/"), 1);
+
+        // 创建文件
+        ASSERT_TRUE(dciFile.writeFile("/test/test.txt", "test\n"));
+        ASSERT_TRUE(dciFile.exists("/test/test.txt"));
+        ASSERT_EQ(dciFile.list("/test"), QStringList{"/test/test.txt"});
+        ASSERT_EQ(dciFile.childrenCount("/test"), 1);
+        ASSERT_EQ(dciFile.type("/test/test.txt"), DDciFile::File);
+        ASSERT_EQ(dciFile.dataRef("/test/test.txt"), QByteArray("test\n"));
+
+        // 软链接
+        // 链接一个不存在的文件
+        ASSERT_TRUE(dciFile.link("/no", "/1.link"));
+        ASSERT_TRUE(dciFile.exists("/1.link"));
+        ASSERT_EQ(dciFile.type("/1.link"), DDciFile::Symlink);
+        ASSERT_EQ(dciFile.symlinkTarget("/1.link"), "/no");
+        ASSERT_EQ(dciFile.symlinkTarget("/1.link", true), "/no");
+        // 当链接目标无效时,无论如何都不允许写入数据
+        ASSERT_FALSE(dciFile.writeFile("/1.link", "", false));
+        ASSERT_FALSE(dciFile.writeFile("/1.link", "", true));
+        // 链接一个目录
+        ASSERT_TRUE(dciFile.link("/test", "/2.link"));
+        ASSERT_TRUE(dciFile.symlinkTarget("/2.link").isEmpty());
+        ASSERT_FALSE(dciFile.mkdir("/2.link/test"));
+        // 链接一个存在的文件
+        ASSERT_TRUE(dciFile.link("/test/test.txt", "/3.link"));
+        ASSERT_EQ(dciFile.symlinkTarget("/3.link"), "/test/test.txt");
+        ASSERT_EQ(dciFile.dataRef("/3.link"), QByteArray("test\n"));
+        ASSERT_TRUE(dciFile.writeFile("/3.link", "TEST", true));
+        ASSERT_EQ(dciFile.dataRef("/test/test.txt"), QByteArray("TEST"));
+        // 相对路径链接
+        ASSERT_TRUE(dciFile.link("./test/test.txt", "/4.link"));
+        ASSERT_EQ(dciFile.symlinkTarget("/4.link"), "/test/test.txt");
+        ASSERT_EQ(dciFile.symlinkTarget("/4.link", true), "./test/test.txt");
+        ASSERT_EQ(dciFile.dataRef("/4.link"), QByteArray("TEST"));
+        // 链接一个软链接,测试 ".." 类型的相对路径
+        ASSERT_TRUE(dciFile.link("../4.link", "/test/5.link"));
+        ASSERT_EQ(dciFile.symlinkTarget("/test/5.link"), "/4.link");
+        ASSERT_EQ(dciFile.symlinkTarget("/test/5.link", true), "../4.link");
+        ASSERT_EQ(dciFile.dataRef("/test/5.link"), QByteArray("TEST"));
+        ASSERT_TRUE(dciFile.writeFile("/test/5.link", "test\n", true));
+        ASSERT_EQ(dciFile.dataRef("/test/test.txt"), QByteArray("test\n"));
+        // 链接同目录文件
+        ASSERT_TRUE(dciFile.link("test.txt", "/test/6.link"));
+        ASSERT_EQ(dciFile.dataRef("/test/6.link"), QByteArray("test\n"));
+        // 链接对象删除
+        ASSERT_TRUE(dciFile.remove("/4.link"));
+        ASSERT_TRUE(dciFile.exists("/test/5.link"));
+        ASSERT_EQ(dciFile.symlinkTarget("/test/5.link"), "/4.link");
+        ASSERT_TRUE(dciFile.dataRef("/test/5.link").isEmpty());
+        ASSERT_FALSE(dciFile.writeFile("/test/5.link", "", true));
+        // 链接对象改名
+        ASSERT_TRUE(dciFile.rename("/test/6.link", "/test/7.link"));
+        ASSERT_EQ(dciFile.dataRef("/test/7.link"), QByteArray("test\n"));
+        // 链接对象更换目录
+        ASSERT_TRUE(dciFile.rename("/test/7.link", "/7.link"));
+        ASSERT_EQ(dciFile.symlinkTarget("/7.link"), "/test.txt");
+        ASSERT_TRUE(dciFile.dataRef("/7.link").isEmpty());
+
+        {
+            // 复制数据
+            DDciFile dciFile2(dciFile.toData());
+            ASSERT_TRUE(dciFile2.isValid());
+            ASSERT_EQ(dciFile2.list("/"), dciFile.list("/"));
+            ASSERT_EQ(dciFile2.list("/test"), dciFile.list("/test"));
+            ASSERT_EQ(dciFile2.dataRef("/test/test.txt"), QByteArray("test\n"));
+            ASSERT_EQ(dciFile2.dataRef("/test/3.link"),
+                      dciFile2.dataRef("/test/text.txt"));
+            ASSERT_EQ(dciFile.toData(), dciFile2.toData());
+        }
+
+        // 清理链接文件
+        for (const QString &file : dciFile.list("/", false)) {
+            if (dciFile.type(file) == DDciFile::Symlink)
+                dciFile.remove(file);
+        }
+        for (const QString &file : dciFile.list("/test", false)) {
+            if (dciFile.type(file) == DDciFile::Symlink)
+                dciFile.remove(file);
+        }
+
+        // 改写文件数据
+        ASSERT_FALSE(dciFile.writeFile("/test/test.txt", "override the\"test.txt\""));
+        ASSERT_TRUE(dciFile.writeFile("/test/test.txt", "override the\"test.txt\"", true));
+        ASSERT_EQ(dciFile.dataRef("/test/test.txt"), "override the\"test.txt\"");
+
+        // 文件删除
+        ASSERT_TRUE(dciFile.remove("/test/test.txt"));
+        ASSERT_FALSE(dciFile.exists("/test/test.txt"));
+        ASSERT_EQ(dciFile.list("/test"), QStringList{});
+
+        // 目录删除
+        ASSERT_TRUE(dciFile.writeFile("/test/test.txt", ""));
+        ASSERT_TRUE(dciFile.remove("/test"));
+        ASSERT_FALSE(dciFile.exists("/test"));
+        ASSERT_EQ(dciFile.list("/"), QStringList{});
+        ASSERT_EQ(dciFile.toData(), QByteArrayLiteral("DCI\0\1\0\0\0"));
+    }
+
+    {
+        DDciFile dciFile;
+        ASSERT_TRUE(dciFile.mkdir("/test"));
+        ASSERT_TRUE(dciFile.writeFile("/test.txt", ""));
+
+        // 重命名
+        ASSERT_TRUE(dciFile.rename("/test.txt", "/test/new_test.txt"));
+        ASSERT_TRUE(dciFile.exists("/test/new_test.txt"));
+        ASSERT_FALSE(dciFile.rename("/test.txt", "/test/new_test.txt"));
+        ASSERT_EQ(dciFile.list("/test", true), QStringList{"new_test.txt"});
+        ASSERT_EQ(dciFile.list("/test"), QStringList{"/test/new_test.txt"});
+        // override 重命名
+        ASSERT_TRUE(dciFile.writeFile("/test.txt", "test", false));
+        ASSERT_FALSE(dciFile.rename("/test.txt", "/test/new_test.txt", false));
+        ASSERT_TRUE(dciFile.rename("/test.txt", "/test/new_test.txt", true));
+        ASSERT_EQ(dciFile.dataRef("/test/new_test.txt"), "test");
+
+        // 文件清空
+        ASSERT_TRUE(dciFile.remove("/"));
+        ASSERT_EQ(dciFile.list("/"), QStringList{});
+        ASSERT_EQ(dciFile.childrenCount("/"), 0);
+        ASSERT_EQ(dciFile.toData(), QByteArrayLiteral("DCI\0\1\0\0\0"));
+    }
+
+    // DDciFile::copy
+    {
+        DDciFile dciFile;
+        // 文件复制
+        ASSERT_TRUE(dciFile.writeFile("/test.txt", "test"));
+        ASSERT_TRUE(dciFile.copy("/test.txt", "/test.txt.new"));
+        ASSERT_TRUE(dciFile.exists("/test.txt.new"));
+        ASSERT_EQ(dciFile.list("/", true), (QStringList{"test.txt", "test.txt.new"}));
+        ASSERT_EQ(dciFile.dataRef("/test.txt"), dciFile.dataRef("/test.txt.new"));
+
+        // 目录复制
+        ASSERT_TRUE(dciFile.mkdir("/test"));
+        ASSERT_TRUE(dciFile.rename("/test.txt", "/test/test.txt"));
+        ASSERT_TRUE(dciFile.copy("/test", "/test.new"));
+        ASSERT_TRUE(dciFile.exists("/test.new/test.txt"));
+        ASSERT_EQ(dciFile.list("/test.new", true), (QStringList{"test.txt"}));
+        ASSERT_EQ(dciFile.dataRef("/test/test.txt"), dciFile.dataRef("/test.new/test.txt"));
+    }
+
+    // 文件排序
+    {
+        DDciFile dciFile;
+        dciFile.mkdir("/b01");
+        dciFile.writeFile("/a2.txt", "");
+        dciFile.writeFile("/b2", "");
+        dciFile.writeFile("/a11.txt", "");
+        const auto &list = dciFile.list("/", true);
+        ASSERT_EQ(list, (QStringList{"a2.txt", "a11.txt", "b01", "b2"}));
+
+        dciFile.writeFile("/b01/222", "");
+        dciFile.link("/b01/33", "/b01/1111");
+        dciFile.writeFile("/b01/33", "");
+        ASSERT_EQ(dciFile.list("/b01", true), (QStringList{"33", "222", "1111"}));
+
+        ASSERT_TRUE(dciFile.rename("/a11.txt", "/b01/200"));
+        ASSERT_TRUE(dciFile.copy("/a2.txt", "/b01/200.txt"));
+        ASSERT_EQ(dciFile.list("/", true), (QStringList{"a2.txt", "b01", "b2"}));
+        ASSERT_EQ(dciFile.list("/b01", true), (QStringList{"33", "200", "200.txt", "222", "1111"}));
+    }
+}
+
+class TestDCIFileHelper {
+public:
+    TestDCIFileHelper(const QString &fileName)
+        : fileName(fileName)
+    {
+        if (QFile::exists(fileName))
+            QFile::remove(fileName);
+    }
+    ~TestDCIFileHelper() {
+        QFile::remove(fileName);
+    }
+
+    inline QString dciFormatFilePath(const QString &subfile = QString()) const {
+        return "dci:" + fileName + subfile;
+    }
+
+    inline QString sourceFileName() const {
+        return fileName;
+    }
+
+private:
+    QString fileName;
+};
+
+static QByteArray readAll(const QString &file) {
+    QFile f(file);
+    if (!f.open(QIODevice::ReadOnly))
+        return {};
+    return f.readAll();
+}
+
+TEST_F(ut_DCI, DFileEngine) {
+    DDciFile::registerFileEngine();
+
+    {
+        TestDCIFileHelper helper(QDir::temp().absoluteFilePath("test.dci"));
+        // 空 dci 文件创建
+        QFile file(helper.dciFormatFilePath());
+        ASSERT_TRUE(file.exists());
+        QFileInfo info(file);
+        ASSERT_TRUE(info.isDir());
+        ASSERT_TRUE(info.isRoot());
+        // 文件夹不可写入
+        ASSERT_FALSE(file.open(QIODevice::WriteOnly));
+        // 空文件遍历
+        QDir dir(info.absoluteFilePath());
+        ASSERT_EQ(dir.entryList(), QStringList{});
+    }
+
+    {
+        TestDCIFileHelper helper(QDir::temp().absoluteFilePath("test.dci"));
+        {
+            // 内部文件创建
+            QFile file(helper.dciFormatFilePath("/test.txt"));
+            ASSERT_FALSE(file.open(QIODevice::ReadOnly));
+            ASSERT_TRUE(file.open(QIODevice::ReadOnly | QIODevice::WriteOnly));
+            ASSERT_TRUE(file.exists());
+            ASSERT_TRUE(QFileInfo(file).isFile());
+            ASSERT_TRUE(file.write("Hello") == 5);
+            ASSERT_TRUE(file.seek(0));
+            ASSERT_EQ(file.readAll(), "Hello");
+            ASSERT_TRUE(file.flush());
+            // 链接文件创建,绝对路径链接
+            ASSERT_TRUE(file.link(helper.dciFormatFilePath("/test.txt.link")));
+            {
+                QFile linkFile(helper.dciFormatFilePath("/test.txt.link"));
+                ASSERT_TRUE(QFileInfo(linkFile).isSymLink());
+                ASSERT_EQ(linkFile.symLinkTarget(), "/test.txt");
+                ASSERT_TRUE(linkFile.open(QIODevice::ReadOnly));
+                ASSERT_EQ(linkFile.readAll(), "Hello");
+                ASSERT_EQ(linkFile.size(), file.size());
+            }
+
+            file.close();
+
+            // 文件信息
+            QFileInfo info1(file);
+            QFileInfo info2(helper.sourceFileName());
+            ASSERT_FALSE(info1.isRoot());
+            ASSERT_EQ(info1.permissions(), info2.permissions());
+            ASSERT_EQ(info1.fileTime(QFile::FileAccessTime),
+                      info2.fileTime(QFile::FileAccessTime));
+            ASSERT_EQ(info1.fileTime(QFile::FileBirthTime),
+                      info2.fileTime(QFile::FileBirthTime));
+            ASSERT_EQ(info1.fileTime(QFile::FileMetadataChangeTime),
+                      info2.fileTime(QFile::FileMetadataChangeTime));
+            ASSERT_EQ(info1.fileTime(QFile::FileModificationTime),
+                      info2.fileTime(QFile::FileModificationTime));
+            ASSERT_EQ(info1.ownerId(), info2.ownerId());
+            ASSERT_EQ(info1.owner(), info2.owner());
+            ASSERT_EQ(info1.groupId(), info2.groupId());
+            ASSERT_EQ(info1.group(), info2.group());
+
+            // 目录遍历
+            QDir dir(helper.dciFormatFilePath());
+            ASSERT_EQ(dir.entryList(), (QStringList{"test.txt", "test.txt.link"}));
+
+            // 文件大小
+            ASSERT_EQ(file.size(), 5);
+            ASSERT_TRUE(file.resize(10));
+            ASSERT_EQ(file.size(), 10);
+            ASSERT_EQ(QFile(helper.dciFormatFilePath("/test.txt.link")).size(), 10);
+        }
+
+        {
+            // 文件读取
+            QFile file(helper.dciFormatFilePath("/test.txt"));
+            ASSERT_TRUE(file.exists());
+            ASSERT_TRUE(file.open(QIODevice::ReadOnly));
+            ASSERT_EQ(file.readAll(), QByteArrayLiteral("Hello\0\0\0\0\0"));
+            file.close();
+            // [/test.txt, /test.txt.link]
+        }
+
+        {
+            // 文件内容改写
+            QFile file(helper.dciFormatFilePath("/test.txt"));
+            ASSERT_TRUE(file.open(QIODevice::ReadWrite));
+            ASSERT_TRUE(file.seek(1));
+            ASSERT_TRUE(file.putChar('E'));
+            char ch;
+            ASSERT_TRUE(file.getChar(&ch));
+            ASSERT_EQ(ch, 'l');
+            ASSERT_EQ(file.readAll(), QByteArrayLiteral("lo\0\0\0\0\0"));
+            ASSERT_TRUE(file.seek(0));
+            ASSERT_EQ(file.readAll(), QByteArrayLiteral("HEllo\0\0\0\0\0"));
+            ASSERT_EQ(readAll(helper.dciFormatFilePath("/test.txt.link")),
+                      QByteArrayLiteral("HEllo\0\0\0\0\0"));
+        }
+
+        // 目录创建
+        ASSERT_TRUE(QDir(helper.dciFormatFilePath()).mkdir("1"));
+        ASSERT_FALSE(QDir(helper.dciFormatFilePath()).mkdir("2/3"));
+        ASSERT_TRUE(QDir(helper.dciFormatFilePath()).mkpath("2/3"));
+        ASSERT_TRUE(QFileInfo(helper.dciFormatFilePath("/1")).isDir());
+        // [/test.txt, /test.txt.link, /1, /2, /2/3]
+
+        // 目录 rename
+        ASSERT_FALSE(QFile::rename(helper.dciFormatFilePath("/1"), "/1"));
+        ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/1"),
+                                  helper.dciFormatFilePath("/1.new")));
+        ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/2"),
+                                  helper.dciFormatFilePath("/2.new")));
+        ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/2.new/3"),
+                                  helper.dciFormatFilePath("/3")));
+        // [/test.txt, /test.txt.link, /1.new, /2.new, /3]
+
+        // 文件 rename
+        ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/test.txt"),
+                                  helper.dciFormatFilePath("/test.txt.new")));
+        ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/test.txt.new"),
+                                  helper.dciFormatFilePath("/1.new/test.txt")));
+        // [/1.new, /1.new/test.txt.new, /test.txt.link, /2.new, /3]
+        // 此时的 link file 应该无效
+        ASSERT_TRUE(readAll(helper.dciFormatFilePath("/test.txt.link")).isEmpty());
+        // 链接文件 rename
+        ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/test.txt.link"),
+                                  helper.dciFormatFilePath("/1.new/test.txt.link")));
+
+        // 复制
+        ASSERT_TRUE(QFile::copy(helper.dciFormatFilePath("/1.new/test.txt"),
+                                helper.dciFormatFilePath("/test.txt")));
+        // 链接文件复制
+        ASSERT_TRUE(QFile::copy(helper.dciFormatFilePath("/1.new/test.txt.link"),
+                                helper.dciFormatFilePath("/test.txt.link")));
+        // 检查复制/rename后的链接文件
+        ASSERT_EQ(QFile(helper.dciFormatFilePath("/test.txt.link")).symLinkTarget(),
+                  QStringLiteral("/test.txt"));
+        ASSERT_EQ(readAll(helper.dciFormatFilePath("/test.txt.link")),
+                  readAll(helper.dciFormatFilePath("/test.txt")));
+        ASSERT_EQ(QFile(helper.dciFormatFilePath("/1.new/test.txt.link")).symLinkTarget(),
+                  QStringLiteral("/test.txt"));
+        ASSERT_EQ(readAll(helper.dciFormatFilePath("/1.new/test.txt.link")),
+                  readAll(helper.dciFormatFilePath("/test.txt")));
+        // 复制目录
+        ASSERT_TRUE(QFile::copy(helper.dciFormatFilePath("/1.new"),
+                                 helper.dciFormatFilePath("/1")));
+        ASSERT_EQ(QDir(helper.dciFormatFilePath("/1")).entryList(),
+                  QDir(helper.dciFormatFilePath("/1.new")).entryList());
+        // [/1.new, /1.new/test.txt, /1.new/test.txt.link, /2.new, /3, /test.txt, /test.txt.link, /1]
+
+        // 目录遍历
+        QStringList list {
+            helper.dciFormatFilePath("/1"),
+            helper.dciFormatFilePath("/1/test.txt.link"),
+            helper.dciFormatFilePath("/1/test.txt"),
+            helper.dciFormatFilePath("/1.new"),
+            helper.dciFormatFilePath("/1.new/test.txt"),
+            helper.dciFormatFilePath("/1.new/test.txt.link"),
+            helper.dciFormatFilePath("/2.new"),
+            helper.dciFormatFilePath("/3"),
+            helper.dciFormatFilePath("/test.txt"),
+            helper.dciFormatFilePath("/test.txt.link")
+        };
+        QDirIterator di(helper.dciFormatFilePath(), QDirIterator::Subdirectories);
+        while (di.hasNext()) {
+            const QString &file = di.next();
+            ASSERT_TRUE(list.removeOne(file));
+        }
+        ASSERT_TRUE(list.isEmpty());
+
+        // 删除
+        ASSERT_TRUE(QFile::remove(helper.dciFormatFilePath("/test.txt")));
+        ASSERT_TRUE(QFile::remove(helper.dciFormatFilePath("/test.txt.link")));
+        ASSERT_TRUE(QFile::remove(helper.dciFormatFilePath("/2.new")));
+        ASSERT_TRUE(QFile::remove(helper.dciFormatFilePath("/1.new")));
+        // [/3]
+        ASSERT_EQ(QDir(helper.dciFormatFilePath()).entryList(),
+                  (QStringList {"1", "3"}));
+    }
+}
index 45259d344695d9c85d980ef11ac377e41b84cc72..d11d5e934903e29f781db6aa7f570da746ec0dfc 100644 (file)
@@ -1,24 +1,6 @@
-/*
- * Copyright (C) 2019 Deepin Technology Co., Ltd.
- *               2019 Gary Wang
- *
- * Author:     Gary Wang <wzc782970009@gmail.com>
- *
- * Maintainer: Gary Wang <wangzichong@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <QDebug>
 #include <QString>
index b59591d199fc79a11bb28ffd338ac935845cd72c..ea3c40e472b7d873678be8e82184c8cd00f877aa 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include "util/ddisksizeformatter.h"
index 2c08d7a364d35782e2f930167b97bfb3173206a1..0d788bb7397beb2d47cc0d7561eaeec9e12050ec 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include <QDir>
index 8c9db93faa8dd7a1ab09302608329e1ccacd6df5..cd516a9a57e4500b910c54fd07388565a898b271 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include <QDir>
index abf714051a3debd8fd204e7009dfd2a8e80af161..9c1967b03b183827c3f1dd205524dc743ed5ecd4 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include <QObject>
index 735208b0b6dfa237b9b83abe9e71bd4fc6389c21..9c9117ab4196d5b72bd8c7ae7db1bebb24bb69de 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include <QDir>
index 2c1c53238c506e9992c71b961e023b7a34ecb552..f174af5ebdbbefae2afd135755fc6bb4ef10c54a 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include "util/dpinyin.h"
index 66a8a6f04084ecc7f01b04d6f9fb60b7a55134a4..1d162ac02460cf11bb4b6332b3a2496982849b8f 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include <QFile>
index 8c3054be7e933bfdd70a9bdb535369bc641f25d9..c27bbca0f721813d15de19ab24d1d90159a2f824 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include "dsecurestring.h"
index 0b8af7f625a0210a3fa5089fa25d23f3656903e1..2c186d827d646c7010b1a564ad739c85fac1f2b7 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include <QFile>
index f64b8a5ffc56a89e2d002319c707d249916f84bc..32f18a41b055ac22bd30ae09d8c9499ab1319a47 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include <QDir>
index 1478164afe5377c47e26871bb006ff4f5564f2eb..23d5e92f3ef2a021cd0c758ba1134576c44e357f 100644 (file)
@@ -1,30 +1,13 @@
-/*
- * Copyright (C) 2020 ~ 2020 Deepin Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <QObject>
 #include <gtest/gtest.h>
 #include <QTest>
 #include <QtConcurrent>
 
-#include <util/DThreadUtils>
+#include <DThreadUtils>
 
 DCORE_USE_NAMESPACE
 
index 136373f64e2e22d6afcbe07dac48032be6f5cf91..6ee1cf96354851db591b2a95ab77b1dd61efb249 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include "util/dtimeunitformatter.h"
index 80dbd49fb336543b69dfeacce3a6e6ccabc2152d..e58e95c4231eb86891f6a8fcd63f4b6376b746cd 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include <QDir>
index e90401485ce9dcca4da8b505263398e97b3889d1..395ae96177e7502945d822f179b86002119f0747 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "ut_dutil.h"
 
index a902ae6a552546a88c7f77cf025226c97484c033..92005a70fbbdcd17a87288154ad701ab5cf69c92 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #pragma once
 
index 5d5dbf27d3e69cba1e2e2d46b3b75f5467164bba..d66129890c8fffb8511a88f83dffffeb616bb1d9 100644 (file)
@@ -1,26 +1,9 @@
-/*
- * Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
-#include <util/DVtableHook>
+#include <DVtableHook>
 
 namespace TestClass {
 class A
index d8fe5dbfb91a37178cadad9c1c046401318d2bf7..101b6b771259446c73f61afd8b6c5cffc1061932 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include <QFile>
index 177c6afc9bb38a6b5cf608e3f87ab9a64f2fa636..7ce3a70b58a0002bdf907741825fb1341aee29b3 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include <QFile>
index f62a5a856945c8fdd9a0d1b0298fd51b23933f43..bb9ded4f2295b8d3a7a9cf086d72ba618f92b8af 100644 (file)
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
- *
- * Author:     Wang Fei <wangfeia@uniontech.com>
- *
- * Maintainer: Wang Fei <wangfeia@uniontech.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <gtest/gtest.h>
 #include <QFile>
index 4e14d305806ab31268644d48dc8f2235dc2b5f2b..76687ddbbe360442026f6a77c7483c41ace87029 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include "ut_singleton.h"
 
index f07c4ed166e69c50dc239d8f901b37307466b7c5..5355cca8178c5c457f6d1b90a91f2c1af8281537 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #pragma once
 
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644 (file)
index 0000000..23c8d02
--- /dev/null
@@ -0,0 +1,4 @@
+add_subdirectory(dci)
+add_subdirectory(deepin-os-release)
+add_subdirectory(qdbusxml2cpp)
+add_subdirectory(settings)
diff --git a/tools/dci/CMakeLists.txt b/tools/dci/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c35c9ed
--- /dev/null
@@ -0,0 +1,22 @@
+set(BIN_NAME dci)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+find_package(Qt5 REQUIRED COMPONENTS Core)
+add_definitions(-DDTK_NO_PROJECT)
+# start dci
+include(../../src/dci/dci.cmake)
+add_executable(${BIN_NAME}
+  ${dci_SRCS}
+  main.cpp
+)
+target_link_libraries(${BIN_NAME} PRIVATE
+  Qt5::Core 
+)
+target_include_directories(${BIN_NAME} PUBLIC
+  ${Qt5Core_PRIVATE_INCLUDE_DIRS}
+  ../../include/
+  ../../include/dci/
+  ../../include/DtkCore/
+  ../../include/base/
+  ../../include/global/
+)
+#end dci
diff --git a/tools/dci/dci.pro b/tools/dci/dci.pro
new file mode 100644 (file)
index 0000000..934d632
--- /dev/null
@@ -0,0 +1,23 @@
+QT -= gui
+
+CONFIG += c++11 console
+CONFIG -= app_bundle
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+DEFINES += DTK_NO_PROJECT
+
+# 支持从当前目录找到动态库
+mac {
+    QMAKE_RPATHDIR = "@executable_path"
+}
+
+SOURCES += \
+    main.cpp
+
+include($$PWD/../../src/dci/dci.pri)
+INCLUDEPATH += $$PWD/../../src
+
+#不要依赖于 dtk 的其它任何文件,要确保能在 macox 上独立编译
diff --git a/tools/dci/main.cpp b/tools/dci/main.cpp
new file mode 100644 (file)
index 0000000..d336f13
--- /dev/null
@@ -0,0 +1,177 @@
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <QCoreApplication>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include <QDir>
+#include <QFileInfo>
+#include <QFile>
+#include <QDebug>
+
+#include <unistd.h>
+
+#include "dci/ddcifile.h"
+
+static QString getSymLinkTarget(const QString &file, const QString &originPath) {
+    char target[512] = {0};
+    const auto ret = readlink(file.toLocal8Bit().constData(), target, 511);
+    if (ret <= 0)
+        return QString();
+    QString tar = QByteArray(target, ret);
+    QString path = originPath;
+    if (originPath.endsWith('/'))
+        path = path.left(path.length() - 1);
+    if (tar.startsWith(path)) {
+        tar = tar.remove(0, path.length());
+    }
+    return tar;
+}
+
+static bool copyFilesToDci(DDciFile *dci, const QString &targetDir, const QString &sourceDir, const QString &originPath) {
+    QDir dir(sourceDir);
+    for (const auto &info : dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)) {
+        const QString &newFile = QDir(targetDir).filePath(info.fileName());
+        if (info.isDir()) {
+            if (!dci->mkdir(newFile))
+                return false;
+            if (!copyFilesToDci(dci, newFile, info.absoluteFilePath(), originPath))
+                return false;
+        } else if (info.isSymLink()) {
+            if (!dci->link(getSymLinkTarget(info.absoluteFilePath(), originPath), newFile))
+                return false;
+        } else if (info.isFile()) {
+            QFile file(info.absoluteFilePath());
+            if (!file.open(QIODevice::ReadOnly))
+                return false;
+            if (!dci->writeFile(newFile, file.readAll()))
+                return false;
+        }
+    }
+
+    return true;
+}
+
+QString cleanPath(const QString &path) {
+    return path.size() < 2 || !path.endsWith(QDir::separator()) ? path : path.chopped(1);
+}
+
+bool createTo(const QString &sourceDir, const QString &targetDir) {
+    QFileInfo info(cleanPath(sourceDir));
+    if (!info.isDir())
+        return false;
+
+    const QString &iconName = info.fileName();
+    if (iconName.isEmpty()) {
+        printf("The icon name is not correctly resolved in the source path: \"%s\".\n", qPrintable(sourceDir));
+        return false;
+    }
+
+    const auto newFile = QDir(targetDir).filePath(iconName + ".dci");
+    if (QFile::exists(newFile)) {
+        printf("The path \"%s\" already exists.\n", qPrintable(newFile));
+        return false;
+    }
+
+    DDciFile dci;
+    if (!copyFilesToDci(&dci, "/", sourceDir, sourceDir))
+        return false;
+
+    return dci.writeToFile(newFile);
+}
+
+static bool copyFilesFromDci(const DDciFile *dci, const QString &targetDir, const QString &sourceDir) {
+    QDir target(targetDir);
+    for (const QString &file : dci->list(sourceDir)) {
+        const QString &newFileName = QFileInfo(file).fileName();
+        const QString &newFilePath = target.filePath(newFileName);
+        const auto &type = dci->type(file);
+        if (type == DDciFile::Directory) {
+            if (!target.mkdir(newFileName))
+                return false;
+            if (!copyFilesFromDci(dci, newFilePath, file))
+                return false;
+        } else if (type == DDciFile::File) {
+            QFile newFile(newFilePath);
+            if (!newFile.open(QIODevice::WriteOnly))
+                return false;
+            const auto &data = dci->dataRef(file);
+            if (newFile.write(data) != data.size())
+                return false;
+        } else if (type == DDciFile::Symlink) {
+            if (!QFile::link(dci->symlinkTarget(file, true), newFilePath))
+                return false;
+        } else {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool exportTo(const QString &dciFile, const QString &targetDir) {
+    QFileInfo info(dciFile);
+    if (!info.isFile() || info.suffix() != "dci")
+        return false;
+    const QString &newDir = QDir(targetDir).filePath(info.baseName());
+    if (QDir(newDir).exists())
+        return false;
+    if (!QDir::current().mkdir(newDir))
+        return false;
+
+    DDciFile dci(dciFile);
+    if (!dci.isValid())
+        return false;
+
+    return copyFilesFromDci(&dci, newDir, "/");
+}
+
+int main(int argc, char *argv[])
+{
+    QCoreApplication a(argc, argv);
+
+    a.setOrganizationName("zccrs");
+    a.setApplicationName("dci");
+    a.setApplicationVersion("0.0.1");
+
+    QCommandLineParser commandParser;
+    commandParser.setApplicationDescription("DCI tool is a command tool that automatically packs and unpacks DCI directories.\n"
+                                            "If you have created an icon directory according to the correct DCI directory specification,\n"
+                                            "you can easily make the DCI icons with this tool.\n"
+                                            "The commands of DCI tools can be expressed as follows:\n"
+                                            "\t dci --create [target file path] [source directory path]\n"
+                                            "\t dci --export [target directory path] [source file path]\n"
+                                            "For example, the tool is used in the following ways: \n"
+                                            "\t dci --create ~/Desktop ~/Desktop/action_add\n"
+                                            "\t dci --export ~/Desktop ~/Desktop/action_add.dci\n");
+
+    auto options = QList<QCommandLineOption> {
+        QCommandLineOption("create", "Create the new dci files by the directorys", "targetDirectiry"),
+        QCommandLineOption("export", "Export the dci files to the directorys", "targetDirectory"),
+    };
+    commandParser.addOptions(options);
+    commandParser.addPositionalArgument("sources", "The directorys of create or the dci files of export",
+                                        "[dir1 dir2...]/[file1 file2...]");
+    commandParser.addHelpOption();
+    commandParser.addVersionOption();
+    commandParser.process(a);
+
+    if (commandParser.isSet(options.at(0))) {
+        for (const QString &dir : commandParser.positionalArguments()) {
+            if (!createTo(dir, commandParser.value(options.at(0)))) {
+                printf("Failed on create dci file for \"%s\"\n", qPrintable(dir));
+            }
+        }
+    } else if (commandParser.isSet(options.at(1))) {
+        for (const QString &dci : commandParser.positionalArguments()) {
+            if (!exportTo(dci, commandParser.value(options.at(1)))) {
+                printf("Failed on export the \"%s\" dci file\n", qPrintable(dci));
+            }
+        }
+    } else {
+        commandParser.showHelp(-1);
+    }
+
+    return 0;
+}
diff --git a/tools/deepin-os-release/CMakeLists.txt b/tools/deepin-os-release/CMakeLists.txt
new file mode 100644 (file)
index 0000000..25cb0e8
--- /dev/null
@@ -0,0 +1,30 @@
+set(BIN_NAME deepin-os-release)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+set(CMAKE_AUTOMOC ON)
+find_package(Qt5 REQUIRED COMPONENTS Core)
+add_definitions(-DDTK_NO_PROJECT)
+# start dci
+set(dci_SRCS
+  ../../include/global/dsysinfo.h
+  ../../include/global/ddesktopentry.h
+  ../../src/dsysinfo.cpp
+  ../../src/ddesktopentry.cpp
+)
+add_executable(${BIN_NAME}
+  ${dci_SRCS}
+  main.cpp
+)
+target_link_libraries(${BIN_NAME} PRIVATE
+  Qt5::Core
+)
+target_include_directories(${BIN_NAME} PUBLIC
+  ${Qt5Core_PRIVATE_INCLUDE_DIRS}
+  ../../include/
+  ../../include/dci/
+  ../../include/DtkCore/
+  ../../include/base/
+  ../../include/global/
+)
+install(TARGETS ${BIN_NAME} DESTINATION "${TOOL_INSTALL_DIR}")
+
+#end dci
index b0a608504d5aaefc6c3690807c555bbf53823bc1..a7d0614eb2ad850ff483e6922386c7e99717bbac 100644 (file)
@@ -1,23 +1,7 @@
-/*
- * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
- *
- * Author:     zccrs <zccrs@live.com>
- *
- * Maintainer: zccrs <zhangjide@deepin.com>
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
 #include "dsysinfo.h"
 
 #include <QCoreApplication>
diff --git a/tools/qdbusxml2cpp/CMakeLists.txt b/tools/qdbusxml2cpp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1dda035
--- /dev/null
@@ -0,0 +1,19 @@
+set(BIN_NAME qdbusxml2cpp-fix)
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+find_package(Qt5 REQUIRED COMPONENTS Core)
+find_package(Qt5 REQUIRED COMPONENTS DBus)
+
+add_executable(${BIN_NAME}
+  qdbusxml2cpp.cpp 
+)
+
+target_link_libraries(
+  ${BIN_NAME} PRIVATE
+  Qt5::DBus
+  Qt5::Core 
+)
+target_include_directories(${BIN_NAME} PUBLIC
+  ${Qt5DBus_PRIVATE_INCLUDE_DIRS}
+)
+install(TARGETS ${BIN_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
index 069626da8ca3281af8890395889d4fc728eb9edb..3f86649bd4a69188fb08f10d0d716493dadcb3f7 100644 (file)
@@ -1,35 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL21$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <qbytearray.h>
 #include <qdebug.h>
@@ -97,7 +68,7 @@ static const char includeList[] =
     "#include <QtCore/QStringList>\n"
     "#include <QtCore/QVariant>\n"
     "\n"
-    "#include <DBusExtendedAbstractInterface>\n";
+    "#include <DDBusExtendedAbstractInterface>\n";
 
 static const char forwardDeclarations[] =
     "QT_BEGIN_NAMESPACE\n"
diff --git a/tools/script/dtk-license.py b/tools/script/dtk-license.py
deleted file mode 100644 (file)
index cf49e81..0000000
+++ /dev/null
@@ -1,320 +0,0 @@
-#!/usr/bin/env python3
-
-import fnmatch
-import os
-import argparse
-import re
-import datetime
-
-
-license_header = '''/*\n'''
-license_empty = ''' *\n'''
-license_copyright_prifix = ''' * Copyright (C) {0}\n'''
-license_copyright_indent = ''' *               {0}\n'''
-license_author_prifix = ''' * Author:     {0}\n'''
-license_person_indent = ''' *             {0}\n'''
-license_maintainer_prifix = ''' * Maintainer: {0}\n'''
-license_tail = ''' */\n\n'''
-default_license_body = ''' * 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
- * 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 <http://www.gnu.org/licenses/>.
-'''
-
-
-def isInt(value):
-    try:
-        int(value)
-        return True
-    except ValueError:
-        return False
-
-
-class Copyright:
-    def __init__(self, line):
-        self.year_start = datetime.datetime.now().year
-        self.year_end = datetime.datetime.now().year
-
-        if "copyright" in line.lower():
-            line = line[re.search("copyright", line, re.IGNORECASE).end():]
-
-        copyright_sysbol = re.search("\(.\)", line, re.IGNORECASE)
-        if copyright_sysbol is not None:
-            copyright_brace_end_pos = copyright_sysbol.end()
-            line = line[copyright_brace_end_pos:]
-            pass
-
-        if re.search("[0-9]+", line) is not None:
-            year_start_pos = re.search("[0-9]+", line).start()
-            year_start_end_pos = re.search("[0-9]+", line).end()
-            self.year_start = int(line[year_start_pos: year_start_end_pos])
-            line = line[year_start_end_pos:]
-
-        if re.search("[0-9]+", line) is not None:
-            year_end_pos = re.search("[0-9]+", line).start()
-            year_end_end_pos = re.search("[0-9]+", line).end()
-            if re.search("~", line) is not None:
-                year_spilt = re.search("~", line).start()
-                if (year_spilt < year_end_pos):
-                    self.year_end = int(line[year_end_pos: year_end_end_pos])
-                    line = line[year_end_end_pos:]
-
-        self.name = line.strip()
-
-    def string(self):
-        return "{0} ~ {1} {2}".format(
-            self.year_start,
-            self.year_end,
-            self.name)
-
-
-class Person:
-    def __init__(self, line):
-        if re.search(":", line, re.IGNORECASE) is not None:
-            split_end = re.search(":", line).end()
-            line = line[split_end:]
-
-        self.name = line.strip()
-
-
-def filter_files(file_list, ext_list):
-    for file in file_list:
-        for ext in ext_list:
-            if fnmatch.fnmatch(file, ext):
-                yield file
-                break
-
-
-class Source:
-    def __init__(self, filename):
-        self.body = []
-        self.copyrights = {}
-        self.authors = {}
-        self.maintainers = {}
-        self.filename = filename
-        self.is_deepin_copyright = False
-        self.update_license(filename)
-
-    def update_license(self, filename):
-        find_license_start = False
-        find_license_end = False
-
-        parse_copyright_start = False
-        parse_author_start = False
-        parse_maintainer_start = False
-
-        with open(filename, "r") as f:
-            print("process:", filename)
-            for line in f:
-                if line.strip().startswith("/*") and (0 == len(self.body)):
-                    find_license_start = True
-
-                if (0 != len(self.body)) or (0 != len(line.strip())):
-                    self.body.append(line)
-
-                if "*/" in line.strip() and find_license_start:
-                    if not find_license_end:
-                        # print("clean body")
-                        self.body = []
-                    find_license_end = True
-
-                if find_license_start and not find_license_end:
-                    if re.search("(\*)+", line):
-                        # remove *****
-                        line = line[re.search("(\*)+", line).end():]
-
-                    if "copyright" in line.lower():
-                        parse_copyright_start = True
-                        parse_author_start = False
-                        parse_maintainer_start = False
-                    if "author" in line.lower():
-                        parse_copyright_start = False
-                        parse_author_start = True
-                        parse_maintainer_start = False
-                    if "maintainer" in line.lower():
-                        parse_copyright_start = False
-                        parse_author_start = False
-                        parse_maintainer_start = True
-                    if 0 == len(line.strip()):
-                        parse_copyright_start = False
-                        parse_author_start = False
-                        parse_maintainer_start = False
-
-                    if parse_copyright_start:
-                        cr = Copyright(line)
-                        self.copyrights[cr.name] = cr
-
-                    if parse_author_start:
-                        p = Person(line)
-                        self.authors[p.name] = p
-
-                    if parse_maintainer_start:
-                        p = Person(line)
-                        self.maintainers[p.name] = p
-
-    def fix_deepin(self):
-        new_cr = {}
-        self.is_deepin_copyright = False
-        for k, cr in self.copyrights.items():
-            if "deepin" in cr.name.lower():
-                cr.name = "Deepin Technology Co., Ltd."
-                self.is_deepin_copyright = True
-            new_cr[cr.name] = cr
-        self.copyrights = new_cr
-
-    def dump(self):
-        for cr in self.copyrights:
-            print(cr.year_start, cr.year_end, cr.name)
-        for a in self.authors:
-            print(a.name)
-        for a in self.maintainers:
-            print(a.name)
-        print("body size:", len(self.body))
-
-    def save(self):
-        wf = open(self.filename, 'w')
-        wf.write(license_header)
-
-        if len(self.copyrights):
-            write_first = False
-            for k, cr in self.copyrights.items():
-                if not write_first:
-                    wf.write(license_copyright_prifix.format(cr.string()))
-                    write_first = True
-                else:
-                    wf.write(license_copyright_indent.format(cr.string()))
-            wf.write(license_empty)
-
-        if len(self.authors):
-            write_first = False
-            for k, author in self.authors.items():
-                if not write_first:
-                    wf.write(license_author_prifix.format(author.name))
-                    write_first = True
-                else:
-                    wf.write(license_person_indent.format(author.name))
-            wf.write(license_empty)
-
-        if len(self.maintainers):
-            write_first = False
-            for k, maintainer in self.maintainers.items():
-                if not write_first:
-                    wf.write(license_maintainer_prifix.format(maintainer.name))
-                    write_first = True
-                else:
-                    wf.write(license_person_indent.format(maintainer.name))
-            wf.write(license_empty)
-
-        wf.write(default_license_body)
-        wf.write(license_tail)
-
-        for l in self.body:
-            wf.write(l)
-        wf.close()
-
-
-def parse_args():
-    parser = argparse.ArgumentParser()
-    parser.add_argument('--authors', help='''authors split by ":". For example:\n
-        "Iceyer <me@iceyer.net>:Li He <lihe@deepin.com>"''')
-    parser.add_argument('--maintainers', help='''maintainers split by ":". For example: \n
-        "Iceyer <me@iceyer.net>:Li He <lihe@deepin.com>"''')
-    parser.add_argument('--copyrights', help='''copyrights split by ":". For example: \n
-        "2011 ~ 2017 Deepin Technology Co., Ltd.:2011 ~ 2017 Li He"''')
-    parser.add_argument('--fix-deepin',  dest='fixdeepin',
-                        action='store_const', const=True, default=False, help='''
-                        fix deein copyrights''')
-    parser.add_argument('dir',  help='''
-                        source code dir''')
-
-    args = parser.parse_args()
-    return args
-
-
-def match_files(dir):
-    EXTENSIONS = "*.cpp", "*.c", "*.h", "*.go"
-    matches = []
-    for root, dirnames, filenames in os.walk(dir):
-        for filename in filter_files(filenames, EXTENSIONS):
-            matches.append(os.path.join(root, filename))
-    return matches
-
-
-def test():
-    cr = Copyright("Copyright (C)  Deepin Technology Co., Ltd.")
-    print(cr.year_start, cr.year_end, cr.name)
-    cr = Copyright("Copyright (C) 2007 Deepin Technology Co., Ltd.")
-    print(cr.year_start, cr.year_end, cr.name)
-    cr = Copyright("Copyright (C) 2007 ~ 2016 Deepin Technology Co., Ltd.")
-    print(cr.year_start, cr.year_end, cr.name)
-
-    p = Person("Author:     Wang Yong <wangyong@deepin.com>")
-    print(p.name)
-    p = Person("    Wang Yong <wangyong@deepin.com>")
-    print(p.name)
-
-
-def get_authors(args):
-    authors = []
-    if args.authors is not None:
-        for a in args.authors.split(":"):
-            authors.append(Person(a))
-    return authors
-
-
-def get_copyrights(args):
-    copyrights = []
-    if args.copyrights is not None:
-        for a in args.copyrights.split(":"):
-            copyrights.append(Copyright(a))
-    return copyrights
-
-
-def get_maintainers(args):
-    maintainers = []
-    if args.maintainers is not None:
-        for a in args.maintainers.split(":"):
-            maintainers.append(Copyright(a))
-    return maintainers
-
-
-def main():
-    args = parse_args()
-    authors = get_authors(args)
-    copyrights = get_copyrights(args)
-    maintainers = get_maintainers(args)
-    fixdeepin = args.fixdeepin
-    files = match_files(args.dir)
-
-    for filename in files:
-        s = Source(filename)
-        # s.dump()
-        if fixdeepin:
-            s.fix_deepin()
-        if not s.is_deepin_copyright and 0 != len(s.copyrights):
-            continue
-
-        if 0 == len(s.copyrights):
-            for cr in copyrights:
-                s.copyrights[cr.name] = cr
-
-        for p in authors:
-            s.authors[p.name] = p
-
-        for p in maintainers:
-            s.maintainers[p.name] = p
-
-        s.save()
-        # s.dump()
-
-
-if __name__ == "__main__":
-    main()
diff --git a/tools/script/dtk-translate.py b/tools/script/dtk-translate.py
deleted file mode 100644 (file)
index 97bb7e6..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env python3
-
-import sys,os,fnmatch
-from subprocess import call
-
-translations_dir = os.getcwd() + "/translations"
-if (len(sys.argv) == 2):
-    translations_dir = sys.argv[1] + "/translations"
-
-print("set translations dir:", translations_dir)
-
-tslist = fnmatch.filter(os.listdir(translations_dir), '*.ts')
-
-# This would print all the files and directories
-for tsfile in tslist:
-    tspath = translations_dir + "/" + tsfile
-    print ("process", tspath)
-    call(["lrelease", tspath])
diff --git a/tools/settings/CMakeLists.txt b/tools/settings/CMakeLists.txt
new file mode 100644 (file)
index 0000000..052eeaa
--- /dev/null
@@ -0,0 +1,29 @@
+set(BIN_NAME dtk-settings)
+
+find_package(Qt5 REQUIRED COMPONENTS Core)
+find_package(Qt5 REQUIRED COMPONENTS Xml)
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(QGSettings REQUIRED gsettings-qt)
+add_executable(${BIN_NAME}
+  main.cpp
+)
+target_link_libraries(
+  ${BIN_NAME} PRIVATE
+  Qt5::Core
+  Qt5::Xml
+  dtkcore
+  ${QGSettings_LIBRARIES}
+)
+target_include_directories( ${BIN_NAME} PUBLIC
+  ${QGSettings_INCLUDE_DIRS}
+  ../../include/util/
+  ../../include/dci/
+  ../../include/log/
+  ../../include/base/
+  ../../include/global/
+  ../../include/DtkCore/
+  ../../include/settings/
+  ../../include/filesystem/
+  ../../include/
+)
+install(TARGETS ${BIN_NAME} DESTINATION "${TOOL_INSTALL_DIR}")
index d82ffab9344af9559436e7024e8c9f8eb41fbc3f..3fd0ea7e7f18c3603c67fd773c6ef28c50b8e3e3 100644 (file)
@@ -1,19 +1,6 @@
-/*
- * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
- *
- * This program 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
- * 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 Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
 
 #include <QCoreApplication>
 
diff --git a/tools/tools.pro b/tools/tools.pro
deleted file mode 100644 (file)
index 253f225..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-TEMPLATE = subdirs
-
-!mac:!win*: SUBDIRS += settings deepin-os-release qdbusxml2cpp